version 1.40, 2004/12/07 22:43:14
|
version 1.46, 2005/02/23 19:34:37
|
|
|
//%2004//////////////////////////////////////////////////////////////////////// |
//%2005//////////////////////////////////////////////////////////////////////// |
// | // |
// Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development | // Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development |
// Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems. | // Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems. |
|
|
// IBM Corp.; EMC Corporation, The Open Group. | // IBM Corp.; EMC Corporation, The Open Group. |
// Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.; | // Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.; |
// IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group. | // IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group. |
|
// Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.; |
|
// EMC Corporation; VERITAS Software Corporation; The Open Group. |
// | // |
// Permission is hereby granted, free of charge, to any person obtaining a copy | // Permission is hereby granted, free of charge, to any person obtaining a copy |
// of this software and associated documentation files (the "Software"), to | // of this software and associated documentation files (the "Software"), to |
|
|
//%///////////////////////////////////////////////////////////////////////////// | //%///////////////////////////////////////////////////////////////////////////// |
| |
#ifdef PEGASUS_HAS_SSL | #ifdef PEGASUS_HAS_SSL |
|
|
|
#ifdef PEGASUS_PLATFORM_WIN32_IX86_MSVC |
|
//Bugzilla 2366 |
|
//Use the X509_NAME in wincrypt.h on Windows. See X509.h for more info. |
|
#include <windows.h> |
|
#include <wincrypt.h> |
|
#endif |
|
|
#define OPENSSL_NO_KRB5 1 | #define OPENSSL_NO_KRB5 1 |
#include <openssl/err.h> | #include <openssl/err.h> |
#include <openssl/ssl.h> | #include <openssl/ssl.h> |
|
|
typedef unsigned long (* CRYPTO_SET_ID_CALLBACK)(void); | typedef unsigned long (* CRYPTO_SET_ID_CALLBACK)(void); |
}; | }; |
| |
|
typedef struct x509_store_ctx_st X509_STORE_CTX; |
| |
typedef struct Timestamp | typedef struct Timestamp |
{ | { |
|
|
{ | { |
PEG_METHOD_ENTER(TRC_SSL, "SSLCallback::verificationCRLCallback"); | PEG_METHOD_ENTER(TRC_SSL, "SSLCallback::verificationCRLCallback"); |
| |
X509_OBJECT obj; |
char buf[1024]; |
X509_NAME *subject; |
|
X509_NAME *issuer; |
|
X509 *xs; |
|
X509_CRL *crl; |
|
X509_REVOKED *revoked; |
|
long serial; |
|
BIO *bio; |
|
int i, n, rc; |
|
char *cp; |
|
char *cp2; |
|
| |
|
//check whether a CRL store was specified |
if (sslCRLStore == NULL) | if (sslCRLStore == NULL) |
{ | { |
Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL: CRL store is NULL"); |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL3, "---> SSL: CRL store is NULL"); |
return ok; | return ok; |
} | } |
| |
//ATTN: The following excerpt is based off of modssl's callback function |
//get the current certificate info |
//We may need to do something here for legal purposes. I have informed Warren of this, will resolve before 2.5 ships. |
X509* currentCert; |
|
X509_NAME* issuerName; |
/* |
X509_NAME* subjectName; |
* Determine certificate ingredients in advance |
ASN1_INTEGER* serialNumber; |
*/ |
|
xs = X509_STORE_CTX_get_current_cert(ctx); |
|
subject = X509_get_subject_name(xs); |
|
issuer = X509_get_issuer_name(xs); |
|
|
|
/* |
|
* OpenSSL provides the general mechanism to deal with CRLs but does not |
|
* use them automatically when verifying certificates, so we do it |
|
* explicitly here. We will check the CRL for the currently checked |
|
* certificate, if there is such a CRL in the store. |
|
* |
|
* We come through this procedure for each certificate in the certificate |
|
* chain, starting with the root-CA's certificate. At each step we've to |
|
* both verify the signature on the CRL (to make sure it's a valid CRL) |
|
* and it's revocation list (to make sure the current certificate isn't |
|
* revoked). But because to check the signature on the CRL we need the |
|
* public key of the issuing CA certificate (which was already processed |
|
* one round before), we've a little problem. But we can both solve it and |
|
* at the same time optimize the processing by using the following |
|
* verification scheme (idea and code snippets borrowed from the GLOBUS |
|
* project): |
|
* |
|
* 1. We'll check the signature of a CRL in each step when we find a CRL |
|
* through the _subject_ name of the current certificate. This CRL |
|
* itself will be needed the first time in the next round, of course. |
|
* But we do the signature processing one round before this where the |
|
* public key of the CA is available. |
|
* |
|
* 2. We'll check the revocation list of a CRL in each step when |
|
* we find a CRL through the _issuer_ name of the current certificate. |
|
* This CRLs signature was then already verified one round before. |
|
* |
|
* This verification scheme allows a CA to revoke its own certificate as |
|
* well, of course. |
|
*/ |
|
|
|
/* |
|
* Try to retrieve a CRL corresponding to the _subject_ of |
|
* the current certificate in order to verify it's integrity. |
|
*/ |
|
|
|
memset((char *)&obj, 0, sizeof(obj)); |
|
X509_STORE_CTX pStoreCtx; |
|
X509_STORE_CTX_init(&pStoreCtx, sslCRLStore, NULL, NULL); |
|
rc = X509_STORE_get_by_subject(&pStoreCtx, X509_LU_CRL, subject, &obj); |
|
X509_STORE_CTX_cleanup(&pStoreCtx); |
|
crl = obj.data.crl; |
|
|
|
////////////////////////////////////////// |
|
|
|
Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL : return code is %d", rc); |
|
|
|
if (rc > 0 && crl != NULL) |
|
{ |
|
Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL: CRL found"); |
|
|
|
// |
|
// Log information about CRL |
|
// (A little bit complicated because of ASN.1 and BIOs...) |
|
// |
|
bio = BIO_new(BIO_s_mem()); |
|
BIO_printf(bio, "lastUpdate: "); |
|
ASN1_UTCTIME_print(bio, X509_CRL_get_lastUpdate(crl)); |
|
BIO_printf(bio, ", nextUpdate: "); |
|
ASN1_UTCTIME_print(bio, X509_CRL_get_nextUpdate(crl)); |
|
n = BIO_pending(bio); |
|
cp = (char*) malloc(n+1); |
|
n = BIO_read(bio, cp, n); |
|
cp[n] = '\0'; |
|
BIO_free(bio); |
|
cp2 = X509_NAME_oneline(subject, NULL, 0); |
|
|
|
Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL : CRL Issuer : %s, %s", cp2, cp); |
|
|
|
//ATTN: This throws a memory error |
|
//free(cp2); |
|
free(cp); |
|
|
|
// |
|
// Verify the signature on this CRL |
|
// |
|
if (X509_CRL_verify(crl, X509_get_pubkey(xs)) <= 0) |
|
{ |
|
Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL : Invalid signature on CRL"); |
|
//ATTN: Do we need to set this? |
|
//X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE); |
|
//X509_OBJECT_free_contents(&obj); |
|
PEG_METHOD_EXIT(); |
|
return 0; |
|
} |
|
| |
// |
currentCert = X509_STORE_CTX_get_current_cert(ctx); |
// Check date of CRL to make sure it's not expired |
subjectName = X509_get_subject_name(currentCert); |
// |
issuerName = X509_get_issuer_name(currentCert); |
i = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl)); |
serialNumber = X509_get_serialNumber(currentCert); |
if (i == 0) |
|
{ |
//log certificate information |
Tracer::trace(TRC_SSL, Tracer::LEVEL2, "CRL has invalid nextUpdate field\n"); |
//this is information in the "public" key, so it does no harm to log it |
//ATTN: Do we need to set this? |
X509_NAME_oneline(issuerName, buf, sizeof(buf)); |
//X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, "---> SSL: Certificate Data: Issuer/Subject"); |
//X509_OBJECT_free_contents(&obj); |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, buf); |
PEG_METHOD_EXIT(); |
X509_NAME_oneline(subjectName, buf, sizeof(buf)); |
return 0; |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, buf); |
} |
|
if (i < 0) |
//initialize the CRL store |
|
X509_STORE_CTX crlStoreCtx; |
|
if (!X509_STORE_CTX_init(&crlStoreCtx, sslCRLStore, NULL, NULL)) |
{ | { |
Tracer::trace(TRC_SSL, Tracer::LEVEL2, "CRL has expired\n"); |
//fail if a CRL store was specified but we cannot open it |
//ATTN: Do we need to set this? |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL2, "---> SSL: Error: Could not initialize CRL store context"); |
//X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED); |
|
//X509_OBJECT_free_contents(&obj); |
|
PEG_METHOD_EXIT(); |
|
return 0; | return 0; |
} | } |
| |
X509_OBJECT_free_contents(&obj); |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, "---> SSL: Initialized CRL store"); |
| |
} else |
//attempt to get a CRL issued by the certificate's issuer |
|
X509_OBJECT obj; |
|
if (X509_STORE_get_by_subject(&crlStoreCtx, X509_LU_CRL, issuerName, &obj) <= 0) |
{ | { |
Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL: No CRL for that subject found"); |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL3, "---> SSL: No CRL by that issuer"); |
|
return ok; |
} | } |
|
X509_STORE_CTX_cleanup(&crlStoreCtx); |
| |
Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL: CRL checks out ok\n"); |
//get CRL |
|
X509_CRL* crl = obj.data.crl; |
// |
if (crl == NULL) |
// Try to retrieve a CRL corresponding to the _issuer_ of |
|
// the current certificate in order to check for revocation. |
|
// |
|
memset((char *)&obj, 0, sizeof(obj)); |
|
|
|
X509_STORE_CTX_init(&pStoreCtx, sslCRLStore, NULL, NULL); |
|
rc = X509_STORE_get_by_subject(&pStoreCtx, X509_LU_CRL, issuer, &obj); |
|
X509_STORE_CTX_cleanup(&pStoreCtx); |
|
crl = obj.data.crl; |
|
|
|
if (rc > 0 && crl != NULL) |
|
{ | { |
// |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, "---> SSL: CRL is null"); |
// Check if the current certificate is revoked by this CRL |
return ok; |
// |
} else |
#if SSL_LIBRARY_VERSION < 0x00904000 |
|
n = sk_num(X509_CRL_get_REVOKED(crl)); |
|
#else |
|
n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); |
|
#endif |
|
|
|
Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL: Number of certificates revoked by the issuer %d", n); |
|
|
|
for (i = 0; i < n; i++) |
|
{ | { |
#if SSL_LIBRARY_VERSION < 0x00904000 |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, "---> SSL: Found CRL by that issuer"); |
revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i); |
} |
#else |
|
revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); |
|
#endif |
|
|
|
serial = ASN1_INTEGER_get(revoked->serialNumber); |
|
| |
printf("Got serial number %ld\n", serial); |
//get revoked certificates |
|
STACK_OF(X509_REVOKED)* revokedCerts = NULL; |
|
revokedCerts = X509_CRL_get_REVOKED(crl); |
|
int numRevoked = sk_X509_REVOKED_num(revokedCerts); |
|
Tracer::trace(TRC_SSL, Tracer::LEVEL4,"---> SSL: Number of certificates revoked by the issuer %d\n", numRevoked); |
| |
if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(xs)) == 0) |
//check whether the subject's certificate is revoked |
|
X509_REVOKED* revokedCert = NULL; |
|
for (int i = 0; i < sk_X509_REVOKED_num(revokedCerts); i++) |
{ | { |
serial = ASN1_INTEGER_get(revoked->serialNumber); |
revokedCert = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i); |
cp = X509_NAME_oneline(issuer, NULL, 0); |
|
|
|
Tracer::trace(TRC_SSL, Tracer::LEVEL4, |
|
"---> SSL : Certificate with serial %ld revoked per CRL from issuer %s\n", serial, cp); |
|
//ATTN: Mem error if uncomment following |
|
//free(cp); |
|
| |
|
//a matching serial number indicates revocation |
|
if (ASN1_INTEGER_cmp(revokedCert->serialNumber, serialNumber) == 0) |
|
{ |
|
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL2, "---> SSL: Certificate is revoked"); |
X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); | X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); |
X509_OBJECT_free_contents(&obj); |
|
|
|
PEG_METHOD_EXIT(); |
|
return 0; | return 0; |
} | } |
} | } |
| |
X509_OBJECT_free_contents(&obj); |
PEG_TRACE_STRING(TRC_SSL, Tracer::LEVEL4, "---> SSL: Certificate is not revoked at this level"); |
} |
|
| |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
return ok; | return ok; |
|
|
} else | } else |
{ | { |
revoked = verificationCRLCallback(preVerifyOk,ctx,exData->_crlStore); | revoked = verificationCRLCallback(preVerifyOk,ctx,exData->_crlStore); |
|
Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL: CRL callback returned %d", revoked); |
|
|
|
if (revoked == 0) //with the SSL callbacks '0' indicates failure |
|
{ |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
return (revoked); |
return 0; |
|
} |
} | } |
} | } |
| |
|
|
if (exData->_crlStore) | if (exData->_crlStore) |
{ | { |
revoked = verificationCRLCallback(preVerifyOk,ctx,exData->_crlStore); | revoked = verificationCRLCallback(preVerifyOk,ctx,exData->_crlStore); |
Tracer::trace(TRC_SSL, Tracer::LEVEL4, |
Tracer::trace(TRC_SSL, Tracer::LEVEL4, "---> SSL: CRL callback returned %d", revoked); |
"---> SSL: CRL callback returned %d", revoked); |
|
|
if (revoked == 0) //with the SSL callbacks '0' indicates failure |
|
{ |
|
PEG_METHOD_EXIT(); |
|
return 0; |
|
} |
} | } |
| |
// | // |
|
|
SSLContextRep::SSLContextRep(const String& trustStore, | SSLContextRep::SSLContextRep(const String& trustStore, |
const String& certPath, | const String& certPath, |
const String& keyPath, | const String& keyPath, |
|
const String& crlPath, |
SSLCertificateVerifyFunction* verifyCert, | SSLCertificateVerifyFunction* verifyCert, |
const String& randomFile) {} |
const String& randomFile) |
|
{} |
| |
SSLContextRep::SSLContextRep(const SSLContextRep& sslContextRep) {} | SSLContextRep::SSLContextRep(const SSLContextRep& sslContextRep) {} |
| |