8 Commits

Author SHA1 Message Date
Steffen Jaeckel
4d59a9fe45 Release libstrophe-0.13.1
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-21 13:47:07 +01:00
Steffen Jaeckel
40f2452fb0 Fix SCRAM-*-PLUS SASL mechanisms with OpenSSL and TLS < v1.3
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-21 13:19:47 +01:00
Steffen Jaeckel
1cf09b1870 Only signal "stream negotiation success" once.
The code-path which checks whether the server requires an "xmpp-session"
was untested and therefore the connection-callback handler was triggered
twice with the state `XMPP_CONN_CONNECT`. This happened if the server
supports stream-management and requires a session.

Reported via https://github.com/profanity-im/profanity/issues/1954

Fixup of c7d410f38b

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-14 11:40:59 +01:00
Steffen Jaeckel
5edc480932 Fix sock_connect()
`rc` must be reset in order to make the `do-while` loop work in case
there's no `sockopt_cb` set.

Fixup of f3460460e9

Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-14 11:40:38 +01:00
Steffen Jaeckel
641211ecae Minor improvements
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-12 17:47:24 +01:00
Steffen Jaeckel
d8fd6e6d14 Improve documentation
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-12 17:46:52 +01:00
Omar Polo
9410530507 Replace usage of EBADFD (not in POSIX) 2024-02-05 11:09:25 +01:00
Steffen Jaeckel
d6d71e97f8 Release libstrophe-0.13.0
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
2024-02-01 09:34:32 +01:00
13 changed files with 159 additions and 122 deletions

View File

@@ -1,3 +1,19 @@
0.13.1
- Fix SCRAM-*-PLUS SASL mechanisms with OpenSSL and TLS < v1.3 (40f2452)
- Only signal "stream negotiation success" once (1cf09b1)
- Fix `sock_connect()` not looping over all DNS records returned if no `sockopt_cb` is set (5edc480)
- Replace usage of EBADFD, it's not in POSIX (#235)
0.13.0
- Fix connected/connecting signaling to user (#227)
- Fix wording of licensing terms (#225)
- Prepare for future changes in OpenSSL (#226)
- Improve Stream Management (#227) (#230)
- Add SCRAM-PLUS Variants (#228)
- Introduce XEP-0138 stream compression (#231)
- Deprecated the following API (#227):
- xmpp_conn_disable_tls() - replaced by a flag set by xmpp_conn_set_flags()
0.12.3
- Improve TCP-connection establishment (#221)
- Handle case where the server doesn't provide the `bind` feature (#224)
@@ -21,7 +37,7 @@
- Fix some build steps when builddir != srcdir (#208)
- Allow the user to disable build of examples (#209)
- CI builds against OpenSSL 3 (#206)
- Change the call signature of the following API:
- Change the call signature of the following API (#208):
- xmpp_conn_set_client_cert() - the PKCS#12 file has now to be passed via the `cert`
parameter. Originally it was via `key`. Currently both styles are supported,
but in a future release only passing via `cert` will be accepted.

View File

@@ -38,7 +38,7 @@ PROJECT_NAME = Strophe
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 0.12
PROJECT_NUMBER = 0.13
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

View File

@@ -88,7 +88,7 @@ or if you have everything configured properly:
Then open `docs/html/index.html`.
An online version of the documentation of the latest release is available on http://strophe.im/libstrophe/
An online version of the documentation of the latest release is available on https://strophe.im/libstrophe/
Releases
--------

View File

@@ -1,6 +1,6 @@
m4_define([v_maj], [0])
m4_define([v_min], [12])
m4_define([v_patch], [3])
m4_define([v_min], [13])
m4_define([v_patch], [1])
m4_define([project_version], [v_maj.v_min.v_patch])
m4_define([lt_cur], m4_eval(v_maj + v_min))

View File

@@ -5,7 +5,7 @@ includedir=@includedir@
Name: libstrophe
Description: A simple, lightweight C library for writing XMPP clients
URL: http://strophe.im/libstrophe/
URL: https://strophe.im/libstrophe/
Version: @VERSION@
Requires:
Requires.private: @PC_REQUIRES@

View File

@@ -799,9 +799,7 @@ static void _auth(xmpp_conn_t *conn)
conn->ctx, "auth",
"Password hasn't been set, and SASL ANONYMOUS unsupported.");
xmpp_disconnect(conn);
} else if ((conn->sasl_support & SASL_MASK_SCRAM_PLUS) ||
((conn->sasl_support & SASL_MASK_SCRAM_WEAK) &&
!conn->only_strong_auth)) {
} else if (conn->sasl_support & SASL_MASK_SCRAM) {
size_t n;
scram_ctx = strophe_alloc(conn->ctx, sizeof(*scram_ctx));
memset(scram_ctx, 0, sizeof(*scram_ctx));
@@ -859,8 +857,7 @@ static void _auth(xmpp_conn_t *conn)
/* SASL algorithm was tried, unset flag */
conn->sasl_support &= ~scram_ctx->alg->mask;
} else if ((conn->sasl_support & SASL_MASK_DIGESTMD5) &&
conn->weak_auth_enabled) {
} else if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
auth = _make_sasl_auth(conn, "DIGEST-MD5");
if (!auth) {
disconnect_mem_error(conn);
@@ -874,8 +871,7 @@ static void _auth(xmpp_conn_t *conn)
/* SASL DIGEST-MD5 was tried, unset flag */
conn->sasl_support &= ~SASL_MASK_DIGESTMD5;
} else if ((conn->sasl_support & SASL_MASK_PLAIN) &&
conn->weak_auth_enabled) {
} else if (conn->sasl_support & SASL_MASK_PLAIN) {
auth = _make_sasl_auth(conn, "PLAIN");
if (!auth) {
disconnect_mem_error(conn);
@@ -1099,21 +1095,19 @@ static int _handle_features_compress(xmpp_conn_t *conn,
static int
_handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
xmpp_stanza_t *bind, *session, *opt;
xmpp_stanza_t *bind, *session;
xmpp_stanza_t *resume;
const char *ns;
char h[11];
UNUSED(userdata);
/* remove missing features handler */
/* Remove missing features handler */
xmpp_timed_handler_delete(conn, _handle_missing_features_sasl);
/* check whether resource binding is required */
bind = xmpp_stanza_get_child_by_name(stanza, "bind");
/* Check whether resource binding is required */
bind = xmpp_stanza_get_child_by_name_and_ns(stanza, "bind", XMPP_NS_BIND);
if (bind) {
ns = xmpp_stanza_get_ns(bind);
conn->bind_required = ns != NULL && strcmp(ns, XMPP_NS_BIND) == 0;
conn->bind_required = 1;
bind = xmpp_stanza_copy(bind);
if (!bind) {
disconnect_mem_error(conn);
@@ -1123,25 +1117,31 @@ _handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
conn->bind_required = 0;
}
/* check whether session establishment is required */
session = xmpp_stanza_get_child_by_name(stanza, "session");
/* Check whether session establishment is required.
*
* The mechanism is deprecated, but we still support it.
*
* RFC3921 contains Ch. 3 "Session Establishment".
*
* RFC6121 removes this and explains in Ch. 1.4:
* "Interoperability Note: [...] Implementation and deployment experience
* has shown that this additional step is unnecessary. [...]" */
session = xmpp_stanza_get_child_by_name_and_ns(stanza, "session",
XMPP_NS_SESSION);
if (session) {
ns = xmpp_stanza_get_ns(session);
opt = xmpp_stanza_get_child_by_name(session, "optional");
if (!opt)
conn->session_required =
ns != NULL && strcmp(ns, XMPP_NS_SESSION) == 0;
conn->session_required =
xmpp_stanza_get_child_by_name(session, "optional") == NULL;
}
/* Check stream-management support */
if (xmpp_stanza_get_child_by_name_and_ns(stanza, "sm", XMPP_NS_SM)) {
/* stream management supported */
conn->sm_state->sm_support = 1;
}
/* we are expecting either <bind/> and <session/> since this is a
/* We are expecting either <bind/> and optionally <session/> since this is a
XMPP style connection or we <resume/> the previous session */
/* check whether we can <resume/> the previous session */
/* Check whether we can <resume/> the previous session */
if (!conn->sm_disable && conn->sm_state->can_resume &&
conn->sm_state->previd && conn->sm_state->bound_jid) {
resume = xmpp_stanza_new(conn->ctx);
@@ -1188,11 +1188,57 @@ static int _handle_missing_features_sasl(xmpp_conn_t *conn, void *userdata)
return 0;
}
static void _session_start(xmpp_conn_t *conn)
{
xmpp_stanza_t *session;
xmpp_stanza_t *iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_session1");
if (!iq) {
disconnect_mem_error(conn);
return;
}
session = xmpp_stanza_new(conn->ctx);
if (!session) {
xmpp_stanza_release(iq);
disconnect_mem_error(conn);
return;
}
/* setup response handlers */
handler_add_id(conn, _handle_session, "_xmpp_session1", NULL);
handler_add_timed(conn, _handle_missing_session, SESSION_TIMEOUT, NULL);
xmpp_stanza_set_name(session, "session");
xmpp_stanza_set_ns(session, XMPP_NS_SESSION);
xmpp_stanza_add_child_ex(iq, session, 0);
/* send session establishment request */
send_stanza(conn, iq, XMPP_QUEUE_STROPHE);
}
static void _sm_enable(xmpp_conn_t *conn)
{
xmpp_stanza_t *enable = xmpp_stanza_new(conn->ctx);
if (!enable) {
disconnect_mem_error(conn);
return;
}
xmpp_stanza_set_name(enable, "enable");
xmpp_stanza_set_ns(enable, XMPP_NS_SM);
if (!conn->sm_state->dont_request_resume)
xmpp_stanza_set_attribute(enable, "resume", "true");
handler_add(conn, _handle_sm, XMPP_NS_SM, NULL, NULL, NULL);
send_stanza(conn, enable, XMPP_QUEUE_SM_STROPHE);
conn->sm_state->sm_sent_nr = 0;
conn->sm_state->sm_enabled = 1;
}
static int
_handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{
const char *type;
xmpp_stanza_t *iq, *session, *binding, *jid_stanza, *enable = NULL;
xmpp_stanza_t *binding, *jid_stanza;
UNUSED(userdata);
@@ -1215,58 +1261,19 @@ _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
}
}
/* send enable directly after the bind request */
if (conn->sm_state->sm_support && !conn->sm_disable) {
enable = xmpp_stanza_new(conn->ctx);
if (!enable) {
disconnect_mem_error(conn);
return 0;
}
xmpp_stanza_set_name(enable, "enable");
xmpp_stanza_set_ns(enable, XMPP_NS_SM);
if (!conn->sm_state->dont_request_resume)
xmpp_stanza_set_attribute(enable, "resume", "true");
handler_add(conn, _handle_sm, XMPP_NS_SM, NULL, NULL, NULL);
send_stanza(conn, enable, XMPP_QUEUE_SM_STROPHE);
conn->sm_state->sm_sent_nr = 0;
conn->sm_state->sm_enabled = 1;
}
/* establish a session if required */
if (conn->session_required) {
/* setup response handlers */
handler_add_id(conn, _handle_session, "_xmpp_session1", NULL);
handler_add_timed(conn, _handle_missing_session, SESSION_TIMEOUT,
NULL);
/* send session request */
iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_session1");
if (!iq) {
disconnect_mem_error(conn);
return 0;
}
session = xmpp_stanza_new(conn->ctx);
if (!session) {
xmpp_stanza_release(iq);
disconnect_mem_error(conn);
return 0;
}
xmpp_stanza_set_name(session, "session");
xmpp_stanza_set_ns(session, XMPP_NS_SESSION);
xmpp_stanza_add_child_ex(iq, session, 0);
/* send session establishment request */
send_stanza(conn, iq, XMPP_QUEUE_STROPHE);
_session_start(conn);
}
/* send enable directly after the bind request */
else if (conn->sm_state->sm_support && !conn->sm_disable) {
_sm_enable(conn);
}
/* if there's no xmpp session required and we didn't try to enable
* stream-management, we're done here and the stream-negotiation was
* successful
*/
if (!conn->session_required && !enable) {
else {
_stream_negotiation_success(conn);
}
} else {
@@ -1303,8 +1310,11 @@ _handle_session(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
xmpp_disconnect(conn);
} else if (type && strcmp(type, "result") == 0) {
strophe_debug(conn->ctx, "xmpp", "Session establishment successful.");
_stream_negotiation_success(conn);
if (conn->sm_state->sm_support && !conn->sm_disable) {
_sm_enable(conn);
} else {
_stream_negotiation_success(conn);
}
} else {
strophe_error(conn->ctx, "xmpp",
"Server sent malformed session reply.");

View File

@@ -259,8 +259,6 @@ struct _xmpp_conn_t {
int sasl_support; /* if true, field is a bitfield of supported
mechanisms */
int auth_legacy_enabled;
int weak_auth_enabled;
int only_strong_auth;
int secured; /* set when stream is secured with TLS */
xmpp_certfail_handler certfail_handler;
xmpp_password_callback password_callback;

View File

@@ -63,7 +63,7 @@ static int _conn_decompress(struct xmpp_compression *comp,
break;
default:
strophe_error(comp->conn->ctx, "zlib", "inflate error %d", ret);
comp->conn->error = EBADFD;
comp->conn->error = ret;
conn_disconnect(comp->conn);
break;
}
@@ -129,7 +129,7 @@ _compression_write(xmpp_conn_t *conn, const void *buff, size_t len, int flush)
}
if (ret != Z_OK) {
strophe_error(conn->ctx, "zlib", "deflate error %d", ret);
conn->error = EBADFD;
conn->error = ret;
conn_disconnect(conn);
return ret;
}
@@ -227,22 +227,22 @@ int compression_init(xmpp_conn_t *conn)
comp->compression.stream.next_out = comp->compression.buffer;
comp->compression.stream.avail_out = STROPHE_COMPRESSION_BUFFER_SIZE;
int err = deflateInit(&comp->compression.stream, Z_DEFAULT_COMPRESSION);
if (err != Z_OK) {
int ret = deflateInit(&comp->compression.stream, Z_DEFAULT_COMPRESSION);
if (ret != Z_OK) {
strophe_free_and_null(conn->ctx, comp->compression.buffer);
conn->error = EBADFD;
conn->error = ret;
conn_disconnect(conn);
return err;
return ret;
}
_init_zlib_compression(conn->ctx, &comp->decompression);
err = inflateInit(&comp->decompression.stream);
if (err != Z_OK) {
ret = inflateInit(&comp->decompression.stream);
if (ret != Z_OK) {
strophe_free_and_null(conn->ctx, comp->decompression.buffer);
conn->error = EBADFD;
conn->error = ret;
conn_disconnect(conn);
return err;
return ret;
}
return 0;
}

View File

@@ -1133,8 +1133,6 @@ long xmpp_conn_get_flags(const xmpp_conn_t *conn)
XMPP_CONN_FLAG_DISABLE_SM * conn->sm_disable |
XMPP_CONN_FLAG_ENABLE_COMPRESSION * conn->compression.allowed |
XMPP_CONN_FLAG_COMPRESSION_DONT_RESET * conn->compression.dont_reset |
XMPP_CONN_FLAG_WEAK_AUTH * conn->weak_auth_enabled |
XMPP_CONN_FLAG_STRONG_AUTH * conn->only_strong_auth |
XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled;
return flags;
@@ -1149,14 +1147,14 @@ long xmpp_conn_get_flags(const xmpp_conn_t *conn)
*
* Supported flags are:
*
* - XMPP_CONN_FLAG_DISABLE_TLS
* - XMPP_CONN_FLAG_MANDATORY_TLS
* - XMPP_CONN_FLAG_LEGACY_SSL
* - XMPP_CONN_FLAG_TRUST_TLS
* - XMPP_CONN_FLAG_LEGACY_AUTH
* - XMPP_CONN_FLAG_DISABLE_SM
* - XMPP_CONN_FLAG_ENABLE_COMPRESSION
* - XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
* - \ref XMPP_CONN_FLAG_DISABLE_TLS
* - \ref XMPP_CONN_FLAG_MANDATORY_TLS
* - \ref XMPP_CONN_FLAG_LEGACY_SSL
* - \ref XMPP_CONN_FLAG_TRUST_TLS
* - \ref XMPP_CONN_FLAG_LEGACY_AUTH
* - \ref XMPP_CONN_FLAG_DISABLE_SM
* - \ref XMPP_CONN_FLAG_ENABLE_COMPRESSION
* - \ref XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
*
* @param conn a Strophe connection object
* @param flags ORed connection flags
@@ -1190,14 +1188,11 @@ int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags)
(flags & XMPP_CONN_FLAG_ENABLE_COMPRESSION) ? 1 : 0;
conn->compression.dont_reset =
(flags & XMPP_CONN_FLAG_COMPRESSION_DONT_RESET) ? 1 : 0;
conn->weak_auth_enabled = (flags & XMPP_CONN_FLAG_WEAK_AUTH) ? 1 : 0;
conn->only_strong_auth = (flags & XMPP_CONN_FLAG_STRONG_AUTH) ? 1 : 0;
flags &= ~(XMPP_CONN_FLAG_DISABLE_TLS | XMPP_CONN_FLAG_MANDATORY_TLS |
XMPP_CONN_FLAG_LEGACY_SSL | XMPP_CONN_FLAG_TRUST_TLS |
XMPP_CONN_FLAG_LEGACY_AUTH | XMPP_CONN_FLAG_DISABLE_SM |
XMPP_CONN_FLAG_ENABLE_COMPRESSION |
XMPP_CONN_FLAG_COMPRESSION_DONT_RESET |
XMPP_CONN_FLAG_WEAK_AUTH | XMPP_CONN_FLAG_STRONG_AUTH);
XMPP_CONN_FLAG_COMPRESSION_DONT_RESET);
if (flags) {
strophe_error(conn->ctx, "conn", "Flags 0x%04lx unknown", flags);
return XMPP_EINVOP;

View File

@@ -207,7 +207,7 @@ sock_t sock_connect(xmpp_sock_t *xsock)
{
struct addrinfo *ainfo;
sock_t sock;
int rc = 0;
int rc;
char buf[64];
do {
@@ -228,6 +228,7 @@ sock_t sock_connect(xmpp_sock_t *xsock)
sock = socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
if (sock != INVALID_SOCKET) {
rc = 0;
if (xsock->conn->sockopt_cb) {
/* Don't allow user to overwrite sockfd value. */
sock_t sock_copy = sock;

View File

@@ -750,8 +750,9 @@ int tls_init_channel_binding(tls_t *tls,
{
const char *label = NULL;
size_t labellen = 0;
int ssl_version = SSL_version(tls->ssl);
switch (SSL_version(tls->ssl)) {
switch (ssl_version) {
case SSL3_VERSION:
*binding_prefix = "tls-unique";
*binding_prefix_len = strlen("tls-unique");
@@ -774,7 +775,7 @@ int tls_init_channel_binding(tls_t *tls,
break;
#endif
default:
strophe_error(tls->ctx, "tls", "Unsupported TLS Version: %s",
strophe_error(tls->ctx, "tls", "Unsupported TLS/SSL Version: %s",
SSL_get_version(tls->ssl));
return -1;
}
@@ -785,11 +786,28 @@ int tls_init_channel_binding(tls_t *tls,
if (!tls->channel_binding_data)
return -1;
if (SSL_export_keying_material(tls->ssl, tls->channel_binding_data,
tls->channel_binding_size, label, labellen,
NULL, 0, 0) != 1) {
strophe_error(tls->ctx, "tls", "Could not get channel binding data");
return -1;
if (ssl_version <= TLS1_2_VERSION) {
size_t len;
if (SSL_session_reused(tls->ssl)) {
len = SSL_get_peer_finished(tls->ssl, tls->channel_binding_data,
tls->channel_binding_size);
} else {
len = SSL_get_finished(tls->ssl, tls->channel_binding_data,
tls->channel_binding_size);
}
if (len != tls->channel_binding_size) {
strophe_error(tls->ctx, "tls",
"Got channel binding data of wrong size %zu", len);
return -1;
}
} else {
if (SSL_export_keying_material(tls->ssl, tls->channel_binding_data,
tls->channel_binding_size, label,
labellen, NULL, 0, 0) != 1) {
strophe_error(tls->ctx, "tls",
"Could not get channel binding data");
return -1;
}
}
return 0;
}

View File

@@ -208,14 +208,6 @@ typedef struct _xmpp_sm_t xmpp_sm_state_t;
* Only enable this flag if you know what you're doing.
*/
#define XMPP_CONN_FLAG_COMPRESSION_DONT_RESET (1UL << 7)
/** @def XMPP_CONN_FLAG_WEAK_AUTH
* Allow weak authentication methods (DIGEST-MD5 and PLAIN).
*/
#define XMPP_CONN_FLAG_WEAK_AUTH (1UL << 8)
/** @def XMPP_CONN_FLAG_STRONG_AUTH
* Only allow strong authentication methods (Only the SCRAM-*-PLUS variants).
*/
#define XMPP_CONN_FLAG_STRONG_AUTH (1UL << 9)
/* connect callback */
typedef enum {
@@ -715,11 +707,18 @@ void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len);
*/
void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len);
/**
/*
* Formerly "private but exported" functions made public for now to announce
* deprecation */
#include <stdarg.h>
/**
* XMPP_DEPRECATED(x) macro to show a compiler warning for deprecated API
* functions
*
* @param x The function that can be used as a replacement or 'internal' if
* there is no replacement
*/
#if defined(__GNUC__)
#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 405)
#define XMPP_DEPRECATED(x) __attribute__((deprecated("replaced by " #x)))

View File

@@ -175,7 +175,7 @@ static void test_stanza_error(xmpp_ctx_t *ctx)
xmpp_stanza_reply_error(stanza, "cancel", "service-unavailable", NULL);
assert(error != NULL);
mood = xmpp_stanza_new_from_string(ctx, str_mood);
assert(stanza != NULL);
assert(mood != NULL);
assert(xmpp_stanza_get_to(error) != NULL);
COMPARE("romeo@montague.lit/home", xmpp_stanza_get_to(error));