summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordefanor <defanor@uberspace.net>2023-09-23 11:35:39 +0300
committerdefanor <defanor@uberspace.net>2023-09-24 13:14:42 +0300
commit7a63d327772dc64978e9159d49885a9ae7dd9b4e (patch)
treee3201f3fd56236a223ffc0f8be70f1b9950d6ad0
parent4ea2ee3870d95b6d95c8e9c7c08277bc9d34d5e0 (diff)
Move GnuTLS operations from Jingle module into TLS module
-rw-r--r--examples/basic.c3
-rw-r--r--src/rexmpp.c6
-rw-r--r--src/rexmpp.h6
-rw-r--r--src/rexmpp.rs5
-rw-r--r--src/rexmpp_jingle.c482
-rw-r--r--src/rexmpp_jingle.h10
-rw-r--r--src/rexmpp_tls.c479
-rw-r--r--src/rexmpp_tls.h76
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 <gio/gnetworking.h>
#include <nice.h>
#include <agent.h>
-#include <gnutls/dtls.h>
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
#include <srtp2/srtp.h>
#include <math.h>
#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 <crypto/> 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,
- <https://datatracker.ietf.org/doc/html/rfc4572#page-8>. */
- 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 <opus/opus.h>
#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 <gnutls/crypto.h>
#include <gnutls/x509.h>
#include <gnutls/dane.h>
+#include <gnutls/dtls.h>
#elif defined(USE_OPENSSL)
#include <openssl/ssl.h>
#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 <crypto/> 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,
+ <https://datatracker.ietf.org/doc/html/rfc4572#page-8>. */
+ 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 <openssl/ssl.h>
@@ -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