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 88189251Ssamstatic int wps_build_serial_number(struct wps_device_data *dev, 89189251Ssam struct wpabuf *msg) 90189251Ssam{ 91189251Ssam size_t len; 92189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Serial Number"); 93189251Ssam wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER); 94189251Ssam len = dev->serial_number ? os_strlen(dev->serial_number) : 0; 95252726Srpaulo#ifndef CONFIG_WPS_STRICT 96189251Ssam if (len == 0) { 97189251Ssam /* 98189251Ssam * Some deployed WPS implementations fail to parse zero-length 99252726Srpaulo * attributes. As a workaround, send a space character if the 100189251Ssam * device attribute string is empty. 101189251Ssam */ 102189251Ssam wpabuf_put_be16(msg, 1); 103252726Srpaulo wpabuf_put_u8(msg, ' '); 104252726Srpaulo return 0; 105189251Ssam } 106252726Srpaulo#endif /* CONFIG_WPS_STRICT */ 107252726Srpaulo wpabuf_put_be16(msg, len); 108252726Srpaulo wpabuf_put_data(msg, dev->serial_number, len); 109189251Ssam return 0; 110189251Ssam} 111189251Ssam 112189251Ssam 113189251Ssamint wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg) 114189251Ssam{ 115189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Primary Device Type"); 116189251Ssam wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE); 117214734Srpaulo wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN); 118214734Srpaulo wpabuf_put_data(msg, dev->pri_dev_type, WPS_DEV_TYPE_LEN); 119189251Ssam return 0; 120189251Ssam} 121189251Ssam 122189251Ssam 123252726Srpauloint wps_build_secondary_dev_type(struct wps_device_data *dev, 124252726Srpaulo struct wpabuf *msg) 125189251Ssam{ 126252726Srpaulo if (!dev->num_sec_dev_types) 127252726Srpaulo return 0; 128252726Srpaulo 129252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: * Secondary Device Type"); 130252726Srpaulo wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST); 131252726Srpaulo wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types); 132252726Srpaulo wpabuf_put_data(msg, dev->sec_dev_type, 133252726Srpaulo WPS_DEV_TYPE_LEN * dev->num_sec_dev_types); 134252726Srpaulo 135252726Srpaulo return 0; 136252726Srpaulo} 137252726Srpaulo 138252726Srpaulo 139252726Srpauloint wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg, 140252726Srpaulo unsigned int num_req_dev_types, 141252726Srpaulo const u8 *req_dev_types) 142252726Srpaulo{ 143252726Srpaulo unsigned int i; 144252726Srpaulo 145252726Srpaulo for (i = 0; i < num_req_dev_types; i++) { 146252726Srpaulo wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type", 147252726Srpaulo req_dev_types + i * WPS_DEV_TYPE_LEN, 148252726Srpaulo WPS_DEV_TYPE_LEN); 149252726Srpaulo wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE); 150252726Srpaulo wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN); 151252726Srpaulo wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN, 152252726Srpaulo WPS_DEV_TYPE_LEN); 153252726Srpaulo } 154252726Srpaulo 155252726Srpaulo return 0; 156252726Srpaulo} 157252726Srpaulo 158252726Srpaulo 159252726Srpauloint wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg) 160252726Srpaulo{ 161189251Ssam size_t len; 162189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Device Name"); 163189251Ssam wpabuf_put_be16(msg, ATTR_DEV_NAME); 164189251Ssam len = dev->device_name ? os_strlen(dev->device_name) : 0; 165252726Srpaulo#ifndef CONFIG_WPS_STRICT 166189251Ssam if (len == 0) { 167189251Ssam /* 168189251Ssam * Some deployed WPS implementations fail to parse zero-length 169252726Srpaulo * attributes. As a workaround, send a space character if the 170189251Ssam * device attribute string is empty. 171189251Ssam */ 172189251Ssam wpabuf_put_be16(msg, 1); 173252726Srpaulo wpabuf_put_u8(msg, ' '); 174252726Srpaulo return 0; 175189251Ssam } 176252726Srpaulo#endif /* CONFIG_WPS_STRICT */ 177252726Srpaulo wpabuf_put_be16(msg, len); 178252726Srpaulo wpabuf_put_data(msg, dev->device_name, len); 179189251Ssam return 0; 180189251Ssam} 181189251Ssam 182189251Ssam 183189251Ssamint wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg) 184189251Ssam{ 185189251Ssam if (wps_build_manufacturer(dev, msg) || 186189251Ssam wps_build_model_name(dev, msg) || 187189251Ssam wps_build_model_number(dev, msg) || 188189251Ssam wps_build_serial_number(dev, msg) || 189189251Ssam wps_build_primary_dev_type(dev, msg) || 190189251Ssam wps_build_dev_name(dev, msg)) 191189251Ssam return -1; 192189251Ssam return 0; 193189251Ssam} 194189251Ssam 195189251Ssam 196189251Ssamint wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg) 197189251Ssam{ 198189251Ssam wpa_printf(MSG_DEBUG, "WPS: * OS Version"); 199189251Ssam wpabuf_put_be16(msg, ATTR_OS_VERSION); 200189251Ssam wpabuf_put_be16(msg, 4); 201189251Ssam wpabuf_put_be32(msg, 0x80000000 | dev->os_version); 202189251Ssam return 0; 203189251Ssam} 204189251Ssam 205189251Ssam 206252726Srpauloint wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg) 207252726Srpaulo{ 208252726Srpaulo if (dev->vendor_ext_m1 != NULL) { 209252726Srpaulo wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension M1", 210252726Srpaulo wpabuf_head_u8(dev->vendor_ext_m1), 211252726Srpaulo wpabuf_len(dev->vendor_ext_m1)); 212252726Srpaulo wpabuf_put_be16(msg, ATTR_VENDOR_EXT); 213252726Srpaulo wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1)); 214252726Srpaulo wpabuf_put_buf(msg, dev->vendor_ext_m1); 215252726Srpaulo } 216252726Srpaulo return 0; 217252726Srpaulo} 218252726Srpaulo 219252726Srpaulo 220189251Ssamint wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg) 221189251Ssam{ 222189251Ssam wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands); 223189251Ssam wpabuf_put_be16(msg, ATTR_RF_BANDS); 224189251Ssam wpabuf_put_be16(msg, 1); 225189251Ssam wpabuf_put_u8(msg, dev->rf_bands); 226189251Ssam return 0; 227189251Ssam} 228189251Ssam 229189251Ssam 230252726Srpauloint wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg) 231252726Srpaulo{ 232252726Srpaulo int i; 233252726Srpaulo 234252726Srpaulo for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { 235252726Srpaulo if (dev->vendor_ext[i] == NULL) 236252726Srpaulo continue; 237252726Srpaulo wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension", 238252726Srpaulo wpabuf_head_u8(dev->vendor_ext[i]), 239252726Srpaulo wpabuf_len(dev->vendor_ext[i])); 240252726Srpaulo wpabuf_put_be16(msg, ATTR_VENDOR_EXT); 241252726Srpaulo wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i])); 242252726Srpaulo wpabuf_put_buf(msg, dev->vendor_ext[i]); 243252726Srpaulo } 244252726Srpaulo 245252726Srpaulo return 0; 246252726Srpaulo} 247252726Srpaulo 248252726Srpaulo 249189251Ssamstatic int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str, 250189251Ssam size_t str_len) 251189251Ssam{ 252189251Ssam if (str == NULL) { 253189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received"); 254189251Ssam return -1; 255189251Ssam } 256189251Ssam 257189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len); 258189251Ssam 259189251Ssam os_free(dev->manufacturer); 260189251Ssam dev->manufacturer = os_malloc(str_len + 1); 261189251Ssam if (dev->manufacturer == NULL) 262189251Ssam return -1; 263189251Ssam os_memcpy(dev->manufacturer, str, str_len); 264189251Ssam dev->manufacturer[str_len] = '\0'; 265189251Ssam 266189251Ssam return 0; 267189251Ssam} 268189251Ssam 269189251Ssam 270189251Ssamstatic int wps_process_model_name(struct wps_device_data *dev, const u8 *str, 271189251Ssam size_t str_len) 272189251Ssam{ 273189251Ssam if (str == NULL) { 274189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Model Name received"); 275189251Ssam return -1; 276189251Ssam } 277189251Ssam 278189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len); 279189251Ssam 280189251Ssam os_free(dev->model_name); 281189251Ssam dev->model_name = os_malloc(str_len + 1); 282189251Ssam if (dev->model_name == NULL) 283189251Ssam return -1; 284189251Ssam os_memcpy(dev->model_name, str, str_len); 285189251Ssam dev->model_name[str_len] = '\0'; 286189251Ssam 287189251Ssam return 0; 288189251Ssam} 289189251Ssam 290189251Ssam 291189251Ssamstatic int wps_process_model_number(struct wps_device_data *dev, const u8 *str, 292189251Ssam size_t str_len) 293189251Ssam{ 294189251Ssam if (str == NULL) { 295189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Model Number received"); 296189251Ssam return -1; 297189251Ssam } 298189251Ssam 299189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len); 300189251Ssam 301189251Ssam os_free(dev->model_number); 302189251Ssam dev->model_number = os_malloc(str_len + 1); 303189251Ssam if (dev->model_number == NULL) 304189251Ssam return -1; 305189251Ssam os_memcpy(dev->model_number, str, str_len); 306189251Ssam dev->model_number[str_len] = '\0'; 307189251Ssam 308189251Ssam return 0; 309189251Ssam} 310189251Ssam 311189251Ssam 312189251Ssamstatic int wps_process_serial_number(struct wps_device_data *dev, 313189251Ssam const u8 *str, size_t str_len) 314189251Ssam{ 315189251Ssam if (str == NULL) { 316189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Serial Number received"); 317189251Ssam return -1; 318189251Ssam } 319189251Ssam 320189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len); 321189251Ssam 322189251Ssam os_free(dev->serial_number); 323189251Ssam dev->serial_number = os_malloc(str_len + 1); 324189251Ssam if (dev->serial_number == NULL) 325189251Ssam return -1; 326189251Ssam os_memcpy(dev->serial_number, str, str_len); 327189251Ssam dev->serial_number[str_len] = '\0'; 328189251Ssam 329189251Ssam return 0; 330189251Ssam} 331189251Ssam 332189251Ssam 333189251Ssamstatic int wps_process_dev_name(struct wps_device_data *dev, const u8 *str, 334189251Ssam size_t str_len) 335189251Ssam{ 336189251Ssam if (str == NULL) { 337189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Device Name received"); 338189251Ssam return -1; 339189251Ssam } 340189251Ssam 341189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len); 342189251Ssam 343189251Ssam os_free(dev->device_name); 344189251Ssam dev->device_name = os_malloc(str_len + 1); 345189251Ssam if (dev->device_name == NULL) 346189251Ssam return -1; 347189251Ssam os_memcpy(dev->device_name, str, str_len); 348189251Ssam dev->device_name[str_len] = '\0'; 349189251Ssam 350189251Ssam return 0; 351189251Ssam} 352189251Ssam 353189251Ssam 354189251Ssamstatic int wps_process_primary_dev_type(struct wps_device_data *dev, 355189251Ssam const u8 *dev_type) 356189251Ssam{ 357214734Srpaulo#ifndef CONFIG_NO_STDOUT_DEBUG 358214734Srpaulo char devtype[WPS_DEV_TYPE_BUFSIZE]; 359214734Srpaulo#endif /* CONFIG_NO_STDOUT_DEBUG */ 360189251Ssam 361189251Ssam if (dev_type == NULL) { 362189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received"); 363189251Ssam return -1; 364189251Ssam } 365189251Ssam 366214734Srpaulo os_memcpy(dev->pri_dev_type, dev_type, WPS_DEV_TYPE_LEN); 367214734Srpaulo wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: %s", 368214734Srpaulo wps_dev_type_bin2str(dev->pri_dev_type, devtype, 369214734Srpaulo sizeof(devtype))); 370189251Ssam 371189251Ssam return 0; 372189251Ssam} 373189251Ssam 374189251Ssam 375189251Ssamint wps_process_device_attrs(struct wps_device_data *dev, 376189251Ssam struct wps_parse_attr *attr) 377189251Ssam{ 378189251Ssam if (wps_process_manufacturer(dev, attr->manufacturer, 379189251Ssam attr->manufacturer_len) || 380189251Ssam wps_process_model_name(dev, attr->model_name, 381189251Ssam attr->model_name_len) || 382189251Ssam wps_process_model_number(dev, attr->model_number, 383189251Ssam attr->model_number_len) || 384189251Ssam wps_process_serial_number(dev, attr->serial_number, 385189251Ssam attr->serial_number_len) || 386189251Ssam wps_process_primary_dev_type(dev, attr->primary_dev_type) || 387189251Ssam wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len)) 388189251Ssam return -1; 389189251Ssam return 0; 390189251Ssam} 391189251Ssam 392189251Ssam 393189251Ssamint wps_process_os_version(struct wps_device_data *dev, const u8 *ver) 394189251Ssam{ 395189251Ssam if (ver == NULL) { 396189251Ssam wpa_printf(MSG_DEBUG, "WPS: No OS Version received"); 397189251Ssam return -1; 398189251Ssam } 399189251Ssam 400189251Ssam dev->os_version = WPA_GET_BE32(ver); 401189251Ssam wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version); 402189251Ssam 403189251Ssam return 0; 404189251Ssam} 405189251Ssam 406189251Ssam 407189251Ssamint wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands) 408189251Ssam{ 409189251Ssam if (bands == NULL) { 410189251Ssam wpa_printf(MSG_DEBUG, "WPS: No RF Bands received"); 411189251Ssam return -1; 412189251Ssam } 413189251Ssam 414189251Ssam dev->rf_bands = *bands; 415189251Ssam wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", dev->rf_bands); 416189251Ssam 417189251Ssam return 0; 418189251Ssam} 419189251Ssam 420189251Ssam 421189251Ssamvoid wps_device_data_dup(struct wps_device_data *dst, 422189251Ssam const struct wps_device_data *src) 423189251Ssam{ 424189251Ssam if (src->device_name) 425189251Ssam dst->device_name = os_strdup(src->device_name); 426189251Ssam if (src->manufacturer) 427189251Ssam dst->manufacturer = os_strdup(src->manufacturer); 428189251Ssam if (src->model_name) 429189251Ssam dst->model_name = os_strdup(src->model_name); 430189251Ssam if (src->model_number) 431189251Ssam dst->model_number = os_strdup(src->model_number); 432189251Ssam if (src->serial_number) 433189251Ssam dst->serial_number = os_strdup(src->serial_number); 434214734Srpaulo os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN); 435189251Ssam dst->os_version = src->os_version; 436189251Ssam dst->rf_bands = src->rf_bands; 437189251Ssam} 438189251Ssam 439189251Ssam 440189251Ssamvoid wps_device_data_free(struct wps_device_data *dev) 441189251Ssam{ 442189251Ssam os_free(dev->device_name); 443189251Ssam dev->device_name = NULL; 444189251Ssam os_free(dev->manufacturer); 445189251Ssam dev->manufacturer = NULL; 446189251Ssam os_free(dev->model_name); 447189251Ssam dev->model_name = NULL; 448189251Ssam os_free(dev->model_number); 449189251Ssam dev->model_number = NULL; 450189251Ssam os_free(dev->serial_number); 451189251Ssam dev->serial_number = NULL; 452189251Ssam} 453