New function and options to check OCSP response validity.
This commit is contained in:
parent
4ff18c8c3e
commit
f196522159
11
CHANGES
11
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]
|
||||
|
55
apps/ocsp.c
55
apps/ocsp.c
@ -64,12 +64,16 @@
|
||||
#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: ");
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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}
|
||||
|
Loading…
Reference in New Issue
Block a user