Initial CRL based revocation checking.
authorDr. Stephen Henson <steve@openssl.org>
Mon, 7 May 2001 22:52:50 +0000 (22:52 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Mon, 7 May 2001 22:52:50 +0000 (22:52 +0000)
CHANGES
apps/verify.c
crypto/x509/x509_txt.c
crypto/x509/x509_vfy.c
crypto/x509/x509_vfy.h
ssl/s3_both.c

diff --git a/CHANGES b/CHANGES
index 2d32583..c49d03a 100644 (file)
--- a/CHANGES
+++ b/CHANGES
          *) 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
index f384de6..0802728 100644 (file)
@@ -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();
index cfb478d..417be41 100644 (file)
@@ -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);
index 298016b..c8fa53d 100644 (file)
@@ -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));
        }
index 3ae8a90..22b6c95 100644 (file)
@@ -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);
index 3e98cb0..b4d1b84 100644 (file)
@@ -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: