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.4
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 field must contain the PAE group address
386  if(!macCompAddr(&msg.destMacAddr, &PAE_GROUP_ADDR))
387  return;
388 
389  //The received MPDU must contain the PAE EtherType
390  if(msg.ethType != ETH_TYPE_EAPOL)
391  return;
392 
393  //Malformed EAPOL packet?
394  if(msg.length < sizeof(EapolPdu))
395  return;
396 
397  //Point to the EAPOL packet
398  pdu = (EapolPdu *) context->rxBuffer;
399 
400  //Debug message
401  TRACE_INFO("Port %" PRIu8 ": EAPOL packet received (%" PRIuSIZE " bytes)...\r\n",
402  portIndex, msg.length);
403 
404  //Dump EAPOL header contents for debugging purpose
406 
407  //Sanity check
408  if(portIndex > context->numPorts)
409  return;
410 
411  //Point to the port that matches the specified port index
412  port = &context->ports[portIndex - 1];
413 
414  //Malformed EAPOL packet?
415  if(msg.length < ntohs(pdu->packetBodyLen))
416  {
417  //Number of EAPOL frames that have been received by this authenticator
418  //in which the Packet Body Length field is invalid
419  port->stats.eapLengthErrorFramesRx++;
420 
421  //Exit immediately
422  return;
423  }
424 
425  //Any octets following the Packet Body field in the frame conveying the
426  //EAPOL PDU shall be ignored (refer to IEEE Std 802.1X-2004, section 11.4)
427  length = ntohs(pdu->packetBodyLen);
428 
429  //Number of valid EAPOL frames of any type that have been received
430  port->stats.eapolFramesRx++;
431  //Protocol version number carried in the most recently received EAPOL frame
432  port->stats.lastEapolFrameVersion = pdu->protocolVersion;
433 
434  //Save the MAC address of the supplicant
435  port->supplicantMacAddr = msg.srcMacAddr;
436 
437  //Check packet type
438  if(pdu->packetType == EAPOL_TYPE_EAP)
439  {
440  //Process incoming EAP packet
442  length);
443  }
444  else if(pdu->packetType == EAPOL_TYPE_START)
445  {
446  //Number of EAPOL Start frames that have been received
447  port->stats.eapolStartFramesRx++;
448 
449  //The eapolStart variable is set TRUE if an EAPOL PDU carrying a packet
450  //type of EAPOL-Start is received
451  port->eapolStart = TRUE;
452  }
453  else if(pdu->packetType == EAPOL_TYPE_LOGOFF)
454  {
455  //Number of EAPOL Logoff frames that have been received
456  port->stats.eapolLogoffFramesRx++;
457 
458  //The Logoff variable is set TRUE if an EAPOL PDU carrying a packet type
459  //of EAPOL-Logoff is received
460  port->eapolLogoff = TRUE;
461  }
462  else
463  {
464  //Number of EAPOL frames that have been received by this authenticator
465  //in which the frame type is not recognized
466  port->stats.invalidEapolFramesRx++;
467  }
468 }
469 
470 
471 /**
472  * @brief Process incoming EAP packet
473  * @param[in] port Pointer to the port context
474  * @param[in] packet Pointer to the received EAP packet
475  * @param[in] length Length of the packet, in bytes
476  **/
477 
479  const EapPacket *packet, size_t length)
480 {
481  //Malformed EAP packet?
482  if(length < sizeof(EapPacket))
483  return;
484 
485  //Debug message
486  TRACE_DEBUG("Port %" PRIu8 ": EAP packet received (%" PRIuSIZE " bytes)...\r\n",
487  port->portIndex, length);
488 
489  //Dump EAP header contents for debugging purpose
490  eapDumpHeader(packet);
491 
492  //A message with the Length field set to a value larger than the number of
493  //received octets must be silently discarded (refer to RFC 3748, section 4.1)
494  if(ntohs(packet->length) > length)
495  return;
496 
497  //Octets outside the range of the Length field should be treated as data
498  //link layer padding and must be ignored upon reception
499  length = ntohs(packet->length);
500 
501  //Based on the Code field, the EAP layer demultiplexes incoming EAP packets
502  //to the EAP peer and authenticator layers
503  if(packet->code == EAP_CODE_RESPONSE)
504  {
505  //Point to the EAP response
506  port->eapRespData = (uint8_t *) packet;
507  port->eapRespDataLen = length;
508 
509  //The eapolEap variable is set TRUE by an external entity if an EAPOL
510  //PDU carrying a Packet Type of EAP-Packet is received
511  port->eapolEap = TRUE;
512 
513  //Invoke EAP to perform whatever processing is needed
514  authenticatorFsm(port->context);
515  }
516  else
517  {
518  //Unless a host implements an EAP peer layer, EAP Request, Success and
519  //Failure packets will be silently discarded (refer to RFC 3748,
520  //section 2.3)
521  }
522 }
523 
524 
525 /**
526  * @brief Build RADIUS Access-Request packet
527  * @param[in] port Pointer to the port context
528  **/
529 
531 {
532  error_t error;
533  size_t i;
534  size_t n;
535  IpAddr ipAddr;
536  MacAddr macAddr;
537  RadiusPacket *packet;
538  AuthenticatorContext *context;
539  uint8_t buffer[32];
540 
541  //Point to the 802.1X authenticator context
542  context = port->context;
543 
544  //Total length of the RADIUS packet
545  port->aaaReqDataLen = 0;
546 
547  //The Request Authenticator value must be changed each time a new
548  //Identifier is used (refer to RFC 2865, section 4.1)
549  error = context->prngAlgo->read(context->prngContext,
550  port->reqAuthenticator, 16);
551  //Any error to report?
552  if(error)
553  return error;
554 
555  //Generate a new RADIUS packet identifier
556  port->aaaReqId = authenticatorGetNextRadiusId(context);
557 
558  //Point to the buffer where to format the RADIUS packet
559  packet = (RadiusPacket *) port->aaaReqData;
560 
561  //The Length field indicates the length of the packet including the Code,
562  //Identifier, Length, Authenticator and Attribute fields
563  n = sizeof(RadiusPacket);
564 
565  //Format RADIUS packet
566  packet->code = RADIUS_CODE_ACCESS_REQUEST;
567  packet->identifier = port->aaaReqId;
568  packet->length = htons(n);
569 
570  //The Authenticator field is 16 octets. This value is used to authenticate
571  //the reply from the RADIUS server (refer to RFC 2865, section 3)
572  osMemcpy(packet->authenticator, port->reqAuthenticator, 16);
573 
574  //The NAS must include the Type-Data field of the EAP-Response/Identity
575  //in the User-Name attribute in every subsequent Access-Request (refer to
576  //RFC 3579, section 2.1)
577  radiusAddAttribute(packet, RADIUS_ATTR_USER_NAME, port->aaaIdentity,
578  osStrlen(port->aaaIdentity));
579 
580  //The Service-Type attribute indicates the type of service the user has
581  //requested, or the type of service to be provided (refer to RFC 2865,
582  //section 5.6)
584 
585  //Add Service-Type attribute
587  sizeof(uint32_t));
588 
589  //The Framed-MTU attribute indicates the Maximum Transmission Unit to be
590  //configured for the user (refer to RFC 2865, section 5.12)
591  STORE32BE(EAP_MAX_FRAG_SIZE, buffer);
592 
593  //Add Framed-MTU attribute
595  sizeof(uint32_t));
596 
597  //Get exclusive access
599 
600  //Retrieve the IP address of the NAS
601  error = ipSelectSourceAddr(&context->serverInterface, &context->serverIpAddr,
602  &ipAddr);
603 
604  //Release exclusive access
606 
607  //Any error to report?
608  if(error)
609  return error;
610 
611  //Either NAS-Identifier, NAS-IP-Address or NAS-IPv6-Address attributes
612  //must be included (refer to RFC 3579, section 3)
613  if(ipAddr.length == sizeof(Ipv4Addr))
614  {
616  ipAddr.length);
617  }
618  else if(ipAddr.length == sizeof(Ipv6Addr))
619  {
621  ipAddr.length);
622  }
623  else
624  {
625  return ERROR_INVALID_ADDRESS;
626  }
627 
628  //The NAS-Port attribute indicates the physical port number of the NAS which
629  //is authenticating the user (refer to RFC 2865, section 5.5)
630  STORE32BE(port->portIndex, buffer);
631 
632  //Add NAS-Port attribute
633  radiusAddAttribute(packet, RADIUS_ATTR_NAS_PORT, buffer, sizeof(uint32_t));
634 
635  //The NAS-Port-Type attribute indicates the type of the physical port of
636  //the NAS which is authenticating the user. It can be used instead of or in
637  //addition to the NAS-Port attribute (refer to RFC 2865, section 5.41)
639 
640  //Add NAS-Port-Type attribute
642  sizeof(uint32_t));
643 
644  //The NAS-Port-Id attribute contains a text string which identifies the
645  //port of the NAS which is authenticating the user (refer to RFC 2869,
646  //section 5.17)
647  osSprintf((char_t *) buffer, "%s_%" PRIu8, context->interface->name,
648  port->portIndex);
649 
651  osStrlen((char_t *) buffer));
652 
653  //Retrieve the MAC address of the bridge
654  netGetMacAddr(context->serverInterface, &macAddr);
655  macAddrToString(&macAddr, (char_t *) buffer);
656 
657  //The Called-Station-Id attribute is used to store the bridge or access
658  //point MAC address in ASCII format (refer to RFC 3580, section 3.20)
660  osStrlen((char_t *) buffer));
661 
662  //Retrieve the MAC address of the supplicant
663  macAddrToString(&port->supplicantMacAddr, (char_t *) buffer);
664 
665  //The Calling-Station-Id attribute is used to store the supplicant MAC
666  //address in ASCII format (refer to RFC 3580, section 3.21)
668  osStrlen((char_t *) buffer));
669 
670  //Any State attribute received from previous Access-Challenge?
671  if(port->serverStateLen > 0)
672  {
673  //The NAS must include the State attribute unchanged in that
674  //Access-Request (refer to RFC 2865, section 5.24)
675  radiusAddAttribute(packet, RADIUS_ATTR_STATE, port->serverState,
676  port->serverStateLen);
677  }
678 
679  //The NAS places EAP messages received from the authenticating peer into
680  //one or more EAP-Message attributes and forwards them to the RADIUS server
681  //within an Access-Request message (refer to RFC 3579, section 3.1)
682  for(i = 0; i < port->eapRespDataLen; i += n)
683  {
684  //Each attribute can contain up to 253 octets of binary data
685  n = MIN(port->eapRespDataLen - i, RADIUS_MAX_ATTR_VALUE_LEN);
686 
687  //Make sure the buffer is large enough to hold the EAP-Message attribute
688  if((htons(packet->length) + sizeof(RadiusAttribute) + n) >
690  {
691  return ERROR_BUFFER_OVERFLOW;
692  }
693 
694  //If multiple EAP-Message attributes are contained within an Access-
695  //Request, they must be in order and they must be consecutive attributes
697  port->eapRespData + i, n);
698  }
699 
700  //When the checksum is calculated the signature string should be considered
701  //to be sixteen octets of zero (refer to RFC 2869, section 5.14)
702  osMemset(buffer, 0, MD5_DIGEST_SIZE);
703 
704  //Make sure the buffer is large enough to hold the Message-Authenticator
705  //attribute
706  if((htons(packet->length) + sizeof(RadiusAttribute) + MD5_DIGEST_SIZE) >
708  {
709  return ERROR_BUFFER_OVERFLOW;
710  }
711 
712  //Add Message-Authenticator attribute
715 
716  //Retrieve the total length of the RADIUS packet
717  n = htons(packet->length);
718 
719  //Transactions between the client and RADIUS server are authenticated through
720  //the use of a shared secret (refer to RFC 2865, section 1)
721  error = hmacInit(&context->hmacContext, MD5_HASH_ALGO, context->serverKey,
722  context->serverKeyLen);
723  //Any error to report?
724  if(error)
725  return error;
726 
727  //When present in an Access-Request packet, Message-Authenticator is an
728  //HMAC-MD5 hash of the entire Access-Request packet, including Type, ID,
729  //Length and Authenticator, using the shared secret as the key (refer to
730  //RFC 3579, section 3.2)
731  hmacUpdate(&context->hmacContext, port->aaaReqData, n);
732  hmacFinal(&context->hmacContext, buffer);
733 
734  //Copy the resulting HMAC-MD5 hash
735  osMemcpy(port->aaaReqData + n - MD5_DIGEST_SIZE, buffer, MD5_DIGEST_SIZE);
736 
737  //Save the total length of the RADIUS packet
738  port->aaaReqDataLen = n;
739  //Initialize retransmission counter
740  port->aaaRetransCount = 0;
741 
742  //Sucessful processing
743  return NO_ERROR;
744 }
745 
746 
747 /**
748  * @brief Send RADIUS Access-Request packet
749  * @param[in] port Pointer to the port context
750  **/
751 
753 {
754  error_t error;
755  SocketMsg msg;
756  AuthenticatorContext *context;
757 
758  //Initialize status code
759  error = NO_ERROR;
760 
761  //Point to the 802.1X authenticator context
762  context = port->context;
763 
764  //Valid RADIUS packet?
765  if(port->aaaReqDataLen > 0)
766  {
767  //Exactly one RADIUS packet is encapsulated in the UDP data field,
768  //where the UDP destination Port field indicates 1812 (refer to
769  //RFC 2865, section 3)
770  msg = SOCKET_DEFAULT_MSG;
771  msg.data = port->aaaReqData;
772  msg.length = port->aaaReqDataLen;
773  msg.destIpAddr = context->serverIpAddr;
774  msg.destPort = context->serverPort;
775 
776 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
777  //Specify the egress port
778  msg.switchPort = context->serverPortIndex;
779 #endif
780 
781  //Debug message
782  TRACE_INFO("Sending RADIUS packet (%" PRIuSIZE " bytes)...\r\n",
783  port->aaaReqDataLen);
784 
785  //Dump RADIUS header contents for debugging purpose
786  radiusDumpPacket((RadiusPacket *) port->aaaReqData, port->aaaReqDataLen);
787 
788  //Send UDP datagram
789  error = socketSendMsg(context->serverSocket, &msg, 0);
790 
791  //Increment retransmission counter
792  port->aaaRetransCount++;
793  //Set retransmission timeout
794  port->aaaRetransTimer = AUTHENTICATOR_RADIUS_TIMEOUT;
795  }
796 
797  //Return status code
798  return error;
799 }
800 
801 
802 /**
803  * @brief Process incoming RADIUS packet
804  * @param[in] context Pointer to the 802.1X authenticator context
805  **/
806 
808 {
809  error_t error;
810  uint_t i;
811  size_t n;
812  size_t length;
813  SocketMsg msg;
815  EapPacket *eapPacket;
816  const RadiusPacket *packet;
817  const RadiusAttribute *attribute;
818  Md5Context *md5Context;
819  HmacContext *hmacContext;
820  uint8_t digest[MD5_DIGEST_SIZE];
821 
822  //Point to the receive buffer
823  msg = SOCKET_DEFAULT_MSG;
824  msg.data = context->rxBuffer;
826 
827  //Receive EAPOL MPDU
828  error = socketReceiveMsg(context->serverSocket, &msg, 0);
829  //Failed to receive packet
830  if(error)
831  return;
832 
833  //Debug message
834  TRACE_INFO("RADIUS packet received (%" PRIuSIZE " bytes)...\r\n",
835  msg.length);
836 
837 #if (ETH_PORT_TAGGING_SUPPORT == ENABLED)
838  //Check the port number on which the EAPOL PDU was received
839  if(msg.switchPort != context->serverPortIndex && context->serverPortIndex != 0)
840  return;
841 #endif
842 
843  //Ensure the source IP address matches the RADIUS server's IP address
844  if(!ipCompAddr(&msg.srcIpAddr, &context->serverIpAddr))
845  return;
846 
847  //The officially assigned port number for RADIUS is 1812 (refer to RFC 2865,
848  //section 3)
849  if(msg.srcPort != context->serverPort)
850  return;
851 
852  //Malformed RADIUS packet?
853  if(msg.length < sizeof(RadiusPacket))
854  return;
855 
856  //Point to the RADIUS packet
857  packet = (RadiusPacket *) context->rxBuffer;
858 
859  //If the packet is shorter than the Length field indicates, it must be
860  //silently discarded (refer to RFC 2865, section 3)
861  if(msg.length < ntohs(packet->length))
862  return;
863 
864  //Dump RADIUS header contents for debugging purpose
865  radiusDumpPacket(packet, ntohs(packet->length));
866 
867  //Octets outside the range of the Length field must be treated as padding
868  //and ignored on reception
869  length = ntohs(packet->length) - sizeof(RadiusPacket);
870 
871  //The RADIUS packet type is determined by the Code field
872  if(packet->code != RADIUS_CODE_ACCESS_ACCEPT &&
873  packet->code != RADIUS_CODE_ACCESS_REJECT &&
874  packet->code != RADIUS_CODE_ACCESS_CHALLENGE)
875  {
876  return;
877  }
878 
879  //The Identifier field aids in matching requests and replies
880  for(i = 0; i < context->numPorts; i++)
881  {
882  //Point to the current port
883  port = &context->ports[i];
884 
885  //The Identifier field is matched with a pending Access-Request
886  if(port->eapFullAuthState == EAP_FULL_AUTH_STATE_AAA_IDLE &&
887  !port->aaaEapResp)
888  {
889  //Matching identifier?
890  if(port->aaaReqId == packet->identifier)
891  {
892  break;
893  }
894  }
895  }
896 
897  //No matching request found?
898  if(i >= context->numPorts)
899  return;
900 
901  //Point to the MD5 context
902  md5Context = &context->hmacContext.hashContext.md5Context;
903  //Initialize MD5 calculation
904  md5Init(md5Context);
905 
906  //The Response Authenticator contains a one-way MD5 hash calculated over the
907  //RADIUS packet, beginning with the Code field, including the Identifier, the
908  //Length, the Request Authenticator field from the Access-Request packet, and
909  //the response Attributes, followed by the shared secret (refer to RFC 2865,
910  //section 3)
911  md5Update(md5Context, packet, 4);
912  md5Update(md5Context, port->reqAuthenticator, 16);
913  md5Update(md5Context, packet->attributes, length);
914  md5Update(md5Context, context->serverKey, context->serverKeyLen);
915  md5Final(md5Context, digest);
916 
917  //Debug message
918  TRACE_DEBUG("Calculated Response Authenticator:\r\n");
919  TRACE_DEBUG_ARRAY(" ", digest, MD5_DIGEST_SIZE);
920 
921  //The Response Authenticator field must contain the correct response for the
922  //pending Access-Request. Invalid packets are silently discarded
923  if(osMemcmp(digest, packet->authenticator, MD5_DIGEST_SIZE) != 0)
924  {
925  //Debug message
926  TRACE_WARNING("Invalid Response Authenticator value!\r\n");
927  //Exit immediately
928  return;
929  }
930 
931  //The Message-Authenticator attribute must be used to protect all
932  //Access-Request, Access-Challenge, Access-Accept, and Access-Reject
933  //packets containing an EAP-Message attribute (refer to RFC 3579,
934  //section 3.2)
936 
937  //Access-Challenge, Access-Accept, or Access-Reject packets including
938  //EAP-Message attribute(s) without a Message-Authenticator attribute should
939  //be silently discarded by the NAS (refer to RFC 3579, section 3.1)
940  if(attribute == NULL)
941  return;
942 
943  //Malformed Message-Authenticator attribute?
944  if(attribute->length != (sizeof(RadiusAttribute) + MD5_DIGEST_SIZE))
945  return;
946 
947  //Save the offset to the Message-Authenticator value
948  n = attribute->value - packet->attributes;
949 
950  //When the checksum is calculated the signature string should be considered
951  //to be sixteen octets of zero (refer to RFC 2869, section 5.14)
952  osMemset(digest, 0, MD5_DIGEST_SIZE);
953 
954  //Point to the HMAC context
955  hmacContext = &context->hmacContext;
956 
957  //Initialize HMAC-MD5 calculation
958  error = hmacInit(hmacContext, MD5_HASH_ALGO, context->serverKey,
959  context->serverKeyLen);
960  //Any error to report?
961  if(error)
962  return;
963 
964  //For Access-Challenge, Access-Accept, and Access-Reject packets, the
965  //Message-Authenticator is calculated as follows, using the Request-
966  //Authenticator from the Access-Request this packet is in reply to (refer
967  //to RFC 3579, section 3.2)
968  hmacUpdate(hmacContext, packet, 4);
969  hmacUpdate(hmacContext, port->reqAuthenticator, 16);
970  hmacUpdate(hmacContext, packet->attributes, n);
971  hmacUpdate(hmacContext, digest, 16);
972  hmacUpdate(hmacContext, packet->attributes + n + 16, length - n - 16);
973  hmacFinal(hmacContext, digest);
974 
975  //Debug message
976  TRACE_DEBUG("Calculated Message Authenticator:\r\n");
977  TRACE_DEBUG_ARRAY(" ", digest, MD5_DIGEST_SIZE);
978 
979  //A NAS supporting the EAP-Message attribute must calculate the correct
980  //value of the Message-Authenticator and must silently discard the packet
981  //if it does not match the value sent (refer to RFC 3579, section 3.1)
982  if(osMemcmp(digest, attribute->value, MD5_DIGEST_SIZE) != 0)
983  {
984  //Debug message
985  TRACE_WARNING("Invalid Message Authenticator value!\r\n");
986  //Exit immediately
987  return;
988  }
989 
990  //Search the RADIUS packet for the State attribute
991  attribute = radiusGetAttribute(packet, RADIUS_ATTR_STATE, 0);
992 
993  //State attribute found?
994  if(attribute != NULL)
995  {
996  //Retrieve the length of the attribute value
997  n = attribute->length - sizeof(RadiusAttribute);
998 
999  //Check the length of the attribute
1000  if(n >= 1 && n <= AUTHENTICATOR_MAX_STATE_SIZE)
1001  {
1002  //The actual format of the information is site or application
1003  //specific, and a robust implementation should support the field
1004  //as undistinguished octets (refer to RFC 2865, section 5.24)
1005  osMemcpy(port->serverState, attribute->value, n);
1006  port->serverStateLen = n;
1007  }
1008  }
1009 
1010  //EAP-Message attribute(s) encapsulate a single EAP packet which the NAS
1011  //decapsulates and passes on to the authenticating peer
1012  port->aaaEapReqDataLen = 0;
1013 
1014  //Decapsulate the EAP packet
1015  for(i = 0; ; i++)
1016  {
1017  //Point to the next EAP-Message attribute
1018  attribute = radiusGetAttribute(packet, RADIUS_ATTR_EAP_MESSAGE, i);
1019 
1020  //EAP-Message attribute found?
1021  if(attribute != NULL)
1022  {
1023  //Retrieve the length of the fragment
1024  n = attribute->length - sizeof(RadiusAttribute);
1025 
1026  //Make sure the buffer is large enough to hold the reconstructed EAP
1027  //packet
1028  if((port->aaaEapReqDataLen + n) <= AUTHENTICATOR_TX_BUFFER_SIZE)
1029  {
1030  //Copy the current fragment
1031  osMemcpy(context->txBuffer + port->aaaEapReqDataLen,
1032  attribute->value, n);
1033 
1034  //Adjust the length of the reconstructed EAP packet
1035  port->aaaEapReqDataLen += n;
1036  }
1037  else
1038  {
1039  //The reassembly process failed
1040  port->aaaEapReqDataLen = 0;
1041  break;
1042  }
1043  }
1044  else
1045  {
1046  //The reassembly process is now complete
1047  break;
1048  }
1049  }
1050 
1051  //Malformed EAP packet?
1052  if(port->aaaEapReqDataLen < sizeof(EapPacket))
1053  return;
1054 
1055  //Point to the EAP packet
1056  eapPacket = (EapPacket *) context->txBuffer;
1057 
1058  //Check Code field
1059  if(eapPacket->code == EAP_CODE_REQUEST ||
1060  eapPacket->code == EAP_CODE_SUCCESS ||
1061  eapPacket->code == EAP_CODE_FAILURE)
1062  {
1063  //The corresponding request (or success/failure) packet is stored in
1064  //aaaEapReqData
1065  osMemcpy(port->aaaEapReqData, context->txBuffer, port->aaaEapReqDataLen);
1066 
1067  //Debug message
1068  TRACE_DEBUG("Port %" PRIu8 ": Sending EAP packet (%" PRIuSIZE " bytes)...\r\n",
1069  port->portIndex, port->aaaEapReqDataLen);
1070 
1071  //Dump EAP header contents for debugging purpose
1072  eapDumpHeader(eapPacket);
1073 
1074  //When the authenticator has finished processing the message, it sets one
1075  //of the signals aaaEapReq, aaaSuccess, and aaaFail
1076  if(eapPacket->code == EAP_CODE_REQUEST)
1077  {
1078  port->aaaEapReq = TRUE;
1079  }
1080  else if(eapPacket->code == EAP_CODE_SUCCESS)
1081  {
1082  port->aaaSuccess = TRUE;
1083  }
1084  else
1085  {
1086  port->aaaFail = TRUE;
1087  }
1088 
1089  }
1090  else
1091  {
1092  //The aaaEapNoReq flag indicates that the most recent response has been
1093  //processed, but that there is no new request to send
1094  port->aaaEapNoReq = TRUE;
1095  }
1096 
1097  //Invoke EAP to perform whatever processing is needed
1098  authenticatorFsm(port->context);
1099 }
1100 
1101 
1102 /**
1103  * @brief Generate a new RADIUS packet identifier
1104  * @param[in] context Pointer to the 802.1X authenticator context
1105  **/
1106 
1108 {
1109  uint_t i;
1110  bool_t acceptable;
1112 
1113  //Generate a new RADIUS packet identifier
1114  do
1115  {
1116  //Increment identifier value
1117  context->radiusId++;
1118 
1119  //Loop through the ports
1120  for(acceptable = TRUE, i = 0; i < context->numPorts; i++)
1121  {
1122  //Point to the current port
1123  port = &context->ports[i];
1124 
1125  //Pending Access-Request?
1126  if(port->eapFullAuthState == EAP_FULL_AUTH_STATE_AAA_IDLE &&
1127  !port->aaaEapResp)
1128  {
1129  //Check whether the identifier is a duplicate
1130  if(port->aaaReqId == context->radiusId)
1131  {
1132  acceptable = FALSE;
1133  }
1134  }
1135  }
1136 
1137  //Repeat as necessary until a unique identifier value is generated
1138  } while(!acceptable);
1139 
1140  //Return the identifier value
1141  return context->radiusId;
1142 }
1143 
1144 #endif
error_t ethAcceptMacAddr(NetInterface *interface, const MacAddr *macAddr)
Add a unicast/multicast address to the MAC filter table.
Definition: ethernet.c:594
void radiusDumpPacket(const RadiusPacket *packet, size_t length)
Dump RADIUS packet for debugging purpose.
Definition: radius_debug.c:259
#define htons(value)
Definition: cpu_endian.h:413
int bool_t
Definition: compiler_port.h:53
HMAC algorithm context.
Definition: hmac.h:59
void eapDumpHeader(const EapPacket *header)
Dump EAP header for debugging purpose.
Definition: eap_debug.c:105
@ AUTHENTICATOR_TERMINATE_CAUSE_PORT_FAILURE
@ RADIUS_SERVICE_TYPE_FRAMED
Framed.
uint32_t destPorts
Definition: nic.h:152
@ RADIUS_ATTR_MESSAGE_AUTHENTICATOR
Message-Authenticator.
@ EAP_CODE_RESPONSE
Response.
Definition: eap.h:153
void authenticatorGeneratePortAddr(AuthenticatorPort *port)
Port's MAC address generation.
signed int int_t
Definition: compiler_port.h:49
#define netMutex
Definition: net_legacy.h:195
IP network address.
Definition: ip.h:90
@ ERROR_BUFFER_OVERFLOW
Definition: error.h:142
RadiusPacket
Definition: radius.h:89
error_t authenticatorSendRadiusRequest(AuthenticatorPort *port)
Send RADIUS Access-Request packet.
@ RADIUS_CODE_ACCESS_REQUEST
Access-Request.
Definition: radius.h:59
void authenticatorProcessRadiusPacket(AuthenticatorContext *context)
Process incoming RADIUS packet.
EapolPdu
Definition: eap.h:211
@ RADIUS_ATTR_STATE
State.
#define TRUE
Definition: os_port.h:50
Message and ancillary data.
Definition: socket.h:241
@ EAP_CODE_FAILURE
Failure.
Definition: eap.h:155
void md5Final(Md5Context *context, uint8_t *digest)
Finish the MD5 message digest.
Ipv6Addr
Definition: ipv6.h:260
@ RADIUS_ATTR_NAS_PORT
NAS-Port.
void * data
Pointer to the payload.
Definition: socket.h:242
#define osMemcmp(p1, p2, length)
Definition: os_port.h:153
error_t authenticatorDropPaeGroupAddr(AuthenticatorContext *context)
Remove the PAE group address from the static MAC table.
@ RADIUS_ATTR_NAS_IPV6_ADDR
NAS-IPv6-Address.
void authenticatorProcessEapPacket(AuthenticatorPort *port, const EapPacket *packet, size_t length)
Process incoming EAP packet.
#define osStrlen(s)
Definition: os_port.h:165
uint8_t authenticatorGetNextRadiusId(AuthenticatorContext *context)
Generate a new RADIUS packet identifier.
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:297
Data logging functions for debugging purpose (RADIUS)
@ RADIUS_CODE_ACCESS_REJECT
Access-Reject.
Definition: radius.h:61
error_t ethDropMacAddr(NetInterface *interface, const MacAddr *macAddr)
Remove a unicast/multicast address from the MAC filter table.
Definition: ethernet.c:666
uint16_t ethType
Ethernet type field.
Definition: socket.h:256
void md5Init(Md5Context *context)
Initialize MD5 message digest context.
uint16_t destPort
Destination port.
Definition: socket.h:252
Helper functions for 802.1X authenticator.
Authenticator state machine procedures.
@ RADIUS_PORT_TYPE_ETHERNET
Ethernet.
#define AUTHENTICATOR_MAX_STATE_SIZE
#define RADIUS_MAX_ATTR_VALUE_LEN
error_t socketSendMsg(Socket *socket, const SocketMsg *message, uint_t flags)
Send a message to a connectionless socket.
Definition: socket.c:1634
Formatting and parsing of RADIUS attributes.
#define FALSE
Definition: os_port.h:46
const SocketMsg SOCKET_DEFAULT_MSG
Definition: socket.c:52
@ RADIUS_ATTR_EAP_MESSAGE
EAP-Message.
size_t length
Actual length of the payload, in bytes.
Definition: socket.h:244
@ RADIUS_ATTR_FRAMED_MTU
Framed-MTU.
@ RADIUS_ATTR_CALLING_STATION_ID
Calling-Station-Id.
void eapolDumpHeader(const EapolPdu *header)
Dump EAPOL header for debugging purpose.
Definition: eap_debug.c:85
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
802.1X authenticator
error_t
Error codes.
Definition: error.h:43
bool_t ipCompAddr(const IpAddr *ipAddr1, const IpAddr *ipAddr2)
Compare IP addresses.
Definition: ip.c:317
#define osSprintf(dest,...)
Definition: os_port.h:231
#define AUTHENTICATOR_RADIUS_TIMEOUT
uint8_t pdu[]
#define EAP_MAX_FRAG_SIZE
Definition: eap.h:98
const RadiusAttribute * radiusGetAttribute(const RadiusPacket *packet, uint8_t type, uint_t index)
Search a RADIUS packet for a given attribute.
@ ERROR_INVALID_ADDRESS
Definition: error.h:103
char_t * macAddrToString(const MacAddr *macAddr, char_t *str)
Convert a MAC address to a dash delimited string.
Definition: ethernet.c:919
error_t ipSelectSourceAddr(NetInterface **interface, const IpAddr *destAddr, IpAddr *srcAddr)
IP source address selection.
Definition: ip.c:119
@ EAPOL_TYPE_EAP
EAPOL-EAP.
Definition: eap.h:134
#define MD5_HASH_ALGO
Definition: md5.h:49
#define NetInterface
Definition: net.h:36
void authenticatorDecrementTimer(uint_t *x)
Decrement timer value.
@ EAP_CODE_SUCCESS
Success.
Definition: eap.h:154
IpAddr srcIpAddr
Source IP address.
Definition: socket.h:249
uint8_t switchPort
Switch port identifier.
Definition: socket.h:259
error_t socketReceiveMsg(Socket *socket, SocketMsg *message, uint_t flags)
Receive a message from a connectionless socket.
Definition: socket.c:1894
error_t netGetMacAddr(NetInterface *interface, MacAddr *macAddr)
Retrieve MAC address.
Definition: net.c:516
MD5 algorithm context.
Definition: md5.h:62
error_t authenticatorBuildRadiusRequest(AuthenticatorPort *port)
Build RADIUS Access-Request packet.
RADIUS (Remote Authentication Dial In User Service)
#define TRACE_INFO(...)
Definition: debug.h:95
@ ETH_TYPE_EAPOL
Definition: ethernet.h:169
uint8_t length
Definition: tcp.h:368
@ RADIUS_ATTR_NAS_PORT_TYPE
NAS-Port-Type.
#define MIN(a, b)
Definition: os_port.h:63
@ RADIUS_CODE_ACCESS_ACCEPT
Access-Accept.
Definition: radius.h:60
Authenticator state machine.
@ EAP_CODE_REQUEST
Request.
Definition: eap.h:152
#define MD5_DIGEST_SIZE
Definition: md5.h:45
@ RADIUS_CODE_ACCESS_CHALLENGE
Access-Challenge.
Definition: radius.h:64
error_t authenticatorSendEapolPdu(AuthenticatorPort *port, const uint8_t *pdu, size_t length)
Send EAPOL PDU.
MacAddr
Definition: ethernet.h:195
MacAddr srcMacAddr
Source MAC address.
Definition: socket.h:254
error_t authenticatorAcceptPaeGroupAddr(AuthenticatorContext *context)
Add the PAE group address to the static MAC table.
RadiusAttribute
@ RADIUS_ATTR_USER_NAME
User-Name.
uint16_t port
Definition: dns_common.h:267
#define ntohs(value)
Definition: cpu_endian.h:421
__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
IpAddr destIpAddr
Destination IP address.
Definition: socket.h:251
#define TRACE_WARNING(...)
Definition: debug.h:85
#define TRACE_DEBUG(...)
Definition: debug.h:107
#define MAX(a, b)
Definition: os_port.h:67
MacAddr destMacAddr
Destination MAC address.
Definition: socket.h:255
char char_t
Definition: compiler_port.h:48
#define AuthenticatorContext
Definition: authenticator.h:36
Data logging functions for debugging purpose (EAP)
@ EAP_FULL_AUTH_STATE_AAA_IDLE
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:108
uint8_t n
void authenticatorTick(AuthenticatorContext *context)
Handle periodic operations.
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
__weak_func void hmacFinal(HmacContext *context, uint8_t *digest)
Finish the HMAC calculation.
Definition: hmac.c:218
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
EapPacket
Definition: eap.h:224
MacAddr macAddr
Definition: nic.h:150
@ RADIUS_ATTR_NAS_IP_ADDR
NAS-IP-Address.
uint8_t srcPort
Definition: nic.h:151
@ AUTHENTICATOR_TERMINATE_CAUSE_NOT_TERMINATED_YET
@ RADIUS_ATTR_CALLED_STATION_ID
Called-Station-Id.
@ RADIUS_ATTR_SERVICE_TYPE
Service-Type.
#define macCompAddr(macAddr1, macAddr2)
Definition: ethernet.h:130
size_t size
Size of the payload, in bytes.
Definition: socket.h:243
void authenticatorFsm(AuthenticatorContext *context)
Authenticator state machine implementation.
@ RADIUS_ATTR_NAS_PORT_ID
NAS-Port-Id.
Ipv4Addr ipAddr
Definition: ipcp.h:105
bool_t authenticatorGetLinkState(AuthenticatorPort *port)
Get link state.
void authenticatorProcessEapolPdu(AuthenticatorContext *context)
Process incoming EAPOL PDU.
@ EAPOL_TYPE_LOGOFF
EAPOL-Logoff.
Definition: eap.h:136
#define SWITCH_CPU_PORT_MASK
Definition: nic.h:60
@ EAPOL_TYPE_START
EAPOL-Start.
Definition: eap.h:135
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:50
#define osMemset(p, value, length)
Definition: os_port.h:135
#define AuthenticatorPort
Definition: authenticator.h:40
__weak_func error_t hmacInit(HmacContext *context, const HashAlgo *hash, const void *key, size_t keyLen)
Initialize HMAC calculation.
Definition: hmac.c:140
uint16_t srcPort
Source port.
Definition: socket.h:250
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
#define AUTHENTICATOR_RX_BUFFER_SIZE
Definition: authenticator.h:85
void md5Update(Md5Context *context, const void *data, size_t length)
Update the MD5 context with a portion of the message being hashed.
@ NO_ERROR
Success.
Definition: error.h:44
uint8_t c
Definition: ndp.h:514
bool_t override
Definition: nic.h:153
#define AUTHENTICATOR_TX_BUFFER_SIZE
Definition: authenticator.h:78
Debugging facilities.
Forwarding database entry.
Definition: nic.h:149
void radiusAddAttribute(RadiusPacket *packet, uint8_t type, const void *value, size_t length)
Append an attribute to a RADIUS packet.