Initial commit

This commit is contained in:
jiapengyu
2026-07-27 13:37:48 +08:00
commit ecba71e2a5
39123 changed files with 5989154 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
FILE(GLOB SRC_NTLMCLIENT "ntlm.c" "unicode_builtin.c" "util.c")
ADD_DEFINITIONS(-DNTLM_STATIC=1)
DISABLE_WARNINGS(implicit-fallthrough)
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)
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
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})
+33
View File
@@ -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__ */
+64
View File
@@ -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__ */
@@ -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);
}
@@ -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__ */
+145
View File
@@ -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);
}
}
+18
View File
@@ -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__ */
+130
View File
@@ -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);
}
+21
View File
@@ -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__ */
File diff suppressed because it is too large Load Diff
+174
View File
@@ -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__ */
+320
View File
@@ -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__ */
+36
View File
@@ -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__ */
+445
View File
@@ -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);
}
+201
View File
@@ -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);
}
File diff suppressed because it is too large Load Diff
+21
View File
@@ -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;
}
+14
View File
@@ -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__ */