1189251Ssam/* 2189251Ssam * Wi-Fi Protected Setup - device attributes 3189251Ssam * Copyright (c) 2008, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12189251Ssam#include "wps_i.h" 13189251Ssam#include "wps_dev_attr.h" 14189251Ssam 15189251Ssam 16252726Srpauloint wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg) 17189251Ssam{ 18189251Ssam size_t len; 19189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Manufacturer"); 20189251Ssam wpabuf_put_be16(msg, ATTR_MANUFACTURER); 21189251Ssam len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0; 22252726Srpaulo#ifndef CONFIG_WPS_STRICT 23189251Ssam if (len == 0) { 24189251Ssam /* 25189251Ssam * Some deployed WPS implementations fail to parse zero-length 26252726Srpaulo * attributes. As a workaround, send a space character if the 27189251Ssam * device attribute string is empty. 28189251Ssam */ 29189251Ssam wpabuf_put_be16(msg, 1); 30252726Srpaulo wpabuf_put_u8(msg, ' '); 31252726Srpaulo return 0; 32189251Ssam } 33252726Srpaulo#endif /* CONFIG_WPS_STRICT */ 34252726Srpaulo wpabuf_put_be16(msg, len); 35252726Srpaulo wpabuf_put_data(msg, dev->manufacturer, len); 36189251Ssam return 0; 37189251Ssam} 38189251Ssam 39189251Ssam 40252726Srpauloint wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg) 41189251Ssam{ 42189251Ssam size_t len; 43189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Model Name"); 44189251Ssam wpabuf_put_be16(msg, ATTR_MODEL_NAME); 45189251Ssam len = dev->model_name ? os_strlen(dev->model_name) : 0; 46252726Srpaulo#ifndef CONFIG_WPS_STRICT 47189251Ssam if (len == 0) { 48189251Ssam /* 49189251Ssam * Some deployed WPS implementations fail to parse zero-length 50252726Srpaulo * attributes. As a workaround, send a space character if the 51189251Ssam * device attribute string is empty. 52189251Ssam */ 53189251Ssam wpabuf_put_be16(msg, 1); 54252726Srpaulo wpabuf_put_u8(msg, ' '); 55252726Srpaulo return 0; 56189251Ssam } 57252726Srpaulo#endif /* CONFIG_WPS_STRICT */ 58252726Srpaulo wpabuf_put_be16(msg, len); 59252726Srpaulo wpabuf_put_data(msg, dev->model_name, len); 60189251Ssam return 0; 61189251Ssam} 62189251Ssam 63189251Ssam 64252726Srpauloint wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg) 65189251Ssam{ 66189251Ssam size_t len; 67189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Model Number"); 68189251Ssam wpabuf_put_be16(msg, ATTR_MODEL_NUMBER); 69189251Ssam len = dev->model_number ? os_strlen(dev->model_number) : 0; 70252726Srpaulo#ifndef CONFIG_WPS_STRICT 71189251Ssam if (len == 0) { 72189251Ssam /* 73189251Ssam * Some deployed WPS implementations fail to parse zero-length 74252726Srpaulo * attributes. As a workaround, send a space character if the 75189251Ssam * device attribute string is empty. 76189251Ssam */ 77189251Ssam wpabuf_put_be16(msg, 1); 78252726Srpaulo wpabuf_put_u8(msg, ' '); 79252726Srpaulo return 0; 80189251Ssam } 81252726Srpaulo#endif /* CONFIG_WPS_STRICT */ 82252726Srpaulo wpabuf_put_be16(msg, len); 83252726Srpaulo wpabuf_put_data(msg, dev->model_number, len); 84189251Ssam return 0; 85189251Ssam} 86189251Ssam 87189251Ssam 88281806Srpauloint wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg) 89189251Ssam{ 90189251Ssam size_t len; 91189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Serial Number"); 92189251Ssam wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER); 93189251Ssam len = dev->serial_number ? os_strlen(dev->serial_number) : 0; 94252726Srpaulo#ifndef CONFIG_WPS_STRICT 95189251Ssam if (len == 0) { 96189251Ssam /* 97189251Ssam * Some deployed WPS implementations fail to parse zero-length 98252726Srpaulo * attributes. As a workaround, send a space character if the 99189251Ssam * device attribute string is empty. 100189251Ssam */ 101189251Ssam wpabuf_put_be16(msg, 1); 102252726Srpaulo wpabuf_put_u8(msg, ' '); 103252726Srpaulo return 0; 104189251Ssam } 105252726Srpaulo#endif /* CONFIG_WPS_STRICT */ 106252726Srpaulo wpabuf_put_be16(msg, len); 107252726Srpaulo wpabuf_put_data(msg, dev->serial_number, len); 108189251Ssam return 0; 109189251Ssam} 110189251Ssam 111189251Ssam 112189251Ssamint wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg) 113189251Ssam{ 114189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Primary Device Type"); 115189251Ssam wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE); 116214734Srpaulo wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN); 117214734Srpaulo wpabuf_put_data(msg, dev->pri_dev_type, WPS_DEV_TYPE_LEN); 118189251Ssam return 0; 119189251Ssam} 120189251Ssam 121189251Ssam 122252726Srpauloint wps_build_secondary_dev_type(struct wps_device_data *dev, 123252726Srpaulo struct wpabuf *msg) 124189251Ssam{ 125252726Srpaulo if (!dev->num_sec_dev_types) 126252726Srpaulo return 0; 127252726Srpaulo 128252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: * Secondary Device Type"); 129252726Srpaulo wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST); 130252726Srpaulo wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types); 131252726Srpaulo wpabuf_put_data(msg, dev->sec_dev_type, 132252726Srpaulo WPS_DEV_TYPE_LEN * dev->num_sec_dev_types); 133252726Srpaulo 134252726Srpaulo return 0; 135252726Srpaulo} 136252726Srpaulo 137252726Srpaulo 138252726Srpauloint wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg, 139252726Srpaulo unsigned int num_req_dev_types, 140252726Srpaulo const u8 *req_dev_types) 141252726Srpaulo{ 142252726Srpaulo unsigned int i; 143252726Srpaulo 144252726Srpaulo for (i = 0; i < num_req_dev_types; i++) { 145252726Srpaulo wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type", 146252726Srpaulo req_dev_types + i * WPS_DEV_TYPE_LEN, 147252726Srpaulo WPS_DEV_TYPE_LEN); 148252726Srpaulo wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE); 149252726Srpaulo wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN); 150252726Srpaulo wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN, 151252726Srpaulo WPS_DEV_TYPE_LEN); 152252726Srpaulo } 153252726Srpaulo 154252726Srpaulo return 0; 155252726Srpaulo} 156252726Srpaulo 157252726Srpaulo 158252726Srpauloint wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg) 159252726Srpaulo{ 160189251Ssam size_t len; 161189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Device Name"); 162189251Ssam wpabuf_put_be16(msg, ATTR_DEV_NAME); 163189251Ssam len = dev->device_name ? os_strlen(dev->device_name) : 0; 164252726Srpaulo#ifndef CONFIG_WPS_STRICT 165189251Ssam if (len == 0) { 166189251Ssam /* 167189251Ssam * Some deployed WPS implementations fail to parse zero-length 168252726Srpaulo * attributes. As a workaround, send a space character if the 169189251Ssam * device attribute string is empty. 170189251Ssam */ 171189251Ssam wpabuf_put_be16(msg, 1); 172252726Srpaulo wpabuf_put_u8(msg, ' '); 173252726Srpaulo return 0; 174189251Ssam } 175252726Srpaulo#endif /* CONFIG_WPS_STRICT */ 176252726Srpaulo wpabuf_put_be16(msg, len); 177252726Srpaulo wpabuf_put_data(msg, dev->device_name, len); 178189251Ssam return 0; 179189251Ssam} 180189251Ssam 181189251Ssam 182189251Ssamint wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg) 183189251Ssam{ 184189251Ssam if (wps_build_manufacturer(dev, msg) || 185189251Ssam wps_build_model_name(dev, msg) || 186189251Ssam wps_build_model_number(dev, msg) || 187189251Ssam wps_build_serial_number(dev, msg) || 188189251Ssam wps_build_primary_dev_type(dev, msg) || 189189251Ssam wps_build_dev_name(dev, msg)) 190189251Ssam return -1; 191189251Ssam return 0; 192189251Ssam} 193189251Ssam 194189251Ssam 195189251Ssamint wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg) 196189251Ssam{ 197189251Ssam wpa_printf(MSG_DEBUG, "WPS: * OS Version"); 198189251Ssam wpabuf_put_be16(msg, ATTR_OS_VERSION); 199189251Ssam wpabuf_put_be16(msg, 4); 200189251Ssam wpabuf_put_be32(msg, 0x80000000 | dev->os_version); 201189251Ssam return 0; 202189251Ssam} 203189251Ssam 204189251Ssam 205252726Srpauloint wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg) 206252726Srpaulo{ 207252726Srpaulo if (dev->vendor_ext_m1 != NULL) { 208252726Srpaulo wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension M1", 209252726Srpaulo wpabuf_head_u8(dev->vendor_ext_m1), 210252726Srpaulo wpabuf_len(dev->vendor_ext_m1)); 211252726Srpaulo wpabuf_put_be16(msg, ATTR_VENDOR_EXT); 212252726Srpaulo wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1)); 213252726Srpaulo wpabuf_put_buf(msg, dev->vendor_ext_m1); 214252726Srpaulo } 215252726Srpaulo return 0; 216252726Srpaulo} 217252726Srpaulo 218252726Srpaulo 219281806Srpauloint wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg, 220281806Srpaulo u8 rf_band) 221189251Ssam{ 222281806Srpaulo return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands); 223189251Ssam} 224189251Ssam 225189251Ssam 226252726Srpauloint wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg) 227252726Srpaulo{ 228252726Srpaulo int i; 229252726Srpaulo 230252726Srpaulo for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { 231252726Srpaulo if (dev->vendor_ext[i] == NULL) 232252726Srpaulo continue; 233252726Srpaulo wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension", 234252726Srpaulo wpabuf_head_u8(dev->vendor_ext[i]), 235252726Srpaulo wpabuf_len(dev->vendor_ext[i])); 236252726Srpaulo wpabuf_put_be16(msg, ATTR_VENDOR_EXT); 237252726Srpaulo wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i])); 238252726Srpaulo wpabuf_put_buf(msg, dev->vendor_ext[i]); 239252726Srpaulo } 240252726Srpaulo 241252726Srpaulo return 0; 242252726Srpaulo} 243252726Srpaulo 244252726Srpaulo 245189251Ssamstatic int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str, 246189251Ssam size_t str_len) 247189251Ssam{ 248189251Ssam if (str == NULL) { 249189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received"); 250189251Ssam return -1; 251189251Ssam } 252189251Ssam 253189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len); 254189251Ssam 255189251Ssam os_free(dev->manufacturer); 256281806Srpaulo dev->manufacturer = dup_binstr(str, str_len); 257189251Ssam if (dev->manufacturer == NULL) 258189251Ssam return -1; 259189251Ssam 260189251Ssam return 0; 261189251Ssam} 262189251Ssam 263189251Ssam 264189251Ssamstatic int wps_process_model_name(struct wps_device_data *dev, const u8 *str, 265189251Ssam size_t str_len) 266189251Ssam{ 267189251Ssam if (str == NULL) { 268189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Model Name received"); 269189251Ssam return -1; 270189251Ssam } 271189251Ssam 272189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len); 273189251Ssam 274189251Ssam os_free(dev->model_name); 275281806Srpaulo dev->model_name = dup_binstr(str, str_len); 276189251Ssam if (dev->model_name == NULL) 277189251Ssam return -1; 278189251Ssam 279189251Ssam return 0; 280189251Ssam} 281189251Ssam 282189251Ssam 283189251Ssamstatic int wps_process_model_number(struct wps_device_data *dev, const u8 *str, 284189251Ssam size_t str_len) 285189251Ssam{ 286189251Ssam if (str == NULL) { 287189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Model Number received"); 288189251Ssam return -1; 289189251Ssam } 290189251Ssam 291189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len); 292189251Ssam 293189251Ssam os_free(dev->model_number); 294281806Srpaulo dev->model_number = dup_binstr(str, str_len); 295189251Ssam if (dev->model_number == NULL) 296189251Ssam return -1; 297189251Ssam 298189251Ssam return 0; 299189251Ssam} 300189251Ssam 301189251Ssam 302189251Ssamstatic int wps_process_serial_number(struct wps_device_data *dev, 303189251Ssam const u8 *str, size_t str_len) 304189251Ssam{ 305189251Ssam if (str == NULL) { 306189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Serial Number received"); 307189251Ssam return -1; 308189251Ssam } 309189251Ssam 310189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len); 311189251Ssam 312189251Ssam os_free(dev->serial_number); 313281806Srpaulo dev->serial_number = dup_binstr(str, str_len); 314189251Ssam if (dev->serial_number == NULL) 315189251Ssam return -1; 316189251Ssam 317189251Ssam return 0; 318189251Ssam} 319189251Ssam 320189251Ssam 321189251Ssamstatic int wps_process_dev_name(struct wps_device_data *dev, const u8 *str, 322189251Ssam size_t str_len) 323189251Ssam{ 324189251Ssam if (str == NULL) { 325189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Device Name received"); 326189251Ssam return -1; 327189251Ssam } 328189251Ssam 329189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len); 330189251Ssam 331189251Ssam os_free(dev->device_name); 332281806Srpaulo dev->device_name = dup_binstr(str, str_len); 333189251Ssam if (dev->device_name == NULL) 334189251Ssam return -1; 335189251Ssam 336189251Ssam return 0; 337189251Ssam} 338189251Ssam 339189251Ssam 340189251Ssamstatic int wps_process_primary_dev_type(struct wps_device_data *dev, 341189251Ssam const u8 *dev_type) 342189251Ssam{ 343214734Srpaulo#ifndef CONFIG_NO_STDOUT_DEBUG 344214734Srpaulo char devtype[WPS_DEV_TYPE_BUFSIZE]; 345214734Srpaulo#endif /* CONFIG_NO_STDOUT_DEBUG */ 346189251Ssam 347189251Ssam if (dev_type == NULL) { 348189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received"); 349189251Ssam return -1; 350189251Ssam } 351189251Ssam 352214734Srpaulo os_memcpy(dev->pri_dev_type, dev_type, WPS_DEV_TYPE_LEN); 353214734Srpaulo wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: %s", 354214734Srpaulo wps_dev_type_bin2str(dev->pri_dev_type, devtype, 355214734Srpaulo sizeof(devtype))); 356189251Ssam 357189251Ssam return 0; 358189251Ssam} 359189251Ssam 360189251Ssam 361189251Ssamint wps_process_device_attrs(struct wps_device_data *dev, 362189251Ssam struct wps_parse_attr *attr) 363189251Ssam{ 364189251Ssam if (wps_process_manufacturer(dev, attr->manufacturer, 365189251Ssam attr->manufacturer_len) || 366189251Ssam wps_process_model_name(dev, attr->model_name, 367189251Ssam attr->model_name_len) || 368189251Ssam wps_process_model_number(dev, attr->model_number, 369189251Ssam attr->model_number_len) || 370189251Ssam wps_process_serial_number(dev, attr->serial_number, 371189251Ssam attr->serial_number_len) || 372189251Ssam wps_process_primary_dev_type(dev, attr->primary_dev_type) || 373189251Ssam wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len)) 374189251Ssam return -1; 375189251Ssam return 0; 376189251Ssam} 377189251Ssam 378189251Ssam 379189251Ssamint wps_process_os_version(struct wps_device_data *dev, const u8 *ver) 380189251Ssam{ 381189251Ssam if (ver == NULL) { 382189251Ssam wpa_printf(MSG_DEBUG, "WPS: No OS Version received"); 383189251Ssam return -1; 384189251Ssam } 385189251Ssam 386189251Ssam dev->os_version = WPA_GET_BE32(ver); 387189251Ssam wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version); 388189251Ssam 389189251Ssam return 0; 390189251Ssam} 391189251Ssam 392189251Ssam 393346981Scyvoid wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext) 394346981Scy{ 395346981Scy dev->multi_ap_ext = ext; 396346981Scy wpa_printf(MSG_DEBUG, "WPS: Multi-AP extension value %02x", 397346981Scy dev->multi_ap_ext); 398346981Scy} 399346981Scy 400346981Scy 401189251Ssamint wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands) 402189251Ssam{ 403189251Ssam if (bands == NULL) { 404189251Ssam wpa_printf(MSG_DEBUG, "WPS: No RF Bands received"); 405189251Ssam return -1; 406189251Ssam } 407189251Ssam 408189251Ssam dev->rf_bands = *bands; 409189251Ssam wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", dev->rf_bands); 410189251Ssam 411189251Ssam return 0; 412189251Ssam} 413189251Ssam 414189251Ssam 415189251Ssamvoid wps_device_data_free(struct wps_device_data *dev) 416189251Ssam{ 417189251Ssam os_free(dev->device_name); 418189251Ssam dev->device_name = NULL; 419189251Ssam os_free(dev->manufacturer); 420189251Ssam dev->manufacturer = NULL; 421189251Ssam os_free(dev->model_name); 422189251Ssam dev->model_name = NULL; 423189251Ssam os_free(dev->model_number); 424189251Ssam dev->model_number = NULL; 425189251Ssam os_free(dev->serial_number); 426189251Ssam dev->serial_number = NULL; 427189251Ssam} 428