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