eap_tls.c
Go to the documentation of this file.
1 /**
2  * @file eap_tls.c
3  * @brief EAP-TLS authentication protocol
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2022-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneEAP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL 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  //Release TLS context
97  eapCloseTls(context);
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  //Release TLS context
224  eapCloseTls(context);
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 Initialize TLS context
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 Release TLS context
379  * @param[in] context Pointer to the 802.1X supplicant context
380  **/
381 
383 {
384  //Release TLS context
385  if(context->tlsContext != NULL)
386  {
387  tlsFree(context->tlsContext);
388  context->tlsContext = NULL;
389  }
390 }
391 
392 
393 /**
394  * @brief TLS send callback (I/O abstraction layer)
395  * @param[in] handle Pointer the 802.1X supplicant context
396  * @param[in] data Pointer to a buffer containing the data to be transmitted
397  * @param[in] length Number of data bytes to send
398  * @param[out] written Number of bytes that have been transmitted
399  * @param[in] flags Unused parameter
400  * @return Error code
401  **/
402 
403 error_t eapTlsSendCallback(void *handle, const void *data, size_t length,
404  size_t *written, uint_t flags)
405 {
406  error_t error;
407  SupplicantContext *context;
408 
409  //Point to the 802.1X supplicant context
410  context = (SupplicantContext *) handle;
411 
412  //Make sure the datagram is large enough to hold the TLS message
413  if((context->txBufferWritePos + length) <= SUPPLICANT_TX_BUFFER_SIZE)
414  {
415  //The data consists of the encapsulated TLS packet in TLS record format
416  //(refer to RFC 5216, section 3.1)
417  osMemcpy(context->txBuffer + context->txBufferWritePos, data, length);
418 
419  //Adjust the total length of the data
420  context->txBufferWritePos += length;
421  context->txBufferLen += length;
422 
423  //Total number of data that have been written
424  *written = length;
425 
426  //Successful processing
427  error = NO_ERROR;
428 
429  }
430  else
431  {
432  //Report an error
433  error = ERROR_BUFFER_OVERFLOW;
434  }
435 
436  //Return status code
437  return error;
438 }
439 
440 
441 /**
442  * @brief TLS receive callback (I/O abstraction layer)
443  * @param[in] handle Pointer the 802.1X supplicant context
444  * @param[out] data Buffer where to store the incoming data
445  * @param[in] size Maximum number of bytes that can be received
446  * @param[out] received Number of bytes that have been received
447  * @param[in] flags Unused parameter
448  * @return Error code
449  **/
450 
451 error_t eapTlsReceiveCallback(void *handle, void *data, size_t size,
452  size_t *received, uint_t flags)
453 {
454  error_t error;
455  size_t n;
456  SupplicantContext *context;
457 
458  //Point to the 802.1X supplicant context
459  context = (SupplicantContext *) handle;
460 
461  //Any data pending in the receive buffer?
462  if(context->rxBufferLen > 0)
463  {
464  //Limit the number of bytes to copy
465  n = MIN(context->rxBufferLen, size);
466 
467  //The data consists of the encapsulated TLS packet in TLS record format
468  //(refer to RFC 5216, section 3.2)
469  osMemcpy(data, context->rxBuffer + context->rxBufferPos, n);
470 
471  //Number of bytes left to process
472  context->rxBufferPos += n;
473  context->rxBufferLen -= n;
474 
475  //Total number of data that have been received
476  *received = n;
477 
478  //Successful processing
479  error = NO_ERROR;
480  }
481  else
482  {
483  //Report an error
484  error = ERROR_WOULD_BLOCK;
485  }
486 
487  //Return status code
488  return error;
489 }
490 
491 #endif
unsigned int uint_t
Definition: compiler_port.h:50
#define PRIuSIZE
#define LOAD32BE(p)
Definition: cpu_endian.h:210
#define htons(value)
Definition: cpu_endian.h:413
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
uint8_t n
EAP (Extensible Authentication Protocol)
@ EAP_CODE_RESPONSE
Response.
Definition: eap.h:153
@ EAP_TLS_FLAGS_L
Length included.
Definition: eap.h:186
@ EAP_TLS_FLAGS_S
EAP-TLS start.
Definition: eap.h:188
@ EAP_TLS_FLAGS_M
More fragments.
Definition: eap.h:187
EapPacket
Definition: eap.h:224
EapTlsPacket
Definition: eap.h:267
@ EAP_METHOD_TYPE_TLS
EAP-TLS.
Definition: eap.h:172
void eapDumpHeader(const EapPacket *header)
Dump EAP header for debugging purpose.
Definition: eap_debug.c:105
Data logging functions for debugging purpose (EAP)
@ EAP_METHOD_STATE_INIT
Definition: eap_peer_fsm.h:77
@ EAP_METHOD_STATE_MAY_CONT
Definition: eap_peer_fsm.h:79
@ EAP_METHOD_STATE_DONE
Definition: eap_peer_fsm.h:80
@ EAP_METHOD_STATE_CONT
Definition: eap_peer_fsm.h:78
@ EAP_DECISION_UNCOND_SUCC
Definition: eap_peer_fsm.h:92
@ EAP_DECISION_FAIL
Definition: eap_peer_fsm.h:90
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:403
void eapTlsBuildResponse(SupplicantContext *context)
Build EAP-TLS response.
Definition: eap_tls.c:234
void eapCloseTls(SupplicantContext *context)
Release TLS context.
Definition: eap_tls.c:382
error_t eapTlsCheckRequest(SupplicantContext *context, const EapTlsPacket *request, size_t length)
Check incoming EAP-TLS request.
Definition: eap_tls.c:51
void eapTlsProcessRequest(SupplicantContext *context, const EapTlsPacket *request, size_t length)
Process incoming EAP-TLS request.
Definition: eap_tls.c:79
error_t eapOpenTls(SupplicantContext *context)
Initialize TLS context.
Definition: eap_tls.c:332
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:451
EAP-TLS authentication protocol.
#define EAP_TLS_MAX_INIT_FRAG_SIZE
Definition: eap_tls.h:43
#define EAP_TLS_TX_BUFFER_START_POS
Definition: eap_tls.h:39
#define EAP_TLS_MAX_FRAG_SIZE
Definition: eap_tls.h:47
error_t
Error codes.
Definition: error.h:43
@ ERROR_UNEXPECTED_VALUE
Definition: error.h:72
@ ERROR_WOULD_BLOCK
Definition: error.h:96
@ ERROR_INVALID_REQUEST
Definition: error.h:65
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_BUFFER_OVERFLOW
Definition: error.h:142
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
@ ERROR_WRONG_STATE
Definition: error.h:209
@ ERROR_INVALID_LENGTH
Definition: error.h:111
uint8_t data[]
Definition: ethernet.h:222
uint8_t p
Definition: ndp.h:300
#define osMemmove(dest, src, length)
Definition: os_port.h:147
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define MIN(a, b)
Definition: os_port.h:63
#define SupplicantContext
Definition: supplicant.h:36
#define SUPPLICANT_TX_BUFFER_SIZE
Definition: supplicant.h:72
uint8_t length
Definition: tcp.h:368
uint8_t flags
Definition: tcp.h:351
error_t tlsRestoreSessionState(TlsContext *context, const TlsSessionState *session)
Restore TLS session.
Definition: tls.c:2690
error_t tlsConnect(TlsContext *context)
Initiate the TLS handshake.
Definition: tls.c:1758
error_t tlsSaveSessionState(const TlsContext *context, TlsSessionState *session)
Save TLS session.
Definition: tls.c:2621
TlsContext * tlsInit(void)
TLS context initialization.
Definition: tls.c:65
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:1984
error_t tlsSetConnectionEnd(TlsContext *context, TlsConnectionEnd entity)
Set operation mode (client or server)
Definition: tls.c:344
error_t tlsSetSocketCallbacks(TlsContext *context, TlsSocketSendCallback socketSendCallback, TlsSocketReceiveCallback socketReceiveCallback, TlsSocketHandle handle)
Set socket send and receive callbacks.
Definition: tls.c:248
void tlsFree(TlsContext *context)
Release TLS context.
Definition: tls.c:2464
error_t tlsSetTransportProtocol(TlsContext *context, TlsTransportProtocol transportProtocol)
Set the transport protocol to be used.
Definition: tls.c:314
@ TLS_TRANSPORT_PROTOCOL_EAP
Definition: tls.h:943
void * TlsSocketHandle
Socket handle.
Definition: tls.h:1888
#define TLS_VERSION_1_3
Definition: tls.h:97
@ TLS_CONNECTION_END_CLIENT
Definition: tls.h:953