Skip to content

Commit

Permalink
Add support for SASL2
Browse files Browse the repository at this point in the history
  • Loading branch information
singpolyma committed Jul 22, 2024
1 parent 5142eea commit 3b0c462
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 74 deletions.
164 changes: 90 additions & 74 deletions src/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,20 @@ _handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
}
}

/* check for SASL */
child = xmpp_stanza_get_child_by_name_and_ns(stanza, "mechanisms",
XMPP_NS_SASL);
/* check for SASL2 */
child = xmpp_stanza_get_child_by_name_and_ns(stanza, "authentication",
XMPP_NS_SASL2);

if (child) {
conn->sasl_support |= SASL_MASK_SASL2;
_foreach_child(conn, child, "mechanism", _handle_sasl_children);
} else {
/* check for SASL */
child = xmpp_stanza_get_child_by_name_and_ns(stanza, "mechanisms",
XMPP_NS_SASL);
if (child) {
_foreach_child(conn, child, "mechanism", _handle_sasl_children);
}
}

/* Disable PLAIN when other secure mechanisms are supported */
Expand Down Expand Up @@ -350,17 +359,27 @@ _handle_sasl_result(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
/* fall back to next auth method */
_auth(conn);
} else if (strcmp(name, "success") == 0) {
/* SASL auth successful, we need to restart the stream */
strophe_debug(conn->ctx, "xmpp", "SASL %s auth successful",
(char *)userdata);

/* reset parser */
conn_prepare_reset(conn, conn->compression.allowed
? _handle_open_compress
: _handle_open_sasl);
if (conn->sasl_support & SASL_MASK_SASL2) {
/* New features will come, but no restart */
if (conn->compression.allowed) {
_handle_open_compress(conn);
} else {
_handle_open_sasl(conn);
}
} else {
/* SASL auth successful, we need to restart the stream */

/* send stream tag */
conn_open_stream(conn);
/* reset parser */
conn_prepare_reset(conn, conn->compression.allowed
? _handle_open_compress
: _handle_open_sasl);

/* send stream tag */
conn_open_stream(conn);
}
} else {
/* got unexpected reply */
strophe_error(conn->ctx, "xmpp",
Expand Down Expand Up @@ -389,6 +408,7 @@ static int _handle_digestmd5_challenge(xmpp_conn_t *conn,
"handle digest-md5 (challenge) called for %s", name);

if (strcmp(name, "challenge") == 0) {
const char* sasl_ns = conn->sasl_support & SASL_MASK_SASL2 ? XMPP_NS_SASL2 : XMPP_NS_SASL;
text = xmpp_stanza_get_text(stanza);
response = sasl_digest_md5(conn->ctx, text, conn->jid, conn->pass);
if (!response) {
Expand All @@ -403,7 +423,7 @@ static int _handle_digestmd5_challenge(xmpp_conn_t *conn,
return 0;
}
xmpp_stanza_set_name(auth, "response");
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
xmpp_stanza_set_ns(auth, sasl_ns);

authdata = xmpp_stanza_new(conn->ctx);
if (!authdata) {
Expand All @@ -416,7 +436,7 @@ static int _handle_digestmd5_challenge(xmpp_conn_t *conn,

xmpp_stanza_add_child_ex(auth, authdata, 0);

handler_add(conn, _handle_digestmd5_rspauth, XMPP_NS_SASL, NULL, NULL,
handler_add(conn, _handle_digestmd5_rspauth, sasl_ns, NULL, NULL,
NULL);

send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
Expand Down Expand Up @@ -444,14 +464,15 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t *conn,
"handle digest-md5 (rspauth) called for %s", name);

if (strcmp(name, "challenge") == 0) {
const char* sasl_ns = conn->sasl_support & SASL_MASK_SASL2 ? XMPP_NS_SASL2 : XMPP_NS_SASL;
/* assume it's an rspauth response */
auth = xmpp_stanza_new(conn->ctx);
if (!auth) {
disconnect_mem_error(conn);
return 0;
}
xmpp_stanza_set_name(auth, "response");
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
xmpp_stanza_set_ns(auth, sasl_ns);
send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
} else {
return _handle_sasl_result(conn, stanza, "DIGEST-MD5");
Expand Down Expand Up @@ -488,6 +509,7 @@ static int _handle_scram_challenge(xmpp_conn_t *conn,
scram_ctx->alg->scram_name, name);

if (strcmp(name, "challenge") == 0) {
const char* sasl_ns = conn->sasl_support & SASL_MASK_SASL2 ? XMPP_NS_SASL2 : XMPP_NS_SASL;
text = xmpp_stanza_get_text(stanza);
if (!text)
goto err;
Expand All @@ -508,7 +530,7 @@ static int _handle_scram_challenge(xmpp_conn_t *conn,
if (!auth)
goto err_free_response;
xmpp_stanza_set_name(auth, "response");
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
xmpp_stanza_set_ns(auth, sasl_ns);

authdata = xmpp_stanza_new(conn->ctx);
if (!authdata)
Expand Down Expand Up @@ -658,16 +680,41 @@ static xmpp_stanza_t *_make_starttls(xmpp_conn_t *conn)
return starttls;
}

static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t *conn, const char *mechanism)
static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t *conn, const char *mechanism, const char *initial_data)
{
xmpp_stanza_t *auth;
xmpp_stanza_t *auth, *init, *inittxt;

/* build auth stanza */
inittxt = xmpp_stanza_new(conn->ctx);
if (!inittxt) return NULL;
auth = xmpp_stanza_new(conn->ctx);
if (auth) {
xmpp_stanza_set_name(auth, "auth");
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
if (conn->sasl_support & SASL_MASK_SASL2) {
xmpp_stanza_set_name(auth, "authenticate");
xmpp_stanza_set_ns(auth, XMPP_NS_SASL2);
if (initial_data) {
init = xmpp_stanza_new(conn->ctx);
if (!init) {
xmpp_stanza_release(auth);
return NULL;
}
xmpp_stanza_set_name(init, "initial-response");
xmpp_stanza_set_ns(init, XMPP_NS_SASL2);
xmpp_stanza_set_text(inittxt, initial_data);
xmpp_stanza_add_child_ex(init, inittxt, 0);
xmpp_stanza_add_child_ex(auth, init, 0);
}
} else {
xmpp_stanza_set_name(auth, "auth");
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
if (initial_data) {
xmpp_stanza_set_text(inittxt, initial_data);
xmpp_stanza_add_child_ex(auth, inittxt, 0);
}
}
xmpp_stanza_set_attribute(auth, "mechanism", mechanism);
} else {
xmpp_stanza_release(inittxt);
}

return auth;
Expand All @@ -681,7 +728,6 @@ static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t *conn, const char *mechanism)
static void _auth(xmpp_conn_t *conn)
{
xmpp_stanza_t *auth;
xmpp_stanza_t *authdata;
struct scram_user_data *scram_ctx;
char *authid;
char *str;
Expand Down Expand Up @@ -734,15 +780,17 @@ static void _auth(xmpp_conn_t *conn)
return;
}

const char* sasl_ns = conn->sasl_support & SASL_MASK_SASL2 ? XMPP_NS_SASL2 : XMPP_NS_SASL;

if (anonjid && (conn->sasl_support & SASL_MASK_ANONYMOUS)) {
/* some crap here */
auth = _make_sasl_auth(conn, "ANONYMOUS");
auth = _make_sasl_auth(conn, "ANONYMOUS", NULL);
if (!auth) {
disconnect_mem_error(conn);
return;
}

handler_add(conn, _handle_sasl_result, XMPP_NS_SASL, NULL, NULL,
handler_add(conn, _handle_sasl_result, sasl_ns, NULL, NULL,
"ANONYMOUS");

send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
Expand All @@ -751,39 +799,28 @@ static void _auth(xmpp_conn_t *conn)
conn->sasl_support &= ~SASL_MASK_ANONYMOUS;
} else if (conn->sasl_support & SASL_MASK_EXTERNAL) {
/* more crap here */
auth = _make_sasl_auth(conn, "EXTERNAL");
if (!auth) {
disconnect_mem_error(conn);
return;
}

authdata = xmpp_stanza_new(conn->ctx);
if (!authdata) {
xmpp_stanza_release(auth);
disconnect_mem_error(conn);
return;
}
str = tls_id_on_xmppaddr(conn, 0);
if (!str || (tls_id_on_xmppaddr_num(conn) == 1 &&
strcmp(str, conn->jid) == 0)) {
xmpp_stanza_set_text(authdata, "=");
str = strophe_strdup(conn->ctx, "=");
} else {
strophe_free(conn->ctx, str);
str = xmpp_base64_encode(conn->ctx, (void *)conn->jid,
strlen(conn->jid));
if (!str) {
xmpp_stanza_release(authdata);
xmpp_stanza_release(auth);
disconnect_mem_error(conn);
return;
}
xmpp_stanza_set_text(authdata, str);
}
strophe_free(conn->ctx, str);

xmpp_stanza_add_child_ex(auth, authdata, 0);
auth = _make_sasl_auth(conn, "EXTERNAL", str);
strophe_free(conn->ctx, str);
if (!auth) {
disconnect_mem_error(conn);
return;
}

handler_add(conn, _handle_sasl_result, XMPP_NS_SASL, NULL, NULL,
handler_add(conn, _handle_sasl_result, sasl_ns, NULL, NULL,
"EXTERNAL");

send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
Expand All @@ -810,18 +847,11 @@ static void _auth(xmpp_conn_t *conn)
}
}

auth = _make_sasl_auth(conn, scram_ctx->alg->scram_name);
if (!auth) {
disconnect_mem_error(conn);
return;
}

scram_ctx->conn = conn;
scram_ctx->sasl_plus =
scram_ctx->alg->mask & SASL_MASK_SCRAM_PLUS ? 1 : 0;
if (_make_scram_init_msg(scram_ctx)) {
strophe_free(conn->ctx, scram_ctx);
xmpp_stanza_release(auth);
disconnect_mem_error(conn);
return;
}
Expand All @@ -832,56 +862,39 @@ static void _auth(xmpp_conn_t *conn)
if (!str) {
strophe_free(conn->ctx, scram_ctx->scram_init);
strophe_free(conn->ctx, scram_ctx);
xmpp_stanza_release(auth);
disconnect_mem_error(conn);
return;
}

authdata = xmpp_stanza_new(conn->ctx);
if (!authdata) {
strophe_free(conn->ctx, str);
strophe_free(conn->ctx, scram_ctx->scram_init);
strophe_free(conn->ctx, scram_ctx);
xmpp_stanza_release(auth);
auth = _make_sasl_auth(conn, scram_ctx->alg->scram_name, str);
strophe_free(conn->ctx, str);
if (!auth) {
disconnect_mem_error(conn);
return;
}
xmpp_stanza_set_text(authdata, str);
strophe_free(conn->ctx, str);
xmpp_stanza_add_child_ex(auth, authdata, 0);

handler_add(conn, _handle_scram_challenge, XMPP_NS_SASL, NULL, NULL,
handler_add(conn, _handle_scram_challenge, sasl_ns, NULL, NULL,
(void *)scram_ctx);

send_stanza(conn, auth, XMPP_QUEUE_STROPHE);

/* SASL algorithm was tried, unset flag */
conn->sasl_support &= ~scram_ctx->alg->mask;
} else if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
auth = _make_sasl_auth(conn, "DIGEST-MD5");
auth = _make_sasl_auth(conn, "DIGEST-MD5", NULL);
if (!auth) {
disconnect_mem_error(conn);
return;
}

handler_add(conn, _handle_digestmd5_challenge, XMPP_NS_SASL, NULL, NULL,
handler_add(conn, _handle_digestmd5_challenge, sasl_ns, NULL, NULL,
NULL);

send_stanza(conn, auth, XMPP_QUEUE_STROPHE);

/* SASL DIGEST-MD5 was tried, unset flag */
conn->sasl_support &= ~SASL_MASK_DIGESTMD5;
} else if (conn->sasl_support & SASL_MASK_PLAIN) {
auth = _make_sasl_auth(conn, "PLAIN");
if (!auth) {
disconnect_mem_error(conn);
return;
}
authdata = xmpp_stanza_new(conn->ctx);
if (!authdata) {
disconnect_mem_error(conn);
return;
}
authid = _get_authid(conn);
if (!authid) {
disconnect_mem_error(conn);
Expand All @@ -892,13 +905,16 @@ static void _auth(xmpp_conn_t *conn)
disconnect_mem_error(conn);
return;
}
xmpp_stanza_set_text(authdata, str);

auth = _make_sasl_auth(conn, "PLAIN", str);
strophe_free(conn->ctx, str);
strophe_free(conn->ctx, authid);
if (!auth) {
disconnect_mem_error(conn);
return;
}

xmpp_stanza_add_child_ex(auth, authdata, 0);

handler_add(conn, _handle_sasl_result, XMPP_NS_SASL, NULL, NULL,
handler_add(conn, _handle_sasl_result, sasl_ns, NULL, NULL,
"PLAIN");

send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
Expand Down
1 change: 1 addition & 0 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ struct _xmpp_send_queue_t {
#define SASL_MASK_SCRAMSHA1_PLUS (1 << 7)
#define SASL_MASK_SCRAMSHA256_PLUS (1 << 8)
#define SASL_MASK_SCRAMSHA512_PLUS (1 << 9)
#define SASL_MASK_SASL2 (1 << 10)

#define SASL_MASK_SCRAM_PLUS \
(SASL_MASK_SCRAMSHA1_PLUS | SASL_MASK_SCRAMSHA256_PLUS | \
Expand Down
4 changes: 4 additions & 0 deletions strophe.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ extern "C" {
* Namespace definition for 'urn:ietf:params:xml:ns:xmpp-sasl'.
*/
#define XMPP_NS_SASL "urn:ietf:params:xml:ns:xmpp-sasl"
/** @def XMPP_NS_SASL2
* Namespace definition for 'urn:xmpp:sasl:2'.
*/
#define XMPP_NS_SASL2 "urn:xmpp:sasl:2"
/** @def XMPP_NS_BIND
* Namespace definition for 'urn:ietf:params:xml:ns:xmpp-bind'.
*/
Expand Down

0 comments on commit 3b0c462

Please sign in to comment.