Hi, Martin! A month ago we implemented our own OCSP verification support in OpenSSL, integrated into X509 verification. Today I suddenly found out that there is an alternative implementation you've done (I wish I found it before writing our own! ;) ).
Our patch is a little bit different and aims on simplicity and transparency for apps/libs that use openssl, like openldap. Anyway, it's a good idea to register another one at RT so it won't be lost somewhere in the Internet. Patch adds three verification flags: - X509_V_FLAG_OCSP_CHECK [enable OCSP checks] - X509_V_FLAG_OCSP_CHECK_ALL [check the whole chain] - X509_V_FLAG_OCSP_NO_NONCE [disable nonce checks - speedup!] and one special function X509_set_cert_ocsp_opt(..,const char *filename, const char *ocsp_url, const int ocsp_do_validation) that allows user to manually control OCSP verification for the CA specified by it's filename. You can read more at gmane: http://comments.gmane.org/gmane.comp.encryption.openssl.devel/21122 I tried to integrate our patch into OpenSSL as much as possible, so some OCSP procedure internal errors are added to be reported with ERR_print() routines. Hope someday extended OCSP functionality will go into upstream, though this will require additional efforts. Attached patch was tested in our company and seems to work fine. -- Best wishes, Alexander Komyagin
diff --git a/apps/apps.c b/apps/apps.c index 4e11915..47f8c2f 100644 --- a/apps/apps.c +++ b/apps/apps.c @@ -2338,8 +2338,12 @@ int args_verify(char ***pargs, int *pargc, flags |= X509_V_FLAG_CB_ISSUER_CHECK; else if (!strcmp(arg, "-crl_check")) flags |= X509_V_FLAG_CRL_CHECK; + else if (!strcmp(arg, "-ocsp_check")) + flags |= X509_V_FLAG_OCSP_CHECK; else if (!strcmp(arg, "-crl_check_all")) flags |= X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL; + else if (!strcmp(arg, "-ocsp_check_all")) + flags |= X509_V_FLAG_OCSP_CHECK|X509_V_FLAG_OCSP_CHECK_ALL; else if (!strcmp(arg, "-policy_check")) flags |= X509_V_FLAG_POLICY_CHECK; else if (!strcmp(arg, "-explicit_policy")) diff --git a/apps/verify.c b/apps/verify.c index b9749dc..8eeaf7b 100644 --- a/apps/verify.c +++ b/apps/verify.c @@ -65,6 +65,8 @@ #include <openssl/x509.h> #include <openssl/x509v3.h> #include <openssl/pem.h> +#include <openssl/x509_vfy_ocsp.h> +#include <openssl/ocsp_clnt.h> #undef PROG #define PROG verify_main @@ -88,6 +90,8 @@ int MAIN(int argc, char **argv) X509_STORE *cert_ctx=NULL; X509_LOOKUP *lookup=NULL; X509_VERIFY_PARAM *vpm = NULL; + char *ocsp_url = NULL; + int ocsp_validate = X509_OCSP_VALIDATE_ENABLED; #ifndef OPENSSL_NO_ENGINE char *engine=NULL; #endif @@ -95,6 +99,7 @@ int MAIN(int argc, char **argv) cert_ctx=X509_STORE_new(); if (cert_ctx == NULL) goto end; X509_STORE_set_verify_cb(cert_ctx,cb); + X509_STORE_set_ocsp_process_resp(cert_ctx, &ocsp_process_responder); ERR_load_crypto_strings(); @@ -123,6 +128,16 @@ int MAIN(int argc, char **argv) if (argc-- < 1) goto end; CAfile= *(++argv); } + else if (strcmp(*argv,"-ocsp_url") == 0) + { + if (argc-- < 1) goto end; + ocsp_url= *(++argv); + } + else if (strcmp(*argv,"-ocsp_validate") == 0) + { + if (argc-- < 1) goto end; + ocsp_validate = atoi(*(++argv)); + } else if (args_verify(&argv, &argc, &badarg, bio_err, &vpm)) { @@ -222,6 +237,18 @@ int MAIN(int argc, char **argv) goto end; } + if (ocsp_url && CAfile) + { + BIO_printf(bio_err, "Setting OCSP params for %s (%s,%d)... ", CAfile, ocsp_url, ocsp_validate); + i = X509_set_cert_ocsp_opt(cert_ctx, CAfile, ocsp_url, ocsp_validate); + if(!i) { + BIO_printf(bio_err, "Error setting OCSP params for %s\n", CAfile); + ERR_print_errors(bio_err); + goto end; + } + BIO_printf(bio_err, "Done.\n"); + } + if (argc < 1) check(cert_ctx, NULL, untrusted, trusted, crls, e); else for (i=0; i<argc; i++) diff --git a/crypto/asn1/x_x509.c b/crypto/asn1/x_x509.c index de3df9e..2074a84 100644 --- a/crypto/asn1/x_x509.c +++ b/crypto/asn1/x_x509.c @@ -81,6 +81,10 @@ IMPLEMENT_ASN1_FUNCTIONS(X509_CINF) extern void policy_cache_free(X509_POLICY_CACHE *cache); +#ifndef OPENSSL_NO_OCSP +extern void X509_CERT_OCSP_free(X509_CERT_OCSP *ocsp); +#endif + static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, void *exarg) { @@ -102,6 +106,9 @@ static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, ret->aux = NULL; ret->crldp = NULL; CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509, ret, &ret->ex_data); +#ifndef OPENSSL_NO_OCSP + ret->ocsp = NULL; +#endif break; case ASN1_OP_D2I_POST: @@ -122,7 +129,9 @@ static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, sk_IPAddressFamily_pop_free(ret->rfc3779_addr, IPAddressFamily_free); ASIdentifiers_free(ret->rfc3779_asid); #endif - +#ifndef OPENSSL_NO_OCSP + if (ret->ocsp != NULL) X509_CERT_OCSP_free(ret->ocsp); +#endif if (ret->name != NULL) OPENSSL_free(ret->name); break; diff --git a/crypto/x509/Makefile b/crypto/x509/Makefile index 72c8227..5a56e42 100644 --- a/crypto/x509/Makefile +++ b/crypto/x509/Makefile @@ -18,13 +18,13 @@ APPS= LIB=$(TOP)/libcrypto.a LIBSRC= x509_def.c x509_d2.c x509_r2x.c x509_cmp.c \ - x509_obj.c x509_req.c x509spki.c x509_vfy.c \ + x509_obj.c x509_req.c x509spki.c x509_vfy.c x509_vfy_ocsp.c \ x509_set.c x509cset.c x509rset.c x509_err.c \ x509name.c x509_v3.c x509_ext.c x509_att.c \ x509type.c x509_lu.c x_all.c x509_txt.c \ x509_trs.c by_file.c by_dir.c x509_vpm.c LIBOBJ= x509_def.o x509_d2.o x509_r2x.o x509_cmp.o \ - x509_obj.o x509_req.o x509spki.o x509_vfy.o \ + x509_obj.o x509_req.o x509spki.o x509_vfy.o x509_vfy_ocsp.o\ x509_set.o x509cset.o x509rset.o x509_err.o \ x509name.o x509_v3.o x509_ext.o x509_att.o \ x509type.o x509_lu.o x_all.o x509_txt.o \ @@ -32,7 +32,7 @@ LIBOBJ= x509_def.o x509_d2.o x509_r2x.o x509_cmp.o \ SRC= $(LIBSRC) -EXHEADER= x509.h x509_vfy.h +EXHEADER= x509.h x509_vfy.h x509_vfy_ocsp.h HEADER= $(EXHEADER) ALL= $(GENERAL) $(SRC) $(HEADER) @@ -95,7 +95,7 @@ by_dir.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h by_dir.o: ../../include/openssl/pkcs7.h ../../include/openssl/safestack.h by_dir.o: ../../include/openssl/sha.h ../../include/openssl/stack.h by_dir.o: ../../include/openssl/symhacks.h ../../include/openssl/x509.h -by_dir.o: ../../include/openssl/x509_vfy.h ../cryptlib.h by_dir.c +by_dir.o: ../../include/openssl/x509_vfy.h ../../include/openssl/x509_vfy_ocsp.h ../cryptlib.h by_dir.c by_file.o: ../../e_os.h ../../include/openssl/asn1.h by_file.o: ../../include/openssl/bio.h ../../include/openssl/buffer.h by_file.o: ../../include/openssl/crypto.h ../../include/openssl/e_os2.h @@ -109,7 +109,7 @@ by_file.o: ../../include/openssl/pem2.h ../../include/openssl/pkcs7.h by_file.o: ../../include/openssl/safestack.h ../../include/openssl/sha.h by_file.o: ../../include/openssl/stack.h ../../include/openssl/symhacks.h by_file.o: ../../include/openssl/x509.h ../../include/openssl/x509_vfy.h -by_file.o: ../cryptlib.h by_file.c +by_file.o: ../cryptlib.h ../../include/openssl/x509_vfy_ocsp.h by_file.c x509_att.o: ../../e_os.h ../../include/openssl/asn1.h x509_att.o: ../../include/openssl/bio.h ../../include/openssl/buffer.h x509_att.o: ../../include/openssl/conf.h ../../include/openssl/crypto.h @@ -311,8 +311,22 @@ x509_vfy.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h x509_vfy.o: ../../include/openssl/pkcs7.h ../../include/openssl/safestack.h x509_vfy.o: ../../include/openssl/sha.h ../../include/openssl/stack.h x509_vfy.o: ../../include/openssl/symhacks.h ../../include/openssl/x509.h -x509_vfy.o: ../../include/openssl/x509_vfy.h ../../include/openssl/x509v3.h +x509_vfy.o: ../../include/openssl/x509_vfy.h ../../include/openssl/x509_vfy_ocsp.h ../../include/openssl/x509v3.h x509_vfy.o: ../cryptlib.h x509_vfy.c +x509_vfy_ocsp.o: ../../e_os.h ../../include/openssl/asn1.h +x509_vfy_ocsp.o: ../../include/openssl/bio.h ../../include/openssl/buffer.h +x509_vfy_ocsp.o: ../../include/openssl/conf.h ../../include/openssl/crypto.h +x509_vfy_ocsp.o: ../../include/openssl/e_os2.h ../../include/openssl/ec.h +x509_vfy_ocsp.o: ../../include/openssl/ecdh.h ../../include/openssl/ecdsa.h +x509_vfy_ocsp.o: ../../include/openssl/err.h ../../include/openssl/evp.h +x509_vfy_ocsp.o: ../../include/openssl/lhash.h ../../include/openssl/obj_mac.h +x509_vfy_ocsp.o: ../../include/openssl/objects.h ../../include/openssl/opensslconf.h +x509_vfy_ocsp.o: ../../include/openssl/opensslv.h ../../include/openssl/ossl_typ.h +x509_vfy_ocsp.o: ../../include/openssl/pkcs7.h ../../include/openssl/safestack.h +x509_vfy_ocsp.o: ../../include/openssl/sha.h ../../include/openssl/stack.h +x509_vfy_ocsp.o: ../../include/openssl/symhacks.h ../../include/openssl/x509.h +x509_vfy_ocsp.o: ../../include/openssl/x509_vfy.h ../../include/openssl/x509_vfy_ocsp.h ../../include/openssl/x509v3.h +x509_vfy_ocsp.o: ../cryptlib.h ../../include/openssl/ocsp.h x509_vfy_ocsp.c x509_vpm.o: ../../e_os.h ../../include/openssl/asn1.h x509_vpm.o: ../../include/openssl/bio.h ../../include/openssl/buffer.h x509_vpm.o: ../../include/openssl/conf.h ../../include/openssl/crypto.h @@ -404,4 +418,4 @@ x_all.o: ../../include/openssl/ossl_typ.h ../../include/openssl/pkcs7.h x_all.o: ../../include/openssl/rsa.h ../../include/openssl/safestack.h x_all.o: ../../include/openssl/sha.h ../../include/openssl/stack.h x_all.o: ../../include/openssl/symhacks.h ../../include/openssl/x509.h -x_all.o: ../../include/openssl/x509_vfy.h ../cryptlib.h x_all.c +x_all.o: ../../include/openssl/x509_vfy.h ../../include/openssl/x509_vfy_ocsp.h ../cryptlib.h x_all.c diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h index 092dd74..e5233fe 100644 --- a/crypto/x509/x509.h +++ b/crypto/x509/x509.h @@ -276,6 +276,17 @@ typedef struct x509_cert_aux_st STACK_OF(X509_ALGOR) *other; /* other unspecified info */ } X509_CERT_AUX; +/* This struct represents OCSP-related validation + * data that can be manually set for certificate + */ +#ifndef OPENSSL_NO_OCSP +typedef struct x509_cert_ocsp_st + { + char *ocsp_url; /* forced OCSP URL */ + int ocsp_validate; /* validation mode: if disabled, one has no OCSP */ + } X509_CERT_OCSP; +#endif + struct x509_st { X509_CINF *cert_info; @@ -306,6 +317,9 @@ struct x509_st unsigned char sha1_hash[SHA_DIGEST_LENGTH]; #endif X509_CERT_AUX *aux; +#ifndef OPENSSL_NO_OCSP + X509_CERT_OCSP *ocsp; +#endif } /* X509 */; DECLARE_STACK_OF(X509) @@ -859,6 +873,10 @@ int X509_add1_reject_object(X509 *x, ASN1_OBJECT *obj); void X509_trust_clear(X509 *x); void X509_reject_clear(X509 *x); +#ifndef OPENSSL_NO_OCSP +int X509_set0_ocsp(X509 *cert, X509_CERT_OCSP *ocsp); +#endif + DECLARE_ASN1_FUNCTIONS(X509_REVOKED) DECLARE_ASN1_FUNCTIONS(X509_CRL_INFO) DECLARE_ASN1_FUNCTIONS(X509_CRL) diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c index 38525a8..c2d21e1 100644 --- a/crypto/x509/x509_lu.c +++ b/crypto/x509/x509_lu.c @@ -187,6 +187,10 @@ X509_STORE *X509_STORE_new(void) ret->verify=0; ret->verify_cb=0; +#ifndef OPENSSL_NO_OCSP + ret->ocsp_process_responder=0; +#endif + if ((ret->param = X509_VERIFY_PARAM_new()) == NULL) return NULL; @@ -712,5 +716,13 @@ void X509_STORE_set_verify_cb(X509_STORE *ctx, ctx->verify_cb = verify_cb; } +#ifndef OPENSSL_NO_OCSP +void X509_STORE_set_ocsp_process_resp(X509_STORE *ctx, + ocsp_process_responder_f func) + { + ctx->ocsp_process_responder = func; + } +#endif + IMPLEMENT_STACK_OF(X509_LOOKUP) IMPLEMENT_STACK_OF(X509_OBJECT) diff --git a/crypto/x509/x509_txt.c b/crypto/x509/x509_txt.c index c44f753..4fcb077 100644 --- a/crypto/x509/x509_txt.c +++ b/crypto/x509/x509_txt.c @@ -183,6 +183,24 @@ const char *X509_verify_cert_error_string(long n) return("unsupported or invalid name syntax"); case X509_V_ERR_CRL_PATH_VALIDATION_ERROR: return("CRL path validation error"); + case X509_V_ERR_CERT_UNKNOWN: + return("certificate is unknown for OCSP"); + case X509_V_ERR_UNABLE_TO_FIND_OCSP_URL: + return("unable to find OCSP responder url"); + case X509_V_ERR_UNPARSABLE_OCSP_URL: + return("failed to parse OCSP responder url"); + case X509_V_ERR_SSL_USED_FOR_OCSP: + return("ssl connections to OCSP servers are not supported"); + case X509_V_ERR_OCSP_PROCESS_FUNC_NOT_SET: + return("OCSP responder process function is not set"); + case X509_V_ERR_OCSP_RESPONDER_STATUS_FAIL: + return("OCSP responder reports fail"); + case X509_V_ERR_OCSP_NONCE_VERIFY_ERROR: + return("OCSP nonce verification failure"); + case X509_V_ERR_OCSP_RESPONSE_VERIFICATION: + return("OCSP response verification failure"); + case X509_V_ERR_OCSP_RESPONDER_QUERY_FAILED: + return("failed to query OCSP responder"); default: BIO_snprintf(buf,sizeof buf,"error number %ld",n); diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index b0779db..ab970cc 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -70,6 +70,8 @@ #include <openssl/x509v3.h> #include <openssl/objects.h> +#include "x509_vfy_ocsp.h" + /* CRL score values */ /* No unhandled critical extensions */ @@ -669,6 +671,16 @@ static int check_trust(X509_STORE_CTX *ctx) static int check_revocation(X509_STORE_CTX *ctx) { int i, last, ok; + +#ifndef OPENSSL_NO_OCSP + /* Check if we need to check against OCSP */ + if (ctx->param->flags & X509_V_FLAG_OCSP_CHECK) + { + ok = check_revocation_ocsp(ctx); + if (!ok) return ok; + } +#endif + if (!(ctx->param->flags & X509_V_FLAG_CRL_CHECK)) return 1; if (ctx->param->flags & X509_V_FLAG_CRL_CHECK_ALL) @@ -2205,6 +2217,58 @@ void X509_STORE_CTX_set0_param(X509_STORE_CTX *ctx, X509_VERIFY_PARAM *param) ctx->param = param; } +#ifndef OPENSSL_NO_OCSP +void X509_CERT_OCSP_free(X509_CERT_OCSP *ocsp) + { + if (!ocsp) + return; + if (ocsp->ocsp_url) + OPENSSL_free(ocsp->ocsp_url); + OPENSSL_free(ocsp); + } + +X509_CERT_OCSP *X509_CERT_OCSP_new(void) + { + X509_CERT_OCSP *ocsp; + ocsp = OPENSSL_malloc(sizeof(X509_CERT_OCSP)); + memset(ocsp, 0, sizeof(X509_CERT_OCSP)); + ocsp->ocsp_validate = X509_OCSP_VALIDATE_ENABLED; + return ocsp; + } + +int X509_set0_ocsp(X509 *cert, X509_CERT_OCSP *ocsp) + { + if (cert->ocsp) + X509_CERT_OCSP_free(cert->ocsp); + cert->ocsp = ocsp; + return 1; + } + +int X509_CERT_OCSP_set1_url(X509_CERT_OCSP *ocsp, char* url) + { + if (ocsp->ocsp_url) + OPENSSL_free(ocsp->ocsp_url); + + if (url == NULL) + { + ocsp->ocsp_url = NULL; + return 1; + } + + ocsp->ocsp_url = BUF_strdup(url); + if (ocsp->ocsp_url) + return 1; + + return 0; + } + +int X509_CERT_OCSP_set_validate(X509_CERT_OCSP *ocsp, int validate) + { + ocsp->ocsp_validate = validate; + return 1; + } +#endif + IMPLEMENT_STACK_OF(X509) IMPLEMENT_ASN1_SET_OF(X509) diff --git a/crypto/x509/x509_vfy.h b/crypto/x509/x509_vfy.h index fe09b30..e70ba76 100644 --- a/crypto/x509/x509_vfy.h +++ b/crypto/x509/x509_vfy.h @@ -73,6 +73,10 @@ #include <openssl/crypto.h> #include <openssl/symhacks.h> +#ifndef OPENSSL_NO_OCSP +#include <openssl/x509_vfy_ocsp.h> +#endif + #ifdef __cplusplus extern "C" { #endif @@ -206,6 +210,10 @@ struct x509_store_st CRYPTO_EX_DATA ex_data; int references; + +#ifndef OPENSSL_NO_OCSP + ocsp_process_responder_f ocsp_process_responder; /* Connect to OCSP responder */ +#endif } /* X509_STORE */; int X509_STORE_set_depth(X509_STORE *store, int depth); @@ -357,6 +365,17 @@ void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth); /* The application is not happy */ #define X509_V_ERR_APPLICATION_VERIFICATION 50 +/* OCSP related errors */ +#define X509_V_ERR_CERT_UNKNOWN 55 +#define X509_V_ERR_UNABLE_TO_FIND_OCSP_URL 56 +#define X509_V_ERR_UNPARSABLE_OCSP_URL 57 +#define X509_V_ERR_SSL_USED_FOR_OCSP 58 +#define X509_V_ERR_OCSP_PROCESS_FUNC_NOT_SET 59 +#define X509_V_ERR_OCSP_RESPONDER_STATUS_FAIL 60 +#define X509_V_ERR_OCSP_NONCE_VERIFY_ERROR 61 +#define X509_V_ERR_OCSP_RESPONSE_VERIFICATION 62 +#define X509_V_ERR_OCSP_RESPONDER_QUERY_FAILED 63 + /* Certificate verify flags */ /* Send issuer+subject checks to verify_cb */ @@ -389,6 +408,12 @@ void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth); #define X509_V_FLAG_USE_DELTAS 0x2000 /* Check selfsigned CA signature */ #define X509_V_FLAG_CHECK_SS_SIGNATURE 0x4000 +/* Check revocation against OCSP */ +#define X509_V_FLAG_OCSP_CHECK 0x8000 +/* Check revocation against OCSP for whole chain */ +#define X509_V_FLAG_OCSP_CHECK_ALL 0x10000 +/* Disable OCSP nonce */ +#define X509_V_FLAG_OCSP_NO_NONCE 0x20000 #define X509_VP_FLAG_DEFAULT 0x1 @@ -422,6 +447,11 @@ int X509_STORE_set1_param(X509_STORE *ctx, X509_VERIFY_PARAM *pm); void X509_STORE_set_verify_cb(X509_STORE *ctx, int (*verify_cb)(int, X509_STORE_CTX *)); +#ifndef OPENSSL_NO_OCSP +void X509_STORE_set_ocsp_process_resp(X509_STORE *ctx, + ocsp_process_responder_f func); +#endif + X509_STORE_CTX *X509_STORE_CTX_new(void); int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x); @@ -560,6 +590,13 @@ STACK_OF(POLICYQUALINFO) * const X509_POLICY_NODE * X509_policy_node_get0_parent(const X509_POLICY_NODE *node); +#ifndef OPENSSL_NO_OCSP +X509_CERT_OCSP *X509_CERT_OCSP_new(); +void X509_CERT_OCSP_free(X509_CERT_OCSP *ocsp); +int X509_CERT_OCSP_set1_url(X509_CERT_OCSP *ocsp, char* url); +int X509_CERT_OCSP_set_validate(X509_CERT_OCSP *ocsp, int validate); +#endif + #ifdef __cplusplus } #endif diff --git a/crypto/x509/x509_vfy_ocsp.c b/crypto/x509/x509_vfy_ocsp.c new file mode 100644 index 0000000..9a9798a --- /dev/null +++ b/crypto/x509/x509_vfy_ocsp.c @@ -0,0 +1,401 @@ +/* crypto/x509/x509_vfy_ocsp.c */ +/* Copyright (C) 2012 Altell Ltd. (komya...@altell.ru) + * All rights reserved. + * + * This file is a helper to incorporate OCSP check into X509 + * certificate verification process. + * Written by Alexander Komyagin (komya...@altell.ru). + * + */ + +#ifndef OPENSSL_NO_OCSP + +#include <openssl/crypto.h> +#include <openssl/lhash.h> +#include <openssl/buffer.h> +#include <openssl/evp.h> +#include <openssl/asn1.h> +#include <openssl/x509.h> +#include <openssl/ocsp.h> +#include <openssl/ssl.h> +#include <stdio.h> + +#include "x509_vfy_ocsp.h" + +static char *get_ocsp_url(X509 *x, STACK_OF(OPENSSL_STRING) **p_aia) +{ + STACK_OF(OPENSSL_STRING) *aia; + + // ok, reading from the cert + aia = X509_get1_ocsp(x); + + if (aia) + { + *p_aia = aia; //thou shalt not lose the given pointer + return sk_OPENSSL_STRING_value(aia, 0); + } + + return NULL; +} + +int check_cert_ocsp(X509_STORE_CTX *ctx) +{ + X509 *x; + int cnum; + int ok = 1; + cnum = ctx->error_depth; + x = sk_X509_value(ctx->chain, cnum); + + ctx->current_cert = x; + ctx->current_issuer = NULL; + ctx->current_crl_score = 0; + ctx->current_reasons = 0; + ctx->error = 0; + + STACK_OF(OPENSSL_STRING) *aia = NULL; + char *url = NULL; + char *host = NULL, *port = NULL, *path = NULL; + int use_ssl = -1; + X509 *px = NULL; + OCSP_REQUEST *req = NULL; + OCSP_RESPONSE *resp = NULL; + OCSP_CERTID *id = NULL; + STACK_OF(X509_EXTENSION) *exts; + int i; + OCSP_BASICRESP *bs = NULL; + int status, reason; + ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; + X509_STORE *store = NULL; + + // Get the issuer (must be the next cert in chain) + // We know that the last cert in chain is not checked, + // so NULL must be an error + px = sk_X509_value(ctx->chain, cnum+1); + if (! px) + { + ctx->error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; + goto err1; + } + + // See if we got any OCSP settings requested for this CA + if (px->ocsp) + { + // If ocsp is disabled for issuer, pretend everything is ok + if (px->ocsp->ocsp_validate == X509_OCSP_VALIDATE_DISABLED) + { + ctx->error = 0; + goto err1; + } + + // Check if OCSP url is forced + if (px->ocsp->ocsp_url) + url = px->ocsp->ocsp_url; + } + + // Check for embedded URL. According to RFC2560 (sect 3.1), issuer + // SHALL specify it in order to use OCSP. + if (! url) + url = get_ocsp_url(x, &aia); + + // Build up OCSP query from certificate + if (! url) + { + ctx->error = X509_V_ERR_UNABLE_TO_FIND_OCSP_URL; + goto err1; + } + + if (!OCSP_parse_url(url, &host, &port, &path, &use_ssl)) + { + ctx->error = X509_V_ERR_UNPARSABLE_OCSP_URL; + goto err1; + } + + //FIXME: ssl connection to OCSP responder is not supported yet + if (use_ssl) + { + ctx->error = X509_V_ERR_SSL_USED_FOR_OCSP; + goto err1; + } + + req = OCSP_REQUEST_new(); + if (!req) + { + ctx->error = X509_V_ERR_OUT_OF_MEM; + goto err1; + } + + id = OCSP_cert_to_id(NULL, x, px); + if (!id) + { + DBG("Failed getting OCSP cert id"); + ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; + goto err1; + } + + if (!OCSP_request_add0_id(req, id)) + { + DBG("Failed adding id to request"); + OCSP_CERTID_free(id); //otherwise id seems to be freed by req + ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; + goto err1; + } + + // Query server + if (! (ctx->param->flags & X509_V_FLAG_OCSP_NO_NONCE)) + OCSP_request_add1_nonce(req, NULL, -1); + + // If process func is not set it means ctx hadn't come from SSL_CTX, + // or it's a bug + if (!ctx->ctx->ocsp_process_responder) + { + ctx->error = X509_V_ERR_OCSP_PROCESS_FUNC_NOT_SET; + goto err1; + } + + resp = (OCSP_RESPONSE *)ctx->ctx->ocsp_process_responder((void *)req, host, path, port, use_ssl, NULL, -1); + if (!resp) + { + DBG("OCSP responder processing failure"); + ctx->error = X509_V_ERR_OCSP_RESPONDER_QUERY_FAILED; + goto err1; + } + + i = OCSP_response_status(resp); + + if (i != OCSP_RESPONSE_STATUS_SUCCESSFUL) + { + DBG("Responder Error: %s (%d)", + OCSP_response_status_str(i), i); + ctx->error = X509_V_ERR_OCSP_RESPONDER_STATUS_FAIL; + goto err1; + } + + bs = OCSP_response_get1_basic(resp); + if (!bs) + { + DBG("Error parsing response"); + ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; + goto err1; + } + + // Validate responder + store = ctx->ctx; + if(!store) + { + DBG("Error getting store!"); + ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; + goto err1; + } + + if (! (ctx->param->flags & X509_V_FLAG_OCSP_NO_NONCE)) + { + i = OCSP_check_nonce(req, bs); + if (i == -1) + DBG("WARNING: no nonce in response"); + else if (i <= 0) + { + ctx->error = X509_V_ERR_OCSP_NONCE_VERIFY_ERROR; + goto err1; + } + } + + i = OCSP_basic_verify(bs, NULL, store, 0); + if(i <= 0) + { + ctx->error = X509_V_ERR_OCSP_RESPONSE_VERIFICATION; + goto err1; + } + else + DBG("Response verify OK\n"); + + // Get response status + if(!OCSP_resp_find_status(bs, id, &status, &reason, + &rev, &thisupd, &nextupd)) + { + DBG("ERROR: No Status found in OCSP response."); + ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; + goto err1; + } + + DBG("Status: %s", OCSP_cert_status_str(status)); + + switch(status) + { + case V_OCSP_CERTSTATUS_GOOD: + ctx->error = 0; + break; + case V_OCSP_CERTSTATUS_REVOKED: + ctx->error = X509_V_ERR_CERT_REVOKED; + break; + case V_OCSP_CERTSTATUS_UNKNOWN: + default: + ctx->error = X509_V_ERR_CERT_UNKNOWN; + } + +err1: + //call verify callback if needed + if (ctx->error != 0) + ok = ctx->verify_cb(0, ctx); + + //free occupied resources + if (aia) + X509_email_free(aia); + if (host) + OPENSSL_free(host); + if (path) + OPENSSL_free(path); + if (port) + OPENSSL_free(port); + if (req) + OCSP_REQUEST_free(req); + if (resp) + OCSP_RESPONSE_free(resp); + if (bs) + OCSP_BASICRESP_free(bs); + + return ok; +} + +int check_revocation_ocsp(X509_STORE_CTX *ctx) +{ + int i, last, ok; + + DBG("check_revocation_ocsp: Here"); + + if (!(ctx->param->flags & X509_V_FLAG_OCSP_CHECK)) + return 1; + + if (ctx->param->purpose == X509_PURPOSE_OCSP_HELPER) + { + DBG("OCSP won't verify itself ;)"); + return 1; + } + + // No OCSP for self-signed certs + if (sk_X509_num(ctx->chain) == 1) + { + DBG("Self-signed cert: OCSP won't be used"); + return 1; + } + + if (ctx->param->flags & X509_V_FLAG_OCSP_CHECK_ALL) + last = sk_X509_num(ctx->chain) - 1; + else + { + // If checking CRL paths this isn't the EE certificate + if (ctx->parent) + return 1; + last = 1; + } + + // Note that we won't check the last cert in chain, since it makes + // little sense to check revocation status for the self-signed + // root certificate ;) + for(i = 0; i < last; i++) + { + ctx->error_depth = i; + ok = check_cert_ocsp(ctx); + if (!ok) return ok; + } + return 1; +} + +int X509_set_cert_ocsp_opt(X509_STORE *cert_ctx, const char *name, + const char *ocsp_url, const int ocsp_validate) +{ + FILE *fp = NULL; + X509 *x = NULL; + int ret = 1; + X509_OBJECT *obj = NULL; + + X509_OBJECT *obj_orig = NULL; + X509_CERT_OCSP *ocsp = NULL; + X509 *cert = NULL; + + if (!cert_ctx || !name) + { + ret = 0; + goto err; + } + + // check the value before it goes down the hill + if ((ocsp_validate != X509_OCSP_VALIDATE_ENABLED) && + (ocsp_validate != X509_OCSP_VALIDATE_DISABLED)) + { + DBG("Bad ocsp_validate value"); + ret = 0; + goto err; + } + + // read cert file + fp = fopen(name, "r"); + // is it PEM? + x = PEM_read_X509_AUX(fp, NULL, NULL, NULL); + if (!x) + { + // or maybe DER? + x = d2i_X509_fp(fp, NULL); + } + + if (!x) //whatever... + { + DBG("Failed to parse cert %s", name); + ret = 0; + goto err; + } + + obj=(X509_OBJECT *)OPENSSL_malloc(sizeof(X509_OBJECT)); + if (!obj) + { + DBG("Failed to allocate object"); + ret = 0; + goto err; + } + + obj->type = X509_LU_X509; + obj->data.x509 = x; + + X509_OBJECT_up_ref_count(obj); + + // try to find it in store + obj_orig = X509_OBJECT_retrieve_match(cert_ctx->objs, obj); + if (! obj_orig) + { + DBG("Can't find cert in store"); + ret = 0; + goto err; + } + cert = obj_orig->data.x509; + + // create ocsp struct, fill and save it + ocsp = X509_CERT_OCSP_new(); + if (!ocsp) + { + DBG("Failed allocating ocsp struct"); + ret = 0; + goto err; + } + + if (! X509_CERT_OCSP_set1_url(ocsp, ocsp_url)) + { + DBG("Failed setting ocsp_url"); + } + + if (! X509_CERT_OCSP_set_validate(ocsp, ocsp_validate)) + { + DBG("Failed setting ocsp_validate"); + } + + ret = X509_set0_ocsp(cert, ocsp); + +err: + if(x) + X509_free(x); + if(obj) + OPENSSL_free(obj); + if(fp) + fclose(fp); + + return ret; +} +#endif diff --git a/crypto/x509/x509_vfy_ocsp.h b/crypto/x509/x509_vfy_ocsp.h new file mode 100644 index 0000000..8a79d82 --- /dev/null +++ b/crypto/x509/x509_vfy_ocsp.h @@ -0,0 +1,54 @@ +/* crypto/x509/x509_vfy_ocsp.c */ +/* Copyright (C) 2012 Altell Ltd. (komya...@altell.ru) + * All rights reserved. + * + * This file is a helper to incorporate OCSP check into X509 + * certificate verification process. + * Written by Alexander Komyagin (komya...@altell.ru). + * + */ + + +#ifndef OPENSSL_NO_OCSP + +#ifndef HEADER_X509_VFY_OCSP_H +#define HEADER_X509_VFY_OCSP_H + +#ifndef HEADER_X509_H +#include <openssl/x509.h> +#endif + +//#define OCSP_VFY_DEBUG + +#ifdef OCSP_VFY_DEBUG + #include <syslog.h> + #define DBG(fmt, args...) do { syslog(LOG_INFO, "%s(%d): " fmt, __FUNCTION__, __LINE__, ##args); } while(0) +#else + #define DBG(fmt, args...) do { /* NOP */ } while(0) +#endif //OCSP_VFY_DEBUG + +#define X509_OCSP_VALIDATE_ENABLED 0 +#define X509_OCSP_VALIDATE_DISABLED 1 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void * (*ocsp_process_responder_f)(void *, + char *, char *, char *, int, + void *, + int); + +int X509_set_cert_ocsp_opt(X509_STORE *cert_ctx, const char *name, + const char *ocsp_url, const int ocsp_validate); + +int check_cert_ocsp(X509_STORE_CTX *ctx); +int check_revocation_ocsp(X509_STORE_CTX *ctx); + +#ifdef __cplusplus +} +#endif + +#endif //HEADER_X509_VFY_OCSP_H + +#endif //OPENSSL_NO_OCSP diff --git a/include/openssl/ocsp_clnt.h b/include/openssl/ocsp_clnt.h new file mode 120000 index 0000000..137121b --- /dev/null +++ b/include/openssl/ocsp_clnt.h @@ -0,0 +1 @@ +../../ssl/ocsp_clnt.h \ No newline at end of file diff --git a/include/openssl/x509_vfy_ocsp.h b/include/openssl/x509_vfy_ocsp.h new file mode 120000 index 0000000..f53b4a1 --- /dev/null +++ b/include/openssl/x509_vfy_ocsp.h @@ -0,0 +1 @@ +../../crypto/x509/x509_vfy_ocsp.h \ No newline at end of file diff --git a/ssl/Makefile b/ssl/Makefile index 07e4028..afb486c 100644 --- a/ssl/Makefile +++ b/ssl/Makefile @@ -30,7 +30,7 @@ LIBSRC= \ ssl_lib.c ssl_err2.c ssl_cert.c ssl_sess.c \ ssl_ciph.c ssl_stat.c ssl_rsa.c \ ssl_asn1.c ssl_txt.c ssl_algs.c \ - bio_ssl.c ssl_err.c kssl.c tls_srp.c t1_reneg.c + bio_ssl.c ssl_err.c kssl.c tls_srp.c t1_reneg.c ocsp_clnt.c LIBOBJ= \ s2_meth.o s2_srvr.o s2_clnt.o s2_lib.o s2_enc.o s2_pkt.o \ s3_meth.o s3_srvr.o s3_clnt.o s3_lib.o s3_enc.o s3_pkt.o s3_both.o \ @@ -41,11 +41,11 @@ LIBOBJ= \ ssl_lib.o ssl_err2.o ssl_cert.o ssl_sess.o \ ssl_ciph.o ssl_stat.o ssl_rsa.o \ ssl_asn1.o ssl_txt.o ssl_algs.o \ - bio_ssl.o ssl_err.o kssl.o tls_srp.o t1_reneg.o + bio_ssl.o ssl_err.o kssl.o tls_srp.o t1_reneg.o ocsp_clnt.o SRC= $(LIBSRC) -EXHEADER= ssl.h ssl2.h ssl3.h ssl23.h tls1.h dtls1.h kssl.h srtp.h +EXHEADER= ssl.h ssl2.h ssl3.h ssl23.h tls1.h dtls1.h kssl.h srtp.h ocsp_clnt.h HEADER= $(EXHEADER) ssl_locl.h kssl_lcl.h ALL= $(GENERAL) $(SRC) $(HEADER) diff --git a/ssl/ocsp_clnt.c b/ssl/ocsp_clnt.c new file mode 100644 index 0000000..bb40c9a --- /dev/null +++ b/ssl/ocsp_clnt.c @@ -0,0 +1,184 @@ +/* ssl/ocsp_clnt.c */ +/* Copyright (C) 2012 Altell Ltd. (komya...@altell.ru) + * All rights reserved. + * + * This file is a helper to incorporate OCSP proto into libssl. + * Written by Alexander Komyagin (komya...@altell.ru). + * + */ + +#ifndef OPENSSL_NO_OCSP + +#include <openssl/crypto.h> +#include <openssl/lhash.h> +#include <openssl/buffer.h> +#include <openssl/evp.h> +#include <openssl/asn1.h> +#include <openssl/ocsp.h> +#include <openssl/ssl.h> +#include <stdio.h> + +#ifdef OPENSSL_SYSNAME_WIN32 +# define openssl_fdset(a,b) FD_SET((unsigned int)a, b) +#else +# define openssl_fdset(a,b) FD_SET(a, b) +#endif + +static OCSP_RESPONSE *query_responder(BIO *cbio, char *path, + STACK_OF(CONF_VALUE) *headers, + OCSP_REQUEST *req, int req_timeout) +{ + int fd; + int rv; + int i; + OCSP_REQ_CTX *ctx = NULL; + OCSP_RESPONSE *rsp = NULL; + fd_set confds; + struct timeval tv; + + if (req_timeout != -1) + BIO_set_nbio(cbio, 1); + + rv = BIO_do_connect(cbio); + + if ((rv <= 0) && ((req_timeout == -1) || !BIO_should_retry(cbio))) + { + DBG("Error connecting BIO"); + return NULL; + } + + if (BIO_get_fd(cbio, &fd) <= 0) + { + DBG("Can't get connection fd"); + goto err; + } + + if (req_timeout != -1 && rv <= 0) + { + FD_ZERO(&confds); + openssl_fdset(fd, &confds); + tv.tv_usec = 0; + tv.tv_sec = req_timeout; + rv = select(fd + 1, NULL, (void *)&confds, NULL, &tv); + if (rv == 0) + { + DBG("Timeout on connect"); + return NULL; + } + } + + ctx = OCSP_sendreq_new(cbio, path, NULL, -1); + if (!ctx) + { + DBG("OCSP_sendreq_new failed"); + return NULL; + } + + for (i = 0; i < sk_CONF_VALUE_num(headers); i++) + { + CONF_VALUE *hdr = sk_CONF_VALUE_value(headers, i); + if (!OCSP_REQ_CTX_add1_header(ctx, hdr->name, hdr->value)) + { + DBG("OCSP_REQ_CTX_add1_header failed"); + goto err; + } + } + + if (!OCSP_REQ_CTX_set1_req(ctx, req)) + { + DBG("OCSP_REQ_CTX_set1_req failed"); + goto err; + } + + for (;;) + { + rv = OCSP_sendreq_nbio(&rsp, ctx); + if (rv != -1) + break; + if (req_timeout == -1) + continue; + FD_ZERO(&confds); + openssl_fdset(fd, &confds); + tv.tv_usec = 0; + tv.tv_sec = req_timeout; + if (BIO_should_read(cbio)) + rv = select(fd + 1, (void *)&confds, NULL, NULL, &tv); + else if (BIO_should_write(cbio)) + rv = select(fd + 1, NULL, (void *)&confds, NULL, &tv); + else + { + DBG("Unexpected retry condition"); + goto err; + } + if (rv == 0) + { + DBG("Timeout on request"); + break; + } + if (rv == -1) + { + DBG("Select error"); + break; + } + + } +err: + + if (ctx) + OCSP_REQ_CTX_free(ctx); + + return rsp; +} + +void *ocsp_process_responder(void *_req, + char *host, char *path, char *port, int use_ssl, + void *_headers, + int req_timeout) +{ + BIO *cbio = NULL; + SSL_CTX *ctx = NULL; + OCSP_RESPONSE *resp = NULL; + OCSP_REQUEST *req = (OCSP_REQUEST *)_req; + STACK_OF(CONF_VALUE) *headers = (STACK_OF(CONF_VALUE) *)_headers; + cbio = BIO_new_connect(host); + if (!cbio) + { + DBG("Error creating connect BIO"); + goto end; + } + if (port) BIO_set_conn_port(cbio, port); + if (use_ssl == 1) + { + BIO *sbio; +#if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3) + ctx = SSL_CTX_new(SSLv23_client_method()); +#elif !defined(OPENSSL_NO_SSL3) + ctx = SSL_CTX_new(SSLv3_client_method()); +#elif !defined(OPENSSL_NO_SSL2) + ctx = SSL_CTX_new(SSLv2_client_method()); +#else + DBG("SSL is disabled"); + goto end; +#endif + if (ctx == NULL) + { + DBG("Error creating SSL context"); + goto end; + } + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + sbio = BIO_new_ssl(ctx, 1); + cbio = BIO_push(sbio, cbio); + } + resp = query_responder(cbio, path, headers, req, req_timeout); + if (!resp) + DBG("Error querying OCSP responsder"); + +end: + if (cbio) + BIO_free_all(cbio); + if (ctx) + SSL_CTX_free(ctx); + return (void *)resp; +} + +#endif diff --git a/ssl/ocsp_clnt.h b/ssl/ocsp_clnt.h new file mode 100644 index 0000000..46f2f1e --- /dev/null +++ b/ssl/ocsp_clnt.h @@ -0,0 +1,46 @@ +/* ssl/ocsp_clnt.h */ +/* Copyright (C) 2012 Altell Ltd. (komya...@altell.ru) + * All rights reserved. + * + * This file is a helper to incorporate OCSP check into X509 + * certificate verification process. + * Written by Alexander Komyagin (komya...@altell.ru). + * + */ + + +#ifndef OPENSSL_NO_OCSP + +#ifndef HEADER_OCSP_CLNT_H +#define HEADER_OCSP_CLNT_H + +#ifndef HEADER_X509_H +#include <openssl/x509.h> +#endif + +#include <openssl/ocsp.h> +#include <openssl/x509_vfy_ocsp.h> + +#ifdef OCSP_VFY_DEBUG + #include <syslog.h> + #define DBG(fmt, args...) do { syslog(LOG_INFO, "%s(%d): " fmt, __FUNCTION__, __LINE__, ##args); } while(0) +#else + #define DBG(fmt, args...) do { /* NOP */ } while(0) +#endif //OCSP_VFY_DEBUG + +#ifdef __cplusplus +extern "C" { +#endif + +void *ocsp_process_responder(void *_req, + char *host, char *path, char *port, int use_ssl, + void *_headers, + int req_timeout); + +#ifdef __cplusplus +} +#endif + +#endif //HEADER_OCSP_CLNT_H + +#endif //OPENSSL_NO_OCSP diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index f82d071..4808f62 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -160,6 +160,10 @@ #include <openssl/engine.h> #endif +#ifndef OPENSSL_NO_OCSP +#include <openssl/ocsp_clnt.h> +#endif + const char *SSL_version_str=OPENSSL_VERSION_TEXT; SSL3_ENC_METHOD ssl3_undef_enc_method={ @@ -1758,6 +1762,9 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) if (ret->sessions == NULL) goto err; ret->cert_store=X509_STORE_new(); if (ret->cert_store == NULL) goto err; +#ifndef OPENSSL_NO_OCSP + X509_STORE_set_ocsp_process_resp(ret->cert_store, &ocsp_process_responder); +#endif ssl_create_cipher_list(ret->method, &ret->cipher_list,&ret->cipher_list_by_id,