eap_peer_fsm.c
Go to the documentation of this file.
1 /**
2  * @file eap_peer_fsm.c
3  * @brief EAP peer state machine
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2022-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneEAP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL SUPPLICANT_TRACE_LEVEL
33 
34 //Dependencies
35 #include "supplicant/supplicant.h"
38 #include "eap/eap_peer_fsm.h"
40 #include "eap/eap_debug.h"
41 #include "debug.h"
42 
43 //Check EAP library configuration
44 #if (EAP_SUPPORT == ENABLED)
45 
46 //EAP peer states
48 {
49  {EAP_PEER_STATE_DISABLED, "DISABLED"},
50  {EAP_PEER_STATE_INITIALIZE, "INITIALIZE"},
51  {EAP_PEER_STATE_IDLE, "IDLE"},
52  {EAP_PEER_STATE_RECEIVED, "RECEIVED"},
53  {EAP_PEER_STATE_METHOD, "METHOD"},
54  {EAP_PEER_STATE_GET_METHOD, "GET_METHOD"},
55  {EAP_PEER_STATE_IDENTITY, "IDENTITY"},
56  {EAP_PEER_STATE_NOTIFICATION, "NOTIFICATION"},
57  {EAP_PEER_STATE_RETRANSMIT, "RETRANSMIT"},
58  {EAP_PEER_STATE_DISCARD, "DISCARD"},
59  {EAP_PEER_STATE_SEND_RESPONSE, "SEND_RESPONSE"},
60  {EAP_PEER_STATE_SUCCESS, "SUCCESS"},
61  {EAP_PEER_STATE_FAILURE, "FAILURE"}
62 };
63 
64 
65 /**
66  * @brief EAP peer state machine initialization
67  * @param[in] context Pointer to the 802.1X supplicant context
68  **/
69 
71 {
72  //Enter initial state
74 }
75 
76 
77 /**
78  * @brief EAP peer state machine implementation
79  * @param[in] context Pointer to the 802.1X supplicant context
80  **/
81 
83 {
84  //A global transition can occur from any of the possible states
85  if(!context->portEnabled)
86  {
87  //Switch to the DISABLED state
89  }
90  else if(context->eapRestart && context->portEnabled)
91  {
92  //Switch to the INITIALIZE state
94  }
95  else
96  {
97  //All exit conditions for the state are evaluated continuously until one
98  //of the conditions is met (refer to RFC 4137, section 3.1)
99  switch(context->eapPeerState)
100  {
101  //DISABLED state?
103  //Immediate transition to INITIALIZE occurs when the port becomes
104  //enabled
105  if(context->portEnabled)
106  {
107  //Switch to the INITIALIZE state
109  }
110 
111  break;
112 
113  //INITIALIZE state?
115  //Unconditional transition (UCT) to IDLE state
117  break;
118 
119  //IDLE state?
120  case EAP_PEER_STATE_IDLE:
121  //The state machine spends most of its time here, waiting for something
122  //to happen
123  if(context->eapReq)
124  {
125  //Switch to the RECEIVED state
127  }
128  else if((context->altAccept && context->decision != EAP_DECISION_FAIL) ||
129  (context->idleWhile == 0 && context->decision == EAP_DECISION_UNCOND_SUCC))
130  {
131  //Switch to the SUCCESS state
133  }
134  else if(context->altReject ||
135  (context->idleWhile == 0 && context->decision != EAP_DECISION_UNCOND_SUCC) ||
136  (context->altAccept && context->methodState != EAP_METHOD_STATE_CONT &&
137  context->decision == EAP_DECISION_FAIL))
138  {
139  //Switch to the FAILURE state
141  }
142  else
143  {
144  //Just for sanity
145  }
146 
147  break;
148 
149  //RECEIVED state?
151  //Evaluate conditions for the current state
152  if(context->rxReq && context->reqId != context->lastId &&
153  context->reqMethod == context->selectedMethod &&
154  context->methodState != EAP_METHOD_STATE_DONE)
155  {
156  //Switch to the METHOD state
158  }
159  else if(context->rxReq && context->reqId != context->lastId &&
160  context->selectedMethod == EAP_METHOD_TYPE_NONE &&
161  context->reqMethod != EAP_METHOD_TYPE_IDENTITY &&
162  context->reqMethod != EAP_METHOD_TYPE_NOTIFICATION)
163  {
164  //Switch to the GET_METHOD state
166  }
167  else if(context->rxReq && context->reqId != context->lastId &&
168  context->selectedMethod == EAP_METHOD_TYPE_NONE &&
169  context->reqMethod == EAP_METHOD_TYPE_IDENTITY)
170  {
171  //Switch to the IDENTITY state
173  }
174  else if(context->rxReq && context->reqId != context->lastId &&
175  context->reqMethod == EAP_METHOD_TYPE_NOTIFICATION &&
176  context->allowNotifications)
177  {
178  //Switch to the NOTIFICATION state
180  }
181  else if(context->rxReq && context->reqId == context->lastId)
182  {
183  //Switch to the RETRANSMIT state
185  }
186  else if(context->rxSuccess && context->reqId == context->lastId &&
187  context->decision != EAP_DECISION_FAIL)
188  {
189  //Switch to the SUCCESS state
191  }
192  //Errata
193  else if(context->rxSuccess && context->lastId != EAP_LAST_ID_NONE &&
194  context->reqId == ((context->lastId + 1) % 256) &&
195  context->decision != EAP_DECISION_FAIL)
196  {
197  //Switch to the SUCCESS state
199  }
200  //Errata
201  else if(context->rxSuccess && context->lastId == EAP_LAST_ID_NONE &&
202  context->allowCanned)
203  {
204  //Switch to the SUCCESS state
206  }
207  else if(context->methodState != EAP_METHOD_STATE_CONT &&
208  ((context->rxFailure && context->decision != EAP_DECISION_UNCOND_SUCC) ||
209  (context->rxSuccess && context->decision == EAP_DECISION_FAIL)) &&
210  context->reqId == context->lastId)
211  {
212  //Switch to the FAILURE state
214  }
215  //Errata
216  else if(context->methodState != EAP_METHOD_STATE_CONT &&
217  ((context->rxFailure && context->decision != EAP_DECISION_UNCOND_SUCC) ||
218  (context->rxSuccess && context->decision == EAP_DECISION_FAIL)) &&
219  context->lastId != EAP_LAST_ID_NONE &&
220  context->reqId == ((context->lastId + 1) % 256))
221  {
222  //Switch to the FAILURE state
224  }
225  //Errata
226  else if(context->methodState != EAP_METHOD_STATE_CONT &&
227  context->rxFailure && context->lastId == EAP_LAST_ID_NONE &&
228  context->allowCanned)
229  {
230  //Switch to the FAILURE state
232  }
233  else
234  {
235  //Switch to the DISCARD state
237  }
238 
239  break;
240 
241  //METHOD state?
243  //Evaluate conditions for the current state
244  if(context->ignore)
245  {
246  //Switch to the DISCARD state
248  }
249  else if(context->methodState == EAP_METHOD_STATE_DONE &&
250  context->decision == EAP_DECISION_FAIL)
251  {
252  //Switch to the FAILURE state
254  }
255  else
256  {
257  //Switch to the SEND_RESPONSE state
259  }
260 
261  break;
262 
263  //GET_METHOD state?
265  //Evaluate conditions for the current state
266  if(context->selectedMethod == context->reqMethod)
267  {
268  //Switch to the METHOD state
270  }
271  else
272  {
273  //Switch to the SEND_RESPONSE state
275  }
276 
277  break;
278 
279  //IDENTITY state?
281  //Unconditional transition (UCT) to SEND_RESPONSE state
283  break;
284 
285  //NOTIFICATION state?
287  //Unconditional transition (UCT) to SEND_RESPONSE state
289  break;
290 
291  //RETRANSMIT state?
293  //Unconditional transition (UCT) to SEND_RESPONSE state
295  break;
296 
297  //DISCARD state?
299  //Unconditional transition (UCT) to IDLE state
301  break;
302 
303  //SEND_RESPONSE state?
305  //Unconditional transition (UCT) to IDLE state
307  break;
308 
309  //SUCCESS state?
311  //Final state indicating success
312  break;
313 
314  //FAILURE state?
316  //Final state indicating failure
317  break;
318 
319  //Invalid state?
320  default:
321  //Just for sanity
322  supplicantFsmError(context);
323  break;
324  }
325  }
326 }
327 
328 
329 /**
330  * @brief Update EAP peer state
331  * @param[in] context Pointer to the 802.1X supplicant context
332  * @param[in] newState New state to switch to
333  **/
334 
336  EapPeerState newState)
337 {
338  EapPeerState oldState;
339 
340  //Retrieve current state
341  oldState = context->eapPeerState;
342 
343  //Any state change?
344  if(newState != oldState)
345  {
346  //Dump the state transition
347  TRACE_DEBUG("EAP peer state machine %s -> %s\r\n",
350  }
351 
352  //Switch to the new state
353  context->eapPeerState = newState;
354 
355  //On entry to a state, the procedures defined for the state are executed
356  //exactly once (refer to RFC 4137, section 3.1)
357  switch(newState)
358  {
359  //DISABLED state?
361  //This state is reached whenever service from the lower layer is
362  //interrupted or unavailable
363  break;
364 
365  //INITIALIZE state?
367  //Initialize variables when the state machine is activated
368  context->selectedMethod = EAP_METHOD_TYPE_NONE;
369  context->methodState = EAP_METHOD_STATE_NONE;
370  context->allowNotifications = TRUE;
371  context->decision = EAP_DECISION_FAIL;
372  context->idleWhile = context->clientTimeout;
373  context->lastId = EAP_LAST_ID_NONE;
374  context->eapSuccess = FALSE;
375  context->eapFail = FALSE;
376  context->eapKeyData = NULL;
377  context->eapKeyAvailable = FALSE;
378  context->eapRestart = FALSE;
379  break;
380 
381  //IDLE state?
382  case EAP_PEER_STATE_IDLE:
383  //The state machine spends most of its time here, waiting for something
384  //to happen
385  break;
386 
387  //RECEIVED state?
389  //This state is entered when an EAP packet is received
390  eapParseReq(context);
391  break;
392 
393  //METHOD state?
395  //The method must decide whether to process the packet or to discard it
396  //silently (refer to RFC 4137, section 4.2)
397  context->ignore = eapCheckReq(context);
398 
399  //Check whether the packet should be processed or discarded
400  if(!context->ignore)
401  {
402  //The request from the authenticator is processed, and an appropriate
403  //response packet is built
404  eapProcessReq(context);
405  eapBuildResp(context);
406 
407  //Check whether EAP key is available
408  if(eapIsKeyAvailable(context))
409  {
410  context->eapKeyData = eapPeerGetKey(context);
411  }
412  }
413 
414  break;
415 
416  //GET_METHOD state?
418  //This state is entered when a request for a new type comes in. Either
419  //the correct method is started, or a Nak response is built
420  if(eapAllowMethod(context, context->reqMethod))
421  {
422  context->selectedMethod = context->reqMethod;
423  context->methodState = EAP_METHOD_STATE_INIT;
424  }
425  else
426  {
427  eapBuildNak(context);
428  }
429 
430  break;
431 
432  //IDENTITY state?
434  //Handle request for Identity method and build a response
435  eapProcessIdentity(context);
436  eapBuildIdentity(context);
437  break;
438 
439  //NOTIFICATION state?
441  //Handle request for Notification method and build a response
442  eapProcessNotify(context);
443  eapBuildNotify(context);
444  break;
445 
446  //RETRANSMIT state?
448  //Retransmit the previous response packet
449  context->eapRespData = context->lastRespData;
450  context->eapRespDataLen = context->lastRespDataLen;
451  break;
452 
453  //DISCARD state?
455  //The request was discarded and no response packet will be sent
456  context->eapReq = FALSE;
457  context->eapNoResp = TRUE;
458  break;
459 
460  //SEND_RESPONSE state?
462  //A response packet is ready to be sent
463  context->lastId = context->reqId;
464  context->lastRespData = context->eapRespData;
465  context->lastRespDataLen = context->eapRespDataLen;
466  context->eapReq = FALSE;
467  context->eapResp = TRUE;
468  context->idleWhile = context->clientTimeout;
469  break;
470 
471  //SUCCESS state?
473  //Valid EAP key?
474  if(context->eapKeyData != NULL)
475  {
476  context->eapKeyAvailable = TRUE;
477  }
478 
479  //Final state indicating success
480  context->eapSuccess = TRUE;
481  break;
482 
483  //FAILURE state?
485  //Final state indicating failure
486  context->eapFail = TRUE;
487  break;
488 
489  //Invalid state?
490  default:
491  //Just for sanity
492  break;
493  }
494 
495  //Any state change?
496  if(newState != oldState)
497  {
498  //Any registered callback?
499  if(context->eapPeerStateChangeCallback != NULL)
500  {
501  //Invoke user callback function
502  context->eapPeerStateChangeCallback(context, newState);
503  }
504  }
505 
506  //Check whether the port is enabled
507  if(context->portEnabled)
508  {
509  //The EAP peer state machine is busy
510  context->busy = TRUE;
511  }
512 }
513 
514 #endif
@ EAP_PEER_STATE_IDENTITY
Definition: eap_peer_fsm.h:60
void eapPeerChangeState(SupplicantContext *context, EapPeerState newState)
Update EAP peer state.
Definition: eap_peer_fsm.c:335
Supplicant state machine.
void supplicantFsmError(SupplicantContext *context)
Supplicant state machine error handler.
uint8_t * eapPeerGetKey(SupplicantContext *context)
Obtain key material for use by EAP or lower layers.
@ EAP_METHOD_TYPE_NONE
None.
Definition: eap.h:165
const char_t * eapGetParamName(uint_t value, const EapParamName *paramList, size_t paramListLen)
Convert a parameter to string representation.
Definition: eap_debug.c:219
802.1X supplicant
#define TRUE
Definition: os_port.h:50
#define SupplicantContext
Definition: supplicant.h:36
@ EAP_PEER_STATE_RECEIVED
Definition: eap_peer_fsm.h:57
void eapProcessIdentity(SupplicantContext *context)
Process the contents of Identity request.
@ EAP_PEER_STATE_DISABLED
Definition: eap_peer_fsm.h:54
#define EAP_LAST_ID_NONE
Definition: eap_peer_fsm.h:40
const EapParamName eapPeerStates[]
Definition: eap_peer_fsm.c:47
@ EAP_METHOD_STATE_INIT
Definition: eap_peer_fsm.h:77
@ EAP_PEER_STATE_FAILURE
Definition: eap_peer_fsm.h:66
@ EAP_PEER_STATE_IDLE
Definition: eap_peer_fsm.h:56
bool_t eapAllowMethod(SupplicantContext *context, EapMethodType method)
Check whether the specified EAP method is allowed.
void eapBuildResp(SupplicantContext *context)
Create a response message.
@ EAP_PEER_STATE_DISCARD
Definition: eap_peer_fsm.h:63
EAP peer state machine procedures.
void eapPeerFsm(SupplicantContext *context)
EAP peer state machine implementation.
Definition: eap_peer_fsm.c:82
void eapParseReq(SupplicantContext *context)
Determine the code, identifier value, and type of the current request.
@ EAP_PEER_STATE_NOTIFICATION
Definition: eap_peer_fsm.h:61
#define FALSE
Definition: os_port.h:46
@ EAP_METHOD_TYPE_IDENTITY
Identity.
Definition: eap.h:166
@ EAP_PEER_STATE_GET_METHOD
Definition: eap_peer_fsm.h:59
void eapBuildIdentity(SupplicantContext *context)
Create the appropriate Identity response.
@ EAP_DECISION_UNCOND_SUCC
Definition: eap_peer_fsm.h:92
void eapPeerInitFsm(SupplicantContext *context)
EAP peer state machine initialization.
Definition: eap_peer_fsm.c:70
@ EAP_PEER_STATE_INITIALIZE
Definition: eap_peer_fsm.h:55
void eapProcessNotify(SupplicantContext *context)
Process the contents of Notification request.
void eapBuildNotify(SupplicantContext *context)
Create the appropriate Notification response.
@ EAP_PEER_STATE_SUCCESS
Definition: eap_peer_fsm.h:65
@ EAP_METHOD_STATE_CONT
Definition: eap_peer_fsm.h:78
@ EAP_PEER_STATE_RETRANSMIT
Definition: eap_peer_fsm.h:62
#define TRACE_DEBUG(...)
Definition: debug.h:107
@ EAP_DECISION_FAIL
Definition: eap_peer_fsm.h:90
@ EAP_METHOD_TYPE_NOTIFICATION
Notification.
Definition: eap.h:167
Data logging functions for debugging purpose (EAP)
bool_t eapIsKeyAvailable(SupplicantContext *context)
Check whether EAP key is available.
@ EAP_PEER_STATE_METHOD
Definition: eap_peer_fsm.h:58
@ EAP_METHOD_STATE_DONE
Definition: eap_peer_fsm.h:80
void eapProcessReq(SupplicantContext *context)
Parse and process a request.
bool_t eapCheckReq(SupplicantContext *context)
Test for the validity of a message.
@ EAP_METHOD_STATE_NONE
Parameter value/name binding.
Definition: eap_debug.h:50
Debugging facilities.
@ EAP_PEER_STATE_SEND_RESPONSE
Definition: eap_peer_fsm.h:64
void eapBuildNak(SupplicantContext *context)
Create a NAK response.
Helper functions for 802.1X supplicant.
#define arraysize(a)
Definition: os_port.h:71
EAP peer state machine.
EapPeerState
EAP peer states.
Definition: eap_peer_fsm.h:53