Mercurial > hg > freeDiameter-dtls
view 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 |
line wrap: on
line source
/********************************************************************************************************* * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@freediameter.net> * * * * Copyright (c) 2013, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * * permitted provided that the following conditions are met: * * * * * Redistributions of source code must retain the above * * copyright notice, this list of conditions and the * * following disclaimer. * * * * * Redistributions in binary form must reproduce the above * * copyright notice, this list of conditions and the * * following disclaimer in the documentation and/or other * * materials provided with the distribution. * * * * * Neither the name of the WIDE Project or NICT nor the * * names of its contributors may be used to endorse or * * promote products derived from this software without * * specific prior written permission of WIDE Project and * * NICT. * * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *********************************************************************************************************/ /* This file contains the code for DTLS over multi-stream SCTP implementation */ #include "fdcore-internal.h" #include "cnxctx.h" #define DTLS_TYPE_application_data 23 #ifdef GNUTLS_VERSION_300 /* Check if data is available for gnutls on a given context */ static int sctp_dtls_pull_timeout(gnutls_transport_ptr_t tr, unsigned int ms) { struct cnxctx * conn = (struct cnxctx *)tr; fd_set rfds; struct timeval tv; FD_ZERO (&rfds); FD_SET (conn->cc_socket, &rfds); tv.tv_sec = 0; tv.tv_usec = ms * 1000; while(tv.tv_usec >= 1000000) { tv.tv_usec -= 1000000; tv.tv_sec++; } return select (conn->cc_socket + 1, &rfds, NULL, NULL, &tv); } #endif /* GNUTLS_VERSION_300 */ /* Send data over the connection, called by gnutls */ static ssize_t sctp_dtls_pushv(gnutls_transport_ptr_t tr, const giovec_t * iov, int iovcnt) { struct cnxctx * conn = (struct cnxctx *)tr; ssize_t ret; TRACE_ENTRY("%p %p %d", tr, iov, iovcnt); CHECK_PARAMS_DO( tr && iov, { errno = EINVAL; return -1; } ); /* If no unordered delivery is allowed, send over stream 0 always */ if (conn->cc_sctp_para.unordered == 0) { ret = fd_sctp_sendstrv(conn, 0, (const struct iovec *)iov, iovcnt); } /* Otherwise, we need to check the type of record. */ else { if ((iovcnt > 0) && (iov->iov_len > 0) && (*((uint8_t *)iov->iov_base) == DTLS_TYPE_application_data)) { /* Data is sent over different streams */ if (conn->cc_sctp_para.str_out > 32) { TODO("Limiting to 32 streams. Remove this limit when anti-replay is disabled"); conn->cc_sctp_para.str_out = 32; } if (conn->cc_sctp_para.str_out > 1) { conn->cc_sctp_para.next += 1; conn->cc_sctp_para.next %= conn->cc_sctp_para.str_out; } else { conn->cc_sctp_para.next = 0; } ret = fd_sctp_sendstrv(conn, conn->cc_sctp_para.next, (const struct iovec *)iov, iovcnt); } else { /* other TLS messages are always sent over stream 0 */ ret = fd_sctp_sendstrv(conn, 0, (const struct iovec *)iov, iovcnt); } } return ret; } #ifndef GNUTLS_VERSION_212 static ssize_t sctp_dtls_push(gnutls_transport_ptr_t tr, const void * data, size_t len) { giovec_t iov; iov.iov_base = (void *)data; iov.iov_len = len; return sctp_dtls_pushv(tr, &iov, 1); } #endif /* GNUTLS_VERSION_212 */ /* Retrieve data received on any stream */ static ssize_t sctp_dtls_pull(gnutls_transport_ptr_t tr, void * buf, size_t len) { struct cnxctx * conn = (struct cnxctx *)tr; /* If needed we can use fd_sctp_recvmeta to retrieve more information here */ return fd_cnx_s_recv(conn, buf, len); } /* Set the parameters of a session to use the cnxctx object */ #ifndef GNUTLS_VERSION_300 GCC_DIAG_OFF("-Wdeprecated-declarations") #endif /* !GNUTLS_VERSION_300 */ int fd_sctp_dtls_settransport(gnutls_session_t session, struct cnxctx * conn) { /* Set the transport pointer passed to push & pull callbacks */ GNUTLS_TRACE( gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) conn ) ); /* Reset the low water value, since we don't use sockets */ #ifndef GNUTLS_VERSION_300 /* starting version 2.12, this call is not needed */ GNUTLS_TRACE( gnutls_transport_set_lowat( session, 0 ) ); #else /* GNUTLS_VERSION_300 */ /* but in 3.0 we have to provide the pull_timeout callback */ GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( session, sctp_dtls_pull_timeout ) ); #endif /* GNUTLS_VERSION_300 */ /* Set the push and pull callbacks */ GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctp_dtls_pull) ); #ifndef GNUTLS_VERSION_212 GNUTLS_TRACE( gnutls_transport_set_push_function(session, sctp_dtls_push) ); #else /* GNUTLS_VERSION_212 */ GNUTLS_TRACE( gnutls_transport_set_vec_push_function(session, sctp_dtls_pushv) ); #endif /* GNUTLS_VERSION_212 */ return; } #ifndef GNUTLS_VERSION_300 GCC_DIAG_ON("-Wdeprecated-declarations") #endif /* !GNUTLS_VERSION_300 */ /* Set additional session parameters before handshake. The GNUTLS_DATAGRAM is already set in fd_tls_prepare */ int fd_sctp_dtls_prepare(gnutls_session_t session) { /* We do not use cookies at the moment. Not sure it is useful or not */ TODO("Cookie exchange?"); /* gnutls_dtls_prestate_set (session, &prestate); */ gnutls_dtls_set_mtu(session, 2^14 /* as per RFC 6083 */); gnutls_dtls_set_timeouts(session, 70000, 60000); /* Set retrans > total so that there is no retransmission, since SCTP is reliable */ #ifdef GNUTLS_VERSION_320 TODO("Disable replay protection"); #endif /* GNUTLS_VERSION_320 */ } static ssize_t fd_dtls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz, uint8_t seq[8]) { ssize_t ret; again: CHECK_GNUTLS_DO( ret = gnutls_record_recv_seq(session, data, sz, seq), { switch (ret) { case GNUTLS_E_REHANDSHAKE: if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) { CHECK_GNUTLS_DO( ret = gnutls_handshake(session), { if (TRACE_BOOL(INFO)) { fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); } goto end; } ); } case GNUTLS_E_AGAIN: case GNUTLS_E_INTERRUPTED: if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) goto again; TRACE_DEBUG(FULL, "Connection is closing, so abord gnutls_record_recv now."); break; case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: /* The connection is closed */ TRACE_DEBUG(FULL, "Got 0 size while reading the socket, probably connection closed..."); break; default: if (gnutls_error_is_fatal (ret) == 0) { LOG_N("Ignoring non-fatal GNU TLS error: %s", gnutls_strerror (ret)); goto again; } LOG_E("Fatal GNUTLS error: %s", gnutls_strerror (ret)); } } ); if (ret == 0) CHECK_GNUTLS_DO( gnutls_bye(session, GNUTLS_SHUT_RDWR), ); end: if (ret <= 0) fd_cnx_markerror(conn); return ret; } /* Receiver thread that reassemble the decrypted messages (when size is > 2<<14) for upper layer */ void * fd_sctp_dtls_rcvthr(void * arg) { struct cnxctx * conn = arg; TRACE_ENTRY("%p", arg); CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), return NULL ); /* Set the thread name */ { char buf[48]; snprintf(buf, sizeof(buf), "Receiver (%d) DTLS", conn->cc_socket); fd_log_threadname ( buf ); } ASSERT( fd_cnx_teststate(conn, CC_STATUS_TLS) ); ASSERT( fd_cnx_target_queue(conn) ); /* The next function only returns when there is an error on the socket */ do { TODO("Reassemble the packets based on their sequence number & stream"); #if 0 uint8_t header[4]; struct fd_cnx_rcvdata rcv_data; struct fd_msg_pmdl *pmdl=NULL; ssize_t ret = 0; size_t received = 0; uint8_t seq[8]; do { ret = fd_dtls_recv_handle_error(conn, session, &header[received], sizeof(header) - received, seq); if (ret <= 0) { /* The connection is closed */ goto out; } received += ret; } while (received < sizeof(header)); rcv_data.length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3]; /* Check the received word is a valid beginning of a Diameter message */ if ((header[0] != DIAMETER_VERSION) /* defined in <libfreeDiameter.h> */ || (rcv_data.length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */ /* The message is suspect */ LOG_E( "Received suspect header [ver: %d, size: %zd] from '%s', assume disconnection", (int)header[0], rcv_data.length, conn->cc_remid); fd_cnx_markerror(conn); goto out; } /* Ok, now we can really receive the data */ CHECK_MALLOC( rcv_data.buffer = fd_cnx_alloc_msg_buffer( rcv_data.length, &pmdl ) ); memcpy(rcv_data.buffer, header, sizeof(header)); while (received < rcv_data.length) { pthread_cleanup_push(free_rcvdata, &rcv_data); /* In case we are canceled, clean the partialy built buffer */ ret = fd_tls_recv_handle_error(conn, session, rcv_data.buffer + received, rcv_data.length - received); pthread_cleanup_pop(0); if (ret <= 0) { free_rcvdata(&rcv_data); goto out; } received += ret; } fd_hook_call(HOOK_DATA_RECEIVED, NULL, NULL, &rcv_data, pmdl); /* We have received a complete message, pass it to the daemon */ CHECK_FCT_DO( ret = fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer), { free_rcvdata(&rcv_data); CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); return ret; } ); #endif // 0 } while (1); out: TRACE_DEBUG(FULL, "Thread terminated"); return NULL; }