diff options
36 files changed, 1148 insertions, 445 deletions
diff --git a/hostapd/config_file.c b/hostapd/config_file.c index bd664743..949a9d17 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -22,6 +22,14 @@ #include "config_file.h" +#ifndef CONFIG_NO_RADIUS +#ifdef EAP_SERVER +static struct hostapd_radius_attr * +hostapd_parse_radius_attr(const char *value); +#endif /* EAP_SERVER */ +#endif /* CONFIG_NO_RADIUS */ + + #ifndef CONFIG_NO_VLAN static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, const char *fname) @@ -208,7 +216,7 @@ static int hostapd_config_read_eap_user(const char *fname, FILE *f; char buf[512], *pos, *start, *pos2; int line = 0, ret = 0, num_methods; - struct hostapd_eap_user *user, *tail = NULL; + struct hostapd_eap_user *user = NULL, *tail = NULL; if (!fname) return 0; @@ -242,6 +250,27 @@ static int hostapd_config_read_eap_user(const char *fname, if (buf[0] == '\0') continue; +#ifndef CONFIG_NO_RADIUS + if (user && os_strncmp(buf, "radius_accept_attr=", 19) == 0) { + struct hostapd_radius_attr *attr, *a; + attr = hostapd_parse_radius_attr(buf + 19); + if (attr == NULL) { + wpa_printf(MSG_ERROR, "Invalid radius_auth_req_attr: %s", + buf + 19); + goto failed; + } + if (user->accept_attr == NULL) { + user->accept_attr = attr; + } else { + a = user->accept_attr; + while (a->next) + a = a->next; + a->next = attr; + } + continue; + } +#endif /* CONFIG_NO_RADIUS */ + user = NULL; if (buf[0] != '"' && buf[0] != '*') { @@ -468,11 +497,8 @@ static int hostapd_config_read_eap_user(const char *fname, continue; failed: - if (user) { - os_free(user->password); - os_free(user->identity); - os_free(user); - } + if (user) + hostapd_config_free_eap_user(user); ret = -1; break; } diff --git a/hostapd/hostapd.eap_user b/hostapd/hostapd.eap_user index 12a2c612..00edc95a 100644 --- a/hostapd/hostapd.eap_user +++ b/hostapd/hostapd.eap_user @@ -48,6 +48,12 @@ # TTLS-CHAP, TTLS-MSCHAP, TTLS-MSCHAPV2. TTLS-PAP and TTLS-CHAP require a # plaintext password while TTLS-MSCHAP and TTLS-MSCHAPV2 can use NT password # hash. +# +# Arbitrary RADIUS attributes can be added into Access-Accept packets similarly +# to the way radius_auth_req_attr is used for Access-Request packet in +# hostapd.conf. For EAP server, this is configured separately for each user +# entry with radius_accept_attr=<value> line(s) following the main user entry +# line. # Phase 1 users "user" MD5 "password" diff --git a/hostapd/hostapd.eap_user_sqlite b/hostapd/hostapd.eap_user_sqlite index 2c1f130f..826db349 100644 --- a/hostapd/hostapd.eap_user_sqlite +++ b/hostapd/hostapd.eap_user_sqlite @@ -16,3 +16,11 @@ INSERT INTO users(identity,methods,password,phase2) VALUES ('DOMAIN\mschapv2 use INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS'); INSERT INTO wildcards(identity,methods) VALUES ('0','AKA'); + +CREATE TABLE authlog( + timestamp TEXT, + session TEXT, + nas_ip TEXT, + username TEXT, + note TEXT +); diff --git a/hostapd/main.c b/hostapd/main.c index 68bc9b58..a9d7da51 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -729,6 +729,8 @@ int main(int argc, char *argv[]) hostapd_global_ctrl_iface_deinit(&interfaces); /* Deinitialize all interfaces */ for (i = 0; i < interfaces.count; i++) { + if (!interfaces.iface[i]) + continue; interfaces.iface[i]->driver_ap_teardown = !!(interfaces.iface[i]->drv_flags & WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index b995892c..f744985a 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -388,8 +388,9 @@ static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr) } -static void hostapd_config_free_eap_user(struct hostapd_eap_user *user) +void hostapd_config_free_eap_user(struct hostapd_eap_user *user) { + hostapd_config_free_radius_attr(user->accept_attr); os_free(user->identity); os_free(user->password); os_free(user); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index e1e34e2d..f6ca8b1f 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -128,6 +128,7 @@ struct hostapd_eap_user { * nt_password_hash() */ unsigned int remediation:1; int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ + struct hostapd_radius_attr *accept_attr; }; struct hostapd_radius_attr { @@ -601,6 +602,7 @@ int hostapd_mac_comp(const void *a, const void *b); int hostapd_mac_comp_empty(const void *a); struct hostapd_config * hostapd_config_defaults(void); void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); +void hostapd_config_free_eap_user(struct hostapd_eap_user *user); void hostapd_config_free_bss(struct hostapd_bss_config *conf); void hostapd_config_free(struct hostapd_config *conf); int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 7691012f..6e3decd8 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -81,6 +81,7 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, user->force_version = eap_user->force_version; user->ttls_auth = eap_user->ttls_auth; user->remediation = eap_user->remediation; + user->accept_attr = eap_user->accept_attr; return 0; } @@ -114,6 +115,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) srv.eap_req_id_text_len = conf->eap_req_id_text_len; srv.pwd_group = conf->pwd_group; srv.server_id = conf->server_id ? conf->server_id : "hostapd"; + srv.sqlite_file = conf->eap_user_sqlite; #ifdef CONFIG_RADIUS_TEST srv.dump_msk_file = conf->dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index bfa3c49c..bc5bb6cf 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -760,7 +760,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) return -1; } - if (wpa_debug_level == MSG_MSGDUMP) + if (wpa_debug_level <= MSG_MSGDUMP) conf->radius->msg_dumps = 1; #ifndef CONFIG_NO_RADIUS hapd->radius = radius_client_init(hapd, conf->radius); diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index be7df51b..9a705a45 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -24,7 +24,6 @@ enum wps_event; union wps_event_data; struct hostapd_iface; -struct hostapd_dynamic_iface; struct hapd_interfaces { int (*reload_config)(struct hostapd_iface *iface); @@ -37,7 +36,6 @@ struct hapd_interfaces { int (*driver_init)(struct hostapd_iface *iface); size_t count; - size_t count_dynamic; int global_ctrl_sock; char *global_iface_path; char *global_iface_name; @@ -45,7 +43,6 @@ struct hapd_interfaces { gid_t ctrl_iface_group; #endif /* CONFIG_NATIVE_WINDOWS */ struct hostapd_iface **iface; - struct hostapd_dynamic_iface **dynamic_iface; size_t terminate_on_error; }; @@ -358,16 +355,6 @@ struct hostapd_iface { void (*scan_cb)(struct hostapd_iface *iface); }; -/** - * struct hostapd_dynamic_iface - hostapd per dynamically allocated - * or added interface data structure - */ -struct hostapd_dynamic_iface { - char parent[IFNAMSIZ + 1]; - char iface[IFNAMSIZ + 1]; - unsigned int usage; -}; - /* hostapd.c */ int hostapd_for_each_interface(struct hapd_interfaces *interfaces, int (*cb)(struct hostapd_iface *iface, diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 0b90e3b2..c7d051be 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -473,7 +473,6 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; - u8 addr[ETH_ALEN]; if (!(sta->flags & WLAN_STA_AUTH)) { if (sta->flags & WLAN_STA_GAS) { @@ -484,6 +483,8 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) return; } + hostapd_drv_sta_deauth(hapd, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); mlme_deauthenticate_indication(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -491,9 +492,7 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) "session timeout"); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; - os_memcpy(addr, sta->addr, ETH_ALEN); ap_free_sta(hapd, sta); - hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index 509e557c..4e4a3526 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -474,123 +474,6 @@ static int vlan_set_name_type(unsigned int name_type) #endif /* CONFIG_VLAN_NETLINK */ -/** - * Increase the usage counter for given parent/ifname combination. - * If create is set, then this iface is added to the global list. - * Returns - * -1 on error - * 0 if iface is not in list - * 1 if iface is in list (was there or has been added) - */ -static int hapd_get_dynamic_iface(const char *parent, const char *ifname, - int create, struct hostapd_data *hapd) -{ - size_t i; - struct hostapd_dynamic_iface *j = NULL, **tmp; - struct hapd_interfaces *hapd_global = hapd->iface->interfaces; - - if (!parent) - parent = ""; - - for (i = 0; i < hapd_global->count_dynamic; i++) { - j = hapd_global->dynamic_iface[i]; - if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 && - os_strncmp(j->parent, parent, sizeof(j->parent)) == 0) - break; - } - if (i < hapd_global->count_dynamic) { - j->usage++; - return 1; - } - - /* new entry required */ - if (!create) - return 0; - - j = os_zalloc(sizeof(*j)); - if (!j) - return -1; - os_strlcpy(j->iface, ifname, sizeof(j->iface)); - os_strlcpy(j->parent, parent, sizeof(j->parent)); - - tmp = os_realloc_array(hapd_global->dynamic_iface, i + 1, - sizeof(*hapd_global->dynamic_iface)); - if (!tmp) { - wpa_printf(MSG_ERROR, "VLAN: Failed to allocate memory in %s", - __func__); - return -1; - } - hapd_global->count_dynamic++; - hapd_global->dynamic_iface = tmp; - hapd_global->dynamic_iface[i] = j; - - return 1; -} - - -/** - * Decrease the usage counter for given ifname. - * Returns - * -1 on error or if iface was not found - * 0 if iface was found and is still present - * 1 if iface was removed from global list - */ -static int hapd_put_dynamic_iface(const char *parent, const char *ifname, - struct hostapd_data *hapd) -{ - size_t i; - struct hostapd_dynamic_iface *j = NULL, **tmp; - struct hapd_interfaces *hapd_glob = hapd->iface->interfaces; - - if (!parent) - parent = ""; - - for (i = 0; i < hapd_glob->count_dynamic; i++) { - j = hapd_glob->dynamic_iface[i]; - if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 && - os_strncmp(j->parent, parent, sizeof(j->parent)) == 0) - break; - } - - if (i == hapd_glob->count_dynamic) { - /* - * Interface not in global list. This can happen if alloc in - * _get_ failed. - */ - return -1; - } - - if (j->usage > 0) { - j->usage--; - return 0; - } - - os_free(j); - for (; i < hapd_glob->count_dynamic - 1; i++) - hapd_glob->dynamic_iface[i] = hapd_glob->dynamic_iface[i + 1]; - hapd_glob->dynamic_iface[hapd_glob->count_dynamic - 1] = NULL; - hapd_glob->count_dynamic--; - - if (hapd_glob->count_dynamic == 0) { - os_free(hapd_glob->dynamic_iface); - hapd_glob->dynamic_iface = NULL; - return 1; - } - - tmp = os_realloc_array(hapd_glob->dynamic_iface, - hapd_glob->count_dynamic, - sizeof(*hapd_glob->dynamic_iface)); - if (!tmp) { - wpa_printf(MSG_ERROR, "VLAN: Failed to release memory in %s", - __func__); - return -1; - } - hapd_glob->dynamic_iface = tmp; - - return 1; -} - - static void vlan_newlink(char *ifname, struct hostapd_data *hapd) { char vlan_ifname[IFNAMSIZ]; @@ -598,7 +481,6 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) struct hostapd_vlan *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; int vlan_naming = hapd->conf->ssid.vlan_naming; - int ret; wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); @@ -618,9 +500,7 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) "brvlan%d", vlan->vlan_id); } - ret = br_addbr(br_name); - if (hapd_get_dynamic_iface(NULL, br_name, ret == 0, - hapd)) + if (!br_addbr(br_name)) vlan->clean |= DVLAN_CLEAN_BR; ifconfig_up(br_name); @@ -638,24 +518,17 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) "vlan%d", vlan->vlan_id); ifconfig_up(tagged_interface); - ret = vlan_add(tagged_interface, vlan->vlan_id, - vlan_ifname); - if (hapd_get_dynamic_iface(NULL, vlan_ifname, - ret == 0, hapd)) + if (!vlan_add(tagged_interface, vlan->vlan_id, + vlan_ifname)) vlan->clean |= DVLAN_CLEAN_VLAN; - ret = br_addif(br_name, vlan_ifname); - if (hapd_get_dynamic_iface(br_name, - vlan_ifname, - ret == 0, hapd)) + if (!br_addif(br_name, vlan_ifname)) vlan->clean |= DVLAN_CLEAN_VLAN_PORT; ifconfig_up(vlan_ifname); } - ret = br_addif(br_name, ifname); - if (hapd_get_dynamic_iface(br_name, ifname, ret == 0, - hapd)) + if (!br_addif(br_name, ifname)) vlan->clean |= DVLAN_CLEAN_WLAN_PORT; ifconfig_up(ifname); @@ -694,8 +567,7 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) "brvlan%d", vlan->vlan_id); } - if ((vlan->clean & DVLAN_CLEAN_WLAN_PORT) && - hapd_put_dynamic_iface(br_name, vlan->ifname, hapd)) + if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) br_delif(br_name, vlan->ifname); if (tagged_interface) { @@ -709,20 +581,15 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vlan->vlan_id); - if ((vlan->clean & DVLAN_CLEAN_VLAN_PORT) && - hapd_put_dynamic_iface(br_name, vlan_ifname, - hapd)) + if (vlan->clean & DVLAN_CLEAN_VLAN_PORT) br_delif(br_name, vlan_ifname); ifconfig_down(vlan_ifname); - if ((vlan->clean & DVLAN_CLEAN_VLAN) && - hapd_put_dynamic_iface(NULL, vlan_ifname, - hapd)) + if (vlan->clean & DVLAN_CLEAN_VLAN) vlan_rem(vlan_ifname); } if ((vlan->clean & DVLAN_CLEAN_BR) && - hapd_put_dynamic_iface(NULL, br_name, hapd) && br_getnumports(br_name) == 0) { ifconfig_down(br_name); br_delbr(br_name); diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 88afae48..81e588fb 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -537,4 +537,14 @@ int __must_check tls_connection_set_session_ticket_cb( void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, void *ctx); +void tls_connection_set_log_cb(struct tls_connection *conn, + void (*log_cb)(void *ctx, const char *msg), + void *ctx); + +#define TLS_BREAK_VERIFY_DATA BIT(0) +#define TLS_BREAK_SRV_KEY_X_HASH BIT(1) +#define TLS_BREAK_SRV_KEY_X_SIGNATURE BIT(2) + +void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags); + #endif /* TLS_H */ diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index 91f06900..6563ed21 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -28,6 +28,7 @@ struct tls_global { struct tls_connection { struct tlsv1_client *client; struct tlsv1_server *server; + struct tls_global *global; }; @@ -85,6 +86,7 @@ struct tls_connection * tls_connection_init(void *tls_ctx) conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; + conn->global = global; #ifdef CONFIG_TLS_INTERNAL_CLIENT if (!global->server) { @@ -109,6 +111,28 @@ struct tls_connection * tls_connection_init(void *tls_ctx) } +#ifdef CONFIG_TESTING_OPTIONS +#ifdef CONFIG_TLS_INTERNAL_SERVER +void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags) +{ + if (conn->server) + tlsv1_server_set_test_flags(conn->server, flags); +} +#endif /* CONFIG_TLS_INTERNAL_SERVER */ +#endif /* CONFIG_TESTING_OPTIONS */ + + +void tls_connection_set_log_cb(struct tls_connection *conn, + void (*log_cb)(void *ctx, const char *msg), + void *ctx) +{ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + tlsv1_server_set_log_cb(conn->server, log_cb, ctx); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ +} + + void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) { if (conn == NULL) diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 197b232f..698a5ac0 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -35,6 +35,7 @@ struct eap_user { unsigned int remediation:1; int ttls_auth; /* bitfield of * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */ + struct hostapd_radius_attr *accept_attr; }; struct eap_eapol_interface { @@ -80,6 +81,7 @@ struct eapol_callbacks { int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); const char * (*get_eap_req_id_text)(void *ctx, size_t *len); + void (*log_msg)(void *ctx, const char *msg); }; struct eap_config { @@ -108,6 +110,10 @@ struct eap_config { const u8 *server_id; size_t server_id_len; + +#ifdef CONFIG_TESTING_OPTIONS + u32 tls_test_flags; +#endif /* CONFIG_TESTING_OPTIONS */ }; diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index 003e2020..3a6802b7 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -191,10 +191,16 @@ struct eap_sm { const u8 *server_id; size_t server_id_len; + +#ifdef CONFIG_TESTING_OPTIONS + u32 tls_test_flags; +#endif /* CONFIG_TESTING_OPTIONS */ }; int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, int phase2); +void eap_log_msg(struct eap_sm *sm, const char *fmt, ...) +PRINTF_FORMAT(2, 3); void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len); #endif /* EAP_I_H */ diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index 233e2726..65d00dda 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -119,6 +119,32 @@ int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, } +void eap_log_msg(struct eap_sm *sm, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + + if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->log_msg == NULL) + return; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) + return; + va_start(ap, fmt); + vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + + sm->eapol_cb->log_msg(sm->eapol_ctx, buf); + + os_free(buf); +} + + SM_STATE(EAP, DISABLED) { SM_ENTRY(EAP, DISABLED); @@ -366,6 +392,7 @@ try_another_method: } if (sm->m == NULL) { wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method"); + eap_log_msg(sm, "Could not find suitable EAP method"); sm->decision = DECISION_FAILURE; return; } @@ -377,6 +404,8 @@ try_another_method: wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "vendor=%u method=%u", vendor, sm->currentMethod); + eap_log_msg(sm, "Propose EAP method vendor=%u method=%u", + vendor, sm->currentMethod); } @@ -693,6 +722,7 @@ SM_STEP(EAP) "respMethod=%d currentMethod=%d", sm->rxResp, sm->respId, sm->currentId, sm->respMethod, sm->currentMethod); + eap_log_msg(sm, "Discard received EAP message"); SM_ENTER(EAP, DISCARD); } break; @@ -1297,6 +1327,10 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->server_id = conf->server_id; sm->server_id_len = conf->server_id_len; +#ifdef CONFIG_TESTING_OPTIONS + sm->tls_test_flags = conf->tls_test_flags; +#endif /* CONFIG_TESTING_OPTIONS */ + wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); return sm; diff --git a/src/eap_server/eap_server_identity.c b/src/eap_server/eap_server_identity.c index 51dc4e8b..b3c20873 100644 --- a/src/eap_server/eap_server_identity.c +++ b/src/eap_server/eap_server_identity.c @@ -102,6 +102,7 @@ static void eap_identity_process(struct eap_sm *sm, void *priv, struct eap_identity_data *data = priv; const u8 *pos; size_t len; + char *buf; if (data->pick_up) { if (eap_identity_check(sm, data, respData)) { @@ -119,6 +120,12 @@ static void eap_identity_process(struct eap_sm *sm, void *priv, return; /* Should not happen - frame already validated */ wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len); + buf = os_malloc(len * 3 + 1); + if (buf) { + printf_encode(buf, len * 3 + 1, pos, len); + eap_log_msg(sm, "EAP-Response/Identity '%s'", buf); + os_free(buf); + } if (sm->identity) sm->update_user = TRUE; os_free(sm->identity); diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index 3153d2ec..790c7195 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -290,6 +290,7 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, const u8 *username, *user; size_t username_len, user_len; int res; + char *buf; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, &len); @@ -329,6 +330,13 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); + buf = os_malloc(name_len * 3 + 1); + if (buf) { + printf_encode(buf, name_len * 3 + 1, name, name_len); + eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf); + os_free(buf); + } + /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index de5ab0dd..01853e68 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -34,6 +34,15 @@ struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, } +#ifdef CONFIG_TLS_INTERNAL +static void eap_server_tls_log_cb(void *ctx, const char *msg) +{ + struct eap_sm *sm = ctx; + eap_log_msg(sm, "TLS: %s", msg); +} +#endif /* CONFIG_TLS_INTERNAL */ + + int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer) { @@ -52,6 +61,13 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, return -1; } +#ifdef CONFIG_TLS_INTERNAL + tls_connection_set_log_cb(data->conn, eap_server_tls_log_cb, sm); +#ifdef CONFIG_TESTING_OPTIONS + tls_connection_set_test_flags(data->conn, sm->tls_test_flags); +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_TLS_INTERNAL */ + if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) { wpa_printf(MSG_INFO, "SSL: Failed to configure verification " "of TLS peer certificate"); diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index 647bd2fa..24225a45 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -984,6 +984,16 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, } if (parse.user_name) { + char *nbuf; + nbuf = os_malloc(parse.user_name_len * 3 + 1); + if (nbuf) { + printf_encode(nbuf, parse.user_name_len * 3 + 1, + parse.user_name, + parse.user_name_len); + eap_log_msg(sm, "TTLS-User-Name '%s'", nbuf); + os_free(nbuf); + } + os_free(sm->identity); sm->identity = os_malloc(parse.user_name_len); if (sm->identity == NULL) { diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index 525bdeef..a76fa13b 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -1023,7 +1023,8 @@ static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) static struct eapol_callbacks eapol_cb = { eapol_sm_get_eap_user, - eapol_sm_get_eap_req_id_text + eapol_sm_get_eap_req_id_text, + NULL }; diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 6b86932a..c929fe18 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -8,11 +8,16 @@ #include "includes.h" #include <net/if.h> +#ifdef CONFIG_SQLITE +#include <sqlite3.h> +#endif /* CONFIG_SQLITE */ #include "common.h" #include "radius.h" #include "eloop.h" #include "eap_server/eap.h" +#include "ap/ap_config.h" +#include "crypto/tls.h" #include "radius_server.h" /** @@ -68,6 +73,8 @@ struct radius_session { unsigned int sess_id; struct eap_sm *eap; struct eap_eapol_interface *eap_if; + char *username; /* from User-Name attribute */ + char *nas_ip; struct radius_msg *last_msg; char *last_from_addr; @@ -79,6 +86,8 @@ struct radius_session { u8 last_authenticator[16]; unsigned int remediation:1; + + struct hostapd_radius_attr *accept_attr; }; /** @@ -312,6 +321,10 @@ struct radius_server_data { char *subscr_remediation_url; u8 subscr_remediation_method; + +#ifdef CONFIG_SQLITE + sqlite3 *db; +#endif /* CONFIG_SQLITE */ }; @@ -329,6 +342,52 @@ static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx); static void radius_server_session_remove_timeout(void *eloop_ctx, void *timeout_ctx); +void srv_log(struct radius_session *sess, const char *fmt, ...) +PRINTF_FORMAT(2, 3); + +void srv_log(struct radius_session *sess, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) + return; + va_start(ap, fmt); + vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + + RADIUS_DEBUG("[0x%x %s] %s", sess->sess_id, sess->nas_ip, buf); + +#ifdef CONFIG_SQLITE + if (sess->server->db) { + char *sql; + sql = sqlite3_mprintf("INSERT INTO authlog" + "(timestamp,session,nas_ip,username,note)" + " VALUES (" + "strftime('%%Y-%%m-%%d %%H:%%M:%%f'," + "'now'),%u,%Q,%Q,%Q)", + sess->sess_id, sess->nas_ip, + sess->username, buf); + if (sql) { + if (sqlite3_exec(sess->server->db, sql, NULL, NULL, + NULL) != SQLITE_OK) { + RADIUS_ERROR("Failed to add authlog entry into sqlite database: %s", + sqlite3_errmsg(sess->server->db)); + } + sqlite3_free(sql); + } + } +#endif /* CONFIG_SQLITE */ + + os_free(buf); +} + static struct radius_client * radius_server_get_client(struct radius_server_data *data, struct in_addr *addr, @@ -394,6 +453,8 @@ static void radius_server_session_free(struct radius_server_data *data, radius_msg_free(sess->last_msg); os_free(sess->last_from_addr); radius_msg_free(sess->last_reply); + os_free(sess->username); + os_free(sess->nas_ip); os_free(sess); data->num_sess--; } @@ -473,47 +534,104 @@ radius_server_new_session(struct radius_server_data *data, } +#ifdef CONFIG_TESTING_OPTIONS +static void radius_server_testing_options_tls(struct radius_session *sess, + const char *tls, + struct eap_config *eap_conf) +{ + int test = atoi(tls); + + switch (test) { + case 1: + srv_log(sess, "TLS test - break VerifyData"); + eap_conf->tls_test_flags = TLS_BREAK_VERIFY_DATA; + break; + case 2: + srv_log(sess, "TLS test - break ServerKeyExchange ServerParams hash"); + eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_HASH; + break; + case 3: + srv_log(sess, "TLS test - break ServerKeyExchange ServerParams Signature"); + eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_SIGNATURE; + break; + default: + srv_log(sess, "Unrecognized TLS test"); + break; + } +} +#endif /* CONFIG_TESTING_OPTIONS */ + +static void radius_server_testing_options(struct radius_session *sess, + struct eap_config *eap_conf) +{ +#ifdef CONFIG_TESTING_OPTIONS + const char *pos; + + pos = os_strstr(sess->username, "@test-"); + if (pos == NULL) + return; + pos += 6; + if (os_strncmp(pos, "tls-", 4) == 0) + radius_server_testing_options_tls(sess, pos + 4, eap_conf); + else + srv_log(sess, "Unrecognized test: %s", pos); +#endif /* CONFIG_TESTING_OPTIONS */ +} + + static struct radius_session * radius_server_get_new_session(struct radius_server_data *data, struct radius_client *client, - struct radius_msg *msg) + struct radius_msg *msg, const char *from_addr) { u8 *user; size_t user_len; int res; struct radius_session *sess; struct eap_config eap_conf; + struct eap_user tmp; RADIUS_DEBUG("Creating a new session"); - user = os_malloc(256); - if (user == NULL) { - return NULL; - } - res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256); - if (res < 0 || res > 256) { + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &user, + &user_len, NULL) < 0) { RADIUS_DEBUG("Could not get User-Name"); - os_free(user); return NULL; } - user_len = res; RADIUS_DUMP_ASCII("User-Name", user, user_len); - res = data->get_eap_user(data->conf_ctx, user, user_len, 0, NULL); - os_free(user); + os_memset(&tmp, 0, sizeof(tmp)); + res = data->get_eap_user(data->conf_ctx, user, user_len, 0, &tmp); + os_free(tmp.password); - if (res == 0) { - RADIUS_DEBUG("Matching user entry found"); - sess = radius_server_new_session(data, client); - if (sess == NULL) { - RADIUS_DEBUG("Failed to create a new session"); - return NULL; - } - } else { + if (res != 0) { RADIUS_DEBUG("User-Name not found from user database"); return NULL; } + RADIUS_DEBUG("Matching user entry found"); + sess = radius_server_new_session(data, client); + if (sess == NULL) { + RADIUS_DEBUG("Failed to create a new session"); + return NULL; + } + sess->accept_attr = tmp.accept_attr; + + sess->username = os_malloc(user_len * 2 + 1); + if (sess->username == NULL) { + radius_server_session_free(data, sess); + return NULL; + } + printf_encode(sess->username, user_len * 2 + 1, user, user_len); + + sess->nas_ip = os_strdup(from_addr); + if (sess->nas_ip == NULL) { + radius_server_session_free(data, sess); + return NULL; + } + + srv_log(sess, "New session created"); + os_memset(&eap_conf, 0, sizeof(eap_conf)); eap_conf.ssl_ctx = data->ssl_ctx; eap_conf.msg_ctx = data->msg_ctx; @@ -533,6 +651,7 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.pwd_group = data->pwd_group; eap_conf.server_id = (const u8 *) data->server_id; eap_conf.server_id_len = os_strlen(data->server_id); + radius_server_testing_options(sess, &eap_conf); sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); if (sess->eap == NULL) { @@ -661,6 +780,19 @@ radius_server_encapsulate_eap(struct radius_server_data *data, return NULL; } + if (code == RADIUS_CODE_ACCESS_ACCEPT) { + struct hostapd_radius_attr *attr; + for (attr = sess->accept_attr; attr; attr = attr->next) { + if (!radius_msg_add_attr(msg, attr->type, + wpabuf_head(attr->val), + wpabuf_len(attr->val))) { + wpa_printf(MSG_ERROR, "Could not add RADIUS attribute"); + radius_msg_free(msg); + return NULL; + } + } + } + if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, client->shared_secret_len, hdr->authenticator) < 0) { @@ -769,7 +901,8 @@ static int radius_server_request(struct radius_server_data *data, from_addr, from_port); return -1; } else { - sess = radius_server_get_new_session(data, client, msg); + sess = radius_server_get_new_session(data, client, msg, + from_addr); if (sess == NULL) { RADIUS_DEBUG("Could not create a new session"); radius_server_reject(data, client, msg, from, fromlen, @@ -855,6 +988,10 @@ static int radius_server_request(struct radius_server_data *data, if (sess->eap_if->eapSuccess || sess->eap_if->eapFail) is_complete = 1; + if (sess->eap_if->eapFail) + srv_log(sess, "EAP authentication failed"); + else if (sess->eap_if->eapSuccess) + srv_log(sess, "EAP authentication succeeded"); reply = radius_server_encapsulate_eap(data, client, sess, msg); @@ -869,10 +1006,12 @@ static int radius_server_request(struct radius_server_data *data, switch (radius_msg_get_hdr(reply)->code) { case RADIUS_CODE_ACCESS_ACCEPT: + srv_log(sess, "Sending Access-Accept"); data->counters.access_accepts++; client->counters.access_accepts++; break; case RADIUS_CODE_ACCESS_REJECT: + srv_log(sess, "Sending Access-Reject"); data->counters.access_rejects++; client->counters.access_rejects++; break; @@ -1482,6 +1621,17 @@ radius_server_init(struct radius_server_conf *conf) os_strdup(conf->subscr_remediation_url); } +#ifdef CONFIG_SQLITE + if (conf->sqlite_file) { + if (sqlite3_open(conf->sqlite_file, &data->db)) { + RADIUS_ERROR("Could not open SQLite file '%s'", + conf->sqlite_file); + radius_server_deinit(data); + return NULL; + } + } +#endif /* CONFIG_SQLITE */ + #ifdef CONFIG_RADIUS_TEST if (conf->dump_msk_file) data->dump_msk_file = os_strdup(conf->dump_msk_file); @@ -1569,6 +1719,12 @@ void radius_server_deinit(struct radius_server_data *data) os_free(data->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ os_free(data->subscr_remediation_url); + +#ifdef CONFIG_SQLITE + if (data->db) + sqlite3_close(data->db); +#endif /* CONFIG_SQLITE */ + os_free(data); } @@ -1725,8 +1881,10 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, ret = data->get_eap_user(data->conf_ctx, identity, identity_len, phase2, user); - if (ret == 0 && user) + if (ret == 0 && user) { + sess->accept_attr = user->accept_attr; sess->remediation = user->remediation; + } return ret; } @@ -1740,10 +1898,18 @@ static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len) } +static void radius_server_log_msg(void *ctx, const char *msg) +{ + struct radius_session *sess = ctx; + srv_log(sess, "EAP: %s", msg); +} + + static struct eapol_callbacks radius_server_eapol_cb = { .get_eap_user = radius_server_get_eap_user, .get_eap_req_id_text = radius_server_get_eap_req_id_text, + .log_msg = radius_server_log_msg, }; diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index e85d0090..46ac3127 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -40,6 +40,11 @@ struct radius_server_conf { char *client_file; /** + * sqlite_file - SQLite database for storing debug log information + */ + const char *sqlite_file; + + /** * conf_ctx - Context pointer for callbacks * * This is used as the ctx argument in get_eap_user() calls. diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index 12148b61..4a4f0b69 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -459,10 +459,15 @@ struct tlsv1_client * tlsv1_client_init(void) count = 0; suites = conn->cipher_suites; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index 3269ecf6..8367e361 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -1,6 +1,6 @@ /* * TLSv1 client - read handshake message - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -410,9 +410,11 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, - const u8 *buf, size_t len) + const u8 *buf, size_t len, + tls_key_exchange key_exchange) { - const u8 *pos, *end; + const u8 *pos, *end, *server_params, *server_params_end; + u8 alert; tlsv1_client_free_dh(conn); @@ -421,6 +423,7 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (end - pos < 3) goto fail; + server_params = pos; conn->dh_p_len = WPA_GET_BE16(pos); pos += 2; if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) { @@ -465,6 +468,59 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, pos += conn->dh_ys_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", conn->dh_ys, conn->dh_ys_len); + server_params_end = pos; + + if (key_exchange == TLS_KEY_X_DHE_RSA) { + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + int hlen; + + if (conn->rl.tls_version == TLS_VERSION_1_2) { +#ifdef CONFIG_TLSV12 + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used + * signature and hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + if (end - pos < 2) + goto fail; + if (pos[0] != TLS_HASH_ALG_SHA256 || + pos[1] != TLS_SIGN_ALG_RSA) { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm", + pos[0], pos[1]); + goto fail; + } + pos += 2; + + hlen = tlsv12_key_x_server_params_hash( + conn->rl.tls_version, conn->client_random, + conn->server_random, server_params, + server_params_end - server_params, hash); +#else /* CONFIG_TLSV12 */ + goto fail; +#endif /* CONFIG_TLSV12 */ + } else { + hlen = tls_key_x_server_params_hash( + conn->rl.tls_version, conn->client_random, + conn->server_random, server_params, + server_params_end - server_params, hash); + } + + if (hlen < 0) + goto fail; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerKeyExchange hash", + hash, hlen); + + if (tls_verify_signature(conn->rl.tls_version, + conn->server_rsa_key, + hash, hlen, pos, end - pos, + &alert) < 0) + goto fail; + } return 0; @@ -543,8 +599,10 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len); suite = tls_get_cipher_suite(conn->rl.cipher_suite); - if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) { - if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) { + if (suite && (suite->key_exchange == TLS_KEY_X_DH_anon || + suite->key_exchange == TLS_KEY_X_DHE_RSA)) { + if (tlsv1_process_diffie_hellman(conn, pos, len, + suite->key_exchange) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -873,6 +931,8 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); return -1; } diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c index d789efb4..839eb90a 100644 --- a/src/tls/tlsv1_client_write.c +++ b/src/tls/tlsv1_client_write.c @@ -1,6 +1,6 @@ /* * TLSv1 client - write handshake message - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -205,7 +205,7 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, } -static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) +static int tlsv1_key_x_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) { /* ClientDiffieHellmanPublic */ u8 *csecret, *csecret_start, *dh_yc, *shared; @@ -399,8 +399,8 @@ static int tls_write_client_key_exchange(struct tlsv1_client *conn, hs_length = pos; pos += 3; /* body - ClientKeyExchange */ - if (keyx == TLS_KEY_X_DH_anon) { - if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0) + if (keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) { + if (tlsv1_key_x_dh(conn, &pos, end) < 0) return -1; } else { if (tlsv1_key_x_rsa(conn, &pos, end) < 0) diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c index 4578b227..8a4645bf 100644 --- a/src/tls/tlsv1_common.c +++ b/src/tls/tlsv1_common.c @@ -1,6 +1,6 @@ /* * TLSv1 common routines - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "crypto/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "x509v3.h" @@ -33,6 +34,10 @@ static const struct tls_cipher_suite tls_cipher_suites[] = { TLS_HASH_SHA }, { TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, + { TLS_DHE_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_DHE_RSA, TLS_CIPHER_DES_CBC, + TLS_HASH_SHA}, + { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon, TLS_CIPHER_RC4_128, TLS_HASH_MD5 }, { TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon, @@ -41,16 +46,24 @@ static const struct tls_cipher_suite tls_cipher_suites[] = { TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, { TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }, + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }, { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon, @@ -319,3 +332,163 @@ int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out, outlen); } + + +#ifdef CONFIG_TLSV12 +int tlsv12_key_x_server_params_hash(u16 tls_version, + const u8 *client_random, + const u8 *server_random, + const u8 *server_params, + size_t server_params_len, u8 *hash) +{ + size_t hlen; + struct crypto_hash *ctx; + + ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); + if (ctx == NULL) + return -1; + crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_params, server_params_len); + hlen = SHA256_MAC_LEN; + if (crypto_hash_finish(ctx, hash, &hlen) < 0) + return -1; + + return hlen; +} +#endif /* CONFIG_TLSV12 */ + + +int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random, + const u8 *server_random, + const u8 *server_params, + size_t server_params_len, u8 *hash) +{ + u8 *hpos; + size_t hlen; + enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; + struct crypto_hash *ctx; + + hpos = hash; + + if (alg == SIGN_ALG_RSA) { + ctx = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); + if (ctx == NULL) + return -1; + crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_params, server_params_len); + hlen = MD5_MAC_LEN; + if (crypto_hash_finish(ctx, hash, &hlen) < 0) + return -1; + hpos += hlen; + } + + ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); + if (ctx == NULL) + return -1; + crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_params, server_params_len); + hlen = hash + sizeof(hash) - hpos; + if (crypto_hash_finish(ctx, hpos, &hlen) < 0) + return -1; + hpos += hlen; + return hpos - hash; +} + + +int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk, + const u8 *data, size_t data_len, + const u8 *pos, size_t len, u8 *alert) +{ + u8 *buf; + const u8 *end = pos + len; + const u8 *decrypted; + u16 slen; + size_t buflen; + + if (end - pos < 2) { + *alert = TLS_ALERT_DECODE_ERROR; + return -1; + } + slen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < slen) { + *alert = TLS_ALERT_DECODE_ERROR; + return -1; + } + if (end - pos > slen) { + wpa_hexdump(MSG_MSGDUMP, "Additional data after Signature", + pos + slen, end - pos - slen); + end = pos + slen; + } + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos); + if (pk == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No public key to verify signature"); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + + buflen = end - pos; + buf = os_malloc(end - pos); + if (buf == NULL) { + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + if (crypto_public_key_decrypt_pkcs1(pk, pos, end - pos, buf, &buflen) < + 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature"); + os_free(buf); + *alert = TLS_ALERT_DECRYPT_ERROR; + return -1; + } + decrypted = buf; + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", + decrypted, buflen); + +#ifdef CONFIG_TLSV12 + if (tls_version >= TLS_VERSION_1_2) { + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || + * H + */ + if (buflen >= 19 + 32 && + os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256"); + decrypted = buf + 19; + buflen -= 19; + } else { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo"); + os_free(buf); + *alert = TLS_ALERT_DECRYPT_ERROR; + return -1; + } + } +#endif /* CONFIG_TLSV12 */ + + if (buflen != data_len || os_memcmp(decrypted, data, data_len) != 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in CertificateVerify - did not match calculated hash"); + os_free(buf); + *alert = TLS_ALERT_DECRYPT_ERROR; + return -1; + } + + os_free(buf); + + return 0; +} diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h index f28c0cdc..26e68af1 100644 --- a/src/tls/tlsv1_common.h +++ b/src/tls/tlsv1_common.h @@ -1,6 +1,6 @@ /* * TLSv1 common definitions - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -257,5 +257,16 @@ int tls_version_ok(u16 ver); const char * tls_version_str(u16 ver); int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); +int tlsv12_key_x_server_params_hash(u16 tls_version, const u8 *client_random, + const u8 *server_random, + const u8 *server_params, + size_t server_params_len, u8 *hash); +int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random, + const u8 *server_random, + const u8 *server_params, + size_t server_params_len, u8 *hash); +int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk, + const u8 *data, size_t data_len, + const u8 *pos, size_t len, u8 *alert); #endif /* TLSV1_COMMON_H */ diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c index 2880309e..4aeccf6a 100644 --- a/src/tls/tlsv1_server.c +++ b/src/tls/tlsv1_server.c @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -21,6 +21,31 @@ */ +void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) + return; + va_start(ap, fmt); + vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + + wpa_printf(MSG_DEBUG, "TLSv1: %s", buf); + if (conn->log_cb) + conn->log_cb(conn->log_cb_ctx, buf); + + os_free(buf); +} + + void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description) { conn->alert_level = level; @@ -250,8 +275,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, out_pos, &olen, &alert); if (used < 0) { - wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " - "failed"); + tlsv1_server_log(conn, "Record layer processing failed"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } @@ -265,14 +289,13 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, if (ct == TLS_CONTENT_TYPE_ALERT) { if (olen < 2) { - wpa_printf(MSG_DEBUG, "TLSv1: Alert " - "underflow"); + tlsv1_server_log(conn, "Alert underflow"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", - out_pos[0], out_pos[1]); + tlsv1_server_log(conn, "Received alert %d:%d", + out_pos[0], out_pos[1]); if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { /* Continue processing */ pos += used; @@ -285,13 +308,23 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, } if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " - "0x%x", pos[0]); + tlsv1_server_log(conn, "Unexpected content type 0x%x", + pos[0]); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } +#ifdef CONFIG_TESTING_OPTIONS + if ((conn->test_flags && + (TLS_BREAK_VERIFY_DATA | TLS_BREAK_SRV_KEY_X_HASH | + TLS_BREAK_SRV_KEY_X_SIGNATURE)) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-FAILURE: Client ApplData received after invalid handshake"); + conn->test_failure_reported = 1; + } +#endif /* CONFIG_TESTING_OPTIONS */ + out_pos += olen; if (out_pos > out_end) { wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " @@ -361,8 +394,15 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred) count = 0; suites = conn->cipher_suites; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; @@ -618,3 +658,19 @@ void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; } + + +void tlsv1_server_set_log_cb(struct tlsv1_server *conn, + void (*cb)(void *ctx, const char *msg), void *ctx) +{ + conn->log_cb = cb; + conn->log_cb_ctx = ctx; +} + + +#ifdef CONFIG_TESTING_OPTIONS +void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags) +{ + conn->test_flags = flags; +} +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h index a18c69e3..b2b28d1e 100644 --- a/src/tls/tlsv1_server.h +++ b/src/tls/tlsv1_server.h @@ -45,4 +45,9 @@ void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, tlsv1_server_session_ticket_cb cb, void *ctx); +void tlsv1_server_set_log_cb(struct tlsv1_server *conn, + void (*cb)(void *ctx, const char *msg), void *ctx); + +void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags); + #endif /* TLSV1_SERVER_H */ diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h index 1f61533a..9a36d8f0 100644 --- a/src/tls/tlsv1_server_i.h +++ b/src/tls/tlsv1_server_i.h @@ -51,13 +51,24 @@ struct tlsv1_server { tlsv1_server_session_ticket_cb session_ticket_cb; void *session_ticket_cb_ctx; + void (*log_cb)(void *ctx, const char *msg); + void *log_cb_ctx; + int use_session_ticket; u8 *dh_secret; size_t dh_secret_len; + +#ifdef CONFIG_TESTING_OPTIONS + u32 test_flags; + int test_failure_reported; +#endif /* CONFIG_TESTING_OPTIONS */ }; +void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...) +PRINTF_FORMAT(2, 3); + void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description); int tlsv1_server_derive_keys(struct tlsv1_server *conn, const u8 *pre_master_secret, diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index 6f6539b1..04622b5e 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -1,6 +1,6 @@ /* * TLSv1 server - read handshake message - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,6 +27,23 @@ static int tls_process_change_cipher_spec(struct tlsv1_server *conn, size_t *in_len); +static int testing_cipher_suite_filter(struct tlsv1_server *conn, u16 suite) +{ +#ifdef CONFIG_TESTING_OPTIONS + if ((conn->test_flags & + (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE)) && + suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 && + suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA && + suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 && + suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA && + suite != TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) + return 1; +#endif /* CONFIG_TESTING_OPTIONS */ + + return 0; +} + + static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -38,8 +55,8 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, u16 ext_type, ext_len; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -53,13 +70,13 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, /* HandshakeType msg_type */ if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { - wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " - "message %d (expected ClientHello)", *pos); + tlsv1_server_log(conn, "Received unexpected handshake message %d (expected ClientHello)", + *pos); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello"); + tlsv1_server_log(conn, "Received ClientHello"); pos++; /* uint24 length */ len = WPA_GET_BE24(pos); @@ -78,13 +95,13 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, if (end - pos < 2) goto decode_error; conn->client_version = WPA_GET_BE16(pos); - wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d", - conn->client_version >> 8, conn->client_version & 0xff); + tlsv1_server_log(conn, "Client version %d.%d", + conn->client_version >> 8, + conn->client_version & 0xff); if (conn->client_version < TLS_VERSION_1) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " - "ClientHello %u.%u", - conn->client_version >> 8, - conn->client_version & 0xff); + tlsv1_server_log(conn, "Unexpected protocol version in ClientHello %u.%u", + conn->client_version >> 8, + conn->client_version & 0xff); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; @@ -101,8 +118,8 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->rl.tls_version = TLS_VERSION_1_1; else conn->rl.tls_version = conn->client_version; - wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", - tls_version_str(conn->rl.tls_version)); + tlsv1_server_log(conn, "Using TLS v%s", + tls_version_str(conn->rl.tls_version)); /* Random random */ if (end - pos < TLS_RANDOM_LEN) @@ -137,6 +154,8 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, cipher_suite = 0; for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) { + if (testing_cipher_suite_filter(conn, conn->cipher_suites[i])) + continue; c = pos; for (j = 0; j < num_suites; j++) { u16 tmp = WPA_GET_BE16(c); @@ -149,8 +168,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, } pos += num_suites * 2; if (!cipher_suite) { - wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite " - "available"); + tlsv1_server_log(conn, "No supported cipher suite available"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; @@ -180,16 +198,15 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, compr_null_found = 1; } if (!compr_null_found) { - wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL " - "compression"); + tlsv1_server_log(conn, "Client does not accept NULL compression"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; } if (end - pos == 1) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the " - "end of ClientHello: 0x%02x", *pos); + tlsv1_server_log(conn, "Unexpected extra octet in the end of ClientHello: 0x%02x", + *pos); goto decode_error; } @@ -198,12 +215,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, ext_len = WPA_GET_BE16(pos); pos += 2; - wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello " - "extensions", ext_len); + tlsv1_server_log(conn, "%u bytes of ClientHello extensions", + ext_len); if (end - pos != ext_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello " - "extension list length %u (expected %u)", - ext_len, (unsigned int) (end - pos)); + tlsv1_server_log(conn, "Invalid ClientHello extension list length %u (expected %u)", + ext_len, (unsigned int) (end - pos)); goto decode_error; } @@ -216,8 +232,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, while (pos < end) { if (end - pos < 2) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid " - "extension_type field"); + tlsv1_server_log(conn, "Invalid extension_type field"); goto decode_error; } @@ -225,8 +240,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos += 2; if (end - pos < 2) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid " - "extension_data length field"); + tlsv1_server_log(conn, "Invalid extension_data length field"); goto decode_error; } @@ -234,13 +248,12 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos += 2; if (end - pos < ext_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid " - "extension_data field"); + tlsv1_server_log(conn, "Invalid extension_data field"); goto decode_error; } - wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension " - "type %u", ext_type); + tlsv1_server_log(conn, "ClientHello Extension type %u", + ext_type); wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello " "Extension data", pos, ext_len); @@ -260,14 +273,13 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, *in_len = end - in_data; - wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to " - "ServerHello"); + tlsv1_server_log(conn, "ClientHello OK - proceed to ServerHello"); conn->state = SERVER_HELLO; return 0; decode_error: - wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello"); + tlsv1_server_log(conn, "Failed to decode ClientHello"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -284,8 +296,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, int reason; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -295,8 +307,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, left = *in_len; if (left < 4) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message " - "(len=%lu)", (unsigned long) left); + tlsv1_server_log(conn, "Too short Certificate message (len=%lu)", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -308,9 +320,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, left -= 4; if (len > left) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message " - "length (len=%lu != left=%lu)", - (unsigned long) len, (unsigned long) left); + tlsv1_server_log(conn, "Unexpected Certificate message length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -318,8 +329,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { if (conn->verify_peer) { - wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " - "Certificate"); + tlsv1_server_log(conn, "Client did not include Certificate"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -329,17 +339,15 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, in_len); } if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) { - wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " - "message %d (expected Certificate/" - "ClientKeyExchange)", type); + tlsv1_server_log(conn, "Received unexpected handshake message %d (expected Certificate/ClientKeyExchange)", + type); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, - "TLSv1: Received Certificate (certificate_list len %lu)", - (unsigned long) len); + tlsv1_server_log(conn, "Received Certificate (certificate_list len %lu)", + (unsigned long) len); /* * opaque ASN.1Cert<2^24-1>; @@ -352,8 +360,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, end = pos + len; if (end - pos < 3) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate " - "(left=%lu)", (unsigned long) left); + tlsv1_server_log(conn, "Too short Certificate (left=%lu)", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -363,10 +371,9 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, pos += 3; if ((size_t) (end - pos) != list_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list " - "length (len=%lu left=%lu)", - (unsigned long) list_len, - (unsigned long) (end - pos)); + tlsv1_server_log(conn, "Unexpected certificate_list length (len=%lu left=%lu)", + (unsigned long) list_len, + (unsigned long) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -375,8 +382,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, idx = 0; while (pos < end) { if (end - pos < 3) { - wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " - "certificate_list"); + tlsv1_server_log(conn, "Failed to parse certificate_list"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); x509_certificate_chain_free(chain); @@ -387,25 +393,23 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, pos += 3; if ((size_t) (end - pos) < cert_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate " - "length (len=%lu left=%lu)", - (unsigned long) cert_len, - (unsigned long) (end - pos)); + tlsv1_server_log(conn, "Unexpected certificate length (len=%lu left=%lu)", + (unsigned long) cert_len, + (unsigned long) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); x509_certificate_chain_free(chain); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)", - (unsigned long) idx, (unsigned long) cert_len); + tlsv1_server_log(conn, "Certificate %lu (len %lu)", + (unsigned long) idx, (unsigned long) cert_len); if (idx == 0) { crypto_public_key_free(conn->client_rsa_key); if (tls_parse_cert(pos, cert_len, &conn->client_rsa_key)) { - wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " - "the certificate"); + tlsv1_server_log(conn, "Failed to parse the certificate"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE); x509_certificate_chain_free(chain); @@ -415,8 +419,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, cert = x509_certificate_parse(pos, cert_len); if (cert == NULL) { - wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " - "the certificate"); + tlsv1_server_log(conn, "Failed to parse the certificate"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE); x509_certificate_chain_free(chain); @@ -436,8 +439,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain, &reason, 0) < 0) { int tls_reason; - wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " - "validation failed (reason=%d)", reason); + tlsv1_server_log(conn, "Server certificate chain validation failed (reason=%d)", + reason); switch (reason) { case X509_VALIDATE_BAD_CERTIFICATE: tls_reason = TLS_ALERT_BAD_CERTIFICATE; @@ -494,9 +497,8 @@ static int tls_process_client_key_exchange_rsa( encr_len = WPA_GET_BE16(pos); pos += 2; if (pos + encr_len > end) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange " - "format: encr_len=%u left=%u", - encr_len, (unsigned int) (end - pos)); + tlsv1_server_log(conn, "Invalid ClientKeyExchange format: encr_len=%u left=%u", + encr_len, (unsigned int) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -539,15 +541,13 @@ static int tls_process_client_key_exchange_rsa( } if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret " - "length %lu", (unsigned long) outlen); + tlsv1_server_log(conn, "Unexpected PreMasterSecret length %lu", + (unsigned long) outlen); use_random = 1; } if (!use_random && WPA_GET_BE16(out) != conn->client_version) { - wpa_printf(MSG_DEBUG, "TLSv1: Client version in " - "ClientKeyExchange does not match with version in " - "ClientHello"); + tlsv1_server_log(conn, "Client version in ClientKeyExchange does not match with version in ClientHello"); use_random = 1; } @@ -582,7 +582,7 @@ static int tls_process_client_key_exchange_rsa( } -static int tls_process_client_key_exchange_dh_anon( +static int tls_process_client_key_exchange_dh( struct tlsv1_server *conn, const u8 *pos, const u8 *end) { const u8 *dh_yc; @@ -600,6 +600,7 @@ static int tls_process_client_key_exchange_dh_anon( * } ClientDiffieHellmanPublic; */ + tlsv1_server_log(conn, "ClientDiffieHellmanPublic received"); wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic", pos, end - pos); @@ -612,8 +613,7 @@ static int tls_process_client_key_exchange_dh_anon( } if (end - pos < 3) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value " - "length"); + tlsv1_server_log(conn, "Invalid client public value length"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -623,8 +623,8 @@ static int tls_process_client_key_exchange_dh_anon( dh_yc = pos + 2; if (dh_yc + dh_yc_len > end) { - wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow " - "(length %d)", dh_yc_len); + tlsv1_server_log(conn, "Client public value overflow (length %d)", + dh_yc_len); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -695,8 +695,8 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, const struct tls_cipher_suite *suite; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -706,8 +706,8 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, left = *in_len; if (left < 4) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange " - "(Left=%lu)", (unsigned long) left); + tlsv1_server_log(conn, "Too short ClientKeyExchange (Left=%lu)", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -719,9 +719,8 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, left -= 4; if (len > left) { - wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange " - "length (len=%lu != left=%lu)", - (unsigned long) len, (unsigned long) left); + tlsv1_server_log(conn, "Mismatch in ClientKeyExchange length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -730,14 +729,14 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, end = pos + len; if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { - wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " - "message %d (expected ClientKeyExchange)", type); + tlsv1_server_log(conn, "Received unexpected handshake message %d (expected ClientKeyExchange)", + type); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange"); + tlsv1_server_log(conn, "Received ClientKeyExchange"); wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len); @@ -747,11 +746,11 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, else keyx = suite->key_exchange; - if (keyx == TLS_KEY_X_DH_anon && - tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0) + if ((keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) && + tls_process_client_key_exchange_dh(conn, pos, end) < 0) return -1; - if (keyx != TLS_KEY_X_DH_anon && + if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA && tls_process_client_key_exchange_rsa(conn, pos, end) < 0) return -1; @@ -769,15 +768,14 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, const u8 *pos, *end; size_t left, len; u8 type; - size_t hlen, buflen; - u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf; + size_t hlen; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos; enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; - u16 slen; + u8 alert; if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { if (conn->verify_peer) { - wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " - "CertificateVerify"); + tlsv1_server_log(conn, "Client did not include CertificateVerify"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -788,8 +786,8 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, } if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -799,8 +797,8 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, left = *in_len; if (left < 4) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify " - "message (len=%lu)", (unsigned long) left); + tlsv1_server_log(conn, "Too short CertificateVerify message (len=%lu)", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -812,9 +810,8 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, left -= 4; if (len > left) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify " - "message length (len=%lu != left=%lu)", - (unsigned long) len, (unsigned long) left); + tlsv1_server_log(conn, "Unexpected CertificateVerify message length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -823,14 +820,14 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, end = pos + len; if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) { - wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " - "message %d (expected CertificateVerify)", type); + tlsv1_server_log(conn, "Received unexpected handshake message %d (expected CertificateVerify)", + type); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify"); + tlsv1_server_log(conn, "Received CertificateVerify"); /* * struct { @@ -917,90 +914,13 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); - if (end - pos < 2) { - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECODE_ERROR); - return -1; - } - slen = WPA_GET_BE16(pos); - pos += 2; - if (end - pos < slen) { - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECODE_ERROR); - return -1; - } - - wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos); - if (conn->client_rsa_key == NULL) { - wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify " - "signature"); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); - return -1; - } - - buflen = end - pos; - buf = os_malloc(end - pos); - if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key, - pos, end - pos, buf, &buflen) < 0) - { - wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature"); - os_free(buf); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECRYPT_ERROR); + if (tls_verify_signature(conn->rl.tls_version, conn->client_rsa_key, + hash, hlen, pos, end - pos, &alert) < 0) { + tlsv1_server_log(conn, "Invalid Signature in CertificateVerify"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } - wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", - buf, buflen); - -#ifdef CONFIG_TLSV12 - if (conn->rl.tls_version >= TLS_VERSION_1_2) { - /* - * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 - * - * DigestInfo ::= SEQUENCE { - * digestAlgorithm DigestAlgorithm, - * digest OCTET STRING - * } - * - * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} - * - * DER encoded DigestInfo for SHA256 per RFC 3447: - * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || - * H - */ - if (buflen >= 19 + 32 && - os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" - "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) - { - wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = " - "SHA-256"); - os_memmove(buf, buf + 19, buflen - 19); - buflen -= 19; - } else { - wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized " - "DigestInfo"); - os_free(buf); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECRYPT_ERROR); - return -1; - } - } -#endif /* CONFIG_TLSV12 */ - - if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in " - "CertificateVerify - did not match with calculated " - "hash"); - os_free(buf); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECRYPT_ERROR); - return -1; - } - - os_free(buf); - *in_len = end - in_data; conn->state = CHANGE_CIPHER_SPEC; @@ -1017,8 +937,8 @@ static int tls_process_change_cipher_spec(struct tlsv1_server *conn, size_t left; if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected ChangeCipherSpec; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -1028,21 +948,21 @@ static int tls_process_change_cipher_spec(struct tlsv1_server *conn, left = *in_len; if (left < 1) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec"); + tlsv1_server_log(conn, "Too short ChangeCipherSpec"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (*pos != TLS_CHANGE_CIPHER_SPEC) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " - "received data 0x%x", *pos); + tlsv1_server_log(conn, "Expected ChangeCipherSpec; received data 0x%x", + *pos); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec"); + tlsv1_server_log(conn, "Received ChangeCipherSpec"); if (tlsv1_record_change_read_cipher(&conn->rl) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher " "for record layer"); @@ -1067,9 +987,18 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, u8 verify_data[TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; +#ifdef CONFIG_TESTING_OPTIONS + if ((conn->test_flags & + (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE)) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after invalid ServerKeyExchange"); + conn->test_failure_reported = 1; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Finished; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -1079,9 +1008,8 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, left = *in_len; if (left < 4) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for " - "Finished", - (unsigned long) left); + tlsv1_server_log(conn, "Too short record (left=%lu) forFinished", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -1101,18 +1029,16 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, left -= 4; if (len > left) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished " - "(len=%lu > left=%lu)", - (unsigned long) len, (unsigned long) left); + tlsv1_server_log(conn, "Too short buffer for Finished (len=%lu > left=%lu)", + (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (len != TLS_VERIFY_DATA_LEN) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length " - "in Finished: %lu (expected %d)", - (unsigned long) len, TLS_VERIFY_DATA_LEN); + tlsv1_server_log(conn, "Unexpected verify_data length in Finished: %lu (expected %d)", + (unsigned long) len, TLS_VERIFY_DATA_LEN); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -1175,18 +1101,17 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, verify_data, TLS_VERIFY_DATA_LEN); if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { - wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); + tlsv1_server_log(conn, "Mismatch in verify_data"); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received Finished"); + tlsv1_server_log(conn, "Received Finished"); *in_len = end - in_data; if (conn->use_session_ticket) { /* Abbreviated handshake using session ticket; RFC 4507 */ - wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed " - "successfully"); + tlsv1_server_log(conn, "Abbreviated handshake completed successfully"); conn->state = ESTABLISHED; } else { /* Full handshake */ @@ -1202,13 +1127,12 @@ int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, { if (ct == TLS_CONTENT_TYPE_ALERT) { if (*len < 2) { - wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow"); + tlsv1_server_log(conn, "Alert underflow"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", - buf[0], buf[1]); + tlsv1_server_log(conn, "Received alert %d:%d", buf[0], buf[1]); *len = 2; conn->state = FAILED; return -1; @@ -1240,9 +1164,8 @@ int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, return -1; break; default: - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d " - "while processing received message", - conn->state); + tlsv1_server_log(conn, "Unexpected state %d while processing received message", + conn->state); return -1; } diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c index 6d8e55ed..619b9ab8 100644 --- a/src/tls/tlsv1_server_write.c +++ b/src/tls/tlsv1_server_write.c @@ -1,6 +1,6 @@ /* * TLSv1 server - write handshake message - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -48,7 +48,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello"); + tlsv1_server_log(conn, "Send ServerHello"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; @@ -104,8 +104,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, conn->client_random, conn->server_random, conn->master_secret); if (res < 0) { - wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " - "indicated failure"); + tlsv1_server_log(conn, "SessionTicket callback indicated failure"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_HANDSHAKE_FAILURE); return -1; @@ -170,7 +169,7 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); + tlsv1_server_log(conn, "Send Certificate"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; @@ -245,7 +244,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, { tls_key_exchange keyx; const struct tls_cipher_suite *suite; - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *rhdr, *hs_start, *hs_length, *server_params; size_t rlen; u8 *dh_ys; size_t dh_ys_len; @@ -261,8 +260,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, return 0; } - if (keyx != TLS_KEY_X_DH_anon) { - /* TODO? */ + if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA) { wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet " "supported with key exchange type %d", keyx); return -1; @@ -354,7 +352,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange"); + tlsv1_server_log(conn, "Send ServerKeyExchange"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; @@ -369,6 +367,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += 3; /* body - ServerDHParams */ + server_params = pos; /* dh_p */ if (pos + 2 + conn->cred->dh_p_len > end) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " @@ -412,6 +411,138 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += dh_ys_len; os_free(dh_ys); + /* + * select (SignatureAlgorithm) + * { case anonymous: struct { }; + * case rsa: + * digitally-signed struct { + * opaque md5_hash[16]; + * opaque sha_hash[20]; + * }; + * case dsa: + * digitally-signed struct { + * opaque sha_hash[20]; + * }; + * } Signature; + * + * md5_hash + * MD5(ClientHello.random + ServerHello.random + ServerParams); + * + * sha_hash + * SHA(ClientHello.random + ServerHello.random + ServerParams); + */ + + if (keyx == TLS_KEY_X_DHE_RSA) { + u8 hash[100]; + u8 *signed_start; + size_t clen; + int hlen; + + if (conn->rl.tls_version >= TLS_VERSION_1_2) { +#ifdef CONFIG_TLSV12 + hlen = tlsv12_key_x_server_params_hash( + conn->rl.tls_version, conn->client_random, + conn->server_random, server_params, + pos - server_params, hash + 19); + + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used + * signature and hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + if (hlen < 0 || pos + 2 > end) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + *pos++ = TLS_HASH_ALG_SHA256; + *pos++ = TLS_SIGN_ALG_RSA; + + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 + * 04 20 || H + */ + hlen += 19; + os_memcpy(hash, + "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65" + "\x03\x04\x02\x01\x05\x00\x04\x20", 19); + +#else /* CONFIG_TLSV12 */ + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; +#endif /* CONFIG_TLSV12 */ + } else { + hlen = tls_key_x_server_params_hash( + conn->rl.tls_version, conn->client_random, + conn->server_random, server_params, + pos - server_params, hash); + } + + if (hlen < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "TLS: ServerKeyExchange signed_params hash", + hash, hlen); +#ifdef CONFIG_TESTING_OPTIONS + if (conn->test_flags & TLS_BREAK_SRV_KEY_X_HASH) { + tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params hash"); + hash[hlen - 1] ^= 0x80; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* + * RFC 2246, 4.7: + * In digital signing, one-way hash functions are used as input + * for a signing algorithm. A digitally-signed element is + * encoded as an opaque vector <0..2^16-1>, where the length is + * specified by the signing algorithm and key. + * + * In RSA signing, a 36-byte structure of two hashes (one SHA + * and one MD5) is signed (encrypted with the private key). It + * is encoded with PKCS #1 block type 0 or type 1 as described + * in [PKCS1]. + */ + signed_start = pos; /* length to be filled */ + pos += 2; + clen = end - pos; + if (conn->cred == NULL || + crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen, + pos, &clen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE16(signed_start, clen); +#ifdef CONFIG_TESTING_OPTIONS + if (conn->test_flags & TLS_BREAK_SRV_KEY_X_SIGNATURE) { + tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params signature"); + pos[clen - 1] ^= 0x80; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + pos += clen; + } + WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, @@ -445,7 +576,7 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest"); + tlsv1_server_log(conn, "Send CertificateRequest"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; @@ -505,7 +636,7 @@ static int tls_write_server_hello_done(struct tlsv1_server *conn, size_t rlen; u8 payload[4]; - wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone"); + tlsv1_server_log(conn, "Send ServerHelloDone"); /* opaque fragment[TLSPlaintext.length] */ @@ -541,7 +672,7 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, size_t rlen; u8 payload[1]; - wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); + tlsv1_server_log(conn, "Send ChangeCipherSpec"); payload[0] = TLS_CHANGE_CIPHER_SPEC; @@ -578,7 +709,7 @@ static int tls_write_server_finished(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); + tlsv1_server_log(conn, "Send Finished"); /* Encrypted Handshake Message: Finished */ @@ -635,6 +766,12 @@ static int tls_write_server_finished(struct tlsv1_server *conn, } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); +#ifdef CONFIG_TESTING_OPTIONS + if (conn->test_flags & TLS_BREAK_VERIFY_DATA) { + tlsv1_server_log(conn, "TESTING: Break verify_data (server)"); + verify_data[1 + 3 + 1] ^= 0x80; + } +#endif /* CONFIG_TESTING_OPTIONS */ /* Handshake */ pos = hs_start = verify_data; @@ -736,7 +873,7 @@ static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn, *out_len = pos - msg; - wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully"); + tlsv1_server_log(conn, "Handshake completed successfully"); conn->state = ESTABLISHED; return msg; @@ -755,8 +892,8 @@ u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len) /* Abbreviated handshake was already completed. */ return NULL; } - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while " - "generating reply", conn->state); + tlsv1_server_log(conn, "Unexpected state %d while generating reply", + conn->state); return NULL; } } @@ -767,7 +904,7 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, { u8 *alert, *pos, *length; - wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description); + tlsv1_server_log(conn, "Send Alert(%d:%d)", level, description); *out_len = 0; alert = os_malloc(10); diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 2935ce73..fdf8ac36 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -40,6 +40,7 @@ #include "blacklist.h" #include "autoscan.h" #include "wnm_sta.h" +#include "offchannel.h" static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len); @@ -452,6 +453,10 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, ret = set_disallow_aps(wpa_s, value); } else if (os_strcasecmp(cmd, "no_keep_alive") == 0) { wpa_s->no_keep_alive = !!atoi(value); +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { + wpa_s->ext_mgmt_frame_handling = !!atoi(value); +#endif /* CONFIG_TESTING_OPTIONS */ } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); @@ -5591,6 +5596,8 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) #ifdef CONFIG_INTERWORKING hs20_cancel_fetch_osu(wpa_s); #endif /* CONFIG_INTERWORKING */ + + wpa_s->ext_mgmt_frame_handling = 0; } @@ -5879,6 +5886,103 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, } +#ifdef CONFIG_TESTING_OPTIONS + +static void wpas_ctrl_iface_mgmt_tx_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result + result) +{ + wpa_msg(wpa_s, MSG_INFO, "MGMT-TX-STATUS freq=%u dst=" MACSTR + " src=" MACSTR " bssid=" MACSTR " result=%s", + freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), + result == OFFCHANNEL_SEND_ACTION_SUCCESS ? + "SUCCESS" : (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? + "NO_ACK" : "FAILED")); +} + + +static int wpas_ctrl_iface_mgmt_tx(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos, *param; + size_t len; + u8 *buf, da[ETH_ALEN], bssid[ETH_ALEN]; + int res, used; + int freq = 0, no_cck = 0, wait_time = 0; + + /* <DA> <BSSID> [freq=<MHz>] [wait_time=<ms>] [no_cck=1] + * <action=Action frame payload> */ + + wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd); + + pos = cmd; + used = hwaddr_aton2(pos, da); + if (used < 0) + return -1; + pos += used; + while (*pos == ' ') + pos++; + used = hwaddr_aton2(pos, bssid); + if (used < 0) + return -1; + pos += used; + + param = os_strstr(pos, " freq="); + if (param) { + param += 6; + freq = atoi(param); + } + + param = os_strstr(pos, " no_cck="); + if (param) { + param += 8; + no_cck = atoi(param); + } + + param = os_strstr(pos, " wait_time="); + if (param) { + param += 11; + wait_time = atoi(param); + } + + param = os_strstr(pos, " action="); + if (param == NULL) + return -1; + param += 8; + + len = os_strlen(param); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(param, buf, len) < 0) { + os_free(buf); + return -1; + } + + res = offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, bssid, + buf, len, wait_time, + wpas_ctrl_iface_mgmt_tx_cb, no_cck); + os_free(buf); + return res; +} + + +static void wpas_ctrl_iface_mgmt_tx_done(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "External MGMT TX - done waiting"); + offchannel_send_action_done(wpa_s); +} + +#endif /* CONFIG_TESTING_OPTIONS */ + + char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len) { @@ -6421,6 +6525,13 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) { reply_len = wpas_ctrl_radio_work(wpa_s, buf + 11, reply, reply_size); +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) { + if (wpas_ctrl_iface_mgmt_tx(wpa_s, buf + 8) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) { + wpas_ctrl_iface_mgmt_tx_done(wpa_s); +#endif /* CONFIG_TESTING_OPTIONS */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index bea7f8ad..65c162ee 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -3141,6 +3141,23 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, u16 fc, stype; const struct ieee80211_mgmt *mgmt; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->ext_mgmt_frame_handling) { + struct rx_mgmt *rx = &data->rx_mgmt; + size_t hex_len = 2 * rx->frame_len + 1; + char *hex = os_malloc(hex_len); + if (hex) { + wpa_snprintf_hex(hex, hex_len, + rx->frame, rx->frame_len); + wpa_msg(wpa_s, MSG_INFO, "MGMT-RX freq=%d datarate=%u ssi_signal=%d %s", + rx->freq, rx->datarate, rx->ssi_signal, + hex); + os_free(hex); + } + break; + } +#endif /* CONFIG_TESTING_OPTIONS */ + mgmt = (const struct ieee80211_mgmt *) data->rx_mgmt.frame; fc = le_to_host16(mgmt->frame_control); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 376a250b..fd162d72 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -818,6 +818,7 @@ struct wpa_supplicant { u8 last_gas_dialog_token, prev_gas_dialog_token; unsigned int no_keep_alive:1; + unsigned int ext_mgmt_frame_handling:1; #ifdef CONFIG_WNM u8 wnm_dialog_token; |
