summaryrefslogtreecommitdiff
path: root/deps
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2019-03-21 15:42:57 -1000
committerEdward Thomson <ethomson@edwardthomson.com>2019-06-10 19:58:22 +0100
commita7f65f03bd1150f48398eaf48703f90025b41342 (patch)
treeca7bc5c4a11f9f4fa13e04404d89547620b1a8c5 /deps
parent79fc8281879b804657de6db440630b4dc45423d5 (diff)
downloadlibgit2-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.txt18
-rw-r--r--deps/ntlmclient/compat.h33
-rw-r--r--deps/ntlmclient/crypt.h64
-rw-r--r--deps/ntlmclient/crypt_commoncrypto.c120
-rw-r--r--deps/ntlmclient/crypt_commoncrypto.h18
-rw-r--r--deps/ntlmclient/crypt_mbedtls.c145
-rw-r--r--deps/ntlmclient/crypt_mbedtls.h18
-rw-r--r--deps/ntlmclient/crypt_openssl.c130
-rw-r--r--deps/ntlmclient/crypt_openssl.h21
-rw-r--r--deps/ntlmclient/ntlm.c1420
-rw-r--r--deps/ntlmclient/ntlm.h174
-rw-r--r--deps/ntlmclient/ntlmclient.h320
-rw-r--r--deps/ntlmclient/unicode.h36
-rw-r--r--deps/ntlmclient/unicode_builtin.c445
-rw-r--r--deps/ntlmclient/unicode_iconv.c201
-rw-r--r--deps/ntlmclient/utf8.h1257
-rw-r--r--deps/ntlmclient/util.c21
-rw-r--r--deps/ntlmclient/util.h14
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], &timestamp, 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__ */