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