LCOV - code coverage report
Current view: top level - src - auth.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 847 0.0 %
Date: 2023-08-10 00:00:00 Functions: 0 34 0.0 %

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

Generated by: LCOV version 1.14