Mercurial > hg > freeDiameter-dtls
comparison libfdcore/sctp_dtls.c @ 1188:e1ced4db7f67
Backup work in progress on DTLS, not usable
author | Sebastien Decugis <sdecugis@freediameter.net> |
---|---|
date | Tue, 11 Jun 2013 18:13:29 +0800 |
parents | |
children | 1e8267ad057c |
comparison
equal
deleted
inserted
replaced
1187:b4a0f9ee3129 | 1188:e1ced4db7f67 |
---|---|
1 /********************************************************************************************************* | |
2 * Software License Agreement (BSD License) * | |
3 * Author: Sebastien Decugis <sdecugis@freediameter.net> * | |
4 * * | |
5 * Copyright (c) 2013, WIDE Project and NICT * | |
6 * All rights reserved. * | |
7 * * | |
8 * Redistribution and use of this software in source and binary forms, with or without modification, are * | |
9 * permitted provided that the following conditions are met: * | |
10 * * | |
11 * * Redistributions of source code must retain the above * | |
12 * copyright notice, this list of conditions and the * | |
13 * following disclaimer. * | |
14 * * | |
15 * * Redistributions in binary form must reproduce the above * | |
16 * copyright notice, this list of conditions and the * | |
17 * following disclaimer in the documentation and/or other * | |
18 * materials provided with the distribution. * | |
19 * * | |
20 * * Neither the name of the WIDE Project or NICT nor the * | |
21 * names of its contributors may be used to endorse or * | |
22 * promote products derived from this software without * | |
23 * specific prior written permission of WIDE Project and * | |
24 * NICT. * | |
25 * * | |
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * | |
27 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * | |
28 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * | |
29 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * | |
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * | |
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * | |
32 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * | |
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * | |
34 *********************************************************************************************************/ | |
35 | |
36 /* This file contains the code for DTLS over multi-stream SCTP implementation */ | |
37 | |
38 #include "fdcore-internal.h" | |
39 #include "cnxctx.h" | |
40 | |
41 | |
42 #define DTLS_TYPE_application_data 23 | |
43 | |
44 #ifdef GNUTLS_VERSION_300 | |
45 /* Check if data is available for gnutls on a given context */ | |
46 static int sctp_dtls_pull_timeout(gnutls_transport_ptr_t tr, unsigned int ms) | |
47 { | |
48 struct cnxctx * conn = (struct cnxctx *)tr; | |
49 fd_set rfds; | |
50 struct timeval tv; | |
51 | |
52 FD_ZERO (&rfds); | |
53 FD_SET (conn->cc_socket, &rfds); | |
54 | |
55 tv.tv_sec = 0; | |
56 tv.tv_usec = ms * 1000; | |
57 | |
58 while(tv.tv_usec >= 1000000) | |
59 { | |
60 tv.tv_usec -= 1000000; | |
61 tv.tv_sec++; | |
62 } | |
63 | |
64 return select (conn->cc_socket + 1, &rfds, NULL, NULL, &tv); | |
65 } | |
66 #endif /* GNUTLS_VERSION_300 */ | |
67 | |
68 /* Send data over the connection, called by gnutls */ | |
69 static ssize_t sctp_dtls_pushv(gnutls_transport_ptr_t tr, const giovec_t * iov, int iovcnt) | |
70 { | |
71 struct cnxctx * conn = (struct cnxctx *)tr; | |
72 ssize_t ret; | |
73 | |
74 TRACE_ENTRY("%p %p %d", tr, iov, iovcnt); | |
75 CHECK_PARAMS_DO( tr && iov, { errno = EINVAL; return -1; } ); | |
76 | |
77 /* If no unordered delivery is allowed, send over stream 0 always */ | |
78 if (conn->cc_sctp_para.unordered == 0) { | |
79 ret = fd_sctp_sendstrv(conn, 0, (const struct iovec *)iov, iovcnt); | |
80 } | |
81 /* Otherwise, we need to check the type of record. */ | |
82 else { | |
83 if ((iovcnt > 0) | |
84 && (iov->iov_len > 0) && | |
85 (*((uint8_t *)iov->iov_base) == DTLS_TYPE_application_data)) { | |
86 /* Data is sent over different streams */ | |
87 if (conn->cc_sctp_para.str_out > 32) { | |
88 TODO("Limiting to 32 streams. Remove this limit when anti-replay is disabled"); | |
89 conn->cc_sctp_para.str_out = 32; | |
90 } | |
91 if (conn->cc_sctp_para.str_out > 1) { | |
92 conn->cc_sctp_para.next += 1; | |
93 conn->cc_sctp_para.next %= conn->cc_sctp_para.str_out; | |
94 } else { | |
95 conn->cc_sctp_para.next = 0; | |
96 } | |
97 ret = fd_sctp_sendstrv(conn, conn->cc_sctp_para.next, (const struct iovec *)iov, iovcnt); | |
98 | |
99 } else { | |
100 /* other TLS messages are always sent over stream 0 */ | |
101 ret = fd_sctp_sendstrv(conn, 0, (const struct iovec *)iov, iovcnt); | |
102 } | |
103 } | |
104 | |
105 return ret; | |
106 } | |
107 | |
108 #ifndef GNUTLS_VERSION_212 | |
109 static ssize_t sctp_dtls_push(gnutls_transport_ptr_t tr, const void * data, size_t len) | |
110 { | |
111 giovec_t iov; | |
112 iov.iov_base = (void *)data; | |
113 iov.iov_len = len; | |
114 return sctp_dtls_pushv(tr, &iov, 1); | |
115 } | |
116 #endif /* GNUTLS_VERSION_212 */ | |
117 | |
118 /* Retrieve data received on any stream */ | |
119 static ssize_t sctp_dtls_pull(gnutls_transport_ptr_t tr, void * buf, size_t len) | |
120 { | |
121 struct cnxctx * conn = (struct cnxctx *)tr; | |
122 | |
123 /* If needed we can use fd_sctp_recvmeta to retrieve more information here */ | |
124 return fd_cnx_s_recv(conn, buf, len); | |
125 } | |
126 | |
127 /* Set the parameters of a session to use the cnxctx object */ | |
128 #ifndef GNUTLS_VERSION_300 | |
129 GCC_DIAG_OFF("-Wdeprecated-declarations") | |
130 #endif /* !GNUTLS_VERSION_300 */ | |
131 int fd_sctp_dtls_settransport(gnutls_session_t session, struct cnxctx * conn) | |
132 { | |
133 /* Set the transport pointer passed to push & pull callbacks */ | |
134 GNUTLS_TRACE( gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) conn ) ); | |
135 | |
136 /* Reset the low water value, since we don't use sockets */ | |
137 #ifndef GNUTLS_VERSION_300 | |
138 /* starting version 2.12, this call is not needed */ | |
139 GNUTLS_TRACE( gnutls_transport_set_lowat( session, 0 ) ); | |
140 #else /* GNUTLS_VERSION_300 */ | |
141 /* but in 3.0 we have to provide the pull_timeout callback */ | |
142 GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( session, sctp_dtls_pull_timeout ) ); | |
143 #endif /* GNUTLS_VERSION_300 */ | |
144 | |
145 /* Set the push and pull callbacks */ | |
146 GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctp_dtls_pull) ); | |
147 #ifndef GNUTLS_VERSION_212 | |
148 GNUTLS_TRACE( gnutls_transport_set_push_function(session, sctp_dtls_push) ); | |
149 #else /* GNUTLS_VERSION_212 */ | |
150 GNUTLS_TRACE( gnutls_transport_set_vec_push_function(session, sctp_dtls_pushv) ); | |
151 #endif /* GNUTLS_VERSION_212 */ | |
152 | |
153 return; | |
154 } | |
155 #ifndef GNUTLS_VERSION_300 | |
156 GCC_DIAG_ON("-Wdeprecated-declarations") | |
157 #endif /* !GNUTLS_VERSION_300 */ | |
158 | |
159 | |
160 | |
161 | |
162 /* Set additional session parameters before handshake. The GNUTLS_DATAGRAM is already set in fd_tls_prepare */ | |
163 int fd_sctp_dtls_prepare(gnutls_session_t session) | |
164 { | |
165 /* We do not use cookies at the moment. Not sure it is useful or not */ | |
166 TODO("Cookie exchange?"); | |
167 /* gnutls_dtls_prestate_set (session, &prestate); */ | |
168 | |
169 gnutls_dtls_set_mtu(session, 2^14 /* as per RFC 6083 */); | |
170 | |
171 gnutls_dtls_set_timeouts(session, 70000, 60000); /* Set retrans > total so that there is no retransmission, since SCTP is reliable */ | |
172 | |
173 #ifdef GNUTLS_VERSION_320 | |
174 TODO("Disable replay protection"); | |
175 #endif /* GNUTLS_VERSION_320 */ | |
176 | |
177 | |
178 } | |
179 | |
180 | |
181 static ssize_t fd_dtls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz, uint8_t seq[8]) | |
182 { | |
183 ssize_t ret; | |
184 again: | |
185 CHECK_GNUTLS_DO( ret = gnutls_record_recv_seq(session, data, sz, seq), | |
186 { | |
187 switch (ret) { | |
188 case GNUTLS_E_REHANDSHAKE: | |
189 if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) { | |
190 CHECK_GNUTLS_DO( ret = gnutls_handshake(session), | |
191 { | |
192 if (TRACE_BOOL(INFO)) { | |
193 fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); | |
194 } | |
195 goto end; | |
196 } ); | |
197 } | |
198 | |
199 case GNUTLS_E_AGAIN: | |
200 case GNUTLS_E_INTERRUPTED: | |
201 if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) | |
202 goto again; | |
203 TRACE_DEBUG(FULL, "Connection is closing, so abord gnutls_record_recv now."); | |
204 break; | |
205 | |
206 case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: | |
207 /* The connection is closed */ | |
208 TRACE_DEBUG(FULL, "Got 0 size while reading the socket, probably connection closed..."); | |
209 break; | |
210 | |
211 default: | |
212 if (gnutls_error_is_fatal (ret) == 0) { | |
213 LOG_N("Ignoring non-fatal GNU TLS error: %s", gnutls_strerror (ret)); | |
214 goto again; | |
215 } | |
216 LOG_E("Fatal GNUTLS error: %s", gnutls_strerror (ret)); | |
217 } | |
218 } ); | |
219 | |
220 if (ret == 0) | |
221 CHECK_GNUTLS_DO( gnutls_bye(session, GNUTLS_SHUT_RDWR), ); | |
222 | |
223 end: | |
224 if (ret <= 0) | |
225 fd_cnx_markerror(conn); | |
226 return ret; | |
227 } | |
228 | |
229 | |
230 /* Receiver thread that reassemble the decrypted messages (when size is > 2<<14) for upper layer */ | |
231 void * fd_sctp_dtls_rcvthr(void * arg) { | |
232 | |
233 struct cnxctx * conn = arg; | |
234 | |
235 TRACE_ENTRY("%p", arg); | |
236 CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), return NULL ); | |
237 | |
238 /* Set the thread name */ | |
239 { | |
240 char buf[48]; | |
241 snprintf(buf, sizeof(buf), "Receiver (%d) DTLS", conn->cc_socket); | |
242 fd_log_threadname ( buf ); | |
243 } | |
244 | |
245 ASSERT( fd_cnx_teststate(conn, CC_STATUS_TLS) ); | |
246 ASSERT( fd_cnx_target_queue(conn) ); | |
247 | |
248 /* The next function only returns when there is an error on the socket */ | |
249 do { | |
250 | |
251 TODO("Reassemble the packets based on their sequence number & stream"); | |
252 | |
253 | |
254 #if 0 | |
255 | |
256 uint8_t header[4]; | |
257 struct fd_cnx_rcvdata rcv_data; | |
258 struct fd_msg_pmdl *pmdl=NULL; | |
259 ssize_t ret = 0; | |
260 size_t received = 0; | |
261 uint8_t seq[8]; | |
262 | |
263 do { | |
264 ret = fd_dtls_recv_handle_error(conn, session, &header[received], sizeof(header) - received, seq); | |
265 if (ret <= 0) { | |
266 /* The connection is closed */ | |
267 goto out; | |
268 } | |
269 received += ret; | |
270 } while (received < sizeof(header)); | |
271 | |
272 rcv_data.length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3]; | |
273 | |
274 /* Check the received word is a valid beginning of a Diameter message */ | |
275 if ((header[0] != DIAMETER_VERSION) /* defined in <libfreeDiameter.h> */ | |
276 || (rcv_data.length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */ | |
277 /* The message is suspect */ | |
278 LOG_E( "Received suspect header [ver: %d, size: %zd] from '%s', assume disconnection", (int)header[0], rcv_data.length, conn->cc_remid); | |
279 fd_cnx_markerror(conn); | |
280 goto out; | |
281 } | |
282 | |
283 /* Ok, now we can really receive the data */ | |
284 CHECK_MALLOC( rcv_data.buffer = fd_cnx_alloc_msg_buffer( rcv_data.length, &pmdl ) ); | |
285 memcpy(rcv_data.buffer, header, sizeof(header)); | |
286 | |
287 while (received < rcv_data.length) { | |
288 pthread_cleanup_push(free_rcvdata, &rcv_data); /* In case we are canceled, clean the partialy built buffer */ | |
289 ret = fd_tls_recv_handle_error(conn, session, rcv_data.buffer + received, rcv_data.length - received); | |
290 pthread_cleanup_pop(0); | |
291 | |
292 if (ret <= 0) { | |
293 free_rcvdata(&rcv_data); | |
294 goto out; | |
295 } | |
296 received += ret; | |
297 } | |
298 | |
299 fd_hook_call(HOOK_DATA_RECEIVED, NULL, NULL, &rcv_data, pmdl); | |
300 | |
301 /* We have received a complete message, pass it to the daemon */ | |
302 CHECK_FCT_DO( ret = fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), | |
303 { | |
304 free_rcvdata(&rcv_data); | |
305 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); | |
306 return ret; | |
307 } ); | |
308 #endif // 0 | |
309 | |
310 } while (1); | |
311 | |
312 out: | |
313 TRACE_DEBUG(FULL, "Thread terminated"); | |
314 return NULL; | |
315 } |