From: Alex Bergmann Date: Mon, 22 Nov 2010 08:42:54 +0000 (+0100) Subject: OCSP Support X-Git-Tag: release_2_1_11~205 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b6aaecfb83aad6d34c65209e9bc38b552cd0b92f;p=thirdparty%2Ffreeradius-server.git OCSP Support --- diff --git a/raddb/eap.conf b/raddb/eap.conf index 941ebe9b897..03b28bc9de8 100644 --- a/raddb/eap.conf +++ b/raddb/eap.conf @@ -381,6 +381,41 @@ # returns. # client = "/path/to/openssl verify -CApath ${..CA_path} %{TLS-Client-Cert-Filename}" } + + # + # OCSP Configuration + # Certificates can be verified against an OCSP + # Responder. This makes it possible to immediately + # revoke certificates without the distribution of + # new Certificate Revokation Lists (CRLs). + # + ocsp { + # + # Enable it. The default is "no". + # Deleting the entire "ocsp" subsection + # Also disables caching. + # + check_ocsp = no + + # + # The OCSP Responder URL can be automatically + # extracted from the certificate in question. + # To override the OCSP Responder URL set + # "define_ocsp_responder = yes". + # + define_ocsp_responder = yes + + # + # If the OCSP Responder address is not extracted + # from the certificate, the URL can be defined here. + # + # Limitation: Currently the HTTP Request is not + # sending the "Host: " information to the web-server. + # This can be a problem if the OCSP Responder is + # running as a vhost. + # + ocsp_url = "http://127.0.0.1/ocsp/" + } } # The TTLS module implements the EAP-TTLS protocol, diff --git a/src/modules/rlm_eap/libeap/cb.c b/src/modules/rlm_eap/libeap/cb.c index ce54f896bc7..f7a0bebc488 100644 --- a/src/modules/rlm_eap/libeap/cb.c +++ b/src/modules/rlm_eap/libeap/cb.c @@ -136,6 +136,7 @@ int cbtls_password(char *buf, */ int eaptls_handle_idx = -1; int eaptls_conf_idx = -1; +int eaptls_store_idx = -1; /* OCSP Store */ int eaptls_session_idx = -1; #endif /* !defined(NO_OPENSSL) */ diff --git a/src/modules/rlm_eap/libeap/eap_tls.h b/src/modules/rlm_eap/libeap/eap_tls.h index f661562a53d..ff3098e566f 100644 --- a/src/modules/rlm_eap/libeap/eap_tls.h +++ b/src/modules/rlm_eap/libeap/eap_tls.h @@ -371,6 +371,7 @@ void session_init(tls_session_t *ssn); /* SSL Indicies for ex data */ extern int eaptls_handle_idx; extern int eaptls_conf_idx; +extern int eaptls_store_idx; /* OCSP Store */ extern int eaptls_session_idx; #endif /*_EAP_TLS_H*/ diff --git a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c index cb4c8464873..0e8829c1a0a 100644 --- a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c +++ b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c @@ -43,6 +43,9 @@ RCSID("$Id$") #include #endif +/* OCSP Header */ +#include + static CONF_PARSER cache_config[] = { { "enable", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, session_cache_enable), NULL, "no" }, @@ -63,6 +66,16 @@ static CONF_PARSER verify_config[] = { { NULL, -1, 0, NULL, NULL } /* end the list */ }; +static CONF_PARSER ocsp_config[] = { + { "check_ocsp", PW_TYPE_BOOLEAN, + offsetof(EAP_TLS_CONF, check_ocsp), NULL, "no"}, + { "define_ocsp_responder", PW_TYPE_BOOLEAN, + offsetof(EAP_TLS_CONF, define_ocsp_responder), NULL, "no"}, + { "ocsp_url", PW_TYPE_FILENAME, + offsetof(EAP_TLS_CONF, ocsp_url), NULL, NULL }, + { NULL, -1, 0, NULL, NULL } /* end the list */ +}; + static CONF_PARSER module_config[] = { { "rsa_key_exchange", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, rsa_key), NULL, "no" }, @@ -109,6 +122,8 @@ static CONF_PARSER module_config[] = { { "verify", PW_TYPE_SUBSECTION, 0, NULL, (const void *) verify_config }, + { "ocsp", PW_TYPE_SUBSECTION, 0, NULL, (const void *) ocsp_config }, + { NULL, -1, 0, NULL, NULL } /* end the list */ }; @@ -223,6 +238,124 @@ static SSL_SESSION *cbtls_get_session(UNUSED SSL *s, return NULL; } +/* + * This function extracts the OCSP Responder URL + * from an existing x509 certificate. + */ +int ocsp_parse_cert_url(X509 *cert, char **phost, char **pport, char **ppath, int *pssl) +{ + int i; + + AUTHORITY_INFO_ACCESS *aia; + ACCESS_DESCRIPTION *ad; + + aia = X509_get_ext_d2i(cert, NID_info_access, NULL, NULL); + + for (i = 0; i < sk_ACCESS_DESCRIPTION_num(aia); i++) { + ad = sk_ACCESS_DESCRIPTION_value(aia, 0); + if (OBJ_obj2nid(ad->method) == NID_ad_OCSP) { + if (ad->location->type == GEN_URI) { + if(OCSP_parse_url(ad->location->d.ia5->data, + phost, pport, ppath, pssl)) + return 1; + } + } + } + return 0; +} + +/* + * This function sends a OCSP request to a defined OCSP responder + * and checks the OCSP response for correctness. + */ +int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert, EAP_TLS_CONF *conf) +{ + OCSP_CERTID *certid; + OCSP_REQUEST *req; + OCSP_RESPONSE *resp; + OCSP_BASICRESP *bresp = NULL; + char *host = NULL; + char *port = NULL; + char *path = NULL; + int use_ssl = -1; + BIO *cbio; + int ocsp_ok; + int status; + + /* + * Create OCSP Request + */ + certid = OCSP_cert_to_id(NULL, client_cert, issuer_cert); + req = OCSP_REQUEST_new(); + OCSP_request_add0_id(req, certid); + OCSP_request_add1_nonce(req, NULL, 8); + + /* + * Send OCSP Request and get OCSP Response + */ + + /* Get OCSP responder URL */ + if(conf->define_ocsp_responder) { + OCSP_parse_url(conf->ocsp_url, &host, &port, &path, &use_ssl); + } + else { + ocsp_parse_cert_url(client_cert, &host, &port, &path, &use_ssl); + } + + DEBUG2("[ocsp] --> Resonder URL = http://%s:%s%s", host, port, path); + + /* Setup BIO socket to OCSP responder */ + cbio = BIO_new_connect(host); + BIO_set_conn_port(cbio, port); + BIO_do_connect(cbio); + + /* Send OCSP request and wait for response */ + resp = OCSP_sendreq_bio(cbio, path, req); + if(resp==0) { + radlog(L_ERR, "Error: Couldn't get OCSP response"); + ocsp_ok = 0; + goto ocsp_end; + } + + /* Verify OCSP response */ + status = OCSP_response_status(resp); + if(status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + radlog(L_ERR, "Error: OCSP response status: %s", OCSP_response_status_str(status)); + ocsp_ok = 0; + goto ocsp_end; + } + bresp = OCSP_response_get1_basic(resp); + if(OCSP_check_nonce(req, bresp)!=1) { + radlog(L_ERR, "Error: OCSP response has wrong nonce value"); + ocsp_ok = 0; + goto ocsp_end; + } + if(OCSP_basic_verify(bresp, NULL, store, 0)!=1){ + radlog(L_ERR, "Error: Couldn't verify OCSP basic response"); + ocsp_ok = 0; + goto ocsp_end; + } + + ocsp_ok = 1; + +ocsp_end: + /* Free OCSP Stuff */ + OCSP_REQUEST_free(req); + OCSP_RESPONSE_free(resp); + free(host); + free(port); + free(path); + BIO_free_all(cbio); + OCSP_BASICRESP_free(bresp); + + if(ocsp_ok) + DEBUG2("[ocsp] --> Certificate is valid!"); + else + DEBUG2("[ocsp] --> Certificate has been expired/revoked!"); + + return ocsp_ok; +} + /* * For creating certificate attributes. */ @@ -274,11 +407,13 @@ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) char buf[64]; EAP_HANDLER *handler = NULL; X509 *client_cert; + X509 *issuer_cert; SSL *ssl; int err, depth, lookup; EAP_TLS_CONF *conf; int my_ok = ok; REQUEST *request; + X509_STORE *ocsp_store = NULL; ASN1_INTEGER *sn = NULL; ASN1_TIME *asn_time = NULL; @@ -302,6 +437,7 @@ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) handler = (EAP_HANDLER *)SSL_get_ex_data(ssl, 0); request = handler->request; conf = (EAP_TLS_CONF *)SSL_get_ex_data(ssl, 1); + ocsp_store = (X509_STORE *)SSL_get_ex_data(ssl, 2); /* * Get the Serial Number @@ -433,6 +569,12 @@ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) } } } /* check_cert_cn */ + if (my_ok && conf->check_ocsp){ + RDEBUG2("--> Starting OCSP Request"); + if(X509_STORE_CTX_get1_issuer(&issuer_cert, ctx, client_cert)!=1) + radlog(L_ERR, "Error: Couldn't get issuer_cert for %s", common_name); + my_ok = ocsp_check(ocsp_store, issuer_cert, client_cert, conf); + } while (conf->verify_client_cert_cmd) { char filename[256]; @@ -515,6 +657,33 @@ static void eaptls_session_free(UNUSED void *parent, void *data_ptr, pairfree(&vp); } +/* + * Create Global X509 revocation store and use it to verify + * OCSP responses + * + * - Load the trusted CAs + * - Load the trusted issuer certificates + */ +static X509_STORE *init_revocation_store(EAP_TLS_CONF *conf) +{ + X509_STORE *store = NULL; + + store = X509_STORE_new(); + + /* Load the CAs we trust */ + if (conf->ca_file || conf->ca_path) + if(!X509_STORE_load_locations(store, conf->ca_file, conf->ca_path)) { + radlog(L_ERR, "rlm_eap: X509_STORE error %s", ERR_error_string(ERR_get_error(), NULL)); + radlog(L_ERR, "rlm_eap_tls: Error reading Trusted root CA list %s",conf->ca_file ); + return NULL; + } + +#ifdef X509_V_FLAG_CRL_CHECK + if (conf->check_crl) + X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK); +#endif + return store; +} /* * Create Global context SSL and use it in every new session @@ -817,6 +986,11 @@ static SSL_CTX *init_tls_ctx(EAP_TLS_CONF *conf) NULL, NULL, NULL); } + if (eaptls_store_idx < 0) { + eaptls_store_idx = SSL_get_ex_new_index(0, "eaptls_store_idx", + NULL, NULL, NULL); + } + if (eaptls_session_idx < 0) { eaptls_session_idx = SSL_get_ex_new_index(0, "eaptls_session_idx", NULL, NULL, @@ -847,6 +1021,9 @@ static int eaptls_detach(void *arg) if (inst->ctx) SSL_CTX_free(inst->ctx); inst->ctx = NULL; + if (inst->store) X509_STORE_free(inst->store); + inst->store = NULL; + free(inst); return 0; @@ -945,6 +1122,15 @@ static int eaptls_attach(CONF_SECTION *cs, void **instance) return -1; } + /* + * Initialize OCSP Revocation Store + */ + inst->store = init_revocation_store(conf); + if (inst->store == NULL) { + eaptls_detach(inst); + return -1; + } + if (load_dh_params(inst->ctx, conf->dh_file) < 0) { eaptls_detach(inst); return -1; @@ -1076,6 +1262,7 @@ static int eaptls_initiate(void *type_arg, EAP_HANDLER *handler) */ SSL_set_ex_data(ssn->ssl, 0, (void *)handler); SSL_set_ex_data(ssn->ssl, 1, (void *)inst->conf); + SSL_set_ex_data(ssn->ssl, 2, (void *)inst->store); ssn->length_flag = inst->conf->include_length;