diff --git a/src/auth.c b/src/auth.c index 19fe1d9..0b972d3 100644 --- a/src/auth.c +++ b/src/auth.c @@ -18,6 +18,10 @@ #include #include +/* Added by Milan Kubik. + * Needed for component handshake handler (xep-0114) to compute SHA1 digest. */ +#include + #include "strophe.h" #include "common.h" #include "sasl.h" @@ -52,9 +56,21 @@ */ #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 _handle_open_sasl(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_missing_legacy(xmpp_conn_t * const conn, void * const userdata); static int _handle_legacy(xmpp_conn_t * const conn, @@ -89,6 +105,8 @@ static int _handle_session(xmpp_conn_t * const conn, 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, @@ -1148,3 +1166,108 @@ static int _handle_missing_legacy(xmpp_conn_t * const conn, return 0; } +void auth_handle_component_open(xmpp_conn_t * const conn) +{ + /* reset all timed handlers */ + handler_reset_timed(conn, 0); + + /* Handler for component accept */ + + handler_add(conn, _handle_error, + NULL, "stream:error", NULL, NULL); + + handler_add(conn, _handle_component_hs_response, NULL, + "handshake", NULL, NULL); + + handler_add_timed(conn, _handle_missing_handshake, + HANDSHAKE_TIMEOUT, NULL); + + _handle_component_auth(conn); +} + +/* Will compute SHA1 and authenticate the component to the server */ +int _handle_component_auth(xmpp_conn_t * const conn) +{ + unsigned char md_value[EVP_MAX_MD_SIZE]; + EVP_MD_CTX *mdctx; + const EVP_MD *md; + char *digest; + unsigned int md_len, i; + + /* Feed the session id and passphrase to the algorithm. + * We need to compute SHA1(session_id + passphrase) + */ + md = EVP_get_digestbyname("sha1"); + mdctx = EVP_MD_CTX_create(); + EVP_DigestInit_ex(mdctx, md, NULL); + + /* Feed in the data. */ + EVP_DigestUpdate(mdctx, conn->stream_id, strlen(conn->stream_id)); + EVP_DigestUpdate(mdctx, conn->pass, strlen(conn->pass)); + EVP_DigestFinal_ex(mdctx, md_value, &md_len); + EVP_MD_CTX_destroy(mdctx); + + digest = malloc(2*md_len+1); + if (digest) { + memset(digest, 0, 2*md_len+1); + /* convert the digest into string representation */ + for (i = 0; i < md_len; i++) + snprintf(digest+i*2, 3,"%02x", md_value[i]); + + xmpp_debug(conn->ctx, "auth", "Digest: %s, len: %d", (char *) digest, strlen((char *)digest)); + + /* Send the digest to the server */ + xmpp_send_raw_string(conn, "%s", + XMPP_NS_COMPONENT, digest); + xmpp_debug(conn->ctx, "auth", "Sent component handshake to the server."); + free(digest); + } else { + xmpp_debug(conn->ctx, "auth", "Couldn't allocate memory for component handshake digest."); + xmpp_disconnect(conn); + return XMPP_EMEM; + } + + return 0; +} + +int _handle_component_hs_response(xmpp_conn_t * const conn, + xmpp_stanza_t * const stanza, + void * const userdata) +{ + /* Check if the received stanza is and set auth to true + * and fire connection handler. + */ + + 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); + + xmpp_debug(conn->ctx, "auth", "Handshake failed: %s", + msg); + xmpp_disconnect(conn); + free(msg); + 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; +} diff --git a/src/common.h b/src/common.h index 12d256b..fb6b6eb 100644 --- a/src/common.h +++ b/src/common.h @@ -264,6 +264,7 @@ void disconnect_mem_error(xmpp_conn_t * const conn); /* auth functions */ void auth_handle_open(xmpp_conn_t * const conn); +void auth_handle_component_open(xmpp_conn_t * const conn); /* replacement snprintf and vsnprintf */ int xmpp_snprintf (char *str, size_t count, const char *fmt, ...); diff --git a/src/conn.c b/src/conn.c index 17af864..d71ae9d 100644 --- a/src/conn.c +++ b/src/conn.c @@ -439,6 +439,73 @@ int xmpp_connect_client(xmpp_conn_t * const conn, return 0; } +/** Initiate a component connection to server. + * This function returns immediately after starting the connection + * process to the XMPP server, and notifiations of connection state changes + * will be sent to the internal callback function that will set up handler + * for the component handshake as defined in XEP-0114. + * The domain and port to connect to must be provided in this case as the JID + * provided to the call serves as component identifier to the server and is + * not subject to DNS resolution. + * + * @param conn a Strophe connection object + * @param server a string with domain to use directly as the domain can't be + * extracted from the component name/JID. If this is not set, the call + * will fail. + * @param port an integer port number to use to connect to server expecting + * an external component. If this is 0, the port 5347 will be assumed. + * @param callback a xmpp_conn_handler callback function that will receive + * notifications of connection status + * @param userdata an opaque data pointer that will be passed to the callback + * + * @return 0 on success and -1 on an error + * + * @ingroup Connections + */ +int xmpp_connect_component(xmpp_conn_t * const conn, const char * const server, + unsigned short port, xmpp_conn_handler callback, + void * const userdata) +{ + int connectport; + + conn->type = XMPP_COMPONENT; + /* JID serves as an identificator here and will be used as "to" attribute + of the stream */ + conn->domain = xmpp_strdup(conn->ctx, conn->jid); + + /* The server domain, jid and password MUST be specified. */ + if (!(server && conn->jid && conn->pass)) return -1; + + + connectport = port ? port : 5347; + + xmpp_debug(conn->ctx, "xmpp", "Connecting via %s", server); + conn->sock = sock_connect(server, connectport); + xmpp_debug(conn->ctx, "xmpp", "sock_connect to %s:%d returned %d", + server, connectport, conn->sock); + if (conn->sock == -1) return -1; + + /* XEP-0114 does not support TLS */ + conn->tls_disabled = 1; + + /* setup handler */ + conn->conn_handler = callback; + conn->userdata = userdata; + + conn_prepare_reset(conn, auth_handle_component_open); + + /* FIXME: it could happen that the connect returns immediately as + * successful, though this is pretty unlikely. This would be a little + * hard to fix, since we'd have to detect and fire off the callback + * from within the event loop */ + + conn->state = XMPP_STATE_CONNECTING; + conn->timeout_stamp = time_stamp(); + xmpp_debug(conn->ctx, "xmpp", "attempting to connect to %s", server); + + return 0; +} + /** Cleanly disconnect the connection. * This function is only called by the stream parser when * is received, and it not intended to be called by code outside of Strophe. @@ -740,7 +807,7 @@ static void _handle_stream_start(char *name, char **attrs, conn_disconnect(conn); } } - + /* call stream open handler */ conn->open_handler(conn); } diff --git a/strophe.h b/strophe.h index 2f29381..80a5390 100644 --- a/strophe.h +++ b/strophe.h @@ -222,9 +222,10 @@ int xmpp_connect_client(xmpp_conn_t * const conn, xmpp_conn_handler callback, void * const userdata); -/* -int xmpp_connect_component(conn, name) -*/ +int xmpp_connect_component(xmpp_conn_t * const conn, const char * const server, + unsigned short port, xmpp_conn_handler callback, + void * const userdata); + void xmpp_disconnect(xmpp_conn_t * const conn); void xmpp_send(xmpp_conn_t * const conn,