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