Line data Source code
1 : /* auth.c
2 : ** strophe XMPP client library -- auth functions and handlers
3 : **
4 : ** Copyright (C) 2005-2009 Collecta, Inc.
5 : **
6 : ** This software is provided AS-IS with no warranty, either express or
7 : ** implied.
8 : **
9 : ** This program is dual licensed under the MIT and GPLv3 licenses.
10 : */
11 :
12 : /** @file
13 : * Authentication function and handlers.
14 : */
15 :
16 : #include <stdio.h>
17 : #include <stdlib.h>
18 : #include <string.h>
19 :
20 : #include "strophe.h"
21 : #include "common.h"
22 : #include "sasl.h"
23 : #include "sha1.h"
24 :
25 : #ifdef _MSC_VER
26 : #define strcasecmp _stricmp
27 : #endif
28 :
29 : /* TODO: these should configurable at runtime on a per connection basis */
30 :
31 : #ifndef FEATURES_TIMEOUT
32 : /** @def FEATURES_TIMEOUT
33 : * Time to wait for <stream:features/> stanza.
34 : */
35 : #define FEATURES_TIMEOUT 15000 /* 15 seconds */
36 : #endif
37 : #ifndef BIND_TIMEOUT
38 : /** @def BIND_TIMEOUT
39 : * Time to wait for <bind/> stanza reply.
40 : */
41 : #define BIND_TIMEOUT 15000 /* 15 seconds */
42 : #endif
43 : #ifndef SESSION_TIMEOUT
44 : /** @def SESSION_TIMEOUT
45 : * Time to wait for <session/> stanza reply.
46 : */
47 : #define SESSION_TIMEOUT 15000 /* 15 seconds */
48 : #endif
49 : #ifndef LEGACY_TIMEOUT
50 : /** @def LEGACY_TIMEOUT
51 : * Time to wait for legacy authentication to complete.
52 : */
53 : #define LEGACY_TIMEOUT 15000 /* 15 seconds */
54 : #endif
55 : #ifndef HANDSHAKE_TIMEOUT
56 : /** @def HANDSHAKE_TIMEOUT
57 : * Time to wait for component authentication to complete
58 : */
59 : #define HANDSHAKE_TIMEOUT 15000 /* 15 seconds */
60 : #endif
61 :
62 : static void _auth(xmpp_conn_t *conn);
63 : static void _auth_legacy(xmpp_conn_t *conn);
64 : static void _handle_open_sasl(xmpp_conn_t *conn);
65 : static void _handle_open_tls(xmpp_conn_t *conn);
66 :
67 : static int _handle_component_auth(xmpp_conn_t *conn);
68 : static int _handle_component_hs_response(xmpp_conn_t *conn,
69 : xmpp_stanza_t *stanza,
70 : void *userdata);
71 :
72 : static int
73 : _handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata);
74 : static int
75 : _handle_sasl_result(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata);
76 : static int _handle_digestmd5_challenge(xmpp_conn_t *conn,
77 : xmpp_stanza_t *stanza,
78 : void *userdata);
79 : static int _handle_digestmd5_rspauth(xmpp_conn_t *conn,
80 : xmpp_stanza_t *stanza,
81 : void *userdata);
82 : static int _handle_scram_challenge(xmpp_conn_t *conn,
83 : xmpp_stanza_t *stanza,
84 : void *userdata);
85 : static char *_make_scram_init_msg(xmpp_conn_t *conn);
86 :
87 : static int _handle_missing_features_sasl(xmpp_conn_t *conn, void *userdata);
88 : static int _handle_missing_bind(xmpp_conn_t *conn, void *userdata);
89 : static int
90 : _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata);
91 : static int
92 : _handle_session(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata);
93 : static int _handle_missing_session(xmpp_conn_t *conn, void *userdata);
94 : static int _handle_missing_handshake(xmpp_conn_t *conn, void *userdata);
95 : static int _handle_sm(xmpp_conn_t *const conn,
96 : xmpp_stanza_t *const stanza,
97 : void *const userdata);
98 :
99 : /* stream:error handler */
100 : static int
101 0 : _handle_error(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
102 : {
103 0 : xmpp_stanza_t *child;
104 0 : const char *name;
105 :
106 0 : UNUSED(userdata);
107 :
108 : /* free old stream error if it's still there */
109 0 : if (conn->stream_error) {
110 0 : xmpp_stanza_release(conn->stream_error->stanza);
111 0 : if (conn->stream_error->text)
112 0 : strophe_free(conn->ctx, conn->stream_error->text);
113 0 : strophe_free(conn->ctx, conn->stream_error);
114 : }
115 :
116 : /* create stream error structure */
117 0 : conn->stream_error = (xmpp_stream_error_t *)strophe_alloc(
118 0 : conn->ctx, sizeof(xmpp_stream_error_t));
119 :
120 0 : conn->stream_error->text = NULL;
121 0 : conn->stream_error->type = XMPP_SE_UNDEFINED_CONDITION;
122 :
123 0 : if (conn->stream_error) {
124 0 : child = xmpp_stanza_get_children(stanza);
125 0 : do {
126 0 : const char *ns = NULL;
127 :
128 0 : if (child) {
129 0 : ns = xmpp_stanza_get_ns(child);
130 : }
131 :
132 0 : if (ns && strcmp(ns, XMPP_NS_STREAMS_IETF) == 0) {
133 0 : name = xmpp_stanza_get_name(child);
134 0 : if (strcmp(name, "text") == 0) {
135 0 : if (conn->stream_error->text)
136 0 : strophe_free(conn->ctx, conn->stream_error->text);
137 0 : conn->stream_error->text = xmpp_stanza_get_text(child);
138 0 : } else if (strcmp(name, "bad-format") == 0)
139 0 : conn->stream_error->type = XMPP_SE_BAD_FORMAT;
140 0 : else if (strcmp(name, "bad-namespace-prefix") == 0)
141 0 : conn->stream_error->type = XMPP_SE_BAD_NS_PREFIX;
142 0 : else if (strcmp(name, "conflict") == 0)
143 0 : conn->stream_error->type = XMPP_SE_CONFLICT;
144 0 : else if (strcmp(name, "connection-timeout") == 0)
145 0 : conn->stream_error->type = XMPP_SE_CONN_TIMEOUT;
146 0 : else if (strcmp(name, "host-gone") == 0)
147 0 : conn->stream_error->type = XMPP_SE_HOST_GONE;
148 0 : else if (strcmp(name, "host-unknown") == 0)
149 0 : conn->stream_error->type = XMPP_SE_HOST_UNKNOWN;
150 0 : else if (strcmp(name, "improper-addressing") == 0)
151 0 : conn->stream_error->type = XMPP_SE_IMPROPER_ADDR;
152 0 : else if (strcmp(name, "internal-server-error") == 0)
153 0 : conn->stream_error->type = XMPP_SE_INTERNAL_SERVER_ERROR;
154 0 : else if (strcmp(name, "invalid-from") == 0)
155 0 : conn->stream_error->type = XMPP_SE_INVALID_FROM;
156 0 : else if (strcmp(name, "invalid-id") == 0)
157 0 : conn->stream_error->type = XMPP_SE_INVALID_ID;
158 0 : else if (strcmp(name, "invalid-namespace") == 0)
159 0 : conn->stream_error->type = XMPP_SE_INVALID_NS;
160 0 : else if (strcmp(name, "invalid-xml") == 0)
161 0 : conn->stream_error->type = XMPP_SE_INVALID_XML;
162 0 : else if (strcmp(name, "not-authorized") == 0)
163 0 : conn->stream_error->type = XMPP_SE_NOT_AUTHORIZED;
164 0 : else if (strcmp(name, "policy-violation") == 0)
165 0 : conn->stream_error->type = XMPP_SE_POLICY_VIOLATION;
166 0 : else if (strcmp(name, "remote-connection-failed") == 0)
167 0 : conn->stream_error->type = XMPP_SE_REMOTE_CONN_FAILED;
168 0 : else if (strcmp(name, "resource-constraint") == 0)
169 0 : conn->stream_error->type = XMPP_SE_RESOURCE_CONSTRAINT;
170 0 : else if (strcmp(name, "restricted-xml") == 0)
171 0 : conn->stream_error->type = XMPP_SE_RESTRICTED_XML;
172 0 : else if (strcmp(name, "see-other-host") == 0)
173 0 : conn->stream_error->type = XMPP_SE_SEE_OTHER_HOST;
174 0 : else if (strcmp(name, "system-shutdown") == 0)
175 0 : conn->stream_error->type = XMPP_SE_SYSTEM_SHUTDOWN;
176 0 : else if (strcmp(name, "undefined-condition") == 0)
177 0 : conn->stream_error->type = XMPP_SE_UNDEFINED_CONDITION;
178 0 : else if (strcmp(name, "unsupported-encoding") == 0)
179 0 : conn->stream_error->type = XMPP_SE_UNSUPPORTED_ENCODING;
180 0 : else if (strcmp(name, "unsupported-stanza-type") == 0)
181 0 : conn->stream_error->type = XMPP_SE_UNSUPPORTED_STANZA_TYPE;
182 0 : else if (strcmp(name, "unsupported-version") == 0)
183 0 : conn->stream_error->type = XMPP_SE_UNSUPPORTED_VERSION;
184 0 : else if (strcmp(name, "xml-not-well-formed") == 0)
185 0 : conn->stream_error->type = XMPP_SE_XML_NOT_WELL_FORMED;
186 : }
187 0 : } while ((child = xmpp_stanza_get_next(child)));
188 :
189 0 : conn->stream_error->stanza = xmpp_stanza_clone(stanza);
190 : }
191 :
192 0 : return 1;
193 : }
194 :
195 : /* stream:features handlers */
196 0 : static int _handle_missing_features(xmpp_conn_t *conn, void *userdata)
197 : {
198 0 : UNUSED(userdata);
199 :
200 0 : strophe_debug(conn->ctx, "xmpp", "didn't get stream features");
201 :
202 : /* legacy auth will be attempted */
203 0 : _auth(conn);
204 :
205 0 : return 0;
206 : }
207 :
208 : static int
209 0 : _handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
210 : {
211 0 : xmpp_stanza_t *child, *mech;
212 0 : const char *ns;
213 0 : char *text;
214 :
215 0 : UNUSED(userdata);
216 :
217 : /* remove the handler that detects missing stream:features */
218 0 : xmpp_timed_handler_delete(conn, _handle_missing_features);
219 :
220 : /* check for TLS */
221 0 : if (!conn->secured) {
222 0 : if (!conn->tls_disabled) {
223 0 : child = xmpp_stanza_get_child_by_name(stanza, "starttls");
224 0 : if (child) {
225 0 : ns = xmpp_stanza_get_ns(child);
226 0 : conn->tls_support = ns != NULL && strcmp(ns, XMPP_NS_TLS) == 0;
227 : }
228 : } else {
229 0 : conn->tls_support = 0;
230 : }
231 : }
232 :
233 : /* check for SASL */
234 0 : child = xmpp_stanza_get_child_by_name(stanza, "mechanisms");
235 0 : ns = child ? xmpp_stanza_get_ns(child) : NULL;
236 0 : if (child && ns && strcmp(ns, XMPP_NS_SASL) == 0) {
237 0 : for (mech = xmpp_stanza_get_children(child); mech;
238 0 : mech = xmpp_stanza_get_next(mech)) {
239 0 : if (xmpp_stanza_get_name(mech) &&
240 0 : strcmp(xmpp_stanza_get_name(mech), "mechanism") == 0) {
241 0 : text = xmpp_stanza_get_text(mech);
242 0 : if (text == NULL)
243 0 : continue;
244 :
245 0 : if (strcasecmp(text, "PLAIN") == 0)
246 0 : conn->sasl_support |= SASL_MASK_PLAIN;
247 0 : else if (strcasecmp(text, "EXTERNAL") == 0 &&
248 0 : (conn->tls_client_cert || conn->tls_client_key))
249 0 : conn->sasl_support |= SASL_MASK_EXTERNAL;
250 0 : else if (strcasecmp(text, "DIGEST-MD5") == 0)
251 0 : conn->sasl_support |= SASL_MASK_DIGESTMD5;
252 0 : else if (strcasecmp(text, "SCRAM-SHA-1") == 0)
253 0 : conn->sasl_support |= SASL_MASK_SCRAMSHA1;
254 0 : else if (strcasecmp(text, "SCRAM-SHA-256") == 0)
255 0 : conn->sasl_support |= SASL_MASK_SCRAMSHA256;
256 0 : else if (strcasecmp(text, "SCRAM-SHA-512") == 0)
257 0 : conn->sasl_support |= SASL_MASK_SCRAMSHA512;
258 0 : else if (strcasecmp(text, "ANONYMOUS") == 0)
259 0 : conn->sasl_support |= SASL_MASK_ANONYMOUS;
260 :
261 0 : strophe_free(conn->ctx, text);
262 : }
263 : }
264 : }
265 :
266 : /* Disable PLAIN when other secure mechanisms are supported */
267 0 : if (conn->sasl_support & ~(SASL_MASK_PLAIN | SASL_MASK_ANONYMOUS))
268 0 : conn->sasl_support &= ~SASL_MASK_PLAIN;
269 :
270 0 : _auth(conn);
271 :
272 0 : return 0;
273 : }
274 :
275 : /* returns the correct auth id for a component or a client.
276 : * returned string must be freed by caller */
277 0 : static char *_get_authid(xmpp_conn_t *conn)
278 : {
279 0 : char *authid = NULL;
280 :
281 0 : if (conn->type == XMPP_CLIENT) {
282 : /* authid is the node portion of jid */
283 0 : if (!conn->jid)
284 : return NULL;
285 0 : authid = xmpp_jid_node(conn->ctx, conn->jid);
286 : }
287 :
288 : return authid;
289 : }
290 :
291 0 : static int _handle_proceedtls_default(xmpp_conn_t *conn,
292 : xmpp_stanza_t *stanza,
293 : void *userdata)
294 : {
295 0 : const char *name;
296 :
297 0 : UNUSED(userdata);
298 :
299 0 : name = xmpp_stanza_get_name(stanza);
300 0 : strophe_debug(conn->ctx, "xmpp", "handle proceedtls called for %s", name);
301 :
302 0 : if (strcmp(name, "proceed") == 0) {
303 0 : strophe_debug(conn->ctx, "xmpp", "proceeding with TLS");
304 :
305 0 : if (conn_tls_start(conn) == 0) {
306 0 : conn_prepare_reset(conn, _handle_open_tls);
307 0 : conn_open_stream(conn);
308 : } else {
309 : /* failed tls spoils the connection, so disconnect */
310 0 : xmpp_disconnect(conn);
311 : }
312 : }
313 :
314 0 : return 0;
315 : }
316 :
317 : static int
318 0 : _handle_sasl_result(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
319 : {
320 0 : const char *name;
321 :
322 0 : name = xmpp_stanza_get_name(stanza);
323 :
324 : /* the server should send a <success> or <failure> stanza */
325 0 : if (strcmp(name, "failure") == 0) {
326 0 : strophe_debug(conn->ctx, "xmpp", "SASL %s auth failed",
327 : (char *)userdata);
328 :
329 : /* fall back to next auth method */
330 0 : _auth(conn);
331 0 : } else if (strcmp(name, "success") == 0) {
332 : /* SASL auth successful, we need to restart the stream */
333 0 : strophe_debug(conn->ctx, "xmpp", "SASL %s auth successful",
334 : (char *)userdata);
335 :
336 : /* reset parser */
337 0 : conn_prepare_reset(conn, _handle_open_sasl);
338 :
339 : /* send stream tag */
340 0 : conn_open_stream(conn);
341 : } else {
342 : /* got unexpected reply */
343 0 : strophe_error(conn->ctx, "xmpp",
344 : "Got unexpected reply to SASL %s authentication.",
345 : (char *)userdata);
346 0 : xmpp_disconnect(conn);
347 : }
348 :
349 0 : return 0;
350 : }
351 :
352 : /* handle the challenge phase of digest auth */
353 0 : static int _handle_digestmd5_challenge(xmpp_conn_t *conn,
354 : xmpp_stanza_t *stanza,
355 : void *userdata)
356 : {
357 0 : char *text;
358 0 : char *response;
359 0 : xmpp_stanza_t *auth, *authdata;
360 0 : const char *name;
361 :
362 0 : UNUSED(userdata);
363 :
364 0 : name = xmpp_stanza_get_name(stanza);
365 0 : strophe_debug(conn->ctx, "xmpp",
366 : "handle digest-md5 (challenge) called for %s", name);
367 :
368 0 : if (strcmp(name, "challenge") == 0) {
369 0 : text = xmpp_stanza_get_text(stanza);
370 0 : response = sasl_digest_md5(conn->ctx, text, conn->jid, conn->pass);
371 0 : if (!response) {
372 0 : disconnect_mem_error(conn);
373 0 : return 0;
374 : }
375 0 : strophe_free(conn->ctx, text);
376 :
377 0 : auth = xmpp_stanza_new(conn->ctx);
378 0 : if (!auth) {
379 0 : disconnect_mem_error(conn);
380 0 : return 0;
381 : }
382 0 : xmpp_stanza_set_name(auth, "response");
383 0 : xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
384 :
385 0 : authdata = xmpp_stanza_new(conn->ctx);
386 0 : if (!authdata) {
387 0 : disconnect_mem_error(conn);
388 0 : return 0;
389 : }
390 :
391 0 : xmpp_stanza_set_text(authdata, response);
392 0 : strophe_free(conn->ctx, response);
393 :
394 0 : xmpp_stanza_add_child_ex(auth, authdata, 0);
395 :
396 0 : handler_add(conn, _handle_digestmd5_rspauth, XMPP_NS_SASL, NULL, NULL,
397 : NULL);
398 :
399 0 : send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
400 :
401 : } else {
402 0 : return _handle_sasl_result(conn, stanza, "DIGEST-MD5");
403 : }
404 :
405 : /* remove ourselves */
406 0 : return 0;
407 : }
408 :
409 : /* handle the rspauth phase of digest auth */
410 0 : static int _handle_digestmd5_rspauth(xmpp_conn_t *conn,
411 : xmpp_stanza_t *stanza,
412 : void *userdata)
413 : {
414 0 : xmpp_stanza_t *auth;
415 0 : const char *name;
416 :
417 0 : UNUSED(userdata);
418 :
419 0 : name = xmpp_stanza_get_name(stanza);
420 0 : strophe_debug(conn->ctx, "xmpp",
421 : "handle digest-md5 (rspauth) called for %s", name);
422 :
423 0 : if (strcmp(name, "challenge") == 0) {
424 : /* assume it's an rspauth response */
425 0 : auth = xmpp_stanza_new(conn->ctx);
426 0 : if (!auth) {
427 0 : disconnect_mem_error(conn);
428 0 : return 0;
429 : }
430 0 : xmpp_stanza_set_name(auth, "response");
431 0 : xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
432 0 : send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
433 : } else {
434 0 : return _handle_sasl_result(conn, stanza, "DIGEST-MD5");
435 : }
436 :
437 0 : return 1;
438 : }
439 :
440 : struct scram_user_data {
441 : char *scram_init;
442 : const struct hash_alg *alg;
443 : };
444 :
445 : /* handle the challenge phase of SCRAM-SHA-1 auth */
446 0 : static int _handle_scram_challenge(xmpp_conn_t *conn,
447 : xmpp_stanza_t *stanza,
448 : void *userdata)
449 : {
450 0 : char *text;
451 0 : char *response;
452 0 : xmpp_stanza_t *auth;
453 0 : xmpp_stanza_t *authdata;
454 0 : const char *name;
455 0 : char *challenge;
456 0 : struct scram_user_data *scram_ctx = (struct scram_user_data *)userdata;
457 0 : int rc;
458 :
459 0 : name = xmpp_stanza_get_name(stanza);
460 0 : strophe_debug(conn->ctx, "xmpp", "handle %s (challenge) called for %s",
461 0 : scram_ctx->alg->scram_name, name);
462 :
463 0 : if (strcmp(name, "challenge") == 0) {
464 0 : text = xmpp_stanza_get_text(stanza);
465 0 : if (!text)
466 0 : goto err;
467 :
468 0 : challenge = xmpp_base64_decode_str(conn->ctx, text, strlen(text));
469 0 : strophe_free(conn->ctx, text);
470 0 : if (!challenge)
471 0 : goto err;
472 :
473 0 : response = sasl_scram(conn->ctx, scram_ctx->alg, challenge,
474 0 : scram_ctx->scram_init, conn->jid, conn->pass);
475 0 : strophe_free(conn->ctx, challenge);
476 0 : if (!response)
477 0 : goto err;
478 :
479 0 : auth = xmpp_stanza_new(conn->ctx);
480 0 : if (!auth)
481 0 : goto err_free_response;
482 0 : xmpp_stanza_set_name(auth, "response");
483 0 : xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
484 :
485 0 : authdata = xmpp_stanza_new(conn->ctx);
486 0 : if (!authdata)
487 0 : goto err_release_auth;
488 0 : xmpp_stanza_set_text(authdata, response);
489 0 : strophe_free(conn->ctx, response);
490 :
491 0 : xmpp_stanza_add_child_ex(auth, authdata, 0);
492 :
493 0 : send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
494 :
495 0 : rc = 1; /* Keep handler */
496 : } else {
497 : /*
498 : * Free scram_ctx after calling _handle_sasl_result(). If authentication
499 : * fails, we want to try other mechanism which may be different SCRAM
500 : * mechanism. If we freed scram_ctx before the function, _auth() would
501 : * be able to allocate new scram_ctx object with the same address and
502 : * handler_add() would consider new SCRAM handler as duplicate, because
503 : * current handler is not removed yet. As result, libstrophe wouldn't
504 : * handle incoming challenge stanza.
505 : */
506 0 : rc = _handle_sasl_result(conn, stanza,
507 0 : (void *)scram_ctx->alg->scram_name);
508 0 : strophe_free(conn->ctx, scram_ctx->scram_init);
509 0 : strophe_free(conn->ctx, scram_ctx);
510 : }
511 :
512 : return rc;
513 :
514 0 : err_release_auth:
515 0 : xmpp_stanza_release(auth);
516 0 : err_free_response:
517 0 : strophe_free(conn->ctx, response);
518 0 : err:
519 0 : strophe_free(conn->ctx, scram_ctx->scram_init);
520 0 : strophe_free(conn->ctx, scram_ctx);
521 0 : disconnect_mem_error(conn);
522 0 : return 0;
523 : }
524 :
525 0 : static char *_make_scram_init_msg(xmpp_conn_t *conn)
526 : {
527 0 : xmpp_ctx_t *ctx = conn->ctx;
528 0 : size_t message_len;
529 0 : char *node;
530 0 : char *message;
531 0 : char nonce[32];
532 :
533 0 : node = xmpp_jid_node(ctx, conn->jid);
534 0 : if (!node) {
535 0 : return NULL;
536 : }
537 0 : xmpp_rand_nonce(ctx->rand, nonce, sizeof(nonce));
538 0 : message_len = strlen(node) + strlen(nonce) + 8 + 1;
539 0 : message = strophe_alloc(ctx, message_len);
540 0 : if (message) {
541 0 : strophe_snprintf(message, message_len, "n,,n=%s,r=%s", node, nonce);
542 : }
543 0 : strophe_free(ctx, node);
544 :
545 : return message;
546 : }
547 :
548 0 : static xmpp_stanza_t *_make_starttls(xmpp_conn_t *conn)
549 : {
550 0 : xmpp_stanza_t *starttls;
551 :
552 : /* build start stanza */
553 0 : starttls = xmpp_stanza_new(conn->ctx);
554 0 : if (starttls) {
555 0 : xmpp_stanza_set_name(starttls, "starttls");
556 0 : xmpp_stanza_set_ns(starttls, XMPP_NS_TLS);
557 : }
558 :
559 0 : return starttls;
560 : }
561 :
562 0 : static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t *conn, const char *mechanism)
563 : {
564 0 : xmpp_stanza_t *auth;
565 :
566 : /* build auth stanza */
567 0 : auth = xmpp_stanza_new(conn->ctx);
568 0 : if (auth) {
569 0 : xmpp_stanza_set_name(auth, "auth");
570 0 : xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
571 0 : xmpp_stanza_set_attribute(auth, "mechanism", mechanism);
572 : }
573 :
574 0 : return auth;
575 : }
576 :
577 : /* authenticate the connection
578 : * this may get called multiple times. if any auth method fails,
579 : * this will get called again until one auth method succeeds or every
580 : * method fails
581 : */
582 0 : static void _auth(xmpp_conn_t *conn)
583 : {
584 0 : xmpp_stanza_t *auth;
585 0 : xmpp_stanza_t *authdata;
586 0 : struct scram_user_data *scram_ctx;
587 0 : char *authid;
588 0 : char *str;
589 0 : int anonjid;
590 :
591 : /* if there is no node in conn->jid, we assume anonymous connect */
592 0 : str = xmpp_jid_node(conn->ctx, conn->jid);
593 0 : if (str == NULL) {
594 : anonjid = 1;
595 : } else {
596 0 : strophe_free(conn->ctx, str);
597 0 : anonjid = 0;
598 : }
599 :
600 0 : if (conn->tls_support) {
601 0 : tls_t *tls = tls_new(conn);
602 :
603 : /* If we couldn't init tls, it isn't there, so go on */
604 0 : if (!tls) {
605 0 : conn->tls_support = 0;
606 0 : _auth(conn);
607 0 : return;
608 : } else {
609 0 : tls_free(tls);
610 : }
611 :
612 0 : auth = _make_starttls(conn);
613 :
614 0 : if (!auth) {
615 0 : disconnect_mem_error(conn);
616 0 : return;
617 : }
618 :
619 0 : handler_add(conn, _handle_proceedtls_default, XMPP_NS_TLS, NULL, NULL,
620 : NULL);
621 :
622 0 : send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
623 :
624 : /* TLS was tried, unset flag */
625 0 : conn->tls_support = 0;
626 : /* _auth() will be called later */
627 0 : return;
628 : }
629 :
630 0 : if (conn->tls_mandatory && !xmpp_conn_is_secured(conn)) {
631 0 : strophe_error(conn->ctx, "xmpp",
632 : "TLS is not supported, but set as "
633 : "mandatory for this connection");
634 0 : conn_disconnect(conn);
635 0 : return;
636 : }
637 :
638 0 : if (anonjid && conn->sasl_support & SASL_MASK_ANONYMOUS) {
639 : /* some crap here */
640 0 : auth = _make_sasl_auth(conn, "ANONYMOUS");
641 0 : if (!auth) {
642 0 : disconnect_mem_error(conn);
643 0 : return;
644 : }
645 :
646 0 : handler_add(conn, _handle_sasl_result, XMPP_NS_SASL, NULL, NULL,
647 : "ANONYMOUS");
648 :
649 0 : send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
650 :
651 : /* SASL ANONYMOUS was tried, unset flag */
652 0 : conn->sasl_support &= ~SASL_MASK_ANONYMOUS;
653 0 : } else if (conn->sasl_support & SASL_MASK_EXTERNAL) {
654 : /* more crap here */
655 0 : auth = _make_sasl_auth(conn, "EXTERNAL");
656 0 : if (!auth) {
657 0 : disconnect_mem_error(conn);
658 0 : return;
659 : }
660 :
661 0 : authdata = xmpp_stanza_new(conn->ctx);
662 0 : if (!authdata) {
663 0 : xmpp_stanza_release(auth);
664 0 : disconnect_mem_error(conn);
665 0 : return;
666 : }
667 0 : str = tls_id_on_xmppaddr(conn, 0);
668 0 : if (!str || (tls_id_on_xmppaddr_num(conn) == 1 &&
669 0 : strcmp(str, conn->jid) == 0)) {
670 0 : xmpp_stanza_set_text(authdata, "=");
671 : } else {
672 0 : strophe_free(conn->ctx, str);
673 0 : str = xmpp_base64_encode(conn->ctx, (void *)conn->jid,
674 0 : strlen(conn->jid));
675 0 : if (!str) {
676 0 : xmpp_stanza_release(authdata);
677 0 : xmpp_stanza_release(auth);
678 0 : disconnect_mem_error(conn);
679 0 : return;
680 : }
681 0 : xmpp_stanza_set_text(authdata, str);
682 : }
683 0 : strophe_free(conn->ctx, str);
684 :
685 0 : xmpp_stanza_add_child_ex(auth, authdata, 0);
686 :
687 0 : handler_add(conn, _handle_sasl_result, XMPP_NS_SASL, NULL, NULL,
688 : "EXTERNAL");
689 :
690 0 : send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
691 :
692 : /* SASL EXTERNAL was tried, unset flag */
693 0 : conn->sasl_support &= ~SASL_MASK_EXTERNAL;
694 0 : } else if (anonjid) {
695 0 : strophe_error(conn->ctx, "auth",
696 : "No node in JID, and SASL ANONYMOUS unsupported.");
697 0 : xmpp_disconnect(conn);
698 0 : } else if (conn->pass == NULL) {
699 0 : strophe_error(
700 0 : conn->ctx, "auth",
701 : "Password hasn't been set, and SASL ANONYMOUS unsupported.");
702 0 : xmpp_disconnect(conn);
703 0 : } else if (conn->sasl_support & SASL_MASK_SCRAM) {
704 0 : scram_ctx = strophe_alloc(conn->ctx, sizeof(*scram_ctx));
705 0 : if (conn->sasl_support & SASL_MASK_SCRAMSHA512)
706 0 : scram_ctx->alg = &scram_sha512;
707 0 : else if (conn->sasl_support & SASL_MASK_SCRAMSHA256)
708 0 : scram_ctx->alg = &scram_sha256;
709 0 : else if (conn->sasl_support & SASL_MASK_SCRAMSHA1)
710 0 : scram_ctx->alg = &scram_sha1;
711 0 : auth = _make_sasl_auth(conn, scram_ctx->alg->scram_name);
712 0 : if (!auth) {
713 0 : disconnect_mem_error(conn);
714 0 : return;
715 : }
716 :
717 : /* don't free scram_init on success */
718 0 : scram_ctx->scram_init = _make_scram_init_msg(conn);
719 0 : if (!scram_ctx->scram_init) {
720 0 : strophe_free(conn->ctx, scram_ctx);
721 0 : xmpp_stanza_release(auth);
722 0 : disconnect_mem_error(conn);
723 0 : return;
724 : }
725 :
726 0 : str = xmpp_base64_encode(conn->ctx,
727 : (unsigned char *)scram_ctx->scram_init,
728 : strlen(scram_ctx->scram_init));
729 0 : if (!str) {
730 0 : strophe_free(conn->ctx, scram_ctx->scram_init);
731 0 : strophe_free(conn->ctx, scram_ctx);
732 0 : xmpp_stanza_release(auth);
733 0 : disconnect_mem_error(conn);
734 0 : return;
735 : }
736 :
737 0 : authdata = xmpp_stanza_new(conn->ctx);
738 0 : if (!authdata) {
739 0 : strophe_free(conn->ctx, str);
740 0 : strophe_free(conn->ctx, scram_ctx->scram_init);
741 0 : strophe_free(conn->ctx, scram_ctx);
742 0 : xmpp_stanza_release(auth);
743 0 : disconnect_mem_error(conn);
744 0 : return;
745 : }
746 0 : xmpp_stanza_set_text(authdata, str);
747 0 : strophe_free(conn->ctx, str);
748 0 : xmpp_stanza_add_child_ex(auth, authdata, 0);
749 :
750 0 : handler_add(conn, _handle_scram_challenge, XMPP_NS_SASL, NULL, NULL,
751 : (void *)scram_ctx);
752 :
753 0 : send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
754 :
755 : /* SASL SCRAM-SHA-1 was tried, unset flag */
756 0 : conn->sasl_support &= ~scram_ctx->alg->mask;
757 0 : } else if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
758 0 : auth = _make_sasl_auth(conn, "DIGEST-MD5");
759 0 : if (!auth) {
760 0 : disconnect_mem_error(conn);
761 0 : return;
762 : }
763 :
764 0 : handler_add(conn, _handle_digestmd5_challenge, XMPP_NS_SASL, NULL, NULL,
765 : NULL);
766 :
767 0 : send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
768 :
769 : /* SASL DIGEST-MD5 was tried, unset flag */
770 0 : conn->sasl_support &= ~SASL_MASK_DIGESTMD5;
771 0 : } else if (conn->sasl_support & SASL_MASK_PLAIN) {
772 0 : auth = _make_sasl_auth(conn, "PLAIN");
773 0 : if (!auth) {
774 0 : disconnect_mem_error(conn);
775 0 : return;
776 : }
777 0 : authdata = xmpp_stanza_new(conn->ctx);
778 0 : if (!authdata) {
779 0 : disconnect_mem_error(conn);
780 0 : return;
781 : }
782 0 : authid = _get_authid(conn);
783 0 : if (!authid) {
784 0 : disconnect_mem_error(conn);
785 0 : return;
786 : }
787 0 : str = sasl_plain(conn->ctx, authid, conn->pass);
788 0 : if (!str) {
789 0 : disconnect_mem_error(conn);
790 0 : return;
791 : }
792 0 : xmpp_stanza_set_text(authdata, str);
793 0 : strophe_free(conn->ctx, str);
794 0 : strophe_free(conn->ctx, authid);
795 :
796 0 : xmpp_stanza_add_child_ex(auth, authdata, 0);
797 :
798 0 : handler_add(conn, _handle_sasl_result, XMPP_NS_SASL, NULL, NULL,
799 : "PLAIN");
800 :
801 0 : send_stanza(conn, auth, XMPP_QUEUE_STROPHE);
802 :
803 : /* SASL PLAIN was tried */
804 0 : conn->sasl_support &= ~SASL_MASK_PLAIN;
805 0 : } else if (conn->type == XMPP_CLIENT && conn->auth_legacy_enabled) {
806 : /* legacy client authentication */
807 0 : _auth_legacy(conn);
808 : } else {
809 0 : strophe_error(conn->ctx, "auth",
810 : "Cannot authenticate with known methods");
811 0 : xmpp_disconnect(conn);
812 : }
813 : }
814 :
815 0 : static void _auth_success(xmpp_conn_t *conn)
816 : {
817 0 : tls_clear_password_cache(conn);
818 0 : conn->authenticated = 1;
819 : /* call connection handler */
820 0 : conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
821 0 : }
822 :
823 : /** Set up handlers at stream start.
824 : * This function is called internally to Strophe for handling the opening
825 : * of an XMPP stream. It's called by the parser when a stream is opened
826 : * or reset, and adds the initial handlers for <stream:error/> and
827 : * <stream:features/>. This function is not intended for use outside
828 : * of Strophe.
829 : *
830 : * @param conn a Strophe connection object
831 : */
832 0 : void auth_handle_open(xmpp_conn_t *conn)
833 : {
834 : /* reset all timed handlers */
835 0 : handler_reset_timed(conn, 0);
836 :
837 : /* setup handler for stream:error, we will keep this handler
838 : * for reopened streams until connection is disconnected */
839 0 : handler_add(conn, _handle_error, XMPP_NS_STREAMS, "error", NULL, NULL);
840 :
841 : /* setup handlers for incoming <stream:features> */
842 0 : handler_add(conn, _handle_features, XMPP_NS_STREAMS, "features", NULL,
843 : NULL);
844 0 : handler_add_timed(conn, _handle_missing_features, FEATURES_TIMEOUT, NULL);
845 0 : }
846 :
847 : /* called when stream:stream tag received after TLS establishment */
848 0 : static void _handle_open_tls(xmpp_conn_t *conn)
849 : {
850 : /* setup handlers for incoming <stream:features> */
851 0 : handler_add(conn, _handle_features, XMPP_NS_STREAMS, "features", NULL,
852 : NULL);
853 0 : handler_add_timed(conn, _handle_missing_features, FEATURES_TIMEOUT, NULL);
854 0 : }
855 :
856 : /* called when stream:stream tag received after SASL auth */
857 0 : static void _handle_open_sasl(xmpp_conn_t *conn)
858 : {
859 0 : strophe_debug(conn->ctx, "xmpp", "Reopened stream successfully.");
860 :
861 : /* setup stream:features handlers */
862 0 : handler_add(conn, _handle_features_sasl, XMPP_NS_STREAMS, "features", NULL,
863 : NULL);
864 0 : handler_add_timed(conn, _handle_missing_features_sasl, FEATURES_TIMEOUT,
865 : NULL);
866 0 : }
867 :
868 0 : static int _do_bind(xmpp_conn_t *conn, xmpp_stanza_t *bind)
869 : {
870 0 : xmpp_stanza_t *iq, *res, *text;
871 0 : char *resource;
872 :
873 : /* setup response handlers */
874 0 : handler_add_id(conn, _handle_bind, "_xmpp_bind1", NULL);
875 0 : handler_add_timed(conn, _handle_missing_bind, BIND_TIMEOUT, NULL);
876 :
877 : /* send bind request */
878 0 : iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_bind1");
879 0 : if (!iq) {
880 0 : disconnect_mem_error(conn);
881 0 : return 0;
882 : }
883 :
884 : /* request a specific resource if we have one */
885 0 : resource = xmpp_jid_resource(conn->ctx, conn->jid);
886 0 : if ((resource != NULL) && (strlen(resource) == 0)) {
887 : /* jabberd2 doesn't handle an empty resource */
888 0 : strophe_free(conn->ctx, resource);
889 0 : resource = NULL;
890 : }
891 :
892 : /* if we have a resource to request, do it. otherwise the
893 : server will assign us one */
894 0 : if (resource) {
895 0 : res = xmpp_stanza_new(conn->ctx);
896 0 : if (!res) {
897 0 : xmpp_stanza_release(bind);
898 0 : xmpp_stanza_release(iq);
899 0 : disconnect_mem_error(conn);
900 0 : return 0;
901 : }
902 0 : xmpp_stanza_set_name(res, "resource");
903 0 : text = xmpp_stanza_new(conn->ctx);
904 0 : if (!text) {
905 0 : xmpp_stanza_release(res);
906 0 : xmpp_stanza_release(bind);
907 0 : xmpp_stanza_release(iq);
908 0 : disconnect_mem_error(conn);
909 0 : return 0;
910 : }
911 0 : xmpp_stanza_set_text(text, resource);
912 0 : xmpp_stanza_add_child_ex(res, text, 0);
913 0 : xmpp_stanza_add_child_ex(bind, res, 0);
914 0 : strophe_free(conn->ctx, resource);
915 : }
916 :
917 0 : xmpp_stanza_add_child_ex(iq, bind, 0);
918 :
919 : /* send bind request */
920 0 : send_stanza(conn, iq, XMPP_QUEUE_STROPHE);
921 0 : return 0;
922 : }
923 :
924 : static int
925 0 : _handle_features_sasl(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
926 : {
927 0 : xmpp_stanza_t *bind, *session, *opt;
928 0 : xmpp_stanza_t *resume;
929 0 : const char *ns;
930 0 : char h[11];
931 :
932 0 : UNUSED(userdata);
933 :
934 : /* remove missing features handler */
935 0 : xmpp_timed_handler_delete(conn, _handle_missing_features_sasl);
936 :
937 : /* check whether resource binding is required */
938 0 : bind = xmpp_stanza_get_child_by_name(stanza, "bind");
939 0 : if (bind) {
940 0 : ns = xmpp_stanza_get_ns(bind);
941 0 : conn->bind_required = ns != NULL && strcmp(ns, XMPP_NS_BIND) == 0;
942 0 : bind = xmpp_stanza_copy(bind);
943 0 : if (!bind) {
944 0 : disconnect_mem_error(conn);
945 0 : return 0;
946 : }
947 : } else {
948 0 : conn->bind_required = 0;
949 : }
950 :
951 : /* check whether session establishment is required */
952 0 : session = xmpp_stanza_get_child_by_name(stanza, "session");
953 0 : if (session) {
954 0 : ns = xmpp_stanza_get_ns(session);
955 0 : opt = xmpp_stanza_get_child_by_name(session, "optional");
956 0 : if (!opt)
957 0 : conn->session_required =
958 0 : ns != NULL && strcmp(ns, XMPP_NS_SESSION) == 0;
959 : }
960 :
961 0 : if (xmpp_stanza_get_child_by_name_and_ns(stanza, "sm", XMPP_NS_SM)) {
962 : /* stream management supported */
963 0 : conn->sm_state->sm_support = 1;
964 : }
965 :
966 : /* we are expecting either <bind/> and <session/> since this is a
967 : XMPP style connection or we <resume/> the previous session */
968 :
969 : /* check whether we can <resume/> the previous session */
970 0 : if (!conn->sm_disable && conn->sm_state->can_resume &&
971 0 : conn->sm_state->previd && conn->sm_state->bound_jid) {
972 0 : resume = xmpp_stanza_new(conn->ctx);
973 0 : if (!resume) {
974 0 : disconnect_mem_error(conn);
975 0 : return 0;
976 : }
977 0 : conn->sm_state->bind = bind;
978 0 : conn->sm_state->resume = 1;
979 0 : xmpp_stanza_set_name(resume, "resume");
980 0 : xmpp_stanza_set_ns(resume, XMPP_NS_SM);
981 0 : xmpp_stanza_set_attribute(resume, "previd", conn->sm_state->previd);
982 0 : strophe_snprintf(h, sizeof(h), "%u", conn->sm_state->sm_handled_nr);
983 0 : xmpp_stanza_set_attribute(resume, "h", h);
984 0 : send_stanza(conn, resume, XMPP_QUEUE_SM_STROPHE);
985 0 : handler_add(conn, _handle_sm, XMPP_NS_SM, NULL, NULL, NULL);
986 : }
987 : /* if bind is required, go ahead and start it */
988 0 : else if (conn->bind_required) {
989 : /* bind resource */
990 0 : _do_bind(conn, bind);
991 : } else {
992 : /* can't bind, disconnect */
993 0 : if (bind) {
994 0 : xmpp_stanza_release(bind);
995 : }
996 0 : strophe_error(conn->ctx, "xmpp",
997 : "Stream features does not allow "
998 : "resource bind.");
999 0 : xmpp_disconnect(conn);
1000 : }
1001 :
1002 : return 0;
1003 : }
1004 :
1005 0 : static int _handle_missing_features_sasl(xmpp_conn_t *conn, void *userdata)
1006 : {
1007 0 : UNUSED(userdata);
1008 :
1009 0 : strophe_error(conn->ctx, "xmpp",
1010 : "Did not receive stream features "
1011 : "after SASL authentication.");
1012 0 : xmpp_disconnect(conn);
1013 0 : return 0;
1014 : }
1015 :
1016 : static int
1017 0 : _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
1018 : {
1019 0 : const char *type;
1020 0 : xmpp_stanza_t *iq, *enable, *session, *binding, *jid_stanza;
1021 :
1022 0 : UNUSED(userdata);
1023 :
1024 : /* delete missing bind handler */
1025 0 : xmpp_timed_handler_delete(conn, _handle_missing_bind);
1026 :
1027 : /* server has replied to bind request */
1028 0 : type = xmpp_stanza_get_type(stanza);
1029 0 : if (type && strcmp(type, "error") == 0) {
1030 0 : strophe_error(conn->ctx, "xmpp", "Binding failed.");
1031 0 : xmpp_disconnect(conn);
1032 0 : } else if (type && strcmp(type, "result") == 0) {
1033 0 : binding = xmpp_stanza_get_child_by_name(stanza, "bind");
1034 0 : strophe_debug(conn->ctx, "xmpp", "Bind successful.");
1035 :
1036 0 : if (binding) {
1037 0 : jid_stanza = xmpp_stanza_get_child_by_name(binding, "jid");
1038 0 : if (jid_stanza) {
1039 0 : conn->bound_jid = xmpp_stanza_get_text(jid_stanza);
1040 : }
1041 : }
1042 :
1043 : /* establish a session if required */
1044 0 : if (conn->session_required) {
1045 : /* setup response handlers */
1046 0 : handler_add_id(conn, _handle_session, "_xmpp_session1", NULL);
1047 0 : handler_add_timed(conn, _handle_missing_session, SESSION_TIMEOUT,
1048 : NULL);
1049 :
1050 : /* send session request */
1051 0 : iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_session1");
1052 0 : if (!iq) {
1053 0 : disconnect_mem_error(conn);
1054 0 : return 0;
1055 : }
1056 :
1057 0 : session = xmpp_stanza_new(conn->ctx);
1058 0 : if (!session) {
1059 0 : xmpp_stanza_release(iq);
1060 0 : disconnect_mem_error(conn);
1061 0 : return 0;
1062 : }
1063 :
1064 0 : xmpp_stanza_set_name(session, "session");
1065 0 : xmpp_stanza_set_ns(session, XMPP_NS_SESSION);
1066 :
1067 0 : xmpp_stanza_add_child_ex(iq, session, 0);
1068 :
1069 : /* send session establishment request */
1070 0 : send_stanza(conn, iq, XMPP_QUEUE_STROPHE);
1071 : }
1072 :
1073 0 : if (conn->sm_state->sm_support && !conn->sm_disable) {
1074 0 : enable = xmpp_stanza_new(conn->ctx);
1075 0 : if (!enable) {
1076 0 : disconnect_mem_error(conn);
1077 0 : return 0;
1078 : }
1079 0 : xmpp_stanza_set_name(enable, "enable");
1080 0 : xmpp_stanza_set_ns(enable, XMPP_NS_SM);
1081 0 : if (!conn->sm_state->dont_request_resume)
1082 0 : xmpp_stanza_set_attribute(enable, "resume", "true");
1083 0 : handler_add(conn, _handle_sm, XMPP_NS_SM, NULL, NULL, NULL);
1084 0 : send_stanza(conn, enable, XMPP_QUEUE_SM_STROPHE);
1085 : }
1086 :
1087 0 : if (!conn->session_required) {
1088 0 : _auth_success(conn);
1089 : }
1090 : } else {
1091 0 : strophe_error(conn->ctx, "xmpp", "Server sent malformed bind reply.");
1092 0 : xmpp_disconnect(conn);
1093 : }
1094 :
1095 : return 0;
1096 : }
1097 :
1098 0 : static int _handle_missing_bind(xmpp_conn_t *conn, void *userdata)
1099 : {
1100 0 : UNUSED(userdata);
1101 :
1102 0 : strophe_error(conn->ctx, "xmpp", "Server did not reply to bind request.");
1103 0 : xmpp_disconnect(conn);
1104 0 : return 0;
1105 : }
1106 :
1107 : static int
1108 0 : _handle_session(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
1109 : {
1110 0 : const char *type;
1111 :
1112 0 : UNUSED(userdata);
1113 :
1114 : /* delete missing session handler */
1115 0 : xmpp_timed_handler_delete(conn, _handle_missing_session);
1116 :
1117 : /* server has replied to the session request */
1118 0 : type = xmpp_stanza_get_type(stanza);
1119 0 : if (type && strcmp(type, "error") == 0) {
1120 0 : strophe_error(conn->ctx, "xmpp", "Session establishment failed.");
1121 0 : xmpp_disconnect(conn);
1122 0 : } else if (type && strcmp(type, "result") == 0) {
1123 0 : strophe_debug(conn->ctx, "xmpp", "Session establishment successful.");
1124 :
1125 0 : _auth_success(conn);
1126 : } else {
1127 0 : strophe_error(conn->ctx, "xmpp",
1128 : "Server sent malformed session reply.");
1129 0 : xmpp_disconnect(conn);
1130 : }
1131 :
1132 0 : return 0;
1133 : }
1134 :
1135 0 : static int _handle_missing_session(xmpp_conn_t *conn, void *userdata)
1136 : {
1137 0 : UNUSED(userdata);
1138 :
1139 0 : strophe_error(conn->ctx, "xmpp",
1140 : "Server did not reply to session request.");
1141 0 : xmpp_disconnect(conn);
1142 0 : return 0;
1143 : }
1144 :
1145 0 : static int _handle_missing_legacy(xmpp_conn_t *conn, void *userdata)
1146 : {
1147 0 : UNUSED(userdata);
1148 :
1149 0 : strophe_error(conn->ctx, "xmpp",
1150 : "Server did not reply to legacy "
1151 : "authentication request.");
1152 0 : xmpp_disconnect(conn);
1153 0 : return 0;
1154 : }
1155 :
1156 0 : static int _handle_sm(xmpp_conn_t *const conn,
1157 : xmpp_stanza_t *const stanza,
1158 : void *const userdata)
1159 : {
1160 0 : xmpp_stanza_t *failed_cause;
1161 0 : const char *name, *id, *previd, *resume, *h, *cause;
1162 0 : xmpp_send_queue_t *e;
1163 0 : unsigned long ul_h = 0;
1164 :
1165 0 : UNUSED(userdata);
1166 :
1167 0 : name = xmpp_stanza_get_name(stanza);
1168 0 : if (!name)
1169 0 : goto LBL_ERR;
1170 :
1171 0 : if (strcmp(name, "enabled") == 0) {
1172 0 : conn->sm_state->sm_enabled = 1;
1173 0 : conn->sm_state->sm_handled_nr = 0;
1174 0 : resume = xmpp_stanza_get_attribute(stanza, "resume");
1175 0 : if (resume && (strcasecmp(resume, "true") || strcmp(resume, "1"))) {
1176 0 : id = xmpp_stanza_get_attribute(stanza, "id");
1177 0 : if (!id) {
1178 0 : strophe_error(conn->ctx, "xmpp",
1179 : "SM error: server said it can resume, but "
1180 : "didn't provide an ID.");
1181 0 : name = NULL;
1182 0 : goto LBL_ERR;
1183 : }
1184 0 : conn->sm_state->can_resume = 1;
1185 0 : conn->sm_state->id = strophe_strdup(conn->ctx, id);
1186 : }
1187 0 : } else if (strcmp(name, "resumed") == 0) {
1188 0 : previd = xmpp_stanza_get_attribute(stanza, "previd");
1189 0 : if (!previd || strcmp(previd, conn->sm_state->previd)) {
1190 0 : strophe_error(conn->ctx, "xmpp",
1191 : "SM error: previd didn't match, ours is \"%s\".",
1192 0 : conn->sm_state->previd);
1193 0 : name = NULL;
1194 0 : goto LBL_ERR;
1195 : }
1196 0 : h = xmpp_stanza_get_attribute(stanza, "h");
1197 0 : if (!h || string_to_ul(h, &ul_h)) {
1198 0 : strophe_error(conn->ctx, "xmpp",
1199 : "SM error: failed parsing 'h', it got converted "
1200 : "to %llu.",
1201 : ul_h);
1202 0 : name = NULL;
1203 0 : goto LBL_ERR;
1204 : }
1205 0 : conn->sm_state->sm_enabled = 1;
1206 0 : conn->sm_state->id = conn->sm_state->previd;
1207 0 : conn->sm_state->previd = NULL;
1208 0 : conn->bound_jid = conn->sm_state->bound_jid;
1209 0 : conn->sm_state->bound_jid = NULL;
1210 0 : if (conn->sm_state->sm_queue.head)
1211 0 : conn->sm_state->sm_sent_nr = conn->sm_state->sm_queue.head->sm_h;
1212 : else
1213 0 : conn->sm_state->sm_sent_nr = ul_h;
1214 0 : while ((e = pop_queue_front(&conn->sm_state->sm_queue))) {
1215 0 : if (e->sm_h >= ul_h) {
1216 : /* Re-send what was already sent out and is still in the
1217 : * SM queue (i.e. it hasn't been ACK'ed by the server)
1218 : */
1219 0 : send_raw(conn, e->data, e->len, e->owner, NULL);
1220 : }
1221 0 : strophe_free(conn->ctx, queue_element_free(conn->ctx, e));
1222 : }
1223 0 : strophe_debug(conn->ctx, "xmpp", "Session resumed successfully.");
1224 0 : _auth_success(conn);
1225 0 : } else if (strcmp(name, "failed") == 0) {
1226 0 : name = NULL;
1227 :
1228 0 : failed_cause =
1229 0 : xmpp_stanza_get_child_by_ns(stanza, XMPP_NS_STANZAS_IETF);
1230 0 : if (!failed_cause)
1231 0 : goto LBL_ERR;
1232 :
1233 0 : cause = xmpp_stanza_get_name(failed_cause);
1234 0 : if (!cause)
1235 0 : goto LBL_ERR;
1236 :
1237 0 : if (!strcmp(cause, "item-not-found") ||
1238 0 : !strcmp(cause, "feature-not-implemented")) {
1239 0 : if (conn->sm_state->resume) {
1240 0 : conn->sm_state->resume = 0;
1241 0 : conn->sm_state->can_resume = 0;
1242 : /* remember that the server reports having support
1243 : * for resumption, but actually it doesn't ...
1244 : */
1245 0 : conn->sm_state->dont_request_resume =
1246 0 : !strcmp(cause, "feature-not-implemented");
1247 0 : strophe_free(conn->ctx, conn->sm_state->previd);
1248 0 : conn->sm_state->previd = NULL;
1249 0 : strophe_free(conn->ctx, conn->sm_state->bound_jid);
1250 0 : conn->sm_state->bound_jid = NULL;
1251 0 : _do_bind(conn, conn->sm_state->bind);
1252 0 : conn->sm_state->bind = NULL;
1253 : }
1254 : }
1255 0 : conn->sm_state->sm_handled_nr = 0;
1256 : } else {
1257 : /* unknown stanza received */
1258 : name = NULL;
1259 : }
1260 :
1261 : LBL_ERR:
1262 0 : if (!name) {
1263 0 : char *err = "Couldn't convert stanza to text!";
1264 0 : char *buf;
1265 0 : size_t buflen;
1266 0 : switch (xmpp_stanza_to_text(stanza, &buf, &buflen)) {
1267 : case XMPP_EOK:
1268 : break;
1269 0 : case XMPP_EMEM:
1270 0 : disconnect_mem_error(conn);
1271 0 : return 0;
1272 0 : default:
1273 0 : buf = err;
1274 0 : break;
1275 : }
1276 0 : strophe_warn(conn->ctx, "xmpp", "SM error: Stanza received was: %s",
1277 : buf);
1278 0 : if (buf != err)
1279 0 : strophe_free(conn->ctx, buf);
1280 0 : conn->sm_state->sm_enabled = 0;
1281 : }
1282 : return 0;
1283 : }
1284 :
1285 : static int
1286 0 : _handle_legacy(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
1287 : {
1288 0 : const char *type;
1289 0 : const char *name;
1290 :
1291 0 : UNUSED(userdata);
1292 :
1293 : /* delete missing handler */
1294 0 : xmpp_timed_handler_delete(conn, _handle_missing_legacy);
1295 :
1296 : /* server responded to legacy auth request */
1297 0 : type = xmpp_stanza_get_type(stanza);
1298 0 : name = xmpp_stanza_get_name(stanza);
1299 0 : if (!type || strcmp(name, "iq") != 0) {
1300 0 : strophe_error(conn->ctx, "xmpp",
1301 : "Server sent us an unexpected response "
1302 : "to legacy authentication request.");
1303 0 : xmpp_disconnect(conn);
1304 0 : } else if (strcmp(type, "error") == 0) {
1305 : /* legacy client auth failed, no more fallbacks */
1306 0 : strophe_error(conn->ctx, "xmpp",
1307 : "Legacy client authentication failed.");
1308 0 : xmpp_disconnect(conn);
1309 0 : } else if (strcmp(type, "result") == 0) {
1310 : /* auth succeeded */
1311 0 : strophe_debug(conn->ctx, "xmpp", "Legacy auth succeeded.");
1312 :
1313 0 : _auth_success(conn);
1314 : } else {
1315 0 : strophe_error(conn->ctx, "xmpp",
1316 : "Server sent us a legacy authentication "
1317 : "response with a bad type.");
1318 0 : xmpp_disconnect(conn);
1319 : }
1320 :
1321 0 : return 0;
1322 : }
1323 :
1324 0 : static void _auth_legacy(xmpp_conn_t *conn)
1325 : {
1326 0 : xmpp_stanza_t *iq;
1327 0 : xmpp_stanza_t *authdata;
1328 0 : xmpp_stanza_t *query;
1329 0 : xmpp_stanza_t *child;
1330 0 : char *str;
1331 :
1332 0 : strophe_debug(conn->ctx, "auth", "Legacy authentication request");
1333 :
1334 0 : iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_auth1");
1335 0 : if (!iq)
1336 0 : goto err;
1337 :
1338 0 : query = xmpp_stanza_new(conn->ctx);
1339 0 : if (!query)
1340 0 : goto err_free;
1341 0 : xmpp_stanza_set_name(query, "query");
1342 0 : xmpp_stanza_set_ns(query, XMPP_NS_AUTH);
1343 0 : xmpp_stanza_add_child_ex(iq, query, 0);
1344 :
1345 0 : child = xmpp_stanza_new(conn->ctx);
1346 0 : if (!child)
1347 0 : goto err_free;
1348 0 : xmpp_stanza_set_name(child, "username");
1349 0 : xmpp_stanza_add_child_ex(query, child, 0);
1350 :
1351 0 : authdata = xmpp_stanza_new(conn->ctx);
1352 0 : if (!authdata)
1353 0 : goto err_free;
1354 0 : str = xmpp_jid_node(conn->ctx, conn->jid);
1355 0 : if (!str) {
1356 0 : xmpp_stanza_release(authdata);
1357 0 : goto err_free;
1358 : }
1359 0 : xmpp_stanza_set_text(authdata, str);
1360 0 : strophe_free(conn->ctx, str);
1361 0 : xmpp_stanza_add_child_ex(child, authdata, 0);
1362 :
1363 0 : child = xmpp_stanza_new(conn->ctx);
1364 0 : if (!child)
1365 0 : goto err_free;
1366 0 : xmpp_stanza_set_name(child, "password");
1367 0 : xmpp_stanza_add_child_ex(query, child, 0);
1368 :
1369 0 : authdata = xmpp_stanza_new(conn->ctx);
1370 0 : if (!authdata)
1371 0 : goto err_free;
1372 0 : xmpp_stanza_set_text(authdata, conn->pass);
1373 0 : xmpp_stanza_add_child_ex(child, authdata, 0);
1374 :
1375 0 : child = xmpp_stanza_new(conn->ctx);
1376 0 : if (!child)
1377 0 : goto err_free;
1378 0 : xmpp_stanza_set_name(child, "resource");
1379 0 : xmpp_stanza_add_child_ex(query, child, 0);
1380 :
1381 0 : authdata = xmpp_stanza_new(conn->ctx);
1382 0 : if (!authdata)
1383 0 : goto err_free;
1384 0 : str = xmpp_jid_resource(conn->ctx, conn->jid);
1385 0 : if (str) {
1386 0 : xmpp_stanza_set_text(authdata, str);
1387 0 : strophe_free(conn->ctx, str);
1388 : } else {
1389 0 : xmpp_stanza_release(authdata);
1390 0 : xmpp_stanza_release(iq);
1391 0 : strophe_error(conn->ctx, "auth",
1392 : "Cannot authenticate without resource");
1393 0 : xmpp_disconnect(conn);
1394 0 : return;
1395 : }
1396 0 : xmpp_stanza_add_child_ex(child, authdata, 0);
1397 :
1398 0 : handler_add_id(conn, _handle_legacy, "_xmpp_auth1", NULL);
1399 0 : handler_add_timed(conn, _handle_missing_legacy, LEGACY_TIMEOUT, NULL);
1400 :
1401 0 : send_stanza(conn, iq, XMPP_QUEUE_STROPHE);
1402 0 : return;
1403 :
1404 0 : err_free:
1405 0 : xmpp_stanza_release(iq);
1406 0 : err:
1407 0 : disconnect_mem_error(conn);
1408 : }
1409 :
1410 0 : void auth_handle_component_open(xmpp_conn_t *conn)
1411 : {
1412 0 : int rc;
1413 :
1414 : /* reset all timed handlers */
1415 0 : handler_reset_timed(conn, 0);
1416 :
1417 0 : handler_add(conn, _handle_error, XMPP_NS_STREAMS, "error", NULL, NULL);
1418 0 : handler_add(conn, _handle_component_hs_response, NULL, "handshake", NULL,
1419 : NULL);
1420 0 : handler_add_timed(conn, _handle_missing_handshake, HANDSHAKE_TIMEOUT, NULL);
1421 :
1422 0 : rc = _handle_component_auth(conn);
1423 0 : if (rc != 0) {
1424 0 : strophe_error(conn->ctx, "auth", "Component authentication failed.");
1425 0 : xmpp_disconnect(conn);
1426 : }
1427 0 : }
1428 :
1429 : /* Will compute SHA1 and authenticate the component to the server */
1430 0 : int _handle_component_auth(xmpp_conn_t *conn)
1431 : {
1432 0 : uint8_t md_value[SHA1_DIGEST_SIZE];
1433 0 : SHA1_CTX mdctx;
1434 0 : char *digest;
1435 0 : size_t i;
1436 :
1437 0 : if (conn->stream_id == NULL) {
1438 0 : strophe_error(conn->ctx, "auth",
1439 : "Received no stream id from the server.");
1440 0 : return XMPP_EINT;
1441 : }
1442 :
1443 : /* Feed the session id and passphrase to the algorithm.
1444 : * We need to compute SHA1(session_id + passphrase)
1445 : */
1446 0 : crypto_SHA1_Init(&mdctx);
1447 0 : crypto_SHA1_Update(&mdctx, (uint8_t *)conn->stream_id,
1448 0 : strlen(conn->stream_id));
1449 0 : crypto_SHA1_Update(&mdctx, (uint8_t *)conn->pass, strlen(conn->pass));
1450 0 : crypto_SHA1_Final(&mdctx, md_value);
1451 :
1452 0 : digest = strophe_alloc(conn->ctx, 2 * sizeof(md_value) + 1);
1453 0 : if (digest) {
1454 : /* convert the digest into string representation */
1455 0 : for (i = 0; i < sizeof(md_value); i++)
1456 0 : strophe_snprintf(digest + i * 2, 3, "%02x", md_value[i]);
1457 0 : digest[2 * sizeof(md_value)] = '\0';
1458 :
1459 0 : strophe_debug(conn->ctx, "auth", "Digest: %s, len: %d", digest,
1460 : strlen(digest));
1461 :
1462 : /* Send the digest to the server */
1463 0 : send_raw_string(conn, "<handshake xmlns='%s'>%s</handshake>",
1464 : XMPP_NS_COMPONENT, digest);
1465 0 : strophe_debug(conn->ctx, "auth",
1466 : "Sent component handshake to the server.");
1467 0 : strophe_free(conn->ctx, digest);
1468 : } else {
1469 0 : strophe_debug(conn->ctx, "auth",
1470 : "Couldn't allocate memory for component "
1471 : "handshake digest.");
1472 0 : return XMPP_EMEM;
1473 : }
1474 :
1475 : return 0;
1476 : }
1477 :
1478 : /* Check if the received stanza is <handshake/> and set auth to true
1479 : * and fire connection handler.
1480 : */
1481 0 : int _handle_component_hs_response(xmpp_conn_t *conn,
1482 : xmpp_stanza_t *stanza,
1483 : void *userdata)
1484 : {
1485 0 : const char *name;
1486 :
1487 0 : UNUSED(userdata);
1488 :
1489 0 : xmpp_timed_handler_delete(conn, _handle_missing_handshake);
1490 :
1491 0 : name = xmpp_stanza_get_name(stanza);
1492 0 : if (strcmp(name, "handshake") != 0) {
1493 0 : char *msg;
1494 0 : size_t msg_size;
1495 0 : xmpp_stanza_to_text(stanza, &msg, &msg_size);
1496 0 : if (msg) {
1497 0 : strophe_debug(conn->ctx, "auth", "Handshake failed: %s", msg);
1498 0 : strophe_free(conn->ctx, msg);
1499 : }
1500 0 : xmpp_disconnect(conn);
1501 0 : return XMPP_EINT;
1502 : } else {
1503 0 : _auth_success(conn);
1504 : }
1505 :
1506 : /* We don't need this handler anymore, return 0 so it can be deleted
1507 : * from the list of handlers.
1508 : */
1509 0 : return 0;
1510 : }
1511 :
1512 0 : int _handle_missing_handshake(xmpp_conn_t *conn, void *userdata)
1513 : {
1514 0 : UNUSED(userdata);
1515 :
1516 0 : strophe_error(conn->ctx, "xmpp",
1517 : "Server did not reply to handshake request.");
1518 0 : xmpp_disconnect(conn);
1519 0 : return 0;
1520 : }
1521 :
1522 0 : void auth_handle_open_raw(xmpp_conn_t *conn)
1523 : {
1524 0 : handler_reset_timed(conn, 0);
1525 : /* user handlers are not called before authentication is completed. */
1526 0 : _auth_success(conn);
1527 0 : }
1528 :
1529 0 : void auth_handle_open_stub(xmpp_conn_t *conn)
1530 : {
1531 0 : strophe_warn(conn->ctx, "auth", "Stub callback is called.");
1532 0 : }
|