1277 lines
37 KiB
C
1277 lines
37 KiB
C
/* auth.c
|
||
** strophe XMPP client library -- auth functions and handlers
|
||
**
|
||
** Copyright (C) 2005-2009 Collecta, Inc.
|
||
**
|
||
** This software is provided AS-IS with no warranty, either express or
|
||
** implied.
|
||
**
|
||
** This program is dual licensed under the MIT and GPLv3 licenses.
|
||
*/
|
||
|
||
/** @file
|
||
* Authentication function and handlers.
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#include "strophe.h"
|
||
#include "common.h"
|
||
#include "sasl.h"
|
||
#include "sha1.h"
|
||
#include "rand.h"
|
||
|
||
#ifdef _MSC_VER
|
||
// VS2015 and newer are more standard compliant
|
||
#if _MSC_VER >= 1900
|
||
#define strcasecmp _stricmp
|
||
#else
|
||
#define strcasecmp stricmp
|
||
#endif
|
||
#endif
|
||
|
||
/* TODO: these should configurable at runtime on a per connection basis */
|
||
|
||
#ifndef FEATURES_TIMEOUT
|
||
/** @def FEATURES_TIMEOUT
|
||
* Time to wait for <stream:features/> stanza.
|
||
*/
|
||
#define FEATURES_TIMEOUT 15000 /* 15 seconds */
|
||
#endif
|
||
#ifndef BIND_TIMEOUT
|
||
/** @def BIND_TIMEOUT
|
||
* Time to wait for <bind/> stanza reply.
|
||
*/
|
||
#define BIND_TIMEOUT 15000 /* 15 seconds */
|
||
#endif
|
||
#ifndef SESSION_TIMEOUT
|
||
/** @def SESSION_TIMEOUT
|
||
* Time to wait for <session/> stanza reply.
|
||
*/
|
||
#define SESSION_TIMEOUT 15000 /* 15 seconds */
|
||
#endif
|
||
#ifndef LEGACY_TIMEOUT
|
||
/** @def LEGACY_TIMEOUT
|
||
* Time to wait for legacy authentication to complete.
|
||
*/
|
||
#define LEGACY_TIMEOUT 15000 /* 15 seconds */
|
||
#endif
|
||
#ifndef HANDSHAKE_TIMEOUT
|
||
/** @def HANDSHAKE_TIMEOUT
|
||
* Time to wait for component authentication to complete
|
||
*/
|
||
#define HANDSHAKE_TIMEOUT 15000 /* 15 seconds */
|
||
#endif
|
||
|
||
static void _auth(xmpp_conn_t * const conn);
|
||
static void _auth_legacy(xmpp_conn_t *conn);
|
||
static void _handle_open_sasl(xmpp_conn_t * const conn);
|
||
static void _handle_open_tls(xmpp_conn_t * const conn);
|
||
|
||
static int _handle_component_auth(xmpp_conn_t * const conn);
|
||
static int _handle_component_hs_response(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata);
|
||
|
||
static int _handle_features_sasl(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata);
|
||
static int _handle_sasl_result(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata);
|
||
static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata);
|
||
static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata);
|
||
static int _handle_scram_sha1_challenge(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata);
|
||
static char *_make_scram_sha1_init_msg(xmpp_conn_t * const conn);
|
||
|
||
static int _handle_missing_features_sasl(xmpp_conn_t * const conn,
|
||
void * const userdata);
|
||
static int _handle_missing_bind(xmpp_conn_t * const conn,
|
||
void * const userdata);
|
||
static int _handle_bind(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata);
|
||
static int _handle_session(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata);
|
||
static int _handle_missing_session(xmpp_conn_t * const conn,
|
||
void * const userdata);
|
||
static int _handle_missing_handshake(xmpp_conn_t * const conn,
|
||
void * const userdata);
|
||
|
||
/* stream:error handler */
|
||
static int _handle_error(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata)
|
||
{
|
||
xmpp_stanza_t *child;
|
||
const char *name;
|
||
|
||
/* free old stream error if it's still there */
|
||
if (conn->stream_error) {
|
||
xmpp_stanza_release(conn->stream_error->stanza);
|
||
if (conn->stream_error->text)
|
||
xmpp_free(conn->ctx, conn->stream_error->text);
|
||
xmpp_free(conn->ctx, conn->stream_error);
|
||
}
|
||
|
||
/* create stream error structure */
|
||
conn->stream_error = (xmpp_stream_error_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_stream_error_t));
|
||
|
||
conn->stream_error->text = NULL;
|
||
conn->stream_error->type = XMPP_SE_UNDEFINED_CONDITION;
|
||
|
||
if (conn->stream_error) {
|
||
child = xmpp_stanza_get_children(stanza);
|
||
do {
|
||
const char *ns = NULL;
|
||
|
||
if (child) {
|
||
ns = xmpp_stanza_get_ns(child);
|
||
}
|
||
|
||
if (ns && strcmp(ns, XMPP_NS_STREAMS_IETF) == 0) {
|
||
name = xmpp_stanza_get_name(child);
|
||
if (strcmp(name, "text") == 0) {
|
||
if (conn->stream_error->text)
|
||
xmpp_free(conn->ctx, conn->stream_error->text);
|
||
conn->stream_error->text = xmpp_stanza_get_text(child);
|
||
} else if (strcmp(name, "bad-format") == 0)
|
||
conn->stream_error->type = XMPP_SE_BAD_FORMAT;
|
||
else if (strcmp(name, "bad-namespace-prefix") == 0)
|
||
conn->stream_error->type = XMPP_SE_BAD_NS_PREFIX;
|
||
else if (strcmp(name, "conflict") == 0)
|
||
conn->stream_error->type = XMPP_SE_CONFLICT;
|
||
else if (strcmp(name, "connection-timeout") == 0)
|
||
conn->stream_error->type = XMPP_SE_CONN_TIMEOUT;
|
||
else if (strcmp(name, "host-gone") == 0)
|
||
conn->stream_error->type = XMPP_SE_HOST_GONE;
|
||
else if (strcmp(name, "host-unknown") == 0)
|
||
conn->stream_error->type = XMPP_SE_HOST_UNKNOWN;
|
||
else if (strcmp(name, "improper-addressing") == 0)
|
||
conn->stream_error->type = XMPP_SE_IMPROPER_ADDR;
|
||
else if (strcmp(name, "internal-server-error") == 0)
|
||
conn->stream_error->type = XMPP_SE_INTERNAL_SERVER_ERROR;
|
||
else if (strcmp(name, "invalid-from") == 0)
|
||
conn->stream_error->type = XMPP_SE_INVALID_FROM;
|
||
else if (strcmp(name, "invalid-id") == 0)
|
||
conn->stream_error->type = XMPP_SE_INVALID_ID;
|
||
else if (strcmp(name, "invalid-namespace") == 0)
|
||
conn->stream_error->type = XMPP_SE_INVALID_NS;
|
||
else if (strcmp(name, "invalid-xml") == 0)
|
||
conn->stream_error->type = XMPP_SE_INVALID_XML;
|
||
else if (strcmp(name, "not-authorized") == 0)
|
||
conn->stream_error->type = XMPP_SE_NOT_AUTHORIZED;
|
||
else if (strcmp(name, "policy-violation") == 0)
|
||
conn->stream_error->type = XMPP_SE_POLICY_VIOLATION;
|
||
else if (strcmp(name, "remote-connection-failed") == 0)
|
||
conn->stream_error->type = XMPP_SE_REMOTE_CONN_FAILED;
|
||
else if (strcmp(name, "resource-constraint") == 0)
|
||
conn->stream_error->type = XMPP_SE_RESOURCE_CONSTRAINT;
|
||
else if (strcmp(name, "restricted-xml") == 0)
|
||
conn->stream_error->type = XMPP_SE_RESTRICTED_XML;
|
||
else if (strcmp(name, "see-other-host") == 0)
|
||
conn->stream_error->type = XMPP_SE_SEE_OTHER_HOST;
|
||
else if (strcmp(name, "system-shutdown") == 0)
|
||
conn->stream_error->type = XMPP_SE_SYSTEM_SHUTDOWN;
|
||
else if (strcmp(name, "undefined-condition") == 0)
|
||
conn->stream_error->type = XMPP_SE_UNDEFINED_CONDITION;
|
||
else if (strcmp(name, "unsupported-encoding") == 0)
|
||
conn->stream_error->type = XMPP_SE_UNSUPPORTED_ENCODING;
|
||
else if (strcmp(name, "unsupported-stanza-type") == 0)
|
||
conn->stream_error->type = XMPP_SE_UNSUPPORTED_STANZA_TYPE;
|
||
else if (strcmp(name, "unsupported-version") == 0)
|
||
conn->stream_error->type = XMPP_SE_UNSUPPORTED_VERSION;
|
||
else if (strcmp(name, "xml-not-well-formed") == 0)
|
||
conn->stream_error->type = XMPP_SE_XML_NOT_WELL_FORMED;
|
||
}
|
||
} while ((child = xmpp_stanza_get_next(child)));
|
||
|
||
conn->stream_error->stanza = xmpp_stanza_clone(stanza);
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* stream:features handlers */
|
||
static int _handle_missing_features(xmpp_conn_t * const conn,
|
||
void * const userdata)
|
||
{
|
||
xmpp_debug(conn->ctx, "xmpp", "didn't get stream features");
|
||
|
||
/* legacy auth will be attempted */
|
||
_auth(conn);
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
static int _handle_features(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata)
|
||
{
|
||
xmpp_stanza_t *child, *mech;
|
||
const char *ns;
|
||
char *text;
|
||
|
||
/* remove the handler that detects missing stream:features */
|
||
xmpp_timed_handler_delete(conn, _handle_missing_features);
|
||
|
||
/* check for TLS */
|
||
if (!conn->secured) {
|
||
if (!conn->tls_disabled && !(conn->extsock && conn->extsock->is_websocket)) { /* websocket xmpp uses ssl at the http layer */
|
||
child = xmpp_stanza_get_child_by_name(stanza, "starttls");
|
||
if (child) {
|
||
ns = xmpp_stanza_get_ns(child);
|
||
conn->tls_support = ns != NULL && strcmp(ns, XMPP_NS_TLS) == 0;
|
||
}
|
||
} else {
|
||
conn->tls_support = 0;
|
||
}
|
||
}
|
||
|
||
/* check for SASL */
|
||
child = xmpp_stanza_get_child_by_name(stanza, "mechanisms");
|
||
ns = child ? xmpp_stanza_get_ns(child) : NULL;
|
||
if (child && ns && strcmp(ns, XMPP_NS_SASL) == 0) {
|
||
for (mech = xmpp_stanza_get_children(child); mech;
|
||
mech = xmpp_stanza_get_next(mech)) {
|
||
if (xmpp_stanza_get_name(mech) && strcmp(xmpp_stanza_get_name(mech), "mechanism") == 0) {
|
||
text = xmpp_stanza_get_text(mech);
|
||
if (text == NULL)
|
||
continue;
|
||
|
||
if (strcasecmp(text, "PLAIN") == 0)
|
||
conn->sasl_support |= SASL_MASK_PLAIN;
|
||
else if (strcasecmp(text, "DIGEST-MD5") == 0)
|
||
conn->sasl_support |= SASL_MASK_DIGESTMD5;
|
||
else if (strcasecmp(text, "SCRAM-SHA-1") == 0)
|
||
conn->sasl_support |= SASL_MASK_SCRAMSHA1;
|
||
else if (strcasecmp(text, "ANONYMOUS") == 0)
|
||
conn->sasl_support |= SASL_MASK_ANONYMOUS;
|
||
|
||
xmpp_free(conn->ctx, text);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Disable PLAIN when other secure mechanisms are supported */
|
||
if (conn->sasl_support & ~(SASL_MASK_PLAIN | SASL_MASK_ANONYMOUS))
|
||
conn->sasl_support &= ~SASL_MASK_PLAIN;
|
||
|
||
_auth(conn);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* returns the correct auth id for a component or a client.
|
||
* returned string must be freed by caller */
|
||
static char *_get_authid(xmpp_conn_t * const conn)
|
||
{
|
||
char *authid = NULL;
|
||
|
||
if (conn->type == XMPP_CLIENT) {
|
||
/* authid is the node portion of jid */
|
||
if (!conn->jid) return NULL;
|
||
authid = xmpp_jid_node(conn->ctx, conn->jid);
|
||
}
|
||
|
||
return authid;
|
||
}
|
||
|
||
static int _handle_proceedtls_default(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata)
|
||
{
|
||
const char *name;
|
||
|
||
name = xmpp_stanza_get_name(stanza);
|
||
xmpp_debug(conn->ctx, "xmpp", "handle proceedtls called for %s", name);
|
||
|
||
if (strcmp(name, "proceed") == 0) {
|
||
xmpp_debug(conn->ctx, "xmpp", "proceeding with TLS");
|
||
|
||
if (conn_tls_start(conn) == 0) {
|
||
conn_prepare_reset(conn, _handle_open_tls);
|
||
conn_open_stream(conn);
|
||
} else {
|
||
/* failed tls spoils the connection, so disconnect */
|
||
xmpp_disconnect(conn);
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int _handle_sasl_result(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata)
|
||
{
|
||
const char *name;
|
||
|
||
name = xmpp_stanza_get_name(stanza);
|
||
|
||
/* the server should send a <success> or <failure> stanza */
|
||
if (strcmp(name, "failure") == 0) {
|
||
xmpp_debug(conn->ctx, "xmpp", "SASL %s auth failed",
|
||
(char *)userdata);
|
||
|
||
/* fall back to next auth method */
|
||
_auth(conn);
|
||
} else if (strcmp(name, "success") == 0) {
|
||
/* SASL auth successful, we need to restart the stream */
|
||
xmpp_debug(conn->ctx, "xmpp", "SASL %s auth successful",
|
||
(char *)userdata);
|
||
|
||
/* reset parser */
|
||
conn_prepare_reset(conn, _handle_open_sasl);
|
||
|
||
/* send stream tag */
|
||
conn_open_stream(conn);
|
||
} else {
|
||
/* got unexpected reply */
|
||
xmpp_error(conn->ctx, "xmpp", "Got unexpected reply to SASL %s"\
|
||
"authentication.", (char *)userdata);
|
||
xmpp_disconnect(conn);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* handle the challenge phase of digest auth */
|
||
static int _handle_digestmd5_challenge(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata)
|
||
{
|
||
char *text;
|
||
char *response;
|
||
xmpp_stanza_t *auth, *authdata;
|
||
const char *name;
|
||
|
||
name = xmpp_stanza_get_name(stanza);
|
||
xmpp_debug(conn->ctx, "xmpp",\
|
||
"handle digest-md5 (challenge) called for %s", name);
|
||
|
||
if (strcmp(name, "challenge") == 0) {
|
||
text = xmpp_stanza_get_text(stanza);
|
||
response = sasl_digest_md5(conn->ctx, text, conn->jid, conn->pass);
|
||
if (!response) {
|
||
disconnect_mem_error(conn);
|
||
return 0;
|
||
}
|
||
xmpp_free(conn->ctx, text);
|
||
|
||
auth = xmpp_stanza_new(conn->ctx);
|
||
if (!auth) {
|
||
disconnect_mem_error(conn);
|
||
return 0;
|
||
}
|
||
xmpp_stanza_set_name(auth, "response");
|
||
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
|
||
|
||
authdata = xmpp_stanza_new(conn->ctx);
|
||
if (!authdata) {
|
||
disconnect_mem_error(conn);
|
||
return 0;
|
||
}
|
||
|
||
xmpp_stanza_set_text(authdata, response);
|
||
xmpp_free(conn->ctx, response);
|
||
|
||
xmpp_stanza_add_child(auth, authdata);
|
||
xmpp_stanza_release(authdata);
|
||
|
||
handler_add(conn, _handle_digestmd5_rspauth,
|
||
XMPP_NS_SASL, NULL, NULL, NULL);
|
||
|
||
xmpp_send(conn, auth);
|
||
xmpp_stanza_release(auth);
|
||
|
||
} else {
|
||
return _handle_sasl_result(conn, stanza, "DIGEST-MD5");
|
||
}
|
||
|
||
/* remove ourselves */
|
||
return 0;
|
||
}
|
||
|
||
/* handle the rspauth phase of digest auth */
|
||
static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata)
|
||
{
|
||
xmpp_stanza_t *auth;
|
||
const char *name;
|
||
|
||
name = xmpp_stanza_get_name(stanza);
|
||
xmpp_debug(conn->ctx, "xmpp",
|
||
"handle digest-md5 (rspauth) called for %s", name);
|
||
|
||
|
||
if (strcmp(name, "challenge") == 0) {
|
||
/* assume it's an rspauth response */
|
||
auth = xmpp_stanza_new(conn->ctx);
|
||
if (!auth) {
|
||
disconnect_mem_error(conn);
|
||
return 0;
|
||
}
|
||
xmpp_stanza_set_name(auth, "response");
|
||
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
|
||
xmpp_send(conn, auth);
|
||
xmpp_stanza_release(auth);
|
||
} else {
|
||
return _handle_sasl_result(conn, stanza, "DIGEST-MD5");
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* handle the challenge phase of SCRAM-SHA-1 auth */
|
||
static int _handle_scram_sha1_challenge(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata)
|
||
{
|
||
char *text;
|
||
char *response;
|
||
xmpp_stanza_t *auth, *authdata;
|
||
const char *name;
|
||
char *challenge;
|
||
char *scram_init = (char *)userdata;
|
||
|
||
name = xmpp_stanza_get_name(stanza);
|
||
xmpp_debug(conn->ctx, "xmpp",
|
||
"handle SCRAM-SHA-1 (challenge) called for %s", name);
|
||
|
||
if (strcmp(name, "challenge") == 0) {
|
||
text = xmpp_stanza_get_text(stanza);
|
||
if (!text)
|
||
goto err;
|
||
|
||
challenge = xmpp_base64_decode_str(conn->ctx, text, strlen(text));
|
||
xmpp_free(conn->ctx, text);
|
||
if (!challenge)
|
||
goto err;
|
||
|
||
response = sasl_scram_sha1(conn->ctx, challenge, scram_init,
|
||
conn->jid, conn->pass);
|
||
xmpp_free(conn->ctx, challenge);
|
||
if (!response)
|
||
goto err;
|
||
|
||
auth = xmpp_stanza_new(conn->ctx);
|
||
if (!auth)
|
||
goto err_free_response;
|
||
xmpp_stanza_set_name(auth, "response");
|
||
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
|
||
|
||
authdata = xmpp_stanza_new(conn->ctx);
|
||
if (!authdata)
|
||
goto err_release_auth;
|
||
xmpp_stanza_set_text(authdata, response);
|
||
xmpp_free(conn->ctx, response);
|
||
|
||
xmpp_stanza_add_child(auth, authdata);
|
||
xmpp_stanza_release(authdata);
|
||
|
||
xmpp_send(conn, auth);
|
||
xmpp_stanza_release(auth);
|
||
|
||
} else {
|
||
xmpp_free(conn->ctx, scram_init);
|
||
return _handle_sasl_result(conn, stanza, "SCRAM-SHA-1");
|
||
}
|
||
|
||
return 1;
|
||
|
||
err_release_auth:
|
||
xmpp_stanza_release(auth);
|
||
err_free_response:
|
||
xmpp_free(conn->ctx, response);
|
||
err:
|
||
xmpp_free(conn->ctx, scram_init);
|
||
disconnect_mem_error(conn);
|
||
return 0;
|
||
}
|
||
|
||
static char *_make_scram_sha1_init_msg(xmpp_conn_t * const conn)
|
||
{
|
||
xmpp_ctx_t *ctx = conn->ctx;
|
||
size_t message_len;
|
||
char *node;
|
||
char *message;
|
||
char nonce[32];
|
||
|
||
node = xmpp_jid_node(ctx, conn->jid);
|
||
if (!node) {
|
||
return NULL;
|
||
}
|
||
xmpp_rand_nonce(ctx->rand, nonce, sizeof(nonce));
|
||
message_len = strlen(node) + strlen(nonce) + 8 + 1;
|
||
message = xmpp_alloc(ctx, message_len);
|
||
if (message) {
|
||
xmpp_snprintf(message, message_len, "n,,n=%s,r=%s", node, nonce);
|
||
}
|
||
xmpp_free(ctx, node);
|
||
|
||
return message;
|
||
}
|
||
|
||
static xmpp_stanza_t *_make_starttls(xmpp_conn_t * const conn)
|
||
{
|
||
xmpp_stanza_t *starttls;
|
||
|
||
/* build start stanza */
|
||
starttls = xmpp_stanza_new(conn->ctx);
|
||
if (starttls) {
|
||
xmpp_stanza_set_name(starttls, "starttls");
|
||
xmpp_stanza_set_ns(starttls, XMPP_NS_TLS);
|
||
}
|
||
|
||
return starttls;
|
||
}
|
||
|
||
static xmpp_stanza_t *_make_sasl_auth(xmpp_conn_t * const conn,
|
||
const char * const mechanism)
|
||
{
|
||
xmpp_stanza_t *auth;
|
||
|
||
/* build auth stanza */
|
||
auth = xmpp_stanza_new(conn->ctx);
|
||
if (auth) {
|
||
xmpp_stanza_set_name(auth, "auth");
|
||
xmpp_stanza_set_ns(auth, XMPP_NS_SASL);
|
||
xmpp_stanza_set_attribute(auth, "mechanism", mechanism);
|
||
}
|
||
|
||
return auth;
|
||
}
|
||
|
||
/* authenticate the connection
|
||
* this may get called multiple times. if any auth method fails,
|
||
* this will get called again until one auth method succeeds or every
|
||
* method fails
|
||
*/
|
||
static void _auth(xmpp_conn_t * const conn)
|
||
{
|
||
xmpp_stanza_t *auth;
|
||
xmpp_stanza_t *authdata;
|
||
char *authid;
|
||
char *scram_init;
|
||
char *str;
|
||
int anonjid;
|
||
|
||
/* if there is no node in conn->jid, we assume anonymous connect */
|
||
str = xmpp_jid_node(conn->ctx, conn->jid);
|
||
if (str == NULL) {
|
||
anonjid = 1;
|
||
} else {
|
||
xmpp_free(conn->ctx, str);
|
||
anonjid = 0;
|
||
}
|
||
|
||
if (conn->tls_support) {
|
||
tls_t *tls = tls_new(conn);
|
||
|
||
/* If we couldn't init tls, it isn't there, so go on */
|
||
if (!tls) {
|
||
conn->tls_support = 0;
|
||
_auth(conn);
|
||
return;
|
||
} else {
|
||
tls_free(tls);
|
||
}
|
||
|
||
auth = _make_starttls(conn);
|
||
|
||
if (!auth) {
|
||
disconnect_mem_error(conn);
|
||
return;
|
||
}
|
||
|
||
handler_add(conn, _handle_proceedtls_default,
|
||
XMPP_NS_TLS, NULL, NULL, NULL);
|
||
|
||
xmpp_send(conn, auth);
|
||
xmpp_stanza_release(auth);
|
||
|
||
/* TLS was tried, unset flag */
|
||
conn->tls_support = 0;
|
||
/* _auth() will be called later */
|
||
return;
|
||
}
|
||
|
||
if (conn->tls_mandatory && !xmpp_conn_is_secured(conn)) {
|
||
xmpp_error(conn->ctx, "xmpp", "TLS is not supported, but set as "
|
||
"mandatory for this connection");
|
||
conn_disconnect(conn);
|
||
return;
|
||
}
|
||
|
||
if (anonjid && conn->sasl_support & SASL_MASK_ANONYMOUS) {
|
||
/* some crap here */
|
||
auth = _make_sasl_auth(conn, "ANONYMOUS");
|
||
if (!auth) {
|
||
disconnect_mem_error(conn);
|
||
return;
|
||
}
|
||
|
||
handler_add(conn, _handle_sasl_result, XMPP_NS_SASL,
|
||
NULL, NULL, "ANONYMOUS");
|
||
|
||
xmpp_send(conn, auth);
|
||
xmpp_stanza_release(auth);
|
||
|
||
/* SASL ANONYMOUS was tried, unset flag */
|
||
conn->sasl_support &= ~SASL_MASK_ANONYMOUS;
|
||
} else if (anonjid) {
|
||
xmpp_error(conn->ctx, "auth",
|
||
"No node in JID, and SASL ANONYMOUS unsupported.");
|
||
xmpp_disconnect(conn);
|
||
} else if (conn->sasl_support & SASL_MASK_SCRAMSHA1) {
|
||
auth = _make_sasl_auth(conn, "SCRAM-SHA-1");
|
||
if (!auth) {
|
||
disconnect_mem_error(conn);
|
||
return;
|
||
}
|
||
|
||
/* don't free scram_init on success */
|
||
scram_init = _make_scram_sha1_init_msg(conn);
|
||
if (!scram_init) {
|
||
xmpp_stanza_release(auth);
|
||
disconnect_mem_error(conn);
|
||
return;
|
||
}
|
||
|
||
str = xmpp_base64_encode(conn->ctx, (unsigned char *)scram_init,
|
||
strlen(scram_init));
|
||
if (!str) {
|
||
xmpp_free(conn->ctx, scram_init);
|
||
xmpp_stanza_release(auth);
|
||
disconnect_mem_error(conn);
|
||
return;
|
||
}
|
||
|
||
authdata = xmpp_stanza_new(conn->ctx);
|
||
if (!authdata) {
|
||
xmpp_free(conn->ctx, str);
|
||
xmpp_free(conn->ctx, scram_init);
|
||
xmpp_stanza_release(auth);
|
||
disconnect_mem_error(conn);
|
||
return;
|
||
}
|
||
xmpp_stanza_set_text(authdata, str);
|
||
xmpp_free(conn->ctx, str);
|
||
xmpp_stanza_add_child(auth, authdata);
|
||
xmpp_stanza_release(authdata);
|
||
|
||
handler_add(conn, _handle_scram_sha1_challenge,
|
||
XMPP_NS_SASL, NULL, NULL, (void *)scram_init);
|
||
|
||
xmpp_send(conn, auth);
|
||
xmpp_stanza_release(auth);
|
||
|
||
/* SASL SCRAM-SHA-1 was tried, unset flag */
|
||
conn->sasl_support &= ~SASL_MASK_SCRAMSHA1;
|
||
} else if (conn->sasl_support & SASL_MASK_DIGESTMD5) {
|
||
auth = _make_sasl_auth(conn, "DIGEST-MD5");
|
||
if (!auth) {
|
||
disconnect_mem_error(conn);
|
||
return;
|
||
|
||
}
|
||
|
||
handler_add(conn, _handle_digestmd5_challenge,
|
||
XMPP_NS_SASL, NULL, NULL, NULL);
|
||
|
||
xmpp_send(conn, auth);
|
||
xmpp_stanza_release(auth);
|
||
|
||
/* SASL DIGEST-MD5 was tried, unset flag */
|
||
conn->sasl_support &= ~SASL_MASK_DIGESTMD5;
|
||
} else if (conn->sasl_support & SASL_MASK_PLAIN) {
|
||
auth = _make_sasl_auth(conn, "PLAIN");
|
||
if (!auth) {
|
||
disconnect_mem_error(conn);
|
||
return;
|
||
}
|
||
authdata = xmpp_stanza_new(conn->ctx);
|
||
if (!authdata) {
|
||
disconnect_mem_error(conn);
|
||
return;
|
||
}
|
||
authid = _get_authid(conn);
|
||
if (!authid) {
|
||
disconnect_mem_error(conn);
|
||
return;
|
||
}
|
||
str = sasl_plain(conn->ctx, authid, conn->pass);
|
||
if (!str) {
|
||
disconnect_mem_error(conn);
|
||
return;
|
||
}
|
||
xmpp_stanza_set_text(authdata, str);
|
||
xmpp_free(conn->ctx, str);
|
||
xmpp_free(conn->ctx, authid);
|
||
|
||
xmpp_stanza_add_child(auth, authdata);
|
||
xmpp_stanza_release(authdata);
|
||
|
||
handler_add(conn, _handle_sasl_result,
|
||
XMPP_NS_SASL, NULL, NULL, "PLAIN");
|
||
|
||
xmpp_send(conn, auth);
|
||
xmpp_stanza_release(auth);
|
||
|
||
/* SASL PLAIN was tried */
|
||
conn->sasl_support &= ~SASL_MASK_PLAIN;
|
||
} else if (conn->type == XMPP_CLIENT && conn->auth_legacy_enabled) {
|
||
/* legacy client authentication */
|
||
_auth_legacy(conn);
|
||
} else {
|
||
xmpp_error(conn->ctx, "auth", "Cannot authenticate with known methods");
|
||
xmpp_disconnect(conn);
|
||
}
|
||
}
|
||
|
||
|
||
/** Set up handlers at stream start.
|
||
* This function is called internally to Strophe for handling the opening
|
||
* of an XMPP stream. It's called by the parser when a stream is opened
|
||
* or reset, and adds the initial handlers for <stream:error/> and
|
||
* <stream:features/>. This function is not intended for use outside
|
||
* of Strophe.
|
||
*
|
||
* @param conn a Strophe connection object
|
||
*/
|
||
void auth_handle_open(xmpp_conn_t * const conn)
|
||
{
|
||
/* reset all timed handlers */
|
||
handler_reset_timed(conn, 0);
|
||
|
||
/* setup handler for stream:error, we will keep this handler
|
||
* for reopened streams until connection is disconnected */
|
||
handler_add(conn, _handle_error, XMPP_NS_STREAMS, "error", NULL, NULL);
|
||
|
||
/* setup handlers for incoming <stream:features> */
|
||
handler_add(conn, _handle_features,
|
||
XMPP_NS_STREAMS, "features", NULL, NULL);
|
||
handler_add_timed(conn, _handle_missing_features, FEATURES_TIMEOUT, NULL);
|
||
}
|
||
|
||
/* called when stream:stream tag received after TLS establishment */
|
||
static void _handle_open_tls(xmpp_conn_t * const conn)
|
||
{
|
||
/* setup handlers for incoming <stream:features> */
|
||
handler_add(conn, _handle_features,
|
||
XMPP_NS_STREAMS, "features", NULL, NULL);
|
||
handler_add_timed(conn, _handle_missing_features, FEATURES_TIMEOUT, NULL);
|
||
}
|
||
|
||
/* called when stream:stream tag received after SASL auth */
|
||
static void _handle_open_sasl(xmpp_conn_t * const conn)
|
||
{
|
||
xmpp_debug(conn->ctx, "xmpp", "Reopened stream successfully.");
|
||
|
||
/* setup stream:features handlers */
|
||
handler_add(conn, _handle_features_sasl,
|
||
XMPP_NS_STREAMS, "features", NULL, NULL);
|
||
handler_add_timed(conn, _handle_missing_features_sasl,
|
||
FEATURES_TIMEOUT, NULL);
|
||
}
|
||
|
||
static int _handle_features_sasl(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata)
|
||
{
|
||
xmpp_stanza_t *bind, *session, *iq, *res, *text, *opt;
|
||
const char *ns;
|
||
char *resource;
|
||
|
||
/* remove missing features handler */
|
||
xmpp_timed_handler_delete(conn, _handle_missing_features_sasl);
|
||
|
||
/* we are expecting <bind/> and <session/> since this is a
|
||
XMPP style connection */
|
||
|
||
/* check whether resource binding is required */
|
||
bind = xmpp_stanza_get_child_by_name(stanza, "bind");
|
||
if (bind) {
|
||
ns = xmpp_stanza_get_ns(bind);
|
||
conn->bind_required = ns != NULL && strcmp(ns, XMPP_NS_BIND) == 0;
|
||
}
|
||
|
||
/* check whether session establishment is required */
|
||
session = xmpp_stanza_get_child_by_name(stanza, "session");
|
||
if (session) {
|
||
ns = xmpp_stanza_get_ns(session);
|
||
opt = xmpp_stanza_get_child_by_name(session, "optional");
|
||
if (!opt)
|
||
conn->session_required = ns != NULL &&
|
||
strcmp(ns, XMPP_NS_SESSION) == 0;
|
||
}
|
||
|
||
/* if bind is required, go ahead and start it */
|
||
if (conn->bind_required) {
|
||
/* bind resource */
|
||
|
||
/* setup response handlers */
|
||
handler_add_id(conn, _handle_bind, "_xmpp_bind1", NULL);
|
||
handler_add_timed(conn, _handle_missing_bind,
|
||
BIND_TIMEOUT, NULL);
|
||
|
||
/* send bind request */
|
||
iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_bind1");
|
||
if (!iq) {
|
||
disconnect_mem_error(conn);
|
||
return 0;
|
||
}
|
||
|
||
bind = xmpp_stanza_copy(bind);
|
||
if (!bind) {
|
||
xmpp_stanza_release(iq);
|
||
disconnect_mem_error(conn);
|
||
return 0;
|
||
}
|
||
|
||
/* request a specific resource if we have one */
|
||
resource = xmpp_jid_resource(conn->ctx, conn->jid);
|
||
if ((resource != NULL) && (strlen(resource) == 0)) {
|
||
/* jabberd2 doesn't handle an empty resource */
|
||
xmpp_free(conn->ctx, resource);
|
||
resource = NULL;
|
||
}
|
||
|
||
/* if we have a resource to request, do it. otherwise the
|
||
server will assign us one */
|
||
if (resource) {
|
||
res = xmpp_stanza_new(conn->ctx);
|
||
if (!res) {
|
||
xmpp_stanza_release(bind);
|
||
xmpp_stanza_release(iq);
|
||
disconnect_mem_error(conn);
|
||
return 0;
|
||
}
|
||
xmpp_stanza_set_name(res, "resource");
|
||
text = xmpp_stanza_new(conn->ctx);
|
||
if (!text) {
|
||
xmpp_stanza_release(res);
|
||
xmpp_stanza_release(bind);
|
||
xmpp_stanza_release(iq);
|
||
disconnect_mem_error(conn);
|
||
return 0;
|
||
}
|
||
xmpp_stanza_set_text(text, resource);
|
||
xmpp_stanza_add_child(res, text);
|
||
xmpp_stanza_release(text);
|
||
xmpp_stanza_add_child(bind, res);
|
||
xmpp_stanza_release(res);
|
||
xmpp_free(conn->ctx, resource);
|
||
}
|
||
|
||
xmpp_stanza_add_child(iq, bind);
|
||
xmpp_stanza_release(bind);
|
||
|
||
/* send bind request */
|
||
xmpp_send(conn, iq);
|
||
xmpp_stanza_release(iq);
|
||
} else {
|
||
/* can't bind, disconnect */
|
||
xmpp_error(conn->ctx, "xmpp", "Stream features does not allow "\
|
||
"resource bind.");
|
||
xmpp_disconnect(conn);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int _handle_missing_features_sasl(xmpp_conn_t * const conn,
|
||
void * const userdata)
|
||
{
|
||
xmpp_error(conn->ctx, "xmpp", "Did not receive stream features "\
|
||
"after SASL authentication.");
|
||
xmpp_disconnect(conn);
|
||
return 0;
|
||
}
|
||
|
||
static int _handle_bind(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata)
|
||
{
|
||
const char *type;
|
||
xmpp_stanza_t *iq, *session;
|
||
|
||
/* delete missing bind handler */
|
||
xmpp_timed_handler_delete(conn, _handle_missing_bind);
|
||
|
||
/* server has replied to bind request */
|
||
type = xmpp_stanza_get_type(stanza);
|
||
if (type && strcmp(type, "error") == 0) {
|
||
xmpp_error(conn->ctx, "xmpp", "Binding failed.");
|
||
xmpp_disconnect(conn);
|
||
} else if (type && strcmp(type, "result") == 0) {
|
||
xmpp_stanza_t *binding = xmpp_stanza_get_child_by_name(stanza, "bind");
|
||
xmpp_debug(conn->ctx, "xmpp", "Bind successful.");
|
||
|
||
if (binding) {
|
||
xmpp_stanza_t *jid_stanza = xmpp_stanza_get_child_by_name(binding,
|
||
"jid");
|
||
if (jid_stanza) {
|
||
conn->bound_jid = xmpp_stanza_get_text(jid_stanza);
|
||
}
|
||
}
|
||
|
||
/* establish a session if required */
|
||
if (conn->session_required) {
|
||
/* setup response handlers */
|
||
handler_add_id(conn, _handle_session, "_xmpp_session1", NULL);
|
||
handler_add_timed(conn, _handle_missing_session,
|
||
SESSION_TIMEOUT, NULL);
|
||
|
||
/* send session request */
|
||
iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_session1");
|
||
if (!iq) {
|
||
disconnect_mem_error(conn);
|
||
return 0;
|
||
}
|
||
|
||
session = xmpp_stanza_new(conn->ctx);
|
||
if (!session) {
|
||
xmpp_stanza_release(iq);
|
||
disconnect_mem_error(conn);
|
||
return 0;
|
||
}
|
||
|
||
xmpp_stanza_set_name(session, "session");
|
||
xmpp_stanza_set_ns(session, XMPP_NS_SESSION);
|
||
|
||
xmpp_stanza_add_child(iq, session);
|
||
xmpp_stanza_release(session);
|
||
|
||
/* send session establishment request */
|
||
xmpp_send(conn, iq);
|
||
xmpp_stanza_release(iq);
|
||
} else {
|
||
conn->authenticated = 1;
|
||
|
||
/* call connection handler */
|
||
conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL,
|
||
conn->userdata);
|
||
}
|
||
} else {
|
||
xmpp_error(conn->ctx, "xmpp", "Server sent malformed bind reply.");
|
||
xmpp_disconnect(conn);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int _handle_missing_bind(xmpp_conn_t * const conn,
|
||
void * const userdata)
|
||
{
|
||
xmpp_error(conn->ctx, "xmpp", "Server did not reply to bind request.");
|
||
xmpp_disconnect(conn);
|
||
return 0;
|
||
}
|
||
|
||
static int _handle_session(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata)
|
||
{
|
||
const char *type;
|
||
|
||
/* delete missing session handler */
|
||
xmpp_timed_handler_delete(conn, _handle_missing_session);
|
||
|
||
/* server has replied to the session request */
|
||
type = xmpp_stanza_get_type(stanza);
|
||
if (type && strcmp(type, "error") == 0) {
|
||
xmpp_error(conn->ctx, "xmpp", "Session establishment failed.");
|
||
xmpp_disconnect(conn);
|
||
} else if (type && strcmp(type, "result") == 0) {
|
||
xmpp_debug(conn->ctx, "xmpp", "Session establishment successful.");
|
||
|
||
conn->authenticated = 1;
|
||
|
||
/* call connection handler */
|
||
conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
|
||
} else {
|
||
xmpp_error(conn->ctx, "xmpp", "Server sent malformed session reply.");
|
||
xmpp_disconnect(conn);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int _handle_missing_session(xmpp_conn_t * const conn,
|
||
void * const userdata)
|
||
{
|
||
xmpp_error(conn->ctx, "xmpp", "Server did not reply to session request.");
|
||
xmpp_disconnect(conn);
|
||
return 0;
|
||
}
|
||
|
||
static int _handle_missing_legacy(xmpp_conn_t * const conn,
|
||
void * const userdata)
|
||
{
|
||
xmpp_error(conn->ctx, "xmpp", "Server did not reply to legacy "\
|
||
"authentication request.");
|
||
xmpp_disconnect(conn);
|
||
return 0;
|
||
}
|
||
|
||
static int _handle_legacy(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata)
|
||
{
|
||
const char *type;
|
||
const char *name;
|
||
|
||
/* delete missing handler */
|
||
xmpp_timed_handler_delete(conn, _handle_missing_legacy);
|
||
|
||
/* server responded to legacy auth request */
|
||
type = xmpp_stanza_get_type(stanza);
|
||
name = xmpp_stanza_get_name(stanza);
|
||
if (!type || strcmp(name, "iq") != 0) {
|
||
xmpp_error(conn->ctx, "xmpp", "Server sent us an unexpected response "\
|
||
"to legacy authentication request.");
|
||
xmpp_disconnect(conn);
|
||
} else if (strcmp(type, "error") == 0) {
|
||
/* legacy client auth failed, no more fallbacks */
|
||
xmpp_error(conn->ctx, "xmpp", "Legacy client authentication failed.");
|
||
xmpp_disconnect(conn);
|
||
} else if (strcmp(type, "result") == 0) {
|
||
/* auth succeeded */
|
||
xmpp_debug(conn->ctx, "xmpp", "Legacy auth succeeded.");
|
||
|
||
conn->authenticated = 1;
|
||
conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
|
||
} else {
|
||
xmpp_error(conn->ctx, "xmpp", "Server sent us a legacy authentication "\
|
||
"response with a bad type.");
|
||
xmpp_disconnect(conn);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static void _auth_legacy(xmpp_conn_t *conn)
|
||
{
|
||
xmpp_stanza_t *iq;
|
||
xmpp_stanza_t *authdata;
|
||
xmpp_stanza_t *query;
|
||
xmpp_stanza_t *child;
|
||
char *str;
|
||
|
||
xmpp_debug(conn->ctx, "auth", "Legacy authentication request");
|
||
|
||
iq = xmpp_iq_new(conn->ctx, "set", "_xmpp_auth1");
|
||
if (!iq)
|
||
goto err;
|
||
|
||
query = xmpp_stanza_new(conn->ctx);
|
||
if (!query)
|
||
goto err_free;
|
||
xmpp_stanza_set_name(query, "query");
|
||
xmpp_stanza_set_ns(query, XMPP_NS_AUTH);
|
||
xmpp_stanza_add_child(iq, query);
|
||
xmpp_stanza_release(query);
|
||
|
||
child = xmpp_stanza_new(conn->ctx);
|
||
if (!child)
|
||
goto err_free;
|
||
xmpp_stanza_set_name(child, "username");
|
||
xmpp_stanza_add_child(query, child);
|
||
xmpp_stanza_release(child);
|
||
|
||
authdata = xmpp_stanza_new(conn->ctx);
|
||
if (!authdata)
|
||
goto err_free;
|
||
str = xmpp_jid_node(conn->ctx, conn->jid);
|
||
if (!str) {
|
||
xmpp_stanza_release(authdata);
|
||
goto err_free;
|
||
}
|
||
xmpp_stanza_set_text(authdata, str);
|
||
xmpp_free(conn->ctx, str);
|
||
xmpp_stanza_add_child(child, authdata);
|
||
xmpp_stanza_release(authdata);
|
||
|
||
child = xmpp_stanza_new(conn->ctx);
|
||
if (!child)
|
||
goto err_free;
|
||
xmpp_stanza_set_name(child, "password");
|
||
xmpp_stanza_add_child(query, child);
|
||
xmpp_stanza_release(child);
|
||
|
||
authdata = xmpp_stanza_new(conn->ctx);
|
||
if (!authdata)
|
||
goto err_free;
|
||
xmpp_stanza_set_text(authdata, conn->pass);
|
||
xmpp_stanza_add_child(child, authdata);
|
||
xmpp_stanza_release(authdata);
|
||
|
||
child = xmpp_stanza_new(conn->ctx);
|
||
if (!child)
|
||
goto err_free;
|
||
xmpp_stanza_set_name(child, "resource");
|
||
xmpp_stanza_add_child(query, child);
|
||
xmpp_stanza_release(child);
|
||
|
||
authdata = xmpp_stanza_new(conn->ctx);
|
||
if (!authdata)
|
||
goto err_free;
|
||
str = xmpp_jid_resource(conn->ctx, conn->jid);
|
||
if (str) {
|
||
xmpp_stanza_set_text(authdata, str);
|
||
xmpp_free(conn->ctx, str);
|
||
} else {
|
||
xmpp_stanza_release(authdata);
|
||
xmpp_stanza_release(iq);
|
||
xmpp_error(conn->ctx, "auth", "Cannot authenticate without resource");
|
||
xmpp_disconnect(conn);
|
||
return;
|
||
}
|
||
xmpp_stanza_add_child(child, authdata);
|
||
xmpp_stanza_release(authdata);
|
||
|
||
handler_add_id(conn, _handle_legacy, "_xmpp_auth1", NULL);
|
||
handler_add_timed(conn, _handle_missing_legacy, LEGACY_TIMEOUT, NULL);
|
||
|
||
xmpp_send(conn, iq);
|
||
xmpp_stanza_release(iq);
|
||
return;
|
||
|
||
err_free:
|
||
xmpp_stanza_release(iq);
|
||
err:
|
||
disconnect_mem_error(conn);
|
||
}
|
||
|
||
void auth_handle_component_open(xmpp_conn_t * const conn)
|
||
{
|
||
int rc;
|
||
|
||
/* reset all timed handlers */
|
||
handler_reset_timed(conn, 0);
|
||
|
||
handler_add(conn, _handle_error, XMPP_NS_STREAMS, "error", NULL, NULL);
|
||
handler_add(conn, _handle_component_hs_response, NULL,
|
||
"handshake", NULL, NULL);
|
||
handler_add_timed(conn, _handle_missing_handshake, HANDSHAKE_TIMEOUT, NULL);
|
||
|
||
rc = _handle_component_auth(conn);
|
||
if (rc != 0) {
|
||
xmpp_error(conn->ctx, "auth", "Component authentication failed.");
|
||
xmpp_disconnect(conn);
|
||
}
|
||
}
|
||
|
||
/* Will compute SHA1 and authenticate the component to the server */
|
||
int _handle_component_auth(xmpp_conn_t * const conn)
|
||
{
|
||
uint8_t md_value[SHA1_DIGEST_SIZE];
|
||
SHA1_CTX mdctx;
|
||
char *digest;
|
||
size_t i;
|
||
|
||
if (conn->stream_id == NULL) {
|
||
xmpp_error(conn->ctx, "auth", "Received no stream id from the server.");
|
||
return XMPP_EINT;
|
||
}
|
||
|
||
/* Feed the session id and passphrase to the algorithm.
|
||
* We need to compute SHA1(session_id + passphrase)
|
||
*/
|
||
crypto_SHA1_Init(&mdctx);
|
||
crypto_SHA1_Update(&mdctx, (uint8_t*)conn->stream_id,
|
||
strlen(conn->stream_id));
|
||
crypto_SHA1_Update(&mdctx, (uint8_t*)conn->pass, strlen(conn->pass));
|
||
crypto_SHA1_Final(&mdctx, md_value);
|
||
|
||
digest = xmpp_alloc(conn->ctx, 2*sizeof(md_value)+1);
|
||
if (digest) {
|
||
/* convert the digest into string representation */
|
||
for (i = 0; i < sizeof(md_value); i++)
|
||
xmpp_snprintf(digest+i*2, 3, "%02x", md_value[i]);
|
||
digest[2*sizeof(md_value)] = '\0';
|
||
|
||
xmpp_debug(conn->ctx, "auth", "Digest: %s, len: %d",
|
||
digest, strlen(digest));
|
||
|
||
/* Send the digest to the server */
|
||
xmpp_send_raw_string(conn, "<handshake xmlns='%s'>%s</handshake>",
|
||
XMPP_NS_COMPONENT, digest);
|
||
xmpp_debug(conn->ctx, "auth", "Sent component handshake to the server.");
|
||
xmpp_free(conn->ctx, digest);
|
||
} else {
|
||
xmpp_debug(conn->ctx, "auth", "Couldn't allocate memory for component "\
|
||
"handshake digest.");
|
||
return XMPP_EMEM;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Check if the received stanza is <handshake/> and set auth to true
|
||
* and fire connection handler.
|
||
*/
|
||
int _handle_component_hs_response(xmpp_conn_t * const conn,
|
||
xmpp_stanza_t * const stanza,
|
||
void * const userdata)
|
||
{
|
||
const char *name;
|
||
|
||
xmpp_timed_handler_delete(conn, _handle_missing_handshake);
|
||
|
||
name = xmpp_stanza_get_name(stanza);
|
||
if (strcmp(name, "handshake") != 0) {
|
||
char *msg;
|
||
size_t msg_size;
|
||
xmpp_stanza_to_text(stanza, &msg, &msg_size);
|
||
if (msg) {
|
||
xmpp_debug(conn->ctx, "auth", "Handshake failed: %s", msg);
|
||
xmpp_free(conn->ctx, msg);
|
||
}
|
||
xmpp_disconnect(conn);
|
||
return XMPP_EINT;
|
||
} else {
|
||
conn->authenticated = 1;
|
||
conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
|
||
}
|
||
|
||
/* We don't need this handler anymore, return 0 so it can be deleted
|
||
* from the list of handlers.
|
||
*/
|
||
return 0;
|
||
}
|
||
|
||
int _handle_missing_handshake(xmpp_conn_t * const conn, void * const userdata)
|
||
{
|
||
xmpp_error(conn->ctx, "xmpp", "Server did not reply to handshake request.");
|
||
xmpp_disconnect(conn);
|
||
return 0;
|
||
}
|
||
|
||
void auth_handle_open_raw(xmpp_conn_t * const conn)
|
||
{
|
||
handler_reset_timed(conn, 0);
|
||
/* user handlers are not called before authentication is completed. */
|
||
conn->authenticated = 1;
|
||
conn->conn_handler(conn, XMPP_CONN_CONNECT, 0, NULL, conn->userdata);
|
||
}
|
||
|
||
void auth_handle_open_stub(xmpp_conn_t * const conn)
|
||
{
|
||
xmpp_warn(conn->ctx, "auth", "Stub callback is called.");
|
||
}
|