xref: /dragonfly/contrib/wpa_supplicant/src/eap_peer/eap_wsc.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
1 /*
2  * EAP-WSC peer for Wi-Fi Protected Setup
3  * Copyright (c) 2007-2009, 2012, 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 "uuid.h"
13 #include "eap_i.h"
14 #include "eap_common/eap_wsc_common.h"
15 #include "wps/wps.h"
16 #include "wps/wps_defs.h"
17 
18 
19 struct eap_wsc_data {
20           enum { WAIT_START, MESG, WAIT_FRAG_ACK, FAIL } state;
21           int registrar;
22           struct wpabuf *in_buf;
23           struct wpabuf *out_buf;
24           enum wsc_op_code in_op_code, out_op_code;
25           size_t out_used;
26           size_t fragment_size;
27           struct wps_data *wps;
28           struct wps_context *wps_ctx;
29 };
30 
31 
eap_wsc_state_txt(int state)32 static const char * eap_wsc_state_txt(int state)
33 {
34           switch (state) {
35           case WAIT_START:
36                     return "WAIT_START";
37           case MESG:
38                     return "MESG";
39           case WAIT_FRAG_ACK:
40                     return "WAIT_FRAG_ACK";
41           case FAIL:
42                     return "FAIL";
43           default:
44                     return "?";
45           }
46 }
47 
48 
eap_wsc_state(struct eap_wsc_data * data,int state)49 static void eap_wsc_state(struct eap_wsc_data *data, int state)
50 {
51           wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
52                        eap_wsc_state_txt(data->state),
53                        eap_wsc_state_txt(state));
54           data->state = state;
55 }
56 
57 
eap_wsc_new_ap_settings(struct wps_credential * cred,const char * params)58 static int eap_wsc_new_ap_settings(struct wps_credential *cred,
59                                            const char *params)
60 {
61           const char *pos, *end;
62           size_t len;
63 
64           os_memset(cred, 0, sizeof(*cred));
65 
66           pos = os_strstr(params, "new_ssid=");
67           if (pos == NULL)
68                     return 0;
69           pos += 9;
70           end = os_strchr(pos, ' ');
71           if (end == NULL)
72                     len = os_strlen(pos);
73           else
74                     len = end - pos;
75           if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
76               hexstr2bin(pos, cred->ssid, len / 2)) {
77                     wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid");
78                     return -1;
79           }
80           cred->ssid_len = len / 2;
81 
82           pos = os_strstr(params, "new_auth=");
83           if (pos == NULL) {
84                     wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth");
85                     return -1;
86           }
87           if (os_strncmp(pos + 9, "OPEN", 4) == 0)
88                     cred->auth_type = WPS_AUTH_OPEN;
89           else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
90                     cred->auth_type = WPS_AUTH_WPAPSK;
91           else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
92                     cred->auth_type = WPS_AUTH_WPA2PSK;
93           else {
94                     wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth");
95                     return -1;
96           }
97 
98           pos = os_strstr(params, "new_encr=");
99           if (pos == NULL) {
100                     wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr");
101                     return -1;
102           }
103           if (os_strncmp(pos + 9, "NONE", 4) == 0)
104                     cred->encr_type = WPS_ENCR_NONE;
105 #ifdef CONFIG_TESTING_OPTIONS
106           else if (os_strncmp(pos + 9, "WEP", 3) == 0)
107                     cred->encr_type = WPS_ENCR_WEP;
108 #endif /* CONFIG_TESTING_OPTIONS */
109           else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
110                     cred->encr_type = WPS_ENCR_TKIP;
111           else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
112                     cred->encr_type = WPS_ENCR_AES;
113           else {
114                     wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr");
115                     return -1;
116           }
117 
118           pos = os_strstr(params, "new_key=");
119           if (pos == NULL)
120                     return 0;
121           pos += 8;
122           end = os_strchr(pos, ' ');
123           if (end == NULL)
124                     len = os_strlen(pos);
125           else
126                     len = end - pos;
127           if ((len & 1) || len > 2 * sizeof(cred->key) ||
128               hexstr2bin(pos, cred->key, len / 2)) {
129                     wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key");
130                     return -1;
131           }
132           cred->key_len = len / 2;
133 
134           return 1;
135 }
136 
137 
eap_wsc_init(struct eap_sm * sm)138 static void * eap_wsc_init(struct eap_sm *sm)
139 {
140           struct eap_wsc_data *data;
141           const u8 *identity;
142           size_t identity_len;
143           int registrar;
144           struct wps_config cfg;
145           const char *pos, *end;
146           const char *phase1;
147           struct wps_context *wps;
148           struct wps_credential new_ap_settings;
149           int res;
150           int nfc = 0;
151           u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
152 
153           wps = sm->wps;
154           if (wps == NULL) {
155                     wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
156                     return NULL;
157           }
158 
159           identity = eap_get_config_identity(sm, &identity_len);
160 
161           if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
162               os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
163                     registrar = 1; /* Supplicant is Registrar */
164           else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
165               os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
166                     registrar = 0; /* Supplicant is Enrollee */
167           else {
168                     wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
169                                           identity, identity_len);
170                     return NULL;
171           }
172 
173           data = os_zalloc(sizeof(*data));
174           if (data == NULL)
175                     return NULL;
176           data->state = registrar ? MESG : WAIT_START;
177           data->registrar = registrar;
178           data->wps_ctx = wps;
179 
180           os_memset(&cfg, 0, sizeof(cfg));
181           cfg.wps = wps;
182           cfg.registrar = registrar;
183 
184           phase1 = eap_get_config_phase1(sm);
185           if (phase1 == NULL) {
186                     wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
187                                  "set");
188                     os_free(data);
189                     return NULL;
190           }
191 
192           pos = os_strstr(phase1, "pin=");
193           if (pos) {
194                     pos += 4;
195                     cfg.pin = (const u8 *) pos;
196                     while (*pos != '\0' && *pos != ' ')
197                               pos++;
198                     cfg.pin_len = pos - (const char *) cfg.pin;
199                     if (cfg.pin_len == 6 &&
200                         os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) {
201                               cfg.pin = NULL;
202                               cfg.pin_len = 0;
203                               nfc = 1;
204                     }
205           } else {
206                     pos = os_strstr(phase1, "pbc=1");
207                     if (pos)
208                               cfg.pbc = 1;
209           }
210 
211           pos = os_strstr(phase1, "dev_pw_id=");
212           if (pos) {
213                     u16 id = atoi(pos + 10);
214                     if (id == DEV_PW_NFC_CONNECTION_HANDOVER)
215                               nfc = 1;
216                     if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER)
217                               cfg.dev_pw_id = id;
218           }
219 
220           if (cfg.pin == NULL && !cfg.pbc && !nfc) {
221                     wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
222                                  "configuration data");
223                     os_free(data);
224                     return NULL;
225           }
226 
227           pos = os_strstr(phase1, " pkhash=");
228           if (pos) {
229                     size_t len;
230                     pos += 8;
231                     end = os_strchr(pos, ' ');
232                     if (end)
233                               len = end - pos;
234                     else
235                               len = os_strlen(pos);
236                     if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
237                         hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
238                               wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
239                               os_free(data);
240                               return NULL;
241                     }
242                     cfg.peer_pubkey_hash = pkhash;
243           }
244 
245           res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
246           if (res < 0) {
247                     os_free(data);
248                     wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP "
249                                  "settings");
250                     return NULL;
251           }
252           if (res == 1) {
253                     wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
254                                  "WPS");
255                     cfg.new_ap_settings = &new_ap_settings;
256           }
257 
258           if (os_strstr(phase1, "multi_ap=1"))
259                     cfg.multi_ap_backhaul_sta = 1;
260 
261           data->wps = wps_init(&cfg);
262           if (data->wps == NULL) {
263                     os_free(data);
264                     wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed");
265                     return NULL;
266           }
267           res = eap_get_config_fragment_size(sm);
268           if (res > 0)
269                     data->fragment_size = res;
270           else
271                     data->fragment_size = WSC_FRAGMENT_SIZE;
272           wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
273                        (unsigned int) data->fragment_size);
274 
275           if (registrar && cfg.pin) {
276                     wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
277                                               cfg.pin, cfg.pin_len, 0);
278           }
279 
280           /* Use reduced client timeout for WPS to avoid long wait */
281           if (sm->ClientTimeout > 30)
282                     sm->ClientTimeout = 30;
283 
284           return data;
285 }
286 
287 
eap_wsc_deinit(struct eap_sm * sm,void * priv)288 static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
289 {
290           struct eap_wsc_data *data = priv;
291           wpabuf_free(data->in_buf);
292           wpabuf_free(data->out_buf);
293           wps_deinit(data->wps);
294           os_free(data->wps_ctx->network_key);
295           data->wps_ctx->network_key = NULL;
296           os_free(data);
297 }
298 
299 
eap_wsc_build_msg(struct eap_wsc_data * data,struct eap_method_ret * ret,u8 id)300 static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
301                                                    struct eap_method_ret *ret, u8 id)
302 {
303           struct wpabuf *resp;
304           u8 flags;
305           size_t send_len, plen;
306 
307           ret->ignore = FALSE;
308           wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
309           ret->allowNotifications = TRUE;
310 
311           flags = 0;
312           send_len = wpabuf_len(data->out_buf) - data->out_used;
313           if (2 + send_len > data->fragment_size) {
314                     send_len = data->fragment_size - 2;
315                     flags |= WSC_FLAGS_MF;
316                     if (data->out_used == 0) {
317                               flags |= WSC_FLAGS_LF;
318                               send_len -= 2;
319                     }
320           }
321           plen = 2 + send_len;
322           if (flags & WSC_FLAGS_LF)
323                     plen += 2;
324           resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
325                                    EAP_CODE_RESPONSE, id);
326           if (resp == NULL)
327                     return NULL;
328 
329           wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
330           wpabuf_put_u8(resp, flags); /* Flags */
331           if (flags & WSC_FLAGS_LF)
332                     wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
333 
334           wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
335                               send_len);
336           data->out_used += send_len;
337 
338           ret->methodState = METHOD_MAY_CONT;
339           ret->decision = DECISION_FAIL;
340 
341           if (data->out_used == wpabuf_len(data->out_buf)) {
342                     wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
343                                  "(message sent completely)",
344                                  (unsigned long) send_len);
345                     wpabuf_free(data->out_buf);
346                     data->out_buf = NULL;
347                     data->out_used = 0;
348                     if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
349                         data->out_op_code == WSC_NACK ||
350                         data->out_op_code == WSC_Done) {
351                               eap_wsc_state(data, FAIL);
352                               ret->methodState = METHOD_DONE;
353                     } else
354                               eap_wsc_state(data, MESG);
355           } else {
356                     wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
357                                  "(%lu more to send)", (unsigned long) send_len,
358                                  (unsigned long) wpabuf_len(data->out_buf) -
359                                  data->out_used);
360                     eap_wsc_state(data, WAIT_FRAG_ACK);
361           }
362 
363           return resp;
364 }
365 
366 
eap_wsc_process_cont(struct eap_wsc_data * data,const u8 * buf,size_t len,u8 op_code)367 static int eap_wsc_process_cont(struct eap_wsc_data *data,
368                                         const u8 *buf, size_t len, u8 op_code)
369 {
370           /* Process continuation of a pending message */
371           if (op_code != data->in_op_code) {
372                     wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
373                                  "fragment (expected %d)",
374                                  op_code, data->in_op_code);
375                     return -1;
376           }
377 
378           if (len > wpabuf_tailroom(data->in_buf)) {
379                     wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
380                     eap_wsc_state(data, FAIL);
381                     return -1;
382           }
383 
384           wpabuf_put_data(data->in_buf, buf, len);
385           wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
386                        "for %lu bytes more", (unsigned long) len,
387                        (unsigned long) wpabuf_tailroom(data->in_buf));
388 
389           return 0;
390 }
391 
392 
eap_wsc_process_fragment(struct eap_wsc_data * data,struct eap_method_ret * ret,u8 id,u8 flags,u8 op_code,u16 message_length,const u8 * buf,size_t len)393 static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
394                                                             struct eap_method_ret *ret,
395                                                             u8 id, u8 flags, u8 op_code,
396                                                             u16 message_length,
397                                                             const u8 *buf, size_t len)
398 {
399           /* Process a fragment that is not the last one of the message */
400           if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
401                     wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
402                                  "fragmented packet");
403                     ret->ignore = TRUE;
404                     return NULL;
405           }
406 
407           if (data->in_buf == NULL) {
408                     /* First fragment of the message */
409                     data->in_buf = wpabuf_alloc(message_length);
410                     if (data->in_buf == NULL) {
411                               wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
412                                            "message");
413                               ret->ignore = TRUE;
414                               return NULL;
415                     }
416                     data->in_op_code = op_code;
417                     wpabuf_put_data(data->in_buf, buf, len);
418                     wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
419                                  "fragment, waiting for %lu bytes more",
420                                  (unsigned long) len,
421                                  (unsigned long) wpabuf_tailroom(data->in_buf));
422           }
423 
424           return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
425 }
426 
427 
eap_wsc_process(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const struct wpabuf * reqData)428 static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
429                                                struct eap_method_ret *ret,
430                                                const struct wpabuf *reqData)
431 {
432           struct eap_wsc_data *data = priv;
433           const u8 *start, *pos, *end;
434           size_t len;
435           u8 op_code, flags, id;
436           u16 message_length = 0;
437           enum wps_process_res res;
438           struct wpabuf tmpbuf;
439           struct wpabuf *r;
440 
441           pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
442                                      &len);
443           if (pos == NULL || len < 2) {
444                     ret->ignore = TRUE;
445                     return NULL;
446           }
447 
448           id = eap_get_id(reqData);
449 
450           start = pos;
451           end = start + len;
452 
453           op_code = *pos++;
454           flags = *pos++;
455           if (flags & WSC_FLAGS_LF) {
456                     if (end - pos < 2) {
457                               wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
458                               ret->ignore = TRUE;
459                               return NULL;
460                     }
461                     message_length = WPA_GET_BE16(pos);
462                     pos += 2;
463 
464                     if (message_length < end - pos || message_length > 50000) {
465                               wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
466                                            "Length");
467                               ret->ignore = TRUE;
468                               return NULL;
469                     }
470           }
471 
472           wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
473                        "Flags 0x%x Message Length %d",
474                        op_code, flags, message_length);
475 
476           if (data->state == WAIT_FRAG_ACK) {
477                     if (op_code != WSC_FRAG_ACK) {
478                               wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
479                                            "in WAIT_FRAG_ACK state", op_code);
480                               ret->ignore = TRUE;
481                               return NULL;
482                     }
483                     wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
484                     eap_wsc_state(data, MESG);
485                     return eap_wsc_build_msg(data, ret, id);
486           }
487 
488           if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
489               op_code != WSC_Done && op_code != WSC_Start) {
490                     wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
491                                  op_code);
492                     ret->ignore = TRUE;
493                     return NULL;
494           }
495 
496           if (data->state == WAIT_START) {
497                     if (op_code != WSC_Start) {
498                               wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
499                                            "in WAIT_START state", op_code);
500                               ret->ignore = TRUE;
501                               return NULL;
502                     }
503                     wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
504                     eap_wsc_state(data, MESG);
505                     /* Start message has empty payload, skip processing */
506                     goto send_msg;
507           } else if (op_code == WSC_Start) {
508                     wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
509                                  op_code);
510                     ret->ignore = TRUE;
511                     return NULL;
512           }
513 
514           if (data->in_buf &&
515               eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
516                     ret->ignore = TRUE;
517                     return NULL;
518           }
519 
520           if (flags & WSC_FLAGS_MF) {
521                     return eap_wsc_process_fragment(data, ret, id, flags, op_code,
522                                                             message_length, pos,
523                                                             end - pos);
524           }
525 
526           if (data->in_buf == NULL) {
527                     /* Wrap unfragmented messages as wpabuf without extra copy */
528                     wpabuf_set(&tmpbuf, pos, end - pos);
529                     data->in_buf = &tmpbuf;
530           }
531 
532           res = wps_process_msg(data->wps, op_code, data->in_buf);
533           switch (res) {
534           case WPS_DONE:
535                     wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
536                                  "successfully - wait for EAP failure");
537                     eap_wsc_state(data, FAIL);
538                     break;
539           case WPS_CONTINUE:
540                     eap_wsc_state(data, MESG);
541                     break;
542           case WPS_FAILURE:
543           case WPS_PENDING:
544                     wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
545                     eap_wsc_state(data, FAIL);
546                     break;
547           }
548 
549           if (data->in_buf != &tmpbuf)
550                     wpabuf_free(data->in_buf);
551           data->in_buf = NULL;
552 
553 send_msg:
554           if (data->out_buf == NULL) {
555                     data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
556                     if (data->out_buf == NULL) {
557                               wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
558                                            "message from WPS");
559                               eap_wsc_state(data, FAIL);
560                               ret->methodState = METHOD_DONE;
561                               ret->decision = DECISION_FAIL;
562                               return NULL;
563                     }
564                     data->out_used = 0;
565           }
566 
567           eap_wsc_state(data, MESG);
568           r = eap_wsc_build_msg(data, ret, id);
569           if (data->state == FAIL && ret->methodState == METHOD_DONE) {
570                     /* Use reduced client timeout for WPS to avoid long wait */
571                     if (sm->ClientTimeout > 2)
572                               sm->ClientTimeout = 2;
573           }
574           return r;
575 }
576 
577 
eap_peer_wsc_register(void)578 int eap_peer_wsc_register(void)
579 {
580           struct eap_method *eap;
581 
582           eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
583                                             EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
584                                             "WSC");
585           if (eap == NULL)
586                     return -1;
587 
588           eap->init = eap_wsc_init;
589           eap->deinit = eap_wsc_deinit;
590           eap->process = eap_wsc_process;
591 
592           return eap_peer_method_register(eap);
593 }
594