diff --git a/hostapd/Makefile b/hostapd/Makefile index fa5435d9..45afedf4 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -6,6 +6,21 @@ ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g endif +ifdef LIBS +# If LIBS is set with some global build system defaults, clone those for +# LIBS_c, LIBS_h, and LIBS_n to cover hostapd_cli, hlr_auc_gw, and +# nt_password_hash as well. +ifndef LIBS_c +LIBS_c := $(LIBS) +endif +ifndef LIBS_h +LIBS_h := $(LIBS) +endif +ifndef LIBS_n +LIBS_n := $(LIBS) +endif +endif + CFLAGS += $(EXTRA_CFLAGS) CFLAGS += -I$(abspath ../src) CFLAGS += -I$(abspath ../src/utils) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index f2163b83..503d4797 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2132,6 +2132,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) { os_free(bss->ocsp_stapling_response); bss->ocsp_stapling_response = os_strdup(pos); + } else if (os_strcmp(buf, "ocsp_stapling_response_multi") == 0) { + os_free(bss->ocsp_stapling_response_multi); + bss->ocsp_stapling_response_multi = os_strdup(pos); } else if (os_strcmp(buf, "dh_file") == 0) { os_free(bss->dh_file); bss->dh_file = os_strdup(pos); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 4f51140b..ecd4328c 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -795,6 +795,11 @@ eap_server=0 # -respout /tmp/ocsp-cache.der #ocsp_stapling_response=/tmp/ocsp-cache.der +# Cached OCSP stapling response list (DER encoded OCSPResponseList) +# This is similar to ocsp_stapling_response, but the extended version defined in +# RFC 6961 to allow multiple OCSP responses to be provided. +#ocsp_stapling_response_multi=/tmp/ocsp-multi-cache.der + # dh_file: File path to DH/DSA parameters file (in PEM format) # This is an optional configuration file for setting parameters for an # ephemeral DH key exchange. In most cases, the default RSA authentication does diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index cf9b2ceb..88074f2e 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -471,6 +471,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->private_key); os_free(conf->private_key_passwd); os_free(conf->ocsp_stapling_response); + os_free(conf->ocsp_stapling_response_multi); os_free(conf->dh_file); os_free(conf->openssl_ciphers); os_free(conf->pac_opaque_encr_key); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index ff9dcb05..44bccccb 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -341,6 +341,7 @@ struct hostapd_bss_config { int check_crl; unsigned int tls_session_lifetime; char *ocsp_stapling_response; + char *ocsp_stapling_response_multi; char *dh_file; char *openssl_ciphers; u8 *pac_opaque_encr_key; diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index c9111f6c..cdb49cdd 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -173,6 +173,8 @@ int authsrv_init(struct hostapd_data *hapd) params.openssl_ciphers = hapd->conf->openssl_ciphers; params.ocsp_stapling_response = hapd->conf->ocsp_stapling_response; + params.ocsp_stapling_response_multi = + hapd->conf->ocsp_stapling_response_multi; if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index fd072015..aad0d81a 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -549,8 +549,8 @@ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, #ifdef CONFIG_ACS -static void hostapd_acs_channel_selected(struct hostapd_data *hapd, - struct acs_selected_channels *acs_res) +void hostapd_acs_channel_selected(struct hostapd_data *hapd, + struct acs_selected_channels *acs_res) { int ret, i; @@ -945,6 +945,8 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, ieee802_1x_receive(hapd, src, data, data_len); } +#endif /* HOSTAPD */ + static struct hostapd_channel_data * hostapd_get_mode_channel( struct hostapd_iface *iface, unsigned int freq) @@ -1019,10 +1021,9 @@ static void hostapd_single_channel_get_survey(struct hostapd_iface *iface, } -static void hostapd_event_get_survey(struct hostapd_data *hapd, - struct survey_results *survey_results) +void hostapd_event_get_survey(struct hostapd_iface *iface, + struct survey_results *survey_results) { - struct hostapd_iface *iface = hapd->iface; struct freq_survey *survey, *tmp; struct hostapd_channel_data *chan; @@ -1054,6 +1055,7 @@ static void hostapd_event_get_survey(struct hostapd_data *hapd, } +#ifdef HOSTAPD #ifdef NEED_AP_MLME static void hostapd_event_iface_unavailable(struct hostapd_data *hapd) @@ -1261,7 +1263,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->connect_failed_reason.code); break; case EVENT_SURVEY: - hostapd_event_get_survey(hapd, &data->survey_results); + hostapd_event_get_survey(hapd->iface, &data->survey_results); break; #ifdef NEED_AP_MLME case EVENT_INTERFACE_UNAVAILABLE: diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 8161a593..7b59f802 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -493,6 +493,11 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, int ssi_signal); void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int offset, int width, int cf1, int cf2); +struct survey_results; +void hostapd_event_get_survey(struct hostapd_iface *iface, + struct survey_results *survey_results); +void hostapd_acs_channel_selected(struct hostapd_data *hapd, + struct acs_selected_channels *acs_res); const struct hostapd_eap_user * hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index f5666035..e3b3d94e 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -475,6 +475,7 @@ int add_common_radius_attr(struct hostapd_data *hapd, { char buf[128]; struct hostapd_radius_attr *attr; + int len; if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_IP_ADDRESS) && @@ -506,15 +507,15 @@ int add_common_radius_attr(struct hostapd_data *hapd, return -1; } - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), - wpa_ssid_txt(hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len)); - buf[sizeof(buf) - 1] = '\0'; + len = os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":", + MAC2STR(hapd->own_addr)); + os_memcpy(&buf[len], hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len); + len += hapd->conf->ssid.ssid_len; if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_CALLED_STATION_ID) && !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, os_strlen(buf))) { + (u8 *) buf, len)) { wpa_printf(MSG_ERROR, "Could not add Called-Station-Id"); return -1; } diff --git a/src/common/sae.c b/src/common/sae.c index b962ea22..6c00a7e3 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -816,6 +816,7 @@ static int sae_derive_keys(struct sae_data *sae, const u8 *k) os_memset(keyseed, 0, sizeof(keyseed)); os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); + os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); os_memset(keys, 0, sizeof(keys)); wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); diff --git a/src/common/sae.h b/src/common/sae.h index c07026cd..a4270bc2 100644 --- a/src/common/sae.h +++ b/src/common/sae.h @@ -45,6 +45,7 @@ struct sae_data { enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state; u16 send_confirm; u8 pmk[SAE_PMK_LEN]; + u8 pmkid[SAE_PMKID_LEN]; struct crypto_bignum *peer_commit_scalar; int group; int sync; diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c index f6658e6e..ffcba66a 100644 --- a/src/crypto/sha1-internal.c +++ b/src/crypto/sha1-internal.c @@ -297,7 +297,6 @@ void SHA1Final(unsigned char digest[20], SHA1_CTX* context) 255); } /* Wipe variables */ - i = 0; os_memset(context->buffer, 0, 64); os_memset(context->state, 0, 20); os_memset(context->count, 0, 8); diff --git a/src/crypto/tls.h b/src/crypto/tls.h index bca94d67..453b4deb 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -96,6 +96,7 @@ struct tls_config { #define TLS_CONN_EAP_FAST BIT(7) #define TLS_CONN_DISABLE_TLSv1_0 BIT(8) #define TLS_CONN_EXT_CERT_CHECK BIT(9) +#define TLS_CONN_REQUIRE_OCSP_ALL BIT(10) /** * struct tls_connection_params - Parameters for TLS connection @@ -140,6 +141,9 @@ struct tls_config { * @flags: Parameter options (TLS_CONN_*) * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response * or %NULL if OCSP is not enabled + * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling + * response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if + * ocsp_multi is not enabled * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -180,6 +184,7 @@ struct tls_connection_params { unsigned int flags; const char *ocsp_stapling_response; + const char *ocsp_stapling_response_multi; }; diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index b1bec4a5..c4cd3c1a 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -37,6 +37,8 @@ struct tls_global { union tls_event_data *data); void *cb_ctx; int cert_in_cb; + + char *ocsp_stapling_response; }; struct tls_connection { @@ -133,6 +135,7 @@ void tls_deinit(void *ssl_ctx) if (global->params_set) gnutls_certificate_free_credentials(global->xcred); os_free(global->session_data); + os_free(global->ocsp_stapling_response); os_free(global); } @@ -347,6 +350,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn == NULL || params == NULL) return -1; + if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + wpa_printf(MSG_INFO, + "GnuTLS: ocsp=3 not supported"); + return -1; + } + if (params->flags & TLS_CONN_EXT_CERT_CHECK) { wpa_printf(MSG_INFO, "GnuTLS: tls_ext_cert_check=1 not supported"); @@ -602,6 +611,44 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } +#if GNUTLS_VERSION_NUMBER >= 0x030103 +static int server_ocsp_status_req(gnutls_session_t session, void *ptr, + gnutls_datum_t *resp) +{ + struct tls_global *global = ptr; + char *cached; + size_t len; + + if (!global->ocsp_stapling_response) { + wpa_printf(MSG_DEBUG, "GnuTLS: OCSP status callback - no response configured"); + return GNUTLS_E_NO_CERTIFICATE_STATUS; + } + + cached = os_readfile(global->ocsp_stapling_response, &len); + if (!cached) { + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP status callback - could not read response file (%s)", + global->ocsp_stapling_response); + return GNUTLS_E_NO_CERTIFICATE_STATUS; + } + + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP status callback - send cached response"); + resp->data = gnutls_malloc(len); + if (!resp->data) { + os_free(resp); + return GNUTLS_E_MEMORY_ERROR; + } + + os_memcpy(resp->data, cached, len); + resp->size = len; + os_free(cached); + + return GNUTLS_E_SUCCESS; +} +#endif /* 3.1.3 */ + + int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { @@ -696,6 +743,17 @@ int tls_global_set_params(void *tls_ctx, } } +#if GNUTLS_VERSION_NUMBER >= 0x030103 + os_free(global->ocsp_stapling_response); + if (params->ocsp_stapling_response) + global->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + else + global->ocsp_stapling_response = NULL; + gnutls_certificate_set_ocsp_status_request_function( + global->xcred, server_ocsp_status_req, global); +#endif /* 3.1.3 */ + global->params_set = 1; return 0; diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index 8b90d565..01a7c97d 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -331,6 +331,13 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (params->ocsp_stapling_response) + cred->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + if (params->ocsp_stapling_response_multi) + cred->ocsp_stapling_response_multi = + os_strdup(params->ocsp_stapling_response_multi); + return 0; #else /* CONFIG_TLS_INTERNAL_SERVER */ return -1; diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 1d75ba7a..62277c46 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -3890,6 +3890,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn == NULL) return -1; + if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + wpa_printf(MSG_INFO, + "OpenSSL: ocsp=3 not supported"); + return -1; + } + /* * If the engine isn't explicitly configured, and any of the * cert/key fields are actually PKCS#11 URIs, then automatically diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index ed5e4a8e..cdd15043 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -1735,9 +1735,6 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, if (nl80211_init_bss(bss)) goto failed; - if (linux_iface_up(drv->global->ioctl_sock, ifname) > 0) - drv->start_iface_up = 1; - if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params)) goto failed; @@ -2240,6 +2237,11 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP) bss->static_ap = 1; + if (first && + nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE && + linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0) + drv->start_iface_up = 1; + if (wpa_driver_nl80211_capa(drv)) return -1; diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c index 4dfdb3f9..62174682 100644 --- a/src/eap_common/eap_eke_common.c +++ b/src/eap_common/eap_eke_common.c @@ -44,9 +44,7 @@ static int eap_eke_dhcomp_len(u8 dhgroup, u8 encr) int dhlen; dhlen = eap_eke_dh_len(dhgroup); - if (dhlen < 0) - return -1; - if (encr != EAP_EKE_ENCR_AES128_CBC) + if (dhlen < 0 || encr != EAP_EKE_ENCR_AES128_CBC) return -1; return AES_BLOCK_SIZE + dhlen; } @@ -166,14 +164,11 @@ int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub) size_t pub_len, i; generator = eap_eke_dh_generator(group); - if (generator < 0 || generator > 255) + dh = eap_eke_dh_group(group); + if (generator < 0 || generator > 255 || !dh) return -1; gen = generator; - dh = eap_eke_dh_group(group); - if (dh == NULL) - return -1; - /* x = random number 2 .. p-1 */ if (random_get_bytes(ret_priv, dh->prime_len)) return -1; @@ -411,11 +406,8 @@ int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, size_t len; const struct dh_group *dh; - if (sess->encr != EAP_EKE_ENCR_AES128_CBC) - return -1; - dh = eap_eke_dh_group(sess->dhgroup); - if (dh == NULL) + if (sess->encr != EAP_EKE_ENCR_AES128_CBC || !dh) return -1; /* Decrypt peer DHComponent */ @@ -635,6 +627,7 @@ int eap_eke_prot(struct eap_eke_session *sess, if (*prot_len < block_size + data_len + pad + icv_len) { wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for Prot() data"); + return -1; } pos = prot; @@ -653,10 +646,8 @@ int eap_eke_prot(struct eap_eke_session *sess, pos += pad; } - if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0) - return -1; - - if (eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0) + if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0 || + eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0) return -1; pos += icv_len; @@ -684,9 +675,8 @@ int eap_eke_decrypt_prot(struct eap_eke_session *sess, else return -1; - if (prot_len < 2 * block_size + icv_len) - return -1; - if ((prot_len - icv_len) % block_size) + if (prot_len < 2 * block_size + icv_len || + (prot_len - icv_len) % block_size) return -1; if (eap_eke_mac(sess->mac, sess->ki, prot + block_size, @@ -737,22 +727,14 @@ int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, sess->mac = mac; sess->prf_len = eap_eke_prf_len(prf); - if (sess->prf_len < 0) - return -1; sess->nonce_len = eap_eke_nonce_len(prf); - if (sess->nonce_len < 0) - return -1; sess->auth_len = eap_eke_auth_len(prf); - if (sess->auth_len < 0) - return -1; sess->dhcomp_len = eap_eke_dhcomp_len(sess->dhgroup, sess->encr); - if (sess->dhcomp_len < 0) - return -1; sess->pnonce_len = eap_eke_pnonce_len(sess->mac); - if (sess->pnonce_len < 0) - return -1; sess->pnonce_ps_len = eap_eke_pnonce_ps_len(sess->mac); - if (sess->pnonce_ps_len < 0) + if (sess->prf_len < 0 || sess->nonce_len < 0 || sess->auth_len < 0 || + sess->dhcomp_len < 0 || sess->pnonce_len < 0 || + sess->pnonce_ps_len < 0) return -1; return 0; diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index c270832b..406c1624 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -196,8 +196,10 @@ static int eap_tls_init_connection(struct eap_sm *sm, if (config->ocsp) params->flags |= TLS_CONN_REQUEST_OCSP; - if (config->ocsp == 2) + if (config->ocsp >= 2) params->flags |= TLS_CONN_REQUIRE_OCSP; + if (config->ocsp == 3) + params->flags |= TLS_CONN_REQUIRE_OCSP_ALL; data->conn = tls_connection_init(data->ssl_ctx); if (data->conn == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c index 25b9f124..e4294bb2 100644 --- a/src/eap_peer/eap_tnc.c +++ b/src/eap_peer/eap_tnc.c @@ -10,6 +10,7 @@ #include "common.h" #include "eap_i.h" +#include "eap_config.h" #include "tncc.h" @@ -35,12 +36,16 @@ struct eap_tnc_data { static void * eap_tnc_init(struct eap_sm *sm) { struct eap_tnc_data *data; + struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = WAIT_START; - data->fragment_size = 1300; + if (config && config->fragment_size) + data->fragment_size = config->fragment_size; + else + data->fragment_size = 1300; data->tncc = tncc_init(); if (data->tncc == NULL) { os_free(data); @@ -345,11 +350,6 @@ static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, ret->decision = DECISION_UNCOND_SUCC; ret->allowNotifications = TRUE; - if (data->out_buf) { - data->state = PROC_MSG; - return eap_tnc_build_msg(data, ret, id); - } - if (tncs_done) { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_RESPONSE, eap_get_id(reqData)); diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c index 7ca956e5..99655131 100644 --- a/src/eap_peer/tncc.c +++ b/src/eap_peer/tncc.c @@ -694,6 +694,8 @@ enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc, enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION; int recommendation_msg = 0; + wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Received IF-TNCCS message", + msg, len); buf = dup_binstr(msg, len); if (buf == NULL) return TNCCS_PROCESS_ERROR; diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 0bcdb453..b24bbf89 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -308,7 +308,7 @@ static int radius_client_handle_send_error(struct radius_client_data *radius, int _errno = errno; wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno)); if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || - _errno == EBADF || _errno == ENETUNREACH) { + _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Send failed - maybe interface status changed -" diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 669f658c..64ef933e 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -270,7 +270,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * much we can do here without knowing what * exactly caused the server to misbehave. */ - wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); return -1; } diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index cc404c15..9bc0d211 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -111,7 +111,6 @@ int tls_derive_keys(struct tlsv1_client *conn, pos += conn->rl.iv_size; /* server_write_IV */ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; } else { /* * Use IV field to set the mask value for TLS v1.1. A fixed diff --git a/src/tls/tlsv1_client_ocsp.c b/src/tls/tlsv1_client_ocsp.c index 2d5cdb94..1d7b68ca 100644 --- a/src/tls/tlsv1_client_ocsp.c +++ b/src/tls/tlsv1_client_ocsp.c @@ -316,6 +316,7 @@ static int tls_process_ocsp_single_response(struct tlsv1_client *conn, static enum tls_ocsp_result tls_process_ocsp_responses(struct tlsv1_client *conn, + struct x509_certificate *cert, struct x509_certificate *issuer, const u8 *resp, size_t len) { @@ -335,8 +336,7 @@ tls_process_ocsp_responses(struct tlsv1_client *conn, hdr.class, hdr.tag); return TLS_OCSP_INVALID; } - if (tls_process_ocsp_single_response(conn, conn->server_cert, - issuer, + if (tls_process_ocsp_single_response(conn, cert, issuer, hdr.payload, hdr.length, &res) == 0) return res; @@ -350,8 +350,9 @@ tls_process_ocsp_responses(struct tlsv1_client *conn, static enum tls_ocsp_result -tls_process_basic_ocsp_response(struct tlsv1_client *conn, const u8 *resp, - size_t len) +tls_process_basic_ocsp_response(struct tlsv1_client *conn, + struct x509_certificate *srv_cert, + const u8 *resp, size_t len) { struct asn1_hdr hdr; const u8 *pos, *end; @@ -365,6 +366,7 @@ tls_process_basic_ocsp_response(struct tlsv1_client *conn, const u8 *resp, struct x509_name name; /* used if key_hash == NULL */ char buf[100]; os_time_t produced_at; + enum tls_ocsp_result res; wpa_hexdump(MSG_MSGDUMP, "OCSP: BasicOCSPResponse", resp, len); @@ -594,20 +596,20 @@ tls_process_basic_ocsp_response(struct tlsv1_client *conn, const u8 *resp, /* Ignore for now. */ } - if (!conn->server_cert) { + if (!srv_cert) { wpa_printf(MSG_DEBUG, "OCSP: Server certificate not known - cannot check OCSP response"); goto no_resp; } - if (conn->server_cert->next) { + if (srv_cert->next) { /* Issuer has already been verified in the chain */ - issuer = conn->server_cert->next; + issuer = srv_cert->next; } else { /* Find issuer from the set of trusted certificates */ for (issuer = conn->cred ? conn->cred->trusted_certs : NULL; issuer; issuer = issuer->next) { - if (x509_name_compare(&conn->server_cert->issuer, + if (x509_name_compare(&srv_cert->issuer, &issuer->subject) == 0) break; } @@ -625,7 +627,7 @@ tls_process_basic_ocsp_response(struct tlsv1_client *conn, const u8 *resp, } else { for (signer = certs; signer; signer = signer->next) { if (!ocsp_responder_id_match(signer, &name, key_hash) || - x509_name_compare(&conn->server_cert->issuer, + x509_name_compare(&srv_cert->issuer, &issuer->subject) != 0 || !(signer->ext_key_usage & X509_EXT_KEY_USAGE_OCSP) || @@ -654,8 +656,13 @@ tls_process_basic_ocsp_response(struct tlsv1_client *conn, const u8 *resp, return TLS_OCSP_INVALID; } - return tls_process_ocsp_responses(conn, issuer, responses, - responses_len); + res = tls_process_ocsp_responses(conn, srv_cert, issuer, + responses, responses_len); + if (res == TLS_OCSP_REVOKED) + srv_cert->ocsp_revoked = 1; + else if (res == TLS_OCSP_GOOD) + srv_cert->ocsp_good = 1; + return res; no_resp: x509_free_name(&name); @@ -677,6 +684,9 @@ enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn, u8 resp_status; struct asn1_oid oid; char obuf[80]; + struct x509_certificate *cert; + enum tls_ocsp_result res = TLS_OCSP_NO_RESPONSE; + enum tls_ocsp_result res_first = res; wpa_hexdump(MSG_MSGDUMP, "TLSv1: OCSPResponse", resp, len); @@ -769,5 +779,25 @@ enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn, return TLS_OCSP_INVALID; } - return tls_process_basic_ocsp_response(conn, hdr.payload, hdr.length); + cert = conn->server_cert; + while (cert) { + if (!cert->ocsp_good && !cert->ocsp_revoked) { + char sbuf[128]; + + x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); + wpa_printf(MSG_DEBUG, + "OCSP: Trying to find certificate status for %s", + sbuf); + + res = tls_process_basic_ocsp_response(conn, cert, + hdr.payload, + hdr.length); + if (cert == conn->server_cert) + res_first = res; + } + if (res == TLS_OCSP_REVOKED || cert->issuer_trusted) + break; + cert = cert->next; + } + return res == TLS_OCSP_REVOKED ? res : res_first; } diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index ff124520..244c3cb0 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -326,7 +326,7 @@ static void tls_cert_chain_failure_event(struct tlsv1_client *conn, int depth, union tls_event_data ev; char subject[128]; - if (!conn->event_cb) + if (!conn->event_cb || !cert) return; os_memset(&ev, 0, sizeof(ev)); @@ -790,14 +790,40 @@ fail: } +static enum tls_ocsp_result +tls_process_certificate_status_ocsp_response(struct tlsv1_client *conn, + const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + u32 ocsp_resp_len; + + /* opaque OCSPResponse<1..2^24-1>; */ + if (end - pos < 3) { + wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return TLS_OCSP_INVALID; + } + ocsp_resp_len = WPA_GET_BE24(pos); + pos += 3; + if (end - pos < ocsp_resp_len) { + wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return TLS_OCSP_INVALID; + } + + return tls_process_ocsp_response(conn, pos, ocsp_resp_len); +} + + static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len; u8 type, status_type; - u32 ocsp_resp_len; enum tls_ocsp_result res; + struct x509_certificate *cert; + int depth; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, @@ -850,6 +876,7 @@ static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct, * CertificateStatusType status_type; * select (status_type) { * case ocsp: OCSPResponse; + * case ocsp_multi: OCSPResponseList; * } response; * } CertificateStatus; */ @@ -862,32 +889,110 @@ static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct, wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatus status_type %u", status_type); - if (status_type != 1 /* ocsp */) { + if (status_type == 1 /* ocsp */) { + res = tls_process_certificate_status_ocsp_response( + conn, pos, end - pos); + } else if (status_type == 2 /* ocsp_multi */) { + int good = 0, revoked = 0; + u32 resp_len; + + res = TLS_OCSP_NO_RESPONSE; + + /* + * opaque OCSPResponse<0..2^24-1>; + * + * struct { + * OCSPResponse ocsp_response_list<1..2^24-1>; + * } OCSPResponseList; + */ + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponseList"); + res = TLS_OCSP_INVALID; + goto done; + } + resp_len = WPA_GET_BE24(pos); + pos += 3; + if (end - pos < resp_len) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponseList(len=%u)", + resp_len); + res = TLS_OCSP_INVALID; + goto done; + } + end = pos + resp_len; + + while (end - pos >= 3) { + resp_len = WPA_GET_BE24(pos); + pos += 3; + if (resp_len > end - pos) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponse(len=%u; left=%d) in ocsp_multi", + resp_len, (int) (end - pos)); + res = TLS_OCSP_INVALID; + break; + } + if (!resp_len) + continue; /* Skip an empty response */ + res = tls_process_certificate_status_ocsp_response( + conn, pos - 3, resp_len + 3); + if (res == TLS_OCSP_REVOKED) + revoked++; + else if (res == TLS_OCSP_GOOD) + good++; + pos += resp_len; + } + + if (revoked) + res = TLS_OCSP_REVOKED; + else if (good) + res = TLS_OCSP_GOOD; + } else { wpa_printf(MSG_DEBUG, "TLSv1: Ignore unsupported CertificateStatus"); goto skip; } - /* opaque OCSPResponse<1..2^24-1>; */ - if (end - pos < 3) { - wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse"); - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); - return -1; - } - ocsp_resp_len = WPA_GET_BE24(pos); - pos += 3; - if (end - pos < ocsp_resp_len) { - wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse"); - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); +done: + if (res == TLS_OCSP_REVOKED) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_CERTIFICATE_REVOKED); + for (cert = conn->server_cert, depth = 0; cert; + cert = cert->next, depth++) { + if (cert->ocsp_revoked) { + tls_cert_chain_failure_event( + conn, depth, cert, TLS_FAIL_REVOKED, + "certificate revoked"); + } + } return -1; } - res = tls_process_ocsp_response(conn, pos, ocsp_resp_len); - switch (res) { - case TLS_OCSP_NO_RESPONSE: - if (!(conn->flags & TLS_CONN_REQUIRE_OCSP)) - goto skip; + if (conn->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + /* + * Verify that each certificate on the chain that is not part + * of the trusted certificates has a good status. If not, + * terminate handshake. + */ + for (cert = conn->server_cert, depth = 0; cert; + cert = cert->next, depth++) { + if (!cert->ocsp_good) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + tls_cert_chain_failure_event( + conn, depth, cert, + TLS_FAIL_UNSPECIFIED, + "bad certificate status response"); + return -1; + } + if (cert->issuer_trusted) + break; + } + } + + if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && res != TLS_OCSP_GOOD) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + res == TLS_OCSP_INVALID ? TLS_ALERT_DECODE_ERROR : TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); if (conn->server_cert) tls_cert_chain_failure_event( @@ -895,28 +1000,8 @@ static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct, TLS_FAIL_UNSPECIFIED, "bad certificate status response"); return -1; - case TLS_OCSP_INVALID: - if (!(conn->flags & TLS_CONN_REQUIRE_OCSP)) - goto skip; /* ignore - process as if no response */ - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); - if (conn->server_cert) - tls_cert_chain_failure_event( - conn, 0, conn->server_cert, - TLS_FAIL_UNSPECIFIED, - "bad certificate status response"); - return -1; - case TLS_OCSP_GOOD: - wpa_printf(MSG_DEBUG, "TLSv1: OCSP response good"); - break; - case TLS_OCSP_REVOKED: - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_CERTIFICATE_REVOKED); - if (conn->server_cert) - tls_cert_chain_failure_event( - conn, 0, conn->server_cert, TLS_FAIL_REVOKED, - "certificate revoked"); - return -1; } + conn->ocsp_resp_received = 1; skip: diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c index 8e8cb5e4..04d895e6 100644 --- a/src/tls/tlsv1_client_write.c +++ b/src/tls/tlsv1_client_write.c @@ -192,6 +192,46 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) pos += 2; WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */ pos += 2; + + wpa_printf(MSG_DEBUG, + "TLSv1: Add status_request_v2 extension for OCSP stapling"); + /* ExtensionsType extension_type = status_request_v2(17) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 7); + pos += 2; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusType status_type; + * uint16 request_length; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * case ocsp_multi: OCSPStatusRequest; + * } request; + * } CertificateStatusRequestItemV2; + * + * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType; + * + * struct { + * CertificateStatusRequestItemV2 + * certificate_status_req_list<1..2^16-1>; + * } CertificateStatusRequestListV2; + */ + + /* certificate_status_req_list<1..2^16-1> */ + WPA_PUT_BE16(pos, 5); + pos += 2; + + /* CertificateStatusRequestItemV2 */ + *pos++ = 2; /* status_type = ocsp_multi(2) */ + /* OCSPStatusRequest as shown above for v1 */ + WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */ + pos += 2; + WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */ + pos += 2; } if (pos == ext_start + 2) diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h index 7a252fee..e30b15a0 100644 --- a/src/tls/tlsv1_common.h +++ b/src/tls/tlsv1_common.h @@ -170,6 +170,7 @@ enum { #define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */ #define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */ #define TLS_EXT_SIGNATURE_ALGORITHMS 13 /* RFC 5246 */ +#define TLS_EXT_STATUS_REQUEST_V2 17 /* RFC 6961 */ #define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */ #define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */ diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c index 92f97c7f..52c1ae01 100644 --- a/src/tls/tlsv1_cred.c +++ b/src/tls/tlsv1_cred.c @@ -36,6 +36,8 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred) crypto_private_key_free(cred->key); os_free(cred->dh_p); os_free(cred->dh_g); + os_free(cred->ocsp_stapling_response); + os_free(cred->ocsp_stapling_response_multi); os_free(cred); } diff --git a/src/tls/tlsv1_cred.h b/src/tls/tlsv1_cred.h index b4bfe38d..716e93c3 100644 --- a/src/tls/tlsv1_cred.h +++ b/src/tls/tlsv1_cred.h @@ -24,6 +24,9 @@ struct tlsv1_credentials { size_t dh_p_len; u8 *dh_g; /* generator */ size_t dh_g_len; + + char *ocsp_stapling_response; + char *ocsp_stapling_response_multi; }; diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h index 96d79b3a..29c66787 100644 --- a/src/tls/tlsv1_server_i.h +++ b/src/tls/tlsv1_server_i.h @@ -55,6 +55,9 @@ struct tlsv1_server { void *log_cb_ctx; int use_session_ticket; + unsigned int status_request:1; + unsigned int status_request_v2:1; + unsigned int status_request_multi:1; u8 *dh_secret; size_t dh_secret_len; diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index 8347d7ac..4aa8a019 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -46,6 +46,78 @@ static int testing_cipher_suite_filter(struct tlsv1_server *conn, u16 suite) } +static void tls_process_status_request_item(struct tlsv1_server *conn, + const u8 *req, size_t req_len) +{ + const u8 *pos, *end; + u8 status_type; + + pos = req; + end = req + req_len; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusType status_type; + * uint16 request_length; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * case ocsp_multi: OCSPStatusRequest; + * } request; + * } CertificateStatusRequestItemV2; + * + * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType; + */ + + if (end - pos < 1) + return; /* Truncated data */ + + status_type = *pos++; + wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatusType %u", status_type); + if (status_type != 1 && status_type != 2) + return; /* Unsupported status type */ + /* + * For now, only OCSP stapling is supported, so ignore the specific + * request, if any. + */ + wpa_hexdump(MSG_DEBUG, "TLSv1: OCSPStatusRequest", pos, end - pos); + + if (status_type == 2) + conn->status_request_multi = 1; +} + + +static void tls_process_status_request_v2(struct tlsv1_server *conn, + const u8 *ext, size_t ext_len) +{ + const u8 *pos, *end; + + conn->status_request_v2 = 1; + + pos = ext; + end = ext + ext_len; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusRequestItemV2 + * certificate_status_req_list<1..2^16-1>; + * } CertificateStatusRequestListV2; + */ + + while (end - pos >= 2) { + u16 len; + + len = WPA_GET_BE16(pos); + pos += 2; + if (len > end - pos) + break; /* Truncated data */ + tls_process_status_request_item(conn, pos, len); + pos += len; + } +} + + static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -267,6 +339,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, ext_len); conn->session_ticket_len = ext_len; } + } else if (ext_type == TLS_EXT_STATUS_REQUEST) { + conn->status_request = 1; + } else if (ext_type == TLS_EXT_STATUS_REQUEST_V2) { + tls_process_status_request_v2(conn, pos, + ext_len); } pos += ext_len; diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c index e7c5e225..bdc6c119 100644 --- a/src/tls/tlsv1_server_write.c +++ b/src/tls/tlsv1_server_write.c @@ -42,7 +42,7 @@ static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) static int tls_write_server_hello(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *rhdr, *hs_start, *hs_length, *ext_start; struct os_time now; size_t rlen; @@ -97,6 +97,32 @@ static int tls_write_server_hello(struct tlsv1_server *conn, /* CompressionMethod compression_method */ *pos++ = TLS_COMPRESSION_NULL; + /* Extension */ + ext_start = pos; + pos += 2; + + if (conn->status_request) { + /* Add a status_request extension with empty extension_data */ + /* ExtensionsType extension_type = status_request(5) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 0); + pos += 2; + } + + if (conn->status_request_v2) { + /* + Add a status_request_v2 extension with empty extension_data + */ + /* ExtensionsType extension_type = status_request_v2(17) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 0); + pos += 2; + } + if (conn->session_ticket && conn->session_ticket_cb) { int res = conn->session_ticket_cb( conn->session_ticket_cb_ctx, @@ -133,6 +159,11 @@ static int tls_write_server_hello(struct tlsv1_server *conn, */ } + if (pos == ext_start + 2) + pos -= 2; /* no extensions */ + else + WPA_PUT_BE16(ext_start, pos - ext_start - 2); + WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); @@ -244,6 +275,93 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, } +static int tls_write_server_certificate_status(struct tlsv1_server *conn, + u8 **msgpos, u8 *end, + int ocsp_multi, + char *ocsp_resp, + size_t ocsp_resp_len) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + + if (!ocsp_resp) { + /* + * Client did not request certificate status or there is no + * matching response cached. + */ + return 0; + } + + pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 1 + 3 + ocsp_resp_len > + (unsigned int) (end - pos)) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + tlsv1_server_log(conn, "Send CertificateStatus (multi=%d)", ocsp_multi); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + + /* body - CertificateStatus + * + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPResponse; + * case ocsp_multi: OCSPResponseList; + * } response; + * } CertificateStatus; + * + * opaque OCSPResponse<1..2^24-1>; + * + * struct { + * OCSPResponse ocsp_response_list<1..2^24-1>; + * } OCSPResponseList; + */ + + /* CertificateStatusType status_type */ + if (ocsp_multi) + *pos++ = 2; /* ocsp_multi(2) */ + else + *pos++ = 1; /* ocsp(1) */ + /* uint24 length of OCSPResponse */ + WPA_PUT_BE24(pos, ocsp_resp_len); + pos += 3; + os_memcpy(pos, ocsp_resp, ocsp_resp_len); + pos += ocsp_resp_len; + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + static int tls_write_server_key_exchange(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { @@ -810,24 +928,46 @@ static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) { u8 *msg, *end, *pos; size_t msglen; + int ocsp_multi = 0; + char *ocsp_resp = NULL; + size_t ocsp_resp_len = 0; *out_len = 0; - msglen = 1000 + tls_server_cert_chain_der_len(conn); + if (conn->status_request_multi && + conn->cred->ocsp_stapling_response_multi) { + ocsp_resp = os_readfile( + conn->cred->ocsp_stapling_response_multi, + &ocsp_resp_len); + ocsp_multi = 1; + } else if ((conn->status_request || conn->status_request_v2) && + conn->cred->ocsp_stapling_response) { + ocsp_resp = os_readfile(conn->cred->ocsp_stapling_response, + &ocsp_resp_len); + } + if (!ocsp_resp) + ocsp_resp_len = 0; + + msglen = 1000 + tls_server_cert_chain_der_len(conn) + ocsp_resp_len; msg = os_malloc(msglen); - if (msg == NULL) + if (msg == NULL) { + os_free(ocsp_resp); return NULL; + } pos = msg; end = msg + msglen; if (tls_write_server_hello(conn, &pos, end) < 0) { os_free(msg); + os_free(ocsp_resp); return NULL; } if (conn->use_session_ticket) { + os_free(ocsp_resp); + /* Abbreviated handshake using session ticket; RFC 4507 */ if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || tls_write_server_finished(conn, &pos, end) < 0) { @@ -844,12 +984,16 @@ static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) /* Full handshake */ if (tls_write_server_certificate(conn, &pos, end) < 0 || + tls_write_server_certificate_status(conn, &pos, end, ocsp_multi, + ocsp_resp, ocsp_resp_len) < 0 || tls_write_server_key_exchange(conn, &pos, end) < 0 || tls_write_server_certificate_request(conn, &pos, end) < 0 || tls_write_server_hello_done(conn, &pos, end) < 0) { os_free(msg); + os_free(ocsp_resp); return NULL; } + os_free(ocsp_resp); *out_len = pos - msg; diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c index 5521390a..75f222c4 100644 --- a/src/tls/x509v3.c +++ b/src/tls/x509v3.c @@ -1339,6 +1339,7 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, size_t left; char sbuf[128]; unsigned long value; + const u8 *subject_dn; /* tbsCertificate TBSCertificate ::= SEQUENCE */ if (asn1_get_next(buf, len, &hdr) < 0 || @@ -1436,7 +1437,6 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, return -1; /* subject Name */ - const u8 *subject_dn; subject_dn = pos; if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) return -1; @@ -2038,6 +2038,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, os_get_time(&now); for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { + cert->issuer_trusted = 0; x509_name_string(&cert->subject, buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); @@ -2123,6 +2124,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, wpa_printf(MSG_DEBUG, "X509: Trusted certificate " "found to complete the chain"); + cert->issuer_trusted = 1; chain_trusted = 1; } } diff --git a/src/tls/x509v3.h b/src/tls/x509v3.h index dcdb4a30..7df8e2ab 100644 --- a/src/tls/x509v3.h +++ b/src/tls/x509v3.h @@ -106,6 +106,11 @@ struct x509_certificate { size_t cert_len; const u8 *tbs_cert_start; size_t tbs_cert_len; + + /* Meta data used for certificate validation */ + unsigned int ocsp_good:1; + unsigned int ocsp_revoked:1; + unsigned int issuer_trusted:1; }; enum { diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index f62b30a4..95690bff 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -856,6 +856,12 @@ OBJS += src/ap/peerkey_auth.c endif endif +ifdef CONFIG_ACS +L_CFLAGS += -DCONFIG_ACS +OBJS += src/ap/acs.c +LIBS += -lm +endif + ifdef CONFIG_PCSC # PC/SC interface for smartcards (USIM, GSM SIM) L_CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 6bab7d1e..8fa35e5c 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -6,6 +6,17 @@ ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g endif +ifdef LIBS +# If LIBS is set with some global build system defaults, clone those for +# LIBS_c and LIBS_p to cover wpa_passphrase and wpa_cli as well. +ifndef LIBS_c +LIBS_c := $(LIBS) +endif +ifndef LIBS_p +LIBS_p := $(LIBS) +endif +endif + export LIBDIR ?= /usr/local/lib/ export INCDIR ?= /usr/local/include/ export BINDIR ?= /usr/local/sbin/ @@ -884,6 +895,12 @@ OBJS += ../src/ap/peerkey_auth.o endif endif +ifdef CONFIG_ACS +CFLAGS += -DCONFIG_ACS +OBJS += ../src/ap/acs.o +LIBS += -lm +endif + ifdef CONFIG_PCSC # PC/SC interface for smartcards (USIM, GSM SIM) CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 27fa2a9b..105fb1c6 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -213,6 +213,14 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, if (wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf)) return -1; +#ifdef CONFIG_ACS + if (ssid->acs) { + /* Setting channel to 0 in order to enable ACS */ + conf->channel = 0; + wpa_printf(MSG_DEBUG, "Use automatic channel selection"); + } +#endif /* CONFIG_ACS */ + if (ieee80211_is_dfs(ssid->frequency) && wpa_s->conf->country[0]) { conf->ieee80211h = 1; conf->ieee80211d = 1; @@ -558,6 +566,11 @@ static void wpas_ap_configured_cb(void *ctx) { struct wpa_supplicant *wpa_s = ctx; +#ifdef CONFIG_ACS + if (wpa_s->current_ssid && wpa_s->current_ssid->acs) + wpa_s->assoc_freq = wpa_s->ap_iface->freq; +#endif /* CONFIG_ACS */ + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); if (wpa_s->ap_configured_cb) diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index f2ae4fd9..85717e95 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1920,6 +1920,9 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(mixed_cell, 0, 1) }, { INT_RANGE(frequency, 0, 65000) }, { INT_RANGE(fixed_freq, 0, 1) }, +#ifdef CONFIG_ACS + { INT_RANGE(acs, 0, 1) }, +#endif /* CONFIG_ACS */ #ifdef CONFIG_MESH { FUNC(mesh_basic_rates) }, { INT(dot11MeshMaxRetries) }, diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 80e3e560..6ea113ec 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -747,6 +747,9 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(no_auto_peer); INT(frequency); INT(fixed_freq); +#ifdef CONFIG_ACS + INT(acs); +#endif /* CONFIG_ACS */ write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1); INT(disabled); INT(peerkey); diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index de8157a1..b296826b 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -431,6 +431,18 @@ struct wpa_ssid { */ int fixed_freq; +#ifdef CONFIG_ACS + /** + * ACS - Automatic Channel Selection for AP mode + * + * If present, it will be handled together with frequency. + * frequency will be used to determine hardware mode only, when it is + * used for both hardware mode and channel when used alone. This will + * force the channel to be set to 0, thus enabling ACS. + */ + int acs; +#endif /* CONFIG_ACS */ + /** * mesh_basic_rates - BSS Basic rate set for mesh network * diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index d1274f6d..ecf8d2dd 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -3937,6 +3937,15 @@ static int wpa_supplicant_ctrl_iface_get_capability( } #endif /* CONFIG_FIPS */ +#ifdef CONFIG_ACS + if (os_strcmp(field, "acs") == 0) { + res = os_snprintf(buf, buflen, "ACS"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_ACS */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c index 2c71b2d6..7b367513 100644 --- a/wpa_supplicant/ctrl_iface_unix.c +++ b/wpa_supplicant/ctrl_iface_unix.c @@ -1058,6 +1058,9 @@ void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) struct sockaddr_un from; socklen_t fromlen = sizeof(from); + if (priv->sock == -1) + return; + for (;;) { wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to " "attach", priv->wpa_s->ifname); diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index e2e4bdc6..8b1d1212 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -513,3 +513,29 @@ CONFIG_PEERKEY=y # OS X builds. This is only for building eapol_test. #CONFIG_OSX=y + +# Automatic Channel Selection +# This will allow wpa_supplicant to pick the channel automatically when channel +# is set to "0". +# +# TODO: Extend parser to be able to parse "channel=acs_survey" as an alternative +# to "channel=0". This would enable us to eventually add other ACS algorithms in +# similar way. +# +# Automatic selection is currently only done through initialization, later on +# we hope to do background checks to keep us moving to more ideal channels as +# time goes by. ACS is currently only supported through the nl80211 driver and +# your driver must have survey dump capability that is filled by the driver +# during scanning. +# +# TODO: In analogy to hostapd be able to customize the ACS survey algorithm with +# a newly to create wpa_supplicant.conf variable acs_num_scans. +# +# Supported ACS drivers: +# * ath9k +# * ath5k +# * ath10k +# +# For more details refer to: +# http://wireless.kernel.org/en/users/Documentation/acs +#CONFIG_ACS=y diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 1ac177e8..3c60cc12 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -3942,6 +3942,22 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->mesh_peer.ie_len); #endif /* CONFIG_MESH */ break; + case EVENT_SURVEY: +#ifdef CONFIG_AP + if (!wpa_s->ap_iface) + break; + hostapd_event_get_survey(wpa_s->ap_iface, + &data->survey_results); +#endif /* CONFIG_AP */ + break; + case EVENT_ACS_CHANNEL_SELECTED: +#ifdef CONFIG_ACS + if (!wpa_s->ap_iface) + break; + hostapd_acs_channel_selected(wpa_s->ap_iface->bss[0], + &data->acs_selected_channels); +#endif /* CONFIG_ACS */ + break; default: wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); break; diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c index 7ebd4d2e..4caa55ce 100644 --- a/wpa_supplicant/mesh_mpm.c +++ b/wpa_supplicant/mesh_mpm.c @@ -20,11 +20,11 @@ #include "mesh_rsn.h" struct mesh_peer_mgmt_ie { - const u8 *proto_id; - const u8 *llid; - const u8 *plid; - const u8 *reason; - const u8 *pmk; + const u8 *proto_id; /* Mesh Peering Protocol Identifier (2 octets) */ + const u8 *llid; /* Local Link ID (2 octets) */ + const u8 *plid; /* Peer Link ID (conditional, 2 octets) */ + const u8 *reason; /* Reason Code (conditional, 2 octets) */ + const u8 *chosen_pmk; /* Chosen PMK (optional, 16 octets) */ }; static void plink_timer(void *eloop_ctx, void *user_data); @@ -72,10 +72,10 @@ static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s, { os_memset(mpm_ie, 0, sizeof(*mpm_ie)); - /* remove optional PMK at end */ - if (len >= 16) { - len -= 16; - mpm_ie->pmk = ie + len - 16; + /* Remove optional Chosen PMK field at end */ + if (len >= SAE_PMKID_LEN) { + mpm_ie->chosen_pmk = ie + len - SAE_PMKID_LEN; + len -= SAE_PMKID_LEN; } if ((action_field == PLINK_OPEN && len != 4) || @@ -101,8 +101,8 @@ static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s, len -= 2; } - /* plid, present for confirm, and possibly close */ - if (len) + /* Peer Link ID, present for confirm, and possibly close */ + if (len >= 2) mpm_ie->plid = ie; return 0; @@ -1014,6 +1014,7 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, if ((mconf->security & MESH_CONF_SEC_AMPE) && mesh_rsn_process_ampe(wpa_s, sta, &elems, &mgmt->u.action.category, + peer_mgmt_ie.chosen_pmk, ies, ie_len)) { wpa_printf(MSG_DEBUG, "MPM: RSN process rejected frame"); return; diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c index 747f1ae6..5d88274b 100644 --- a/wpa_supplicant/mesh_rsn.c +++ b/wpa_supplicant/mesh_rsn.c @@ -328,10 +328,7 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid) { - /* don't expect wpa auth to cache the pmkid for now */ - rsn_pmkid(sta->sae->pmk, PMK_LEN, rsn->wpa_s->own_addr, - sta->addr, pmkid, - wpa_key_mgmt_sha256(wpa_auth_sta_key_mgmt(sta->wpa_sm))); + os_memcpy(pmkid, sta->sae->pmkid, SAE_PMKID_LEN); } @@ -503,6 +500,7 @@ free: int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, struct ieee802_11_elems *elems, const u8 *cat, + const u8 *chosen_pmk, const u8 *start, size_t elems_len) { int ret = 0; @@ -516,6 +514,12 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, (elems->mic - 2) - cat }; + if (chosen_pmk && os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN)) { + wpa_msg(wpa_s, MSG_DEBUG, + "Mesh RSN: Invalid PMKID (Chosen PMK did not match calculated PMKID)"); + return -1; + } + if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie"); return -1; diff --git a/wpa_supplicant/mesh_rsn.h b/wpa_supplicant/mesh_rsn.h index b1471b2d..89601d40 100644 --- a/wpa_supplicant/mesh_rsn.h +++ b/wpa_supplicant/mesh_rsn.h @@ -30,6 +30,7 @@ int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta, const u8 *cat, struct wpabuf *buf); int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, struct ieee802_11_elems *elems, const u8 *cat, + const u8 *chosen_pmk, const u8 *start, size_t elems_len); void mesh_auth_timer(void *eloop_ctx, void *user_data); diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 45dae509..d6acbd03 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -774,13 +774,6 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role, case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: - /* - * Peer has an active GO, so if the role allows it and - * we do not have any active roles, become client. - */ - if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s && !cli_wpa_s) - return P2PS_SETUP_CLIENT; - if (cli_wpa_s) conncap = P2PS_SETUP_GROUP_OWNER; else { diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 29683bc4..e5889929 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -5227,7 +5227,7 @@ int wpa_supplicant_run(struct wpa_global *global) if (global->params.wait_for_monitor) { for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) - if (wpa_s->ctrl_iface) + if (wpa_s->ctrl_iface && !wpa_s->p2p_mgmt) wpa_supplicant_ctrl_iface_wait( wpa_s->ctrl_iface); } diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index e33b720a..e204061f 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -586,6 +586,8 @@ fast_reauth=1 # 0 = do not use OCSP stapling (TLS certificate status extension) # 1 = try to use OCSP stapling, but not require response # 2 = require valid OCSP stapling response +# 3 = require valid OCSP stapling response for all not-trusted +# certificates in the server certificate chain # # sim_num: Identifier for which SIM to use in multi-SIM devices # @@ -748,8 +750,18 @@ fast_reauth=1 # IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically # generated WEP keys # NONE = WPA is not used; plaintext or static WEP could be used +# WPA-NONE = WPA-None for IBSS (deprecated; use proto=RSN key_mgmt=WPA-PSK +# instead) +# FT-PSK = Fast BSS Transition (IEEE 802.11r) with pre-shared key +# FT-EAP = Fast BSS Transition (IEEE 802.11r) with EAP authentication # WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms # WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms +# SAE = Simultaneous authentication of equals; pre-shared key/password -based +# authentication with stronger security than WPA-PSK especially when using +# not that strong password +# FT-SAE = SAE with FT +# WPA-EAP-SUITE-B = Suite B 128-bit level +# WPA-EAP-SUITE-B-192 = Suite B 192-bit level # If not set, this defaults to: WPA-PSK WPA-EAP # # ieee80211w: whether management frame protection is enabled @@ -1074,6 +1086,8 @@ fast_reauth=1 # 0 = do not use OCSP stapling (TLS certificate status extension) # 1 = try to use OCSP stapling, but not require response # 2 = require valid OCSP stapling response +# 3 = require valid OCSP stapling response for all not-trusted +# certificates in the server certificate chain # # openssl_ciphers: OpenSSL specific cipher configuration # This can be used to override the global openssl_ciphers configuration