LCOV - code coverage report
Current view: top level - eap_server - eap_server.c (source / functions) Hit Total Coverage
Test: hostapd hwsim test run 1401872338 Lines: 595 741 80.3 %
Date: 2014-06-04 Functions: 49 55 89.1 %

          Line data    Source code
       1             : /*
       2             :  * hostapd / EAP Full Authenticator state machine (RFC 4137)
       3             :  * Copyright (c) 2004-2007, 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             :  * This state machine is based on the full authenticator state machine defined
       9             :  * in RFC 4137. However, to support backend authentication in RADIUS
      10             :  * authentication server functionality, parts of backend authenticator (also
      11             :  * from RFC 4137) are mixed in. This functionality is enabled by setting
      12             :  * backend_auth configuration variable to TRUE.
      13             :  */
      14             : 
      15             : #include "includes.h"
      16             : 
      17             : #include "common.h"
      18             : #include "eap_i.h"
      19             : #include "state_machine.h"
      20             : #include "common/wpa_ctrl.h"
      21             : 
      22             : #define STATE_MACHINE_DATA struct eap_sm
      23             : #define STATE_MACHINE_DEBUG_PREFIX "EAP"
      24             : 
      25             : #define EAP_MAX_AUTH_ROUNDS 50
      26             : 
      27             : static void eap_user_free(struct eap_user *user);
      28             : 
      29             : 
      30             : /* EAP state machines are described in RFC 4137 */
      31             : 
      32             : static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
      33             :                                    int eapSRTT, int eapRTTVAR,
      34             :                                    int methodTimeout);
      35             : static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp);
      36             : static int eap_sm_getId(const struct wpabuf *data);
      37             : static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id);
      38             : static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id);
      39             : static int eap_sm_nextId(struct eap_sm *sm, int id);
      40             : static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
      41             :                                  size_t len);
      42             : static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor);
      43             : static int eap_sm_Policy_getDecision(struct eap_sm *sm);
      44             : static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
      45             : 
      46             : 
      47        5568 : static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src)
      48             : {
      49        5568 :         if (src == NULL)
      50           0 :                 return -1;
      51             : 
      52        5568 :         wpabuf_free(*dst);
      53        5568 :         *dst = wpabuf_dup(src);
      54        5568 :         return *dst ? 0 : -1;
      55             : }
      56             : 
      57             : 
      58         255 : static int eap_copy_data(u8 **dst, size_t *dst_len,
      59             :                          const u8 *src, size_t src_len)
      60             : {
      61         255 :         if (src == NULL)
      62           0 :                 return -1;
      63             : 
      64         255 :         os_free(*dst);
      65         255 :         *dst = os_malloc(src_len);
      66         255 :         if (*dst) {
      67         255 :                 os_memcpy(*dst, src, src_len);
      68         255 :                 *dst_len = src_len;
      69         255 :                 return 0;
      70             :         } else {
      71           0 :                 *dst_len = 0;
      72           0 :                 return -1;
      73             :         }
      74             : }
      75             : 
      76             : #define EAP_COPY(dst, src) \
      77             :         eap_copy_data((dst), (dst ## Len), (src), (src ## Len))
      78             : 
      79             : 
      80             : /**
      81             :  * eap_user_get - Fetch user information from the database
      82             :  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
      83             :  * @identity: Identity (User-Name) of the user
      84             :  * @identity_len: Length of identity in bytes
      85             :  * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user
      86             :  * Returns: 0 on success, or -1 on failure
      87             :  *
      88             :  * This function is used to fetch user information for EAP. The user will be
      89             :  * selected based on the specified identity. sm->user and
      90             :  * sm->user_eap_method_index are updated for the new user when a matching user
      91             :  * is found. sm->user can be used to get user information (e.g., password).
      92             :  */
      93         142 : int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
      94             :                  int phase2)
      95             : {
      96             :         struct eap_user *user;
      97             : 
      98         284 :         if (sm == NULL || sm->eapol_cb == NULL ||
      99         142 :             sm->eapol_cb->get_eap_user == NULL)
     100           0 :                 return -1;
     101             : 
     102         142 :         eap_user_free(sm->user);
     103         142 :         sm->user = NULL;
     104             : 
     105         142 :         user = os_zalloc(sizeof(*user));
     106         142 :         if (user == NULL)
     107           0 :             return -1;
     108             : 
     109         142 :         if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity,
     110             :                                        identity_len, phase2, user) != 0) {
     111           1 :                 eap_user_free(user);
     112           1 :                 return -1;
     113             :         }
     114             : 
     115         141 :         sm->user = user;
     116         141 :         sm->user_eap_method_index = 0;
     117             : 
     118         141 :         return 0;
     119             : }
     120             : 
     121             : 
     122        1028 : void eap_log_msg(struct eap_sm *sm, const char *fmt, ...)
     123             : {
     124             :         va_list ap;
     125             :         char *buf;
     126             :         int buflen;
     127             : 
     128        1028 :         if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->log_msg == NULL)
     129        2046 :                 return;
     130             : 
     131           5 :         va_start(ap, fmt);
     132           5 :         buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
     133           5 :         va_end(ap);
     134             : 
     135           5 :         buf = os_malloc(buflen);
     136           5 :         if (buf == NULL)
     137           0 :                 return;
     138           5 :         va_start(ap, fmt);
     139           5 :         vsnprintf(buf, buflen, fmt, ap);
     140           5 :         va_end(ap);
     141             : 
     142           5 :         sm->eapol_cb->log_msg(sm->eapol_ctx, buf);
     143             : 
     144           5 :         os_free(buf);
     145             : }
     146             : 
     147             : 
     148         672 : SM_STATE(EAP, DISABLED)
     149             : {
     150         672 :         SM_ENTRY(EAP, DISABLED);
     151         672 :         sm->num_rounds = 0;
     152         672 : }
     153             : 
     154             : 
     155         441 : SM_STATE(EAP, INITIALIZE)
     156             : {
     157         441 :         SM_ENTRY(EAP, INITIALIZE);
     158             : 
     159         441 :         if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) {
     160             :                 /*
     161             :                  * Need to allow internal Identity method to be used instead
     162             :                  * of passthrough at the beginning of reauthentication.
     163             :                  */
     164          11 :                 eap_server_clear_identity(sm);
     165             :         }
     166             : 
     167         441 :         sm->currentId = -1;
     168         441 :         sm->eap_if.eapSuccess = FALSE;
     169         441 :         sm->eap_if.eapFail = FALSE;
     170         441 :         sm->eap_if.eapTimeout = FALSE;
     171         441 :         os_free(sm->eap_if.eapKeyData);
     172         441 :         sm->eap_if.eapKeyData = NULL;
     173         441 :         sm->eap_if.eapKeyDataLen = 0;
     174         441 :         sm->eap_if.eapKeyAvailable = FALSE;
     175         441 :         sm->eap_if.eapRestart = FALSE;
     176             : 
     177             :         /*
     178             :          * This is not defined in RFC 4137, but method state needs to be
     179             :          * reseted here so that it does not remain in success state when
     180             :          * re-authentication starts.
     181             :          */
     182         441 :         if (sm->m && sm->eap_method_priv) {
     183          70 :                 sm->m->reset(sm, sm->eap_method_priv);
     184          70 :                 sm->eap_method_priv = NULL;
     185             :         }
     186         441 :         sm->m = NULL;
     187         441 :         sm->user_eap_method_index = 0;
     188             : 
     189         441 :         if (sm->backend_auth) {
     190           2 :                 sm->currentMethod = EAP_TYPE_NONE;
     191             :                 /* parse rxResp, respId, respMethod */
     192           2 :                 eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
     193           2 :                 if (sm->rxResp) {
     194           2 :                         sm->currentId = sm->respId;
     195             :                 }
     196             :         }
     197         441 :         sm->num_rounds = 0;
     198         441 :         sm->method_pending = METHOD_PENDING_NONE;
     199             : 
     200        2646 :         wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
     201        2646 :                 MACSTR, MAC2STR(sm->peer_addr));
     202         441 : }
     203             : 
     204             : 
     205           2 : SM_STATE(EAP, PICK_UP_METHOD)
     206             : {
     207           2 :         SM_ENTRY(EAP, PICK_UP_METHOD);
     208             : 
     209           2 :         if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) {
     210           2 :                 sm->currentMethod = sm->respMethod;
     211           2 :                 if (sm->m && sm->eap_method_priv) {
     212           0 :                         sm->m->reset(sm, sm->eap_method_priv);
     213           0 :                         sm->eap_method_priv = NULL;
     214             :                 }
     215           2 :                 sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF,
     216             :                                                   sm->currentMethod);
     217           2 :                 if (sm->m && sm->m->initPickUp) {
     218           2 :                         sm->eap_method_priv = sm->m->initPickUp(sm);
     219           4 :                         if (sm->eap_method_priv == NULL) {
     220           0 :                                 wpa_printf(MSG_DEBUG, "EAP: Failed to "
     221             :                                            "initialize EAP method %d",
     222           0 :                                            sm->currentMethod);
     223           0 :                                 sm->m = NULL;
     224           0 :                                 sm->currentMethod = EAP_TYPE_NONE;
     225             :                         }
     226             :                 } else {
     227           0 :                         sm->m = NULL;
     228           0 :                         sm->currentMethod = EAP_TYPE_NONE;
     229             :                 }
     230             :         }
     231             : 
     232           2 :         wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
     233           2 :                 "method=%u", sm->currentMethod);
     234           2 : }
     235             : 
     236             : 
     237        1025 : SM_STATE(EAP, IDLE)
     238             : {
     239        1025 :         SM_ENTRY(EAP, IDLE);
     240             : 
     241        1025 :         sm->eap_if.retransWhile = eap_sm_calculateTimeout(
     242             :                 sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
     243             :                 sm->methodTimeout);
     244        1025 : }
     245             : 
     246             : 
     247           0 : SM_STATE(EAP, RETRANSMIT)
     248             : {
     249           0 :         SM_ENTRY(EAP, RETRANSMIT);
     250             : 
     251           0 :         sm->retransCount++;
     252           0 :         if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
     253           0 :                 if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
     254           0 :                         sm->eap_if.eapReq = TRUE;
     255             :         }
     256           0 : }
     257             : 
     258             : 
     259        1018 : SM_STATE(EAP, RECEIVED)
     260             : {
     261        1018 :         SM_ENTRY(EAP, RECEIVED);
     262             : 
     263             :         /* parse rxResp, respId, respMethod */
     264        1018 :         eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
     265        1018 :         sm->num_rounds++;
     266        1018 : }
     267             : 
     268             : 
     269           1 : SM_STATE(EAP, DISCARD)
     270             : {
     271           1 :         SM_ENTRY(EAP, DISCARD);
     272           1 :         sm->eap_if.eapResp = FALSE;
     273           1 :         sm->eap_if.eapNoReq = TRUE;
     274           1 : }
     275             : 
     276             : 
     277        1024 : SM_STATE(EAP, SEND_REQUEST)
     278             : {
     279        1024 :         SM_ENTRY(EAP, SEND_REQUEST);
     280             : 
     281        1024 :         sm->retransCount = 0;
     282        1024 :         if (sm->eap_if.eapReqData) {
     283        1024 :                 if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
     284             :                 {
     285        1024 :                         sm->eap_if.eapResp = FALSE;
     286        1024 :                         sm->eap_if.eapReq = TRUE;
     287             :                 } else {
     288           0 :                         sm->eap_if.eapResp = FALSE;
     289           0 :                         sm->eap_if.eapReq = FALSE;
     290             :                 }
     291             :         } else {
     292           0 :                 wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData");
     293           0 :                 sm->eap_if.eapResp = FALSE;
     294           0 :                 sm->eap_if.eapReq = FALSE;
     295           0 :                 sm->eap_if.eapNoReq = TRUE;
     296             :         }
     297        1024 : }
     298             : 
     299             : 
     300        1008 : SM_STATE(EAP, INTEGRITY_CHECK)
     301             : {
     302        1008 :         SM_ENTRY(EAP, INTEGRITY_CHECK);
     303             : 
     304        1008 :         if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) {
     305           0 :                 sm->ignore = TRUE;
     306        1008 :                 return;
     307             :         }
     308             : 
     309        1008 :         if (sm->m->check) {
     310        1008 :                 sm->ignore = sm->m->check(sm, sm->eap_method_priv,
     311             :                                           sm->eap_if.eapRespData);
     312             :         }
     313             : }
     314             : 
     315             : 
     316        1024 : SM_STATE(EAP, METHOD_REQUEST)
     317             : {
     318        1024 :         SM_ENTRY(EAP, METHOD_REQUEST);
     319             : 
     320        1024 :         if (sm->m == NULL) {
     321           0 :                 wpa_printf(MSG_DEBUG, "EAP: method not initialized");
     322        1024 :                 return;
     323             :         }
     324             : 
     325        1024 :         sm->currentId = eap_sm_nextId(sm, sm->currentId);
     326        1024 :         wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d",
     327             :                    sm->currentId);
     328        1024 :         sm->lastId = sm->currentId;
     329        1024 :         wpabuf_free(sm->eap_if.eapReqData);
     330        2048 :         sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv,
     331        1024 :                                                 sm->currentId);
     332        1024 :         if (sm->m->getTimeout)
     333         422 :                 sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv);
     334             :         else
     335         602 :                 sm->methodTimeout = 0;
     336             : }
     337             : 
     338             : 
     339        1040 : SM_STATE(EAP, METHOD_RESPONSE)
     340             : {
     341        1040 :         SM_ENTRY(EAP, METHOD_RESPONSE);
     342             : 
     343        1040 :         if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
     344        1040 :                 return;
     345             : 
     346        1040 :         sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData);
     347        1040 :         if (sm->m->isDone(sm, sm->eap_method_priv)) {
     348         558 :                 eap_sm_Policy_update(sm, NULL, 0);
     349         558 :                 os_free(sm->eap_if.eapKeyData);
     350         558 :                 if (sm->m->getKey) {
     351          39 :                         sm->eap_if.eapKeyData = sm->m->getKey(
     352             :                                 sm, sm->eap_method_priv,
     353             :                                 &sm->eap_if.eapKeyDataLen);
     354             :                 } else {
     355         519 :                         sm->eap_if.eapKeyData = NULL;
     356         519 :                         sm->eap_if.eapKeyDataLen = 0;
     357             :                 }
     358         558 :                 sm->methodState = METHOD_END;
     359             :         } else {
     360         482 :                 sm->methodState = METHOD_CONTINUE;
     361             :         }
     362             : }
     363             : 
     364             : 
     365         572 : SM_STATE(EAP, PROPOSE_METHOD)
     366             : {
     367             :         int vendor;
     368             :         EapType type;
     369             : 
     370         572 :         SM_ENTRY(EAP, PROPOSE_METHOD);
     371             : 
     372             : try_another_method:
     373         572 :         type = eap_sm_Policy_getNextMethod(sm, &vendor);
     374         572 :         if (vendor == EAP_VENDOR_IETF)
     375         489 :                 sm->currentMethod = type;
     376             :         else
     377          83 :                 sm->currentMethod = EAP_TYPE_EXPANDED;
     378         572 :         if (sm->m && sm->eap_method_priv) {
     379         124 :                 sm->m->reset(sm, sm->eap_method_priv);
     380         124 :                 sm->eap_method_priv = NULL;
     381             :         }
     382         572 :         sm->m = eap_server_get_eap_method(vendor, type);
     383         572 :         if (sm->m) {
     384         572 :                 sm->eap_method_priv = sm->m->init(sm);
     385         572 :                 if (sm->eap_method_priv == NULL) {
     386           0 :                         wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP "
     387           0 :                                    "method %d", sm->currentMethod);
     388           0 :                         sm->m = NULL;
     389           0 :                         sm->currentMethod = EAP_TYPE_NONE;
     390           0 :                         goto try_another_method;
     391             :                 }
     392             :         }
     393         572 :         if (sm->m == NULL) {
     394           0 :                 wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method");
     395           0 :                 eap_log_msg(sm, "Could not find suitable EAP method");
     396           0 :                 sm->decision = DECISION_FAILURE;
     397         572 :                 return;
     398             :         }
     399         705 :         if (sm->currentMethod == EAP_TYPE_IDENTITY ||
     400         133 :             sm->currentMethod == EAP_TYPE_NOTIFICATION)
     401         439 :                 sm->methodState = METHOD_CONTINUE;
     402             :         else
     403         133 :                 sm->methodState = METHOD_PROPOSED;
     404             : 
     405         572 :         wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
     406         572 :                 "vendor=%u method=%u", vendor, sm->currentMethod);
     407         572 :         eap_log_msg(sm, "Propose EAP method vendor=%u method=%u",
     408         572 :                     vendor, sm->currentMethod);
     409             : }
     410             : 
     411             : 
     412           9 : SM_STATE(EAP, NAK)
     413             : {
     414             :         const struct eap_hdr *nak;
     415           9 :         size_t len = 0;
     416             :         const u8 *pos;
     417           9 :         const u8 *nak_list = NULL;
     418             : 
     419           9 :         SM_ENTRY(EAP, NAK);
     420             : 
     421           9 :         if (sm->eap_method_priv) {
     422           9 :                 sm->m->reset(sm, sm->eap_method_priv);
     423           9 :                 sm->eap_method_priv = NULL;
     424             :         }
     425           9 :         sm->m = NULL;
     426             : 
     427           9 :         if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
     428           9 :                 return;
     429             : 
     430           9 :         nak = wpabuf_head(sm->eap_if.eapRespData);
     431           9 :         if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) {
     432           9 :                 len = be_to_host16(nak->length);
     433           9 :                 if (len > wpabuf_len(sm->eap_if.eapRespData))
     434           0 :                         len = wpabuf_len(sm->eap_if.eapRespData);
     435           9 :                 pos = (const u8 *) (nak + 1);
     436           9 :                 len -= sizeof(*nak);
     437           9 :                 if (*pos == EAP_TYPE_NAK) {
     438           9 :                         pos++;
     439           9 :                         len--;
     440           9 :                         nak_list = pos;
     441             :                 }
     442             :         }
     443           9 :         eap_sm_Policy_update(sm, nak_list, len);
     444             : }
     445             : 
     446             : 
     447        1006 : SM_STATE(EAP, SELECT_ACTION)
     448             : {
     449        1006 :         SM_ENTRY(EAP, SELECT_ACTION);
     450             : 
     451        1006 :         sm->decision = eap_sm_Policy_getDecision(sm);
     452        1006 : }
     453             : 
     454             : 
     455           0 : SM_STATE(EAP, TIMEOUT_FAILURE)
     456             : {
     457           0 :         SM_ENTRY(EAP, TIMEOUT_FAILURE);
     458             : 
     459           0 :         sm->eap_if.eapTimeout = TRUE;
     460           0 : }
     461             : 
     462             : 
     463          87 : SM_STATE(EAP, FAILURE)
     464             : {
     465          87 :         SM_ENTRY(EAP, FAILURE);
     466             : 
     467          87 :         wpabuf_free(sm->eap_if.eapReqData);
     468          87 :         sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId);
     469          87 :         wpabuf_free(sm->lastReqData);
     470          87 :         sm->lastReqData = NULL;
     471          87 :         sm->eap_if.eapFail = TRUE;
     472             : 
     473         522 :         wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
     474         522 :                 MACSTR, MAC2STR(sm->peer_addr));
     475          87 : }
     476             : 
     477             : 
     478          33 : SM_STATE(EAP, SUCCESS)
     479             : {
     480          33 :         SM_ENTRY(EAP, SUCCESS);
     481             : 
     482          33 :         wpabuf_free(sm->eap_if.eapReqData);
     483          33 :         sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId);
     484          33 :         wpabuf_free(sm->lastReqData);
     485          33 :         sm->lastReqData = NULL;
     486          33 :         if (sm->eap_if.eapKeyData)
     487          33 :                 sm->eap_if.eapKeyAvailable = TRUE;
     488          33 :         sm->eap_if.eapSuccess = TRUE;
     489             : 
     490         198 :         wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
     491         198 :                 MACSTR, MAC2STR(sm->peer_addr));
     492          33 : }
     493             : 
     494             : 
     495         314 : SM_STATE(EAP, INITIALIZE_PASSTHROUGH)
     496             : {
     497         314 :         SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH);
     498             : 
     499         314 :         wpabuf_free(sm->eap_if.aaaEapRespData);
     500         314 :         sm->eap_if.aaaEapRespData = NULL;
     501         314 : }
     502             : 
     503             : 
     504        1311 : SM_STATE(EAP, IDLE2)
     505             : {
     506        1311 :         SM_ENTRY(EAP, IDLE2);
     507             : 
     508        1311 :         sm->eap_if.retransWhile = eap_sm_calculateTimeout(
     509             :                 sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
     510             :                 sm->methodTimeout);
     511        1311 : }
     512             : 
     513             : 
     514           0 : SM_STATE(EAP, RETRANSMIT2)
     515             : {
     516           0 :         SM_ENTRY(EAP, RETRANSMIT2);
     517             : 
     518           0 :         sm->retransCount++;
     519           0 :         if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
     520           0 :                 if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
     521           0 :                         sm->eap_if.eapReq = TRUE;
     522             :         }
     523           0 : }
     524             : 
     525             : 
     526        1307 : SM_STATE(EAP, RECEIVED2)
     527             : {
     528        1307 :         SM_ENTRY(EAP, RECEIVED2);
     529             : 
     530             :         /* parse rxResp, respId, respMethod */
     531        1307 :         eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
     532        1307 : }
     533             : 
     534             : 
     535           0 : SM_STATE(EAP, DISCARD2)
     536             : {
     537           0 :         SM_ENTRY(EAP, DISCARD2);
     538           0 :         sm->eap_if.eapResp = FALSE;
     539           0 :         sm->eap_if.eapNoReq = TRUE;
     540           0 : }
     541             : 
     542             : 
     543        1311 : SM_STATE(EAP, SEND_REQUEST2)
     544             : {
     545        1311 :         SM_ENTRY(EAP, SEND_REQUEST2);
     546             : 
     547        1311 :         sm->retransCount = 0;
     548        1311 :         if (sm->eap_if.eapReqData) {
     549        1311 :                 if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
     550             :                 {
     551        1311 :                         sm->eap_if.eapResp = FALSE;
     552        1311 :                         sm->eap_if.eapReq = TRUE;
     553             :                 } else {
     554           0 :                         sm->eap_if.eapResp = FALSE;
     555           0 :                         sm->eap_if.eapReq = FALSE;
     556             :                 }
     557             :         } else {
     558           0 :                 wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData");
     559           0 :                 sm->eap_if.eapResp = FALSE;
     560           0 :                 sm->eap_if.eapReq = FALSE;
     561           0 :                 sm->eap_if.eapNoReq = TRUE;
     562             :         }
     563        1311 : }
     564             : 
     565             : 
     566        1621 : SM_STATE(EAP, AAA_REQUEST)
     567             : {
     568        1621 :         SM_ENTRY(EAP, AAA_REQUEST);
     569             : 
     570        1621 :         if (sm->eap_if.eapRespData == NULL) {
     571           0 :                 wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData");
     572        1621 :                 return;
     573             :         }
     574             : 
     575             :         /*
     576             :          * if (respMethod == IDENTITY)
     577             :          *      aaaIdentity = eapRespData
     578             :          * This is already taken care of by the EAP-Identity method which
     579             :          * stores the identity into sm->identity.
     580             :          */
     581             : 
     582        1621 :         eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData);
     583             : }
     584             : 
     585             : 
     586        1311 : SM_STATE(EAP, AAA_RESPONSE)
     587             : {
     588        1311 :         SM_ENTRY(EAP, AAA_RESPONSE);
     589             : 
     590        1311 :         eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
     591        1311 :         sm->currentId = eap_sm_getId(sm->eap_if.eapReqData);
     592        1311 :         sm->methodTimeout = sm->eap_if.aaaMethodTimeout;
     593        1311 : }
     594             : 
     595             : 
     596        1621 : SM_STATE(EAP, AAA_IDLE)
     597             : {
     598        1621 :         SM_ENTRY(EAP, AAA_IDLE);
     599             : 
     600        1621 :         sm->eap_if.aaaFail = FALSE;
     601        1621 :         sm->eap_if.aaaSuccess = FALSE;
     602        1621 :         sm->eap_if.aaaEapReq = FALSE;
     603        1621 :         sm->eap_if.aaaEapNoReq = FALSE;
     604        1621 :         sm->eap_if.aaaEapResp = TRUE;
     605        1621 : }
     606             : 
     607             : 
     608           0 : SM_STATE(EAP, TIMEOUT_FAILURE2)
     609             : {
     610           0 :         SM_ENTRY(EAP, TIMEOUT_FAILURE2);
     611             : 
     612           0 :         sm->eap_if.eapTimeout = TRUE;
     613           0 : }
     614             : 
     615             : 
     616          46 : SM_STATE(EAP, FAILURE2)
     617             : {
     618          46 :         SM_ENTRY(EAP, FAILURE2);
     619             : 
     620          46 :         eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
     621          46 :         sm->eap_if.eapFail = TRUE;
     622          46 : }
     623             : 
     624             : 
     625         255 : SM_STATE(EAP, SUCCESS2)
     626             : {
     627         255 :         SM_ENTRY(EAP, SUCCESS2);
     628             : 
     629         255 :         eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
     630             : 
     631         255 :         sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable;
     632         255 :         if (sm->eap_if.aaaEapKeyAvailable) {
     633         255 :                 EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData);
     634             :         } else {
     635           0 :                 os_free(sm->eap_if.eapKeyData);
     636           0 :                 sm->eap_if.eapKeyData = NULL;
     637           0 :                 sm->eap_if.eapKeyDataLen = 0;
     638             :         }
     639             : 
     640         255 :         sm->eap_if.eapSuccess = TRUE;
     641             : 
     642             :         /*
     643             :          * Start reauthentication with identity request even though we know the
     644             :          * previously used identity. This is needed to get reauthentication
     645             :          * started properly.
     646             :          */
     647         255 :         sm->start_reauth = TRUE;
     648         255 : }
     649             : 
     650             : 
     651       28331 : SM_STEP(EAP)
     652             : {
     653       28331 :         if (sm->eap_if.eapRestart && sm->eap_if.portEnabled)
     654         441 :                 SM_ENTER_GLOBAL(EAP, INITIALIZE);
     655       27890 :         else if (!sm->eap_if.portEnabled)
     656         672 :                 SM_ENTER_GLOBAL(EAP, DISABLED);
     657       27218 :         else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
     658           0 :                 if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
     659           0 :                         wpa_printf(MSG_DEBUG, "EAP: more than %d "
     660             :                                    "authentication rounds - abort",
     661             :                                    EAP_MAX_AUTH_ROUNDS);
     662           0 :                         sm->num_rounds++;
     663           0 :                         SM_ENTER_GLOBAL(EAP, FAILURE);
     664             :                 }
     665       27218 :         } else switch (sm->EAP_state) {
     666             :         case EAP_INITIALIZE:
     667         441 :                 if (sm->backend_auth) {
     668           2 :                         if (!sm->rxResp)
     669           0 :                                 SM_ENTER(EAP, SELECT_ACTION);
     670           4 :                         else if (sm->rxResp &&
     671           4 :                                  (sm->respMethod == EAP_TYPE_NAK ||
     672           2 :                                   (sm->respMethod == EAP_TYPE_EXPANDED &&
     673           0 :                                    sm->respVendor == EAP_VENDOR_IETF &&
     674           0 :                                    sm->respVendorMethod == EAP_TYPE_NAK)))
     675           0 :                                 SM_ENTER(EAP, NAK);
     676             :                         else
     677           2 :                                 SM_ENTER(EAP, PICK_UP_METHOD);
     678             :                 } else {
     679         439 :                         SM_ENTER(EAP, SELECT_ACTION);
     680             :                 }
     681         441 :                 break;
     682             :         case EAP_PICK_UP_METHOD:
     683           2 :                 if (sm->currentMethod == EAP_TYPE_NONE) {
     684           0 :                         SM_ENTER(EAP, SELECT_ACTION);
     685             :                 } else {
     686           2 :                         SM_ENTER(EAP, METHOD_RESPONSE);
     687             :                 }
     688           2 :                 break;
     689             :         case EAP_DISABLED:
     690           0 :                 if (sm->eap_if.portEnabled)
     691           0 :                         SM_ENTER(EAP, INITIALIZE);
     692           0 :                 break;
     693             :         case EAP_IDLE:
     694        4255 :                 if (sm->eap_if.retransWhile == 0)
     695           0 :                         SM_ENTER(EAP, RETRANSMIT);
     696        4255 :                 else if (sm->eap_if.eapResp)
     697        1018 :                         SM_ENTER(EAP, RECEIVED);
     698        4255 :                 break;
     699             :         case EAP_RETRANSMIT:
     700           0 :                 if (sm->retransCount > sm->MaxRetrans)
     701           0 :                         SM_ENTER(EAP, TIMEOUT_FAILURE);
     702             :                 else
     703           0 :                         SM_ENTER(EAP, IDLE);
     704           0 :                 break;
     705             :         case EAP_RECEIVED:
     706        2035 :                 if (sm->rxResp && (sm->respId == sm->currentId) &&
     707        2025 :                     (sm->respMethod == EAP_TYPE_NAK ||
     708        1428 :                      (sm->respMethod == EAP_TYPE_EXPANDED &&
     709         420 :                       sm->respVendor == EAP_VENDOR_IETF &&
     710           0 :                       sm->respVendorMethod == EAP_TYPE_NAK))
     711           9 :                     && (sm->methodState == METHOD_PROPOSED))
     712           9 :                         SM_ENTER(EAP, NAK);
     713        2017 :                 else if (sm->rxResp && (sm->respId == sm->currentId) &&
     714        1008 :                          ((sm->respMethod == sm->currentMethod) ||
     715           0 :                           (sm->respMethod == EAP_TYPE_EXPANDED &&
     716           0 :                            sm->respVendor == EAP_VENDOR_IETF &&
     717           0 :                            sm->respVendorMethod == sm->currentMethod)))
     718        1008 :                         SM_ENTER(EAP, INTEGRITY_CHECK);
     719             :                 else {
     720           2 :                         wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: "
     721             :                                    "rxResp=%d respId=%d currentId=%d "
     722             :                                    "respMethod=%d currentMethod=%d",
     723           1 :                                    sm->rxResp, sm->respId, sm->currentId,
     724           1 :                                    sm->respMethod, sm->currentMethod);
     725           1 :                         eap_log_msg(sm, "Discard received EAP message");
     726           1 :                         SM_ENTER(EAP, DISCARD);
     727             :                 }
     728        1018 :                 break;
     729             :         case EAP_DISCARD:
     730           1 :                 SM_ENTER(EAP, IDLE);
     731           1 :                 break;
     732             :         case EAP_SEND_REQUEST:
     733        1024 :                 SM_ENTER(EAP, IDLE);
     734        1024 :                 break;
     735             :         case EAP_INTEGRITY_CHECK:
     736        1008 :                 if (sm->ignore)
     737           0 :                         SM_ENTER(EAP, DISCARD);
     738             :                 else
     739        1008 :                         SM_ENTER(EAP, METHOD_RESPONSE);
     740        1008 :                 break;
     741             :         case EAP_METHOD_REQUEST:
     742        1024 :                 if (sm->m == NULL) {
     743             :                         /*
     744             :                          * This transition is not mentioned in RFC 4137, but it
     745             :                          * is needed to handle cleanly a case where EAP method
     746             :                          * initialization fails.
     747             :                          */
     748           0 :                         SM_ENTER(EAP, FAILURE);
     749           0 :                         break;
     750             :                 }
     751        1024 :                 SM_ENTER(EAP, SEND_REQUEST);
     752        1024 :                 break;
     753             :         case EAP_METHOD_RESPONSE:
     754             :                 /*
     755             :                  * Note: Mechanism to allow EAP methods to wait while going
     756             :                  * through pending processing is an extension to RFC 4137
     757             :                  * which only defines the transits to SELECT_ACTION and
     758             :                  * METHOD_REQUEST from this METHOD_RESPONSE state.
     759             :                  */
     760        1105 :                 if (sm->methodState == METHOD_END)
     761         558 :                         SM_ENTER(EAP, SELECT_ACTION);
     762         547 :                 else if (sm->method_pending == METHOD_PENDING_WAIT) {
     763          65 :                         wpa_printf(MSG_DEBUG, "EAP: Method has pending "
     764             :                                    "processing - wait before proceeding to "
     765             :                                    "METHOD_REQUEST state");
     766         482 :                 } else if (sm->method_pending == METHOD_PENDING_CONT) {
     767          30 :                         wpa_printf(MSG_DEBUG, "EAP: Method has completed "
     768             :                                    "pending processing - reprocess pending "
     769             :                                    "EAP message");
     770          30 :                         sm->method_pending = METHOD_PENDING_NONE;
     771          30 :                         SM_ENTER(EAP, METHOD_RESPONSE);
     772             :                 } else
     773         452 :                         SM_ENTER(EAP, METHOD_REQUEST);
     774        1105 :                 break;
     775             :         case EAP_PROPOSE_METHOD:
     776             :                 /*
     777             :                  * Note: Mechanism to allow EAP methods to wait while going
     778             :                  * through pending processing is an extension to RFC 4137
     779             :                  * which only defines the transit to METHOD_REQUEST from this
     780             :                  * PROPOSE_METHOD state.
     781             :                  */
     782         572 :                 if (sm->method_pending == METHOD_PENDING_WAIT) {
     783           0 :                         wpa_printf(MSG_DEBUG, "EAP: Method has pending "
     784             :                                    "processing - wait before proceeding to "
     785             :                                    "METHOD_REQUEST state");
     786           0 :                         if (sm->user_eap_method_index > 0)
     787           0 :                                 sm->user_eap_method_index--;
     788         572 :                 } else if (sm->method_pending == METHOD_PENDING_CONT) {
     789           0 :                         wpa_printf(MSG_DEBUG, "EAP: Method has completed "
     790             :                                    "pending processing - reprocess pending "
     791             :                                    "EAP message");
     792           0 :                         sm->method_pending = METHOD_PENDING_NONE;
     793           0 :                         SM_ENTER(EAP, PROPOSE_METHOD);
     794             :                 } else
     795         572 :                         SM_ENTER(EAP, METHOD_REQUEST);
     796         572 :                 break;
     797             :         case EAP_NAK:
     798           9 :                 SM_ENTER(EAP, SELECT_ACTION);
     799           9 :                 break;
     800             :         case EAP_SELECT_ACTION:
     801        1006 :                 if (sm->decision == DECISION_FAILURE)
     802          87 :                         SM_ENTER(EAP, FAILURE);
     803         919 :                 else if (sm->decision == DECISION_SUCCESS)
     804          33 :                         SM_ENTER(EAP, SUCCESS);
     805         886 :                 else if (sm->decision == DECISION_PASSTHROUGH)
     806         314 :                         SM_ENTER(EAP, INITIALIZE_PASSTHROUGH);
     807             :                 else
     808         572 :                         SM_ENTER(EAP, PROPOSE_METHOD);
     809        1006 :                 break;
     810             :         case EAP_TIMEOUT_FAILURE:
     811           0 :                 break;
     812             :         case EAP_FAILURE:
     813          87 :                 break;
     814             :         case EAP_SUCCESS:
     815         141 :                 break;
     816             : 
     817             :         case EAP_INITIALIZE_PASSTHROUGH:
     818         314 :                 if (sm->currentId == -1)
     819           0 :                         SM_ENTER(EAP, AAA_IDLE);
     820             :                 else
     821         314 :                         SM_ENTER(EAP, AAA_REQUEST);
     822         314 :                 break;
     823             :         case EAP_IDLE2:
     824        3932 :                 if (sm->eap_if.eapResp)
     825        1307 :                         SM_ENTER(EAP, RECEIVED2);
     826        2625 :                 else if (sm->eap_if.retransWhile == 0)
     827           0 :                         SM_ENTER(EAP, RETRANSMIT2);
     828        3932 :                 break;
     829             :         case EAP_RETRANSMIT2:
     830           0 :                 if (sm->retransCount > sm->MaxRetrans)
     831           0 :                         SM_ENTER(EAP, TIMEOUT_FAILURE2);
     832             :                 else
     833           0 :                         SM_ENTER(EAP, IDLE2);
     834           0 :                 break;
     835             :         case EAP_RECEIVED2:
     836        1307 :                 if (sm->rxResp && (sm->respId == sm->currentId))
     837        1307 :                         SM_ENTER(EAP, AAA_REQUEST);
     838             :                 else
     839           0 :                         SM_ENTER(EAP, DISCARD2);
     840        1307 :                 break;
     841             :         case EAP_DISCARD2:
     842           0 :                 SM_ENTER(EAP, IDLE2);
     843           0 :                 break;
     844             :         case EAP_SEND_REQUEST2:
     845        1311 :                 SM_ENTER(EAP, IDLE2);
     846        1311 :                 break;
     847             :         case EAP_AAA_REQUEST:
     848        1621 :                 SM_ENTER(EAP, AAA_IDLE);
     849        1621 :                 break;
     850             :         case EAP_AAA_RESPONSE:
     851        1311 :                 SM_ENTER(EAP, SEND_REQUEST2);
     852        1311 :                 break;
     853             :         case EAP_AAA_IDLE:
     854        4872 :                 if (sm->eap_if.aaaFail)
     855          46 :                         SM_ENTER(EAP, FAILURE2);
     856        4826 :                 else if (sm->eap_if.aaaSuccess)
     857         255 :                         SM_ENTER(EAP, SUCCESS2);
     858        4571 :                 else if (sm->eap_if.aaaEapReq)
     859        1311 :                         SM_ENTER(EAP, AAA_RESPONSE);
     860        3260 :                 else if (sm->eap_if.aaaTimeout)
     861           0 :                         SM_ENTER(EAP, TIMEOUT_FAILURE2);
     862        4872 :                 break;
     863             :         case EAP_TIMEOUT_FAILURE2:
     864           0 :                 break;
     865             :         case EAP_FAILURE2:
     866          46 :                 break;
     867             :         case EAP_SUCCESS2:
     868         811 :                 break;
     869             :         }
     870       28331 : }
     871             : 
     872             : 
     873        2336 : static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
     874             :                                    int eapSRTT, int eapRTTVAR,
     875             :                                    int methodTimeout)
     876             : {
     877             :         int rto, i;
     878             : 
     879        2336 :         if (methodTimeout) {
     880             :                 /*
     881             :                  * EAP method (either internal or through AAA server, provided
     882             :                  * timeout hint. Use that as-is as a timeout for retransmitting
     883             :                  * the EAP request if no response is received.
     884             :                  */
     885         422 :                 wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
     886             :                            "(from EAP method hint)", methodTimeout);
     887         422 :                 return methodTimeout;
     888             :         }
     889             : 
     890             :         /*
     891             :          * RFC 3748 recommends algorithms described in RFC 2988 for estimation
     892             :          * of the retransmission timeout. This should be implemented once
     893             :          * round-trip time measurements are available. For nowm a simple
     894             :          * backoff mechanism is used instead if there are no EAP method
     895             :          * specific hints.
     896             :          *
     897             :          * SRTT = smoothed round-trip time
     898             :          * RTTVAR = round-trip time variation
     899             :          * RTO = retransmission timeout
     900             :          */
     901             : 
     902             :         /*
     903             :          * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for
     904             :          * initial retransmission and then double the RTO to provide back off
     905             :          * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3
     906             :          * modified RTOmax.
     907             :          */
     908        1914 :         rto = 3;
     909        1914 :         for (i = 0; i < retransCount; i++) {
     910           0 :                 rto *= 2;
     911           0 :                 if (rto >= 20) {
     912           0 :                         rto = 20;
     913           0 :                         break;
     914             :                 }
     915             :         }
     916             : 
     917        1914 :         wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
     918             :                    "(from dynamic back off; retransCount=%d)",
     919             :                    rto, retransCount);
     920             : 
     921        1914 :         return rto;
     922             : }
     923             : 
     924             : 
     925        2327 : static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
     926             : {
     927             :         const struct eap_hdr *hdr;
     928             :         size_t plen;
     929             : 
     930             :         /* parse rxResp, respId, respMethod */
     931        2327 :         sm->rxResp = FALSE;
     932        2327 :         sm->respId = -1;
     933        2327 :         sm->respMethod = EAP_TYPE_NONE;
     934        2327 :         sm->respVendor = EAP_VENDOR_IETF;
     935        2327 :         sm->respVendorMethod = EAP_TYPE_NONE;
     936             : 
     937        2327 :         if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) {
     938           0 :                 wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p "
     939             :                            "len=%lu", resp,
     940             :                            resp ? (unsigned long) wpabuf_len(resp) : 0);
     941           0 :                 return;
     942             :         }
     943             : 
     944        2327 :         hdr = wpabuf_head(resp);
     945        2327 :         plen = be_to_host16(hdr->length);
     946        2327 :         if (plen > wpabuf_len(resp)) {
     947           0 :                 wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
     948             :                            "(len=%lu plen=%lu)",
     949             :                            (unsigned long) wpabuf_len(resp),
     950             :                            (unsigned long) plen);
     951           0 :                 return;
     952             :         }
     953             : 
     954        2327 :         sm->respId = hdr->identifier;
     955             : 
     956        2327 :         if (hdr->code == EAP_CODE_RESPONSE)
     957        2327 :                 sm->rxResp = TRUE;
     958             : 
     959        2327 :         if (plen > sizeof(*hdr)) {
     960        2327 :                 u8 *pos = (u8 *) (hdr + 1);
     961        2327 :                 sm->respMethod = *pos++;
     962        2327 :                 if (sm->respMethod == EAP_TYPE_EXPANDED) {
     963         441 :                         if (plen < sizeof(*hdr) + 8) {
     964           0 :                                 wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
     965             :                                            "expanded EAP-Packet (plen=%lu)",
     966             :                                            (unsigned long) plen);
     967           0 :                                 return;
     968             :                         }
     969         441 :                         sm->respVendor = WPA_GET_BE24(pos);
     970         441 :                         pos += 3;
     971         441 :                         sm->respVendorMethod = WPA_GET_BE32(pos);
     972             :                 }
     973             :         }
     974             : 
     975        6981 :         wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
     976             :                    "respMethod=%u respVendor=%u respVendorMethod=%u",
     977        4654 :                    sm->rxResp, sm->respId, sm->respMethod, sm->respVendor,
     978             :                    sm->respVendorMethod);
     979             : }
     980             : 
     981             : 
     982        1311 : static int eap_sm_getId(const struct wpabuf *data)
     983             : {
     984             :         const struct eap_hdr *hdr;
     985             : 
     986        1311 :         if (data == NULL || wpabuf_len(data) < sizeof(*hdr))
     987           0 :                 return -1;
     988             : 
     989        1311 :         hdr = wpabuf_head(data);
     990        1311 :         wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier);
     991        1311 :         return hdr->identifier;
     992             : }
     993             : 
     994             : 
     995          33 : static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id)
     996             : {
     997             :         struct wpabuf *msg;
     998             :         struct eap_hdr *resp;
     999          33 :         wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id);
    1000             : 
    1001          33 :         msg = wpabuf_alloc(sizeof(*resp));
    1002          33 :         if (msg == NULL)
    1003           0 :                 return NULL;
    1004          33 :         resp = wpabuf_put(msg, sizeof(*resp));
    1005          33 :         resp->code = EAP_CODE_SUCCESS;
    1006          33 :         resp->identifier = id;
    1007          33 :         resp->length = host_to_be16(sizeof(*resp));
    1008             : 
    1009          33 :         return msg;
    1010             : }
    1011             : 
    1012             : 
    1013          87 : static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id)
    1014             : {
    1015             :         struct wpabuf *msg;
    1016             :         struct eap_hdr *resp;
    1017          87 :         wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id);
    1018             : 
    1019          87 :         msg = wpabuf_alloc(sizeof(*resp));
    1020          87 :         if (msg == NULL)
    1021           0 :                 return NULL;
    1022          87 :         resp = wpabuf_put(msg, sizeof(*resp));
    1023          87 :         resp->code = EAP_CODE_FAILURE;
    1024          87 :         resp->identifier = id;
    1025          87 :         resp->length = host_to_be16(sizeof(*resp));
    1026             : 
    1027          87 :         return msg;
    1028             : }
    1029             : 
    1030             : 
    1031        1024 : static int eap_sm_nextId(struct eap_sm *sm, int id)
    1032             : {
    1033        1024 :         if (id < 0) {
    1034             :                 /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a
    1035             :                  * random number */
    1036         439 :                 id = rand() & 0xff;
    1037         439 :                 if (id != sm->lastId)
    1038         438 :                         return id;
    1039             :         }
    1040         586 :         return (id + 1) & 0xff;
    1041             : }
    1042             : 
    1043             : 
    1044             : /**
    1045             :  * eap_sm_process_nak - Process EAP-Response/Nak
    1046             :  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
    1047             :  * @nak_list: Nak list (allowed methods) from the supplicant
    1048             :  * @len: Length of nak_list in bytes
    1049             :  *
    1050             :  * This function is called when EAP-Response/Nak is received from the
    1051             :  * supplicant. This can happen for both phase 1 and phase 2 authentications.
    1052             :  */
    1053          10 : void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len)
    1054             : {
    1055             :         int i;
    1056             :         size_t j;
    1057             : 
    1058          10 :         if (sm->user == NULL)
    1059          10 :                 return;
    1060             : 
    1061          10 :         wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method "
    1062             :                    "index %d)", sm->user_eap_method_index);
    1063             : 
    1064          10 :         wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods",
    1065          10 :                     (u8 *) sm->user->methods,
    1066             :                     EAP_MAX_METHODS * sizeof(sm->user->methods[0]));
    1067          10 :         wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer",
    1068             :                     nak_list, len);
    1069             : 
    1070          10 :         i = sm->user_eap_method_index;
    1071         142 :         while (i < EAP_MAX_METHODS &&
    1072         132 :                (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
    1073          66 :                 sm->user->methods[i].method != EAP_TYPE_NONE)) {
    1074          56 :                 if (sm->user->methods[i].vendor != EAP_VENDOR_IETF)
    1075           0 :                         goto not_found;
    1076         102 :                 for (j = 0; j < len; j++) {
    1077          56 :                         if (nak_list[j] == sm->user->methods[i].method) {
    1078          10 :                                 break;
    1079             :                         }
    1080             :                 }
    1081             : 
    1082          56 :                 if (j < len) {
    1083             :                         /* found */
    1084          10 :                         i++;
    1085          10 :                         continue;
    1086             :                 }
    1087             : 
    1088             :         not_found:
    1089             :                 /* not found - remove from the list */
    1090          46 :                 if (i + 1 < EAP_MAX_METHODS) {
    1091          46 :                         os_memmove(&sm->user->methods[i],
    1092             :                                    &sm->user->methods[i + 1],
    1093             :                                    (EAP_MAX_METHODS - i - 1) *
    1094             :                                    sizeof(sm->user->methods[0]));
    1095             :                 }
    1096          46 :                 sm->user->methods[EAP_MAX_METHODS - 1].vendor =
    1097             :                         EAP_VENDOR_IETF;
    1098          46 :                 sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE;
    1099             :         }
    1100             : 
    1101          10 :         wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods",
    1102          10 :                     (u8 *) sm->user->methods, EAP_MAX_METHODS *
    1103             :                     sizeof(sm->user->methods[0]));
    1104             : }
    1105             : 
    1106             : 
    1107         567 : static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
    1108             :                                  size_t len)
    1109             : {
    1110         567 :         if (nak_list == NULL || sm == NULL || sm->user == NULL)
    1111         558 :                 return;
    1112             : 
    1113           9 :         if (sm->user->phase2) {
    1114           0 :                 wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user"
    1115             :                            " info was selected - reject");
    1116           0 :                 sm->decision = DECISION_FAILURE;
    1117           0 :                 return;
    1118             :         }
    1119             : 
    1120           9 :         eap_sm_process_nak(sm, nak_list, len);
    1121             : }
    1122             : 
    1123             : 
    1124         572 : static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor)
    1125             : {
    1126             :         EapType next;
    1127         572 :         int idx = sm->user_eap_method_index;
    1128             : 
    1129             :         /* In theory, there should be no problems with starting
    1130             :          * re-authentication with something else than EAP-Request/Identity and
    1131             :          * this does indeed work with wpa_supplicant. However, at least Funk
    1132             :          * Supplicant seemed to ignore re-auth if it skipped
    1133             :          * EAP-Request/Identity.
    1134             :          * Re-auth sets currentId == -1, so that can be used here to select
    1135             :          * whether Identity needs to be requested again. */
    1136         572 :         if (sm->identity == NULL || sm->currentId == -1) {
    1137         439 :                 *vendor = EAP_VENDOR_IETF;
    1138         439 :                 next = EAP_TYPE_IDENTITY;
    1139         439 :                 sm->update_user = TRUE;
    1140         266 :         } else if (sm->user && idx < EAP_MAX_METHODS &&
    1141         183 :                    (sm->user->methods[idx].vendor != EAP_VENDOR_IETF ||
    1142          50 :                     sm->user->methods[idx].method != EAP_TYPE_NONE)) {
    1143         133 :                 *vendor = sm->user->methods[idx].vendor;
    1144         133 :                 next = sm->user->methods[idx].method;
    1145         133 :                 sm->user_eap_method_index++;
    1146             :         } else {
    1147           0 :                 *vendor = EAP_VENDOR_IETF;
    1148           0 :                 next = EAP_TYPE_NONE;
    1149             :         }
    1150         572 :         wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d",
    1151             :                    *vendor, next);
    1152         572 :         return next;
    1153             : }
    1154             : 
    1155             : 
    1156        1006 : static int eap_sm_Policy_getDecision(struct eap_sm *sm)
    1157             : {
    1158        1006 :         if (!sm->eap_server && sm->identity && !sm->start_reauth) {
    1159         314 :                 wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH");
    1160         314 :                 return DECISION_PASSTHROUGH;
    1161             :         }
    1162             : 
    1163         812 :         if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY &&
    1164         120 :             sm->m->isSuccess(sm, sm->eap_method_priv)) {
    1165          33 :                 wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> "
    1166             :                            "SUCCESS");
    1167          33 :                 sm->update_user = TRUE;
    1168          33 :                 return DECISION_SUCCESS;
    1169             :         }
    1170             : 
    1171         870 :         if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) &&
    1172         211 :             !sm->m->isSuccess(sm, sm->eap_method_priv)) {
    1173          87 :                 wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> "
    1174             :                            "FAILURE");
    1175          87 :                 sm->update_user = TRUE;
    1176          87 :                 return DECISION_FAILURE;
    1177             :         }
    1178             : 
    1179         696 :         if ((sm->user == NULL || sm->update_user) && sm->identity &&
    1180         124 :             !sm->start_reauth) {
    1181             :                 /*
    1182             :                  * Allow Identity method to be started once to allow identity
    1183             :                  * selection hint to be sent from the authentication server,
    1184             :                  * but prevent a loop of Identity requests by only allowing
    1185             :                  * this to happen once.
    1186             :                  */
    1187         124 :                 int id_req = 0;
    1188         128 :                 if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY &&
    1189           8 :                     sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
    1190           4 :                     sm->user->methods[0].method == EAP_TYPE_IDENTITY)
    1191           0 :                         id_req = 1;
    1192         124 :                 if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) {
    1193           0 :                         wpa_printf(MSG_DEBUG, "EAP: getDecision: user not "
    1194             :                                    "found from database -> FAILURE");
    1195           0 :                         return DECISION_FAILURE;
    1196             :                 }
    1197         124 :                 if (id_req && sm->user &&
    1198           0 :                     sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
    1199           0 :                     sm->user->methods[0].method == EAP_TYPE_IDENTITY) {
    1200           0 :                         wpa_printf(MSG_DEBUG, "EAP: getDecision: stop "
    1201             :                                    "identity request loop -> FAILURE");
    1202           0 :                         sm->update_user = TRUE;
    1203           0 :                         return DECISION_FAILURE;
    1204             :                 }
    1205         124 :                 sm->update_user = FALSE;
    1206             :         }
    1207         572 :         sm->start_reauth = FALSE;
    1208             : 
    1209         709 :         if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
    1210         137 :             (sm->user->methods[sm->user_eap_method_index].vendor !=
    1211          54 :              EAP_VENDOR_IETF ||
    1212          54 :              sm->user->methods[sm->user_eap_method_index].method !=
    1213             :              EAP_TYPE_NONE)) {
    1214         137 :                 wpa_printf(MSG_DEBUG, "EAP: getDecision: another method "
    1215             :                            "available -> CONTINUE");
    1216         137 :                 return DECISION_CONTINUE;
    1217             :         }
    1218             : 
    1219         435 :         if (sm->identity == NULL || sm->currentId == -1) {
    1220         435 :                 wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
    1221             :                            "yet -> CONTINUE");
    1222         435 :                 return DECISION_CONTINUE;
    1223             :         }
    1224             : 
    1225           0 :         wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> "
    1226             :                    "FAILURE");
    1227           0 :         return DECISION_FAILURE;
    1228             : }
    1229             : 
    1230             : 
    1231           2 : static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method)
    1232             : {
    1233           2 :         return method == EAP_TYPE_IDENTITY ? TRUE : FALSE;
    1234             : }
    1235             : 
    1236             : 
    1237             : /**
    1238             :  * eap_server_sm_step - Step EAP server state machine
    1239             :  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
    1240             :  * Returns: 1 if EAP state was changed or 0 if not
    1241             :  *
    1242             :  * This function advances EAP state machine to a new state to match with the
    1243             :  * current variables. This should be called whenever variables used by the EAP
    1244             :  * state machine have changed.
    1245             :  */
    1246       10809 : int eap_server_sm_step(struct eap_sm *sm)
    1247             : {
    1248       10809 :         int res = 0;
    1249             :         do {
    1250       28331 :                 sm->changed = FALSE;
    1251       28331 :                 SM_STEP_RUN(EAP);
    1252       28331 :                 if (sm->changed)
    1253       17522 :                         res = 1;
    1254       28331 :         } while (sm->changed);
    1255       10809 :         return res;
    1256             : }
    1257             : 
    1258             : 
    1259         526 : static void eap_user_free(struct eap_user *user)
    1260             : {
    1261         526 :         if (user == NULL)
    1262         910 :                 return;
    1263         142 :         os_free(user->password);
    1264         142 :         user->password = NULL;
    1265         142 :         os_free(user);
    1266             : }
    1267             : 
    1268             : 
    1269             : /**
    1270             :  * eap_server_sm_init - Allocate and initialize EAP server state machine
    1271             :  * @eapol_ctx: Context data to be used with eapol_cb calls
    1272             :  * @eapol_cb: Pointer to EAPOL callback functions
    1273             :  * @conf: EAP configuration
    1274             :  * Returns: Pointer to the allocated EAP state machine or %NULL on failure
    1275             :  *
    1276             :  * This function allocates and initializes an EAP state machine.
    1277             :  */
    1278         383 : struct eap_sm * eap_server_sm_init(void *eapol_ctx,
    1279             :                                    struct eapol_callbacks *eapol_cb,
    1280             :                                    struct eap_config *conf)
    1281             : {
    1282             :         struct eap_sm *sm;
    1283             : 
    1284         383 :         sm = os_zalloc(sizeof(*sm));
    1285         383 :         if (sm == NULL)
    1286           0 :                 return NULL;
    1287         383 :         sm->eapol_ctx = eapol_ctx;
    1288         383 :         sm->eapol_cb = eapol_cb;
    1289         383 :         sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */
    1290         383 :         sm->ssl_ctx = conf->ssl_ctx;
    1291         383 :         sm->msg_ctx = conf->msg_ctx;
    1292         383 :         sm->eap_sim_db_priv = conf->eap_sim_db_priv;
    1293         383 :         sm->backend_auth = conf->backend_auth;
    1294         383 :         sm->eap_server = conf->eap_server;
    1295         383 :         if (conf->pac_opaque_encr_key) {
    1296           1 :                 sm->pac_opaque_encr_key = os_malloc(16);
    1297           1 :                 if (sm->pac_opaque_encr_key) {
    1298           1 :                         os_memcpy(sm->pac_opaque_encr_key,
    1299             :                                   conf->pac_opaque_encr_key, 16);
    1300             :                 }
    1301             :         }
    1302         383 :         if (conf->eap_fast_a_id) {
    1303           1 :                 sm->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
    1304           1 :                 if (sm->eap_fast_a_id) {
    1305           1 :                         os_memcpy(sm->eap_fast_a_id, conf->eap_fast_a_id,
    1306             :                                   conf->eap_fast_a_id_len);
    1307           1 :                         sm->eap_fast_a_id_len = conf->eap_fast_a_id_len;
    1308             :                 }
    1309             :         }
    1310         383 :         if (conf->eap_fast_a_id_info)
    1311           1 :                 sm->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
    1312         383 :         sm->eap_fast_prov = conf->eap_fast_prov;
    1313         383 :         sm->pac_key_lifetime = conf->pac_key_lifetime;
    1314         383 :         sm->pac_key_refresh_time = conf->pac_key_refresh_time;
    1315         383 :         sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
    1316         383 :         sm->tnc = conf->tnc;
    1317         383 :         sm->wps = conf->wps;
    1318         383 :         if (conf->assoc_wps_ie)
    1319          83 :                 sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie);
    1320         383 :         if (conf->assoc_p2p_ie)
    1321           0 :                 sm->assoc_p2p_ie = wpabuf_dup(conf->assoc_p2p_ie);
    1322         383 :         if (conf->peer_addr)
    1323         381 :                 os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN);
    1324         383 :         sm->fragment_size = conf->fragment_size;
    1325         383 :         sm->pwd_group = conf->pwd_group;
    1326         383 :         sm->pbc_in_m1 = conf->pbc_in_m1;
    1327         383 :         sm->server_id = conf->server_id;
    1328         383 :         sm->server_id_len = conf->server_id_len;
    1329             : 
    1330             : #ifdef CONFIG_TESTING_OPTIONS
    1331         383 :         sm->tls_test_flags = conf->tls_test_flags;
    1332             : #endif /* CONFIG_TESTING_OPTIONS */
    1333             : 
    1334         383 :         wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
    1335             : 
    1336         383 :         return sm;
    1337             : }
    1338             : 
    1339             : 
    1340             : /**
    1341             :  * eap_server_sm_deinit - Deinitialize and free an EAP server state machine
    1342             :  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
    1343             :  *
    1344             :  * This function deinitializes EAP state machine and frees all allocated
    1345             :  * resources.
    1346             :  */
    1347         383 : void eap_server_sm_deinit(struct eap_sm *sm)
    1348             : {
    1349         383 :         if (sm == NULL)
    1350         383 :                 return;
    1351         383 :         wpa_printf(MSG_DEBUG, "EAP: Server state machine removed");
    1352         383 :         if (sm->m && sm->eap_method_priv)
    1353         371 :                 sm->m->reset(sm, sm->eap_method_priv);
    1354         383 :         wpabuf_free(sm->eap_if.eapReqData);
    1355         383 :         os_free(sm->eap_if.eapKeyData);
    1356         383 :         wpabuf_free(sm->lastReqData);
    1357         383 :         wpabuf_free(sm->eap_if.eapRespData);
    1358         383 :         os_free(sm->identity);
    1359         383 :         os_free(sm->pac_opaque_encr_key);
    1360         383 :         os_free(sm->eap_fast_a_id);
    1361         383 :         os_free(sm->eap_fast_a_id_info);
    1362         383 :         wpabuf_free(sm->eap_if.aaaEapReqData);
    1363         383 :         wpabuf_free(sm->eap_if.aaaEapRespData);
    1364         383 :         os_free(sm->eap_if.aaaEapKeyData);
    1365         383 :         eap_user_free(sm->user);
    1366         383 :         wpabuf_free(sm->assoc_wps_ie);
    1367         383 :         wpabuf_free(sm->assoc_p2p_ie);
    1368         383 :         os_free(sm);
    1369             : }
    1370             : 
    1371             : 
    1372             : /**
    1373             :  * eap_sm_notify_cached - Notify EAP state machine of cached PMK
    1374             :  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
    1375             :  *
    1376             :  * This function is called when PMKSA caching is used to skip EAP
    1377             :  * authentication.
    1378             :  */
    1379          13 : void eap_sm_notify_cached(struct eap_sm *sm)
    1380             : {
    1381          13 :         if (sm == NULL)
    1382          13 :                 return;
    1383             : 
    1384          13 :         sm->EAP_state = EAP_SUCCESS;
    1385             : }
    1386             : 
    1387             : 
    1388             : /**
    1389             :  * eap_sm_pending_cb - EAP state machine callback for a pending EAP request
    1390             :  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
    1391             :  *
    1392             :  * This function is called when data for a pending EAP-Request is received.
    1393             :  */
    1394          30 : void eap_sm_pending_cb(struct eap_sm *sm)
    1395             : {
    1396          30 :         if (sm == NULL)
    1397          30 :                 return;
    1398          30 :         wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received");
    1399          30 :         if (sm->method_pending == METHOD_PENDING_WAIT)
    1400          30 :                 sm->method_pending = METHOD_PENDING_CONT;
    1401             : }
    1402             : 
    1403             : 
    1404             : /**
    1405             :  * eap_sm_method_pending - Query whether EAP method is waiting for pending data
    1406             :  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
    1407             :  * Returns: 1 if method is waiting for pending data or 0 if not
    1408             :  */
    1409           0 : int eap_sm_method_pending(struct eap_sm *sm)
    1410             : {
    1411           0 :         if (sm == NULL)
    1412           0 :                 return 0;
    1413           0 :         return sm->method_pending == METHOD_PENDING_WAIT;
    1414             : }
    1415             : 
    1416             : 
    1417             : /**
    1418             :  * eap_get_identity - Get the user identity (from EAP-Response/Identity)
    1419             :  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
    1420             :  * @len: Buffer for returning identity length
    1421             :  * Returns: Pointer to the user identity or %NULL if not available
    1422             :  */
    1423         314 : const u8 * eap_get_identity(struct eap_sm *sm, size_t *len)
    1424             : {
    1425         314 :         *len = sm->identity_len;
    1426         314 :         return sm->identity;
    1427             : }
    1428             : 
    1429             : 
    1430             : /**
    1431             :  * eap_get_interface - Get pointer to EAP-EAPOL interface data
    1432             :  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
    1433             :  * Returns: Pointer to the EAP-EAPOL interface data
    1434             :  */
    1435         383 : struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm)
    1436             : {
    1437         383 :         return &sm->eap_if;
    1438             : }
    1439             : 
    1440             : 
    1441             : /**
    1442             :  * eap_server_clear_identity - Clear EAP identity information
    1443             :  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
    1444             :  *
    1445             :  * This function can be used to clear the EAP identity information in the EAP
    1446             :  * server context. This allows the EAP/Identity method to be used again after
    1447             :  * EAPOL-Start or EAPOL-Logoff.
    1448             :  */
    1449          85 : void eap_server_clear_identity(struct eap_sm *sm)
    1450             : {
    1451          85 :         os_free(sm->identity);
    1452          85 :         sm->identity = NULL;
    1453          85 : }

Generated by: LCOV version 1.10