xref: /freebsd/contrib/wpa/src/wps/wps_attr_parse.c (revision 406a584d7e80c2617dc035ede0d922215a12141c)
1 /*
2  * Wi-Fi Protected Setup - attribute parsing
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_defs.h"
13 #include "wps_attr_parse.h"
14 
15 #ifndef CONFIG_WPS_STRICT
16 #define WPS_WORKAROUNDS
17 #endif /* CONFIG_WPS_STRICT */
18 
19 
20 static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
21 					  u8 id, u8 len, const u8 *pos)
22 {
23 	wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
24 		   id, len);
25 	switch (id) {
26 	case WFA_ELEM_VERSION2:
27 		if (len != 1) {
28 			wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
29 				   "%u", len);
30 			return -1;
31 		}
32 		attr->version2 = pos;
33 		break;
34 	case WFA_ELEM_AUTHORIZEDMACS:
35 		attr->authorized_macs = pos;
36 		attr->authorized_macs_len = len;
37 		break;
38 	case WFA_ELEM_NETWORK_KEY_SHAREABLE:
39 		if (len != 1) {
40 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
41 				   "Shareable length %u", len);
42 			return -1;
43 		}
44 		attr->network_key_shareable = pos;
45 		break;
46 	case WFA_ELEM_REQUEST_TO_ENROLL:
47 		if (len != 1) {
48 			wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
49 				   "length %u", len);
50 			return -1;
51 		}
52 		attr->request_to_enroll = pos;
53 		break;
54 	case WFA_ELEM_SETTINGS_DELAY_TIME:
55 		if (len != 1) {
56 			wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
57 				   "Time length %u", len);
58 			return -1;
59 		}
60 		attr->settings_delay_time = pos;
61 		break;
62 	case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS:
63 		if (len != 2) {
64 			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u",
65 				   len);
66 			return -1;
67 		}
68 		attr->registrar_configuration_methods = pos;
69 		break;
70 	default:
71 		wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
72 			   "Extension subelement %u", id);
73 		break;
74 	}
75 
76 	return 0;
77 }
78 
79 
80 static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
81 				    u16 len)
82 {
83 	const u8 *end = pos + len;
84 	u8 id, elen;
85 
86 	while (end - pos >= 2) {
87 		id = *pos++;
88 		elen = *pos++;
89 		if (elen > end - pos)
90 			break;
91 		if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
92 			return -1;
93 		pos += elen;
94 	}
95 
96 	return 0;
97 }
98 
99 
100 static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
101 				u16 len)
102 {
103 	u32 vendor_id;
104 
105 	if (len < 3) {
106 		wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
107 		return 0;
108 	}
109 
110 	vendor_id = WPA_GET_BE24(pos);
111 	switch (vendor_id) {
112 	case WPS_VENDOR_ID_WFA:
113 		return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
114 	}
115 
116 	/* Handle unknown vendor extensions */
117 
118 	wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
119 		   vendor_id);
120 
121 	if (len > WPS_MAX_VENDOR_EXT_LEN) {
122 		wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
123 			   len);
124 		return -1;
125 	}
126 
127 	if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
128 		wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
129 			   "attribute (max %d vendor extensions)",
130 			   MAX_WPS_PARSE_VENDOR_EXT);
131 		return -1;
132 	}
133 	attr->vendor_ext[attr->num_vendor_ext] = pos;
134 	attr->vendor_ext_len[attr->num_vendor_ext] = len;
135 	attr->num_vendor_ext++;
136 
137 	return 0;
138 }
139 
140 
141 static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
142 			const u8 *pos, u16 len)
143 {
144 	switch (type) {
145 	case ATTR_VERSION:
146 		if (len != 1) {
147 			wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
148 				   len);
149 			return -1;
150 		}
151 		attr->version = pos;
152 		break;
153 	case ATTR_MSG_TYPE:
154 		if (len != 1) {
155 			wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
156 				   "length %u", len);
157 			return -1;
158 		}
159 		attr->msg_type = pos;
160 		break;
161 	case ATTR_ENROLLEE_NONCE:
162 		if (len != WPS_NONCE_LEN) {
163 			wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
164 				   "length %u", len);
165 			return -1;
166 		}
167 		attr->enrollee_nonce = pos;
168 		break;
169 	case ATTR_REGISTRAR_NONCE:
170 		if (len != WPS_NONCE_LEN) {
171 			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
172 				   "length %u", len);
173 			return -1;
174 		}
175 		attr->registrar_nonce = pos;
176 		break;
177 	case ATTR_UUID_E:
178 		if (len != WPS_UUID_LEN) {
179 			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
180 				   len);
181 			return -1;
182 		}
183 		attr->uuid_e = pos;
184 		break;
185 	case ATTR_UUID_R:
186 		if (len != WPS_UUID_LEN) {
187 			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
188 				   len);
189 			return -1;
190 		}
191 		attr->uuid_r = pos;
192 		break;
193 	case ATTR_AUTH_TYPE_FLAGS:
194 		if (len != 2) {
195 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
196 				   "Type Flags length %u", len);
197 			return -1;
198 		}
199 		attr->auth_type_flags = pos;
200 		break;
201 	case ATTR_ENCR_TYPE_FLAGS:
202 		if (len != 2) {
203 			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
204 				   "Flags length %u", len);
205 			return -1;
206 		}
207 		attr->encr_type_flags = pos;
208 		break;
209 	case ATTR_CONN_TYPE_FLAGS:
210 		if (len != 1) {
211 			wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
212 				   "Flags length %u", len);
213 			return -1;
214 		}
215 		attr->conn_type_flags = pos;
216 		break;
217 	case ATTR_CONFIG_METHODS:
218 		if (len != 2) {
219 			wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
220 				   "length %u", len);
221 			return -1;
222 		}
223 		attr->config_methods = pos;
224 		break;
225 	case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
226 		if (len != 2) {
227 			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
228 				   "Registrar Config Methods length %u", len);
229 			return -1;
230 		}
231 		attr->sel_reg_config_methods = pos;
232 		break;
233 	case ATTR_PRIMARY_DEV_TYPE:
234 		if (len != WPS_DEV_TYPE_LEN) {
235 			wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
236 				   "Type length %u", len);
237 			return -1;
238 		}
239 		attr->primary_dev_type = pos;
240 		break;
241 	case ATTR_RF_BANDS:
242 		if (len != 1) {
243 			wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
244 				   "%u", len);
245 			return -1;
246 		}
247 		attr->rf_bands = pos;
248 		break;
249 	case ATTR_ASSOC_STATE:
250 		if (len != 2) {
251 			wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
252 				   "length %u", len);
253 			return -1;
254 		}
255 		attr->assoc_state = pos;
256 		break;
257 	case ATTR_CONFIG_ERROR:
258 		if (len != 2) {
259 			wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
260 				   "Error length %u", len);
261 			return -1;
262 		}
263 		attr->config_error = pos;
264 		break;
265 	case ATTR_DEV_PASSWORD_ID:
266 		if (len != 2) {
267 			wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
268 				   "ID length %u", len);
269 			return -1;
270 		}
271 		attr->dev_password_id = pos;
272 		break;
273 	case ATTR_OOB_DEVICE_PASSWORD:
274 		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
275 		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
276 		    WPS_OOB_DEVICE_PASSWORD_LEN ||
277 		    (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
278 		     WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
279 		     WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
280 		     DEV_PW_NFC_CONNECTION_HANDOVER)) {
281 			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
282 				   "Password length %u", len);
283 			return -1;
284 		}
285 		attr->oob_dev_password = pos;
286 		attr->oob_dev_password_len = len;
287 		break;
288 	case ATTR_OS_VERSION:
289 		if (len != 4) {
290 			wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
291 				   "%u", len);
292 			return -1;
293 		}
294 		attr->os_version = pos;
295 		break;
296 	case ATTR_WPS_STATE:
297 		if (len != 1) {
298 			wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
299 				   "Setup State length %u", len);
300 			return -1;
301 		}
302 		attr->wps_state = pos;
303 		break;
304 	case ATTR_AUTHENTICATOR:
305 		if (len != WPS_AUTHENTICATOR_LEN) {
306 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
307 				   "length %u", len);
308 			return -1;
309 		}
310 		attr->authenticator = pos;
311 		break;
312 	case ATTR_R_HASH1:
313 		if (len != WPS_HASH_LEN) {
314 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
315 				   len);
316 			return -1;
317 		}
318 		attr->r_hash1 = pos;
319 		break;
320 	case ATTR_R_HASH2:
321 		if (len != WPS_HASH_LEN) {
322 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
323 				   len);
324 			return -1;
325 		}
326 		attr->r_hash2 = pos;
327 		break;
328 	case ATTR_E_HASH1:
329 		if (len != WPS_HASH_LEN) {
330 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
331 				   len);
332 			return -1;
333 		}
334 		attr->e_hash1 = pos;
335 		break;
336 	case ATTR_E_HASH2:
337 		if (len != WPS_HASH_LEN) {
338 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
339 				   len);
340 			return -1;
341 		}
342 		attr->e_hash2 = pos;
343 		break;
344 	case ATTR_R_SNONCE1:
345 		if (len != WPS_SECRET_NONCE_LEN) {
346 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
347 				   "%u", len);
348 			return -1;
349 		}
350 		attr->r_snonce1 = pos;
351 		break;
352 	case ATTR_R_SNONCE2:
353 		if (len != WPS_SECRET_NONCE_LEN) {
354 			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
355 				   "%u", len);
356 			return -1;
357 		}
358 		attr->r_snonce2 = pos;
359 		break;
360 	case ATTR_E_SNONCE1:
361 		if (len != WPS_SECRET_NONCE_LEN) {
362 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
363 				   "%u", len);
364 			return -1;
365 		}
366 		attr->e_snonce1 = pos;
367 		break;
368 	case ATTR_E_SNONCE2:
369 		if (len != WPS_SECRET_NONCE_LEN) {
370 			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
371 				   "%u", len);
372 			return -1;
373 		}
374 		attr->e_snonce2 = pos;
375 		break;
376 	case ATTR_KEY_WRAP_AUTH:
377 		if (len != WPS_KWA_LEN) {
378 			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
379 				   "Authenticator length %u", len);
380 			return -1;
381 		}
382 		attr->key_wrap_auth = pos;
383 		break;
384 	case ATTR_AUTH_TYPE:
385 		if (len != 2) {
386 			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
387 				   "Type length %u", len);
388 			return -1;
389 		}
390 		attr->auth_type = pos;
391 		break;
392 	case ATTR_ENCR_TYPE:
393 		if (len != 2) {
394 			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
395 				   "Type length %u", len);
396 			return -1;
397 		}
398 		attr->encr_type = pos;
399 		break;
400 	case ATTR_NETWORK_INDEX:
401 		if (len != 1) {
402 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
403 				   "length %u", len);
404 			return -1;
405 		}
406 		attr->network_idx = pos;
407 		break;
408 	case ATTR_NETWORK_KEY_INDEX:
409 		if (len != 1) {
410 			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
411 				   "length %u", len);
412 			return -1;
413 		}
414 		attr->network_key_idx = pos;
415 		break;
416 	case ATTR_MAC_ADDR:
417 		if (len != ETH_ALEN) {
418 			wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
419 				   "length %u", len);
420 			return -1;
421 		}
422 		attr->mac_addr = pos;
423 		break;
424 	case ATTR_SELECTED_REGISTRAR:
425 		if (len != 1) {
426 			wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
427 				   " length %u", len);
428 			return -1;
429 		}
430 		attr->selected_registrar = pos;
431 		break;
432 	case ATTR_REQUEST_TYPE:
433 		if (len != 1) {
434 			wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
435 				   "length %u", len);
436 			return -1;
437 		}
438 		attr->request_type = pos;
439 		break;
440 	case ATTR_RESPONSE_TYPE:
441 		if (len != 1) {
442 			wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
443 				   "length %u", len);
444 			return -1;
445 		}
446 		attr->response_type = pos;
447 		break;
448 	case ATTR_MANUFACTURER:
449 		attr->manufacturer = pos;
450 		if (len > WPS_MANUFACTURER_MAX_LEN)
451 			attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN;
452 		else
453 			attr->manufacturer_len = len;
454 		break;
455 	case ATTR_MODEL_NAME:
456 		attr->model_name = pos;
457 		if (len > WPS_MODEL_NAME_MAX_LEN)
458 			attr->model_name_len = WPS_MODEL_NAME_MAX_LEN;
459 		else
460 			attr->model_name_len = len;
461 		break;
462 	case ATTR_MODEL_NUMBER:
463 		attr->model_number = pos;
464 		if (len > WPS_MODEL_NUMBER_MAX_LEN)
465 			attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN;
466 		else
467 			attr->model_number_len = len;
468 		break;
469 	case ATTR_SERIAL_NUMBER:
470 		attr->serial_number = pos;
471 		if (len > WPS_SERIAL_NUMBER_MAX_LEN)
472 			attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN;
473 		else
474 			attr->serial_number_len = len;
475 		break;
476 	case ATTR_DEV_NAME:
477 		if (len > WPS_DEV_NAME_MAX_LEN) {
478 			wpa_printf(MSG_DEBUG,
479 				   "WPS: Ignore too long Device Name (len=%u)",
480 				   len);
481 			break;
482 		}
483 		attr->dev_name = pos;
484 		attr->dev_name_len = len;
485 		break;
486 	case ATTR_PUBLIC_KEY:
487 		/*
488 		 * The Public Key attribute is supposed to be exactly 192 bytes
489 		 * in length. Allow couple of bytes shorter one to try to
490 		 * interoperate with implementations that do not use proper
491 		 * zero-padding.
492 		 */
493 		if (len < 190 || len > 192) {
494 			wpa_printf(MSG_DEBUG,
495 				   "WPS: Ignore Public Key with unexpected length %u",
496 				   len);
497 			break;
498 		}
499 		attr->public_key = pos;
500 		attr->public_key_len = len;
501 		break;
502 	case ATTR_ENCR_SETTINGS:
503 		attr->encr_settings = pos;
504 		attr->encr_settings_len = len;
505 		break;
506 	case ATTR_CRED:
507 		if (attr->num_cred >= MAX_CRED_COUNT) {
508 			wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
509 				   "attribute (max %d credentials)",
510 				   MAX_CRED_COUNT);
511 			break;
512 		}
513 		attr->cred[attr->num_cred] = pos;
514 		attr->cred_len[attr->num_cred] = len;
515 		attr->num_cred++;
516 		break;
517 	case ATTR_SSID:
518 		if (len > SSID_MAX_LEN) {
519 			wpa_printf(MSG_DEBUG,
520 				   "WPS: Ignore too long SSID (len=%u)", len);
521 			break;
522 		}
523 		attr->ssid = pos;
524 		attr->ssid_len = len;
525 		break;
526 	case ATTR_NETWORK_KEY:
527 		attr->network_key = pos;
528 		attr->network_key_len = len;
529 		break;
530 	case ATTR_AP_SETUP_LOCKED:
531 		if (len != 1) {
532 			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
533 				   "length %u", len);
534 			return -1;
535 		}
536 		attr->ap_setup_locked = pos;
537 		break;
538 	case ATTR_REQUESTED_DEV_TYPE:
539 		if (len != WPS_DEV_TYPE_LEN) {
540 			wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
541 				   "Type length %u", len);
542 			return -1;
543 		}
544 		if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
545 			wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
546 				   "Type attribute (max %u types)",
547 				   MAX_REQ_DEV_TYPE_COUNT);
548 			break;
549 		}
550 		attr->req_dev_type[attr->num_req_dev_type] = pos;
551 		attr->num_req_dev_type++;
552 		break;
553 	case ATTR_SECONDARY_DEV_TYPE_LIST:
554 		if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
555 		    (len % WPS_DEV_TYPE_LEN) > 0) {
556 			wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
557 				   "Type length %u", len);
558 			return -1;
559 		}
560 		attr->sec_dev_type_list = pos;
561 		attr->sec_dev_type_list_len = len;
562 		break;
563 	case ATTR_VENDOR_EXT:
564 		if (wps_parse_vendor_ext(attr, pos, len) < 0)
565 			return -1;
566 		break;
567 	case ATTR_AP_CHANNEL:
568 		if (len != 2) {
569 			wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
570 				   "length %u", len);
571 			return -1;
572 		}
573 		attr->ap_channel = pos;
574 		break;
575 	default:
576 		wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
577 			   "len=%u", type, len);
578 		break;
579 	}
580 
581 	return 0;
582 }
583 
584 
585 int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
586 {
587 	const u8 *pos, *end;
588 	u16 type, len;
589 #ifdef WPS_WORKAROUNDS
590 	u16 prev_type = 0;
591 #endif /* WPS_WORKAROUNDS */
592 
593 	os_memset(attr, 0, sizeof(*attr));
594 	pos = wpabuf_head(msg);
595 	end = pos + wpabuf_len(msg);
596 
597 	while (pos < end) {
598 		if (end - pos < 4) {
599 			wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
600 				   "%lu bytes remaining",
601 				   (unsigned long) (end - pos));
602 			return -1;
603 		}
604 
605 		type = WPA_GET_BE16(pos);
606 		pos += 2;
607 		len = WPA_GET_BE16(pos);
608 		pos += 2;
609 		wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
610 			   type, len);
611 		if (len > end - pos) {
612 			wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
613 			wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
614 #ifdef WPS_WORKAROUNDS
615 			/*
616 			 * Some deployed APs seem to have a bug in encoding of
617 			 * Network Key attribute in the Credential attribute
618 			 * where they add an extra octet after the Network Key
619 			 * attribute at least when open network is being
620 			 * provisioned.
621 			 */
622 			if ((type & 0xff00) != 0x1000 &&
623 			    prev_type == ATTR_NETWORK_KEY) {
624 				wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
625 					   "to skip unexpected octet after "
626 					   "Network Key");
627 				pos -= 3;
628 				continue;
629 			}
630 #endif /* WPS_WORKAROUNDS */
631 			return -1;
632 		}
633 
634 #ifdef WPS_WORKAROUNDS
635 		if (type == 0 && len == 0) {
636 			/*
637 			 * Mac OS X 10.6 seems to be adding 0x00 padding to the
638 			 * end of M1. Skip those to avoid interop issues.
639 			 */
640 			int i;
641 			for (i = 0; i < end - pos; i++) {
642 				if (pos[i])
643 					break;
644 			}
645 			if (i == end - pos) {
646 				wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
647 					   "unexpected message padding");
648 				break;
649 			}
650 		}
651 #endif /* WPS_WORKAROUNDS */
652 
653 		if (wps_set_attr(attr, type, pos, len) < 0)
654 			return -1;
655 
656 #ifdef WPS_WORKAROUNDS
657 		prev_type = type;
658 #endif /* WPS_WORKAROUNDS */
659 		pos += len;
660 	}
661 
662 	return 0;
663 }
664