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