diff --git a/CHANGES b/CHANGES index 24be99630bccbf2afe86f64544972502d32b8d76..becaab49f8b4f675de2cdd140edd47dc93ce6132 100644 --- 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] diff --git a/apps/ocsp.c b/apps/ocsp.c index 723e0dc9ab9554d381f22771aabd0107cd96b5c2..ba456fc58faffebbf22b7850b29aa346ed2ff2c4 100644 --- a/apps/ocsp.c +++ b/apps/ocsp.c @@ -64,12 +64,16 @@ #include #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: "); diff --git a/crypto/ocsp/ocsp.h b/crypto/ocsp/ocsp.h index 18249abc38aa8a0e148c70eedda3b5acf1fb689e..bccdf77b4f2373b0418392d0659d465dedb2fa26 100644 --- a/crypto/ocsp/ocsp.h +++ b/crypto/ocsp/ocsp.h @@ -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 diff --git a/crypto/ocsp/ocsp_cl.c b/crypto/ocsp/ocsp_cl.c index 909525210f1d53a83840bb9a30071a27027f69fa..9b3e6dd8ca2251189ccb70f52ac0f1f77aa0bf5d 100644 --- a/crypto/ocsp/ocsp_cl.c +++ b/crypto/ocsp/ocsp_cl.c @@ -62,6 +62,7 @@ */ #include +#include #include #include #include @@ -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; + } diff --git a/crypto/ocsp/ocsp_err.c b/crypto/ocsp/ocsp_err.c index 79107190e5f53021636f07356653e211b963c992..1cbf9cab306c08ad116febb760e5b099cbb25391 100644 --- a/crypto/ocsp/ocsp_err.c +++ b/crypto/ocsp/ocsp_err.c @@ -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}