diff --git a/hostapd/config_file.c b/hostapd/config_file.c index e2ca2789b..aeec1d9e2 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2505,6 +2505,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->check_crl = atoi(pos); } else if (os_strcmp(buf, "check_crl_strict") == 0) { bss->check_crl_strict = atoi(pos); + } else if (os_strcmp(buf, "crl_reload_interval") == 0) { + bss->crl_reload_interval = atoi(pos); } else if (os_strcmp(buf, "tls_session_lifetime") == 0) { bss->tls_session_lifetime = atoi(pos); } else if (os_strcmp(buf, "tls_flags") == 0) { diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 739d36772..ab37f03b5 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -922,7 +922,8 @@ eap_server=0 # valid CRL signed by the CA is required to be included in the ca_cert file. # This can be done by using PEM format for CA certificate and CRL and # concatenating these into one file. Whenever CRL changes, hostapd needs to be -# restarted to take the new CRL into use. +# restarted to take the new CRL into use. Alternatively, crl_reload_interval can +# be used to configure periodic updating of the loaded CRL information. # 0 = do not verify CRLs (default) # 1 = check the CRL of the user certificate # 2 = check all CRLs in the certificate path @@ -935,6 +936,16 @@ eap_server=0 # 1 = do not ignore errors (default) #check_crl_strict=1 +# CRL reload interval in seconds +# This can be used to reload ca_cert file and the included CRL on every new TLS +# session if difference between last reload and the current reload time in +# seconds is greater than crl_reload_interval. +# Note: If interval time is very short, CPU overhead may be negatively affected +# and it is advised to not go below 300 seconds. +# This is applicable only with check_crl values 1 and 2. +# 0 = do not reload CRLs (default) +# crl_reload_interval = 300 + # TLS Session Lifetime in seconds # This can be used to allow TLS sessions to be cached and resumed with an # abbreviated handshake when using EAP-TLS/TTLS/PEAP. diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 8ca80d3f8..6963df460 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -392,6 +392,7 @@ struct hostapd_bss_config { char *private_key_passwd; int check_crl; int check_crl_strict; + unsigned int crl_reload_interval; unsigned int tls_session_lifetime; unsigned int tls_flags; char *ocsp_stapling_response; diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index d93b96750..1bb3d9f6f 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -201,6 +201,16 @@ int authsrv_init(struct hostapd_data *hapd) os_memset(&conf, 0, sizeof(conf)); conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + if (hapd->conf->crl_reload_interval > 0 && + hapd->conf->check_crl <= 0) { + wpa_printf(MSG_INFO, + "Cannot enable CRL reload functionality - it depends on check_crl being set"); + } else if (hapd->conf->crl_reload_interval > 0) { + conf.crl_reload_interval = + hapd->conf->crl_reload_interval; + wpa_printf(MSG_INFO, + "Enabled CRL reload functionality"); + } conf.tls_flags = hapd->conf->tls_flags; conf.event_cb = authsrv_tls_event; conf.cb_ctx = hapd; diff --git a/src/crypto/tls.h b/src/crypto/tls.h index e13444a6c..413cccddc 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -82,6 +82,7 @@ struct tls_config { int cert_in_cb; const char *openssl_ciphers; unsigned int tls_session_lifetime; + unsigned int crl_reload_interval; unsigned int tls_flags; void (*event_cb)(void *ctx, enum tls_event ev, diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 705fa29a3..99caa68bd 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -214,7 +214,11 @@ static struct tls_context *tls_global = NULL; struct tls_data { SSL_CTX *ssl; unsigned int tls_session_lifetime; + int check_crl; int check_crl_strict; + char *ca_cert; + unsigned int crl_reload_interval; + struct os_reltime crl_last_reload; }; struct tls_connection { @@ -305,6 +309,37 @@ static void tls_show_errors(int level, const char *func, const char *txt) #endif /* CONFIG_NO_STDOUT_DEBUG */ +static X509_STORE * tls_crl_cert_reload(const char *ca_cert, int check_crl) +{ + int flags; + X509_STORE *store; + + store = X509_STORE_new(); + if (!store) { + wpa_printf(MSG_DEBUG, + "OpenSSL: %s - failed to allocate new certificate store", + __func__); + return NULL; + } + + if (ca_cert && X509_STORE_load_locations(store, ca_cert, NULL) != 1) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + X509_STORE_free(store); + return NULL; + } + + if (check_crl) + flags = X509_V_FLAG_CRL_CHECK; + if (check_crl == 2) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + + X509_STORE_set_flags(store, flags); + + return store; +} + + #ifdef CONFIG_NATIVE_WINDOWS /* Windows CryptoAPI and access to certificate stores */ @@ -993,8 +1028,10 @@ void * tls_init(const struct tls_config *conf) return NULL; } data->ssl = ssl; - if (conf) + if (conf) { data->tls_session_lifetime = conf->tls_session_lifetime; + data->crl_reload_interval = conf->crl_reload_interval; + } SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); @@ -1076,6 +1113,7 @@ void tls_deinit(void *ssl_ctx) os_free(context); if (data->tls_session_lifetime > 0) SSL_CTX_flush_sessions(ssl, 0); + os_free(data->ca_cert); SSL_CTX_free(ssl); tls_openssl_ref_count--; @@ -1471,8 +1509,28 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) SSL_CTX *ssl = data->ssl; struct tls_connection *conn; long options; + X509_STORE *new_cert_store; + struct os_reltime now; struct tls_context *context = SSL_CTX_get_app_data(ssl); + /* Replace X509 store if it is time to update CRL. */ + if (data->crl_reload_interval > 0 && os_get_reltime(&now) == 0 && + os_reltime_expired(&now, &data->crl_last_reload, + data->crl_reload_interval)) { + wpa_printf(MSG_INFO, + "OpenSSL: Flushing X509 store with ca_cert file"); + new_cert_store = tls_crl_cert_reload(data->ca_cert, + data->check_crl); + if (!new_cert_store) { + wpa_printf(MSG_ERROR, + "OpenSSL: Error replacing X509 store with ca_cert file"); + } else { + /* Replace old store */ + SSL_CTX_set_cert_store(ssl, new_cert_store); + data->crl_last_reload = now; + } + } + conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; @@ -2393,6 +2451,9 @@ static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) SSL_CTX_set_client_CA_list(ssl_ctx, SSL_load_client_CA_file(ca_cert)); #endif /* OPENSSL_NO_STDIO */ + + os_free(data->ca_cert); + data->ca_cert = os_strdup(ca_cert); } return 0; @@ -2417,7 +2478,9 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict) flags |= X509_V_FLAG_CRL_CHECK_ALL; X509_STORE_set_flags(cs, flags); + data->check_crl = check_crl; data->check_crl_strict = strict; + os_get_reltime(&data->crl_last_reload); } return 0; }