Branch data Line data Source code
1 : : /*
2 : : * Wi-Fi Protected Setup - Enrollee
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 "crypto/crypto.h"
13 : : #include "crypto/sha256.h"
14 : : #include "crypto/random.h"
15 : : #include "wps_i.h"
16 : : #include "wps_dev_attr.h"
17 : :
18 : :
19 : 64 : static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
20 : : {
21 : : u8 state;
22 [ - + ]: 64 : if (wps->wps->ap)
23 : 0 : state = wps->wps->wps_state;
24 : : else
25 : 64 : state = WPS_STATE_NOT_CONFIGURED;
26 : 64 : wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State (%d)",
27 : : state);
28 : 64 : wpabuf_put_be16(msg, ATTR_WPS_STATE);
29 : 64 : wpabuf_put_be16(msg, 1);
30 : 64 : wpabuf_put_u8(msg, state);
31 : 64 : return 0;
32 : : }
33 : :
34 : :
35 : 62 : static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
36 : : {
37 : : u8 *hash;
38 : : const u8 *addr[4];
39 : : size_t len[4];
40 : :
41 [ - + ]: 62 : if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
42 : 0 : return -1;
43 : 62 : wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
44 : 62 : wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
45 : : wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
46 : :
47 [ + - ][ - + ]: 62 : if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
48 : 0 : wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
49 : : "E-Hash derivation");
50 : 0 : return -1;
51 : : }
52 : :
53 : 62 : wpa_printf(MSG_DEBUG, "WPS: * E-Hash1");
54 : 62 : wpabuf_put_be16(msg, ATTR_E_HASH1);
55 : 62 : wpabuf_put_be16(msg, SHA256_MAC_LEN);
56 : 62 : hash = wpabuf_put(msg, SHA256_MAC_LEN);
57 : : /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
58 : 62 : addr[0] = wps->snonce;
59 : 62 : len[0] = WPS_SECRET_NONCE_LEN;
60 : 62 : addr[1] = wps->psk1;
61 : 62 : len[1] = WPS_PSK_LEN;
62 : 62 : addr[2] = wpabuf_head(wps->dh_pubkey_e);
63 : 62 : len[2] = wpabuf_len(wps->dh_pubkey_e);
64 : 62 : addr[3] = wpabuf_head(wps->dh_pubkey_r);
65 : 62 : len[3] = wpabuf_len(wps->dh_pubkey_r);
66 : 62 : hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
67 : 62 : wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
68 : :
69 : 62 : wpa_printf(MSG_DEBUG, "WPS: * E-Hash2");
70 : 62 : wpabuf_put_be16(msg, ATTR_E_HASH2);
71 : 62 : wpabuf_put_be16(msg, SHA256_MAC_LEN);
72 : 62 : hash = wpabuf_put(msg, SHA256_MAC_LEN);
73 : : /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
74 : 62 : addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
75 : 62 : addr[1] = wps->psk2;
76 : 62 : hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
77 : 62 : wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
78 : :
79 : 62 : return 0;
80 : : }
81 : :
82 : :
83 : 62 : static int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
84 : : {
85 : 62 : wpa_printf(MSG_DEBUG, "WPS: * E-SNonce1");
86 : 62 : wpabuf_put_be16(msg, ATTR_E_SNONCE1);
87 : 62 : wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
88 : 62 : wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
89 : 62 : return 0;
90 : : }
91 : :
92 : :
93 : 62 : static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
94 : : {
95 : 62 : wpa_printf(MSG_DEBUG, "WPS: * E-SNonce2");
96 : 62 : wpabuf_put_be16(msg, ATTR_E_SNONCE2);
97 : 62 : wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
98 : 62 : wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
99 : : WPS_SECRET_NONCE_LEN);
100 : 62 : return 0;
101 : : }
102 : :
103 : :
104 : 64 : static struct wpabuf * wps_build_m1(struct wps_data *wps)
105 : : {
106 : : struct wpabuf *msg;
107 : : u16 config_methods;
108 : :
109 [ - + ]: 64 : if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
110 : 0 : return NULL;
111 : 64 : wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
112 : 64 : wps->nonce_e, WPS_NONCE_LEN);
113 : :
114 : 64 : wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
115 : 64 : msg = wpabuf_alloc(1000);
116 [ - + ]: 64 : if (msg == NULL)
117 : 0 : return NULL;
118 : :
119 : 64 : config_methods = wps->wps->config_methods;
120 [ - + ][ # # ]: 64 : if (wps->wps->ap && !wps->pbc_in_m1 &&
[ # # ]
121 [ # # ]: 0 : (wps->dev_password_len != 0 ||
122 : 0 : (config_methods & WPS_CONFIG_DISPLAY))) {
123 : : /*
124 : : * These are the methods that the AP supports as an Enrollee
125 : : * for adding external Registrars, so remove PushButton.
126 : : *
127 : : * As a workaround for Windows 7 mechanism for probing WPS
128 : : * capabilities from M1, leave PushButton option if no PIN
129 : : * method is available or if WPS configuration enables PBC
130 : : * workaround.
131 : : */
132 : 0 : config_methods &= ~WPS_CONFIG_PUSHBUTTON;
133 : : #ifdef CONFIG_WPS2
134 : 0 : config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
135 : : WPS_CONFIG_PHY_PUSHBUTTON);
136 : : #endif /* CONFIG_WPS2 */
137 : : }
138 : :
139 [ + - + - ]: 128 : if (wps_build_version(msg) ||
140 [ + - ]: 128 : wps_build_msg_type(msg, WPS_M1) ||
141 [ + - ]: 128 : wps_build_uuid_e(msg, wps->uuid_e) ||
142 [ + - ]: 128 : wps_build_mac_addr(msg, wps->mac_addr_e) ||
143 [ + - ]: 128 : wps_build_enrollee_nonce(wps, msg) ||
144 [ + - ]: 128 : wps_build_public_key(wps, msg) ||
145 [ + - ]: 128 : wps_build_auth_type_flags(wps, msg) ||
146 [ + - ]: 128 : wps_build_encr_type_flags(wps, msg) ||
147 [ + - ]: 128 : wps_build_conn_type_flags(wps, msg) ||
148 [ + - ]: 128 : wps_build_config_methods(msg, config_methods) ||
149 [ + - ]: 128 : wps_build_wps_state(wps, msg) ||
150 [ + - ]: 128 : wps_build_device_attrs(&wps->wps->dev, msg) ||
151 : 64 : wps_build_rf_bands(&wps->wps->dev, msg,
152 [ + - ]: 128 : wps->wps->rf_band_cb(wps->wps->cb_ctx)) ||
153 [ + - ]: 128 : wps_build_assoc_state(wps, msg) ||
154 [ + - ]: 128 : wps_build_dev_password_id(msg, wps->dev_pw_id) ||
155 [ + - ]: 128 : wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
156 [ + - ]: 128 : wps_build_os_version(&wps->wps->dev, msg) ||
157 [ - + ]: 128 : wps_build_wfa_ext(msg, 0, NULL, 0) ||
158 : 64 : wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
159 : 0 : wpabuf_free(msg);
160 : 0 : return NULL;
161 : : }
162 : :
163 : 64 : wps->state = RECV_M2;
164 : 64 : return msg;
165 : : }
166 : :
167 : :
168 : 62 : static struct wpabuf * wps_build_m3(struct wps_data *wps)
169 : : {
170 : : struct wpabuf *msg;
171 : :
172 : 62 : wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
173 : :
174 [ - + ]: 62 : if (wps->dev_password == NULL) {
175 : 0 : wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
176 : 0 : return NULL;
177 : : }
178 : 62 : wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
179 : :
180 : 62 : msg = wpabuf_alloc(1000);
181 [ - + ]: 62 : if (msg == NULL)
182 : 0 : return NULL;
183 : :
184 [ + - + - ]: 124 : if (wps_build_version(msg) ||
185 [ + - ]: 124 : wps_build_msg_type(msg, WPS_M3) ||
186 [ + - ]: 124 : wps_build_registrar_nonce(wps, msg) ||
187 [ + - ]: 124 : wps_build_e_hash(wps, msg) ||
188 [ - + ]: 124 : wps_build_wfa_ext(msg, 0, NULL, 0) ||
189 : 62 : wps_build_authenticator(wps, msg)) {
190 : 0 : wpabuf_free(msg);
191 : 0 : return NULL;
192 : : }
193 : :
194 : 62 : wps->state = RECV_M4;
195 : 62 : return msg;
196 : : }
197 : :
198 : :
199 : 62 : static struct wpabuf * wps_build_m5(struct wps_data *wps)
200 : : {
201 : : struct wpabuf *msg, *plain;
202 : :
203 : 62 : wpa_printf(MSG_DEBUG, "WPS: Building Message M5");
204 : :
205 : 62 : plain = wpabuf_alloc(200);
206 [ - + ]: 62 : if (plain == NULL)
207 : 0 : return NULL;
208 : :
209 : 62 : msg = wpabuf_alloc(1000);
210 [ - + ]: 62 : if (msg == NULL) {
211 : 0 : wpabuf_free(plain);
212 : 0 : return NULL;
213 : : }
214 : :
215 [ + - + - ]: 124 : if (wps_build_version(msg) ||
216 [ + - ]: 124 : wps_build_msg_type(msg, WPS_M5) ||
217 [ + - ]: 124 : wps_build_registrar_nonce(wps, msg) ||
218 [ + - ]: 124 : wps_build_e_snonce1(wps, plain) ||
219 [ + - ]: 124 : wps_build_key_wrap_auth(wps, plain) ||
220 [ + - ]: 124 : wps_build_encr_settings(wps, msg, plain) ||
221 [ - + ]: 124 : wps_build_wfa_ext(msg, 0, NULL, 0) ||
222 : 62 : wps_build_authenticator(wps, msg)) {
223 : 0 : wpabuf_free(plain);
224 : 0 : wpabuf_free(msg);
225 : 0 : return NULL;
226 : : }
227 : 62 : wpabuf_free(plain);
228 : :
229 : 62 : wps->state = RECV_M6;
230 : 62 : return msg;
231 : : }
232 : :
233 : :
234 : 0 : static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
235 : : {
236 : 0 : wpa_printf(MSG_DEBUG, "WPS: * SSID");
237 : 0 : wpabuf_put_be16(msg, ATTR_SSID);
238 : 0 : wpabuf_put_be16(msg, wps->wps->ssid_len);
239 : 0 : wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
240 : 0 : return 0;
241 : : }
242 : :
243 : :
244 : 0 : static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
245 : : {
246 : 0 : u16 auth_type = wps->wps->auth_types;
247 : :
248 : : /* Select the best authentication type */
249 [ # # ]: 0 : if (auth_type & WPS_AUTH_WPA2PSK)
250 : 0 : auth_type = WPS_AUTH_WPA2PSK;
251 [ # # ]: 0 : else if (auth_type & WPS_AUTH_WPAPSK)
252 : 0 : auth_type = WPS_AUTH_WPAPSK;
253 [ # # ]: 0 : else if (auth_type & WPS_AUTH_OPEN)
254 : 0 : auth_type = WPS_AUTH_OPEN;
255 [ # # ]: 0 : else if (auth_type & WPS_AUTH_SHARED)
256 : 0 : auth_type = WPS_AUTH_SHARED;
257 : :
258 : 0 : wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", auth_type);
259 : 0 : wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
260 : 0 : wpabuf_put_be16(msg, 2);
261 : 0 : wpabuf_put_be16(msg, auth_type);
262 : 0 : return 0;
263 : : }
264 : :
265 : :
266 : 0 : static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
267 : : {
268 : 0 : u16 encr_type = wps->wps->encr_types;
269 : :
270 : : /* Select the best encryption type */
271 [ # # ]: 0 : if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
272 [ # # ]: 0 : if (encr_type & WPS_ENCR_AES)
273 : 0 : encr_type = WPS_ENCR_AES;
274 [ # # ]: 0 : else if (encr_type & WPS_ENCR_TKIP)
275 : 0 : encr_type = WPS_ENCR_TKIP;
276 : : } else {
277 [ # # ]: 0 : if (encr_type & WPS_ENCR_WEP)
278 : 0 : encr_type = WPS_ENCR_WEP;
279 [ # # ]: 0 : else if (encr_type & WPS_ENCR_NONE)
280 : 0 : encr_type = WPS_ENCR_NONE;
281 : : }
282 : :
283 : 0 : wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", encr_type);
284 : 0 : wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
285 : 0 : wpabuf_put_be16(msg, 2);
286 : 0 : wpabuf_put_be16(msg, encr_type);
287 : 0 : return 0;
288 : : }
289 : :
290 : :
291 : 0 : static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
292 : : {
293 : 0 : wpa_printf(MSG_DEBUG, "WPS: * Network Key");
294 : 0 : wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
295 : 0 : wpabuf_put_be16(msg, wps->wps->network_key_len);
296 : 0 : wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
297 : 0 : return 0;
298 : : }
299 : :
300 : :
301 : 0 : static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
302 : : {
303 : 0 : wpa_printf(MSG_DEBUG, "WPS: * MAC Address (AP BSSID)");
304 : 0 : wpabuf_put_be16(msg, ATTR_MAC_ADDR);
305 : 0 : wpabuf_put_be16(msg, ETH_ALEN);
306 : 0 : wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
307 : 0 : return 0;
308 : : }
309 : :
310 : :
311 : 0 : static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
312 : : {
313 [ # # ]: 0 : if (wps->wps->ap_settings) {
314 : 0 : wpa_printf(MSG_DEBUG, "WPS: * AP Settings (pre-configured)");
315 : 0 : wpabuf_put_data(plain, wps->wps->ap_settings,
316 : 0 : wps->wps->ap_settings_len);
317 : 0 : return 0;
318 : : }
319 : :
320 [ # # # # ]: 0 : return wps_build_cred_ssid(wps, plain) ||
321 [ # # ]: 0 : wps_build_cred_mac_addr(wps, plain) ||
322 [ # # ]: 0 : wps_build_cred_auth_type(wps, plain) ||
323 [ # # ]: 0 : wps_build_cred_encr_type(wps, plain) ||
324 : 0 : wps_build_cred_network_key(wps, plain);
325 : : }
326 : :
327 : :
328 : 62 : static struct wpabuf * wps_build_m7(struct wps_data *wps)
329 : : {
330 : : struct wpabuf *msg, *plain;
331 : :
332 : 62 : wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
333 : :
334 : 62 : plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
335 [ - + ]: 62 : if (plain == NULL)
336 : 0 : return NULL;
337 : :
338 : 62 : msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
339 [ - + ]: 62 : if (msg == NULL) {
340 : 0 : wpabuf_free(plain);
341 : 0 : return NULL;
342 : : }
343 : :
344 [ + - + - ]: 124 : if (wps_build_version(msg) ||
345 [ + - ]: 124 : wps_build_msg_type(msg, WPS_M7) ||
346 [ + - ]: 124 : wps_build_registrar_nonce(wps, msg) ||
347 [ - + ]: 124 : wps_build_e_snonce2(wps, plain) ||
348 [ # # + - ]: 62 : (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
349 [ + - ]: 124 : wps_build_key_wrap_auth(wps, plain) ||
350 [ + - ]: 124 : wps_build_encr_settings(wps, msg, plain) ||
351 [ - + ]: 124 : wps_build_wfa_ext(msg, 0, NULL, 0) ||
352 : 62 : wps_build_authenticator(wps, msg)) {
353 : 0 : wpabuf_free(plain);
354 : 0 : wpabuf_free(msg);
355 : 0 : return NULL;
356 : : }
357 : 62 : wpabuf_free(plain);
358 : :
359 [ - + ][ # # ]: 62 : if (wps->wps->ap && wps->wps->registrar) {
360 : : /*
361 : : * If the Registrar is only learning our current configuration,
362 : : * it may not continue protocol run to successful completion.
363 : : * Store information here to make sure it remains available.
364 : : */
365 : 0 : wps_device_store(wps->wps->registrar, &wps->peer_dev,
366 : 0 : wps->uuid_r);
367 : : }
368 : :
369 : 62 : wps->state = RECV_M8;
370 : 62 : return msg;
371 : : }
372 : :
373 : :
374 : 62 : static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
375 : : {
376 : : struct wpabuf *msg;
377 : :
378 : 62 : wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
379 : :
380 : 62 : msg = wpabuf_alloc(1000);
381 [ - + ]: 62 : if (msg == NULL)
382 : 0 : return NULL;
383 : :
384 [ + - + - ]: 124 : if (wps_build_version(msg) ||
385 [ + - ]: 124 : wps_build_msg_type(msg, WPS_WSC_DONE) ||
386 [ + - ]: 124 : wps_build_enrollee_nonce(wps, msg) ||
387 [ - + ]: 124 : wps_build_registrar_nonce(wps, msg) ||
388 : 62 : wps_build_wfa_ext(msg, 0, NULL, 0)) {
389 : 0 : wpabuf_free(msg);
390 : 0 : return NULL;
391 : : }
392 : :
393 [ - + ]: 62 : if (wps->wps->ap)
394 : 0 : wps->state = RECV_ACK;
395 : : else {
396 : 62 : wps_success_event(wps->wps, wps->peer_dev.mac_addr);
397 : 62 : wps->state = WPS_FINISHED;
398 : : }
399 : 62 : return msg;
400 : : }
401 : :
402 : :
403 : 316 : struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
404 : : enum wsc_op_code *op_code)
405 : : {
406 : : struct wpabuf *msg;
407 : :
408 [ + + + + : 316 : switch (wps->state) {
+ - + - ]
409 : : case SEND_M1:
410 : 64 : msg = wps_build_m1(wps);
411 : 64 : *op_code = WSC_MSG;
412 : 64 : break;
413 : : case SEND_M3:
414 : 62 : msg = wps_build_m3(wps);
415 : 62 : *op_code = WSC_MSG;
416 : 62 : break;
417 : : case SEND_M5:
418 : 62 : msg = wps_build_m5(wps);
419 : 62 : *op_code = WSC_MSG;
420 : 62 : break;
421 : : case SEND_M7:
422 : 62 : msg = wps_build_m7(wps);
423 : 62 : *op_code = WSC_MSG;
424 : 62 : break;
425 : : case RECEIVED_M2D:
426 [ - + ]: 4 : if (wps->wps->ap) {
427 : 0 : msg = wps_build_wsc_nack(wps);
428 : 0 : *op_code = WSC_NACK;
429 : 0 : break;
430 : : }
431 : 4 : msg = wps_build_wsc_ack(wps);
432 : 4 : *op_code = WSC_ACK;
433 [ + - ]: 4 : if (msg) {
434 : : /* Another M2/M2D may be received */
435 : 4 : wps->state = RECV_M2;
436 : : }
437 : 4 : break;
438 : : case SEND_WSC_NACK:
439 : 0 : msg = wps_build_wsc_nack(wps);
440 : 0 : *op_code = WSC_NACK;
441 : 0 : break;
442 : : case WPS_MSG_DONE:
443 : 62 : msg = wps_build_wsc_done(wps);
444 : 62 : *op_code = WSC_Done;
445 : 62 : break;
446 : : default:
447 : 0 : wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
448 : 0 : "a message", wps->state);
449 : 0 : msg = NULL;
450 : 0 : break;
451 : : }
452 : :
453 [ + + ][ + - ]: 316 : if (*op_code == WSC_MSG && msg) {
454 : : /* Save a copy of the last message for Authenticator derivation
455 : : */
456 : 250 : wpabuf_free(wps->last_msg);
457 : 250 : wps->last_msg = wpabuf_dup(msg);
458 : : }
459 : :
460 : 316 : return msg;
461 : : }
462 : :
463 : :
464 : 62 : static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
465 : : {
466 [ - + ]: 62 : if (r_nonce == NULL) {
467 : 0 : wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
468 : 0 : return -1;
469 : : }
470 : :
471 : 62 : os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
472 : 62 : wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
473 : 62 : wps->nonce_r, WPS_NONCE_LEN);
474 : :
475 : 62 : return 0;
476 : : }
477 : :
478 : :
479 : 248 : static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
480 : : {
481 [ - + ]: 248 : if (e_nonce == NULL) {
482 : 0 : wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
483 : 0 : return -1;
484 : : }
485 : :
486 [ - + ]: 248 : if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
487 : 0 : wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
488 : 0 : return -1;
489 : : }
490 : :
491 : 248 : return 0;
492 : : }
493 : :
494 : :
495 : 62 : static int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
496 : : {
497 [ - + ]: 62 : if (uuid_r == NULL) {
498 : 0 : wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
499 : 0 : return -1;
500 : : }
501 : :
502 : 62 : os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
503 : 62 : wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
504 : :
505 : 62 : return 0;
506 : : }
507 : :
508 : :
509 : 62 : static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
510 : : size_t pk_len)
511 : : {
512 [ + - ][ - + ]: 62 : if (pk == NULL || pk_len == 0) {
513 : 0 : wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
514 : 0 : return -1;
515 : : }
516 : :
517 : 62 : wpabuf_free(wps->dh_pubkey_r);
518 : 62 : wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
519 [ - + ]: 62 : if (wps->dh_pubkey_r == NULL)
520 : 0 : return -1;
521 : :
522 [ - + ]: 62 : if (wps_derive_keys(wps) < 0)
523 : 0 : return -1;
524 : :
525 : 62 : return 0;
526 : : }
527 : :
528 : :
529 : 62 : static int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
530 : : {
531 [ - + ]: 62 : if (r_hash1 == NULL) {
532 : 0 : wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
533 : 0 : return -1;
534 : : }
535 : :
536 : 62 : os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
537 : 62 : wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
538 : :
539 : 62 : return 0;
540 : : }
541 : :
542 : :
543 : 62 : static int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
544 : : {
545 [ - + ]: 62 : if (r_hash2 == NULL) {
546 : 0 : wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
547 : 0 : return -1;
548 : : }
549 : :
550 : 62 : os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
551 : 62 : wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
552 : :
553 : 62 : return 0;
554 : : }
555 : :
556 : :
557 : 62 : static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
558 : : {
559 : : u8 hash[SHA256_MAC_LEN];
560 : : const u8 *addr[4];
561 : : size_t len[4];
562 : :
563 [ - + ]: 62 : if (r_snonce1 == NULL) {
564 : 0 : wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
565 : 0 : return -1;
566 : : }
567 : :
568 : 62 : wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
569 : : WPS_SECRET_NONCE_LEN);
570 : :
571 : : /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
572 : 62 : addr[0] = r_snonce1;
573 : 62 : len[0] = WPS_SECRET_NONCE_LEN;
574 : 62 : addr[1] = wps->psk1;
575 : 62 : len[1] = WPS_PSK_LEN;
576 : 62 : addr[2] = wpabuf_head(wps->dh_pubkey_e);
577 : 62 : len[2] = wpabuf_len(wps->dh_pubkey_e);
578 : 62 : addr[3] = wpabuf_head(wps->dh_pubkey_r);
579 : 62 : len[3] = wpabuf_len(wps->dh_pubkey_r);
580 : 62 : hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
581 : :
582 [ - + ]: 62 : if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
583 : 0 : wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
584 : : "not match with the pre-committed value");
585 : 0 : wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
586 : 0 : wps_pwd_auth_fail_event(wps->wps, 1, 1, wps->peer_dev.mac_addr);
587 : 0 : return -1;
588 : : }
589 : :
590 : 62 : wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
591 : : "half of the device password");
592 : :
593 : 62 : return 0;
594 : : }
595 : :
596 : :
597 : 62 : static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
598 : : {
599 : : u8 hash[SHA256_MAC_LEN];
600 : : const u8 *addr[4];
601 : : size_t len[4];
602 : :
603 [ - + ]: 62 : if (r_snonce2 == NULL) {
604 : 0 : wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
605 : 0 : return -1;
606 : : }
607 : :
608 : 62 : wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
609 : : WPS_SECRET_NONCE_LEN);
610 : :
611 : : /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
612 : 62 : addr[0] = r_snonce2;
613 : 62 : len[0] = WPS_SECRET_NONCE_LEN;
614 : 62 : addr[1] = wps->psk2;
615 : 62 : len[1] = WPS_PSK_LEN;
616 : 62 : addr[2] = wpabuf_head(wps->dh_pubkey_e);
617 : 62 : len[2] = wpabuf_len(wps->dh_pubkey_e);
618 : 62 : addr[3] = wpabuf_head(wps->dh_pubkey_r);
619 : 62 : len[3] = wpabuf_len(wps->dh_pubkey_r);
620 : 62 : hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
621 : :
622 [ - + ]: 62 : if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
623 : 0 : wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
624 : : "not match with the pre-committed value");
625 : 0 : wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
626 : 0 : wps_pwd_auth_fail_event(wps->wps, 1, 2, wps->peer_dev.mac_addr);
627 : 0 : return -1;
628 : : }
629 : :
630 : 62 : wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
631 : : "half of the device password");
632 : :
633 : 62 : return 0;
634 : : }
635 : :
636 : :
637 : 62 : static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
638 : : size_t cred_len, int wps2)
639 : : {
640 : : struct wps_parse_attr attr;
641 : : struct wpabuf msg;
642 : 62 : int ret = 0;
643 : :
644 : 62 : wpa_printf(MSG_DEBUG, "WPS: Received Credential");
645 : 62 : os_memset(&wps->cred, 0, sizeof(wps->cred));
646 : 62 : wpabuf_set(&msg, cred, cred_len);
647 [ + - - + ]: 124 : if (wps_parse_msg(&msg, &attr) < 0 ||
648 : 62 : wps_process_cred(&attr, &wps->cred))
649 : 0 : return -1;
650 : :
651 [ - + ]: 62 : if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
652 : : 0) {
653 : 0 : wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential ("
654 : : MACSTR ") does not match with own address (" MACSTR
655 : 0 : ")", MAC2STR(wps->cred.mac_addr),
656 : 0 : MAC2STR(wps->wps->dev.mac_addr));
657 : : /*
658 : : * In theory, this could be consider fatal error, but there are
659 : : * number of deployed implementations using other address here
660 : : * due to unclarity in the specification. For interoperability
661 : : * reasons, allow this to be processed since we do not really
662 : : * use the MAC Address information for anything.
663 : : */
664 : : #ifdef CONFIG_WPS_STRICT
665 [ # # ]: 0 : if (wps2) {
666 : 0 : wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
667 : : "MAC Address in AP Settings");
668 : 0 : return -1;
669 : : }
670 : : #endif /* CONFIG_WPS_STRICT */
671 : : }
672 : :
673 : : #ifdef CONFIG_WPS2
674 [ - + ]: 62 : if (!(wps->cred.encr_type &
675 : : (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
676 [ # # ]: 0 : if (wps->cred.encr_type & WPS_ENCR_WEP) {
677 : 0 : wpa_printf(MSG_INFO, "WPS: Reject Credential "
678 : : "due to WEP configuration");
679 : 0 : wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
680 : 0 : return -2;
681 : : }
682 : :
683 : 0 : wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
684 : 0 : "invalid encr_type 0x%x", wps->cred.encr_type);
685 : 0 : return -1;
686 : : }
687 : : #endif /* CONFIG_WPS2 */
688 : :
689 [ + - ]: 62 : if (wps->wps->cred_cb) {
690 : 62 : wps->cred.cred_attr = cred - 4;
691 : 62 : wps->cred.cred_attr_len = cred_len + 4;
692 : 62 : ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
693 : 62 : wps->cred.cred_attr = NULL;
694 : 62 : wps->cred.cred_attr_len = 0;
695 : : }
696 : :
697 : 62 : return ret;
698 : : }
699 : :
700 : :
701 : 62 : static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
702 : : size_t cred_len[], size_t num_cred, int wps2)
703 : : {
704 : : size_t i;
705 : 62 : int ok = 0;
706 : :
707 [ - + ]: 62 : if (wps->wps->ap)
708 : 0 : return 0;
709 : :
710 [ - + ]: 62 : if (num_cred == 0) {
711 : 0 : wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
712 : : "received");
713 : 0 : return -1;
714 : : }
715 : :
716 [ + + ]: 124 : for (i = 0; i < num_cred; i++) {
717 : : int res;
718 : 62 : res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
719 [ + - ]: 62 : if (res == 0)
720 : 62 : ok++;
721 [ # # ]: 0 : else if (res == -2)
722 : 0 : wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped");
723 : : else
724 : 0 : return -1;
725 : : }
726 : :
727 [ - + ]: 62 : if (ok == 0) {
728 : 0 : wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute "
729 : : "received");
730 : 0 : return -1;
731 : : }
732 : :
733 : 62 : return 0;
734 : : }
735 : :
736 : :
737 : 62 : static int wps_process_ap_settings_e(struct wps_data *wps,
738 : : struct wps_parse_attr *attr,
739 : : struct wpabuf *attrs, int wps2)
740 : : {
741 : : struct wps_credential cred;
742 : :
743 [ + - ]: 62 : if (!wps->wps->ap)
744 : 62 : return 0;
745 : :
746 [ # # ]: 0 : if (wps_process_ap_settings(attr, &cred) < 0)
747 : 0 : return -1;
748 : :
749 : 0 : wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
750 : : "Registrar");
751 : :
752 [ # # ]: 0 : if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
753 : : 0) {
754 : 0 : wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
755 : : MACSTR ") does not match with own address (" MACSTR
756 : 0 : ")", MAC2STR(cred.mac_addr),
757 : 0 : MAC2STR(wps->wps->dev.mac_addr));
758 : : /*
759 : : * In theory, this could be consider fatal error, but there are
760 : : * number of deployed implementations using other address here
761 : : * due to unclarity in the specification. For interoperability
762 : : * reasons, allow this to be processed since we do not really
763 : : * use the MAC Address information for anything.
764 : : */
765 : : #ifdef CONFIG_WPS_STRICT
766 [ # # ]: 0 : if (wps2) {
767 : 0 : wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
768 : : "MAC Address in AP Settings");
769 : 0 : return -1;
770 : : }
771 : : #endif /* CONFIG_WPS_STRICT */
772 : : }
773 : :
774 : : #ifdef CONFIG_WPS2
775 [ # # ]: 0 : if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
776 : : {
777 [ # # ]: 0 : if (cred.encr_type & WPS_ENCR_WEP) {
778 : 0 : wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
779 : : "due to WEP configuration");
780 : 0 : wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
781 : 0 : return -1;
782 : : }
783 : :
784 : 0 : wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
785 : 0 : "invalid encr_type 0x%x", cred.encr_type);
786 : 0 : return -1;
787 : : }
788 : : #endif /* CONFIG_WPS2 */
789 : :
790 : : #ifdef CONFIG_WPS_STRICT
791 [ # # ]: 0 : if (wps2) {
792 [ # # ]: 0 : if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
793 [ # # ]: 0 : WPS_ENCR_TKIP ||
794 : 0 : (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
795 : : WPS_AUTH_WPAPSK) {
796 : 0 : wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
797 : : "AP Settings: WPA-Personal/TKIP only");
798 : 0 : wps->error_indication =
799 : : WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
800 : 0 : return -1;
801 : : }
802 : : }
803 : : #endif /* CONFIG_WPS_STRICT */
804 : :
805 : : #ifdef CONFIG_WPS2
806 [ # # ]: 0 : if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
807 : : {
808 : 0 : wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
809 : : "TKIP+AES");
810 : 0 : cred.encr_type |= WPS_ENCR_AES;
811 : : }
812 : :
813 [ # # ]: 0 : if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
814 : : WPS_AUTH_WPAPSK) {
815 : 0 : wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
816 : : "WPAPSK+WPA2PSK");
817 : 0 : cred.auth_type |= WPS_AUTH_WPA2PSK;
818 : : }
819 : : #endif /* CONFIG_WPS2 */
820 : :
821 [ # # ]: 0 : if (wps->wps->cred_cb) {
822 : 0 : cred.cred_attr = wpabuf_head(attrs);
823 : 0 : cred.cred_attr_len = wpabuf_len(attrs);
824 : 0 : wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
825 : : }
826 : :
827 : 62 : return 0;
828 : : }
829 : :
830 : :
831 : 62 : static int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id)
832 : : {
833 : : u16 id;
834 : :
835 [ - + ]: 62 : if (dev_pw_id == NULL) {
836 : 0 : wpa_printf(MSG_DEBUG, "WPS: Device Password ID");
837 : 0 : return -1;
838 : : }
839 : :
840 : 62 : id = WPA_GET_BE16(dev_pw_id);
841 [ + - ]: 62 : if (wps->dev_pw_id == id) {
842 : 62 : wpa_printf(MSG_DEBUG, "WPS: Device Password ID %u", id);
843 : 62 : return 0;
844 : : }
845 : :
846 : : #ifdef CONFIG_P2P
847 [ # # ][ # # ]: 0 : if ((id == DEV_PW_DEFAULT &&
848 [ # # ]: 0 : wps->dev_pw_id == DEV_PW_REGISTRAR_SPECIFIED) ||
849 [ # # ]: 0 : (id == DEV_PW_REGISTRAR_SPECIFIED &&
850 : 0 : wps->dev_pw_id == DEV_PW_DEFAULT)) {
851 : : /*
852 : : * Common P2P use cases indicate whether the PIN is from the
853 : : * client or GO using Device Password Id in M1/M2 in a way that
854 : : * does not look fully compliant with WSC specification. Anyway,
855 : : * this is deployed and needs to be allowed, so ignore changes
856 : : * between Registrar-Specified and Default PIN.
857 : : */
858 : 0 : wpa_printf(MSG_DEBUG, "WPS: Allow PIN Device Password ID "
859 : : "change");
860 : 0 : return 0;
861 : : }
862 : : #endif /* CONFIG_P2P */
863 : :
864 : 0 : wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password "
865 : 0 : "ID from %u to %u", wps->dev_pw_id, id);
866 : :
867 [ # # ][ # # ]: 0 : if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) {
868 : 0 : wpa_printf(MSG_DEBUG,
869 : : "WPS: Workaround - ignore PBC-to-PIN change");
870 : 0 : return 0;
871 : : }
872 : :
873 [ # # ][ # # ]: 0 : if (wps->alt_dev_password && wps->alt_dev_pw_id == id) {
874 : 0 : wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password");
875 : 0 : os_free(wps->dev_password);
876 : 0 : wps->dev_pw_id = wps->alt_dev_pw_id;
877 : 0 : wps->dev_password = wps->alt_dev_password;
878 : 0 : wps->dev_password_len = wps->alt_dev_password_len;
879 : 0 : wps->alt_dev_password = NULL;
880 : 0 : wps->alt_dev_password_len = 0;
881 : 0 : return 0;
882 : : }
883 : :
884 : 62 : return -1;
885 : : }
886 : :
887 : :
888 : 62 : static enum wps_process_res wps_process_m2(struct wps_data *wps,
889 : : const struct wpabuf *msg,
890 : : struct wps_parse_attr *attr)
891 : : {
892 : 62 : wpa_printf(MSG_DEBUG, "WPS: Received M2");
893 : :
894 [ - + ]: 62 : if (wps->state != RECV_M2) {
895 : 0 : wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
896 : 0 : "receiving M2", wps->state);
897 : 0 : wps->state = SEND_WSC_NACK;
898 : 0 : return WPS_CONTINUE;
899 : : }
900 : :
901 [ + - + - ]: 124 : if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
902 [ + - ]: 124 : wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
903 [ - + ]: 124 : wps_process_uuid_r(wps, attr->uuid_r) ||
904 : 62 : wps_process_dev_pw_id(wps, attr->dev_password_id)) {
905 : 0 : wps->state = SEND_WSC_NACK;
906 : 0 : return WPS_CONTINUE;
907 : : }
908 : :
909 : : /*
910 : : * Stop here on an AP as an Enrollee if AP Setup is locked unless the
911 : : * special locked mode is used to allow protocol run up to M7 in order
912 : : * to support external Registrars that only learn the current AP
913 : : * configuration without changing it.
914 : : */
915 [ - + ][ # # ]: 62 : if (wps->wps->ap &&
916 [ # # ][ # # ]: 0 : ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
917 : 0 : wps->dev_password == NULL)) {
918 : 0 : wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
919 : : "registration of a new Registrar");
920 : 0 : wps->config_error = WPS_CFG_SETUP_LOCKED;
921 : 0 : wps->state = SEND_WSC_NACK;
922 : 0 : return WPS_CONTINUE;
923 : : }
924 : :
925 [ + - + - ]: 124 : if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
926 [ - + ]: 124 : wps_process_authenticator(wps, attr->authenticator, msg) ||
927 : 62 : wps_process_device_attrs(&wps->peer_dev, attr)) {
928 : 0 : wps->state = SEND_WSC_NACK;
929 : 0 : return WPS_CONTINUE;
930 : : }
931 : :
932 : 62 : wps->state = SEND_M3;
933 : 62 : return WPS_CONTINUE;
934 : : }
935 : :
936 : :
937 : 4 : static enum wps_process_res wps_process_m2d(struct wps_data *wps,
938 : : struct wps_parse_attr *attr)
939 : : {
940 : 4 : wpa_printf(MSG_DEBUG, "WPS: Received M2D");
941 : :
942 [ - + ]: 4 : if (wps->state != RECV_M2) {
943 : 0 : wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
944 : 0 : "receiving M2D", wps->state);
945 : 0 : wps->state = SEND_WSC_NACK;
946 : 0 : return WPS_CONTINUE;
947 : : }
948 : :
949 : 4 : wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
950 : 4 : attr->manufacturer, attr->manufacturer_len);
951 : 4 : wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
952 : 4 : attr->model_name, attr->model_name_len);
953 : 4 : wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
954 : 4 : attr->model_number, attr->model_number_len);
955 : 4 : wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
956 : 4 : attr->serial_number, attr->serial_number_len);
957 : 4 : wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
958 : 4 : attr->dev_name, attr->dev_name_len);
959 : :
960 [ + - ]: 4 : if (wps->wps->event_cb) {
961 : : union wps_event_data data;
962 : 4 : struct wps_event_m2d *m2d = &data.m2d;
963 : 4 : os_memset(&data, 0, sizeof(data));
964 [ + - ]: 4 : if (attr->config_methods)
965 : 4 : m2d->config_methods =
966 : 4 : WPA_GET_BE16(attr->config_methods);
967 : 4 : m2d->manufacturer = attr->manufacturer;
968 : 4 : m2d->manufacturer_len = attr->manufacturer_len;
969 : 4 : m2d->model_name = attr->model_name;
970 : 4 : m2d->model_name_len = attr->model_name_len;
971 : 4 : m2d->model_number = attr->model_number;
972 : 4 : m2d->model_number_len = attr->model_number_len;
973 : 4 : m2d->serial_number = attr->serial_number;
974 : 4 : m2d->serial_number_len = attr->serial_number_len;
975 : 4 : m2d->dev_name = attr->dev_name;
976 : 4 : m2d->dev_name_len = attr->dev_name_len;
977 : 4 : m2d->primary_dev_type = attr->primary_dev_type;
978 [ + - ]: 4 : if (attr->config_error)
979 : 4 : m2d->config_error =
980 : 4 : WPA_GET_BE16(attr->config_error);
981 [ - + ]: 4 : if (attr->dev_password_id)
982 : 0 : m2d->dev_password_id =
983 : 0 : WPA_GET_BE16(attr->dev_password_id);
984 : 4 : wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
985 : : }
986 : :
987 : 4 : wps->state = RECEIVED_M2D;
988 : 4 : return WPS_CONTINUE;
989 : : }
990 : :
991 : :
992 : 62 : static enum wps_process_res wps_process_m4(struct wps_data *wps,
993 : : const struct wpabuf *msg,
994 : : struct wps_parse_attr *attr)
995 : : {
996 : : struct wpabuf *decrypted;
997 : : struct wps_parse_attr eattr;
998 : :
999 : 62 : wpa_printf(MSG_DEBUG, "WPS: Received M4");
1000 : :
1001 [ - + ]: 62 : if (wps->state != RECV_M4) {
1002 : 0 : wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1003 : 0 : "receiving M4", wps->state);
1004 : 0 : wps->state = SEND_WSC_NACK;
1005 : 0 : return WPS_CONTINUE;
1006 : : }
1007 : :
1008 [ + - + - ]: 124 : if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
1009 [ + - ]: 124 : wps_process_authenticator(wps, attr->authenticator, msg) ||
1010 [ - + ]: 124 : wps_process_r_hash1(wps, attr->r_hash1) ||
1011 : 62 : wps_process_r_hash2(wps, attr->r_hash2)) {
1012 : 0 : wps->state = SEND_WSC_NACK;
1013 : 0 : return WPS_CONTINUE;
1014 : : }
1015 : :
1016 : 62 : decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
1017 : : attr->encr_settings_len);
1018 [ - + ]: 62 : if (decrypted == NULL) {
1019 : 0 : wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
1020 : : "Settings attribute");
1021 : 0 : wps->state = SEND_WSC_NACK;
1022 : 0 : return WPS_CONTINUE;
1023 : : }
1024 : :
1025 [ - + ]: 62 : if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
1026 : 0 : wpabuf_free(decrypted);
1027 : 0 : wps->state = SEND_WSC_NACK;
1028 : 0 : return WPS_CONTINUE;
1029 : : }
1030 : :
1031 : 62 : wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
1032 : : "attribute");
1033 [ + - + - ]: 124 : if (wps_parse_msg(decrypted, &eattr) < 0 ||
1034 [ - + ]: 124 : wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
1035 : 62 : wps_process_r_snonce1(wps, eattr.r_snonce1)) {
1036 : 0 : wpabuf_free(decrypted);
1037 : 0 : wps->state = SEND_WSC_NACK;
1038 : 0 : return WPS_CONTINUE;
1039 : : }
1040 : 62 : wpabuf_free(decrypted);
1041 : :
1042 : 62 : wps->state = SEND_M5;
1043 : 62 : return WPS_CONTINUE;
1044 : : }
1045 : :
1046 : :
1047 : 62 : static enum wps_process_res wps_process_m6(struct wps_data *wps,
1048 : : const struct wpabuf *msg,
1049 : : struct wps_parse_attr *attr)
1050 : : {
1051 : : struct wpabuf *decrypted;
1052 : : struct wps_parse_attr eattr;
1053 : :
1054 : 62 : wpa_printf(MSG_DEBUG, "WPS: Received M6");
1055 : :
1056 [ - + ]: 62 : if (wps->state != RECV_M6) {
1057 : 0 : wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1058 : 0 : "receiving M6", wps->state);
1059 : 0 : wps->state = SEND_WSC_NACK;
1060 : 0 : return WPS_CONTINUE;
1061 : : }
1062 : :
1063 [ + - - + ]: 124 : if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
1064 : 62 : wps_process_authenticator(wps, attr->authenticator, msg)) {
1065 : 0 : wps->state = SEND_WSC_NACK;
1066 : 0 : return WPS_CONTINUE;
1067 : : }
1068 : :
1069 : 62 : decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
1070 : : attr->encr_settings_len);
1071 [ - + ]: 62 : if (decrypted == NULL) {
1072 : 0 : wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
1073 : : "Settings attribute");
1074 : 0 : wps->state = SEND_WSC_NACK;
1075 : 0 : return WPS_CONTINUE;
1076 : : }
1077 : :
1078 [ - + ]: 62 : if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
1079 : 0 : wpabuf_free(decrypted);
1080 : 0 : wps->state = SEND_WSC_NACK;
1081 : 0 : return WPS_CONTINUE;
1082 : : }
1083 : :
1084 : 62 : wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
1085 : : "attribute");
1086 [ + - + - ]: 124 : if (wps_parse_msg(decrypted, &eattr) < 0 ||
1087 [ - + ]: 124 : wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
1088 : 62 : wps_process_r_snonce2(wps, eattr.r_snonce2)) {
1089 : 0 : wpabuf_free(decrypted);
1090 : 0 : wps->state = SEND_WSC_NACK;
1091 : 0 : return WPS_CONTINUE;
1092 : : }
1093 : 62 : wpabuf_free(decrypted);
1094 : :
1095 [ - + ]: 62 : if (wps->wps->ap)
1096 : 0 : wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
1097 : : NULL);
1098 : :
1099 : 62 : wps->state = SEND_M7;
1100 : 62 : return WPS_CONTINUE;
1101 : : }
1102 : :
1103 : :
1104 : 62 : static enum wps_process_res wps_process_m8(struct wps_data *wps,
1105 : : const struct wpabuf *msg,
1106 : : struct wps_parse_attr *attr)
1107 : : {
1108 : : struct wpabuf *decrypted;
1109 : : struct wps_parse_attr eattr;
1110 : :
1111 : 62 : wpa_printf(MSG_DEBUG, "WPS: Received M8");
1112 : :
1113 [ - + ]: 62 : if (wps->state != RECV_M8) {
1114 : 0 : wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1115 : 0 : "receiving M8", wps->state);
1116 : 0 : wps->state = SEND_WSC_NACK;
1117 : 0 : return WPS_CONTINUE;
1118 : : }
1119 : :
1120 [ + - - + ]: 124 : if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
1121 : 62 : wps_process_authenticator(wps, attr->authenticator, msg)) {
1122 : 0 : wps->state = SEND_WSC_NACK;
1123 : 0 : return WPS_CONTINUE;
1124 : : }
1125 : :
1126 [ - + ][ # # ]: 62 : if (wps->wps->ap && wps->wps->ap_setup_locked) {
1127 : : /*
1128 : : * Stop here if special ap_setup_locked == 2 mode allowed the
1129 : : * protocol to continue beyond M2. This allows ER to learn the
1130 : : * current AP settings without changing them.
1131 : : */
1132 : 0 : wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
1133 : : "registration of a new Registrar");
1134 : 0 : wps->config_error = WPS_CFG_SETUP_LOCKED;
1135 : 0 : wps->state = SEND_WSC_NACK;
1136 : 0 : return WPS_CONTINUE;
1137 : : }
1138 : :
1139 : 62 : decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
1140 : : attr->encr_settings_len);
1141 [ - + ]: 62 : if (decrypted == NULL) {
1142 : 0 : wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
1143 : : "Settings attribute");
1144 : 0 : wps->state = SEND_WSC_NACK;
1145 : 0 : return WPS_CONTINUE;
1146 : : }
1147 : :
1148 [ - + ]: 62 : if (wps_validate_m8_encr(decrypted, wps->wps->ap,
1149 : 62 : attr->version2 != NULL) < 0) {
1150 : 0 : wpabuf_free(decrypted);
1151 : 0 : wps->state = SEND_WSC_NACK;
1152 : 0 : return WPS_CONTINUE;
1153 : : }
1154 : :
1155 : 62 : wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
1156 : : "attribute");
1157 [ + - + - ]: 124 : if (wps_parse_msg(decrypted, &eattr) < 0 ||
1158 [ + - ]: 124 : wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
1159 : 62 : wps_process_creds(wps, eattr.cred, eattr.cred_len,
1160 [ - + ]: 62 : eattr.num_cred, attr->version2 != NULL) ||
1161 : 62 : wps_process_ap_settings_e(wps, &eattr, decrypted,
1162 : 62 : attr->version2 != NULL)) {
1163 : 0 : wpabuf_free(decrypted);
1164 : 0 : wps->state = SEND_WSC_NACK;
1165 : 0 : return WPS_CONTINUE;
1166 : : }
1167 : 62 : wpabuf_free(decrypted);
1168 : :
1169 : 62 : wps->state = WPS_MSG_DONE;
1170 : 62 : return WPS_CONTINUE;
1171 : : }
1172 : :
1173 : :
1174 : 252 : static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
1175 : : const struct wpabuf *msg)
1176 : : {
1177 : : struct wps_parse_attr attr;
1178 : 252 : enum wps_process_res ret = WPS_CONTINUE;
1179 : :
1180 : 252 : wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
1181 : :
1182 [ - + ]: 252 : if (wps_parse_msg(msg, &attr) < 0)
1183 : 0 : return WPS_FAILURE;
1184 : :
1185 [ + - ][ - + ]: 252 : if (attr.enrollee_nonce == NULL ||
1186 : 252 : os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
1187 : 0 : wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
1188 : 0 : return WPS_FAILURE;
1189 : : }
1190 : :
1191 [ - + ]: 252 : if (attr.msg_type == NULL) {
1192 : 0 : wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1193 : 0 : wps->state = SEND_WSC_NACK;
1194 : 0 : return WPS_CONTINUE;
1195 : : }
1196 : :
1197 [ + + + + : 252 : switch (*attr.msg_type) {
+ - ]
1198 : : case WPS_M2:
1199 [ - + ]: 62 : if (wps_validate_m2(msg) < 0)
1200 : 0 : return WPS_FAILURE;
1201 : 62 : ret = wps_process_m2(wps, msg, &attr);
1202 : 62 : break;
1203 : : case WPS_M2D:
1204 [ - + ]: 4 : if (wps_validate_m2d(msg) < 0)
1205 : 0 : return WPS_FAILURE;
1206 : 4 : ret = wps_process_m2d(wps, &attr);
1207 : 4 : break;
1208 : : case WPS_M4:
1209 [ - + ]: 62 : if (wps_validate_m4(msg) < 0)
1210 : 0 : return WPS_FAILURE;
1211 : 62 : ret = wps_process_m4(wps, msg, &attr);
1212 [ + - ][ - + ]: 62 : if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1213 : 0 : wps_fail_event(wps->wps, WPS_M4, wps->config_error,
1214 : 0 : wps->error_indication,
1215 : 0 : wps->peer_dev.mac_addr);
1216 : 62 : break;
1217 : : case WPS_M6:
1218 [ - + ]: 62 : if (wps_validate_m6(msg) < 0)
1219 : 0 : return WPS_FAILURE;
1220 : 62 : ret = wps_process_m6(wps, msg, &attr);
1221 [ + - ][ - + ]: 62 : if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1222 : 0 : wps_fail_event(wps->wps, WPS_M6, wps->config_error,
1223 : 0 : wps->error_indication,
1224 : 0 : wps->peer_dev.mac_addr);
1225 : 62 : break;
1226 : : case WPS_M8:
1227 [ - + ]: 62 : if (wps_validate_m8(msg) < 0)
1228 : 0 : return WPS_FAILURE;
1229 : 62 : ret = wps_process_m8(wps, msg, &attr);
1230 [ + - ][ - + ]: 62 : if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1231 : 0 : wps_fail_event(wps->wps, WPS_M8, wps->config_error,
1232 : 0 : wps->error_indication,
1233 : 0 : wps->peer_dev.mac_addr);
1234 : 62 : break;
1235 : : default:
1236 : 0 : wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
1237 : 0 : *attr.msg_type);
1238 : 0 : return WPS_FAILURE;
1239 : : }
1240 : :
1241 : : /*
1242 : : * Save a copy of the last message for Authenticator derivation if we
1243 : : * are continuing. However, skip M2D since it is not authenticated and
1244 : : * neither is the ACK/NACK response frame. This allows the possibly
1245 : : * following M2 to be processed correctly by using the previously sent
1246 : : * M1 in Authenticator derivation.
1247 : : */
1248 [ + - ][ + + ]: 252 : if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) {
1249 : : /* Save a copy of the last message for Authenticator derivation
1250 : : */
1251 : 248 : wpabuf_free(wps->last_msg);
1252 : 248 : wps->last_msg = wpabuf_dup(msg);
1253 : : }
1254 : :
1255 : 252 : return ret;
1256 : : }
1257 : :
1258 : :
1259 : 0 : static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
1260 : : const struct wpabuf *msg)
1261 : : {
1262 : : struct wps_parse_attr attr;
1263 : :
1264 : 0 : wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
1265 : :
1266 [ # # ]: 0 : if (wps_parse_msg(msg, &attr) < 0)
1267 : 0 : return WPS_FAILURE;
1268 : :
1269 [ # # ]: 0 : if (attr.msg_type == NULL) {
1270 : 0 : wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1271 : 0 : return WPS_FAILURE;
1272 : : }
1273 : :
1274 [ # # ]: 0 : if (*attr.msg_type != WPS_WSC_ACK) {
1275 : 0 : wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
1276 : 0 : *attr.msg_type);
1277 : 0 : return WPS_FAILURE;
1278 : : }
1279 : :
1280 [ # # ][ # # ]: 0 : if (attr.registrar_nonce == NULL ||
1281 : 0 : os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
1282 : : {
1283 : 0 : wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
1284 : 0 : return WPS_FAILURE;
1285 : : }
1286 : :
1287 [ # # ][ # # ]: 0 : if (attr.enrollee_nonce == NULL ||
1288 : 0 : os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
1289 : 0 : wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
1290 : 0 : return WPS_FAILURE;
1291 : : }
1292 : :
1293 [ # # ][ # # ]: 0 : if (wps->state == RECV_ACK && wps->wps->ap) {
1294 : 0 : wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
1295 : : "completed successfully");
1296 : 0 : wps_success_event(wps->wps, wps->peer_dev.mac_addr);
1297 : 0 : wps->state = WPS_FINISHED;
1298 : 0 : return WPS_DONE;
1299 : : }
1300 : :
1301 : 0 : return WPS_FAILURE;
1302 : : }
1303 : :
1304 : :
1305 : 0 : static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
1306 : : const struct wpabuf *msg)
1307 : : {
1308 : : struct wps_parse_attr attr;
1309 : : u16 config_error;
1310 : :
1311 : 0 : wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
1312 : :
1313 [ # # ]: 0 : if (wps_parse_msg(msg, &attr) < 0)
1314 : 0 : return WPS_FAILURE;
1315 : :
1316 [ # # ]: 0 : if (attr.msg_type == NULL) {
1317 : 0 : wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1318 : 0 : return WPS_FAILURE;
1319 : : }
1320 : :
1321 [ # # ]: 0 : if (*attr.msg_type != WPS_WSC_NACK) {
1322 : 0 : wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
1323 : 0 : *attr.msg_type);
1324 : 0 : return WPS_FAILURE;
1325 : : }
1326 : :
1327 [ # # ][ # # ]: 0 : if (attr.registrar_nonce == NULL ||
1328 : 0 : os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
1329 : : {
1330 : 0 : wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
1331 : 0 : wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
1332 : 0 : attr.registrar_nonce, WPS_NONCE_LEN);
1333 : 0 : wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
1334 : 0 : wps->nonce_r, WPS_NONCE_LEN);
1335 : 0 : return WPS_FAILURE;
1336 : : }
1337 : :
1338 [ # # ][ # # ]: 0 : if (attr.enrollee_nonce == NULL ||
1339 : 0 : os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
1340 : 0 : wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
1341 : 0 : wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
1342 : 0 : attr.enrollee_nonce, WPS_NONCE_LEN);
1343 : 0 : wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
1344 : 0 : wps->nonce_e, WPS_NONCE_LEN);
1345 : 0 : return WPS_FAILURE;
1346 : : }
1347 : :
1348 [ # # ]: 0 : if (attr.config_error == NULL) {
1349 : 0 : wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
1350 : : "in WSC_NACK");
1351 : 0 : return WPS_FAILURE;
1352 : : }
1353 : :
1354 : 0 : config_error = WPA_GET_BE16(attr.config_error);
1355 : 0 : wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
1356 : : "Configuration Error %d", config_error);
1357 : :
1358 [ # # # # ]: 0 : switch (wps->state) {
1359 : : case RECV_M4:
1360 : 0 : wps_fail_event(wps->wps, WPS_M3, config_error,
1361 : 0 : wps->error_indication, wps->peer_dev.mac_addr);
1362 : 0 : break;
1363 : : case RECV_M6:
1364 : 0 : wps_fail_event(wps->wps, WPS_M5, config_error,
1365 : 0 : wps->error_indication, wps->peer_dev.mac_addr);
1366 : 0 : break;
1367 : : case RECV_M8:
1368 : 0 : wps_fail_event(wps->wps, WPS_M7, config_error,
1369 : 0 : wps->error_indication, wps->peer_dev.mac_addr);
1370 : 0 : break;
1371 : : default:
1372 : 0 : break;
1373 : : }
1374 : :
1375 : : /* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
1376 : : * Enrollee is Authenticator */
1377 : 0 : wps->state = SEND_WSC_NACK;
1378 : :
1379 : 0 : return WPS_FAILURE;
1380 : : }
1381 : :
1382 : :
1383 : 252 : enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
1384 : : enum wsc_op_code op_code,
1385 : : const struct wpabuf *msg)
1386 : : {
1387 : :
1388 : 252 : wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
1389 : : "op_code=%d)",
1390 : : (unsigned long) wpabuf_len(msg), op_code);
1391 : :
1392 [ - + ]: 252 : if (op_code == WSC_UPnP) {
1393 : : /* Determine the OpCode based on message type attribute */
1394 : : struct wps_parse_attr attr;
1395 [ # # ][ # # ]: 0 : if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
1396 [ # # ]: 0 : if (*attr.msg_type == WPS_WSC_ACK)
1397 : 0 : op_code = WSC_ACK;
1398 [ # # ]: 0 : else if (*attr.msg_type == WPS_WSC_NACK)
1399 : 0 : op_code = WSC_NACK;
1400 : : }
1401 : : }
1402 : :
1403 [ + - - - ]: 252 : switch (op_code) {
1404 : : case WSC_MSG:
1405 : : case WSC_UPnP:
1406 : 252 : return wps_process_wsc_msg(wps, msg);
1407 : : case WSC_ACK:
1408 [ # # ]: 0 : if (wps_validate_wsc_ack(msg) < 0)
1409 : 0 : return WPS_FAILURE;
1410 : 0 : return wps_process_wsc_ack(wps, msg);
1411 : : case WSC_NACK:
1412 [ # # ]: 0 : if (wps_validate_wsc_nack(msg) < 0)
1413 : 0 : return WPS_FAILURE;
1414 : 0 : return wps_process_wsc_nack(wps, msg);
1415 : : default:
1416 : 0 : wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
1417 : 252 : return WPS_FAILURE;
1418 : : }
1419 : : }
|