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 #include "nx_udp.h"
26 
27 /**************************************************************************/
28 /*                                                                        */
29 /*  FUNCTION                                               RELEASE        */
30 /*                                                                        */
31 /*    _nx_secure_dtls_receive_callback                    PORTABLE C      */
32 /*                                                           6.1          */
33 /*  AUTHOR                                                                */
34 /*                                                                        */
35 /*    Timothy Stapko, Microsoft Corporation                               */
36 /*                                                                        */
37 /*  DESCRIPTION                                                           */
38 /*                                                                        */
39 /*    This function serves as the notification callback provided to NetX  */
40 /*    that is invoked when a UDP packet has been received. It checks the  */
41 /*    DTLS session cache for a matching client (based on IP address and   */
42 /*    port) and then invokes the application callback with the            */
43 /*    appropriate session object. If a previous session was not found, a  */
44 /*    new session is allocated (if available) and the DTLS handshake is   */
45 /*    initiated. Once the handshake is complete the user callback is      */
46 /*    invoked as above.                                                   */
47 /*                                                                        */
48 /*  INPUT                                                                 */
49 /*                                                                        */
50 /*    socket_ptr                            UDP socket                    */
51 /*                                                                        */
52 /*  OUTPUT                                                                */
53 /*                                                                        */
54 /*    N/A                                                                 */
55 /*                                                                        */
56 /*  CALLS                                                                 */
57 /*                                                                        */
58 /*    _nxd_udp_source_extract               Extract IP address and port   */
59 /*    _nx_udp_socket_port_get               Get local port                */
60 /*    nx_secure_dtls_session_cache_find     Check DTLS session cache      */
61 /*    nx_secure_dtls_session_cache_get_new  Get new DTLS session          */
62 /*    nx_secure_tls_packet_release          Release packet                */
63 /*    nx_packet_allocate                    Allocate packet               */
64 /*    _nxd_udp_socket_send                  Send UDP packet               */
65 /*    nx_udp_socket_receive                 Receive packet                */
66 /*    tx_thread_wait_abort                  Abort wait process            */
67 /*    tx_mutex_get                          Get protection mutex          */
68 /*    tx_mutex_put                          Put protection mutex          */
69 /*    [nx_secure_dtls_error_notify]         Notify application of error   */
70 /*    [nx_secure_dtls_receive_notify]       Notify application of packet  */
71 /*                                            receive                     */
72 /*                                                                        */
73 /*  CALLED BY                                                             */
74 /*                                                                        */
75 /*    Application Code                                                    */
76 /*                                                                        */
77 /*  RELEASE HISTORY                                                       */
78 /*                                                                        */
79 /*    DATE              NAME                      DESCRIPTION             */
80 /*                                                                        */
81 /*  05-19-2020     Timothy Stapko           Initial Version 6.0           */
82 /*  09-30-2020     Timothy Stapko           Modified comment(s),          */
83 /*                                            released packet securely,   */
84 /*                                            resulting in version 6.1    */
85 /*                                                                        */
86 /**************************************************************************/
_nx_secure_dtls_receive_callback(NX_UDP_SOCKET * socket_ptr)87 VOID _nx_secure_dtls_receive_callback(NX_UDP_SOCKET *socket_ptr)
88 {
89 #ifdef NX_SECURE_ENABLE_DTLS
90 TX_INTERRUPT_SAVE_AREA
91 
92 NXD_ADDRESS ip_address;
93 UINT remote_port;
94 UINT local_port;
95 UINT status;
96 ULONG packet_type;
97 NX_PACKET *packet_ptr;
98 UCHAR *send_data;
99 NX_PACKET *temp_packet_ptr;
100 NX_SECURE_DTLS_SERVER *dtls_server;
101 NX_SECURE_DTLS_SESSION *dtls_session;
102 
103     if(socket_ptr -> nx_udp_socket_receive_head == NX_NULL)
104     {
105       return;
106     }
107 
108     /* Extract IP address and port from received packet. */
109     socket_ptr -> nx_udp_socket_receive_head -> nx_packet_prepend_ptr += sizeof(NX_UDP_HEADER);
110     status =  _nxd_udp_source_extract(socket_ptr->nx_udp_socket_receive_head, &ip_address, &remote_port);
111     socket_ptr -> nx_udp_socket_receive_head -> nx_packet_prepend_ptr -= sizeof(NX_UDP_HEADER);
112 
113     if(status != NX_SUCCESS)
114     {
115         return;
116     }
117 
118     /* Extract local port from socket. */
119     status = _nx_udp_socket_port_get(socket_ptr, &local_port);
120 
121     if(status != NX_SUCCESS)
122     {
123         return;
124     }
125 
126     /* Get the DTLS server from our UDP socket so we can access the session cache. */
127     dtls_server = (NX_SECURE_DTLS_SERVER*)(socket_ptr -> nx_udp_socket_reserved_ptr);
128 
129     /* Check session cache for existing DTLS session. */
130     status = nx_secure_dtls_session_cache_find(dtls_server, &dtls_session, &ip_address, remote_port, local_port);
131 
132     /* Do we have an established session? */
133     if(status == NX_SECURE_DTLS_SESSION_NOT_FOUND)
134     {
135 
136         /* If received packet is ALERT, just drop it.  */
137         if ((socket_ptr -> nx_udp_socket_receive_head -> nx_packet_length < NX_SECURE_DTLS_RECORD_HEADER_SIZE) ||
138             (socket_ptr -> nx_udp_socket_receive_head -> nx_packet_prepend_ptr[8] == NX_SECURE_TLS_ALERT))
139         {
140 
141             /* Lockout interrupts.  */
142             TX_DISABLE
143 
144             /* Remove the header packet from the queue.  */
145             packet_ptr = socket_ptr -> nx_udp_socket_receive_head;
146             socket_ptr -> nx_udp_socket_receive_head = packet_ptr -> nx_packet_queue_next;
147             packet_ptr -> nx_packet_queue_next = NX_NULL;
148 
149             /* If this was the last packet, set the tail pointer to NULL.  */
150             if (socket_ptr -> nx_udp_socket_receive_head == NX_NULL)
151             {
152                 socket_ptr -> nx_udp_socket_receive_tail =  NX_NULL;
153             }
154 
155             /* Decrease the queued packet count.  */
156             socket_ptr -> nx_udp_socket_receive_count--;
157 
158             /* Restore interrupts.  */
159             TX_RESTORE
160 
161             /* Release the packet.  */
162             nx_secure_tls_packet_release(packet_ptr);
163 
164             return;
165         }
166 
167         /* Get a new session. */
168         status = nx_secure_dtls_session_cache_get_new(dtls_server, &dtls_session, &ip_address, remote_port, local_port);
169 
170         /* Make sure we got a session. */
171         if(status == NX_SECURE_TLS_NO_FREE_DTLS_SESSIONS)
172         {
173             /* No session? Drop the connection with an internal error alert.
174                See RFC 5246 Sec. 7.2.2 - internal error is used for memory allocation failures.
175 
176                We don't have a DTLS session, so build a simple DTLS alert record to send. */
177 
178             /* Lockout interrupts.  */
179             TX_DISABLE
180 
181             /* Remove the header packet from the queue.  */
182             packet_ptr = socket_ptr -> nx_udp_socket_receive_head;
183             socket_ptr -> nx_udp_socket_receive_head = packet_ptr -> nx_packet_queue_next;
184             packet_ptr -> nx_packet_queue_next = NX_NULL;
185 
186             /* If this was the last packet, set the tail pointer to NULL.  */
187             if (socket_ptr -> nx_udp_socket_receive_head == NX_NULL)
188             {
189                 socket_ptr -> nx_udp_socket_receive_tail =  NX_NULL;
190             }
191 
192             /* Decrease the queued packet count.  */
193             socket_ptr -> nx_udp_socket_receive_count--;
194 
195             /* Restore interrupts.  */
196             TX_RESTORE
197 
198             /* Release the packet.  */
199             nx_secure_tls_packet_release(packet_ptr);
200 
201             /* Get a packet to send the alert to the client. */
202             if (ip_address.nxd_ip_version == NX_IP_VERSION_V4)
203             {
204                 packet_type = NX_IPv4_UDP_PACKET;
205             }
206             else
207             {
208                 packet_type = NX_IPv6_UDP_PACKET;
209             }
210 
211             status =  nx_packet_allocate(socket_ptr->nx_udp_socket_ip_ptr->nx_ip_default_packet_pool,
212                                          &packet_ptr, packet_type, NX_NO_WAIT);
213             if(status != NX_SUCCESS)
214             {
215                 return;
216             }
217 
218             if (((ULONG)(packet_ptr -> nx_packet_data_end) - (ULONG)(packet_ptr -> nx_packet_append_ptr)) < 15)
219             {
220 
221                 /* Packet buffer too small. */
222                 nx_secure_tls_packet_release(packet_ptr);
223                 return;
224             }
225 
226             send_data = packet_ptr -> nx_packet_append_ptr;
227 
228             /* Build the DTLS record header. */
229             send_data[0] = NX_SECURE_TLS_ALERT;
230 
231             /* Set the version number - use DTLS 1.2. */
232             send_data[1] = (UCHAR)(NX_SECURE_DTLS_VERSION_MAJOR);
233             send_data[2] = (UCHAR)(NX_SECURE_DTLS_VERSION_MINOR_1_2);
234 
235             /* DTLS Epoch counter. */
236             send_data[3] = 0;
237             send_data[4] = 0;
238 
239             /* DTLS sequence number. */
240             send_data[5]  = 0;
241             send_data[6]  = 0;
242             send_data[7]  = 0;
243             send_data[8]  = 0;
244             send_data[9]  = 0;
245             send_data[10] = 0;
246 
247             /* DTLS message length - 2 bytes for the alert. */
248             send_data[11] = 0;
249             send_data[12] = 2;
250 
251             /* Populate the record with the alert level and alert number to send to the remote host. */
252             send_data[13] = (UCHAR)(NX_SECURE_TLS_ALERT_LEVEL_FATAL);
253             send_data[14] = (UCHAR)(NX_SECURE_TLS_ALERT_INTERNAL_ERROR);
254 
255             /* Make sure the caller has the right length of data to send. */
256             packet_ptr -> nx_packet_append_ptr = packet_ptr -> nx_packet_append_ptr + 15;
257             packet_ptr -> nx_packet_length = 15;
258 
259             /* Send the UDP packet containing our record. */
260             status = _nxd_udp_socket_send(socket_ptr, packet_ptr, &ip_address, remote_port);
261 
262             /* Notify the application that we had to drop an incoming connection. */
263             if(dtls_server->nx_secure_dtls_error_notify != NX_NULL)
264             {
265                 dtls_server->nx_secure_dtls_error_notify(NX_NULL, NX_SECURE_TLS_NO_FREE_DTLS_SESSIONS);
266             }
267 
268             return;
269         }
270 
271         /* Receive our packet. */
272         status =  nx_udp_socket_receive(socket_ptr, &packet_ptr, NX_NO_WAIT);
273         if (status)
274         {
275             return;
276         }
277 
278         /* New session, retrieve the UDP packet and place in our session receive queue. */
279         dtls_session -> nx_secure_dtls_receive_queue_head = packet_ptr;
280 
281         /* Make sure the queue pointer is cleared. */
282         packet_ptr -> nx_packet_queue_next = NX_NULL;
283 
284         /* Notify the application of a new connection - it is up to the application to call
285            nx_secure_dtls_session_start to kick off the handshake! */
286         dtls_server -> nx_secure_dtls_connect_notify(dtls_session, &ip_address, remote_port);
287     }
288     else
289     {
290 
291         /* Receive our packet. */
292         status =  nx_udp_socket_receive(socket_ptr, &packet_ptr, NX_NO_WAIT);
293         if (status)
294         {
295             return;
296         }
297 
298         /* Get the protection before modifying the queue pointer. */
299         tx_mutex_get(&_nx_secure_tls_protection, TX_WAIT_FOREVER);
300 
301         if (!dtls_session -> nx_secure_dtls_session_in_use)
302         {
303 
304             /* Session is not in use.  */
305             tx_mutex_put(&_nx_secure_tls_protection);
306             return;
307         }
308 
309         /* Established session, make sure that we append to the end of the receive queue. */
310         if (dtls_session -> nx_secure_dtls_receive_queue_head == NX_NULL)
311         {
312             dtls_session -> nx_secure_dtls_receive_queue_head = packet_ptr;
313         }
314         else
315         {
316             temp_packet_ptr = dtls_session -> nx_secure_dtls_receive_queue_head;
317             while(temp_packet_ptr -> nx_packet_queue_next != NX_NULL)
318             {
319                 temp_packet_ptr = temp_packet_ptr -> nx_packet_queue_next;
320             }
321 
322             temp_packet_ptr -> nx_packet_queue_next = packet_ptr;
323         }
324 
325         /* Make sure the queue pointer is cleared. */
326         packet_ptr -> nx_packet_queue_next = NX_NULL;
327 
328         /* Is there any thread waiting for packet? */
329         if (dtls_session -> nx_secure_dtls_thread_suspended)
330         {
331             /* Yes. Just abort it. */
332             tx_thread_wait_abort(dtls_session -> nx_secure_dtls_thread_suspended);
333             dtls_session -> nx_secure_dtls_thread_suspended = NX_NULL;
334         }
335 
336         /* If the handshake isn't finished, don't notify application. */
337         if (dtls_session -> nx_secure_dtls_tls_session.nx_secure_tls_server_state < NX_SECURE_TLS_SERVER_STATE_HANDSHAKE_FINISHED)
338         {
339 
340             /* Release the protection. */
341             tx_mutex_put(&_nx_secure_tls_protection);
342             return;
343         }
344 
345         /* Release the protection. */
346         tx_mutex_put(&_nx_secure_tls_protection);
347 
348         /* Invoke the session callback to notify application of packet receive. */
349         dtls_server -> nx_secure_dtls_receive_notify(dtls_session);
350     }
351 #else
352     NX_PARAMETER_NOT_USED(socket_ptr);
353 
354     return;
355 #endif /* NX_SECURE_ENABLE_DTLS */
356 }
357 
358