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