]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
EAP-pwd server: Add support for salted password databases
authorDan Harkins <dharkins@lounge.org>
Fri, 25 May 2018 18:40:04 +0000 (21:40 +0300)
committerJouni Malinen <j@w1.fi>
Mon, 28 May 2018 19:08:51 +0000 (22:08 +0300)
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>
hostapd/config_file.c
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/authsrv.c
src/ap/ieee802_1x.c
src/eap_server/eap.h
src/eap_server/eap_server.c
src/eap_server/eap_server_pwd.c

index 502ea3d942e826031a7d39161ce78075c4249f81..ebd1c49ffe4a8681d7d0111f2cefa061e5c7fe06 100644 (file)
@@ -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 */
 
 
index 1c28d662a5c471a10d9abc0c7fd4b1bebf911e56..820cba95658dbfee49c58a10f05733c8f542d128 100644 (file)
@@ -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);
 }
 
index 7d47cb5ac59029b96f8edb34cdf656b91d6c45f1..5b71126096bc4b80df705d3e3c15092b1e35aba7 100644 (file)
@@ -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;
index be69d83b257293f21e09ec93bada945051ae6707..315c30bbeb31e3d8225404f7c1efe7ed70c79da8 100644 (file)
@@ -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;
index 51043ba6a63705b3c9e1adb0de3e6890a8013896..c7d15a5264a6e42ed7874ce3535877557eb3896f 100644 (file)
@@ -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;
index f9ee7619fffc5f52994a5bb13840a48f43249d59..bb3641f846e1d3c39952d59d00d513d0fb4acf5d 100644 (file)
@@ -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;
index c1619f910162cc0fb84e68ae3028cd52a448fae8..c9da72e9ebab9cfe582cdb42a2079913fb7ef8f0 100644 (file)
@@ -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);
 }
 
index 12a549ed108de6ff28520ccc7e0c42c30bf4c282..d0fa54a3aba7db411def7e720a3a83dbb542aaf4 100644 (file)
@@ -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;