/******************************************************************************/ /* */ /* X r d C r y p t o g s i X 5 0 9 C h a i n . c c */ /* */ /* (c) 2014 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. */ /* */ /******************************************************************************/ #include #include #include "XrdCrypto/XrdCryptoFactory.hh" #include "XrdCrypto/XrdCryptogsiX509Chain.hh" #include "XrdCrypto/XrdCryptoTrace.hh" // ---------------------------------------------------------------------------// // // // XrdCryptogsiX509Chain (was XrdCryptosslgsiX509Chain) // // // // Enforce GSI policies on X509 certificate chains // // // // ---------------------------------------------------------------------------// //___________________________________________________________________________ bool XrdCryptogsiX509Chain::Verify(EX509ChainErr &errcode, x509ChainVerifyOpt_t *vopt) { // Verify the chain EPNAME("X509Chain::Verify"); errcode = kNone; // There must be at least a CA and a { EEC or subCA }. if (size < 2) { DEBUG("Nothing to verify (size: "<opt : 0; int when = (vopt) ? vopt->when : (int)time(0); int plen = (vopt) ? vopt->pathlen : -1; XrdCryptoX509Crl *crl = (vopt) ? vopt->crl : 0; // // Global path depth length consistency check if (plen > -1 && plen < size) { errcode = kTooMany; lastError = "checking path depth: "; lastError += X509ChainError(errcode); } // // Check the first certificate: it MUST be of CA type, valid, // self-signed XrdCryptoX509ChainNode *node = begin; XrdCryptoX509 *xcer = node->Cert(); // Certificate under exam XrdCryptoX509 *xsig = xcer; // Signing certificate if (statusCA == kUnknown) { if (!XrdCryptoX509Chain::Verify(errcode, "CA: ", XrdCryptoX509::kCA, when, xcer, xsig)) return 0; statusCA = kValid; } else if (statusCA == kAbsent || statusCA == kInvalid) { errcode = kNoCA; lastError = X509ChainError(errcode); return 0; } // // Update the max path depth len if (plen > -1) plen -= 1; // // Check sub-CA's certificate, if any while (node->Next() && node->Next()->Cert()->type == XrdCryptoX509::kCA) { xsig = xcer; node = node->Next(); xcer = node->Cert(); if (!XrdCryptoX509Chain::Verify(errcode, "Sub-CA: ", XrdCryptoX509::kCA, when, xcer, xsig, crl)) return 0; // // Update the max path depth len if (plen > -1) plen -= 1; } // // If subCA verification case we are done if (opt & kOptsCheckSubCA) return 1; // // Check the end-point entity certificate if (!node->Next() || // We expect somethign else if not in subCA checking mode (node->Next() && node->Next()->Cert()->type != XrdCryptoX509::kEEC)) { errcode = kNoEEC; lastError = X509ChainError(errcode); return 0; } // // Check the end-point entity certificate xsig = xcer; node = node->Next(); xcer = node->Cert(); if (!XrdCryptoX509Chain::Verify(errcode, "EEC: ", XrdCryptoX509::kUnknown, when, xcer, xsig, crl)) return 0; // // Update the max path depth len if (plen > -1) plen -= 1; // // Only one end-point entity certificate if (node->Next() && node->Next()->Cert()->type == XrdCryptoX509::kEEC) { errcode = kTooManyEEC; lastError = X509ChainError(errcode); return 0; } // // There are proxy certificates xsig = xcer; node = node->Next(); while (node && (plen == -1 || plen > 0)) { // Attache to certificate xcer = node->Cert(); // // Must be a recognized proxy certificate if (xcer && xcer->type != XrdCryptoX509::kProxy) { errcode = kInvalidProxy; lastError = X509ChainError(errcode); return 0; } // Proxy subject name must follow some rules if (!SubjectOK(errcode, xcer)) return 0; // Check if ProxyCertInfo extension is there (required by RFC3820) int pxplen = -1; bool b; if (opt & kOptsRfc3820) { const void *extdata = xcer->GetExtension(gsiProxyCertInfo_OID); if (!extdata) extdata = xcer->GetExtension(gsiProxyCertInfo_OLD_OID); if (!extdata || !cfact || !(cfact && (*(cfact->ProxyCertInfo()))(extdata, pxplen, &b))) { errcode = kMissingExtension; lastError = "rfc3820: "; lastError += X509ChainError(errcode); return 0; } } // Update plen, if needed if (plen == -1) { plen = (pxplen > -1) ? pxplen : plen; } else { plen--; // Aply stricter rules if required plen = (pxplen > -1 && pxplen < plen) ? pxplen : plen; } // Standard verification if (!XrdCryptoX509Chain::Verify(errcode, "Proxy: ", XrdCryptoX509::kProxy, when, xcer, xsig)) return 0; // Get next xsig = xcer; node = node->Next(); } // We are done (successfully!) return 1; } //___________________________________________________________________________ bool XrdCryptogsiX509Chain::SubjectOK(EX509ChainErr &errcode, XrdCryptoX509 *xcer) { // Apply GSI rules for proxy subject names // Check inputs if (!xcer) { errcode = kNoCertificate; lastError = "subject check:"; lastError += X509ChainError(errcode); return 0; } // This applies only to proxies if (xcer->type != XrdCryptoX509::kProxy) return 1; // Pointers to names if (!(xcer->Subject()) || !(xcer->Issuer())) { errcode = kInvalidNames; lastError = "subject check:"; lastError += X509ChainError(errcode); return 0; } // Subject name must start with issuer name. // We need the length of the common part between issuer and subject. // We allow proxies issued by other proxies. In such cases we must // ignore the last '/CN=' in the issuer name; this explains the need // for the following gymnastic. int ilen = strlen(xcer->Issuer()); if (strncmp(xcer->Subject(), xcer->Issuer(), ilen)) { // Check if the issuer is a proxy: ignore the last 'CN=' char *pcn = (char *) strstr(xcer->Issuer(), "/CN="); if (pcn) { char *pcnn = 0; while ((pcnn = (char *) strstr(pcn+1,"/CN="))) pcn = pcnn; ilen = (int)(pcn - xcer->Issuer()); } if (strncmp(xcer->Subject() + ilen,"/CN=",4)) { errcode = kInvalidNames; lastError = "proxy subject check: found additional chars :"; lastError += X509ChainError(errcode); return 0; } if (strncmp(xcer->Subject(), xcer->Issuer(), ilen)) { errcode = kInvalidNames; lastError = "proxy issuer check: issuer not found in subject :"; lastError += X509ChainError(errcode); return 0; } } // A common name must be appendend char *pp = (char *)strstr(xcer->Subject()+ilen, "CN="); if (!pp) { errcode = kInvalidNames; lastError = "proxy subject check: no appended 'CN='"; lastError += X509ChainError(errcode); return 0; } // But only one pp = strstr(pp+strlen("CN="), "CN="); if (pp) { errcode = kInvalidNames; lastError = "proxy subject check: too many appended 'CN='s"; lastError += X509ChainError(errcode); return 0; } // We are done return 1; }