aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Harkins <dharkins@lounge.org>2018-05-25 18:40:04 (GMT)
committerJouni Malinen <j@w1.fi>2018-05-28 19:08:51 (GMT)
commitd52ead3db7b28ce34df729376820f44811eec4c1 (patch)
tree7931563db1e44c4b6647db0afa7130c322a7a981
parenta8712ce5b3c897964e26ec7040db72e0a85e11fa (diff)
downloadhostap-d52ead3db7b28ce34df729376820f44811eec4c1.zip
hostap-d52ead3db7b28ce34df729376820f44811eec4c1.tar.gz
hostap-d52ead3db7b28ce34df729376820f44811eec4c1.tar.bz2
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>
-rw-r--r--hostapd/config_file.c75
-rw-r--r--src/ap/ap_config.c1
-rw-r--r--src/ap/ap_config.h2
-rw-r--r--src/ap/authsrv.c7
-rw-r--r--src/ap/ieee802_1x.c7
-rw-r--r--src/eap_server/eap.h2
-rw-r--r--src/eap_server/eap_server.c2
-rw-r--r--src/eap_server/eap_server_pwd.c77
8 files changed, 167 insertions, 6 deletions
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 502ea3d..ebd1c49 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 1c28d66..820cba9 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 7d47cb5..5b71126 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 be69d83..315c30b 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 51043ba..c7d15a5 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 f9ee761..bb3641f 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 c1619f9..c9da72e 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 12a549e..d0fa54a 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;