~ubuntu-branches/ubuntu/gutsy/wpasupplicant/gutsy

« back to all changes in this revision

Viewing changes to src/eap_server/eap.c

  • Committer: Bazaar Package Importer
  • Author(s): Reinhard Tartler, Alexander Sack
  • Date: 2007-08-26 16:06:57 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20070826160657-2m8pxoweuxe8f93t
Tags: 0.6.0+0.5.8-0ubuntu1
* New upstream release
* remove patch 11_erroneous_manpage_ref, applied upstream
* remove patch 25_wpas_dbus_unregister_iface_fix, applied upstream

[ Alexander Sack ]
* bumping upstream version to replace development version 0.6.0 with
  this package from stable release branch.
* attempt to fix wierd timeout and high latency issues by going
  back to stable upstream version (0.5.9) (LP: #140763,
  LP: #141233).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * hostapd / EAP Standalone Authenticator state machine (RFC 4137)
3
 
 * Copyright (c) 2004-2007, 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 "eap_i.h"
19
 
#include "state_machine.h"
20
 
 
21
 
#define STATE_MACHINE_DATA struct eap_sm
22
 
#define STATE_MACHINE_DEBUG_PREFIX "EAP"
23
 
 
24
 
#define EAP_MAX_AUTH_ROUNDS 50
25
 
 
26
 
static void eap_user_free(struct eap_user *user);
27
 
 
28
 
 
29
 
/* EAP state machines are described in RFC 4137 */
30
 
 
31
 
static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
32
 
                                   int eapSRTT, int eapRTTVAR,
33
 
                                   int methodTimeout);
34
 
static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len);
35
 
static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len);
36
 
static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len);
37
 
static int eap_sm_nextId(struct eap_sm *sm, int id);
38
 
static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len);
39
 
static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor);
40
 
static int eap_sm_Policy_getDecision(struct eap_sm *sm);
41
 
static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
42
 
 
43
 
 
44
 
static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var)
45
 
{
46
 
        return sm->eapol_cb->get_bool(sm->eapol_ctx, var);
47
 
}
48
 
 
49
 
 
50
 
static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var,
51
 
                           Boolean value)
52
 
{
53
 
        sm->eapol_cb->set_bool(sm->eapol_ctx, var, value);
54
 
}
55
 
 
56
 
 
57
 
static void eapol_set_eapReqData(struct eap_sm *sm,
58
 
                                 const u8 *eapReqData, size_t eapReqDataLen)
59
 
{
60
 
        wpa_hexdump(MSG_MSGDUMP, "EAP: eapReqData -> EAPOL",
61
 
                    sm->eapReqData, sm->eapReqDataLen);
62
 
        sm->eapol_cb->set_eapReqData(sm->eapol_ctx, eapReqData, eapReqDataLen);
63
 
}
64
 
 
65
 
 
66
 
static void eapol_set_eapKeyData(struct eap_sm *sm,
67
 
                                 const u8 *eapKeyData, size_t eapKeyDataLen)
68
 
{
69
 
        wpa_hexdump(MSG_MSGDUMP, "EAP: eapKeyData -> EAPOL",
70
 
                    sm->eapKeyData, sm->eapKeyDataLen);
71
 
        sm->eapol_cb->set_eapKeyData(sm->eapol_ctx, eapKeyData, eapKeyDataLen);
72
 
}
73
 
 
74
 
 
75
 
/**
76
 
 * eap_user_get - Fetch user information from the database
77
 
 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
78
 
 * @identity: Identity (User-Name) of the user
79
 
 * @identity_len: Length of identity in bytes
80
 
 * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user
81
 
 * Returns: 0 on success, or -1 on failure
82
 
 *
83
 
 * This function is used to fetch user information for EAP. The user will be
84
 
 * selected based on the specified identity. sm->user and
85
 
 * sm->user_eap_method_index are updated for the new user when a matching user
86
 
 * is found. sm->user can be used to get user information (e.g., password).
87
 
 */
88
 
int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
89
 
                 int phase2)
90
 
{
91
 
        struct eap_user *user;
92
 
 
93
 
        if (sm == NULL || sm->eapol_cb == NULL ||
94
 
            sm->eapol_cb->get_eap_user == NULL)
95
 
                return -1;
96
 
 
97
 
        eap_user_free(sm->user);
98
 
        sm->user = NULL;
99
 
 
100
 
        user = os_zalloc(sizeof(*user));
101
 
        if (user == NULL)
102
 
            return -1;
103
 
 
104
 
        if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity,
105
 
                                       identity_len, phase2, user) != 0) {
106
 
                eap_user_free(user);
107
 
                return -1;
108
 
        }
109
 
 
110
 
        sm->user = user;
111
 
        sm->user_eap_method_index = 0;
112
 
 
113
 
        return 0;
114
 
}
115
 
 
116
 
 
117
 
SM_STATE(EAP, DISABLED)
118
 
{
119
 
        SM_ENTRY(EAP, DISABLED);
120
 
        sm->num_rounds = 0;
121
 
}
122
 
 
123
 
 
124
 
SM_STATE(EAP, INITIALIZE)
125
 
{
126
 
        SM_ENTRY(EAP, INITIALIZE);
127
 
 
128
 
        sm->currentId = -1;
129
 
        eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
130
 
        eapol_set_bool(sm, EAPOL_eapFail, FALSE);
131
 
        eapol_set_bool(sm, EAPOL_eapTimeout, FALSE);
132
 
        free(sm->eapKeyData);
133
 
        sm->eapKeyData = NULL;
134
 
        sm->eapKeyDataLen = 0;
135
 
        /* eapKeyAvailable = FALSE */
136
 
        eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
137
 
 
138
 
        /*
139
 
         * This is not defined in RFC 4137, but method state needs to be
140
 
         * reseted here so that it does not remain in success state when
141
 
         * re-authentication starts.
142
 
         */
143
 
        if (sm->m && sm->eap_method_priv) {
144
 
                sm->m->reset(sm, sm->eap_method_priv);
145
 
                sm->eap_method_priv = NULL;
146
 
        }
147
 
        sm->m = NULL;
148
 
        sm->user_eap_method_index = 0;
149
 
 
150
 
        if (sm->backend_auth) {
151
 
                sm->currentMethod = EAP_TYPE_NONE;
152
 
                /* parse rxResp, respId, respMethod */
153
 
                eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen);
154
 
                if (sm->rxResp) {
155
 
                        sm->currentId = sm->respId;
156
 
                }
157
 
        }
158
 
        sm->num_rounds = 0;
159
 
        sm->method_pending = METHOD_PENDING_NONE;
160
 
}
161
 
 
162
 
 
163
 
SM_STATE(EAP, PICK_UP_METHOD)
164
 
{
165
 
        SM_ENTRY(EAP, PICK_UP_METHOD);
166
 
 
167
 
        if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) {
168
 
                sm->currentMethod = sm->respMethod;
169
 
                if (sm->m && sm->eap_method_priv) {
170
 
                        sm->m->reset(sm, sm->eap_method_priv);
171
 
                        sm->eap_method_priv = NULL;
172
 
                }
173
 
                sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF,
174
 
                                                  sm->currentMethod);
175
 
                if (sm->m && sm->m->initPickUp) {
176
 
                        sm->eap_method_priv = sm->m->initPickUp(sm);
177
 
                        if (sm->eap_method_priv == NULL) {
178
 
                                wpa_printf(MSG_DEBUG, "EAP: Failed to "
179
 
                                           "initialize EAP method %d",
180
 
                                           sm->currentMethod);
181
 
                                sm->m = NULL;
182
 
                                sm->currentMethod = EAP_TYPE_NONE;
183
 
                        }
184
 
                } else {
185
 
                        sm->m = NULL;
186
 
                        sm->currentMethod = EAP_TYPE_NONE;
187
 
                }
188
 
        }
189
 
}
190
 
 
191
 
 
192
 
SM_STATE(EAP, IDLE)
193
 
{
194
 
        SM_ENTRY(EAP, IDLE);
195
 
 
196
 
        sm->retransWhile = eap_sm_calculateTimeout(sm, sm->retransCount,
197
 
                                                   sm->eapSRTT, sm->eapRTTVAR,
198
 
                                                   sm->methodTimeout);
199
 
}
200
 
 
201
 
 
202
 
SM_STATE(EAP, RETRANSMIT)
203
 
{
204
 
        SM_ENTRY(EAP, RETRANSMIT);
205
 
 
206
 
        /* TODO: Is this needed since EAPOL state machines take care of
207
 
         * retransmit? */
208
 
}
209
 
 
210
 
 
211
 
SM_STATE(EAP, RECEIVED)
212
 
{
213
 
        SM_ENTRY(EAP, RECEIVED);
214
 
 
215
 
        /* parse rxResp, respId, respMethod */
216
 
        eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen);
217
 
        sm->num_rounds++;
218
 
}
219
 
 
220
 
 
221
 
SM_STATE(EAP, DISCARD)
222
 
{
223
 
        SM_ENTRY(EAP, DISCARD);
224
 
        eapol_set_bool(sm, EAPOL_eapResp, FALSE);
225
 
        eapol_set_bool(sm, EAPOL_eapNoReq, TRUE);
226
 
}
227
 
 
228
 
 
229
 
SM_STATE(EAP, SEND_REQUEST)
230
 
{
231
 
        SM_ENTRY(EAP, SEND_REQUEST);
232
 
 
233
 
        sm->retransCount = 0;
234
 
        if (sm->eapReqData) {
235
 
                eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen);
236
 
                free(sm->lastReqData);
237
 
                sm->lastReqData = sm->eapReqData;
238
 
                sm->lastReqDataLen = sm->eapReqDataLen;
239
 
                sm->eapReqData = NULL;
240
 
                sm->eapReqDataLen = 0;
241
 
                eapol_set_bool(sm, EAPOL_eapResp, FALSE);
242
 
                eapol_set_bool(sm, EAPOL_eapReq, TRUE);
243
 
        } else {
244
 
                wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData");
245
 
                eapol_set_bool(sm, EAPOL_eapResp, FALSE);
246
 
                eapol_set_bool(sm, EAPOL_eapReq, FALSE);
247
 
                eapol_set_bool(sm, EAPOL_eapNoReq, TRUE);
248
 
        }
249
 
}
250
 
 
251
 
 
252
 
SM_STATE(EAP, INTEGRITY_CHECK)
253
 
{
254
 
        SM_ENTRY(EAP, INTEGRITY_CHECK);
255
 
 
256
 
        if (sm->m->check) {
257
 
                sm->ignore = sm->m->check(sm, sm->eap_method_priv,
258
 
                                          sm->eapRespData, sm->eapRespDataLen);
259
 
        }
260
 
}
261
 
 
262
 
 
263
 
SM_STATE(EAP, METHOD_REQUEST)
264
 
{
265
 
        SM_ENTRY(EAP, METHOD_REQUEST);
266
 
 
267
 
        if (sm->m == NULL) {
268
 
                wpa_printf(MSG_DEBUG, "EAP: method not initialized");
269
 
                return;
270
 
        }
271
 
 
272
 
        sm->currentId = eap_sm_nextId(sm, sm->currentId);
273
 
        wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d",
274
 
                   sm->currentId);
275
 
        sm->lastId = sm->currentId;
276
 
        free(sm->eapReqData);
277
 
        sm->eapReqData = sm->m->buildReq(sm, sm->eap_method_priv,
278
 
                                         sm->currentId, &sm->eapReqDataLen);
279
 
        if (sm->m->getTimeout)
280
 
                sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv);
281
 
        else
282
 
                sm->methodTimeout = 0;
283
 
}
284
 
 
285
 
 
286
 
SM_STATE(EAP, METHOD_RESPONSE)
287
 
{
288
 
        SM_ENTRY(EAP, METHOD_RESPONSE);
289
 
 
290
 
        sm->m->process(sm, sm->eap_method_priv, sm->eapRespData,
291
 
                       sm->eapRespDataLen);
292
 
        if (sm->m->isDone(sm, sm->eap_method_priv)) {
293
 
                eap_sm_Policy_update(sm, NULL, 0);
294
 
                free(sm->eapKeyData);
295
 
                if (sm->m->getKey) {
296
 
                        sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
297
 
                                                       &sm->eapKeyDataLen);
298
 
                } else {
299
 
                        sm->eapKeyData = NULL;
300
 
                        sm->eapKeyDataLen = 0;
301
 
                }
302
 
                sm->methodState = METHOD_END;
303
 
        } else {
304
 
                sm->methodState = METHOD_CONTINUE;
305
 
        }
306
 
}
307
 
 
308
 
 
309
 
SM_STATE(EAP, PROPOSE_METHOD)
310
 
{
311
 
        int vendor;
312
 
        EapType type;
313
 
 
314
 
        SM_ENTRY(EAP, PROPOSE_METHOD);
315
 
 
316
 
        type = eap_sm_Policy_getNextMethod(sm, &vendor);
317
 
        if (vendor == EAP_VENDOR_IETF)
318
 
                sm->currentMethod = type;
319
 
        else
320
 
                sm->currentMethod = EAP_TYPE_EXPANDED;
321
 
        if (sm->m && sm->eap_method_priv) {
322
 
                sm->m->reset(sm, sm->eap_method_priv);
323
 
                sm->eap_method_priv = NULL;
324
 
        }
325
 
        sm->m = eap_server_get_eap_method(vendor, type);
326
 
        if (sm->m) {
327
 
                sm->eap_method_priv = sm->m->init(sm);
328
 
                if (sm->eap_method_priv == NULL) {
329
 
                        wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP "
330
 
                                   "method %d", sm->currentMethod);
331
 
                        sm->m = NULL;
332
 
                        sm->currentMethod = EAP_TYPE_NONE;
333
 
                }
334
 
        }
335
 
        if (sm->currentMethod == EAP_TYPE_IDENTITY ||
336
 
            sm->currentMethod == EAP_TYPE_NOTIFICATION)
337
 
                sm->methodState = METHOD_CONTINUE;
338
 
        else
339
 
                sm->methodState = METHOD_PROPOSED;
340
 
}
341
 
 
342
 
 
343
 
SM_STATE(EAP, NAK)
344
 
{
345
 
        struct eap_hdr *nak;
346
 
        size_t len = 0;
347
 
        u8 *pos, *nak_list = NULL;
348
 
 
349
 
        SM_ENTRY(EAP, NAK);
350
 
 
351
 
        if (sm->eap_method_priv) {
352
 
                sm->m->reset(sm, sm->eap_method_priv);
353
 
                sm->eap_method_priv = NULL;
354
 
        }
355
 
        sm->m = NULL;
356
 
 
357
 
        nak = (struct eap_hdr *) sm->eapRespData;
358
 
        if (nak && sm->eapRespDataLen > sizeof(*nak)) {
359
 
                len = be_to_host16(nak->length);
360
 
                if (len > sm->eapRespDataLen)
361
 
                        len = sm->eapRespDataLen;
362
 
                pos = (u8 *) (nak + 1);
363
 
                len -= sizeof(*nak);
364
 
                if (*pos == EAP_TYPE_NAK) {
365
 
                        pos++;
366
 
                        len--;
367
 
                        nak_list = pos;
368
 
                }
369
 
        }
370
 
        eap_sm_Policy_update(sm, nak_list, len);
371
 
}
372
 
 
373
 
 
374
 
SM_STATE(EAP, SELECT_ACTION)
375
 
{
376
 
        SM_ENTRY(EAP, SELECT_ACTION);
377
 
 
378
 
        sm->decision = eap_sm_Policy_getDecision(sm);
379
 
}
380
 
 
381
 
 
382
 
SM_STATE(EAP, TIMEOUT_FAILURE)
383
 
{
384
 
        SM_ENTRY(EAP, TIMEOUT_FAILURE);
385
 
 
386
 
        eapol_set_bool(sm, EAPOL_eapTimeout, TRUE);
387
 
}
388
 
 
389
 
 
390
 
SM_STATE(EAP, FAILURE)
391
 
{
392
 
        SM_ENTRY(EAP, FAILURE);
393
 
 
394
 
        free(sm->eapReqData);
395
 
        sm->eapReqData = eap_sm_buildFailure(sm, sm->currentId,
396
 
                                             &sm->eapReqDataLen);
397
 
        if (sm->eapReqData) {
398
 
                eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen);
399
 
                free(sm->eapReqData);
400
 
                sm->eapReqData = NULL;
401
 
                sm->eapReqDataLen = 0;
402
 
        }
403
 
        free(sm->lastReqData);
404
 
        sm->lastReqData = NULL;
405
 
        sm->lastReqDataLen = 0;
406
 
        eapol_set_bool(sm, EAPOL_eapFail, TRUE);
407
 
}
408
 
 
409
 
 
410
 
SM_STATE(EAP, SUCCESS)
411
 
{
412
 
        SM_ENTRY(EAP, SUCCESS);
413
 
 
414
 
        free(sm->eapReqData);
415
 
        sm->eapReqData = eap_sm_buildSuccess(sm, sm->currentId,
416
 
                                             &sm->eapReqDataLen);
417
 
        if (sm->eapReqData) {
418
 
                eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen);
419
 
                free(sm->eapReqData);
420
 
                sm->eapReqData = NULL;
421
 
                sm->eapReqDataLen = 0;
422
 
        }
423
 
        free(sm->lastReqData);
424
 
        sm->lastReqData = NULL;
425
 
        sm->lastReqDataLen = 0;
426
 
        if (sm->eapKeyData) {
427
 
                eapol_set_eapKeyData(sm, sm->eapKeyData, sm->eapKeyDataLen);
428
 
        }
429
 
        eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
430
 
}
431
 
 
432
 
 
433
 
SM_STEP(EAP)
434
 
{
435
 
        if (eapol_get_bool(sm, EAPOL_eapRestart) &&
436
 
            eapol_get_bool(sm, EAPOL_portEnabled))
437
 
                SM_ENTER_GLOBAL(EAP, INITIALIZE);
438
 
        else if (!eapol_get_bool(sm, EAPOL_portEnabled))
439
 
                SM_ENTER_GLOBAL(EAP, DISABLED);
440
 
        else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
441
 
                if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
442
 
                        wpa_printf(MSG_DEBUG, "EAP: more than %d "
443
 
                                   "authentication rounds - abort",
444
 
                                   EAP_MAX_AUTH_ROUNDS);
445
 
                        sm->num_rounds++;
446
 
                        SM_ENTER_GLOBAL(EAP, FAILURE);
447
 
                }
448
 
        } else switch (sm->EAP_state) {
449
 
        case EAP_INITIALIZE:
450
 
                if (sm->backend_auth) {
451
 
                        if (!sm->rxResp)
452
 
                                SM_ENTER(EAP, SELECT_ACTION);
453
 
                        else if (sm->rxResp &&
454
 
                                 (sm->respMethod == EAP_TYPE_NAK ||
455
 
                                  (sm->respMethod == EAP_TYPE_EXPANDED &&
456
 
                                   sm->respVendor == EAP_VENDOR_IETF &&
457
 
                                   sm->respVendorMethod == EAP_TYPE_NAK)))
458
 
                                SM_ENTER(EAP, NAK);
459
 
                        else
460
 
                                SM_ENTER(EAP, PICK_UP_METHOD);
461
 
                } else {
462
 
                        SM_ENTER(EAP, SELECT_ACTION);
463
 
                }
464
 
                break;
465
 
        case EAP_PICK_UP_METHOD:
466
 
                if (sm->currentMethod == EAP_TYPE_NONE) {
467
 
                        SM_ENTER(EAP, SELECT_ACTION);
468
 
                } else {
469
 
                        SM_ENTER(EAP, METHOD_RESPONSE);
470
 
                }
471
 
                break;
472
 
        case EAP_DISABLED:
473
 
                if (eapol_get_bool(sm, EAPOL_portEnabled))
474
 
                        SM_ENTER(EAP, INITIALIZE);
475
 
                break;
476
 
        case EAP_IDLE:
477
 
                if (sm->retransWhile == 0)
478
 
                        SM_ENTER(EAP, RETRANSMIT);
479
 
                else if (eapol_get_bool(sm, EAPOL_eapResp))
480
 
                        SM_ENTER(EAP, RECEIVED);
481
 
                break;
482
 
        case EAP_RETRANSMIT:
483
 
                if (sm->retransCount > sm->MaxRetrans)
484
 
                        SM_ENTER(EAP, TIMEOUT_FAILURE);
485
 
                else
486
 
                        SM_ENTER(EAP, IDLE);
487
 
                break;
488
 
        case EAP_RECEIVED:
489
 
                if (sm->rxResp && (sm->respId == sm->currentId) &&
490
 
                    (sm->respMethod == EAP_TYPE_NAK ||
491
 
                     (sm->respMethod == EAP_TYPE_EXPANDED &&
492
 
                      sm->respVendor == EAP_VENDOR_IETF &&
493
 
                      sm->respVendorMethod == EAP_TYPE_NAK))
494
 
                    && (sm->methodState == METHOD_PROPOSED))
495
 
                        SM_ENTER(EAP, NAK);
496
 
                else if (sm->rxResp && (sm->respId == sm->currentId) &&
497
 
                         ((sm->respMethod == sm->currentMethod) ||
498
 
                          (sm->respMethod == EAP_TYPE_EXPANDED &&
499
 
                           sm->respVendor == EAP_VENDOR_IETF &&
500
 
                           sm->respVendorMethod == sm->currentMethod)))
501
 
                        SM_ENTER(EAP, INTEGRITY_CHECK);
502
 
                else {
503
 
                        wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: "
504
 
                                   "rxResp=%d respId=%d currentId=%d "
505
 
                                   "respMethod=%d currentMethod=%d",
506
 
                                   sm->rxResp, sm->respId, sm->currentId,
507
 
                                   sm->respMethod, sm->currentMethod);
508
 
                        SM_ENTER(EAP, DISCARD);
509
 
                }
510
 
                break;
511
 
        case EAP_DISCARD:
512
 
                SM_ENTER(EAP, IDLE);
513
 
                break;
514
 
        case EAP_SEND_REQUEST:
515
 
                SM_ENTER(EAP, IDLE);
516
 
                break;
517
 
        case EAP_INTEGRITY_CHECK:
518
 
                if (sm->ignore)
519
 
                        SM_ENTER(EAP, DISCARD);
520
 
                else
521
 
                        SM_ENTER(EAP, METHOD_RESPONSE);
522
 
                break;
523
 
        case EAP_METHOD_REQUEST:
524
 
                SM_ENTER(EAP, SEND_REQUEST);
525
 
                break;
526
 
        case EAP_METHOD_RESPONSE:
527
 
                /*
528
 
                 * Note: Mechanism to allow EAP methods to wait while going
529
 
                 * through pending processing is an extension to RFC 4137
530
 
                 * which only defines the transits to SELECT_ACTION and
531
 
                 * METHOD_REQUEST from this METHOD_RESPONSE state.
532
 
                 */
533
 
                if (sm->methodState == METHOD_END)
534
 
                        SM_ENTER(EAP, SELECT_ACTION);
535
 
                else if (sm->method_pending == METHOD_PENDING_WAIT) {
536
 
                        wpa_printf(MSG_DEBUG, "EAP: Method has pending "
537
 
                                   "processing - wait before proceeding to "
538
 
                                   "METHOD_REQUEST state");
539
 
                } else if (sm->method_pending == METHOD_PENDING_CONT) {
540
 
                        wpa_printf(MSG_DEBUG, "EAP: Method has completed "
541
 
                                   "pending processing - reprocess pending "
542
 
                                   "EAP message");
543
 
                        sm->method_pending = METHOD_PENDING_NONE;
544
 
                        SM_ENTER(EAP, METHOD_RESPONSE);
545
 
                } else
546
 
                        SM_ENTER(EAP, METHOD_REQUEST);
547
 
                break;
548
 
        case EAP_PROPOSE_METHOD:
549
 
                /*
550
 
                 * Note: Mechanism to allow EAP methods to wait while going
551
 
                 * through pending processing is an extension to RFC 4137
552
 
                 * which only defines the transit to METHOD_REQUEST from this
553
 
                 * PROPOSE_METHOD state.
554
 
                 */
555
 
                if (sm->method_pending == METHOD_PENDING_WAIT) {
556
 
                        wpa_printf(MSG_DEBUG, "EAP: Method has pending "
557
 
                                   "processing - wait before proceeding to "
558
 
                                   "METHOD_REQUEST state");
559
 
                        if (sm->user_eap_method_index > 0)
560
 
                                sm->user_eap_method_index--;
561
 
                } else if (sm->method_pending == METHOD_PENDING_CONT) {
562
 
                        wpa_printf(MSG_DEBUG, "EAP: Method has completed "
563
 
                                   "pending processing - reprocess pending "
564
 
                                   "EAP message");
565
 
                        sm->method_pending = METHOD_PENDING_NONE;
566
 
                        SM_ENTER(EAP, PROPOSE_METHOD);
567
 
                } else
568
 
                        SM_ENTER(EAP, METHOD_REQUEST);
569
 
                break;
570
 
        case EAP_NAK:
571
 
                SM_ENTER(EAP, SELECT_ACTION);
572
 
                break;
573
 
        case EAP_SELECT_ACTION:
574
 
                if (sm->decision == DECISION_FAILURE)
575
 
                        SM_ENTER(EAP, FAILURE);
576
 
                else if (sm->decision == DECISION_SUCCESS)
577
 
                        SM_ENTER(EAP, SUCCESS);
578
 
                else
579
 
                        SM_ENTER(EAP, PROPOSE_METHOD);
580
 
                break;
581
 
        case EAP_TIMEOUT_FAILURE:
582
 
                break;
583
 
        case EAP_FAILURE:
584
 
                break;
585
 
        case EAP_SUCCESS:
586
 
                break;
587
 
        }
588
 
}
589
 
 
590
 
 
591
 
static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
592
 
                                   int eapSRTT, int eapRTTVAR,
593
 
                                   int methodTimeout)
594
 
{
595
 
        /* For now, retransmission is done in EAPOL state machines, so make
596
 
         * sure EAP state machine does not end up trying to retransmit packets.
597
 
         */
598
 
        return 1;
599
 
}
600
 
 
601
 
 
602
 
static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len)
603
 
{
604
 
        struct eap_hdr *hdr;
605
 
        size_t plen;
606
 
 
607
 
        /* parse rxResp, respId, respMethod */
608
 
        sm->rxResp = FALSE;
609
 
        sm->respId = -1;
610
 
        sm->respMethod = EAP_TYPE_NONE;
611
 
        sm->respVendor = EAP_VENDOR_IETF;
612
 
        sm->respVendorMethod = EAP_TYPE_NONE;
613
 
 
614
 
        if (resp == NULL || len < sizeof(*hdr))
615
 
                return;
616
 
 
617
 
        hdr = (struct eap_hdr *) resp;
618
 
        plen = be_to_host16(hdr->length);
619
 
        if (plen > len) {
620
 
                wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
621
 
                           "(len=%lu plen=%lu)", (unsigned long) len,
622
 
                           (unsigned long) plen);
623
 
                return;
624
 
        }
625
 
 
626
 
        sm->respId = hdr->identifier;
627
 
 
628
 
        if (hdr->code == EAP_CODE_RESPONSE)
629
 
                sm->rxResp = TRUE;
630
 
 
631
 
        if (plen > sizeof(*hdr)) {
632
 
                u8 *pos = (u8 *) (hdr + 1);
633
 
                sm->respMethod = *pos++;
634
 
                if (sm->respMethod == EAP_TYPE_EXPANDED) {
635
 
                        if (plen < sizeof(*hdr) + 8) {
636
 
                                wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
637
 
                                           "expanded EAP-Packet (plen=%lu)",
638
 
                                           (unsigned long) plen);
639
 
                                return;
640
 
                        }
641
 
                        sm->respVendor = WPA_GET_BE24(pos);
642
 
                        pos += 3;
643
 
                        sm->respVendorMethod = WPA_GET_BE32(pos);
644
 
                }
645
 
        }
646
 
 
647
 
        wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
648
 
                   "respMethod=%u respVendor=%u respVendorMethod=%u",
649
 
                   sm->rxResp, sm->respId, sm->respMethod, sm->respVendor,
650
 
                   sm->respVendorMethod);
651
 
}
652
 
 
653
 
 
654
 
static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len)
655
 
{
656
 
        struct eap_hdr *resp;
657
 
        wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id);
658
 
 
659
 
        *len = sizeof(*resp);
660
 
        resp = malloc(*len);
661
 
        if (resp == NULL)
662
 
                return NULL;
663
 
        resp->code = EAP_CODE_SUCCESS;
664
 
        resp->identifier = id;
665
 
        resp->length = host_to_be16(*len);
666
 
 
667
 
        return (u8 *) resp;
668
 
}
669
 
 
670
 
 
671
 
static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len)
672
 
{
673
 
        struct eap_hdr *resp;
674
 
        wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id);
675
 
 
676
 
        *len = sizeof(*resp);
677
 
        resp = malloc(*len);
678
 
        if (resp == NULL)
679
 
                return NULL;
680
 
        resp->code = EAP_CODE_FAILURE;
681
 
        resp->identifier = id;
682
 
        resp->length = host_to_be16(*len);
683
 
 
684
 
        return (u8 *) resp;
685
 
}
686
 
 
687
 
 
688
 
static int eap_sm_nextId(struct eap_sm *sm, int id)
689
 
{
690
 
        if (id < 0) {
691
 
                /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a
692
 
                 * random number */
693
 
                id = rand() & 0xff;
694
 
                if (id != sm->lastId)
695
 
                        return id;
696
 
        }
697
 
        return (id + 1) & 0xff;
698
 
}
699
 
 
700
 
 
701
 
/**
702
 
 * eap_sm_process_nak - Process EAP-Response/Nak
703
 
 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
704
 
 * @nak_list: Nak list (allowed methods) from the supplicant
705
 
 * @len: Length of nak_list in bytes
706
 
 *
707
 
 * This function is called when EAP-Response/Nak is received from the
708
 
 * supplicant. This can happen for both phase 1 and phase 2 authentications.
709
 
 */
710
 
void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len)
711
 
{
712
 
        int i;
713
 
        size_t j;
714
 
 
715
 
        if (sm->user == NULL)
716
 
                return;
717
 
 
718
 
        wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method "
719
 
                   "index %d)", sm->user_eap_method_index);
720
 
 
721
 
        wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods",
722
 
                    (u8 *) sm->user->methods,
723
 
                    EAP_MAX_METHODS * sizeof(sm->user->methods[0]));
724
 
        wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer",
725
 
                    nak_list, len);
726
 
 
727
 
        i = sm->user_eap_method_index;
728
 
        while (i < EAP_MAX_METHODS &&
729
 
               (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
730
 
                sm->user->methods[i].method != EAP_TYPE_NONE)) {
731
 
                if (sm->user->methods[i].vendor != EAP_VENDOR_IETF)
732
 
                        goto not_found;
733
 
                for (j = 0; j < len; j++) {
734
 
                        if (nak_list[j] == sm->user->methods[i].method) {
735
 
                                break;
736
 
                        }
737
 
                }
738
 
 
739
 
                if (j < len) {
740
 
                        /* found */
741
 
                        i++;
742
 
                        continue;
743
 
                }
744
 
 
745
 
        not_found:
746
 
                /* not found - remove from the list */
747
 
                memmove(&sm->user->methods[i], &sm->user->methods[i + 1],
748
 
                        (EAP_MAX_METHODS - i - 1) *
749
 
                        sizeof(sm->user->methods[0]));
750
 
                sm->user->methods[EAP_MAX_METHODS - 1].vendor =
751
 
                        EAP_VENDOR_IETF;
752
 
                sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE;
753
 
        }
754
 
 
755
 
        wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods",
756
 
                    (u8 *) sm->user->methods, EAP_MAX_METHODS *
757
 
                    sizeof(sm->user->methods[0]));
758
 
}
759
 
 
760
 
 
761
 
static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len)
762
 
{
763
 
        if (nak_list == NULL || sm == NULL || sm->user == NULL)
764
 
                return;
765
 
 
766
 
        if (sm->user->phase2) {
767
 
                wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user"
768
 
                           " info was selected - reject");
769
 
                sm->decision = DECISION_FAILURE;
770
 
                return;
771
 
        }
772
 
 
773
 
        eap_sm_process_nak(sm, nak_list, len);
774
 
}
775
 
 
776
 
 
777
 
static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor)
778
 
{
779
 
        EapType next;
780
 
        int idx = sm->user_eap_method_index;
781
 
 
782
 
        /* In theory, there should be no problems with starting
783
 
         * re-authentication with something else than EAP-Request/Identity and
784
 
         * this does indeed work with wpa_supplicant. However, at least Funk
785
 
         * Supplicant seemed to ignore re-auth if it skipped
786
 
         * EAP-Request/Identity.
787
 
         * Re-auth sets currentId == -1, so that can be used here to select
788
 
         * whether Identity needs to be requested again. */
789
 
        if (sm->identity == NULL || sm->currentId == -1) {
790
 
                *vendor = EAP_VENDOR_IETF;
791
 
                next = EAP_TYPE_IDENTITY;
792
 
                sm->update_user = TRUE;
793
 
        } else if (sm->user && idx < EAP_MAX_METHODS &&
794
 
                   (sm->user->methods[idx].vendor != EAP_VENDOR_IETF ||
795
 
                    sm->user->methods[idx].method != EAP_TYPE_NONE)) {
796
 
                *vendor = sm->user->methods[idx].vendor;
797
 
                next = sm->user->methods[idx].method;
798
 
                sm->user_eap_method_index++;
799
 
        } else {
800
 
                *vendor = EAP_VENDOR_IETF;
801
 
                next = EAP_TYPE_NONE;
802
 
        }
803
 
        wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d",
804
 
                   *vendor, next);
805
 
        return next;
806
 
}
807
 
 
808
 
 
809
 
static int eap_sm_Policy_getDecision(struct eap_sm *sm)
810
 
{
811
 
        if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY &&
812
 
            sm->m->isSuccess(sm, sm->eap_method_priv)) {
813
 
                wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> "
814
 
                           "SUCCESS");
815
 
                sm->update_user = TRUE;
816
 
                return DECISION_SUCCESS;
817
 
        }
818
 
 
819
 
        if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) &&
820
 
            !sm->m->isSuccess(sm, sm->eap_method_priv)) {
821
 
                wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> "
822
 
                           "FAILURE");
823
 
                sm->update_user = TRUE;
824
 
                return DECISION_FAILURE;
825
 
        }
826
 
 
827
 
        if ((sm->user == NULL || sm->update_user) && sm->identity) {
828
 
                if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) {
829
 
                        wpa_printf(MSG_DEBUG, "EAP: getDecision: user not "
830
 
                                   "found from database -> FAILURE");
831
 
                        return DECISION_FAILURE;
832
 
                }
833
 
                sm->update_user = FALSE;
834
 
        }
835
 
 
836
 
        if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
837
 
            (sm->user->methods[sm->user_eap_method_index].vendor !=
838
 
             EAP_VENDOR_IETF ||
839
 
             sm->user->methods[sm->user_eap_method_index].method !=
840
 
             EAP_TYPE_NONE)) {
841
 
                wpa_printf(MSG_DEBUG, "EAP: getDecision: another method "
842
 
                           "available -> CONTINUE");
843
 
                return DECISION_CONTINUE;
844
 
        }
845
 
 
846
 
        if (sm->identity == NULL || sm->currentId == -1) {
847
 
                wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
848
 
                           "yet -> CONTINUE");
849
 
                return DECISION_CONTINUE;
850
 
        }
851
 
 
852
 
        wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> "
853
 
                   "FAILURE");
854
 
        return DECISION_FAILURE;
855
 
}
856
 
 
857
 
 
858
 
static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method)
859
 
{
860
 
        return method == EAP_TYPE_IDENTITY ? TRUE : FALSE;
861
 
}
862
 
 
863
 
 
864
 
/**
865
 
 * eap_server_sm_step - Step EAP server state machine
866
 
 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
867
 
 * Returns: 1 if EAP state was changed or 0 if not
868
 
 *
869
 
 * This function advances EAP state machine to a new state to match with the
870
 
 * current variables. This should be called whenever variables used by the EAP
871
 
 * state machine have changed.
872
 
 */
873
 
int eap_server_sm_step(struct eap_sm *sm)
874
 
{
875
 
        int res = 0;
876
 
        do {
877
 
                sm->changed = FALSE;
878
 
                SM_STEP_RUN(EAP);
879
 
                if (sm->changed)
880
 
                        res = 1;
881
 
        } while (sm->changed);
882
 
        return res;
883
 
}
884
 
 
885
 
 
886
 
/**
887
 
 * eap_set_eapRespData - Set EAP response (eapRespData)
888
 
 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
889
 
 * @eapRespData: EAP-Response payload from the supplicant
890
 
 * @eapRespDataLen: Length of eapRespData in bytes
891
 
 *
892
 
 * This function is called when an EAP-Response is received from a supplicant.
893
 
 */
894
 
void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData,
895
 
                         size_t eapRespDataLen)
896
 
{
897
 
        if (sm == NULL)
898
 
                return;
899
 
        free(sm->eapRespData);
900
 
        sm->eapRespData = malloc(eapRespDataLen);
901
 
        if (sm->eapRespData == NULL)
902
 
                return;
903
 
        memcpy(sm->eapRespData, eapRespData, eapRespDataLen);
904
 
        sm->eapRespDataLen = eapRespDataLen;
905
 
        wpa_hexdump(MSG_MSGDUMP, "EAP: EAP-Response received",
906
 
                    eapRespData, eapRespDataLen);
907
 
}
908
 
 
909
 
 
910
 
static void eap_user_free(struct eap_user *user)
911
 
{
912
 
        if (user == NULL)
913
 
                return;
914
 
        free(user->password);
915
 
        user->password = NULL;
916
 
        free(user);
917
 
}
918
 
 
919
 
 
920
 
/**
921
 
 * eap_server_sm_init - Allocate and initialize EAP server state machine
922
 
 * @eapol_ctx: Context data to be used with eapol_cb calls
923
 
 * @eapol_cb: Pointer to EAPOL callback functions
924
 
 * @conf: EAP configuration
925
 
 * Returns: Pointer to the allocated EAP state machine or %NULL on failure
926
 
 *
927
 
 * This function allocates and initializes an EAP state machine.
928
 
 */
929
 
struct eap_sm * eap_server_sm_init(void *eapol_ctx,
930
 
                                   struct eapol_callbacks *eapol_cb,
931
 
                                   struct eap_config *conf)
932
 
{
933
 
        struct eap_sm *sm;
934
 
 
935
 
        sm = os_zalloc(sizeof(*sm));
936
 
        if (sm == NULL)
937
 
                return NULL;
938
 
        sm->eapol_ctx = eapol_ctx;
939
 
        sm->eapol_cb = eapol_cb;
940
 
        sm->MaxRetrans = 10;
941
 
        sm->ssl_ctx = conf->ssl_ctx;
942
 
        sm->eap_sim_db_priv = conf->eap_sim_db_priv;
943
 
        sm->backend_auth = conf->backend_auth;
944
 
 
945
 
        wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
946
 
 
947
 
        return sm;
948
 
}
949
 
 
950
 
 
951
 
/**
952
 
 * eap_server_sm_deinit - Deinitialize and free an EAP server state machine
953
 
 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
954
 
 *
955
 
 * This function deinitializes EAP state machine and frees all allocated
956
 
 * resources.
957
 
 */
958
 
void eap_server_sm_deinit(struct eap_sm *sm)
959
 
{
960
 
        if (sm == NULL)
961
 
                return;
962
 
        wpa_printf(MSG_DEBUG, "EAP: Server state machine removed");
963
 
        if (sm->m && sm->eap_method_priv)
964
 
                sm->m->reset(sm, sm->eap_method_priv);
965
 
        free(sm->eapReqData);
966
 
        free(sm->eapKeyData);
967
 
        free(sm->lastReqData);
968
 
        free(sm->eapRespData);
969
 
        free(sm->identity);
970
 
        eap_user_free(sm->user);
971
 
        free(sm);
972
 
}
973
 
 
974
 
 
975
 
/**
976
 
 * eap_sm_notify_cached - Notify EAP state machine of cached PMK
977
 
 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
978
 
 *
979
 
 * This function is called when PMKSA caching is used to skip EAP
980
 
 * authentication.
981
 
 */
982
 
void eap_sm_notify_cached(struct eap_sm *sm)
983
 
{
984
 
        if (sm == NULL)
985
 
                return;
986
 
 
987
 
        sm->EAP_state = EAP_SUCCESS;
988
 
}
989
 
 
990
 
 
991
 
/**
992
 
 * eap_sm_pending_cb - EAP state machine callback for a pending EAP request
993
 
 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
994
 
 *
995
 
 * This function is called when data for a pending EAP-Request is received.
996
 
 */
997
 
void eap_sm_pending_cb(struct eap_sm *sm)
998
 
{
999
 
        if (sm == NULL)
1000
 
                return;
1001
 
        wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received");
1002
 
        if (sm->method_pending == METHOD_PENDING_WAIT)
1003
 
                sm->method_pending = METHOD_PENDING_CONT;
1004
 
}
1005
 
 
1006
 
 
1007
 
/**
1008
 
 * eap_sm_method_pending - Query whether EAP method is waiting for pending data
1009
 
 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1010
 
 * Returns: 1 if method is waiting for pending data or 0 if not
1011
 
 */
1012
 
int eap_sm_method_pending(struct eap_sm *sm)
1013
 
{
1014
 
        if (sm == NULL)
1015
 
                return 0;
1016
 
        return sm->method_pending == METHOD_PENDING_WAIT;
1017
 
}