/******************************************************************************/
/* */
/* X r d C r y p t o s s l X 5 0 9 C r l. 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 . */
/* */
/* 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 XrdCryptoX509Crl */
/* */
/* ************************************************************************** */
#include
#include
#include
#include
#include
#include "XrdCrypto/XrdCryptosslRSA.hh"
#include "XrdCrypto/XrdCryptosslX509Crl.hh"
#include "XrdCrypto/XrdCryptosslAux.hh"
#include "XrdCrypto/XrdCryptosslTrace.hh"
#include
#include
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define X509_REVOKED_get0_revocationDate(x) (x)->revocationDate
#define X509_REVOKED_get0_serialNumber(x) (x)->serialNumber
#define X509_CRL_get0_lastUpdate X509_CRL_get_lastUpdate
#define X509_CRL_get0_nextUpdate X509_CRL_get_nextUpdate
#endif
//_____________________________________________________________________________
XrdCryptosslX509Crl::XrdCryptosslX509Crl(const char *cf, int opt)
: XrdCryptoX509Crl()
{
// Constructor certificate from file 'cf'.
EPNAME("X509Crl::XrdCryptosslX509Crl_file");
// Init private members
crl = 0; // The crl object
lastupdate = -1; // begin-validity time in secs since Epoch
nextupdate = -1; // end-validity time in secs since Epoch
issuer = ""; // issuer;
issuerhash = ""; // hash of issuer;
srcfile = ""; // source file;
nrevoked = 0; // number of revoked certificates
// Make sure file name is defined;
if (opt == 0) {
if (Init(cf) != 0) {
DEBUG("could not initialize the CRL from "<type != XrdCryptoX509::kCA) {
DEBUG("the CA certificate is undefined or not CA! ("<GetExtension("crlDistributionPoints");
if (!crlext) {
DEBUG("extension 'crlDistributionPoints' not found in the CA certificate");
return;
}
// Bio for exporting the extension
BIO *bext = BIO_new(BIO_s_mem());
ASN1_OBJECT *obj = X509_EXTENSION_get_object(crlext);
i2a_ASN1_OBJECT(bext, obj);
X509V3_EXT_print(bext, crlext, 0, 4);
// data length
char *cbio = 0;
int lbio = (int) BIO_get_mem_data(bext, &cbio);
char *buf = (char *) malloc(lbio+1);
// Read key from BIO to buf
memcpy(buf, cbio, lbio);
buf[lbio] = 0;
BIO_free(bext);
// Save it
XrdOucString uris(buf);
free(buf);
DEBUG("URI string: "<< uris);
XrdOucString uri;
int from = 0;
while ((from = uris.tokenize(uri, from, ' ')) != -1) {
if (uri.beginswith("URI:")) {
uri.replace("URI:","");
uri.replace("\n","");
if (InitFromURI(uri.c_str(), cacert->SubjectHash()) == 0) {
crluri = uri;
// We are done
break;
}
}
}
}
//_____________________________________________________________________________
XrdCryptosslX509Crl::~XrdCryptosslX509Crl()
{
// Destructor
// Cleanup CRL
if (crl)
X509_CRL_free(crl);
}
//_____________________________________________________________________________
int XrdCryptosslX509Crl::Init(const char *cf)
{
// Constructor certificate from file 'cf'.
// Return 0 on success, -1 on failure
EPNAME("X509Crl::Init");
// Make sure file name is defined;
if (!cf) {
DEBUG("file name undefined");
return -1;
}
// Make sure file exists;
struct stat st;
if (stat(cf, &st) != 0) {
if (errno == ENOENT) {
DEBUG("file "< 0) {
// Put it in PEM format
outpem.replace(".crltmp", ".pem");
cmd = "openssl crl -inform DER -in ";
cmd += outtmp;
cmd += " -out ";
cmd += outpem;
cmd += " -text";
// Execute 'openssl crl'
DEBUG("executing ... "<= 0x10000000L
STACK_OF(X509_REVOKED *) rsk = X509_CRL_get_REVOKED(crl);
#else /* OPENSSL */
STACK_OF(X509_REVOKED *) *rsk = X509_CRL_get_REVOKED(crl);
#endif /* OPENSSL */
if (!rsk) {
DEBUG("could not get stack of revoked instances");
return -1;
}
// Number of revocations
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
nrevoked = sk_X509_REVOKED_num(rsk);
#else /* OPENSSL */
nrevoked = sk_num(rsk);
#endif /* OPENSSL */
DEBUG(nrevoked << "certificates have been revoked");
if (nrevoked <= 0) {
DEBUG("no valid certificate has been revoked - nothing to do");
return 0;
}
// Get serial numbers of revoked certificates
char *tagser = 0;
int i = 0;
for (; i < nrevoked; i++ ){
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
X509_REVOKED *rev = sk_X509_REVOKED_value(rsk,i);
#else /* OPENSSL */
X509_REVOKED *rev = (X509_REVOKED *)sk_value(rsk,i);
#endif /* OPENSSL */
if (rev) {
BIGNUM *bn = BN_new();
ASN1_INTEGER_to_BN(X509_REVOKED_get0_serialNumber(rev), bn);
tagser = BN_bn2hex(bn);
BN_free(bn);
TRACE(Dump, "certificate with serial number: "<mtime = XrdCryptosslASN1toUTC(X509_REVOKED_get0_revocationDate(rev));
// Set status
cent->mtime = kCE_ok;
// Release the string for the serial number
OPENSSL_free(tagser);
// Unlock the entry
cent->rwmtx.UnLock();
}
}
return 0;
}
//_____________________________________________________________________________
time_t XrdCryptosslX509Crl::LastUpdate()
{
// Time of last update
// If we do not have it already, try extraction
if (lastupdate < 0) {
// Make sure we have a CRL
if (crl)
// Extract UTC time in secs from Epoch
lastupdate = XrdCryptosslASN1toUTC(X509_CRL_get0_lastUpdate(crl));
}
// return what we have
return lastupdate;
}
//_____________________________________________________________________________
time_t XrdCryptosslX509Crl::NextUpdate()
{
// Time of next update
// If we do not have it already, try extraction
if (nextupdate < 0) {
// Make sure we have a CRL
if (crl)
// Extract UTC time in secs from Epoch
nextupdate = XrdCryptosslASN1toUTC(X509_CRL_get0_nextUpdate(crl));
}
// return what we have
return nextupdate;
}
//_____________________________________________________________________________
const char *XrdCryptosslX509Crl::Issuer()
{
// Return issuer name
EPNAME("X509Crl::Issuer");
// If we do not have it already, try extraction
if (issuer.length() <= 0) {
// Make sure we have a CRL
if (!crl) {
DEBUG("WARNING: no CRL available - cannot extract issuer name");
return (const char *)0;
}
// Extract issuer name
XrdCryptosslNameOneLine(X509_CRL_get_issuer(crl), issuer);
}
// return what we have
return (issuer.length() > 0) ? issuer.c_str() : (const char *)0;
}
//_____________________________________________________________________________
const char *XrdCryptosslX509Crl::IssuerHash(int alg)
{
// Return hash of issuer name
// Use default algorithm (X509_NAME_hash) for alg = 0, old algorithm
// (for v>=1.0.0) when alg = 1
EPNAME("X509::IssuerHash");
#if (OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(__APPLE__))
if (alg == 1) {
// md5 based
if (issueroldhash.length() <= 0) {
// Make sure we have a certificate
if (crl) {
char chash[30] = {0};
snprintf(chash, sizeof(chash),
"%08lx.0",X509_NAME_hash_old(X509_CRL_get_issuer(crl)));
issueroldhash = chash;
} else {
DEBUG("WARNING: no certificate available - cannot extract issuer hash (md5)");
}
}
// return what we have
return (issueroldhash.length() > 0) ? issueroldhash.c_str() : (const char *)0;
}
#else
if (alg == 1) { }
#endif
// If we do not have it already, try extraction
if (issuerhash.length() <= 0) {
// Make sure we have a certificate
if (crl) {
char chash[30] = {0};
snprintf(chash, sizeof(chash),
"%08lx.0",X509_NAME_hash(X509_CRL_get_issuer(crl)));
issuerhash = chash;
} else {
DEBUG("WARNING: no certificate available - cannot extract issuer hash (default)");
}
}
// return what we have
return (issuerhash.length() > 0) ? issuerhash.c_str() : (const char *)0;
}
//_____________________________________________________________________________
bool XrdCryptosslX509Crl::Verify(XrdCryptoX509 *ref)
{
// Verify certificate signature with pub key of ref cert
// We must have been initialized
if (!crl)
return 0;
// We must have something to check with
X509 *r = ref ? (X509 *)(ref->Opaque()) : 0;
EVP_PKEY *rk = r ? X509_get_pubkey(r) : 0;
if (!rk)
return 0;
// Ok: we can verify
return (X509_CRL_verify(crl, rk) > 0);
}
//_____________________________________________________________________________
bool XrdCryptosslX509Crl::IsRevoked(int serialnumber, int when)
{
// Check if certificate with serialnumber is in the
// list of revocated certificates
EPNAME("IsRevoked");
// Reference time
int now = (when > 0) ? when : time(0);
// Warn if CRL should be updated
if (now > NextUpdate()) {
DEBUG("WARNING: CRL is expired: you should download the updated one");
}
// We must have something to check against
if (nrevoked <= 0) {
DEBUG("No certificate in the list");
return 0;
}
// Ok, build the tag
char tagser[20] = {0};
sprintf(tagser,"%x",serialnumber);
// Look into the cache
XrdSutCacheEntry *cent = cache.Get((const char *)tagser);
if (cent && cent->status == kCE_ok) {
// Check the revocation time
if (now > cent->mtime) {
DEBUG("certificate "<rwmtx.UnLock();
return 1;
}
cent->rwmtx.UnLock();
}
// Certificate not revoked
return 0;
}
//_____________________________________________________________________________
bool XrdCryptosslX509Crl::IsRevoked(const char *sernum, int when)
{
// Check if certificate with 'sernum' is in the
// list of revocated certificates
EPNAME("IsRevoked");
// Reference time
int now = (when > 0) ? when : time(0);
// Warn if CRL should be updated
if (now > NextUpdate()) {
DEBUG("WARNING: CRL is expired: you should download the updated one");
}
// We must have something to check against
if (nrevoked <= 0) {
DEBUG("No certificate in the list");
return 0;
}
// Look into the cache
XrdSutCacheEntry *cent = cache.Get((const char *)sernum);
if (cent && cent->status == kCE_ok) {
// Check the revocation time
if (now > cent->mtime) {
DEBUG("certificate "<rwmtx.UnLock();
return 1;
}
cent->rwmtx.UnLock();
}
// Certificate not revoked
return 0;
}
//_____________________________________________________________________________
void XrdCryptosslX509Crl::Dump()
{
// Dump content
EPNAME("X509Crl::Dump");
// Time strings
struct tm tst;
char stbeg[256] = {0};
time_t tbeg = LastUpdate();
localtime_r(&tbeg,&tst);
asctime_r(&tst,stbeg);
stbeg[strlen(stbeg)-1] = 0;
char stend[256] = {0};
time_t tend = NextUpdate();
localtime_r(&tend,&tst);
asctime_r(&tst,stend);
stend[strlen(stend)-1] = 0;
PRINT("+++++++++++++++ X509 CRL dump +++++++++++++++++++++++");
PRINT("+");
PRINT("+ File: "<