PLAIN mechanism sends password in BASE64 encoding which everyone can read. As result, debug logs expose password and users often don't remove it when post the logs in the Internet. Usually, both a secure mechanism and PLAIN are used in the scenario when username or password is incorrect. PLAIN fails in this scenario anyway.
1272 lines
36 KiB
C
1272 lines
36 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
|
||
#define strcasecmp stricmp
|
||
#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) {
|
||
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.");
|
||
}
|