Skip to content

Commit f995cb5

Browse files
committed
Implement authentication with FAST
https://xmpp.org/extensions/xep-0484.html
1 parent 5157960 commit f995cb5

File tree

6 files changed

+151
-21
lines changed

6 files changed

+151
-21
lines changed

src/auth.c

Lines changed: 124 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,12 @@ static int _handle_missing_features(xmpp_conn_t *conn, void *userdata)
212212
return 0;
213213
}
214214

215-
typedef void (*text_handler)(xmpp_conn_t *conn, const char *text);
215+
typedef void (*text_handler)(xmpp_conn_t *conn, const char *text, void *userdata);
216216
static void _foreach_child(xmpp_conn_t *conn,
217217
xmpp_stanza_t *parent,
218218
const char *name,
219-
text_handler hndl)
219+
text_handler hndl,
220+
void *userdata)
220221
{
221222
xmpp_stanza_t *children;
222223
for (children = xmpp_stanza_get_children(parent); children;
@@ -227,29 +228,32 @@ static void _foreach_child(xmpp_conn_t *conn,
227228
if (text == NULL)
228229
continue;
229230

230-
hndl(conn, text);
231+
hndl(conn, text, userdata);
231232

232233
strophe_free(conn->ctx, text);
233234
}
234235
}
235236
}
236237

237-
static void _handle_sasl_children(xmpp_conn_t *conn, const char *text)
238+
static void _handle_sasl_children(xmpp_conn_t *conn, const char *text, void *userdata)
238239
{
240+
int *support = userdata;
239241
if (strcasecmp(text, "PLAIN") == 0) {
240-
conn->sasl_support |= SASL_MASK_PLAIN;
242+
*support |= SASL_MASK_PLAIN;
241243
} else if (strcasecmp(text, "EXTERNAL") == 0 &&
242244
(conn->tls_client_cert || conn->tls_client_key)) {
243-
conn->sasl_support |= SASL_MASK_EXTERNAL;
245+
*support |= SASL_MASK_EXTERNAL;
244246
} else if (strcasecmp(text, "DIGEST-MD5") == 0) {
245-
conn->sasl_support |= SASL_MASK_DIGESTMD5;
247+
*support |= SASL_MASK_DIGESTMD5;
246248
} else if (strcasecmp(text, "ANONYMOUS") == 0) {
247-
conn->sasl_support |= SASL_MASK_ANONYMOUS;
249+
*support |= SASL_MASK_ANONYMOUS;
250+
} else if (strcasecmp(text, "HT-SHA-256-EXPR") == 0) {
251+
*support |= SASL_MASK_HT_SHA_256_EXPR;
248252
} else {
249253
size_t n;
250254
for (n = 0; n < scram_algs_num; ++n) {
251255
if (strcasecmp(text, scram_algs[n]->scram_name) == 0) {
252-
conn->sasl_support |= scram_algs[n]->mask;
256+
*support |= scram_algs[n]->mask;
253257
break;
254258
}
255259
}
@@ -284,13 +288,27 @@ _handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
284288

285289
if (child) {
286290
conn->sasl_support |= SASL_MASK_SASL2;
287-
_foreach_child(conn, child, "mechanism", _handle_sasl_children);
291+
_foreach_child(conn, child, "mechanism", _handle_sasl_children,
292+
&conn->sasl_support);
293+
294+
295+
xmpp_stanza_t *inlin = xmpp_stanza_get_child_by_name_and_ns(child,
296+
"inline", XMPP_NS_SASL2);
297+
if (inlin) {
298+
xmpp_stanza_t *fast = xmpp_stanza_get_child_by_name_and_ns(inlin,
299+
"fast", XMPP_NS_FAST);
300+
if (fast) {
301+
_foreach_child(conn, fast, "mechanism", _handle_sasl_children,
302+
&conn->fast_support);
303+
}
304+
}
288305
} else {
289306
/* check for SASL */
290307
child = xmpp_stanza_get_child_by_name_and_ns(stanza, "mechanisms",
291308
XMPP_NS_SASL);
292309
if (child) {
293-
_foreach_child(conn, child, "mechanism", _handle_sasl_children);
310+
_foreach_child(conn, child, "mechanism", _handle_sasl_children,
311+
&conn->sasl_support);
294312
}
295313
}
296314

@@ -756,7 +774,7 @@ static xmpp_stanza_t *_make_starttls(xmpp_conn_t *conn)
756774
return starttls;
757775
}
758776

759-
static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t *conn, const char *mechanism, const char *initial_data)
777+
static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t *conn, const char *mechanism, const char *initial_data, int fast_count)
760778
{
761779
xmpp_stanza_t *auth, *init, *user_agent;
762780
xmpp_stanza_t *inittxt = NULL;
@@ -836,6 +854,31 @@ static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t *conn, const char *mechanism,
836854
}
837855
xmpp_stanza_add_child_ex(auth, user_agent, 0);
838856
}
857+
if (fast_count >= 0) {
858+
char count_str[50];
859+
size_t result = snprintf(count_str, sizeof(count_str), "%d", fast_count);
860+
if (result > 0 && result < sizeof(count_str)) {
861+
xmpp_stanza_t *fast = xmpp_stanza_new(conn->ctx);
862+
if (!fast) {
863+
xmpp_stanza_release(auth);
864+
return NULL;
865+
}
866+
xmpp_stanza_set_name(fast, "fast");
867+
xmpp_stanza_set_ns(fast, XMPP_NS_FAST);
868+
xmpp_stanza_set_attribute(fast, "count", count_str);
869+
xmpp_stanza_add_child_ex(auth, fast, 0);
870+
}
871+
} else if (conn->fast_support & SASL_MASK_HT_SHA_256_EXPR) {
872+
xmpp_stanza_t *request_token = xmpp_stanza_new(conn->ctx);
873+
if (!request_token) {
874+
xmpp_stanza_release(auth);
875+
return NULL;
876+
}
877+
xmpp_stanza_set_name(request_token, "request-token");
878+
xmpp_stanza_set_ns(request_token, XMPP_NS_FAST);
879+
xmpp_stanza_set_attribute(request_token, "mechanism", "HT-SHA-256-EXPR");
880+
xmpp_stanza_add_child_ex(auth, request_token, 0);
881+
}
839882
} else {
840883
xmpp_stanza_set_name(auth, "auth");
841884
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
@@ -916,7 +959,7 @@ static void _auth(xmpp_conn_t *conn)
916959

917960
if (anonjid && (conn->sasl_support & SASL_MASK_ANONYMOUS)) {
918961
/* some crap here */
919-
auth = _make_sasl_auth(conn, "ANONYMOUS", NULL);
962+
auth = _make_sasl_auth(conn, "ANONYMOUS", NULL, -1);
920963
if (!auth) {
921964
disconnect_mem_error(conn);
922965
return;
@@ -945,7 +988,7 @@ static void _auth(xmpp_conn_t *conn)
945988
}
946989
}
947990

948-
auth = _make_sasl_auth(conn, "EXTERNAL", str);
991+
auth = _make_sasl_auth(conn, "EXTERNAL", str, -1);
949992
strophe_free(conn->ctx, str);
950993
if (!auth) {
951994
disconnect_mem_error(conn);
@@ -963,6 +1006,69 @@ static void _auth(xmpp_conn_t *conn)
9631006
strophe_error(conn->ctx, "auth",
9641007
"No node in JID, and SASL ANONYMOUS unsupported.");
9651008
xmpp_disconnect(conn);
1009+
} else if (conn->fast_token && xmpp_conn_is_secured(conn) && conn->fast_support & SASL_MASK_HT_SHA_256_EXPR) {
1010+
const char *binding_type;
1011+
size_t binding_type_len;
1012+
if (tls_init_channel_binding(conn->tls, &binding_type,
1013+
&binding_type_len)) {
1014+
return;
1015+
}
1016+
if (strcmp(binding_type, "tls-exporter")) {
1017+
strophe_error(
1018+
conn->ctx, "auth",
1019+
"Can't use FAST without tls-exporter");
1020+
return;
1021+
}
1022+
1023+
uint8_t init[41];
1024+
size_t binding_data_len;
1025+
const uint8_t *cbdata =
1026+
tls_get_channel_binding_data(conn->tls, &binding_data_len);
1027+
if (binding_data_len > 32) {
1028+
strophe_error(
1029+
conn->ctx, "auth",
1030+
"Channel binding data is too big");
1031+
return;
1032+
}
1033+
memcpy(init, "Initiator", sizeof("Initiator")-1); // No NUL terminator
1034+
memcpy(init+sizeof("Initiator")-1, cbdata, binding_data_len);
1035+
1036+
authid = _get_authid(conn);
1037+
if (!authid) {
1038+
disconnect_mem_error(conn);
1039+
return;
1040+
}
1041+
1042+
const uint8_t *token = (uint8_t*)conn->fast_token;
1043+
uint8_t *buf = strophe_alloc(conn->ctx,
1044+
strlen(authid) + 1 + SHA256_DIGEST_SIZE);
1045+
memcpy(buf, authid, strlen(authid) + 1); // Copy NUL terminator too
1046+
crypto_HMAC(&scram_sha256,
1047+
token,
1048+
strlen(conn->fast_token),
1049+
init,
1050+
sizeof("Initiator") - 1 + binding_data_len,
1051+
buf + strlen(authid) + 1);
1052+
1053+
str = xmpp_base64_encode(conn->ctx,
1054+
buf,
1055+
strlen(authid) + 1 + SHA256_DIGEST_SIZE);
1056+
1057+
auth = _make_sasl_auth(conn, "HT-SHA-256-EXPR", str, conn->fast_count);
1058+
strophe_free(conn->ctx, str);
1059+
strophe_free(conn->ctx, buf);
1060+
strophe_free(conn->ctx, authid);
1061+
if (!auth) {
1062+
disconnect_mem_error(conn);
1063+
return;
1064+
}
1065+
1066+
handler_add(conn, _handle_ht_challenge, sasl_ns, NULL, NULL, NULL);
1067+
1068+
send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
1069+
1070+
/* FAST was tried, unset flag */
1071+
conn->fast_support &= ~SASL_MASK_HT_SHA_256_EXPR;
9661072
} else if (conn->pass == NULL) {
9671073
strophe_error(
9681074
conn->ctx, "auth",
@@ -998,7 +1104,7 @@ static void _auth(xmpp_conn_t *conn)
9981104
return;
9991105
}
10001106

1001-
auth = _make_sasl_auth(conn, scram_ctx->alg->scram_name, str);
1107+
auth = _make_sasl_auth(conn, scram_ctx->alg->scram_name, str, -1);
10021108
strophe_free(conn->ctx, str);
10031109
if (!auth) {
10041110
disconnect_mem_error(conn);
@@ -1013,7 +1119,7 @@ static void _auth(xmpp_conn_t *conn)
10131119
/* SASL algorithm was tried, unset flag */
10141120
conn->sasl_support &= ~scram_ctx->alg->mask;
10151121
} else if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
1016-
auth = _make_sasl_auth(conn, "DIGEST-MD5", NULL);
1122+
auth = _make_sasl_auth(conn, "DIGEST-MD5", NULL, -1);
10171123
if (!auth) {
10181124
disconnect_mem_error(conn);
10191125
return;
@@ -1038,7 +1144,7 @@ static void _auth(xmpp_conn_t *conn)
10381144
return;
10391145
}
10401146

1041-
auth = _make_sasl_auth(conn, "PLAIN", str);
1147+
auth = _make_sasl_auth(conn, "PLAIN", str, -1);
10421148
strophe_free(conn->ctx, str);
10431149
strophe_free(conn->ctx, authid);
10441150
if (!auth) {
@@ -1226,7 +1332,7 @@ static int _handle_features_compress(xmpp_conn_t *conn,
12261332
XMPP_NS_FEATURE_COMPRESSION);
12271333
if (conn->compression.allowed && child) {
12281334
_foreach_child(conn, child, "method",
1229-
compression_handle_feature_children);
1335+
compression_handle_feature_children, NULL);
12301336
}
12311337

12321338
if (conn->compression.supported) {

src/common.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ int conn_int_nop(struct conn_interface *intf);
228228

229229
int compression_init(xmpp_conn_t *conn);
230230
void compression_free(xmpp_conn_t *conn);
231-
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text);
231+
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text, void *userdata);
232232

233233
struct _xmpp_conn_t {
234234
struct conn_interface intf;
@@ -262,6 +262,7 @@ struct _xmpp_conn_t {
262262
int tls_failed; /* set when tls fails, so we don't try again */
263263
int sasl_support; /* if true, field is a bitfield of supported
264264
mechanisms */
265+
int fast_support;
265266
int auth_legacy_enabled;
266267
int secured; /* set when stream is secured with TLS */
267268
xmpp_certfail_handler certfail_handler;
@@ -289,6 +290,8 @@ struct _xmpp_conn_t {
289290
char *domain;
290291
char *jid;
291292
char *pass;
293+
char *fast_token;
294+
int fast_count;
292295
char *user_agent_id;
293296
char *user_agent_software;
294297
char *user_agent_device;

src/compression.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,9 @@ void compression_free(xmpp_conn_t *conn)
262262
}
263263
}
264264

265-
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text)
265+
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text, void *userdata)
266266
{
267+
UNUSED(userdata);
267268
if (strcasecmp(text, "zlib") == 0) {
268269
conn->compression.supported = 1;
269270
}

src/compression_dummy.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ void compression_free(xmpp_conn_t *conn)
2929
UNUSED(conn);
3030
}
3131

32-
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text)
32+
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text, void *userdata)
3333
{
3434
UNUSED(text);
35+
UNUSED(userdata);
3536
conn->compression.supported = 0;
3637
}

src/conn.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,24 @@ void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass)
623623
conn->pass = pass ? strophe_strdup(conn->ctx, pass) : NULL;
624624
}
625625

626+
/** Set the fast token and count used to authenticate the connection.
627+
* If any token was previously set, it will be discarded. The function
628+
* will make a copy of the token string.
629+
*
630+
* @param conn a Strophe connection object
631+
* @param token the FAST token
632+
* @param count the count of times this token has been tried
633+
*
634+
* @ingroup Connections
635+
*/
636+
void xmpp_conn_set_fast(xmpp_conn_t *conn, const char *token, int count)
637+
{
638+
if (conn->fast_token)
639+
strophe_free(conn->ctx, conn->fast_token);
640+
conn->fast_token = token ? strophe_strdup(conn->ctx, token) : NULL;
641+
conn->fast_count = count;
642+
}
643+
626644
/** Set the fast token handler to receive new tokens from the server
627645
*
628646
* @param conn a Strophe connection object

strophe.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *conn);
406406
char *xmpp_conn_cert_xmppaddr(xmpp_conn_t *conn, unsigned int n);
407407
const char *xmpp_conn_get_pass(const xmpp_conn_t *conn);
408408
void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass);
409+
void xmpp_conn_set_fast(xmpp_conn_t *conn, const char *token, int count);
409410
typedef void (*xmpp_fast_token_handler)(xmpp_conn_t *conn, const char *token, void *userdata);
410411
void xmpp_conn_set_fast_token_handler(xmpp_conn_t *conn, xmpp_fast_token_handler handler, void *userdata);
411412
const char *xmpp_conn_get_user_agent_id(const xmpp_conn_t *conn);

0 commit comments

Comments
 (0)