LCOV - code coverage report
Current view: top level - src - auth.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 913 0.0 %
Date: 2024-02-24 14:12:49 Functions: 0 43 0.0 %

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

Generated by: LCOV version 1.14