/******************************************************************************/
/* */
/* 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;
}