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