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