LCOV - code coverage report
Current view: top level - src - auth.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 933 0
Test Date: 2024-07-22 12:36:40 Functions: 0.0 % 42 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            0 :                 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            0 : 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            0 :             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            0 :             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            0 :             goto err;
     506              : 
     507            0 :         auth = xmpp_stanza_new(conn->ctx);
     508            0 :         if (!auth)
     509            0 :             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            0 :         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            0 :         goto err_msg;
     610              :     }
     611            0 :     if (binding_type_len > sizeof(buf)) {
     612            0 :         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            0 :             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, *opt;
    1099            0 :     xmpp_stanza_t *resume;
    1100            0 :     const char *ns;
    1101            0 :     char h[11];
    1102              : 
    1103            0 :     UNUSED(userdata);
    1104              : 
    1105              :     /* remove missing features handler */
    1106            0 :     xmpp_timed_handler_delete(conn, _handle_missing_features_sasl);
    1107              : 
    1108              :     /* check whether resource binding is required */
    1109            0 :     bind = xmpp_stanza_get_child_by_name(stanza, "bind");
    1110            0 :     if (bind) {
    1111            0 :         ns = xmpp_stanza_get_ns(bind);
    1112            0 :         conn->bind_required = ns != NULL && strcmp(ns, XMPP_NS_BIND) == 0;
    1113            0 :         bind = xmpp_stanza_copy(bind);
    1114            0 :         if (!bind) {
    1115            0 :             disconnect_mem_error(conn);
    1116            0 :             return 0;
    1117              :         }
    1118              :     } else {
    1119            0 :         conn->bind_required = 0;
    1120              :     }
    1121              : 
    1122              :     /* check whether session establishment is required */
    1123            0 :     session = xmpp_stanza_get_child_by_name(stanza, "session");
    1124            0 :     if (session) {
    1125            0 :         ns = xmpp_stanza_get_ns(session);
    1126            0 :         opt = xmpp_stanza_get_child_by_name(session, "optional");
    1127            0 :         if (!opt)
    1128            0 :             conn->session_required =
    1129            0 :                 ns != NULL && strcmp(ns, XMPP_NS_SESSION) == 0;
    1130              :     }
    1131              : 
    1132            0 :     if (xmpp_stanza_get_child_by_name_and_ns(stanza, "sm", XMPP_NS_SM)) {
    1133              :         /* stream management supported */
    1134            0 :         conn->sm_state->sm_support = 1;
    1135              :     }
    1136              : 
    1137              :     /* we are expecting either <bind/> and <session/> since this is a
    1138              :        XMPP style connection or we <resume/> the previous session */
    1139              : 
    1140              :     /* check whether we can <resume/> the previous session */
    1141            0 :     if (!conn->sm_disable && conn->sm_state->can_resume &&
    1142            0 :         conn->sm_state->previd && conn->sm_state->bound_jid) {
    1143            0 :         resume = xmpp_stanza_new(conn->ctx);
    1144            0 :         if (!resume) {
    1145            0 :             disconnect_mem_error(conn);
    1146            0 :             return 0;
    1147              :         }
    1148            0 :         conn->sm_state->bind = bind;
    1149            0 :         conn->sm_state->resume = 1;
    1150            0 :         xmpp_stanza_set_name(resume, "resume");
    1151            0 :         xmpp_stanza_set_ns(resume, XMPP_NS_SM);
    1152            0 :         xmpp_stanza_set_attribute(resume, "previd", conn->sm_state->previd);
    1153            0 :         strophe_snprintf(h, sizeof(h), "%u", conn->sm_state->sm_handled_nr);
    1154            0 :         xmpp_stanza_set_attribute(resume, "h", h);
    1155            0 :         send_stanza(conn, resume, XMPP_QUEUE_SM_STROPHE);
    1156            0 :         handler_add(conn, _handle_sm, XMPP_NS_SM, NULL, NULL, NULL);
    1157              :     }
    1158              :     /* if bind is required, go ahead and start it */
    1159            0 :     else if (conn->bind_required) {
    1160              :         /* bind resource */
    1161            0 :         _do_bind(conn, bind);
    1162              :     } else {
    1163              :         /* can't bind, disconnect */
    1164            0 :         if (bind) {
    1165            0 :             xmpp_stanza_release(bind);
    1166              :         }
    1167            0 :         strophe_error(conn->ctx, "xmpp",
    1168              :                       "Stream features does not allow "
    1169              :                       "resource bind.");
    1170            0 :         xmpp_disconnect(conn);
    1171              :     }
    1172              : 
    1173              :     return 0;
    1174              : }
    1175              : 
    1176            0 : static int _handle_missing_features_sasl(xmpp_conn_t *conn, void *userdata)
    1177              : {
    1178            0 :     UNUSED(userdata);
    1179              : 
    1180            0 :     strophe_error(conn->ctx, "xmpp",
    1181              :                   "Did not receive stream features "
    1182              :                   "after SASL authentication.");
    1183            0 :     xmpp_disconnect(conn);
    1184            0 :     return 0;
    1185              : }
    1186              : 
    1187              : static int
    1188            0 : _handle_bind(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
    1189              : {
    1190            0 :     const char *type;
    1191            0 :     xmpp_stanza_t *iq, *session, *binding, *jid_stanza, *enable = NULL;
    1192              : 
    1193            0 :     UNUSED(userdata);
    1194              : 
    1195              :     /* delete missing bind handler */
    1196            0 :     xmpp_timed_handler_delete(conn, _handle_missing_bind);
    1197              : 
    1198              :     /* server has replied to bind request */
    1199            0 :     type = xmpp_stanza_get_type(stanza);
    1200            0 :     if (type && strcmp(type, "error") == 0) {
    1201            0 :         strophe_error(conn->ctx, "xmpp", "Binding failed.");
    1202            0 :         xmpp_disconnect(conn);
    1203            0 :     } else if (type && strcmp(type, "result") == 0) {
    1204            0 :         binding = xmpp_stanza_get_child_by_name(stanza, "bind");
    1205            0 :         strophe_debug(conn->ctx, "xmpp", "Bind successful.");
    1206              : 
    1207            0 :         if (binding) {
    1208            0 :             jid_stanza = xmpp_stanza_get_child_by_name(binding, "jid");
    1209            0 :             if (jid_stanza) {
    1210            0 :                 conn->bound_jid = xmpp_stanza_get_text(jid_stanza);
    1211              :             }
    1212              :         }
    1213              : 
    1214              :         /* send enable directly after the bind request */
    1215            0 :         if (conn->sm_state->sm_support && !conn->sm_disable) {
    1216            0 :             enable = xmpp_stanza_new(conn->ctx);
    1217            0 :             if (!enable) {
    1218            0 :                 disconnect_mem_error(conn);
    1219            0 :                 return 0;
    1220              :             }
    1221            0 :             xmpp_stanza_set_name(enable, "enable");
    1222            0 :             xmpp_stanza_set_ns(enable, XMPP_NS_SM);
    1223            0 :             if (!conn->sm_state->dont_request_resume)
    1224            0 :                 xmpp_stanza_set_attribute(enable, "resume", "true");
    1225            0 :             handler_add(conn, _handle_sm, XMPP_NS_SM, NULL, NULL, NULL);
    1226            0 :             send_stanza(conn, enable, XMPP_QUEUE_SM_STROPHE);
    1227            0 :             conn->sm_state->sm_sent_nr = 0;
    1228            0 :             conn->sm_state->sm_enabled = 1;
    1229              :         }
    1230              : 
    1231              :         /* establish a session if required */
    1232            0 :         if (conn->session_required) {
    1233              :             /* setup response handlers */
    1234            0 :             handler_add_id(conn, _handle_session, "_xmpp_session1", NULL);
    1235            0 :             handler_add_timed(conn, _handle_missing_session, SESSION_TIMEOUT,
    1236              :                               NULL);
    1237              : 
    1238              :             /* send session request */
    1239            0 :             iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_session1");
    1240            0 :             if (!iq) {
    1241            0 :                 disconnect_mem_error(conn);
    1242            0 :                 return 0;
    1243              :             }
    1244              : 
    1245            0 :             session = xmpp_stanza_new(conn->ctx);
    1246            0 :             if (!session) {
    1247            0 :                 xmpp_stanza_release(iq);
    1248            0 :                 disconnect_mem_error(conn);
    1249            0 :                 return 0;
    1250              :             }
    1251              : 
    1252            0 :             xmpp_stanza_set_name(session, "session");
    1253            0 :             xmpp_stanza_set_ns(session, XMPP_NS_SESSION);
    1254              : 
    1255            0 :             xmpp_stanza_add_child_ex(iq, session, 0);
    1256              : 
    1257              :             /* send session establishment request */
    1258            0 :             send_stanza(conn, iq, XMPP_QUEUE_STROPHE);
    1259              :         }
    1260              : 
    1261              :         /* if there's no xmpp session required and we didn't try to enable
    1262              :          * stream-management, we're done here and the stream-negotiation was
    1263              :          * successful
    1264              :          */
    1265            0 :         if (!conn->session_required && !enable) {
    1266            0 :             _stream_negotiation_success(conn);
    1267              :         }
    1268              :     } else {
    1269            0 :         strophe_error(conn->ctx, "xmpp", "Server sent malformed bind reply.");
    1270            0 :         xmpp_disconnect(conn);
    1271              :     }
    1272              : 
    1273              :     return 0;
    1274              : }
    1275              : 
    1276            0 : static int _handle_missing_bind(xmpp_conn_t *conn, void *userdata)
    1277              : {
    1278            0 :     UNUSED(userdata);
    1279              : 
    1280            0 :     strophe_error(conn->ctx, "xmpp", "Server did not reply to bind request.");
    1281            0 :     xmpp_disconnect(conn);
    1282            0 :     return 0;
    1283              : }
    1284              : 
    1285              : static int
    1286            0 : _handle_session(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
    1287              : {
    1288            0 :     const char *type;
    1289              : 
    1290            0 :     UNUSED(userdata);
    1291              : 
    1292              :     /* delete missing session handler */
    1293            0 :     xmpp_timed_handler_delete(conn, _handle_missing_session);
    1294              : 
    1295              :     /* server has replied to the session request */
    1296            0 :     type = xmpp_stanza_get_type(stanza);
    1297            0 :     if (type && strcmp(type, "error") == 0) {
    1298            0 :         strophe_error(conn->ctx, "xmpp", "Session establishment failed.");
    1299            0 :         xmpp_disconnect(conn);
    1300            0 :     } else if (type && strcmp(type, "result") == 0) {
    1301            0 :         strophe_debug(conn->ctx, "xmpp", "Session establishment successful.");
    1302              : 
    1303            0 :         _stream_negotiation_success(conn);
    1304              :     } else {
    1305            0 :         strophe_error(conn->ctx, "xmpp",
    1306              :                       "Server sent malformed session reply.");
    1307            0 :         xmpp_disconnect(conn);
    1308              :     }
    1309              : 
    1310            0 :     return 0;
    1311              : }
    1312              : 
    1313            0 : static int _handle_missing_session(xmpp_conn_t *conn, void *userdata)
    1314              : {
    1315            0 :     UNUSED(userdata);
    1316              : 
    1317            0 :     strophe_error(conn->ctx, "xmpp",
    1318              :                   "Server did not reply to session request.");
    1319            0 :     xmpp_disconnect(conn);
    1320            0 :     return 0;
    1321              : }
    1322              : 
    1323            0 : static int _handle_missing_legacy(xmpp_conn_t *conn, void *userdata)
    1324              : {
    1325            0 :     UNUSED(userdata);
    1326              : 
    1327            0 :     strophe_error(conn->ctx, "xmpp",
    1328              :                   "Server did not reply to legacy "
    1329              :                   "authentication request.");
    1330            0 :     xmpp_disconnect(conn);
    1331            0 :     return 0;
    1332              : }
    1333              : 
    1334            0 : static int _get_h_attribute(xmpp_stanza_t *stanza, unsigned long *ul_h)
    1335              : {
    1336            0 :     const char *h = xmpp_stanza_get_attribute(stanza, "h");
    1337            0 :     if (!h || string_to_ul(h, ul_h)) {
    1338            0 :         strophe_error(
    1339            0 :             stanza->ctx, "xmpp",
    1340              :             "SM error: failed parsing 'h', \"%s\" got converted to %llu.",
    1341              :             STR_MAYBE_NULL(h), *ul_h);
    1342            0 :         return -1;
    1343              :     }
    1344              :     return 0;
    1345              : }
    1346              : 
    1347            0 : static void _sm_queue_cleanup(xmpp_conn_t *conn, unsigned long ul_h)
    1348              : {
    1349            0 :     xmpp_send_queue_t *e;
    1350            0 :     while ((e = peek_queue_front(&conn->sm_state->sm_queue))) {
    1351            0 :         if (e->sm_h >= ul_h)
    1352              :             break;
    1353            0 :         e = pop_queue_front(&conn->sm_state->sm_queue);
    1354            0 :         strophe_free(conn->ctx, queue_element_free(conn->ctx, e));
    1355              :     }
    1356            0 : }
    1357              : 
    1358            0 : static void _sm_queue_resend(xmpp_conn_t *conn)
    1359              : {
    1360            0 :     xmpp_send_queue_t *e;
    1361            0 :     while ((e = pop_queue_front(&conn->sm_state->sm_queue))) {
    1362              :         /* Re-send what was already sent out and is still in the
    1363              :          * SM queue (i.e. it hasn't been ACK'ed by the server)
    1364              :          */
    1365            0 :         strophe_debug_verbose(2, conn->ctx, "conn", "SM_Q_RESEND: %p, h=%lu", e,
    1366              :                               e->sm_h);
    1367            0 :         send_raw(conn, e->data, e->len, e->owner, NULL);
    1368            0 :         strophe_free(conn->ctx, queue_element_free(conn->ctx, e));
    1369              :     }
    1370            0 : }
    1371              : 
    1372            0 : static int _handle_sm(xmpp_conn_t *const conn,
    1373              :                       xmpp_stanza_t *const stanza,
    1374              :                       void *const userdata)
    1375              : {
    1376            0 :     xmpp_stanza_t *failed_cause, *bind = NULL;
    1377            0 :     const char *name, *id, *previd, *resume, *cause;
    1378            0 :     unsigned long ul_h = 0;
    1379              : 
    1380            0 :     UNUSED(userdata);
    1381              : 
    1382            0 :     name = xmpp_stanza_get_name(stanza);
    1383            0 :     if (!name)
    1384            0 :         goto err_sm;
    1385              : 
    1386            0 :     if (strcmp(name, "enabled") == 0) {
    1387            0 :         conn->sm_state->sm_handled_nr = 0;
    1388            0 :         resume = xmpp_stanza_get_attribute(stanza, "resume");
    1389            0 :         if (resume && (strcasecmp(resume, "true") || strcmp(resume, "1"))) {
    1390            0 :             id = xmpp_stanza_get_attribute(stanza, "id");
    1391            0 :             if (!id) {
    1392            0 :                 strophe_error(conn->ctx, "xmpp",
    1393              :                               "SM error: server said it can resume, but "
    1394              :                               "didn't provide an ID.");
    1395            0 :                 name = NULL;
    1396            0 :                 goto err_sm;
    1397              :             }
    1398            0 :             conn->sm_state->can_resume = 1;
    1399            0 :             conn->sm_state->id = strophe_strdup(conn->ctx, id);
    1400              :         }
    1401              :         /* We maybe have stuff in the SM queue if we tried to resume, but the
    1402              :          * server doesn't remember all details of our session, but the `h` was
    1403              :          * still available.
    1404              :          */
    1405            0 :         _sm_queue_resend(conn);
    1406            0 :         _stream_negotiation_success(conn);
    1407            0 :     } else if (strcmp(name, "resumed") == 0) {
    1408            0 :         previd = xmpp_stanza_get_attribute(stanza, "previd");
    1409            0 :         if (!previd || strcmp(previd, conn->sm_state->previd)) {
    1410            0 :             strophe_error(conn->ctx, "xmpp",
    1411              :                           "SM error: previd didn't match, ours is \"%s\".",
    1412            0 :                           conn->sm_state->previd);
    1413            0 :             name = NULL;
    1414            0 :             goto err_sm;
    1415              :         }
    1416            0 :         if (_get_h_attribute(stanza, &ul_h)) {
    1417            0 :             name = NULL;
    1418            0 :             goto err_sm;
    1419              :         }
    1420            0 :         conn->sm_state->sm_enabled = 1;
    1421            0 :         conn->sm_state->id = conn->sm_state->previd;
    1422            0 :         conn->sm_state->previd = NULL;
    1423            0 :         conn->bound_jid = conn->sm_state->bound_jid;
    1424            0 :         conn->sm_state->bound_jid = NULL;
    1425            0 :         if (conn->sm_state->sm_queue.head)
    1426            0 :             conn->sm_state->sm_sent_nr = conn->sm_state->sm_queue.head->sm_h;
    1427              :         else
    1428            0 :             conn->sm_state->sm_sent_nr = ul_h;
    1429            0 :         _sm_queue_cleanup(conn, ul_h);
    1430            0 :         _sm_queue_resend(conn);
    1431            0 :         strophe_debug(conn->ctx, "xmpp", "Session resumed successfully.");
    1432            0 :         _stream_negotiation_success(conn);
    1433            0 :     } else if (strcmp(name, "failed") == 0) {
    1434            0 :         name = NULL;
    1435            0 :         conn->sm_state->sm_enabled = 0;
    1436              : 
    1437            0 :         failed_cause =
    1438            0 :             xmpp_stanza_get_child_by_ns(stanza, XMPP_NS_STANZAS_IETF);
    1439            0 :         if (!failed_cause)
    1440            0 :             goto err_sm;
    1441              : 
    1442            0 :         cause = xmpp_stanza_get_name(failed_cause);
    1443            0 :         if (!cause)
    1444            0 :             goto err_sm;
    1445              : 
    1446            0 :         if (!strcmp(cause, "item-not-found")) {
    1447            0 :             if (conn->sm_state->resume) {
    1448              :                 /* It's no error if there's no `h` attribute included
    1449              :                  * but if there is, it gives a hint at what the server
    1450              :                  * already received.
    1451              :                  */
    1452            0 :                 if (!_get_h_attribute(stanza, &ul_h)) {
    1453              :                     /* In cases there's no `h` included, drop all elements. */
    1454            0 :                     ul_h = (unsigned long)-1;
    1455              :                 }
    1456            0 :                 _sm_queue_cleanup(conn, ul_h);
    1457              :             }
    1458            0 :         } else if (!strcmp(cause, "feature-not-implemented")) {
    1459            0 :             conn->sm_state->resume = 0;
    1460            0 :             conn->sm_state->can_resume = 0;
    1461              :             /* remember that the server reports having support
    1462              :              * for resumption, but actually it doesn't ...
    1463              :              */
    1464            0 :             conn->sm_state->dont_request_resume = 1;
    1465              :         }
    1466            0 :         bind = conn->sm_state->bind;
    1467            0 :         conn->sm_state->bind = NULL;
    1468            0 :         reset_sm_state(conn->sm_state);
    1469            0 :         _do_bind(conn, bind);
    1470              :     } else {
    1471              :         /* unknown stanza received */
    1472              :         name = NULL;
    1473              :     }
    1474              : 
    1475              : err_sm:
    1476            0 :     if (!name) {
    1477            0 :         char *err = "Couldn't convert stanza to text!";
    1478            0 :         char *buf;
    1479            0 :         size_t buflen;
    1480            0 :         switch (xmpp_stanza_to_text(stanza, &buf, &buflen)) {
    1481              :         case XMPP_EOK:
    1482              :             break;
    1483            0 :         case XMPP_EMEM:
    1484            0 :             disconnect_mem_error(conn);
    1485            0 :             return 0;
    1486            0 :         default:
    1487            0 :             buf = err;
    1488            0 :             break;
    1489              :         }
    1490            0 :         strophe_warn(conn->ctx, "xmpp", "SM error: Stanza received was: %s",
    1491              :                      buf);
    1492            0 :         if (buf != err)
    1493            0 :             strophe_free(conn->ctx, buf);
    1494              :         /* Don't disable for <failure> cases, they're no hard errors */
    1495            0 :         conn->sm_state->sm_enabled = bind != NULL;
    1496              :     }
    1497              :     return 0;
    1498              : }
    1499              : 
    1500              : static int
    1501            0 : _handle_legacy(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
    1502              : {
    1503            0 :     const char *type;
    1504            0 :     const char *name;
    1505              : 
    1506            0 :     UNUSED(userdata);
    1507              : 
    1508              :     /* delete missing handler */
    1509            0 :     xmpp_timed_handler_delete(conn, _handle_missing_legacy);
    1510              : 
    1511              :     /* server responded to legacy auth request */
    1512            0 :     type = xmpp_stanza_get_type(stanza);
    1513            0 :     name = xmpp_stanza_get_name(stanza);
    1514            0 :     if (!type || strcmp(name, "iq") != 0) {
    1515            0 :         strophe_error(conn->ctx, "xmpp",
    1516              :                       "Server sent us an unexpected response "
    1517              :                       "to legacy authentication request.");
    1518            0 :         xmpp_disconnect(conn);
    1519            0 :     } else if (strcmp(type, "error") == 0) {
    1520              :         /* legacy client auth failed, no more fallbacks */
    1521            0 :         strophe_error(conn->ctx, "xmpp",
    1522              :                       "Legacy client authentication failed.");
    1523            0 :         xmpp_disconnect(conn);
    1524            0 :     } else if (strcmp(type, "result") == 0) {
    1525              :         /* auth succeeded */
    1526            0 :         strophe_debug(conn->ctx, "xmpp", "Legacy auth succeeded.");
    1527              : 
    1528            0 :         _stream_negotiation_success(conn);
    1529              :     } else {
    1530            0 :         strophe_error(conn->ctx, "xmpp",
    1531              :                       "Server sent us a legacy authentication "
    1532              :                       "response with a bad type.");
    1533            0 :         xmpp_disconnect(conn);
    1534              :     }
    1535              : 
    1536            0 :     return 0;
    1537              : }
    1538              : 
    1539            0 : static void _auth_legacy(xmpp_conn_t *conn)
    1540              : {
    1541            0 :     xmpp_stanza_t *iq;
    1542            0 :     xmpp_stanza_t *authdata;
    1543            0 :     xmpp_stanza_t *query;
    1544            0 :     xmpp_stanza_t *child;
    1545            0 :     char *str;
    1546              : 
    1547            0 :     strophe_debug(conn->ctx, "auth", "Legacy authentication request");
    1548              : 
    1549            0 :     iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_auth1");
    1550            0 :     if (!iq)
    1551            0 :         goto err;
    1552              : 
    1553            0 :     query = xmpp_stanza_new(conn->ctx);
    1554            0 :     if (!query)
    1555            0 :         goto err_free;
    1556            0 :     xmpp_stanza_set_name(query, "query");
    1557            0 :     xmpp_stanza_set_ns(query, XMPP_NS_AUTH);
    1558            0 :     xmpp_stanza_add_child_ex(iq, query, 0);
    1559              : 
    1560            0 :     child = xmpp_stanza_new(conn->ctx);
    1561            0 :     if (!child)
    1562            0 :         goto err_free;
    1563            0 :     xmpp_stanza_set_name(child, "username");
    1564            0 :     xmpp_stanza_add_child_ex(query, child, 0);
    1565              : 
    1566            0 :     authdata = xmpp_stanza_new(conn->ctx);
    1567            0 :     if (!authdata)
    1568            0 :         goto err_free;
    1569            0 :     str = xmpp_jid_node(conn->ctx, conn->jid);
    1570            0 :     if (!str) {
    1571            0 :         xmpp_stanza_release(authdata);
    1572            0 :         goto err_free;
    1573              :     }
    1574            0 :     xmpp_stanza_set_text(authdata, str);
    1575            0 :     strophe_free(conn->ctx, str);
    1576            0 :     xmpp_stanza_add_child_ex(child, authdata, 0);
    1577              : 
    1578            0 :     child = xmpp_stanza_new(conn->ctx);
    1579            0 :     if (!child)
    1580            0 :         goto err_free;
    1581            0 :     xmpp_stanza_set_name(child, "password");
    1582            0 :     xmpp_stanza_add_child_ex(query, child, 0);
    1583              : 
    1584            0 :     authdata = xmpp_stanza_new(conn->ctx);
    1585            0 :     if (!authdata)
    1586            0 :         goto err_free;
    1587            0 :     xmpp_stanza_set_text(authdata, conn->pass);
    1588            0 :     xmpp_stanza_add_child_ex(child, authdata, 0);
    1589              : 
    1590            0 :     child = xmpp_stanza_new(conn->ctx);
    1591            0 :     if (!child)
    1592            0 :         goto err_free;
    1593            0 :     xmpp_stanza_set_name(child, "resource");
    1594            0 :     xmpp_stanza_add_child_ex(query, child, 0);
    1595              : 
    1596            0 :     authdata = xmpp_stanza_new(conn->ctx);
    1597            0 :     if (!authdata)
    1598            0 :         goto err_free;
    1599            0 :     str = xmpp_jid_resource(conn->ctx, conn->jid);
    1600            0 :     if (str) {
    1601            0 :         xmpp_stanza_set_text(authdata, str);
    1602            0 :         strophe_free(conn->ctx, str);
    1603              :     } else {
    1604            0 :         xmpp_stanza_release(authdata);
    1605            0 :         xmpp_stanza_release(iq);
    1606            0 :         strophe_error(conn->ctx, "auth",
    1607              :                       "Cannot authenticate without resource");
    1608            0 :         xmpp_disconnect(conn);
    1609            0 :         return;
    1610              :     }
    1611            0 :     xmpp_stanza_add_child_ex(child, authdata, 0);
    1612              : 
    1613            0 :     handler_add_id(conn, _handle_legacy, "_xmpp_auth1", NULL);
    1614            0 :     handler_add_timed(conn, _handle_missing_legacy, LEGACY_TIMEOUT, NULL);
    1615              : 
    1616            0 :     send_stanza(conn, iq, XMPP_QUEUE_STROPHE);
    1617            0 :     return;
    1618              : 
    1619            0 : err_free:
    1620            0 :     xmpp_stanza_release(iq);
    1621            0 : err:
    1622            0 :     disconnect_mem_error(conn);
    1623              : }
    1624              : 
    1625            0 : void auth_handle_component_open(xmpp_conn_t *conn)
    1626              : {
    1627            0 :     int rc;
    1628              : 
    1629              :     /* reset all timed handlers */
    1630            0 :     handler_reset_timed(conn, 0);
    1631              : 
    1632            0 :     handler_add(conn, _handle_error, XMPP_NS_STREAMS, "error", NULL, NULL);
    1633            0 :     handler_add(conn, _handle_component_hs_response, NULL, "handshake", NULL,
    1634              :                 NULL);
    1635            0 :     handler_add_timed(conn, _handle_missing_handshake, HANDSHAKE_TIMEOUT, NULL);
    1636              : 
    1637            0 :     rc = _handle_component_auth(conn);
    1638            0 :     if (rc != 0) {
    1639            0 :         strophe_error(conn->ctx, "auth", "Component authentication failed.");
    1640            0 :         xmpp_disconnect(conn);
    1641              :     }
    1642            0 : }
    1643              : 
    1644              : /* Will compute SHA1 and authenticate the component to the server */
    1645            0 : int _handle_component_auth(xmpp_conn_t *conn)
    1646              : {
    1647            0 :     uint8_t md_value[SHA1_DIGEST_SIZE];
    1648            0 :     SHA1_CTX mdctx;
    1649            0 :     char *digest;
    1650            0 :     size_t i;
    1651              : 
    1652            0 :     if (conn->stream_id == NULL) {
    1653            0 :         strophe_error(conn->ctx, "auth",
    1654              :                       "Received no stream id from the server.");
    1655            0 :         return XMPP_EINT;
    1656              :     }
    1657              : 
    1658              :     /* Feed the session id and passphrase to the algorithm.
    1659              :      * We need to compute SHA1(session_id + passphrase)
    1660              :      */
    1661            0 :     crypto_SHA1_Init(&mdctx);
    1662            0 :     crypto_SHA1_Update(&mdctx, (uint8_t *)conn->stream_id,
    1663              :                        strlen(conn->stream_id));
    1664            0 :     crypto_SHA1_Update(&mdctx, (uint8_t *)conn->pass, strlen(conn->pass));
    1665            0 :     crypto_SHA1_Final(&mdctx, md_value);
    1666              : 
    1667            0 :     digest = strophe_alloc(conn->ctx, 2 * sizeof(md_value) + 1);
    1668            0 :     if (digest) {
    1669              :         /* convert the digest into string representation */
    1670            0 :         for (i = 0; i < sizeof(md_value); i++)
    1671            0 :             strophe_snprintf(digest + i * 2, 3, "%02x", md_value[i]);
    1672            0 :         digest[2 * sizeof(md_value)] = '\0';
    1673              : 
    1674            0 :         strophe_debug(conn->ctx, "auth", "Digest: %s, len: %d", digest,
    1675              :                       strlen(digest));
    1676              : 
    1677              :         /* Send the digest to the server */
    1678            0 :         send_raw_string(conn, "<handshake xmlns='%s'>%s</handshake>",
    1679              :                         XMPP_NS_COMPONENT, digest);
    1680            0 :         strophe_debug(conn->ctx, "auth",
    1681              :                       "Sent component handshake to the server.");
    1682            0 :         strophe_free(conn->ctx, digest);
    1683              :     } else {
    1684            0 :         strophe_debug(conn->ctx, "auth",
    1685              :                       "Couldn't allocate memory for component "
    1686              :                       "handshake digest.");
    1687            0 :         return XMPP_EMEM;
    1688              :     }
    1689              : 
    1690              :     return 0;
    1691              : }
    1692              : 
    1693              : /* Check if the received stanza is <handshake/> and set auth to true
    1694              :  * and fire connection handler.
    1695              :  */
    1696            0 : int _handle_component_hs_response(xmpp_conn_t *conn,
    1697              :                                   xmpp_stanza_t *stanza,
    1698              :                                   void *userdata)
    1699              : {
    1700            0 :     const char *name;
    1701              : 
    1702            0 :     UNUSED(userdata);
    1703              : 
    1704            0 :     xmpp_timed_handler_delete(conn, _handle_missing_handshake);
    1705              : 
    1706            0 :     name = xmpp_stanza_get_name(stanza);
    1707            0 :     if (strcmp(name, "handshake") != 0) {
    1708            0 :         char *msg;
    1709            0 :         size_t msg_size;
    1710            0 :         xmpp_stanza_to_text(stanza, &msg, &msg_size);
    1711            0 :         if (msg) {
    1712            0 :             strophe_debug(conn->ctx, "auth", "Handshake failed: %s", msg);
    1713            0 :             strophe_free(conn->ctx, msg);
    1714              :         }
    1715            0 :         xmpp_disconnect(conn);
    1716            0 :         return XMPP_EINT;
    1717              :     } else {
    1718            0 :         _stream_negotiation_success(conn);
    1719              :     }
    1720              : 
    1721              :     /* We don't need this handler anymore, return 0 so it can be deleted
    1722              :      * from the list of handlers.
    1723              :      */
    1724            0 :     return 0;
    1725              : }
    1726              : 
    1727            0 : int _handle_missing_handshake(xmpp_conn_t *conn, void *userdata)
    1728              : {
    1729            0 :     UNUSED(userdata);
    1730              : 
    1731            0 :     strophe_error(conn->ctx, "xmpp",
    1732              :                   "Server did not reply to handshake request.");
    1733            0 :     xmpp_disconnect(conn);
    1734            0 :     return 0;
    1735              : }
    1736              : 
    1737            0 : void auth_handle_open_raw(xmpp_conn_t *conn)
    1738              : {
    1739            0 :     handler_reset_timed(conn, 0);
    1740              :     /* user handlers are not called before stream negotiation has completed. */
    1741            0 :     _stream_negotiation_success(conn);
    1742            0 : }
    1743              : 
    1744            0 : void auth_handle_open_stub(xmpp_conn_t *conn)
    1745              : {
    1746            0 :     strophe_warn(conn->ctx, "auth", "Stub callback is called.");
    1747            0 : }
        

Generated by: LCOV version 2.0-1