From 81f169e95c86fe9b2c3a7ba51a85f7a00763a0e7 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Wed, 17 Jan 2001 01:31:34 +0000 Subject: [PATCH] Initial OCSP certificate verify. Not complete, it just supports a "trusted OCSP global root CA". --- CHANGES | 10 +++++++ apps/apps.c | 29 +++++++++++++++++++ apps/apps.h | 1 + apps/ocsp.c | 37 ++++++++++++++++++++++++ apps/smime.c | 33 +-------------------- crypto/ocsp/ocsp.h | 9 ++++++ crypto/ocsp/ocsp_err.c | 2 ++ crypto/ocsp/ocsp_lib.c | 11 +++++-- crypto/ocsp/ocsp_vfy.c | 63 ++++++++++++++++++++++++++++++++++++++--- crypto/x509/x509.h | 3 +- crypto/x509/x509_trs.c | 8 ++++++ crypto/x509v3/v3_purp.c | 34 ++++++++++++++++++++-- crypto/x509v3/x509v3.h | 6 +++- 13 files changed, 204 insertions(+), 42 deletions(-) diff --git a/CHANGES b/CHANGES index c11115318e..d0e2699364 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,16 @@ Changes between 0.9.6 and 0.9.7 [xx XXX 2000] + *) Initial OCSP certificate verification added to OCSP_basic_verify() + and related routines. This uses the standard OpenSSL certificate + verify routines to perform initial checks (just CA validity) and + to obtain the certificate chain. Then additional checks will be + performed on the chain. Currently the root CA is checked to see + if it is explicitly trusted for OCSP signing. This is used to set + a root CA as a global signing root: that is any certificate that + chains to that CA is an acceptable OCSP signing certificate. + [Steve Henson] + *) New '-extfile ...' option to 'openssl ca' for reading X.509v3 extensions from a separate configuration file. As when reading extensions from the main configuration file, diff --git a/apps/apps.c b/apps/apps.c index ca3f557ca2..bdd8c71426 100644 --- a/apps/apps.c +++ b/apps/apps.c @@ -837,3 +837,32 @@ void print_name(BIO *out, char *title, X509_NAME *nm, unsigned long lflags) } } +X509_STORE *setup_verify(BIO *bp, char *CAfile, char *CApath) +{ + X509_STORE *store; + X509_LOOKUP *lookup; + if(!(store = X509_STORE_new())) goto end; + lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file()); + if (lookup == NULL) goto end; + if (CAfile) { + if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) { + BIO_printf(bp, "Error loading file %s\n", CAfile); + goto end; + } + } else X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT); + + lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir()); + if (lookup == NULL) goto end; + if (CApath) { + if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) { + BIO_printf(bp, "Error loading directory %s\n", CApath); + goto end; + } + } else X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT); + + ERR_clear_error(); + return store; + end: + X509_STORE_free(store); + return NULL; +} diff --git a/apps/apps.h b/apps/apps.h index 11133cb1d2..2da89e2112 100644 --- a/apps/apps.h +++ b/apps/apps.h @@ -158,6 +158,7 @@ X509 *load_cert(BIO *err, char *file, int format); EVP_PKEY *load_key(BIO *err, char *file, int format, char *pass, ENGINE *e); EVP_PKEY *load_pubkey(BIO *err, char *file, int format, ENGINE *e); STACK_OF(X509) *load_certs(BIO *err, char *file, int format); +X509_STORE *setup_verify(BIO *bp, char *CAfile, char *CApath); #define FORMAT_UNDEF 0 #define FORMAT_ASN1 1 diff --git a/apps/ocsp.c b/apps/ocsp.c index cfd4f18d2e..3125583ace 100644 --- a/apps/ocsp.c +++ b/apps/ocsp.c @@ -82,14 +82,18 @@ int MAIN(int argc, char **argv) int add_nonce = 1; OCSP_REQUEST *req = NULL; OCSP_RESPONSE *resp = NULL; + OCSP_BASICRESP *bs = NULL; X509 *issuer = NULL, *cert = NULL; X509 *signer = NULL; EVP_PKEY *key = NULL; BIO *cbio = NULL, *derbio = NULL; BIO *out = NULL; int req_text = 0, resp_text = 0; + char *CAfile = NULL, *CApath = NULL; + X509_STORE *store = NULL; int ret = 1; int badarg = 0; + int i; if (bio_err == NULL) bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); ERR_load_crypto_strings(); args = argv + 1; @@ -153,6 +157,24 @@ int MAIN(int argc, char **argv) } else badarg = 1; } + else if (!strcmp (*args, "-CAfile")) + { + if (args[1]) + { + args++; + CAfile = *args; + } + else badarg = 1; + } + else if (!strcmp (*args, "-CApath")) + { + if (args[1]) + { + args++; + CApath = *args; + } + else badarg = 1; + } else if (!strcmp(*args, "-signkey")) { if (args[1]) @@ -386,11 +408,25 @@ int MAIN(int argc, char **argv) if (resp_text) OCSP_RESPONSE_print(out, resp, 0); + store = setup_verify(bio_err, CAfile, CApath); + if(!store) goto end; + + bs = OCSP_response_get1_basic(resp); + + i = OCSP_basic_verify(bs, NULL, store, 0); + + if(i <= 0) + { + BIO_printf(bio_err, "Response verify error (%d)\n", i); + ERR_print_errors(bio_err); + } + ret = 0; end: ERR_print_errors(bio_err); X509_free(signer); + X509_STORE_free(store); EVP_PKEY_free(key); X509_free(issuer); X509_free(cert); @@ -398,6 +434,7 @@ end: BIO_free(out); OCSP_REQUEST_free(req); OCSP_RESPONSE_free(resp); + OCSP_BASICRESP_free(bs); EXIT(ret); } diff --git a/apps/smime.c b/apps/smime.c index 0a16bbc4dd..e0d31b20be 100644 --- a/apps/smime.c +++ b/apps/smime.c @@ -68,7 +68,6 @@ #undef PROG #define PROG smime_main -static X509_STORE *setup_verify(char *CAfile, char *CApath); static int save_certs(char *signerfile, STACK_OF(X509) *signers); #define SMIME_OP 0x10 @@ -431,7 +430,7 @@ int MAIN(int argc, char **argv) } if(operation == SMIME_VERIFY) { - if(!(store = setup_verify(CAfile, CApath))) goto end; + if(!(store = setup_verify(bio_err, CAfile, CApath))) goto end; } ret = 3; @@ -530,36 +529,6 @@ end: return (ret); } -static X509_STORE *setup_verify(char *CAfile, char *CApath) -{ - X509_STORE *store; - X509_LOOKUP *lookup; - if(!(store = X509_STORE_new())) goto end; - lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file()); - if (lookup == NULL) goto end; - if (CAfile) { - if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) { - BIO_printf(bio_err, "Error loading file %s\n", CAfile); - goto end; - } - } else X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT); - - lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir()); - if (lookup == NULL) goto end; - if (CApath) { - if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) { - BIO_printf(bio_err, "Error loading directory %s\n", CApath); - goto end; - } - } else X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT); - - ERR_clear_error(); - return store; - end: - X509_STORE_free(store); - return NULL; -} - static int save_certs(char *signerfile, STACK_OF(X509) *signers) { int i; diff --git a/crypto/ocsp/ocsp.h b/crypto/ocsp/ocsp.h index 60b843a2fa..1cb6eadfbb 100644 --- a/crypto/ocsp/ocsp.h +++ b/crypto/ocsp/ocsp.h @@ -79,6 +79,12 @@ extern "C" { #define OCSP_NOCERTS 0x1 #define OCSP_NOINTERN 0x2 #define OCSP_NOSIGS 0x4 +#define OCSP_NOCHAIN 0x8 +#define OCSP_NOVERIFY 0x10 +#define OCSP_NOEXPLICIT 0x20 +#define OCSP_NOCASIGN 0x40 +#define OCSP_NODELEGATED 0x80 +#define OCSP_NOCHECKS 0x100 /* CertID ::= SEQUENCE { * hashAlgorithm AlgorithmIdentifier, @@ -434,6 +440,7 @@ int OCSP_resp_find_status(OCSP_BASICRESP *bs, OCSP_CERTID *id, int *status, int OCSP_request_verify(OCSP_REQUEST *req, EVP_PKEY *pkey); +int OCSP_id_issuer_cmp(OCSP_CERTID *a, OCSP_CERTID *b); int OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b); OCSP_BASICRESP *OCSP_basic_response_new(int tag, @@ -565,6 +572,7 @@ void ERR_load_OCSP_strings(void); /* Reason codes. */ #define OCSP_R_BAD_DATA 108 #define OCSP_R_BAD_TAG 100 +#define OCSP_R_CERTIFICATE_VERIFY_ERROR 126 #define OCSP_R_DIGEST_ERR 101 #define OCSP_R_FAILED_TO_OPEN 109 #define OCSP_R_FAILED_TO_READ 110 @@ -579,6 +587,7 @@ void ERR_load_OCSP_strings(void); #define OCSP_R_NO_RESPONSE_DATA 104 #define OCSP_R_NO_SIGNATURE 105 #define OCSP_R_REVOKED_NO_TIME 106 +#define OCSP_R_ROOT_CA_NOT_TRUSTED 127 #define OCSP_R_SERVER_READ_ERROR 116 #define OCSP_R_SERVER_RESPONSE_ERROR 117 #define OCSP_R_SERVER_RESPONSE_PARSE_ERROR 118 diff --git a/crypto/ocsp/ocsp_err.c b/crypto/ocsp/ocsp_err.c index 70a27561b1..f4335d28c9 100644 --- a/crypto/ocsp/ocsp_err.c +++ b/crypto/ocsp/ocsp_err.c @@ -87,6 +87,7 @@ static ERR_STRING_DATA OCSP_str_reasons[]= { {OCSP_R_BAD_DATA ,"bad data"}, {OCSP_R_BAD_TAG ,"bad tag"}, +{OCSP_R_CERTIFICATE_VERIFY_ERROR ,"certificate verify error"}, {OCSP_R_DIGEST_ERR ,"digest err"}, {OCSP_R_FAILED_TO_OPEN ,"failed to open"}, {OCSP_R_FAILED_TO_READ ,"failed to read"}, @@ -101,6 +102,7 @@ static ERR_STRING_DATA OCSP_str_reasons[]= {OCSP_R_NO_RESPONSE_DATA ,"no response data"}, {OCSP_R_NO_SIGNATURE ,"no signature"}, {OCSP_R_REVOKED_NO_TIME ,"revoked no time"}, +{OCSP_R_ROOT_CA_NOT_TRUSTED ,"root ca not trusted"}, {OCSP_R_SERVER_READ_ERROR ,"server read error"}, {OCSP_R_SERVER_RESPONSE_ERROR ,"server response error"}, {OCSP_R_SERVER_RESPONSE_PARSE_ERROR ,"server response parse error"}, diff --git a/crypto/ocsp/ocsp_lib.c b/crypto/ocsp/ocsp_lib.c index f9d2978402..bdd4cfccff 100644 --- a/crypto/ocsp/ocsp_lib.c +++ b/crypto/ocsp/ocsp_lib.c @@ -163,14 +163,21 @@ err: return NULL; } -int OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b) + +int OCSP_id_issuer_cmp(OCSP_CERTID *a, OCSP_CERTID *b) { int ret; ret = OBJ_cmp(a->hashAlgorithm->algorithm, b->hashAlgorithm->algorithm); if (ret) return ret; ret = ASN1_OCTET_STRING_cmp(a->issuerNameHash, b->issuerNameHash); if (ret) return ret; - ret = ASN1_OCTET_STRING_cmp(a->issuerKeyHash, b->issuerKeyHash); + return ASN1_OCTET_STRING_cmp(a->issuerKeyHash, b->issuerKeyHash); + } + +int OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b) + { + int ret; + ret = OCSP_id_issuer_cmp(a, b); if (ret) return ret; return ASN1_INTEGER_cmp(a->serialNumber, b->serialNumber); } diff --git a/crypto/ocsp/ocsp_vfy.c b/crypto/ocsp/ocsp_vfy.c index 2ea3f4a923..6110825b19 100644 --- a/crypto/ocsp/ocsp_vfy.c +++ b/crypto/ocsp/ocsp_vfy.c @@ -68,13 +68,15 @@ static X509 *ocsp_find_signer_sk(STACK_OF(X509) *certs, OCSP_RESPID *id); int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, X509_STORE *st, unsigned long flags) { - X509 *signer; - int ret; + X509 *signer, *x; + STACK_OF(X509) *chain = NULL; + X509_STORE_CTX ctx; + int i, ret = 0; signer = ocsp_find_signer(bs, certs, st, flags); if (!signer) { OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND); - return 0; + goto end; } if(!(flags & OCSP_NOSIGS)) { @@ -85,9 +87,62 @@ int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, if(ret <= 0) { OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, OCSP_R_SIGNATURE_FAILURE); - return 0; + goto end; } } + if(!(flags & OCSP_NOVERIFY)) + { + if(flags & OCSP_NOCHAIN) + X509_STORE_CTX_init(&ctx, st, signer, NULL); + else + X509_STORE_CTX_init(&ctx, st, signer, bs->certs); + + X509_STORE_CTX_set_purpose(&ctx, X509_PURPOSE_OCSP_HELPER); + ret = X509_verify_cert(&ctx); + chain = X509_STORE_CTX_get1_chain(&ctx); + X509_STORE_CTX_cleanup(&ctx); + if (ret <= 0) + { + i = X509_STORE_CTX_get_error(&ctx); + OCSPerr(OCSP_F_OCSP_BASIC_VERIFY,OCSP_R_CERTIFICATE_VERIFY_ERROR); + ERR_add_error_data(2, "Verify error:", + X509_verify_cert_error_string(i)); + goto end; + } + if(flags & OCSP_NOCHECKS) + { + ret = 1; + goto end; + } + /* At this point we have a valid certificate chain + * need to verify it against the OCSP criteria. + */ +#if 0 + if(ocsp_check_issuer(bs, chain, flags)) + { + ret = 1; + goto end; + } +#endif + + /* Easy case: explicitly trusted. Get root CA and + * check for explicit trust + */ + if(flags & OCSP_NOEXPLICIT) goto end; + + x = sk_X509_value(chain, sk_X509_num(chain) - 1); + if(X509_check_trust(x, NID_OCSP_sign, 0) != X509_TRUST_TRUSTED) + { + OCSPerr(OCSP_F_OCSP_BASIC_VERIFY,OCSP_R_ROOT_CA_NOT_TRUSTED); + goto end; + } + ret = 1; + } + + + + end: + if(chain) sk_X509_pop_free(chain, X509_free); return 1; } diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h index 332cad553a..4b1957253d 100644 --- a/crypto/x509/x509.h +++ b/crypto/x509/x509.h @@ -300,10 +300,11 @@ DECLARE_STACK_OF(X509_TRUST) #define X509_TRUST_SSL_SERVER 3 #define X509_TRUST_EMAIL 4 #define X509_TRUST_OBJECT_SIGN 5 +#define X509_TRUST_OCSP_SIGN 6 /* Keep these up to date! */ #define X509_TRUST_MIN 1 -#define X509_TRUST_MAX 5 +#define X509_TRUST_MAX 6 /* trust_flags values */ diff --git a/crypto/x509/x509_trs.c b/crypto/x509/x509_trs.c index a7b1543461..0de252c65d 100644 --- a/crypto/x509/x509_trs.c +++ b/crypto/x509/x509_trs.c @@ -66,6 +66,7 @@ static int tr_cmp(const X509_TRUST * const *a, static void trtable_free(X509_TRUST *p); static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags); +static int trust_1oid(X509_TRUST *trust, X509 *x, int flags); static int trust_compat(X509_TRUST *trust, X509 *x, int flags); static int obj_trust(int id, X509 *x, int flags); @@ -81,6 +82,7 @@ static X509_TRUST trstandard[] = { {X509_TRUST_SSL_CLIENT, 0, trust_1oidany, "SSL Client", NID_client_auth, NULL}, {X509_TRUST_SSL_SERVER, 0, trust_1oidany, "SSL Client", NID_server_auth, NULL}, {X509_TRUST_EMAIL, 0, trust_1oidany, "S/MIME email", NID_email_protect, NULL}, +{X509_TRUST_OCSP_SIGN, 0, trust_1oid, "OCSP responder", NID_OCSP_sign, NULL} }; #define X509_TRUST_COUNT (sizeof(trstandard)/sizeof(X509_TRUST)) @@ -235,6 +237,12 @@ static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags) return trust_compat(trust, x, flags); } +static int trust_1oid(X509_TRUST *trust, X509 *x, int flags) +{ + if(x->aux) return obj_trust(trust->arg1, x, flags); + return X509_TRUST_UNTRUSTED; +} + static int trust_compat(X509_TRUST *trust, X509 *x, int flags) { X509_check_purpose(x, -1, 0); diff --git a/crypto/x509v3/v3_purp.c b/crypto/x509v3/v3_purp.c index 717e23d5c6..9d67bd92d5 100644 --- a/crypto/x509v3/v3_purp.c +++ b/crypto/x509v3/v3_purp.c @@ -61,7 +61,6 @@ #include #include - static void x509v3_cache_extensions(X509 *x); static int ca_check(const X509 *x); @@ -74,6 +73,7 @@ static int check_purpose_smime_sign(const X509_PURPOSE *xp, const X509 *x, int c static int check_purpose_smime_encrypt(const X509_PURPOSE *xp, const X509 *x, int ca); static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, int ca); static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca); +static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca); static int xp_cmp(const X509_PURPOSE * const *a, const X509_PURPOSE * const *b); @@ -87,6 +87,7 @@ static X509_PURPOSE xstandard[] = { {X509_PURPOSE_SMIME_ENCRYPT, X509_TRUST_EMAIL, 0, check_purpose_smime_encrypt, "S/MIME encryption", "smimeencrypt", NULL}, {X509_PURPOSE_CRL_SIGN, X509_TRUST_COMPAT, 0, check_purpose_crl_sign, "CRL signing", "crlsign", NULL}, {X509_PURPOSE_ANY, X509_TRUST_DEFAULT, 0, no_check, "Any Purpose", "any", NULL}, + {X509_PURPOSE_OCSP_HELPER, X509_TRUST_COMPAT, 0, ocsp_helper, "OCSP helper", "ocsphelper", NULL}, }; #define X509_PURPOSE_COUNT (sizeof(xstandard)/sizeof(X509_PURPOSE)) @@ -144,7 +145,6 @@ int X509_PURPOSE_get_by_sname(char *sname) return -1; } - int X509_PURPOSE_get_by_id(int purpose) { X509_PURPOSE tmp; @@ -320,6 +320,15 @@ static void x509v3_cache_extensions(X509 *x) case NID_ms_sgc: case NID_ns_sgc: x->ex_xkusage |= XKU_SGC; + break; + + case NID_OCSP_sign: + x->ex_xkusage |= XKU_OCSP_SIGN; + break; + + case NID_time_stamp: + x->ex_xkusage |= XKU_TIMESTAMP; + break; } } sk_ASN1_OBJECT_pop_free(extusage, ASN1_OBJECT_free); @@ -470,6 +479,27 @@ static int check_purpose_crl_sign(const X509_PURPOSE *xp, const X509 *x, int ca) return 1; } +/* OCSP helper: this is *not* a full OCSP check. It just checks that + * each CA is valid. Additional checks must be made on the chain. + */ + +static int ocsp_helper(const X509_PURPOSE *xp, const X509 *x, int ca) +{ + /* Must be a valid CA */ + if(ca) { + int ca_ret; + ca_ret = ca_check(x); + if(ca_ret != 2) return ca_ret; + if(x->ex_flags & EXFLAG_NSCERT) { + if(x->ex_nscert & NS_ANY_CA) return ca_ret; + return 0; + } + return 0; + } + /* leaf certificate is checked in OCSP_verify() */ + return 1; +} + static int no_check(const X509_PURPOSE *xp, const X509 *x, int ca) { return 1; diff --git a/crypto/x509v3/x509v3.h b/crypto/x509v3/x509v3.h index 8bf67bcbe0..29421b754d 100644 --- a/crypto/x509v3/x509v3.h +++ b/crypto/x509v3/x509v3.h @@ -342,12 +342,15 @@ DECLARE_ASN1_SET_OF(POLICYINFO) #define NS_SSL_CA 0x04 #define NS_SMIME_CA 0x02 #define NS_OBJSIGN_CA 0x01 +#define NS_ANY_CA (NS_SSL_CA|NS_SMIME_CA|NS_OBJSIGN_CA) #define XKU_SSL_SERVER 0x1 #define XKU_SSL_CLIENT 0x2 #define XKU_SMIME 0x4 #define XKU_CODE_SIGN 0x8 #define XKU_SGC 0x10 +#define XKU_OCSP_SIGN 0x20 +#define XKU_TIMESTAMP 0x40 #define X509_PURPOSE_DYNAMIC 0x1 #define X509_PURPOSE_DYNAMIC_NAME 0x2 @@ -370,9 +373,10 @@ typedef struct x509_purpose_st { #define X509_PURPOSE_SMIME_ENCRYPT 5 #define X509_PURPOSE_CRL_SIGN 6 #define X509_PURPOSE_ANY 7 +#define X509_PURPOSE_OCSP_HELPER 8 #define X509_PURPOSE_MIN 1 -#define X509_PURPOSE_MAX 7 +#define X509_PURPOSE_MAX 8 /* Flags for X509V3_EXT_print() */ -- 2.34.1