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