summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordefanor <defanor@uberspace.net>2021-09-20 11:41:56 +0300
committerdefanor <defanor@uberspace.net>2021-09-20 11:41:56 +0300
commitb5694f7a20fc85d03016dc028d7e566dd707e50c (patch)
treea33d24b009d456c603b4f24c55422970f25aee59
parent917211b67cc27f07b3743611ee9389ff966ffba5 (diff)
Restore client certificate (SASL EXTERNAL) authentication
As well as the ability to set a trusted server certificate.
-rw-r--r--examples/basic.c14
-rw-r--r--src/rexmpp.c44
-rw-r--r--src/rexmpp.h9
-rw-r--r--src/rexmpp_tls.c61
-rw-r--r--src/rexmpp_tls.h10
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 <stdio.h>
#include <errno.h>
#include <syslog.h>
-#include <gnutls/gnutls.h>
#include <gsasl.h>
#include <rexmpp.h>
@@ -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