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:
parent
a8712ce5b3
commit
d52ead3db7
8 changed files with 167 additions and 6 deletions
|
@ -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 */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue