# HG changeset patch # User Sebastien Decugis # Date 1370431346 -28800 # Node ID 22de21feec64e4d4ced6aebd06705ef3f1f2273c # Parent 773498f595206e6c704acb9b552f6906352a8bd6 Preparing for DTLS support diff -r 773498f59520 -r 22de21feec64 doc/freediameter.conf.sample --- a/doc/freediameter.conf.sample Wed Jun 05 15:02:29 2013 +0800 +++ b/doc/freediameter.conf.sample Wed Jun 05 19:22:26 2013 +0800 @@ -35,7 +35,8 @@ # freeDiameter 1.2.0 introduces the support of DTLS over SCTP (RFC6083) instead of TLS over SCTP (RFC3436), # as specified in RFC6733. If you need compatibility with older implementation that use TLS over SCTP, you -# can open an additional SCTP server port using this method: +# can open an additional SCTP server port using TLS/SCTP by specifying the following parameter. +# Note that no TCP server is started on the following port. # Default: 0 (disabled). Use 3869 for compatibility with freeDiameter < 1.2.0. #SctpSec3436 = 0; diff -r 773498f59520 -r 22de21feec64 libfdcore/cnxctx.c --- a/libfdcore/cnxctx.c Wed Jun 05 15:02:29 2013 +0800 +++ b/libfdcore/cnxctx.c Wed Jun 05 19:22:26 2013 +0800 @@ -95,6 +95,8 @@ return conn; } +#define CC_ID_HDR "{----} " + /* Create and bind a server socket to the given endpoint and port */ struct cnxctx * fd_cnx_serv_tcp(uint16_t port, int family, struct fd_endpoint * ep) { @@ -138,7 +140,7 @@ rc = getnameinfo(sa, sSAlen(sa), addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); if (rc) snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); - snprintf(cnx->cc_id, sizeof(cnx->cc_id), "TCP srv [%s]:%hu (%d)", addrbuf, port, cnx->cc_socket); + snprintf(cnx->cc_id, sizeof(cnx->cc_id), CC_ID_HDR "TCP srv [%s]:%hu (%d)", addrbuf, port, cnx->cc_socket); } cnx->cc_proto = IPPROTO_TCP; @@ -178,7 +180,7 @@ CHECK_FCT_DO( fd_sctp_create_bind_server( &cnx->cc_socket, cnx->cc_family, ep_list, port ), goto error ); /* Generate the name for the connection object */ - snprintf(cnx->cc_id, sizeof(cnx->cc_id), "SCTP srv :%hu (%d)", port, cnx->cc_socket); + snprintf(cnx->cc_id, sizeof(cnx->cc_id), CC_ID_HDR "SCTP srv :%hu (%d)", port, cnx->cc_socket); cnx->cc_proto = IPPROTO_SCTP; @@ -248,7 +250,7 @@ } /* Numeric values for debug... */ - snprintf(cli->cc_id, sizeof(cli->cc_id), "%s from [%s]:%s (%d<-%d)", + snprintf(cli->cc_id, sizeof(cli->cc_id), CC_ID_HDR "%s from [%s]:%s (%d<-%d)", IPPROTO_NAME(cli->cc_proto), addrbuf, portbuf, serv->cc_socket, cli->cc_socket); @@ -312,7 +314,7 @@ { int rc; - snprintf(cnx->cc_id, sizeof(cnx->cc_id), "TCP,#%d->%s", cnx->cc_socket, sa_buf); + snprintf(cnx->cc_id, sizeof(cnx->cc_id), CC_ID_HDR "TCP,#%d->%s", cnx->cc_socket, sa_buf); /* ...Name for log messages */ rc = getnameinfo(sa, addrlen, cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0); @@ -375,7 +377,7 @@ { int rc; - snprintf(cnx->cc_id, sizeof(cnx->cc_id), "SCTP,#%d->%s", cnx->cc_socket, sa_buf); + snprintf(cnx->cc_id, sizeof(cnx->cc_id), CC_ID_HDR "SCTP,#%d->%s", cnx->cc_socket, sa_buf); /* ...Name for log messages */ rc = getnameinfo((sSA *)&primary, sSAlen(&primary), cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0); @@ -432,16 +434,39 @@ CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } ); return st & flag; } +void fd_cnx_update_id(struct cnxctx * conn) { + if (conn->cc_state & CC_STATUS_CLOSING) + conn->cc_id[1] = 'C'; + else + conn->cc_id[1] = '-'; + + if (conn->cc_state & CC_STATUS_ERROR) + conn->cc_id[2] = 'E'; + else + conn->cc_id[2] = '-'; + + if (conn->cc_state & CC_STATUS_SIGNALED) + conn->cc_id[3] = 'S'; + else + conn->cc_id[3] = '-'; + + if (conn->cc_state & CC_STATUS_TLS) + conn->cc_id[4] = 'T'; + else + conn->cc_id[4] = '-'; +} void fd_cnx_addstate(struct cnxctx * conn, uint32_t orstate) { CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } ); conn->cc_state |= orstate; + fd_cnx_update_id(conn); CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } ); } void fd_cnx_setstate(struct cnxctx * conn, uint32_t abstate) { CHECK_POSIX_DO( pthread_mutex_lock(&state_lock), { ASSERT(0); } ); conn->cc_state = abstate; + fd_cnx_update_id(conn); CHECK_POSIX_DO( pthread_mutex_unlock(&state_lock), { ASSERT(0); } ); } @@ -658,7 +683,7 @@ return ret; } -#ifndef DISABLE_SCTP +#ifndef DISABLE_SCTP /* WE use this function only in SCTP code */ static uint8_t * fd_cnx_realloc_msg_buffer(uint8_t * buffer, size_t expected_len, struct fd_msg_pmdl ** pmdl) { uint8_t * ret = NULL; @@ -852,12 +877,12 @@ -/* Returns 0 on error, received data size otherwise (always >= 0) */ +/* Returns 0 on error, received data size otherwise (always >= 0). This is not used for DTLS-protected associations. */ static ssize_t fd_tls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz) { ssize_t ret; again: - CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz), + CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz), { switch (ret) { case GNUTLS_E_REHANDSHAKE: @@ -884,7 +909,11 @@ break; default: - TRACE_DEBUG(INFO, "This GNU TLS error is not handled, assume unrecoverable error"); + 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)); } } ); @@ -897,7 +926,7 @@ return ret; } -/* Wrapper around gnutls_record_send to handle some error codes */ +/* Wrapper around gnutls_record_send to handle some error codes. This is also used for DTLS-protected associations */ static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz) { ssize_t ret; @@ -924,7 +953,11 @@ break; default: - TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error"); + 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)); } } ); end: @@ -936,9 +969,15 @@ /* The function that receives TLS data and re-builds a Diameter message -- it exits only on error or cancelation */ +/* For the case of DTLS, since we are not using SCTP_UNORDERED, the messages over a single stream are ordered. + Furthermore, as long as messages are shorter than the MTU [2^14 = 16384 bytes], they are delivered in a single + record, as far as I understand. + For larger messages, however, it is possible that pieces of messages coming from different streams can get interleaved. + As a result, we do not use the following function for DTLS reception, because we use the sequence number to rebuild the + messages. */ int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session) { - /* No guarantee that GnuTLS preserves the message boundaries, so we re-build it as in TCP */ + /* No guarantee that GnuTLS preserves the message boundaries, so we re-build it as in TCP. */ do { uint8_t header[4]; struct fd_cnx_rcvdata rcv_data; @@ -1024,8 +1063,13 @@ } /* Prepare a gnutls session object for handshake */ -int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds) +int fd_tls_prepare(gnutls_session_t * session, int mode, int dtls, char * priority, void * alt_creds) { + if (dtls) { + LOG_E("DTLS sessions not yet supported"); + return ENOTSUP; + } + /* Create the session context */ CHECK_GNUTLS_DO( gnutls_init (session, mode), return ENOMEM ); @@ -1065,17 +1109,18 @@ CHECK_PARAMS(conn); /* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */ - if (verbose && TRACE_BOOL(FULL)) { + #ifdef DEBUG + if (verbose) { const char *tmp; gnutls_kx_algorithm_t kx; gnutls_credentials_type_t cred; - fd_log_debug("TLS Session information for connection '%s':", conn->cc_id); + LOG_A("TLS Session information for connection '%s':", conn->cc_id); /* print the key exchange's algorithm name */ GNUTLS_TRACE( kx = gnutls_kx_get (session) ); GNUTLS_TRACE( tmp = gnutls_kx_get_name (kx) ); - fd_log_debug("\t - Key Exchange: %s", tmp); + LOG_A("\t - Key Exchange: %s", tmp); /* Check the authentication type used and switch * to the appropriate. */ @@ -1083,35 +1128,35 @@ switch (cred) { case GNUTLS_CRD_IA: - fd_log_debug("\t - TLS/IA session"); + LOG_A("\t - TLS/IA session"); break; case GNUTLS_CRD_PSK: /* This returns NULL in server side. */ if (gnutls_psk_client_get_hint (session) != NULL) - fd_log_debug("\t - PSK authentication. PSK hint '%s'", + LOG_A("\t - PSK authentication. PSK hint '%s'", gnutls_psk_client_get_hint (session)); /* This returns NULL in client side. */ if (gnutls_psk_server_get_username (session) != NULL) - fd_log_debug("\t - PSK authentication. Connected as '%s'", + LOG_A("\t - PSK authentication. Connected as '%s'", gnutls_psk_server_get_username (session)); break; case GNUTLS_CRD_ANON: /* anonymous authentication */ - fd_log_debug("\t - Anonymous DH using prime of %d bits", + LOG_A("\t - Anonymous DH using prime of %d bits", gnutls_dh_get_prime_bits (session)); break; case GNUTLS_CRD_CERTIFICATE: /* certificate authentication */ /* Check if we have been using ephemeral Diffie-Hellman. */ if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS) { - fd_log_debug("\t - Ephemeral DH using prime of %d bits", + LOG_A("\t - Ephemeral DH using prime of %d bits", gnutls_dh_get_prime_bits (session)); } break; #ifdef ENABLE_SRP case GNUTLS_CRD_SRP: - fd_log_debug("\t - SRP session with username %s", + LOG_A("\t - SRP session with username %s", gnutls_srp_server_get_username (session)); break; #endif /* ENABLE_SRP */ @@ -1124,24 +1169,25 @@ /* print the protocol's name (ie TLS 1.0) */ tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session)); - fd_log_debug("\t - Protocol: %s", tmp); + LOG_A("\t - Protocol: %s", tmp); /* print the certificate type of the peer. ie X.509 */ tmp = gnutls_certificate_type_get_name (gnutls_certificate_type_get (session)); - fd_log_debug("\t - Certificate Type: %s", tmp); + LOG_A("\t - Certificate Type: %s", tmp); /* print the compression algorithm (if any) */ tmp = gnutls_compression_get_name (gnutls_compression_get (session)); - fd_log_debug("\t - Compression: %s", tmp); + LOG_A("\t - Compression: %s", tmp); /* print the name of the cipher used. ie 3DES. */ tmp = gnutls_cipher_get_name (gnutls_cipher_get (session)); - fd_log_debug("\t - Cipher: %s", tmp); + LOG_A("\t - Cipher: %s", tmp); /* Print the MAC algorithms name. ie SHA1 */ tmp = gnutls_mac_get_name (gnutls_mac_get (session)); - fd_log_debug("\t - MAC: %s", tmp); + LOG_A("\t - MAC: %s", tmp); } + #endif /* DEBUG */ /* First, use built-in verification */ CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (session, >ret), return EINVAL ); @@ -1172,27 +1218,27 @@ now = time(NULL); - if (verbose && TRACE_BOOL(FULL)) { + #ifdef DEBUG char serial[40]; char dn[128]; size_t size; unsigned int algo, bits; time_t expiration_time, activation_time; - fd_log_debug("TLS Certificate information for connection '%s' (%d certs provided):", conn->cc_id, cert_list_size); + LOG_D("TLS Certificate information for connection '%s' (%d certs provided):", conn->cc_id, cert_list_size); for (i = 0; i < cert_list_size; i++) { CHECK_GNUTLS_DO( gnutls_x509_crt_init (&cert), return EINVAL); CHECK_GNUTLS_DO( gnutls_x509_crt_import (cert, &cert_list[i], GNUTLS_X509_FMT_DER), return EINVAL); - fd_log_debug(" Certificate %d info:", i); + LOG_A(" Certificate %d info:", i); GNUTLS_TRACE( expiration_time = gnutls_x509_crt_get_expiration_time (cert) ); GNUTLS_TRACE( activation_time = gnutls_x509_crt_get_activation_time (cert) ); - fd_log_debug("\t - Certificate is valid since: %s", ctime (&activation_time)); - fd_log_debug("\t - Certificate expires: %s", ctime (&expiration_time)); + LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Certificate is valid since: %.24s", ctime (&activation_time)); + LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Certificate expires: %.24s", ctime (&expiration_time)); /* Print the serial number of the certificate. */ size = sizeof (serial); @@ -1205,29 +1251,29 @@ for (j = 0; j < size; j++) { snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%02hhx", serial[j]); } - fd_log_debug("%s", buf); + LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "%s", buf); } /* Extract some of the public key algorithm's parameters */ GNUTLS_TRACE( algo = gnutls_x509_crt_get_pk_algorithm (cert, &bits) ); - fd_log_debug("\t - Certificate public key: %s", + LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Certificate public key: %s", gnutls_pk_algorithm_get_name (algo)); /* Print the version of the X.509 certificate. */ - fd_log_debug("\t - Certificate version: #%d", + LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Certificate version: #%d", gnutls_x509_crt_get_version (cert)); size = sizeof (dn); GNUTLS_TRACE( gnutls_x509_crt_get_dn (cert, dn, &size) ); - fd_log_debug("\t - DN: %s", dn); + LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - DN: %s", dn); size = sizeof (dn); GNUTLS_TRACE( gnutls_x509_crt_get_issuer_dn (cert, dn, &size) ); - fd_log_debug("\t - Issuer's DN: %s", dn); + LOG( i ? FD_LOG_ANNOYING : FD_LOG_DEBUG, "\t - Issuer's DN: %s", dn); GNUTLS_TRACE( gnutls_x509_crt_deinit (cert) ); } - } + #endif /* DEBUG */ /* Check validity of all the certificates */ for (i = 0; i < cert_list_size; i++) @@ -1290,7 +1336,7 @@ conn = gnutls_session_get_ptr (session); /* Trace the session information -- http://www.gnu.org/software/gnutls/manual/gnutls.html#Obtaining-session-information */ - if (TRACE_BOOL(FULL)) { +#ifdef DEBUG const char *tmp; gnutls_credentials_type_t cred; gnutls_kx_algorithm_t kx; @@ -1298,13 +1344,13 @@ dhe = ecdh = 0; - fd_log_debug("TLS Session information for connection '%s':", conn->cc_id); + LOG_A("TLS Session information for connection '%s':", conn->cc_id); /* print the key exchange's algorithm name */ GNUTLS_TRACE( kx = gnutls_kx_get (session) ); GNUTLS_TRACE( tmp = gnutls_kx_get_name (kx) ); - fd_log_debug("\t- Key Exchange: %s", tmp); + LOG_A("\t- Key Exchange: %s", tmp); /* Check the authentication type used and switch * to the appropriate. @@ -1313,13 +1359,13 @@ switch (cred) { case GNUTLS_CRD_IA: - fd_log_debug("\t - TLS/IA session"); + LOG_A("\t - TLS/IA session"); break; #ifdef ENABLE_SRP case GNUTLS_CRD_SRP: - fd_log_debug("\t - SRP session with username %s", + LOG_A("\t - SRP session with username %s", gnutls_srp_server_get_username (session)); break; #endif @@ -1328,12 +1374,12 @@ /* This returns NULL in server side. */ if (gnutls_psk_client_get_hint (session) != NULL) - fd_log_debug("\t - PSK authentication. PSK hint '%s'", + LOG_A("\t - PSK authentication. PSK hint '%s'", gnutls_psk_client_get_hint (session)); /* This returns NULL in client side. */ if (gnutls_psk_server_get_username (session) != NULL) - fd_log_debug("\t - PSK authentication. Connected as '%s'", + LOG_A("\t - PSK authentication. Connected as '%s'", gnutls_psk_server_get_username (session)); if (kx == GNUTLS_KX_ECDHE_PSK) @@ -1343,7 +1389,7 @@ break; case GNUTLS_CRD_ANON: /* anonymous authentication */ - fd_log_debug("\t - Anonymous DH using prime of %d bits", + LOG_A("\t - Anonymous DH using prime of %d bits", gnutls_dh_get_prime_bits (session)); if (kx == GNUTLS_KX_ANON_ECDH) ecdh = 1; @@ -1366,7 +1412,7 @@ cert_list = gnutls_certificate_get_peers (session, &cert_list_size); - fd_log_debug("\t Peer provided %d certificates.", cert_list_size); + LOG_A("\t Peer provided %d certificates.", cert_list_size); if (cert_list_size > 0) { @@ -1378,7 +1424,7 @@ gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER); - fd_log_debug("\t Certificate info:"); + LOG_A("\t Certificate info:"); /* This is the preferred way of printing short information about a certificate. */ @@ -1386,7 +1432,7 @@ ret = gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE, &cinfo); if (ret == 0) { - fd_log_debug("\t\t%s", cinfo.data); + LOG_A("\t\t%s", cinfo.data); gnutls_free (cinfo.data); } @@ -1409,46 +1455,46 @@ break; default: - fd_log_debug("\t - unknown session type (%d)", cred); + LOG_A("\t - unknown session type (%d)", cred); } /* switch */ if (ecdh != 0) - fd_log_debug("\t - Ephemeral ECDH using curve %s", + LOG_A("\t - Ephemeral ECDH using curve %s", gnutls_ecc_curve_get_name (gnutls_ecc_curve_get (session))); else if (dhe != 0) - fd_log_debug("\t - Ephemeral DH using prime of %d bits", + LOG_A("\t - Ephemeral DH using prime of %d bits", gnutls_dh_get_prime_bits (session)); /* print the protocol's name (ie TLS 1.0) */ tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session)); - fd_log_debug("\t - Protocol: %s", tmp); + LOG_A("\t - Protocol: %s", tmp); /* print the certificate type of the peer. * ie X.509 */ tmp = gnutls_certificate_type_get_name (gnutls_certificate_type_get (session)); - fd_log_debug("\t - Certificate Type: %s", tmp); + LOG_A("\t - Certificate Type: %s", tmp); /* print the compression algorithm (if any) */ tmp = gnutls_compression_get_name (gnutls_compression_get (session)); - fd_log_debug("\t - Compression: %s", tmp); + LOG_A("\t - Compression: %s", tmp); /* print the name of the cipher used. * ie 3DES. */ tmp = gnutls_cipher_get_name (gnutls_cipher_get (session)); - fd_log_debug("\t - Cipher: %s", tmp); + LOG_A("\t - Cipher: %s", tmp); /* Print the MAC algorithms name. * ie SHA1 */ tmp = gnutls_mac_get_name (gnutls_mac_get (session)); - fd_log_debug("\t - MAC: %s", tmp); + LOG_A("\t - MAC: %s", tmp); - } +#endif /* DEBUG */ /* This verification function uses the trusted CAs in the credentials * structure. So you must have installed one or more CA certificates. @@ -1508,14 +1554,29 @@ #endif /* GNUTLS_VERSION_300 */ +static int fd_cnx_may_dtls(struct cnxctx * conn) { +#ifndef DISABLE_SCTP + if ((conn->cc_proto == IPPROTO_SCTP) && (conn->cc_tls_para.algo == ALGO_HANDSHAKE_DEFAULT)) + return 1; +#endif /* DISABLE_SCTP */ + return 0; +} + +static int fd_cnx_uses_dtls(struct cnxctx * conn) { + return fd_cnx_may_dtls(conn) && (fd_cnx_teststate(conn, CC_STATUS_TLS)); +} + /* TLS handshake a connection; no need to have called start_clear before. Reception is active if handhsake is successful */ -int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds) +int fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, void * alt_creds) { - TRACE_ENTRY( "%p %d %p %p", conn, mode, priority, alt_creds); + int dtls = 0; + + TRACE_ENTRY( "%p %d %d %p %p", conn, mode, algo, priority, alt_creds); CHECK_PARAMS( conn && (!fd_cnx_teststate(conn, CC_STATUS_TLS)) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) ); /* Save the mode */ conn->cc_tls_para.mode = mode; + conn->cc_tls_para.algo = algo; /* Cancel receiving thread if any -- it should already be terminated anyway, we just release the resources */ CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */); @@ -1523,11 +1584,13 @@ /* Once TLS handshake is done, we don't stop after the first message */ conn->cc_loop = 1; + dtls = fd_cnx_may_dtls(conn); + /* Prepare the master session credentials and priority */ - CHECK_FCT( fd_tls_prepare(&conn->cc_tls_para.session, mode, priority, alt_creds) ); + CHECK_FCT( fd_tls_prepare(&conn->cc_tls_para.session, mode, dtls, priority, alt_creds) ); /* Special case: multi-stream TLS is not natively managed in GNU TLS, we use a wrapper library */ - if (conn->cc_sctp_para.pairs > 1) { + if ((!dtls) && (conn->cc_sctp_para.pairs > 1)) { #ifdef DISABLE_SCTP ASSERT(0); CHECK_FCT( ENOTSUP ); @@ -1540,8 +1603,13 @@ GNUTLS_TRACE( gnutls_transport_set_ptr( conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn ) ); /* Set the push and pull callbacks */ - GNUTLS_TRACE( gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv) ); - GNUTLS_TRACE( gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send) ); + if (!dtls) { + GNUTLS_TRACE( gnutls_transport_set_pull_function(conn->cc_tls_para.session, (void *)fd_cnx_s_recv) ); + GNUTLS_TRACE( gnutls_transport_set_push_function(conn->cc_tls_para.session, (void *)fd_cnx_s_send) ); + } else { + TODO("DTLS push/pull functions"); + return ENOTSUP; + } } /* additional initialization for gnutls 3.x */ @@ -1588,9 +1656,9 @@ }); #endif /* GNUTLS_VERSION_300 */ } - + /* Multi-stream TLS: handshake other streams as well */ - if (conn->cc_sctp_para.pairs > 1) { + if ((!dtls) && (conn->cc_sctp_para.pairs > 1)) { #ifndef DISABLE_SCTP /* Start reading the messages from the master session. That way, if the remote peer closed, we are not stuck inside handshake */ CHECK_FCT(fd_sctp3436_startthreads(conn, 0)); @@ -1603,7 +1671,13 @@ #endif /* DISABLE_SCTP */ } else { /* Start decrypting the data */ - CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_tls_single, conn ) ); + if (!dtls) { + CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_tls_single, conn ) ); + } else { + TODO("Signal the dtls_push function that multiple streams can be used from this point."); + TODO("Create DTLS rcvthr (must reassembly based on seq numbers & stream id ?)"); + return ENOTSUP; + } } return 0; @@ -1736,40 +1810,49 @@ #ifndef DISABLE_SCTP case IPPROTO_SCTP: { - if (flags & FD_CNX_ORDERED) { - /* We send over stream #0 */ - CHECK_FCT( send_simple(conn, buf, len) ); - } else { - /* Default case : no flag specified */ - - int another_str = 0; /* do we send over stream #0 ? */ - - if ((conn->cc_sctp_para.str_out > 1) && ((!fd_cnx_teststate(conn, CC_STATUS_TLS)) || (conn->cc_sctp_para.pairs > 1))) { - /* Update the id of the stream we will send this message over */ - conn->cc_sctp_para.next += 1; - conn->cc_sctp_para.next %= (fd_cnx_teststate(conn, CC_STATUS_TLS) ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out); - another_str = (conn->cc_sctp_para.next ? 1 : 0); - } + int dtls = fd_cnx_uses_dtls(conn); - if ( ! another_str ) { + if (!dtls) { + if (flags & FD_CNX_ORDERED) { + /* We send over stream #0 */ CHECK_FCT( send_simple(conn, buf, len) ); } else { - if (!fd_cnx_teststate(conn, CC_STATUS_TLS)) { - CHECK_FCT_DO( fd_sctp_sendstr(conn, conn->cc_sctp_para.next, buf, len), { fd_cnx_markerror(conn); return ENOTCONN; } ); + /* Default case : no flag specified */ + + int another_str = 0; /* do we send over stream #0 ? */ + + if ((conn->cc_sctp_para.str_out > 1) && ((!fd_cnx_teststate(conn, CC_STATUS_TLS)) || (conn->cc_sctp_para.pairs > 1))) { + /* Update the id of the stream we will send this message over */ + conn->cc_sctp_para.next += 1; + conn->cc_sctp_para.next %= (fd_cnx_teststate(conn, CC_STATUS_TLS) ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out); + another_str = (conn->cc_sctp_para.next ? 1 : 0); + } + + if ( ! another_str ) { + CHECK_FCT( send_simple(conn, buf, len) ); } else { - /* push the record to the appropriate session */ - ssize_t ret; - size_t sent = 0; - ASSERT(conn->cc_sctp3436_data.array != NULL); - do { - CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctp3436_data.array[conn->cc_sctp_para.next].session, buf + sent, len - sent), ); - if (ret <= 0) - return ENOTCONN; + if (!fd_cnx_teststate(conn, CC_STATUS_TLS)) { + CHECK_FCT_DO( fd_sctp_sendstr(conn, conn->cc_sctp_para.next, buf, len), { fd_cnx_markerror(conn); return ENOTCONN; } ); + } else { + /* push the record to the appropriate session */ + ssize_t ret; + size_t sent = 0; + ASSERT(conn->cc_sctp3436_data.array != NULL); + do { + CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctp3436_data.array[conn->cc_sctp_para.next].session, buf + sent, len - sent), ); + if (ret <= 0) + return ENOTCONN; - sent += ret; - } while ( sent < len ); + sent += ret; + } while ( sent < len ); + } } } + } else { + /* DTLS */ + /* We signal the push function directly to tell if using stream 0 or round-robin */ + TODO("DTLS send"); + return ENOTSUP; } } break; @@ -1801,7 +1884,8 @@ /* Initiate shutdown of the TLS session(s): call gnutls_bye(WR), then read until error */ if (fd_cnx_teststate(conn, CC_STATUS_TLS)) { #ifndef DISABLE_SCTP - if (conn->cc_sctp_para.pairs > 1) { + int dtls = fd_cnx_uses_dtls(conn); + if ((!dtls) && (conn->cc_sctp_para.pairs > 1)) { if (! fd_cnx_teststate(conn, CC_STATUS_ERROR )) { /* Bye on master session */ CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_WR), fd_cnx_markerror(conn) ); diff -r 773498f59520 -r 22de21feec64 libfdcore/cnxctx.h --- a/libfdcore/cnxctx.h Wed Jun 05 15:02:29 2013 +0800 +++ b/libfdcore/cnxctx.h Wed Jun 05 19:22:26 2013 +0800 @@ -40,7 +40,7 @@ /* The connection context structure */ struct cnxctx { - char cc_id[60]; /* The name of this connection */ + char cc_id[60]; /* The name of this connection. the first 5 chars are reserved for flags display (cc_state). */ char cc_remid[60]; /* Id of remote peer */ int cc_socket; /* The socket object of the connection -- <=0 if no socket is created */ @@ -64,6 +64,7 @@ struct { DiamId_t cn; /* If not NULL, remote certif will be checked to match this Common Name */ int mode; /* GNUTLS_CLIENT / GNUTLS_SERVER */ + int algo; /* ALGO_HANDSHAKE_DEFAULT / ALGO_HANDSHAKE_3436 */ gnutls_session_t session; /* Session object (stream #0 in case of SCTP) */ } cc_tls_para; @@ -96,7 +97,7 @@ /* TLS */ int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session); -int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds); +int fd_tls_prepare(gnutls_session_t * session, int mode, int dtls, char * priority, void * alt_creds); #ifndef GNUTLS_VERSION_300 int fd_tls_verify_credentials(gnutls_session_t session, struct cnxctx * conn, int verbose); #endif /* GNUTLS_VERSION_300 */ diff -r 773498f59520 -r 22de21feec64 libfdcore/config.c --- a/libfdcore/config.c Wed Jun 05 15:02:29 2013 +0800 +++ b/libfdcore/config.c Wed Jun 05 19:22:26 2013 +0800 @@ -93,6 +93,9 @@ CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Tw Timer ............... : %u\n", fd_g_config->cnf_timer_tw), return NULL); CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local port ............. : %hu\n", fd_g_config->cnf_port), return NULL); CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local secure port ...... : %hu\n", fd_g_config->cnf_port_tls), return NULL); + if (fd_g_config->cnf_port_3436) { + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local SCTP TLS port .... : %hu\n", fd_g_config->cnf_port_3436), return NULL); + } CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of SCTP streams . : %hu\n", fd_g_config->cnf_sctp_str), return NULL); CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of server threads : %hu\n", fd_g_config->cnf_dispthr), return NULL); if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) { diff -r 773498f59520 -r 22de21feec64 libfdcore/fdcore-internal.h --- a/libfdcore/fdcore-internal.h Wed Jun 05 15:02:29 2013 +0800 +++ b/libfdcore/fdcore-internal.h Wed Jun 05 19:22:26 2013 +0800 @@ -341,7 +341,9 @@ struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list); int fd_cnx_start_clear(struct cnxctx * conn, int loop); void fd_cnx_sethostname(struct cnxctx * conn, DiamId_t hn); -int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds); +#define ALGO_HANDSHAKE_DEFAULT 0 /* TLS for TCP, DTLS for SCTP */ +#define ALGO_HANDSHAKE_3436 1 /* For TLS for SCTP also */ +int fd_cnx_handshake(struct cnxctx * conn, int mode, int algo, char * priority, void * alt_creds); char * fd_cnx_getid(struct cnxctx * conn); int fd_cnx_getproto(struct cnxctx * conn); int fd_cnx_getTLS(struct cnxctx * conn); diff -r 773498f59520 -r 22de21feec64 libfdcore/p_ce.c --- a/libfdcore/p_ce.c Wed Jun 05 15:02:29 2013 +0800 +++ b/libfdcore/p_ce.c Wed Jun 05 19:22:26 2013 +0800 @@ -785,7 +785,7 @@ } else { fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE); - CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL), + CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_CLIENT, ALGO_HANDSHAKE_3436, peer->p_hdr.info.config.pic_priority, NULL), { /* Handshake failed ... */ fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "TLS handshake failed after CER/CEA exchange", NULL); @@ -954,7 +954,7 @@ /* Handshake if needed */ if (isi & PI_SEC_TLS_OLD) { fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE); - CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_SERVER, peer->p_hdr.info.config.pic_priority, NULL), + CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, peer->p_hdr.info.config.pic_priority, NULL), { /* Handshake failed ... */ fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "TLS handshake failed after CER/CEA exchange", NULL); diff -r 773498f59520 -r 22de21feec64 libfdcore/p_cnx.c --- a/libfdcore/p_cnx.c Wed Jun 05 15:02:29 2013 +0800 +++ b/libfdcore/p_cnx.c Wed Jun 05 19:22:26 2013 +0800 @@ -281,7 +281,9 @@ /* Handshake if needed (secure port) */ if (nc->dotls) { - CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL), + CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT, + (peer->p_hdr.info.config.pic_flags.sctpsec == PI_SCTPSEC_3436) ? ALGO_HANDSHAKE_3436 : ALGO_HANDSHAKE_DEFAULT, + peer->p_hdr.info.config.pic_priority, NULL), { /* Handshake failed ... */ fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "TLS Handshake failed", NULL); diff -r 773498f59520 -r 22de21feec64 libfdcore/p_psm.c --- a/libfdcore/p_psm.c Wed Jun 05 15:02:29 2013 +0800 +++ b/libfdcore/p_psm.c Wed Jun 05 19:22:26 2013 +0800 @@ -784,7 +784,7 @@ switch (cur_state) { case STATE_WAITCNXACK_ELEC: case STATE_WAITCNXACK: - LOG_D("%s: Connection established", peer->p_hdr.info.pi_diamid); + LOG_D("%s: Connection established, %s", peer->p_hdr.info.pi_diamid, fd_cnx_getid(cnx)); fd_p_ce_handle_newcnx(peer, cnx); break; diff -r 773498f59520 -r 22de21feec64 libfdcore/peers.c --- a/libfdcore/peers.c Wed Jun 05 15:02:29 2013 +0800 +++ b/libfdcore/peers.c Wed Jun 05 19:22:26 2013 +0800 @@ -429,7 +429,7 @@ } } if (details > 1) { - CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " [from:%s] flags:%s%s%s%s%s%s%s lft:%ds", + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " [from:%s] flags:%s%s%s%s%s%s%s%s lft:%ds", peer->p_dbgorig ?: "unset", peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_DEFAULT ? "-" : (peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP ? "4" : "6"), @@ -438,6 +438,7 @@ peer->p_hdr.info.config.pic_flags.alg ? "P" : "-", peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE ? "N" :"-", peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD ? "O" :"-", + peer->p_hdr.info.config.pic_flags.sctpsec & PI_SCTPSEC_3436 ? "3" :"-", peer->p_hdr.info.config.pic_flags.exp ? "E" : "-", peer->p_hdr.info.config.pic_flags.persist ? "P" : "-", peer->p_hdr.info.config.pic_lft), return NULL); diff -r 773498f59520 -r 22de21feec64 libfdcore/sctp3436.c --- a/libfdcore/sctp3436.c Wed Jun 05 15:02:29 2013 +0800 +++ b/libfdcore/sctp3436.c Wed Jun 05 19:22:26 2013 +0800 @@ -565,7 +565,7 @@ /* Initialize the session objects and start the handshake in a separate thread */ for (i = 1; i < conn->cc_sctp_para.pairs; i++) { /* Set credentials and priority */ - CHECK_FCT( fd_tls_prepare(&conn->cc_sctp3436_data.array[i].session, conn->cc_tls_para.mode, priority, alt_creds) ); + CHECK_FCT( fd_tls_prepare(&conn->cc_sctp3436_data.array[i].session, conn->cc_tls_para.mode, 0, priority, alt_creds) ); /* additional initialization for gnutls 3.x */ #ifdef GNUTLS_VERSION_300 diff -r 773498f59520 -r 22de21feec64 libfdcore/server.c --- a/libfdcore/server.c Wed Jun 05 15:02:29 2013 +0800 +++ b/libfdcore/server.c Wed Jun 05 19:22:26 2013 +0800 @@ -53,7 +53,7 @@ struct cnxctx * conn; /* server connection context (listening socket) */ int proto; /* IPPROTO_TCP or IPPROTO_SCTP */ - int secur; /* TLS is started immediatly after connection ? */ + int secur; /* TLS is started immediatly after connection ? 0: no; 1: yes (TLS/TCP or DTLS/SCTP); 2: yes (TLS/TCP or TLS/SCTP) */ pthread_t thr; /* The thread listening for new connections */ enum s_state state; /* state of the thread */ @@ -104,9 +104,9 @@ enum s_state st = get_status(s); if (details) { - CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{server}(@%p)'%s': %s, %s, %s", s, fd_cnx_getid(s->conn), + CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "{server}(@%p)'%s': %s, %s(%d), %s", s, fd_cnx_getid(s->conn), IPPROTO_NAME( s->proto ), - s->secur ? "Secur" : "NotSecur", + s->secur ? "Secur" : "NotSecur", s->secur, (st == NOT_CREATED) ? "Thread not created" : ((st == RUNNING) ? "Thread running" : ((st == TERMINATED) ? "Thread terminated" : @@ -146,8 +146,11 @@ TRACE_ENTRY("%p", c); + memset(&rcv_data, 0, sizeof(rcv_data)); + CHECK_PARAMS_DO(c && c->conn && c->chain.head, goto fatal_error ); + s = c->chain.head->o; /* Name the current thread */ @@ -155,7 +158,7 @@ /* Handshake if we are a secure server port, or start clear otherwise */ if (s->secur) { - int ret = fd_cnx_handshake(c->conn, GNUTLS_SERVER, NULL, NULL); + int ret = fd_cnx_handshake(c->conn, GNUTLS_SERVER, (s->secur == 1) ? ALGO_HANDSHAKE_DEFAULT : ALGO_HANDSHAKE_3436, NULL, NULL); if (ret != 0) { char buf[1024]; snprintf(buf, sizeof(buf), "TLS handshake failed for client '%s', connection aborted.", fd_cnx_getid(c->conn)); @@ -363,6 +366,14 @@ CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); } + /* Create the other server on 3436 secure port */ + if (fd_g_config->cnf_port_3436) { + CHECK_MALLOC( s = new_serv(IPPROTO_SCTP, 2) ); + CHECK_MALLOC( s->conn = fd_cnx_serv_sctp(fd_g_config->cnf_port_3436, empty_conf_ep ? NULL : &fd_g_config->cnf_endpoints) ); + fd_list_insert_before( &FD_SERVERS, &s->chain ); + CHECK_POSIX( pthread_create( &s->thr, NULL, serv_th, s ) ); + } + #endif /* DISABLE_SCTP */ } diff -r 773498f59520 -r 22de21feec64 tests/testcnx.c --- a/tests/testcnx.c Wed Jun 05 15:02:29 2013 +0800 +++ b/tests/testcnx.c Wed Jun 05 19:22:26 2013 +0800 @@ -548,6 +548,7 @@ struct handshake_flags { struct cnxctx * cnx; gnutls_certificate_credentials_t creds; + int algo; int ret; }; @@ -556,7 +557,7 @@ { struct handshake_flags * hf = arg; fd_log_threadname ( "testcnx:handshake" ); - hf->ret = fd_cnx_handshake(hf->cnx, GNUTLS_CLIENT, NULL, hf->creds); + hf->ret = fd_cnx_handshake(hf->cnx, GNUTLS_CLIENT, hf->algo, NULL, hf->creds); return NULL; } @@ -863,7 +864,7 @@ /* At this point in legacy Diameter we start the handshake */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT /* No impact on TCP */, NULL, NULL) ); CHECK( 0, pthread_join(thr, NULL) ); CHECK( 0, hf.ret ); @@ -904,6 +905,7 @@ cf.proto = IPPROTO_SCTP; memset(&hf, 0, sizeof(hf)); + hf.algo = ALGO_HANDSHAKE_3436; /* this is mandatory for old TLS mechanism */ /* Initialize remote certificate */ CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), ); @@ -948,7 +950,7 @@ /* At this point in legacy Diameter we start the handshake */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) ); CHECK( 0, pthread_join(thr, NULL) ); CHECK( 0, hf.ret ); @@ -1014,7 +1016,7 @@ /* Start the handshake directly */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) ); CHECK( 0, pthread_join(thr, NULL) ); CHECK( 0, hf.ret ); @@ -1045,7 +1047,11 @@ } #ifndef DISABLE_SCTP - /* SCTP Client / server emulating new Diameter behavior (handshake at connection directly) */ + + + /* SCTP Client / server emulating new Diameter behavior (DTLS handshake at connection directly) */ + TODO("Enabled after DTLS implementation"); + if (0) { struct connect_flags cf; struct handshake_flags hf; @@ -1079,7 +1085,73 @@ /* Start the handshake directly */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) ); + CHECK( 0, pthread_join(thr, NULL) ); + CHECK( 0, hf.ret ); + + /* Send a few TLS protected messages, and replies */ + for (i = 0; i < 2 * NB_STREAMS; i++) { + CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0)); + CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz)); + CHECK( cer_sz, rcv_sz ); + CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) ); + free(rcv_buf); + + CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0)); + CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz)); + CHECK( cer_sz, rcv_sz ); + CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) ); + free(rcv_buf); + } + + + /* Now close the connection */ + CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) ); + fd_cnx_destroy(server_side); + CHECK( 0, pthread_join(thr, NULL) ); + + /* Free the credentials */ + gnutls_certificate_free_keys(hf.creds); + gnutls_certificate_free_cas(hf.creds); + gnutls_certificate_free_credentials(hf.creds); + } + + /* SCTP Client / server emulating old intermediary Diameter behavior (TLS handshake at connection directly) */ + { + struct connect_flags cf; + struct handshake_flags hf; + + memset(&cf, 0, sizeof(cf)); + cf.proto = IPPROTO_SCTP; + + memset(&hf, 0, sizeof(hf)); + hf.algo = ALGO_HANDSHAKE_3436; /* this is mandatory for old TLS mechanism */ + + /* Initialize remote certificate */ + CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), ); + CHECK( GNUTLS_E_SUCCESS, ret ); + /* Set the CA */ + CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), ); + CHECK( 1, ret ); + /* Set the key */ + CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), ); + CHECK( GNUTLS_E_SUCCESS, ret ); + + /* Start the client thread */ + CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) ); + + /* Accept the connection of the client */ + server_side = fd_cnx_serv_accept(listener_sctp); + CHECK( 1, server_side ? 1 : 0 ); + + /* Retrieve the client connection object */ + CHECK( 0, pthread_join( thr, (void *)&client_side ) ); + CHECK( 1, client_side ? 1 : 0 ); + hf.cnx = client_side; + + /* Start the handshake directly */ + CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) ); CHECK( 0, pthread_join(thr, NULL) ); CHECK( 0, hf.ret ); @@ -1113,6 +1185,9 @@ /* Test with different number of streams between server and client */ #ifndef DISABLE_SCTP + /* DTLS / SCTP style */ + TODO("Enabled after DTLS implementation"); + if (0) { struct connect_flags cf; struct handshake_flags hf; @@ -1147,7 +1222,7 @@ /* Start the handshake directly */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) ); CHECK( 0, pthread_join(thr, NULL) ); CHECK( 0, hf.ret ); @@ -1186,7 +1261,7 @@ /* Start the handshake directly */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) ); CHECK( 0, pthread_join(thr, NULL) ); CHECK( 0, hf.ret ); @@ -1216,6 +1291,113 @@ gnutls_certificate_free_cas(hf.creds); gnutls_certificate_free_credentials(hf.creds); } + + /* TLS / SCTP style */ + { + struct connect_flags cf; + struct handshake_flags hf; + + memset(&cf, 0, sizeof(cf)); + cf.proto = IPPROTO_SCTP; + + memset(&hf, 0, sizeof(hf)); + hf.algo = ALGO_HANDSHAKE_3436; + + /* Initialize remote certificate */ + CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), ); + CHECK( GNUTLS_E_SUCCESS, ret ); + /* Set the CA */ + CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), ); + CHECK( 1, ret ); + /* Set the key */ + CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), ); + CHECK( GNUTLS_E_SUCCESS, ret ); + + /* Start the client thread with more streams than the server */ + fd_g_config->cnf_sctp_str = 2 * NB_STREAMS; + CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) ); + + /* Accept the connection of the client */ + server_side = fd_cnx_serv_accept(listener_sctp); + CHECK( 1, server_side ? 1 : 0 ); + + /* Retrieve the client connection object */ + CHECK( 0, pthread_join( thr, (void *)&client_side ) ); + CHECK( 1, client_side ? 1 : 0 ); + hf.cnx = client_side; + + /* Start the handshake directly */ + CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) ); + CHECK( 0, pthread_join(thr, NULL) ); + CHECK( 0, hf.ret ); + + /* Send a few TLS protected message, and replies */ + for (i = 0; i < 4 * NB_STREAMS; i++) { + CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0)); + CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz)); + CHECK( cer_sz, rcv_sz ); + CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) ); + free(rcv_buf); + + CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0)); + CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz)); + CHECK( cer_sz, rcv_sz ); + CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) ); + free(rcv_buf); + } + + /* Now close the connection */ + CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) ); + fd_cnx_destroy(server_side); + CHECK( 0, pthread_join(thr, NULL) ); + + /* Do the same test but with more streams on the server this time */ + fd_g_config->cnf_sctp_str = NB_STREAMS / 2; + CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) ); + + /* Accept the connection of the client */ + server_side = fd_cnx_serv_accept(listener_sctp); + CHECK( 1, server_side ? 1 : 0 ); + + /* Retrieve the client connection object */ + CHECK( 0, pthread_join( thr, (void *)&client_side ) ); + CHECK( 1, client_side ? 1 : 0 ); + hf.cnx = client_side; + + /* Start the handshake directly */ + CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) ); + CHECK( 0, pthread_join(thr, NULL) ); + CHECK( 0, hf.ret ); + + /* Send a few TLS protected message, and replies */ + for (i = 0; i < 2 * NB_STREAMS; i++) { + CHECK( 0, fd_cnx_send(server_side, cer_buf, cer_sz, 0)); + CHECK( 0, fd_cnx_receive(client_side, NULL, &rcv_buf, &rcv_sz)); + CHECK( cer_sz, rcv_sz ); + CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) ); + free(rcv_buf); + + CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0)); + CHECK( 0, fd_cnx_receive(server_side, NULL, &rcv_buf, &rcv_sz)); + CHECK( cer_sz, rcv_sz ); + CHECK( 0, memcmp( rcv_buf, cer_buf, cer_sz ) ); + free(rcv_buf); + } + + /* Now close the connection */ + CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) ); + fd_cnx_destroy(server_side); + CHECK( 0, pthread_join(thr, NULL) ); + + + /* Free the credentials */ + gnutls_certificate_free_keys(hf.creds); + gnutls_certificate_free_cas(hf.creds); + gnutls_certificate_free_credentials(hf.creds); + } + #endif /* DISABLE_SCTP */ @@ -1256,7 +1438,7 @@ /* Start the handshake directly */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) ); fd_cnx_destroy(server_side); CHECK( 0, pthread_join(thr, NULL) ); @@ -1273,6 +1455,9 @@ /* Same in SCTP */ #ifndef DISABLE_SCTP + /* DTLS */ + TODO("Enabled after DTLS implementation"); + if (0) { struct connect_flags cf; struct handshake_flags hf; @@ -1307,7 +1492,57 @@ /* Start the handshake directly */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) ); + fd_cnx_destroy(server_side); + CHECK( 0, pthread_join(thr, NULL) ); + + /* Now close the connection */ + CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) ); + CHECK( 0, pthread_join(thr, NULL) ); + + /* Free the credentials */ + gnutls_certificate_free_keys(hf.creds); + gnutls_certificate_free_cas(hf.creds); + gnutls_certificate_free_credentials(hf.creds); + } + + /* TLS */ + { + struct connect_flags cf; + struct handshake_flags hf; + + memset(&cf, 0, sizeof(cf)); + cf.proto = IPPROTO_SCTP; + + memset(&hf, 0, sizeof(hf)); + hf.algo = ALGO_HANDSHAKE_3436; + + /* Initialize remote certificate */ + CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), ); + CHECK( GNUTLS_E_SUCCESS, ret ); + /* Set the CA */ + CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, ¬rust_ca, GNUTLS_X509_FMT_PEM), ); + CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), ); + CHECK( 1, ret ); + /* Set the key */ + CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, ¬rust_cert, ¬rust_priv, GNUTLS_X509_FMT_PEM), ); + CHECK( GNUTLS_E_SUCCESS, ret ); + + /* Start the client thread */ + CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) ); + + /* Accept the connection of the client */ + server_side = fd_cnx_serv_accept(listener_sctp); + CHECK( 1, server_side ? 1 : 0 ); + + /* Retrieve the client connection object */ + CHECK( 0, pthread_join( thr, (void *)&client_side ) ); + CHECK( 1, client_side ? 1 : 0 ); + hf.cnx = client_side; + + /* Start the handshake directly */ + CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); + CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) ); fd_cnx_destroy(server_side); CHECK( 0, pthread_join(thr, NULL) ); @@ -1356,7 +1591,7 @@ /* Start the handshake directly */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) ); fd_cnx_destroy(server_side); CHECK( 0, pthread_join(thr, NULL) ); @@ -1408,7 +1643,7 @@ /* Start the handshake, check it is successful */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) ); CHECK( 0, pthread_join(thr, NULL) ); CHECK( 0, hf.ret ); @@ -1434,7 +1669,7 @@ /* Start the handshake, check it is successful */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( EINVAL, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) ); fd_cnx_destroy(server_side); CHECK( 0, pthread_join(thr, NULL) ); @@ -1488,7 +1723,7 @@ /* Start the handshake */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) ); CHECK( 0, pthread_join(thr, NULL) ); CHECK( 0, hf.ret ); @@ -1542,6 +1777,8 @@ #ifndef DISABLE_SCTP /* And re-test with a SCTP connection */ + TODO("Enabled after DTLS implementation"); + if (0) { struct connect_flags cf; struct handshake_flags hf; @@ -1581,7 +1818,100 @@ /* Start the handshake */ CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); - CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, NULL, NULL) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_DEFAULT, NULL, NULL) ); + CHECK( 0, pthread_join(thr, NULL) ); + CHECK( 0, hf.ret ); + + /* Test some simple functions */ + + /* fd_cnx_getid */ + str = fd_cnx_getid(server_side); + CHECK( 1, str ? 1 : 0 ); + CHECK( 1, (str[0] != '\0') ? 1 : 0 ); + + /* fd_cnx_getproto */ + i = fd_cnx_getproto(server_side); + CHECK( IPPROTO_SCTP, i); + + /* fd_cnx_getTLS */ + i = fd_cnx_getTLS(server_side); + CHECK( 1, i ? 1 : 0 ); + + /* fd_cnx_getcred */ + CHECK( 0, fd_cnx_getcred(server_side, &cert_list, &cert_list_size) ); + CHECK( 1, (cert_list_size > 0) ? 1 : 0 ); + /* We could also verify that the cert_list really contains the client_cert and ca certificates */ + + /* fd_cnx_getremoteid */ + str = fd_cnx_getremoteid(server_side); + CHECK( 1, str ? 1 : 0 ); + CHECK( 1, (str[0] != '\0') ? 1 : 0 ); + + /* fd_cnx_recv_setaltfifo */ + CHECK( 0, fd_cnx_send(client_side, cer_buf, cer_sz, 0)); + CHECK( 0, fd_fifo_new(&myfifo, 0) ); + CHECK( 0, fd_cnx_recv_setaltfifo(server_side, myfifo) ); + CHECK( 0, clock_gettime(CLOCK_REALTIME, &now) ); + do { + CHECK( 0, fd_event_timedget(myfifo, &now, ETIMEDOUT, &ev_code, NULL, (void *)&rcv_buf) ); + free(rcv_buf); + } while (ev_code != FDEVP_CNX_MSG_RECV); + + /* Now close the connection */ + CHECK( 0, pthread_create(&thr, NULL, destroy_thr, client_side) ); + fd_cnx_destroy(server_side); + CHECK( 0, pthread_join(thr, NULL) ); + + fd_event_destroy(&myfifo, free); + + /* Free the credentials */ + gnutls_certificate_free_keys(hf.creds); + gnutls_certificate_free_cas(hf.creds); + gnutls_certificate_free_credentials(hf.creds); + } + + /* TLS */ + { + struct connect_flags cf; + struct handshake_flags hf; + char * str; + const gnutls_datum_t *cert_list; + unsigned int cert_list_size; + struct fifo * myfifo = NULL; + struct timespec now; + int ev_code; + + memset(&cf, 0, sizeof(cf)); + cf.proto = IPPROTO_SCTP; + + memset(&hf, 0, sizeof(hf)); + hf.algo = ALGO_HANDSHAKE_3436; + + /* Initialize remote certificate */ + CHECK_GNUTLS_DO( ret = gnutls_certificate_allocate_credentials (&hf.creds), ); + CHECK( GNUTLS_E_SUCCESS, ret ); + /* Set the CA */ + CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_trust_mem( hf.creds, &ca, GNUTLS_X509_FMT_PEM), ); + CHECK( 1, ret ); + /* Set the key */ + CHECK_GNUTLS_DO( ret = gnutls_certificate_set_x509_key_mem( hf.creds, &client_cert, &client_priv, GNUTLS_X509_FMT_PEM), ); + CHECK( GNUTLS_E_SUCCESS, ret ); + + /* Start the client thread */ + CHECK( 0, pthread_create(&thr, NULL, connect_thr, &cf) ); + + /* Accept the connection of the client */ + server_side = fd_cnx_serv_accept(listener_sctp); + CHECK( 1, server_side ? 1 : 0 ); + + /* Retrieve the client connection object */ + CHECK( 0, pthread_join( thr, (void *)&client_side ) ); + CHECK( 1, client_side ? 1 : 0 ); + hf.cnx = client_side; + + /* Start the handshake */ + CHECK( 0, pthread_create(&thr, NULL, handshake_thr, &hf) ); + CHECK( 0, fd_cnx_handshake(server_side, GNUTLS_SERVER, ALGO_HANDSHAKE_3436, NULL, NULL) ); CHECK( 0, pthread_join(thr, NULL) ); CHECK( 0, hf.ret );