diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2019-03-21 15:42:57 -1000 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2019-06-10 19:58:22 +0100 |
commit | a7f65f03bd1150f48398eaf48703f90025b41342 (patch) | |
tree | ca7bc5c4a11f9f4fa13e04404d89547620b1a8c5 /deps | |
parent | 79fc8281879b804657de6db440630b4dc45423d5 (diff) | |
download | libgit2-a7f65f03bd1150f48398eaf48703f90025b41342.tar.gz |
ntlm: add ntlmclient as a dependency
Include https://github.com/ethomson/ntlmclient as a dependency.
Diffstat (limited to 'deps')
-rw-r--r-- | deps/ntlmclient/CMakeLists.txt | 18 | ||||
-rw-r--r-- | deps/ntlmclient/compat.h | 33 | ||||
-rw-r--r-- | deps/ntlmclient/crypt.h | 64 | ||||
-rw-r--r-- | deps/ntlmclient/crypt_commoncrypto.c | 120 | ||||
-rw-r--r-- | deps/ntlmclient/crypt_commoncrypto.h | 18 | ||||
-rw-r--r-- | deps/ntlmclient/crypt_mbedtls.c | 145 | ||||
-rw-r--r-- | deps/ntlmclient/crypt_mbedtls.h | 18 | ||||
-rw-r--r-- | deps/ntlmclient/crypt_openssl.c | 130 | ||||
-rw-r--r-- | deps/ntlmclient/crypt_openssl.h | 21 | ||||
-rw-r--r-- | deps/ntlmclient/ntlm.c | 1420 | ||||
-rw-r--r-- | deps/ntlmclient/ntlm.h | 174 | ||||
-rw-r--r-- | deps/ntlmclient/ntlmclient.h | 320 | ||||
-rw-r--r-- | deps/ntlmclient/unicode.h | 36 | ||||
-rw-r--r-- | deps/ntlmclient/unicode_builtin.c | 445 | ||||
-rw-r--r-- | deps/ntlmclient/unicode_iconv.c | 201 | ||||
-rw-r--r-- | deps/ntlmclient/utf8.h | 1257 | ||||
-rw-r--r-- | deps/ntlmclient/util.c | 21 | ||||
-rw-r--r-- | deps/ntlmclient/util.h | 14 |
18 files changed, 4455 insertions, 0 deletions
diff --git a/deps/ntlmclient/CMakeLists.txt b/deps/ntlmclient/CMakeLists.txt new file mode 100644 index 000000000..393257daf --- /dev/null +++ b/deps/ntlmclient/CMakeLists.txt @@ -0,0 +1,18 @@ +FILE(GLOB SRC_NTLMCLIENT "ntlm.c" "unicode_builtin.c" "util.c") + +ADD_DEFINITIONS(-DNTLM_STATIC=1) + +IF (HTTPS_BACKEND STREQUAL "SecureTransport") + ADD_DEFINITIONS(-DCRYPT_COMMONCRYPTO) + SET(SRC_NTLMCLIENT_CRYPTO "crypt_commoncrypto.c") +ELSEIF (HTTPS_BACKEND STREQUAL "OpenSSL") + ADD_DEFINITIONS(-DCRYPT_OPENSSL) + SET(SRC_NTLMCLIENT_CRYPTO "crypt_openssl.c") +ELSEIF (HTTPS_BACKEND STREQUAL "mbedTLS") + ADD_DEFINITIONS(-DCRYPT_MBEDTLS) + SET(SRC_NTLMCLIENT_CRYPTO "crypt_mbedtls.c") +ELSE () + MESSAGE(FATAL_ERROR "Unable to use libgit2's HTTPS backend (${HTTPS_BACKEND}) for NTLM crypto") +ENDIF() + +ADD_LIBRARY(ntlmclient OBJECT ${SRC_NTLMCLIENT} ${SRC_NTLMCLIENT_CRYPTO}) diff --git a/deps/ntlmclient/compat.h b/deps/ntlmclient/compat.h new file mode 100644 index 000000000..efdf34514 --- /dev/null +++ b/deps/ntlmclient/compat.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#ifndef PRIVATE_COMPAT_H__ +#define PRIVATE_COMPAT_H__ + +#if defined (_MSC_VER) + typedef unsigned char bool; +# ifndef true +# define true 1 +# endif +# ifndef false +# define false 0 +# endif +#else +# include <stdbool.h> +#endif + +#ifdef __linux__ +# include <endian.h> +# define htonll htobe64 +#endif + +#ifndef MIN +# define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#endif /* PRIVATE_COMPAT_H__ */ diff --git a/deps/ntlmclient/crypt.h b/deps/ntlmclient/crypt.h new file mode 100644 index 000000000..48be39946 --- /dev/null +++ b/deps/ntlmclient/crypt.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#ifndef PRIVATE_CRYPT_COMMON_H__ +#define PRIVATE_CRYPT_COMMON_H__ + +#if defined(CRYPT_OPENSSL) +# include "crypt_openssl.h" +#elif defined(CRYPT_MBEDTLS) +# include "crypt_mbedtls.h" +#elif defined(CRYPT_COMMONCRYPTO) +# include "crypt_commoncrypto.h" +#else +# error "no crypto support" +#endif + +#define CRYPT_DES_BLOCKSIZE 8 +#define CRYPT_MD4_DIGESTSIZE 16 +#define CRYPT_MD5_DIGESTSIZE 16 + +typedef unsigned char ntlm_des_block[CRYPT_DES_BLOCKSIZE]; + +extern bool ntlm_random_bytes( + ntlm_client *ntlm, + unsigned char *out, + size_t len); + +extern bool ntlm_des_encrypt( + ntlm_des_block *out, + ntlm_des_block *plaintext, + ntlm_des_block *key); + +extern bool ntlm_md4_digest( + unsigned char out[CRYPT_MD4_DIGESTSIZE], + const unsigned char *in, + size_t in_len); + +extern ntlm_hmac_ctx *ntlm_hmac_ctx_init(void); + +extern bool ntlm_hmac_ctx_reset(ntlm_hmac_ctx *ctx); + +extern bool ntlm_hmac_md5_init( + ntlm_hmac_ctx *ctx, + const unsigned char *key, + size_t key_len); + +extern bool ntlm_hmac_md5_update( + ntlm_hmac_ctx *ctx, + const unsigned char *data, + size_t data_len); + +extern bool ntlm_hmac_md5_final( + unsigned char *out, + size_t *out_len, + ntlm_hmac_ctx *ctx); + +extern void ntlm_hmac_ctx_free(ntlm_hmac_ctx *ctx); + +#endif /* PRIVATE_CRYPT_COMMON_H__ */ diff --git a/deps/ntlmclient/crypt_commoncrypto.c b/deps/ntlmclient/crypt_commoncrypto.c new file mode 100644 index 000000000..54a0f097b --- /dev/null +++ b/deps/ntlmclient/crypt_commoncrypto.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include <CommonCrypto/CommonCrypto.h> + +#include "ntlm.h" +#include "crypt.h" + +bool ntlm_random_bytes( + ntlm_client *ntlm, + unsigned char *out, + size_t len) +{ + int fd, ret; + size_t total = 0; + + if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { + ntlm_client_set_errmsg(ntlm, strerror(errno)); + return false; + } + + while (total < len) { + if ((ret = read(fd, out, (len - total))) < 0) { + ntlm_client_set_errmsg(ntlm, strerror(errno)); + return false; + } else if (ret == 0) { + ntlm_client_set_errmsg(ntlm, "unexpected eof on random device"); + return false; + } + + total += ret; + } + + close(fd); + return true; +} + +bool ntlm_des_encrypt( + ntlm_des_block *out, + ntlm_des_block *plaintext, + ntlm_des_block *key) +{ + size_t written; + + CCCryptorStatus result = CCCrypt(kCCEncrypt, + kCCAlgorithmDES, kCCOptionECBMode, + key, sizeof(ntlm_des_block), NULL, + plaintext, sizeof(ntlm_des_block), + out, sizeof(ntlm_des_block), &written); + + return (result == kCCSuccess) ? true : false; +} + +bool ntlm_md4_digest( + unsigned char out[CRYPT_MD4_DIGESTSIZE], + const unsigned char *in, + size_t in_len) +{ + return !!CC_MD4(in, in_len, out); +} + +ntlm_hmac_ctx *ntlm_hmac_ctx_init(void) +{ + return calloc(1, sizeof(ntlm_hmac_ctx)); +} + +bool ntlm_hmac_ctx_reset(ntlm_hmac_ctx *ctx) +{ + memset(ctx, 0, sizeof(ntlm_hmac_ctx)); + return true; +} + +bool ntlm_hmac_md5_init( + ntlm_hmac_ctx *ctx, + const unsigned char *key, + size_t key_len) +{ + CCHmacInit(&ctx->native, kCCHmacAlgMD5, key, key_len); + return true; +} + +bool ntlm_hmac_md5_update( + ntlm_hmac_ctx *ctx, + const unsigned char *data, + size_t data_len) +{ + CCHmacUpdate(&ctx->native, data, data_len); + return true; +} + +bool ntlm_hmac_md5_final( + unsigned char *out, + size_t *out_len, + ntlm_hmac_ctx *ctx) +{ + if (*out_len < CRYPT_MD5_DIGESTSIZE) + return false; + + CCHmacFinal(&ctx->native, out); + + *out_len = CRYPT_MD5_DIGESTSIZE; + return true; +} + +void ntlm_hmac_ctx_free(ntlm_hmac_ctx *ctx) +{ + free(ctx); +} diff --git a/deps/ntlmclient/crypt_commoncrypto.h b/deps/ntlmclient/crypt_commoncrypto.h new file mode 100644 index 000000000..e4075c9f6 --- /dev/null +++ b/deps/ntlmclient/crypt_commoncrypto.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#ifndef PRIVATE_CRYPT_COMMONCRYPTO_H__ +#define PRIVATE_CRYPT_COMMONCRYPTO_H__ + +#include <CommonCrypto/CommonCrypto.h> + +typedef struct { + CCHmacContext native; +} ntlm_hmac_ctx; + +#endif /* PRIVATE_CRYPT_COMMONCRYPTO_H__ */ diff --git a/deps/ntlmclient/crypt_mbedtls.c b/deps/ntlmclient/crypt_mbedtls.c new file mode 100644 index 000000000..bbab02d7d --- /dev/null +++ b/deps/ntlmclient/crypt_mbedtls.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#include <stdlib.h> +#include <string.h> + +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/des.h" +#include "mbedtls/entropy.h" +#include "mbedtls/md4.h" + +#include "ntlm.h" +#include "crypt.h" + +bool ntlm_random_bytes( + ntlm_client *ntlm, + unsigned char *out, + size_t len) +{ + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_context entropy; + bool ret = true; + + const unsigned char personalization[] = { + 0xec, 0xb5, 0xd1, 0x0b, 0x8f, 0x15, 0x1f, 0xc2, + 0xe4, 0x8e, 0xec, 0x36, 0xf7, 0x0a, 0x45, 0x9a, + 0x1f, 0xe1, 0x35, 0x58, 0xb1, 0xcb, 0xfd, 0x8a, + 0x57, 0x5c, 0x75, 0x7d, 0x2f, 0xc9, 0x70, 0xac + }; + + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + + if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, + &entropy, personalization, sizeof(personalization)) || + mbedtls_ctr_drbg_random(&ctr_drbg, out, len)) { + ntlm_client_set_errmsg(ntlm, "random generation failed"); + ret = false; + } + + mbedtls_entropy_free(&entropy); + mbedtls_ctr_drbg_free(&ctr_drbg); + + return ret; +} + +bool ntlm_des_encrypt( + ntlm_des_block *out, + ntlm_des_block *plaintext, + ntlm_des_block *key) +{ + mbedtls_des_context ctx; + bool success = false; + + mbedtls_des_init(&ctx); + + if (mbedtls_des_setkey_enc(&ctx, *key) || + mbedtls_des_crypt_ecb(&ctx, *plaintext, *out)) + goto done; + + success = true; + +done: + mbedtls_des_free(&ctx); + return success; +} + +bool ntlm_md4_digest( + unsigned char out[CRYPT_MD4_DIGESTSIZE], + const unsigned char *in, + size_t in_len) +{ + mbedtls_md4_context ctx; + + mbedtls_md4_init(&ctx); + mbedtls_md4_starts(&ctx); + mbedtls_md4_update(&ctx, in, in_len); + mbedtls_md4_finish(&ctx, out); + mbedtls_md4_free(&ctx); + + return true; +} + +ntlm_hmac_ctx *ntlm_hmac_ctx_init(void) +{ + ntlm_hmac_ctx *ctx; + const mbedtls_md_info_t *info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5); + + if ((ctx = calloc(1, sizeof(ntlm_hmac_ctx))) == NULL) + return NULL; + + mbedtls_md_init(&ctx->mbed); + + if (mbedtls_md_setup(&ctx->mbed, info, 1) != 0) { + free(ctx); + return false; + } + + return ctx; +} + +bool ntlm_hmac_ctx_reset(ntlm_hmac_ctx *ctx) +{ + return !mbedtls_md_hmac_reset(&ctx->mbed); +} + +bool ntlm_hmac_md5_init( + ntlm_hmac_ctx *ctx, + const unsigned char *key, + size_t key_len) +{ + return !mbedtls_md_hmac_starts(&ctx->mbed, key, key_len); +} + +bool ntlm_hmac_md5_update( + ntlm_hmac_ctx *ctx, + const unsigned char *in, + size_t in_len) +{ + return !mbedtls_md_hmac_update(&ctx->mbed, in, in_len); +} + +bool ntlm_hmac_md5_final( + unsigned char *out, + size_t *out_len, + ntlm_hmac_ctx *ctx) +{ + if (*out_len < CRYPT_MD5_DIGESTSIZE) + return false; + + return !mbedtls_md_hmac_finish(&ctx->mbed, out); +} + +void ntlm_hmac_ctx_free(ntlm_hmac_ctx *ctx) +{ + if (ctx) { + mbedtls_md_free(&ctx->mbed); + free(ctx); + } +} diff --git a/deps/ntlmclient/crypt_mbedtls.h b/deps/ntlmclient/crypt_mbedtls.h new file mode 100644 index 000000000..eb49a4596 --- /dev/null +++ b/deps/ntlmclient/crypt_mbedtls.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#ifndef PRIVATE_CRYPT_MBEDTLS_H__ +#define PRIVATE_CRYPT_MBEDTLS_H__ + +#include "mbedtls/md.h" + +typedef struct { + mbedtls_md_context_t mbed; +} ntlm_hmac_ctx; + +#endif /* PRIVATE_CRYPT_MBEDTLS_H__ */ diff --git a/deps/ntlmclient/crypt_openssl.c b/deps/ntlmclient/crypt_openssl.c new file mode 100644 index 000000000..785be10e5 --- /dev/null +++ b/deps/ntlmclient/crypt_openssl.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#include <stdlib.h> +#include <string.h> + +#include <openssl/rand.h> +#include <openssl/des.h> +#include <openssl/md4.h> +#include <openssl/hmac.h> +#include <openssl/err.h> + +#include "ntlm.h" +#include "compat.h" +#include "util.h" +#include "crypt.h" + +bool ntlm_random_bytes( + ntlm_client *ntlm, + unsigned char *out, + size_t len) +{ + int rc = RAND_bytes(out, len); + + if (rc != 1) { + ntlm_client_set_errmsg(ntlm, ERR_lib_error_string(ERR_get_error())); + return false; + } + + return true; +} + +bool ntlm_des_encrypt( + ntlm_des_block *out, + ntlm_des_block *plaintext, + ntlm_des_block *key) +{ + DES_key_schedule keysched; + + memset(out, 0, sizeof(ntlm_des_block)); + + DES_set_key(key, &keysched); + DES_ecb_encrypt(plaintext, out, &keysched, DES_ENCRYPT); + + return true; +} + +bool ntlm_md4_digest( + unsigned char out[CRYPT_MD4_DIGESTSIZE], + const unsigned char *in, + size_t in_len) +{ + MD4(in, in_len, out); + return true; +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static inline void HMAC_CTX_free(HMAC_CTX *ctx) +{ + if (ctx) + HMAC_CTX_cleanup(ctx); + + free(ctx); +} + +static inline int HMAC_CTX_reset(HMAC_CTX *ctx) +{ + HMAC_CTX_cleanup(ctx); + memzero(ctx, sizeof(HMAC_CTX)); + return 1; +} + +static inline HMAC_CTX *HMAC_CTX_new(void) +{ + return calloc(1, sizeof(HMAC_CTX)); +} +#endif + +ntlm_hmac_ctx *ntlm_hmac_ctx_init(void) +{ + return HMAC_CTX_new(); +} + +bool ntlm_hmac_ctx_reset(ntlm_hmac_ctx *ctx) +{ + return HMAC_CTX_reset(ctx); +} + +bool ntlm_hmac_md5_init( + ntlm_hmac_ctx *ctx, + const unsigned char *key, + size_t key_len) +{ + return HMAC_Init_ex(ctx, key, key_len, EVP_md5(), NULL); +} + +bool ntlm_hmac_md5_update( + ntlm_hmac_ctx *ctx, + const unsigned char *in, + size_t in_len) +{ + return HMAC_Update(ctx, in, in_len); +} + +bool ntlm_hmac_md5_final( + unsigned char *out, + size_t *out_len, + ntlm_hmac_ctx *ctx) +{ + unsigned int len; + + if (*out_len < CRYPT_MD5_DIGESTSIZE) + return false; + + if (!HMAC_Final(ctx, out, &len)) + return false; + + *out_len = len; + return true; +} + +void ntlm_hmac_ctx_free(ntlm_hmac_ctx *ctx) +{ + HMAC_CTX_free(ctx); +} diff --git a/deps/ntlmclient/crypt_openssl.h b/deps/ntlmclient/crypt_openssl.h new file mode 100644 index 000000000..4195db9a5 --- /dev/null +++ b/deps/ntlmclient/crypt_openssl.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#ifndef PRIVATE_CRYPT_OPENSSL_H__ +#define PRIVATE_CRYPT_OPENSSL_H__ + +#include <openssl/hmac.h> + +/* OpenSSL 1.1.0 uses opaque structs, we'll reuse these. */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L +typedef struct hmac_ctx_st ntlm_hmac_ctx; +#else +# define ntlm_hmac_ctx HMAC_CTX +#endif + +#endif /* PRIVATE_CRYPT_OPENSSL_H__ */ diff --git a/deps/ntlmclient/ntlm.c b/deps/ntlmclient/ntlm.c new file mode 100644 index 000000000..9d0f3b8e3 --- /dev/null +++ b/deps/ntlmclient/ntlm.c @@ -0,0 +1,1420 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <ctype.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <arpa/inet.h> + +#include "ntlm.h" +#include "unicode.h" +#include "utf8.h" +#include "crypt.h" +#include "compat.h" +#include "util.h" + +unsigned char ntlm_client_signature[] = NTLM_SIGNATURE; + +static bool supports_unicode(ntlm_client *ntlm) +{ + return (ntlm->flags & NTLM_CLIENT_DISABLE_UNICODE) ? + false : true; +} + +static inline bool increment_size(size_t *out, size_t incr) +{ + if (SIZE_MAX - *out < incr) { + *out = (size_t)-1; + return false; + } + + *out = *out + incr; + return true; +} + +ntlm_client *ntlm_client_init(ntlm_client_flags flags) +{ + ntlm_client *ntlm = NULL; + + if ((ntlm = malloc(sizeof(ntlm_client))) == NULL) + return NULL; + + memset(ntlm, 0, sizeof(ntlm_client)); + + ntlm->flags = flags; + + if ((ntlm->hmac_ctx = ntlm_hmac_ctx_init()) == NULL || + (ntlm->unicode_ctx = ntlm_unicode_ctx_init(ntlm)) == NULL) { + ntlm_hmac_ctx_free(ntlm->hmac_ctx); + ntlm_unicode_ctx_free(ntlm->unicode_ctx); + free(ntlm); + return NULL; + } + + return ntlm; +} + +void ntlm_client_set_errmsg(ntlm_client *ntlm, const char *errmsg) +{ + ntlm->state = NTLM_STATE_ERROR; + ntlm->errmsg = errmsg; +} + +const char *ntlm_client_errmsg(ntlm_client *ntlm) +{ + assert(ntlm); + return ntlm->errmsg ? ntlm->errmsg : "no error"; +} + +int ntlm_client_set_version( + ntlm_client *ntlm, + uint8_t major, + uint8_t minor, + uint16_t build) +{ + assert(ntlm); + + ntlm->host_version.major = major; + ntlm->host_version.minor = minor; + ntlm->host_version.build = build; + ntlm->host_version.reserved = 0x0f000000; + + ntlm->flags |= NTLM_ENABLE_HOSTVERSION; + + return 0; +} + +int ntlm_client_set_hostname( + ntlm_client *ntlm, + const char *hostname, + const char *domain) +{ + assert(ntlm); + + free(ntlm->hostname); + free(ntlm->hostdomain); + free(ntlm->hostname_utf16); + + ntlm->hostname = NULL; + ntlm->hostdomain = NULL; + ntlm->hostname_utf16 = NULL; + + if (hostname && (ntlm->hostname = strdup(hostname)) == NULL) { + ntlm_client_set_errmsg(ntlm, "out of memory"); + return -1; + } + + if (domain && (ntlm->hostdomain = strdup(domain)) == NULL) { + ntlm_client_set_errmsg(ntlm, "out of memory"); + return -1; + } + + if (hostname && supports_unicode(ntlm) && !ntlm_unicode_utf8_to_16( + &ntlm->hostname_utf16, + &ntlm->hostname_utf16_len, + ntlm->unicode_ctx, + hostname, + strlen(hostname))) + return -1; + + return 0; +} + +static void free_credentials(ntlm_client *ntlm) +{ + if (ntlm->password) + memzero(ntlm->password, strlen(ntlm->password)); + + if (ntlm->password_utf16) + memzero(ntlm->password_utf16, ntlm->password_utf16_len); + + free(ntlm->username); + free(ntlm->username_upper); + free(ntlm->userdomain); + free(ntlm->password); + + free(ntlm->username_utf16); + free(ntlm->username_upper_utf16); + free(ntlm->userdomain_utf16); + free(ntlm->password_utf16); + + ntlm->username = NULL; + ntlm->username_upper = NULL; + ntlm->userdomain = NULL; + ntlm->password = NULL; + + ntlm->username_utf16 = NULL; + ntlm->username_upper_utf16 = NULL; + ntlm->userdomain_utf16 = NULL; + ntlm->password_utf16 = NULL; +} + +int ntlm_client_set_credentials( + ntlm_client *ntlm, + const char *username, + const char *domain, + const char *password) +{ + assert(ntlm); + + free_credentials(ntlm); + + if ((username && (ntlm->username = strdup(username)) == NULL) || + (domain && (ntlm->userdomain = strdup(domain)) == NULL) || + (password && (ntlm->password = strdup(password)) == NULL)) { + ntlm_client_set_errmsg(ntlm, "out of memory"); + return -1; + } + + if (username && supports_unicode(ntlm)) { + if ((ntlm->username_upper = strdup(username)) == NULL) { + ntlm_client_set_errmsg(ntlm, "out of memory"); + return -1; + } + utf8upr(ntlm->username_upper); + + if (!ntlm_unicode_utf8_to_16( + &ntlm->username_utf16, + &ntlm->username_utf16_len, + ntlm->unicode_ctx, + ntlm->username, + strlen(ntlm->username))) + return -1; + + if (!ntlm_unicode_utf8_to_16( + &ntlm->username_upper_utf16, + &ntlm->username_upper_utf16_len, + ntlm->unicode_ctx, + ntlm->username_upper, + strlen(ntlm->username_upper))) + return -1; + } + + if (domain && supports_unicode(ntlm) && !ntlm_unicode_utf8_to_16( + &ntlm->userdomain_utf16, + &ntlm->userdomain_utf16_len, + ntlm->unicode_ctx, + ntlm->userdomain, + strlen(ntlm->userdomain))) + return -1; + + return 0; +} + +int ntlm_client_set_target(ntlm_client *ntlm, const char *target) +{ + assert(ntlm); + + free(ntlm->target); + free(ntlm->target_utf16); + + ntlm->target = NULL; + ntlm->target_utf16 = NULL; + + if (target) { + if ((ntlm->target = strdup(target)) == NULL) { + ntlm_client_set_errmsg(ntlm, "out of memory"); + return -1; + } + + if (supports_unicode(ntlm) && !ntlm_unicode_utf8_to_16( + &ntlm->target_utf16, + &ntlm->target_utf16_len, + ntlm->unicode_ctx, + ntlm->target, + strlen(ntlm->target))) + return -1; + } + + return 0; +} + +int ntlm_client_set_nonce(ntlm_client *ntlm, uint64_t nonce) +{ + assert(ntlm); + ntlm->nonce = nonce; + return 0; +} + +int ntlm_client_set_timestamp(ntlm_client *ntlm, uint64_t timestamp) +{ + assert(ntlm); + ntlm->timestamp = timestamp; + return 0; +} + +static inline bool write_buf( + ntlm_client *ntlm, + ntlm_buf *out, + const unsigned char *buf, + size_t len) +{ + if (out->len - out->pos < len) { + ntlm_client_set_errmsg(ntlm, "out of buffer space"); + return false; + } + + memcpy(&out->buf[out->pos], buf, len); + out->pos += len; + return true; +} + +static inline bool write_byte( + ntlm_client *ntlm, + ntlm_buf *out, + uint8_t value) +{ + if (out->len - out->pos < 1) { + ntlm_client_set_errmsg(ntlm, "out of buffer space"); + return false; + } + + out->buf[out->pos++] = value; + return true; +} + +static inline bool write_int16( + ntlm_client *ntlm, + ntlm_buf *out, + uint16_t value) +{ + if (out->len - out->pos < 2) { + ntlm_client_set_errmsg(ntlm, "out of buffer space"); + return false; + } + + out->buf[out->pos++] = (value & 0x000000ff); + out->buf[out->pos++] = (value & 0x0000ff00) >> 8; + return true; +} + +static inline bool write_int32( + ntlm_client *ntlm, + ntlm_buf *out, + uint32_t value) +{ + if (out->len - out->pos < 2) { + ntlm_client_set_errmsg(ntlm, "out of buffer space"); + return false; + } + + out->buf[out->pos++] = (value & 0x000000ff); + out->buf[out->pos++] = (value & 0x0000ff00) >> 8; + out->buf[out->pos++] = (value & 0x00ff0000) >> 16; + out->buf[out->pos++] = (value & 0xff000000) >> 24; + return true; +} + +static inline bool write_version( + ntlm_client *ntlm, + ntlm_buf *out, + ntlm_version *version) +{ + return write_byte(ntlm, out, version->major) && + write_byte(ntlm, out, version->minor) && + write_int16(ntlm, out, version->build) && + write_int32(ntlm, out, version->reserved); +} + +static inline bool write_bufinfo( + ntlm_client *ntlm, + ntlm_buf *out, + size_t len, + size_t offset) +{ + if (len > UINT16_MAX) { + ntlm_client_set_errmsg(ntlm, "invalid string, too long"); + return false; + } + + if (offset > UINT32_MAX) { + ntlm_client_set_errmsg(ntlm, "invalid string, invalid offset"); + return false; + } + + return write_int16(ntlm, out, (uint16_t)len) && + write_int16(ntlm, out, (uint16_t)len) && + write_int32(ntlm, out, (uint32_t)offset); +} + +static inline bool read_buf( + unsigned char *out, + ntlm_client *ntlm, + ntlm_buf *message, + size_t len) +{ + if (message->len - message->pos < len) { + ntlm_client_set_errmsg(ntlm, "truncated message"); + return false; + } + + memcpy(out, &message->buf[message->pos], len); + message->pos += len; + + return true; +} + +static inline bool read_byte( + uint8_t *out, + ntlm_client *ntlm, + ntlm_buf *message) +{ + if (message->len - message->pos < 1) { + ntlm_client_set_errmsg(ntlm, "truncated message"); + return false; + } + + *out = message->buf[message->pos++]; + return true; +} + +static inline bool read_int16( + uint16_t *out, + ntlm_client *ntlm, + ntlm_buf *message) +{ + if (message->len - message->pos < 2) { + ntlm_client_set_errmsg(ntlm, "truncated message"); + return false; + } + + *out = + ((message->buf[message->pos] & 0xff)) | + ((message->buf[message->pos+1] & 0xff) << 8); + + message->pos += 2; + return true; +} + +static inline bool read_int32( + uint32_t *out, + ntlm_client *ntlm, + ntlm_buf *message) +{ + if (message->len - message->pos < 4) { + ntlm_client_set_errmsg(ntlm, "truncated message"); + return false; + } + + *out = + ((message->buf[message->pos] & 0xff)) | + ((message->buf[message->pos+1] & 0xff) << 8) | + ((message->buf[message->pos+2] & 0xff) << 16) | + ((message->buf[message->pos+3] & 0xff) << 24); + + message->pos += 4; + return true; +} + +static inline bool read_int64( + uint64_t *out, + ntlm_client *ntlm, + ntlm_buf *message) +{ + if (message->len - message->pos < 8) { + ntlm_client_set_errmsg(ntlm, "truncated message"); + return false; + } + + *out = + ((uint64_t)(message->buf[message->pos] & 0xff)) | + ((uint64_t)(message->buf[message->pos+1] & 0xff) << 8) | + ((uint64_t)(message->buf[message->pos+2] & 0xff) << 16) | + ((uint64_t)(message->buf[message->pos+3] & 0xff) << 24) | + ((uint64_t)(message->buf[message->pos+4] & 0xff) << 32) | + ((uint64_t)(message->buf[message->pos+5] & 0xff) << 40) | + ((uint64_t)(message->buf[message->pos+6] & 0xff) << 48) | + ((uint64_t)(message->buf[message->pos+7] & 0xff) << 56); + + message->pos += 8; + return true; +} + +static inline bool read_version( + ntlm_version *out, + ntlm_client *ntlm, + ntlm_buf *message) +{ + return read_byte(&out->major, ntlm, message) && + read_byte(&out->minor, ntlm, message) && + read_int16(&out->build, ntlm, message) && + read_int32(&out->reserved, ntlm, message); +} + +static inline bool read_bufinfo( + uint16_t *out_len, + uint32_t *out_offset, + ntlm_client *ntlm, + ntlm_buf *message) +{ + uint16_t allocated; + + return read_int16(out_len, ntlm, message) && + read_int16(&allocated, ntlm, message) && + read_int32(out_offset, ntlm, message); +} + +static inline bool read_string_unicode( + char **out, + ntlm_client *ntlm, + ntlm_buf *message, + uint8_t string_len) +{ + size_t out_len; + int ret = ntlm_unicode_utf16_to_8(out, + &out_len, + ntlm->unicode_ctx, + (char *)&message->buf[message->pos], + string_len); + + message->pos += string_len; + + return ret; +} + +static inline bool read_string_ascii( + char **out, + ntlm_client *ntlm, + ntlm_buf *message, + uint8_t string_len) +{ + char *str; + + if ((str = malloc(string_len + 1)) == NULL) { + ntlm_client_set_errmsg(ntlm, "out of memory"); + return false; + } + + memcpy(str, &message->buf[message->pos], string_len); + str[string_len] = '\0'; + + message->pos += string_len; + + *out = str; + return true; +} + +static inline bool read_string( + char **out, + ntlm_client *ntlm, + ntlm_buf *message, + uint8_t string_len, + bool unicode) +{ + if (unicode) + return read_string_unicode(out, ntlm, message, string_len); + else + return read_string_ascii(out, ntlm, message, string_len); +} + +static inline bool read_target_info( + char **server_out, + char **domain_out, + char **server_dns_out, + char **domain_dns_out, + ntlm_client *ntlm, + ntlm_buf *message, + bool unicode) +{ + uint16_t block_type, block_len; + bool done = false; + + *server_out = NULL; + *domain_out = NULL; + *server_dns_out = NULL; + *domain_dns_out = NULL; + + while (!done && (message->len - message->pos) >= 4) { + if (!read_int16(&block_type, ntlm, message) || + !read_int16(&block_len, ntlm, message)) { + ntlm_client_set_errmsg(ntlm, "truncated target info block"); + return false; + } + + if (!block_type && block_len) { + ntlm_client_set_errmsg(ntlm, "invalid target info block"); + return -1; + } + + switch (block_type) { + case NTLM_TARGET_INFO_DOMAIN: + if (!read_string(domain_out, ntlm, message, block_len, unicode)) + return -1; + break; + case NTLM_TARGET_INFO_SERVER: + if (!read_string(server_out, ntlm, message, block_len, unicode)) + return -1; + break; + case NTLM_TARGET_INFO_DOMAIN_DNS: + if (!read_string(domain_dns_out, ntlm, message, block_len, unicode)) + return -1; + break; + case NTLM_TARGET_INFO_SERVER_DNS: + if (!read_string(server_dns_out, ntlm, message, block_len, unicode)) + return -1; + break; + case NTLM_TARGET_INFO_END: + done = true; + break; + default: + ntlm_client_set_errmsg(ntlm, "unknown target info block type"); + return -1; + } + } + + if (message->len != message->pos) { + ntlm_client_set_errmsg(ntlm, + "invalid extra data in target info section"); + return false; + } + + return true; +} + +int ntlm_client_negotiate( + const unsigned char **out, + size_t *out_len, + ntlm_client *ntlm) +{ + size_t hostname_len, hostname_offset, domain_len, domain_offset; + uint32_t flags = 0; + + assert(out && out_len && ntlm); + + *out = NULL; + *out_len = 0; + + if (ntlm->state != NTLM_STATE_NEGOTIATE) { + ntlm_client_set_errmsg(ntlm, "ntlm handle in invalid state"); + return -1; + } + + flags |= NTLM_NEGOTIATE_OEM; + + if (supports_unicode(ntlm)) + flags |= NTLM_NEGOTIATE_UNICODE; + + if (!(ntlm->flags & NTLM_CLIENT_DISABLE_NTLM2) || + (ntlm->flags & NTLM_CLIENT_ENABLE_NTLM)) + flags |= NTLM_NEGOTIATE_NTLM; + + if (!(ntlm->flags & NTLM_CLIENT_DISABLE_REQUEST_TARGET)) + flags |= NTLM_NEGOTIATE_REQUEST_TARGET; + + hostname_len = ntlm->hostname ? strlen(ntlm->hostname) : 0; + domain_len = ntlm->hostdomain ? strlen(ntlm->hostdomain) : 0; + + /* Minimum header size */ + ntlm->negotiate.len = 16; + + /* Include space for security buffer descriptors */ + if (domain_len) + increment_size(&ntlm->negotiate.len, 8); + + if (hostname_len) + increment_size(&ntlm->negotiate.len, 8); + + if (ntlm->flags & NTLM_ENABLE_HOSTVERSION) + increment_size(&ntlm->negotiate.len, 8); + + /* Location of security buffers */ + if (hostname_len) { + flags |= NTLM_NEGOTIATE_WORKSTATION_SUPPLIED; + hostname_offset = ntlm->negotiate.len; + increment_size(&ntlm->negotiate.len, hostname_len); + } + + if (domain_len) { + flags |= NTLM_NEGOTIATE_DOMAIN_SUPPLIED; + domain_offset = ntlm->negotiate.len; + increment_size(&ntlm->negotiate.len, domain_len); + } + + if (ntlm->negotiate.len == (size_t)-1) { + ntlm_client_set_errmsg(ntlm, "message too large"); + return -1; + } + + if ((ntlm->negotiate.buf = malloc(ntlm->negotiate.len)) == NULL) { + ntlm_client_set_errmsg(ntlm, "out of memory"); + return -1; + } + + memset(ntlm->negotiate.buf, 0, ntlm->negotiate.len); + + if (!write_buf(ntlm, &ntlm->negotiate, + ntlm_client_signature, sizeof(ntlm_client_signature)) || + !write_int32(ntlm, &ntlm->negotiate, 1) || + !write_int32(ntlm, &ntlm->negotiate, flags)) + return -1; + + /* Domain information */ + if (domain_len > 0 && + !write_bufinfo(ntlm, &ntlm->negotiate, domain_len, domain_offset)) + return -1; + + /* Workstation information */ + if (hostname_len > 0 && + !write_bufinfo(ntlm, &ntlm->negotiate, hostname_len, hostname_offset)) + return -1; + + /* Version number */ + if (!!(ntlm->flags & NTLM_ENABLE_HOSTVERSION) && + !write_version(ntlm, &ntlm->negotiate, &ntlm->host_version)) + return -1; + + if (hostname_len > 0) { + assert(hostname_offset == ntlm->negotiate.pos); + if (!write_buf(ntlm, &ntlm->negotiate, + (const unsigned char *)ntlm->hostname, hostname_len)) + return -1; + } + + if (domain_len > 0) { + assert(domain_offset == ntlm->negotiate.pos); + if (!write_buf(ntlm, &ntlm->negotiate, + (const unsigned char *)ntlm->hostdomain, domain_len)) + return -1; + } + + assert(ntlm->negotiate.pos == ntlm->negotiate.len); + + ntlm->state = NTLM_STATE_CHALLENGE; + + *out = ntlm->negotiate.buf; + *out_len = ntlm->negotiate.len; + + return 0; +} + +int ntlm_client_set_challenge( + ntlm_client *ntlm, + const unsigned char *challenge_msg, + size_t challenge_msg_len) +{ + unsigned char signature[8]; + ntlm_buf challenge; + uint32_t type_indicator, header_end; + uint16_t name_len, info_len = 0; + uint32_t name_offset, info_offset = 0; + bool unicode, has_target_info = false; + + assert(ntlm && (challenge_msg || !challenge_msg_len)); + + if (ntlm->state != NTLM_STATE_NEGOTIATE && + ntlm->state != NTLM_STATE_CHALLENGE) { + ntlm_client_set_errmsg(ntlm, "ntlm handle in invalid state"); + return -1; + } + + challenge.buf = (unsigned char *)challenge_msg; + challenge.len = challenge_msg_len; + challenge.pos = 0; + + if (!read_buf(signature, ntlm, &challenge, 8) || + !read_int32(&type_indicator, ntlm, &challenge) || + !read_bufinfo(&name_len, &name_offset, ntlm, &challenge) || + !read_int32(&ntlm->challenge.flags, ntlm, &challenge) || + !read_int64(&ntlm->challenge.nonce, ntlm, &challenge)) + return -1; + + if (memcmp(signature, + ntlm_client_signature, sizeof(ntlm_client_signature)) != 0) { + ntlm_client_set_errmsg(ntlm, "invalid message signature"); + return -1; + } + + if (type_indicator != 2) { + ntlm_client_set_errmsg(ntlm, "invalid message indicator"); + return -1; + } + + /* + * If there's additional space before the data section, that's the + * target information description section. + */ + header_end = challenge.len; + + if (name_offset && name_offset < header_end) + header_end = name_offset; + + if ((header_end - challenge.pos) >= 16) { + has_target_info = true; + } + + if (!has_target_info && + (ntlm->challenge.flags & NTLM_NEGOTIATE_TARGET_INFO)) { + ntlm_client_set_errmsg(ntlm, + "truncated message; expected target info"); + return -1; + } + + /* + * If there's a target info section then advanced over the reserved + * space and read the target information. + */ + if (has_target_info) { + uint64_t reserved; + + if (!read_int64(&reserved, ntlm, &challenge)) { + ntlm_client_set_errmsg(ntlm, + "truncated message; expected reserved space"); + return -1; + } + + if (reserved != 0) { + ntlm_client_set_errmsg(ntlm, + "invalid message; expected reserved space to be empty"); + return -1; + } + + if (!read_bufinfo(&info_len, &info_offset, ntlm, &challenge)) { + ntlm_client_set_errmsg(ntlm, + "truncated message; expected target info"); + return -1; + } + } + + unicode = !!(ntlm->challenge.flags & NTLM_NEGOTIATE_UNICODE); + + /* + * If there's still additional space before the data section, + * that's the server's version information. + */ + if (info_offset && info_offset < header_end) + header_end = info_offset; + + if (ntlm->challenge.flags & NTLM_NEGOTIATE_VERSION) { + if ((header_end - challenge.pos) != sizeof(ntlm_version) || + !read_version(&ntlm->challenge.target_version, + ntlm, &challenge)) { + ntlm_client_set_errmsg(ntlm, + "truncated message; expected version"); + return -1; + } + } + + /* validate data section */ + if ((name_offset && name_offset < challenge.pos) || + challenge.len < name_len || + (challenge.len - name_len) < name_offset) { + ntlm_client_set_errmsg(ntlm, + "invalid message; invalid target name buffer"); + return -1; + } + if ((info_offset && info_offset < challenge.pos) || + challenge.len < info_len || + (challenge.len - info_len) < info_offset) { + ntlm_client_set_errmsg(ntlm, + "invalid message; invalid target info buffer"); + return -1; + } + + /* advance to the data section */ + if (name_len && name_offset) { + challenge.pos = name_offset; + + if (!read_string(&ntlm->challenge.target, + ntlm, &challenge, name_len, unicode)) { + ntlm_client_set_errmsg(ntlm, + "truncated message; truncated target name"); + return -1; + } + } + + if (info_len && info_offset) { + ntlm_buf info_buf; + + challenge.pos = info_offset; + + /* create a copy of the target info; we need the literal data */ + if ((ntlm->challenge.target_info = malloc(info_len)) == NULL) { + ntlm_client_set_errmsg(ntlm, "out of memory"); + return -1; + } + + if (!read_buf(ntlm->challenge.target_info, + ntlm, &challenge, info_len)) { + ntlm_client_set_errmsg(ntlm, + "truncated message; truncated target info"); + return -1; + } + + info_buf.buf = ntlm->challenge.target_info; + info_buf.pos = 0; + info_buf.len = info_len; + + /* then set up the target info and parse it */ + if (!read_target_info(&ntlm->challenge.target_server, + &ntlm->challenge.target_domain, + &ntlm->challenge.target_server_dns, + &ntlm->challenge.target_domain_dns, + ntlm, &info_buf, unicode)) + return -1; + + ntlm->challenge.target_info_len = info_len; + } + + ntlm->state = NTLM_STATE_RESPONSE; + + return 0; +} + +uint64_t ntlm_client_challenge_nonce(ntlm_client *ntlm) +{ + return ntlm->challenge.nonce; +} + +const char *ntlm_client_target(ntlm_client *ntlm) +{ + return ntlm->challenge.target; +} + +const char *ntlm_client_target_server(ntlm_client *ntlm) +{ + return ntlm->challenge.target_server; +} + +const char *ntlm_client_target_domain(ntlm_client *ntlm) +{ + return ntlm->challenge.target_domain; +} + +const char *ntlm_client_target_server_dns(ntlm_client *ntlm) +{ + return ntlm->challenge.target_server_dns; +} + +const char *ntlm_client_target_domain_dns(ntlm_client *ntlm) +{ + return ntlm->challenge.target_domain_dns; +} + +#define EVEN_PARITY(a) \ + (!!((a) & 0x01ll) ^ !!((a) & 0x02ll) ^ \ + !!((a) & 0x04ll) ^ !!((a) & 0x08ll) ^ \ + !!((a) & 0x10ll) ^ !!((a) & 0x20ll) ^ \ + !!((a) & 0x40ll) ^ !!((a) & 0x80ll)) + +static void generate_odd_parity(ntlm_des_block *block) +{ + size_t i; + + for (i = 0; i < sizeof(ntlm_des_block); i++) + (*block)[i] |= (1 ^ EVEN_PARITY((*block)[i])); +} + +static void des_key_from_password( + ntlm_des_block *out, + const unsigned char *plaintext, + size_t plaintext_len) +{ + size_t i; + + plaintext_len = MIN(plaintext_len, 7); + + memset(*out, 0, sizeof(ntlm_des_block)); + + for (i = 0; i < plaintext_len; i++) { + size_t j = (7 - i); + uint8_t mask = (0xff >> j); + + (*out)[i] |= ((plaintext[i] & (0xff - mask)) >> i); + (*out)[i+1] |= ((plaintext[i] & mask) << j); + } + + generate_odd_parity(out); +} + +static inline bool generate_lm_hash( + ntlm_des_block out[2], + const char *password) +{ + /* LM encrypts this known plaintext using the password as a key */ + ntlm_des_block plaintext = NTLM_LM_PLAINTEXT; + ntlm_des_block keystr1, keystr2; + size_t keystr1_len, keystr2_len; + ntlm_des_block key1, key2; + size_t password_len, i; + + /* Copy the first 14 characters of the password, uppercased */ + memset(&keystr1, 0, sizeof(keystr1)); + memset(&keystr2, 0, sizeof(keystr2)); + + password_len = password ? strlen(password) : 0; + + /* Split the password into two 7 byte chunks */ + keystr1_len = MIN(7, password_len); + keystr2_len = (password_len > 7) ? MIN(14, password_len) - 7 : 0; + + for (i = 0; i < keystr1_len; i++) + keystr1[i] = (unsigned char)toupper(password[i]); + for (i = 0; i < keystr2_len; i++) + keystr2[i] = (unsigned char)toupper(password[i+7]); + + /* DES encrypt the LM constant using the password as the key */ + des_key_from_password(&key1, keystr1, keystr1_len); + des_key_from_password(&key2, keystr2, keystr2_len); + + return ntlm_des_encrypt(&out[0], &plaintext, &key1) && + ntlm_des_encrypt(&out[1], &plaintext, &key2); +} + +static void des_keys_from_lm_hash(ntlm_des_block out[3], ntlm_des_block lm_hash[2]) +{ + ntlm_des_block split[3]; + + memcpy(&split[0][0], &lm_hash[0][0], 7); + + memcpy(&split[1][0], &lm_hash[0][7], 1); + memcpy(&split[1][1], &lm_hash[1][0], 6); + + memcpy(&split[2][0], &lm_hash[1][6], 2); + + des_key_from_password(&out[0], split[0], 7); + des_key_from_password(&out[1], split[1], 7); + des_key_from_password(&out[2], split[2], 2); +} + +static bool generate_lm_response(ntlm_client *ntlm) +{ + ntlm_des_block lm_hash[2], key[3], lm_response[3]; + ntlm_des_block *challenge = (ntlm_des_block *)&ntlm->challenge.nonce; + + /* Generate the LM hash from the password */ + if (!generate_lm_hash(lm_hash, ntlm->password)) + return false; + + /* Convert that LM hash to three DES keys */ + des_keys_from_lm_hash(key, lm_hash); + + /* Finally, encrypt the challenge with each of these keys */ + if (!ntlm_des_encrypt(&lm_response[0], challenge, &key[0]) || + !ntlm_des_encrypt(&lm_response[1], challenge, &key[1]) || + !ntlm_des_encrypt(&lm_response[2], challenge, &key[2])) + return false; + + memcpy(&ntlm->lm_response[0], lm_response[0], 8); + memcpy(&ntlm->lm_response[8], lm_response[1], 8); + memcpy(&ntlm->lm_response[16], lm_response[2], 8); + + ntlm->lm_response_len = sizeof(ntlm->lm_response); + + return true; +} + +static bool generate_ntlm_hash( + unsigned char out[NTLM_NTLM_HASH_LEN], ntlm_client *ntlm) +{ + /* Generate the LM hash from the (Unicode) password */ + if (ntlm->password && !ntlm_unicode_utf8_to_16( + &ntlm->password_utf16, + &ntlm->password_utf16_len, + ntlm->unicode_ctx, + ntlm->password, + strlen(ntlm->password))) + return false; + + return ntlm_md4_digest(out, + (const unsigned char *)ntlm->password_utf16, + ntlm->password_utf16_len); +} + +static bool generate_ntlm_response(ntlm_client *ntlm) +{ + unsigned char ntlm_hash[NTLM_NTLM_HASH_LEN] = {0}; + ntlm_des_block key[3], ntlm_response[3]; + ntlm_des_block *challenge = + (ntlm_des_block *)&ntlm->challenge.nonce; + + if (!generate_ntlm_hash(ntlm_hash, ntlm)) + return false; + + /* Convert that LM hash to three DES keys */ + des_key_from_password(&key[0], &ntlm_hash[0], 7); + des_key_from_password(&key[1], &ntlm_hash[7], 7); + des_key_from_password(&key[2], &ntlm_hash[14], 2); + + /* Finally, encrypt the challenge with each of these keys */ + if (!ntlm_des_encrypt(&ntlm_response[0], challenge, &key[0]) || + !ntlm_des_encrypt(&ntlm_response[1], challenge, &key[1]) || + !ntlm_des_encrypt(&ntlm_response[2], challenge, &key[2])) + return false; + + memcpy(&ntlm->ntlm_response[0], ntlm_response[0], 8); + memcpy(&ntlm->ntlm_response[8], ntlm_response[1], 8); + memcpy(&ntlm->ntlm_response[16], ntlm_response[2], 8); + + ntlm->ntlm_response_len = sizeof(ntlm->ntlm_response); + return true; +} + +static bool generate_ntlm2_hash( + unsigned char out[NTLM_NTLM2_HASH_LEN], ntlm_client *ntlm) +{ + unsigned char ntlm_hash[NTLM_NTLM_HASH_LEN] = {0}; + const unsigned char *username = NULL, *target = NULL; + size_t username_len = 0, target_len = 0, out_len = NTLM_NTLM2_HASH_LEN; + + if (!generate_ntlm_hash(ntlm_hash, ntlm)) + return false; + + if (ntlm->username_upper_utf16) { + username = (const unsigned char *)ntlm->username_upper_utf16; + username_len = ntlm->username_upper_utf16_len; + } + + if (ntlm->target_utf16) { + target = (const unsigned char *)ntlm->target_utf16; + target_len = ntlm->target_utf16_len; + } + + if (!ntlm_hmac_ctx_reset(ntlm->hmac_ctx) || + !ntlm_hmac_md5_init(ntlm->hmac_ctx, ntlm_hash, sizeof(ntlm_hash)) || + !ntlm_hmac_md5_update(ntlm->hmac_ctx, username, username_len) || + !ntlm_hmac_md5_update(ntlm->hmac_ctx, target, target_len) || + !ntlm_hmac_md5_final(out, &out_len, ntlm->hmac_ctx)) { + ntlm_client_set_errmsg(ntlm, "failed to create HMAC-MD5"); + return false; + } + + assert(out_len == NTLM_NTLM2_HASH_LEN); + return true; +} + +static bool generate_ntlm2_challengehash( + unsigned char out[16], + ntlm_client *ntlm, + unsigned char ntlm2_hash[NTLM_NTLM2_HASH_LEN], + const unsigned char *blob, + size_t blob_len) +{ + size_t out_len = 16; + + if (!ntlm_hmac_ctx_reset(ntlm->hmac_ctx) || + !ntlm_hmac_md5_init(ntlm->hmac_ctx, + ntlm2_hash, NTLM_NTLM2_HASH_LEN) || + !ntlm_hmac_md5_update(ntlm->hmac_ctx, + (const unsigned char *)&ntlm->challenge.nonce, 8) || + !ntlm_hmac_md5_update(ntlm->hmac_ctx, blob, blob_len) || + !ntlm_hmac_md5_final(out, &out_len, ntlm->hmac_ctx)) { + ntlm_client_set_errmsg(ntlm, "failed to create HMAC-MD5"); + return false; + } + + assert(out_len == 16); + return true; +} + +static bool generate_lm2_response(ntlm_client *ntlm, + unsigned char ntlm2_hash[NTLM_NTLM2_HASH_LEN]) +{ + unsigned char lm2_challengehash[16]; + size_t lm2_len = 16; + uint64_t local_nonce; + + local_nonce = htonll(ntlm->nonce); + + if (!ntlm_hmac_ctx_reset(ntlm->hmac_ctx) || + !ntlm_hmac_md5_init(ntlm->hmac_ctx, + ntlm2_hash, NTLM_NTLM2_HASH_LEN) || + !ntlm_hmac_md5_update(ntlm->hmac_ctx, + (const unsigned char *)&ntlm->challenge.nonce, 8) || + !ntlm_hmac_md5_update(ntlm->hmac_ctx, + (const unsigned char *)&local_nonce, 8) || + !ntlm_hmac_md5_final(lm2_challengehash, &lm2_len, ntlm->hmac_ctx)) { + ntlm_client_set_errmsg(ntlm, "failed to create HMAC-MD5"); + return false; + } + + assert(lm2_len == 16); + + memcpy(&ntlm->lm_response[0], lm2_challengehash, 16); + memcpy(&ntlm->lm_response[16], &local_nonce, 8); + + ntlm->lm_response_len = 24; + return true; +} + +static bool generate_timestamp(ntlm_client *ntlm) +{ + if (!ntlm->timestamp) + ntlm->timestamp = (time(NULL) + 11644473600) * 10000000; + + return true; +} + +static bool generate_nonce(ntlm_client *ntlm) +{ + unsigned char buf[8]; + + if (ntlm->nonce) + return true; + + if (!ntlm_random_bytes(ntlm, buf, 8)) + return false; + + memcpy(&ntlm->nonce, buf, sizeof(uint64_t)); + return true; +} + +static bool generate_ntlm2_response(ntlm_client *ntlm) +{ + size_t blob_len, ntlm2_response_len; + uint32_t signature; + uint64_t timestamp, nonce; + unsigned char ntlm2_hash[NTLM_NTLM2_HASH_LEN]; + unsigned char challengehash[16]; + unsigned char *blob; + + if (!generate_timestamp(ntlm) || + !generate_nonce(ntlm) || + !generate_ntlm2_hash(ntlm2_hash, ntlm)) + return false; + + blob_len = ntlm->challenge.target_info_len + 32; + ntlm2_response_len = blob_len + 16; + + if ((ntlm->ntlm2_response = malloc(ntlm2_response_len)) == NULL) { + ntlm_client_set_errmsg(ntlm, "out of memory"); + return false; + } + + /* position the blob in the response; we'll use it then return it */ + blob = ntlm->ntlm2_response + 16; + + /* the blob's integer values are in network byte order */ + signature = htonl(0x01010000); + timestamp = htonll(ntlm->timestamp); + nonce = htonll(ntlm->nonce); + + /* construct the blob */ + memcpy(&blob[0], &signature, 4); + memset(&blob[4], 0, 4); + memcpy(&blob[8], ×tamp, 8); + memcpy(&blob[16], &nonce, 8); + memset(&blob[24], 0, 4); + memcpy(&blob[28], ntlm->challenge.target_info, ntlm->challenge.target_info_len); + memset(&blob[28 + ntlm->challenge.target_info_len], 0, 4); + + if (!generate_ntlm2_challengehash(challengehash, ntlm, ntlm2_hash, blob, blob_len)) + return false; + + memcpy(ntlm->ntlm2_response, challengehash, 16); + ntlm->ntlm2_response_len = ntlm2_response_len; + + if (!generate_lm2_response(ntlm, ntlm2_hash)) + return false; + + return true; +} + +int ntlm_client_response( + const unsigned char **out, + size_t *out_len, + ntlm_client *ntlm) +{ + unsigned char *domain, *username, *hostname, *ntlm_rep, *session; + size_t lm_rep_len, lm_rep_offset, ntlm_rep_len, ntlm_rep_offset, + domain_len, domain_offset, username_len, username_offset, + hostname_len, hostname_offset, session_len, session_offset; + uint32_t flags = 0; + bool unicode; + + assert(out && out_len && ntlm); + + *out = NULL; + *out_len = 0; + + if (ntlm->state != NTLM_STATE_RESPONSE) { + ntlm_client_set_errmsg(ntlm, "ntlm handle in invalid state"); + return -1; + } + + /* + * Minimum message size is 64 bytes: + * 8 byte signature, + * 4 byte message indicator, + * 6x8 byte security buffers + * 4 byte flags + */ + ntlm->response.len = 64; + + unicode = supports_unicode(ntlm) && + (ntlm->challenge.flags & NTLM_NEGOTIATE_UNICODE); + + if (unicode) + flags |= NTLM_NEGOTIATE_UNICODE; + else + flags |= NTLM_NEGOTIATE_OEM; + + if (unicode) { + domain = (unsigned char *)ntlm->userdomain_utf16; + domain_len = ntlm->userdomain_utf16_len; + + username = (unsigned char *)ntlm->username_utf16; + username_len = ntlm->username_utf16_len; + + hostname = (unsigned char *)ntlm->hostname_utf16; + hostname_len = ntlm->hostname_utf16_len; + } else { + domain = (unsigned char *)ntlm->userdomain; + domain_len = ntlm->userdomain ? strlen(ntlm->userdomain) : 0; + + username = (unsigned char *)ntlm->username; + username_len = ntlm->username ? strlen(ntlm->username) : 0; + + hostname = (unsigned char *)ntlm->hostname; + hostname_len = ntlm->hostname ? strlen(ntlm->hostname) : 0; + } + + /* Negotiate our requested authentication type with the server's */ + if (!(ntlm->flags & NTLM_CLIENT_DISABLE_NTLM2) && + (ntlm->challenge.flags & NTLM_NEGOTIATE_NTLM)) { + flags |= NTLM_NEGOTIATE_NTLM; + + if (!generate_ntlm2_response(ntlm)) + return -1; + } else if ((ntlm->flags & NTLM_CLIENT_ENABLE_NTLM) && + (ntlm->challenge.flags & NTLM_NEGOTIATE_NTLM)) { + flags |= NTLM_NEGOTIATE_NTLM; + + if (!generate_ntlm_response(ntlm) || + !generate_lm_response(ntlm)) + return -1; + } else if (ntlm->flags & NTLM_CLIENT_ENABLE_LM) { + if (!generate_lm_response(ntlm)) + return -1; + } else { + ntlm_client_set_errmsg(ntlm, + "no encryption options could be negotiated"); + return -1; + } + + domain_offset = ntlm->response.len; + increment_size(&ntlm->response.len, domain_len); + + username_offset = ntlm->response.len; + increment_size(&ntlm->response.len, username_len); + + hostname_offset = ntlm->response.len; + increment_size(&ntlm->response.len, hostname_len); + + lm_rep_len = ntlm->lm_response_len; + lm_rep_offset = ntlm->response.len; + increment_size(&ntlm->response.len, lm_rep_len); + + ntlm_rep = ntlm->ntlm2_response_len ? + ntlm->ntlm2_response : ntlm->ntlm_response; + ntlm_rep_len = ntlm->ntlm2_response_len ? + ntlm->ntlm2_response_len : ntlm->ntlm_response_len; + ntlm_rep_offset = ntlm->response.len; + increment_size(&ntlm->response.len, ntlm_rep_len); + + session = NULL; + session_len = 0; + session_offset = ntlm->response.len; + increment_size(&ntlm->response.len, session_len); + + if (ntlm->response.len == (size_t)-1) { + ntlm_client_set_errmsg(ntlm, "message too large"); + return -1; + } + + if ((ntlm->response.buf = malloc(ntlm->response.len)) == NULL) { + ntlm_client_set_errmsg(ntlm, "out of memory"); + return -1; + } + + memset(ntlm->response.buf, 0, ntlm->response.len); + + if (!write_buf(ntlm, &ntlm->response, + ntlm_client_signature, sizeof(ntlm_client_signature)) || + !write_int32(ntlm, &ntlm->response, 3) || + !write_bufinfo(ntlm, &ntlm->response, lm_rep_len, lm_rep_offset) || + !write_bufinfo(ntlm, &ntlm->response, ntlm_rep_len, ntlm_rep_offset) || + !write_bufinfo(ntlm, &ntlm->response, domain_len, domain_offset) || + !write_bufinfo(ntlm, &ntlm->response, username_len, username_offset) || + !write_bufinfo(ntlm, &ntlm->response, hostname_len, hostname_offset) || + !write_bufinfo(ntlm, &ntlm->response, session_len, session_offset) || + !write_int32(ntlm, &ntlm->response, flags) || + !write_buf(ntlm, &ntlm->response, domain, domain_len) || + !write_buf(ntlm, &ntlm->response, username, username_len) || + !write_buf(ntlm, &ntlm->response, hostname, hostname_len) || + !write_buf(ntlm, &ntlm->response, ntlm->lm_response, lm_rep_len) || + !write_buf(ntlm, &ntlm->response, ntlm_rep, ntlm_rep_len) || + !write_buf(ntlm, &ntlm->response, session, session_len)) + return -1; + + assert(ntlm->response.pos == ntlm->response.len); + + ntlm->state = NTLM_STATE_COMPLETE; + + *out = ntlm->response.buf; + *out_len = ntlm->response.len; + + return 0; +} + +void ntlm_client_reset(ntlm_client *ntlm) +{ + ntlm_client_flags flags; + ntlm_hmac_ctx *hmac_ctx; + ntlm_unicode_ctx *unicode_ctx; + + assert(ntlm); + + free(ntlm->negotiate.buf); + free(ntlm->challenge.target_info); + free(ntlm->challenge.target); + free(ntlm->challenge.target_domain); + free(ntlm->challenge.target_domain_dns); + free(ntlm->challenge.target_server); + free(ntlm->challenge.target_server_dns); + free(ntlm->response.buf); + + free(ntlm->hostname); + free(ntlm->hostname_utf16); + free(ntlm->hostdomain); + + free(ntlm->target); + free(ntlm->target_utf16); + + free(ntlm->ntlm2_response); + + free_credentials(ntlm); + + flags = ntlm->flags; + hmac_ctx = ntlm->hmac_ctx; + unicode_ctx = ntlm->unicode_ctx; + + memset(ntlm, 0, sizeof(struct ntlm_client)); + + ntlm->flags = flags; + ntlm->hmac_ctx = hmac_ctx; + ntlm->unicode_ctx = unicode_ctx; +} + +void ntlm_client_free(ntlm_client *ntlm) +{ + if (!ntlm) + return; + + ntlm_client_reset(ntlm); + + ntlm_hmac_ctx_free(ntlm->hmac_ctx); + ntlm_unicode_ctx_free(ntlm->unicode_ctx); + + free(ntlm); +} diff --git a/deps/ntlmclient/ntlm.h b/deps/ntlmclient/ntlm.h new file mode 100644 index 000000000..0dad91ec0 --- /dev/null +++ b/deps/ntlmclient/ntlm.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#ifndef PRIVATE_NTLM_H__ +#define PRIVATE_NTLM_H__ + +#include "ntlmclient.h" +#include "unicode.h" +#include "crypt.h" +#include "compat.h" + +#define NTLM_LM_RESPONSE_LEN 24 +#define NTLM_NTLM_RESPONSE_LEN 24 +#define NTLM_NTLM_HASH_LEN 16 +#define NTLM_NTLM2_HASH_LEN 16 + +#define NTLM_SIGNATURE { 'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00 } + +#define NTLM_LM_PLAINTEXT { 0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 } + +typedef enum { + NTLM_STATE_NEGOTIATE = 0, + NTLM_STATE_CHALLENGE = 1, + NTLM_STATE_RESPONSE = 2, + NTLM_STATE_ERROR = 3, + NTLM_STATE_COMPLETE = 4, +} ntlm_state; + +typedef struct { + unsigned char *buf; + size_t pos; + size_t len; +} ntlm_buf; + +typedef struct { + uint8_t major; + uint8_t minor; + uint16_t build; + uint32_t reserved; +} ntlm_version; + +typedef struct { + uint32_t flags; + uint64_t nonce; + ntlm_version target_version; + + /* The unparsed target information from the server */ + unsigned char *target_info; + size_t target_info_len; + + /* The target information parsed into usable strings */ + char *target; + char *target_server; + char *target_domain; + char *target_server_dns; + char *target_domain_dns; +} ntlm_challenge; + +struct ntlm_client { + ntlm_client_flags flags; + + ntlm_state state; + + /* crypto contexts */ + ntlm_hmac_ctx *hmac_ctx; + ntlm_unicode_ctx *unicode_ctx; + + /* error message as set by the library */ + const char *errmsg; + + char *hostname; + char *hostdomain; + ntlm_version host_version; + + char *target; + + char *username; + char *username_upper; + char *userdomain; + char *password; + + /* strings as converted to utf16 */ + char *target_utf16; + char *username_utf16; + char *username_upper_utf16; + char *userdomain_utf16; + char *hostname_utf16; + char *password_utf16; + + /* timestamp and nonce; only for debugging */ + uint64_t nonce; + uint64_t timestamp; + + size_t username_utf16_len; + size_t username_upper_utf16_len; + size_t userdomain_utf16_len; + size_t hostname_utf16_len; + size_t password_utf16_len; + size_t target_utf16_len; + + unsigned char lm_response[NTLM_LM_RESPONSE_LEN]; + size_t lm_response_len; + + unsigned char ntlm_response[NTLM_NTLM_RESPONSE_LEN]; + size_t ntlm_response_len; + + unsigned char *ntlm2_response; + size_t ntlm2_response_len; + + ntlm_buf negotiate; + ntlm_challenge challenge; + ntlm_buf response; +}; + +typedef enum { + NTLM_ENABLE_HOSTVERSION = (1 << 31), +} ntlm_client_internal_flags; + +typedef enum { + NTLM_TARGET_INFO_END = 0, + NTLM_TARGET_INFO_SERVER = 1, + NTLM_TARGET_INFO_DOMAIN = 2, + NTLM_TARGET_INFO_SERVER_DNS = 3, + NTLM_TARGET_INFO_DOMAIN_DNS = 4, +} ntlm_target_info_type_t; + +typedef enum { + /* Unicode strings are supported in security buffers */ + NTLM_NEGOTIATE_UNICODE = 0x00000001, + + /* OEM (ANSI) strings are supported in security buffers */ + NTLM_NEGOTIATE_OEM = 0x00000002, + + /* Request the target realm from the server */ + NTLM_NEGOTIATE_REQUEST_TARGET = 0x00000004, + + /* NTLM authentication is supported */ + NTLM_NEGOTIATE_NTLM = 0x00000200, + + /* Negotiate domain name */ + NTLM_NEGOTIATE_DOMAIN_SUPPLIED = 0x00001000, + + /* Negotiate workstation (client) name */ + NTLM_NEGOTIATE_WORKSTATION_SUPPLIED = 0x00002000, + + /* Indicates that a local context is available */ + NTLM_NEGOTIATE_LOCAL_CALL = 0x00004000, + + /* Request a dummy signature */ + NTLM_NEGOTIATE_ALWAYS_SIGN = 0x00008000, + + /* Target (server) is a domain */ + NTLM_NEGOTIATE_TYPE_DOMAIN = 0x00010000, + + /* NTLM2 signing and sealing is supported */ + NTLM_NEGOTIATE_NTLM2_SIGN_AND_SEAL = 0x00080000, + + /* A target information block is included */ + NTLM_NEGOTIATE_TARGET_INFO = 0x00800000, + + /* Version information should be provided */ + NTLM_NEGOTIATE_VERSION = 0x01000000, +} ntlm_negotiate_t; + +extern int ntlm_client_set_nonce(ntlm_client *ntlm, uint64_t nonce); +extern int ntlm_client_set_timestamp(ntlm_client *ntlm, uint64_t timestamp); +extern void ntlm_client_set_errmsg(ntlm_client *ntlm, const char *errmsg); + +#endif /* PRIVATE_NTLM_H__ */ diff --git a/deps/ntlmclient/ntlmclient.h b/deps/ntlmclient/ntlmclient.h new file mode 100644 index 000000000..d109a5c89 --- /dev/null +++ b/deps/ntlmclient/ntlmclient.h @@ -0,0 +1,320 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ +#ifndef INCLUDE_NTLMCLIENT_H__ +#define INCLUDE_NTLMCLIENT_H__ + +#include <stdlib.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define NTLM_CLIENT_VERSION "0.0.1" +#define NTLM_CLIENT_VERSION_MAJOR 0 +#define NTLM_CLIENT_VERSION_MINOR 0 +#define NTLM_CLIENT_VERSION_TEENY 1 + +typedef struct ntlm_client ntlm_client; + +/* + * Flags for initializing the `ntlm_client` context. A combination of + * these flags can be provided to `ntlm_client_init`. + */ +typedef enum { + /** Default settings for the `ntlm_client`. */ + NTLM_CLIENT_DEFAULTS = 0, + + /** + * Disable Unicode negotiation. By default, strings are converted + * into UTF-16 when supplied to the remote host, but if this flag + * is specified, localizable strings (like username and password) + * will only be sent to the server as they were provided to the + * library. Since the NTLM protocol does not deliver the locale + * information, these will be interpreted by the remote host in + * whatever locale is configured, and likely be corrupted unless + * you limit yourself to ASCII. + * + * You are discouraged from setting this flag. + */ + NTLM_CLIENT_DISABLE_UNICODE = (1 << 0), + + /* + * Enable LM ("Lan Manager") authentication support. By default, + * LM authentication is disabled, since most remote servers have + * disabled support for it, and because it is both trivially + * brute-forced _and_ subject to rainbow table lookups. If this + * flag is enabled, LM is still not used unless NTLM2 support is + * also disabled. + * + * You are discouraged from setting this flag. + */ + NTLM_CLIENT_ENABLE_LM = (1 << 1), + + /* + * Enable NTLM ("Lan Manager") authentication support. By default, + * NTLM authentication is disabled, since most remote servers have + * disabled support for it, due to its weakness. If this flag is + * enabled, NTLM is still not used unless NTLM2 support is also + * disabled. + * + * You are discouraged from setting this flag. + */ + NTLM_CLIENT_ENABLE_NTLM = (1 << 2), + + /* + * Disable NTLM2 authentication support. By default, _only_ NTLM2 + * support is enabled, since most remote servers will only support + * it due to its (relative) lack of weakness. If this flag is + * set, either NTLM or LM (or both) must be explicitly enabled or + * there will be no mechanisms available to use. + * + * You are discouraged from setting this flag. + */ + NTLM_CLIENT_DISABLE_NTLM2 = (1 << 3), + + /* + * Request the target's name. By default, you are expected to + * provide the name of the target you are authenticating to (eg, + * the remote hostname). If set, the remote host will provide + * its idea of its hostname in the challenge message. You may + * then set the authentication target based on it. + */ + NTLM_CLIENT_DISABLE_REQUEST_TARGET = (1 << 4), +} ntlm_client_flags; + + +/** Declare a public function exported for application use. */ +#if __GNUC__ >= 4 && !defined(NTLM_STATIC) +# define NTLM_EXTERN(type) extern \ + __attribute__((visibility("default"))) \ + type +#elif defined(_MSC_VER) && !defined(NTLM_STATIC) +# define NTLM_EXTERN(type) __declspec(dllexport) type +#else +# define NTLM_EXTERN(type) extern type +#endif + +/** + * Initializes an `ntlm_client` context, which can begin sending + * and receiving NTLM authentication messages. + * + * @param flags the `ntlm_client_flag_t`s to use for negotiation. + * @return the `ntlm_client` context, or `NULL` if out-of-memory. + */ +NTLM_EXTERN(ntlm_client *) ntlm_client_init(ntlm_client_flags flags); + +/** + * Gets the error message for the most recent error that occurred. If + * a function returns an error, more details can be retrieved with this + * function. The string returned is a constant string; it should not + * be freed. + * + * @return a constant string containing the error message. + */ +NTLM_EXTERN(const char *) ntlm_client_errmsg(ntlm_client *ntlm); + +/** + * Sets the local hostname and domain. These strings should be in + * ASCII. They will be provided to the remote host during the + * negotiation phase. + * + * @param ntlm the `ntlm_client` context to configure + * @param hostname the hostname of the local machine + * @param domain the domain of the local machine + * @return 0 on success, non-zero on failure + */ +NTLM_EXTERN(int) ntlm_client_set_hostname( + ntlm_client *ntlm, + const char *hostname, + const char *domain); + +/** + * Sets the local operating system version. These numbers are expected + * to correspond to Windows operating system versions; for example + * major version 6, minor version 2, build 9200 would correspond to + * Windows 8 (aka "NT 6.2"). + * + * It is not likely that you need to set the local version. + * + * @param ntlm the `ntlm_client` context to configure + * @param major the major version number of the local operating system + * @param minor the minor version number of the local operating system + * @param build the build number of the local operating system + * @return 0 on success, non-zero on failure + */ +NTLM_EXTERN(int) ntlm_client_set_version( + ntlm_client *ntlm, + uint8_t major, + uint8_t minor, + uint16_t build); + +/** + * Sets the username and password to authenticate with to the remote + * host. Username and password may be specified in UTF-8 but the + * domain should be in ASCII. These will not be sent to the remote host + * but will instead be used to compute the LM, NTLM or NTLM2 responses, + * which will be provided to the remote host during the response phase. + * + * @param ntlm the `ntlm_client` context to configure + * @param username the username to authenticate with + * @param domain the domain of the user authenticating + * @param password the password to authenticate with + * @return 0 on success, non-zero on failure + */ +NTLM_EXTERN(int) ntlm_client_set_credentials( + ntlm_client *ntlm, + const char *username, + const char *domain, + const char *password); + +/** + * Sets the authentication target, your idea of the remote host's + * name. The target should be provided as ASCII. It will be + * provided to the remote host during the response phase. + * + * @param ntlm the `ntlm_client` context to configure + * @param target the name of the authentication target + * @return 0 on success, non-zero on failure + */ +NTLM_EXTERN(int) ntlm_client_set_target( + ntlm_client *ntlm, + const char *target); + +/** + * Gets the remote host's nonce, as it was provided in the challenge + * message. This is an opaque 8 byte value that is used to compute + * the LM, NTLM and NTLM2 responses. + * + * @param ntlm the `ntlm_client` context to query + * @return the challenge from the remote host + */ +NTLM_EXTERN(uint64_t) ntlm_client_challenge_nonce( + ntlm_client *ntlm); + +/** + * Gets the remote hosts's target name, which can be used as the + * authentication target. This will be given as it was provided + * in the challenge message. + * + * @param ntlm the `ntlm_client` context to query + * @return the remote host's target name + */ +NTLM_EXTERN(const char *) ntlm_client_target(ntlm_client *ntlm); + +/** + * Gets the remote hosts's name, which is generally its short name. + * This will be given as it was provided in the challenge message. + * + * @param ntlm the `ntlm_client` context to query + * @return the remote host's server name + */ +NTLM_EXTERN(const char *) ntlm_client_target_server(ntlm_client *ntlm); + +/** + * Gets the remote hosts's domain, which is generally the short or + * NT-style domain name. This will be given as it was provided in + * the challenge message. + * + * @param ntlm the `ntlm_client` context to query + * @return the remote host's domain + */ +NTLM_EXTERN(const char *) ntlm_client_target_domain(ntlm_client *ntlm); + +/** + * Gets the remote hosts's DNS name, which is generally the long-style + * Active Directory or fully-qualified hostname. This will be given + * as it was provided in the challenge message. + * + * @param ntlm the `ntlm_client` context to query + * @return the remote host's DNS name + */ +NTLM_EXTERN(const char *) ntlm_client_target_server_dns(ntlm_client *ntlm); + +/** + * Gets the remote hosts's DNS domain, which is generally the long-style + * Active Directory or fully-qualified domain name. This will be given + * as it was provided in the challenge message. + * + * @param ntlm the `ntlm_client` context to query + * @return the remote host's DNS domain + */ +NTLM_EXTERN(const char *) ntlm_client_target_domain_dns(ntlm_client *ntlm); + +/** + * Computes a negotiation message (aka a "Type 1" message) to begin + * NTLM authentication with the server. The local hostname should be + * set before calling this function (if necessary). This message + * should be delivered to the server to indicate a willingness to begin + * NTLM authentication. This buffer should not be freed by the caller. + * + * @param out a pointer to the negotiation message + * @param out_len a pointer to the length of the negotiation message + * @param ntlm the `ntlm_client` context + * @return 0 on success, non-zero on failure + */ +NTLM_EXTERN(int) ntlm_client_negotiate( + const unsigned char **out, + size_t *out_len, + ntlm_client *ntlm); + +/** + * Parses a challenge message (aka a "Type 2" message) from the server. + * This must be called in order to calculate the response to the + * authentication. + * + * @param ntlm the `ntlm_client` context + * @param message the challenge message from the server + * @param message_len the length of the challenge message + * @return 0 on success, non-zero on failure + */ +NTLM_EXTERN(int) ntlm_client_set_challenge( + ntlm_client *ntlm, + const unsigned char *message, + size_t message_len); + +/** + * Computes a response message (aka a "Type 3" message) to complete + * NTLM authentication with the server. The credentials should be + * set before calling this function. This message should be delivered + * to the server to complete authentication. This buffer should not + * be freed by the caller. + * + * @param out a pointer to the response message + * @param out_len a pointer to the length of the response message + * @param ntlm the `ntlm_client` context + * @return 0 on success, non-zero on failure + */ +NTLM_EXTERN(int) ntlm_client_response( + const unsigned char **out, + size_t *out_len, + ntlm_client *ntlm); + +/** + * Resets an `ntlm_client` context completely, so that authentication + * may be retried. You must set _all_ parameters again, including the + * target, username, password, etc. Once these values are configured + * again, the negotiation can begin. + * + * @param ntlm the `ntlm_client` context to reset + */ +NTLM_EXTERN(void) ntlm_client_reset(ntlm_client *ntlm); + +/** + * Frees an `ntlm_client` context. This should be done to free memory + * belonging to the context. The context cannot be reused. + * + * @param ntlm the `ntlm_client` context to free + */ +NTLM_EXTERN(void) ntlm_client_free(ntlm_client *ntlm); + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDE_NTLMCLIENT_H__ */ diff --git a/deps/ntlmclient/unicode.h b/deps/ntlmclient/unicode.h new file mode 100644 index 000000000..e3b17bcf7 --- /dev/null +++ b/deps/ntlmclient/unicode.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#ifndef PRIVATE_UNICODE_H__ +#define PRIVATE_UNICODE_H__ + +#include "compat.h" + +#define NTLM_UNICODE_MAX_LEN 2048 + +typedef struct ntlm_unicode_ctx ntlm_unicode_ctx; + +extern ntlm_unicode_ctx *ntlm_unicode_ctx_init(ntlm_client *ntlm); + +bool ntlm_unicode_utf8_to_16( + char **converted, + size_t *converted_len, + ntlm_unicode_ctx *ctx, + const char *string, + size_t string_len); + +bool ntlm_unicode_utf16_to_8( + char **converted, + size_t *converted_len, + ntlm_unicode_ctx *ctx, + const char *string, + size_t string_len); + +extern void ntlm_unicode_ctx_free(ntlm_unicode_ctx *ctx); + +#endif /* PRIVATE_UNICODE_H__ */ diff --git a/deps/ntlmclient/unicode_builtin.c b/deps/ntlmclient/unicode_builtin.c new file mode 100644 index 000000000..e1856cca9 --- /dev/null +++ b/deps/ntlmclient/unicode_builtin.c @@ -0,0 +1,445 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#include <stdlib.h> +#include <stdint.h> + +#include "ntlm.h" +#include "unicode.h" +#include "compat.h" + +struct ntlm_unicode_ctx { + ntlm_client *ntlm; +}; + +typedef unsigned int UTF32; /* at least 32 bits */ +typedef unsigned short UTF16; /* at least 16 bits */ +typedef unsigned char UTF8; /* typically 8 bits */ + +/* Some fundamental constants */ +#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD +#define UNI_MAX_BMP (UTF32)0x0000FFFF +#define UNI_MAX_UTF16 (UTF32)0x0010FFFF +#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF + +#define UNI_MAX_UTF8_BYTES_PER_CODE_POINT 4 + +typedef enum { + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum { + strictConversion = 0, + lenientConversion +} ConversionFlags; + + +static const int halfShift = 10; /* used for shifting by 10 bits */ + +static const UTF32 halfBase = 0x0010000UL; +static const UTF32 halfMask = 0x3FFUL; + +#define UNI_SUR_HIGH_START (UTF32)0xD800 +#define UNI_SUR_HIGH_END (UTF32)0xDBFF +#define UNI_SUR_LOW_START (UTF32)0xDC00 +#define UNI_SUR_LOW_END (UTF32)0xDFFF +#define false 0 +#define true 1 + +/* --------------------------------------------------------------------- */ + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is + * left as-is for anyone who may want to do such conversion, which was + * allowed in earlier algorithms. + */ +static const char trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +/* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. + */ +static const UTF32 offsetsFromUTF8[6] = { + 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; + +/* + * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed + * into the first byte, depending on how many bytes follow. There are + * as many entries in this table as there are UTF-8 sequence types. + * (I.e., one byte sequence, two byte... etc.). Remember that sequencs + * for *legal* UTF-8 will be 4 or fewer bytes total. + */ +static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +/* --------------------------------------------------------------------- */ + +/* The interface converts a whole buffer to avoid function-call overhead. + * Constants have been gathered. Loops & conditionals have been removed as + * much as possible for efficiency, in favor of drop-through switches. + * (See "Note A" at the bottom of the file for equivalent code.) + * If your compiler supports it, the "isLegalUTF8" call can be turned + * into an inline function. + */ + +static ConversionResult ConvertUTF16toUTF8 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16* source = *sourceStart; + UTF8* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + UTF32 ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* Figure out how many bytes the result will require */ + if (ch < (UTF32)0x80) { bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; + } else if (ch < (UTF32)0x110000) { bytesToWrite = 4; + } else { bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + } + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; /* Back up source pointer! */ + target -= bytesToWrite; result = targetExhausted; break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * If not calling this from ConvertUTF8to*, then the length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns false. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ + +static inline bool isLegalUTF8(const UTF8 *source, int length) { + UTF8 a; + const UTF8 *srcptr = source+length; + switch (length) { + default: return false; + /* Everything else falls through when "true"... */ + case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 2: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: if (a < 0xA0) return false; break; + case 0xED: if (a > 0x9F) return false; break; + case 0xF0: if (a < 0x90) return false; break; + case 0xF4: if (a > 0x8F) return false; break; + default: if (a < 0x80) return false; + } + + case 1: if (*source >= 0x80 && *source < 0xC2) return false; + } + if (*source > 0xF4) return false; + return true; +} + +static ConversionResult ConvertUTF8toUTF16 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8* source = *sourceStart; + UTF16* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (extraBytesToRead >= sourceEnd - source) { + result = sourceExhausted; break; + } + /* Do this check whether lenient or strict */ + if (!isLegalUTF8(source, extraBytesToRead+1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ + case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ + case 3: ch += *source++; ch <<= 6; + case 2: ch += *source++; ch <<= 6; + case 1: ch += *source++; ch <<= 6; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up source pointer! */ + result = targetExhausted; break; + } + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead+1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_UTF16) { + if (flags == strictConversion) { + result = sourceIllegal; + source -= (extraBytesToRead+1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up source pointer! */ + result = targetExhausted; break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + + +ntlm_unicode_ctx *ntlm_unicode_ctx_init(ntlm_client *ntlm) +{ + ntlm_unicode_ctx *ctx; + + if ((ctx = malloc(sizeof(ntlm_unicode_ctx))) == NULL) + return NULL; + + ctx->ntlm = ntlm; + return ctx; +} + +typedef enum { + unicode_builtin_utf8_to_16, + unicode_builtin_utf16_to_8 +} unicode_builtin_encoding_direction; + +static inline bool unicode_builtin_encoding_convert( + char **converted, + size_t *converted_len, + ntlm_unicode_ctx *ctx, + const char *string, + size_t string_len, + unicode_builtin_encoding_direction direction) +{ + const char *in_start, *in_end; + char *out, *out_start, *out_end, *new_out; + size_t out_size, out_len; + bool success = false; + ConversionResult result; + + *converted = NULL; + *converted_len = 0; + + in_start = string; + in_end = in_start + string_len; + + /* + * When translating UTF8 to UTF16, these strings are only used + * internally, and we obey the given length, so we can simply + * use a buffer that is 2x the size. Add an extra byte to NUL + * terminate the results (two bytes for UTF16). + */ + if (direction == unicode_builtin_utf8_to_16) + out_size = (string_len * 2 + 2); + else + out_size = (string_len / 2 + 1); + + /* Round to the nearest multiple of 8 */ + out_size = (out_size + 7) & ~7; + + if ((out = malloc(out_size)) == NULL) { + ntlm_client_set_errmsg(ctx->ntlm, "out of memory"); + return false; + } + + out_start = out; + out_end = out_start + out_size; + + /* Make room for NUL termination */ + if (direction == unicode_builtin_utf16_to_8) + out_end--; + + while (true) { + if (direction == unicode_builtin_utf8_to_16) + result = ConvertUTF8toUTF16( + (const UTF8 **)&in_start, (UTF8 *)in_end, + (UTF16 **)&out_start, (UTF16 *)out_end, strictConversion); + else + result = ConvertUTF16toUTF8( + (const UTF16 **)&in_start, (UTF16 *)in_end, + (UTF8 **)&out_start, (UTF8 *)out_end, lenientConversion); + + switch (result) { + case conversionOK: + success = true; + goto done; + case sourceExhausted: + ntlm_client_set_errmsg(ctx->ntlm, + "invalid unicode string; trailing data remains"); + goto done; + case targetExhausted: + break; + case sourceIllegal: + ntlm_client_set_errmsg(ctx->ntlm, + "invalid unicode string; trailing data remains"); + goto done; + default: + ntlm_client_set_errmsg(ctx->ntlm, + "unknown unicode conversion failure"); + goto done; + } + + /* Grow buffer size by 1.5 (rounded up to a multiple of 8) */ + out_size = ((((out_size << 1) - (out_size >> 1)) + 7) & ~7); + + if (out_size > NTLM_UNICODE_MAX_LEN) { + ntlm_client_set_errmsg(ctx->ntlm, + "unicode conversion too large"); + goto done; + } + + if ((new_out = realloc(out, out_size)) == NULL) { + ntlm_client_set_errmsg(ctx->ntlm, "out of memory"); + goto done; + } + + out_len = out_start - out; + + out = new_out; + out_start = new_out + out_len; + out_end = out + out_size; + + /* Make room for NUL termination */ + out_end -= (direction == unicode_builtin_utf8_to_16) ? 2 : 1; + } + +done: + if (!success) { + free(out); + return false; + } + + out_len = (out_start - out); + + /* NUL terminate */ + out[out_len] = '\0'; + + if (direction == unicode_builtin_utf8_to_16) + out[out_len+1] = '\0'; + + *converted = out; + *converted_len = out_len; + return true; +} + +bool ntlm_unicode_utf8_to_16( + char **converted, + size_t *converted_len, + ntlm_unicode_ctx *ctx, + const char *string, + size_t string_len) +{ + return unicode_builtin_encoding_convert(converted, converted_len, + ctx, string, string_len, unicode_builtin_utf8_to_16); +} + +bool ntlm_unicode_utf16_to_8( + char **converted, + size_t *converted_len, + ntlm_unicode_ctx *ctx, + const char *string, + size_t string_len) +{ + return unicode_builtin_encoding_convert(converted, converted_len, + ctx, string, string_len, unicode_builtin_utf16_to_8); +} + +void ntlm_unicode_ctx_free(ntlm_unicode_ctx *ctx) +{ + if (ctx) + free(ctx); +} diff --git a/deps/ntlmclient/unicode_iconv.c b/deps/ntlmclient/unicode_iconv.c new file mode 100644 index 000000000..d1fe07e26 --- /dev/null +++ b/deps/ntlmclient/unicode_iconv.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#include <locale.h> +#include <iconv.h> +#include <string.h> +#include <errno.h> + +#include "ntlmclient.h" +#include "unicode.h" +#include "ntlm.h" +#include "compat.h" + +struct ntlm_unicode_ctx { + ntlm_client *ntlm; + iconv_t utf8_to_16; + iconv_t utf16_to_8; +}; + +ntlm_unicode_ctx *ntlm_unicode_ctx_init(ntlm_client *ntlm) +{ + ntlm_unicode_ctx *ctx; + + if ((ctx = calloc(1, sizeof(ntlm_unicode_ctx))) == NULL) + return NULL; + + ctx->ntlm = ntlm; + ctx->utf8_to_16 = (iconv_t)-1; + ctx->utf16_to_8 = (iconv_t)-1; + + return ctx; +} + +typedef enum { + unicode_iconv_utf8_to_16, + unicode_iconv_utf16_to_8 +} unicode_iconv_encoding_direction; + +static inline bool unicode_iconv_init(ntlm_unicode_ctx *ctx) +{ + if (ctx->utf8_to_16 != (iconv_t)-1 || ctx->utf16_to_8 != (iconv_t)-1) + return true; + + if ((ctx->utf8_to_16 = iconv_open("UTF-16LE", "UTF-8")) == (iconv_t)-1 || + (ctx->utf16_to_8 = iconv_open("UTF-8", "UTF-16LE")) == (iconv_t)-1) { + if (errno == EINVAL) + ntlm_client_set_errmsg(ctx->ntlm, + "iconv does not support UTF8 <-> UTF16 conversion"); + else + ntlm_client_set_errmsg(ctx->ntlm, strerror(errno)); + + return false; + } + + return true; +} + +static inline bool unicode_iconv_encoding_convert( + char **converted, + size_t *converted_len, + ntlm_unicode_ctx *ctx, + const char *string, + size_t string_len, + unicode_iconv_encoding_direction direction) +{ + char *in_start, *out_start, *out, *new_out; + size_t in_start_len, out_start_len, out_size, nul_size, ret, written = 0; + iconv_t converter; + + *converted = NULL; + *converted_len = 0; + + if (!unicode_iconv_init(ctx)) + return false; + + /* + * When translating UTF8 to UTF16, these strings are only used + * internally, and we obey the given length, so we can simply + * use a buffer that is 2x the size. When translating from UTF16 + * to UTF8, we may need to return to callers, so we need to NUL + * terminate and expect an extra byte for UTF8, two for UTF16. + */ + if (direction == unicode_iconv_utf8_to_16) { + converter = ctx->utf8_to_16; + out_size = (string_len * 2) + 2; + nul_size = 2; + } else { + converter = ctx->utf16_to_8; + out_size = (string_len / 2) + 1; + nul_size = 1; + } + + /* Round to the nearest multiple of 8 */ + out_size = (out_size + 7) & ~7; + + if ((out = malloc(out_size)) == NULL) { + ntlm_client_set_errmsg(ctx->ntlm, "out of memory"); + return false; + } + + in_start = (char *)string; + in_start_len = string_len; + + while (true) { + out_start = out + written; + out_start_len = (out_size - nul_size) - written; + + ret = iconv(converter, &in_start, &in_start_len, &out_start, &out_start_len); + written = (out_size - nul_size) - out_start_len; + + if (ret == 0) + break; + + if (ret == (size_t)-1 && errno != E2BIG) { + ntlm_client_set_errmsg(ctx->ntlm, strerror(errno)); + goto on_error; + } + + /* Grow buffer size by 1.5 (rounded up to a multiple of 8) */ + out_size = ((((out_size << 1) - (out_size >> 1)) + 7) & ~7); + + if (out_size > NTLM_UNICODE_MAX_LEN) { + ntlm_client_set_errmsg(ctx->ntlm, + "unicode conversion too large"); + goto on_error; + } + + if ((new_out = realloc(out, out_size)) == NULL) { + ntlm_client_set_errmsg(ctx->ntlm, "out of memory"); + goto on_error; + } + + out = new_out; + } + + if (in_start_len != 0) { + ntlm_client_set_errmsg(ctx->ntlm, + "invalid unicode string; trailing data remains"); + goto on_error; + } + + /* NUL terminate */ + out[written] = '\0'; + + if (direction == unicode_iconv_utf8_to_16) + out[written + 1] = '\0'; + + *converted = out; + + if (converted_len) + *converted_len = written; + + return true; + +on_error: + free(out); + return false; +} + +bool ntlm_unicode_utf8_to_16( + char **converted, + size_t *converted_len, + ntlm_unicode_ctx *ctx, + const char *string, + size_t string_len) +{ + return unicode_iconv_encoding_convert( + converted, converted_len, ctx, string, string_len, + unicode_iconv_utf8_to_16); +} + +bool ntlm_unicode_utf16_to_8( + char **converted, + size_t *converted_len, + ntlm_unicode_ctx *ctx, + const char *string, + size_t string_len) +{ + return unicode_iconv_encoding_convert( + converted, converted_len, ctx, string, string_len, + unicode_iconv_utf16_to_8); +} + +void ntlm_unicode_ctx_free(ntlm_unicode_ctx *ctx) +{ + if (!ctx) + return; + + if (ctx->utf16_to_8 != (iconv_t)-1) + iconv_close(ctx->utf16_to_8); + + if (ctx->utf8_to_16 != (iconv_t)-1) + iconv_close(ctx->utf8_to_16); + + free(ctx); +} diff --git a/deps/ntlmclient/utf8.h b/deps/ntlmclient/utf8.h new file mode 100644 index 000000000..a26ae85c3 --- /dev/null +++ b/deps/ntlmclient/utf8.h @@ -0,0 +1,1257 @@ +// The latest version of this library is available on GitHub; +// https://github.com/sheredom/utf8.h + +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to <http://unlicense.org/> + +#ifndef SHEREDOM_UTF8_H_INCLUDED +#define SHEREDOM_UTF8_H_INCLUDED + +#if defined(_MSC_VER) +#pragma warning(push) + +// disable 'bytes padding added after construct' warning +#pragma warning(disable : 4820) +#endif + +#include <stddef.h> +#include <stdlib.h> + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(_MSC_VER) +typedef __int32 utf8_int32_t; +#else +#include <stdint.h> +typedef int32_t utf8_int32_t; +#endif + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wcast-qual" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__clang__) || defined(__GNUC__) +#define utf8_nonnull __attribute__((nonnull)) +#define utf8_pure __attribute__((pure)) +#define utf8_restrict __restrict__ +#define utf8_weak __attribute__((weak)) +#elif defined(_MSC_VER) +#define utf8_nonnull +#define utf8_pure +#define utf8_restrict __restrict +#define utf8_weak __inline +#else +#error Non clang, non gcc, non MSVC compiler found! +#endif + +#ifdef __cplusplus +#define utf8_null NULL +#else +#define utf8_null 0 +#endif + +// Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 > +// src2 respectively, case insensitive. +utf8_nonnull utf8_pure utf8_weak int utf8casecmp(const void *src1, + const void *src2); + +// Append the utf8 string src onto the utf8 string dst. +utf8_nonnull utf8_weak void *utf8cat(void *utf8_restrict dst, + const void *utf8_restrict src); + +// Find the first match of the utf8 codepoint chr in the utf8 string src. +utf8_nonnull utf8_pure utf8_weak void *utf8chr(const void *src, + utf8_int32_t chr); + +// Return less than 0, 0, greater than 0 if src1 < src2, +// src1 == src2, src1 > src2 respectively. +utf8_nonnull utf8_pure utf8_weak int utf8cmp(const void *src1, + const void *src2); + +// Copy the utf8 string src onto the memory allocated in dst. +utf8_nonnull utf8_weak void *utf8cpy(void *utf8_restrict dst, + const void *utf8_restrict src); + +// Number of utf8 codepoints in the utf8 string src that consists entirely +// of utf8 codepoints not from the utf8 string reject. +utf8_nonnull utf8_pure utf8_weak size_t utf8cspn(const void *src, + const void *reject); + +// Duplicate the utf8 string src by getting its size, malloc'ing a new buffer +// copying over the data, and returning that. Or 0 if malloc failed. +utf8_nonnull utf8_weak void *utf8dup(const void *src); + +// Number of utf8 codepoints in the utf8 string str, +// excluding the null terminating byte. +utf8_nonnull utf8_pure utf8_weak size_t utf8len(const void *str); + +// Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 > +// src2 respectively, case insensitive. Checking at most n bytes of each utf8 +// string. +utf8_nonnull utf8_pure utf8_weak int utf8ncasecmp(const void *src1, + const void *src2, size_t n); + +// Append the utf8 string src onto the utf8 string dst, +// writing at most n+1 bytes. Can produce an invalid utf8 +// string if n falls partway through a utf8 codepoint. +utf8_nonnull utf8_weak void *utf8ncat(void *utf8_restrict dst, + const void *utf8_restrict src, size_t n); + +// Return less than 0, 0, greater than 0 if src1 < src2, +// src1 == src2, src1 > src2 respectively. Checking at most n +// bytes of each utf8 string. +utf8_nonnull utf8_pure utf8_weak int utf8ncmp(const void *src1, + const void *src2, size_t n); + +// Copy the utf8 string src onto the memory allocated in dst. +// Copies at most n bytes. If there is no terminating null byte in +// the first n bytes of src, the string placed into dst will not be +// null-terminated. If the size (in bytes) of src is less than n, +// extra null terminating bytes are appended to dst such that at +// total of n bytes are written. Can produce an invalid utf8 +// string if n falls partway through a utf8 codepoint. +utf8_nonnull utf8_weak void *utf8ncpy(void *utf8_restrict dst, + const void *utf8_restrict src, size_t n); + +// Similar to utf8dup, except that at most n bytes of src are copied. If src is +// longer than n, only n bytes are copied and a null byte is added. +// +// Returns a new string if successful, 0 otherwise +utf8_nonnull utf8_weak void *utf8ndup(const void *src, size_t n); + +// Locates the first occurence in the utf8 string str of any byte in the +// utf8 string accept, or 0 if no match was found. +utf8_nonnull utf8_pure utf8_weak void *utf8pbrk(const void *str, + const void *accept); + +// Find the last match of the utf8 codepoint chr in the utf8 string src. +utf8_nonnull utf8_pure utf8_weak void *utf8rchr(const void *src, int chr); + +// Number of bytes in the utf8 string str, +// including the null terminating byte. +utf8_nonnull utf8_pure utf8_weak size_t utf8size(const void *str); + +// Number of utf8 codepoints in the utf8 string src that consists entirely +// of utf8 codepoints from the utf8 string accept. +utf8_nonnull utf8_pure utf8_weak size_t utf8spn(const void *src, + const void *accept); + +// The position of the utf8 string needle in the utf8 string haystack. +utf8_nonnull utf8_pure utf8_weak void *utf8str(const void *haystack, + const void *needle); + +// The position of the utf8 string needle in the utf8 string haystack, case +// insensitive. +utf8_nonnull utf8_pure utf8_weak void *utf8casestr(const void *haystack, + const void *needle); + +// Return 0 on success, or the position of the invalid +// utf8 codepoint on failure. +utf8_nonnull utf8_pure utf8_weak void *utf8valid(const void *str); + +// Sets out_codepoint to the next utf8 codepoint in str, and returns the address +// of the utf8 codepoint after the current one in str. +utf8_nonnull utf8_weak void * +utf8codepoint(const void *utf8_restrict str, + utf8_int32_t *utf8_restrict out_codepoint); + +// Returns the size of the given codepoint in bytes. +utf8_weak size_t utf8codepointsize(utf8_int32_t chr); + +// Write a codepoint to the given string, and return the address to the next +// place after the written codepoint. Pass how many bytes left in the buffer to +// n. If there is not enough space for the codepoint, this function returns +// null. +utf8_nonnull utf8_weak void *utf8catcodepoint(void *utf8_restrict str, + utf8_int32_t chr, size_t n); + +// Returns 1 if the given character is lowercase, or 0 if it is not. +utf8_weak int utf8islower(utf8_int32_t chr); + +// Returns 1 if the given character is uppercase, or 0 if it is not. +utf8_weak int utf8isupper(utf8_int32_t chr); + +// Transform the given string into all lowercase codepoints. +utf8_nonnull utf8_weak void utf8lwr(void *utf8_restrict str); + +// Transform the given string into all uppercase codepoints. +utf8_nonnull utf8_weak void utf8upr(void *utf8_restrict str); + +// Make a codepoint lower case if possible. +utf8_weak utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp); + +// Make a codepoint upper case if possible. +utf8_weak utf8_int32_t utf8uprcodepoint(utf8_int32_t cp); + +#undef utf8_weak +#undef utf8_pure +#undef utf8_nonnull + +int utf8casecmp(const void *src1, const void *src2) { + utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp; + + for (;;) { + src1 = utf8codepoint(src1, &src1_cp); + src2 = utf8codepoint(src2, &src2_cp); + + // Take a copy of src1 & src2 + src1_orig_cp = src1_cp; + src2_orig_cp = src2_cp; + + // Lower the srcs if required + src1_cp = utf8lwrcodepoint(src1_cp); + src2_cp = utf8lwrcodepoint(src2_cp); + + // Check if the lowered codepoints match + if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) { + return 0; + } else if (src1_cp == src2_cp) { + continue; + } + + // If they don't match, then we return which of the original's are less + if (src1_orig_cp < src2_orig_cp) { + return -1; + } else if (src1_orig_cp > src2_orig_cp) { + return 1; + } + } +} + +void *utf8cat(void *utf8_restrict dst, const void *utf8_restrict src) { + char *d = (char *)dst; + const char *s = (const char *)src; + + // find the null terminating byte in dst + while ('\0' != *d) { + d++; + } + + // overwriting the null terminating byte in dst, append src byte-by-byte + while ('\0' != *s) { + *d++ = *s++; + } + + // write out a new null terminating byte into dst + *d = '\0'; + + return dst; +} + +void *utf8chr(const void *src, utf8_int32_t chr) { + char c[5] = {'\0', '\0', '\0', '\0', '\0'}; + + if (0 == chr) { + // being asked to return position of null terminating byte, so + // just run s to the end, and return! + const char *s = (const char *)src; + while ('\0' != *s) { + s++; + } + return (void *)s; + } else if (0 == ((utf8_int32_t)0xffffff80 & chr)) { + // 1-byte/7-bit ascii + // (0b0xxxxxxx) + c[0] = (char)chr; + } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) { + // 2-byte/11-bit utf8 code point + // (0b110xxxxx 0b10xxxxxx) + c[0] = 0xc0 | (char)(chr >> 6); + c[1] = 0x80 | (char)(chr & 0x3f); + } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) { + // 3-byte/16-bit utf8 code point + // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) + c[0] = 0xe0 | (char)(chr >> 12); + c[1] = 0x80 | (char)((chr >> 6) & 0x3f); + c[2] = 0x80 | (char)(chr & 0x3f); + } else { // if (0 == ((int)0xffe00000 & chr)) { + // 4-byte/21-bit utf8 code point + // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) + c[0] = 0xf0 | (char)(chr >> 18); + c[1] = 0x80 | (char)((chr >> 12) & 0x3f); + c[2] = 0x80 | (char)((chr >> 6) & 0x3f); + c[3] = 0x80 | (char)(chr & 0x3f); + } + + // we've made c into a 2 utf8 codepoint string, one for the chr we are + // seeking, another for the null terminating byte. Now use utf8str to + // search + return utf8str(src, c); +} + +int utf8cmp(const void *src1, const void *src2) { + const unsigned char *s1 = (const unsigned char *)src1; + const unsigned char *s2 = (const unsigned char *)src2; + + while (('\0' != *s1) || ('\0' != *s2)) { + if (*s1 < *s2) { + return -1; + } else if (*s1 > *s2) { + return 1; + } + + s1++; + s2++; + } + + // both utf8 strings matched + return 0; +} + +int utf8coll(const void *src1, const void *src2); + +void *utf8cpy(void *utf8_restrict dst, const void *utf8_restrict src) { + char *d = (char *)dst; + const char *s = (const char *)src; + + // overwriting anything previously in dst, write byte-by-byte + // from src + while ('\0' != *s) { + *d++ = *s++; + } + + // append null terminating byte + *d = '\0'; + + return dst; +} + +size_t utf8cspn(const void *src, const void *reject) { + const char *s = (const char *)src; + size_t chars = 0; + + while ('\0' != *s) { + const char *r = (const char *)reject; + size_t offset = 0; + + while ('\0' != *r) { + // checking that if *r is the start of a utf8 codepoint + // (it is not 0b10xxxxxx) and we have successfully matched + // a previous character (0 < offset) - we found a match + if ((0x80 != (0xc0 & *r)) && (0 < offset)) { + return chars; + } else { + if (*r == s[offset]) { + // part of a utf8 codepoint matched, so move our checking + // onwards to the next byte + offset++; + r++; + } else { + // r could be in the middle of an unmatching utf8 code point, + // so we need to march it on to the next character beginning, + + do { + r++; + } while (0x80 == (0xc0 & *r)); + + // reset offset too as we found a mismatch + offset = 0; + } + } + } + + // the current utf8 codepoint in src did not match reject, but src + // could have been partway through a utf8 codepoint, so we need to + // march it onto the next utf8 codepoint starting byte + do { + s++; + } while ((0x80 == (0xc0 & *s))); + chars++; + } + + return chars; +} + +size_t utf8size(const void *str); + +void *utf8dup(const void *src) { + const char *s = (const char *)src; + char *n = utf8_null; + + // figure out how many bytes (including the terminator) we need to copy first + size_t bytes = utf8size(src); + + n = (char *)malloc(bytes); + + if (utf8_null == n) { + // out of memory so we bail + return utf8_null; + } else { + bytes = 0; + + // copy src byte-by-byte into our new utf8 string + while ('\0' != s[bytes]) { + n[bytes] = s[bytes]; + bytes++; + } + + // append null terminating byte + n[bytes] = '\0'; + return n; + } +} + +void *utf8fry(const void *str); + +size_t utf8len(const void *str) { + const unsigned char *s = (const unsigned char *)str; + size_t length = 0; + + while ('\0' != *s) { + if (0xf0 == (0xf8 & *s)) { + // 4-byte utf8 code point (began with 0b11110xxx) + s += 4; + } else if (0xe0 == (0xf0 & *s)) { + // 3-byte utf8 code point (began with 0b1110xxxx) + s += 3; + } else if (0xc0 == (0xe0 & *s)) { + // 2-byte utf8 code point (began with 0b110xxxxx) + s += 2; + } else { // if (0x00 == (0x80 & *s)) { + // 1-byte ascii (began with 0b0xxxxxxx) + s += 1; + } + + // no matter the bytes we marched s forward by, it was + // only 1 utf8 codepoint + length++; + } + + return length; +} + +int utf8ncasecmp(const void *src1, const void *src2, size_t n) { + utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp; + + do { + const unsigned char *const s1 = (const unsigned char *)src1; + const unsigned char *const s2 = (const unsigned char *)src2; + + // first check that we have enough bytes left in n to contain an entire + // codepoint + if (0 == n) { + return 0; + } + + if ((1 == n) && ((0xc0 == (0xe0 & *s1)) || (0xc0 == (0xe0 & *s2)))) { + const utf8_int32_t c1 = (0xe0 & *s1); + const utf8_int32_t c2 = (0xe0 & *s2); + + if (c1 < c2) { + return -1; + } else if (c1 > c2) { + return 1; + } else { + return 0; + } + } + + if ((2 >= n) && ((0xe0 == (0xf0 & *s1)) || (0xe0 == (0xf0 & *s2)))) { + const utf8_int32_t c1 = (0xf0 & *s1); + const utf8_int32_t c2 = (0xf0 & *s2); + + if (c1 < c2) { + return -1; + } else if (c1 > c2) { + return 1; + } else { + return 0; + } + } + + if ((3 >= n) && ((0xf0 == (0xf8 & *s1)) || (0xf0 == (0xf8 & *s2)))) { + const utf8_int32_t c1 = (0xf8 & *s1); + const utf8_int32_t c2 = (0xf8 & *s2); + + if (c1 < c2) { + return -1; + } else if (c1 > c2) { + return 1; + } else { + return 0; + } + } + + src1 = utf8codepoint(src1, &src1_cp); + src2 = utf8codepoint(src2, &src2_cp); + n -= utf8codepointsize(src1_cp); + + // Take a copy of src1 & src2 + src1_orig_cp = src1_cp; + src2_orig_cp = src2_cp; + + // Lower srcs if required + src1_cp = utf8lwrcodepoint(src1_cp); + src2_cp = utf8lwrcodepoint(src2_cp); + + // Check if the lowered codepoints match + if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) { + return 0; + } else if (src1_cp == src2_cp) { + continue; + } + + // If they don't match, then we return which of the original's are less + if (src1_orig_cp < src2_orig_cp) { + return -1; + } else if (src1_orig_cp > src2_orig_cp) { + return 1; + } + } while (0 < n); + + // both utf8 strings matched + return 0; +} + +void *utf8ncat(void *utf8_restrict dst, const void *utf8_restrict src, + size_t n) { + char *d = (char *)dst; + const char *s = (const char *)src; + + // find the null terminating byte in dst + while ('\0' != *d) { + d++; + } + + // overwriting the null terminating byte in dst, append src byte-by-byte + // stopping if we run out of space + do { + *d++ = *s++; + } while (('\0' != *s) && (0 != --n)); + + // write out a new null terminating byte into dst + *d = '\0'; + + return dst; +} + +int utf8ncmp(const void *src1, const void *src2, size_t n) { + const unsigned char *s1 = (const unsigned char *)src1; + const unsigned char *s2 = (const unsigned char *)src2; + + while ((('\0' != *s1) || ('\0' != *s2)) && (0 != n--)) { + if (*s1 < *s2) { + return -1; + } else if (*s1 > *s2) { + return 1; + } + + s1++; + s2++; + } + + // both utf8 strings matched + return 0; +} + +void *utf8ncpy(void *utf8_restrict dst, const void *utf8_restrict src, + size_t n) { + char *d = (char *)dst; + const char *s = (const char *)src; + + // overwriting anything previously in dst, write byte-by-byte + // from src + do { + *d++ = *s++; + } while (('\0' != *s) && (0 != --n)); + + // append null terminating byte + while (0 != n) { + *d++ = '\0'; + n--; + } + + return dst; +} + +void *utf8ndup(const void *src, size_t n) { + const char *s = (const char *)src; + char *c = utf8_null; + size_t bytes = 0; + + // Find the end of the string or stop when n is reached + while ('\0' != s[bytes] && bytes < n) { + bytes++; + } + + // In case bytes is actually less than n, we need to set it + // to be used later in the copy byte by byte. + n = bytes; + + c = (char *)malloc(bytes + 1); + if (utf8_null == c) { + // out of memory so we bail + return utf8_null; + } + + bytes = 0; + + // copy src byte-by-byte into our new utf8 string + while ('\0' != s[bytes] && bytes < n) { + c[bytes] = s[bytes]; + bytes++; + } + + // append null terminating byte + c[bytes] = '\0'; + return c; +} + +void *utf8rchr(const void *src, int chr) { + const char *s = (const char *)src; + const char *match = utf8_null; + char c[5] = {'\0', '\0', '\0', '\0', '\0'}; + + if (0 == chr) { + // being asked to return position of null terminating byte, so + // just run s to the end, and return! + while ('\0' != *s) { + s++; + } + return (void *)s; + } else if (0 == ((int)0xffffff80 & chr)) { + // 1-byte/7-bit ascii + // (0b0xxxxxxx) + c[0] = (char)chr; + } else if (0 == ((int)0xfffff800 & chr)) { + // 2-byte/11-bit utf8 code point + // (0b110xxxxx 0b10xxxxxx) + c[0] = 0xc0 | (char)(chr >> 6); + c[1] = 0x80 | (char)(chr & 0x3f); + } else if (0 == ((int)0xffff0000 & chr)) { + // 3-byte/16-bit utf8 code point + // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) + c[0] = 0xe0 | (char)(chr >> 12); + c[1] = 0x80 | (char)((chr >> 6) & 0x3f); + c[2] = 0x80 | (char)(chr & 0x3f); + } else { // if (0 == ((int)0xffe00000 & chr)) { + // 4-byte/21-bit utf8 code point + // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) + c[0] = 0xf0 | (char)(chr >> 18); + c[1] = 0x80 | (char)((chr >> 12) & 0x3f); + c[2] = 0x80 | (char)((chr >> 6) & 0x3f); + c[3] = 0x80 | (char)(chr & 0x3f); + } + + // we've created a 2 utf8 codepoint string in c that is + // the utf8 character asked for by chr, and a null + // terminating byte + + while ('\0' != *s) { + size_t offset = 0; + + while (s[offset] == c[offset]) { + offset++; + } + + if ('\0' == c[offset]) { + // we found a matching utf8 code point + match = s; + s += offset; + } else { + s += offset; + + // need to march s along to next utf8 codepoint start + // (the next byte that doesn't match 0b10xxxxxx) + if ('\0' != *s) { + do { + s++; + } while (0x80 == (0xc0 & *s)); + } + } + } + + // return the last match we found (or 0 if no match was found) + return (void *)match; +} + +void *utf8pbrk(const void *str, const void *accept) { + const char *s = (const char *)str; + + while ('\0' != *s) { + const char *a = (const char *)accept; + size_t offset = 0; + + while ('\0' != *a) { + // checking that if *a is the start of a utf8 codepoint + // (it is not 0b10xxxxxx) and we have successfully matched + // a previous character (0 < offset) - we found a match + if ((0x80 != (0xc0 & *a)) && (0 < offset)) { + return (void *)s; + } else { + if (*a == s[offset]) { + // part of a utf8 codepoint matched, so move our checking + // onwards to the next byte + offset++; + a++; + } else { + // r could be in the middle of an unmatching utf8 code point, + // so we need to march it on to the next character beginning, + + do { + a++; + } while (0x80 == (0xc0 & *a)); + + // reset offset too as we found a mismatch + offset = 0; + } + } + } + + // we found a match on the last utf8 codepoint + if (0 < offset) { + return (void *)s; + } + + // the current utf8 codepoint in src did not match accept, but src + // could have been partway through a utf8 codepoint, so we need to + // march it onto the next utf8 codepoint starting byte + do { + s++; + } while ((0x80 == (0xc0 & *s))); + } + + return utf8_null; +} + +size_t utf8size(const void *str) { + const char *s = (const char *)str; + size_t size = 0; + while ('\0' != s[size]) { + size++; + } + + // we are including the null terminating byte in the size calculation + size++; + return size; +} + +size_t utf8spn(const void *src, const void *accept) { + const char *s = (const char *)src; + size_t chars = 0; + + while ('\0' != *s) { + const char *a = (const char *)accept; + size_t offset = 0; + + while ('\0' != *a) { + // checking that if *r is the start of a utf8 codepoint + // (it is not 0b10xxxxxx) and we have successfully matched + // a previous character (0 < offset) - we found a match + if ((0x80 != (0xc0 & *a)) && (0 < offset)) { + // found a match, so increment the number of utf8 codepoints + // that have matched and stop checking whether any other utf8 + // codepoints in a match + chars++; + s += offset; + break; + } else { + if (*a == s[offset]) { + offset++; + a++; + } else { + // a could be in the middle of an unmatching utf8 codepoint, + // so we need to march it on to the next character beginning, + do { + a++; + } while (0x80 == (0xc0 & *a)); + + // reset offset too as we found a mismatch + offset = 0; + } + } + } + + // if a got to its terminating null byte, then we didn't find a match. + // Return the current number of matched utf8 codepoints + if ('\0' == *a) { + return chars; + } + } + + return chars; +} + +void *utf8str(const void *haystack, const void *needle) { + const char *h = (const char *)haystack; + + // if needle has no utf8 codepoints before the null terminating + // byte then return haystack + if ('\0' == *((const char *)needle)) { + return (void *)haystack; + } + + while ('\0' != *h) { + const char *maybeMatch = h; + const char *n = (const char *)needle; + + while (*h == *n && (*h != '\0' && *n != '\0')) { + n++; + h++; + } + + if ('\0' == *n) { + // we found the whole utf8 string for needle in haystack at + // maybeMatch, so return it + return (void *)maybeMatch; + } else { + // h could be in the middle of an unmatching utf8 codepoint, + // so we need to march it on to the next character beginning, + if ('\0' != *h) { + do { + h++; + } while (0x80 == (0xc0 & *h)); + } + } + } + + // no match + return utf8_null; +} + +void *utf8casestr(const void *haystack, const void *needle) { + const void *h = haystack; + + // if needle has no utf8 codepoints before the null terminating + // byte then return haystack + if ('\0' == *((const char *)needle)) { + return (void *)haystack; + } + + for (;;) { + const void *maybeMatch = h; + const void *n = needle; + utf8_int32_t h_cp, n_cp; + + h = utf8codepoint(h, &h_cp); + n = utf8codepoint(n, &n_cp); + + while ((0 != h_cp) && (0 != n_cp)) { + h_cp = utf8lwrcodepoint(h_cp); + n_cp = utf8lwrcodepoint(n_cp); + + // if we find a mismatch, bail out! + if (h_cp != n_cp) { + break; + } + + h = utf8codepoint(h, &h_cp); + n = utf8codepoint(n, &n_cp); + } + + if (0 == n_cp) { + // we found the whole utf8 string for needle in haystack at + // maybeMatch, so return it + return (void *)maybeMatch; + } + + if (0 == h_cp) { + // no match + return utf8_null; + } + } +} + +void *utf8valid(const void *str) { + const char *s = (const char *)str; + + while ('\0' != *s) { + if (0xf0 == (0xf8 & *s)) { + // ensure each of the 3 following bytes in this 4-byte + // utf8 codepoint began with 0b10xxxxxx + if ((0x80 != (0xc0 & s[1])) || (0x80 != (0xc0 & s[2])) || + (0x80 != (0xc0 & s[3]))) { + return (void *)s; + } + + // ensure that our utf8 codepoint ended after 4 bytes + if (0x80 == (0xc0 & s[4])) { + return (void *)s; + } + + // ensure that the top 5 bits of this 4-byte utf8 + // codepoint were not 0, as then we could have used + // one of the smaller encodings + if ((0 == (0x07 & s[0])) && (0 == (0x30 & s[1]))) { + return (void *)s; + } + + // 4-byte utf8 code point (began with 0b11110xxx) + s += 4; + } else if (0xe0 == (0xf0 & *s)) { + // ensure each of the 2 following bytes in this 3-byte + // utf8 codepoint began with 0b10xxxxxx + if ((0x80 != (0xc0 & s[1])) || (0x80 != (0xc0 & s[2]))) { + return (void *)s; + } + + // ensure that our utf8 codepoint ended after 3 bytes + if (0x80 == (0xc0 & s[3])) { + return (void *)s; + } + + // ensure that the top 5 bits of this 3-byte utf8 + // codepoint were not 0, as then we could have used + // one of the smaller encodings + if ((0 == (0x0f & s[0])) && (0 == (0x20 & s[1]))) { + return (void *)s; + } + + // 3-byte utf8 code point (began with 0b1110xxxx) + s += 3; + } else if (0xc0 == (0xe0 & *s)) { + // ensure the 1 following byte in this 2-byte + // utf8 codepoint began with 0b10xxxxxx + if (0x80 != (0xc0 & s[1])) { + return (void *)s; + } + + // ensure that our utf8 codepoint ended after 2 bytes + if (0x80 == (0xc0 & s[2])) { + return (void *)s; + } + + // ensure that the top 4 bits of this 2-byte utf8 + // codepoint were not 0, as then we could have used + // one of the smaller encodings + if (0 == (0x1e & s[0])) { + return (void *)s; + } + + // 2-byte utf8 code point (began with 0b110xxxxx) + s += 2; + } else if (0x00 == (0x80 & *s)) { + // 1-byte ascii (began with 0b0xxxxxxx) + s += 1; + } else { + // we have an invalid 0b1xxxxxxx utf8 code point entry + return (void *)s; + } + } + + return utf8_null; +} + +void *utf8codepoint(const void *utf8_restrict str, + utf8_int32_t *utf8_restrict out_codepoint) { + const char *s = (const char *)str; + + if (0xf0 == (0xf8 & s[0])) { + // 4 byte utf8 codepoint + *out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) | + ((0x3f & s[2]) << 6) | (0x3f & s[3]); + s += 4; + } else if (0xe0 == (0xf0 & s[0])) { + // 3 byte utf8 codepoint + *out_codepoint = + ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]); + s += 3; + } else if (0xc0 == (0xe0 & s[0])) { + // 2 byte utf8 codepoint + *out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]); + s += 2; + } else { + // 1 byte utf8 codepoint otherwise + *out_codepoint = s[0]; + s += 1; + } + + return (void *)s; +} + +size_t utf8codepointsize(utf8_int32_t chr) { + if (0 == ((utf8_int32_t)0xffffff80 & chr)) { + return 1; + } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) { + return 2; + } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) { + return 3; + } else { // if (0 == ((int)0xffe00000 & chr)) { + return 4; + } +} + +void *utf8catcodepoint(void *utf8_restrict str, utf8_int32_t chr, size_t n) { + char *s = (char *)str; + + if (0 == ((utf8_int32_t)0xffffff80 & chr)) { + // 1-byte/7-bit ascii + // (0b0xxxxxxx) + if (n < 1) { + return utf8_null; + } + s[0] = (char)chr; + s += 1; + } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) { + // 2-byte/11-bit utf8 code point + // (0b110xxxxx 0b10xxxxxx) + if (n < 2) { + return utf8_null; + } + s[0] = 0xc0 | (char)(chr >> 6); + s[1] = 0x80 | (char)(chr & 0x3f); + s += 2; + } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) { + // 3-byte/16-bit utf8 code point + // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) + if (n < 3) { + return utf8_null; + } + s[0] = 0xe0 | (char)(chr >> 12); + s[1] = 0x80 | (char)((chr >> 6) & 0x3f); + s[2] = 0x80 | (char)(chr & 0x3f); + s += 3; + } else { // if (0 == ((int)0xffe00000 & chr)) { + // 4-byte/21-bit utf8 code point + // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) + if (n < 4) { + return utf8_null; + } + s[0] = 0xf0 | (char)(chr >> 18); + s[1] = 0x80 | (char)((chr >> 12) & 0x3f); + s[2] = 0x80 | (char)((chr >> 6) & 0x3f); + s[3] = 0x80 | (char)(chr & 0x3f); + s += 4; + } + + return s; +} + +int utf8islower(utf8_int32_t chr) { return chr != utf8uprcodepoint(chr); } + +int utf8isupper(utf8_int32_t chr) { return chr != utf8lwrcodepoint(chr); } + +void utf8lwr(void *utf8_restrict str) { + void *p, *pn; + utf8_int32_t cp; + + p = (char *)str; + pn = utf8codepoint(p, &cp); + + while (cp != 0) { + const utf8_int32_t lwr_cp = utf8lwrcodepoint(cp); + const size_t size = utf8codepointsize(lwr_cp); + + if (lwr_cp != cp) { + utf8catcodepoint(p, lwr_cp, size); + } + + p = pn; + pn = utf8codepoint(p, &cp); + } +} + +void utf8upr(void *utf8_restrict str) { + void *p, *pn; + utf8_int32_t cp; + + p = (char *)str; + pn = utf8codepoint(p, &cp); + + while (cp != 0) { + const utf8_int32_t lwr_cp = utf8uprcodepoint(cp); + const size_t size = utf8codepointsize(lwr_cp); + + if (lwr_cp != cp) { + utf8catcodepoint(p, lwr_cp, size); + } + + p = pn; + pn = utf8codepoint(p, &cp); + } +} + +utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp) { + if (((0x0041 <= cp) && (0x005a >= cp)) || + ((0x00c0 <= cp) && (0x00d6 >= cp)) || + ((0x00d8 <= cp) && (0x00de >= cp)) || + ((0x0391 <= cp) && (0x03a1 >= cp)) || + ((0x03a3 <= cp) && (0x03ab >= cp))) { + cp += 32; + } else if (((0x0100 <= cp) && (0x012f >= cp)) || + ((0x0132 <= cp) && (0x0137 >= cp)) || + ((0x014a <= cp) && (0x0177 >= cp)) || + ((0x0182 <= cp) && (0x0185 >= cp)) || + ((0x01a0 <= cp) && (0x01a5 >= cp)) || + ((0x01de <= cp) && (0x01ef >= cp)) || + ((0x01f8 <= cp) && (0x021f >= cp)) || + ((0x0222 <= cp) && (0x0233 >= cp)) || + ((0x0246 <= cp) && (0x024f >= cp)) || + ((0x03d8 <= cp) && (0x03ef >= cp))) { + cp |= 0x1; + } else if (((0x0139 <= cp) && (0x0148 >= cp)) || + ((0x0179 <= cp) && (0x017e >= cp)) || + ((0x01af <= cp) && (0x01b0 >= cp)) || + ((0x01b3 <= cp) && (0x01b6 >= cp)) || + ((0x01cd <= cp) && (0x01dc >= cp))) { + cp += 1; + cp &= ~0x1; + } else { + switch (cp) { + default: break; + case 0x0178: cp = 0x00ff; break; + case 0x0243: cp = 0x0180; break; + case 0x018e: cp = 0x01dd; break; + case 0x023d: cp = 0x019a; break; + case 0x0220: cp = 0x019e; break; + case 0x01b7: cp = 0x0292; break; + case 0x01c4: cp = 0x01c6; break; + case 0x01c7: cp = 0x01c9; break; + case 0x01ca: cp = 0x01cc; break; + case 0x01f1: cp = 0x01f3; break; + case 0x01f7: cp = 0x01bf; break; + case 0x0187: cp = 0x0188; break; + case 0x018b: cp = 0x018c; break; + case 0x0191: cp = 0x0192; break; + case 0x0198: cp = 0x0199; break; + case 0x01a7: cp = 0x01a8; break; + case 0x01ac: cp = 0x01ad; break; + case 0x01af: cp = 0x01b0; break; + case 0x01b8: cp = 0x01b9; break; + case 0x01bc: cp = 0x01bd; break; + case 0x01f4: cp = 0x01f5; break; + case 0x023b: cp = 0x023c; break; + case 0x0241: cp = 0x0242; break; + case 0x03fd: cp = 0x037b; break; + case 0x03fe: cp = 0x037c; break; + case 0x03ff: cp = 0x037d; break; + case 0x037f: cp = 0x03f3; break; + case 0x0386: cp = 0x03ac; break; + case 0x0388: cp = 0x03ad; break; + case 0x0389: cp = 0x03ae; break; + case 0x038a: cp = 0x03af; break; + case 0x038c: cp = 0x03cc; break; + case 0x038e: cp = 0x03cd; break; + case 0x038f: cp = 0x03ce; break; + case 0x0370: cp = 0x0371; break; + case 0x0372: cp = 0x0373; break; + case 0x0376: cp = 0x0377; break; + case 0x03f4: cp = 0x03d1; break; + case 0x03cf: cp = 0x03d7; break; + case 0x03f9: cp = 0x03f2; break; + case 0x03f7: cp = 0x03f8; break; + case 0x03fa: cp = 0x03fb; break; + }; + } + + return cp; +} + +utf8_int32_t utf8uprcodepoint(utf8_int32_t cp) { + if (((0x0061 <= cp) && (0x007a >= cp)) || + ((0x00e0 <= cp) && (0x00f6 >= cp)) || + ((0x00f8 <= cp) && (0x00fe >= cp)) || + ((0x03b1 <= cp) && (0x03c1 >= cp)) || + ((0x03c3 <= cp) && (0x03cb >= cp))) { + cp -= 32; + } else if (((0x0100 <= cp) && (0x012f >= cp)) || + ((0x0132 <= cp) && (0x0137 >= cp)) || + ((0x014a <= cp) && (0x0177 >= cp)) || + ((0x0182 <= cp) && (0x0185 >= cp)) || + ((0x01a0 <= cp) && (0x01a5 >= cp)) || + ((0x01de <= cp) && (0x01ef >= cp)) || + ((0x01f8 <= cp) && (0x021f >= cp)) || + ((0x0222 <= cp) && (0x0233 >= cp)) || + ((0x0246 <= cp) && (0x024f >= cp)) || + ((0x03d8 <= cp) && (0x03ef >= cp))) { + cp &= ~0x1; + } else if (((0x0139 <= cp) && (0x0148 >= cp)) || + ((0x0179 <= cp) && (0x017e >= cp)) || + ((0x01af <= cp) && (0x01b0 >= cp)) || + ((0x01b3 <= cp) && (0x01b6 >= cp)) || + ((0x01cd <= cp) && (0x01dc >= cp))) { + cp -= 1; + cp |= 0x1; + } else { + switch (cp) { + default: break; + case 0x00ff: cp = 0x0178; break; + case 0x0180: cp = 0x0243; break; + case 0x01dd: cp = 0x018e; break; + case 0x019a: cp = 0x023d; break; + case 0x019e: cp = 0x0220; break; + case 0x0292: cp = 0x01b7; break; + case 0x01c6: cp = 0x01c4; break; + case 0x01c9: cp = 0x01c7; break; + case 0x01cc: cp = 0x01ca; break; + case 0x01f3: cp = 0x01f1; break; + case 0x01bf: cp = 0x01f7; break; + case 0x0188: cp = 0x0187; break; + case 0x018c: cp = 0x018b; break; + case 0x0192: cp = 0x0191; break; + case 0x0199: cp = 0x0198; break; + case 0x01a8: cp = 0x01a7; break; + case 0x01ad: cp = 0x01ac; break; + case 0x01b0: cp = 0x01af; break; + case 0x01b9: cp = 0x01b8; break; + case 0x01bd: cp = 0x01bc; break; + case 0x01f5: cp = 0x01f4; break; + case 0x023c: cp = 0x023b; break; + case 0x0242: cp = 0x0241; break; + case 0x037b: cp = 0x03fd; break; + case 0x037c: cp = 0x03fe; break; + case 0x037d: cp = 0x03ff; break; + case 0x03f3: cp = 0x037f; break; + case 0x03ac: cp = 0x0386; break; + case 0x03ad: cp = 0x0388; break; + case 0x03ae: cp = 0x0389; break; + case 0x03af: cp = 0x038a; break; + case 0x03cc: cp = 0x038c; break; + case 0x03cd: cp = 0x038e; break; + case 0x03ce: cp = 0x038f; break; + case 0x0371: cp = 0x0370; break; + case 0x0373: cp = 0x0372; break; + case 0x0377: cp = 0x0376; break; + case 0x03d1: cp = 0x03f4; break; + case 0x03d7: cp = 0x03cf; break; + case 0x03f2: cp = 0x03f9; break; + case 0x03f8: cp = 0x03f7; break; + case 0x03fb: cp = 0x03fa; break; + }; + } + + return cp; +} + +#undef utf8_restrict +#undef utf8_null + +#ifdef __cplusplus +} // extern "C" +#endif + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // SHEREDOM_UTF8_H_INCLUDED diff --git a/deps/ntlmclient/util.c b/deps/ntlmclient/util.c new file mode 100644 index 000000000..d0e3e53be --- /dev/null +++ b/deps/ntlmclient/util.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#include <stdlib.h> +#include <stdint.h> + +#include "compat.h" +#include "util.h" + +void memzero(void *data, size_t size) +{ + volatile uint8_t *scan = (volatile uint8_t *)data; + + while (size--) + *scan++ = 0x0; +} diff --git a/deps/ntlmclient/util.h b/deps/ntlmclient/util.h new file mode 100644 index 000000000..1c1806ba3 --- /dev/null +++ b/deps/ntlmclient/util.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#ifndef PRIVATE_UTIL_H__ +#define PRIVATE_UTIL_H__ + +extern void memzero(void *data, size_t size); + +#endif /* PRIVATE_UTIL_H__ */ |