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