]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/wpa/src/eap_server/eap_server_wsc.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / wpa / src / eap_server / eap_server_wsc.c
1 /*
2  * EAP-WSC server for Wi-Fi Protected Setup
3  * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "includes.h"
16
17 #include "common.h"
18 #include "eloop.h"
19 #include "eap_i.h"
20 #include "eap_common/eap_wsc_common.h"
21 #include "wps/wps.h"
22
23
24 struct eap_wsc_data {
25         enum { START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
26         int registrar;
27         struct wpabuf *in_buf;
28         struct wpabuf *out_buf;
29         enum wsc_op_code in_op_code, out_op_code;
30         size_t out_used;
31         size_t fragment_size;
32         struct wps_data *wps;
33         int ext_reg_timeout;
34 };
35
36
37 #ifndef CONFIG_NO_STDOUT_DEBUG
38 static const char * eap_wsc_state_txt(int state)
39 {
40         switch (state) {
41         case START:
42                 return "START";
43         case MESG:
44                 return "MESG";
45         case FRAG_ACK:
46                 return "FRAG_ACK";
47         case WAIT_FRAG_ACK:
48                 return "WAIT_FRAG_ACK";
49         case DONE:
50                 return "DONE";
51         case FAIL:
52                 return "FAIL";
53         default:
54                 return "?";
55         }
56 }
57 #endif /* CONFIG_NO_STDOUT_DEBUG */
58
59
60 static void eap_wsc_state(struct eap_wsc_data *data, int state)
61 {
62         wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
63                    eap_wsc_state_txt(data->state),
64                    eap_wsc_state_txt(state));
65         data->state = state;
66 }
67
68
69 static void eap_wsc_ext_reg_timeout(void *eloop_ctx, void *timeout_ctx)
70 {
71         struct eap_sm *sm = eloop_ctx;
72         struct eap_wsc_data *data = timeout_ctx;
73
74         if (sm->method_pending != METHOD_PENDING_WAIT)
75                 return;
76
77         wpa_printf(MSG_DEBUG, "EAP-WSC: Timeout while waiting for an External "
78                    "Registrar");
79         data->ext_reg_timeout = 1;
80         eap_sm_pending_cb(sm);
81 }
82
83
84 static void * eap_wsc_init(struct eap_sm *sm)
85 {
86         struct eap_wsc_data *data;
87         int registrar;
88         struct wps_config cfg;
89
90         if (sm->identity && sm->identity_len == WSC_ID_REGISTRAR_LEN &&
91             os_memcmp(sm->identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) ==
92             0)
93                 registrar = 0; /* Supplicant is Registrar */
94         else if (sm->identity && sm->identity_len == WSC_ID_ENROLLEE_LEN &&
95                  os_memcmp(sm->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)
96                  == 0)
97                 registrar = 1; /* Supplicant is Enrollee */
98         else {
99                 wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
100                                   sm->identity, sm->identity_len);
101                 return NULL;
102         }
103
104         data = os_zalloc(sizeof(*data));
105         if (data == NULL)
106                 return NULL;
107         data->state = registrar ? START : MESG;
108         data->registrar = registrar;
109
110         os_memset(&cfg, 0, sizeof(cfg));
111         cfg.wps = sm->wps;
112         cfg.registrar = registrar;
113         if (registrar) {
114                 if (sm->wps == NULL || sm->wps->registrar == NULL) {
115                         wpa_printf(MSG_INFO, "EAP-WSC: WPS Registrar not "
116                                    "initialized");
117                         os_free(data);
118                         return NULL;
119                 }
120         } else {
121                 if (sm->user == NULL || sm->user->password == NULL) {
122                         /*
123                          * In theory, this should not really be needed, but
124                          * Windows 7 uses Registrar mode to probe AP's WPS
125                          * capabilities before trying to use Enrollee and fails
126                          * if the AP does not allow that probing to happen..
127                          */
128                         wpa_printf(MSG_DEBUG, "EAP-WSC: No AP PIN (password) "
129                                    "configured for Enrollee functionality - "
130                                    "allow for probing capabilities (M1)");
131                 } else {
132                         cfg.pin = sm->user->password;
133                         cfg.pin_len = sm->user->password_len;
134                 }
135         }
136         cfg.assoc_wps_ie = sm->assoc_wps_ie;
137         cfg.peer_addr = sm->peer_addr;
138         if (0 /* TODO: could provide option for forcing PSK format */)
139                  cfg.use_psk_key = 1;
140         data->wps = wps_init(&cfg);
141         if (data->wps == NULL) {
142                 os_free(data);
143                 return NULL;
144         }
145         data->fragment_size = WSC_FRAGMENT_SIZE;
146
147         return data;
148 }
149
150
151 static void eap_wsc_reset(struct eap_sm *sm, void *priv)
152 {
153         struct eap_wsc_data *data = priv;
154         eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
155         wpabuf_free(data->in_buf);
156         wpabuf_free(data->out_buf);
157         wps_deinit(data->wps);
158         os_free(data);
159 }
160
161
162 static struct wpabuf * eap_wsc_build_start(struct eap_sm *sm,
163                                            struct eap_wsc_data *data, u8 id)
164 {
165         struct wpabuf *req;
166
167         req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2,
168                             EAP_CODE_REQUEST, id);
169         if (req == NULL) {
170                 wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
171                            "request");
172                 return NULL;
173         }
174
175         wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/Start");
176         wpabuf_put_u8(req, WSC_Start); /* Op-Code */
177         wpabuf_put_u8(req, 0); /* Flags */
178
179         return req;
180 }
181
182
183 static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id)
184 {
185         struct wpabuf *req;
186         u8 flags;
187         size_t send_len, plen;
188
189         flags = 0;
190         send_len = wpabuf_len(data->out_buf) - data->out_used;
191         if (2 + send_len > data->fragment_size) {
192                 send_len = data->fragment_size - 2;
193                 flags |= WSC_FLAGS_MF;
194                 if (data->out_used == 0) {
195                         flags |= WSC_FLAGS_LF;
196                         send_len -= 2;
197                 }
198         }
199         plen = 2 + send_len;
200         if (flags & WSC_FLAGS_LF)
201                 plen += 2;
202         req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
203                             EAP_CODE_REQUEST, id);
204         if (req == NULL) {
205                 wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
206                            "request");
207                 return NULL;
208         }
209
210         wpabuf_put_u8(req, data->out_op_code); /* Op-Code */
211         wpabuf_put_u8(req, flags); /* Flags */
212         if (flags & WSC_FLAGS_LF)
213                 wpabuf_put_be16(req, wpabuf_len(data->out_buf));
214
215         wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
216                         send_len);
217         data->out_used += send_len;
218
219         if (data->out_used == wpabuf_len(data->out_buf)) {
220                 wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
221                            "(message sent completely)",
222                            (unsigned long) send_len);
223                 wpabuf_free(data->out_buf);
224                 data->out_buf = NULL;
225                 data->out_used = 0;
226                 eap_wsc_state(data, MESG);
227         } else {
228                 wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
229                            "(%lu more to send)", (unsigned long) send_len,
230                            (unsigned long) wpabuf_len(data->out_buf) -
231                            data->out_used);
232                 eap_wsc_state(data, WAIT_FRAG_ACK);
233         }
234
235         return req;
236 }
237
238
239 static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id)
240 {
241         struct eap_wsc_data *data = priv;
242
243         switch (data->state) {
244         case START:
245                 return eap_wsc_build_start(sm, data, id);
246         case MESG:
247                 if (data->out_buf == NULL) {
248                         data->out_buf = wps_get_msg(data->wps,
249                                                     &data->out_op_code);
250                         if (data->out_buf == NULL) {
251                                 wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to "
252                                            "receive message from WPS");
253                                 return NULL;
254                         }
255                         data->out_used = 0;
256                 }
257                 /* pass through */
258         case WAIT_FRAG_ACK:
259                 return eap_wsc_build_msg(data, id);
260         case FRAG_ACK:
261                 return eap_wsc_build_frag_ack(id, EAP_CODE_REQUEST);
262         default:
263                 wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected state %d in "
264                            "buildReq", data->state);
265                 return NULL;
266         }
267 }
268
269
270 static Boolean eap_wsc_check(struct eap_sm *sm, void *priv,
271                              struct wpabuf *respData)
272 {
273         const u8 *pos;
274         size_t len;
275
276         pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
277                                respData, &len);
278         if (pos == NULL || len < 2) {
279                 wpa_printf(MSG_INFO, "EAP-WSC: Invalid frame");
280                 return TRUE;
281         }
282
283         return FALSE;
284 }
285
286
287 static int eap_wsc_process_cont(struct eap_wsc_data *data,
288                                 const u8 *buf, size_t len, u8 op_code)
289 {
290         /* Process continuation of a pending message */
291         if (op_code != data->in_op_code) {
292                 wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
293                            "fragment (expected %d)",
294                            op_code, data->in_op_code);
295                 eap_wsc_state(data, FAIL);
296                 return -1;
297         }
298
299         if (len > wpabuf_tailroom(data->in_buf)) {
300                 wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
301                 eap_wsc_state(data, FAIL);
302                 return -1;
303         }
304
305         wpabuf_put_data(data->in_buf, buf, len);
306         wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu "
307                    "bytes more", (unsigned long) len,
308                    (unsigned long) wpabuf_tailroom(data->in_buf));
309
310         return 0;
311 }
312
313
314 static int eap_wsc_process_fragment(struct eap_wsc_data *data,
315                                     u8 flags, u8 op_code, u16 message_length,
316                                     const u8 *buf, size_t len)
317 {
318         /* Process a fragment that is not the last one of the message */
319         if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
320                 wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length "
321                            "field in a fragmented packet");
322                 return -1;
323         }
324
325         if (data->in_buf == NULL) {
326                 /* First fragment of the message */
327                 data->in_buf = wpabuf_alloc(message_length);
328                 if (data->in_buf == NULL) {
329                         wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
330                                    "message");
331                         return -1;
332                 }
333                 data->in_op_code = op_code;
334                 wpabuf_put_data(data->in_buf, buf, len);
335                 wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in "
336                            "first fragment, waiting for %lu bytes more",
337                            (unsigned long) len,
338                            (unsigned long) wpabuf_tailroom(data->in_buf));
339         }
340
341         return 0;
342 }
343
344
345 static void eap_wsc_process(struct eap_sm *sm, void *priv,
346                             struct wpabuf *respData)
347 {
348         struct eap_wsc_data *data = priv;
349         const u8 *start, *pos, *end;
350         size_t len;
351         u8 op_code, flags;
352         u16 message_length = 0;
353         enum wps_process_res res;
354         struct wpabuf tmpbuf;
355
356         eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
357         if (data->ext_reg_timeout) {
358                 eap_wsc_state(data, FAIL);
359                 return;
360         }
361
362         pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
363                                respData, &len);
364         if (pos == NULL || len < 2)
365                 return; /* Should not happen; message already verified */
366
367         start = pos;
368         end = start + len;
369
370         op_code = *pos++;
371         flags = *pos++;
372         if (flags & WSC_FLAGS_LF) {
373                 if (end - pos < 2) {
374                         wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
375                         return;
376                 }
377                 message_length = WPA_GET_BE16(pos);
378                 pos += 2;
379
380                 if (message_length < end - pos) {
381                         wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
382                                    "Length");
383                         return;
384                 }
385         }
386
387         wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
388                    "Flags 0x%x Message Length %d",
389                    op_code, flags, message_length);
390
391         if (data->state == WAIT_FRAG_ACK) {
392                 if (op_code != WSC_FRAG_ACK) {
393                         wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
394                                    "in WAIT_FRAG_ACK state", op_code);
395                         eap_wsc_state(data, FAIL);
396                         return;
397                 }
398                 wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
399                 eap_wsc_state(data, MESG);
400                 return;
401         }
402
403         if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
404             op_code != WSC_Done) {
405                 wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
406                            op_code);
407                 eap_wsc_state(data, FAIL);
408                 return;
409         }
410
411         if (data->in_buf &&
412             eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
413                 eap_wsc_state(data, FAIL);
414                 return;
415         }
416
417         if (flags & WSC_FLAGS_MF) {
418                 if (eap_wsc_process_fragment(data, flags, op_code,
419                                              message_length, pos, end - pos) <
420                     0)
421                         eap_wsc_state(data, FAIL);
422                 else
423                         eap_wsc_state(data, FRAG_ACK);
424                 return;
425         }
426
427         if (data->in_buf == NULL) {
428                 /* Wrap unfragmented messages as wpabuf without extra copy */
429                 wpabuf_set(&tmpbuf, pos, end - pos);
430                 data->in_buf = &tmpbuf;
431         }
432
433         res = wps_process_msg(data->wps, op_code, data->in_buf);
434         switch (res) {
435         case WPS_DONE:
436                 wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
437                            "successfully - report EAP failure");
438                 eap_wsc_state(data, FAIL);
439                 break;
440         case WPS_CONTINUE:
441                 eap_wsc_state(data, MESG);
442                 break;
443         case WPS_FAILURE:
444                 wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
445                 eap_wsc_state(data, FAIL);
446                 break;
447         case WPS_PENDING:
448                 eap_wsc_state(data, MESG);
449                 sm->method_pending = METHOD_PENDING_WAIT;
450                 eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
451                 eloop_register_timeout(5, 0, eap_wsc_ext_reg_timeout,
452                                        sm, data);
453                 break;
454         }
455
456         if (data->in_buf != &tmpbuf)
457                 wpabuf_free(data->in_buf);
458         data->in_buf = NULL;
459 }
460
461
462 static Boolean eap_wsc_isDone(struct eap_sm *sm, void *priv)
463 {
464         struct eap_wsc_data *data = priv;
465         return data->state == FAIL;
466 }
467
468
469 static Boolean eap_wsc_isSuccess(struct eap_sm *sm, void *priv)
470 {
471         /* EAP-WSC will always result in EAP-Failure */
472         return FALSE;
473 }
474
475
476 static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv)
477 {
478         /* Recommended retransmit times: retransmit timeout 5 seconds,
479          * per-message timeout 15 seconds, i.e., 3 tries. */
480         sm->MaxRetrans = 2; /* total 3 attempts */
481         return 5;
482 }
483
484
485 int eap_server_wsc_register(void)
486 {
487         struct eap_method *eap;
488         int ret;
489
490         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
491                                       EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
492                                       "WSC");
493         if (eap == NULL)
494                 return -1;
495
496         eap->init = eap_wsc_init;
497         eap->reset = eap_wsc_reset;
498         eap->buildReq = eap_wsc_buildReq;
499         eap->check = eap_wsc_check;
500         eap->process = eap_wsc_process;
501         eap->isDone = eap_wsc_isDone;
502         eap->isSuccess = eap_wsc_isSuccess;
503         eap->getTimeout = eap_wsc_getTimeout;
504
505         ret = eap_server_method_register(eap);
506         if (ret)
507                 eap_server_method_free(eap);
508         return ret;
509 }