/******************************************************************************/ /* */ /* X r d C r y p t o s s l X 5 0 9 R e q. c c */ /* */ /* (c) 2005 G. Ganis , CERN */ /* */ /* This file is part of the XRootD software suite. */ /* */ /* XRootD is free software: you can redistribute it and/or modify it under */ /* the terms of the GNU Lesser General Public License as published by the */ /* Free Software Foundation, either version 3 of the License, or (at your */ /* option) any later version. */ /* */ /* XRootD is distributed in the hope that it will be useful, but WITHOUT */ /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ /* License for more details. */ /* */ /* You should have received a copy of the GNU Lesser General Public License */ /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */ /* */ /* The copyright holder's institutional names and contributor's names may not */ /* be used to endorse or promote products derived from this software without */ /* specific prior written permission of the institution or contributor. */ /* */ /******************************************************************************/ /* ************************************************************************** */ /* */ /* OpenSSL implementation of XrdCryptoX509Req */ /* */ /* ************************************************************************** */ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #include "XrdCrypto/XrdCryptosslRSA.hh" #include "XrdCrypto/XrdCryptosslX509Req.hh" #include "XrdCrypto/XrdCryptosslAux.hh" #include "XrdCrypto/XrdCryptosslTrace.hh" #include <openssl/pem.h> //_____________________________________________________________________________ XrdCryptosslX509Req::XrdCryptosslX509Req(XrdSutBucket *buck) : XrdCryptoX509Req() { // Constructor certificate from BIO 'bcer' EPNAME("X509Req::XrdCryptosslX509Req_bio"); // Init private members creq = 0; // The certificate object subject = ""; // subject; subjecthash = ""; // hash of subject; subjectoldhash = ""; // hash of subject (md5 algorithm); bucket = 0; // bucket for serialization pki = 0; // PKI of the certificate // Make sure we got something; if (!buck) { DEBUG("got undefined opaque buffer"); return; } // // Create a bio_mem to store the certificates BIO *bmem = BIO_new(BIO_s_mem()); if (!bmem) { DEBUG("unable to create BIO for memory operations"); return; } // Write data to BIO int nw = BIO_write(bmem,(const void *)(buck->buffer),buck->size); if (nw != buck->size) { DEBUG("problems writing data to memory BIO (nw: "<<nw<<")"); return; } // Get certificate request from BIO if (!PEM_read_bio_X509_REQ(bmem,&creq,0,0)) { DEBUG("unable to read certificate request to memory BIO"); return; } // // Free BIO BIO_free(bmem); // // Init some of the private members (the others upon need) Subject(); // // Get the public key EVP_PKEY *evpp = X509_REQ_get_pubkey(creq); // if (evpp) { // init pki with the partial key if (!pki) pki = new XrdCryptosslRSA(evpp, 0); } else { DEBUG("could not access the public key"); } } //_____________________________________________________________________________ XrdCryptosslX509Req::XrdCryptosslX509Req(X509_REQ *xc) : XrdCryptoX509Req() { // Constructor: import X509_REQ object EPNAME("X509Req::XrdCryptosslX509Req_x509"); // Init private members creq = 0; // The certificate object subject = ""; // subject; subjecthash = ""; // hash of subject; subjectoldhash = ""; // hash of subject (md5 algorithm); bucket = 0; // bucket for serialization pki = 0; // PKI of the certificate // Make sure we got something; if (!xc) { DEBUG("got undefined X509 object"); return; } // Set certificate creq = xc; // // Init some of the private members (the others upon need) Subject(); // // Get the public key EVP_PKEY *evpp = X509_REQ_get_pubkey(creq); // if (evpp) { // init pki with the partial key if (!pki) pki = new XrdCryptosslRSA(evpp, 0); } else { DEBUG("could not access the public key"); } } //_____________________________________________________________________________ XrdCryptosslX509Req::~XrdCryptosslX509Req() { // Destructor // Cleanup certificate if (creq) X509_REQ_free(creq); // Cleanup key if (pki) delete pki; } //_____________________________________________________________________________ const char *XrdCryptosslX509Req::Subject() { // Return subject name EPNAME("X509Req::Subject"); // If we do not have it already, try extraction if (subject.length() <= 0) { // Make sure we have a certificate if (!creq) { DEBUG("WARNING: no certificate available - cannot extract subject name"); return (const char *)0; } // Extract subject name XrdCryptosslNameOneLine(X509_REQ_get_subject_name(creq), subject); } // return what we have return (subject.length() > 0) ? subject.c_str() : (const char *)0; } //_____________________________________________________________________________ const char *XrdCryptosslX509Req::SubjectHash(int alg) { // Return hash of subject name // Use default algorithm (X509_NAME_hash) for alg = 0, old algorithm // (for v>=1.0.0) when alg = 1 EPNAME("X509::SubjectHash"); #if (OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(__APPLE__)) if (alg == 1) { // md5 based if (subjectoldhash.length() <= 0) { // Make sure we have a certificate if (creq) { char chash[30] = {0}; snprintf(chash, sizeof(chash), "%08lx.0",X509_NAME_hash_old(X509_REQ_get_subject_name(creq))); subjectoldhash = chash; } else { DEBUG("WARNING: no certificate available - cannot extract subject hash (md5)"); } } // return what we have return (subjectoldhash.length() > 0) ? subjectoldhash.c_str() : (const char *)0; } #else if (alg == 1) { } #endif // If we do not have it already, try extraction if (subjecthash.length() <= 0) { // Make sure we have a certificate if (creq) { char chash[30] = {0}; snprintf(chash, sizeof(chash), "%08lx.0",X509_NAME_hash(X509_REQ_get_subject_name(creq))); subjecthash = chash; } else { DEBUG("WARNING: no certificate available - cannot extract subject hash (default)"); } } // return what we have return (subjecthash.length() > 0) ? subjecthash.c_str() : (const char *)0; } //_____________________________________________________________________________ XrdCryptoX509Reqdata XrdCryptosslX509Req::GetExtension(const char *oid) { // Return issuer name EPNAME("X509Req::GetExtension"); XrdCryptoX509Reqdata ext = 0; // Make sure we got something to look for if (!oid) { DEBUG("OID string not defined"); return ext; } // Make sure we got something to look for if (!creq) { DEBUG("certificate is not initialized"); return ext; } // Are there any extension? STACK_OF(X509_EXTENSION) *esk = X509_REQ_get_extensions(creq); // #if OPENSSL_VERSION_NUMBER >= 0x10000000L int numext = sk_X509_EXTENSION_num(esk); #else /* OPENSSL */ int numext = sk_num(esk); #endif /* OPENSSL */ if (numext <= 0) { DEBUG("certificate has got no extensions"); return ext; } DEBUG("certificate request has "<<numext<<" extensions"); // If the string is the Standard Name of a known extension check // searche the corresponding NID int nid = OBJ_sn2nid(oid); bool usenid = (nid > 0); // Loop to identify the one we would like int i = 0; X509_EXTENSION *wext = 0; for (i = 0; i< numext; i++) { #if OPENSSL_VERSION_NUMBER >= 0x10000000L wext = sk_X509_EXTENSION_value(esk, i); #else /* OPENSSL */ wext = (X509_EXTENSION *)sk_value(esk, i); #endif /* OPENSSL */ if (usenid) { int enid = OBJ_obj2nid(X509_EXTENSION_get_object(wext)); if (enid == nid) break; } else { // Try matching of the text char s[256]; OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(wext), 1); if (!strcmp(s, oid)) break; } wext = 0; } // We are done if nothing was found if (!wext) { DEBUG("Extension "<<oid<<" not found"); return ext; } // We are done return (XrdCryptoX509Reqdata)wext; } //_____________________________________________________________________________ XrdSutBucket *XrdCryptosslX509Req::Export() { // Export in form of bucket EPNAME("X509Req::Export"); // If we have already done it, return the previous result if (bucket) { DEBUG("serialization already performed:" " return previous result ("<<bucket->size<<" bytes)"); return bucket; } // Make sure we got something to export if (!creq) { DEBUG("certificate is not initialized"); return 0; } // // Now we create a bio_mem to serialize the certificate BIO *bmem = BIO_new(BIO_s_mem()); if (!bmem) { DEBUG("unable to create BIO for memory operations"); return 0; } // Write certificate to BIO if (!PEM_write_bio_X509_REQ(bmem, creq)) { DEBUG("unable to write certificate request to memory BIO"); return 0; } // Extract pointer to BIO data and length of segment char *bdata = 0; int blen = BIO_get_mem_data(bmem, &bdata); DEBUG("BIO data: "<<blen<<" bytes at 0x"<<(int *)bdata); // create the bucket now bucket = new XrdSutBucket(0,0,kXRS_x509_req); if (bucket) { // Fill bucket bucket->SetBuf(bdata, blen); DEBUG("result of serialization: "<<bucket->size<<" bytes"); } else { DEBUG("unable to create bucket for serialized format"); BIO_free(bmem); return 0; } // // Free BIO BIO_free(bmem); // // We are done return bucket; } //_____________________________________________________________________________ bool XrdCryptosslX509Req::Verify() { // Verify signature of the request EPNAME("X509Req::Verify"); // We must have been initialized if (!creq) return 0; // Ok: we can verify int rc = X509_REQ_verify(creq,X509_REQ_get_pubkey(creq)); if (rc <= 0) { // Failure if (rc == 0) { // Signatures are not OK DEBUG("signature not OK"); } else { // General failure DEBUG("could not verify signature"); } return 0; } // OK return 1; }