diff --git a/ChangeLog b/ChangeLog index 1569633..bfedde7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,5 @@ -0.8.5 - - libtoolize to generate .so +TBA + - SCRAM-SHA-1 authentication mechanism +0.8.5 + - libtoolize to generate .so diff --git a/Makefile.am b/Makefile.am index 8e7d9b7..f942380 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,15 +14,18 @@ STROPHE_LIBS = libstrophe.la ## Main build targets lib_LTLIBRARIES = libstrophe.la -libstrophe_la_CFLAGS=$(STROPHE_FLAGS) $(PARSER_CFLAGS) -libstrophe_la_LDFLAGS=$(SSL_LIBS) $(PARSER_LIBS) +libstrophe_la_CFLAGS = $(STROPHE_FLAGS) $(PARSER_CFLAGS) +libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) +# Export only public API +libstrophe_la_LDFLAGS += -export-symbols-regex '^xmpp_' libstrophe_la_SOURCES = src/auth.c src/conn.c src/ctx.c \ src/event.c src/handler.c src/hash.c \ - src/jid.c src/md5.c src/sasl.c src/sha1.c \ + src/jid.c src/md5.c src/sasl.c src/scram.c src/sha1.c \ src/snprintf.c src/sock.c src/stanza.c src/thread.c \ src/tls_openssl.c src/util.c \ src/common.h src/hash.h src/md5.h src/ostypes.h src/parser.h \ - src/sasl.h src/sha1.h src/sock.h src/thread.h src/tls.h src/util.h + src/sasl.h src/scram.h src/sha1.h src/sock.h src/thread.h src/tls.h \ + src/util.h if PARSER_EXPAT libstrophe_la_SOURCES += src/parser_expat.c diff --git a/configure.ac b/configure.ac index 2e521f3..7caa7a7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([libstrophe], [0.8.5], [jack@metajack.im]) +AC_INIT([libstrophe], [0.8.7], [jack@metajack.im]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([foreign]) LT_INIT([dlopen]) diff --git a/src/auth.c b/src/auth.c index 7530b27..b7a2a31 100644 --- a/src/auth.c +++ b/src/auth.c @@ -19,12 +19,13 @@ #include #include #include +#include #include "strophe.h" #include "common.h" #include "sasl.h" -#ifdef _WIN32 +#ifdef _MSC_VER #define strcasecmp stricmp #endif @@ -74,6 +75,10 @@ static int _handle_digestmd5_challenge(xmpp_conn_t * const conn, 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); @@ -228,6 +233,8 @@ static int _handle_features(xmpp_conn_t * const conn, 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; @@ -415,6 +422,117 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t * const conn, 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; + 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 = (char *)base64_decode(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 *_get_nonce(xmpp_ctx_t *ctx) +{ + unsigned char buffer[sizeof(clock_t) + sizeof(time_t)] = {0}; + clock_t ticks = clock(); + time_t t; + + if (ticks != (clock_t)-1) { + *(clock_t *)buffer = ticks; + } + t = time((time_t *)(buffer + sizeof(clock_t))); + if (t == (time_t)-1) { + *(time_t *)(buffer + sizeof(clock_t)) = (time_t)rand(); + } + + return base64_encode(ctx, buffer, sizeof(buffer)); +} + +static char *_make_scram_sha1_init_msg(xmpp_conn_t * const conn) +{ + size_t message_len; + char *node; + char *message; + char *nonce; + + node = xmpp_jid_node(conn->ctx, conn->jid); + if (!node) { + return NULL; + } + + nonce = _get_nonce(conn->ctx); + if (!nonce) { + return NULL; + } + message_len = strlen(node) + strlen(nonce) + 8 + 1; + message = xmpp_alloc(conn->ctx, message_len); + if (message) { + xmpp_snprintf(message, message_len, "n,,n=%s,r=%s", node, nonce); + xmpp_free(conn->ctx, node); + } + xmpp_free(conn->ctx, nonce); + + return message; +} + static xmpp_stanza_t *_make_starttls(xmpp_conn_t * const conn) { xmpp_stanza_t *starttls; @@ -454,6 +572,7 @@ static void _auth(xmpp_conn_t * const conn) { xmpp_stanza_t *auth, *authdata, *query, *child, *iq; char *str, *authid; + char *scram_init; int anonjid; /* if there is no node in conn->jid, we assume anonymous connect */ @@ -516,6 +635,51 @@ static void _auth(xmpp_conn_t * const conn) 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 = (char *)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) { diff --git a/src/common.h b/src/common.h index 9434e6f..c0a6834 100644 --- a/src/common.h +++ b/src/common.h @@ -21,12 +21,10 @@ #include #include -#ifndef _WIN32 -#include -#endif #include "strophe.h" +#include "ostypes.h" #include "sock.h" #include "tls.h" #include "hash.h" @@ -147,6 +145,7 @@ struct _xmpp_handlist_t { #define SASL_MASK_PLAIN 0x01 #define SASL_MASK_DIGESTMD5 0x02 #define SASL_MASK_ANONYMOUS 0x04 +#define SASL_MASK_SCRAMSHA1 0x08 typedef void (*xmpp_open_handler)(xmpp_conn_t * const conn); diff --git a/src/conn.c b/src/conn.c index e35a735..886f481 100644 --- a/src/conn.c +++ b/src/conn.c @@ -270,7 +270,7 @@ int xmpp_conn_release(xmpp_conn_t * const conn) if (conn->domain) xmpp_free(ctx, conn->domain); if (conn->jid) xmpp_free(ctx, conn->jid); - if (conn->bound_jid) xmpp_free(ctx, conn->bound_jid); + if (conn->bound_jid) xmpp_free(ctx, conn->bound_jid); if (conn->pass) xmpp_free(ctx, conn->pass); if (conn->stream_id) xmpp_free(ctx, conn->stream_id); if (conn->lang) xmpp_free(ctx, conn->lang); diff --git a/src/ctx.c b/src/ctx.c index a2975dc..e953394 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -252,29 +252,34 @@ void xmpp_log(const xmpp_ctx_t * const ctx, char *buf; va_list copy; - buf = smbuf; va_copy(copy, ap); - ret = xmpp_vsnprintf(buf, 1023, fmt, ap); - if (ret > 1023) { + ret = xmpp_vsnprintf(smbuf, sizeof(smbuf), fmt, ap); + if (ret >= (int)sizeof(smbuf)) { buf = (char *)xmpp_alloc(ctx, ret + 1); if (!buf) { buf = NULL; xmpp_error(ctx, "log", "Failed allocating memory for log message."); - va_end(copy); + va_end(copy); return; } oldret = ret; ret = xmpp_vsnprintf(buf, ret + 1, fmt, copy); if (ret > oldret) { xmpp_error(ctx, "log", "Unexpected error"); + xmpp_free(ctx, buf); + va_end(copy); return; } } else { - va_end(copy); + buf = smbuf; } + va_end(copy); if (ctx->log->handler) ctx->log->handler(ctx->log->userdata, level, area, buf); + + if (buf != smbuf) + xmpp_free(ctx, buf); } /** Write to the log at the ERROR level. diff --git a/src/handler.c b/src/handler.c index d382ca8..201e665 100644 --- a/src/handler.c +++ b/src/handler.c @@ -23,14 +23,9 @@ #include #include -#ifndef _WIN32 -#include -#else -#include "ostypes.h" -#endif - #include "strophe.h" #include "common.h" +#include "ostypes.h" /** Fire off all stanza handlers that match. * This function is called internally by the event loop whenever stanzas diff --git a/src/md5.c b/src/md5.c index 01231b1..7f11da9 100644 --- a/src/md5.c +++ b/src/md5.c @@ -43,6 +43,9 @@ (cp)[3] = ((value) >> 24) & 0xFF; \ } while(0) +static void MD5Transform(uint32_t buf[4], const unsigned char inext[64], + struct MD5Context *ctx); + /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. @@ -179,8 +182,8 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ -void MD5Transform(uint32_t buf[4], const unsigned char inext[64], - struct MD5Context *ctx) +static void MD5Transform(uint32_t buf[4], const unsigned char inext[64], + struct MD5Context *ctx) { register uint32_t a, b, c, d, i; uint32_t in[16]; diff --git a/src/md5.h b/src/md5.h index 3095f95..f2a9ac3 100644 --- a/src/md5.h +++ b/src/md5.h @@ -11,15 +11,8 @@ #ifndef MD5_H #define MD5_H -/* we use the uint32_t type from stdint.h - * if it is not available, add a typedef here: - */ /* make sure the stdint.h types are available */ -#if defined(_MSC_VER) /* Microsoft Visual C++ */ - typedef unsigned int uint32_t; -#else -#include -#endif +#include "ostypes.h" struct MD5Context { uint32_t buf[4]; @@ -31,8 +24,6 @@ void MD5Init(struct MD5Context *context); void MD5Update(struct MD5Context *context, unsigned char const *buf, uint32_t len); void MD5Final(unsigned char digest[16], struct MD5Context *context); -void MD5Transform(uint32_t buf[4], const unsigned char in[64], - struct MD5Context *ctx); #ifdef DEBUG_MD5 void MD5DumpBytes(unsigned char *b, int len); diff --git a/src/ostypes.h b/src/ostypes.h index 0794b2b..d8992a6 100644 --- a/src/ostypes.h +++ b/src/ostypes.h @@ -20,8 +20,18 @@ #ifndef __LIBSTROPHE_OSTYPES_H__ #define __LIBSTROPHE_OSTYPES_H__ -#ifdef _WIN32 +#if defined (_MSC_VER) && _MSC_VER < 1600 +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef __int64 int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; typedef unsigned __int64 uint64_t; +#else +#include #endif #endif /* __LIBSTROPHE_OSTYPES_H__ */ diff --git a/src/sasl.c b/src/sasl.c index 73fbbc9..b422dbc 100644 --- a/src/sasl.c +++ b/src/sasl.c @@ -16,26 +16,19 @@ * SASL authentication. */ +#include #include #include "strophe.h" #include "common.h" +#include "ostypes.h" #include "sasl.h" #include "md5.h" +#include "sha1.h" +#include "scram.h" -/* make sure the stdint.h types are available */ -#if defined(_MSC_VER) /* Microsoft Visual C++ */ - typedef signed char int8_t; - typedef short int int16_t; - typedef int int32_t; - typedef __int64 int64_t; - - typedef unsigned char uint8_t; - typedef unsigned short int uint16_t; - typedef unsigned int uint32_t; - /* no uint64_t */ -#else -#include +#ifdef _WIN32 +#define strtok_r strtok_s #endif @@ -360,6 +353,113 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge, return response; } +/** generate auth response string for the SASL SCRAM-SHA-1 mechanism */ +char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge, + const char *first_bare, const char *jid, + const char *password) +{ + uint8_t key[SHA1_DIGEST_SIZE]; + uint8_t sign[SHA1_DIGEST_SIZE]; + char *r = NULL; + char *s = NULL; + char *i = NULL; + char *sval; + size_t sval_len; + long ival; + char *tmp; + char *ptr; + char *saveptr = NULL; + char *response; + char *auth; + char *response_b64; + char *sign_b64; + char *result = NULL; + size_t response_len; + size_t auth_len; + int j; + + tmp = xmpp_strdup(ctx, challenge); + if (!tmp) { + return NULL; + } + + ptr = strtok_r(tmp, ",", &saveptr); + while (ptr) { + if (strncmp(ptr, "r=", 2) == 0) { + r = ptr; + } else if (strncmp(ptr, "s=", 2) == 0) { + s = ptr + 2; + } else if (strncmp(ptr, "i=", 2) == 0) { + i = ptr + 2; + } + ptr = strtok_r(NULL, ",", &saveptr); + } + + if (!r || !s || !i) { + goto out; + } + + sval = (char *)base64_decode(ctx, s, strlen(s)); + if (!sval) { + goto out; + } + sval_len = base64_decoded_len(ctx, s, strlen(s)); + ival = strtol(i, &saveptr, 10); + + auth_len = 10 + strlen(r) + strlen(first_bare) + strlen(challenge); + auth = xmpp_alloc(ctx, auth_len); + if (!auth) { + goto out_sval; + } + + response_len = 39 + strlen(r); + response = xmpp_alloc(ctx, response_len); + if (!response) { + goto out_auth; + } + + xmpp_snprintf(response, response_len, "c=biws,%s", r); + xmpp_snprintf(auth, auth_len, "%s,%s,%s", first_bare + 3, challenge, + response); + + SCRAM_SHA1_ClientKey((uint8_t *)password, strlen(password), + (uint8_t *)sval, sval_len, (uint32_t)ival, key); + SCRAM_SHA1_ClientSignature(key, (uint8_t *)auth, strlen(auth), sign); + for (j = 0; j < SHA1_DIGEST_SIZE; j++) { + sign[j] ^= key[j]; + } + + sign_b64 = base64_encode(ctx, sign, sizeof(sign)); + if (!sign_b64) { + goto out_response; + } + + if (strlen(response) + strlen(sign_b64) + 3 + 1 > response_len) { + xmpp_free(ctx, sign_b64); + goto out_response; + } + strcat(response, ",p="); + strcat(response, sign_b64); + xmpp_free(ctx, sign_b64); + + response_b64 = base64_encode(ctx, (unsigned char *)response, + strlen(response)); + if (!response_b64) { + goto out_response; + } + result = response_b64; + +out_response: + xmpp_free(ctx, response); +out_auth: + xmpp_free(ctx, auth); +out_sval: + xmpp_free(ctx, sval); +out: + xmpp_free(ctx, tmp); + return result; +} + /** Base64 encoding routines. Implemented according to RFC 3548 */ @@ -552,8 +652,8 @@ unsigned char *base64_decode(xmpp_ctx_t *ctx, if (hextet != 64) goto _base64_decode_error; break; } + *d = '\0'; } - *d = '\0'; return dbuf; _base64_decode_error: diff --git a/src/sasl.h b/src/sasl.h index 09d5b8d..1b9dec6 100644 --- a/src/sasl.h +++ b/src/sasl.h @@ -26,6 +26,9 @@ char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password); char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge, const char *jid, const char *password); +char *sasl_scram_sha1(xmpp_ctx_t *ctx, const char *challenge, + const char *first_bare, const char *jid, + const char *password); /** Base64 encoding routines. Implemented according to RFC 3548 */ diff --git a/src/scram.c b/src/scram.c new file mode 100644 index 0000000..4bff3f0 --- /dev/null +++ b/src/scram.c @@ -0,0 +1,146 @@ +/* scram.c + * strophe XMPP client library + * + * SCRAM-SHA1 helper functions according to RFC5802 + * HMAC-SHA1 implementation according to RFC2104 + * + * Copyright (C) 2013 Dmitry Podgorny + * + * This software is provided AS-IS with no warranty, either express + * or implied. + * + * This software is distributed under license and may not be copied, + * modified or distributed except as expressly authorized under the + * terms of the license contained in the file LICENSE.txt in this + * distribution. + */ + +/** @file + * SCRAM-SHA1 helper functions. + */ + +#include +#include + +#include "sha1.h" +#include "ostypes.h" + +#include "scram.h" + +/* block size for HMAC */ +#define BLOCK_SIZE 64 +#if BLOCK_SIZE < SHA1_DIGEST_SIZE +#error BLOCK_SIZE must not be less than SHA1_DIGEST_SIZE +#endif + +static const uint8_t ipad = 0x36; +static const uint8_t opad = 0x5C; + +static void SHA1(const uint8_t* data, size_t len, + uint8_t digest[SHA1_DIGEST_SIZE]) +{ + SHA1_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, data, len); + SHA1_Final(&ctx, digest); +} + +static void HMAC_SHA1(const uint8_t *key, size_t key_len, + const uint8_t *text, size_t len, + uint8_t digest[SHA1_DIGEST_SIZE]) +{ + uint8_t key_pad[BLOCK_SIZE]; + uint8_t key_ipad[BLOCK_SIZE]; + uint8_t key_opad[BLOCK_SIZE]; + uint8_t sha_digest[SHA1_DIGEST_SIZE]; + int i; + SHA1_CTX ctx; + + memset(key_pad, 0, sizeof(key_pad)); + if (key_len <= BLOCK_SIZE) { + memcpy(key_pad, key, key_len); + } else { + /* according to RFC2104 */ + SHA1(key, key_len, key_pad); + } + + for (i = 0; i < BLOCK_SIZE; i++) { + key_ipad[i] = key_pad[i] ^ ipad; + key_opad[i] = key_pad[i] ^ opad; + } + + SHA1_Init(&ctx); + SHA1_Update(&ctx, key_ipad, BLOCK_SIZE); + SHA1_Update(&ctx, text, len); + SHA1_Final(&ctx, sha_digest); + + SHA1_Init(&ctx); + SHA1_Update(&ctx, key_opad, BLOCK_SIZE); + SHA1_Update(&ctx, sha_digest, SHA1_DIGEST_SIZE); + SHA1_Final(&ctx, digest); +} + +static void SCRAM_SHA1_Hi(const uint8_t *text, size_t len, + const uint8_t *salt, size_t salt_len, uint32_t i, + uint8_t digest[SHA1_DIGEST_SIZE]) +{ + int j, k; + uint8_t tmp[128]; + + static uint8_t int1[] = {0x0, 0x0, 0x0, 0x1}; + + /* assume salt + INT(1) isn't longer than sizeof(tmp) */ + assert(salt_len <= sizeof(tmp) - sizeof(int1)); + + memset(digest, 0, SHA1_DIGEST_SIZE); + if (i == 0) { + return; + } + + memcpy(tmp, salt, salt_len); + memcpy(&tmp[salt_len], int1, sizeof(int1)); + + /* 'text' for Hi is a 'key' for HMAC */ + HMAC_SHA1(text, len, tmp, salt_len + sizeof(int1), digest); + memcpy(tmp, digest, SHA1_DIGEST_SIZE); + + for (j = 1; j < i; j++) { + HMAC_SHA1(text, len, tmp, SHA1_DIGEST_SIZE, tmp); + for (k = 0; k < SHA1_DIGEST_SIZE; k++) { + digest[k] ^= tmp[k]; + } + } +} + +void SCRAM_SHA1_ClientKey(const uint8_t *password, size_t len, + const uint8_t *salt, size_t salt_len, uint32_t i, + uint8_t key[SHA1_DIGEST_SIZE]) +{ + uint8_t salted[SHA1_DIGEST_SIZE]; + + /* XXX: Normalize(password) is omitted */ + + SCRAM_SHA1_Hi(password, len, salt, salt_len, i, salted); + HMAC_SHA1(salted, SHA1_DIGEST_SIZE, (uint8_t *)"Client Key", + strlen("Client Key"), key); +} + +void SCRAM_SHA1_ClientSignature(const uint8_t ClientKey[SHA1_DIGEST_SIZE], + const uint8_t *AuthMessage, size_t len, + uint8_t sign[SHA1_DIGEST_SIZE]) +{ + uint8_t stored[SHA1_DIGEST_SIZE]; + + SHA1(ClientKey, SHA1_DIGEST_SIZE, stored); + HMAC_SHA1(stored, SHA1_DIGEST_SIZE, AuthMessage, len, sign); +} + +void SCRAM_SHA1_ClientProof(const uint8_t ClientKey[SHA1_DIGEST_SIZE], + const uint8_t ClientSignature[SHA1_DIGEST_SIZE], + uint8_t proof[SHA1_DIGEST_SIZE]) +{ + int i; + for (i = 0; i < SHA1_DIGEST_SIZE; i++) { + proof[i] = ClientKey[i] ^ ClientSignature[i]; + } +} diff --git a/src/scram.h b/src/scram.h new file mode 100644 index 0000000..a40f83d --- /dev/null +++ b/src/scram.h @@ -0,0 +1,39 @@ +/* scram.h + * strophe XMPP client library -- SCRAM-SHA1 helper functions + * + * Copyright (C) 2013 Dmitry Podgorny + * + * This software is provided AS-IS with no warranty, either express + * or implied. + * + * This software is distributed under license and may not be copied, + * modified or distributed except as expressly authorized under the + * terms of the license contained in the file LICENSE.txt in this + * distribution. + */ + +/** @file + * SCRAM-SHA1 helper functions. + */ + +#ifndef __LIBSTROPHE_SCRAM_H__ +#define __LIBSTROPHE_SCRAM_H__ + +/* make sure the stdint.h types are available */ +#include "ostypes.h" + +#include "sha1.h" + +void SCRAM_SHA1_ClientKey(const uint8_t *password, size_t len, + const uint8_t *salt, size_t salt_len, uint32_t i, + uint8_t key[SHA1_DIGEST_SIZE]); + +void SCRAM_SHA1_ClientSignature(const uint8_t ClientKey[SHA1_DIGEST_SIZE], + const uint8_t *AuthMessage, size_t len, + uint8_t sign[SHA1_DIGEST_SIZE]); + +void SCRAM_SHA1_ClientProof(const uint8_t ClientKey[SHA1_DIGEST_SIZE], + const uint8_t ClientSignature[SHA1_DIGEST_SIZE], + uint8_t proof[SHA1_DIGEST_SIZE]); + +#endif /* __LIBSTROPHE_SCRAM_H__ */ diff --git a/src/sha1.c b/src/sha1.c index ec4cb6c..21cad40 100644 --- a/src/sha1.c +++ b/src/sha1.c @@ -86,23 +86,11 @@ A million repetitions of "a" #include /* make sure the stdint.h types are available */ -#if defined(_MSC_VER) /* Microsoft Visual C++ */ - typedef signed char int8_t; - typedef short int int16_t; - typedef int int32_t; - typedef __int64 int64_t; - - typedef unsigned char uint8_t; - typedef unsigned short int uint16_t; - typedef unsigned int uint32_t; - /* no uint64_t */ -#else -#include -#endif +#include "ostypes.h" #include "sha1.h" -void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); +static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) @@ -140,7 +128,7 @@ void SHAPrintContext(SHA1_CTX *context, char *msg){ #endif /* VERBOSE */ /* Hash a single 512-bit block. This is the core of the algorithm. */ -void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) +static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) { uint32_t a, b, c, d, e; typedef union { diff --git a/src/sha1.h b/src/sha1.h index b637f66..10266cb 100644 --- a/src/sha1.h +++ b/src/sha1.h @@ -12,6 +12,9 @@ extern "C" { #endif +/* make sure the stdint.h types are available */ +#include "ostypes.h" + typedef struct { uint32_t state[5]; uint32_t count[2]; diff --git a/src/tls_openssl.c b/src/tls_openssl.c index e087468..f9da3bf 100644 --- a/src/tls_openssl.c +++ b/src/tls_openssl.c @@ -25,7 +25,6 @@ #endif #include -#include #include "common.h" #include "tls.h" diff --git a/src/tls_schannel.c b/src/tls_schannel.c index 71b0ab0..82545ef 100644 --- a/src/tls_schannel.c +++ b/src/tls_schannel.c @@ -75,11 +75,11 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock) SecPkgCred_CipherStrengths spc_cs; SecPkgCred_SupportedProtocols spc_sp; - OSVERSIONINFO osvi; - - memset(&osvi, 0, sizeof(osvi)); - osvi.dwOSVersionInfoSize = sizeof(osvi); - + OSVERSIONINFO osvi; + + memset(&osvi, 0, sizeof(osvi)); + osvi.dwOSVersionInfoSize = sizeof(osvi); + GetVersionEx(&osvi); /* no TLS support on win9x/me, despite what anyone says */ @@ -102,7 +102,7 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock) return NULL; } - if (!(pInitSecurityInterface = + if (!(pInitSecurityInterface = (void *)GetProcAddress(tls->hsec32, "InitSecurityInterfaceA"))) { tls_free(tls); return NULL; @@ -148,26 +148,26 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock) /* This bunch of queries should trip up wine until someone fixes * schannel support there */ - ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_SUPPORTED_ALGS, &spc_sa); - if (ret != SEC_E_OK) - { - tls_free(tls); - return NULL; - } - - ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_CIPHER_STRENGTHS, &spc_cs); - if (ret != SEC_E_OK) - { - tls_free(tls); - return NULL; - } - - ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_SUPPORTED_PROTOCOLS, &spc_sp); - if (ret != SEC_E_OK) - { - tls_free(tls); - return NULL; - } + ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_SUPPORTED_ALGS, &spc_sa); + if (ret != SEC_E_OK) + { + tls_free(tls); + return NULL; + } + + ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_CIPHER_STRENGTHS, &spc_cs); + if (ret != SEC_E_OK) + { + tls_free(tls); + return NULL; + } + + ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_SUPPORTED_PROTOCOLS, &spc_sp); + if (ret != SEC_E_OK) + { + tls_free(tls); + return NULL; + } return tls; } @@ -196,7 +196,7 @@ void tls_free(tls_t *tls) FreeLibrary(tls->hsec32); tls->hsec32 = NULL; } - + xmpp_free(tls->ctx, tls); return; } @@ -360,7 +360,7 @@ int tls_start(tls_t *tls) return 0; } - tls->sft->QueryContextAttributes(&(tls->hctxt), SECPKG_ATTR_STREAM_SIZES, + tls->sft->QueryContextAttributes(&(tls->hctxt), SECPKG_ATTR_STREAM_SIZES, &(tls->spcss)); tls->recvbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage @@ -401,7 +401,7 @@ int tls_is_recoverable(int error) int tls_pending(tls_t *tls) { // There are 3 cases: // - there is data in ready buffer, so it is by default pending - // - there is data in recv buffer. If it is not decrypted yet, means it + // - there is data in recv buffer. If it is not decrypted yet, means it // was incomplete. This should be processed again only if there is data // on the physical connection // - there is data on the physical connection. This case is treated @@ -440,7 +440,7 @@ int tls_read(tls_t *tls, void * const buff, const size_t len) read = tls_read(tls, newbuff, len - bytes); if (read == -1) { - if (tls_is_recoverable(tls->lasterror)) { + if (tls_is_recoverable(tls->lasterror)) { return bytes; } @@ -454,11 +454,11 @@ int tls_read(tls_t *tls, void * const buff, const size_t len) /* next, top up our recv buffer */ bytes = sock_read(tls->sock, tls->recvbuffer + tls->recvbufferpos, tls->recvbuffermaxlen - tls->recvbufferpos); - + if (bytes == 0) { tls->lasterror = WSAECONNRESET; return -1; - } + } if (bytes == -1) { if (!tls_is_recoverable(sock_error())) { @@ -499,7 +499,7 @@ int tls_read(tls_t *tls, void * const buff, const size_t len) ret = tls->sft->DecryptMessage(&(tls->hctxt), &sbddec, 0, NULL); if (ret == SEC_E_OK) { - memcpy(tls->readybuffer, sbdec[1].pvBuffer, sbdec[1].cbBuffer); + memcpy(tls->readybuffer, sbdec[1].pvBuffer, sbdec[1].cbBuffer); tls->readybufferpos = 0; tls->readybufferlen = sbdec[1].cbBuffer; /* have we got some data left over? If so, copy it to the start @@ -631,8 +631,8 @@ int tls_write(tls_t *tls, const void * const buff, const size_t len) if (ret == -1 && !tls_is_recoverable(tls_error(tls))) { return -1; - } - + } + if (remain > tls->spcss.cbMaximumMessage) { sent += tls->spcss.cbMaximumMessage; remain -= tls->spcss.cbMaximumMessage; diff --git a/src/util.c b/src/util.c index c442fe7..cf9e9ff 100644 --- a/src/util.c +++ b/src/util.c @@ -24,11 +24,11 @@ #else #include #include -#include #endif #include "strophe.h" #include "common.h" +#include "ostypes.h" #include "util.h" /** implement our own strdup that uses the ctx allocator */ diff --git a/src/util.h b/src/util.h index dc00381..8bda48c 100644 --- a/src/util.h +++ b/src/util.h @@ -19,11 +19,7 @@ #ifndef __LIBSTROPHE_UTIL_H__ #define __LIBSTROPHE_UTIL_H__ -#ifndef _WIN32 -#include -#else #include "ostypes.h" -#endif /* timing functions */ uint64_t time_stamp(void);