/******************************************************************************/ /* */ /* X r d C r y p t o s s l g s i A u x . h h */ /* */ /* (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 . */ /* */ /* 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. */ /* */ /******************************************************************************/ /* ************************************************************************** */ /* */ /* GSI utility functions */ /* */ /* ************************************************************************** */ #include #include #include #include #include #include #include #include #include #include #include "XrdSut/XrdSutRndm.hh" #include "XrdCrypto/XrdCryptogsiX509Chain.hh" #include "XrdCrypto/XrdCryptosslAux.hh" #include "XrdCrypto/XrdCryptosslRSA.hh" #include "XrdCrypto/XrdCryptosslTrace.hh" #include "XrdCrypto/XrdCryptosslX509.hh" #include "XrdCrypto/XrdCryptosslX509Req.hh" //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // // // Extensions OID relevant for proxies // // // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // X509v3 Key Usage: critical #define KEY_USAGE_OID "2.5.29.15" // X509v3 Subject Alternative Name: must be absent #define SUBJ_ALT_NAME_OID "2.5.29.17" //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // // // VOMS relevant stuff // // // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// #define XRDGSI_VOMS_ATCAP_OID "1.3.6.1.4.1.8005.100.100.4" #define XRDGSI_VOMS_ACSEQ_OID "1.3.6.1.4.1.8005.100.100.5" #define BIO_PRINT(b,c) \ BUF_MEM *bptr; \ BIO_get_mem_ptr(b, &bptr); \ if (bptr) { \ char *s = new char[bptr->length+1]; \ memcpy(s, bptr->data, bptr->length); \ s[bptr->length] = '\0'; \ PRINT(c << s); \ delete [] s; \ } else { \ PRINT("ERROR: "<length+1]; \ memcpy(s, bptr->data, bptr->length); \ s[bptr->length] = '\0'; \ str = s; \ delete [] s; \ } else { \ PRINT("ERROR: GET_STRING: BIO internal buffer undefined!"); \ } \ if (b) BIO_free(b); #if OPENSSL_VERSION_NUMBER >= 0x0090800f # define XRDGSI_CONST const #else # define XRDGSI_CONST #endif #if OPENSSL_VERSION_NUMBER < 0x10100000L static RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey) { if (pkey->type != EVP_PKEY_RSA) { return NULL; } return pkey->pkey.rsa; } #endif int XrdCryptosslX509Asn1PrintInfo(int tag, int xclass, int constructed, int indent); int XrdCryptosslX509FillUnknownExt(XRDGSI_CONST unsigned char **pp, long length); int XrdCryptosslX509FillVOMS(XRDGSI_CONST unsigned char **pp, long length, bool &getvat, XrdOucString &vat); //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// // // // Handlers of the ProxyCertInfo extension following RFC3820 // // // //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// ASN1_SEQUENCE(PROXY_CERT_INFO_EXTENSION_OLD) = { ASN1_SIMPLE(PROXY_CERT_INFO_EXTENSION, proxyPolicy, PROXY_POLICY), ASN1_EXP_OPT(PROXY_CERT_INFO_EXTENSION, pcPathLengthConstraint, ASN1_INTEGER, 1) } ASN1_SEQUENCE_END_name(PROXY_CERT_INFO_EXTENSION, PROXY_CERT_INFO_EXTENSION_OLD) IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(PROXY_CERT_INFO_EXTENSION, PROXY_CERT_INFO_EXTENSION_OLD, PROXY_CERT_INFO_EXTENSION_OLD) //___________________________________________________________________________ bool XrdCryptosslProxyCertInfo(const void *extdata, int &pathlen, bool *haspolicy) { // // Check presence of a proxyCertInfo and retrieve the path length constraint. // Written following RFC3820, examples in openssl-/crypto source code. // in gridsite code and Globus proxycertinfo.h / .c. // if 'haspolicy' is defined, the existence of a policy field is checked; // the content ignored for the time being. // Make sure we got an extension if (!extdata) { return 0; } // Structure the buffer X509_EXTENSION *ext = (X509_EXTENSION *)extdata; // Check ProxyCertInfo OID char s[80] = {0}; OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ext), 1); // Now extract the path length constraint, if any unsigned char *p = X509_EXTENSION_get_data(ext)->data; PROXY_CERT_INFO_EXTENSION *pci = 0; if (!strcmp(s, gsiProxyCertInfo_OID)) pci = d2i_PROXY_CERT_INFO_EXTENSION(0, (XRDGSI_CONST unsigned char **)(&p), X509_EXTENSION_get_data(ext)->length); else if (!strcmp(s, gsiProxyCertInfo_OLD_OID)) pci = d2i_PROXY_CERT_INFO_EXTENSION_OLD(0, (XRDGSI_CONST unsigned char **)(&p), X509_EXTENSION_get_data(ext)->length); if (!pci) { return 0; } // Default length is -1, i.e. check disabled pathlen = -1; if (pci->pcPathLengthConstraint) { pathlen = ASN1_INTEGER_get(pci->pcPathLengthConstraint); } // If required, check the existence of a policy field if (haspolicy) { *haspolicy = (pci->proxyPolicy) ? 1 : 0; } // We are done return 1; } //___________________________________________________________________________ void XrdCryptosslSetPathLenConstraint(void *extdata, int pathlen) { // // Set the patch length constraint valur in proxyCertInfo extension ext // to 'pathlen'. // Make sure we got an extension if (!extdata) return; // Structure the buffer X509_EXTENSION *ext = (X509_EXTENSION *)extdata; // Check ProxyCertInfo OID char s[80] = {0}; OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ext), 1); // Now extract the path length constraint, if any unsigned char *p = X509_EXTENSION_get_data(ext)->data; PROXY_CERT_INFO_EXTENSION *pci = 0; if (!strcmp(s, gsiProxyCertInfo_OID)) pci = d2i_PROXY_CERT_INFO_EXTENSION(0, (XRDGSI_CONST unsigned char **)(&p), X509_EXTENSION_get_data(ext)->length); else if (!strcmp(s, gsiProxyCertInfo_OLD_OID)) pci = d2i_PROXY_CERT_INFO_EXTENSION_OLD(0, (XRDGSI_CONST unsigned char **)(&p), X509_EXTENSION_get_data(ext)->length); if (!pci) return; // Set the new length if (pci->pcPathLengthConstraint) { ASN1_INTEGER_set(pci->pcPathLengthConstraint, pathlen); } // We are done return; } //____________________________________________________________________________ int XrdCryptosslX509CreateProxy(const char *fnc, const char *fnk, XrdProxyOpt_t *pxopt, XrdCryptogsiX509Chain *xp, XrdCryptoRSA **kp, const char *fnp) { // Create a proxy certificate following the GSI specification (RFC 3820) // for the EEC certificate in file 'fnc', private key in 'fnk'. // A chain containing the proxy certificate and the EEC is returned in 'xp' // and its full RSA key in 'kp'. // The structure pxopt can be used to change the default options about // number of bits for the key, duration validity and max path signature depth. // If 'fpn' is defined, a PEM file is created with, in order, the proxy // certificate, the related private key and the EEC certificate (standard // GSI format). // Policy fields in the CertProxyExtension not yet included. // Return 0 in case of success, < 0 otherwise EPNAME("X509CreateProxy"); // Make sure the files are specified if (!fnc || !fnk || !xp || !kp) { PRINT("invalid inputs "); return -1; } // // Init OpenSSL OpenSSL_add_all_ciphers(); OpenSSL_add_all_digests(); ERR_load_crypto_strings(); // Use default options, if not specified int bits = (pxopt && pxopt->bits >= 512) ? pxopt->bits : 512; int valid = (pxopt) ? pxopt->valid : 43200; // 12 hours int depthlen = (pxopt) ? pxopt->depthlen : -1; // unlimited // // Get EEC certificate from fnc X509 *xEEC = 0; FILE *fc = fopen(fnc, "r"); if (fc) { // Read out the certificate if (PEM_read_X509(fc, &xEEC, 0, 0)) { DEBUG("EEC certificate loaded from file: "< XrdCryptosslASN1toUTC(X509_get_notAfter(xEEC))) { PRINT("EEC certificate has expired"); X509_free(xEEC); return -kErrPX_ExpiredEEC; } // // Get EEC private key from fnk EVP_PKEY *ekEEC = 0; FILE *fk = fopen(fnk, "r"); if (fk) { // Read out the private key XrdOucString sbj; XrdCryptosslNameOneLine(X509_get_subject_name(xEEC), sbj); PRINT("Your identity: "< // with is a random unsigned int used also as serial // number. // Duplicate user subject name X509_NAME *psubj = X509_NAME_dup(X509_get_subject_name(xEEC)); // Create an entry with the common name unsigned char sn[20] = {0}; sprintf((char *)sn, "%d", serial); if (!X509_NAME_add_entry_by_txt(psubj, (char *)"CN", MBSTRING_ASC, sn, -1, -1, 0)) { PRINT("could not add CN - (serial: "<proxyPolicy->policyLanguage = OBJ_txt2obj("1.3.6.1.5.5.7.21.1", 1); // // Set the new length if (depthlen > -1) { if ((pci->pcPathLengthConstraint = ASN1_INTEGER_new())) { ASN1_INTEGER_set(pci->pcPathLengthConstraint, depthlen); } else { PRINT("could not set the path length contrain"); return -kErrPX_SetPathDepth; } } // // create extension X509_EXTENSION *ext = X509_EXTENSION_new(); if (!ext) { PRINT("could not create extension object"); return -kErrPX_NoResources; } // Set extension name. ASN1_OBJECT *obj = OBJ_txt2obj(gsiProxyCertInfo_OID, 1); if (!obj || X509_EXTENSION_set_object(ext, obj) != 1) { PRINT("could not set extension name"); return -kErrPX_SetAttribute; } // flag as critical if (X509_EXTENSION_set_critical(ext, 1) != 1) { PRINT("could not set extension critical flag"); return -kErrPX_SetAttribute; } // Extract data in format for extension X509_EXTENSION_get_data(ext)->length = i2d_PROXY_CERT_INFO_EXTENSION(pci, 0); if (!(X509_EXTENSION_get_data(ext)->data = (unsigned char *)malloc(X509_EXTENSION_get_data(ext)->length+1))) { PRINT("could not allocate data field for extension"); return -kErrPX_NoResources; } unsigned char *pp = X509_EXTENSION_get_data(ext)->data; if ((i2d_PROXY_CERT_INFO_EXTENSION(pci, &pp)) <= 0) { PRINT("problem converting data for extension"); return -kErrPX_Error; } // Create a stack STACK_OF(X509_EXTENSION) *esk = sk_X509_EXTENSION_new_null(); if (!esk) { PRINT("could not create stack for extensions"); return -kErrPX_NoResources; } // // Now we add the new extension if (sk_X509_EXTENSION_push(esk, ext) == 0) { PRINT("could not push the extension in the stack"); return -kErrPX_Error; } // Add extension if (!(X509_REQ_add_extensions(preq, esk))) { PRINT("problem adding extension"); return -kErrPX_SetAttribute; } // // Sign the request if (!(X509_REQ_sign(preq, ekPX, EVP_sha1()))) { PRINT("problems signing the request"); return -kErrPX_Signing; } // // Create new proxy cert X509 *xPX = X509_new(); if (!xPX) { PRINT("could not create certificate object for proxies"); return -kErrPX_NoResources; } // Set version number if (X509_set_version(xPX, 2L) != 1) { PRINT("could not set version"); return -kErrPX_SetAttribute; } // Set serial number if (ASN1_INTEGER_set(X509_get_serialNumber(xPX), serial) != 1) { PRINT("could not set serial number"); return -kErrPX_SetAttribute; } // Set subject name if (X509_set_subject_name(xPX, psubj) != 1) { PRINT("could not set subject name"); return -kErrPX_SetAttribute; } X509_NAME_free(psubj); // Set issuer name if (X509_set_issuer_name(xPX, X509_get_subject_name(xEEC)) != 1) { PRINT("could not set issuer name"); return -kErrPX_SetAttribute; } // Set public key if (X509_set_pubkey(xPX, ekPX) != 1) { PRINT("could not set issuer name"); return -kErrPX_SetAttribute; } // Set proxy validity: notBefore now if (!X509_gmtime_adj(X509_get_notBefore(xPX), 0)) { PRINT("could not set notBefore"); return -kErrPX_SetAttribute; } // Set proxy validity: notAfter expire_secs from now if (!X509_gmtime_adj(X509_get_notAfter(xPX), valid)) { PRINT("could not set notAfter"); return -kErrPX_SetAttribute; } // First duplicate the extensions of the EE certificate X509_EXTENSION *xEECext = 0; int nEECext = X509_get_ext_count(xEEC); DEBUG("number of extensions found in the original certificate: "<< nEECext); int i = 0; bool haskeyusage = 0; for (i = 0; i< nEECext; i++) { xEECext = X509_get_ext(xEEC, i); char s[256]; OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(xEECext), 1); // Flag key usage extension if (!haskeyusage && !strcmp(s, KEY_USAGE_OID)) haskeyusage = 1; // Skip subject alternative name extension if (!strcmp(s, SUBJ_ALT_NAME_OID)) continue; // Duplicate and add to the stack X509_EXTENSION *xEECextdup = X509_EXTENSION_dup(xEECext); if (X509_add_ext(xPX, xEECextdup, -1) == 0) { PRINT("could not push the extension '"<>> WARNING: critical extension 'Key Usage' not found in original certificate! "); PRINT(">>> WARNING: this proxy may not be accepted by some parsers. "); } // Add the extension if (X509_add_ext(xPX, ext, -1) != 1) { PRINT("could not add extension"); return -kErrPX_SetAttribute; } // // Sign the certificate if (!(X509_sign(xPX, ekEEC, EVP_sha1()))) { PRINT("problems signing the certificate"); return -kErrPX_Signing; } // Fill outputs XrdCryptoX509 *xcPX = new XrdCryptosslX509(xPX); if (!xcPX) { PRINT("could not create container for proxy certificate"); return -kErrPX_NoResources; } // We need the full key ((XrdCryptosslX509 *)xcPX)->SetPKI((XrdCryptoX509data)ekPX); xp->PushBack(xcPX); XrdCryptoX509 *xcEEC = new XrdCryptosslX509(xEEC); if (!xcEEC) { PRINT("could not create container for EEC certificate"); return -kErrPX_NoResources; } xp->PushBack(xcEEC); *kp = new XrdCryptosslRSA(ekPX); if (!(*kp)) { PRINT("could not creatr out PKI"); return -kErrPX_NoResources; } // // Write to a file if requested int rc = 0; if (fnp) { // Open the file in write mode FILE *fp = fopen(fnp,"w"); int ifp = -1; if (!fp) { PRINT("cannot open file to save the proxy certificate (file: "<= 0x10000000L sk_X509_EXTENSION_free(esk); #else /* OPENSSL */ sk_free(esk); #endif /* OPENSSL */ // We are done return rc; } //____________________________________________________________________________ int XrdCryptosslX509CreateProxyReq(XrdCryptoX509 *xcpi, XrdCryptoX509Req **xcro, XrdCryptoRSA **kcro) { // Create a proxy certificate request following the GSI specification // (RFC 3820) for the proxy certificate 'xpi'. // The proxy certificate is returned in 'xpo' and its full RSA key in 'kpo'. // Policy fields in the CertProxyExtension not yet included. // Return 0 in case of success, < 0 otherwise EPNAME("X509CreateProxyReq"); // Make sure we got an proxy certificate as input if (!xcpi || !(xcpi->Opaque())) { PRINT("input proxy certificate not specified"); return -1; } // Point to the cerificate X509 *xpi = (X509 *)(xcpi->Opaque()); // Make sure the certificate is not expired if (!(xcpi->IsValid())) { PRINT("EEC certificate has expired"); return -kErrPX_ExpiredEEC; } // // Create a new request X509_REQ *xro = X509_REQ_new(); if (!xro) { PRINT("cannot to create cert request"); return -kErrPX_NoResources; } // // Use same num of bits as the signing certificate, but // less than 512 int bits = EVP_PKEY_bits(X509_get_pubkey(xpi)); bits = (bits < 512) ? 512 : bits; // // Create the new PKI for the proxy (exponent 65537) RSA *kro = RSA_new(); if (!kro) { PRINT("proxy key could not be generated - return"); return -kErrPX_GenerateKey; } BIGNUM *e = BN_new(); if (!e) { PRINT("proxy key could not be generated - return"); RSA_free(kro); return -kErrPX_GenerateKey; } BN_set_word(e, 0x10001); if (RSA_generate_key_ex(kro, bits, e, NULL) != 1) { RSA_free(kro); BN_free(e); PRINT("proxy key could not be generated - return"); return -kErrPX_GenerateKey; } BN_free(e); // // Set the key into the request EVP_PKEY *ekro = EVP_PKEY_new(); if (!ekro) { PRINT("could not create a EVP_PKEY * instance - return"); return -kErrPX_NoResources; } EVP_PKEY_assign_RSA(ekro, kro); X509_REQ_set_pubkey(xro, ekro); // // Generate a serial number. Specification says that this *should* // unique, so we just draw an unsigned random integer unsigned int serial = XrdSutRndm::GetUInt(); // // The subject name is the certificate subject + /CN= // with is a random unsigned int used also as serial // number. // Duplicate user subject name X509_NAME *psubj = X509_NAME_dup(X509_get_subject_name(xpi)); if (xcro && *xcro && *((int *)(*xcro)) <= 10100) { // Delete existing proxy CN addition; for backward compatibility #if OPENSSL_VERSION_NUMBER >= 0x10000000L int ne = X509_NAME_entry_count(psubj); #else /* OPENSSL */ int ne = psubj->entries->num; #endif /* OPENSSL */ if (ne >= 0) { X509_NAME_ENTRY *cne = X509_NAME_delete_entry(psubj, ne-1); if (cne) { X509_NAME_ENTRY_free(cne); } else { DEBUG("problems modifying subject name"); } } *xcro = 0; } // Create an entry with the common name unsigned char sn[20] = {0}; sprintf((char *)sn, "%d", serial); if (!X509_NAME_add_entry_by_txt(psubj, (char *)"CN", MBSTRING_ASC, sn, -1, -1, 0)) { PRINT("could not add CN - (serial: "<proxyPolicy->policyLanguage = OBJ_txt2obj("1.3.6.1.5.5.7.21.1", 1); // // Create a stack STACK_OF(X509_EXTENSION) *esk = sk_X509_EXTENSION_new_null(); if (!esk) { PRINT("could not create stack for extensions"); return -kErrPX_NoResources; } // // Get signature path depth from present proxy X509_EXTENSION *xpiext = 0; int npiext = X509_get_ext_count(xpi); int i = 0; bool haskeyusage = 0; int indepthlen = -1; for (i = 0; i< npiext; i++) { xpiext = X509_get_ext(xpi, i); char s[256]; OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(xpiext), 1); // Flag key usage extension if (!haskeyusage && !strcmp(s, KEY_USAGE_OID)) haskeyusage = 1; // Skip subject alternative name extension if (!strcmp(s, SUBJ_ALT_NAME_OID)) continue; // Get signature path depth from present proxy if (!strcmp(s, gsiProxyCertInfo_OID) || !strcmp(s, gsiProxyCertInfo_OLD_OID)) { unsigned char *p = X509_EXTENSION_get_data(xpiext)->data; PROXY_CERT_INFO_EXTENSION *inpci = 0; if (!strcmp(s, gsiProxyCertInfo_OID)) inpci = d2i_PROXY_CERT_INFO_EXTENSION(0, (XRDGSI_CONST unsigned char **)(&p), X509_EXTENSION_get_data(xpiext)->length); else inpci = d2i_PROXY_CERT_INFO_EXTENSION_OLD(0, (XRDGSI_CONST unsigned char **)(&p), X509_EXTENSION_get_data(xpiext)->length); if (inpci && inpci->pcPathLengthConstraint) indepthlen = ASN1_INTEGER_get(inpci->pcPathLengthConstraint); DEBUG("IN depth length: "<>> WARNING: critical extension 'Key Usage' not found in original certificate! "); PRINT(">>> WARNING: this proxy may not be accepted by some parsers. "); } // // Set the new length if (indepthlen > -1) { if ((pci->pcPathLengthConstraint = ASN1_INTEGER_new())) { int depthlen = (indepthlen > 0) ? (indepthlen-1) : 0; ASN1_INTEGER_set(pci->pcPathLengthConstraint, depthlen); } else { PRINT("could not set the path length contrain"); return -kErrPX_SetPathDepth; } } // // create extension X509_EXTENSION *ext = X509_EXTENSION_new(); if (!ext) { PRINT("could not create extension object"); return -kErrPX_NoResources; } // Extract data in format for extension X509_EXTENSION_get_data(ext)->length = i2d_PROXY_CERT_INFO_EXTENSION(pci, 0); if (!(X509_EXTENSION_get_data(ext)->data = (unsigned char *)malloc(X509_EXTENSION_get_data(ext)->length+1))) { PRINT("could not allocate data field for extension"); return -kErrPX_NoResources; } unsigned char *pp = X509_EXTENSION_get_data(ext)->data; if ((i2d_PROXY_CERT_INFO_EXTENSION(pci, &pp)) <= 0) { PRINT("problem converting data for extension"); return -kErrPX_Error; } // Set extension name. ASN1_OBJECT *obj = OBJ_txt2obj(gsiProxyCertInfo_OID, 1); if (!obj || X509_EXTENSION_set_object(ext, obj) != 1) { PRINT("could not set extension name"); return -kErrPX_SetAttribute; } // flag as critical if (X509_EXTENSION_set_critical(ext, 1) != 1) { PRINT("could not set extension critical flag"); return -kErrPX_SetAttribute; } if (sk_X509_EXTENSION_push(esk, ext) == 0) { PRINT("could not push the extension in the stack"); return -kErrPX_Error; } // Add extensions if (!(X509_REQ_add_extensions(xro, esk))) { PRINT("problem adding extension"); return -kErrPX_SetAttribute; } // // Sign the request if (!(X509_REQ_sign(xro, ekro, EVP_sha1()))) { PRINT("problems signing the request"); return -kErrPX_Signing; } // Prepare output *xcro = new XrdCryptosslX509Req(xro); *kcro = new XrdCryptosslRSA(ekro); // Cleanup #if OPENSSL_VERSION_NUMBER >= 0x10000000L sk_X509_EXTENSION_pop_free(esk, X509_EXTENSION_free); #else /* OPENSSL */ sk_free(esk); #endif /* OPENSSL */ // We are done return 0; } //____________________________________________________________________________ int XrdCryptosslX509SignProxyReq(XrdCryptoX509 *xcpi, XrdCryptoRSA *kcpi, XrdCryptoX509Req *xcri, XrdCryptoX509 **xcpo) { // Sign a proxy certificate request. // Return 0 in case of success, < 0 otherwise EPNAME("X509SignProxyReq"); // Make sure we got the right inputs if (!xcpi || !kcpi || !xcri || !xcpo) { PRINT("invalid inputs"); return -1; } // Make sure the certificate is not expired int timeleft = xcpi->NotAfter() - (int)time(0) + XrdCryptoTZCorr(); if (timeleft < 0) { PRINT("EEC certificate has expired"); return -kErrPX_ExpiredEEC; } // Point to the cerificate X509 *xpi = (X509 *)(xcpi->Opaque()); // Check key consistency if (kcpi->status != XrdCryptoRSA::kComplete) { PRINT("inconsistent key loaded"); return -kErrPX_BadEECkey; } // Point to the cerificate RSA *kpi = EVP_PKEY_get0_RSA((EVP_PKEY *)(kcpi->Opaque())); // // Set the key into the request EVP_PKEY *ekpi = EVP_PKEY_new(); if (!ekpi) { PRINT("could not create a EVP_PKEY * instance - return"); return -kErrPX_NoResources; } EVP_PKEY_set1_RSA(ekpi, kpi); // Get request in raw form X509_REQ *xri = (X509_REQ *)(xcri->Opaque()); // Extract subject names XrdOucString psbj, rsbj; XrdCryptosslNameOneLine(X509_get_subject_name(xpi), psbj); XrdCryptosslNameOneLine(X509_REQ_get_subject_name(xri), rsbj); if (psbj.length() <= 0 || rsbj.length() <= 0) { PRINT("names undefined"); return -kErrPX_BadNames; } // Check the subject name: the new proxy one must be in the form // ' + /CN=' XrdOucString neecp(psbj); XrdOucString neecr(rsbj,0,rsbj.rfind("/CN=")-1); if (neecr.length() <= 0 || neecr.length() <= 0 || neecp != neecr) { if (xcri->Version() <= 10100) { // Support previous format neecp.erase(psbj.rfind("/CN=")); if (neecr.length() <= 0 || neecr.length() <= 0 || neecp != neecr) { PRINT("Request subject not in the form ' + /CN='"); PRINT(" Versn: "<Version()); PRINT(" Proxy: "< + /CN='"); PRINT(" Versn: "<Version()); PRINT(" Proxy: "<data; PROXY_CERT_INFO_EXTENSION *inpci = 0; if (!strcmp(s, gsiProxyCertInfo_OID)) inpci = d2i_PROXY_CERT_INFO_EXTENSION(0, (XRDGSI_CONST unsigned char **)(&p), X509_EXTENSION_get_data(xpiext)->length); else inpci = d2i_PROXY_CERT_INFO_EXTENSION_OLD(0, (XRDGSI_CONST unsigned char **)(&p), X509_EXTENSION_get_data(xpiext)->length); if (inpci && inpci->pcPathLengthConstraint) indepthlen = ASN1_INTEGER_get(inpci->pcPathLengthConstraint); DEBUG("IN depth length: "<= 0x10000000L int nriext = sk_X509_EXTENSION_num(xrisk); #else /* OPENSSL */ int nriext = sk_num(xrisk); #endif /* OPENSSL */ if (nriext == 0 || !haskeyusage) { PRINT("wrong extensions in request: "<< nriext<<", "<data; PROXY_CERT_INFO_EXTENSION *reqpci = d2i_PROXY_CERT_INFO_EXTENSION(0, (XRDGSI_CONST unsigned char **)(&p), X509_EXTENSION_get_data(xriext)->length); if (reqpci && reqpci->pcPathLengthConstraint) reqdepthlen = ASN1_INTEGER_get(reqpci->pcPathLengthConstraint); } DEBUG("REQ depth length: "<proxyPolicy->policyLanguage = OBJ_txt2obj("1.3.6.1.5.5.7.21.1", 1); // // Set the new length if (outdepthlen > -1) { if ((pci->pcPathLengthConstraint = ASN1_INTEGER_new())) { int depthlen = (outdepthlen > 0) ? (outdepthlen-1) : 0; ASN1_INTEGER_set(pci->pcPathLengthConstraint, depthlen); } else { PRINT("could not set the path length contrain"); return -kErrPX_SetPathDepth; } } // create extension X509_EXTENSION *ext = X509_EXTENSION_new(); if (!ext) { PRINT("could not create extension object"); return -kErrPX_NoResources; } // Extract data in format for extension X509_EXTENSION_get_data(ext)->length = i2d_PROXY_CERT_INFO_EXTENSION(pci, 0); if (!(X509_EXTENSION_get_data(ext)->data = (unsigned char *)malloc(X509_EXTENSION_get_data(ext)->length+1))) { PRINT("could not allocate data field for extension"); return -kErrPX_NoResources; } unsigned char *pp = X509_EXTENSION_get_data(ext)->data; if ((i2d_PROXY_CERT_INFO_EXTENSION(pci, &pp)) <= 0) { PRINT("problem converting data for extension"); return -kErrPX_Error; } PROXY_CERT_INFO_EXTENSION_free( pci ); // Set extension name. ASN1_OBJECT *obj = OBJ_txt2obj(gsiProxyCertInfo_OID, 1); if (!obj || X509_EXTENSION_set_object(ext, obj) != 1) { PRINT("could not set extension name"); return -kErrPX_SetAttribute; } ASN1_OBJECT_free( obj ); // flag as critical if (X509_EXTENSION_set_critical(ext, 1) != 1) { PRINT("could not set extension critical flag"); return -kErrPX_SetAttribute; } // Add the extension if (X509_add_ext(xpo, ext, -1) == 0) { PRINT("could not add extension"); return -kErrPX_SetAttribute; } // // Sign the certificate if (!(X509_sign(xpo, ekpi, EVP_sha1()))) { PRINT("problems signing the certificate"); return -kErrPX_Signing; } EVP_PKEY_free( ekpi ); // decrement reference counter X509_EXTENSION_free( ext ); // Prepare outputs *xcpo = new XrdCryptosslX509(xpo); // Cleanup #if OPENSSL_VERSION_NUMBER >= 0x10000000L sk_X509_EXTENSION_free(xrisk); #else /* OPENSSL */ sk_free(xrisk); #endif /* OPENSSL */ // We are done return 0; } //____________________________________________________________________________ int XrdCryptosslX509GetVOMSAttr(XrdCryptoX509 *xcpi, XrdOucString &vat) { // Get VOMS attributes from the certificate, if present // Return 0 in case of success, 1 if VOMS info is not available, < 0 if any // error occured EPNAME("X509GetVOMSAttr"); int rc = -1; // Make sure we got the right inputs if (!xcpi) { PRINT("invalid inputs"); return rc; } // Point to the cerificate X509 *xpi = (X509 *)(xcpi->Opaque()); rc = 1; bool getvat = 0; // Go through the extensions X509_EXTENSION *xpiext = 0; int npiext = X509_get_ext_count(xpi); int i = 0; for (i = 0; i< npiext; i++) { xpiext = X509_get_ext(xpi, i); char s[256]; OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(xpiext), 1); // Notify what we found DEBUG("found extension '"<data; long length = X509_EXTENSION_get_data(xpiext)->length; int ret = XrdCryptosslX509FillVOMS(&pp, length, getvat, vat); DEBUG("ret: " << ret << " - vat: " << vat); } // Done return rc; } //____________________________________________________________________________ int XrdCryptosslX509FillVOMS(XRDGSI_CONST unsigned char **pp, long length, bool &getvat, XrdOucString &vat) { // Look recursively for the VOMS attributes // Return 2 if found, 1 if to continue searching, 0 to stop EPNAME("X509FillVOMS"); XRDGSI_CONST unsigned char *p,*ep,*tot,*op,*opp; long len; int tag, xclass, ret = 0; int /*nl,*/ hl,j,r; ASN1_OBJECT *o = 0; ASN1_OCTET_STRING *os = 0; bool gotvat = 0; p = *pp; tot = p + length; op = p - 1; while ((p < tot) && (op < p)) { op = p; j = ASN1_get_object(&p, &len, &tag, &xclass, length); #ifdef LINT j = j; #endif if (j & 0x80) { PRINT("ERROR: error in encoding"); ret = 0; goto end; } hl = (p-op); length -= hl; /* if j == 0x21 it is a constructed indefinite length object */ if (j & V_ASN1_CONSTRUCTED) { ep = p + len; if (len > length) { PRINT("ERROR:CONST: length is greater than " <= tot)) break; } } else { while (p < ep) { r = XrdCryptosslX509FillVOMS(&p, (long)len, getvat, vat); if (r == 0) { ret = 0; goto end; } } } } else { // nl = 0; if (tag == V_ASN1_OBJECT) { opp = op; if (d2i_ASN1_OBJECT(&o, &opp, len+hl)) { BIO *mem = BIO_new(BIO_s_mem()); i2a_ASN1_OBJECT(mem, o); XrdOucString objstr; BIO_GET_STRING(mem, objstr); // Looking for the right extension ... if (objstr == XRDGSI_VOMS_ATCAP_OID || objstr == "idatcap") getvat = 1; DEBUG("AOBJ:"<length > 0) { opp = os->data; // Testing whether the octet string is printable for (i=0; ilength; i++) { if (( (opp[i] < ' ') && (opp[i] != '\n') && (opp[i] != '\r') && (opp[i] != '\t')) || (opp[i] > '~')) { printable = 0; break; } } if (printable) { // Printable string: it may be what we need if (getvat) { if (vat.length() > 0) vat += ","; vat += (const char *)opp; gotvat = 1; } DEBUG("OBJS:" << (const char *)opp << " (len: "<length<<")"); } } if (os) { ASN1_OCTET_STRING_free(os); os = 0; } } p += len; if ((tag == V_ASN1_EOC) && (xclass == 0)) { ret = 2; /* End of sequence */ goto end; } } length -= len; } ret = 1; if (gotvat) { getvat = 0; ret = 2; } end: if (o) ASN1_OBJECT_free(o); if (os) ASN1_OCTET_STRING_free(os); *pp = p; DEBUG("ret: "<Opaque()); // Are there any extension? int numext = X509_get_ext_count(cert); if (numext <= 0) { emsg = "certificate has got no extensions"; return -1; } TRACE(ALL,"certificate has "<data; pci = d2i_PROXY_CERT_INFO_EXTENSION(0, (XRDGSI_CONST unsigned char **)(&p), X509_EXTENSION_get_data(ext)->length); } else { PRINT("WARNING: multiple proxyCertInfo extensions found: taking the first"); } } else if (!strncmp(s, gsiProxyCertInfo_OLD_OID, sizeof(gsiProxyCertInfo_OLD_OID))) { if (ext == 0) { ext = xext; // Now get the extension unsigned char *p = X509_EXTENSION_get_data(ext)->data; pci = d2i_PROXY_CERT_INFO_EXTENSION_OLD(0, (XRDGSI_CONST unsigned char **)(&p), X509_EXTENSION_get_data(ext)->length); } else { PRINT("WARNING: multiple proxyCertInfo extensions found: taking the first"); } } } // // If the extension was not found it is probably a legacy (v2) proxy: signal it if (!ext) { emsg = "proxyCertInfo extension not found"; return -2; } if (!pci) { emsg = "proxyCertInfo extension could not be deserialized"; return -1; } // Check if there is a policy if ((pci->proxyPolicy) == 0) { emsg = "could not access policy from proxyCertInfo extension"; return -1; } if ((pci->proxyPolicy->policyLanguage) == 0) { emsg = "could not access policy language from proxyCertInfo extension"; return -1; } // Done return 0; }