Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / wps / wps_attr_parse.c
index 95463eb..11a967b 100644 (file)
@@ -2,20 +2,15 @@
  * Wi-Fi Protected Setup - attribute parsing
  * Copyright (c) 2008, 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 "wps_i.h"
+#include "wps_defs.h"
+#include "wps_attr_parse.h"
 
 #ifndef CONFIG_WPS_STRICT
 #define WPS_WORKAROUNDS
@@ -64,6 +59,14 @@ static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
                }
                attr->settings_delay_time = pos;
                break;
+       case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u",
+                                  len);
+                       return -1;
+               }
+               attr->registrar_configuration_methods = pos;
+               break;
        default:
                wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
                           "Extension subelement %u", id);
@@ -80,7 +83,7 @@ static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
        const u8 *end = pos + len;
        u8 id, elen;
 
-       while (pos + 2 < end) {
+       while (pos + 2 <= end) {
                id = *pos++;
                elen = *pos++;
                if (pos + elen > end)
@@ -108,12 +111,29 @@ static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
        switch (vendor_id) {
        case WPS_VENDOR_ID_WFA:
                return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
-       default:
-               wpa_printf(MSG_MSGDUMP, "WPS: Skip unknown Vendor Extension "
-                          "(Vendor ID %u)", vendor_id);
-               break;
        }
 
+       /* Handle unknown vendor extensions */
+
+       wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
+                  vendor_id);
+
+       if (len > WPS_MAX_VENDOR_EXT_LEN) {
+               wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
+                          len);
+               return -1;
+       }
+
+       if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
+               wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
+                          "attribute (max %d vendor extensions)",
+                          MAX_WPS_PARSE_VENDOR_EXT);
+               return -1;
+       }
+       attr->vendor_ext[attr->num_vendor_ext] = pos;
+       attr->vendor_ext_len[attr->num_vendor_ext] = len;
+       attr->num_vendor_ext++;
+
        return 0;
 }
 
@@ -251,12 +271,19 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
                attr->dev_password_id = pos;
                break;
        case ATTR_OOB_DEVICE_PASSWORD:
-               if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) {
+               if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
+                   len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+                   WPS_OOB_DEVICE_PASSWORD_LEN ||
+                   (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+                    WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
+                    WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
+                    DEV_PW_NFC_CONNECTION_HANDOVER)) {
                        wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
                                   "Password length %u", len);
                        return -1;
                }
                attr->oob_dev_password = pos;
+               attr->oob_dev_password_len = len;
                break;
        case ATTR_OS_VERSION:
                if (len != 4) {
@@ -394,22 +421,6 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
                }
                attr->mac_addr = pos;
                break;
-       case ATTR_KEY_PROVIDED_AUTO:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
-                                  "Automatically length %u", len);
-                       return -1;
-               }
-               attr->key_prov_auto = pos;
-               break;
-       case ATTR_802_1X_ENABLED:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->dot1x_enabled = pos;
-               break;
        case ATTR_SELECTED_REGISTRAR:
                if (len != 1) {
                        wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
@@ -436,25 +447,55 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
                break;
        case ATTR_MANUFACTURER:
                attr->manufacturer = pos;
-               attr->manufacturer_len = len;
+               if (len > WPS_MANUFACTURER_MAX_LEN)
+                       attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN;
+               else
+                       attr->manufacturer_len = len;
                break;
        case ATTR_MODEL_NAME:
                attr->model_name = pos;
-               attr->model_name_len = len;
+               if (len > WPS_MODEL_NAME_MAX_LEN)
+                       attr->model_name_len = WPS_MODEL_NAME_MAX_LEN;
+               else
+                       attr->model_name_len = len;
                break;
        case ATTR_MODEL_NUMBER:
                attr->model_number = pos;
-               attr->model_number_len = len;
+               if (len > WPS_MODEL_NUMBER_MAX_LEN)
+                       attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN;
+               else
+                       attr->model_number_len = len;
                break;
        case ATTR_SERIAL_NUMBER:
                attr->serial_number = pos;
-               attr->serial_number_len = len;
+               if (len > WPS_SERIAL_NUMBER_MAX_LEN)
+                       attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN;
+               else
+                       attr->serial_number_len = len;
                break;
        case ATTR_DEV_NAME:
+               if (len > WPS_DEV_NAME_MAX_LEN) {
+                       wpa_printf(MSG_DEBUG,
+                                  "WPS: Ignore too long Device Name (len=%u)",
+                                  len);
+                       break;
+               }
                attr->dev_name = pos;
                attr->dev_name_len = len;
                break;
        case ATTR_PUBLIC_KEY:
+               /*
+                * The Public Key attribute is supposed to be exactly 192 bytes
+                * in length. Allow couple of bytes shorter one to try to
+                * interoperate with implementations that do not use proper
+                * zero-padding.
+                */
+               if (len < 190 || len > 192) {
+                       wpa_printf(MSG_DEBUG,
+                                  "WPS: Ignore Public Key with unexpected length %u",
+                                  len);
+                       break;
+               }
                attr->public_key = pos;
                attr->public_key_len = len;
                break;
@@ -474,6 +515,11 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
                attr->num_cred++;
                break;
        case ATTR_SSID:
+               if (len > SSID_MAX_LEN) {
+                       wpa_printf(MSG_DEBUG,
+                                  "WPS: Ignore too long SSID (len=%u)", len);
+                       break;
+               }
                attr->ssid = pos;
                attr->ssid_len = len;
                break;
@@ -481,14 +527,6 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
                attr->network_key = pos;
                attr->network_key_len = len;
                break;
-       case ATTR_EAP_TYPE:
-               attr->eap_type = pos;
-               attr->eap_type_len = len;
-               break;
-       case ATTR_EAP_IDENTITY:
-               attr->eap_identity = pos;
-               attr->eap_identity_len = len;
-               break;
        case ATTR_AP_SETUP_LOCKED:
                if (len != 1) {
                        wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
@@ -512,10 +550,28 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
                attr->req_dev_type[attr->num_req_dev_type] = pos;
                attr->num_req_dev_type++;
                break;
+       case ATTR_SECONDARY_DEV_TYPE_LIST:
+               if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
+                   (len % WPS_DEV_TYPE_LEN) > 0) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
+                                  "Type length %u", len);
+                       return -1;
+               }
+               attr->sec_dev_type_list = pos;
+               attr->sec_dev_type_list_len = len;
+               break;
        case ATTR_VENDOR_EXT:
                if (wps_parse_vendor_ext(attr, pos, len) < 0)
                        return -1;
                break;
+       case ATTR_AP_CHANNEL:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->ap_channel = pos;
+               break;
        default:
                wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
                           "len=%u", type, len);
@@ -530,7 +586,9 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
 {
        const u8 *pos, *end;
        u16 type, len;
+#ifdef WPS_WORKAROUNDS
        u16 prev_type = 0;
+#endif /* WPS_WORKAROUNDS */
 
        os_memset(attr, 0, sizeof(*attr));
        pos = wpabuf_head(msg);
@@ -595,7 +653,9 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
                if (wps_set_attr(attr, type, pos, len) < 0)
                        return -1;
 
+#ifdef WPS_WORKAROUNDS
                prev_type = type;
+#endif /* WPS_WORKAROUNDS */
                pos += len;
        }