1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** NetX Secure Component                                                 */
16 /**                                                                       */
17 /**    Datagram Transport Layer Security (DTLS)                           */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define NX_SECURE_SOURCE_CODE
23 
24 #include "nx_secure_dtls.h"
25 
26 #ifdef NX_SECURE_ENABLE_DTLS
27 /**************************************************************************/
28 /*                                                                        */
29 /*  FUNCTION                                               RELEASE        */
30 /*                                                                        */
31 /*    _nx_secure_dtls_process_record                      PORTABLE C      */
32 /*                                                           6.1.10       */
33 /*  AUTHOR                                                                */
34 /*                                                                        */
35 /*    Timothy Stapko, Microsoft Corporation                               */
36 /*                                                                        */
37 /*  DESCRIPTION                                                           */
38 /*                                                                        */
39 /*    This function processes a single DTLS record, handling both         */
40 /*    handshake and application records. When multiple records are        */
41 /*    received in a single lower-level network packet (e.g. UDP), the     */
42 /*    "record_offset" parameter is used to offset into the packet buffer. */
43 /*                                                                        */
44 /*  INPUT                                                                 */
45 /*                                                                        */
46 /*    dtls_session                          Pointer to DTLS control block */
47 /*    packet_ptr                            NX_PACKET containing a record */
48 /*    record_offset                         Offset of record in packet    */
49 /*    bytes_processed                       Return for size of packet     */
50 /*    wait_option                           Control timeout options       */
51 /*                                                                        */
52 /*  OUTPUT                                                                */
53 /*                                                                        */
54 /*    status                                Completion status             */
55 /*                                                                        */
56 /*  CALLS                                                                 */
57 /*                                                                        */
58 /*    _nx_secure_dtls_client_handshake      DTLS Client state machine     */
59 /*    _nx_secure_dtls_process_header        Process record header         */
60 /*    _nx_secure_dtls_server_handshake      DTLS Server state machine     */
61 /*    _nx_secure_dtls_verify_mac            Verify record MAC checksum    */
62 /*    _nx_secure_tls_process_changecipherspec                             */
63 /*                                          Process ChangeCipherSpec      */
64 /*    _nx_secure_tls_record_payload_decrypt Decrypt record data           */
65 /*    nx_secure_tls_packet_release          Release packet                */
66 /*    tx_mutex_get                          Get protection mutex          */
67 /*    tx_mutex_put                          Put protection mutex          */
68 /*                                                                        */
69 /*  CALLED BY                                                             */
70 /*                                                                        */
71 /*    _nx_secure_dtls_session_receive       Receive DTLS data             */
72 /*                                                                        */
73 /*  RELEASE HISTORY                                                       */
74 /*                                                                        */
75 /*    DATE              NAME                      DESCRIPTION             */
76 /*                                                                        */
77 /*  05-19-2020     Timothy Stapko           Initial Version 6.0           */
78 /*  09-30-2020     Timothy Stapko           Modified comment(s),          */
79 /*                                            adjusted logic for TLS      */
80 /*                                            modifications,              */
81 /*                                            resulting in version 6.1    */
82 /*  12-31-2020     Timothy Stapko           Modified comment(s),          */
83 /*                                            improved buffer length      */
84 /*                                            verification,               */
85 /*                                            resulting in version 6.1.3  */
86 /*  01-31-2022     Timothy Stapko           Modified comment(s),          */
87 /*                                            fixed out-of-order handling,*/
88 /*                                            resulting in version 6.1.10 */
89 /*                                                                        */
90 /**************************************************************************/
_nx_secure_dtls_process_record(NX_SECURE_DTLS_SESSION * dtls_session,NX_PACKET * packet_ptr,ULONG record_offset,ULONG * bytes_processed,ULONG wait_option)91 UINT _nx_secure_dtls_process_record(NX_SECURE_DTLS_SESSION *dtls_session, NX_PACKET *packet_ptr,
92                                     ULONG record_offset, ULONG *bytes_processed, ULONG wait_option)
93 {
94 UINT                   status;
95 UINT                   error_status;
96 USHORT                 header_length;
97 UCHAR                  header_data[NX_SECURE_DTLS_RECORD_HEADER_SIZE]; /* DTLS record header is larger than TLS. Allocate enough space for both. */
98 USHORT                 message_type;
99 UINT                   message_length;
100 UCHAR                 *packet_data;
101 NX_SECURE_TLS_SESSION *tls_session;
102 UCHAR                  epoch_seq_num[8];
103 ULONG                  seq_num_rewind[2];
104 ULONG                  window_rewind;
105 NX_PACKET             *decrypted_packet;
106 
107     /* Basic state machine:
108      * 1. Process header, which will set the state and return some data.
109      * 2. Advance the packet pointer by the size of the header (returned by header processing)
110      *    and process the record.
111      * 3. Take any actions necessary based on state.
112      */
113 
114     /* Get a reference to basic TLS state. */
115     tls_session = &dtls_session -> nx_secure_dtls_tls_session;
116 
117     header_length = NX_SECURE_DTLS_RECORD_HEADER_SIZE;
118 
119     /* Save the sequence number in case we reject a record (e.g. handshake message missing). */
120     seq_num_rewind[0] = tls_session -> nx_secure_tls_remote_sequence_number[0];
121     seq_num_rewind[1] = tls_session -> nx_secure_tls_remote_sequence_number[1];
122     window_rewind = dtls_session -> nx_secure_dtls_sliding_window;
123 
124     /* Process the DTLS record header, which will set the state. */
125     status = _nx_secure_dtls_process_header(dtls_session, packet_ptr, record_offset, &message_type, &message_length, header_data, &header_length);
126 
127     if (status == NX_SECURE_TLS_OUT_OF_ORDER_MESSAGE || status == NX_SECURE_TLS_INVALID_EPOCH || status == NX_SECURE_TLS_REPEAT_MESSAGE_RECEIVED)
128     {
129         /* Received an out-of-order message. Ignore it. */
130         *bytes_processed = (ULONG)header_length + message_length;
131 
132         /* Pretend this record never happened. */
133         return(NX_CONTINUE);
134     }
135 
136     if (status != NX_SECURE_TLS_SUCCESS)
137     {
138         return(status);
139     }
140 
141     /* Ignore empty records. */
142     if (message_length == 0)
143     {
144         /* Update the number of bytes we processed. */
145         *bytes_processed = (ULONG)header_length + message_length;
146         return(NX_CONTINUE);
147     }
148 
149     if (((ULONG)(packet_ptr -> nx_packet_append_ptr) - (ULONG)(packet_ptr -> nx_packet_prepend_ptr)) <
150         ((ULONG)header_length + message_length))
151     {
152 
153         /* Chained packet is not supported. */
154         return(NX_SECURE_TLS_INVALID_PACKET);
155     }
156 
157     /* Now extract the record. */
158     packet_data = &packet_ptr -> nx_packet_prepend_ptr[header_length];
159 
160     /*status = nx_packet_data_extract_offset(packet_ptr, header_length + record_offset, packet_data, message_length, &bytes_copied);*/
161 
162     /* Update the number of bytes we processed. */
163     *bytes_processed = (ULONG)header_length + message_length;
164 
165     /* Check for active encryption of incoming records. If encrypted, decrypt before further processing. */
166     if (tls_session -> nx_secure_tls_remote_session_active)
167     {
168         epoch_seq_num[0] = header_data[10];
169         epoch_seq_num[1] = header_data[9];
170         epoch_seq_num[2] = header_data[8];
171         epoch_seq_num[3] = header_data[7];
172         epoch_seq_num[4] = header_data[6];
173         epoch_seq_num[5] = header_data[5];
174         epoch_seq_num[6] = header_data[4];
175         epoch_seq_num[7] = header_data[3];
176 
177         /* Decrypt the record data. */
178         status = _nx_secure_tls_record_payload_decrypt(tls_session, packet_ptr, header_length, message_length,
179                                                        &decrypted_packet, (ULONG *)epoch_seq_num, (UCHAR)message_type,
180                                                        wait_option);
181 
182         /* Check the MAC hash. */
183         if (status == NX_SECURE_TLS_SUCCESS)
184         {
185 
186             /* Verify the hash MAC in the decrypted record. */
187             packet_data = decrypted_packet -> nx_packet_prepend_ptr;
188             message_length = (UINT)decrypted_packet -> nx_packet_length;
189             error_status = NX_SECURE_TLS_SUCCESS;
190         }
191         else
192         {
193 
194             /* Save off the error status so we can return it after the mac check. */
195             error_status = status;
196         }
197 
198         /* !!! NOTE - the MAC check MUST always be performed regardless of the error state of
199                       the payload decryption operation. Skipping the MAC check on padding failures
200                       could enable a timing-based attack allowing an attacker to determine whether
201                       padding was valid or not, causing an information leak. */
202         status = _nx_secure_dtls_verify_mac(dtls_session, header_data, header_length, packet_data, &message_length);
203 
204         /* Check to see if decryption or verification failed. */
205         if(error_status != NX_SECURE_TLS_SUCCESS)
206         {
207             /* Decryption failed. Rewind the sequence number and sliding window. */
208             tls_session -> nx_secure_tls_remote_sequence_number[0] = seq_num_rewind[0];
209             tls_session -> nx_secure_tls_remote_sequence_number[1] = seq_num_rewind[1];
210             dtls_session -> nx_secure_dtls_sliding_window = window_rewind;
211             return(error_status);
212         }
213 
214         /* Check to see if decryption or verification failed. */
215         if (status == NX_SECURE_TLS_SUCCESS)
216         {
217             /* Copy data to original packet to keep IP header available. */
218             /* Note: At the point of this memcpy the plaintext should never be larger than the cipher.
219                Assertion check is to protect against future changes inadvertently causing an overflow. */
220             NX_ASSERT((ULONG)(packet_ptr -> nx_packet_data_end - packet_ptr -> nx_packet_prepend_ptr) >= message_length);
221             NX_SECURE_MEMCPY(packet_ptr -> nx_packet_prepend_ptr, packet_data, message_length); /* Use case of memcpy is verified. */
222             packet_data = packet_ptr -> nx_packet_prepend_ptr;
223             packet_ptr -> nx_packet_append_ptr = packet_ptr -> nx_packet_prepend_ptr + message_length;
224             packet_ptr -> nx_packet_length = message_length;
225         }
226 
227         /* Release decrypted packet. */
228         nx_secure_tls_packet_release(decrypted_packet);
229 
230         /* Check to see if decryption or verification failed. */
231         if (status != NX_SECURE_TLS_SUCCESS)
232         {
233             return(status);
234         }
235     }
236 
237     switch (message_type)
238     {
239     case NX_SECURE_TLS_CHANGE_CIPHER_SPEC:
240         /* Received a ChangeCipherSpec message - from now on all messages from remote host
241            will be encrypted using the session keys. */
242         status = _nx_secure_tls_process_changecipherspec(tls_session, packet_data, message_length);
243         if(status != NX_SUCCESS)
244         {
245             /* Received out-of-order CCS message. */
246             status = NX_SECURE_TLS_OUT_OF_ORDER_MESSAGE;
247         }
248         else
249         {
250             /* New epoch if we receive a CCS message. */
251             dtls_session -> nx_secure_dtls_remote_epoch = (USHORT)(dtls_session -> nx_secure_dtls_remote_epoch + 1);
252         }
253 
254         break;
255     case NX_SECURE_TLS_ALERT:
256         /* We have received an alert. Check what the alert was and take appropriate action. */
257 
258         /* Save off the alert level and value for the application to access. */
259         tls_session->nx_secure_tls_received_alert_level = packet_data[0];
260         tls_session->nx_secure_tls_received_alert_value = packet_data[1];
261 
262         status = NX_SECURE_TLS_ALERT_RECEIVED;
263         break;
264     case NX_SECURE_TLS_HANDSHAKE:
265 #ifndef NX_SECURE_TLS_SERVER_DISABLED
266         /* The socket is a TLS server, so process incoming handshake messages in that context. */
267         if (tls_session -> nx_secure_tls_socket_type == NX_SECURE_TLS_SESSION_TYPE_SERVER)
268         {
269             status = _nx_secure_dtls_server_handshake(dtls_session, packet_data, message_length, wait_option);
270         }
271 #endif
272 #ifndef NX_SECURE_TLS_CLIENT_DISABLED
273         /* The socket is a TLS client, so process incoming handshake messages in that context. */
274         if (tls_session -> nx_secure_tls_socket_type == NX_SECURE_TLS_SESSION_TYPE_CLIENT)
275         {
276             status = _nx_secure_dtls_client_handshake(dtls_session, packet_data, message_length, wait_option);
277         }
278 #endif
279         break;
280     case NX_SECURE_TLS_APPLICATION_DATA:
281         /* The remote host is sending application data records now. Pass decrypted data back
282            to the networking API for the application to process. */
283 
284         /* First, check for 0-length records. These can be sent but are internal to TLS so tell the
285            caller to continue receiving. 0-length records are sent as part of the BEAST attack
286            mitigation by some TLS implementations (notably OpenSSL). */
287         if (message_length == 0)
288         {
289             status = NX_CONTINUE;
290         }
291         break;
292     default:
293         /* An unrecognized message was received, likely an error, possibly an attack. */
294         status = NX_SECURE_TLS_UNRECOGNIZED_MESSAGE_TYPE;
295         break;
296     }
297 
298     /* If we received an out-of-order handshake message, rewind the sequence number. */
299     if(status == NX_SECURE_TLS_OUT_OF_ORDER_MESSAGE)
300     {
301         /* Handshake message was not what we expected, so rewind the sequence number. */
302         tls_session -> nx_secure_tls_remote_sequence_number[0] = seq_num_rewind[0];
303         tls_session -> nx_secure_tls_remote_sequence_number[1] = seq_num_rewind[1];
304         dtls_session -> nx_secure_dtls_sliding_window = window_rewind;
305         status = NX_CONTINUE;
306     }
307 
308 
309     return(status);
310 }
311 #endif /* NX_SECURE_ENABLE_DTLS */
312 
313