eap_tls.c
Go to the documentation of this file.
1 /**
2  * @file eap_tls.c
3  * @brief EAP-TLS authentication method
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 EAP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "eap/eap.h"
36 #include "eap/eap_tls.h"
37 #include "eap/eap_debug.h"
38 #include "debug.h"
39 
40 //Check EAP library configuration
41 #if (EAP_TLS_SUPPORT == ENABLED)
42 
43 
44 /**
45  * @brief Check incoming EAP-TLS request
46  * @param[in] context Pointer to the 802.1X supplicant context
47  * @param[in] request Pointer to the received request
48  * @param[in] length Length of the request, in bytes
49  **/
50 
52  const EapTlsPacket *request, size_t length)
53 {
54  //Check the length of the EAP request
55  if(length < sizeof(EapTlsPacket))
56  return ERROR_INVALID_LENGTH;
57 
58  //The L flag is set to indicate the presence of the four-octet TLS Message
59  //Length field (refer to RFC 5216, section 2.1.5)
60  if((request->flags & EAP_TLS_FLAGS_L) != 0)
61  {
62  //Malformed request?
63  if(length < (sizeof(EapTlsPacket) + sizeof(uint32_t)))
64  return ERROR_INVALID_LENGTH;
65  }
66 
67  //The request is valid
68  return NO_ERROR;
69 }
70 
71 
72 /**
73  * @brief Process incoming EAP-TLS request
74  * @param[in] context Pointer to the 802.1X supplicant context
75  * @param[in] request Pointer to the received request
76  * @param[in] length Length of the request, in bytes
77  **/
78 
80  const EapTlsPacket *request, size_t length)
81 {
82  error_t error;
83  size_t n;
84  uint8_t data;
85 
86  //Check method state
87  if(context->methodState == EAP_METHOD_STATE_INIT)
88  {
89  //The method starts by initializing its own method-specific state
90  context->txBufferWritePos = EAP_TLS_TX_BUFFER_START_POS;
91  context->txBufferReadPos = EAP_TLS_TX_BUFFER_START_POS;
92  context->txBufferLen = 0;
93  context->rxBufferPos = 0;
94  context->rxBufferLen = 0;
95 
96  //Abort previous TLS session, if any
98 
99  //The S flag is set only within the EAP-TLS start message sent from the
100  //EAP server to the peer (refer to RFC 5216, section 2.1.5)
101  if((request->flags & EAP_TLS_FLAGS_S) != 0)
102  {
103  //Initialize TLS context
104  error = eapOpenTls(context);
105 
106  //Check status code
107  if(!error)
108  {
109  //Restore TLS session
110  error = tlsRestoreSessionState(context->tlsContext,
111  &context->tlsSession);
112  }
113 
114  //Check status code
115  if(!error)
116  {
117  //The EAP-TLS conversation will then begin with the peer sending an
118  //EAP-Response containing a TLS ClientHello handshake message
119  error = tlsConnect(context->tlsContext);
120  }
121  }
122  else
123  {
124  //The S flag is not set
125  error = ERROR_INVALID_REQUEST;
126  }
127  }
128  else if(context->methodState == EAP_METHOD_STATE_CONT ||
129  context->methodState == EAP_METHOD_STATE_MAY_CONT)
130  {
131  //The data consists of the encapsulated TLS packet in TLS record format
132  //(refer to RFC 5216, section 3.2)
133  context->rxBufferPos = request->data - context->rxBuffer;
134  context->rxBufferLen = length - sizeof(EapTlsPacket);
135 
136  //The L flag is set to indicate the presence of the four-octet TLS
137  //Message Length field (refer to RFC 5216, section 2.1.5)
138  if((request->flags & EAP_TLS_FLAGS_L) != 0)
139  {
140  //The TLS Message Length field provides the total length of the TLS
141  //message or set of messages that is being fragmented
142  n = LOAD32BE(request->data);
143 
144  //Point to the next field
145  context->rxBufferPos += sizeof(uint32_t);
146  context->rxBufferLen -= sizeof(uint32_t);
147  }
148 
149  //Perform TLS handshake
150  error = tlsConnect(context->tlsContext);
151 
152  //Check status code
153  if(!error)
154  {
155  //EAP-TLS with TLS 1.3?
156  if(context->tlsContext->version == TLS_VERSION_1_3)
157  {
158  //When an EAP-TLS server has successfully processed the TLS client
159  //Finished and sent its last handshake message (Finished or a
160  //post-handshake message), it sends an encrypted TLS record with
161  //application data 0x00
162  error = tlsRead(context->tlsContext, &data, sizeof(data), &n, 0);
163 
164  //Check status code
165  if(!error)
166  {
167  //The 0x00 byte serves as protected success indication
168  if(n != sizeof(data) || data != 0x00)
169  {
170  //Report an error
171  error = ERROR_UNEXPECTED_VALUE;
172  }
173  }
174  }
175  }
176 
177  //Check status code
178  if(!error)
179  {
180  //Save TLS session
181  error = tlsSaveSessionState(context->tlsContext,
182  &context->tlsSession);
183  }
184  }
185  else
186  {
187  //Invalid method state
188  error = ERROR_WRONG_STATE;
189  }
190 
191  //Next, the method must update methodState and decision (refer to RFC 4137,
192  //section 4.2)
193  if(error == ERROR_WOULD_BLOCK)
194  {
195  //The authenticator can decide either to continue the method or to end
196  //the conversation
197  context->methodState = EAP_METHOD_STATE_MAY_CONT;
198 
199  //The decision variable is always set to FAIL
200  context->decision = EAP_DECISION_FAIL;
201  }
202  else
203  {
204  //The method never continues at this point
205  context->methodState = EAP_METHOD_STATE_DONE;
206 
207  //Check whether the EAP-TLS mutual authentication is successful
208  if(error == NO_ERROR)
209  {
210  //If both the server has informed us that it will allow access, and
211  //the next packet will be EAP Success, and we're willing to use this
212  //access, set decision to UNCOND_SUCC
213  context->decision = EAP_DECISION_UNCOND_SUCC;
214  }
215  else
216  {
217  //If either the authenticator has informed us that it will not allow
218  //access, or we're not willing to talk to this authenticator, set
219  //decision to FAIL
220  context->decision = EAP_DECISION_FAIL;
221  }
222 
223  //Close TLS session
224  eapCloseTls(context, error);
225  }
226 }
227 
228 
229 /**
230  * @brief Build EAP-TLS response
231  * @param[in] context Pointer to the 802.1X supplicant context
232  **/
233 
235 {
236  size_t n;
237  size_t fragLen;
238  uint8_t *p;
239  EapTlsPacket *response;
240 
241  //Point to the buffer where to format the EAP packet
242  response = (EapTlsPacket *) context->eapRespData;
243 
244  //Format EAP packet
245  response->code = EAP_CODE_RESPONSE;
246  response->identifier = context->reqId;
247  response->type = EAP_METHOD_TYPE_TLS;
248  response->flags = 0;
249 
250  //Point to the next field
251  p = response->data;
252  n = sizeof(EapTlsPacket);
253 
254  //Check the length of the TLS handshake message
255  if(context->txBufferLen < EAP_TLS_MAX_FRAG_SIZE)
256  {
257  //TLS handshake messages should not be fragmented into multiple TLS
258  //records if they fit within a single TLS record (refer to RFC 5216,
259  //section 2.1.5)
260  fragLen = context->txBufferLen;
261  }
262  else
263  {
264  //The M bit is set on all but the last fragment (refer to RFC 5216,
265  //section 3.2)
266  response->flags |= EAP_TLS_FLAGS_M;
267 
268  //First fragment?
269  if(context->txBufferReadPos == EAP_TLS_TX_BUFFER_START_POS)
270  {
271  //The L bit is set to indicate the presence of the four-octet TLS
272  //Message Length field, and must be set for the first fragment of a
273  //fragmented TLS message or set of messages
274  response->flags |= EAP_TLS_FLAGS_L;
275 
276  //The TLS Message Length field provides the total length of the TLS
277  //message or set of messages that is being fragmented
278  STORE32BE(context->txBufferLen, p);
279 
280  //Point to the next field
281  p += sizeof(uint32_t);
282  n += sizeof(uint32_t);
283 
284  //Calculate the length of the first fragment
285  fragLen = MIN(context->txBufferLen, EAP_TLS_MAX_INIT_FRAG_SIZE);
286  }
287  else
288  {
289  //Calculate the length of subsequent fragments
290  fragLen = MIN(context->txBufferLen, EAP_TLS_MAX_FRAG_SIZE);
291  }
292  }
293 
294  //The data consists of the encapsulated TLS packet in TLS record format
295  //(refer to RFC 5216, section 3.1)
296  osMemmove(p, context->txBuffer + context->txBufferReadPos, fragLen);
297 
298  //Total length of the EAP packet
299  n += fragLen;
300  //Convert the length field to network byte order
301  response->length = htons(n);
302 
303  //Debug message
304  TRACE_DEBUG("Sending EAP packet (%" PRIuSIZE " bytes)\r\n", n);
305  //Dump EAP header contents for debugging purpose
306  eapDumpHeader((EapPacket *) response);
307 
308  //Save the length of the EAP response
309  context->eapRespDataLen = n;
310 
311  //Point to the next fragment
312  context->txBufferReadPos += fragLen;
313  context->txBufferLen -= fragLen;
314 
315  //Last fragment?
316  if(context->txBufferLen == 0)
317  {
318  //Flush transmit buffer
319  context->txBufferWritePos = EAP_TLS_TX_BUFFER_START_POS;
320  context->txBufferReadPos = EAP_TLS_TX_BUFFER_START_POS;
321  context->txBufferLen = 0;
322  }
323 }
324 
325 
326 /**
327  * @brief Open TLS session
328  * @param[in] context Pointer to the 802.1X supplicant context
329  * @return Error code
330  **/
331 
333 {
334  error_t error;
335 
336  //Initialize TLS context
337  context->tlsContext = tlsInit();
338  //Initialization failed?
339  if(context->tlsContext == NULL)
340  return ERROR_OUT_OF_MEMORY;
341 
342  //Set the transport protocol to be used (EAP)
343  error = tlsSetTransportProtocol(context->tlsContext,
345  //Any error to report?
346  if(error)
347  return error;
348 
349  //Set send and receive callbacks (I/O abstraction layer)
350  error = tlsSetSocketCallbacks(context->tlsContext, eapTlsSendCallback,
352  //Any error to report?
353  if(error)
354  return error;
355 
356  //Select client operation mode
357  error = tlsSetConnectionEnd(context->tlsContext, TLS_CONNECTION_END_CLIENT);
358  //Any error to report?
359  if(error)
360  return error;
361 
362  //Invoke user-defined callback, if any
363  if(context->tlsInitCallback != NULL)
364  {
365  //Perform TLS related initialization
366  error = context->tlsInitCallback(context, context->tlsContext);
367  //Any error to report?
368  if(error)
369  return error;
370  }
371 
372  //Successful processing
373  return NO_ERROR;
374 }
375 
376 
377 /**
378  * @brief Close TLS session
379  * @param[in] context Pointer to the 802.1X supplicant context
380  * @param[in] error Status code describing the reason for closing the TLS
381  * session
382  **/
383 
384 void eapCloseTls(SupplicantContext *context, error_t error)
385 {
386  //Valid TLS context?
387  if(context->tlsContext != NULL)
388  {
389  //Invoke user-defined callback, if any
390  if(context->tlsCompleteCallback != NULL)
391  {
392  context->tlsCompleteCallback(context, context->tlsContext, error);
393  }
394 
395  //Release TLS context
396  tlsFree(context->tlsContext);
397  context->tlsContext = NULL;
398  }
399 }
400 
401 
402 /**
403  * @brief TLS send callback (I/O abstraction layer)
404  * @param[in] handle Pointer the 802.1X supplicant context
405  * @param[in] data Pointer to a buffer containing the data to be transmitted
406  * @param[in] length Number of data bytes to send
407  * @param[out] written Number of bytes that have been transmitted
408  * @param[in] flags Unused parameter
409  * @return Error code
410  **/
411 
412 error_t eapTlsSendCallback(void *handle, const void *data, size_t length,
413  size_t *written, uint_t flags)
414 {
415  error_t error;
416  SupplicantContext *context;
417 
418  //Point to the 802.1X supplicant context
419  context = (SupplicantContext *) handle;
420 
421  //Make sure the datagram is large enough to hold the TLS message
422  if((context->txBufferWritePos + length) <= SUPPLICANT_TX_BUFFER_SIZE)
423  {
424  //The data consists of the encapsulated TLS packet in TLS record format
425  //(refer to RFC 5216, section 3.1)
426  osMemcpy(context->txBuffer + context->txBufferWritePos, data, length);
427 
428  //Adjust the total length of the data
429  context->txBufferWritePos += length;
430  context->txBufferLen += length;
431 
432  //Total number of data that have been written
433  *written = length;
434 
435  //Successful processing
436  error = NO_ERROR;
437 
438  }
439  else
440  {
441  //Report an error
442  error = ERROR_BUFFER_OVERFLOW;
443  }
444 
445  //Return status code
446  return error;
447 }
448 
449 
450 /**
451  * @brief TLS receive callback (I/O abstraction layer)
452  * @param[in] handle Pointer the 802.1X supplicant context
453  * @param[out] data Buffer where to store the incoming data
454  * @param[in] size Maximum number of bytes that can be received
455  * @param[out] received Number of bytes that have been received
456  * @param[in] flags Unused parameter
457  * @return Error code
458  **/
459 
460 error_t eapTlsReceiveCallback(void *handle, void *data, size_t size,
461  size_t *received, uint_t flags)
462 {
463  error_t error;
464  size_t n;
465  SupplicantContext *context;
466 
467  //Point to the 802.1X supplicant context
468  context = (SupplicantContext *) handle;
469 
470  //Any data pending in the receive buffer?
471  if(context->rxBufferLen > 0)
472  {
473  //Limit the number of bytes to copy
474  n = MIN(context->rxBufferLen, size);
475 
476  //The data consists of the encapsulated TLS packet in TLS record format
477  //(refer to RFC 5216, section 3.2)
478  osMemcpy(data, context->rxBuffer + context->rxBufferPos, n);
479 
480  //Number of bytes left to process
481  context->rxBufferPos += n;
482  context->rxBufferLen -= n;
483 
484  //Total number of data that have been received
485  *received = n;
486 
487  //Successful processing
488  error = NO_ERROR;
489  }
490  else
491  {
492  //Report an error
493  error = ERROR_WOULD_BLOCK;
494  }
495 
496  //Return status code
497  return error;
498 }
499 
500 #endif
TlsContext * tlsInit(void)
TLS context initialization.
Definition: tls.c:65
#define htons(value)
Definition: cpu_endian.h:413
@ EAP_METHOD_TYPE_TLS
EAP-TLS.
Definition: eap.h:172
error_t tlsSetConnectionEnd(TlsContext *context, TlsConnectionEnd entity)
Set operation mode (client or server)
Definition: tls.c:349
@ EAP_TLS_FLAGS_L
Length included.
Definition: eap.h:186
void eapDumpHeader(const EapPacket *header)
Dump EAP header for debugging purpose.
Definition: eap_debug.c:105
error_t tlsSetTransportProtocol(TlsContext *context, TlsTransportProtocol transportProtocol)
Set the transport protocol to be used.
Definition: tls.c:319
error_t eapTlsSendCallback(void *handle, const void *data, size_t length, size_t *written, uint_t flags)
TLS send callback (I/O abstraction layer)
Definition: eap_tls.c:412
@ EAP_CODE_RESPONSE
Response.
Definition: eap.h:153
@ ERROR_WOULD_BLOCK
Definition: error.h:96
#define LOAD32BE(p)
Definition: cpu_endian.h:210
@ ERROR_BUFFER_OVERFLOW
Definition: error.h:142
uint8_t p
Definition: ndp.h:300
@ ERROR_UNEXPECTED_VALUE
Definition: error.h:72
uint8_t data[]
Definition: ethernet.h:222
void eapTlsBuildResponse(SupplicantContext *context)
Build EAP-TLS response.
Definition: eap_tls.c:234
#define SupplicantContext
Definition: supplicant.h:36
EapTlsPacket
Definition: eap.h:267
@ ERROR_CONNECTION_RESET
Definition: error.h:79
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
EAP-TLS authentication method.
@ EAP_METHOD_STATE_INIT
Definition: eap_peer_fsm.h:77
error_t tlsRestoreSessionState(TlsContext *context, const TlsSessionState *session)
Restore TLS session.
Definition: tls.c:2700
void eapCloseTls(SupplicantContext *context, error_t error)
Close TLS session.
Definition: eap_tls.c:384
@ ERROR_WRONG_STATE
Definition: error.h:209
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
error_t
Error codes.
Definition: error.h:43
error_t eapOpenTls(SupplicantContext *context)
Open TLS session.
Definition: eap_tls.c:332
#define SUPPLICANT_TX_BUFFER_SIZE
Definition: supplicant.h:72
@ EAP_DECISION_UNCOND_SUCC
Definition: eap_peer_fsm.h:92
#define TLS_VERSION_1_3
Definition: tls.h:97
void eapTlsProcessRequest(SupplicantContext *context, const EapTlsPacket *request, size_t length)
Process incoming EAP-TLS request.
Definition: eap_tls.c:79
@ ERROR_INVALID_LENGTH
Definition: error.h:111
@ TLS_TRANSPORT_PROTOCOL_EAP
Definition: tls.h:958
@ EAP_TLS_FLAGS_S
EAP-TLS start.
Definition: eap.h:188
error_t tlsSaveSessionState(const TlsContext *context, TlsSessionState *session)
Save TLS session.
Definition: tls.c:2631
uint8_t length
Definition: tcp.h:368
error_t tlsRead(TlsContext *context, void *data, size_t size, size_t *received, uint_t flags)
Receive application data from a the remote host using TLS.
Definition: tls.c:1989
#define MIN(a, b)
Definition: os_port.h:63
@ EAP_METHOD_STATE_CONT
Definition: eap_peer_fsm.h:78
error_t eapTlsReceiveCallback(void *handle, void *data, size_t size, size_t *received, uint_t flags)
TLS receive callback (I/O abstraction layer)
Definition: eap_tls.c:460
uint8_t flags
Definition: tcp.h:351
#define TRACE_DEBUG(...)
Definition: debug.h:107
@ EAP_DECISION_FAIL
Definition: eap_peer_fsm.h:90
Data logging functions for debugging purpose (EAP)
uint8_t n
@ EAP_TLS_FLAGS_M
More fragments.
Definition: eap.h:187
EapPacket
Definition: eap.h:224
error_t tlsSetSocketCallbacks(TlsContext *context, TlsSocketSendCallback socketSendCallback, TlsSocketReceiveCallback socketReceiveCallback, TlsSocketHandle handle)
Set socket send and receive callbacks.
Definition: tls.c:253
#define EAP_TLS_TX_BUFFER_START_POS
Definition: eap_tls.h:39
@ TLS_CONNECTION_END_CLIENT
Definition: tls.h:968
#define EAP_TLS_MAX_FRAG_SIZE
Definition: eap_tls.h:47
error_t eapTlsCheckRequest(SupplicantContext *context, const EapTlsPacket *request, size_t length)
Check incoming EAP-TLS request.
Definition: eap_tls.c:51
@ EAP_METHOD_STATE_DONE
Definition: eap_peer_fsm.h:80
void tlsFree(TlsContext *context)
Release TLS context.
Definition: tls.c:2469
@ EAP_METHOD_STATE_MAY_CONT
Definition: eap_peer_fsm.h:79
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:50
EAP (Extensible Authentication Protocol)
#define EAP_TLS_MAX_INIT_FRAG_SIZE
Definition: eap_tls.h:43
error_t tlsConnect(TlsContext *context)
Initiate the TLS handshake.
Definition: tls.c:1763
@ ERROR_INVALID_REQUEST
Definition: error.h:65
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
#define osMemmove(dest, src, length)
Definition: os_port.h:147
void * TlsSocketHandle
Socket handle.
Definition: tls.h:1905