summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <idra@samba.org>2013-07-04 16:37:12 -0400
committerSimo Sorce <simo@redhat.com>2013-07-18 22:37:25 -0400
commit994d9639caeec0164134e3bd6c16512defe93021 (patch)
treeb7709922d0159a6250bb59b23dcf8a14af752cce
parent399c16829ceb8dad8f3f6e25e626212e4fca332e (diff)
downloadgss-ntlmssp-994d9639caeec0164134e3bd6c16512defe93021.tar.gz
gss-ntlmssp-994d9639caeec0164134e3bd6c16512defe93021.tar.xz
gss-ntlmssp-994d9639caeec0164134e3bd6c16512defe93021.zip
NTLM Crypto functions
Adds crypto function helpers needed by NTLM authentication
-rw-r--r--BUILD.txt3
-rw-r--r--Makefile.am9
-rw-r--r--configure.ac13
-rw-r--r--contrib/gssntlmssp.spec.in2
-rw-r--r--external/m4_ax_check_openssl.m4124
-rw-r--r--src/crypto.c221
-rw-r--r--src/crypto.h117
-rw-r--r--src/ntlm.c28
-rw-r--r--src/ntlm.h166
-rw-r--r--src/ntlm_common.h55
-rw-r--r--src/ntlm_crypto.c409
11 files changed, 1089 insertions, 58 deletions
diff --git a/BUILD.txt b/BUILD.txt
index b2c9487..2885e37 100644
--- a/BUILD.txt
+++ b/BUILD.txt
@@ -15,6 +15,7 @@ In order to build gss-ntlmssp the following development packages are needed:
libtool
libxml2
libxslt
+ libunistring
m4
pkgconfig
-
+ openssl
diff --git a/Makefile.am b/Makefile.am
index cb75e1e..798841b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -64,15 +64,19 @@ AM_CPPFLAGS = \
-DSYSCONFDIR=\"$(sysconfdir)\" \
-DLOCALEDIR=\"$(localedir)\"
-EXTRA_DIST = build/config.rpath
+#EXTRA_DIST = build/config.rpath
-GSS_NTLM_LIBS = $(GSSAPI_LIBS)
+GN_MECHGLUE_LIBS = $(GSSAPI_LIBS) $(CRYPTO_LIBS)
GN_MECHGLUE_OBJ = \
+ src/crypto.c \
+ src/ntlm_crypto.c \
src/ntlm.c \
src/gss_ntlmssp.c
dist_noinst_HEADERS = \
+ src/crypto.h \
+ src/ntlm_common.h \
src/ntlm.h \
src/gss_ntlmssp.h
@@ -85,6 +89,7 @@ gssntlmssp_la_SOURCES = \
gssntlmssp_la_CFLAGS = \
$(AM_FLAGS)
gssntlmssp_la_LDFLAGS = \
+ $(GN_MECHGLUE_LIBS) \
-avoid-version \
-module
diff --git a/configure.ac b/configure.ac
index e3a6887..352d535 100644
--- a/configure.ac
+++ b/configure.ac
@@ -61,6 +61,7 @@ m4_include([external/pkg.m4])
m4_include([external/docbook.m4])
m4_include([external/sizes.m4])
m4_include([external/ax_pthread.m4])
+m4_include([external/m4_ax_check_openssl.m4])
AX_PTHREAD(,[AC_MSG_ERROR([Could not find Pthreads support])])
@@ -86,6 +87,18 @@ AC_CHECK_LIB(gssapi_krb5, gss_export_cred,,
AC_SUBST([GSSAPI_CFLAGS])
AC_SUBST([GSSAPI_LIBS])
+AC_CHECK_HEADERS([unicase.h uniconv.h],,
+ [AC_MSG_ERROR([Could not find libunistring headers])])
+UNISTRING_LIBS="-lunistring"
+AC_CHECK_LIB(unistring, u8_toupper,,
+ [AC_MSG_ERROR([libunistring does not support u8_toupper])],
+ [$UNISTRING_LIBS])
+
+AX_CHECK_OPENSSL(,[AC_MSG_ERROR([Could not find OpenSSL support])])
+
+CRYPTO_LIBS="$OPENSSL_LIBS"
+AC_SUBST([CRYPTO_LIBS])
+
if test x$HAVE_MANPAGES != x; then
CHECK_XML_TOOLS
CHECK_STYLESHEET([$SGML_CATALOG_FILES],
diff --git a/contrib/gssntlmssp.spec.in b/contrib/gssntlmssp.spec.in
index 00eeb53..c1a85cd 100644
--- a/contrib/gssntlmssp.spec.in
+++ b/contrib/gssntlmssp.spec.in
@@ -29,6 +29,8 @@ BuildRequires: gettext-devel
BuildRequires: pkgconfig
BuildRequires: krb5-devel >= 1.11.2
BuildRequires: findutils
+BuildRequires: libunistring-devel
+BuildRequires: openssl-devel
%description
diff --git a/external/m4_ax_check_openssl.m4 b/external/m4_ax_check_openssl.m4
new file mode 100644
index 0000000..a87c5a6
--- /dev/null
+++ b/external/m4_ax_check_openssl.m4
@@ -0,0 +1,124 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_check_openssl.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_OPENSSL([action-if-found[, action-if-not-found]])
+#
+# DESCRIPTION
+#
+# Look for OpenSSL in a number of default spots, or in a user-selected
+# spot (via --with-openssl). Sets
+#
+# OPENSSL_INCLUDES to the include directives required
+# OPENSSL_LIBS to the -l directives required
+# OPENSSL_LDFLAGS to the -L or -R flags required
+#
+# and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately
+#
+# This macro sets OPENSSL_INCLUDES such that source files should use the
+# openssl/ directory in include directives:
+#
+# #include <openssl/hmac.h>
+#
+# LICENSE
+#
+# Copyright (c) 2009,2010 Zmanda Inc. <http://www.zmanda.com/>
+# Copyright (c) 2009,2010 Dustin J. Mitchell <dustin@zmanda.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 8
+
+AU_ALIAS([CHECK_SSL], [AX_CHECK_OPENSSL])
+AC_DEFUN([AX_CHECK_OPENSSL], [
+ found=false
+ AC_ARG_WITH([openssl],
+ [AS_HELP_STRING([--with-openssl=DIR],
+ [root of the OpenSSL directory])],
+ [
+ case "$withval" in
+ "" | y | ye | yes | n | no)
+ AC_MSG_ERROR([Invalid --with-openssl value])
+ ;;
+ *) ssldirs="$withval"
+ ;;
+ esac
+ ], [
+ # if pkg-config is installed and openssl has installed a .pc file,
+ # then use that information and don't search ssldirs
+ AC_PATH_PROG([PKG_CONFIG], [pkg-config])
+ if test x"$PKG_CONFIG" != x""; then
+ OPENSSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null`
+ if test $? = 0; then
+ OPENSSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null`
+ OPENSSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null`
+ found=true
+ fi
+ fi
+
+ # no such luck; use some default ssldirs
+ if ! $found; then
+ ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr"
+ fi
+ ]
+ )
+
+
+ # note that we #include <openssl/foo.h>, so the OpenSSL headers have to be in
+ # an 'openssl' subdirectory
+
+ if ! $found; then
+ OPENSSL_INCLUDES=
+ for ssldir in $ssldirs; do
+ AC_MSG_CHECKING([for openssl/ssl.h in $ssldir])
+ if test -f "$ssldir/include/openssl/ssl.h"; then
+ OPENSSL_INCLUDES="-I$ssldir/include"
+ OPENSSL_LDFLAGS="-L$ssldir/lib"
+ OPENSSL_LIBS="-lssl -lcrypto"
+ found=true
+ AC_MSG_RESULT([yes])
+ break
+ else
+ AC_MSG_RESULT([no])
+ fi
+ done
+
+ # if the file wasn't found, well, go ahead and try the link anyway -- maybe
+ # it will just work!
+ fi
+
+ # try the preprocessor and linker with our new flags,
+ # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS
+
+ AC_MSG_CHECKING([whether compiling and linking against OpenSSL works])
+ echo "Trying link with OPENSSL_LDFLAGS=$OPENSSL_LDFLAGS;" \
+ "OPENSSL_LIBS=$OPENSSL_LIBS; OPENSSL_INCLUDES=$OPENSSL_INCLUDES" >&AS_MESSAGE_LOG_FD
+
+ save_LIBS="$LIBS"
+ save_LDFLAGS="$LDFLAGS"
+ save_CPPFLAGS="$CPPFLAGS"
+ LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS"
+ LIBS="$OPENSSL_LIBS $LIBS"
+ CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS"
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([#include <openssl/ssl.h>], [SSL_new(NULL)])],
+ [
+ AC_MSG_RESULT([yes])
+ $1
+ ], [
+ AC_MSG_RESULT([no])
+ $2
+ ])
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+
+ AC_SUBST([OPENSSL_INCLUDES])
+ AC_SUBST([OPENSSL_LIBS])
+ AC_SUBST([OPENSSL_LDFLAGS])
+])
diff --git a/src/crypto.c b/src/crypto.c
new file mode 100644
index 0000000..14e6c8d
--- /dev/null
+++ b/src/crypto.c
@@ -0,0 +1,221 @@
+/*
+ Copyright (C) 2013 Simo Sorce <simo@samba.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <errno.h>
+
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+
+#include "crypto.h"
+
+int RAND_BUFFER(struct ntlm_buffer *random)
+{
+ int ret;
+
+ ret = RAND_bytes(random->data, random->length);
+ if (ret != 1) {
+ return ERR_CRYPTO;
+ }
+ return 0;
+}
+
+int HMAC_MD5(struct ntlm_buffer *key,
+ struct ntlm_buffer *payload,
+ struct ntlm_buffer *result)
+{
+ HMAC_CTX hmac_ctx;
+ unsigned int len;
+ int ret = 0;
+
+ if (result->length != 16) return EINVAL;
+
+ HMAC_CTX_init(&hmac_ctx);
+
+ ret = HMAC_Init_ex(&hmac_ctx, key->data, key->length, EVP_md5(), NULL);
+ if (ret == 0) {
+ ret = ERR_CRYPTO;
+ goto done;
+ }
+
+ ret = HMAC_Update(&hmac_ctx, payload->data, payload->length);
+ if (ret == 0) {
+ ret = ERR_CRYPTO;
+ goto done;
+ }
+
+ ret = HMAC_Final(&hmac_ctx, result->data, &len);
+ if (ret == 0) {
+ ret = ERR_CRYPTO;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ HMAC_CTX_cleanup(&hmac_ctx);
+ return ret;
+}
+
+
+
+static int mdx_hash(const EVP_MD *type,
+ struct ntlm_buffer *payload,
+ struct ntlm_buffer *result)
+{
+ EVP_MD_CTX ctx;
+ unsigned int len;
+ int ret;
+
+ if (result->length != 16) return EINVAL;
+
+ EVP_MD_CTX_init(&ctx);
+ ret = EVP_DigestInit_ex(&ctx, type, NULL);
+ if (ret == 0) {
+ ret = ERR_CRYPTO;
+ goto done;
+ }
+
+ ret = EVP_DigestUpdate(&ctx, payload->data, payload->length);
+ if (ret == 0) {
+ ret = ERR_CRYPTO;
+ goto done;
+ }
+
+ ret = EVP_DigestFinal_ex(&ctx, result->data, &len);
+ if (ret == 0) {
+ ret = ERR_CRYPTO;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ EVP_MD_CTX_cleanup(&ctx);
+ return ret;
+}
+
+int MD4_HASH(struct ntlm_buffer *payload,
+ struct ntlm_buffer *result)
+{
+ return mdx_hash(EVP_md4(), payload, result);
+}
+
+int MD5_HASH(struct ntlm_buffer *payload,
+ struct ntlm_buffer *result)
+{
+ return mdx_hash(EVP_md5(), payload, result);
+}
+
+struct ntlm_rc4_handle {
+ EVP_CIPHER_CTX ctx;
+};
+
+int RC4_INIT(struct ntlm_buffer *rc4_key,
+ enum ntlm_cipher_mode mode,
+ struct ntlm_rc4_handle **out)
+{
+ struct ntlm_rc4_handle *handle;
+ int enc;
+ int ret;
+
+ handle = malloc(sizeof(struct ntlm_rc4_handle));
+ if (!handle) return ENOMEM;
+
+ switch (mode) {
+ case NTLM_CIPHER_ENCRYPT:
+ enc = 1;
+ break;
+ case NTLM_CIPHER_DECRYPT:
+ enc = 0;
+ break;
+ default:
+ enc = -1;
+ }
+
+ EVP_CIPHER_CTX_init(&handle->ctx);
+ ret = EVP_CipherInit_ex(&handle->ctx, EVP_rc4(), NULL, NULL, NULL, enc);
+ if (ret == 0) {
+ ret = ERR_CRYPTO;
+ goto done;
+ }
+ ret = EVP_CIPHER_CTX_set_key_length(&handle->ctx, rc4_key->length);
+ if (ret == 0) {
+ ret = ERR_CRYPTO;
+ goto done;
+ }
+ ret = EVP_CipherInit_ex(&handle->ctx, NULL, NULL, rc4_key->data, NULL, -1);
+ if (ret == 0) {
+ ret = ERR_CRYPTO;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ if (ret) {
+ EVP_CIPHER_CTX_cleanup(&handle->ctx);
+ safefree(handle);
+ }
+ *out = handle;
+ return ret;
+}
+
+int RC4_UPDATE(struct ntlm_rc4_handle *handle,
+ struct ntlm_buffer *in, struct ntlm_buffer *out)
+{
+ int outl = 0;
+ int ret = 0;
+ int err;
+
+ if (out->length < in->length) return EINVAL;
+
+ err = EVP_CipherUpdate(&handle->ctx,
+ out->data, &outl, in->data, in->length);
+ if (err != 1) ret = ERR_CRYPTO;
+ if (outl > out->length) ret = ERR_CRYPTO;
+
+ out->length = outl;
+ return ret;
+}
+
+void RC4_FREE(struct ntlm_rc4_handle **handle)
+{
+ if (!handle) return;
+ EVP_CIPHER_CTX_cleanup(&(*handle)->ctx);
+ safefree(*handle);
+}
+
+int RC4K(struct ntlm_buffer *key,
+ enum ntlm_cipher_mode mode,
+ struct ntlm_buffer *payload,
+ struct ntlm_buffer *result)
+{
+ struct ntlm_rc4_handle *handle;
+ int ret;
+
+ if (result->length < payload->length) return EINVAL;
+
+ ret = RC4_INIT(key, mode, &handle);
+ if (ret) return ret;
+
+ ret = RC4_UPDATE(handle, payload, result);
+
+ RC4_FREE(&handle);
+ return ret;
+}
+
diff --git a/src/crypto.h b/src/crypto.h
new file mode 100644
index 0000000..3a2f6f9
--- /dev/null
+++ b/src/crypto.h
@@ -0,0 +1,117 @@
+/*
+ Copyright (C) 2013 Simo Sorce <simo@samba.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SRC_CRYPTO_H_
+#define _SRC_CRYPTO_H_
+
+#include <stdbool.h>
+#include "ntlm_common.h"
+
+/**
+ * @brief Fills the provided preallocated buffer with random data
+ *
+ * @param random A preallocated buffer, length determines the amount of
+ * random bytes the function will return.
+ *
+ * @return 0 for success or error otherwise
+ */
+int RAND_BUFFER(struct ntlm_buffer *random);
+
+/**
+ * @brief HMAC-MD5 function
+ *
+ * @param key The authentication key
+ * @param payload The payload to be authenticated
+ * @param result A preallocated 16 byte buffer
+ *
+ * @return 0 on success or ERR_CRYPTO
+ */
+int HMAC_MD5(struct ntlm_buffer *key,
+ struct ntlm_buffer *payload,
+ struct ntlm_buffer *result);
+
+/**
+ * @brief MD4 Hash Function
+ *
+ * @param payload The payoad to hash
+ * @param result The resulting Hash (preallocated, length must be 16)
+ *
+ * @return 0 on success or an error
+ */
+int MD4_HASH(struct ntlm_buffer *payload,
+ struct ntlm_buffer *result);
+
+/**
+ * @brief MD5 Hash Function
+ *
+ * @param payload The payoad to hash
+ * @param result The resulting Hash (preallocated, length must be 16)
+ *
+ * @return 0 on success or an error
+ */
+int MD5_HASH(struct ntlm_buffer *payload,
+ struct ntlm_buffer *result);
+
+/**
+ * @brief RC4 engine initialization
+ *
+ * @param rc4_key The encryption/decryption key
+ * @param mode The cipher mode
+ * @param state Allocated ntlm_rc4_state structure
+ *
+ * @return 0 on success or error
+ */
+int RC4_INIT(struct ntlm_buffer *rc4_key,
+ enum ntlm_cipher_mode mode,
+ struct ntlm_rc4_handle **handle);
+
+
+/**
+ * @brief RC4 encrypt/decrypt function
+ *
+ * @param state The state initialized by RC4_INIT
+ * @param in Input buffer (plaintext for enc or ciphertext for dec)
+ * @param out Resulting buffer. Must be preallocated.
+ *
+ * @return 0 on success or error
+ */
+int RC4_UPDATE(struct ntlm_rc4_handle *handle,
+ struct ntlm_buffer *in, struct ntlm_buffer *out);
+
+/**
+ * @brief Release an rc4 handle
+ *
+ * @param state A pointer to the rc4 handle
+ */
+void RC4_FREE(struct ntlm_rc4_handle **handle);
+
+/**
+ * @brief RC4 encryption/decryption all in one
+ *
+ * @param key The encryption/decryption key
+ * @param mode The cipher mode
+ * @param payload Input buffer (plaintext for enc or ciphertext for dec)
+ * @param result Resulting buffer. Must be preallocated.
+ *
+ * @return 0 on success or error
+ */
+int RC4K(struct ntlm_buffer *key,
+ enum ntlm_cipher_mode mode,
+ struct ntlm_buffer *payload,
+struct ntlm_buffer *result);
+
+#endif /* _SRC_CRYPTO_H_ */
diff --git a/src/ntlm.c b/src/ntlm.c
index 0c4824d..97771bc 100644
--- a/src/ntlm.c
+++ b/src/ntlm.c
@@ -28,8 +28,6 @@
#include <errno.h>
#include <iconv.h>
#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
@@ -129,32 +127,6 @@ struct wire_channel_binding {
};
#pragma pack(pop)
-/* lm response, v1 or v2 */
-#pragma pack(push, 1)
-union wire_lm_response {
- struct {
- uint8_t resp[24];
- } v1;
- struct {
- uint8_t resp[16];
- uint8_t cli_chal[8];
- } v2;
-};
-#pragma pack(pop)
-
-/* ntlm response, v1 or v2 */
-#pragma pack(push, 1)
-union wire_ntlm_response {
- struct {
- uint8_t resp[24];
- } v1;
- struct {
- uint8_t resp[16];
- uint8_t cli_chal[8];
- } v2;
-};
-#pragma pack(pop)
-
#pragma pack(push, 1)
struct wire_ntlm_cli_chal {
uint8_t resp_type;
diff --git a/src/ntlm.h b/src/ntlm.h
index 5f3a8d8..88fdc41 100644
--- a/src/ntlm.h
+++ b/src/ntlm.h
@@ -18,6 +18,10 @@
#ifndef _NTLM_H_
#define _NTLM_H
+#include <stdbool.h>
+
+#include "ntlm_common.h"
+
/* Negotiate Flags */
#define NTLMSSP_NEGOTIATE_56 (1 << 31)
#define NTLMSSP_NEGOTIATE_KEY_EXCH (1 << 30)
@@ -65,33 +69,6 @@
#define NTLMSSP_VERSION_BUILD 0
#define NTLMSSP_VERSION_REV NTLMSSP_REVISION_W2K3
-enum ntlm_err_code {
- ERR_BASE = 0x4E540000, /* base error space at 'NT00' */
- ERR_DECODE,
- ERR_ENCODE,
-};
-#define NTLM_ERR_MASK 0x4E54FFFF
-#define IS_NTLM_ERR_CODE(x) (((x) & NTLM_ERR_MASK) ? true : false)
-
-#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
-#define safefree(x) do { free(x); x = NULL; } while(0)
-
-struct ntlm_buffer {
- uint8_t *data;
- size_t length;
-};
-
-void ntlm_free_buffer_data(struct ntlm_buffer *buf);
-
-uint64_t ntlm_timestamp_now(void);
-
-
-enum ntlm_role {
- NTLM_CLIENT,
- NTLM_SERVER,
- NTLM_DOMAIN_SERVER,
- NTLM_DOMAIN_CONTROLLER
-};
struct ntlm_ctx;
@@ -114,6 +91,141 @@ int ntlm_init_ctx(struct ntlm_ctx **ctx);
*/
int ntlm_free_ctx(struct ntlm_ctx **ctx);
+void ntlm_free_buffer_data(struct ntlm_buffer *buf);
+
+uint64_t ntlm_timestamp_now(void);
+
+
+/* ############### CRYPTO FUNCTIONS ################ */
+
+struct ntlm_key {
+ uint8_t data[16]; /* up to 16 bytes (128 bits) */
+ size_t length;
+};
+
+/**
+ * @brief Turns a utf8 password into an NT Hash
+ *
+ * @param password The password
+ * @param result The returned hash
+ *
+ * @return 0 on success or an error;
+ */
+int ntlm_pwd_to_nt_hash(const char *password, struct ntlm_key *result);
+
+/**
+ * @brief Generates a NTLMv2 Response Key
+ *
+ * @param ctx An ntlm context
+ * @param nt_hash The NT Hash of the user password
+ * @param user The user name
+ * @param domain The user's domain
+ * @param result The resulting key
+ * (must be a preallocated 16 bytes buffer)
+ *
+ * @return 0 on success or ERR_CRYPTO
+ */
+int NTOWFv2(struct ntlm_ctx *ctx, struct ntlm_key *nt_hash,
+ const char *user, const char *domain, struct ntlm_key *result);
+
+/**
+ * @brief Computes The NTLMv2 Response
+ *
+ * @param ntlmv2_key The NTLMv2 key computed by NTOWFv2()
+ * @param server_chal[8] The server provided challenge
+ * @param client_chal[8] A client generated random challenge
+ * @param timestamp A FILETIME timestamp
+ * @param target_info The target info
+ * @param nt_response The resulting nt_response buffer
+ *
+ * @return 0 on success or error.
+ */
+int ntlmv2_compute_nt_response(struct ntlm_key *ntlmv2_key,
+ uint8_t server_chal[8], uint8_t client_chal[8],
+ uint64_t timestamp,
+ struct ntlm_buffer *target_info,
+ struct ntlm_buffer *nt_response);
+
+/**
+ * @brief Computes The LMv2 Response
+ *
+ * @param ntlmv2_key The NTLMv2 key computed by NTOWFv2()
+ * @param server_chal[8] The server provided challenge
+ * @param client_chal[8] A client generated random challenge
+ * @param lm_response The resulting lm_response buffer
+ *
+ * @return 0 on success or error.
+ */
+int ntlmv2_compute_lm_response(struct ntlm_key *ntlmv2_key,
+ uint8_t server_chal[8], uint8_t client_chal[8],
+ struct ntlm_buffer *lm_response);
+
+/**
+ * @brief Computes the NTLMv2 SessionBaseKey
+ *
+ * @param ntlmv2_key The NTLMv2 key computed by NTOWFv2()
+ * @param nt_response The NTLMv2 response
+ * @param session_base_key The resulting session key
+ *
+ * @return 0 on success or error.
+ */
+int ntlmv2_session_base_key(struct ntlm_key *ntlmv2_key,
+ struct ntlm_buffer *nt_response,
+ struct ntlm_key *session_base_key);
+
+/**
+ * @brief Comutes the NTLM session key
+ *
+ * @param key_exchange_key[16] The Key Exchange Key
+ * @param key_exch KEY_EXCH has been negotited
+ * @param exported_session_key[16] Resulting exported session key
+ *
+ * @return 0 on success or error.
+ */
+int ntlm_exported_session_key(struct ntlm_key *key_exchange_key,
+ bool key_exch,
+ struct ntlm_key *exported_session_key);
+
+/**
+ * @brief Comutes the NTLM encrypted session key
+ *
+ * @param key_exchange_key[16] The Key Exchange Key
+ * @param exported_session_key[16] Resulting exported session key
+ * @param encrypted_random_session_key Resulting encrypted session key
+ *
+ * @return 0 on success or error.
+ */
+int ntlm_encrypted_session_key(struct ntlm_key *key_exchange_key,
+ struct ntlm_key *exported_session_key,
+ struct ntlm_key *encrypted_random_session_key);
+
+/**
+ * @brief Computes all the sign and seal keys from the session key
+ *
+ * @param flags Incoming challenge/authenticate flags
+ * @param client Wheter this ia a client or a server
+ * @param random_session_key The session key
+ * @param sign_send_key Resulting Signing key for send ops
+ * @param sign_recv_key Resulting Signing key for recv ops
+ * @param seal_send_key Resulting Sealing key for send ops
+ * @param seal_recv_key Resulting Sealing key for recv ops
+ * @param seal_send_handle Handle for RC4 encryption (v1 sealing)
+ * @param seal_recv_handle Handle for RC4 decryption (v1 sealing)
+ *
+ * @return 0 on success or error.
+ */
+int ntlm_signseal_keys(uint32_t flags, bool client,
+ struct ntlm_key *random_session_key,
+ struct ntlm_key *sign_send_key,
+ struct ntlm_key *sign_recv_key,
+ struct ntlm_key *seal_send_key,
+ struct ntlm_key *seal_recv_key,
+ struct ntlm_rc4_handle **seal_send_handle,
+ struct ntlm_rc4_handle **seal_recv_handle);
+
+
+/* ############## ENCODING / DECODING ############## */
+
/**
* @brief A utility function to construct a target_info structure
*
diff --git a/src/ntlm_common.h b/src/ntlm_common.h
new file mode 100644
index 0000000..c70fe8d
--- /dev/null
+++ b/src/ntlm_common.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2013 Simo Sorce <simo@samba.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _NTLM_COMMON_H_
+#define _NTLM_COMMON_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+
+enum ntlm_err_code {
+ ERR_BASE = 0x4E540000, /* base error space at 'NT00' */
+ ERR_DECODE,
+ ERR_ENCODE,
+ ERR_CRYPTO,
+};
+#define NTLM_ERR_MASK 0x4E54FFFF
+#define IS_NTLM_ERR_CODE(x) (((x) & NTLM_ERR_MASK) ? true : false)
+
+#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
+#define safefree(x) do { free(x); x = NULL; } while(0)
+#define safezero(x, s) do { \
+ volatile uint8_t *p = (x); \
+ size_t size = (s); \
+ while (size--) { *p++ = 0; } \
+} while(0)
+
+
+struct ntlm_buffer {
+ uint8_t *data;
+ size_t length;
+};
+
+struct ntlm_rc4_handle;
+
+enum ntlm_cipher_mode {
+ NTLM_CIPHER_IGNORE,
+ NTLM_CIPHER_ENCRYPT,
+ NTLM_CIPHER_DECRYPT,
+};
+
+#endif /* _NTLM_COMMON_H_ */
diff --git a/src/ntlm_crypto.c b/src/ntlm_crypto.c
new file mode 100644
index 0000000..52e195a
--- /dev/null
+++ b/src/ntlm_crypto.c
@@ -0,0 +1,409 @@
+/*
+ Copyright (C) 2013 Simo Sorce <simo@samba.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/* This File implements the NTLM protocol as specified by:
+ * [MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol
+ *
+ * Additional cross checking with:
+ * http://davenport.sourceforge.net/ntlm.html
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <unicase.h>
+#include <uniconv.h>
+
+#include "ntlm.h"
+#include "crypto.h"
+
+/* lm response, v1 or v2 */
+#pragma pack(push, 1)
+union wire_lm_response {
+ struct {
+ uint8_t resp[24];
+ } v1;
+ struct {
+ uint8_t resp[16];
+ uint8_t cli_chal[8];
+ } v2;
+};
+#pragma pack(pop)
+
+/* ntlm response, v1 or v2 */
+#pragma pack(push, 1)
+union wire_ntlm_response {
+ struct {
+ uint8_t resp[24];
+ } v1;
+ struct {
+ uint8_t resp[16];
+ uint8_t cli_chal[];
+ } v2;
+};
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+struct wire_ntlmv2_cli_chal {
+ uint8_t resp_version;
+ uint8_t hi_resp_version;
+ uint8_t zero_6[6];
+ uint64_t timestamp;
+ uint8_t client_chal[8];
+ uint8_t zero_4[4];
+ uint8_t target_info[];
+ /* NOTE: the target_info array must terminate with 4 zero bytes.
+ * This is consistent with just copying the target_info array
+ * returned in the challenge message as the last AV_PAIR there is
+ * always MSV_AV_EOL which happens to be 4 bytes of zeros */
+};
+#pragma pack(pop)
+
+/* the max username is 20 chars, max NB domain len is 15, so 128 should be
+ * plenty including conversion to UTF8 using max lenght for each code point
+ */
+#define MAX_USER_DOM_LEN 512
+
+
+int ntlm_pwd_to_nt_hash(const char *password, struct ntlm_key *result)
+{
+ struct ntlm_buffer payload;
+ struct ntlm_buffer hash;
+ char *retstr;
+ size_t out;
+ size_t len;
+ int ret;
+
+ len = strlen(password);
+ retstr = u8_conv_to_encoding("UCS-2LE", iconveh_error,
+ (const uint8_t *)password, len,
+ NULL, NULL, &out);
+ if (!retstr) return ERR_CRYPTO;
+
+ payload.data = (uint8_t *)retstr;
+ payload.length = out;
+ hash.data = result->data;
+ hash.length= result->length;
+
+ ret = MD4_HASH(&payload, &hash);
+ free(retstr);
+ return ret;
+}
+
+int NTOWFv2(struct ntlm_ctx *ctx, struct ntlm_key *nt_hash,
+ const char *user, const char *domain, struct ntlm_key *result)
+{
+ struct ntlm_buffer key = { nt_hash->data, nt_hash->length };
+ struct ntlm_buffer hmac = { result->data, result->length };
+ struct ntlm_buffer payload;
+ uint8_t upcased[MAX_USER_DOM_LEN];
+ uint8_t *retstr;
+ size_t offs;
+ size_t out;
+ size_t len;
+ int ret;
+
+ len = strlen(user);
+ out = MAX_USER_DOM_LEN;
+ retstr = u8_toupper((const uint8_t *)user, len,
+ NULL, NULL, upcased, &out);
+ if (!retstr) return ERR_CRYPTO;
+ offs = out;
+
+ len = strlen(domain);
+ /*
+ out = MAX_USER_DOM_LEN - offs;
+ retstr = u8_toupper((const uint8_t *)domain, len,
+ NULL, NULL, &upcased[offs], &out);
+ if (!retstr) return ERR_CRYPTO;
+ offs += out;
+ */
+ memcpy(&upcased[offs], domain, len);
+ offs += len;
+
+ retstr = (uint8_t *)u8_conv_to_encoding("UCS-2LE", iconveh_error,
+ upcased, offs, NULL, NULL, &out);
+ if (!retstr) return ERR_CRYPTO;
+
+ payload.data = (uint8_t *)retstr;
+ payload.length = out;
+
+ ret = HMAC_MD5(&key, &payload, &hmac);
+ free(retstr);
+ return ret;
+}
+
+int ntlmv2_compute_nt_response(struct ntlm_key *ntlmv2_key,
+ uint8_t server_chal[8], uint8_t client_chal[8],
+ uint64_t timestamp,
+ struct ntlm_buffer *target_info,
+ struct ntlm_buffer *nt_response)
+{
+ union wire_ntlm_response *nt_resp = NULL;
+ struct wire_ntlmv2_cli_chal *r;
+ struct ntlm_buffer key = { ntlmv2_key->data, ntlmv2_key->length };
+ struct ntlm_buffer payload;
+ struct ntlm_buffer nt_proof;
+ size_t r_len;
+ int ret;
+
+ /* add additional 4 0s trailing target_info */
+ r_len = sizeof(struct wire_ntlmv2_cli_chal) + target_info->length + 4;
+ nt_resp = calloc(1, sizeof(nt_resp->v2) + r_len);
+ if (!nt_resp) return ENOMEM;
+
+ r = (struct wire_ntlmv2_cli_chal *)nt_resp->v2.cli_chal;
+ r->resp_version = 1;
+ r->hi_resp_version = 1;
+ r->timestamp = htole64(timestamp);
+ memcpy(r->client_chal, client_chal, 8);
+ memcpy(r->target_info, target_info->data, target_info->length);
+
+ /* use nt_resp as a buffer to calculate the NT proof as they share
+ * the cli_chal part */
+ payload.data = &nt_resp->v2.resp[8];
+ payload.length = 8 + r_len;
+ memcpy(payload.data, server_chal, 8);
+ nt_proof.data = nt_resp->v2.resp;
+ nt_proof.length = 16;
+ ret = HMAC_MD5(&key, &payload, &nt_proof);
+
+ if (ret) {
+ safefree(nt_resp);
+ } else {
+ nt_response->data = (uint8_t *)nt_resp;
+ nt_response->length = 16 + r_len;
+ }
+ return ret;
+}
+
+int ntlmv2_compute_lm_response(struct ntlm_key *ntlmv2_key,
+ uint8_t server_chal[8], uint8_t client_chal[8],
+ struct ntlm_buffer *lm_response)
+{
+ union wire_lm_response *lm_resp = NULL;
+ struct ntlm_buffer key = { ntlmv2_key->data, ntlmv2_key->length };
+ uint8_t payload_buf[16];
+ struct ntlm_buffer payload = { payload_buf, 16 };
+ struct ntlm_buffer lm_proof;
+ int ret;
+
+ /* now caluclate the LM Proof */
+ lm_resp = malloc(sizeof(union wire_lm_response));
+ if (!lm_resp) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ memcpy(payload.data, server_chal, 8);
+ memcpy(&payload.data[8], client_chal, 8);
+ lm_proof.data = lm_resp->v2.resp;
+ lm_proof.length = 16;
+ ret = HMAC_MD5(&key, &payload, &lm_proof);
+
+done:
+ if (ret) {
+ safefree(lm_resp);
+ } else {
+ memcpy(lm_resp->v2.cli_chal, client_chal, 8);
+
+ lm_response->data = (uint8_t *)lm_resp;
+ lm_response->length = 24;
+ }
+ return ret;
+}
+
+int ntlmv2_session_base_key(struct ntlm_key *ntlmv2_key,
+ struct ntlm_buffer *nt_response,
+ struct ntlm_key *session_base_key)
+{
+ struct ntlm_buffer key = { ntlmv2_key->data, ntlmv2_key->length };
+ struct ntlm_buffer hmac = { session_base_key->data,
+ session_base_key->length };
+
+ if (session_base_key->length != 16) return EINVAL;
+
+ return HMAC_MD5(&key, nt_response, &hmac);
+}
+
+int ntlm_exported_session_key(struct ntlm_key *key_exchange_key,
+ bool key_exch,
+ struct ntlm_key *exported_session_key)
+{
+ struct ntlm_buffer nonce;
+
+ if (!key_exch) {
+ *exported_session_key = *key_exchange_key;
+ return 0;
+ }
+
+ exported_session_key->length = 16;
+ nonce.data = exported_session_key->data;
+ nonce.length = exported_session_key->length;
+ return RAND_BUFFER(&nonce);
+}
+
+int ntlm_encrypted_session_key(struct ntlm_key *key_exchange_key,
+ struct ntlm_key *exported_session_key,
+ struct ntlm_key *encrypted_random_session_key)
+{
+ struct ntlm_buffer key = { key_exchange_key->data,
+ key_exchange_key->length };
+ struct ntlm_buffer nonce = { exported_session_key->data,
+ exported_session_key->length };
+ struct ntlm_buffer cipher = { encrypted_random_session_key->data,
+ encrypted_random_session_key->length };
+
+ return RC4K(&key, NTLM_CIPHER_ENCRYPT, &nonce, &cipher);
+}
+
+static int ntlm_key_derivation_function(struct ntlm_key *key,
+ const char *magic_constant,
+ struct ntlm_key *derived_key)
+{
+ uint8_t buf[80]; /* key + constant is never larger than 80 */
+ struct ntlm_buffer payload = { buf, 0 };
+ struct ntlm_buffer result = { derived_key->data, 16 };
+ size_t len;
+ int ret;
+
+ if (key->length > 16) return ERR_CRYPTO;
+ len = strlen(magic_constant);
+ if (len > 64) return ERR_CRYPTO;
+
+ payload.length = key->length;
+ memcpy(payload.data, key->data, key->length);
+ memcpy(&payload.data[payload.length], magic_constant, len);
+ payload.length += len;
+
+ ret = MD5_HASH(&payload, &result);
+ if (ret == 0) {
+ derived_key->length = 16;
+ }
+ return ret;
+}
+
+#define NTLM_MODE_CLIENT true
+#define NTLM_MODE_SERVER false
+
+static int ntlm_signkey(uint32_t flags, bool mode,
+ struct ntlm_key *random_session_key,
+ struct ntlm_key *signing_key)
+{
+ const char *mc;
+
+ if (flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) {
+ if (mode == NTLM_MODE_CLIENT) {
+ mc = "session key to client-to-server signing key magic constant";
+ } else {
+ mc = "session key to server-to-client signing key magic constant";
+ }
+ return ntlm_key_derivation_function(random_session_key,
+ mc, signing_key);
+ } else {
+ signing_key->length = 0;
+ }
+ return 0;
+}
+
+static int ntlm_sealkey(uint32_t flags, bool mode,
+ struct ntlm_key *random_session_key,
+ struct ntlm_key *sealing_key)
+{
+ struct ntlm_key key;
+ const char *mc;
+
+ if (flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) {
+ if (flags & NTLMSSP_NEGOTIATE_128) {
+ key.length = 16;
+ } else if (flags & NTLMSSP_NEGOTIATE_56) {
+ key.length = 7;
+ } else {
+ key.length = 5;
+ }
+ memcpy(key.data, random_session_key->data, key.length);
+
+ if (mode == NTLM_MODE_CLIENT) {
+ mc = "session key to client-to-server sealing key magic constant";
+ } else {
+ mc = "session key to server-to-client sealing key magic constant";
+ }
+
+ return ntlm_key_derivation_function(&key, mc, sealing_key);
+
+ } else if (flags & NTLMSSP_NEGOTIATE_LM_KEY) {
+ if (flags & NTLMSSP_NEGOTIATE_56) {
+ memcpy(sealing_key->data, random_session_key->data, 7);
+ sealing_key->data[7] = 0xA0;
+ } else {
+ memcpy(sealing_key->data, random_session_key->data, 5);
+ sealing_key->data[5] = 0xE5;
+ sealing_key->data[6] = 0x38;
+ sealing_key->data[7] = 0xB0;
+ }
+ sealing_key->length = 8;
+ } else {
+ *sealing_key = *random_session_key;
+ }
+ return 0;
+}
+
+int ntlm_signseal_keys(uint32_t flags, bool client,
+ struct ntlm_key *random_session_key,
+ struct ntlm_key *sign_send_key,
+ struct ntlm_key *sign_recv_key,
+ struct ntlm_key *seal_send_key,
+ struct ntlm_key *seal_recv_key,
+ struct ntlm_rc4_handle **seal_send_handle,
+ struct ntlm_rc4_handle **seal_recv_handle)
+{
+ struct ntlm_buffer rc4_key;
+ bool mode;
+ int ret;
+
+ /* send key */
+ mode = client ? NTLM_MODE_CLIENT : NTLM_MODE_SERVER;
+ ret = ntlm_signkey(flags, mode, random_session_key, sign_send_key);
+ if (ret) return ret;
+ /* recv key */
+ mode = client ? NTLM_MODE_SERVER : NTLM_MODE_CLIENT;
+ ret = ntlm_signkey(flags, mode, random_session_key, sign_recv_key);
+ if (ret) return ret;
+
+ /* send key */
+ mode = client ? NTLM_MODE_CLIENT : NTLM_MODE_SERVER;
+ ret = ntlm_sealkey(flags, mode, random_session_key, seal_send_key);
+ if (ret) return ret;
+ /* recv key */
+ mode = client ? NTLM_MODE_SERVER : NTLM_MODE_CLIENT;
+ ret = ntlm_sealkey(flags, mode, random_session_key, seal_recv_key);
+ if (ret) return ret;
+
+ rc4_key.data = seal_send_key->data;
+ rc4_key.length = seal_send_key->length;
+ ret = RC4_INIT(&rc4_key, NTLM_CIPHER_ENCRYPT, seal_send_handle);
+ if (ret) return ret;
+
+ rc4_key.data = seal_recv_key->data;
+ rc4_key.length = seal_recv_key->length;
+ ret = RC4_INIT(&rc4_key, NTLM_CIPHER_DECRYPT, seal_recv_handle);
+ if (ret) return ret;
+
+ return 0;
+}