Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / eap_peer / eap_gpsk.c
index f6a1955..902b4ba 100644 (file)
@@ -1,20 +1,15 @@
 /*
  * EAP peer method: EAP-GPSK (RFC 5433)
- * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/random.h"
 #include "eap_peer/eap_i.h"
 #include "eap_common/eap_gpsk_common.h"
 
@@ -28,8 +23,8 @@ struct eap_gpsk_data {
        size_t sk_len;
        u8 pk[EAP_GPSK_MAX_PK_LEN];
        size_t pk_len;
-       u8 session_id;
-       int session_id_set;
+       u8 session_id[128];
+       size_t id_len;
        u8 *id_peer;
        size_t id_peer_len;
        u8 *id_server;
@@ -38,6 +33,7 @@ struct eap_gpsk_data {
        int specifier; /* CSuite/Specifier */
        u8 *psk;
        size_t psk_len;
+       u16 forced_cipher; /* force cipher or 0 to allow all supported */
 };
 
 
@@ -85,6 +81,7 @@ static void * eap_gpsk_init(struct eap_sm *sm)
        struct eap_gpsk_data *data;
        const u8 *identity, *password;
        size_t identity_len, password_len;
+       const char *phase1;
 
        password = eap_get_config_password(sm, &password_len);
        if (password == NULL) {
@@ -108,6 +105,18 @@ static void * eap_gpsk_init(struct eap_sm *sm)
                data->id_peer_len = identity_len;
        }
 
+       phase1 = eap_get_config_phase1(sm);
+       if (phase1) {
+               const char *pos;
+
+               pos = os_strstr(phase1, "cipher=");
+               if (pos) {
+                       data->forced_cipher = atoi(pos + 7);
+                       wpa_printf(MSG_DEBUG, "EAP-GPSK: Forced cipher %u",
+                                  data->forced_cipher);
+               }
+       }
+
        data->psk = os_malloc(password_len);
        if (data->psk == NULL) {
                eap_gpsk_deinit(sm, data);
@@ -125,8 +134,11 @@ static void eap_gpsk_deinit(struct eap_sm *sm, void *priv)
        struct eap_gpsk_data *data = priv;
        os_free(data->id_server);
        os_free(data->id_peer);
-       os_free(data->psk);
-       os_free(data);
+       if (data->psk) {
+               os_memset(data->psk, 0, data->psk_len);
+               os_free(data->psk);
+       }
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -200,7 +212,9 @@ static int eap_gpsk_select_csuite(struct eap_sm *sm,
                           i, vendor, specifier);
                if (data->vendor == EAP_GPSK_VENDOR_IETF &&
                    data->specifier == EAP_GPSK_CIPHER_RESERVED &&
-                   eap_gpsk_supported_ciphersuite(vendor, specifier)) {
+                   eap_gpsk_supported_ciphersuite(vendor, specifier) &&
+                   (!data->forced_cipher || data->forced_cipher == specifier))
+               {
                        data->vendor = vendor;
                        data->specifier = specifier;
                }
@@ -225,6 +239,8 @@ static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
                                               size_t *list_len,
                                               const u8 *pos, const u8 *end)
 {
+       size_t len;
+
        if (pos == NULL)
                return NULL;
 
@@ -232,23 +248,25 @@ static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
                wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
                return NULL;
        }
-       *list_len = WPA_GET_BE16(pos);
+       len = WPA_GET_BE16(pos);
        pos += 2;
-       if (end - pos < (int) *list_len) {
+       if (len > (size_t) (end - pos)) {
                wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
                return NULL;
        }
-       if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) {
+       if (len == 0 || (len % sizeof(struct eap_gpsk_csuite))) {
                wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu",
-                          (unsigned long) *list_len);
+                          (unsigned long) len);
                return NULL;
        }
-       *list = pos;
-       pos += *list_len;
 
-       if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0)
+       if (eap_gpsk_select_csuite(sm, data, pos, len) < 0)
                return NULL;
 
+       *list = pos;
+       *list_len = len;
+       pos += len;
+
        return pos;
 }
 
@@ -256,7 +274,7 @@ static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
 static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
                                               struct eap_gpsk_data *data,
                                               struct eap_method_ret *ret,
-                                              const struct wpabuf *reqData,
+                                              u8 identifier,
                                               const u8 *payload,
                                               size_t payload_len)
 {
@@ -278,11 +296,12 @@ static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
        pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list,
                                           &csuite_list_len, pos, end);
        if (pos == NULL) {
+               ret->methodState = METHOD_DONE;
                eap_gpsk_state(data, FAILURE);
                return NULL;
        }
 
-       resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData),
+       resp = eap_gpsk_send_gpsk_2(data, identifier,
                                    csuite_list, csuite_list_len);
        if (resp == NULL)
                return NULL;
@@ -326,7 +345,7 @@ static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
        wpabuf_put_be16(resp, data->id_server_len);
        wpabuf_put_data(resp, data->id_server, data->id_server_len);
 
-       if (os_get_random(data->rand_peer, EAP_GPSK_RAND_LEN)) {
+       if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) {
                wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data "
                           "for RAND_Peer");
                eap_gpsk_state(data, FAILURE);
@@ -359,6 +378,21 @@ static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
                return NULL;
        }
 
+       if (eap_gpsk_derive_session_id(data->psk, data->psk_len,
+                                      data->vendor, data->specifier,
+                                      data->rand_peer, data->rand_server,
+                                      data->id_peer, data->id_peer_len,
+                                      data->id_server, data->id_server_len,
+                                      EAP_TYPE_GPSK,
+                                      data->session_id, &data->id_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id");
+               eap_gpsk_state(data, FAILURE);
+               wpabuf_free(resp);
+               return NULL;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id",
+                   data->session_id, data->id_len);
+
        /* No PD_Payload_1 */
        wpabuf_put_be16(resp, 0);
 
@@ -534,7 +568,7 @@ static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data,
                wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
                return NULL;
        }
-       if (os_memcmp(mic, pos, miclen) != 0) {
+       if (os_memcmp_const(mic, pos, miclen) != 0) {
                wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3");
                wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
                wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
@@ -549,7 +583,7 @@ static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data,
 static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm,
                                               struct eap_gpsk_data *data,
                                               struct eap_method_ret *ret,
-                                              const struct wpabuf *reqData,
+                                              u8 identifier,
                                               const u8 *payload,
                                               size_t payload_len)
 {
@@ -581,7 +615,7 @@ static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm,
                           (unsigned long) (end - pos));
        }
 
-       resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData));
+       resp = eap_gpsk_send_gpsk_4(data, identifier);
        if (resp == NULL)
                return NULL;
 
@@ -636,6 +670,7 @@ static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv,
        struct wpabuf *resp;
        const u8 *pos;
        size_t len;
+       u8 opcode, id;
 
        pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len);
        if (pos == NULL || len < 1) {
@@ -643,25 +678,27 @@ static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv,
                return NULL;
        }
 
-       wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos);
+       id = eap_get_id(reqData);
+       opcode = *pos++;
+       len--;
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", opcode);
 
        ret->ignore = FALSE;
        ret->methodState = METHOD_MAY_CONT;
        ret->decision = DECISION_FAIL;
        ret->allowNotifications = FALSE;
 
-       switch (*pos) {
+       switch (opcode) {
        case EAP_GPSK_OPCODE_GPSK_1:
-               resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData,
-                                              pos + 1, len - 1);
+               resp = eap_gpsk_process_gpsk_1(sm, data, ret, id, pos, len);
                break;
        case EAP_GPSK_OPCODE_GPSK_3:
-               resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData,
-                                              pos + 1, len - 1);
+               resp = eap_gpsk_process_gpsk_3(sm, data, ret, id, pos, len);
                break;
        default:
-               wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with "
-                          "unknown opcode %d", *pos);
+               wpa_printf(MSG_DEBUG,
+                          "EAP-GPSK: Ignoring message with unknown opcode %d",
+                          opcode);
                ret->ignore = TRUE;
                return NULL;
        }
@@ -713,6 +750,24 @@ static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 }
 
 
+static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_gpsk_data *data = priv;
+       u8 *sid;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       sid = os_malloc(data->id_len);
+       if (sid == NULL)
+               return NULL;
+       os_memcpy(sid, data->session_id, data->id_len);
+       *len = data->id_len;
+
+       return sid;
+}
+
+
 int eap_peer_gpsk_register(void)
 {
        struct eap_method *eap;
@@ -729,6 +784,7 @@ int eap_peer_gpsk_register(void)
        eap->isKeyAvailable = eap_gpsk_isKeyAvailable;
        eap->getKey = eap_gpsk_getKey;
        eap->get_emsk = eap_gpsk_get_emsk;
+       eap->getSessionId = eap_gpsk_get_session_id;
 
        ret = eap_peer_method_register(eap);
        if (ret)