summaryrefslogtreecommitdiff
path: root/src/rexmpp.c
diff options
context:
space:
mode:
authordefanor <defanor@uberspace.net>2021-09-19 22:05:38 +0300
committerdefanor <defanor@uberspace.net>2021-09-19 22:05:38 +0300
commitc84f9e76d8e93c37b974c0fc64a6afdf432595cc (patch)
tree0829e751c2d19d7752a8c785ac8c021bba1852ea /src/rexmpp.c
parent26745d3a4e21ecf46492445b9c0251d80890bf62 (diff)
Introduce OpenSSL and no-TLS options, in addition to GnuTLS
Also an option to require TLS is added. There's no DANE TLSA checks with OpenSSL yet, TLS session resumptions and ALPN aren't used with it; just basic connections with certificate verification are added. And now SASL EXTERNAL authentication isn't quite usable.
Diffstat (limited to 'src/rexmpp.c')
-rw-r--r--src/rexmpp.c437
1 files changed, 154 insertions, 283 deletions
diff --git a/src/rexmpp.c b/src/rexmpp.c
index 70ed886..36644f4 100644
--- a/src/rexmpp.c
+++ b/src/rexmpp.c
@@ -17,10 +17,6 @@
#include <libxml/tree.h>
#include <libxml/xmlsave.h>
-#include <gnutls/gnutls.h>
-#include <gnutls/crypto.h>
-#include <gnutls/x509.h>
-#include <gnutls/dane.h>
#include <gsasl.h>
#include <unbound.h>
#include <gpgme.h>
@@ -393,6 +389,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
s->nick_notifications = 1;
s->retrieve_openpgp_keys = 1;
s->autojoin_bookmarked_mucs = 1;
+ s->require_tls = 1;
s->send_buffer = NULL;
s->send_queue = NULL;
s->resolver_ctx = NULL;
@@ -413,8 +410,6 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
s->stanza_queue = NULL;
s->stream_id = NULL;
s->active_iq = NULL;
- s->tls_session_data = NULL;
- s->tls_session_data_size = 0;
s->reconnect_number = 0;
s->next_reconnect_time.tv_sec = 0;
s->next_reconnect_time.tv_usec = 0;
@@ -478,17 +473,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
ub_strerror(err));
}
- err = gnutls_certificate_allocate_credentials(&(s->gnutls_cred));
- if (err) {
- rexmpp_log(s, LOG_CRIT, "gnutls credentials allocation error: %s",
- gnutls_strerror(err));
- xmlFreeParserCtxt(s->xml_parser);
- return REXMPP_E_TLS;
- }
- err = gnutls_certificate_set_x509_system_trust(s->gnutls_cred);
- if (err < 0) {
- rexmpp_log(s, LOG_CRIT, "Certificates loading error: %s",
- gnutls_strerror(err));
+ if (rexmpp_tls_init(s)) {
xmlFreeParserCtxt(s->xml_parser);
return REXMPP_E_TLS;
}
@@ -497,7 +482,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
if (err) {
rexmpp_log(s, LOG_CRIT, "gsasl initialisation error: %s",
gsasl_strerror(err));
- gnutls_certificate_free_credentials(s->gnutls_cred);
+ rexmpp_tls_deinit(s);
xmlFreeParserCtxt(s->xml_parser);
return REXMPP_E_SASL;
}
@@ -510,7 +495,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
rexmpp_log(s, LOG_CRIT, "gpgme initialisation error: %s",
gpgme_strerror(err));
gsasl_done(s->sasl_ctx);
- gnutls_certificate_free_credentials(s->gnutls_cred);
+ rexmpp_tls_deinit(s);
xmlFreeParserCtxt(s->xml_parser);
return REXMPP_E_PGP;
}
@@ -522,10 +507,7 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
structures), but keeps others (e.g., stanza queue and stream ID,
since we may resume the stream afterwards). */
void rexmpp_cleanup (rexmpp_t *s) {
- if (s->tls_state != REXMPP_TLS_INACTIVE &&
- s->tls_state != REXMPP_TLS_AWAITING_DIRECT) {
- gnutls_deinit(s->gnutls_session);
- }
+ rexmpp_tls_cleanup(s);
s->tls_state = REXMPP_TLS_INACTIVE;
if (s->sasl_state != REXMPP_SASL_INACTIVE) {
gsasl_finish(s->sasl_session);
@@ -585,7 +567,7 @@ void rexmpp_done (rexmpp_t *s) {
rexmpp_cleanup(s);
gpgme_release(s->pgp_ctx);
gsasl_done(s->sasl_ctx);
- gnutls_certificate_free_credentials(s->gnutls_cred);
+ rexmpp_tls_deinit(s);
if (s->resolver_ctx != NULL) {
ub_ctx_delete(s->resolver_ctx);
s->resolver_ctx = NULL;
@@ -625,9 +607,6 @@ void rexmpp_done (rexmpp_t *s) {
free(s->active_iq);
s->active_iq = next;
}
- if (s->tls_session_data != NULL) {
- free(s->tls_session_data);
- }
}
void rexmpp_schedule_reconnect (rexmpp_t *s) {
@@ -638,7 +617,7 @@ void rexmpp_schedule_reconnect (rexmpp_t *s) {
return;
}
if (s->reconnect_number == 0) {
- gnutls_rnd(GNUTLS_RND_NONCE, &s->reconnect_seconds, sizeof(time_t));
+ gsasl_nonce((char*)&s->reconnect_seconds, sizeof(time_t));
if (s->reconnect_seconds < 0) {
s->reconnect_seconds = - s->reconnect_seconds;
}
@@ -822,12 +801,14 @@ rexmpp_err_t rexmpp_send_continue (rexmpp_t *s)
rexmpp_log(s, LOG_ERR, "nothing to send");
return REXMPP_E_SEND_BUFFER_EMPTY;
}
- int ret;
+ ssize_t ret;
+ rexmpp_tls_err_t err;
while (1) {
if (s->tls_state == REXMPP_TLS_ACTIVE) {
- ret = gnutls_record_send (s->gnutls_session,
- s->send_buffer,
- s->send_buffer_len);
+ err = rexmpp_tls_send (s,
+ s->send_buffer,
+ s->send_buffer_len,
+ &ret);
} else {
ret = send (s->server_socket,
s->send_buffer + s->send_buffer_sent,
@@ -856,10 +837,9 @@ rexmpp_err_t rexmpp_send_continue (rexmpp_t *s)
}
} else {
if (s->tls_state == REXMPP_TLS_ACTIVE) {
- if (ret != GNUTLS_E_AGAIN) {
+ if (err != REXMPP_TLS_E_AGAIN) {
s->tls_state = REXMPP_TLS_ERROR;
/* Assume a TCP error for now as well. */
- rexmpp_log(s, LOG_ERR, "TLS send error: %s", gnutls_strerror(ret));
rexmpp_cleanup(s);
s->tcp_state = REXMPP_TCP_ERROR;
rexmpp_schedule_reconnect(s);
@@ -1049,12 +1029,13 @@ rexmpp_err_t rexmpp_recv (rexmpp_t *s) {
char chunk_raw[4096], *chunk;
ssize_t chunk_raw_len, chunk_len;
int sasl_err;
+ rexmpp_tls_err_t recv_err;
rexmpp_err_t err = REXMPP_SUCCESS;
/* Loop here in order to consume data from TLS buffers, which
wouldn't show up on select(). */
do {
if (s->tls_state == REXMPP_TLS_ACTIVE) {
- chunk_raw_len = gnutls_record_recv(s->gnutls_session, chunk_raw, 4096);
+ recv_err = rexmpp_tls_recv(s, chunk_raw, 4096, &chunk_raw_len);
} else {
chunk_raw_len = recv(s->server_socket, chunk_raw, 4096, 0);
}
@@ -1116,11 +1097,9 @@ rexmpp_err_t rexmpp_recv (rexmpp_t *s) {
}
} else {
if (s->tls_state == REXMPP_TLS_ACTIVE) {
- if (chunk_raw_len != GNUTLS_E_AGAIN) {
+ if (recv_err != REXMPP_TLS_E_AGAIN) {
s->tls_state = REXMPP_TLS_ERROR;
/* Assume a TCP error for now as well. */
- rexmpp_log(s, LOG_ERR, "TLS recv error: %s",
- gnutls_strerror(chunk_raw_len));
rexmpp_cleanup(s);
s->tcp_state = REXMPP_TCP_ERROR;
rexmpp_schedule_reconnect(s);
@@ -1226,97 +1205,18 @@ rexmpp_err_t rexmpp_try_next_host (rexmpp_t *s) {
return rexmpp_start_connecting(s);
}
-rexmpp_err_t rexmpp_tls_handshake (rexmpp_t *s) {
- s->tls_state = REXMPP_TLS_HANDSHAKE;
- int ret = gnutls_handshake(s->gnutls_session);
- if (ret == GNUTLS_E_AGAIN) {
- rexmpp_log(s, LOG_DEBUG, "Waiting for TLS handshake to complete");
- return REXMPP_E_AGAIN;
- } else if (ret == 0) {
- int status;
-
- int srv_is_secure = 0;
- if (s->stream_state == REXMPP_STREAM_NONE &&
- s->server_srv_tls != NULL) { /* Direct TLS */
- srv_is_secure = s->server_srv_tls->secure;
- } else if (s->stream_state != REXMPP_STREAM_NONE &&
- s->server_srv != NULL) { /* STARTTLS connection */
- srv_is_secure = s->server_srv->secure;
- }
-
- /* Check DANE TLSA records; experimental and purely informative
- now, but may be nice to (optionally) rely on it in the
- future. */
- if ((srv_is_secure || s->manual_host != NULL) &&
- s->server_socket_dns_secure) {
- /* Apparently GnuTLS only checks against the target
- server/derived host, while another possibility is a
- service/source host
- (<https://tools.ietf.org/html/rfc7712#section-5.1>,
- <https://tools.ietf.org/html/rfc7673#section-6>). */
- ret = dane_verify_session_crt(NULL, s->gnutls_session, s->server_host,
- "tcp", s->server_port, 0, 0, &status);
- if (ret) {
- rexmpp_log(s, LOG_WARNING, "DANE verification error: %s",
- dane_strerror(ret));
- } else if (status) {
- if (status & DANE_VERIFY_CA_CONSTRAINTS_VIOLATED) {
- rexmpp_log(s, LOG_WARNING, "The CA constraints were violated");
- }
- if (status & DANE_VERIFY_CERT_DIFFERS) {
- rexmpp_log(s, LOG_WARNING, "The certificate obtained via DNS differs");
- }
- if (status & DANE_VERIFY_UNKNOWN_DANE_INFO) {
- rexmpp_log(s, LOG_WARNING,
- "No known DANE data was found in the DNS record");
- }
- } else {
- rexmpp_log(s, LOG_INFO,
- "DANE verification did not reject the certificate");
- }
- }
-
- ret = gnutls_certificate_verify_peers3(s->gnutls_session,
- s->initial_jid.domain,
- &status);
- if (ret || status) {
- s->tls_state = REXMPP_TLS_ERROR;
- if (ret) {
- rexmpp_log(s, LOG_ERR, "Certificate parsing error: %s",
- gnutls_strerror(ret));
- } else if (status & GNUTLS_CERT_UNEXPECTED_OWNER) {
- rexmpp_log(s, LOG_ERR, "Unexpected certificate owner");
- } else {
- rexmpp_log(s, LOG_ERR, "Untrusted certificate");
- }
- gnutls_bye(s->gnutls_session, GNUTLS_SHUT_RDWR);
- rexmpp_cleanup(s);
- rexmpp_schedule_reconnect(s);
- return REXMPP_E_TLS;
- }
+rexmpp_err_t
+rexmpp_process_tls_conn_err (rexmpp_t *s,
+ rexmpp_tls_err_t err)
+{
+ if (err == REXMPP_TLS_E_OTHER) {
+ s->tls_state = REXMPP_TLS_ERROR;
+ rexmpp_cleanup(s);
+ rexmpp_schedule_reconnect(s);
+ return REXMPP_E_TLS;
+ } else if (err == REXMPP_TLS_SUCCESS) {
+ rexmpp_log(s, LOG_DEBUG, "A TLS connection is established");
s->tls_state = REXMPP_TLS_ACTIVE;
- rexmpp_log(s, LOG_DEBUG, "TLS ready");
-
- if (gnutls_session_is_resumed(s->gnutls_session)) {
- rexmpp_log(s, LOG_INFO, "TLS session is resumed");
- } else {
- if (s->tls_session_data != NULL) {
- rexmpp_log(s, LOG_DEBUG, "TLS session is not resumed");
- free(s->tls_session_data);
- s->tls_session_data = NULL;
- }
- gnutls_session_get_data(s->gnutls_session, NULL,
- &s->tls_session_data_size);
- s->tls_session_data = malloc(s->tls_session_data_size);
- ret = gnutls_session_get_data(s->gnutls_session, s->tls_session_data,
- &s->tls_session_data_size);
- if (ret != GNUTLS_E_SUCCESS) {
- rexmpp_log(s, LOG_ERR, "Failed to get TLS session data: %s",
- gnutls_strerror(ret));
- return REXMPP_E_TLS;
- }
- }
-
if (s->stream_state == REXMPP_STREAM_NONE) {
/* It's a direct TLS connection, so open a stream after
connecting. */
@@ -1327,45 +1227,11 @@ rexmpp_err_t rexmpp_tls_handshake (rexmpp_t *s) {
return rexmpp_stream_open(s);
}
} else {
- rexmpp_log(s, LOG_ERR, "Unexpected TLS handshake error: %s",
- gnutls_strerror(ret));
- rexmpp_cleanup(s);
- rexmpp_schedule_reconnect(s);
- return REXMPP_E_TLS;
+ s->tls_state = REXMPP_TLS_HANDSHAKE;
+ return REXMPP_E_AGAIN;
}
}
-rexmpp_err_t rexmpp_tls_start (rexmpp_t *s) {
- gnutls_datum_t xmpp_client_protocol = {"xmpp-client", strlen("xmpp-client")};
- rexmpp_log(s, LOG_DEBUG, "starting TLS");
- gnutls_init(&s->gnutls_session, GNUTLS_CLIENT);
- gnutls_session_set_ptr(s->gnutls_session, s);
- gnutls_alpn_set_protocols(s->gnutls_session, &xmpp_client_protocol, 1, 0);
- gnutls_server_name_set(s->gnutls_session, GNUTLS_NAME_DNS,
- s->initial_jid.domain,
- strlen(s->initial_jid.domain));
- gnutls_set_default_priority(s->gnutls_session);
- gnutls_credentials_set(s->gnutls_session, GNUTLS_CRD_CERTIFICATE,
- s->gnutls_cred);
- gnutls_transport_set_int(s->gnutls_session, s->server_socket);
- gnutls_handshake_set_timeout(s->gnutls_session,
- GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
- if (s->tls_session_data != NULL) {
- int ret = gnutls_session_set_data(s->gnutls_session,
- s->tls_session_data,
- s->tls_session_data_size);
- if (ret != GNUTLS_E_SUCCESS) {
- rexmpp_log(s, LOG_WARNING, "Failed to set TLS session data: %s",
- gnutls_strerror(ret));
- free(s->tls_session_data);
- s->tls_session_data = NULL;
- s->tls_session_data_size = 0;
- }
- }
- s->tls_state = REXMPP_TLS_HANDSHAKE;
- return rexmpp_tls_handshake(s);
-}
-
rexmpp_err_t rexmpp_connected_to_server (rexmpp_t *s) {
s->tcp_state = REXMPP_TCP_CONNECTED;
rexmpp_log(s, LOG_INFO,
@@ -1374,7 +1240,7 @@ rexmpp_err_t rexmpp_connected_to_server (rexmpp_t *s) {
s->reconnect_number = 0;
xmlCtxtResetPush(s->xml_parser, "", 0, "", "utf-8");
if (s->tls_state == REXMPP_TLS_AWAITING_DIRECT) {
- return rexmpp_tls_start(s);
+ return rexmpp_process_tls_conn_err(s, rexmpp_tls_connect(s));
} else {
return rexmpp_stream_open(s);
}
@@ -1665,6 +1531,120 @@ rexmpp_err_t rexmpp_stream_bind (rexmpp_t *s) {
rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
rexmpp_console_on_recv(s, elem);
+ /* Stream negotiation,
+ https://tools.ietf.org/html/rfc6120#section-4.3 */
+ if (s->stream_state == REXMPP_STREAM_NEGOTIATION) {
+ if (rexmpp_xml_match(elem, "http://etherx.jabber.org/streams", "features")) {
+
+ /* Remember features. */
+ if (s->stream_features != NULL) {
+ xmlFreeNode(s->stream_features);
+ }
+ s->stream_features = xmlCopyNode(elem, 1);
+
+ /* 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 =
+ 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) {
+ /* TLS is required, not established, and there's no such
+ feature available; fail here. */
+ rexmpp_log(s, LOG_ERR,
+ "TLS is required, but the server doesn't advertise such a feature");
+ return REXMPP_E_TLS;
+ }
+
+ /* Nothing to negotiate. */
+ if (xmlFirstElementChild(elem) == NULL) {
+ rexmpp_stream_is_ready(s);
+ return REXMPP_SUCCESS;
+ }
+
+ child = rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-sasl",
+ "mechanisms");
+ if (child != 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);
+ mechanism != NULL;
+ mechanism = xmlNextElementSibling(mechanism)) {
+ if (rexmpp_xml_match(mechanism, "urn:ietf:params:xml:ns:xmpp-sasl",
+ "mechanism")) {
+ char *mech_str = xmlNodeGetContent(mechanism);
+ snprintf(mech_list + strlen(mech_list),
+ 2048 - strlen(mech_list),
+ "%s ",
+ mech_str);
+ free(mech_str);
+ }
+ }
+ const char *mech =
+ gsasl_client_suggest_mechanism(s->sasl_ctx, mech_list);
+ rexmpp_log(s, LOG_INFO, "Selected SASL mechanism: %s", mech);
+ int sasl_err;
+ char *sasl_buf;
+ sasl_err = gsasl_client_start(s->sasl_ctx, mech, &(s->sasl_session));
+ if (sasl_err != GSASL_OK) {
+ rexmpp_log(s, LOG_CRIT, "Failed to initialise SASL session: %s",
+ gsasl_strerror(sasl_err));
+ s->sasl_state = REXMPP_SASL_ERROR;
+ return REXMPP_E_SASL;
+ }
+ sasl_err = gsasl_step64 (s->sasl_session, "", (char**)&sasl_buf);
+ if (sasl_err != GSASL_OK) {
+ if (sasl_err == GSASL_NEEDS_MORE) {
+ rexmpp_log(s, LOG_DEBUG, "SASL needs more data");
+ } else {
+ rexmpp_log(s, LOG_ERR, "SASL error: %s",
+ gsasl_strerror(sasl_err));
+ s->sasl_state = REXMPP_SASL_ERROR;
+ return REXMPP_E_SASL;
+ }
+ }
+ xmlNodePtr auth_cmd = xmlNewNode(NULL, "auth");
+ xmlNewProp(auth_cmd, "mechanism", mech);
+ xmlNewNs(auth_cmd, "urn:ietf:params:xml:ns:xmpp-sasl", NULL);
+ xmlNodeAddContent(auth_cmd, sasl_buf);
+ free(sasl_buf);
+ rexmpp_send(s, auth_cmd);
+ return REXMPP_SUCCESS;
+ }
+
+ child = rexmpp_xml_find_child(elem, "urn:xmpp:sm:3", "sm");
+ if (s->stream_id != NULL && child != NULL) {
+ s->stream_state = REXMPP_STREAM_SM_RESUME;
+ char buf[11];
+ snprintf(buf, 11, "%u", s->stanzas_in_count);
+ xmlNodePtr sm_resume = xmlNewNode(NULL, "resume");
+ xmlNewNs(sm_resume, "urn:xmpp:sm:3", NULL);
+ xmlNewProp(sm_resume, "previd", s->stream_id);
+ xmlNewProp(sm_resume, "h", buf);
+ rexmpp_send(s, sm_resume);
+ return REXMPP_SUCCESS;
+ }
+
+ child =
+ rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-bind", "bind");
+ if (child != NULL) {
+ return rexmpp_stream_bind(s);
+ }
+ } else {
+ rexmpp_log(s, LOG_ERR, "Expected stream features, received %s", elem->name);
+ return REXMPP_E_STREAM;
+ }
+ }
+
/* IQs. These are the ones that should be processed by the library;
if a user-facing application wants to handle them on its own, it
should cancel further processing by the library (so we can send
@@ -1954,110 +1934,6 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
}
}
- /* Stream negotiation,
- https://tools.ietf.org/html/rfc6120#section-4.3 */
- if (s->stream_state == REXMPP_STREAM_NEGOTIATION &&
- rexmpp_xml_match(elem, "http://etherx.jabber.org/streams", "features")) {
-
- /* Remember features. */
- if (s->stream_features != NULL) {
- xmlFreeNode(s->stream_features);
- }
- s->stream_features = xmlCopyNode(elem, 1);
-
- /* Nothing to negotiate. */
- if (xmlFirstElementChild(elem) == NULL) {
- rexmpp_stream_is_ready(s);
- return REXMPP_SUCCESS;
- }
-
- /* 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 =
- 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;
- }
-
- child = rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-sasl",
- "mechanisms");
- if (child != 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);
- mechanism != NULL;
- mechanism = xmlNextElementSibling(mechanism)) {
- if (rexmpp_xml_match(mechanism, "urn:ietf:params:xml:ns:xmpp-sasl",
- "mechanism")) {
- char *mech_str = xmlNodeGetContent(mechanism);
- snprintf(mech_list + strlen(mech_list),
- 2048 - strlen(mech_list),
- "%s ",
- mech_str);
- free(mech_str);
- }
- }
- const char *mech =
- gsasl_client_suggest_mechanism(s->sasl_ctx, mech_list);
- rexmpp_log(s, LOG_INFO, "Selected SASL mechanism: %s", mech);
- int sasl_err;
- char *sasl_buf;
- sasl_err = gsasl_client_start(s->sasl_ctx, mech, &(s->sasl_session));
- if (sasl_err != GSASL_OK) {
- rexmpp_log(s, LOG_CRIT, "Failed to initialise SASL session: %s",
- gsasl_strerror(sasl_err));
- s->sasl_state = REXMPP_SASL_ERROR;
- return REXMPP_E_SASL;
- }
- sasl_err = gsasl_step64 (s->sasl_session, "", (char**)&sasl_buf);
- if (sasl_err != GSASL_OK) {
- if (sasl_err == GSASL_NEEDS_MORE) {
- rexmpp_log(s, LOG_DEBUG, "SASL needs more data");
- } else {
- rexmpp_log(s, LOG_ERR, "SASL error: %s",
- gsasl_strerror(sasl_err));
- s->sasl_state = REXMPP_SASL_ERROR;
- return REXMPP_E_SASL;
- }
- }
- xmlNodePtr auth_cmd = xmlNewNode(NULL, "auth");
- xmlNewProp(auth_cmd, "mechanism", mech);
- xmlNewNs(auth_cmd, "urn:ietf:params:xml:ns:xmpp-sasl", NULL);
- xmlNodeAddContent(auth_cmd, sasl_buf);
- free(sasl_buf);
- rexmpp_send(s, auth_cmd);
- return REXMPP_SUCCESS;
- }
-
- child = rexmpp_xml_find_child(elem, "urn:xmpp:sm:3", "sm");
- if (s->stream_id != NULL && child != NULL) {
- s->stream_state = REXMPP_STREAM_SM_RESUME;
- char buf[11];
- snprintf(buf, 11, "%u", s->stanzas_in_count);
- xmlNodePtr sm_resume = xmlNewNode(NULL, "resume");
- xmlNewNs(sm_resume, "urn:xmpp:sm:3", NULL);
- xmlNewProp(sm_resume, "previd", s->stream_id);
- xmlNewProp(sm_resume, "h", buf);
- rexmpp_send(s, sm_resume);
- return REXMPP_SUCCESS;
- }
-
- child =
- rexmpp_xml_find_child(elem, "urn:ietf:params:xml:ns:xmpp-bind", "bind");
- if (child != NULL) {
- return rexmpp_stream_bind(s);
- }
- }
-
/* Stream errors, https://tools.ietf.org/html/rfc6120#section-4.9 */
if (rexmpp_xml_match(elem, "http://etherx.jabber.org/streams",
"error")) {
@@ -2080,7 +1956,7 @@ rexmpp_err_t rexmpp_process_element (rexmpp_t *s, xmlNodePtr elem) {
if (s->stream_state == REXMPP_STREAM_STARTTLS) {
if (rexmpp_xml_match(elem, "urn:ietf:params:xml:ns:xmpp-tls",
"proceed")) {
- return rexmpp_tls_start(s);
+ return rexmpp_process_tls_conn_err(s, rexmpp_tls_connect(s));
} else if (rexmpp_xml_match(elem, "urn:ietf:params:xml:ns:xmpp-tls",
"failure")) {
rexmpp_log(s, LOG_ERR, "STARTTLS failure");
@@ -2505,7 +2381,7 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
this, if everything goes well. */
if (s->tcp_state == REXMPP_TCP_CONNECTED &&
s->tls_state == REXMPP_TLS_HANDSHAKE) {
- rexmpp_err_t err = rexmpp_tls_handshake(s);
+ rexmpp_err_t err = rexmpp_process_tls_conn_err(s, rexmpp_tls_connect(s));
if (err > REXMPP_E_AGAIN) {
return err;
}
@@ -2527,14 +2403,13 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
if (s->tcp_state == REXMPP_TCP_CONNECTED &&
s->stream_state == REXMPP_STREAM_CLOSED &&
s->tls_state == REXMPP_TLS_CLOSING) {
- int ret = gnutls_bye(s->gnutls_session, GNUTLS_SHUT_RDWR);
- if (ret == GNUTLS_E_SUCCESS) {
+ rexmpp_tls_err_t err = rexmpp_tls_disconnect(s);
+ if (err == REXMPP_TLS_SUCCESS) {
s->tls_state = REXMPP_TLS_INACTIVE;
rexmpp_cleanup(s);
s->tcp_state = REXMPP_TCP_CLOSED;
} else {
- rexmpp_log(s, LOG_WARNING, "Failed to close TLS connection: %s",
- gnutls_strerror(ret));
+ s->tls_state = REXMPP_TLS_ERROR;
return REXMPP_E_TLS;
}
}
@@ -2550,7 +2425,7 @@ rexmpp_err_t rexmpp_run (rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
}
int rexmpp_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
- int conn_fd, max_fd = 0;
+ int conn_fd, tls_fd, max_fd = 0;
if (s->resolver_state != REXMPP_RESOLVER_NONE &&
s->resolver_state != REXMPP_RESOLVER_READY) {
@@ -2579,13 +2454,9 @@ int rexmpp_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
}
if (s->tls_state == REXMPP_TLS_HANDSHAKE) {
- if (gnutls_record_get_direction(s->gnutls_session) == 0) {
- FD_SET(s->server_socket, read_fds);
- } else {
- FD_SET(s->server_socket, write_fds);
- }
- if (s->server_socket + 1 > max_fd) {
- max_fd = s->server_socket + 1;
+ tls_fd = rexmpp_tls_fds(s, read_fds, write_fds);
+ if (tls_fd > max_fd) {
+ max_fd = tls_fd;
}
}