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 <dharkins@lounge.org>
This commit is contained in:
Dan Harkins 2018-05-25 21:40:04 +03:00 committed by Jouni Malinen
parent a8712ce5b3
commit d52ead3db7
8 changed files with 167 additions and 6 deletions

View file

@ -233,6 +233,62 @@ static int hostapd_config_read_maclist(const char *fname,
#ifdef EAP_SERVER #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, static int hostapd_config_read_eap_user(const char *fname,
struct hostapd_bss_config *conf) 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_len = 16;
user->password_hash = 1; user->password_hash = 1;
pos = pos2; 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 { } else {
pos2 = pos; pos2 = pos;
while (*pos2 != '\0' && *pos2 != ' ' && while (*pos2 != '\0' && *pos2 != ' ' &&
@ -543,6 +617,7 @@ static int hostapd_config_read_eap_user(const char *fname,
return ret; return ret;
} }
#endif /* EAP_SERVER */ #endif /* EAP_SERVER */

View file

@ -415,6 +415,7 @@ void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
hostapd_config_free_radius_attr(user->accept_attr); hostapd_config_free_radius_attr(user->accept_attr);
os_free(user->identity); os_free(user->identity);
bin_clear_free(user->password, user->password_len); bin_clear_free(user->password, user->password_len);
bin_clear_free(user->salt, user->salt_len);
os_free(user); os_free(user);
} }

View file

@ -160,6 +160,8 @@ struct hostapd_eap_user {
} methods[EAP_MAX_METHODS]; } methods[EAP_MAX_METHODS];
u8 *password; u8 *password;
size_t password_len; size_t password_len;
u8 *salt;
size_t salt_len; /* non-zero when password is salted */
int phase2; int phase2;
int force_version; int force_version;
unsigned int wildcard_prefix:1; unsigned int wildcard_prefix:1;

View file

@ -77,6 +77,13 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
goto out; goto out;
user->password_len = eap_user->password_len; user->password_len = eap_user->password_len;
user->password_hash = eap_user->password_hash; 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->force_version = eap_user->force_version;
user->macacl = eap_user->macacl; user->macacl = eap_user->macacl;

View file

@ -2143,6 +2143,13 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
goto out; goto out;
user->password_len = eap_user->password_len; user->password_len = eap_user->password_len;
user->password_hash = eap_user->password_hash; 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->force_version = eap_user->force_version;
user->macacl = eap_user->macacl; user->macacl = eap_user->macacl;

View file

@ -31,6 +31,8 @@ struct eap_user {
size_t password_len; size_t password_len;
int password_hash; /* whether password is hashed with int password_hash; /* whether password is hashed with
* nt_password_hash() */ * nt_password_hash() */
u8 *salt;
size_t salt_len;
int phase2; int phase2;
int force_version; int force_version;
unsigned int remediation:1; unsigned int remediation:1;

View file

@ -1820,6 +1820,8 @@ static void eap_user_free(struct eap_user *user)
return; return;
bin_clear_free(user->password, user->password_len); bin_clear_free(user->password, user->password_len);
user->password = NULL; user->password = NULL;
bin_clear_free(user->salt, user->salt_len);
user->salt = NULL;
os_free(user); os_free(user);
} }

View file

@ -27,8 +27,11 @@ struct eap_pwd_data {
u8 *password; u8 *password;
size_t password_len; size_t password_len;
int password_hash; int password_hash;
u8 *salt;
size_t salt_len;
u32 token; u32 token;
u16 group_num; u16 group_num;
u8 password_prep;
EAP_PWD_group *grp; EAP_PWD_group *grp;
struct wpabuf *inbuf; 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); os_memcpy(data->password, sm->user->password, data->password_len);
data->password_hash = sm->user->password_hash; 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->in_frag_pos = data->out_frag_pos = 0;
data->inbuf = data->outbuf = NULL; data->inbuf = data->outbuf = NULL;
/* use default MTU from RFC 5931 if not configured otherwise */ /* 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_peer, data->id_peer_len);
bin_clear_free(data->id_server, data->id_server_len); bin_clear_free(data->id_server, data->id_server_len);
bin_clear_free(data->password, data->password_len); bin_clear_free(data->password, data->password_len);
bin_clear_free(data->salt, data->salt_len);
if (data->grp) { if (data->grp) {
crypto_ec_deinit(data->grp->group); crypto_ec_deinit(data->grp->group);
crypto_ec_point_deinit(data->grp->pwe, 1); 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; 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_be16(data->outbuf, data->group_num);
wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token)); wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token));
wpabuf_put_u8(data->outbuf, data->password_hash ? EAP_PWD_PREP_MS : wpabuf_put_u8(data->outbuf, data->password_prep);
EAP_PWD_PREP_NONE);
wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len); 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); 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) if (data->outbuf == NULL)
goto fin; 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 */ /* 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, element, 2 * prime_len);
wpabuf_put_data(data->outbuf, scalar, order_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) || (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
(os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) || (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) ||
(id->prf != EAP_PWD_DEFAULT_PRF) || (id->prf != EAP_PWD_DEFAULT_PRF) ||
id->prep != (id->prep != data->password_prep)) {
data->password_hash ? EAP_PWD_PREP_MS : EAP_PWD_PREP_NONE) {
wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters"); wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters");
eap_pwd_state(data, FAILURE); eap_pwd_state(data, FAILURE);
return; 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)); data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id));
if (data->id_peer == NULL) { if (data->id_peer == NULL) {
wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); 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; 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); res = hash_nt_password_hash(data->password, pwhashhash);
if (res) if (res)
return; return;