New function and options to check OCSP response validity.
authorDr. Stephen Henson <steve@openssl.org>
Sat, 24 Feb 2001 13:50:06 +0000 (13:50 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Sat, 24 Feb 2001 13:50:06 +0000 (13:50 +0000)
CHANGES
apps/ocsp.c
crypto/ocsp/ocsp.h
crypto/ocsp/ocsp_cl.c
crypto/ocsp/ocsp_err.c

diff --git a/CHANGES b/CHANGES
index 24be996..becaab4 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -3,6 +3,17 @@
 
  Changes between 0.9.6 and 0.9.7  [xx XXX 2000]
 
+  *) Add OCSP_check_validity() function to check the validity of OCSP
+     responses. OCSP responses are prepared in real time and may only
+     be a few seconds old. Simply checking that the current time lies
+     between thisUpdate and nextUpdate max reject otherwise valid responses
+     caused by either OCSP responder or client clock innacuracy. Instead
+     we allow thisUpdate and nextUpdate to fall within a certain period of
+     the current time. The age of the response can also optionally be
+     checked. Two new options -validity_period and -status_age added to
+     ocsp utility.
+     [Steve Henson]
+
   *) If signature or public key algorithm is unrecognized print out its
      OID rather that just UNKOWN.
      [Steve Henson]
index 723e0dc..ba456fc 100644 (file)
 #include <openssl/ssl.h>
 #include "apps.h"
 
+/* Maximum leeway in validity period: default 5 minutes */
+#define MAX_VALIDITY_PERIOD    (5 * 60)
+
 static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer,
                                STACK_OF(OCSP_CERTID) *ids);
 static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer,
                                STACK_OF(OCSP_CERTID) *ids);
 static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req,
-                               STACK *names, STACK_OF(OCSP_CERTID) *ids);
+                               STACK *names, STACK_OF(OCSP_CERTID) *ids,
+                               long nsec, long maxage);
 
 #undef PROG
 #define PROG ocsp_main
@@ -94,6 +98,7 @@ int MAIN(int argc, char **argv)
        BIO *cbio = NULL, *derbio = NULL;
        BIO *out = NULL;
        int req_text = 0, resp_text = 0;
+       long nsec = MAX_VALIDITY_PERIOD, maxage = -1;
        char *CAfile = NULL, *CApath = NULL;
        X509_STORE *store = NULL;
        SSL_CTX *ctx = NULL;
@@ -247,6 +252,38 @@ int MAIN(int argc, char **argv)
                                }
                        else badarg = 1;
                        }
+               else if (!strcmp (*args, "-validity_period"))
+                       {
+                       if (args[1])
+                               {
+                               args++;
+                               nsec = atol(*args);
+                               if (nsec < 0)
+                                       {
+                                       BIO_printf(bio_err,
+                                               "Illegal validity period %s\n",
+                                               *args);
+                                       badarg = 1;
+                                       }
+                               }
+                       else badarg = 1;
+                       }
+               else if (!strcmp (*args, "-status_age"))
+                       {
+                       if (args[1])
+                               {
+                               args++;
+                               maxage = atol(*args);
+                               if (maxage < 0)
+                                       {
+                                       BIO_printf(bio_err,
+                                               "Illegal validity age %s\n",
+                                               *args);
+                                       badarg = 1;
+                                       }
+                               }
+                       else badarg = 1;
+                       }
                 else if (!strcmp(*args, "-signkey"))
                        {
                        if (args[1])
@@ -356,6 +393,8 @@ int MAIN(int argc, char **argv)
                BIO_printf (bio_err, "-CApath dir        trusted certificates directory\n");
                BIO_printf (bio_err, "-CAfile file       trusted certificates file\n");
                BIO_printf (bio_err, "-VAfile file       validator certificates file\n");
+               BIO_printf (bio_err, "-validity_period n maximum validity discrepancy in seconds\n");
+               BIO_printf (bio_err, "-status_age n      maximum status age in seconds\n");
                BIO_printf (bio_err, "-noverify          don't verify response at all\n");
                BIO_printf (bio_err, "-verify_certs file additional certificates to search for signer\n");
                BIO_printf (bio_err, "-trust_other       don't verify additional certificates\n");
@@ -564,7 +603,7 @@ int MAIN(int argc, char **argv)
 
                }
 
-       if (!print_ocsp_summary(out, bs, req, reqnames, ids))
+       if (!print_ocsp_summary(out, bs, req, reqnames, ids, nsec, maxage))
                goto end;
 
        ret = 0;
@@ -652,7 +691,8 @@ static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer,
        }
 
 static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req,
-                                       STACK *names, STACK_OF(OCSP_CERTID) *ids)
+                                       STACK *names, STACK_OF(OCSP_CERTID) *ids,
+                                       long nsec, long maxage)
        {
        OCSP_CERTID *id;
        char *name;
@@ -677,6 +717,15 @@ static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req,
                        BIO_puts(out, "ERROR: No Status found.\n");
                        continue;
                        }
+
+               /* Check validity: if invalid write to output BIO so we
+                * know which response this refers to.
+                */
+               if (!OCSP_check_validity(thisupd, nextupd, nsec, maxage))
+                       {
+                       BIO_puts(out, "WARNING: Status times invalid.\n");
+                       ERR_print_errors(out);
+                       }
                BIO_printf(out, "%s\n", OCSP_cert_status_str(status));
 
                BIO_puts(out, "\tThis Update: ");
index 18249ab..bccdf77 100644 (file)
@@ -444,6 +444,9 @@ int OCSP_resp_find_status(OCSP_BASICRESP *bs, OCSP_CERTID *id, int *status,
                                ASN1_GENERALIZEDTIME **revtime,
                                ASN1_GENERALIZEDTIME **thisupd,
                                ASN1_GENERALIZEDTIME **nextupd);
+int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd,
+                       ASN1_GENERALIZEDTIME *nextupd,
+                       long sec, long maxsec);
 
 int OCSP_request_verify(OCSP_REQUEST *req, EVP_PKEY *pkey);
 
@@ -569,6 +572,7 @@ void ERR_load_OCSP_strings(void);
 #define OCSP_F_OCSP_CHECK_DELEGATED                     106
 #define OCSP_F_OCSP_CHECK_IDS                           107
 #define OCSP_F_OCSP_CHECK_ISSUER                        108
+#define OCSP_F_OCSP_CHECK_VALIDITY                      115
 #define OCSP_F_OCSP_MATCH_ISSUERID                      109
 #define OCSP_F_OCSP_PARSE_URL                           114
 #define OCSP_F_OCSP_REQUEST_SIGN                        110
@@ -580,8 +584,11 @@ void ERR_load_OCSP_strings(void);
 #define OCSP_R_BAD_DATA                                         100
 #define OCSP_R_CERTIFICATE_VERIFY_ERROR                         101
 #define OCSP_R_DIGEST_ERR                               102
+#define OCSP_R_ERROR_IN_NEXTUPDATE_FIELD                122
+#define OCSP_R_ERROR_IN_THISUPDATE_FIELD                123
 #define OCSP_R_ERROR_PARSING_URL                        121
 #define OCSP_R_MISSING_OCSPSIGNING_USAGE                103
+#define OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE             124
 #define OCSP_R_NOT_BASIC_RESPONSE                       104
 #define OCSP_R_NO_CERTIFICATES_IN_CHAIN                         105
 #define OCSP_R_NO_CONTENT                               106
@@ -597,6 +604,9 @@ void ERR_load_OCSP_strings(void);
 #define OCSP_R_SERVER_WRITE_ERROR                       116
 #define OCSP_R_SIGNATURE_FAILURE                        117
 #define OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND             118
+#define OCSP_R_STATUS_EXPIRED                           125
+#define OCSP_R_STATUS_NOT_YET_VALID                     126
+#define OCSP_R_STATUS_TOO_OLD                           127
 #define OCSP_R_UNKNOWN_MESSAGE_DIGEST                   119
 #define OCSP_R_UNKNOWN_NID                              120
 
index 9095252..9b3e6dd 100644 (file)
@@ -62,6 +62,7 @@
  */
 
 #include <stdio.h>
+#include <time.h>
 #include <cryptlib.h>
 #include <openssl/objects.h>
 #include <openssl/rand.h>
@@ -259,8 +260,9 @@ int OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason,
                                ASN1_GENERALIZEDTIME **nextupd)
        {
        int ret;
-       OCSP_CERTSTATUS *cst = single->certStatus;
+       OCSP_CERTSTATUS *cst;
        if(!single) return -1;
+       cst = single->certStatus;
        ret = cst->type;
        if (ret == V_OCSP_CERTSTATUS_REVOKED)
                {
@@ -278,7 +280,7 @@ int OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason,
        return ret;
        }
 
-/* This function combines the previous ones: look a certificate ID and
+/* This function combines the previous ones: look up a certificate ID and
  * if found extract status information. Return 0 is successful.
  */
 
@@ -299,4 +301,70 @@ int OCSP_resp_find_status(OCSP_BASICRESP *bs, OCSP_CERTID *id, int *status,
        return 1;
        }
 
+/* Check validity of thisUpdate and nextUpdate fields. It is possible that the request will
+ * take a few seconds to process and/or the time wont be totally accurate. Therefore to avoid
+ * rejecting otherwise valid time we allow the times to be within 'nsec' of the current time.
+ * Also to avoid accepting very old responses without a nextUpdate field an optional maxage
+ * parameter specifies the maximum age the thisUpdate field can be.
+ */
+
+int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long nsec, long maxsec)
+       {
+       int ret = 1;
+       time_t t_now, t_tmp;
+       time(&t_now);
+       /* Check thisUpdate is valid and not more than nsec in the future */
+       if (!ASN1_GENERALIZEDTIME_check(thisupd))
+               {
+               OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_ERROR_IN_THISUPDATE_FIELD);
+               ret = 0;
+               }
+       else 
+               {
+                       t_tmp = t_now + nsec;
+                       if (X509_cmp_time(thisupd, &t_tmp) > 0)
+                       {
+                       OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_STATUS_NOT_YET_VALID);
+                       ret = 0;
+                       }
+
+               /* If maxsec specified check thisUpdate is not more than maxsec in the past */
+               if (maxsec >= 0)
+                       {
+                       t_tmp = t_now - maxsec;
+                       if (X509_cmp_time(thisupd, &t_tmp) < 0)
+                               {
+                               OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_STATUS_TOO_OLD);
+                               ret = 0;
+                               }
+                       }
+               }
+               
+
+       if (!nextupd) return ret;
 
+       /* Check nextUpdate is valid and not more than nsec in the past */
+       if (!ASN1_GENERALIZEDTIME_check(nextupd))
+               {
+               OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_ERROR_IN_NEXTUPDATE_FIELD);
+               ret = 0;
+               }
+       else 
+               {
+               t_tmp = t_now - nsec;
+               if (X509_cmp_time(nextupd, &t_tmp) < 0)
+                       {
+                       OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_STATUS_EXPIRED);
+                       ret = 0;
+                       }
+               }
+
+       /* Also don't allow nextUpdate to precede thisUpdate */
+       if (ASN1_STRING_cmp(nextupd, thisupd) < 0)
+               {
+               OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE);
+               ret = 0;
+               }
+
+       return ret;
+       }
index 7910719..1cbf9ca 100644 (file)
@@ -75,6 +75,7 @@ static ERR_STRING_DATA OCSP_str_functs[]=
 {ERR_PACK(0,OCSP_F_OCSP_CHECK_DELEGATED,0),    "OCSP_CHECK_DELEGATED"},
 {ERR_PACK(0,OCSP_F_OCSP_CHECK_IDS,0),  "OCSP_CHECK_IDS"},
 {ERR_PACK(0,OCSP_F_OCSP_CHECK_ISSUER,0),       "OCSP_CHECK_ISSUER"},
+{ERR_PACK(0,OCSP_F_OCSP_CHECK_VALIDITY,0),     "OCSP_check_validity"},
 {ERR_PACK(0,OCSP_F_OCSP_MATCH_ISSUERID,0),     "OCSP_MATCH_ISSUERID"},
 {ERR_PACK(0,OCSP_F_OCSP_PARSE_URL,0),  "OCSP_parse_url"},
 {ERR_PACK(0,OCSP_F_OCSP_REQUEST_SIGN,0),       "OCSP_request_sign"},
@@ -89,8 +90,11 @@ static ERR_STRING_DATA OCSP_str_reasons[]=
 {OCSP_R_BAD_DATA                         ,"bad data"},
 {OCSP_R_CERTIFICATE_VERIFY_ERROR         ,"certificate verify error"},
 {OCSP_R_DIGEST_ERR                       ,"digest err"},
+{OCSP_R_ERROR_IN_NEXTUPDATE_FIELD        ,"error in nextupdate field"},
+{OCSP_R_ERROR_IN_THISUPDATE_FIELD        ,"error in thisupdate field"},
 {OCSP_R_ERROR_PARSING_URL                ,"error parsing url"},
 {OCSP_R_MISSING_OCSPSIGNING_USAGE        ,"missing ocspsigning usage"},
+{OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE     ,"nextupdate before thisupdate"},
 {OCSP_R_NOT_BASIC_RESPONSE               ,"not basic response"},
 {OCSP_R_NO_CERTIFICATES_IN_CHAIN         ,"no certificates in chain"},
 {OCSP_R_NO_CONTENT                       ,"no content"},
@@ -106,6 +110,9 @@ static ERR_STRING_DATA OCSP_str_reasons[]=
 {OCSP_R_SERVER_WRITE_ERROR               ,"server write error"},
 {OCSP_R_SIGNATURE_FAILURE                ,"signature failure"},
 {OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND     ,"signer certificate not found"},
+{OCSP_R_STATUS_EXPIRED                   ,"status expired"},
+{OCSP_R_STATUS_NOT_YET_VALID             ,"status not yet valid"},
+{OCSP_R_STATUS_TOO_OLD                   ,"status too old"},
 {OCSP_R_UNKNOWN_MESSAGE_DIGEST           ,"unknown message digest"},
 {OCSP_R_UNKNOWN_NID                      ,"unknown nid"},
 {0,NULL}