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.0
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->allowCanned)
195  {
196  //Switch to the SUCCESS state
198  }
199  else if(context->methodState != EAP_METHOD_STATE_CONT &&
200  ((context->rxFailure && context->decision != EAP_DECISION_UNCOND_SUCC) ||
201  (context->rxSuccess && context->decision == EAP_DECISION_FAIL)) &&
202  context->reqId == context->lastId)
203  {
204  //Switch to the FAILURE state
206  }
207  //Errata
208  else if(context->methodState != EAP_METHOD_STATE_CONT &&
209  context->rxFailure && context->lastId == EAP_LAST_ID_NONE &&
210  context->allowCanned)
211  {
212  //Switch to the FAILURE state
214  }
215  else
216  {
217  //Switch to the DISCARD state
219  }
220 
221  break;
222 
223  //METHOD state?
225  //Evaluate conditions for the current state
226  if(context->ignore)
227  {
228  //Switch to the DISCARD state
230  }
231  else if(context->methodState == EAP_METHOD_STATE_DONE &&
232  context->decision == EAP_DECISION_FAIL)
233  {
234  //Switch to the FAILURE state
236  }
237  else
238  {
239  //Switch to the SEND_RESPONSE state
241  }
242 
243  break;
244 
245  //GET_METHOD state?
247  //Evaluate conditions for the current state
248  if(context->selectedMethod == context->reqMethod)
249  {
250  //Switch to the METHOD state
252  }
253  else
254  {
255  //Switch to the SEND_RESPONSE state
257  }
258 
259  break;
260 
261  //IDENTITY state?
263  //Unconditional transition (UCT) to SEND_RESPONSE state
265  break;
266 
267  //NOTIFICATION state?
269  //Unconditional transition (UCT) to SEND_RESPONSE state
271  break;
272 
273  //RETRANSMIT state?
275  //Unconditional transition (UCT) to SEND_RESPONSE state
277  break;
278 
279  //DISCARD state?
281  //Unconditional transition (UCT) to IDLE state
283  break;
284 
285  //SEND_RESPONSE state?
287  //Unconditional transition (UCT) to IDLE state
289  break;
290 
291  //SUCCESS state?
293  //Final state indicating success
294  break;
295 
296  //FAILURE state?
298  //Final state indicating failure
299  break;
300 
301  //Invalid state?
302  default:
303  //Just for sanity
304  supplicantFsmError(context);
305  break;
306  }
307  }
308 }
309 
310 
311 /**
312  * @brief Update EAP peer state
313  * @param[in] context Pointer to the 802.1X supplicant context
314  * @param[in] newState New state to switch to
315  **/
316 
318  EapPeerState newState)
319 {
320  EapPeerState oldState;
321 
322  //Retrieve current state
323  oldState = context->eapPeerState;
324 
325  //Any state change?
326  if(newState != oldState)
327  {
328  //Dump the state transition
329  TRACE_DEBUG("EAP peer state machine %s -> %s\r\n",
332  }
333 
334  //Switch to the new state
335  context->eapPeerState = newState;
336 
337  //On entry to a state, the procedures defined for the state are executed
338  //exactly once (refer to RFC 4137, section 3.1)
339  switch(newState)
340  {
341  //DISABLED state?
343  //This state is reached whenever service from the lower layer is
344  //interrupted or unavailable
345  break;
346 
347  //INITIALIZE state?
349  //Initialize variables when the state machine is activated
350  context->selectedMethod = EAP_METHOD_TYPE_NONE;
351  context->methodState = EAP_METHOD_STATE_NONE;
352  context->allowNotifications = TRUE;
353  context->decision = EAP_DECISION_FAIL;
354  context->idleWhile = context->clientTimeout;
355  context->lastId = EAP_LAST_ID_NONE;
356  context->eapSuccess = FALSE;
357  context->eapFail = FALSE;
358  context->eapKeyData = NULL;
359  context->eapKeyAvailable = FALSE;
360  context->eapRestart = FALSE;
361  break;
362 
363  //IDLE state?
364  case EAP_PEER_STATE_IDLE:
365  //The state machine spends most of its time here, waiting for something
366  //to happen
367  break;
368 
369  //RECEIVED state?
371  //This state is entered when an EAP packet is received
372  eapParseReq(context);
373  break;
374 
375  //METHOD state?
377  //The method must decide whether to process the packet or to discard it
378  //silently (refer to RFC 4137, section 4.2)
379  context->ignore = eapCheckReq(context);
380 
381  //Check whether the packet should be processed or discarded
382  if(!context->ignore)
383  {
384  //The request from the authenticator is processed, and an appropriate
385  //response packet is built
386  eapProcessReq(context);
387  eapBuildResp(context);
388 
389  //Check whether EAP key is available
390  if(eapIsKeyAvailable(context))
391  {
392  context->eapKeyData = eapPeerGetKey(context);
393  }
394  }
395 
396  break;
397 
398  //GET_METHOD state?
400  //This state is entered when a request for a new type comes in. Either
401  //the correct method is started, or a Nak response is built
402  if(eapAllowMethod(context, context->reqMethod))
403  {
404  context->selectedMethod = context->reqMethod;
405  context->methodState = EAP_METHOD_STATE_INIT;
406  }
407  else
408  {
409  eapBuildNak(context);
410  }
411 
412  break;
413 
414  //IDENTITY state?
416  //Handle request for Identity method and build a response
417  eapProcessIdentity(context);
418  eapBuildIdentity(context);
419  break;
420 
421  //NOTIFICATION state?
423  //Handle request for Notification method and build a response
424  eapProcessNotify(context);
425  eapBuildNotify(context);
426  break;
427 
428  //RETRANSMIT state?
430  //Retransmit the previous response packet
431  context->eapRespData = context->lastRespData;
432  context->eapRespDataLen = context->lastRespDataLen;
433  break;
434 
435  //DISCARD state?
437  //The request was discarded and no response packet will be sent
438  context->eapReq = FALSE;
439  context->eapNoResp = TRUE;
440  break;
441 
442  //SEND_RESPONSE state?
444  //A response packet is ready to be sent
445  context->lastId = context->reqId;
446  context->lastRespData = context->eapRespData;
447  context->lastRespDataLen = context->eapRespDataLen;
448  context->eapReq = FALSE;
449  context->eapResp = TRUE;
450  context->idleWhile = context->clientTimeout;
451  break;
452 
453  //SUCCESS state?
455  //Valid EAP key?
456  if(context->eapKeyData != NULL)
457  {
458  context->eapKeyAvailable = TRUE;
459  }
460 
461  //Final state indicating success
462  context->eapSuccess = TRUE;
463  break;
464 
465  //FAILURE state?
467  //Final state indicating failure
468  context->eapFail = TRUE;
469  break;
470 
471  //Invalid state?
472  default:
473  //Just for sanity
474  break;
475  }
476 
477  //Any state change?
478  if(newState != oldState)
479  {
480  //Any registered callback?
481  if(context->eapPeerStateChangeCallback != NULL)
482  {
483  //Invoke user callback function
484  context->eapPeerStateChangeCallback(context, newState);
485  }
486  }
487 
488  //Check whether the port is enabled
489  if(context->portEnabled)
490  {
491  //The EAP peer state machine is busy
492  context->busy = TRUE;
493  }
494 }
495 
496 #endif
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
@ EAP_METHOD_TYPE_NOTIFICATION
Notification.
Definition: eap.h:167
@ EAP_METHOD_TYPE_NONE
None.
Definition: eap.h:165
@ EAP_METHOD_TYPE_IDENTITY
Identity.
Definition: eap.h:166
const char_t * eapGetParamName(uint_t value, const EapParamName *paramList, size_t paramListLen)
Convert a parameter to string representation.
Definition: eap_debug.c:219
Data logging functions for debugging purpose (EAP)
@ EAP_METHOD_STATE_NONE
void eapPeerInitFsm(SupplicantContext *context)
EAP peer state machine initialization.
Definition: eap_peer_fsm.c:70
void eapPeerChangeState(SupplicantContext *context, EapPeerState newState)
Update EAP peer state.
Definition: eap_peer_fsm.c:317
void eapPeerFsm(SupplicantContext *context)
EAP peer state machine implementation.
Definition: eap_peer_fsm.c:82
const EapParamName eapPeerStates[]
Definition: eap_peer_fsm.c:47
EAP peer state machine.
@ EAP_METHOD_STATE_INIT
Definition: eap_peer_fsm.h:77
@ EAP_METHOD_STATE_DONE
Definition: eap_peer_fsm.h:80
@ EAP_METHOD_STATE_CONT
Definition: eap_peer_fsm.h:78
@ EAP_DECISION_UNCOND_SUCC
Definition: eap_peer_fsm.h:92
@ EAP_DECISION_FAIL
Definition: eap_peer_fsm.h:90
EapPeerState
EAP peer states.
Definition: eap_peer_fsm.h:53
@ EAP_PEER_STATE_METHOD
Definition: eap_peer_fsm.h:58
@ EAP_PEER_STATE_GET_METHOD
Definition: eap_peer_fsm.h:59
@ EAP_PEER_STATE_RETRANSMIT
Definition: eap_peer_fsm.h:62
@ EAP_PEER_STATE_IDLE
Definition: eap_peer_fsm.h:56
@ EAP_PEER_STATE_SEND_RESPONSE
Definition: eap_peer_fsm.h:64
@ EAP_PEER_STATE_NOTIFICATION
Definition: eap_peer_fsm.h:61
@ EAP_PEER_STATE_IDENTITY
Definition: eap_peer_fsm.h:60
@ EAP_PEER_STATE_INITIALIZE
Definition: eap_peer_fsm.h:55
@ EAP_PEER_STATE_SUCCESS
Definition: eap_peer_fsm.h:65
@ EAP_PEER_STATE_DISCARD
Definition: eap_peer_fsm.h:63
@ EAP_PEER_STATE_FAILURE
Definition: eap_peer_fsm.h:66
@ EAP_PEER_STATE_DISABLED
Definition: eap_peer_fsm.h:54
@ EAP_PEER_STATE_RECEIVED
Definition: eap_peer_fsm.h:57
#define EAP_LAST_ID_NONE
Definition: eap_peer_fsm.h:40
void eapProcessReq(SupplicantContext *context)
Parse and process a request.
void eapProcessIdentity(SupplicantContext *context)
Process the contents of Identity request.
uint8_t * eapPeerGetKey(SupplicantContext *context)
Obtain key material for use by EAP or lower layers.
bool_t eapAllowMethod(SupplicantContext *context, EapMethodType method)
Check whether the specified EAP method is allowed.
bool_t eapIsKeyAvailable(SupplicantContext *context)
Check whether EAP key is available.
bool_t eapCheckReq(SupplicantContext *context)
Test for the validity of a message.
void eapBuildResp(SupplicantContext *context)
Create a response message.
void eapBuildIdentity(SupplicantContext *context)
Create the appropriate Identity response.
void eapParseReq(SupplicantContext *context)
Determine the code, identifier value, and type of the current request.
void eapBuildNotify(SupplicantContext *context)
Create the appropriate Notification response.
void eapProcessNotify(SupplicantContext *context)
Process the contents of Notification request.
void eapBuildNak(SupplicantContext *context)
Create a NAK response.
EAP peer state machine procedures.
#define arraysize(a)
Definition: os_port.h:71
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
Parameter value/name binding.
Definition: eap_debug.h:50
802.1X supplicant
#define SupplicantContext
Definition: supplicant.h:36
void supplicantFsmError(SupplicantContext *context)
Supplicant state machine error handler.
Supplicant state machine.
Helper functions for 802.1X supplicant.