From: Dan Harkins Date: Fri, 25 May 2018 18:40:04 +0000 (+0300) Subject: EAP-pwd server: Add support for salted password databases X-Git-Tag: hostap_2_7~319 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d52ead3db7b28ce34df729376820f44811eec4c1;p=thirdparty%2Fhostap.git EAP-pwd server: Add support for salted password databases These changes add support for salted password databases to EAP-pwd per RFC 8146. This commits introduces the framework for enabling this and the salting mechanisms based on SHA-1, SHA256, and SHA512 hash algorithms. Signed-off-by: Dan Harkins --- diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 502ea3d94..ebd1c49ff 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -233,6 +233,62 @@ static int hostapd_config_read_maclist(const char *fname, #ifdef EAP_SERVER + +static int hostapd_config_eap_user_salted(struct hostapd_eap_user *user, + const char *hash, size_t len, + char **pos, int line, + const char *fname) +{ + char *pos2 = *pos; + + while (*pos2 != '\0' && *pos2 != ' ' && *pos2 != '\t' && *pos2 != '#') + pos2++; + + if (pos2 - *pos < (int) (2 * (len + 1))) { /* at least 1 byte of salt */ + wpa_printf(MSG_ERROR, + "Invalid salted %s hash on line %d in '%s'", + hash, line, fname); + return -1; + } + + user->password = os_malloc(len); + if (!user->password) { + wpa_printf(MSG_ERROR, + "Failed to allocate memory for salted %s hash", + hash); + return -1; + } + + if (hexstr2bin(*pos, user->password, len) < 0) { + wpa_printf(MSG_ERROR, + "Invalid salted password on line %d in '%s'", + line, fname); + return -1; + } + user->password_len = len; + *pos += 2 * len; + + user->salt_len = (pos2 - *pos) / 2; + user->salt = os_malloc(user->salt_len); + if (!user->salt) { + wpa_printf(MSG_ERROR, + "Failed to allocate memory for salted %s hash", + hash); + return -1; + } + + if (hexstr2bin(*pos, user->salt, user->salt_len) < 0) { + wpa_printf(MSG_ERROR, + "Invalid salt for password on line %d in '%s'", + line, fname); + return -1; + } + + *pos = pos2; + return 0; +} + + static int hostapd_config_read_eap_user(const char *fname, struct hostapd_bss_config *conf) { @@ -484,6 +540,24 @@ static int hostapd_config_read_eap_user(const char *fname, user->password_len = 16; user->password_hash = 1; pos = pos2; + } else if (os_strncmp(pos, "ssha1:", 6) == 0) { + pos += 6; + if (hostapd_config_eap_user_salted(user, "sha1", 20, + &pos, + line, fname) < 0) + goto failed; + } else if (os_strncmp(pos, "ssha256:", 8) == 0) { + pos += 8; + if (hostapd_config_eap_user_salted(user, "sha256", 32, + &pos, + line, fname) < 0) + goto failed; + } else if (os_strncmp(pos, "ssha512:", 8) == 0) { + pos += 8; + if (hostapd_config_eap_user_salted(user, "sha512", 64, + &pos, + line, fname) < 0) + goto failed; } else { pos2 = pos; while (*pos2 != '\0' && *pos2 != ' ' && @@ -543,6 +617,7 @@ static int hostapd_config_read_eap_user(const char *fname, return ret; } + #endif /* EAP_SERVER */ diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 1c28d662a..820cba956 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -415,6 +415,7 @@ void hostapd_config_free_eap_user(struct hostapd_eap_user *user) hostapd_config_free_radius_attr(user->accept_attr); os_free(user->identity); bin_clear_free(user->password, user->password_len); + bin_clear_free(user->salt, user->salt_len); os_free(user); } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 7d47cb5ac..5b7112609 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -160,6 +160,8 @@ struct hostapd_eap_user { } methods[EAP_MAX_METHODS]; u8 *password; size_t password_len; + u8 *salt; + size_t salt_len; /* non-zero when password is salted */ int phase2; int force_version; unsigned int wildcard_prefix:1; diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index be69d83b2..315c30bbe 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -77,6 +77,13 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, goto out; user->password_len = eap_user->password_len; user->password_hash = eap_user->password_hash; + if (eap_user->salt && eap_user->salt_len) { + user->salt = os_memdup(eap_user->salt, + eap_user->salt_len); + if (!user->salt) + goto out; + user->salt_len = eap_user->salt_len; + } } user->force_version = eap_user->force_version; user->macacl = eap_user->macacl; diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 51043ba6a..c7d15a526 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -2143,6 +2143,13 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, goto out; user->password_len = eap_user->password_len; user->password_hash = eap_user->password_hash; + if (eap_user->salt && eap_user->salt_len) { + user->salt = os_memdup(eap_user->salt, + eap_user->salt_len); + if (!user->salt) + goto out; + user->salt_len = eap_user->salt_len; + } } user->force_version = eap_user->force_version; user->macacl = eap_user->macacl; diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index f9ee7619f..bb3641f84 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -31,6 +31,8 @@ struct eap_user { size_t password_len; int password_hash; /* whether password is hashed with * nt_password_hash() */ + u8 *salt; + size_t salt_len; int phase2; int force_version; unsigned int remediation:1; diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index c1619f910..c9da72e9e 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -1820,6 +1820,8 @@ static void eap_user_free(struct eap_user *user) return; bin_clear_free(user->password, user->password_len); user->password = NULL; + bin_clear_free(user->salt, user->salt_len); + user->salt = NULL; os_free(user); } diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c index 12a549ed1..d0fa54a3a 100644 --- a/src/eap_server/eap_server_pwd.c +++ b/src/eap_server/eap_server_pwd.c @@ -27,8 +27,11 @@ struct eap_pwd_data { u8 *password; size_t password_len; int password_hash; + u8 *salt; + size_t salt_len; u32 token; u16 group_num; + u8 password_prep; EAP_PWD_group *grp; struct wpabuf *inbuf; @@ -115,6 +118,19 @@ static void * eap_pwd_init(struct eap_sm *sm) os_memcpy(data->password, sm->user->password, data->password_len); data->password_hash = sm->user->password_hash; + data->salt_len = sm->user->salt_len; + if (data->salt_len) { + data->salt = os_memdup(sm->user->salt, sm->user->salt_len); + if (!data->salt) { + wpa_printf(MSG_INFO, + "EAP-pwd: Memory allocation of salt failed"); + bin_clear_free(data->id_server, data->id_server_len); + bin_clear_free(data->password, data->password_len); + os_free(data); + return NULL; + } + } + data->in_frag_pos = data->out_frag_pos = 0; data->inbuf = data->outbuf = NULL; /* use default MTU from RFC 5931 if not configured otherwise */ @@ -137,6 +153,7 @@ static void eap_pwd_reset(struct eap_sm *sm, void *priv) bin_clear_free(data->id_peer, data->id_peer_len); bin_clear_free(data->id_server, data->id_server_len); bin_clear_free(data->password, data->password_len); + bin_clear_free(data->salt, data->salt_len); if (data->grp) { crypto_ec_deinit(data->grp->group); crypto_ec_point_deinit(data->grp->pwe, 1); @@ -172,12 +189,45 @@ static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, return; } + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd (server): password", + data->password, data->password_len); + if (data->salt_len) + wpa_hexdump(MSG_DEBUG, "EAP-pwd (server): salt", + data->salt, data->salt_len); + + /* + * If this is a salted password then figure out how it was hashed + * based on the length. + */ + if (data->salt_len) { + switch (data->password_len) { + case 20: + data->password_prep = EAP_PWD_PREP_SSHA1; + break; + case 32: + data->password_prep = EAP_PWD_PREP_SSHA256; + break; + case 64: + data->password_prep = EAP_PWD_PREP_SSHA512; + break; + default: + wpa_printf(MSG_INFO, + "EAP-pwd (server): bad size %d for salted password", + (int) data->password_len); + eap_pwd_state(data, FAILURE); + return; + } + } else { + /* Otherwise, figure out whether it's MS hashed or plain */ + data->password_prep = data->password_hash ? EAP_PWD_PREP_MS : + EAP_PWD_PREP_NONE; + } + wpabuf_put_be16(data->outbuf, data->group_num); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token)); - wpabuf_put_u8(data->outbuf, data->password_hash ? EAP_PWD_PREP_MS : - EAP_PWD_PREP_NONE); + wpabuf_put_u8(data->outbuf, data->password_prep); wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len); } @@ -254,10 +304,17 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len); - data->outbuf = wpabuf_alloc(2 * prime_len + order_len); + data->outbuf = wpabuf_alloc(2 * prime_len + order_len + + (data->salt ? 1 + data->salt_len : 0)); if (data->outbuf == NULL) goto fin; + /* If we're doing salted password prep, add the salt */ + if (data->salt_len) { + wpabuf_put_u8(data->outbuf, data->salt_len); + wpabuf_put_data(data->outbuf, data->salt, data->salt_len); + } + /* We send the element as (x,y) followed by the scalar */ wpabuf_put_data(data->outbuf, element, 2 * prime_len); wpabuf_put_data(data->outbuf, scalar, order_len); @@ -546,12 +603,15 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) || (id->prf != EAP_PWD_DEFAULT_PRF) || - id->prep != - data->password_hash ? EAP_PWD_PREP_MS : EAP_PWD_PREP_NONE) { + (id->prep != data->password_prep)) { wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters"); eap_pwd_state(data, FAILURE); return; } + if (data->id_peer || data->grp) { + wpa_printf(MSG_INFO, "EAP-pwd: data was already allocated"); + return; + } data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id)); if (data->id_peer == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); @@ -569,7 +629,12 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, return; } - if (data->password_hash) { + /* + * If it's PREP_MS then hash the password again, otherwise regardless + * of the prep the client is doing, the password we have is the one to + * use to generate the password element. + */ + if (data->password_prep == EAP_PWD_PREP_MS) { res = hash_nt_password_hash(data->password, pwhashhash); if (res) return;