authenticator_misc.c
Go to the documentation of this file.
1 /**
2  * @file authenticator_misc.c
3  * @brief Helper functions for 802.1X authenticator
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 AUTHENTICATOR_TRACE_LEVEL
33 
34 //Dependencies
39 #include "radius/radius.h"
41 #include "radius/radius_debug.h"
42 #include "eap/eap_debug.h"
43 #include "debug.h"
44 
45 //Check EAP library configuration
46 #if (AUTHENTICATOR_SUPPORT == ENABLED)
47 
48 //PAE group address (refer to IEEE Std 802.1X-2010, section 11.1.1)
49 static const MacAddr PAE_GROUP_ADDR = {{{0x01, 0x80, 0xC2, 0x00, 0x00, 0x03}}};
50 
51 
52 /**
53  * @brief Handle periodic operations
54  * @param[in] context Pointer to the 802.1X authenticator context
55  **/
56 
58 {
59  uint_t i;
60  bool_t macOpState;
62 
63  //Loop through the ports
64  for(i = 0; i < context->numPorts; i++)
65  {
66  //Point to the current port
67  port = &context->ports[i];
68 
69  //Poll link state
70  macOpState = authenticatorGetLinkState(port);
71 
72  //Link state change detected?
73  if(macOpState && !port->portEnabled)
74  {
75  //Session statistics for a port can be retained by the system until a
76  //new session begins on that port
77  port->sessionStats.sessionOctetsRx = 0;
78  port->sessionStats.sessionOctetsTx = 0;
79  port->sessionStats.sessionFramesRx = 0;
80  port->sessionStats.sessionFramesTx = 0;
81  port->sessionStats.sessionTime = 0;
82 
83  //The port is up
84  port->sessionStats.sessionTerminateCause =
86  }
87  else if(!macOpState && port->portEnabled)
88  {
89  //The port is down
90  port->sessionStats.sessionTerminateCause =
92  }
93  else if(macOpState)
94  {
95  //Duration of the session in seconds
96  port->sessionStats.sessionTime++;
97  }
98  else
99  {
100  //No link state change
101  }
102 
103  //The portEnabled variable is externally controlled. Its value reflects
104  //the operational state of the MAC service supporting the port
105  port->portEnabled = macOpState;
106 
107  //Timers are decremented once per second
109  authenticatorDecrementTimer(&port->quietWhile);
110  authenticatorDecrementTimer(&port->reAuthWhen);
111  authenticatorDecrementTimer(&port->retransWhile);
112  authenticatorDecrementTimer(&port->aaaRetransTimer);
113  }
114 
115  //Update authenticator state machines
116  authenticatorFsm(context);
117 
118  //Any registered callback?
119  if(context->tickCallback != NULL)
120  {
121  //Invoke user callback function
122  context->tickCallback(context);
123  }
124 }
125 
126 
127 /**
128  * @brief Port's MAC address generation
129  * @param[in] port Pointer to the port context
130  **/
131 
133 {
134  int_t i;
135  uint8_t c;
136  MacAddr *macAddr;
137  AuthenticatorContext *context;
138 
139  //Point to the 802.1X authenticator context
140  context = port->context;
141 
142  //Get the MAC address of the underlying network interface
143  macAddr = &context->interface->macAddr;
144 
145  //Retrieve port index
146  c = port->portIndex;
147 
148  //Generate a unique MAC address for the port
149  for(i = 5; i >= 0; i--)
150  {
151  //Generate current byte
152  port->macAddr.b[i] = macAddr->b[i] + c;
153 
154  //Propagate the carry if necessary
155  if(port->macAddr.b[i] < macAddr->b[i])
156  {
157  c = 1;
158  }
159  else
160  {
161  c = 0;
162  }
163  }
164 }
165 
166 
167 /**
168  * @brief Get link state
169  * @param[in] port Pointer to the port context
170  * @return Error code
171  **/
172 
174 {
175  bool_t linkState;
176  NetInterface *interface;
177 
178  //Point to the underlying network interface
179  interface = port->context->interface;
180 
181  //Valid switch driver?
182  if(interface->switchDriver != NULL &&
183  interface->switchDriver->getLinkState != NULL)
184  {
185  //Get exclusive access
187 
188  //Retrieve the link state of the specified port
189  linkState = interface->switchDriver->getLinkState(interface,
190  port->portIndex);
191 
192  //Release exclusive access
194  }
195  else
196  {
197  //Retrieve the link state of the network interface
198  linkState = interface->linkState;
199  }
200 
201  //Return link state
202  return linkState;
203 }
204 
205 
206 /**
207  * @brief Add the PAE group address to the static MAC table
208  * @param[in] context Pointer to the 802.1X authenticator context
209  * @return Error code
210  **/
211 
213 {
214  error_t error;
215  SwitchFdbEntry entry;
216  NetInterface *interface;
217 
218  //Initialize status code
219  error = NO_ERROR;
220 
221  //Point to the underlying network interface
222  interface = context->interface;
223 
224  //Get exclusive access
226 
227  //Valid switch driver?
228  if(interface->switchDriver != NULL &&
229  interface->switchDriver->addStaticFdbEntry != NULL)
230  {
231  //Format forwarding database entry
232  entry.macAddr = PAE_GROUP_ADDR;
233  entry.srcPort = 0;
235  entry.override = TRUE;
236 
237  //Update the static MAC table of the switch
238  error = interface->switchDriver->addStaticFdbEntry(interface, &entry);
239  }
240 
241  //Check status code
242  if(!error)
243  {
244  //Add the PAE group address to the MAC filter table
245  error = ethAcceptMacAddr(interface, &PAE_GROUP_ADDR);
246  }
247 
248  //Release exclusive access
250 
251  //Return status code
252  return error;
253 }
254 
255 
256 /**
257  * @brief Remove the PAE group address from the static MAC table
258  * @param[in] context Pointer to the 802.1X authenticator context
259  * @return Error code
260  **/
261 
263 {
264  error_t error;
265  SwitchFdbEntry entry;
266  NetInterface *interface;
267 
268  //Initialize status code
269  error = NO_ERROR;
270 
271  //Point to the underlying network interface
272  interface = context->interface;
273 
274  //Get exclusive access
276 
277  //Valid switch driver?
278  if(interface->switchDriver != NULL &&
279  interface->switchDriver->deleteStaticFdbEntry != NULL)
280  {
281  //Format forwarding database entry
282  entry.macAddr = PAE_GROUP_ADDR;
283  entry.srcPort = 0;
284  entry.destPorts = 0;
285  entry.override = FALSE;
286 
287  //Update the static MAC table of the switch
288  error = interface->switchDriver->deleteStaticFdbEntry(interface, &entry);
289  }
290 
291  //Check status code
292  if(!error)
293  {
294  //Remove the PAE group address to the MAC filter table
295  ethDropMacAddr(interface, &PAE_GROUP_ADDR);
296  }
297 
298  //Release exclusive access
300 
301  //Return status code
302  return error;
303 }
304 
305 
306 /**
307  * @brief Send EAPOL PDU
308  * @param[in] port Pointer to the port context
309  * @param[in] pdu Pointer to the PDU to be transmitted
310  * @param[in] length Length of the PDU, in bytes
311  * @return Error code
312  **/
313 
315  size_t length)
316 {
317  SocketMsg msg;
318 
319  //Point to the PDU to be transmitted
320  msg = SOCKET_DEFAULT_MSG;
321  msg.data = (uint8_t *) pdu;
322  msg.length = length;
323 
324  //The PAE group address is assigned specifically for use by EAPOL clients
325  //designed to maximize plug-and-play interoperability, and should be the
326  //default for those clients (refer to IEEE Std 802.1X-2010, section 11.1.1)
327  msg.destMacAddr = PAE_GROUP_ADDR;
328 
329  //The source address for each MAC service request used to transmit an EAPOL
330  //MPDU shall be an individual address associated with the service access
331  //point at which the request is made (refer to IEEE Std 802.1X-2010,
332  //section 11.1.2)
333  msg.srcMacAddr = port->macAddr;
334 
335  //All EAPOL MPDUs shall be identified using the PAE EtherType (refer to
336  //IEEE Std 802.1X-2010, section 11.1.4)
337  msg.ethType = ETH_TYPE_EAPOL;
338 
339 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
340  //Specify the egress port
341  msg.switchPort = port->portIndex;
342 #endif
343 
344  //Number of EAPOL frames of any type that have been transmitted
345  port->stats.eapolFramesTx++;
346 
347  //Send EAPOL MPDU
348  return socketSendMsg(port->context->peerSocket, &msg, 0);
349 }
350 
351 
352 /**
353  * @brief Process incoming EAPOL PDU
354  * @param[in] context Pointer to the 802.1X authenticator context
355  **/
356 
358 {
359  error_t error;
360  size_t length;
361  uint_t portIndex;
362  SocketMsg msg;
363  EapolPdu *pdu;
365 
366  //Point to the receive buffer
367  msg = SOCKET_DEFAULT_MSG;
368  msg.data = context->rxBuffer;
370 
371  //Receive EAPOL MPDU
372  error = socketReceiveMsg(context->peerSocket, &msg, 0);
373  //Failed to receive packet
374  if(error)
375  return;
376 
377 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
378  //Save the port number on which the EAPOL PDU was received
379  portIndex = MAX(msg.switchPort, 1);
380 #else
381  //The station has a single port
382  portIndex = 1;
383 #endif
384 
385  //The destination MAC address must the group address recognized by the
386  //receiving MSAP for the application scenario
387  if(!macCompAddr(&msg.destMacAddr, &PAE_GROUP_ADDR))
388  return;
389 
390  //The received MPDU must contain the PAE EtherType
391  if(msg.ethType != ETH_TYPE_EAPOL)
392  return;
393 
394  //Malformed EAPOL packet?
395  if(msg.length < sizeof(EapolPdu))
396  return;
397 
398  //Point to the EAPOL packet
399  pdu = (EapolPdu *) context->rxBuffer;
400 
401  //Debug message
402  TRACE_INFO("Port %" PRIu8 ": EAPOL packet received (%" PRIuSIZE " bytes)...\r\n",
403  portIndex, msg.length);
404 
405  //Dump EAPOL header contents for debugging purpose
407 
408  //Sanity check
409  if(portIndex > context->numPorts)
410  return;
411 
412  //Point to the port that matches the specified port index
413  port = &context->ports[portIndex - 1];
414 
415  //Malformed EAPOL packet?
416  if(msg.length < ntohs(pdu->packetBodyLen))
417  {
418  //Number of EAPOL frames that have been received by this authenticator
419  //in which the Packet Body Length field is invalid
420  port->stats.eapLengthErrorFramesRx++;
421 
422  //Exit immediately
423  return;
424  }
425 
426  //Any octets following the Packet Body field in the frame conveying the
427  //EAPOL PDU shall be ignored (refer to IEEE Std 802.1X-2004, section 11.4)
428  length = ntohs(pdu->packetBodyLen);
429 
430  //Number of valid EAPOL frames of any type that have been received
431  port->stats.eapolFramesRx++;
432  //Protocol version number carried in the most recently received EAPOL frame
433  port->stats.lastEapolFrameVersion = pdu->protocolVersion;
434 
435  //Save the MAC address of the supplicant
436  port->supplicantMacAddr = msg.srcMacAddr;
437 
438  //Check packet type
439  if(pdu->packetType == EAPOL_TYPE_EAP)
440  {
441  //Process incoming EAP packet
443  length);
444  }
445  else if(pdu->packetType == EAPOL_TYPE_START)
446  {
447  //Number of EAPOL Start frames that have been received
448  port->stats.eapolStartFramesRx++;
449 
450  //The eapolStart variable is set TRUE if an EAPOL PDU carrying a packet
451  //type of EAPOL-Start is received
452  port->eapolStart = TRUE;
453  }
454  else if(pdu->packetType == EAPOL_TYPE_LOGOFF)
455  {
456  //Number of EAPOL Logoff frames that have been received
457  port->stats.eapolLogoffFramesRx++;
458 
459  //The Logoff variable is set TRUE if an EAPOL PDU carrying a packet type
460  //of EAPOL-Logoff is received
461  port->eapolLogoff = TRUE;
462  }
463  else
464  {
465  //Number of EAPOL frames that have been received by this authenticator
466  //in which the frame type is not recognized
467  port->stats.invalidEapolFramesRx++;
468  }
469 }
470 
471 
472 /**
473  * @brief Process incoming EAP packet
474  * @param[in] port Pointer to the port context
475  * @param[in] packet Pointer to the received EAP packet
476  * @param[in] length Length of the packet, in bytes
477  **/
478 
480  const EapPacket *packet, size_t length)
481 {
482  //Malformed EAP packet?
483  if(length < sizeof(EapPacket))
484  return;
485 
486  //Debug message
487  TRACE_DEBUG("Port %" PRIu8 ": EAP packet received (%" PRIuSIZE " bytes)...\r\n",
488  port->portIndex, length);
489 
490  //Dump EAP header contents for debugging purpose
491  eapDumpHeader(packet);
492 
493  //A message with the Length field set to a value larger than the number of
494  //received octets must be silently discarded (refer to RFC 3748, section 4.1)
495  if(ntohs(packet->length) > length)
496  return;
497 
498  //Octets outside the range of the Length field should be treated as data
499  //link layer padding and must be ignored upon reception
500  length = ntohs(packet->length);
501 
502  //Based on the Code field, the EAP layer demultiplexes incoming EAP packets
503  //to the EAP peer and authenticator layers
504  if(packet->code == EAP_CODE_RESPONSE)
505  {
506  //Point to the EAP response
507  port->eapRespData = (uint8_t *) packet;
508  port->eapRespDataLen = length;
509 
510  //The eapolEap variable is set TRUE by an external entity if an EAPOL
511  //PDU carrying a Packet Type of EAP-Packet is received
512  port->eapolEap = TRUE;
513 
514  //Invoke EAP to perform whatever processing is needed
515  authenticatorFsm(port->context);
516  }
517  else
518  {
519  //Unless a host implements an EAP peer layer, EAP Request, Success and
520  //Failure packets will be silently discarded (refer to RFC 3748,
521  //section 2.3)
522  }
523 }
524 
525 
526 /**
527  * @brief Build RADIUS Access-Request packet
528  * @param[in] port Pointer to the port context
529  **/
530 
532 {
533  error_t error;
534  size_t i;
535  size_t n;
536  IpAddr ipAddr;
537  MacAddr macAddr;
538  RadiusPacket *packet;
539  AuthenticatorContext *context;
540  uint8_t buffer[32];
541 
542  //Point to the 802.1X authenticator context
543  context = port->context;
544 
545  //Total length of the RADIUS packet
546  port->aaaReqDataLen = 0;
547 
548  //The Request Authenticator value must be changed each time a new
549  //Identifier is used (refer to RFC 2865, section 4.1)
550  error = context->prngAlgo->read(context->prngContext,
551  port->reqAuthenticator, 16);
552  //Any error to report?
553  if(error)
554  return error;
555 
556  //Generate a new RADIUS packet identifier
557  port->aaaReqId = authenticatorGetNextRadiusId(context);
558 
559  //Point to the buffer where to format the RADIUS packet
560  packet = (RadiusPacket *) port->aaaReqData;
561 
562  //The Length field indicates the length of the packet including the Code,
563  //Identifier, Length, Authenticator and Attribute fields
564  n = sizeof(RadiusPacket);
565 
566  //Format RADIUS packet
567  packet->code = RADIUS_CODE_ACCESS_REQUEST;
568  packet->identifier = port->aaaReqId;
569  packet->length = htons(n);
570 
571  //The Authenticator field is 16 octets. This value is used to authenticate
572  //the reply from the RADIUS server (refer to RFC 2865, section 3)
573  osMemcpy(packet->authenticator, port->reqAuthenticator, 16);
574 
575  //The NAS must include the Type-Data field of the EAP-Response/Identity
576  //in the User-Name attribute in every subsequent Access-Request (refer to
577  //RFC 3579, section 2.1)
578  radiusAddAttribute(packet, RADIUS_ATTR_USER_NAME, port->aaaIdentity,
579  osStrlen(port->aaaIdentity));
580 
581  //The Service-Type attribute indicates the type of service the user has
582  //requested, or the type of service to be provided (refer to RFC 2865,
583  //section 5.6)
585 
586  //Add Service-Type attribute
588  sizeof(uint32_t));
589 
590  //The Framed-MTU attribute indicates the Maximum Transmission Unit to be
591  //configured for the user (refer to RFC 2865, section 5.12)
592  STORE32BE(EAP_MAX_FRAG_SIZE, buffer);
593 
594  //Add Framed-MTU attribute
596  sizeof(uint32_t));
597 
598  //Get exclusive access
600 
601  //Retrieve the IP address of the NAS
602  error = ipSelectSourceAddr(&context->serverInterface, &context->serverIpAddr,
603  &ipAddr);
604 
605  //Release exclusive access
607 
608  //Any error to report?
609  if(error)
610  return error;
611 
612  //Either NAS-Identifier, NAS-IP-Address or NAS-IPv6-Address attributes
613  //must be included (refer to RFC 3579, section 3)
614  if(ipAddr.length == sizeof(Ipv4Addr))
615  {
617  ipAddr.length);
618  }
619  else if(ipAddr.length == sizeof(Ipv6Addr))
620  {
622  ipAddr.length);
623  }
624  else
625  {
626  return ERROR_INVALID_ADDRESS;
627  }
628 
629  //The NAS-Port attribute indicates the physical port number of the NAS which
630  //is authenticating the user (refer to RFC 2865, section 5.5)
631  STORE32BE(port->portIndex, buffer);
632 
633  //Add NAS-Port attribute
634  radiusAddAttribute(packet, RADIUS_ATTR_NAS_PORT, buffer, sizeof(uint32_t));
635 
636  //The NAS-Port-Type attribute indicates the type of the physical port of
637  //the NAS which is authenticating the user. It can be used instead of or in
638  //addition to the NAS-Port attribute (refer to RFC 2865, section 5.41)
640 
641  //Add NAS-Port-Type attribute
643  sizeof(uint32_t));
644 
645  //The NAS-Port-Id attribute contains a text string which identifies the
646  //port of the NAS which is authenticating the user (refer to RFC 2869,
647  //section 5.17)
648  osSprintf((char_t *) buffer, "%s_%" PRIu8, context->interface->name,
649  port->portIndex);
650 
652  osStrlen((char_t *) buffer));
653 
654  //Retrieve the MAC address of the bridge
655  netGetMacAddr(context->serverInterface, &macAddr);
656  macAddrToString(&macAddr, (char_t *) buffer);
657 
658  //The Called-Station-Id attribute is used to store the bridge or access
659  //point MAC address in ASCII format (refer to RFC 3580, section 3.20)
661  osStrlen((char_t *) buffer));
662 
663  //Retrieve the MAC address of the supplicant
664  macAddrToString(&port->supplicantMacAddr, (char_t *) buffer);
665 
666  //The Calling-Station-Id attribute is used to store the supplicant MAC
667  //address in ASCII format (refer to RFC 3580, section 3.21)
669  osStrlen((char_t *) buffer));
670 
671  //Any State attribute received from previous Access-Challenge?
672  if(port->serverStateLen > 0)
673  {
674  //The NAS must include the State attribute unchanged in that
675  //Access-Request (refer to RFC 2865, section 5.24)
676  radiusAddAttribute(packet, RADIUS_ATTR_STATE, port->serverState,
677  port->serverStateLen);
678  }
679 
680  //The NAS places EAP messages received from the authenticating peer into
681  //one or more EAP-Message attributes and forwards them to the RADIUS server
682  //within an Access-Request message (refer to RFC 3579, section 3.1)
683  for(i = 0; i < port->eapRespDataLen; i += n)
684  {
685  //Each attribute can contain up to 253 octets of binary data
686  n = MIN(port->eapRespDataLen - i, RADIUS_MAX_ATTR_VALUE_LEN);
687 
688  //Make sure the buffer is large enough to hold the EAP-Message attribute
689  if((htons(packet->length) + sizeof(RadiusAttribute) + n) >
691  {
692  return ERROR_BUFFER_OVERFLOW;
693  }
694 
695  //If multiple EAP-Message attributes are contained within an Access-
696  //Request, they must be in order and they must be consecutive attributes
698  port->eapRespData + i, n);
699  }
700 
701  //When the checksum is calculated the signature string should be considered
702  //to be sixteen octets of zero (refer to RFC 2869, section 5.14)
703  osMemset(buffer, 0, MD5_DIGEST_SIZE);
704 
705  //Make sure the buffer is large enough to hold the Message-Authenticator
706  //attribute
707  if((htons(packet->length) + sizeof(RadiusAttribute) + MD5_DIGEST_SIZE) >
709  {
710  return ERROR_BUFFER_OVERFLOW;
711  }
712 
713  //Add Message-Authenticator attribute
716 
717  //Retrieve the total length of the RADIUS packet
718  n = htons(packet->length);
719 
720  //Transactions between the client and RADIUS server are authenticated through
721  //the use of a shared secret (refer to RFC 2865, section 1)
722  error = hmacInit(&context->hmacContext, MD5_HASH_ALGO, context->serverKey,
723  context->serverKeyLen);
724  //Any error to report?
725  if(error)
726  return error;
727 
728  //When present in an Access-Request packet, Message-Authenticator is an
729  //HMAC-MD5 hash of the entire Access-Request packet, including Type, ID,
730  //Length and Authenticator, using the shared secret as the key (refer to
731  //RFC 3579, section 3.2)
732  hmacUpdate(&context->hmacContext, port->aaaReqData, n);
733  hmacFinal(&context->hmacContext, buffer);
734 
735  //Copy the resulting HMAC-MD5 hash
736  osMemcpy(port->aaaReqData + n - MD5_DIGEST_SIZE, buffer, MD5_DIGEST_SIZE);
737 
738  //Save the total length of the RADIUS packet
739  port->aaaReqDataLen = n;
740  //Initialize retransmission counter
741  port->aaaRetransCount = 0;
742 
743  //Sucessful processing
744  return NO_ERROR;
745 }
746 
747 
748 /**
749  * @brief Send RADIUS Access-Request packet
750  * @param[in] port Pointer to the port context
751  **/
752 
754 {
755  error_t error;
756  SocketMsg msg;
757  AuthenticatorContext *context;
758 
759  //Initialize status code
760  error = NO_ERROR;
761 
762  //Point to the 802.1X authenticator context
763  context = port->context;
764 
765  //Valid RADIUS packet?
766  if(port->aaaReqDataLen > 0)
767  {
768  //Exactly one RADIUS packet is encapsulated in the UDP data field,
769  //where the UDP destination Port field indicates 1812 (refer to
770  //RFC 2865, section 3)
771  msg = SOCKET_DEFAULT_MSG;
772  msg.data = port->aaaReqData;
773  msg.length = port->aaaReqDataLen;
774  msg.destIpAddr = context->serverIpAddr;
775  msg.destPort = context->serverPort;
776 
777 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
778  //Specify the egress port
779  msg.switchPort = context->serverPortIndex;
780 #endif
781 
782  //Debug message
783  TRACE_INFO("Sending RADIUS packet (%" PRIuSIZE " bytes)...\r\n",
784  port->aaaReqDataLen);
785 
786  //Dump RADIUS header contents for debugging purpose
787  radiusDumpPacket((RadiusPacket *) port->aaaReqData, port->aaaReqDataLen);
788 
789  //Send UDP datagram
790  error = socketSendMsg(context->serverSocket, &msg, 0);
791 
792  //Increment retransmission counter
793  port->aaaRetransCount++;
794  //Set retransmission timeout
795  port->aaaRetransTimer = AUTHENTICATOR_RADIUS_TIMEOUT;
796  }
797 
798  //Return status code
799  return error;
800 }
801 
802 
803 /**
804  * @brief Process incoming RADIUS packet
805  * @param[in] context Pointer to the 802.1X authenticator context
806  **/
807 
809 {
810  error_t error;
811  uint_t i;
812  size_t n;
813  size_t length;
814  SocketMsg msg;
816  EapPacket *eapPacket;
817  const RadiusPacket *packet;
818  const RadiusAttribute *attribute;
819  Md5Context *md5Context;
820  HmacContext *hmacContext;
821  uint8_t digest[MD5_DIGEST_SIZE];
822 
823  //Point to the receive buffer
824  msg = SOCKET_DEFAULT_MSG;
825  msg.data = context->rxBuffer;
827 
828  //Receive EAPOL MPDU
829  error = socketReceiveMsg(context->serverSocket, &msg, 0);
830  //Failed to receive packet
831  if(error)
832  return;
833 
834  //Debug message
835  TRACE_INFO("RADIUS packet received (%" PRIuSIZE " bytes)...\r\n",
836  msg.length);
837 
838 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
839  //Check the port number on which the EAPOL PDU was received
840  if(msg.switchPort != context->serverPortIndex && context->serverPortIndex != 0)
841  return;
842 #endif
843 
844  //Ensure the source IP address matches the RADIUS server's IP address
845  if(!ipCompAddr(&msg.srcIpAddr, &context->serverIpAddr))
846  return;
847 
848  //The officially assigned port number for RADIUS is 1812 (refer to RFC 2865,
849  //section 3)
850  if(msg.srcPort != context->serverPort)
851  return;
852 
853  //Malformed RADIUS packet?
854  if(msg.length < sizeof(RadiusPacket))
855  return;
856 
857  //Point to the RADIUS packet
858  packet = (RadiusPacket *) context->rxBuffer;
859 
860  //If the packet is shorter than the Length field indicates, it must be
861  //silently discarded (refer to RFC 2865, section 3)
862  if(msg.length < ntohs(packet->length))
863  return;
864 
865  //Dump RADIUS header contents for debugging purpose
866  radiusDumpPacket(packet, ntohs(packet->length));
867 
868  //Octets outside the range of the Length field must be treated as padding
869  //and ignored on reception
870  length = ntohs(packet->length) - sizeof(RadiusPacket);
871 
872  //The RADIUS packet type is determined by the Code field
873  if(packet->code != RADIUS_CODE_ACCESS_ACCEPT &&
874  packet->code != RADIUS_CODE_ACCESS_REJECT &&
875  packet->code != RADIUS_CODE_ACCESS_CHALLENGE)
876  {
877  return;
878  }
879 
880  //The Identifier field aids in matching requests and replies
881  for(i = 0; i < context->numPorts; i++)
882  {
883  //Point to the current port
884  port = &context->ports[i];
885 
886  //The Identifier field is matched with a pending Access-Request
887  if(port->eapFullAuthState == EAP_FULL_AUTH_STATE_AAA_IDLE &&
888  !port->aaaEapResp)
889  {
890  //Matching identifier?
891  if(port->aaaReqId == packet->identifier)
892  {
893  break;
894  }
895  }
896  }
897 
898  //No matching request found?
899  if(i >= context->numPorts)
900  return;
901 
902  //Point to the MD5 context
903  md5Context = &context->hmacContext.hashContext.md5Context;
904  //Initialize MD5 calculation
905  md5Init(md5Context);
906 
907  //The Response Authenticator contains a one-way MD5 hash calculated over the
908  //RADIUS packet, beginning with the Code field, including the Identifier, the
909  //Length, the Request Authenticator field from the Access-Request packet, and
910  //the response Attributes, followed by the shared secret (refer to RFC 2865,
911  //section 3)
912  md5Update(md5Context, packet, 4);
913  md5Update(md5Context, port->reqAuthenticator, 16);
914  md5Update(md5Context, packet->attributes, length);
915  md5Update(md5Context, context->serverKey, context->serverKeyLen);
916  md5Final(md5Context, digest);
917 
918  //Debug message
919  TRACE_DEBUG("Calculated Response Authenticator:\r\n");
920  TRACE_DEBUG_ARRAY(" ", digest, MD5_DIGEST_SIZE);
921 
922  //The Response Authenticator field must contain the correct response for the
923  //pending Access-Request. Invalid packets are silently discarded
924  if(osMemcmp(digest, packet->authenticator, MD5_DIGEST_SIZE) != 0)
925  {
926  //Debug message
927  TRACE_WARNING("Invalid Response Authenticator value!\r\n");
928  //Exit immediately
929  return;
930  }
931 
932  //The Message-Authenticator attribute must be used to protect all
933  //Access-Request, Access-Challenge, Access-Accept, and Access-Reject
934  //packets containing an EAP-Message attribute (refer to RFC 3579,
935  //section 3.2)
937 
938  //Access-Challenge, Access-Accept, or Access-Reject packets including
939  //EAP-Message attribute(s) without a Message-Authenticator attribute should
940  //be silently discarded by the NAS (refer to RFC 3579, section 3.1)
941  if(attribute == NULL)
942  return;
943 
944  //Malformed Message-Authenticator attribute?
945  if(attribute->length != (sizeof(RadiusAttribute) + MD5_DIGEST_SIZE))
946  return;
947 
948  //Save the offset to the Message-Authenticator value
949  n = attribute->value - packet->attributes;
950 
951  //When the checksum is calculated the signature string should be considered
952  //to be sixteen octets of zero (refer to RFC 2869, section 5.14)
953  osMemset(digest, 0, MD5_DIGEST_SIZE);
954 
955  //Point to the HMAC context
956  hmacContext = &context->hmacContext;
957 
958  //Initialize HMAC-MD5 calculation
959  error = hmacInit(hmacContext, MD5_HASH_ALGO, context->serverKey,
960  context->serverKeyLen);
961  //Any error to report?
962  if(error)
963  return;
964 
965  //For Access-Challenge, Access-Accept, and Access-Reject packets, the
966  //Message-Authenticator is calculated as follows, using the Request-
967  //Authenticator from the Access-Request this packet is in reply to (refer
968  //to RFC 3579, section 3.2)
969  hmacUpdate(hmacContext, packet, 4);
970  hmacUpdate(hmacContext, port->reqAuthenticator, 16);
971  hmacUpdate(hmacContext, packet->attributes, n);
972  hmacUpdate(hmacContext, digest, 16);
973  hmacUpdate(hmacContext, packet->attributes + n + 16, length - n - 16);
974  hmacFinal(hmacContext, digest);
975 
976  //Debug message
977  TRACE_DEBUG("Calculated Message Authenticator:\r\n");
978  TRACE_DEBUG_ARRAY(" ", digest, MD5_DIGEST_SIZE);
979 
980  //A NAS supporting the EAP-Message attribute must calculate the correct
981  //value of the Message-Authenticator and must silently discard the packet
982  //if it does not match the value sent (refer to RFC 3579, section 3.1)
983  if(osMemcmp(digest, attribute->value, MD5_DIGEST_SIZE) != 0)
984  {
985  //Debug message
986  TRACE_WARNING("Invalid Message Authenticator value!\r\n");
987  //Exit immediately
988  return;
989  }
990 
991  //Search the RADIUS packet for the State attribute
992  attribute = radiusGetAttribute(packet, RADIUS_ATTR_STATE, 0);
993 
994  //State attribute found?
995  if(attribute != NULL)
996  {
997  //Retrieve the length of the attribute value
998  n = attribute->length - sizeof(RadiusAttribute);
999 
1000  //Check the length of the attribute
1001  if(n >= 1 && n <= AUTHENTICATOR_MAX_STATE_SIZE)
1002  {
1003  //The actual format of the information is site or application
1004  //specific, and a robust implementation should support the field
1005  //as undistinguished octets (refer to RFC 2865, section 5.24)
1006  osMemcpy(port->serverState, attribute->value, n);
1007  port->serverStateLen = n;
1008  }
1009  }
1010 
1011  //EAP-Message attribute(s) encapsulate a single EAP packet which the NAS
1012  //decapsulates and passes on to the authenticating peer
1013  port->aaaEapReqDataLen = 0;
1014 
1015  //Decapsulate the EAP packet
1016  for(i = 0; ; i++)
1017  {
1018  //Point to the next EAP-Message attribute
1019  attribute = radiusGetAttribute(packet, RADIUS_ATTR_EAP_MESSAGE, i);
1020 
1021  //EAP-Message attribute found?
1022  if(attribute != NULL)
1023  {
1024  //Retrieve the length of the fragment
1025  n = attribute->length - sizeof(RadiusAttribute);
1026 
1027  //Make sure the buffer is large enough to hold the reconstructed EAP
1028  //packet
1029  if((port->aaaEapReqDataLen + n) <= AUTHENTICATOR_TX_BUFFER_SIZE)
1030  {
1031  //Copy the current fragment
1032  osMemcpy(context->txBuffer + port->aaaEapReqDataLen,
1033  attribute->value, n);
1034 
1035  //Adjust the length of the reconstructed EAP packet
1036  port->aaaEapReqDataLen += n;
1037  }
1038  else
1039  {
1040  //The reassembly process failed
1041  port->aaaEapReqDataLen = 0;
1042  break;
1043  }
1044  }
1045  else
1046  {
1047  //The reassembly process is now complete
1048  break;
1049  }
1050  }
1051 
1052  //Malformed EAP packet?
1053  if(port->aaaEapReqDataLen < sizeof(EapPacket))
1054  return;
1055 
1056  //Point to the EAP packet
1057  eapPacket = (EapPacket *) context->txBuffer;
1058 
1059  //Check Code field
1060  if(eapPacket->code == EAP_CODE_REQUEST ||
1061  eapPacket->code == EAP_CODE_SUCCESS ||
1062  eapPacket->code == EAP_CODE_FAILURE)
1063  {
1064  //The corresponding request (or success/failure) packet is stored in
1065  //aaaEapReqData
1066  osMemcpy(port->aaaEapReqData, context->txBuffer, port->aaaEapReqDataLen);
1067 
1068  //Debug message
1069  TRACE_DEBUG("Port %" PRIu8 ": Sending EAP packet (%" PRIuSIZE " bytes)...\r\n",
1070  port->portIndex, port->aaaEapReqDataLen);
1071 
1072  //Dump EAP header contents for debugging purpose
1073  eapDumpHeader(eapPacket);
1074 
1075  //When the authenticator has finished processing the message, it sets one
1076  //of the signals aaaEapReq, aaaSuccess, and aaaFail
1077  if(eapPacket->code == EAP_CODE_REQUEST)
1078  {
1079  port->aaaEapReq = TRUE;
1080  }
1081  else if(eapPacket->code == EAP_CODE_SUCCESS)
1082  {
1083  port->aaaSuccess = TRUE;
1084  }
1085  else
1086  {
1087  port->aaaFail = TRUE;
1088  }
1089 
1090  }
1091  else
1092  {
1093  //The aaaEapNoReq flag indicates that the most recent response has been
1094  //processed, but that there is no new request to send
1095  port->aaaEapNoReq = TRUE;
1096  }
1097 
1098  //Invoke EAP to perform whatever processing is needed
1099  authenticatorFsm(port->context);
1100 }
1101 
1102 
1103 /**
1104  * @brief Generate a new RADIUS packet identifier
1105  * @param[in] context Pointer to the 802.1X authenticator context
1106  **/
1107 
1109 {
1110  uint_t i;
1111  bool_t acceptable;
1113 
1114  //Generate a new RADIUS packet identifier
1115  do
1116  {
1117  //Increment identifier value
1118  context->radiusId++;
1119 
1120  //Loop through the ports
1121  for(acceptable = TRUE, i = 0; i < context->numPorts; i++)
1122  {
1123  //Point to the current port
1124  port = &context->ports[i];
1125 
1126  //Pending Access-Request?
1127  if(port->eapFullAuthState == EAP_FULL_AUTH_STATE_AAA_IDLE &&
1128  !port->aaaEapResp)
1129  {
1130  //Check whether the identifier is a duplicate
1131  if(port->aaaReqId == context->radiusId)
1132  {
1133  acceptable = FALSE;
1134  }
1135  }
1136  }
1137 
1138  //Repeat as necessary until a unique identifier value is generated
1139  } while(!acceptable);
1140 
1141  //Return the identifier value
1142  return context->radiusId;
1143 }
1144 
1145 #endif
void md5Update(Md5Context *context, const void *data, size_t length)
Update the MD5 context with a portion of the message being hashed.
802.1X authenticator
#define AUTHENTICATOR_RADIUS_TIMEOUT
#define AUTHENTICATOR_MAX_STATE_SIZE
#define AuthenticatorPort
Definition: authenticator.h:40
@ AUTHENTICATOR_TERMINATE_CAUSE_NOT_TERMINATED_YET
@ AUTHENTICATOR_TERMINATE_CAUSE_PORT_FAILURE
#define AUTHENTICATOR_TX_BUFFER_SIZE
Definition: authenticator.h:78
#define AUTHENTICATOR_RX_BUFFER_SIZE
Definition: authenticator.h:85
#define AuthenticatorContext
Definition: authenticator.h:36
void authenticatorFsm(AuthenticatorContext *context)
Authenticator state machine implementation.
Authenticator state machine.
void authenticatorGeneratePortAddr(AuthenticatorPort *port)
Port's MAC address generation.
uint8_t authenticatorGetNextRadiusId(AuthenticatorContext *context)
Generate a new RADIUS packet identifier.
void authenticatorProcessEapolPdu(AuthenticatorContext *context)
Process incoming EAPOL PDU.
error_t authenticatorSendEapolPdu(AuthenticatorPort *port, const uint8_t *pdu, size_t length)
Send EAPOL PDU.
void authenticatorProcessEapPacket(AuthenticatorPort *port, const EapPacket *packet, size_t length)
Process incoming EAP packet.
error_t authenticatorDropPaeGroupAddr(AuthenticatorContext *context)
Remove the PAE group address from the static MAC table.
error_t authenticatorAcceptPaeGroupAddr(AuthenticatorContext *context)
Add the PAE group address to the static MAC table.
void authenticatorProcessRadiusPacket(AuthenticatorContext *context)
Process incoming RADIUS packet.
bool_t authenticatorGetLinkState(AuthenticatorPort *port)
Get link state.
error_t authenticatorBuildRadiusRequest(AuthenticatorPort *port)
Build RADIUS Access-Request packet.
void authenticatorTick(AuthenticatorContext *context)
Handle periodic operations.
error_t authenticatorSendRadiusRequest(AuthenticatorPort *port)
Send RADIUS Access-Request packet.
Helper functions for 802.1X authenticator.
void authenticatorDecrementTimer(uint_t *x)
Decrement timer value.
Authenticator state machine procedures.
signed int int_t
Definition: compiler_port.h:49
unsigned int uint_t
Definition: compiler_port.h:50
#define PRIuSIZE
char char_t
Definition: compiler_port.h:48
int bool_t
Definition: compiler_port.h:53
#define htons(value)
Definition: cpu_endian.h:413
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
#define ntohs(value)
Definition: cpu_endian.h:421
Debugging facilities.
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:108
#define TRACE_DEBUG(...)
Definition: debug.h:107
#define TRACE_WARNING(...)
Definition: debug.h:85
#define TRACE_INFO(...)
Definition: debug.h:95
uint8_t n
uint16_t port
Definition: dns_common.h:267
@ EAP_CODE_FAILURE
Failure.
Definition: eap.h:155
@ EAP_CODE_REQUEST
Request.
Definition: eap.h:152
@ EAP_CODE_RESPONSE
Response.
Definition: eap.h:153
@ EAP_CODE_SUCCESS
Success.
Definition: eap.h:154
#define EAP_MAX_FRAG_SIZE
Definition: eap.h:98
EapolPdu
Definition: eap.h:211
EapPacket
Definition: eap.h:224
@ EAPOL_TYPE_START
EAPOL-Start.
Definition: eap.h:135
@ EAPOL_TYPE_LOGOFF
EAPOL-Logoff.
Definition: eap.h:136
@ EAPOL_TYPE_EAP
EAPOL-EAP.
Definition: eap.h:134
void eapolDumpHeader(const EapolPdu *header)
Dump EAPOL header for debugging purpose.
Definition: eap_debug.c:85
void eapDumpHeader(const EapPacket *header)
Dump EAP header for debugging purpose.
Definition: eap_debug.c:105
Data logging functions for debugging purpose (EAP)
@ EAP_FULL_AUTH_STATE_AAA_IDLE
error_t
Error codes.
Definition: error.h:43
@ ERROR_INVALID_ADDRESS
Definition: error.h:103
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_BUFFER_OVERFLOW
Definition: error.h:142
error_t ethDropMacAddr(NetInterface *interface, const MacAddr *macAddr)
Remove a unicast/multicast address from the MAC filter table.
Definition: ethernet.c:664
char_t * macAddrToString(const MacAddr *macAddr, char_t *str)
Convert a MAC address to a dash delimited string.
Definition: ethernet.c:917
error_t ethAcceptMacAddr(NetInterface *interface, const MacAddr *macAddr)
Add a unicast/multicast address to the MAC filter table.
Definition: ethernet.c:594
@ ETH_TYPE_EAPOL
Definition: ethernet.h:169
#define macCompAddr(macAddr1, macAddr2)
Definition: ethernet.h:130
MacAddr
Definition: ethernet.h:195
__weak_func error_t hmacInit(HmacContext *context, const HashAlgo *hash, const void *key, size_t keyLen)
Initialize HMAC calculation.
Definition: hmac.c:140
__weak_func void hmacFinal(HmacContext *context, uint8_t *digest)
Finish the HMAC calculation.
Definition: hmac.c:218
__weak_func void hmacUpdate(HmacContext *context, const void *data, size_t length)
Update the HMAC context with a portion of the message being hashed.
Definition: hmac.c:201
error_t ipSelectSourceAddr(NetInterface **interface, const IpAddr *destAddr, IpAddr *srcAddr)
IP source address selection.
Definition: ip.c:117
bool_t ipCompAddr(const IpAddr *ipAddr1, const IpAddr *ipAddr2)
Compare IP addresses.
Definition: ip.c:315
Ipv4Addr ipAddr
Definition: ipcp.h:105
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:267
Ipv6Addr
Definition: ipv6.h:251
#define MD5_DIGEST_SIZE
Definition: md5.h:45
#define MD5_HASH_ALGO
Definition: md5.h:49
uint8_t pdu[]
void md5Init(Md5Context *context)
Initialize MD5 message digest context.
void md5Final(Md5Context *context, uint8_t *digest)
Finish the MD5 message digest.
uint8_t c
Definition: ndp.h:514
error_t netGetMacAddr(NetInterface *interface, MacAddr *macAddr)
Retrieve MAC address.
Definition: net.c:519
#define NetInterface
Definition: net.h:36
#define netMutex
Definition: net_legacy.h:195
#define SWITCH_CPU_PORT_MASK
Definition: nic.h:60
#define osMemset(p, value, length)
Definition: os_port.h:135
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define MIN(a, b)
Definition: os_port.h:63
#define osMemcmp(p1, p2, length)
Definition: os_port.h:153
#define osStrlen(s)
Definition: os_port.h:165
#define osSprintf(dest,...)
Definition: os_port.h:231
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
#define MAX(a, b)
Definition: os_port.h:67
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
RADIUS (Remote Authentication Dial In User Service)
@ RADIUS_CODE_ACCESS_REJECT
Access-Reject.
Definition: radius.h:61
@ RADIUS_CODE_ACCESS_REQUEST
Access-Request.
Definition: radius.h:59
@ RADIUS_CODE_ACCESS_CHALLENGE
Access-Challenge.
Definition: radius.h:64
@ RADIUS_CODE_ACCESS_ACCEPT
Access-Accept.
Definition: radius.h:60
RadiusPacket
Definition: radius.h:89
void radiusAddAttribute(RadiusPacket *packet, uint8_t type, const void *value, size_t length)
Append an attribute to a RADIUS packet.
const RadiusAttribute * radiusGetAttribute(const RadiusPacket *packet, uint8_t type, uint_t index)
Search a RADIUS packet for a given attribute.
Formatting and parsing of RADIUS attributes.
RadiusAttribute
@ RADIUS_ATTR_FRAMED_MTU
Framed-MTU.
@ RADIUS_ATTR_NAS_PORT_ID
NAS-Port-Id.
@ RADIUS_ATTR_NAS_IP_ADDR
NAS-IP-Address.
@ RADIUS_ATTR_STATE
State.
@ RADIUS_ATTR_EAP_MESSAGE
EAP-Message.
@ RADIUS_ATTR_NAS_IPV6_ADDR
NAS-IPv6-Address.
@ RADIUS_ATTR_CALLING_STATION_ID
Calling-Station-Id.
@ RADIUS_ATTR_CALLED_STATION_ID
Called-Station-Id.
@ RADIUS_ATTR_NAS_PORT
NAS-Port.
@ RADIUS_ATTR_USER_NAME
User-Name.
@ RADIUS_ATTR_SERVICE_TYPE
Service-Type.
@ RADIUS_ATTR_MESSAGE_AUTHENTICATOR
Message-Authenticator.
@ RADIUS_ATTR_NAS_PORT_TYPE
NAS-Port-Type.
#define RADIUS_MAX_ATTR_VALUE_LEN
@ RADIUS_SERVICE_TYPE_FRAMED
Framed.
@ RADIUS_PORT_TYPE_ETHERNET
Ethernet.
void radiusDumpPacket(const RadiusPacket *packet, size_t length)
Dump RADIUS packet for debugging purpose.
Definition: radius_debug.c:259
Data logging functions for debugging purpose (RADIUS)
const SocketMsg SOCKET_DEFAULT_MSG
Definition: socket.c:52
error_t socketReceiveMsg(Socket *socket, SocketMsg *message, uint_t flags)
Receive a message from a connectionless socket.
Definition: socket.c:1354
error_t socketSendMsg(Socket *socket, const SocketMsg *message, uint_t flags)
Send a message to a connectionless socket.
Definition: socket.c:1094
HMAC algorithm context.
Definition: hmac.h:59
IP network address.
Definition: ip.h:79
MD5 algorithm context.
Definition: md5.h:62
Message and ancillary data.
Definition: socket.h:230
uint16_t srcPort
Source port.
Definition: socket.h:239
MacAddr srcMacAddr
Source MAC address.
Definition: socket.h:243
void * data
Pointer to the payload.
Definition: socket.h:231
MacAddr destMacAddr
Destination MAC address.
Definition: socket.h:244
size_t size
Size of the payload, in bytes.
Definition: socket.h:232
IpAddr destIpAddr
Destination IP address.
Definition: socket.h:240
IpAddr srcIpAddr
Source IP address.
Definition: socket.h:238
size_t length
Actual length of the payload, in bytes.
Definition: socket.h:233
uint16_t destPort
Destination port.
Definition: socket.h:241
uint16_t ethType
Ethernet type field.
Definition: socket.h:245
uint8_t switchPort
Switch port identifier.
Definition: socket.h:248
Forwarding database entry.
Definition: nic.h:149
MacAddr macAddr
Definition: nic.h:150
uint32_t destPorts
Definition: nic.h:152
bool_t override
Definition: nic.h:153
uint8_t srcPort
Definition: nic.h:151
uint8_t length
Definition: tcp.h:368