1189251Ssam/* 2189251Ssam * Wi-Fi Protected Setup - device attributes 3189251Ssam * Copyright (c) 2008, Jouni Malinen <j@w1.fi> 4189251Ssam * 5189251Ssam * This program is free software; you can redistribute it and/or modify 6189251Ssam * it under the terms of the GNU General Public License version 2 as 7189251Ssam * published by the Free Software Foundation. 8189251Ssam * 9189251Ssam * Alternatively, this software may be distributed under the terms of BSD 10189251Ssam * license. 11189251Ssam * 12189251Ssam * See README and COPYING for more details. 13189251Ssam */ 14189251Ssam 15189251Ssam#include "includes.h" 16189251Ssam 17189251Ssam#include "common.h" 18189251Ssam#include "wps_i.h" 19189251Ssam#include "wps_dev_attr.h" 20189251Ssam 21189251Ssam 22189251Ssamstatic int wps_build_manufacturer(struct wps_device_data *dev, 23189251Ssam struct wpabuf *msg) 24189251Ssam{ 25189251Ssam size_t len; 26189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Manufacturer"); 27189251Ssam wpabuf_put_be16(msg, ATTR_MANUFACTURER); 28189251Ssam len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0; 29189251Ssam if (len == 0) { 30189251Ssam /* 31189251Ssam * Some deployed WPS implementations fail to parse zero-length 32189251Ssam * attributes. As a workaround, send a null character if the 33189251Ssam * device attribute string is empty. 34189251Ssam */ 35189251Ssam wpabuf_put_be16(msg, 1); 36189251Ssam wpabuf_put_u8(msg, '\0'); 37189251Ssam } else { 38189251Ssam wpabuf_put_be16(msg, len); 39189251Ssam wpabuf_put_data(msg, dev->manufacturer, len); 40189251Ssam } 41189251Ssam return 0; 42189251Ssam} 43189251Ssam 44189251Ssam 45189251Ssamstatic int wps_build_model_name(struct wps_device_data *dev, 46189251Ssam struct wpabuf *msg) 47189251Ssam{ 48189251Ssam size_t len; 49189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Model Name"); 50189251Ssam wpabuf_put_be16(msg, ATTR_MODEL_NAME); 51189251Ssam len = dev->model_name ? os_strlen(dev->model_name) : 0; 52189251Ssam if (len == 0) { 53189251Ssam /* 54189251Ssam * Some deployed WPS implementations fail to parse zero-length 55189251Ssam * attributes. As a workaround, send a null character if the 56189251Ssam * device attribute string is empty. 57189251Ssam */ 58189251Ssam wpabuf_put_be16(msg, 1); 59189251Ssam wpabuf_put_u8(msg, '\0'); 60189251Ssam } else { 61189251Ssam wpabuf_put_be16(msg, len); 62189251Ssam wpabuf_put_data(msg, dev->model_name, len); 63189251Ssam } 64189251Ssam return 0; 65189251Ssam} 66189251Ssam 67189251Ssam 68189251Ssamstatic int wps_build_model_number(struct wps_device_data *dev, 69189251Ssam struct wpabuf *msg) 70189251Ssam{ 71189251Ssam size_t len; 72189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Model Number"); 73189251Ssam wpabuf_put_be16(msg, ATTR_MODEL_NUMBER); 74189251Ssam len = dev->model_number ? os_strlen(dev->model_number) : 0; 75189251Ssam if (len == 0) { 76189251Ssam /* 77189251Ssam * Some deployed WPS implementations fail to parse zero-length 78189251Ssam * attributes. As a workaround, send a null character if the 79189251Ssam * device attribute string is empty. 80189251Ssam */ 81189251Ssam wpabuf_put_be16(msg, 1); 82189251Ssam wpabuf_put_u8(msg, '\0'); 83189251Ssam } else { 84189251Ssam wpabuf_put_be16(msg, len); 85189251Ssam wpabuf_put_data(msg, dev->model_number, len); 86189251Ssam } 87189251Ssam return 0; 88189251Ssam} 89189251Ssam 90189251Ssam 91189251Ssamstatic int wps_build_serial_number(struct wps_device_data *dev, 92189251Ssam struct wpabuf *msg) 93189251Ssam{ 94189251Ssam size_t len; 95189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Serial Number"); 96189251Ssam wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER); 97189251Ssam len = dev->serial_number ? os_strlen(dev->serial_number) : 0; 98189251Ssam if (len == 0) { 99189251Ssam /* 100189251Ssam * Some deployed WPS implementations fail to parse zero-length 101189251Ssam * attributes. As a workaround, send a null character if the 102189251Ssam * device attribute string is empty. 103189251Ssam */ 104189251Ssam wpabuf_put_be16(msg, 1); 105189251Ssam wpabuf_put_u8(msg, '\0'); 106189251Ssam } else { 107189251Ssam wpabuf_put_be16(msg, len); 108189251Ssam wpabuf_put_data(msg, dev->serial_number, len); 109189251Ssam } 110189251Ssam return 0; 111189251Ssam} 112189251Ssam 113189251Ssam 114189251Ssamint wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg) 115189251Ssam{ 116189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Primary Device Type"); 117189251Ssam wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE); 118214734Srpaulo wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN); 119214734Srpaulo wpabuf_put_data(msg, dev->pri_dev_type, WPS_DEV_TYPE_LEN); 120189251Ssam return 0; 121189251Ssam} 122189251Ssam 123189251Ssam 124189251Ssamstatic int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg) 125189251Ssam{ 126189251Ssam size_t len; 127189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Device Name"); 128189251Ssam wpabuf_put_be16(msg, ATTR_DEV_NAME); 129189251Ssam len = dev->device_name ? os_strlen(dev->device_name) : 0; 130189251Ssam if (len == 0) { 131189251Ssam /* 132189251Ssam * Some deployed WPS implementations fail to parse zero-length 133189251Ssam * attributes. As a workaround, send a null character if the 134189251Ssam * device attribute string is empty. 135189251Ssam */ 136189251Ssam wpabuf_put_be16(msg, 1); 137189251Ssam wpabuf_put_u8(msg, '\0'); 138189251Ssam } else { 139189251Ssam wpabuf_put_be16(msg, len); 140189251Ssam wpabuf_put_data(msg, dev->device_name, len); 141189251Ssam } 142189251Ssam return 0; 143189251Ssam} 144189251Ssam 145189251Ssam 146189251Ssamint wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg) 147189251Ssam{ 148189251Ssam if (wps_build_manufacturer(dev, msg) || 149189251Ssam wps_build_model_name(dev, msg) || 150189251Ssam wps_build_model_number(dev, msg) || 151189251Ssam wps_build_serial_number(dev, msg) || 152189251Ssam wps_build_primary_dev_type(dev, msg) || 153189251Ssam wps_build_dev_name(dev, msg)) 154189251Ssam return -1; 155189251Ssam return 0; 156189251Ssam} 157189251Ssam 158189251Ssam 159189251Ssamint wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg) 160189251Ssam{ 161189251Ssam wpa_printf(MSG_DEBUG, "WPS: * OS Version"); 162189251Ssam wpabuf_put_be16(msg, ATTR_OS_VERSION); 163189251Ssam wpabuf_put_be16(msg, 4); 164189251Ssam wpabuf_put_be32(msg, 0x80000000 | dev->os_version); 165189251Ssam return 0; 166189251Ssam} 167189251Ssam 168189251Ssam 169189251Ssamint wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg) 170189251Ssam{ 171189251Ssam wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands); 172189251Ssam wpabuf_put_be16(msg, ATTR_RF_BANDS); 173189251Ssam wpabuf_put_be16(msg, 1); 174189251Ssam wpabuf_put_u8(msg, dev->rf_bands); 175189251Ssam return 0; 176189251Ssam} 177189251Ssam 178189251Ssam 179189251Ssamstatic int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str, 180189251Ssam size_t str_len) 181189251Ssam{ 182189251Ssam if (str == NULL) { 183189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received"); 184189251Ssam return -1; 185189251Ssam } 186189251Ssam 187189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len); 188189251Ssam 189189251Ssam os_free(dev->manufacturer); 190189251Ssam dev->manufacturer = os_malloc(str_len + 1); 191189251Ssam if (dev->manufacturer == NULL) 192189251Ssam return -1; 193189251Ssam os_memcpy(dev->manufacturer, str, str_len); 194189251Ssam dev->manufacturer[str_len] = '\0'; 195189251Ssam 196189251Ssam return 0; 197189251Ssam} 198189251Ssam 199189251Ssam 200189251Ssamstatic int wps_process_model_name(struct wps_device_data *dev, const u8 *str, 201189251Ssam size_t str_len) 202189251Ssam{ 203189251Ssam if (str == NULL) { 204189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Model Name received"); 205189251Ssam return -1; 206189251Ssam } 207189251Ssam 208189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len); 209189251Ssam 210189251Ssam os_free(dev->model_name); 211189251Ssam dev->model_name = os_malloc(str_len + 1); 212189251Ssam if (dev->model_name == NULL) 213189251Ssam return -1; 214189251Ssam os_memcpy(dev->model_name, str, str_len); 215189251Ssam dev->model_name[str_len] = '\0'; 216189251Ssam 217189251Ssam return 0; 218189251Ssam} 219189251Ssam 220189251Ssam 221189251Ssamstatic int wps_process_model_number(struct wps_device_data *dev, const u8 *str, 222189251Ssam size_t str_len) 223189251Ssam{ 224189251Ssam if (str == NULL) { 225189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Model Number received"); 226189251Ssam return -1; 227189251Ssam } 228189251Ssam 229189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len); 230189251Ssam 231189251Ssam os_free(dev->model_number); 232189251Ssam dev->model_number = os_malloc(str_len + 1); 233189251Ssam if (dev->model_number == NULL) 234189251Ssam return -1; 235189251Ssam os_memcpy(dev->model_number, str, str_len); 236189251Ssam dev->model_number[str_len] = '\0'; 237189251Ssam 238189251Ssam return 0; 239189251Ssam} 240189251Ssam 241189251Ssam 242189251Ssamstatic int wps_process_serial_number(struct wps_device_data *dev, 243189251Ssam const u8 *str, size_t str_len) 244189251Ssam{ 245189251Ssam if (str == NULL) { 246189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Serial Number received"); 247189251Ssam return -1; 248189251Ssam } 249189251Ssam 250189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len); 251189251Ssam 252189251Ssam os_free(dev->serial_number); 253189251Ssam dev->serial_number = os_malloc(str_len + 1); 254189251Ssam if (dev->serial_number == NULL) 255189251Ssam return -1; 256189251Ssam os_memcpy(dev->serial_number, str, str_len); 257189251Ssam dev->serial_number[str_len] = '\0'; 258189251Ssam 259189251Ssam return 0; 260189251Ssam} 261189251Ssam 262189251Ssam 263189251Ssamstatic int wps_process_dev_name(struct wps_device_data *dev, const u8 *str, 264189251Ssam size_t str_len) 265189251Ssam{ 266189251Ssam if (str == NULL) { 267189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Device Name received"); 268189251Ssam return -1; 269189251Ssam } 270189251Ssam 271189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len); 272189251Ssam 273189251Ssam os_free(dev->device_name); 274189251Ssam dev->device_name = os_malloc(str_len + 1); 275189251Ssam if (dev->device_name == NULL) 276189251Ssam return -1; 277189251Ssam os_memcpy(dev->device_name, str, str_len); 278189251Ssam dev->device_name[str_len] = '\0'; 279189251Ssam 280189251Ssam return 0; 281189251Ssam} 282189251Ssam 283189251Ssam 284189251Ssamstatic int wps_process_primary_dev_type(struct wps_device_data *dev, 285189251Ssam const u8 *dev_type) 286189251Ssam{ 287214734Srpaulo#ifndef CONFIG_NO_STDOUT_DEBUG 288214734Srpaulo char devtype[WPS_DEV_TYPE_BUFSIZE]; 289214734Srpaulo#endif /* CONFIG_NO_STDOUT_DEBUG */ 290189251Ssam 291189251Ssam if (dev_type == NULL) { 292189251Ssam wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received"); 293189251Ssam return -1; 294189251Ssam } 295189251Ssam 296214734Srpaulo os_memcpy(dev->pri_dev_type, dev_type, WPS_DEV_TYPE_LEN); 297214734Srpaulo wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: %s", 298214734Srpaulo wps_dev_type_bin2str(dev->pri_dev_type, devtype, 299214734Srpaulo sizeof(devtype))); 300189251Ssam 301189251Ssam return 0; 302189251Ssam} 303189251Ssam 304189251Ssam 305189251Ssamint wps_process_device_attrs(struct wps_device_data *dev, 306189251Ssam struct wps_parse_attr *attr) 307189251Ssam{ 308189251Ssam if (wps_process_manufacturer(dev, attr->manufacturer, 309189251Ssam attr->manufacturer_len) || 310189251Ssam wps_process_model_name(dev, attr->model_name, 311189251Ssam attr->model_name_len) || 312189251Ssam wps_process_model_number(dev, attr->model_number, 313189251Ssam attr->model_number_len) || 314189251Ssam wps_process_serial_number(dev, attr->serial_number, 315189251Ssam attr->serial_number_len) || 316189251Ssam wps_process_primary_dev_type(dev, attr->primary_dev_type) || 317189251Ssam wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len)) 318189251Ssam return -1; 319189251Ssam return 0; 320189251Ssam} 321189251Ssam 322189251Ssam 323189251Ssamint wps_process_os_version(struct wps_device_data *dev, const u8 *ver) 324189251Ssam{ 325189251Ssam if (ver == NULL) { 326189251Ssam wpa_printf(MSG_DEBUG, "WPS: No OS Version received"); 327189251Ssam return -1; 328189251Ssam } 329189251Ssam 330189251Ssam dev->os_version = WPA_GET_BE32(ver); 331189251Ssam wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version); 332189251Ssam 333189251Ssam return 0; 334189251Ssam} 335189251Ssam 336189251Ssam 337189251Ssamint wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands) 338189251Ssam{ 339189251Ssam if (bands == NULL) { 340189251Ssam wpa_printf(MSG_DEBUG, "WPS: No RF Bands received"); 341189251Ssam return -1; 342189251Ssam } 343189251Ssam 344189251Ssam dev->rf_bands = *bands; 345189251Ssam wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", dev->rf_bands); 346189251Ssam 347189251Ssam return 0; 348189251Ssam} 349189251Ssam 350189251Ssam 351189251Ssamvoid wps_device_data_dup(struct wps_device_data *dst, 352189251Ssam const struct wps_device_data *src) 353189251Ssam{ 354189251Ssam if (src->device_name) 355189251Ssam dst->device_name = os_strdup(src->device_name); 356189251Ssam if (src->manufacturer) 357189251Ssam dst->manufacturer = os_strdup(src->manufacturer); 358189251Ssam if (src->model_name) 359189251Ssam dst->model_name = os_strdup(src->model_name); 360189251Ssam if (src->model_number) 361189251Ssam dst->model_number = os_strdup(src->model_number); 362189251Ssam if (src->serial_number) 363189251Ssam dst->serial_number = os_strdup(src->serial_number); 364214734Srpaulo os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN); 365189251Ssam dst->os_version = src->os_version; 366189251Ssam dst->rf_bands = src->rf_bands; 367189251Ssam} 368189251Ssam 369189251Ssam 370189251Ssamvoid wps_device_data_free(struct wps_device_data *dev) 371189251Ssam{ 372189251Ssam os_free(dev->device_name); 373189251Ssam dev->device_name = NULL; 374189251Ssam os_free(dev->manufacturer); 375189251Ssam dev->manufacturer = NULL; 376189251Ssam os_free(dev->model_name); 377189251Ssam dev->model_name = NULL; 378189251Ssam os_free(dev->model_number); 379189251Ssam dev->model_number = NULL; 380189251Ssam os_free(dev->serial_number); 381189251Ssam dev->serial_number = NULL; 382189251Ssam} 383