From b545dc6775cd703a258efd00f0bcb6bcb1930816 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Mon, 7 May 2001 22:52:50 +0000 Subject: [PATCH] Initial CRL based revocation checking. --- CHANGES | 15 +++ apps/verify.c | 15 ++- crypto/x509/x509_txt.c | 5 +- crypto/x509/x509_vfy.c | 205 ++++++++++++++++++++++++++++++++++++++++- crypto/x509/x509_vfy.h | 9 ++ ssl/s3_both.c | 1 + 6 files changed, 243 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 2d32583ed1..c49d03ae39 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,21 @@ *) applies to 0.9.6a (/0.9.6b) and 0.9.7 +) applies to 0.9.7 only + +) Initial CRL based revocation checking. If the CRL checking flag(s) + are set then the CRL is looked up in the X509_STORE structure and + its validity and signature checked, then if the certificate is found + in the CRL the verify fails with a revoked error. + + Various new CRL related callbacks added to X509_STORE_CTX structure. + + Command line options added to 'verify' application to support this. + + This needs some additional work, such as being able to handle multiple + CRLs with different times, extension based lookup (rather than just + by subject name) and ultimately more complete V2 CRL extension + handling. + [Steve Henson] + +) Add a general user interface API. This is designed to replace things like des_read_password and friends (backward compatibility functions using this new API are provided). The purpose is to remove prompting diff --git a/apps/verify.c b/apps/verify.c index f384de6d29..08027288dc 100644 --- a/apps/verify.c +++ b/apps/verify.c @@ -73,7 +73,7 @@ static int MS_CALLBACK cb(int ok, X509_STORE_CTX *ctx); static int check(X509_STORE *ctx, char *file, STACK_OF(X509) *uchain, STACK_OF(X509) *tchain, int purpose); static STACK_OF(X509) *load_untrusted(char *file); -static int v_verbose=0, issuer_checks = 0; +static int v_verbose=0, vflags = 0; int MAIN(int, char **); @@ -148,7 +148,11 @@ int MAIN(int argc, char **argv) else if (strcmp(*argv,"-help") == 0) goto end; else if (strcmp(*argv,"-issuer_checks") == 0) - issuer_checks=1; + vflags |= X509_V_FLAG_CB_ISSUER_CHECK; + else if (strcmp(*argv,"-crl_check") == 0) + vflags |= X509_V_FLAG_CRL_CHECK; + else if (strcmp(*argv,"-crl_check_all") == 0) + vflags |= X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL; else if (strcmp(*argv,"-verbose") == 0) v_verbose=1; else if (argv[0][0] == '-') @@ -227,7 +231,7 @@ int MAIN(int argc, char **argv) ret=0; end: if (ret == 1) { - BIO_printf(bio_err,"usage: verify [-verbose] [-CApath path] [-CAfile file] [-purpose purpose] [-engine e] cert1 cert2 ...\n"); + BIO_printf(bio_err,"usage: verify [-verbose] [-CApath path] [-CAfile file] [-purpose purpose] [-crl_check] [-engine e] cert1 cert2 ...\n"); BIO_printf(bio_err,"recognized usages:\n"); for(i = 0; i < X509_PURPOSE_get_count(); i++) { X509_PURPOSE *ptmp; @@ -286,8 +290,7 @@ static int check(X509_STORE *ctx, char *file, STACK_OF(X509) *uchain, STACK_OF(X X509_STORE_CTX_init(csc,ctx,x,uchain); if(tchain) X509_STORE_CTX_trusted_stack(csc, tchain); if(purpose >= 0) X509_STORE_CTX_set_purpose(csc, purpose); - if(issuer_checks) - X509_STORE_CTX_set_flags(csc, X509_V_FLAG_CB_ISSUER_CHECK); + X509_STORE_CTX_set_flags(csc, vflags); i=X509_verify_cert(csc); X509_STORE_CTX_free(csc); @@ -375,6 +378,8 @@ static int MS_CALLBACK cb(int ok, X509_STORE_CTX *ctx) if (ctx->error == X509_V_ERR_PATH_LENGTH_EXCEEDED) ok=1; if (ctx->error == X509_V_ERR_INVALID_PURPOSE) ok=1; if (ctx->error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) ok=1; + if (ctx->error == X509_V_ERR_CRL_HAS_EXPIRED) ok=1; + if (ctx->error == X509_V_ERR_CRL_NOT_YET_VALID) ok=1; } if (!v_verbose) ERR_clear_error(); diff --git a/crypto/x509/x509_txt.c b/crypto/x509/x509_txt.c index cfb478d4bc..417be41832 100644 --- a/crypto/x509/x509_txt.c +++ b/crypto/x509/x509_txt.c @@ -83,7 +83,7 @@ const char *X509_verify_cert_error_string(long n) case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: return("unable to decrypt certificate's signature"); case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: - return("unable to decrypt CRL's's signature"); + return("unable to decrypt CRL's signature"); case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: return("unable to decode issuer public key"); case X509_V_ERR_CERT_SIGNATURE_FAILURE: @@ -141,6 +141,9 @@ const char *X509_verify_cert_error_string(long n) case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: return("key usage does not include certificate signing"); + case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: + return("unable to get CRL issuer certificate"); + default: sprintf(buf,"error number %ld",n); return(buf); diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index 298016b450..c8fa53dbec 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -75,6 +75,8 @@ static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x); static int check_chain_purpose(X509_STORE_CTX *ctx); static int check_trust(X509_STORE_CTX *ctx); +static int check_revocation(X509_STORE_CTX *ctx); +static int check_cert(X509_STORE_CTX *ctx); static int internal_verify(X509_STORE_CTX *ctx); const char *X509_version="X.509" OPENSSL_VERSION_PTEXT; @@ -296,6 +298,13 @@ int X509_verify_cert(X509_STORE_CTX *ctx) /* We may as well copy down any DSA parameters that are required */ X509_get_pubkey_parameters(NULL,ctx->chain); + /* Check revocation status: we do this after copying parameters + * because they may be needed for CRL signature verification. + */ + + ok = ctx->check_revocation(ctx); + if(!ok) goto end; + /* At this point, we have a chain and just need to verify it */ if (ctx->verify != NULL) ok=ctx->verify(ctx); @@ -425,7 +434,7 @@ static int check_trust(X509_STORE_CTX *ctx) ok = X509_check_trust(x, ctx->trust, 0); if (ok == X509_TRUST_TRUSTED) return 1; - ctx->error_depth = sk_X509_num(ctx->chain) - 1; + ctx->error_depth = i; ctx->current_cert = x; if (ok == X509_TRUST_REJECTED) ctx->error = X509_V_ERR_CERT_REJECTED; @@ -436,6 +445,196 @@ static int check_trust(X509_STORE_CTX *ctx) #endif } +static int check_revocation(X509_STORE_CTX *ctx) + { + int i, last, ok; + if (!(ctx->flags & X509_V_FLAG_CRL_CHECK)) + return 1; + if (ctx->flags & X509_V_FLAG_CRL_CHECK_ALL) + last = 0; + else + last = sk_X509_num(ctx->chain) - 1; + for(i = 0; i <= last; i++) + { + ctx->error_depth = i; + ok = check_cert(ctx); + if (!ok) return ok; + } + return 1; + } + +static int check_cert(X509_STORE_CTX *ctx) + { + X509_CRL *crl = NULL; + X509 *x; + int ok, cnum; + cnum = ctx->error_depth; + x = sk_X509_value(ctx->chain, cnum); + ctx->current_cert = x; + /* Try to retrieve relevant CRL */ + ok = ctx->get_crl(ctx, &crl, x); + /* If error looking up CRL, nothing we can do except + * notify callback + */ + if(!ok) + { + ctx->error = X509_V_ERR_UNABLE_TO_GET_CRL; + if (ctx->verify_cb) + ok = ctx->verify_cb(0, ctx); + goto err; + } + ctx->current_crl = crl; + ok = ctx->check_crl(ctx, crl); + if (!ok) goto err; + ok = ctx->cert_crl(ctx, crl, x); + err: + ctx->current_crl = NULL; + X509_CRL_free(crl); + return ok; + + } + +/* Retrieve CRL corresponding to certificate: currently just a + * subject lookup: maybe use AKID later... + * Also might look up any included CRLs too (e.g PKCS#7 signedData). + */ +static int get_crl(X509_STORE_CTX *ctx, X509_CRL **crl, X509 *x) + { + int ok; + X509_OBJECT xobj; + ok = X509_STORE_get_by_subject(ctx, X509_LU_CRL, X509_get_issuer_name(x), &xobj); + if (!ok) return 0; + *crl = xobj.data.crl; + return 1; + } + +/* Check CRL validity */ +static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl) + { + X509 *issuer = NULL; + EVP_PKEY *ikey = NULL; + int ok = 0, chnum, cnum, i; + time_t *ptime; + cnum = ctx->error_depth; + chnum = sk_X509_num(ctx->chain) - 1; + /* Find CRL issuer: if not last certificate then issuer + * is next certificate in chain. + */ + if(cnum < chnum) + issuer = sk_X509_value(ctx->chain, cnum + 1); + else + { + issuer = sk_X509_value(ctx->chain, chnum); + /* If not self signed, can't check signature */ + if(!ctx->check_issued(ctx, issuer, issuer)) + { + ctx->error = X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER; + if(ctx->verify_cb) + ok = ctx->verify_cb(0, ctx); + if(!ok) goto err; + } + } + + if(issuer) + { + + /* Attempt to get issuer certificate public key */ + ikey = X509_get_pubkey(issuer); + + if(!ikey) + { + ctx->error=X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY; + if(ctx->verify_cb) + ok = ctx->verify_cb(0, ctx); + if (!ok) goto err; + } + else + { + /* Verify CRL signature */ + if(X509_CRL_verify(crl, ikey) <= 0) + { + ctx->error=X509_V_ERR_CRL_SIGNATURE_FAILURE; + if(ctx->verify_cb) + ok = ctx->verify_cb(0, ctx); + if (!ok) goto err; + } + } + } + + /* OK, CRL signature valid check times */ + if (ctx->flags & X509_V_FLAG_USE_CHECK_TIME) + ptime = &ctx->check_time; + else + ptime = NULL; + + i=X509_cmp_time(X509_CRL_get_lastUpdate(crl), ptime); + if (i == 0) + { + ctx->error=X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD; + ok= 0; + if(ctx->verify_cb) + ok = ctx->verify_cb(0, ctx); + if (!ok) goto err; + } + + if (i > 0) + { + ctx->error=X509_V_ERR_CRL_NOT_YET_VALID; + ok= 0; + if(ctx->verify_cb) + ok = ctx->verify_cb(0, ctx); + if (!ok) goto err; + } + + if(X509_CRL_get_nextUpdate(crl)) + { + i=X509_cmp_time(X509_CRL_get_nextUpdate(crl), ptime); + + if (i == 0) + { + ctx->error=X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD; + ok= 0; + if(ctx->verify_cb) + ok = ctx->verify_cb(0, ctx); + if (!ok) goto err; + } + + if (i < 0) + { + ctx->error=X509_V_ERR_CRL_HAS_EXPIRED; + ok= 0; + if(ctx->verify_cb) + ok = ctx->verify_cb(0, ctx); + if (!ok) goto err; + } + } + + ok = 1; + + err: + EVP_PKEY_free(ikey); + return ok; + } + +/* Check certificate against CRL */ +static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x) + { + int idx, ok; + X509_REVOKED rtmp; + /* Look for serial number of certificate in CRL */ + rtmp.serialNumber = X509_get_serialNumber(x); + idx = sk_X509_REVOKED_find(crl->crl->revoked, &rtmp); + /* Not found: OK */ + if(idx == -1) return 1; + /* Otherwise revoked: want something cleverer than + * this to handle entry extensions in V2 CRLs. + */ + ctx->error = X509_V_ERR_CERT_REVOKED; + if (ctx->verify_cb) + ok = ctx->verify_cb(0, ctx); + return ok; + } + static int internal_verify(X509_STORE_CTX *ctx) { int i,ok=0,n; @@ -885,6 +1084,10 @@ void X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, ctx->get_issuer = X509_STORE_CTX_get1_issuer; ctx->verify_cb = store->verify_cb; ctx->verify = store->verify; + ctx->check_revocation = check_revocation; + ctx->get_crl = get_crl; + ctx->check_crl = check_crl; + ctx->cert_crl = cert_crl; ctx->cleanup = 0; memset(&(ctx->ex_data),0,sizeof(CRYPTO_EX_DATA)); } diff --git a/crypto/x509/x509_vfy.h b/crypto/x509/x509_vfy.h index 3ae8a9022c..22b6c952ba 100644 --- a/crypto/x509/x509_vfy.h +++ b/crypto/x509/x509_vfy.h @@ -214,6 +214,10 @@ struct x509_store_ctx_st /* X509_STORE_CTX */ int (*verify_cb)(int ok,X509_STORE_CTX *ctx); /* error callback */ int (*get_issuer)(X509 **issuer, X509_STORE_CTX *ctx, X509 *x); /* get issuers cert from ctx */ int (*check_issued)(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); /* check issued */ + int (*check_revocation)(X509_STORE_CTX *ctx); /* Check revocation status of chain */ + int (*get_crl)(X509_STORE_CTX *ctx, X509_CRL **crl, X509 *x); /* retrieve CRL */ + int (*check_crl)(X509_STORE_CTX *ctx, X509_CRL *crl); /* Check CRL validity */ + int (*cert_crl)(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x); /* Check certificate against CRL */ int (*cleanup)(X509_STORE_CTX *ctx); /* The following is built up */ @@ -227,6 +231,7 @@ struct x509_store_ctx_st /* X509_STORE_CTX */ int error; X509 *current_cert; X509 *current_issuer; /* cert currently being tested as valid issuer */ + X509_CRL *current_crl; /* current CRL */ CRYPTO_EX_DATA ex_data; }; @@ -283,6 +288,8 @@ struct x509_store_ctx_st /* X509_STORE_CTX */ #define X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH 31 #define X509_V_ERR_KEYUSAGE_NO_CERTSIGN 32 +#define X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER 33 + /* The application is not happy */ #define X509_V_ERR_APPLICATION_VERIFICATION 50 @@ -290,6 +297,8 @@ struct x509_store_ctx_st /* X509_STORE_CTX */ #define X509_V_FLAG_CB_ISSUER_CHECK 0x1 /* Send issuer+subject checks to verify_cb */ #define X509_V_FLAG_USE_CHECK_TIME 0x2 /* Use check time instead of current time */ +#define X509_V_FLAG_CRL_CHECK 0x4 /* Lookup CRLs */ +#define X509_V_FLAG_CRL_CHECK_ALL 0x8 /* Lookup CRLs for whole chain */ int X509_OBJECT_idx_by_subject(STACK_OF(X509_OBJECT) *h, int type, X509_NAME *name); diff --git a/ssl/s3_both.c b/ssl/s3_both.c index 3e98cb00cf..b4d1b8445d 100644 --- a/ssl/s3_both.c +++ b/ssl/s3_both.c @@ -512,6 +512,7 @@ int ssl_verify_alarm_type(long type) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: al=SSL_AD_UNKNOWN_CA; break; case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: -- 2.34.1