1/*
2 * Wi-Fi Protected Setup - device attributes
3 * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "wps_i.h"
13#include "wps_dev_attr.h"
14
15
16int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg)
17{
18	size_t len;
19	wpa_printf(MSG_DEBUG, "WPS:  * Manufacturer");
20	wpabuf_put_be16(msg, ATTR_MANUFACTURER);
21	len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
22#ifndef CONFIG_WPS_STRICT
23	if (len == 0) {
24		/*
25		 * Some deployed WPS implementations fail to parse zero-length
26		 * attributes. As a workaround, send a space character if the
27		 * device attribute string is empty.
28		 */
29		wpabuf_put_be16(msg, 1);
30		wpabuf_put_u8(msg, ' ');
31		return 0;
32	}
33#endif /* CONFIG_WPS_STRICT */
34	wpabuf_put_be16(msg, len);
35	wpabuf_put_data(msg, dev->manufacturer, len);
36	return 0;
37}
38
39
40int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg)
41{
42	size_t len;
43	wpa_printf(MSG_DEBUG, "WPS:  * Model Name");
44	wpabuf_put_be16(msg, ATTR_MODEL_NAME);
45	len = dev->model_name ? os_strlen(dev->model_name) : 0;
46#ifndef CONFIG_WPS_STRICT
47	if (len == 0) {
48		/*
49		 * Some deployed WPS implementations fail to parse zero-length
50		 * attributes. As a workaround, send a space character if the
51		 * device attribute string is empty.
52		 */
53		wpabuf_put_be16(msg, 1);
54		wpabuf_put_u8(msg, ' ');
55		return 0;
56	}
57#endif /* CONFIG_WPS_STRICT */
58	wpabuf_put_be16(msg, len);
59	wpabuf_put_data(msg, dev->model_name, len);
60	return 0;
61}
62
63
64int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg)
65{
66	size_t len;
67	wpa_printf(MSG_DEBUG, "WPS:  * Model Number");
68	wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
69	len = dev->model_number ? os_strlen(dev->model_number) : 0;
70#ifndef CONFIG_WPS_STRICT
71	if (len == 0) {
72		/*
73		 * Some deployed WPS implementations fail to parse zero-length
74		 * attributes. As a workaround, send a space character if the
75		 * device attribute string is empty.
76		 */
77		wpabuf_put_be16(msg, 1);
78		wpabuf_put_u8(msg, ' ');
79		return 0;
80	}
81#endif /* CONFIG_WPS_STRICT */
82	wpabuf_put_be16(msg, len);
83	wpabuf_put_data(msg, dev->model_number, len);
84	return 0;
85}
86
87
88int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg)
89{
90	size_t len;
91	wpa_printf(MSG_DEBUG, "WPS:  * Serial Number");
92	wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
93	len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
94#ifndef CONFIG_WPS_STRICT
95	if (len == 0) {
96		/*
97		 * Some deployed WPS implementations fail to parse zero-length
98		 * attributes. As a workaround, send a space character if the
99		 * device attribute string is empty.
100		 */
101		wpabuf_put_be16(msg, 1);
102		wpabuf_put_u8(msg, ' ');
103		return 0;
104	}
105#endif /* CONFIG_WPS_STRICT */
106	wpabuf_put_be16(msg, len);
107	wpabuf_put_data(msg, dev->serial_number, len);
108	return 0;
109}
110
111
112int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg)
113{
114	wpa_printf(MSG_DEBUG, "WPS:  * Primary Device Type");
115	wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE);
116	wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
117	wpabuf_put_data(msg, dev->pri_dev_type, WPS_DEV_TYPE_LEN);
118	return 0;
119}
120
121
122int wps_build_secondary_dev_type(struct wps_device_data *dev,
123				  struct wpabuf *msg)
124{
125	if (!dev->num_sec_dev_types)
126		return 0;
127
128	wpa_printf(MSG_DEBUG, "WPS:  * Secondary Device Type");
129	wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST);
130	wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
131	wpabuf_put_data(msg, dev->sec_dev_type,
132			WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
133
134	return 0;
135}
136
137
138int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
139			   unsigned int num_req_dev_types,
140			   const u8 *req_dev_types)
141{
142	unsigned int i;
143
144	for (i = 0; i < num_req_dev_types; i++) {
145		wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type",
146			    req_dev_types + i * WPS_DEV_TYPE_LEN,
147			    WPS_DEV_TYPE_LEN);
148		wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE);
149		wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
150		wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN,
151				WPS_DEV_TYPE_LEN);
152	}
153
154	return 0;
155}
156
157
158int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
159{
160	size_t len;
161	wpa_printf(MSG_DEBUG, "WPS:  * Device Name");
162	wpabuf_put_be16(msg, ATTR_DEV_NAME);
163	len = dev->device_name ? os_strlen(dev->device_name) : 0;
164#ifndef CONFIG_WPS_STRICT
165	if (len == 0) {
166		/*
167		 * Some deployed WPS implementations fail to parse zero-length
168		 * attributes. As a workaround, send a space character if the
169		 * device attribute string is empty.
170		 */
171		wpabuf_put_be16(msg, 1);
172		wpabuf_put_u8(msg, ' ');
173		return 0;
174	}
175#endif /* CONFIG_WPS_STRICT */
176	wpabuf_put_be16(msg, len);
177	wpabuf_put_data(msg, dev->device_name, len);
178	return 0;
179}
180
181
182int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg)
183{
184	if (wps_build_manufacturer(dev, msg) ||
185	    wps_build_model_name(dev, msg) ||
186	    wps_build_model_number(dev, msg) ||
187	    wps_build_serial_number(dev, msg) ||
188	    wps_build_primary_dev_type(dev, msg) ||
189	    wps_build_dev_name(dev, msg))
190		return -1;
191	return 0;
192}
193
194
195int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg)
196{
197	wpa_printf(MSG_DEBUG, "WPS:  * OS Version");
198	wpabuf_put_be16(msg, ATTR_OS_VERSION);
199	wpabuf_put_be16(msg, 4);
200	wpabuf_put_be32(msg, 0x80000000 | dev->os_version);
201	return 0;
202}
203
204
205int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg)
206{
207	if (dev->vendor_ext_m1 != NULL) {
208		wpa_hexdump(MSG_DEBUG, "WPS:  * Vendor Extension M1",
209			    wpabuf_head_u8(dev->vendor_ext_m1),
210			    wpabuf_len(dev->vendor_ext_m1));
211		wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
212		wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1));
213		wpabuf_put_buf(msg, dev->vendor_ext_m1);
214	}
215	return 0;
216}
217
218
219int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg,
220		       u8 rf_band)
221{
222	return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands);
223}
224
225
226int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg)
227{
228	int i;
229
230	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
231		if (dev->vendor_ext[i] == NULL)
232			continue;
233		wpa_hexdump(MSG_DEBUG, "WPS:  * Vendor Extension",
234			    wpabuf_head_u8(dev->vendor_ext[i]),
235			    wpabuf_len(dev->vendor_ext[i]));
236		wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
237		wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i]));
238		wpabuf_put_buf(msg, dev->vendor_ext[i]);
239	}
240
241	return 0;
242}
243
244
245int wps_build_application_ext(struct wps_device_data *dev, struct wpabuf *msg)
246{
247	if (!dev->application_ext)
248		return 0;
249
250	wpa_hexdump_buf(MSG_DEBUG, "WPS:  * Application Extension",
251			dev->application_ext);
252	wpabuf_put_be16(msg, ATTR_APPLICATION_EXT);
253	wpabuf_put_be16(msg, wpabuf_len(dev->application_ext));
254	wpabuf_put_buf(msg, dev->application_ext);
255
256	return 0;
257}
258
259
260static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
261				    size_t str_len)
262{
263	if (str == NULL) {
264		wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received");
265		return -1;
266	}
267
268	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len);
269
270	os_free(dev->manufacturer);
271	dev->manufacturer = dup_binstr(str, str_len);
272	if (dev->manufacturer == NULL)
273		return -1;
274
275	return 0;
276}
277
278
279static int wps_process_model_name(struct wps_device_data *dev, const u8 *str,
280				  size_t str_len)
281{
282	if (str == NULL) {
283		wpa_printf(MSG_DEBUG, "WPS: No Model Name received");
284		return -1;
285	}
286
287	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len);
288
289	os_free(dev->model_name);
290	dev->model_name = dup_binstr(str, str_len);
291	if (dev->model_name == NULL)
292		return -1;
293
294	return 0;
295}
296
297
298static int wps_process_model_number(struct wps_device_data *dev, const u8 *str,
299				    size_t str_len)
300{
301	if (str == NULL) {
302		wpa_printf(MSG_DEBUG, "WPS: No Model Number received");
303		return -1;
304	}
305
306	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len);
307
308	os_free(dev->model_number);
309	dev->model_number = dup_binstr(str, str_len);
310	if (dev->model_number == NULL)
311		return -1;
312
313	return 0;
314}
315
316
317static int wps_process_serial_number(struct wps_device_data *dev,
318				     const u8 *str, size_t str_len)
319{
320	if (str == NULL) {
321		wpa_printf(MSG_DEBUG, "WPS: No Serial Number received");
322		return -1;
323	}
324
325	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len);
326
327	os_free(dev->serial_number);
328	dev->serial_number = dup_binstr(str, str_len);
329	if (dev->serial_number == NULL)
330		return -1;
331
332	return 0;
333}
334
335
336static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str,
337				size_t str_len)
338{
339	if (str == NULL) {
340		wpa_printf(MSG_DEBUG, "WPS: No Device Name received");
341		return -1;
342	}
343
344	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len);
345
346	os_free(dev->device_name);
347	dev->device_name = dup_binstr(str, str_len);
348	if (dev->device_name == NULL)
349		return -1;
350
351	return 0;
352}
353
354
355static int wps_process_primary_dev_type(struct wps_device_data *dev,
356					const u8 *dev_type)
357{
358#ifndef CONFIG_NO_STDOUT_DEBUG
359	char devtype[WPS_DEV_TYPE_BUFSIZE];
360#endif /* CONFIG_NO_STDOUT_DEBUG */
361
362	if (dev_type == NULL) {
363		wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received");
364		return -1;
365	}
366
367	os_memcpy(dev->pri_dev_type, dev_type, WPS_DEV_TYPE_LEN);
368	wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: %s",
369		   wps_dev_type_bin2str(dev->pri_dev_type, devtype,
370					sizeof(devtype)));
371
372	return 0;
373}
374
375
376int wps_process_device_attrs(struct wps_device_data *dev,
377			     struct wps_parse_attr *attr)
378{
379	if (wps_process_manufacturer(dev, attr->manufacturer,
380				     attr->manufacturer_len) ||
381	    wps_process_model_name(dev, attr->model_name,
382				   attr->model_name_len) ||
383	    wps_process_model_number(dev, attr->model_number,
384				     attr->model_number_len) ||
385	    wps_process_serial_number(dev, attr->serial_number,
386				      attr->serial_number_len) ||
387	    wps_process_primary_dev_type(dev, attr->primary_dev_type) ||
388	    wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len))
389		return -1;
390	return 0;
391}
392
393
394int wps_process_os_version(struct wps_device_data *dev, const u8 *ver)
395{
396	if (ver == NULL) {
397		wpa_printf(MSG_DEBUG, "WPS: No OS Version received");
398		return -1;
399	}
400
401	dev->os_version = WPA_GET_BE32(ver);
402	wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version);
403
404	return 0;
405}
406
407
408void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext)
409{
410	dev->multi_ap_ext = ext;
411	wpa_printf(MSG_DEBUG, "WPS: Multi-AP extension value %02x",
412		   dev->multi_ap_ext);
413}
414
415
416int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
417{
418	if (bands == NULL) {
419		wpa_printf(MSG_DEBUG, "WPS: No RF Bands received");
420		return -1;
421	}
422
423	dev->rf_bands = *bands;
424	wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", dev->rf_bands);
425
426	return 0;
427}
428
429
430void wps_device_data_free(struct wps_device_data *dev)
431{
432	os_free(dev->device_name);
433	dev->device_name = NULL;
434	os_free(dev->manufacturer);
435	dev->manufacturer = NULL;
436	os_free(dev->model_name);
437	dev->model_name = NULL;
438	os_free(dev->model_number);
439	dev->model_number = NULL;
440	os_free(dev->serial_number);
441	dev->serial_number = NULL;
442	wpabuf_free(dev->application_ext);
443	dev->application_ext = NULL;
444}
445