From: Arran Cudbard-Bell Date: Thu, 17 Jun 2021 17:57:08 +0000 (-0500) Subject: Automatically re-validate certificates when resuming from a session-ticket X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3ad396eb5086229c9c1d778445e806edcb7e85dc;p=thirdparty%2Ffreeradius-server.git Automatically re-validate certificates when resuming from a session-ticket --- diff --git a/share/dictionary/tls/dictionary.freeradius b/share/dictionary/tls/dictionary.freeradius index 600a47e7202..79f85386851 100644 --- a/share/dictionary/tls/dictionary.freeradius +++ b/share/dictionary/tls/dictionary.freeradius @@ -10,10 +10,18 @@ ATTRIBUTE Packet-Type 1 uint32 -VALUE Packet-Type Cache-Load 1 -VALUE Packet-Type Cache-Store 2 -VALUE Packet-Type Cache-Clear 3 -VALUE Packet-Type Certificate 4 +VALUE Packet-Type Session-Load 1 +VALUE Packet-Type Session-Store 2 +VALUE Packet-Type Session-Clear 3 +VALUE Packet-Type Certificate-Validate 4 + +# +# We use response packet types instead of rcodes so that +# the virtual server can be tested in isolation. +# +VALUE Packet-Type success 10 +VALUE Packet-Type failure 11 +VALUE Packet-Type notfound 12 ATTRIBUTE Session-Id 10 octets ATTRIBUTE Session-Data 11 octets diff --git a/src/lib/eap/attrs.h b/src/lib/eap/attrs.h index efe0495f4f5..1eafcf6088e 100644 --- a/src/lib/eap/attrs.h +++ b/src/lib/eap/attrs.h @@ -30,6 +30,7 @@ extern fr_dict_t const *dict_freeradius; extern fr_dict_t const *dict_radius; extern fr_dict_attr_t const *attr_chbind_response_code; +extern fr_dict_attr_t const *attr_eap_identity; extern fr_dict_attr_t const *attr_eap_session_id; extern fr_dict_attr_t const *attr_eap_type; extern fr_dict_attr_t const *attr_virtual_server; diff --git a/src/lib/eap/base.c b/src/lib/eap/base.c index 637cf8eb8d1..24d776b2bae 100644 --- a/src/lib/eap/base.c +++ b/src/lib/eap/base.c @@ -81,6 +81,7 @@ fr_dict_autoload_t eap_base_dict[] = { fr_dict_attr_t const *attr_chbind_response_code; fr_dict_attr_t const *attr_eap_session_id; +fr_dict_attr_t const *attr_eap_identity; fr_dict_attr_t const *attr_eap_type; fr_dict_attr_t const *attr_virtual_server; @@ -98,6 +99,7 @@ fr_dict_attr_t const *attr_user_name; extern fr_dict_attr_autoload_t eap_base_dict_attr[]; fr_dict_attr_autoload_t eap_base_dict_attr[] = { { .out = &attr_chbind_response_code, .name = "Chbind-Response-Code", .type = FR_TYPE_UINT32, .dict = &dict_freeradius }, + { .out = &attr_eap_identity, .name = "EAP-Identity", .type = FR_TYPE_STRING, .dict = &dict_freeradius }, { .out = &attr_eap_session_id, .name = "EAP-Session-Id", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius }, { .out = &attr_eap_type, .name = "EAP-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius }, { .out = &attr_state, .name = "State", .type = FR_TYPE_OCTETS, .dict = &dict_radius }, diff --git a/src/lib/eap/tls.c b/src/lib/eap/tls.c index 6f4d97f0458..33e1d7f2314 100644 --- a/src/lib/eap/tls.c +++ b/src/lib/eap/tls.c @@ -1176,7 +1176,30 @@ eap_tls_session_t *eap_tls_session_init(request_t *request, eap_session_t *eap_s */ eap_tls_session->tls_session = tls_session = fr_tls_session_alloc_server(eap_tls_session, ssl_ctx, request, client_cert); - if (!tls_session) return NULL; + if (unlikely(!tls_session)) return NULL; + + /* + * Add the EAP-Identity value to the TLS session so + * it's available in all the TLS callbacks. + */ + { + fr_pair_t *identity; + + MEM(identity = fr_pair_afrom_da(tls_session, attr_eap_identity)); + fr_pair_value_bstrdup_buffer(identity, eap_session->identity, true); + fr_tls_session_extra_pair_add_shallow(tls_session, identity); + } + + /* + * Add the EAP-Type we're running to the subrequest. + */ + { + fr_pair_t *type_vp; + + MEM(type_vp = fr_pair_afrom_da(tls_session, attr_eap_type)); + type_vp->vp_uint32 = eap_session->type; + fr_tls_session_extra_pair_add_shallow(tls_session, type_vp); + } /* * Associate various bits of opaque data with the session. diff --git a/src/lib/tls/all.mk b/src/lib/tls/all.mk index fdd5bc5dc86..2910cabb621 100644 --- a/src/lib/tls/all.mk +++ b/src/lib/tls/all.mk @@ -12,6 +12,7 @@ SOURCES := \ engine.c \ log.c \ ocsp.c \ + pairs.c \ session.c \ utils.c \ validate.c \ diff --git a/src/lib/tls/attrs.h b/src/lib/tls/attrs.h index 1b1b41f986e..2a49c87370a 100644 --- a/src/lib/tls/attrs.h +++ b/src/lib/tls/attrs.h @@ -68,7 +68,11 @@ extern fr_dict_attr_t const *attr_tls_session_id; extern fr_dict_attr_t const *attr_framed_mtu; -extern fr_value_box_t const *enum_tls_packet_type_cache_load; -extern fr_value_box_t const *enum_tls_packet_type_cache_store; -extern fr_value_box_t const *enum_tls_packet_type_cache_clear; -extern fr_value_box_t const *enum_tls_packet_type_certificate; +extern fr_value_box_t const *enum_tls_packet_type_session_load; +extern fr_value_box_t const *enum_tls_packet_type_session_store; +extern fr_value_box_t const *enum_tls_packet_type_session_clear; +extern fr_value_box_t const *enum_tls_packet_type_certificate_validate; + +extern fr_value_box_t const *enum_tls_packet_type_success; +extern fr_value_box_t const *enum_tls_packet_type_failure; +extern fr_value_box_t const *enum_tls_packet_type_notfound; diff --git a/src/lib/tls/base-h b/src/lib/tls/base-h index c584230ec10..46d82c63831 100644 --- a/src/lib/tls/base-h +++ b/src/lib/tls/base-h @@ -193,13 +193,6 @@ int fr_tls_ocsp_state_cache_compile(fr_tls_cache_t *sections, CONF_SECTION *ser int fr_tls_ocsp_staple_cache_compile(fr_tls_cache_t *sections, CONF_SECTION *server_cs); -/* - * tls/validate.c - */ -int fr_tls_validate_cert_cb(int ok, X509_STORE_CTX *ctx); - -int fr_tls_validate_client_cert_chain(SSL *ssl); - /* * tls/utils.c */ @@ -212,8 +205,8 @@ int fr_tls_utils_asn1time_to_epoch(time_t *out, ASN1_TIME const *asn1); /* * tls/virtual_server.c */ -unlang_action_t fr_tls_call_push(rlm_rcode_t *p_rcode, request_t *child, unlang_function_t resume, - request_t *request, fr_tls_conf_t *conf, fr_tls_session_t *tls_session); +unlang_action_t fr_tls_call_push(request_t *child, unlang_function_t resume, + fr_tls_conf_t *conf, fr_tls_session_t *tls_session); #ifdef __cplusplus } diff --git a/src/lib/tls/base.c b/src/lib/tls/base.c index bbaf42661aa..b790bf89dbe 100644 --- a/src/lib/tls/base.c +++ b/src/lib/tls/base.c @@ -143,17 +143,31 @@ fr_dict_attr_autoload_t tls_dict_attr[] = { { NULL } }; -fr_value_box_t const *enum_tls_packet_type_cache_load; -fr_value_box_t const *enum_tls_packet_type_cache_store; -fr_value_box_t const *enum_tls_packet_type_cache_clear; -fr_value_box_t const *enum_tls_packet_type_certificate; +/* + * request types + */ +fr_value_box_t const *enum_tls_packet_type_session_load; +fr_value_box_t const *enum_tls_packet_type_session_store; +fr_value_box_t const *enum_tls_packet_type_session_clear; +fr_value_box_t const *enum_tls_packet_type_certificate_validate; + +/* + * response types + */ +fr_value_box_t const *enum_tls_packet_type_success; +fr_value_box_t const *enum_tls_packet_type_failure; +fr_value_box_t const *enum_tls_packet_type_notfound; extern fr_dict_enum_autoload_t tls_dict_enum[]; fr_dict_enum_autoload_t tls_dict_enum[] = { - { .out = &enum_tls_packet_type_cache_load, .name = "Cache-Load", .attr = &attr_tls_packet_type }, - { .out = &enum_tls_packet_type_cache_store, .name = "Cache-Store", .attr = &attr_tls_packet_type }, - { .out = &enum_tls_packet_type_cache_clear, .name = "Cache-Clear", .attr = &attr_tls_packet_type }, - { .out = &enum_tls_packet_type_certificate, .name = "Certificate", .attr = &attr_tls_packet_type }, + { .out = &enum_tls_packet_type_session_load, .name = "Session-Load", .attr = &attr_tls_packet_type }, + { .out = &enum_tls_packet_type_session_store, .name = "Session-Store", .attr = &attr_tls_packet_type }, + { .out = &enum_tls_packet_type_session_clear, .name = "Session-Clear", .attr = &attr_tls_packet_type }, + { .out = &enum_tls_packet_type_certificate_validate, .name = "Certificate-Validate", .attr = &attr_tls_packet_type }, + + { .out = &enum_tls_packet_type_success, .name = "Success", .attr = &attr_tls_packet_type }, + { .out = &enum_tls_packet_type_failure, .name = "Failure", .attr = &attr_tls_packet_type }, + { .out = &enum_tls_packet_type_notfound, .name = "Notfound", .attr = &attr_tls_packet_type }, { NULL } }; diff --git a/src/lib/tls/cache.c b/src/lib/tls/cache.c index bca743a422b..3a50ec0516a 100644 --- a/src/lib/tls/cache.c +++ b/src/lib/tls/cache.c @@ -21,6 +21,7 @@ * @brief Functions to support TLS session resumption * * @copyright 2015-2016 The FreeRADIUS server project + * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org) */ RCSID("$Id$") USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ @@ -39,6 +40,7 @@ USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ #include "cache.h" #include "log.h" #include "missing.h" +#include "validate.h" /** Retrieve session ID (in binary form) from the session * @@ -104,32 +106,148 @@ void tls_cache_clear_state_reset(fr_tls_cache_t *cache) cache->clear.state = FR_TLS_CACHE_CLEAR_INIT; } -/** Process the result of `cache store { ... }` +/** Process the result of `session load { ... }` + */ +static unlang_action_t tls_cache_load_result(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, + request_t *request, void *uctx) +{ + fr_tls_session_t *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t); + fr_tls_cache_t *tls_cache = tls_session->cache; + fr_pair_t *vp; + uint8_t const *q, **p; + SSL_SESSION *sess; + + vp = fr_pair_find_by_da(&request->reply_pairs, attr_tls_packet_type, 0); + if (!vp || (vp->vp_uint32 != enum_tls_packet_type_success->vb_uint32)) { + RWDEBUG("Failed acquiring session data"); + error: + tls_cache->load.state = FR_TLS_CACHE_LOAD_FAILED; + return UNLANG_ACTION_CALCULATE_RESULT; + } + + vp = fr_pair_find_by_da(&request->session_state_pairs, attr_tls_session_data, 0); + if (!vp) { + RWDEBUG("No cached session found"); + goto error; + } + + q = vp->vp_octets; /* openssl will mutate q, so we can't use vp_octets directly */ + p = (unsigned char const **)&q; + + sess = d2i_SSL_SESSION(NULL, p, vp->vp_length); + if (!sess) { + fr_tls_log_error(request, "Failed loading persisted session"); + goto error; + } + RDEBUG3("Read %zu bytes of session data. Session deserialized successfully", vp->vp_length); + + /* + * OpenSSL's API is very inconsistent. + * + * We need to set external data here, so it can be + * retrieved in fr_tls_cache_delete. + * + * ex_data is not serialised in i2d_SSL_SESSION + * so we don't have to bother unsetting it. + */ + SSL_SESSION_set_ex_data(sess, FR_TLS_EX_INDEX_TLS_SESSION, + SSL_get_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_TLS_SESSION)); + + tls_cache->load.state = FR_TLS_CACHE_LOAD_RETRIEVED; + tls_cache->load.sess = sess; /* This is consumed in tls_cache_load_cb */ + +#if 0 + /* + * We set the session here temporarily so that + * the certificate validation can run. + * + * FIXME - Do ASYNC session cert validation. + */ + SSL_set_session(ssl, sess); /* Increases ref count */ + + if (fr_tls_validate_client_cert_chain(ssl) != 1) { + RWDEBUG("Validation failed, forcefully expiring resumed session"); + SSL_SESSION_set_timeout(sess, 0); + } + + SSL_remove_session(ssl, sess); /* Decreases ref count */ +#endif + + return UNLANG_ACTION_CALCULATE_RESULT; +} + +/** Push a `session load { ... }` call into the current request, using a subrequest + * + * @param[in] request The current request. + * @Param[in] tls_session The current TLS session. + * @return + * - UNLANG_ACTION_CALCULATE_RESULT on noop. + * - UNLANG_ACTION_PUSHED_CHILD on success. + * - UNLANG_ACTION_FAIL on failure. + */ +static unlang_action_t tls_cache_load_push(request_t *request, fr_tls_session_t *tls_session) +{ + fr_tls_cache_t *tls_cache = tls_session->cache; + fr_tls_conf_t *conf = fr_tls_session_conf(tls_session->ssl); + request_t *child; + fr_pair_t *vp; + + if (tls_cache->load.state != FR_TLS_CACHE_LOAD_REQUESTED) return UNLANG_ACTION_CALCULATE_RESULT; + + fr_assert(tls_cache->load.id); + + MEM(child = unlang_subrequest_alloc(request, dict_tls)); + request = child; + + /* + * Setup the child request for loading + * session resumption data. + */ + MEM(pair_prepend_request(&vp, attr_tls_packet_type) >= 0); + vp->vp_uint32 = enum_tls_packet_type_session_load->vb_uint32; + + /* + * Add the session identifier we're + * trying to load. + */ + tls_cache_session_id_to_vp(child, tls_cache->load.id); + + /* + * Allocate a child, and set it up to call + * the TLS virtual server. + */ + if (fr_tls_call_push(child, tls_cache_load_result, conf, tls_session) < 0) { + talloc_free(child); + tls_cache_load_state_reset(tls_cache); + return UNLANG_ACTION_FAIL; + } + + return UNLANG_ACTION_CALCULATE_RESULT; +} + +/** Process the result of `session store { ... }` */ static unlang_action_t tls_cache_store_result(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx) { fr_tls_session_t *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t); fr_tls_cache_t *tls_cache = tls_session->cache; + fr_pair_t *vp; tls_cache_store_state_reset(tls_cache); - switch (tls_cache->store.rcode) { - case RLM_MODULE_OK: - case RLM_MODULE_UPDATED: + vp = fr_pair_find_by_da(&request->reply_pairs, attr_tls_packet_type, 0); + if (vp && (vp->vp_uint32 == enum_tls_packet_type_success->vb_uint32)) { tls_cache->store.state = FR_TLS_CACHE_STORE_PERSISTED; /* Avoid spurious clear calls */ - break; - - default: /* Every other rcode indicates the cache store ailed */ + } else { RWDEBUG("Failed storing session data"); tls_cache->store.state = FR_TLS_CACHE_STORE_INIT; - break; } return UNLANG_ACTION_CALCULATE_RESULT; } -/** Push a `cache store { ... }` call into the current request, using a subrequest +/** Push a `session store { ... }` call into the current request, using a subrequest * * @param[in] request The current request. * @param[in] conf TLS configuration. @@ -162,7 +280,7 @@ unlang_action_t tls_cache_store_push(request_t *request, fr_tls_conf_t *conf, fr * session resumption data. */ MEM(pair_prepend_request(&vp, attr_tls_packet_type) >= 0); - vp->vp_uint32 = enum_tls_packet_type_cache_store->vb_uint32; + vp->vp_uint32 = enum_tls_packet_type_session_store->vb_uint32; /* * Add the session identifier we're trying @@ -202,36 +320,34 @@ unlang_action_t tls_cache_store_push(request_t *request, fr_tls_conf_t *conf, fr * Allocate a child, and set it up to call * the TLS virtual server. */ - if (fr_tls_call_push(&tls_cache->store.rcode, child, tls_cache_store_result, - request, conf, tls_session) < 0) goto error; + if (fr_tls_call_push(child, tls_cache_store_result, conf, tls_session) < 0) goto error; return UNLANG_ACTION_PUSHED_CHILD; } -/** Process the result of `cache clear { ... }` +/** Process the result of `session clear { ... }` */ static unlang_action_t tls_cache_clear_result(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx) { fr_tls_session_t *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t); fr_tls_cache_t *tls_cache = tls_session->cache; + fr_pair_t *vp; tls_cache_clear_state_reset(tls_cache); - switch (tls_cache->clear.rcode) { - case RLM_MODULE_OK: - case RLM_MODULE_UPDATED: - case RLM_MODULE_NOTFOUND: - case RLM_MODULE_NOOP: - return UNLANG_ACTION_CALCULATE_RESULT; - - default: - RWDEBUG("Failed deleting session data - security may be compromised"); + vp = fr_pair_find_by_da(&request->reply_pairs, attr_tls_packet_type, 0); + if (vp && + ((vp->vp_uint32 == enum_tls_packet_type_success->vb_uint32) || + (vp->vp_uint32 == enum_tls_packet_type_notfound->vb_uint32))) { return UNLANG_ACTION_CALCULATE_RESULT; } + + RWDEBUG("Failed deleting session data - security may be compromised"); + return UNLANG_ACTION_CALCULATE_RESULT; } -/** Push a `cache clear { ... }` call into the current request, using a subrequest +/** Push a `session clear { ... }` call into the current request, using a subrequest * * @param[in] request The current request. * @param[in] conf TLS configuration. @@ -268,7 +384,7 @@ unlang_action_t tls_cache_clear_push(request_t *request, fr_tls_conf_t *conf, fr * session resumption data. */ MEM(pair_prepend_request(&vp, attr_tls_packet_type) >= 0); - vp->vp_uint32 = enum_tls_packet_type_cache_clear->vb_uint32; + vp->vp_uint32 = enum_tls_packet_type_session_clear->vb_uint32; /* * Add the session identifier we're @@ -280,8 +396,7 @@ unlang_action_t tls_cache_clear_push(request_t *request, fr_tls_conf_t *conf, fr * Allocate a child, and set it up to call * the TLS virtual server. */ - if (fr_tls_call_push(&tls_cache->clear.rcode, child, tls_cache_clear_result, - request, conf, tls_session) < 0) { + if (fr_tls_call_push(child, tls_cache_clear_result, conf, tls_session) < 0) { talloc_free(child); tls_cache_clear_state_reset(tls_cache); return UNLANG_ACTION_FAIL; @@ -290,21 +405,27 @@ unlang_action_t tls_cache_clear_push(request_t *request, fr_tls_conf_t *conf, fr return UNLANG_ACTION_CALCULATE_RESULT; } -/** Push a `cache clear { ... }` or `cache load { ... }` depending on what operations are pending +/** Push a `session store { ... }` or session clear { ... }` or `session load { ... }` depending on what operations are pending * * @param[in] request The current request. * @Param[in] tls_session The current TLS session. * @return - * - 1 on noop. - * - 0 on success. - * - -1 on failure. + * - UNLANG_ACTION_CALCULATE_RESULT - No pending actions + * - UNLANG_ACTION_PUSHED_CHILD - Pending operations to evaluate. */ -int fr_tls_cache_pending_push(request_t *request, fr_tls_session_t *tls_session) +unlang_action_t fr_tls_cache_pending_push(request_t *request, fr_tls_session_t *tls_session) { fr_tls_cache_t *tls_cache = tls_session->cache; fr_tls_conf_t *conf = fr_tls_session_conf(tls_session->ssl); - if (!tls_cache) return 1; /* No caching allowed */ + if (!tls_cache) return UNLANG_ACTION_CALCULATE_RESULT; /* No caching allowed */ + + /* + * Load stateful session data + */ + if (tls_cache->load.state == FR_TLS_CACHE_LOAD_REQUESTED) { + return tls_cache_load_push(request, tls_session); + } /* * We only support a single session @@ -322,130 +443,6 @@ int fr_tls_cache_pending_push(request_t *request, fr_tls_session_t *tls_session) return tls_cache_store_push(request, conf, tls_session); } - return 1; -} - -/** Process the result of `cache load { ... }` - */ -static unlang_action_t tls_cache_load_result(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, - request_t *request, void *uctx) -{ - fr_tls_session_t *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t); - fr_tls_cache_t *tls_cache = tls_session->cache; - fr_pair_t *vp; - uint8_t const *q, **p; - SSL_SESSION *sess; - - switch (tls_cache->load.rcode) { - case RLM_MODULE_OK: - case RLM_MODULE_UPDATED: - break; - - default: /* Every other rcode indicates the cache load failed */ - RWDEBUG("Failed acquiring session data"); - error: - tls_cache->load.state = FR_TLS_CACHE_LOAD_FAILED; - return UNLANG_ACTION_CALCULATE_RESULT; - } - - vp = fr_pair_find_by_da(&request->session_state_pairs, attr_tls_session_data, 0); - if (!vp) { - RWDEBUG("No cached session found"); - goto error; - } - - q = vp->vp_octets; /* openssl will mutate q, so we can't use vp_octets directly */ - p = (unsigned char const **)&q; - - sess = d2i_SSL_SESSION(NULL, p, vp->vp_length); - if (!sess) { - fr_tls_log_error(request, "Failed loading persisted session"); - goto error; - } - RDEBUG3("Read %zu bytes of session data. Session deserialized successfully", vp->vp_length); - - /* - * OpenSSL's API is very inconsistent. - * - * We need to set external data here, so it can be - * retrieved in fr_tls_cache_delete. - * - * ex_data is not serialised in i2d_SSL_SESSION - * so we don't have to bother unsetting it. - */ - SSL_SESSION_set_ex_data(sess, FR_TLS_EX_INDEX_TLS_SESSION, - SSL_get_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_TLS_SESSION)); - - tls_cache->load.state = FR_TLS_CACHE_LOAD_RETRIEVED; - tls_cache->load.sess = sess; /* This is consumed in tls_cache_load_cb */ - -#if 0 - /* - * We set the session here temporarily so that - * the certificate validation can run. - * - * FIXME - Do ASYNC session cert validation. - */ - SSL_set_session(ssl, sess); /* Increases ref count */ - - if (fr_tls_validate_client_cert_chain(ssl) != 1) { - RWDEBUG("Validation failed, forcefully expiring resumed session"); - SSL_SESSION_set_timeout(sess, 0); - } - - SSL_remove_session(ssl, sess); /* Decreases ref count */ -#endif - - return UNLANG_ACTION_CALCULATE_RESULT; -} - -/** Push a `cache load { ... }` call into the current request, using a subrequest - * - * @param[in] request The current request. - * @Param[in] tls_session The current TLS session. - * @return - * - UNLANG_ACTION_CALCULATE_RESULT on noop. - * - UNLANG_ACTION_PUSHED_CHILD on success. - * - UNLANG_ACTION_FAIL on failure. - */ -unlang_action_t fr_tls_cache_load_push(request_t *request, fr_tls_session_t *tls_session) -{ - fr_tls_cache_t *tls_cache = tls_session->cache; - fr_tls_conf_t *conf = fr_tls_session_conf(tls_session->ssl); - request_t *child; - fr_pair_t *vp; - - if (tls_cache->load.state != FR_TLS_CACHE_LOAD_REQUESTED) return UNLANG_ACTION_CALCULATE_RESULT; - - fr_assert(tls_cache->load.id); - - MEM(child = unlang_subrequest_alloc(request, dict_tls)); - request = child; - - /* - * Setup the child request for loading - * session resumption data. - */ - MEM(pair_prepend_request(&vp, attr_tls_packet_type) >= 0); - vp->vp_uint32 = enum_tls_packet_type_cache_load->vb_uint32; - - /* - * Add the session identifier we're - * trying to load. - */ - tls_cache_session_id_to_vp(child, tls_cache->load.id); - - /* - * Allocate a child, and set it up to call - * the TLS virtual server. - */ - if (fr_tls_call_push(&tls_cache->load.rcode, child, tls_cache_load_result, - request, conf, tls_session) < 0) { - talloc_free(child); - tls_cache_load_state_reset(tls_cache); - return UNLANG_ACTION_FAIL; - } - return UNLANG_ACTION_CALCULATE_RESULT; } @@ -534,7 +531,7 @@ static SSL_SESSION *tls_cache_load_cb(SSL *ssl, * load session information from a datastore and associated * it with the SSL session. * 3. We asynchronously validate the certificate information - * retrieved during the session cache load. + * retrieved during the session session load. * 3. We call SSL_read() again, which in turn calls this callback * again. */ @@ -677,7 +674,7 @@ int fr_tls_cache_disable_cb(SSL *ssl, return 1; } - RDEBUG2("Session-resumption enabled"); + RDEBUG2("Allowing future session-resumption"); return 0; } @@ -882,6 +879,7 @@ static SSL_TICKET_RETURN tls_cache_session_ticket_app_data_get(SSL *ssl, SSL_SES void *arg) { fr_tls_session_t *tls_session = fr_tls_session(ssl); + fr_tls_conf_t *conf = fr_tls_session_conf(tls_session->ssl); fr_tls_cache_conf_t *tls_cache_conf = arg; /* Not talloced */ request_t *request = NULL; uint8_t *data; @@ -895,7 +893,7 @@ static SSL_TICKET_RETURN tls_cache_session_ticket_app_data_get(SSL *ssl, SSL_SES if (!tls_session->allow_session_resumption || (!(tls_cache_conf->mode & FR_TLS_CACHE_STATELESS))) { ROPTIONAL(RDEBUG2, DEBUG2, "Session resumption not enabled for this TLS session, " - "denying session resumption"); + "denying session resumption via session-ticket"); return SSL_TICKET_RETURN_IGNORE; } @@ -921,7 +919,7 @@ static SSL_TICKET_RETURN tls_cache_session_ticket_app_data_get(SSL *ssl, SSL_SES */ if (SSL_SESSION_get0_ticket_appdata(sess, (void **)&data, &data_len) != 1) { fr_tls_log_error(request, "Failed retrieving application data from session-ticket, " - "denying session resumption"); + "denying session resumption via session-ticket"); return SSL_TICKET_RETURN_IGNORE_RENEW; } @@ -941,7 +939,7 @@ static SSL_TICKET_RETURN tls_cache_session_ticket_app_data_get(SSL *ssl, SSL_SES if (fr_internal_decode_pair_dbuff(request->session_state_ctx, &cursor, request->dict, &dbuff, NULL) < 0) { fr_pair_list_free(&tmp); - RPEDEBUG("Failed decoding session-state, denying session resumption"); + RPEDEBUG("Failed decoding session-state, denying session resumption via session-ticket"); return SSL_TICKET_RETURN_IGNORE_RENEW; } } @@ -953,9 +951,30 @@ static SSL_TICKET_RETURN tls_cache_session_ticket_app_data_get(SSL *ssl, SSL_SES fr_pair_list_append(&request->session_state_pairs, &tmp); - /* - * TODO - Run TLS certificate recv { ... } section - */ + if (conf->virtual_server && tls_session->verify_client_cert) { + RDEBUG2("Requesting certificate re-validation for session-ticket"); + /* + * This sets the validation state of the tls_session + * so that when we call ASYNC_pause_job(), and execution + * jumps back to tls_session_async_handshake_cont + * (just under SSL_read()) + * the code there knows what job it needs to push onto + * the unlang stack. + */ + fr_tls_validate_client_cert_request(tls_session); + + ASYNC_pause_job(); + + /* + * If we couldn't validate the client certificate + * give the client the opportunity to send a new + * one, but _don't_ allow session resumption. + */ + if (!fr_tls_validate_client_cert_success(tls_session)) { + RDEBUG2("Certificate re-validation failed, denying session resumption via session-ticket"); + return SSL_TICKET_RETURN_IGNORE_RENEW; + } + } return (status == SSL_TICKET_SUCCESS_RENEW) ? SSL_TICKET_RETURN_USE_RENEW : SSL_TICKET_RETURN_USE; } diff --git a/src/lib/tls/cache.h b/src/lib/tls/cache.h index 04623b15a24..648ab0e648e 100644 --- a/src/lib/tls/cache.h +++ b/src/lib/tls/cache.h @@ -75,20 +75,17 @@ typedef struct { struct { fr_tls_cache_store_state_t state; //!< Tracks store state. SSL_SESSION *sess; //!< Session to store. - rlm_rcode_t rcode; //!< Result of calling `cache store { ... }`. } store; struct { fr_tls_cache_load_state_t state; //!< Tracks load requests from OpenSSL. uint8_t *id; //!< Session ID to load. SSL_SESSION *sess; //!< Deserialized session. - rlm_rcode_t rcode; //!< Result of calling `cache load { ... }`. } load; struct { fr_tls_cache_clear_state_t state; //!< Tracks delete requests from OpenSSL. uint8_t *id; //!< Session ID to be deleted. - rlm_rcode_t rcode; //!< Result of calling `cache clear { ... }`. } clear; } fr_tls_cache_t; @@ -106,8 +103,6 @@ uint8_t *fr_tls_cache_id(TALLOC_CTX *ctx, SSL_SESSION *sess); unlang_action_t fr_tls_cache_pending_push(request_t *request, fr_tls_session_t *tls_session); -unlang_action_t fr_tls_cache_load_push(request_t *request, fr_tls_session_t *tls_session); - void fr_tls_cache_deny(fr_tls_session_t *tls_session); int fr_tls_cache_disable_cb(SSL *ssl, int is_forward_secure); diff --git a/src/lib/tls/conf-h b/src/lib/tls/conf-h index e0e69bfe2c1..2fa2afb064e 100644 --- a/src/lib/tls/conf-h +++ b/src/lib/tls/conf-h @@ -109,8 +109,6 @@ typedef struct { uint32_t lifetime; //!< The maximum period a session can be resumed after. - bool verify; //!< Revalidate any sessions read in from the cache. - bool require_extms; //!< Only allow session resumption if the client/server //!< supports the extended master session key. This protects //!< against the triple handshake attack. diff --git a/src/lib/tls/conf.c b/src/lib/tls/conf.c index e942659e267..bfd87c28919 100644 --- a/src/lib/tls/conf.c +++ b/src/lib/tls/conf.c @@ -80,7 +80,6 @@ static CONF_PARSER cache_config[] = { { FR_CONF_OFFSET("name", FR_TYPE_TMPL, fr_tls_cache_conf_t, id_name), .dflt = "%{EAP-Type}%{Virtual-Server}", .quote = T_DOUBLE_QUOTED_STRING }, { FR_CONF_OFFSET("lifetime", FR_TYPE_UINT32, fr_tls_cache_conf_t, lifetime), .dflt = "86400" }, - { FR_CONF_OFFSET("verify", FR_TYPE_BOOL, fr_tls_cache_conf_t, verify), .dflt = "no" }, #if OPENSSL_VERSION_NUMBER >= 0x10100000L { FR_CONF_OFFSET("require_extended_master_secret", FR_TYPE_BOOL, fr_tls_cache_conf_t, require_extms), .dflt = "no" }, diff --git a/src/lib/tls/pairs.c b/src/lib/tls/pairs.c new file mode 100644 index 00000000000..7b2468e831a --- /dev/null +++ b/src/lib/tls/pairs.c @@ -0,0 +1,334 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * + * @file tls/pairs.c + * @brief Functions to convert certificate OIDs to attribute pairs + * + * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org) + */ +RCSID("$Id$") +USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ + +#ifdef WITH_TLS +#define LOG_PREFIX "tls - " + +#include +#include + +#include +#include + +#include "attrs.h" +#include "base.h" +#include "session.h" + +/* + * For creating certificate attributes. + */ +static fr_dict_attr_t const **cert_attr_names[][2] = { + { &attr_tls_client_cert_common_name, &attr_tls_cert_common_name }, + { &attr_tls_client_cert_expiration, &attr_tls_cert_expiration }, + { &attr_tls_client_cert_issuer, &attr_tls_cert_issuer }, + { &attr_tls_client_cert_serial, &attr_tls_cert_serial }, + { &attr_tls_client_cert_subject, &attr_tls_cert_subject }, + { &attr_tls_client_cert_subject_alt_name_dns, &attr_tls_cert_subject_alt_name_dns }, + { &attr_tls_client_cert_subject_alt_name_email, &attr_tls_cert_subject_alt_name_email }, + { &attr_tls_client_cert_subject_alt_name_upn, &attr_tls_cert_subject_alt_name_upn } +}; + +#define IDX_COMMON_NAME (0) +#define IDX_EXPIRATION (1) +#define IDX_ISSUER (2) +#define IDX_SERIAL (3) +#define IDX_SUBJECT (4) +#define IDX_SUBJECT_ALT_NAME_DNS (5) +#define IDX_SUBJECT_ALT_NAME_EMAIL (6) +#define IDX_SUBJECT_ALT_NAME_UPN (7) + +static inline CC_HINT(always_inline) +fr_pair_t *fr_tls_session_cert_attr_add(TALLOC_CTX *ctx, request_t *request, fr_pair_list_t *pair_list, + int attr, int attr_index, char const *value) +{ + fr_pair_t *vp; + fr_dict_attr_t const *da = *(cert_attr_names[attr][attr_index]); + + MEM(vp = fr_pair_afrom_da(ctx, da)); + if (value) { + if (fr_pair_value_from_str(vp, value, -1, '\0', true) < 0) { + RPWDEBUG("Failed creating attribute %s", da->name); + talloc_free(vp); + return NULL; + } + } + RINDENT(); + RDEBUG3("%pP", vp); + REXDENT(); + fr_pair_append(pair_list, vp); + + return vp; +} + +DIAG_OFF(DIAG_UNKNOWN_PRAGMAS) +DIAG_OFF(used-but-marked-unused) /* fix spurious warnings for sk macros */ +/** Extract attributes from an X509 certificate + * + * @param[out] pair_list to copy attributes to. + * @param[in] ctx to allocate attributes in. + * @param[in] tls_session current TLS session. + * @param[in] cert to validate. + * @param[in] depth the certificate is in the certificate chain (0 == leaf). + * @return + * - 0 on success. + * - < 0 on failure. + */ +int fr_tls_session_pairs_from_x509_cert(fr_pair_list_t *pair_list, TALLOC_CTX *ctx, + fr_tls_session_t *tls_session, X509 *cert, int depth) +{ + char buffer[2048]; + char attribute[256]; + char **identity; + int attr_index, loc; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + STACK_OF(X509_EXTENSION) const *ext_list = NULL; +#else + STACK_OF(X509_EXTENSION) *ext_list = NULL; +#endif + + ASN1_INTEGER *sn = NULL; + ASN1_TIME *asn_time = NULL; + + fr_pair_t *vp = NULL; + + request_t *request; + +#define CERT_ATTR_ADD(_attr, _attr_index, _value) fr_tls_session_cert_attr_add(ctx, request, pair_list, _attr, _attr_index, _value) + + attr_index = depth; + if (attr_index > 1) attr_index = 1; + + request = fr_tls_session_request(tls_session->ssl); + identity = (char **)SSL_get_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_IDENTITY); + + if (RDEBUG_ENABLED3) { + buffer[0] = '\0'; + X509_NAME_oneline(X509_get_subject_name(cert), buffer, sizeof(buffer)); + buffer[sizeof(buffer) - 1] = '\0'; + RDEBUG3("Creating attributes for \"%s\":", buffer[0] ? buffer : "Cert missing subject OID"); + } + + /* + * Get the Serial Number + */ + sn = X509_get_serialNumber(cert); + if (sn && ((size_t) sn->length < (sizeof(buffer) / 2))) { + char *p = buffer; + int i; + + for (i = 0; i < sn->length; i++) { + sprintf(p, "%02x", (unsigned int)sn->data[i]); + p += 2; + } + + CERT_ATTR_ADD(IDX_SERIAL, attr_index, buffer); + } + + /* + * Get the Expiration Date + */ + buffer[0] = '\0'; + asn_time = X509_get_notAfter(cert); + if (identity && asn_time && (asn_time->length < (int)sizeof(buffer))) { + time_t expires; + + /* + * Add expiration as a time since the epoch + */ + if (fr_tls_utils_asn1time_to_epoch(&expires, asn_time) < 0) { + RPWDEBUG("Failed parsing certificate expiry time"); + } else { + vp = CERT_ATTR_ADD(IDX_EXPIRATION, attr_index, NULL); + vp->vp_date = fr_unix_time_from_sec(expires); + } + } + + /* + * Get the Subject & Issuer + */ + buffer[0] = '\0'; + X509_NAME_oneline(X509_get_subject_name(cert), buffer, sizeof(buffer)); + buffer[sizeof(buffer) - 1] = '\0'; + if (identity && buffer[0]) { + CERT_ATTR_ADD(IDX_SUBJECT, attr_index, buffer); + + /* + * Get the Common Name, if there is a subject. + */ + X509_NAME_get_text_by_NID(X509_get_subject_name(cert), + NID_commonName, buffer, sizeof(buffer)); + buffer[sizeof(buffer) - 1] = '\0'; + + if (buffer[0]) { + CERT_ATTR_ADD(IDX_COMMON_NAME, attr_index, buffer); + } + } + + X509_NAME_oneline(X509_get_issuer_name(cert), buffer, sizeof(buffer)); + buffer[sizeof(buffer) - 1] = '\0'; + if (identity && buffer[0]) { + CERT_ATTR_ADD(IDX_ISSUER, attr_index, buffer); + } + + /* + * Get the RFC822 Subject Alternative Name + */ + loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, 0); + if (loc >= 0) { + X509_EXTENSION *ext = NULL; + GENERAL_NAMES *names = NULL; + int i; + + ext = X509_get_ext(cert, loc); + if (ext && (names = X509V3_EXT_d2i(ext))) { + for (i = 0; i < sk_GENERAL_NAME_num(names); i++) { + GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i); + + switch (name->type) { +#ifdef GEN_EMAIL + case GEN_EMAIL: { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + char const *rfc822Name = (char const *)ASN1_STRING_get0_data(name->d.rfc822Name); +#else + char *rfc822Name = (char *)ASN1_STRING_data(name->d.rfc822Name); +#endif + + CERT_ATTR_ADD(IDX_SUBJECT_ALT_NAME_EMAIL, attr_index, rfc822Name); + break; + } +#endif /* GEN_EMAIL */ +#ifdef GEN_DNS + case GEN_DNS: { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + char const *dNSName = (char const *)ASN1_STRING_get0_data(name->d.dNSName); +#else + char *dNSName = (char *)ASN1_STRING_data(name->d.dNSName); +#endif + CERT_ATTR_ADD(IDX_SUBJECT_ALT_NAME_DNS, attr_index, dNSName); + break; + } +#endif /* GEN_DNS */ +#ifdef GEN_OTHERNAME + case GEN_OTHERNAME: + /* look for a MS UPN */ + if (NID_ms_upn != OBJ_obj2nid(name->d.otherName->type_id)) break; + + /* we've got a UPN - Must be ASN1-encoded UTF8 string */ + if (name->d.otherName->value->type == V_ASN1_UTF8STRING) { + CERT_ATTR_ADD(IDX_SUBJECT_ALT_NAME_UPN, attr_index, + (char *)name->d.otherName->value->value.utf8string); + break; + } + + RWARN("Invalid UPN in Subject Alt Name (should be UTF-8)"); + break; +#endif /* GEN_OTHERNAME */ + default: + /* XXX TODO handle other SAN types */ + break; + } + } + } + if (names != NULL) GENERAL_NAMES_free(names); + } + + /* + * Only add extensions for the actual client certificate + */ + if (attr_index == 0) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + ext_list = X509_get0_extensions(cert); +#else + ext_list = cert->cert_info->extensions; +#endif + + /* + * Grab the X509 extensions, and create attributes out of them. + * For laziness, we re-use the OpenSSL names + */ + if (sk_X509_EXTENSION_num(ext_list) > 0) { + int i, len; + char *p; + BIO *out; + + MEM(out = BIO_new(BIO_s_mem())); + strlcpy(attribute, "TLS-Client-Cert-", sizeof(attribute)); + + for (i = 0; i < sk_X509_EXTENSION_num(ext_list); i++) { + char value[1024]; + ASN1_OBJECT *obj; + X509_EXTENSION *ext; + fr_dict_attr_t const *da; + + ext = sk_X509_EXTENSION_value(ext_list, i); + + obj = X509_EXTENSION_get_object(ext); + if (i2a_ASN1_OBJECT(out, obj) <= 0) { + RPWDEBUG("Skipping X509 Extension (%i) conversion to attribute. " + "Conversion from ASN1 failed...", i); + continue; + } + + len = BIO_read(out, attribute + 16 , sizeof(attribute) - 16 - 1); + if (len <= 0) continue; + + attribute[16 + len] = '\0'; + + for (p = attribute + 16; *p != '\0'; p++) if (*p == ' ') *p = '-'; + + X509V3_EXT_print(out, ext, 0, 0); + len = BIO_read(out, value , sizeof(value) - 1); + if (len <= 0) continue; + + value[len] = '\0'; + + da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), attribute); + if (!da) { + RWDEBUG3("Skipping attribute %s: " + "Add dictionary definition if you want to access it", attribute); + continue; + } + + MEM(vp = fr_pair_afrom_da(ctx, da)); + if (fr_pair_value_from_str(vp, value, -1, '\0', true) < 0) { + RPWDEBUG3("Skipping: %s += '%s'", attribute, value); + talloc_free(vp); + continue; + } + + fr_pair_append(pair_list, vp); + } + BIO_free_all(out); + } + } + + return 0; +} +DIAG_ON(used-but-marked-unused) +DIAG_ON(DIAG_UNKNOWN_PRAGMAS) +#endif diff --git a/src/lib/tls/session.c b/src/lib/tls/session.c index 6e50dd76947..94256762b96 100644 --- a/src/lib/tls/session.c +++ b/src/lib/tls/session.c @@ -48,29 +48,6 @@ #include "log.h" #include "missing.h" -/* - * For creating certificate attributes. - */ -static fr_dict_attr_t const **cert_attr_names[][2] = { - { &attr_tls_client_cert_common_name, &attr_tls_cert_common_name }, - { &attr_tls_client_cert_expiration, &attr_tls_cert_expiration }, - { &attr_tls_client_cert_issuer, &attr_tls_cert_issuer }, - { &attr_tls_client_cert_serial, &attr_tls_cert_serial }, - { &attr_tls_client_cert_subject, &attr_tls_cert_subject }, - { &attr_tls_client_cert_subject_alt_name_dns, &attr_tls_cert_subject_alt_name_dns }, - { &attr_tls_client_cert_subject_alt_name_email, &attr_tls_cert_subject_alt_name_email }, - { &attr_tls_client_cert_subject_alt_name_upn, &attr_tls_cert_subject_alt_name_upn } -}; - -#define IDX_COMMON_NAME (0) -#define IDX_EXPIRATION (1) -#define IDX_ISSUER (2) -#define IDX_SERIAL (3) -#define IDX_SUBJECT (4) -#define IDX_SUBJECT_ALT_NAME_DNS (5) -#define IDX_SUBJECT_ALT_NAME_EMAIL (6) -#define IDX_SUBJECT_ALT_NAME_UPN (7) - static char const *tls_version_str[] = { [SSL2_VERSION] = "SSL 2.0", [SSL3_VERSION] = "SSL 3.0", @@ -370,7 +347,7 @@ unsigned int fr_tls_session_psk_server_cb(SSL *ssl, const char *identity, { size_t psk_len = 0; fr_tls_conf_t *conf; - request_t *request; + request_t *request; conf = (fr_tls_conf_t *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CONF); if (!conf) return 0; @@ -843,277 +820,6 @@ void fr_tls_session_msg_cb(int write_p, int msg_version, int content_type, #endif } -static inline CC_HINT(always_inline) -fr_pair_t *fr_tls_session_cert_attr_add(TALLOC_CTX *ctx, request_t *request, fr_pair_list_t *pair_list, - int attr, int attr_index, char const *value) -{ - fr_pair_t *vp; - fr_dict_attr_t const *da = *(cert_attr_names[attr][attr_index]); - - MEM(vp = fr_pair_afrom_da(ctx, da)); - if (value) { - if (fr_pair_value_from_str(vp, value, -1, '\0', true) < 0) { - RPWDEBUG("Failed creating attribute %s", da->name); - talloc_free(vp); - return NULL; - } - } - RINDENT(); - RDEBUG3("%pP", vp); - REXDENT(); - fr_pair_append(pair_list, vp); - - return vp; -} - -DIAG_OFF(DIAG_UNKNOWN_PRAGMAS) -DIAG_OFF(used-but-marked-unused) /* fix spurious warnings for sk macros */ -/** Extract attributes from an X509 certificate - * - * @param[out] pair_list to copy attributes to. - * @param[in] ctx to allocate attributes in. - * @param[in] tls_session current TLS session. - * @param[in] cert to validate. - * @param[in] depth the certificate is in the certificate chain (0 == leaf). - * @return - * - 0 on success. - * - < 0 on failure. - */ -int fr_tls_session_pairs_from_x509_cert(fr_pair_list_t *pair_list, TALLOC_CTX *ctx, - fr_tls_session_t *tls_session, X509 *cert, int depth) -{ - char buffer[1024]; - char attribute[256]; - char **identity; - int attr_index, loc; - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - STACK_OF(X509_EXTENSION) const *ext_list = NULL; -#else - STACK_OF(X509_EXTENSION) *ext_list = NULL; -#endif - - ASN1_INTEGER *sn = NULL; - ASN1_TIME *asn_time = NULL; - - fr_pair_t *vp = NULL; - - request_t *request; - -#define CERT_ATTR_ADD(_attr, _attr_index, _value) fr_tls_session_cert_attr_add(ctx, request, pair_list, _attr, _attr_index, _value) - - attr_index = depth; - if (attr_index > 1) attr_index = 1; - - request = fr_tls_session_request(tls_session->ssl); - identity = (char **)SSL_get_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_IDENTITY); - - if (RDEBUG_ENABLED3) { - buffer[0] = '\0'; - X509_NAME_oneline(X509_get_subject_name(cert), buffer, sizeof(buffer)); - buffer[sizeof(buffer) - 1] = '\0'; - RDEBUG3("Creating attributes for \"%s\":", buffer[0] ? buffer : "Cert missing subject OID"); - } - - /* - * Get the Serial Number - */ - sn = X509_get_serialNumber(cert); - if (sn && ((size_t) sn->length < (sizeof(buffer) / 2))) { - char *p = buffer; - int i; - - for (i = 0; i < sn->length; i++) { - sprintf(p, "%02x", (unsigned int)sn->data[i]); - p += 2; - } - - CERT_ATTR_ADD(IDX_SERIAL, attr_index, buffer); - } - - /* - * Get the Expiration Date - */ - buffer[0] = '\0'; - asn_time = X509_get_notAfter(cert); - if (identity && asn_time && (asn_time->length < (int)sizeof(buffer))) { - time_t expires; - - /* - * Add expiration as a time since the epoch - */ - if (fr_tls_utils_asn1time_to_epoch(&expires, asn_time) < 0) { - RPWDEBUG("Failed parsing certificate expiry time"); - } else { - vp = CERT_ATTR_ADD(IDX_EXPIRATION, attr_index, NULL); - vp->vp_date = fr_unix_time_from_sec(expires); - } - } - - /* - * Get the Subject & Issuer - */ - buffer[0] = '\0'; - X509_NAME_oneline(X509_get_subject_name(cert), buffer, sizeof(buffer)); - buffer[sizeof(buffer) - 1] = '\0'; - if (identity && buffer[0]) { - CERT_ATTR_ADD(IDX_SUBJECT, attr_index, buffer); - - /* - * Get the Common Name, if there is a subject. - */ - X509_NAME_get_text_by_NID(X509_get_subject_name(cert), - NID_commonName, buffer, sizeof(buffer)); - buffer[sizeof(buffer) - 1] = '\0'; - - if (buffer[0]) { - CERT_ATTR_ADD(IDX_COMMON_NAME, attr_index, buffer); - } - } - - X509_NAME_oneline(X509_get_issuer_name(cert), buffer, sizeof(buffer)); - buffer[sizeof(buffer) - 1] = '\0'; - if (identity && buffer[0]) { - CERT_ATTR_ADD(IDX_ISSUER, attr_index, buffer); - } - - /* - * Get the RFC822 Subject Alternative Name - */ - loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, 0); - if (loc >= 0) { - X509_EXTENSION *ext = NULL; - GENERAL_NAMES *names = NULL; - int i; - - ext = X509_get_ext(cert, loc); - if (ext && (names = X509V3_EXT_d2i(ext))) { - for (i = 0; i < sk_GENERAL_NAME_num(names); i++) { - GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i); - - switch (name->type) { -#ifdef GEN_EMAIL - case GEN_EMAIL: { -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - char const *rfc822Name = (char const *)ASN1_STRING_get0_data(name->d.rfc822Name); -#else - char *rfc822Name = (char *)ASN1_STRING_data(name->d.rfc822Name); -#endif - - CERT_ATTR_ADD(IDX_SUBJECT_ALT_NAME_EMAIL, attr_index, rfc822Name); - break; - } -#endif /* GEN_EMAIL */ -#ifdef GEN_DNS - case GEN_DNS: { -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - char const *dNSName = (char const *)ASN1_STRING_get0_data(name->d.dNSName); -#else - char *dNSName = (char *)ASN1_STRING_data(name->d.dNSName); -#endif - CERT_ATTR_ADD(IDX_SUBJECT_ALT_NAME_DNS, attr_index, dNSName); - break; - } -#endif /* GEN_DNS */ -#ifdef GEN_OTHERNAME - case GEN_OTHERNAME: - /* look for a MS UPN */ - if (NID_ms_upn != OBJ_obj2nid(name->d.otherName->type_id)) break; - - /* we've got a UPN - Must be ASN1-encoded UTF8 string */ - if (name->d.otherName->value->type == V_ASN1_UTF8STRING) { - CERT_ATTR_ADD(IDX_SUBJECT_ALT_NAME_UPN, attr_index, - (char *)name->d.otherName->value->value.utf8string); - break; - } - - RWARN("Invalid UPN in Subject Alt Name (should be UTF-8)"); - break; -#endif /* GEN_OTHERNAME */ - default: - /* XXX TODO handle other SAN types */ - break; - } - } - } - if (names != NULL) GENERAL_NAMES_free(names); - } - - /* - * Only add extensions for the actual client certificate - */ - if (attr_index == 0) { -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - ext_list = X509_get0_extensions(cert); -#else - ext_list = cert->cert_info->extensions; -#endif - - /* - * Grab the X509 extensions, and create attributes out of them. - * For laziness, we re-use the OpenSSL names - */ - if (sk_X509_EXTENSION_num(ext_list) > 0) { - int i, len; - char *p; - BIO *out; - - MEM(out = BIO_new(BIO_s_mem())); - strlcpy(attribute, "TLS-Client-Cert-", sizeof(attribute)); - - for (i = 0; i < sk_X509_EXTENSION_num(ext_list); i++) { - char value[1024]; - ASN1_OBJECT *obj; - X509_EXTENSION *ext; - fr_dict_attr_t const *da; - - ext = sk_X509_EXTENSION_value(ext_list, i); - - obj = X509_EXTENSION_get_object(ext); - if (i2a_ASN1_OBJECT(out, obj) <= 0) { - RPWDEBUG("Skipping X509 Extension (%i) conversion to attribute. " - "Conversion from ASN1 failed...", i); - continue; - } - - len = BIO_read(out, attribute + 16 , sizeof(attribute) - 16 - 1); - if (len <= 0) continue; - - attribute[16 + len] = '\0'; - - for (p = attribute + 16; *p != '\0'; p++) if (*p == ' ') *p = '-'; - - X509V3_EXT_print(out, ext, 0, 0); - len = BIO_read(out, value , sizeof(value) - 1); - if (len <= 0) continue; - - value[len] = '\0'; - - da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), attribute); - if (!da) { - RWDEBUG3("Skipping attribute %s: " - "Add dictionary definition if you want to access it", attribute); - continue; - } - - MEM(vp = fr_pair_afrom_da(ctx, da)); - if (fr_pair_value_from_str(vp, value, -1, '\0', true) < 0) { - RPWDEBUG3("Skipping: %s += '%s'", attribute, value); - talloc_free(vp); - continue; - } - - fr_pair_append(pair_list, vp); - } - BIO_free_all(out); - } - } - - return 0; -} -DIAG_ON(used-but-marked-unused) -DIAG_ON(DIAG_UNKNOWN_PRAGMAS) - /** Decrypt application data * * @note Handshake must have completed before this function may be called. @@ -1529,7 +1235,9 @@ static unlang_action_t tls_session_async_handshake_cont(rlm_rcode_t *p_result, i * asynchronously. */ switch (SSL_get_error(tls_session->ssl, ret)) { - case SSL_ERROR_WANT_ASYNC: /* Certification validation */ + case SSL_ERROR_WANT_ASYNC: /* Certification validation or cache loads */ + { + unlang_action_t ua; /* * Call this function again once we're done * asynchronously satisfying the load request. @@ -1540,11 +1248,36 @@ static unlang_action_t tls_session_async_handshake_cont(rlm_rcode_t *p_result, i goto finish; } - if (fr_tls_cache_load_push(request, tls_session) < 0) { + /* + * First service any pending cache actions + */ + ua = fr_tls_cache_pending_push(request, tls_session); + switch (ua) { + case UNLANG_ACTION_FAIL: if (unlang_function_clear(request) < 0) goto error; goto error; + + case UNLANG_ACTION_PUSHED_CHILD: + return ua; + + default: + break; } - return UNLANG_ACTION_PUSHED_CHILD; + + /* + * Next service any pending certificate + * validation actions. + */ + ua = fr_tls_validate_client_cert_pending_push(request, tls_session); + switch (ua) { + case UNLANG_ACTION_FAIL: + if (unlang_function_clear(request) < 0) goto error; + goto error; + + default: + return ua; + } + } default: /* @@ -1706,10 +1439,9 @@ fr_tls_session_t *fr_tls_session_alloc_client(TALLOC_CTX *ctx, SSL_CTX *ssl_ctx) request_t *request; fr_tls_conf_t *conf = fr_tls_ctx_conf(ssl_ctx); - tls_session = talloc_zero(ctx, fr_tls_session_t); - if (!tls_session) return NULL; - + MEM(tls_session = talloc_zero(ctx, fr_tls_session_t)); talloc_set_destructor(tls_session, _fr_tls_session_free); + fr_pair_list_init(&tls_session->extra_pairs); tls_session->ssl = SSL_new(ssl_ctx); if (!tls_session->ssl) { @@ -1773,28 +1505,25 @@ fr_tls_session_t *fr_tls_session_alloc_client(TALLOC_CTX *ctx, SSL_CTX *ssl_ctx) fr_tls_session_t *fr_tls_session_alloc_server(TALLOC_CTX *ctx, SSL_CTX *ssl_ctx, request_t *request, bool client_cert) { fr_tls_session_t *tls_session = NULL; - SSL *new_tls = NULL; + SSL *ssl = NULL; int verify_mode = 0; fr_pair_t *vp; fr_tls_conf_t *conf = fr_tls_ctx_conf(ssl_ctx); RDEBUG2("Initiating new TLS session"); - new_tls = SSL_new(ssl_ctx); - if (new_tls == NULL) { + MEM(tls_session = talloc_zero(ctx, fr_tls_session_t)); + + ssl = SSL_new(ssl_ctx); + if (ssl == NULL) { fr_tls_log_error(request, "Error creating new TLS session"); return NULL; } + fr_pair_list_init(&tls_session->extra_pairs); - tls_session = talloc_zero(ctx, fr_tls_session_t); - if (tls_session == NULL) { - RERROR("Error allocating memory for TLS session"); - SSL_free(new_tls); - return NULL; - } session_init(tls_session); tls_session->ctx = ssl_ctx; - tls_session->ssl = new_tls; + tls_session->ssl = ssl; talloc_set_destructor(tls_session, _fr_tls_session_free); fr_tls_session_request_bind(tls_session->ssl, request); @@ -1825,9 +1554,9 @@ fr_tls_session_t *fr_tls_session_alloc_server(TALLOC_CTX *ctx, SSL_CTX *ssl_ctx, * Add the message callback to identify what type of * message/handshake is passed */ - SSL_set_msg_callback(new_tls, fr_tls_session_msg_cb); - SSL_set_msg_callback_arg(new_tls, tls_session); - SSL_set_info_callback(new_tls, fr_tls_session_info_cb); + SSL_set_msg_callback(ssl, fr_tls_session_msg_cb); + SSL_set_msg_callback_arg(ssl, tls_session); + SSL_set_info_callback(ssl, fr_tls_session_info_cb); /* * This sets the context sessions can be resumed in. @@ -1931,6 +1660,8 @@ fr_tls_session_t *fr_tls_session_alloc_server(TALLOC_CTX *ctx, SSL_CTX *ssl_ctx, verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; verify_mode |= SSL_VERIFY_CLIENT_ONCE; } + tls_session->verify_client_cert = client_cert; + SSL_set_verify(tls_session->ssl, verify_mode, fr_tls_validate_cert_cb); SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_CONF, (void *)conf); SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_TLS_SESSION, (void *)tls_session); diff --git a/src/lib/tls/session.h b/src/lib/tls/session.h index 985589a2ee5..c5b743942c5 100644 --- a/src/lib/tls/session.h +++ b/src/lib/tls/session.h @@ -28,8 +28,12 @@ RCSIDH(session_h, "$Id$") #include #include +typedef struct fr_tls_session_s fr_tls_session_t; + +#include "cache.h" #include "conf.h" #include "index.h" +#include "validate.h" #ifdef __cplusplus extern "C" { @@ -95,7 +99,7 @@ typedef enum { * In the case of EAP-TLS + dependents a #eap_tls_session_t struct is used to track * the transfer of TLS records. */ -typedef struct { +struct fr_tls_session_s { SSL_CTX *ctx; //!< TLS configuration context. SSL *ssl; //!< This SSL session. SSL_SESSION *session; //!< Session resumption data. @@ -124,6 +128,9 @@ typedef struct { fr_tls_cache_t *cache; //!< Current session resumption state. bool allow_session_resumption; //!< Whether session resumption is allowed. + bool verify_client_cert; //!< Whether client cert verification has been requested. + + fr_tls_validate_t validate; //!< Current session certificate validation state. bool invalid; //!< Whether heartbleed attack was detected. @@ -133,7 +140,10 @@ typedef struct { bool pending_alert; uint8_t pending_alert_level; uint8_t pending_alert_description; -} fr_tls_session_t; + + fr_pair_list_t extra_pairs; //!< Pairs to add to cache and certificate validation + ///< calls. These will be duplicated for every call. +}; /** Return the tls config associated with a tls_session * @@ -223,6 +233,46 @@ static inline request_t *fr_tls_session_request(SSL const *ssl) return talloc_get_type_abort(SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST), request_t); } +/** Add extra pairs to the temporary subrequests + * + * @param[in] child to add extra pairs to. + * @param[in] tls_session to add extra pairs from. + */ +static inline CC_HINT(nonnull) +void fr_tls_session_extra_pairs_copy_to_child(request_t *child, fr_tls_session_t *tls_session) +{ + if (!fr_pair_list_empty(&tls_session->extra_pairs)) { + MEM(fr_pair_list_copy(child->request_ctx, &child->request_pairs, &tls_session->extra_pairs) >= 0); + } +} + +/** Add an additional pair (copying it) to the list of extra pairs + * + * @param[in] tls_session to add extra pairs to. + * @param[in] vp to add to tls_session. + */ +static inline CC_HINT(nonnull) +void fr_tls_session_extra_pair_add(fr_tls_session_t *tls_session, fr_pair_t *vp) +{ + fr_pair_t *copy; + + MEM(copy = fr_pair_copy(tls_session, vp)); + fr_pair_append(&tls_session->extra_pairs, copy); +} + +/** Add an additional pair to the list of extra pairs + * + * @param[in] tls_session to add extra pairs to. + * @param[in] vp to add to tls_session. + */ +static inline CC_HINT(nonnull) +void fr_tls_session_extra_pair_add_shallow(fr_tls_session_t *tls_session, fr_pair_t *vp) +{ + fr_assert(talloc_parent(vp) == tls_session); + fr_pair_append(&tls_session->extra_pairs, vp); +} + + int fr_tls_session_password_cb(char *buf, int num, int rwflag, void *userdata); unsigned int fr_tls_session_psk_client_cb(SSL *ssl, UNUSED char const *hint, diff --git a/src/lib/tls/validate.c b/src/lib/tls/validate.c index ac6588a22d4..a53a80ff606 100644 --- a/src/lib/tls/validate.c +++ b/src/lib/tls/validate.c @@ -30,8 +30,9 @@ #include #include +#include +#include #include - #include #include @@ -411,4 +412,109 @@ int fr_tls_validate_client_cert_chain(SSL *ssl) return ret; } + +/** Process the result of `validate certificate { ... }` + * + */ +static unlang_action_t tls_validate_client_cert_result(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, + request_t *request, void *uctx) +{ + fr_tls_session_t *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t); + fr_pair_t *vp; + + fr_assert(tls_session->validate.state == FR_TLS_VALIDATION_REQUESTED); + + vp = fr_pair_find_by_da(&request->reply_pairs, attr_tls_packet_type, 0); + if (!vp || (vp->vp_uint32 != enum_tls_packet_type_success->vb_uint32)) { + REDEBUG("Failed (re-)validating certificates"); + tls_session->validate.state = FR_TLS_VALIDATION_FAILED; + return UNLANG_ACTION_CALCULATE_RESULT; + } + + tls_session->validate.state = FR_TLS_VALIDATION_SUCCESS; + + RDEBUG2("Certificates (re-)validated"); + + return UNLANG_ACTION_CALCULATE_RESULT; +} + +/** Push a `validate certificate { ... }` call into the current request, using a subrequest + * + * @param[in] request The current request. + * @Param[in] tls_session The current TLS session. + * @return + * - UNLANG_ACTION_CALCULATE_RESULT on noop. + * - UNLANG_ACTION_PUSHED_CHILD on success. + * - UNLANG_ACTION_FAIL on failure. + */ +static unlang_action_t tls_validate_client_cert_push(request_t *request, fr_tls_session_t *tls_session) +{ + fr_tls_conf_t *conf = fr_tls_session_conf(tls_session->ssl); + request_t *child; + fr_pair_t *vp; + unlang_action_t ua; + + MEM(child = unlang_subrequest_alloc(request, dict_tls)); + request = child; + + /* + * Add extra pairs to the subrequest + */ + fr_tls_session_extra_pairs_copy_to_child(child, tls_session); + + /* + * Setup the child request for loading + * session resumption data. + */ + MEM(pair_prepend_request(&vp, attr_tls_packet_type) >= 0); + vp->vp_uint32 = enum_tls_packet_type_certificate_validate->vb_uint32; + + /* + * Allocate a child, and set it up to call + * the TLS virtual server. + */ + ua = fr_tls_call_push(child, tls_validate_client_cert_result, conf, tls_session); + if (ua < 0) { + PERROR("Failed calling TLS virtual server"); + talloc_free(child); + return UNLANG_ACTION_FAIL; + } + + return ua; +} + +/** Check we validated the client cert successfully + * + */ +bool fr_tls_validate_client_cert_success(fr_tls_session_t *tls_session) +{ + return tls_session->validate.state == FR_TLS_VALIDATION_SUCCESS; +} + +/** Setup a validation request + * + */ +void fr_tls_validate_client_cert_request(fr_tls_session_t *tls_session) +{ + fr_assert(tls_session->validate.state == FR_TLS_VALIDATION_INIT); + + tls_session->validate.state = FR_TLS_VALIDATION_REQUESTED; +} + +/** Push a `validate certificate { ... }` section + * + * @param[in] request The current request. + * @Param[in] tls_session The current TLS session. + * @return + * - UNLANG_ACTION_CALCULATE_RESULT - No pending actions + * - UNLANG_ACTION_PUSHED_CHILD - Pending operations to evaluate. + */ +unlang_action_t fr_tls_validate_client_cert_pending_push(request_t *request, fr_tls_session_t *tls_session) +{ + if (tls_session->validate.state == FR_TLS_VALIDATION_REQUESTED) { + return tls_validate_client_cert_push(request, tls_session); + } + + return UNLANG_ACTION_CALCULATE_RESULT; +} #endif /* WITH_TLS */ diff --git a/src/lib/tls/validate.h b/src/lib/tls/validate.h new file mode 100644 index 00000000000..03a4464d90a --- /dev/null +++ b/src/lib/tls/validate.h @@ -0,0 +1,67 @@ +#pragma once +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#ifdef WITH_TLS +/** + * $Id$ + * + * @file lib/tls/validate.h + * @brief Structures for session-resumption management. + * + * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org) + */ +RCSIDH(validate_h, "$Id$") + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Certificate validation states + * + */ +typedef enum { + FR_TLS_VALIDATION_INIT = 0, //!< OpenSSL hasn't requested certificate validation. + FR_TLS_VALIDATION_REQUESTED, //!< OpenSSL requested validation. + FR_TLS_VALIDATION_SUCCESS, //!< Certificate chain was validate. + FR_TLS_VALIDATION_FAILED //!< Certificate validation failed. +} fr_tls_validation_state_t; + +/** Certificate validation state + * + */ +typedef struct { + rlm_rcode_t rcode; + fr_tls_validation_state_t state; //!< Whether OpenSSL has requested + ///< certificate validation. +} fr_tls_validate_t; + +int fr_tls_validate_cert_cb(int ok, X509_STORE_CTX *ctx); + +int fr_tls_validate_client_cert_chain(SSL *ssl); + +bool fr_tls_validate_client_cert_success(fr_tls_session_t *tls_session); + +void fr_tls_validate_client_cert_request(fr_tls_session_t *tls_session); + +unlang_action_t fr_tls_validate_client_cert_pending_push(request_t *request, fr_tls_session_t *tls_session); + +#ifdef __cplusplus +} +#endif +#endif /* WITH_TLS */ diff --git a/src/process/tls/all.mk b/src/process/tls/all.mk index 77a614e52a9..d6652bde5f5 100644 --- a/src/process/tls/all.mk +++ b/src/process/tls/all.mk @@ -1,5 +1,8 @@ -TARGETNAME := process_tls +TARGETNAME := process_tls + +ifneq "$(OPENSSL_LIBS)" "" +TARGET := $(TARGETNAME).a +endif SOURCES := base.c TGT_PREREQS := libfreeradius-tls.a -TGT_LDLIBS := $(PCAP_LIBS) diff --git a/src/process/tls/base.c b/src/process/tls/base.c index 9a042699275..98658e08c5f 100644 --- a/src/process/tls/base.c +++ b/src/process/tls/base.c @@ -17,13 +17,14 @@ /** * $Id$ * @file src/process/tls/base.c - * @brief ARP processing. + * @brief TLS processing. * * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org) */ #include #include #include +#include static fr_dict_t const *dict_tls; @@ -42,140 +43,95 @@ fr_dict_attr_autoload_t process_tls_dict_attr[] = { }; typedef struct { - uint64_t nothing; // so that the next field isn't at offset 0 - - CONF_SECTION *request; - CONF_SECTION *reply; - CONF_SECTION *recv_reply; - CONF_SECTION *reverse_request; - CONF_SECTION *reverse_reply; - CONF_SECTION *do_not_respond; + CONF_SECTION *session_load; + CONF_SECTION *session_store; + CONF_SECTION *session_clear; + CONF_SECTION *certificate_validate; } process_tls_sections_t; typedef struct { - bool test; - process_tls_sections_t sections; } process_tls_t; -#define PROCESS_PACKET_TYPE fr_tls_packet_code_t -#define PROCESS_CODE_MAX FR_ARP_CODE_MAX -#define PROCESS_CODE_DO_NOT_RESPOND FR_ARP_DO_NOT_RESPOND -#define PROCESS_PACKET_CODE_VALID FR_ARP_PACKET_CODE_VALID +#define FR_TLS_PACKET_CODE_VALID(_code) (((_code) > 0) && ((_code) <= FR_PACKET_TYPE_VALUE_NOTFOUND)) + #define PROCESS_INST process_tls_t +#define PROCESS_PACKET_TYPE uint32_t +#define PROCESS_PACKET_CODE_VALID FR_TLS_PACKET_CODE_VALID + #include static fr_process_state_t const process_state[] = { - [ FR_ARP_REQUEST ] = { + [FR_PACKET_TYPE_VALUE_SESSION_LOAD] = { .packet_type = { - [RLM_MODULE_NOOP] = FR_ARP_REPLY, - [RLM_MODULE_OK] = FR_ARP_REPLY, - [RLM_MODULE_UPDATED] = FR_ARP_REPLY, - - [RLM_MODULE_REJECT] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_FAIL] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_INVALID] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_DISALLOW] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_NOTFOUND] = FR_ARP_DO_NOT_RESPOND, + [RLM_MODULE_OK] = FR_PACKET_TYPE_VALUE_SUCCESS, + [RLM_MODULE_UPDATED] = FR_PACKET_TYPE_VALUE_SUCCESS, + + [RLM_MODULE_NOOP] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_REJECT] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_FAIL] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_INVALID] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_DISALLOW] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_NOTFOUND] = FR_PACKET_TYPE_VALUE_NOTFOUND, }, .rcode = RLM_MODULE_NOOP, .recv = recv_generic, - .resume = resume_recv_generic, - .section_offset = PROCESS_CONF_OFFSET(request), + .resume = resume_recv_no_send, + .section_offset = PROCESS_CONF_OFFSET(session_load), }, - [ FR_ARP_REPLY ] = { + [FR_PACKET_TYPE_VALUE_SESSION_STORE] = { .packet_type = { - [RLM_MODULE_NOOP] = FR_ARP_REPLY, - [RLM_MODULE_OK] = FR_ARP_REPLY, - [RLM_MODULE_UPDATED] = FR_ARP_REPLY, - - [RLM_MODULE_REJECT] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_FAIL] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_INVALID] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_DISALLOW] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_NOTFOUND] = FR_ARP_DO_NOT_RESPOND, - }, - .rcode = RLM_MODULE_NOOP, - .send = send_generic, - .resume = resume_send_generic, - .section_offset = PROCESS_CONF_OFFSET(reply), - }, - - [ FR_ARP_REVERSE_REQUEST ] = { - .packet_type = { - [RLM_MODULE_NOOP] = FR_ARP_REVERSE_REPLY, - [RLM_MODULE_OK] = FR_ARP_REVERSE_REPLY, - [RLM_MODULE_UPDATED] = FR_ARP_REVERSE_REPLY, - - [RLM_MODULE_REJECT] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_FAIL] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_INVALID] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_DISALLOW] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_NOTFOUND] = FR_ARP_DO_NOT_RESPOND, + [RLM_MODULE_OK] = FR_PACKET_TYPE_VALUE_SUCCESS, + [RLM_MODULE_UPDATED] = FR_PACKET_TYPE_VALUE_SUCCESS, + + [RLM_MODULE_NOOP] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_REJECT] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_FAIL] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_INVALID] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_DISALLOW] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_NOTFOUND] = FR_PACKET_TYPE_VALUE_NOTFOUND, }, .rcode = RLM_MODULE_NOOP, .recv = recv_generic, - .resume = resume_recv_generic, - .section_offset = PROCESS_CONF_OFFSET(reverse_request), + .resume = resume_recv_no_send, + .section_offset = PROCESS_CONF_OFFSET(session_store), }, - [ FR_ARP_REVERSE_REPLY ] = { + [FR_PACKET_TYPE_VALUE_SESSION_CLEAR] = { .packet_type = { - [RLM_MODULE_NOOP] = FR_ARP_REVERSE_REPLY, - [RLM_MODULE_OK] = FR_ARP_REVERSE_REPLY, - [RLM_MODULE_UPDATED] = FR_ARP_REVERSE_REPLY, - - [RLM_MODULE_REJECT] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_FAIL] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_INVALID] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_DISALLOW] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_NOTFOUND] = FR_ARP_DO_NOT_RESPOND, + [RLM_MODULE_OK] = FR_PACKET_TYPE_VALUE_SUCCESS, + [RLM_MODULE_UPDATED] = FR_PACKET_TYPE_VALUE_SUCCESS, + + [RLM_MODULE_NOOP] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_REJECT] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_FAIL] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_INVALID] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_DISALLOW] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_NOTFOUND] = FR_PACKET_TYPE_VALUE_NOTFOUND, }, .rcode = RLM_MODULE_NOOP, - .send = send_generic, - .resume = resume_send_generic, - .section_offset = PROCESS_CONF_OFFSET(reverse_reply), + .recv = recv_generic, + .resume = resume_recv_no_send, + .section_offset = PROCESS_CONF_OFFSET(session_clear), }, - - // @todo - recv reply, to look at other replies. - - [ FR_ARP_DO_NOT_RESPOND ] = { + [FR_PACKET_TYPE_VALUE_CERTIFICATE_VALIDATE] = { .packet_type = { - [RLM_MODULE_NOOP] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_OK] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_UPDATED] = FR_ARP_DO_NOT_RESPOND, - - [RLM_MODULE_REJECT] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_FAIL] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_INVALID] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_DISALLOW] = FR_ARP_DO_NOT_RESPOND, - [RLM_MODULE_NOTFOUND] = FR_ARP_DO_NOT_RESPOND, + [RLM_MODULE_OK] = FR_PACKET_TYPE_VALUE_SUCCESS, + + [RLM_MODULE_UPDATED] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_NOOP] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_REJECT] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_FAIL] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_INVALID] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_DISALLOW] = FR_PACKET_TYPE_VALUE_FAILURE, + [RLM_MODULE_NOTFOUND] = FR_PACKET_TYPE_VALUE_NOTFOUND, }, .rcode = RLM_MODULE_NOOP, - .send = send_generic, - .resume = resume_send_generic, - .section_offset = PROCESS_CONF_OFFSET(do_not_respond), + .recv = recv_generic, + .resume = resume_recv_no_send, + .section_offset = PROCESS_CONF_OFFSET(certificate_validate), }, }; -/* - * Debug the packet if requested. - */ -static void tls_packet_debug(request_t *request, fr_radius_packet_t const *packet, fr_pair_list_t const *list, bool received) -{ - if (!packet) return; - if (!RDEBUG_ENABLED) return; - - log_request(L_DBG, L_DBG_LVL_1, request, __FILE__, __LINE__, "%s %s", - received ? "Received" : "Sending", - fr_tls_packet_codes[packet->code]); - - if (received || request->parent) { - log_request_pair_list(L_DBG_LVL_1, request, NULL, list, NULL); - } else { - log_request_proto_pair_list(L_DBG_LVL_1, request, NULL, list, NULL); - } -} - static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request) { fr_process_state_t const *state; @@ -183,7 +139,6 @@ static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mc PROCESS_TRACE; (void)talloc_get_type_abort_const(mctx->instance, process_tls_t); - fr_assert(PROCESS_PACKET_CODE_VALID(request->packet->code)); request->component = "tls"; request->module = NULL; @@ -191,49 +146,36 @@ static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mc UPDATE_STATE(packet); - tls_packet_debug(request, request->packet, &request->request_pairs, true); + log_request_pair_list(L_DBG_LVL_1, request, NULL, &request->request_pairs, NULL); return state->recv(p_result, mctx, request); } - static const virtual_server_compile_t compile_list[] = { { .name = "store", .name2 = "session", .component = MOD_AUTHORIZE, - .offset = offsetof(eap_aka_sim_process_conf_t, actions.store_session) + .offset = PROCESS_CONF_OFFSET(session_store) }, { .name = "load", .name2 = "session", .component = MOD_AUTHORIZE, - .offset = offsetof(eap_aka_sim_process_conf_t, actions.load_session) + .offset = PROCESS_CONF_OFFSET(session_load) }, { .name = "clear", .name2 = "session", .component = MOD_AUTHORIZE, - .offset = offsetof(eap_aka_sim_process_conf_t, actions.clear_session) + .offset = PROCESS_CONF_OFFSET(session_clear) }, { - .name = "recv", + .name = "validate", .name2 = "certificate", .component = MOD_AUTHORIZE, - .offset = offsetof(eap_aka_sim_process_conf_t, actions.recv_certificate) + .offset = PROCESS_CONF_OFFSET(certificate_validate) }, - { - .name = "send", - .name2 = "success", - .component = MOD_POST_AUTH, - .offset = offsetof(eap_aka_sim_process_conf_t, actions.send_success) - }, - { - .name = "send", - .name2 = "failure", - .component = MOD_POST_AUTH, - .offset = offsetof(eap_aka_sim_process_conf_t, actions.send_failure) - } COMPILE_TERMINATOR }; diff --git a/src/tests/eapol_test/config/tls/methods-enabled/tls b/src/tests/eapol_test/config/tls/methods-enabled/tls index f7bc40db117..32447884cce 100644 --- a/src/tests/eapol_test/config/tls/methods-enabled/tls +++ b/src/tests/eapol_test/config/tls/methods-enabled/tls @@ -1,13 +1,36 @@ +# +# Should use the same set of snakeoil certs as +# eapol_test. +# +tls-config eap-tls-test { + virtual_server = eap-tls-test + chain { + certificate_file = ${certdir}/rsa/server.pem + + private_key_password = whatever + private_key_file = ${certdir}/rsa/server.pem + ca_file = ${cadir}/rsa/ca.pem + } + + ca_file = ${cadir}/rsa/ca.pem + ca_path = ${cadir} + dh_file = ${certdir}/dh + + fragment_size = 1024 + include_length = no + + cipher_list = "DEFAULT" + ecdh_curve = "prime256v1" + + verify { + } + + ocsp { + } +} + type = tls tls { # Point to the common TLS configuration - tls = tls-common - - # - # As part of checking a client certificate, the EAP-TLS - # sets some attributes such as TLS-Client-Cert-CN. This - # virtual server has access to these attributes, and can - # be used to accept or reject the request. - # -# virtual_server = check-eap-tls + tls = eap-tls-test } diff --git a/src/tests/eapol_test/config/tls/sites-enabled/tls b/src/tests/eapol_test/config/tls/sites-enabled/tls index 973978278aa..b4ff6ac347e 100644 --- a/src/tests/eapol_test/config/tls/sites-enabled/tls +++ b/src/tests/eapol_test/config/tls/sites-enabled/tls @@ -1,42 +1,7 @@ -server inner-tunnel { - namespace = radius - listen { - type = Access-Request - } - - recv Access-Request { - update request { - &User-Name := &outer.User-Name - } - files - pap - chap - mschap - eap - } - - authenticate pap { - pap - } - - authenticate chap { - chap - } - - authenticate mschap { - mschap - } - - authenticate eap { - eap - } - - send Access-Accept { - } - - send Access-Reject { - } +server eap-tls-test { + namespace = tls - send Access-Challenge { + validate certificate { + ok } }