From b5694f7a20fc85d03016dc028d7e566dd707e50c Mon Sep 17 00:00:00 2001 From: defanor Date: Mon, 20 Sep 2021 11:41:56 +0300 Subject: Restore client certificate (SASL EXTERNAL) authentication As well as the ability to set a trusted server certificate. --- examples/basic.c | 14 ++++--------- src/rexmpp.c | 44 +++++++++++++++++++++++----------------- src/rexmpp.h | 9 ++++++++- src/rexmpp_tls.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/rexmpp_tls.h | 10 ++++++++++ 5 files changed, 109 insertions(+), 29 deletions(-) diff --git a/examples/basic.c b/examples/basic.c index e122ee8..fa585c9 100644 --- a/examples/basic.c +++ b/examples/basic.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -137,21 +136,16 @@ int main (int argc, char **argv) { /* Could set a client certificate for SASL EXTERNAL authentication here. */ - /* gnutls_certificate_set_x509_key_file(s.gnutls_cred, */ - /* "cert.pem", */ - /* "key.pem", */ - /* GNUTLS_X509_FMT_PEM); */ + /* rexmpp_tls_set_x509_key_file(&s, "client.crt", "client.key"); */ /* Could also set various other things manually. */ /* s.socks_host = "127.0.0.1"; */ /* s.socks_port = 4321; */ - /* s.manual_host = "foo.custom"; */ - /* gnutls_certificate_set_x509_trust_file(s.gnutls_cred, */ - /* "foo.custom.crt", */ - /* GNUTLS_X509_FMT_PEM); */ + /* s.manual_host = "localhost"; */ + /* rexmpp_tls_set_x509_trust_file(&s, "localhost.crt"); */ /* rexmpp_openpgp_set_home_dir(&s, "pgp"); */ s.roster_cache_file = "roster.xml"; - + /* s.tls_policy = REXMPP_TLS_AVOID; */ /* Once the main structure is initialised and everything is sufficiently configured, we are ready to run the main loop and diff --git a/src/rexmpp.c b/src/rexmpp.c index 5c41625..1e18908 100644 --- a/src/rexmpp.c +++ b/src/rexmpp.c @@ -397,7 +397,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s, s->retrieve_openpgp_keys = 0; #endif s->autojoin_bookmarked_mucs = 1; - s->require_tls = 1; + s->tls_policy = REXMPP_TLS_REQUIRE; s->send_buffer = NULL; s->send_queue = NULL; s->resolver_ctx = NULL; @@ -1557,16 +1557,29 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) { /* TODO: check for required features properly here. Currently assuming that STARTTLS, SASL, and BIND (with an exception for SM) are always required if they are present. */ - xmlNodePtr child = + xmlNodePtr starttls = rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-tls", "starttls"); - if (child != NULL) { - s->stream_state = REXMPP_STREAM_STARTTLS; - xmlNodePtr starttls_cmd = xmlNewNode(NULL, "starttls"); - xmlNewNs(starttls_cmd, "urn:ietf:params:xml:ns:xmpp-tls", NULL); - rexmpp_send(s, starttls_cmd); - return REXMPP_SUCCESS; - } else if (s->require_tls && s->tls_state != REXMPP_TLS_ACTIVE) { + xmlNodePtr sasl = + rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-sasl", + "mechanisms"); + xmlNodePtr bind = + rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-bind", "bind"); + xmlNodePtr sm = rexmpp_xml_find_child(elem, "urn:xmpp:sm:3", "sm"); + + if (starttls != NULL) { + /* Go for TLS, unless we're both trying to avoid it, and have + other options. */ + if (! (s->tls_policy == REXMPP_TLS_AVOID && + (sasl != NULL || bind != NULL || sm != NULL))) { + s->stream_state = REXMPP_STREAM_STARTTLS; + xmlNodePtr starttls_cmd = xmlNewNode(NULL, "starttls"); + xmlNewNs(starttls_cmd, "urn:ietf:params:xml:ns:xmpp-tls", NULL); + rexmpp_send(s, starttls_cmd); + return REXMPP_SUCCESS; + } + } else if (s->tls_policy == REXMPP_TLS_REQUIRE && + s->tls_state != REXMPP_TLS_ACTIVE) { /* TLS is required, not established, and there's no such feature available; fail here. */ rexmpp_log(s, LOG_ERR, @@ -1580,15 +1593,13 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) { return REXMPP_SUCCESS; } - child = rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-sasl", - "mechanisms"); - if (child != NULL) { + if (sasl != NULL) { s->stream_state = REXMPP_STREAM_SASL; s->sasl_state = REXMPP_SASL_NEGOTIATION; char mech_list[2048]; /* todo: perhaps grow it dynamically */ mech_list[0] = '\0'; xmlNodePtr mechanism; - for (mechanism = xmlFirstElementChild(child); + for (mechanism = xmlFirstElementChild(sasl); mechanism != NULL; mechanism = xmlNextElementSibling(mechanism)) { if (rexmpp_xml_match(mechanism, "urn:ietf:params:xml:ns:xmpp-sasl", @@ -1633,8 +1644,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) { return REXMPP_SUCCESS; } - child = rexmpp_xml_find_child(elem, "urn:xmpp:sm:3", "sm"); - if (s->stream_id != NULL && child != NULL) { + if (s->stream_id != NULL && sm != NULL) { s->stream_state = REXMPP_STREAM_SM_RESUME; char buf[11]; snprintf(buf, 11, "%u", s->stanzas_in_count); @@ -1646,9 +1656,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) { return REXMPP_SUCCESS; } - child = - rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-bind", "bind"); - if (child != NULL) { + if (bind != NULL) { return rexmpp_stream_bind(s); } } else { diff --git a/src/rexmpp.h b/src/rexmpp.h index bfcde96..a7b376a 100644 --- a/src/rexmpp.h +++ b/src/rexmpp.h @@ -213,6 +213,13 @@ enum rexmpp_err { }; typedef enum rexmpp_err rexmpp_err_t; +/** @brief TLS policy */ +enum tls_pol { + REXMPP_TLS_REQUIRE, + REXMPP_TLS_PREFER, + REXMPP_TLS_AVOID +}; + typedef void (*log_function_t) (rexmpp_t *s, int priority, const char *format, va_list args); typedef int (*sasl_property_cb_t) (rexmpp_t *s, Gsasl_property prop); typedef int (*xml_in_cb_t) (rexmpp_t *s, xmlNodePtr node); @@ -259,7 +266,7 @@ struct rexmpp int nick_notifications; /* XEP-0172 */ int retrieve_openpgp_keys; /* XEP-0373 */ int autojoin_bookmarked_mucs; /* XEP-0402 */ - int require_tls; + enum tls_pol tls_policy; /* Resource limits. */ uint32_t stanza_queue_size; diff --git a/src/rexmpp_tls.c b/src/rexmpp_tls.c index bd464ce..4881647 100644 --- a/src/rexmpp_tls.c +++ b/src/rexmpp_tls.c @@ -385,3 +385,64 @@ int rexmpp_tls_fds (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) { return 0; #endif } + +rexmpp_tls_err_t +rexmpp_tls_set_x509_key_file (rexmpp_t *s, + const char *cert_file, + const char *key_file) +{ +#if defined(USE_GNUTLS) + int ret = gnutls_certificate_set_x509_key_file(s->tls.gnutls_cred, + cert_file, + key_file, + GNUTLS_X509_FMT_PEM); + if (ret == 0) { + return REXMPP_TLS_SUCCESS; + } else { + rexmpp_log(s, LOG_ERR, + "Failed to set a key file: %s", gnutls_strerror(ret)); + return REXMPP_TLS_E_OTHER; + } +#elif defined(USE_OPENSSL) + if (SSL_CTX_use_certificate_file(s->tls.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, + key_file, + SSL_FILETYPE_PEM) != 1) { + rexmpp_log(s, LOG_ERR, "Failed to set a key file"); + return REXMPP_TLS_E_OTHER; + } + return REXMPP_TLS_SUCCESS; +#else + (void)cert_file; + (void)key_file; + rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support"); + return REXMPP_TLS_E_OTHER; +#endif +} + +rexmpp_tls_err_t +rexmpp_tls_set_x509_trust_file (rexmpp_t *s, + const char *cert_file) +{ +#if defined(USE_GNUTLS) + gnutls_certificate_set_x509_trust_file(s->tls.gnutls_cred, + cert_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) { + 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; + rexmpp_log(s, LOG_ERR, "rexmpp is compiled without TLS support"); + return REXMPP_TLS_E_OTHER; +#endif +} diff --git a/src/rexmpp_tls.h b/src/rexmpp_tls.h index 22515e3..2d54d1e 100644 --- a/src/rexmpp_tls.h +++ b/src/rexmpp_tls.h @@ -71,4 +71,14 @@ rexmpp_tls_err_t rexmpp_tls_recv(rexmpp_t *s, void *data, size_t data_size, ssiz 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, + const char *cert_file, + const char *key_file); + +rexmpp_tls_err_t +rexmpp_tls_set_x509_trust_file (rexmpp_t *s, + const char *cert_file); + + #endif -- cgit v1.2.3