From 7a63d327772dc64978e9159d49885a9ae7dd9b4e Mon Sep 17 00:00:00 2001 From: defanor Date: Sat, 23 Sep 2023 11:35:39 +0300 Subject: Move GnuTLS operations from Jingle module into TLS module --- examples/basic.c | 3 +- src/rexmpp.c | 6 +- src/rexmpp.h | 6 + src/rexmpp.rs | 5 + src/rexmpp_jingle.c | 482 +++++++++++++++++++++------------------------------- src/rexmpp_jingle.h | 10 +- src/rexmpp_tls.c | 479 +++++++++++++++++++++++++++++++++++++++++++-------- src/rexmpp_tls.h | 76 ++++++++- 8 files changed, 687 insertions(+), 380 deletions(-) diff --git a/examples/basic.c b/examples/basic.c index a529c22..60a240a 100644 --- a/examples/basic.c +++ b/examples/basic.c @@ -140,7 +140,8 @@ int main (int argc, char **argv) { /* Could set a client certificate for SASL EXTERNAL authentication and Jingle's DTLS here. */ - rexmpp_tls_set_x509_key_file(&s, "client.crt", "client.key"); + s.x509_key_file = "client.key"; + s.x509_cert_file = "client.crt"; /* Could also set various other things manually. */ /* s.socks_host = "127.0.0.1"; */ diff --git a/src/rexmpp.c b/src/rexmpp.c index 8674b7d..435fda2 100644 --- a/src/rexmpp.c +++ b/src/rexmpp.c @@ -549,6 +549,9 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, s->iq_queue_size = 1024; s->iq_cache_size = 1024; s->max_jingle_sessions = 1024; + s->x509_cert_file = NULL; + s->x509_key_file = NULL; + s->x509_trust_file = NULL; s->log_function = log_func; s->sasl_property_cb = NULL; s->xml_in_cb = NULL; @@ -911,6 +914,7 @@ rexmpp_err_t rexmpp_send_continue (rexmpp_t *s) tls_was_active = (s->tls_state == REXMPP_TLS_ACTIVE); if (tls_was_active) { err = rexmpp_tls_send (s, + s->tls, s->send_buffer, s->send_buffer_len, &ret); @@ -1205,7 +1209,7 @@ rexmpp_err_t rexmpp_recv (rexmpp_t *s) { do { tls_was_active = (s->tls_state == REXMPP_TLS_ACTIVE); if (tls_was_active) { - recv_err = rexmpp_tls_recv(s, chunk_raw, 4096, &chunk_raw_len); + recv_err = rexmpp_tls_recv(s, s->tls, chunk_raw, 4096, &chunk_raw_len); } else { chunk_raw_len = recv(s->server_socket, chunk_raw, 4096, 0); } diff --git a/src/rexmpp.h b/src/rexmpp.h index a3df757..8cc78ed 100644 --- a/src/rexmpp.h +++ b/src/rexmpp.h @@ -289,6 +289,12 @@ struct rexmpp uint32_t iq_cache_size; uint32_t max_jingle_sessions; + /* X.509 settings: for TLS and DTLS, to use for SASL EXTERNAL + authentication and DTLS-SRTP on Jingle calls. */ + const char *x509_key_file; + const char *x509_cert_file; + const char *x509_trust_file; + /* Callbacks. */ log_function_t log_function; sasl_property_cb_t sasl_property_cb; diff --git a/src/rexmpp.rs b/src/rexmpp.rs index 2959ec3..1e1082a 100644 --- a/src/rexmpp.rs +++ b/src/rexmpp.rs @@ -157,6 +157,11 @@ pub struct Rexmpp { pub iq_cache_size: u32, pub max_jingle_sessions: u32, + // X.509 settings (for TLS and DTLS) + pub x509_key_file: *const c_char, + pub x509_cert_file: *const c_char, + pub x509_trust_file: *const c_char, + // Callbacks // c_variadic is experimental and cannot be used on the stable diff --git a/src/rexmpp_jingle.c b/src/rexmpp_jingle.c index 822da25..848d787 100644 --- a/src/rexmpp_jingle.c +++ b/src/rexmpp_jingle.c @@ -37,9 +37,6 @@ A/V calls over ICE-UDP + DTLS-SRTP: #include #include #include -#include -#include -#include #include #include #include "portaudio.h" @@ -53,6 +50,7 @@ A/V calls over ICE-UDP + DTLS-SRTP: #include "rexmpp_jingle.h" #include "rexmpp_base64.h" #include "rexmpp_random.h" +#include "rexmpp_tls.h" /* https://en.wikipedia.org/wiki/G.711 */ @@ -145,6 +143,7 @@ rexmpp_jingle_session_payload_by_id (rexmpp_jingle_session_t *sess, return NULL; } +#ifdef ENABLE_CALLS int rexmpp_jingle_session_configure_audio (rexmpp_jingle_session_t *sess) { if (sess->accept == NULL) { return 0; @@ -186,7 +185,7 @@ int rexmpp_jingle_session_configure_audio (rexmpp_jingle_session_t *sess) { sess->payload_type, sess->sid); return sess->payload_type; } -#endif +#endif /* HAVE_OPUS */ } } descr_child = rexmpp_xml_next_elem_sibling(descr_child); @@ -245,7 +244,7 @@ rexmpp_jingle_run_audio (rexmpp_jingle_session_t *sess) { sess->opus_dec = opus_decoder_create(rate, channels, &opus_error); } -#endif +#endif /* HAVE_OPUS */ rexmpp_log(sess->s, LOG_DEBUG, "Setting up an audio stream: SSRC %" PRIx32 @@ -272,6 +271,7 @@ rexmpp_jingle_run_audio (rexmpp_jingle_session_t *sess) { Pa_GetErrorText(err)); } } +#endif /* ENABLE_CALLS */ rexmpp_jingle_session_t * @@ -327,7 +327,7 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) { opus_decoder_destroy(session->opus_dec); session->opus_dec = NULL; } -#endif +#endif /* HAVE_OPUS */ for (i = 0; i < 2; i++) { rexmpp_jingle_component_t *comp = &session->component[i]; if (comp->dtls_state == REXMPP_TLS_ACTIVE || @@ -336,14 +336,20 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) { /* SRTP structures are allocated upon a TLS connection, so using the TLS state to find when they should be deallocated. */ - srtp_dealloc(comp->srtp_in); - srtp_dealloc(comp->srtp_out); + if (comp->srtp_in != NULL) { + srtp_dealloc(comp->srtp_in); + } + if (comp->srtp_out != NULL) { + srtp_dealloc(comp->srtp_out); + } } if (comp->dtls_state == REXMPP_TLS_HANDSHAKE || comp->dtls_state == REXMPP_TLS_ACTIVE || comp->dtls_state == REXMPP_TLS_CLOSING || comp->dtls_state == REXMPP_TLS_CLOSED) { - gnutls_deinit(comp->dtls_session); + rexmpp_tls_session_free(comp->dtls); + rexmpp_tls_ctx_free(comp->dtls); + comp->dtls = NULL; comp->dtls_state = REXMPP_TLS_INACTIVE; } } @@ -369,7 +375,7 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) { session->turn_password = NULL; } } -#endif +#endif /* ENABLE_CALLS */ free(session); } @@ -444,7 +450,9 @@ rexmpp_jingle_session_create (rexmpp_t *s, sess->component[i].session = sess; sess->component[i].s = s; sess->component[i].dtls_state = REXMPP_TLS_INACTIVE; - sess->component[i].dtls_buf_len = 0; + sess->component[i].dtls = rexmpp_tls_ctx_new(s, 1); + sess->component[i].srtp_out = NULL; + sess->component[i].srtp_in = NULL; } sess->ice_agent = NULL; sess->rtcp_mux = s->jingle_prefer_rtcp_mux; @@ -465,9 +473,8 @@ rexmpp_jingle_session_create (rexmpp_t *s, #ifdef HAVE_OPUS sess->opus_enc = NULL; sess->opus_dec = NULL; -#endif - /* rexmpp_jingle_ice_agent_init(sess); */ -#endif +#endif /* HAVE_POUS */ +#endif /* ENABLE_CALLS */ if (! rexmpp_jingle_session_add(s, sess)) { rexmpp_jingle_session_destroy(sess); sess = NULL; @@ -505,7 +512,7 @@ int rexmpp_jingle_init (rexmpp_t *s) { s->jingle->gloop = g_main_loop_new(NULL, FALSE); Pa_Initialize(); nice_debug_enable(1); -#endif +#endif /* ENABLE_CALLS */ return 0; } @@ -518,7 +525,7 @@ void rexmpp_jingle_stop (rexmpp_t *s) { s->jingle->gloop = NULL; srtp_shutdown(); Pa_Terminate(); -#endif +#endif /* ENABLE_CALLS */ free(s->jingle); s->jingle = NULL; } @@ -1006,32 +1013,18 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent, rexmpp_log(sess->s, LOG_DEBUG, "ICE agent candidate gathering is done"); - gnutls_x509_crt_t *cert_list; - unsigned int cert_list_size = 0; - /* We'll need a certificate a bit later, but checking it before + /* We'll need a fingerprint a bit later, but checking it before allocating other things. */ - int err = gnutls_certificate_get_x509_crt(sess->s->tls->dtls_cred, 0, - &cert_list, &cert_list_size); - if (err) { - rexmpp_log(sess->s, LOG_ERR, - "Failed to read own certificate list: %s", - gnutls_strerror(err)); - return; - } - char fp[32], fp_str[97]; + /* TODO: should use DTLS credentials, not regular TLS ones. Using + these for now because DTLS ones are not allocated yet, and they + are the same anyway. */ + char fp[32], fp_str[32 * 3 + 1]; size_t fp_size = 32; - gnutls_x509_crt_get_fingerprint(cert_list[0], GNUTLS_DIG_SHA256, fp, &fp_size); - unsigned int i; - for (i = 0; i < 32; i++) { - snprintf(fp_str + i * 3, 4, "%02X:", fp[i] & 0xFF); - } - fp_str[95] = 0; - for (i = 0; i < cert_list_size; i++) { - gnutls_x509_crt_deinit(cert_list[i]); + if (rexmpp_tls_session_fp(sess->s, sess->s->tls, "sha-256", fp, fp_str, &fp_size)) { + return; } - gnutls_free(cert_list); rexmpp_xml_t *jingle = rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1"); rexmpp_xml_add_attr(jingle, "sid", sess->sid); @@ -1246,7 +1239,7 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent, } ssize_t -rexmpp_jingle_dtls_push_func (gnutls_transport_ptr_t p, const void *data, size_t size) +rexmpp_jingle_dtls_push_func (void *p, const void *data, size_t size) { rexmpp_jingle_component_t *comp = p; rexmpp_jingle_session_t *sess = comp->session; @@ -1254,19 +1247,21 @@ rexmpp_jingle_dtls_push_func (gnutls_transport_ptr_t p, const void *data, size_t comp->component_id, size, data); } -ssize_t rexmpp_jingle_dtls_generic_pull_func (rexmpp_jingle_session_t *sess, - char *tls_buf, - size_t *tls_buf_len, - gnutls_session_t tls_session, - void *data, - size_t size) +rexmpp_tls_err_t +rexmpp_jingle_dtls_pull_func (void *p, + void *data, + size_t size, + ssize_t *received) { - (void)sess; - size_t ret = -1; + rexmpp_jingle_component_t *comp = p; + char *tls_buf = comp->dtls->dtls_buf; + size_t *tls_buf_len = &(comp->dtls->dtls_buf_len); + + rexmpp_tls_err_t ret = REXMPP_TLS_SUCCESS; if (*tls_buf_len > 0) { if (size >= *tls_buf_len) { memcpy(data, tls_buf, *tls_buf_len); - ret = *tls_buf_len; + *received = *tls_buf_len; *tls_buf_len = 0; } else { if (size > DTLS_SRTP_BUF_SIZE) { @@ -1274,43 +1269,23 @@ ssize_t rexmpp_jingle_dtls_generic_pull_func (rexmpp_jingle_session_t *sess, } memcpy(data, tls_buf, size); memmove(tls_buf, tls_buf + size, DTLS_SRTP_BUF_SIZE - size); - ret = size; + *received = size; *tls_buf_len = *tls_buf_len - size; } } else { - gnutls_transport_set_errno(tls_session, EAGAIN); - ret = -1; + ret = REXMPP_TLS_E_AGAIN; } - return ret; } -ssize_t -rexmpp_jingle_dtls_pull_func (gnutls_transport_ptr_t p, - void *data, - size_t size) +/* The timeout is always zero for DTLS. */ +int rexmpp_jingle_dtls_pull_timeout_func (void *p, + unsigned int ms) { rexmpp_jingle_component_t *comp = p; rexmpp_jingle_session_t *sess = comp->session; - return - rexmpp_jingle_dtls_generic_pull_func(sess, - comp->dtls_buf, - &comp->dtls_buf_len, - comp->dtls_session, - data, - size); -} - - -/* Apparently this should not be called with non-blocking sockets, but - it is. */ -int -rexmpp_jingle_dtls_generic_pull_timeout_func (rexmpp_jingle_session_t *sess, - unsigned int ms, - guint component_id) -{ - if (sess->component[component_id].dtls_buf_len > 0) { - return 1; + if (comp->dtls->dtls_buf_len > 0) { + return comp->dtls->dtls_buf_len; } fd_set rfds; @@ -1326,7 +1301,7 @@ rexmpp_jingle_dtls_generic_pull_timeout_func (rexmpp_jingle_session_t *sess, GPtrArray *sockets = nice_agent_get_sockets(sess->ice_agent, - sess->ice_stream_id, component_id); + sess->ice_stream_id, comp->component_id); guint i; for (i = 0; i < sockets->len; i++) { fd = g_socket_get_fd(sockets->pdata[i]); @@ -1353,7 +1328,7 @@ rexmpp_jingle_dtls_generic_pull_timeout_func (rexmpp_jingle_session_t *sess, ret = 0; sockets = nice_agent_get_sockets(sess->ice_agent, - sess->ice_stream_id, component_id); + sess->ice_stream_id, comp->component_id); for (i = 0; i < sockets->len; i++) { int err = recvfrom(g_socket_get_fd(sockets->pdata[i]), &c, 1, MSG_PEEK, @@ -1373,20 +1348,11 @@ rexmpp_jingle_dtls_generic_pull_timeout_func (rexmpp_jingle_session_t *sess, g_ptr_array_unref(sockets); if (ret > 0) { - return 1; + return ret; } - return 0; } -int rexmpp_jingle_dtls_pull_timeout_func (gnutls_transport_ptr_t p, - unsigned int ms) -{ - rexmpp_jingle_component_t *comp = p; - return rexmpp_jingle_dtls_generic_pull_timeout_func(comp->session, ms, - comp->component_id); -} - const char *rexmpp_ice_component_state_text(int state) { switch (state) { case NICE_COMPONENT_STATE_DISCONNECTED: return "disconnected"; @@ -1430,30 +1396,13 @@ rexmpp_jingle_component_state_changed_cb (NiceAgent *agent, return; } - int active_role = rexmpp_jingle_dtls_is_active(sess, 0); - - gnutls_session_t *tls_session = &sess->component[component_id - 1].dtls_session; - gnutls_init(tls_session, - (active_role ? GNUTLS_CLIENT : GNUTLS_SERVER) | - GNUTLS_DATAGRAM | - GNUTLS_NONBLOCK); - if (! active_role) { - gnutls_certificate_server_set_request(*tls_session, GNUTLS_CERT_REQUEST); - } - gnutls_set_default_priority(*tls_session); - gnutls_credentials_set(*tls_session, GNUTLS_CRD_CERTIFICATE, - sess->s->tls->dtls_cred); - - gnutls_transport_set_ptr(*tls_session, &(sess->component[component_id - 1])); - gnutls_transport_set_push_function(*tls_session, rexmpp_jingle_dtls_push_func); - gnutls_transport_set_pull_function(*tls_session, rexmpp_jingle_dtls_pull_func); - gnutls_transport_set_pull_timeout_function(*tls_session, - rexmpp_jingle_dtls_pull_timeout_func); + rexmpp_jingle_component_t *comp = &(sess->component[component_id - 1]); + rexmpp_dtls_connect(sess->s, + comp->dtls, + comp, + rexmpp_jingle_dtls_is_active(sess, 0)); sess->component[component_id - 1].dtls_state = REXMPP_TLS_HANDSHAKE; - /* todo: use the profile/crypto-suite from element */ - gnutls_srtp_set_profile(*tls_session, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80); - gnutls_handshake(*tls_session); - + rexmpp_tls_handshake(sess->s, comp->dtls); } else if (state == NICE_COMPONENT_STATE_FAILED) { rexmpp_log(sess->s, LOG_ERR, "ICE connection failed for Jingle session %s, ICE stream %d, component %d", @@ -1492,6 +1441,11 @@ rexmpp_jingle_ice_recv_cb (NiceAgent *agent, guint stream_id, guint component_id "Received an SRTP packet while DTLS is inactive"); return; } + if (srtp_in == NULL) { + rexmpp_log(comp->s, LOG_WARNING, + "Received an SRTP packet while SRTP is not set up"); + return; + } if (component_id == 1) { err = srtp_unprotect(srtp_in, buf, (int*)&len); if (err == srtp_err_status_auth_fail && comp->session->rtcp_mux) { @@ -1565,7 +1519,7 @@ rexmpp_jingle_ice_recv_cb (NiceAgent *agent, guint stream_id, guint component_id playback->write_pos %= PA_BUF_SIZE; } } -#endif +#endif /* HAVE_OPUS */ else { /* Some other payload type, possibly with a dynamic ID */ rexmpp_xml_t *payload = @@ -1651,9 +1605,9 @@ rexmpp_jingle_ice_recv_cb (NiceAgent *agent, guint stream_id, guint component_id } } } else { - if (comp->dtls_buf_len + len < DTLS_SRTP_BUF_SIZE) { - memcpy(comp->dtls_buf + comp->dtls_buf_len, buf, len); - comp->dtls_buf_len += len; + if (comp->dtls->dtls_buf_len + len < DTLS_SRTP_BUF_SIZE) { + memcpy(comp->dtls->dtls_buf + comp->dtls->dtls_buf_len, buf, len); + comp->dtls->dtls_buf_len += len; } else { rexmpp_log(comp->s, LOG_WARNING, "Dropping a DTLS packet"); } @@ -1882,34 +1836,26 @@ rexmpp_jingle_call_accept (rexmpp_t *s, rexmpp_jingle_discover_turn(s, sess); return REXMPP_SUCCESS; } -#else +#else /* ENABLE_CALLS */ rexmpp_err_t rexmpp_jingle_call (rexmpp_t *s, - const char *jid, - uint16_t rtp_port_in, - uint16_t rtp_port_out) + const char *jid) { (void)jid; - (void)rtp_port_in; - (void)rtp_port_out; rexmpp_log(s, LOG_ERR, "rexmpp is compiled without support for media calls"); return REXMPP_E_OTHER; } rexmpp_err_t rexmpp_jingle_call_accept (rexmpp_t *s, - const char *sid, - uint16_t rtp_port_in, - uint16_t rtp_port_out) + const char *sid) { (void)sid; - (void)rtp_port_in; - (void)rtp_port_out; rexmpp_log(s, LOG_ERR, "rexmpp is compiled without support for media calls"); return REXMPP_E_OTHER; } -#endif +#endif /* ENABLE_CALLS */ int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) { int handled = 0; @@ -1960,13 +1906,15 @@ int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) { sess->initiate = rexmpp_xml_clone(jingle); sess->ibb_sid = strdup(ibb_sid); } else { - rexmpp_jingle_session_terminate(s, sid, - rexmpp_xml_new_elem("failed-transport", - "urn:xmpp:jingle:1"), - NULL); + rexmpp_jingle_session_terminate + (s, sid, + rexmpp_xml_new_elem("failed-transport", + "urn:xmpp:jingle:1"), + NULL); } } else { - rexmpp_log(s, LOG_ERR, "Jingle IBB transport doesn't have a sid attribute"); + rexmpp_log(s, LOG_ERR, + "Jingle IBB transport doesn't have a sid attribute"); rexmpp_jingle_session_terminate (s, sid, rexmpp_xml_new_elem("unsupported-transports", @@ -1985,7 +1933,7 @@ int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) { "urn:xmpp:jingle:apps:rtp:1", "rtcp-mux") != NULL); sess->initiate = rexmpp_xml_clone(jingle); -#endif +#endif /* ENABLE_CALLS */ } else if (file_description == NULL && rtp_description == NULL) { rexmpp_jingle_session_terminate @@ -1995,10 +1943,11 @@ int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) { NULL); } else if (ibb_transport == NULL && ice_udp_transport == NULL) { - rexmpp_jingle_session_terminate(s, sid, - rexmpp_xml_new_elem("unsupported-transports", - "urn:xmpp:jingle:1"), - NULL); + rexmpp_jingle_session_terminate + (s, sid, + rexmpp_xml_new_elem("unsupported-transports", + "urn:xmpp:jingle:1"), + NULL); } else { /* todo: some other error */ } @@ -2012,8 +1961,6 @@ int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) { rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid); if (session != NULL) { session->accept = rexmpp_xml_clone(jingle); - rexmpp_jingle_session_configure_audio(session); - rexmpp_jingle_run_audio(session); rexmpp_xml_t *content = rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content"); rexmpp_xml_t *file_description = @@ -2032,6 +1979,8 @@ int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) { rexmpp_jingle_ibb_send_cb, strdup(sid)); } else { #ifdef ENABLE_CALLS + rexmpp_jingle_session_configure_audio(session); + rexmpp_jingle_run_audio(session); rexmpp_xml_t *ice_udp_transport = rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1", "transport"); @@ -2041,7 +1990,7 @@ int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) { rexmpp_log(s, LOG_WARNING, "ICE-UDP transport is unset in session-accept"); } -#endif +#endif /* ENABLE_CALLS */ } } else { rexmpp_log(s, LOG_WARNING, "Jingle session %s is not found", sid); @@ -2059,7 +2008,7 @@ int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) { if (ice_udp_transport != NULL) { rexmpp_jingle_ice_udp_add_remote(session, ice_udp_transport); } -#endif +#endif /* ENABLE_CALLS */ } } else { rexmpp_log(s, LOG_WARNING, "Unknown Jingle action: %s", action); @@ -2181,11 +2130,11 @@ int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) { rexmpp_log(s, LOG_ERR, "Failed to acquire GMainContext in rexmpp_jingle_fds"); } -#else +#else /* ENABLE_CALLS */ (void)s; (void)read_fds; (void)write_fds; -#endif +#endif /* ENABLE_CALLS */ return (nfds + 1); } @@ -2211,13 +2160,14 @@ struct timespec * rexmpp_jingle_timeout (rexmpp_t *s, if (sess->component[i].dtls_state != REXMPP_TLS_INACTIVE && sess->component[i].dtls_state != REXMPP_TLS_CLOSED && sess->component[i].dtls_state != REXMPP_TLS_ERROR) { - int tms = gnutls_dtls_get_timeout(sess->component[i].dtls_session); + int tms = rexmpp_dtls_timeout(sess->s, sess->component[i].dtls); if (tms > 0 && (poll_timeout < 0 || tms < poll_timeout)) { poll_timeout = tms; } /* Set poll timeout to at most 5 ms if there are connected components, for timely transmission of Jingle data. */ - if (poll_timeout < 0 || poll_timeout > 5) { + if (sess->component[i].dtls_state == REXMPP_TLS_ACTIVE && + (poll_timeout < 0 || poll_timeout > 5)) { poll_timeout = 5; } } @@ -2239,10 +2189,10 @@ struct timespec * rexmpp_jingle_timeout (rexmpp_t *s, rexmpp_log(s, LOG_ERR, "Failed to acquire GMainContext in rexmpp_jingle_timeout"); } -#else +#else /* ENABLE_CALLS */ (void)s; (void)tv; -#endif +#endif /* ENABLE_CALLS */ return max_tv; } @@ -2255,10 +2205,7 @@ rexmpp_jingle_run (rexmpp_t *s, (void)read_fds; #ifdef ENABLE_CALLS rexmpp_jingle_session_t *sess; - int key_mat_size; - char key_mat[4096]; int err; - gnutls_datum_t client_key, client_salt, server_key, server_salt; unsigned char client_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT * 2], server_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT * 2]; for (sess = s->jingle->sessions; sess != NULL; sess = sess->next) { @@ -2270,190 +2217,141 @@ rexmpp_jingle_run (rexmpp_t *s, rexmpp_jingle_component_t *comp = &sess->component[comp_id]; if (comp->dtls_state == REXMPP_TLS_HANDSHAKE) { - int ret = gnutls_handshake(comp->dtls_session); - if (ret == 0) { + int ret = rexmpp_tls_handshake(s, comp->dtls); + if (ret == REXMPP_TLS_SUCCESS) { rexmpp_log(s, LOG_DEBUG, "DTLS connected for Jingle session %s, component %d", sess->sid, comp->component_id); comp->dtls_state = REXMPP_TLS_ACTIVE; - /* Verify the peer's fingerprint */ - - unsigned int cert_list_size = 0; - const gnutls_datum_t *cert_list; - cert_list = - gnutls_certificate_get_peers(comp->dtls_session, &cert_list_size); - if (cert_list_size != 1) { + rexmpp_xml_t *jingle = comp->session->initiator + ? comp->session->accept + : comp->session->initiate; + rexmpp_xml_t *fingerprint = + rexmpp_xml_find_child + (rexmpp_xml_find_child + (rexmpp_xml_find_child + (jingle, "urn:xmpp:jingle:1", "content"), + "urn:xmpp:jingle:transports:ice-udp:1", "transport"), + "urn:xmpp:jingle:apps:dtls:0", "fingerprint"); + if (fingerprint == NULL) { + /* todo: might be neater to check it upon receiving the + stanzas, instead of checking it here */ rexmpp_log(comp->s, LOG_ERR, - "Unexpected peer certificate list size: %d", - cert_list_size); + "No fingerprint in the peer's Jingle element"); rexmpp_jingle_session_terminate (s, sess->sid, - rexmpp_xml_new_elem("security-error", "urn:xmpp:jingle:1"), - "Unexpected certificate list size; expected exactly 1."); + rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"), + "No fingerprint element"); + return REXMPP_E_TLS; } else { - rexmpp_xml_t *jingle = comp->session->initiator - ? comp->session->accept - : comp->session->initiate; - rexmpp_xml_t *fingerprint = - rexmpp_xml_find_child - (rexmpp_xml_find_child - (rexmpp_xml_find_child - (jingle, "urn:xmpp:jingle:1", "content"), - "urn:xmpp:jingle:transports:ice-udp:1", "transport"), - "urn:xmpp:jingle:apps:dtls:0", "fingerprint"); - if (fingerprint == NULL) { - /* todo: might be neater to check it upon receiving the - stanzas, instead of checking it here */ + const char *hash_str = rexmpp_xml_find_attr_val(fingerprint, "hash"); + if (hash_str == NULL) { rexmpp_log(comp->s, LOG_ERR, - "No fingerprint in the peer's Jingle element"); + "No hash attribute in the peer's fingerprint element"); rexmpp_jingle_session_terminate (s, sess->sid, rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"), - "No fingerprint element"); + "No hash attribute in the fingerprint element"); + return REXMPP_E_TLS; } else { - const char *hash_str = rexmpp_xml_find_attr_val(fingerprint, "hash"); - if (hash_str == NULL) { - rexmpp_log(comp->s, LOG_ERR, - "No hash attribute in the peer's fingerprint element"); - rexmpp_jingle_session_terminate - (s, sess->sid, - rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"), - "No hash attribute in the fingerprint element"); - break; - } else { - gnutls_digest_algorithm_t algo = GNUTLS_DIG_UNKNOWN; - /* gnutls_digest_get_id uses different names, so - checking manually here. These are SDP options, - . */ - if (strcmp(hash_str, "sha-1") == 0) { - algo = GNUTLS_DIG_SHA1; - } else if (strcmp(hash_str, "sha-224") == 0) { - algo = GNUTLS_DIG_SHA224; - } else if (strcmp(hash_str, "sha-256") == 0) { - algo = GNUTLS_DIG_SHA256; - } else if (strcmp(hash_str, "sha-384") == 0) { - algo = GNUTLS_DIG_SHA384; - } else if (strcmp(hash_str, "sha-512") == 0) { - algo = GNUTLS_DIG_SHA512; - } else if (strcmp(hash_str, "md5") == 0) { - algo = GNUTLS_DIG_MD5; - } - if (algo == GNUTLS_DIG_UNKNOWN) { + char fp[64], fp_str[64 * 3]; + size_t fp_size = 64; + if (rexmpp_tls_peer_fp(comp->s, comp->dtls, hash_str, + fp, fp_str, &fp_size)) + { + rexmpp_jingle_session_terminate + (s, sess->sid, + rexmpp_xml_new_elem("connectivity-error", + "urn:xmpp:jingle:1"), + "Failed to obtain the DTLS certificate fingerprint"); + return REXMPP_E_TLS; + } else { + const char *fingerprint_cont = + rexmpp_xml_text_child(fingerprint); + /* Fingerprint string should be uppercase, but + allowing any case for now, while Dino uses + lowercase. */ + int fingerprint_mismatch = strcasecmp(fingerprint_cont, fp_str); + if (fingerprint_mismatch) { rexmpp_log(comp->s, LOG_ERR, - "Unknown hash algorithm in the peer's fingerprint"); + "Peer's fingerprint mismatch: expected %s," + " calculated %s", + fingerprint_cont, fp_str); rexmpp_jingle_session_terminate (s, sess->sid, - rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"), - "Unknown hash algorithm for a DTLS certificate fingerprint"); - break; + rexmpp_xml_new_elem("security-error", "urn:xmpp:jingle:1"), + "DTLS certificate fingerprint mismatch"); + return REXMPP_E_TLS; } else { - - char fp[64], fp_str[64 * 3]; - size_t fp_size = 64; - gnutls_fingerprint(algo, cert_list, fp, &fp_size); - size_t i; - for (i = 0; i < fp_size; i++) { - snprintf(fp_str + i * 3, 4, "%02X:", fp[i] & 0xFF); + /* The fingerprint is fine, proceed to SRTP. */ + rexmpp_log(comp->s, LOG_DEBUG, + "Peer's fingerprint: %s", fp_str); + + rexmpp_tls_srtp_get_keys(s, comp->dtls, + SRTP_AES_128_KEY_LEN, SRTP_SALT_LEN, + client_sess_key, server_sess_key); + + int active_role = rexmpp_jingle_dtls_is_active(sess, 0); + + srtp_policy_t inbound; + memset(&inbound, 0x0, sizeof(srtp_policy_t)); + srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp); + srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp); + inbound.ssrc.type = ssrc_any_inbound; + inbound.key = active_role ? server_sess_key : client_sess_key; + inbound.window_size = 1024; + inbound.allow_repeat_tx = 1; + inbound.next = NULL; + err = srtp_create(&(comp->srtp_in), &inbound); + if (err) { + rexmpp_log(s, LOG_ERR, "Failed to create srtp_in"); } - fp_str[fp_size * 3 - 1] = 0; - - const char *fingerprint_cont = - rexmpp_xml_text_child(fingerprint); - /* Fingerprint string should be uppercase, but - allowing any case for now, while Dino uses - lowercase. */ - int fingerprint_mismatch = strcasecmp(fingerprint_cont, fp_str); - if (fingerprint_mismatch) { - rexmpp_log(comp->s, LOG_ERR, - "Peer's fingerprint mismatch: expected %s, calculated %s", - fingerprint_cont, fp_str); - rexmpp_jingle_session_terminate - (s, sess->sid, - rexmpp_xml_new_elem("security-error", "urn:xmpp:jingle:1"), - "DTLS certificate fingerprint mismatch"); - break; - } else { - /* The fingerprint is fine, proceed to SRTP. */ - rexmpp_log(comp->s, LOG_DEBUG, "Peer's fingerprint: %s", fp_str); - - key_mat_size = - gnutls_srtp_get_keys(comp->dtls_session, key_mat, - SRTP_AES_ICM_128_KEY_LEN_WSALT * 2, - &client_key, &client_salt, - &server_key, &server_salt); - rexmpp_log(s, LOG_DEBUG, "SRTP key material size: %d", - key_mat_size); - memcpy(client_sess_key, client_key.data, - SRTP_AES_128_KEY_LEN); - memcpy(client_sess_key + SRTP_AES_128_KEY_LEN, - client_salt.data, SRTP_SALT_LEN); - - memcpy(server_sess_key, server_key.data, - SRTP_AES_128_KEY_LEN); - memcpy(server_sess_key + SRTP_AES_128_KEY_LEN, - server_salt.data, SRTP_SALT_LEN); - - int active_role = rexmpp_jingle_dtls_is_active(sess, 0); - - srtp_policy_t inbound; - memset(&inbound, 0x0, sizeof(srtp_policy_t)); - srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp); - srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp); - inbound.ssrc.type = ssrc_any_inbound; - inbound.key = active_role ? server_sess_key : client_sess_key; - inbound.window_size = 1024; - inbound.allow_repeat_tx = 1; - inbound.next = NULL; - err = srtp_create(&(comp->srtp_in), &inbound); - if (err) { - rexmpp_log(s, LOG_ERR, "Failed to create srtp_in"); - } - srtp_policy_t outbound; - memset(&outbound, 0x0, sizeof(srtp_policy_t)); - srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp); - srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp); - outbound.ssrc.type = ssrc_any_outbound; - outbound.key = active_role ? client_sess_key : server_sess_key; - outbound.window_size = 1024; - outbound.allow_repeat_tx = 1; - outbound.next = NULL; - err = srtp_create(&(comp->srtp_out), &outbound); - if (err) { - rexmpp_log(s, LOG_ERR, "Failed to create srtp_out"); - } + srtp_policy_t outbound; + memset(&outbound, 0x0, sizeof(srtp_policy_t)); + srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp); + srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp); + outbound.ssrc.type = ssrc_any_outbound; + outbound.key = active_role ? client_sess_key : server_sess_key; + outbound.window_size = 1024; + outbound.allow_repeat_tx = 1; + outbound.next = NULL; + err = srtp_create(&(comp->srtp_out), &outbound); + if (err) { + rexmpp_log(s, LOG_ERR, "Failed to create srtp_out"); } } } } } - } else if (ret != GNUTLS_E_AGAIN) { - rexmpp_log(s, LOG_ERR, "DTLS error for session %s, component %d: %s", - sess->sid, comp->component_id, gnutls_strerror(ret)); + } else if (ret != REXMPP_TLS_E_AGAIN) { + rexmpp_log(s, LOG_ERR, "DTLS error for session %s, component %d", + sess->sid, comp->component_id); comp->dtls_state = REXMPP_TLS_ERROR; if (comp->component_id == 1) { rexmpp_jingle_session_terminate (s, sess->sid, rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"), "DTLS connection error"); - break; + return REXMPP_E_TLS; } } } - /* Check on the DTLS session too. */ + /* Check on the DTLS session, too. */ if (comp->dtls_state == REXMPP_TLS_ACTIVE) { - input_len = gnutls_record_recv(comp->dtls_session, input, 4096); + ssize_t received; + rexmpp_tls_recv(s, comp->dtls, input, 4096, &received); } } /* Send captured audio frames for established sessions. */ if ((sess->component[0].dtls_state == REXMPP_TLS_ACTIVE) + && (sess->component[0].srtp_out != NULL) && (sess->codec != REXMPP_CODEC_UNDEFINED)) { struct ring_buf *capture = &(sess->ring_buffers.capture); - while ((sess->component[0].srtp_out != NULL) && - (capture->write_pos != capture->read_pos)) { + while (capture->write_pos != capture->read_pos) { if (sess->codec == REXMPP_CODEC_PCMU) { for (input_len = 12; input_len < 4096 && capture->write_pos != capture->read_pos; @@ -2517,7 +2415,7 @@ rexmpp_jingle_run (rexmpp_t *s, break; } } -#endif +#endif /* HAVE_OPUS */ /* Setup an RTP header */ uint32_t hl, nl; @@ -2551,9 +2449,9 @@ rexmpp_jingle_run (rexmpp_t *s, } } g_main_context_iteration(g_main_loop_get_context(s->jingle->gloop), 0); -#else +#else /* ENABLE_CALLS */ (void)s; (void)read_fds; -#endif +#endif /* ENABLE_CALLS */ return REXMPP_SUCCESS; } diff --git a/src/rexmpp_jingle.h b/src/rexmpp_jingle.h index a226b9a..57fe324 100644 --- a/src/rexmpp_jingle.h +++ b/src/rexmpp_jingle.h @@ -22,11 +22,12 @@ #ifdef HAVE_OPUS #include #endif -#define DTLS_SRTP_BUF_SIZE 0x4000 #define PA_BUF_SIZE 0x4000 #endif #include "rexmpp.h" +#include "rexmpp_tls.h" + /** @brief Processes incoming Jingle IQs. */ int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem); @@ -76,10 +77,11 @@ struct rexmpp_jingle_component { rexmpp_t *s; rexmpp_jingle_session_t *session; int component_id; - gnutls_session_t dtls_session; - char dtls_buf[DTLS_SRTP_BUF_SIZE]; + rexmpp_tls_t *dtls; + /* gnutls_session_t dtls_session; */ + /* char dtls_buf[DTLS_SRTP_BUF_SIZE]; */ + /* size_t dtls_buf_len; */ enum tls_st dtls_state; - size_t dtls_buf_len; srtp_t srtp_in; srtp_t srtp_out; }; diff --git a/src/rexmpp_tls.c b/src/rexmpp_tls.c index 9234e92..803cae8 100644 --- a/src/rexmpp_tls.c +++ b/src/rexmpp_tls.c @@ -17,6 +17,7 @@ #include #include #include +#include #elif defined(USE_OPENSSL) #include #endif @@ -24,6 +25,16 @@ #include "rexmpp.h" #include "rexmpp_tls.h" +ssize_t +rexmpp_jingle_dtls_push_func (void *p, const void *data, size_t size); +rexmpp_tls_err_t +rexmpp_jingle_dtls_pull_func (void *comp, + void *data, + size_t size, + ssize_t *received); +int rexmpp_jingle_dtls_pull_timeout_func (void *p, + unsigned int ms); + #if defined(USE_OPENSSL) rexmpp_tls_err_t rexmpp_process_openssl_ret (rexmpp_t *s, int ret) { int err = SSL_get_error(s->tls->openssl_conn, ret); @@ -43,101 +54,191 @@ rexmpp_tls_err_t rexmpp_process_openssl_ret (rexmpp_t *s, int ret) { } #endif -int rexmpp_tls_init (rexmpp_t *s) { - s->tls = malloc(sizeof(struct rexmpp_tls)); +rexmpp_tls_t *rexmpp_tls_ctx_new (rexmpp_t *s, int dtls) { + rexmpp_tls_t *tls_ctx = malloc(sizeof(rexmpp_tls_t)); #if defined(USE_GNUTLS) + (void)dtls; int err; - s->tls->tls_session_data = NULL; - s->tls->tls_session_data_size = 0; + tls_ctx->tls_session_data = NULL; + tls_ctx->tls_session_data_size = 0; - err = gnutls_certificate_allocate_credentials(&(s->tls->gnutls_cred)); + err = gnutls_certificate_allocate_credentials(&(tls_ctx->gnutls_cred)); if (err) { rexmpp_log(s, LOG_CRIT, "gnutls credentials allocation error: %s", gnutls_strerror(err)); - return 1; + return NULL; + } + if (! dtls) { + err = gnutls_certificate_set_x509_system_trust(tls_ctx->gnutls_cred); } - err = gnutls_certificate_set_x509_system_trust(s->tls->gnutls_cred); if (err < 0) { rexmpp_log(s, LOG_CRIT, "Certificates loading error: %s", gnutls_strerror(err)); - return 1; + return NULL; } -#ifdef ENABLE_CALLS - err = gnutls_certificate_allocate_credentials(&(s->tls->dtls_cred)); - if (err) { - gnutls_certificate_free_credentials(s->tls->gnutls_cred); - rexmpp_log(s, LOG_CRIT, "gnutls credentials allocation error: %s", - gnutls_strerror(err)); - return 1; - } -#endif - return 0; + + tls_ctx->dtls_buf_len = 0; #elif defined(USE_OPENSSL) - SSL_library_init(); - SSL_load_error_strings(); - s->tls->openssl_direction = REXMPP_OPENSSL_NONE; - s->tls->openssl_conn = NULL; - s->tls->openssl_ctx = SSL_CTX_new(TLS_client_method()); - if (s->tls->openssl_ctx == NULL) { + tls_ctx->openssl_direction = REXMPP_OPENSSL_NONE; + tls_ctx->openssl_conn = NULL; + tls_ctx->openssl_ctx = SSL_CTX_new(dtls + ? DTLS_method() + : TLS_method()); + if (tls_ctx->openssl_ctx == NULL) { rexmpp_log(s, LOG_CRIT, "OpenSSL context creation error"); - return 1; + return NULL; } - SSL_CTX_set_verify(s->tls->openssl_ctx, SSL_VERIFY_PEER, NULL); - if (SSL_CTX_set_default_verify_paths(s->tls->openssl_ctx) == 0) { - rexmpp_log(s, LOG_CRIT, "Failed to set default verify paths for OpenSSL context"); - SSL_CTX_free(s->tls->openssl_ctx); - s->tls->openssl_ctx = NULL; - return 1; + SSL_CTX_set_verify(tls_ctx->openssl_ctx, SSL_VERIFY_PEER, NULL); + if (SSL_CTX_set_default_verify_paths(tls_ctx->openssl_ctx) == 0) { + rexmpp_log(s, LOG_CRIT, + "Failed to set default verify paths for OpenSSL context"); + SSL_CTX_free(tls_ctx->openssl_ctx); + tls_ctx->openssl_ctx = NULL; + return NULL; } - return 0; #else - s->tls = NULL; - return 0; + (void)s; + (void)dtls; +#endif + return tls_ctx; +} + +void rexmpp_tls_ctx_free (rexmpp_tls_t *tls_ctx) { +#if defined(USE_GNUTLS) + gnutls_certificate_free_credentials(tls_ctx->gnutls_cred); + if (tls_ctx->tls_session_data != NULL) { + free(tls_ctx->tls_session_data); + tls_ctx->tls_session_data = NULL; + } +#elif defined(USE_OPENSSL) + if (tls_ctx->openssl_ctx != NULL) { + SSL_CTX_free(tls_ctx->openssl_ctx); + } + tls_ctx->openssl_ctx = NULL; #endif + free(tls_ctx); } +int rexmpp_tls_init (rexmpp_t *s) { +#if defined(USE_OPENSSL) + SSL_library_init(); + SSL_load_error_strings(); +#endif + s->tls = rexmpp_tls_ctx_new(s, 0); + return (s->tls == NULL); +} -void rexmpp_tls_cleanup (rexmpp_t *s) { - if (s->tls_state != REXMPP_TLS_INACTIVE && - s->tls_state != REXMPP_TLS_AWAITING_DIRECT) { +void rexmpp_tls_session_free (rexmpp_tls_t *tls_ctx) { #if defined(USE_GNUTLS) - gnutls_deinit(s->tls->gnutls_session); + gnutls_deinit(tls_ctx->gnutls_session); #elif defined(USE_OPENSSL) - if (s->tls->openssl_conn != NULL) { - SSL_free(s->tls->openssl_conn); - s->tls->openssl_conn = NULL; + if (tls_ctx->openssl_conn != NULL) { + SSL_free(tls_ctx->openssl_conn); + tls_ctx->openssl_conn = NULL; } - s->tls->openssl_direction = REXMPP_OPENSSL_NONE; + tls_ctx->openssl_direction = REXMPP_OPENSSL_NONE; #else (void)s; #endif +} + +void rexmpp_tls_cleanup (rexmpp_t *s) { + if (s->tls_state != REXMPP_TLS_INACTIVE && + s->tls_state != REXMPP_TLS_AWAITING_DIRECT) { + rexmpp_tls_session_free(s->tls); } } void rexmpp_tls_deinit (rexmpp_t *s) { + if (s->tls != NULL) { + rexmpp_tls_ctx_free(s->tls); + s->tls = NULL; + } +} + #if defined(USE_GNUTLS) - gnutls_certificate_free_credentials(s->tls->gnutls_cred); - if (s->tls->tls_session_data != NULL) { - free(s->tls->tls_session_data); - s->tls->tls_session_data = NULL; +ssize_t +rexmpp_dtls_jingle_pull_func_gnutls (gnutls_transport_ptr_t p, + void *data, + size_t size) +{ + rexmpp_jingle_component_t *comp = p; + ssize_t received; + rexmpp_tls_err_t err = + rexmpp_jingle_dtls_pull_func(comp, data, size, &received); + if (err == REXMPP_TLS_SUCCESS) { + return received; + } else if (err == REXMPP_TLS_E_AGAIN) { + gnutls_transport_set_errno(comp->dtls->gnutls_session, EAGAIN); } -#ifdef ENABLE_CALLS - gnutls_certificate_free_credentials(s->tls->dtls_cred); + return -1; +} #endif -#elif defined(USE_OPENSSL) - if (s->tls->openssl_ctx != NULL) { - SSL_CTX_free(s->tls->openssl_ctx); + +rexmpp_tls_err_t +rexmpp_dtls_connect (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, + void *user_data, + int client) { +#if defined(USE_GNUTLS) + gnutls_session_t *tls_session = &(tls_ctx->gnutls_session); + gnutls_init(tls_session, + (client ? GNUTLS_CLIENT : GNUTLS_SERVER) | + GNUTLS_DATAGRAM | + GNUTLS_NONBLOCK); + if (! client) { + gnutls_certificate_server_set_request(*tls_session, GNUTLS_CERT_REQUIRE); } - s->tls->openssl_ctx = NULL; + gnutls_set_default_priority(*tls_session); + rexmpp_tls_set_x509_key_file(s, tls_ctx, NULL, NULL); + gnutls_credentials_set(*tls_session, GNUTLS_CRD_CERTIFICATE, + tls_ctx->gnutls_cred); + + gnutls_transport_set_ptr(*tls_session, user_data); + gnutls_transport_set_push_function(*tls_session, rexmpp_jingle_dtls_push_func); + gnutls_transport_set_pull_function(*tls_session, rexmpp_dtls_jingle_pull_func_gnutls); + gnutls_transport_set_pull_timeout_function(*tls_session, + rexmpp_jingle_dtls_pull_timeout_func); + /* todo: use the profile/crypto-suite from element */ + gnutls_srtp_set_profile(*tls_session, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80); + return REXMPP_TLS_SUCCESS; +#else + /* TODO: OpenSSL */ + (void)s; + (void)tls_ctx; + (void)user_data; + (void)client; + return REXMPP_TLS_E_OTHER; #endif - if (s->tls != NULL) { - free(s->tls); - s->tls = NULL; +} + +rexmpp_tls_err_t rexmpp_tls_handshake (rexmpp_t *s, rexmpp_tls_t *tls_ctx) { +#if defined(USE_GNUTLS) + int ret = gnutls_handshake(tls_ctx->gnutls_session); + if (ret == 0) { + return REXMPP_TLS_SUCCESS; + } else if (ret == GNUTLS_E_AGAIN) { + return REXMPP_TLS_E_AGAIN; + } else { + rexmpp_log(s, LOG_ERR, "Error during a TLS handshake: %s", + gnutls_strerror(ret)); + return REXMPP_TLS_E_OTHER; } +#else + (void)s; + (void)tls_ctx; + /* TODO: OpenSSL */ + /* SSL_do_handshake */ + return REXMPP_TLS_E_OTHER; +#endif } rexmpp_tls_err_t rexmpp_tls_connect (rexmpp_t *s) { + if (s->x509_key_file != NULL && s->x509_cert_file != NULL) { + rexmpp_tls_set_x509_key_file(s, s->tls, NULL, NULL); + } + #if defined(USE_GNUTLS) if (s->tls_state != REXMPP_TLS_HANDSHAKE) { gnutls_datum_t xmpp_client_protocol = @@ -312,12 +413,52 @@ rexmpp_tls_disconnect (rexmpp_t *s) { #endif } +int +rexmpp_tls_srtp_get_keys (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, + size_t key_len, + size_t salt_len, + unsigned char *client_key_wsalt, + unsigned char *server_key_wsalt) +{ +#if defined(USE_GNUTLS) + int key_mat_size; + char key_mat[4096]; + gnutls_datum_t client_key, client_salt, server_key, server_salt; + key_mat_size = + gnutls_srtp_get_keys(tls_ctx->gnutls_session, + key_mat, (key_len + salt_len) * 2, + &client_key, &client_salt, + &server_key, &server_salt); + rexmpp_log(s, LOG_DEBUG, "SRTP key material size: %d", + key_mat_size); + memcpy(client_key_wsalt, client_key.data, key_len); + memcpy(client_key_wsalt + key_len, client_salt.data, salt_len); + memcpy(server_key_wsalt, server_key.data, key_len); + memcpy(server_key_wsalt + key_len, server_salt.data, salt_len); + return 0; +#else + /* TODO: OpenSSL */ + (void)s; + (void)tls_ctx; + (void)key_len; + (void)salt_len; + (void)client_key_wsalt; + (void)server_key_wsalt; + return -1; +#endif +} + rexmpp_tls_err_t -rexmpp_tls_send (rexmpp_t *s, void *data, size_t data_size, ssize_t *written) +rexmpp_tls_send (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, + void *data, + size_t data_size, + ssize_t *written) { #if defined(USE_GNUTLS) *written = -1; - ssize_t ret = gnutls_record_send(s->tls->gnutls_session, + ssize_t ret = gnutls_record_send(tls_ctx->gnutls_session, data, data_size); if (ret >= 0) { @@ -331,7 +472,7 @@ rexmpp_tls_send (rexmpp_t *s, void *data, size_t data_size, ssize_t *written) } #elif defined(USE_OPENSSL) *written = -1; - int ret = SSL_write_ex(s->tls->openssl_conn, data, data_size, + int ret = SSL_write_ex(tls_ctx->openssl_conn, data, data_size, (size_t*)written); if (ret > 0) { return REXMPP_TLS_SUCCESS; @@ -348,10 +489,15 @@ rexmpp_tls_send (rexmpp_t *s, void *data, size_t data_size, ssize_t *written) } rexmpp_tls_err_t -rexmpp_tls_recv (rexmpp_t *s, void *data, size_t data_size, ssize_t *received) { +rexmpp_tls_recv (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, + void *data, + size_t data_size, + ssize_t *received) +{ #if defined(USE_GNUTLS) *received = -1; - ssize_t ret = gnutls_record_recv(s->tls->gnutls_session, data, data_size); + ssize_t ret = gnutls_record_recv(tls_ctx->gnutls_session, data, data_size); if (ret >= 0) { *received = ret; return REXMPP_TLS_SUCCESS; @@ -363,7 +509,7 @@ rexmpp_tls_recv (rexmpp_t *s, void *data, size_t data_size, ssize_t *received) { } #elif defined(USE_OPENSSL) *received = -1; - int ret = SSL_read_ex(s->tls->openssl_conn, data, data_size, + int ret = SSL_read_ex(tls_ctx->openssl_conn, data, data_size, (size_t*)received); if (ret > 0) { return REXMPP_TLS_SUCCESS; @@ -379,6 +525,16 @@ rexmpp_tls_recv (rexmpp_t *s, void *data, size_t data_size, ssize_t *received) { #endif } +unsigned int rexmpp_dtls_timeout (rexmpp_t *s, rexmpp_tls_t *tls_ctx) { + (void)s; +#if defined(USE_GNUTLS) + return gnutls_dtls_get_timeout(tls_ctx->gnutls_session); +#else + (void)tls_ctx; + return -1; +#endif +} + int rexmpp_tls_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) { #if defined(USE_GNUTLS) if (gnutls_record_get_direction(s->tls->gnutls_session) == 0) { @@ -407,20 +563,25 @@ int rexmpp_tls_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) { rexmpp_tls_err_t rexmpp_tls_set_x509_key_file (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, const char *cert_file, const char *key_file) { + if (cert_file == NULL) { + cert_file = s->x509_cert_file; + } + if (key_file == NULL) { + key_file = s->x509_key_file; + } + if (cert_file == NULL || key_file == NULL) { + rexmpp_log(s, LOG_ERR, "No certificate or key file defined"); + return REXMPP_TLS_E_OTHER; + } #if defined(USE_GNUTLS) - int ret = gnutls_certificate_set_x509_key_file(s->tls->gnutls_cred, + int ret = gnutls_certificate_set_x509_key_file(tls_ctx->gnutls_cred, cert_file, key_file, GNUTLS_X509_FMT_PEM); -#ifdef ENABLE_CALLS - gnutls_certificate_set_x509_key_file(s->tls->dtls_cred, - cert_file, - key_file, - GNUTLS_X509_FMT_PEM); -#endif if (ret == 0) { return REXMPP_TLS_SUCCESS; } else { @@ -429,13 +590,13 @@ rexmpp_tls_set_x509_key_file (rexmpp_t *s, return REXMPP_TLS_E_OTHER; } #elif defined(USE_OPENSSL) - if (SSL_CTX_use_certificate_file(s->tls->openssl_ctx, + if (SSL_CTX_use_certificate_file(tls_ctx->openssl_ctx, cert_file, SSL_FILETYPE_PEM) != 1) { rexmpp_log(s, LOG_ERR, "Failed to set a certificate file"); return REXMPP_TLS_E_OTHER; } - if (SSL_CTX_use_PrivateKey_file(s->tls->openssl_ctx, + if (SSL_CTX_use_PrivateKey_file(tls_ctx->openssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) { rexmpp_log(s, LOG_ERR, "Failed to set a key file"); @@ -452,22 +613,190 @@ rexmpp_tls_set_x509_key_file (rexmpp_t *s, rexmpp_tls_err_t rexmpp_tls_set_x509_trust_file (rexmpp_t *s, - const char *cert_file) + rexmpp_tls_t *tls_ctx, + const char *trust_file) { + if (trust_file == NULL) { + trust_file = s->x509_trust_file; + } + if (trust_file == NULL) { + rexmpp_log(s, LOG_ERR, "No trust file is defined"); + return REXMPP_TLS_E_OTHER; + } #if defined(USE_GNUTLS) - gnutls_certificate_set_x509_trust_file(s->tls->gnutls_cred, - cert_file, + gnutls_certificate_set_x509_trust_file(tls_ctx->gnutls_cred, + trust_file, GNUTLS_X509_FMT_PEM); return REXMPP_TLS_SUCCESS; #elif defined(USE_OPENSSL) - if (SSL_CTX_load_verify_locations(s->tls->openssl_ctx, cert_file, NULL) != 1) { + if (SSL_CTX_load_verify_locations(tls_ctx->openssl_ctx, trust_file, NULL) != 1) { rexmpp_log(s, LOG_ERR, "Failed to set a trusted certificate file"); return REXMPP_TLS_E_OTHER; } return REXMPP_TLS_SUCCESS; #else - (void)cert_file; + (void)trust_file; rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support"); return REXMPP_TLS_E_OTHER; #endif } + + +int rexmpp_tls_peer_fp (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, + const char *algo_str, + char *raw_fp, + char *fp_str, + size_t *fp_size) +{ +#if defined(USE_GNUTLS) + unsigned int cert_list_size = 0; + const gnutls_datum_t *cert_list; + cert_list = + gnutls_certificate_get_peers(tls_ctx->gnutls_session, &cert_list_size); + if (cert_list_size != 1) { + rexmpp_log(s, LOG_ERR, + "Unexpected peer certificate list size: %d", + cert_list_size); + return -1; + } + return rexmpp_x509_raw_cert_fp(s, algo_str, cert_list, + raw_fp, fp_str, fp_size); +#else + /* TODO: OpenSSL */ + (void)s; + (void)tls_ctx; + (void)algo_str; + (void)raw_fp; + (void)fp_str; + (void)fp_size; + return -1; +#endif +} + +int rexmpp_tls_session_fp (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, + const char *algo_str, + char *raw_fp, + char *fp_str, + size_t *fp_size) +{ +#if defined(USE_GNUTLS) + gnutls_x509_crt_t *cert_list; + unsigned int cert_list_size = 0; + int err = + gnutls_certificate_get_x509_crt(tls_ctx->gnutls_cred, + 0, &cert_list, &cert_list_size); + if (err) { + rexmpp_log(s, LOG_ERR, + "Failed to read own certificate list: %s", + gnutls_strerror(err)); + return -1; + } + + err = rexmpp_x509_cert_fp(s, algo_str, cert_list[0], raw_fp, fp_str, fp_size); + + size_t i; + for (i = 0; i < cert_list_size; i++) { + gnutls_x509_crt_deinit(cert_list[i]); + } + gnutls_free(cert_list); + return err; +#else + /* TODO: OpenSSL */ + (void)s; + (void)tls_ctx; + (void)algo_str; + (void)raw_fp; + (void)fp_str; + (void)fp_size; + return -1; +#endif +} + +int rexmpp_x509_cert_fp (rexmpp_t *s, + const char *algo_str, + void *cert, + char *raw_fp, + char *fp_str, + size_t *fp_size) +{ +#if defined(USE_GNUTLS) + gnutls_datum_t raw_cert; + int err = gnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_DER, &raw_cert); + if (err != GNUTLS_E_SUCCESS) { + rexmpp_log(s, LOG_ERR, "Failed to export a certificate: %s", + gnutls_strerror(err)); + return err; + } + err = rexmpp_x509_raw_cert_fp(s, algo_str, &raw_cert, raw_fp, fp_str, fp_size); + gnutls_free(raw_cert.data); + return err; +#else + /* TODO: OpenSSL */ + (void)s; + (void)algo_str; + (void)cert; + (void)raw_fp; + (void)fp_str; + (void)fp_size; + return -1; +#endif +} + +int rexmpp_x509_raw_cert_fp (rexmpp_t *s, + const char *algo_str, + const void *raw_cert, + char *raw_fp, + char *fp_str, + size_t *fp_size) +{ +#if defined(USE_GNUTLS) + const gnutls_datum_t *cert = (const gnutls_datum_t*)raw_cert; + gnutls_digest_algorithm_t algo = GNUTLS_DIG_UNKNOWN; + /* gnutls_digest_get_id uses different names, so + checking manually here. These are SDP options, + . */ + if (strcmp(algo_str, "sha-1") == 0) { + algo = GNUTLS_DIG_SHA1; + } else if (strcmp(algo_str, "sha-224") == 0) { + algo = GNUTLS_DIG_SHA224; + } else if (strcmp(algo_str, "sha-256") == 0) { + algo = GNUTLS_DIG_SHA256; + } else if (strcmp(algo_str, "sha-384") == 0) { + algo = GNUTLS_DIG_SHA384; + } else if (strcmp(algo_str, "sha-512") == 0) { + algo = GNUTLS_DIG_SHA512; + } else if (strcmp(algo_str, "md5") == 0) { + algo = GNUTLS_DIG_MD5; + } + if (algo == GNUTLS_DIG_UNKNOWN) { + rexmpp_log(s, LOG_ERR, "Unknown hash algorithm: %s", algo_str); + return -1; + } + + int err = gnutls_fingerprint(algo, cert, raw_fp, fp_size); + if (err != GNUTLS_E_SUCCESS) { + rexmpp_log(s, LOG_ERR, "Failed to calculate a fingerprint: %s", + gnutls_strerror(err)); + return -1; + } + if (fp_str != NULL) { + size_t i; + for (i = 0; i < (*fp_size); i++) { + snprintf(fp_str + i * 3, 4, "%02X:", raw_fp[i] & 0xFF); + } + fp_str[(*fp_size) * 3 - 1] = 0; + } + return 0; +#else + /* TODO: OpenSSL */ + (void)s; + (void)algo_str; + (void)raw_cert; + (void)raw_fp; + (void)fp_str; + (void)fp_size; + return -1; +#endif +} diff --git a/src/rexmpp_tls.h b/src/rexmpp_tls.h index 4406ca7..3c16049 100644 --- a/src/rexmpp_tls.h +++ b/src/rexmpp_tls.h @@ -20,6 +20,8 @@ to write logs and read other values (including server socket). #include "rexmpp.h" #include "config.h" +#define DTLS_SRTP_BUF_SIZE 0x4000 + typedef struct rexmpp_tls rexmpp_tls_t; /** @@ -43,7 +45,8 @@ struct rexmpp_tls { size_t tls_session_data_size; gnutls_session_t gnutls_session; gnutls_certificate_credentials_t gnutls_cred; - gnutls_certificate_credentials_t dtls_cred; + char dtls_buf[DTLS_SRTP_BUF_SIZE]; + size_t dtls_buf_len; }; #elif defined(USE_OPENSSL) #include @@ -67,26 +70,85 @@ int rexmpp_tls_init(rexmpp_t *s); void rexmpp_tls_cleanup(rexmpp_t *s); void rexmpp_tls_deinit(rexmpp_t *s); -rexmpp_tls_err_t rexmpp_tls_connect(rexmpp_t *s); -rexmpp_tls_err_t rexmpp_tls_disconnect(rexmpp_t *s); +rexmpp_tls_t *rexmpp_tls_ctx_new (rexmpp_t *s, int dtls); +void rexmpp_tls_ctx_free (rexmpp_tls_t *tls_ctx); + +void rexmpp_tls_session_free (rexmpp_tls_t *tls_ctx); + +rexmpp_tls_err_t rexmpp_tls_connect (rexmpp_t *s); +rexmpp_tls_err_t rexmpp_tls_handshake (rexmpp_t *s, rexmpp_tls_t *tls_ctx); +rexmpp_tls_err_t rexmpp_tls_disconnect (rexmpp_t *s); +rexmpp_tls_err_t +rexmpp_dtls_connect (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, + void *user_data, + int client); + +int +rexmpp_tls_srtp_get_keys (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, + size_t key_len, + size_t salt_len, + unsigned char *client_key_wsalt, + unsigned char *server_key_wsalt); -rexmpp_tls_err_t rexmpp_tls_send(rexmpp_t *s, void *data, size_t data_size, ssize_t *written); -rexmpp_tls_err_t rexmpp_tls_recv(rexmpp_t *s, void *data, size_t data_size, ssize_t *received); +rexmpp_tls_err_t +rexmpp_tls_send (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, + void *data, + size_t data_size, + ssize_t *written); +rexmpp_tls_err_t +rexmpp_tls_recv (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, + void *data, + size_t data_size, + ssize_t *received); +unsigned int rexmpp_dtls_timeout (rexmpp_t *s, rexmpp_tls_t *tls_ctx); int rexmpp_tls_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds); /** - @brief Sets credentials for both client authentication to the - server (SASL EXTERNAL) and DTLS connections in Jingle sessions. + @brief Sets credentials for a given TLS context: either provided + ones or defined for the whole ::rexmpp structure. */ rexmpp_tls_err_t rexmpp_tls_set_x509_key_file (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, const char *cert_file, const char *key_file); rexmpp_tls_err_t rexmpp_tls_set_x509_trust_file (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, const char *cert_file); +int rexmpp_tls_peer_fp (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, + const char *algo_str, + char *raw_fp, + char *fp_str, + size_t *fp_size); + +int rexmpp_tls_session_fp (rexmpp_t *s, + rexmpp_tls_t *tls_ctx, + const char *algo_str, + char *raw_fp, + char *fp_str, + size_t *fp_size); + +int rexmpp_x509_cert_fp (rexmpp_t *s, + const char *algo_str, + void *cert, + char *raw_fp, + char *fp_str, + size_t *fp_size); + +int rexmpp_x509_raw_cert_fp (rexmpp_t *s, + const char *algo_str, + const void *raw_cert, + char *raw_fp, + char *fp_str, + size_t *fp_size); #endif -- cgit v1.2.3