summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/ntlm
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/lib/ntlm')
-rw-r--r--third_party/heimdal/lib/ntlm/ChangeLog120
-rw-r--r--third_party/heimdal/lib/ntlm/Makefile.am49
-rw-r--r--third_party/heimdal/lib/ntlm/NTMakefile90
-rw-r--r--third_party/heimdal/lib/ntlm/apop.c263
-rw-r--r--third_party/heimdal/lib/ntlm/digest.c994
-rw-r--r--third_party/heimdal/lib/ntlm/heim-auth.h135
-rw-r--r--third_party/heimdal/lib/ntlm/heimntlm.h166
-rw-r--r--third_party/heimdal/lib/ntlm/libheimntlm-exports.def24
-rw-r--r--third_party/heimdal/lib/ntlm/libheimntlm-version.rc36
-rw-r--r--third_party/heimdal/lib/ntlm/ntlm.c2030
-rw-r--r--third_party/heimdal/lib/ntlm/ntlm_err.et60
-rw-r--r--third_party/heimdal/lib/ntlm/test_commonauth.c425
-rw-r--r--third_party/heimdal/lib/ntlm/test_ntlm.c617
-rw-r--r--third_party/heimdal/lib/ntlm/version-script.map30
14 files changed, 5039 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/ntlm/ChangeLog b/third_party/heimdal/lib/ntlm/ChangeLog
new file mode 100644
index 0000000..b2e151a
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/ChangeLog
@@ -0,0 +1,120 @@
+2008-05-14 Love Hornquist Astrand <lha@kth.se>
+
+ * ntlm.c: replace hashes with keys.
+
+2008-04-27 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ntlm.c: Use DES_set_key_unchecked().
+
+2007-12-28 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * heimntlm.h: Add NTLM_TARGET_*
+
+ * ntlm.c: Make heim_ntlm_decode_type3 more useful and provide a
+ username. From Ming Yang.
+
+2007-11-11 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * move doxygen into the main file
+
+ * write doxygen documentation
+
+ * export heim_ntlm_free_buf, start doxygen documentation
+
+2007-07-17 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ntlm.c: Use unsigned char * as argument to HMAC_Update to please
+ OpenSSL and gcc.
+
+ * test_ntlm.c: more verbose what we are testing.
+
+2007-07-10 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: New library version.
+
+2007-06-20 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_ntlm.c: heim_ntlm_calculate_ntlm2_sess_resp
+
+ * ntlm.c: Change prototype to match other heim_ntlm_calculate
+ functions.
+
+ * test_ntlm.c: Its ok if infotarget2 length is longer.
+
+ * ntlm.c: Merge in changes from Puneet Mehra and make work again.
+
+ * ntlm.c (heim_ntlm_ntlmv2_key): target should be uppercase.
+ From Puneet Mehra.
+
+ * version-script.map: Add heim_ntlm_calculate_ntlm2_sess_resp from
+ Puneet Mehra.
+
+ * ntlm.c: Add heim_ntlm_calculate_ntlm2_sess_resp from Puneet
+ Mehra.
+
+ * test_ntlm.c: Test heim_ntlm_calculate_ntlm2_sess_resp from
+ Puneet Mehra.
+
+2007-06-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: EXTRA_DIST += version-script.map.
+
+2007-06-03 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_ntlm.c: Free memory diffrently.
+
+ * ntlm.c: Make free functions free memory.
+
+2007-04-22 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: symbol versioning.
+
+ * version-script.map: symbol versioning.
+
+2007-01-31 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_ntlm.c: No need to include <gssapi.h>.
+
+2007-01-04 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: add LIB_roken for test_ntlm
+
+2006-12-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_ntlm.c: Verify infotarget.
+
+ * ntlm.c: Extract the infotarget from the answer.
+
+ * ntlm.c (heim_ntlm_verify_ntlm2): verify the ntlmv2 reply
+
+2006-12-22 Dave Love <fx@gnu.org>
+
+ * ntlm.c: Include <limits.h>.
+
+2006-12-20 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_ntlm.c: add some new tests.
+
+ * ntlm.c: Add ntlmv2 answer calculating functions.
+
+ * ntlm.c: sent lm hashes, needed for NTLM2 session
+
+ * heimntlm.h: Add NTLM_NEG_NTLM2_SESSION, NTLMv2 session security.
+
+2006-12-19 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ntlm.c (heim_ntlm_build_ntlm1_master): return session master
+ key.
+
+2006-12-18 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ntlm.c (heim_ntlm_build_ntlm1_master): calculate the ntlm
+ version 1 "master" key.
+
+2006-12-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * test_ntlm.c: Add simple parser test app.
+
+ * inital version of a NTLM library, only handles ntml version 1 and
+ ascii strings for now
+
diff --git a/third_party/heimdal/lib/ntlm/Makefile.am b/third_party/heimdal/lib/ntlm/Makefile.am
new file mode 100644
index 0000000..d33efd2
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/Makefile.am
@@ -0,0 +1,49 @@
+# $Id$
+
+include $(top_srcdir)/Makefile.am.common
+
+lib_LTLIBRARIES = libheimntlm.la
+
+dist_include_HEADERS = heimntlm.h $(srcdir)/heimntlm-protos.h
+
+nodist_include_HEADERS = ntlm_err.h
+
+dist_libheimntlm_la_SOURCES = ntlm.c heimntlm.h
+
+nodist_libheimntlm_la_SOURCES = ntlm_err.c
+
+libheimntlm_la_LDFLAGS = -version-info 1:0:1
+
+if versionscript
+libheimntlm_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+endif
+$(libheimntlm_la_OBJECTS): $(srcdir)/version-script.map
+
+libheimntlm_la_LIBADD = \
+ ../krb5/libkrb5.la \
+ $(top_builddir)/lib/wind/libwind.la \
+ $(LIB_hcrypto) \
+ $(LIBADD_roken)
+
+$(srcdir)/heimntlm-protos.h: $(dist_libheimntlm_la_SOURCES)
+ cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -o heimntlm-protos.h $(dist_libheimntlm_la_SOURCES) || rm -f heimntlm-protos.h
+
+$(libheimntlm_la_OBJECTS): $(srcdir)/heimntlm-protos.h ntlm_err.h
+
+TESTS = test_ntlm
+
+check_PROGRAMS = test_ntlm
+
+LDADD = libheimntlm.la $(LIB_roken)
+
+EXTRA_DIST = \
+ NTMakefile \
+ libheimntlm-version.rc \
+ libheimntlm-exports.def \
+ version-script.map \
+ ntlm_err.et
+
+CLEANFILES = \
+ ntlm_err.c ntlm_err.h
+
+ntlm_err.h: ntlm_err.et
diff --git a/third_party/heimdal/lib/ntlm/NTMakefile b/third_party/heimdal/lib/ntlm/NTMakefile
new file mode 100644
index 0000000..527fc89
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/NTMakefile
@@ -0,0 +1,90 @@
+########################################################################
+#
+# Copyright (c) 2009, Secure Endpoints Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# - Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+RELDIR=lib\ntlm
+
+!include ../../windows/NTMakefile.w32
+
+INCFILES= \
+ $(INCDIR)\heimntlm.h \
+ $(INCDIR)\heimntlm-protos.h \
+ $(INCDIR)\ntlm_err.h
+
+libheimntlm_la_SOURCES = ntlm.c heimntlm.h
+
+$(OBJ)\heimntlm-protos.h: $(libheimntlm_la_SOURCES)
+ $(PERL) ../../cf/make-proto.pl -q -P remove -o $(OBJ)\heimntlm-protos.h $(libheimntlm_la_SOURCES)
+
+$(OBJ)\ntlm_err.c $(OBJ)\ntlm_err.h: ntlm_err.et
+ cd $(OBJ)
+ $(BINDIR)\compile_et.exe $(SRCDIR)\ntlm_err.et
+ cd $(SRCDIR)
+
+!ifndef STATICLIBS
+
+RES=$(OBJ)\libheimntlm-version.res
+
+$(LIBHEIMNTLM): $(BINDIR)\heimntlm.dll
+
+$(BINDIR)\heimntlm.dll: $(OBJ)\ntlm.obj $(OBJ)\ntlm_err.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBCOMERR) $(RES)
+ $(DLLGUILINK) -def:libheimntlm-exports.def -implib:$(LIBHEIMNTLM)
+ $(DLLPREP_NODIST)
+
+clean::
+ -$(RM) $(BINDIR)\heimntlm.*
+
+!else
+
+$(LIBHEIMNTLM): $(OBJ)\ntlm.obj $(OBJ)\ntlm_err.obj
+ $(LIBCON)
+
+!endif
+
+all:: $(INCFILES) $(LIBHEIMNTLM)
+
+
+test-binaries: $(OBJ)\test_ntlm.exe
+
+test-run:
+ cd $(OBJ)
+ -test_ntlm.exe
+ cd $(SRCDIR)
+
+$(OBJ)\test_ntlm.exe: $(OBJ)\test_ntlm.obj $(LIBHEIMNTLM) $(LIBHEIMDAL) $(LIBVERS) $(LIBROKEN)
+ $(EXECONLINK)
+ $(EXEPREP_NODIST)
+
+test:: test-binaries test-run
+
+test-exports:
+ $(PERL) ..\..\cf\w32-check-exported-symbols.pl --vs version-script.map --def libheimntlm-exports.def
+
+test:: test-exports
diff --git a/third_party/heimdal/lib/ntlm/apop.c b/third_party/heimdal/lib/ntlm/apop.c
new file mode 100644
index 0000000..b363239
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/apop.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonHMAC.h>
+#include <roken.h>
+#include <hex.h>
+#include "heim-auth.h"
+#include "ntlm_err.h"
+
+char *
+heim_generate_challenge(const char *hostname)
+{
+ char host[MAXHOSTNAMELEN], *str = NULL;
+ uint32_t num, t;
+
+ if (hostname == NULL) {
+ if (gethostname(host, sizeof(host)))
+ return NULL;
+ hostname = host;
+ }
+
+ t = (uint32_t)time(NULL);
+ num = rk_random();
+
+ asprintf(&str, "<%lu%lu@%s>", (unsigned long)t,
+ (unsigned long)num, hostname);
+
+ return str;
+}
+
+char *
+heim_apop_create(const char *challenge, const char *password)
+{
+ char *str = NULL;
+ uint8_t hash[CC_MD5_DIGEST_LENGTH];
+ CC_MD5_CTX ctx;
+
+ CC_MD5_Init(&ctx);
+ CC_MD5_Update(&ctx, challenge, (CC_LONG)strlen(challenge));
+ CC_MD5_Update(&ctx, password, (CC_LONG)strlen(password));
+
+ CC_MD5_Final(hash, &ctx);
+
+ hex_encode(hash, sizeof(hash), &str);
+ if (str)
+ strlwr(str);
+
+ return str;
+}
+
+int
+heim_apop_verify(const char *challenge, const char *password, const char *response)
+{
+ char *str;
+ int res;
+
+ str = heim_apop_create(challenge, password);
+ if (str == NULL)
+ return ENOMEM;
+
+ res = (strcasecmp(str, response) != 0);
+ free(str);
+
+ if (res)
+ return HNTLM_ERR_INVALID_APOP;
+ return 0;
+}
+
+struct heim_cram_md5_data {
+ CC_MD5_CTX ipad;
+ CC_MD5_CTX opad;
+};
+
+
+void
+heim_cram_md5_export(const char *password, heim_CRAM_MD5_STATE *state)
+{
+ size_t keylen = strlen(password);
+ uint8_t key[CC_MD5_BLOCK_BYTES];
+ uint8_t pad[CC_MD5_BLOCK_BYTES];
+ struct heim_cram_md5_data ctx;
+ size_t n;
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ if (keylen > CC_MD5_BLOCK_BYTES) {
+ CC_MD5(password, (CC_LONG)keylen, key);
+ keylen = sizeof(keylen);
+ } else {
+ memcpy(key, password, keylen);
+ }
+
+ memset(pad, 0x36, sizeof(pad));
+ for (n = 0; n < keylen; n++)
+ pad[n] ^= key[n];
+
+ CC_MD5_Init(&ctx.ipad);
+ CC_MD5_Init(&ctx.opad);
+
+ CC_MD5_Update(&ctx.ipad, pad, sizeof(pad));
+
+ memset(pad, 0x5c, sizeof(pad));
+ for (n = 0; n < keylen; n++)
+ pad[n] ^= key[n];
+
+ CC_MD5_Update(&ctx.opad, pad, sizeof(pad));
+
+ memset(pad, 0, sizeof(pad));
+ memset(key, 0, sizeof(key));
+
+ state->istate[0] = htonl(ctx.ipad.A);
+ state->istate[1] = htonl(ctx.ipad.B);
+ state->istate[2] = htonl(ctx.ipad.C);
+ state->istate[3] = htonl(ctx.ipad.D);
+
+ state->ostate[0] = htonl(ctx.opad.A);
+ state->ostate[1] = htonl(ctx.opad.B);
+ state->ostate[2] = htonl(ctx.opad.C);
+ state->ostate[3] = htonl(ctx.opad.D);
+
+ memset(&ctx, 0, sizeof(ctx));
+}
+
+
+heim_cram_md5
+heim_cram_md5_import(void *data, size_t len)
+{
+ heim_CRAM_MD5_STATE state;
+ heim_cram_md5 ctx;
+
+ if (len != sizeof(state))
+ return NULL;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ memcpy(&state, data, sizeof(state));
+
+ ctx->ipad.A = ntohl(state.istate[0]);
+ ctx->ipad.B = ntohl(state.istate[1]);
+ ctx->ipad.C = ntohl(state.istate[2]);
+ ctx->ipad.D = ntohl(state.istate[3]);
+
+ ctx->opad.A = ntohl(state.ostate[0]);
+ ctx->opad.B = ntohl(state.ostate[1]);
+ ctx->opad.C = ntohl(state.ostate[2]);
+ ctx->opad.D = ntohl(state.ostate[3]);
+
+ ctx->ipad.Nl = ctx->opad.Nl = 512;
+ ctx->ipad.Nh = ctx->opad.Nh = 0;
+ ctx->ipad.num = ctx->opad.num = 0;
+
+ return ctx;
+}
+
+int
+heim_cram_md5_verify_ctx(heim_cram_md5 ctx, const char *challenge, const char *response)
+{
+ uint8_t hash[CC_MD5_DIGEST_LENGTH];
+ char *str = NULL;
+ int res;
+
+ CC_MD5_Update(&ctx->ipad, challenge, (CC_LONG)strlen(challenge));
+ CC_MD5_Final(hash, &ctx->ipad);
+
+ CC_MD5_Update(&ctx->opad, hash, sizeof(hash));
+ CC_MD5_Final(hash, &ctx->opad);
+
+ hex_encode(hash, sizeof(hash), &str);
+ if (str == NULL)
+ return ENOMEM;
+
+ res = (strcasecmp(str, response) != 0);
+ free(str);
+
+ if (res)
+ return HNTLM_ERR_INVALID_CRAM_MD5;
+ return 0;
+}
+
+void
+heim_cram_md5_free(heim_cram_md5 ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ free(ctx);
+}
+
+
+char *
+heim_cram_md5_create(const char *challenge, const char *password)
+{
+ CCHmacContext ctx;
+ uint8_t hash[CC_MD5_DIGEST_LENGTH];
+ char *str = NULL;
+
+ CCHmacInit(&ctx, kCCHmacAlgMD5, password, strlen(password));
+ CCHmacUpdate(&ctx, challenge, strlen(challenge));
+ CCHmacFinal(&ctx, hash);
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ hex_encode(hash, sizeof(hash), &str);
+ if (str)
+ strlwr(str);
+
+ return str;
+}
+
+ int
+heim_cram_md5_verify(const char *challenge, const char *password, const char *response)
+{
+ char *str;
+ int res;
+
+ str = heim_cram_md5_create(challenge, password);
+ if (str == NULL)
+ return ENOMEM;
+
+ res = (strcasecmp(str, response) != 0);
+ free(str);
+
+ if (res)
+ return HNTLM_ERR_INVALID_CRAM_MD5;
+ return 0;
+}
+
diff --git a/third_party/heimdal/lib/ntlm/digest.c b/third_party/heimdal/lib/ntlm/digest.c
new file mode 100644
index 0000000..42c39ff
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/digest.c
@@ -0,0 +1,994 @@
+/*
+ * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonHMAC.h>
+#include <assert.h>
+#include <roken.h>
+#include <hex.h>
+#include "heim-auth.h"
+#include "ntlm_err.h"
+
+struct heim_digest_desc {
+#define F_SERVER 1
+#define F_HAVE_HASH 2
+#define F_HAVE_HA1 4
+#define F_USE_PREFIX 8
+ int flags;
+ int type;
+ char *password;
+ uint8_t SecretHash[CC_MD5_DIGEST_LENGTH];
+ char *serverNonce;
+ char *serverRealm;
+ char *serverQOP;
+ char *serverMethod;
+ char *serverMaxbuf;
+ char *serverOpaque;
+ char *clientUsername;
+ char *clientResponse;
+ char *clientURI;
+ char *clientRealm;
+ char *clientNonce;
+ char *clientQOP;
+ char *clientNC;
+ char *serverAlgorithm;
+ char *auth_id;
+
+ /* internally allocated objects returned to caller */
+ char *serverChallenge;
+ char *clientReply;
+ char *serverReply;
+};
+
+#define FREE_AND_CLEAR(x) do { if ((x)) { free((x)); (x) = NULL; } } while(0)
+#define MEMSET_FREE_AND_CLEAR(x) do { if ((x)) { memset(x, 0, strlen(x)); free((x)); (x) = NULL; } } while(0)
+
+static const char digest_prefix[] = "Digest ";
+
+static void
+clear_context(heim_digest_t context)
+{
+ MEMSET_FREE_AND_CLEAR(context->password);
+ memset(context->SecretHash, 0, sizeof(context->SecretHash));
+ context->flags &= ~(F_HAVE_HASH);
+ FREE_AND_CLEAR(context->serverNonce);
+ FREE_AND_CLEAR(context->serverRealm);
+ FREE_AND_CLEAR(context->serverQOP);
+ FREE_AND_CLEAR(context->serverMethod);
+ FREE_AND_CLEAR(context->serverMaxbuf);
+ FREE_AND_CLEAR(context->serverOpaque);
+ FREE_AND_CLEAR(context->clientUsername);
+ FREE_AND_CLEAR(context->clientResponse);
+ FREE_AND_CLEAR(context->clientURI);
+ FREE_AND_CLEAR(context->clientRealm);
+ FREE_AND_CLEAR(context->clientNonce);
+ FREE_AND_CLEAR(context->clientQOP);
+ FREE_AND_CLEAR(context->clientNC);
+ FREE_AND_CLEAR(context->serverAlgorithm);
+ FREE_AND_CLEAR(context->auth_id);
+
+ FREE_AND_CLEAR(context->serverChallenge);
+ FREE_AND_CLEAR(context->clientReply);
+ FREE_AND_CLEAR(context->serverReply);
+}
+
+static void
+digest_userhash(const char *user, const char *realm, const char *password,
+ unsigned char md[CC_MD5_DIGEST_LENGTH])
+{
+ CC_MD5_CTX ctx;
+
+ CC_MD5_Init(&ctx);
+ CC_MD5_Update(&ctx, user, (CC_LONG)strlen(user));
+ CC_MD5_Update(&ctx, ":", 1);
+ CC_MD5_Update(&ctx, realm, (CC_LONG)strlen(realm));
+ CC_MD5_Update(&ctx, ":", 1);
+ CC_MD5_Update(&ctx, password, (CC_LONG)strlen(password));
+ CC_MD5_Final(md, &ctx);
+}
+
+static char *
+build_A1_hash(heim_digest_t context)
+{
+ unsigned char md[CC_MD5_DIGEST_LENGTH];
+ CC_MD5_CTX ctx;
+ char *A1;
+
+ if (context->flags & F_HAVE_HA1) {
+ memcpy(md, context->SecretHash, sizeof(md));
+ } else if (context->flags & F_HAVE_HASH) {
+ memcpy(md, context->SecretHash, sizeof(md));
+ } else if (context->password) {
+ if (context->clientUsername == NULL)
+ return NULL;
+ if (context->serverRealm == NULL)
+ return NULL;
+ digest_userhash(context->clientUsername,
+ context->serverRealm,
+ context->password,
+ md);
+ } else
+ return NULL;
+
+ if ((context->type == HEIM_DIGEST_TYPE_RFC2617_MD5_SESS || context->type == HEIM_DIGEST_TYPE_RFC2831) && (context->flags & F_HAVE_HA1) == 0) {
+ if (context->serverNonce == NULL)
+ return NULL;
+
+ CC_MD5_Init(&ctx);
+ CC_MD5_Update(&ctx, md, sizeof(md));
+ memset(md, 0, sizeof(md));
+ CC_MD5_Update(&ctx, ":", 1);
+ CC_MD5_Update(&ctx, context->serverNonce, (CC_LONG)strlen(context->serverNonce));
+ if (context->clientNonce) {
+ CC_MD5_Update(&ctx, ":", 1);
+ CC_MD5_Update(&ctx, context->clientNonce, (CC_LONG)strlen(context->clientNonce));
+ }
+ if (context->type == HEIM_DIGEST_TYPE_RFC2831 && context->auth_id) {
+ CC_MD5_Update(&ctx, ":", 1);
+ CC_MD5_Update(&ctx, context->auth_id, (CC_LONG)strlen(context->auth_id));
+ }
+ CC_MD5_Final(md, &ctx);
+ }
+ hex_encode(md, sizeof(md), &A1);
+ if (A1)
+ strlwr(A1);
+
+ return A1;
+}
+
+static char *
+build_A2_hash(heim_digest_t context, const char *method)
+{
+ unsigned char md[CC_MD5_DIGEST_LENGTH];
+ CC_MD5_CTX ctx;
+ char *A2;
+
+ CC_MD5_Init(&ctx);
+ if (method)
+ CC_MD5_Update(&ctx, method, (CC_LONG)strlen(method));
+ CC_MD5_Update(&ctx, ":", 1);
+ CC_MD5_Update(&ctx, context->clientURI, (CC_LONG)strlen(context->clientURI));
+
+ /* conf|int */
+ if (context->type == HEIM_DIGEST_TYPE_RFC2831) {
+ if (strcasecmp(context->clientQOP, "auth-int") == 0 || strcasecmp(context->clientQOP, "auth-conf") == 0) {
+ /* XXX if we have a body hash, use that */
+ static char conf_zeros[] = ":00000000000000000000000000000000";
+ CC_MD5_Update(&ctx, conf_zeros, sizeof(conf_zeros) - 1);
+ }
+ } else {
+ /* support auth-int ? */
+ if (context->clientQOP && strcasecmp(context->clientQOP, "auth") != 0)
+ return NULL;
+ }
+
+ CC_MD5_Final(md, &ctx);
+
+ hex_encode(md, sizeof(md), &A2);
+ if (A2)
+ strlwr(A2);
+
+ return A2;
+}
+
+/*
+ *
+ */
+
+struct md5_value {
+ char *mv_name;
+ char *mv_value;
+ struct md5_value *mv_next;
+};
+
+static void
+free_values(struct md5_value *val)
+{
+ struct md5_value *v;
+ while(val) {
+ v = val->mv_next;
+ if (val->mv_name)
+ free(val->mv_name);
+ if (val->mv_value)
+ free(val->mv_value);
+ free(val);
+ val = v;
+ }
+}
+
+/*
+ * Search for entry, if found, remove entry and return string to be freed.
+ */
+
+static char *
+values_find(struct md5_value **val, const char *v)
+{
+ struct md5_value *cur;
+ char *str;
+
+ while (*val != NULL) {
+ if (strcasecmp(v, (*val)->mv_name) == 0)
+ break;
+ val = &(*val)->mv_next;
+ }
+ if (*val == NULL)
+ return NULL;
+ cur = *val;
+ *val = (*val)->mv_next;
+
+ str = cur->mv_value;
+ free(cur->mv_name);
+ free(cur);
+
+ return str;
+}
+
+static int
+parse_values(const char *string, struct md5_value **val)
+{
+ struct md5_value *v;
+ size_t size;
+ char *str, *p1, *p2;
+ size_t sz;
+
+ *val = NULL;
+
+ if ((str = strdup(string)) == NULL)
+ return ENOMEM;
+
+ size = strlen(str);
+
+ p1 = str;
+
+ while (p1 - str < size) {
+ sz = strspn(p1, " \t\n\r,");
+ if (p1[sz] == '\0')
+ break;
+ p1 += sz;
+ sz = strcspn(p1, " \t\n\r=");
+ if (sz == 0 || p1[sz] == '\0')
+ goto error;
+ p2 = p1 + sz;
+
+ if ((v = malloc(sizeof(*v))) == NULL)
+ goto nomem;
+ v->mv_name = v->mv_value = NULL;
+ v->mv_next = *val;
+ *val = v;
+ if ((v->mv_name = malloc(p2 - p1 + 1)) == NULL)
+ goto nomem;
+ strncpy(v->mv_name, p1, p2 - p1);
+ v->mv_name[p2 - p1] = '\0';
+
+ sz = strspn(p2, " \t\n\r");
+ if (p2[sz] == '\0')
+ goto error;
+ p2 += sz;
+
+ if (*p2 != '=')
+ goto error;
+ p2++;
+
+ sz = strspn(p2, " \t\n\r");
+ if (p2[sz] == '\0')
+ goto error;
+ p2 += sz;
+ p1 = p2;
+
+ if (*p2 == '"') {
+ p1++;
+ while (*p2 == '"') {
+ p2++;
+ p2 = strchr(p2, '\"');
+ if (p2 == NULL)
+ goto error;
+ if (p2[0] == '\0')
+ goto error;
+ if (p2[-1] != '\\')
+ break;
+ }
+ } else {
+ sz = strcspn(p2, " \t\n\r=,");
+ p2 += sz;
+ }
+
+#if 0 /* allow empty values */
+ if (p1 == p2)
+ goto error;
+#endif
+
+ if ((v->mv_value = malloc(p2 - p1 + 1)) == NULL)
+ goto nomem;
+ strncpy(v->mv_value, p1, p2 - p1);
+ v->mv_value[p2 - p1] = '\0';
+
+ if (p2[0] == '\0')
+ break;
+ if (p2[0] == '"')
+ p2++;
+
+ sz = strspn(p2, " \t\n\r");
+ if (p2[sz] == '\0')
+ break;
+ p2 += sz;
+
+ if (p2[0] == '\0')
+ break;
+ if (p2[0] != ',')
+ goto error;
+ p1 = p2;
+ }
+
+ free(str);
+
+ return 0;
+ error:
+ free_values(*val);
+ *val = NULL;
+ free(str);
+ return EINVAL;
+ nomem:
+ free_values(*val);
+ *val = NULL;
+ free(str);
+ return ENOMEM;
+}
+
+/*
+ *
+ */
+
+static const char *
+check_prefix(heim_digest_t context, const char *challenge)
+{
+ if (strncasecmp(digest_prefix, challenge, sizeof(digest_prefix) - 1) == 0) {
+
+ challenge += sizeof(digest_prefix) - 1;
+ while (*challenge == 0x20) /* remove extra space */
+ challenge++;
+ context->flags |= F_USE_PREFIX;
+ }
+
+ return challenge;
+}
+
+/*
+ *
+ */
+
+heim_digest_t
+heim_digest_create(int server, int type)
+{
+ heim_digest_t context;
+
+ context = calloc(1, sizeof(*context));
+ if (context == NULL)
+ return NULL;
+ context->flags |= F_SERVER;
+ context->type = type;
+
+ return context;
+}
+
+static char *
+generate_nonce(void)
+{
+ uint8_t rand[8];
+ char *nonce;
+
+ if (CCRandomCopyBytes(kCCRandomDefault, rand, sizeof(rand)) != kCCSuccess)
+ return NULL;
+
+ if (rk_hex_encode(rand, sizeof(rand), &nonce) < 0)
+ return NULL;
+
+ return nonce;
+}
+
+/**
+ * Generate a challange, needs to set serverRealm before calling this function.
+ *
+ * If type is set to HEIM_DIGEST_TYPE_AUTO, the HEIM_DIGEST_TYPE_RFC2831 will be used instead.
+ *
+ * For RFC2617 and RFC2831 QOP is required, so if any qop other then "auth" is requested, it need to be set with heim_diest_set_key().
+ *
+ * @return returns the challenge or NULL on error or failure to build the string. the lifetime
+ * of the string is manage by heim_digest and last until the the context is
+ * freed or until next call to heim_digest_generate_challenge().
+ */
+
+const char *
+heim_digest_generate_challenge(heim_digest_t context)
+{
+ char *challenge = NULL;
+
+ if (context->serverRealm == NULL)
+ return NULL;
+
+ if (context->serverNonce == NULL) {
+ if ((context->serverNonce = generate_nonce()) == NULL)
+ return NULL;
+ }
+
+ if (context->serverQOP == NULL) {
+ if ((context->serverQOP = strdup("auth")) == NULL)
+ return NULL;
+ }
+
+ if (context->serverMaxbuf == NULL) {
+ if ((context->serverMaxbuf = strdup("65536")) == NULL)
+ return NULL;
+ }
+
+ switch(context->type) {
+ case HEIM_DIGEST_TYPE_RFC2617_MD5:
+ asprintf(&challenge, "realm=\"%s\",nonce=\"%s\",algorithm=md5,qop=\"%s\"",
+ context->serverRealm, context->serverNonce,
+ context->serverQOP);
+ break;
+ case HEIM_DIGEST_TYPE_RFC2617_MD5_SESS:
+ asprintf(&challenge, "realm=\"%s\",nonce=\"%s\",algorithm=md5-sess,qop=\"%s\"",
+ context->serverRealm, context->serverNonce, context->serverQOP);
+ break;
+ case HEIM_DIGEST_TYPE_RFC2069:
+ asprintf(&challenge, "realm=\"%s\",nonce=\"%s\"",
+ context->serverRealm, context->serverNonce);
+ break;
+ case HEIM_DIGEST_TYPE_AUTO:
+ context->type = HEIM_DIGEST_TYPE_RFC2831;
+ HEIM_FALLTHROUGH;
+ case HEIM_DIGEST_TYPE_RFC2831:
+ asprintf(&challenge, "realm=\"%s\",nonce=\"%s\",qop=\"%s\",algorithm=md5-sess,charset=utf-8,maxbuf=%s",
+ context->serverRealm, context->serverNonce, context->serverQOP, context->serverMaxbuf);
+ break;
+ }
+
+ FREE_AND_CLEAR(context->serverChallenge);
+ context->serverChallenge = challenge;
+
+ return challenge;
+}
+
+int
+heim_digest_parse_challenge(heim_digest_t context, const char *challenge)
+{
+ struct md5_value *val = NULL;
+ int ret, type;
+
+ challenge = check_prefix(context, challenge);
+
+ ret = parse_values(challenge, &val);
+ if (ret)
+ goto out;
+
+ ret = 1;
+
+ context->serverNonce = values_find(&val, "nonce");
+ if (context->serverNonce == NULL) goto out;
+
+ context->serverRealm = values_find(&val, "realm");
+ if (context->serverRealm == NULL) goto out;
+
+ /* check alg */
+
+ context->serverAlgorithm = values_find(&val, "algorithm");
+ if (context->serverAlgorithm == NULL || strcasecmp(context->serverAlgorithm, "md5") == 0) {
+ type = HEIM_DIGEST_TYPE_RFC2617_MD5;
+ } else if (strcasecmp(context->serverAlgorithm, "md5-sess") == 0) {
+ type = HEIM_DIGEST_TYPE_RFC2617_OR_RFC2831;
+ } else {
+ goto out;
+ }
+
+ context->serverQOP = values_find(&val, "qop");
+ if (context->serverQOP == NULL)
+ type = HEIM_DIGEST_TYPE_RFC2069;
+
+ context->serverOpaque = values_find(&val, "opaque");
+
+ if (context->type != HEIM_DIGEST_TYPE_AUTO && (context->type & type) == 0)
+ goto out;
+ else if (context->type == HEIM_DIGEST_TYPE_AUTO)
+ context->type = type;
+
+ ret = 0;
+ out:
+ free_values(val);
+ if (ret)
+ clear_context(context);
+ return ret;
+}
+
+
+static void
+set_auth_method(heim_digest_t context)
+{
+
+ if (context->serverMethod == NULL) {
+ if (context->type == HEIM_DIGEST_TYPE_RFC2831)
+ context->serverMethod = strdup("AUTHENTICATE");
+ else
+ context->serverMethod = strdup("GET");
+ }
+}
+
+int
+heim_digest_parse_response(heim_digest_t context, const char *response)
+{
+ struct md5_value *val = NULL;
+ char *nonce;
+ int ret;
+
+ response = check_prefix(context, response);
+
+ ret = parse_values(response, &val);
+ if (ret)
+ goto out;
+
+ ret = 1;
+
+ if (context->type == HEIM_DIGEST_TYPE_AUTO) {
+ goto out;
+ } else if (context->type == HEIM_DIGEST_TYPE_RFC2617_OR_RFC2831) {
+ context->clientURI = values_find(&val, "uri");
+ if (context->clientURI) {
+ context->type = HEIM_DIGEST_TYPE_RFC2617_MD5_SESS;
+ } else {
+ context->clientURI = values_find(&val, "digest-uri");
+ context->type = HEIM_DIGEST_TYPE_RFC2831;
+ }
+ } else if (context->type == HEIM_DIGEST_TYPE_RFC2831) {
+ context->clientURI = values_find(&val, "digest-uri");
+ } else {
+ context->clientURI = values_find(&val, "uri");
+ }
+
+ if (context->clientURI == NULL)
+ goto out;
+
+ context->clientUsername = values_find(&val, "username");
+ if (context->clientUsername == NULL) goto out;
+
+ /* if client sent realm, make sure its the same of serverRealm if its set */
+ context->clientRealm = values_find(&val, "realm");
+ if (context->clientRealm && context->serverRealm && strcmp(context->clientRealm, context->serverRealm) != 0)
+ goto out;
+
+ context->clientResponse = values_find(&val, "response");
+ if (context->clientResponse == NULL) goto out;
+
+ nonce = values_find(&val, "nonce");
+ if (nonce == NULL) goto out;
+
+ if (strcmp(nonce, context->serverNonce) != 0) {
+ free(nonce);
+ goto out;
+ }
+ free(nonce);
+
+ if (context->type != HEIM_DIGEST_TYPE_RFC2069) {
+
+ context->clientQOP = values_find(&val, "qop");
+ if (context->clientQOP == NULL) goto out;
+
+ /*
+ * If we have serverQOP, lets check that clientQOP exists
+ * in the list of server entries.
+ */
+
+ if (context->serverQOP) {
+ Boolean found = false;
+ char *b, *e;
+ size_t len, clen = strlen(context->clientQOP);
+
+ b = context->serverQOP;
+ while (b && !found) {
+ e = strchr(b, ',');
+ if (e == NULL)
+ len = strlen(b);
+ else {
+ len = e - b;
+ e += 1;
+ }
+ if (clen == len && strncmp(b, context->clientQOP, len) == 0)
+ found = true;
+ b = e;
+ }
+ if (!found)
+ goto out;
+ }
+
+ context->clientNC = values_find(&val, "nc");
+ if (context->clientNC == NULL) goto out;
+
+ context->clientNonce = values_find(&val, "cnonce");
+ if (context->clientNonce == NULL) goto out;
+ }
+
+ set_auth_method(context);
+
+ ret = 0;
+ out:
+ free_values(val);
+ return ret;
+}
+
+char *
+heim_digest_userhash(const char *user, const char *realm, const char *password)
+{
+ unsigned char md[CC_MD5_DIGEST_LENGTH];
+ char *str = NULL;
+
+ digest_userhash(user, realm, password, md);
+
+ hex_encode(md, sizeof(md), &str);
+ if (str)
+ strlwr(str);
+ return str;
+}
+
+static char *
+build_digest(heim_digest_t context, const char *a1, const char *method)
+{
+ CC_MD5_CTX ctx;
+ uint8_t md[CC_MD5_DIGEST_LENGTH];
+ char *a2, *str = NULL;
+
+ a2 = build_A2_hash(context, method);
+ if (a2 == NULL)
+ return NULL;
+
+ CC_MD5_Init(&ctx);
+ CC_MD5_Update(&ctx, a1, (CC_LONG)strlen(a1));
+ CC_MD5_Update(&ctx, ":", 1);
+ CC_MD5_Update(&ctx, context->serverNonce, (CC_LONG)strlen(context->serverNonce));
+ if (context->type != HEIM_DIGEST_TYPE_RFC2069) {
+ CC_MD5_Update(&ctx, ":", 1);
+ CC_MD5_Update(&ctx, context->clientNC, (CC_LONG)strlen(context->clientNC));
+ CC_MD5_Update(&ctx, ":", 1);
+ CC_MD5_Update(&ctx, context->clientNonce, (CC_LONG)strlen(context->clientNonce));
+ CC_MD5_Update(&ctx, ":", 1);
+ CC_MD5_Update(&ctx, context->clientQOP, (CC_LONG)strlen(context->clientQOP));
+ }
+ CC_MD5_Update(&ctx, ":", 1);
+ CC_MD5_Update(&ctx, a2, (CC_LONG)strlen(a2));
+ CC_MD5_Final(md, &ctx);
+
+ free(a2);
+
+ hex_encode(md, sizeof(md), &str);
+ if (str)
+ strlwr(str);
+
+ return str;
+}
+
+static void
+build_server_response(heim_digest_t context, char *a1, char **response)
+{
+ char *str;
+
+ str = build_digest(context, a1, NULL);
+ if (str == NULL)
+ return;
+
+ FREE_AND_CLEAR(context->serverReply);
+ asprintf(&context->serverReply, "%srspauth=%s",
+ (context->flags & F_USE_PREFIX) ? digest_prefix : "",
+ str);
+ free(str);
+ if (response)
+ *response = context->serverReply;
+}
+
+
+/**
+ * Create response from server to client to server, server verification is in response.
+ * clientUsername and clientURI have to be given.
+ * If realm is not set, its used from server.
+ */
+
+const char *
+heim_digest_create_response(heim_digest_t context, char **response)
+{
+ char *a1, *str, *cnonce = NULL, *opaque = NULL, *uri = NULL, *nc = NULL;
+
+ if (response)
+ *response = NULL;
+
+ if (context->clientUsername == NULL || context->clientURI == NULL)
+ return NULL;
+
+ if (context->clientRealm == NULL) {
+ if (context->serverRealm == NULL)
+ return NULL;
+ if ((context->clientRealm = strdup(context->serverRealm)) == NULL)
+ return NULL;
+ }
+
+ if (context->type != HEIM_DIGEST_TYPE_RFC2069) {
+ if (context->clientNC == NULL) {
+ if ((context->clientNC = strdup("00000001")) == NULL)
+ return NULL;
+ }
+ if (context->clientNonce == NULL) {
+ if ((context->clientNonce = generate_nonce()) == NULL)
+ return NULL;
+ }
+
+ /**
+ * If using non RFC2069, appropriate QOP should be set.
+ *
+ * Pick QOP from server if not given, if its a list, pick the first entry
+ */
+ if (context->clientQOP == NULL) {
+ char *r;
+ if (context->serverQOP == NULL)
+ return NULL;
+ r = strchr(context->serverQOP, ',');
+ if (r == NULL) {
+ if ((context->clientQOP = strdup(context->serverQOP)) == NULL)
+ return NULL;
+ } else {
+ size_t len = (r - context->serverQOP) + 1;
+ if ((context->clientQOP = malloc(len)) == NULL)
+ return NULL;
+ strlcpy(context->clientQOP, context->serverQOP, len);
+ }
+ }
+ }
+
+ set_auth_method(context);
+
+ a1 = build_A1_hash(context);
+ if (a1 == NULL)
+ return NULL;
+
+ str = build_digest(context, a1, context->serverMethod);
+ if (str == NULL) {
+ MEMSET_FREE_AND_CLEAR(a1);
+ return NULL;
+ }
+
+ MEMSET_FREE_AND_CLEAR(context->clientResponse);
+ context->clientResponse = str;
+
+ if (context->clientURI) {
+ const char *name = "digest-uri";
+ if (context->type != HEIM_DIGEST_TYPE_RFC2831)
+ name = "uri";
+ asprintf(&uri, ",%s=\"%s\"", name, context->clientURI);
+ }
+
+ if (context->serverOpaque)
+ asprintf(&opaque, ",opaque=\"%s\"", context->serverOpaque);
+
+ if (context->clientNonce)
+ asprintf(&cnonce, ",cnonce=\"%s\"", context->clientNonce);
+
+ if (context->clientNC)
+ asprintf(&nc, ",nc=%s", context->clientNC);
+
+ asprintf(&context->clientReply,
+ "username=%s,realm=%s,nonce=\"%s\",qop=\"%s\"%s%s%s,response=\"%s\"%s",
+ context->clientUsername, context->clientRealm,
+ context->serverNonce,
+ context->clientQOP,
+ uri ? uri : "",
+ cnonce ? cnonce : "",
+ nc ? nc : "",
+ context->clientResponse,
+ opaque ? opaque : "");
+
+ build_server_response(context, a1, response);
+ MEMSET_FREE_AND_CLEAR(a1);
+ FREE_AND_CLEAR(uri);
+ FREE_AND_CLEAR(opaque);
+ FREE_AND_CLEAR(cnonce);
+ FREE_AND_CLEAR(nc);
+
+ return context->clientReply;
+}
+
+int
+heim_digest_verify(heim_digest_t context, char **response)
+{
+ char *a1;
+ char *str;
+ int res;
+
+ if (response)
+ *response = NULL;
+
+ set_auth_method(context);
+
+ a1 = build_A1_hash(context);
+ if (a1 == NULL)
+ return ENOMEM;
+
+ str = build_digest(context, a1, context->serverMethod);
+ if (str == NULL) {
+ MEMSET_FREE_AND_CLEAR(a1);
+ return ENOMEM;
+ }
+
+ res = (strcmp(str, context->clientResponse) == 0) ? 0 : EINVAL;
+ free(str);
+ if (res) {
+ MEMSET_FREE_AND_CLEAR(a1);
+ return res;
+ }
+
+ /* build server_response */
+ build_server_response(context, a1, response);
+ MEMSET_FREE_AND_CLEAR(a1);
+ /* XXX break ABI and return internally allocated string instead */
+ if (response)
+ *response = strdup(*response);
+
+ return 0;
+}
+
+/**
+ * Create a rspauth= response.
+ * Assumes that the A1hash/password serverNonce, clientNC, clientNonce, clientQOP is set.
+ *
+ * @return the rspauth string (including rspauth), return key are stored in serverReply and will be invalid after another call to heim_digest_*
+ */
+
+const char *
+heim_digest_server_response(heim_digest_t context)
+{
+ char *a1;
+
+ if (context->serverNonce == NULL)
+ return NULL;
+ if (context->clientURI == NULL)
+ return NULL;
+
+ a1 = build_A1_hash(context);
+ if (a1 == NULL)
+ return NULL;
+
+ build_server_response(context, a1, NULL);
+ MEMSET_FREE_AND_CLEAR(a1);
+
+ return context->serverReply;
+}
+
+void
+heim_digest_get_session_key(heim_digest_t context, void **key, size_t *keySize)
+{
+}
+
+void
+heim_digest_release(heim_digest_t context)
+{
+ clear_context(context);
+ free(context);
+}
+
+struct {
+ char *name;
+ size_t offset;
+} keys[] = {
+#define KVN(value) { #value, offsetof(struct heim_digest_desc, value) }
+ KVN(serverNonce),
+ KVN(serverRealm),
+ KVN(serverQOP),
+ KVN(serverMethod),
+ { "method", offsetof(struct heim_digest_desc, serverMethod) },
+ KVN(serverMaxbuf),
+ KVN(clientUsername),
+ { "username", offsetof(struct heim_digest_desc, clientUsername) },
+ KVN(clientResponse),
+ KVN(clientURI),
+ { "uri", offsetof(struct heim_digest_desc, clientURI) },
+ KVN(clientRealm),
+ { "realm", offsetof(struct heim_digest_desc, clientRealm) },
+ KVN(clientNonce),
+ KVN(clientQOP),
+ KVN(clientNC),
+ KVN(serverAlgorithm),
+ KVN(auth_id)
+#undef KVN
+};
+
+const char *
+heim_digest_get_key(heim_digest_t context, const char *key)
+{
+ size_t n;
+
+ for (n = 0; n < sizeof(keys) / sizeof(keys[0]); n++) {
+ if (strcasecmp(key, keys[n].name) == 0) {
+ char **ptr = (char **)((((char *)context) + keys[n].offset));
+ return *ptr;
+ }
+ }
+ return NULL;
+}
+
+int
+heim_digest_set_key(heim_digest_t context, const char *key, const char *value)
+{
+
+ if (strcmp(key, "password") == 0) {
+ FREE_AND_CLEAR(context->password);
+ if ((context->password = strdup(value)) == NULL)
+ return ENOMEM;
+ context->flags &= ~(F_HAVE_HASH|F_HAVE_HA1);
+ } else if (strcmp(key, "userhash") == 0) {
+ ssize_t ret;
+ FREE_AND_CLEAR(context->password);
+
+ ret = hex_decode(value, context->SecretHash, sizeof(context->SecretHash));
+ if (ret != sizeof(context->SecretHash))
+ return EINVAL;
+ context->flags &= ~F_HAVE_HA1;
+ context->flags |= F_HAVE_HASH;
+ } else if (strcmp(key, "H(A1)") == 0) {
+ ssize_t ret;
+ FREE_AND_CLEAR(context->password);
+
+ ret = hex_decode(value, context->SecretHash, sizeof(context->SecretHash));
+ if (ret != sizeof(context->SecretHash))
+ return EINVAL;
+ context->flags &= ~F_HAVE_HASH;
+ context->flags |= F_HAVE_HA1;
+ } else if (strcmp(key, "method") == 0) {
+ FREE_AND_CLEAR(context->serverMethod);
+ if ((context->serverMethod = strdup(value)) == NULL)
+ return ENOMEM;
+ } else {
+ size_t n;
+
+ for (n = 0; n < sizeof(keys) / sizeof(keys[0]); n++) {
+ if (strcasecmp(key, keys[n].name) == 0) {
+ char **ptr = (char **)((((char *)context) + keys[n].offset));
+ FREE_AND_CLEAR(*ptr);
+ if (((*ptr) = strdup(value)) == NULL)
+ return ENOMEM;
+ break;
+ }
+ }
+ if (n == sizeof(keys) / sizeof(keys[0]))
+ return ENOENT;
+ }
+ return 0;
+}
+
diff --git a/third_party/heimdal/lib/ntlm/heim-auth.h b/third_party/heimdal/lib/ntlm/heim-auth.h
new file mode 100644
index 0000000..a828de3
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/heim-auth.h
@@ -0,0 +1,135 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * Generate challange for APOP and CRAM-MD5
+ */
+
+char *
+heim_generate_challenge(const char *hostname); /* hostname can be NULL, the local hostname is used */
+
+/*
+ * APOP
+ */
+
+char *
+heim_apop_create(const char *challenge, const char *password);
+
+int
+heim_apop_verify(const char *challenge, const char *password, const char *response);
+
+/*
+ * CRAM-MD5
+ */
+
+typedef struct heim_HMAC_MD5_STATE_s {
+ uint32_t istate[4];
+ uint32_t ostate[4];
+} heim_CRAM_MD5_STATE;
+
+typedef struct heim_cram_md5_data *heim_cram_md5;
+
+char *
+heim_cram_md5_create(const char *challenge, const char *password);
+
+int
+heim_cram_md5_verify(const char *challenge, const char *password, const char *response);
+
+void
+heim_cram_md5_export(const char *password, heim_CRAM_MD5_STATE *state);
+
+heim_cram_md5
+heim_cram_md5_import(void *data, size_t len);
+
+int
+heim_cram_md5_verify_ctx(heim_cram_md5 ctx, const char *challenge, const char *response);
+
+void
+heim_cram_md5_free(heim_cram_md5 ctx);
+
+/*
+ * DIGEST-MD5
+ *
+ * heim_digest_t d;
+ *
+ * d = heim_digest_create(1, HEIM_DIGEST_TYPE_DIGEST_MD5_HTTP);
+ *
+ * if ((s = heim_digest_generate_challange(d)) != NULL) abort();
+ * send_to_client(s);
+ * response = read_from_client();
+ *
+ * heim_digest_parse_response(d, response);
+ * const char *user = heim_digest_get_key(d, "username");
+ * heim_digest_set_key(d, "password", "sommar17");
+ *
+ * if (heim_digest_verify(d, &response)) abort();
+ *
+ * send_to_client(response);
+ *
+ * heim_digest_release(d);
+ */
+
+typedef struct heim_digest_desc *heim_digest_t;
+
+heim_digest_t
+heim_digest_create(int server, int type);
+
+#define HEIM_DIGEST_TYPE_AUTO 0
+#define HEIM_DIGEST_TYPE_RFC2069 1
+#define HEIM_DIGEST_TYPE_RFC2617_MD5 2
+#define HEIM_DIGEST_TYPE_RFC2617_MD5_SESS 4
+#define HEIM_DIGEST_TYPE_RFC2831 8
+
+#define HEIM_DIGEST_TYPE_RFC2617_OR_RFC2831 12
+
+/* old deprecated names, use the two above instead */
+#define HEIM_DIGEST_TYPE_MD5 2
+#define HEIM_DIGEST_TYPE_MD5_SESS 4
+
+void
+heim_digest_init_set_key(heim_digest_t context, const char *key, const char *value);
+
+const char *
+heim_digest_generate_challenge(heim_digest_t context);
+
+int
+heim_digest_parse_challenge(heim_digest_t context, const char *challenge);
+
+int
+heim_digest_parse_response(heim_digest_t context, const char *response);
+
+const char *
+heim_digest_get_key(heim_digest_t context, const char *key);
+
+int
+heim_digest_set_key(heim_digest_t context, const char *key, const char *value);
+
+void
+heim_digest_set_user_password(heim_digest_t context, const char *password);
+
+void
+heim_digest_set_user_h1hash(heim_digest_t context, void *ptr, size_t size);
+
+int
+heim_digest_verify(heim_digest_t context, char **response);
+
+const char *
+heim_digest_create_response(heim_digest_t context, char **response);
+
+void
+heim_digest_get_session_key(heim_digest_t context, void **key, size_t *keySize);
+
+void
+heim_digest_release(heim_digest_t context);
+
+char *
+heim_digest_userhash(const char *user, const char *realm, const char *password);
+
+const char *
+heim_digest_server_response(heim_digest_t context);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/third_party/heimdal/lib/ntlm/heimntlm.h b/third_party/heimdal/lib/ntlm/heimntlm.h
new file mode 100644
index 0000000..6b24649
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/heimntlm.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_NTLM_H
+#define HEIM_NTLM_H
+
+/**
+ * Buffer for storing data in the NTLM library. When filled in by the
+ * library it should be freed with heim_ntlm_free_buf().
+ */
+struct ntlm_buf {
+ size_t length; /**< length buffer data */
+ void *data; /**< pointer to the data itself */
+};
+
+#define NTLM_NEG_UNICODE 0x00000001
+#define NTLM_NEG_OEM 0x00000002
+#define NTLM_NEG_TARGET 0x00000004
+#define NTLM_MBZ9 0x00000008
+
+#define NTLM_NEG_SIGN 0x00000010
+#define NTLM_NEG_SEAL 0x00000020
+#define NTLM_NEG_DATAGRAM 0x00000040
+#define NTLM_NEG_LM_KEY 0x00000080
+#define NTLM_NEG_NTLM 0x00000200
+#define NTLM_NEG_ANONYMOUS 0x00000800
+
+#define NTLM_MBZ8 0x00000100
+#define NTLM_NEG_NTLM 0x00000200
+#define NTLM_NEG_NT_ONLY 0x00000400
+#define NTLM_MBZ7 0x00000800 /* anon ? */
+
+#define NTLM_OEM_SUPPLIED_DOMAIN 0x00001000
+#define NTLM_OEM_SUPPLIED_WORKSTATION 0x00002000
+#define NTLM_MBZ6 0x00004000 /* local call ? */
+#define NTLM_NEG_ALWAYS_SIGN 0x00008000
+
+#define NTLM_TARGET_DOMAIN 0x00010000
+#define NTLM_TARGET_SERVER 0x00020000
+
+#define NTLM_TARGET_SHARE 0x00040000 /* mbz */
+#define NTLM_NEG_NTLM2_SESSION 0x00080000 /* EXTENDED_SESSIONSECURITY */
+#define NTLM_NEG_NTLM2 0x00080000
+
+#define NTLM_NEG_IDENTIFY 0x00100000
+#define NTLM_MBZ5 0x00200000
+#define NTLM_NON_NT_SESSION_KEY 0x00400000
+#define NTLM_NEG_TARGET_INFO 0x00800000
+
+#define NTLM_MBZ4 0x01000000
+#define NTLM_NEG_VERSION 0x02000000
+#define NTLM_MBZ3 0x04000000
+#define NTLM_MBZ2 0x08000000
+
+#define NTLM_MBZ1 0x10000000
+#define NTLM_ENC_128 0x20000000
+#define NTLM_NEG_KEYEX 0x40000000
+#define NTLM_ENC_56 0x80000000
+
+/**
+ * Struct for the NTLM target info, the strings is assumed to be in
+ * UTF8. When filled in by the library it should be freed with
+ * heim_ntlm_free_targetinfo().
+ */
+
+/* avflags */
+#define NTLM_TI_AV_FLAG_GUEST 0x00000001
+#define NTLM_TI_AV_FLAG_MIC 0x00000002
+
+struct ntlm_targetinfo {
+ char *servername; /**< */
+ char *domainname; /**< */
+ char *dnsdomainname; /**< */
+ char *dnsservername; /**< */
+ char *dnstreename; /**< */
+ uint32_t avflags; /**< */
+ char *targetname;
+ struct ntlm_buf channel_bindings;
+ uint64_t timestamp;
+};
+
+/**
+ * Struct for the NTLM type1 message info, the strings is assumed to
+ * be in UTF8. When filled in by the library it should be freed with
+ * heim_ntlm_free_type1().
+ */
+
+struct ntlm_type1 {
+ uint32_t flags; /**< */
+ char *domain; /**< */
+ char *hostname; /**< */
+ uint32_t os[2]; /**< */
+};
+
+/**
+ * Struct for the NTLM type2 message info, the strings is assumed to
+ * be in UTF8. When filled in by the library it should be freed with
+ * heim_ntlm_free_type2().
+ */
+
+struct ntlm_type2 {
+ uint32_t flags; /**< */
+ char *targetname; /**< */
+ struct ntlm_buf targetinfo; /**< */
+ unsigned char challenge[8]; /**< */
+ uint32_t context[2]; /**< */
+ uint32_t os[2]; /**< */
+};
+
+/**
+ * Struct for the NTLM type3 message info, the strings is assumed to
+ * be in UTF8. When filled in by the library it should be freed with
+ * heim_ntlm_free_type3().
+ */
+
+struct ntlm_type3 {
+ uint32_t flags; /**< */
+ char *username; /**< */
+ char *targetname; /**< */
+ struct ntlm_buf lm; /**< */
+ struct ntlm_buf ntlm; /**< */
+ struct ntlm_buf sessionkey; /**< */
+ char *ws; /**< */
+ uint32_t os[2]; /**< */
+ size_t mic_offset;
+ uint8_t mic[16];
+};
+
+extern time_t heim_ntlm_time_skew;
+
+#include <ntlm_err.h>
+#include <heimntlm-protos.h>
+
+#endif /* NTLM_NTLM_H */
diff --git a/third_party/heimdal/lib/ntlm/libheimntlm-exports.def b/third_party/heimdal/lib/ntlm/libheimntlm-exports.def
new file mode 100644
index 0000000..33ac09e
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/libheimntlm-exports.def
@@ -0,0 +1,24 @@
+EXPORTS
+ heim_ntlm_build_ntlm1_master
+ heim_ntlm_calculate_lm2
+ heim_ntlm_calculate_ntlm1
+ heim_ntlm_calculate_ntlm2
+ heim_ntlm_calculate_ntlm2_sess
+ heim_ntlm_decode_targetinfo
+ heim_ntlm_decode_type1
+ heim_ntlm_decode_type2
+ heim_ntlm_decode_type3
+ heim_ntlm_encode_targetinfo
+ heim_ntlm_encode_type1
+ heim_ntlm_encode_type2
+ heim_ntlm_encode_type3
+ heim_ntlm_free_buf
+ heim_ntlm_free_targetinfo
+ heim_ntlm_free_type1
+ heim_ntlm_free_type2
+ heim_ntlm_free_type3
+ heim_ntlm_nt_key
+ heim_ntlm_ntlmv2_key
+ heim_ntlm_verify_ntlm2
+ heim_ntlm_unparse_flags
+ initialize_ntlm_error_table_r
diff --git a/third_party/heimdal/lib/ntlm/libheimntlm-version.rc b/third_party/heimdal/lib/ntlm/libheimntlm-version.rc
new file mode 100644
index 0000000..2055c9d
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/libheimntlm-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_DLL
+#define RC_FILE_DESC_0409 "Heimdal NTLM Support Library"
+#define RC_FILE_ORIG_0409 "libheimntlm.dll"
+
+#include "../../windows/version.rc"
diff --git a/third_party/heimdal/lib/ntlm/ntlm.c b/third_party/heimdal/lib/ntlm/ntlm.c
new file mode 100644
index 0000000..7384e18
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/ntlm.c
@@ -0,0 +1,2030 @@
+/*
+ * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <roken.h>
+
+#include <wind.h>
+#include <parse_units.h>
+#include <krb5.h>
+
+
+#define HC_DEPRECATED_CRYPTO
+
+#include "krb5-types.h"
+#include "crypto-headers.h"
+
+#include <heimntlm.h>
+
+/*! \mainpage Heimdal NTLM library
+ *
+ * \section intro Introduction
+ *
+ * Heimdal libheimntlm library is a implementation of the NTLM
+ * protocol, both version 1 and 2. The GSS-API mech that uses this
+ * library adds support for transport encryption and integrity
+ * checking.
+ *
+ * NTLM is a protocol for mutual authentication, its still used in
+ * many protocol where Kerberos is not support, one example is
+ * EAP/X802.1x mechanism LEAP from Microsoft and Cisco.
+ *
+ * This is a support library for the core protocol, its used in
+ * Heimdal to implement and GSS-API mechanism. There is also support
+ * in the KDC to do remote digest authenticiation, this to allow
+ * services to authenticate users w/o direct access to the users ntlm
+ * hashes (same as Kerberos arcfour enctype keys).
+ *
+ * More information about the NTLM protocol can found here
+ * http://davenport.sourceforge.net/ntlm.html .
+ *
+ * The Heimdal projects web page: http://www.h5l.org/
+ *
+ * @section ntlm_example NTLM Example
+ *
+ * Example to to use @ref test_ntlm.c .
+ *
+ * @example test_ntlm.c
+ *
+ * Example how to use the NTLM primitives.
+ *
+ */
+
+/** @defgroup ntlm_core Heimdal NTLM library
+ *
+ * The NTLM core functions implement the string2key generation
+ * function, message encode and decode function, and the hash function
+ * functions.
+ */
+
+struct sec_buffer {
+ uint16_t length;
+ uint16_t allocated;
+ uint32_t offset;
+};
+
+static const unsigned char ntlmsigature[8] = "NTLMSSP\x00";
+
+time_t heim_ntlm_time_skew = 300;
+
+/*
+ *
+ */
+
+#define CHECK(f, e) \
+ do { \
+ ret = f; \
+ if (ret != (ssize_t)(e)) { \
+ ret = HNTLM_ERR_DECODE; \
+ goto out; \
+ } \
+ } while(/*CONSTCOND*/0)
+
+#define CHECK_SIZE(f, e) \
+ do { \
+ ssize_t sret = f; \
+ if (sret != (ssize_t)(e)) { \
+ ret = HNTLM_ERR_DECODE; \
+ goto out; \
+ } \
+ } while(/*CONSTCOND*/0)
+
+#define CHECK_OFFSET(f, e) \
+ do { \
+ off_t sret = f; \
+ if (sret != (e)) { \
+ ret = HNTLM_ERR_DECODE; \
+ goto out; \
+ } \
+ } while(/*CONSTCOND*/0)
+
+
+static struct units ntlm_flag_units[] = {
+#define ntlm_flag(x) { #x, NTLM_##x }
+ ntlm_flag(ENC_56),
+ ntlm_flag(NEG_KEYEX),
+ ntlm_flag(ENC_128),
+ ntlm_flag(MBZ1),
+ ntlm_flag(MBZ2),
+ ntlm_flag(MBZ3),
+ ntlm_flag(NEG_VERSION),
+ ntlm_flag(MBZ4),
+ ntlm_flag(NEG_TARGET_INFO),
+ ntlm_flag(NON_NT_SESSION_KEY),
+ ntlm_flag(MBZ5),
+ ntlm_flag(NEG_IDENTIFY),
+ ntlm_flag(NEG_NTLM2),
+ ntlm_flag(TARGET_SHARE),
+ ntlm_flag(TARGET_SERVER),
+ ntlm_flag(TARGET_DOMAIN),
+ ntlm_flag(NEG_ALWAYS_SIGN),
+ ntlm_flag(MBZ6),
+ ntlm_flag(OEM_SUPPLIED_WORKSTATION),
+ ntlm_flag(OEM_SUPPLIED_DOMAIN),
+ ntlm_flag(NEG_ANONYMOUS),
+ ntlm_flag(NEG_NT_ONLY),
+ ntlm_flag(NEG_NTLM),
+ ntlm_flag(MBZ8),
+ ntlm_flag(NEG_LM_KEY),
+ ntlm_flag(NEG_DATAGRAM),
+ ntlm_flag(NEG_SEAL),
+ ntlm_flag(NEG_SIGN),
+ ntlm_flag(MBZ9),
+ ntlm_flag(NEG_TARGET),
+ ntlm_flag(NEG_OEM),
+ ntlm_flag(NEG_UNICODE),
+#undef ntlm_flag
+ {NULL, 0}
+};
+
+size_t
+heim_ntlm_unparse_flags(uint32_t flags, char *s, size_t len)
+{
+ return unparse_flags(flags, ntlm_flag_units, s, len);
+}
+
+
+/**
+ * heim_ntlm_free_buf frees the ntlm buffer
+ *
+ * @param p buffer to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
+void
+heim_ntlm_free_buf(struct ntlm_buf *p)
+{
+ if (p->data)
+ free(p->data);
+ p->data = NULL;
+ p->length = 0;
+}
+
+
+static int
+ascii2ucs2le(const char *string, int up, struct ntlm_buf *buf)
+{
+ uint16_t *data;
+ size_t len, n;
+ uint8_t *p;
+ int ret;
+
+ ret = wind_utf8ucs2_length(string, &len);
+ if (ret)
+ return ret;
+ if (len > UINT_MAX / sizeof(data[0]))
+ return ERANGE;
+
+ data = malloc(len * sizeof(data[0]));
+ if (data == NULL)
+ return ENOMEM;
+
+ ret = wind_utf8ucs2(string, data, &len);
+ if (ret) {
+ free(data);
+ return ret;
+ }
+
+ if (len == 0) {
+ free(data);
+ buf->data = NULL;
+ buf->length = 0;
+ return 0;
+ }
+
+ /* uppercase string, only handle ascii right now */
+ if (up) {
+ for (n = 0; n < len ; n++) {
+ if (data[n] < 128)
+ data[n] = toupper((int)data[n]);
+ }
+ }
+
+ buf->length = len * 2;
+ p = buf->data = malloc(buf->length);
+ if (buf->data == NULL && len != 0) {
+ free(data);
+ heim_ntlm_free_buf(buf);
+ return ENOMEM;
+ }
+
+ for (n = 0; n < len ; n++) {
+ p[(n * 2) + 0] = (data[n] ) & 0xff;
+ p[(n * 2) + 1] = (data[n] >> 8) & 0xff;
+ }
+ memset(data, 0, sizeof(data[0]) * len);
+ free(data);
+
+ return 0;
+}
+
+/*
+ * Sizes in bytes
+ */
+
+#define SIZE_SEC_BUFFER (2+2+4)
+#define SIZE_OS_VERSION (8)
+
+/*
+ *
+ */
+
+static krb5_error_code
+ret_sec_buffer(krb5_storage *sp, struct sec_buffer *buf)
+{
+ krb5_error_code ret;
+ CHECK(krb5_ret_uint16(sp, &buf->length), 0);
+ CHECK(krb5_ret_uint16(sp, &buf->allocated), 0);
+ CHECK(krb5_ret_uint32(sp, &buf->offset), 0);
+out:
+ return ret;
+}
+
+static krb5_error_code
+store_sec_buffer(krb5_storage *sp, const struct sec_buffer *buf)
+{
+ krb5_error_code ret;
+ CHECK(krb5_store_uint16(sp, buf->length), 0);
+ CHECK(krb5_store_uint16(sp, buf->allocated), 0);
+ CHECK(krb5_store_uint32(sp, buf->offset), 0);
+out:
+ return ret;
+}
+
+/*
+ * Strings are either OEM or UNICODE. The later is encoded as ucs2 on
+ * wire, but using utf8 in memory.
+ */
+
+static size_t
+len_string(int ucs2, const char *s)
+{
+ if (ucs2) {
+ size_t len;
+ int ret;
+
+ ret = wind_utf8ucs2_length(s, &len);
+ if (ret == 0)
+ return len * 2;
+ return strlen(s) * 5 * 2;
+ } else {
+ return strlen(s);
+ }
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+ret_string(krb5_storage *sp, int ucs2, size_t len, char **s)
+{
+ krb5_error_code ret;
+ uint16_t *data = NULL;
+
+ *s = malloc(len + 1);
+ if (*s == NULL)
+ return ENOMEM;
+ CHECK_SIZE(krb5_storage_read(sp, *s, len), len);
+
+ (*s)[len] = '\0';
+
+ if (ucs2) {
+ unsigned int flags = WIND_RW_LE;
+ size_t utf16len = len / 2;
+ size_t utf8len;
+
+ data = malloc(utf16len * sizeof(data[0]));
+ if (data == NULL) {
+ free(*s); *s = NULL;
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = wind_ucs2read(*s, len, &flags, data, &utf16len);
+ free(*s); *s = NULL;
+ if (ret) {
+ goto out;
+ }
+
+ CHECK(wind_ucs2utf8_length(data, utf16len, &utf8len), 0);
+
+ utf8len += 1;
+
+ *s = malloc(utf8len);
+ if (*s == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ CHECK(wind_ucs2utf8(data, utf16len, *s, &utf8len), 0);
+ }
+ ret = 0;
+ out:
+ if (data)
+ free(data);
+
+ return ret;
+}
+
+
+
+static krb5_error_code
+ret_sec_string(krb5_storage *sp, int ucs2, struct sec_buffer *desc, char **s)
+{
+ krb5_error_code ret = 0;
+ CHECK_OFFSET(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset);
+ CHECK(ret_string(sp, ucs2, desc->length, s), 0);
+ out:
+ return ret;
+}
+
+static krb5_error_code
+put_string(krb5_storage *sp, int ucs2, const char *s)
+{
+ krb5_error_code ret;
+ struct ntlm_buf buf;
+
+ if (ucs2) {
+ ret = ascii2ucs2le(s, 0, &buf);
+ if (ret)
+ return ret;
+ } else {
+ buf.data = rk_UNCONST(s);
+ buf.length = strlen(s);
+ }
+
+ CHECK_SIZE(krb5_storage_write(sp, buf.data, buf.length), buf.length);
+ if (ucs2)
+ heim_ntlm_free_buf(&buf);
+ ret = 0;
+out:
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+ret_buf(krb5_storage *sp, struct sec_buffer *desc, struct ntlm_buf *buf)
+{
+ krb5_error_code ret;
+
+ buf->data = malloc(desc->length);
+ buf->length = desc->length;
+ CHECK_OFFSET(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset);
+ CHECK_SIZE(krb5_storage_read(sp, buf->data, buf->length), buf->length);
+ ret = 0;
+out:
+ return ret;
+}
+
+static krb5_error_code
+put_buf(krb5_storage *sp, const struct ntlm_buf *buf)
+{
+ krb5_error_code ret;
+ CHECK_SIZE(krb5_storage_write(sp, buf->data, buf->length), buf->length);
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * Frees the ntlm_targetinfo message
+ *
+ * @param ti targetinfo to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
+void
+heim_ntlm_free_targetinfo(struct ntlm_targetinfo *ti)
+{
+ free(ti->servername);
+ free(ti->domainname);
+ free(ti->dnsdomainname);
+ free(ti->dnsservername);
+ free(ti->dnstreename);
+ free(ti->targetname);
+ heim_ntlm_free_buf(&ti->channel_bindings);
+ memset(ti, 0, sizeof(*ti));
+}
+
+static int
+encode_ti_string(krb5_storage *out, uint16_t type, int ucs2, char *s)
+{
+ krb5_error_code ret;
+ CHECK(krb5_store_uint16(out, type), 0);
+ CHECK(krb5_store_uint16(out, len_string(ucs2, s)), 0);
+ CHECK(put_string(out, ucs2, s), 0);
+out:
+ return ret;
+}
+
+/**
+ * Encodes a ntlm_targetinfo message.
+ *
+ * @param ti the ntlm_targetinfo message to encode.
+ * @param ucs2 ignored
+ * @param data is the return buffer with the encoded message, should be
+ * freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_encode_targetinfo(const struct ntlm_targetinfo *ti,
+ int ucs2,
+ struct ntlm_buf *data)
+{
+ krb5_error_code ret;
+ krb5_storage *out;
+
+ data->data = NULL;
+ data->length = 0;
+
+ out = krb5_storage_emem();
+ if (out == NULL)
+ return ENOMEM;
+
+ krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
+
+ if (ti->servername)
+ CHECK(encode_ti_string(out, 1, ucs2, ti->servername), 0);
+ if (ti->domainname)
+ CHECK(encode_ti_string(out, 2, ucs2, ti->domainname), 0);
+ if (ti->dnsservername)
+ CHECK(encode_ti_string(out, 3, ucs2, ti->dnsservername), 0);
+ if (ti->dnsdomainname)
+ CHECK(encode_ti_string(out, 4, ucs2, ti->dnsdomainname), 0);
+ if (ti->dnstreename)
+ CHECK(encode_ti_string(out, 5, ucs2, ti->dnstreename), 0);
+ if (ti->avflags) {
+ CHECK(krb5_store_uint16(out, 6), 0);
+ CHECK(krb5_store_uint16(out, 4), 0);
+ CHECK(krb5_store_uint32(out, ti->avflags), 0);
+ }
+ if (ti->timestamp) {
+ CHECK(krb5_store_uint16(out, 7), 0);
+ CHECK(krb5_store_uint16(out, 8), 0);
+ CHECK(krb5_store_uint32(out, ti->timestamp & 0xffffffff), 0);
+ CHECK(krb5_store_uint32(out, (ti->timestamp >> 32) & 0xffffffff), 0);
+ }
+ if (ti->targetname) {
+ CHECK(encode_ti_string(out, 9, ucs2, ti->targetname), 0);
+ }
+ if (ti->channel_bindings.length) {
+ CHECK(krb5_store_uint16(out, 10), 0);
+ CHECK(krb5_store_uint16(out, ti->channel_bindings.length), 0);
+ CHECK_SIZE(krb5_storage_write(out, ti->channel_bindings.data, ti->channel_bindings.length), ti->channel_bindings.length);
+ }
+
+ /* end tag */
+ CHECK(krb5_store_int16(out, 0), 0);
+ CHECK(krb5_store_int16(out, 0), 0);
+
+ {
+ krb5_data d;
+ ret = krb5_storage_to_data(out, &d);
+ data->data = d.data;
+ data->length = d.length;
+ }
+out:
+ krb5_storage_free(out);
+ return ret;
+}
+
+/**
+ * Decodes an NTLM targetinfo message
+ *
+ * @param data input data buffer with the encode NTLM targetinfo message
+ * @param ucs2 if the strings should be encoded with ucs2 (selected by flag in message).
+ * @param ti the decoded target info, should be freed with heim_ntlm_free_targetinfo().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_decode_targetinfo(const struct ntlm_buf *data,
+ int ucs2,
+ struct ntlm_targetinfo *ti)
+{
+ uint16_t type, len;
+ krb5_storage *in;
+ int ret = 0, done = 0;
+
+ memset(ti, 0, sizeof(*ti));
+
+ if (data->length == 0)
+ return 0;
+
+ in = krb5_storage_from_readonly_mem(data->data, data->length);
+ if (in == NULL)
+ return ENOMEM;
+ krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
+
+ while (!done) {
+ CHECK(krb5_ret_uint16(in, &type), 0);
+ CHECK(krb5_ret_uint16(in, &len), 0);
+
+ switch (type) {
+ case 0:
+ done = 1;
+ break;
+ case 1:
+ CHECK(ret_string(in, ucs2, len, &ti->servername), 0);
+ break;
+ case 2:
+ CHECK(ret_string(in, ucs2, len, &ti->domainname), 0);
+ break;
+ case 3:
+ CHECK(ret_string(in, ucs2, len, &ti->dnsservername), 0);
+ break;
+ case 4:
+ CHECK(ret_string(in, ucs2, len, &ti->dnsdomainname), 0);
+ break;
+ case 5:
+ CHECK(ret_string(in, ucs2, len, &ti->dnstreename), 0);
+ break;
+ case 6:
+ CHECK(krb5_ret_uint32(in, &ti->avflags), 0);
+ break;
+ case 7: {
+ uint32_t tmp;
+ CHECK(krb5_ret_uint32(in, &tmp), 0);
+ ti->timestamp = tmp;
+ CHECK(krb5_ret_uint32(in, &tmp), 0);
+ ti->timestamp |= ((uint64_t)tmp) << 32;
+ break;
+ }
+ case 9:
+ CHECK(ret_string(in, 1, len, &ti->targetname), 0);
+ break;
+ case 10:
+ ti->channel_bindings.data = malloc(len);
+ if (ti->channel_bindings.data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ ti->channel_bindings.length = len;
+ CHECK_SIZE(krb5_storage_read(in, ti->channel_bindings.data, len), len);
+ break;
+ default:
+ krb5_storage_seek(in, len, SEEK_CUR);
+ break;
+ }
+ }
+ out:
+ if (in)
+ krb5_storage_free(in);
+ return ret;
+}
+
+static krb5_error_code
+encode_os_version(krb5_storage *out)
+{
+ krb5_error_code ret;
+ CHECK(krb5_store_uint8(out, 0x06), 0);
+ CHECK(krb5_store_uint8(out, 0x01), 0);
+ CHECK(krb5_store_uint16(out, 0x1db0), 0);
+ CHECK(krb5_store_uint8(out, 0x00), 0);
+ CHECK(krb5_store_uint8(out, 0x00), 0);
+ CHECK(krb5_store_uint8(out, 0x00), 0);
+ CHECK(krb5_store_uint8(out, 0x0f), 0); /* ntlm version 15 */
+ out:
+ return ret;
+}
+
+/**
+ * Frees the ntlm_type1 message
+ *
+ * @param data message to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
+void
+heim_ntlm_free_type1(struct ntlm_type1 *data)
+{
+ if (data->domain)
+ free(data->domain);
+ if (data->hostname)
+ free(data->hostname);
+ memset(data, 0, sizeof(*data));
+}
+
+int
+heim_ntlm_decode_type1(const struct ntlm_buf *buf, struct ntlm_type1 *data)
+{
+ krb5_error_code ret;
+ unsigned char sig[8];
+ uint32_t type;
+ struct sec_buffer domain, hostname;
+ krb5_storage *in;
+ int ucs2;
+
+ memset(data, 0, sizeof(*data));
+
+ in = krb5_storage_from_readonly_mem(buf->data, buf->length);
+ if (in == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
+ CHECK(ct_memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
+ CHECK(krb5_ret_uint32(in, &type), 0);
+ CHECK(type, 1);
+ CHECK(krb5_ret_uint32(in, &data->flags), 0);
+
+ ucs2 = !!(data->flags & NTLM_NEG_UNICODE);
+
+ /*
+ * domain and hostname are unconditionally encoded regardless of
+ * NTLMSSP_NEGOTIATE_OEM_{HOSTNAME,WORKSTATION}_SUPPLIED flag
+ */
+ CHECK(ret_sec_buffer(in, &domain), 0);
+ CHECK(ret_sec_buffer(in, &hostname), 0);
+
+ if (data->flags & NTLM_NEG_VERSION) {
+ CHECK(krb5_ret_uint32(in, &data->os[0]), 0);
+ CHECK(krb5_ret_uint32(in, &data->os[1]), 0);
+ }
+
+ if (data->flags & NTLM_OEM_SUPPLIED_DOMAIN)
+ CHECK(ret_sec_string(in, ucs2, &domain, &data->domain), 0);
+ if (data->flags & NTLM_OEM_SUPPLIED_WORKSTATION)
+ CHECK(ret_sec_string(in, ucs2, &hostname, &data->hostname), 0);
+
+out:
+ if (in)
+ krb5_storage_free(in);
+ if (ret)
+ heim_ntlm_free_type1(data);
+
+ return ret;
+}
+
+/**
+ * Encodes an ntlm_type1 message.
+ *
+ * @param type1 the ntlm_type1 message to encode.
+ * @param data is the return buffer with the encoded message, should be
+ * freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_encode_type1(const struct ntlm_type1 *type1, struct ntlm_buf *data)
+{
+ krb5_error_code ret;
+ struct sec_buffer domain, hostname;
+ krb5_storage *out;
+ uint32_t base, flags;
+ int ucs2 = 0;
+
+ flags = type1->flags;
+ base = 16;
+
+ if (flags & NTLM_NEG_UNICODE)
+ ucs2 = 1;
+
+ if (type1->domain) {
+ base += SIZE_SEC_BUFFER;
+ flags |= NTLM_OEM_SUPPLIED_DOMAIN;
+ }
+ if (type1->hostname) {
+ base += SIZE_SEC_BUFFER;
+ flags |= NTLM_OEM_SUPPLIED_WORKSTATION;
+ }
+ if (flags & NTLM_NEG_VERSION)
+ base += SIZE_OS_VERSION; /* os */
+
+ if (type1->domain) {
+ domain.offset = base;
+ domain.length = len_string(ucs2, type1->domain);
+ domain.allocated = domain.length;
+ } else {
+ domain.offset = 0;
+ domain.length = 0;
+ domain.allocated = 0;
+ }
+
+ if (type1->hostname) {
+ hostname.offset = domain.allocated + domain.offset;
+ hostname.length = len_string(ucs2, type1->hostname);
+ hostname.allocated = hostname.length;
+ } else {
+ hostname.offset = 0;
+ hostname.length = 0;
+ hostname.allocated = 0;
+ }
+
+ out = krb5_storage_emem();
+ if (out == NULL)
+ return ENOMEM;
+
+ krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
+ CHECK_SIZE(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
+ sizeof(ntlmsigature));
+ CHECK(krb5_store_uint32(out, 1), 0);
+ CHECK(krb5_store_uint32(out, flags), 0);
+
+ CHECK(store_sec_buffer(out, &domain), 0);
+ CHECK(store_sec_buffer(out, &hostname), 0);
+
+ if (flags & NTLM_NEG_VERSION) {
+ CHECK(encode_os_version(out), 0);
+ }
+ if (type1->domain)
+ CHECK(put_string(out, ucs2, type1->domain), 0);
+ if (type1->hostname)
+ CHECK(put_string(out, ucs2, type1->hostname), 0);
+
+ {
+ krb5_data d;
+ ret = krb5_storage_to_data(out, &d);
+ data->data = d.data;
+ data->length = d.length;
+ }
+out:
+ krb5_storage_free(out);
+
+ return ret;
+}
+
+/**
+ * Frees the ntlm_type2 message
+ *
+ * @param data message to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
+void
+heim_ntlm_free_type2(struct ntlm_type2 *data)
+{
+ if (data->targetname)
+ free(data->targetname);
+ heim_ntlm_free_buf(&data->targetinfo);
+ memset(data, 0, sizeof(*data));
+}
+
+int
+heim_ntlm_decode_type2(const struct ntlm_buf *buf, struct ntlm_type2 *type2)
+{
+ krb5_error_code ret;
+ unsigned char sig[8];
+ uint32_t type, ctx[2];
+ struct sec_buffer targetname, targetinfo;
+ krb5_storage *in;
+ int ucs2 = 0;
+
+ memset(type2, 0, sizeof(*type2));
+
+ in = krb5_storage_from_readonly_mem(buf->data, buf->length);
+ if (in == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
+ CHECK(ct_memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
+ CHECK(krb5_ret_uint32(in, &type), 0);
+ CHECK(type, 2);
+
+ CHECK(ret_sec_buffer(in, &targetname), 0);
+ CHECK(krb5_ret_uint32(in, &type2->flags), 0);
+ if (type2->flags & NTLM_NEG_UNICODE)
+ ucs2 = 1;
+ CHECK_SIZE(krb5_storage_read(in, type2->challenge, sizeof(type2->challenge)),
+ sizeof(type2->challenge));
+ CHECK(krb5_ret_uint32(in, &ctx[0]), 0); /* context */
+ CHECK(krb5_ret_uint32(in, &ctx[1]), 0);
+ CHECK(ret_sec_buffer(in, &targetinfo), 0);
+ /* os version */
+ if (type2->flags & NTLM_NEG_VERSION) {
+ CHECK(krb5_ret_uint32(in, &type2->os[0]), 0);
+ CHECK(krb5_ret_uint32(in, &type2->os[1]), 0);
+ }
+
+ CHECK(ret_sec_string(in, ucs2, &targetname, &type2->targetname), 0);
+ CHECK(ret_buf(in, &targetinfo, &type2->targetinfo), 0);
+ ret = 0;
+
+out:
+ if (in)
+ krb5_storage_free(in);
+ if (ret)
+ heim_ntlm_free_type2(type2);
+
+ return ret;
+}
+
+/**
+ * Encodes an ntlm_type2 message.
+ *
+ * @param type2 the ntlm_type2 message to encode.
+ * @param data is the return buffer with the encoded message, should be
+ * freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_encode_type2(const struct ntlm_type2 *type2, struct ntlm_buf *data)
+{
+ struct sec_buffer targetname, targetinfo;
+ krb5_error_code ret;
+ krb5_storage *out = NULL;
+ uint32_t base;
+ int ucs2 = 0;
+
+ base = 48;
+
+ if (type2->flags & NTLM_NEG_VERSION)
+ base += SIZE_OS_VERSION;
+
+ if (type2->flags & NTLM_NEG_UNICODE)
+ ucs2 = 1;
+
+ targetname.offset = base;
+ targetname.length = len_string(ucs2, type2->targetname);
+ targetname.allocated = targetname.length;
+
+ targetinfo.offset = targetname.allocated + targetname.offset;
+ targetinfo.length = type2->targetinfo.length;
+ targetinfo.allocated = type2->targetinfo.length;
+
+ out = krb5_storage_emem();
+ if (out == NULL)
+ return ENOMEM;
+
+ krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
+ CHECK_SIZE(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
+ sizeof(ntlmsigature));
+ CHECK(krb5_store_uint32(out, 2), 0);
+ CHECK(store_sec_buffer(out, &targetname), 0);
+ CHECK(krb5_store_uint32(out, type2->flags), 0);
+ CHECK_SIZE(krb5_storage_write(out, type2->challenge, sizeof(type2->challenge)),
+ sizeof(type2->challenge));
+ CHECK(krb5_store_uint32(out, 0), 0); /* context */
+ CHECK(krb5_store_uint32(out, 0), 0);
+ CHECK(store_sec_buffer(out, &targetinfo), 0);
+ /* os version */
+ if (type2->flags & NTLM_NEG_VERSION) {
+ CHECK(encode_os_version(out), 0);
+ }
+ CHECK(put_string(out, ucs2, type2->targetname), 0);
+ CHECK_SIZE(krb5_storage_write(out, type2->targetinfo.data,
+ type2->targetinfo.length),
+ type2->targetinfo.length);
+
+ {
+ krb5_data d;
+ ret = krb5_storage_to_data(out, &d);
+ data->data = d.data;
+ data->length = d.length;
+ }
+
+out:
+ krb5_storage_free(out);
+
+ return ret;
+}
+
+/**
+ * Frees the ntlm_type3 message
+ *
+ * @param data message to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
+void
+heim_ntlm_free_type3(struct ntlm_type3 *data)
+{
+ heim_ntlm_free_buf(&data->lm);
+ heim_ntlm_free_buf(&data->ntlm);
+ if (data->targetname)
+ free(data->targetname);
+ if (data->username)
+ free(data->username);
+ if (data->ws)
+ free(data->ws);
+ heim_ntlm_free_buf(&data->sessionkey);
+ memset(data, 0, sizeof(*data));
+}
+
+/*
+ *
+ */
+
+int
+heim_ntlm_decode_type3(const struct ntlm_buf *buf,
+ int ucs2,
+ struct ntlm_type3 *type3)
+{
+ krb5_error_code ret;
+ unsigned char sig[8];
+ uint32_t type;
+ krb5_storage *in;
+ struct sec_buffer lm, ntlm, target, username, sessionkey, ws;
+ uint32_t min_offset = 0xffffffff;
+
+ memset(type3, 0, sizeof(*type3));
+ memset(&sessionkey, 0, sizeof(sessionkey));
+
+ in = krb5_storage_from_readonly_mem(buf->data, buf->length);
+ if (in == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
+ CHECK(ct_memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
+ CHECK(krb5_ret_uint32(in, &type), 0);
+ CHECK(type, 3);
+ CHECK(ret_sec_buffer(in, &lm), 0);
+ if (lm.allocated)
+ min_offset = min(min_offset, lm.offset);
+ CHECK(ret_sec_buffer(in, &ntlm), 0);
+ if (ntlm.allocated)
+ min_offset = min(min_offset, ntlm.offset);
+ CHECK(ret_sec_buffer(in, &target), 0);
+ min_offset = min(min_offset, target.offset);
+ CHECK(ret_sec_buffer(in, &username), 0);
+ min_offset = min(min_offset, username.offset);
+ CHECK(ret_sec_buffer(in, &ws), 0);
+ if (ws.allocated)
+ min_offset = min(min_offset, ws.offset);
+
+ if (min_offset >= 52) {
+ CHECK(ret_sec_buffer(in, &sessionkey), 0);
+ min_offset = min(min_offset, sessionkey.offset);
+ CHECK(krb5_ret_uint32(in, &type3->flags), 0);
+ }
+ if (min_offset >= 52 + SIZE_SEC_BUFFER + 4 + SIZE_OS_VERSION) {
+ CHECK(krb5_ret_uint32(in, &type3->os[0]), 0);
+ CHECK(krb5_ret_uint32(in, &type3->os[1]), 0);
+ }
+ if (min_offset >= 52 + SIZE_SEC_BUFFER + 4 + SIZE_OS_VERSION + 16) {
+ type3->mic_offset = 52 + SIZE_SEC_BUFFER + 4 + SIZE_OS_VERSION;
+ CHECK_SIZE(krb5_storage_read(in, type3->mic, sizeof(type3->mic)), sizeof(type3->mic));
+ } else
+ type3->mic_offset = 0;
+ CHECK(ret_buf(in, &lm, &type3->lm), 0);
+ CHECK(ret_buf(in, &ntlm, &type3->ntlm), 0);
+ CHECK(ret_sec_string(in, ucs2, &target, &type3->targetname), 0);
+ CHECK(ret_sec_string(in, ucs2, &username, &type3->username), 0);
+ CHECK(ret_sec_string(in, ucs2, &ws, &type3->ws), 0);
+ if (sessionkey.offset)
+ CHECK(ret_buf(in, &sessionkey, &type3->sessionkey), 0);
+
+out:
+ if (in)
+ krb5_storage_free(in);
+ if (ret)
+ heim_ntlm_free_type3(type3);
+
+ return ret;
+}
+
+/**
+ * Encodes an ntlm_type3 message.
+ *
+ * @param type3 the ntlm_type3 message to encode.
+ * @param data is the return buffer with the encoded message, should be
+ * @param[out] mic_offset offset of message integrity code
+ * freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_encode_type3(const struct ntlm_type3 *type3, struct ntlm_buf *data, size_t *mic_offset)
+{
+ struct sec_buffer lm, ntlm, target, username, sessionkey, ws;
+ krb5_error_code ret;
+ krb5_storage *out = NULL;
+ uint32_t base;
+ int ucs2 = 0;
+
+ memset(&lm, 0, sizeof(lm));
+ memset(&ntlm, 0, sizeof(ntlm));
+ memset(&target, 0, sizeof(target));
+ memset(&username, 0, sizeof(username));
+ memset(&ws, 0, sizeof(ws));
+ memset(&sessionkey, 0, sizeof(sessionkey));
+
+ base = 52;
+
+ base += 8; /* sessionkey sec buf */
+ base += 4; /* flags */
+ if (type3->flags & NTLM_NEG_VERSION)
+ base += SIZE_OS_VERSION; /* os flags */
+
+ if (mic_offset) {
+ *mic_offset = base;
+ base += 16;
+ }
+
+ if (type3->flags & NTLM_NEG_UNICODE)
+ ucs2 = 1;
+
+ target.offset = base;
+ target.length = len_string(ucs2, type3->targetname);
+ target.allocated = target.length;
+
+ username.offset = target.offset + target.allocated;
+ username.length = len_string(ucs2, type3->username);
+ username.allocated = username.length;
+
+ ws.offset = username.offset + username.allocated;
+ ws.length = len_string(ucs2, type3->ws);
+ ws.allocated = ws.length;
+
+ lm.offset = ws.offset + ws.allocated;
+ lm.length = type3->lm.length;
+ lm.allocated = type3->lm.length;
+
+ ntlm.offset = lm.offset + lm.allocated;
+ ntlm.length = type3->ntlm.length;
+ ntlm.allocated = ntlm.length;
+
+ sessionkey.offset = ntlm.offset + ntlm.allocated;
+ sessionkey.length = type3->sessionkey.length;
+ sessionkey.allocated = type3->sessionkey.length;
+
+ out = krb5_storage_emem();
+ if (out == NULL)
+ return ENOMEM;
+
+ krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
+ CHECK_SIZE(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
+ sizeof(ntlmsigature));
+ CHECK(krb5_store_uint32(out, 3), 0);
+
+ CHECK(store_sec_buffer(out, &lm), 0);
+ CHECK(store_sec_buffer(out, &ntlm), 0);
+ CHECK(store_sec_buffer(out, &target), 0);
+ CHECK(store_sec_buffer(out, &username), 0);
+ CHECK(store_sec_buffer(out, &ws), 0);
+ CHECK(store_sec_buffer(out, &sessionkey), 0);
+ CHECK(krb5_store_uint32(out, type3->flags), 0);
+
+ /* os version */
+ if (type3->flags & NTLM_NEG_VERSION) {
+ CHECK(encode_os_version(out), 0);
+ }
+
+ if (mic_offset) {
+ static const uint8_t buf[16] = { 0 };
+ CHECK_SIZE(krb5_storage_write(out, buf, sizeof(buf)), sizeof(buf));
+ }
+
+ CHECK(put_string(out, ucs2, type3->targetname), 0);
+ CHECK(put_string(out, ucs2, type3->username), 0);
+ CHECK(put_string(out, ucs2, type3->ws), 0);
+ CHECK(put_buf(out, &type3->lm), 0);
+ CHECK(put_buf(out, &type3->ntlm), 0);
+ CHECK(put_buf(out, &type3->sessionkey), 0);
+
+ {
+ krb5_data d;
+ ret = krb5_storage_to_data(out, &d);
+ data->data = d.data;
+ data->length = d.length;
+ }
+
+out:
+ krb5_storage_free(out);
+
+ return ret;
+}
+
+
+/*
+ *
+ */
+
+static void
+splitandenc(unsigned char *hash,
+ unsigned char *challenge,
+ unsigned char *answer)
+{
+ EVP_CIPHER_CTX ctx;
+ unsigned char key[8];
+
+ key[0] = hash[0];
+ key[1] = (hash[0] << 7) | (hash[1] >> 1);
+ key[2] = (hash[1] << 6) | (hash[2] >> 2);
+ key[3] = (hash[2] << 5) | (hash[3] >> 3);
+ key[4] = (hash[3] << 4) | (hash[4] >> 4);
+ key[5] = (hash[4] << 3) | (hash[5] >> 5);
+ key[6] = (hash[5] << 2) | (hash[6] >> 6);
+ key[7] = (hash[6] << 1);
+
+ EVP_CIPHER_CTX_init(&ctx);
+
+ EVP_CipherInit_ex(&ctx, EVP_des_cbc(), NULL, key, NULL, 1);
+ EVP_Cipher(&ctx, answer, challenge, 8);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ memset_s(key, sizeof(key), 0, sizeof(key));
+}
+
+/**
+ * Calculate the NTLM key, the password is assumed to be in UTF8.
+ *
+ * @param password password to calcute the key for.
+ * @param key calcuted key, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_nt_key(const char *password, struct ntlm_buf *key)
+{
+ struct ntlm_buf buf;
+ EVP_MD_CTX *m;
+ int ret;
+
+ key->data = malloc(MD4_DIGEST_LENGTH);
+ if (key->data == NULL)
+ return ENOMEM;
+ key->length = MD4_DIGEST_LENGTH;
+
+ ret = ascii2ucs2le(password, 0, &buf);
+ if (ret) {
+ heim_ntlm_free_buf(key);
+ return ret;
+ }
+
+ m = EVP_MD_CTX_create();
+ if (m == NULL) {
+ heim_ntlm_free_buf(key);
+ heim_ntlm_free_buf(&buf);
+ return ENOMEM;
+ }
+
+ EVP_DigestInit_ex(m, EVP_md4(), NULL);
+ EVP_DigestUpdate(m, buf.data, buf.length);
+ EVP_DigestFinal_ex(m, key->data, NULL);
+ EVP_MD_CTX_destroy(m);
+
+ heim_ntlm_free_buf(&buf);
+ return 0;
+}
+
+/**
+ * Calculate NTLMv1 response hash
+ *
+ * @param key the ntlm v1 key
+ * @param len length of key
+ * @param challenge sent by the server
+ * @param answer calculated answer, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_calculate_ntlm1(void *key, size_t len,
+ unsigned char challenge[8],
+ struct ntlm_buf *answer)
+{
+ unsigned char res[21];
+
+ if (len != MD4_DIGEST_LENGTH)
+ return HNTLM_ERR_INVALID_LENGTH;
+
+ memcpy(res, key, len);
+ memset(&res[MD4_DIGEST_LENGTH], 0, sizeof(res) - MD4_DIGEST_LENGTH);
+
+ answer->data = malloc(24);
+ if (answer->data == NULL)
+ return ENOMEM;
+ answer->length = 24;
+
+ splitandenc(&res[0], challenge, ((unsigned char *)answer->data) + 0);
+ splitandenc(&res[7], challenge, ((unsigned char *)answer->data) + 8);
+ splitandenc(&res[14], challenge, ((unsigned char *)answer->data) + 16);
+
+ return 0;
+}
+
+int
+heim_ntlm_v1_base_session(void *key, size_t len,
+ struct ntlm_buf *session)
+{
+ EVP_MD_CTX *m;
+
+ session->length = MD4_DIGEST_LENGTH;
+ session->data = malloc(session->length);
+ if (session->data == NULL) {
+ session->length = 0;
+ return ENOMEM;
+ }
+
+ m = EVP_MD_CTX_create();
+ if (m == NULL) {
+ heim_ntlm_free_buf(session);
+ return ENOMEM;
+ }
+ EVP_DigestInit_ex(m, EVP_md4(), NULL);
+ EVP_DigestUpdate(m, key, len);
+ EVP_DigestFinal_ex(m, session->data, NULL);
+ EVP_MD_CTX_destroy(m);
+
+ return 0;
+}
+
+int
+heim_ntlm_v2_base_session(void *key, size_t len,
+ struct ntlm_buf *ntlmResponse,
+ struct ntlm_buf *session)
+{
+ unsigned int hmaclen;
+ HMAC_CTX c;
+
+ if (ntlmResponse->length <= 16)
+ return HNTLM_ERR_INVALID_LENGTH;
+
+ session->data = malloc(16);
+ if (session->data == NULL)
+ return ENOMEM;
+ session->length = 16;
+
+ /* Note: key is the NTLMv2 key */
+ HMAC_CTX_init(&c);
+ if (HMAC_Init_ex(&c, key, len, EVP_md5(), NULL) == 0) {
+ HMAC_CTX_cleanup(&c);
+ return ENOMEM;
+ }
+ HMAC_Update(&c, ntlmResponse->data, 16);
+ HMAC_Final(&c, session->data, &hmaclen);
+ HMAC_CTX_cleanup(&c);
+
+ return 0;
+}
+
+
+int
+heim_ntlm_keyex_wrap(struct ntlm_buf *base_session,
+ struct ntlm_buf *session,
+ struct ntlm_buf *encryptedSession)
+{
+ EVP_CIPHER_CTX c;
+ int ret;
+
+ if (base_session->length != MD4_DIGEST_LENGTH)
+ return HNTLM_ERR_INVALID_LENGTH;
+
+ session->length = MD4_DIGEST_LENGTH;
+ session->data = malloc(session->length);
+ if (session->data == NULL) {
+ session->length = 0;
+ return ENOMEM;
+ }
+ encryptedSession->length = MD4_DIGEST_LENGTH;
+ encryptedSession->data = malloc(encryptedSession->length);
+ if (encryptedSession->data == NULL) {
+ heim_ntlm_free_buf(session);
+ encryptedSession->length = 0;
+ return ENOMEM;
+ }
+
+ EVP_CIPHER_CTX_init(&c);
+
+ ret = EVP_CipherInit_ex(&c, EVP_rc4(), NULL, base_session->data, NULL, 1);
+ if (ret != 1) {
+ EVP_CIPHER_CTX_cleanup(&c);
+ heim_ntlm_free_buf(encryptedSession);
+ heim_ntlm_free_buf(session);
+ return HNTLM_ERR_CRYPTO;
+ }
+
+ if (RAND_bytes(session->data, session->length) != 1) {
+ EVP_CIPHER_CTX_cleanup(&c);
+ heim_ntlm_free_buf(encryptedSession);
+ heim_ntlm_free_buf(session);
+ return HNTLM_ERR_RAND;
+ }
+
+ EVP_Cipher(&c, encryptedSession->data, session->data, encryptedSession->length);
+ EVP_CIPHER_CTX_cleanup(&c);
+
+ return 0;
+
+
+
+}
+
+/**
+ * Generates an NTLMv1 session random with assosited session master key.
+ *
+ * @param key the ntlm v1 key
+ * @param len length of key
+ * @param session generated session nonce, should be freed with heim_ntlm_free_buf().
+ * @param master calculated session master key, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_build_ntlm1_master(void *key, size_t len,
+ struct ntlm_buf *session,
+ struct ntlm_buf *master)
+{
+ struct ntlm_buf sess;
+ int ret;
+
+ ret = heim_ntlm_v1_base_session(key, len, &sess);
+ if (ret)
+ return ret;
+
+ ret = heim_ntlm_keyex_wrap(&sess, session, master);
+ heim_ntlm_free_buf(&sess);
+
+ return ret;
+}
+
+/**
+ * Generates an NTLMv2 session random with associated session master key.
+ *
+ * @param key the NTLMv2 key
+ * @param len length of key
+ * @param blob the NTLMv2 "blob"
+ * @param session generated session nonce, should be freed with heim_ntlm_free_buf().
+ * @param master calculated session master key, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+
+int
+heim_ntlm_build_ntlm2_master(void *key, size_t len,
+ struct ntlm_buf *blob,
+ struct ntlm_buf *session,
+ struct ntlm_buf *master)
+{
+ struct ntlm_buf sess;
+ int ret;
+
+ ret = heim_ntlm_v2_base_session(key, len, blob, &sess);
+ if (ret)
+ return ret;
+
+ ret = heim_ntlm_keyex_wrap(&sess, session, master);
+ heim_ntlm_free_buf(&sess);
+
+ return ret;
+}
+
+/**
+ * Given a key and encrypted session, unwrap the session key
+ *
+ * @param baseKey the sessionBaseKey
+ * @param encryptedSession encrypted session, type3.session field.
+ * @param session generated session nonce, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_keyex_unwrap(struct ntlm_buf *baseKey,
+ struct ntlm_buf *encryptedSession,
+ struct ntlm_buf *session)
+{
+ EVP_CIPHER_CTX c;
+
+ memset(session, 0, sizeof(*session));
+
+ if (encryptedSession->length != MD4_DIGEST_LENGTH)
+ return HNTLM_ERR_INVALID_LENGTH;
+ if (baseKey->length != MD4_DIGEST_LENGTH)
+ return HNTLM_ERR_INVALID_LENGTH;
+
+ session->length = MD4_DIGEST_LENGTH;
+ session->data = malloc(session->length);
+ if (session->data == NULL) {
+ session->length = 0;
+ return ENOMEM;
+ }
+ EVP_CIPHER_CTX_init(&c);
+
+ if (EVP_CipherInit_ex(&c, EVP_rc4(), NULL, baseKey->data, NULL, 0) != 1) {
+ EVP_CIPHER_CTX_cleanup(&c);
+ heim_ntlm_free_buf(session);
+ return HNTLM_ERR_CRYPTO;
+ }
+
+ EVP_Cipher(&c, session->data, encryptedSession->data, session->length);
+ EVP_CIPHER_CTX_cleanup(&c);
+
+ return 0;
+}
+
+
+/**
+ * Generates an NTLMv2 session key.
+ *
+ * @param key the ntlm key
+ * @param len length of key
+ * @param username name of the user, as sent in the message, assumed to be in UTF8.
+ * @param target the name of the target, assumed to be in UTF8.
+ * @param upper_case_target upper case the target, should not be used only for legacy systems
+ * @param ntlmv2 the ntlmv2 session key
+ *
+ * @return 0 on success, or an error code on failure.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_ntlmv2_key(const void *key, size_t len,
+ const char *username,
+ const char *target,
+ int upper_case_target,
+ unsigned char ntlmv2[16])
+{
+ int ret;
+ unsigned int hmaclen;
+ struct ntlm_buf buf;
+ HMAC_CTX c;
+
+ HMAC_CTX_init(&c);
+ if (HMAC_Init_ex(&c, key, len, EVP_md5(), NULL) == 0) {
+ ret = ENOMEM;
+ goto out;
+ }
+ /* uppercase username and turn it into ucs2-le */
+ ret = ascii2ucs2le(username, 1, &buf);
+ if (ret)
+ goto out;
+ HMAC_Update(&c, buf.data, buf.length);
+ free(buf.data);
+ /* turn target into ucs2-le */
+ ret = ascii2ucs2le(target, upper_case_target, &buf);
+ if (ret)
+ goto out;
+ HMAC_Update(&c, buf.data, buf.length);
+ free(buf.data);
+ HMAC_Final(&c, ntlmv2, &hmaclen);
+ out:
+ HMAC_CTX_cleanup(&c);
+ memset(&c, 0, sizeof(c));
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+#define NTTIME_EPOCH 0x019DB1DED53E8000LL
+
+uint64_t
+heim_ntlm_unix2ts_time(time_t unix_time)
+{
+ long long wt;
+ wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
+ return wt;
+}
+
+time_t
+heim_ntlm_ts2unixtime(uint64_t t)
+{
+ t = ((t - (uint64_t)NTTIME_EPOCH) / (uint64_t)10000000);
+ if (t > (((uint64_t)(time_t)(~(uint64_t)0)) >> 1))
+ return 0;
+ return (time_t)t;
+}
+
+/**
+ * Calculate LMv2 response
+ *
+ * @param key the ntlm key
+ * @param len length of key
+ * @param username name of the user, as sent in the message, assumed to be in UTF8.
+ * @param target the name of the target, assumed to be in UTF8.
+ * @param serverchallenge challenge as sent by the server in the type2 message.
+ * @param ntlmv2 calculated session key
+ * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_calculate_lm2(const void *key, size_t len,
+ const char *username,
+ const char *target,
+ const unsigned char serverchallenge[8],
+ unsigned char ntlmv2[16],
+ struct ntlm_buf *answer)
+{
+ unsigned char clientchallenge[8];
+ krb5_error_code ret;
+
+ if (RAND_bytes(clientchallenge, sizeof(clientchallenge)) != 1)
+ return HNTLM_ERR_RAND;
+
+ /* calculate ntlmv2 key */
+
+ heim_ntlm_ntlmv2_key(key, len, username, target, 0, ntlmv2);
+
+ answer->data = malloc(24);
+ if (answer->data == NULL)
+ return ENOMEM;
+ answer->length = 24;
+
+ ret = heim_ntlm_derive_ntlm2_sess(ntlmv2, clientchallenge, 8,
+ serverchallenge, answer->data);
+ if (ret == 0)
+ memcpy(((unsigned char *)answer->data) + 16, clientchallenge, 8);
+
+ return ret;
+}
+
+
+/**
+ * Calculate NTLMv2 response
+ *
+ * @param key the ntlm key
+ * @param len length of key
+ * @param username name of the user, as sent in the message, assumed to be in UTF8.
+ * @param target the name of the target, assumed to be in UTF8.
+ * @param serverchallenge challenge as sent by the server in the type2 message.
+ * @param infotarget infotarget as sent by the server in the type2 message.
+ * @param ntlmv2 calculated session key
+ * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_calculate_ntlm2(const void *key, size_t len,
+ const char *username,
+ const char *target,
+ const unsigned char serverchallenge[8],
+ const struct ntlm_buf *infotarget,
+ unsigned char ntlmv2[16],
+ struct ntlm_buf *answer)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ unsigned char ntlmv2answer[16];
+ krb5_storage *sp;
+ unsigned char clientchallenge[8];
+ uint64_t t;
+
+ t = heim_ntlm_unix2ts_time(time(NULL));
+
+ if (RAND_bytes(clientchallenge, sizeof(clientchallenge)) != 1)
+ return HNTLM_ERR_RAND;
+
+ /* calculate ntlmv2 key */
+
+ heim_ntlm_ntlmv2_key(key, len, username, target, 0, ntlmv2);
+
+ /* calculate and build ntlmv2 answer */
+
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return ENOMEM;
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(krb5_store_uint32(sp, 0x00000101), 0);
+ CHECK(krb5_store_uint32(sp, 0), 0);
+ /* timestamp le 64 bit ts */
+ CHECK(krb5_store_uint32(sp, t & 0xffffffff), 0);
+ CHECK(krb5_store_uint32(sp, t >> 32), 0);
+
+ CHECK_SIZE(krb5_storage_write(sp, clientchallenge, 8), 8);
+
+ CHECK(krb5_store_uint32(sp, 0), 0); /* Z(4) */
+ CHECK_SIZE(krb5_storage_write(sp, infotarget->data, infotarget->length),
+ infotarget->length);
+
+ /*
+ * These last 4 bytes(Z(4)) are not documented by MicroSoft and
+ * SnowLeopard doesn't send them, Lion expected them to be there,
+ * so we have to continue to send them. That is ok, since everyone
+ * else (except Snow) seems to do that too.
+ */
+ CHECK(krb5_store_uint32(sp, 0), 0); /* Z(4) */
+
+ CHECK(krb5_storage_to_data(sp, &data), 0);
+ krb5_storage_free(sp);
+ sp = NULL;
+
+ ret = heim_ntlm_derive_ntlm2_sess(ntlmv2, data.data, data.length,
+ serverchallenge, ntlmv2answer);
+ if (ret)
+ return ret;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_data_free(&data);
+ return ENOMEM;
+ }
+
+ CHECK_SIZE(krb5_storage_write(sp, ntlmv2answer, 16), 16);
+ CHECK_SIZE(krb5_storage_write(sp, data.data, data.length), data.length);
+ krb5_data_free(&data);
+
+ CHECK(krb5_storage_to_data(sp, &data), 0);
+ krb5_storage_free(sp);
+ sp = NULL;
+
+ answer->data = data.data;
+ answer->length = data.length;
+
+ return 0;
+out:
+ if (sp)
+ krb5_storage_free(sp);
+ return ret;
+}
+
+static const int authtimediff = 3600 * 2; /* 2 hours */
+
+static int
+verify_ntlm2(const void *key, size_t len,
+ const char *username,
+ const char *target,
+ int upper_case_target,
+ time_t now,
+ const unsigned char serverchallenge[8],
+ const struct ntlm_buf *answer,
+ struct ntlm_buf *infotarget,
+ unsigned char ntlmv2[16])
+{
+ krb5_error_code ret;
+ unsigned char clientanswer[16];
+ unsigned char clientnonce[8];
+ unsigned char serveranswer[16];
+ krb5_storage *sp;
+ uint64_t t;
+ time_t authtime;
+ uint32_t temp;
+
+ infotarget->length = 0;
+ infotarget->data = NULL;
+
+ if (answer->length < 16)
+ return HNTLM_ERR_INVALID_LENGTH;
+
+ if (now == 0)
+ now = time(NULL);
+
+ /* calculate ntlmv2 key */
+
+ heim_ntlm_ntlmv2_key(key, len, username, target, upper_case_target, ntlmv2);
+
+ /* calculate and build ntlmv2 answer */
+
+ sp = krb5_storage_from_readonly_mem(answer->data, answer->length);
+ if (sp == NULL)
+ return ENOMEM;
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK_SIZE(krb5_storage_read(sp, clientanswer, 16), 16);
+
+ CHECK(krb5_ret_uint32(sp, &temp), 0);
+ CHECK(temp, 0x00000101);
+ CHECK(krb5_ret_uint32(sp, &temp), 0);
+ CHECK(temp, 0);
+ /* timestamp le 64 bit ts */
+ CHECK(krb5_ret_uint32(sp, &temp), 0);
+ t = temp;
+ CHECK(krb5_ret_uint32(sp, &temp), 0);
+ t |= ((uint64_t)temp)<< 32;
+
+ authtime = heim_ntlm_ts2unixtime(t);
+
+ if (labs((int)(authtime - now)) > authtimediff) {
+ ret = HNTLM_ERR_TIME_SKEW;
+ goto out;
+ }
+
+ /* client challenge */
+ CHECK_SIZE(krb5_storage_read(sp, clientnonce, 8), 8);
+
+ CHECK(krb5_ret_uint32(sp, &temp), 0); /* Z(4) */
+
+ /* let pick up targetinfo */
+ infotarget->length = answer->length - (size_t)krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (infotarget->length < 4) {
+ ret = HNTLM_ERR_INVALID_LENGTH;
+ goto out;
+ }
+ infotarget->data = malloc(infotarget->length);
+ if (infotarget->data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ CHECK_SIZE(krb5_storage_read(sp, infotarget->data, infotarget->length),
+ infotarget->length);
+
+ krb5_storage_free(sp);
+ sp = NULL;
+
+ if (answer->length < 16) {
+ ret = HNTLM_ERR_INVALID_LENGTH;
+ goto out;
+ }
+
+ ret = heim_ntlm_derive_ntlm2_sess(ntlmv2,
+ ((unsigned char *)answer->data) + 16,
+ answer->length - 16,
+ serverchallenge,
+ serveranswer);
+ if (ret)
+ goto out;
+
+ if (ct_memcmp(serveranswer, clientanswer, 16) != 0) {
+ heim_ntlm_free_buf(infotarget);
+ return HNTLM_ERR_AUTH;
+ }
+
+ return 0;
+out:
+ heim_ntlm_free_buf(infotarget);
+ if (sp)
+ krb5_storage_free(sp);
+ return ret;
+}
+
+/**
+ * Verify NTLMv2 response.
+ *
+ * @param key the ntlm key
+ * @param len length of key
+ * @param username name of the user, as sent in the message, assumed to be in UTF8.
+ * @param target the name of the target, assumed to be in UTF8.
+ * @param now the time now (0 if the library should pick it up itself)
+ * @param serverchallenge challenge as sent by the server in the type2 message.
+ * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
+ * @param infotarget infotarget as sent by the server in the type2 message.
+ * @param ntlmv2 calculated session key
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_verify_ntlm2(const void *key, size_t len,
+ const char *username,
+ const char *target,
+ time_t now,
+ const unsigned char serverchallenge[8],
+ const struct ntlm_buf *answer,
+ struct ntlm_buf *infotarget,
+ unsigned char ntlmv2[16])
+{
+ int ret;
+
+ /**
+ * First check with the domain as the client passed it to the function.
+ */
+
+ ret = verify_ntlm2(key, len, username, target, 0, now,
+ serverchallenge, answer, infotarget, ntlmv2);
+
+ /**
+ * Second check with domain uppercased.
+ */
+
+ if (ret)
+ ret = verify_ntlm2(key, len, username, target, 1, now,
+ serverchallenge, answer, infotarget, ntlmv2);
+
+ /**
+ * Third check with empty domain.
+ */
+ if (ret)
+ ret = verify_ntlm2(key, len, username, "", 0, now,
+ serverchallenge, answer, infotarget, ntlmv2);
+ return ret;
+}
+
+/*
+ * Calculate the NTLM2 Session Response
+ *
+ * @param clnt_nonce client nonce
+ * @param svr_chal server challage
+ * @param ntlm2_hash ntlm hash
+ * @param lm The LM response, should be freed with heim_ntlm_free_buf().
+ * @param ntlm The NTLM response, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_calculate_ntlm2_sess(const unsigned char clnt_nonce[8],
+ const unsigned char svr_chal[8],
+ const unsigned char ntlm_hash[16],
+ struct ntlm_buf *lm,
+ struct ntlm_buf *ntlm)
+{
+ unsigned char ntlm2_sess_hash[8];
+ unsigned char res[21], *resp;
+ int code;
+
+ code = heim_ntlm_calculate_ntlm2_sess_hash(clnt_nonce, svr_chal,
+ ntlm2_sess_hash);
+ if (code) {
+ return code;
+ }
+
+ lm->data = malloc(24);
+ if (lm->data == NULL) {
+ return ENOMEM;
+ }
+ lm->length = 24;
+
+ ntlm->data = malloc(24);
+ if (ntlm->data == NULL) {
+ free(lm->data);
+ lm->data = NULL;
+ return ENOMEM;
+ }
+ ntlm->length = 24;
+
+ /* first setup the lm resp */
+ memset(lm->data, 0, 24);
+ memcpy(lm->data, clnt_nonce, 8);
+
+ memset(res, 0, sizeof(res));
+ memcpy(res, ntlm_hash, 16);
+
+ resp = ntlm->data;
+ splitandenc(&res[0], ntlm2_sess_hash, resp + 0);
+ splitandenc(&res[7], ntlm2_sess_hash, resp + 8);
+ splitandenc(&res[14], ntlm2_sess_hash, resp + 16);
+
+ return 0;
+}
+
+
+/*
+ * Calculate the NTLM2 Session "Verifier"
+ *
+ * @param clnt_nonce client nonce
+ * @param svr_chal server challage
+ * @param hash The NTLM session verifier
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_calculate_ntlm2_sess_hash(const unsigned char clnt_nonce[8],
+ const unsigned char svr_chal[8],
+ unsigned char verifier[8])
+{
+ unsigned char ntlm2_sess_hash[MD5_DIGEST_LENGTH];
+ EVP_MD_CTX *m;
+
+ m = EVP_MD_CTX_create();
+ if (m == NULL)
+ return ENOMEM;
+
+ EVP_DigestInit_ex(m, EVP_md5(), NULL);
+ EVP_DigestUpdate(m, svr_chal, 8); /* session nonce part 1 */
+ EVP_DigestUpdate(m, clnt_nonce, 8); /* session nonce part 2 */
+ EVP_DigestFinal_ex(m, ntlm2_sess_hash, NULL); /* will only use first 8 bytes */
+ EVP_MD_CTX_destroy(m);
+
+ memcpy(verifier, ntlm2_sess_hash, 8);
+
+ return 0;
+}
+
+
+/*
+ * Derive a NTLM2 session key
+ *
+ * @param sessionkey session key from domain controller
+ * @param clnt_nonce client nonce
+ * @param svr_chal server challenge
+ * @param derivedkey salted session key
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_derive_ntlm2_sess(const unsigned char sessionkey[16],
+ const unsigned char *clnt_nonce, size_t clnt_nonce_length,
+ const unsigned char svr_chal[8],
+ unsigned char derivedkey[16])
+{
+ unsigned int hmaclen;
+ HMAC_CTX c;
+
+ /* HMAC(Ksession, serverchallenge || clientchallenge) */
+ HMAC_CTX_init(&c);
+ if (HMAC_Init_ex(&c, sessionkey, 16, EVP_md5(), NULL) == 0) {
+ HMAC_CTX_cleanup(&c);
+ return ENOMEM;
+ }
+ HMAC_Update(&c, svr_chal, 8);
+ HMAC_Update(&c, clnt_nonce, clnt_nonce_length);
+ HMAC_Final(&c, derivedkey, &hmaclen);
+ HMAC_CTX_cleanup(&c);
+ memset(&c, 0, sizeof(c));
+ return 0;
+}
diff --git a/third_party/heimdal/lib/ntlm/ntlm_err.et b/third_party/heimdal/lib/ntlm/ntlm_err.et
new file mode 100644
index 0000000..fd39aa3
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/ntlm_err.et
@@ -0,0 +1,60 @@
+#
+# Error messages for the ntlm library
+#
+# This might look like a com_err file, but is not
+#
+
+error_table ntlm
+
+prefix HNTLM_ERR
+error_code DECODE, "Failed to decode packet"
+error_code INVALID_LENGTH, "Input length invalid"
+error_code CRYPTO, "Failed crypto primitive"
+error_code RAND, "Random generator failed"
+error_code AUTH, "NTLM authentication failed"
+error_code TIME_SKEW, "Client time skewed to server"
+error_code OEM, "Client set OEM string"
+error_code MISSING_NAME_SEPARATOR, "missing @ or \\\\ in name"
+error_code MISSING_BUFFER, "missing expected buffer"
+error_code INVALID_APOP, "Invalid APOP response"
+error_code INVALID_CRAM_MD5, "Invalid CRAM-MD5 response"
+error_code INVALID_DIGEST_MD5, "Invalid DIGEST-MD5 response"
+error_code INVALID_DIGEST_MD5_RSPAUTH, "Invalid DIGEST-MD5 rspauth"
+error_code INVALID_CHANNEL_BINDINGS, "Invalid channel bindings"
+error_code INVALID_MIC, "Invalid MIC"
+error_code INVALID_SESSIONKEY, "Invalid session key"
+
+
+#
+# NTLM/GSS error codes
+#
+index 64
+error_code NOT_CONFIGURED, "NTLM not configured"
+
+error_code INVALID_CHALLANGE, "Invalid client challenge"
+error_code INVALID_LMv1_RESPONSE, "Invalid client LMv1 response"
+error_code INVALID_NT_RESPONSE, "Invalid client NT response"
+error_code INVALID_LMv2_RESPONSE, "Invalid client LMv2 response"
+error_code INVALID_NTv1_RESPONSE, "Invalid client NTv1 response"
+error_code INVALID_NTv2_RESPONSE, "Invalid client NTv2 response"
+error_code INVALID_NTv1_ANSWER, "Invalid client NTv1 answer"
+error_code INVALID_NTv2_ANSWER, "Invalid client NTv2 answer"
+error_code INVALID_SESSION_KEY, "Invalid session key"
+
+error_code INVALID_NO_GUEST, "Invalid guest login request"
+
+error_code NO_NETR_CONFIGURED, "No NETR configured"
+
+
+#
+# Scram errors
+#
+prefix HSCRAM_ERR
+index 128
+
+error_code INVALID_MESSAGE, "Invalid SCRAM message"
+error_code INVALID_PROOF, "Invalid SCRAM proof"
+error_code INVALID_ROLE, "Invalid SCRAM role"
+
+
+end
diff --git a/third_party/heimdal/lib/ntlm/test_commonauth.c b/third_party/heimdal/lib/ntlm/test_commonauth.c
new file mode 100644
index 0000000..89b8c7e
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/test_commonauth.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <roken.h>
+#include <err.h>
+#include "heim-auth.h"
+
+static int
+test_sasl_digest_md5(void)
+{
+ heim_digest_t ctx;
+ const char *user, *challenge, *resp;
+ char *r;
+
+ if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_AUTO)) == NULL)
+ abort();
+
+ if (heim_digest_parse_challenge(ctx, "realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",algorithm=md5-sess,charset=utf-8"))
+ abort();
+
+ /* check that server detects changing QOP */
+ if (!heim_digest_parse_response(ctx, "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth-int"))
+ errx(1, "don't detect changing qop");
+
+ /* should pass */
+ if (heim_digest_parse_response(ctx, "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth"))
+ abort();
+
+ if ((user = heim_digest_get_key(ctx, "username")) == NULL)
+ abort();
+ if (strcmp(user, "chris") != 0)
+ abort();
+
+ /*
+ * check password
+ */
+
+ heim_digest_set_key(ctx, "password", "secret");
+
+ if (heim_digest_verify(ctx, &r))
+ abort();
+
+ if (strcmp(r, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0)
+ abort();
+
+ free(r);
+
+ /*
+ * Also check userhash
+ */
+
+ r = heim_digest_userhash("chris", "elwood.innosoft.com", "secret");
+ if (strcmp(r, "eb5a750053e4d2c34aa84bbc9b0b6ee7") != 0)
+ abort();
+
+ heim_digest_set_key(ctx, "userhash", r);
+ free(r);
+
+ if (heim_digest_verify(ctx, &r))
+ abort();
+
+ if (strcmp(r, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0)
+ abort();
+
+ free(r);
+
+ /* check that it failes */
+
+ heim_digest_set_key(ctx, "username", "notright");
+ heim_digest_set_key(ctx, "password", "secret");
+
+ if (heim_digest_verify(ctx, &r) == 0)
+ abort();
+
+ if ((user = heim_digest_get_key(ctx, "username")) == NULL)
+ abort();
+ if (strcmp(user, "notright") != 0)
+ abort();
+
+
+ /* Done */
+
+ heim_digest_release(ctx);
+
+
+ /*
+ * Check heim_digest_generate_challenge()
+ */
+
+ if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL)
+ abort();
+
+ heim_digest_set_key(ctx, "serverRealm", "elwood.innosoft.com");
+ heim_digest_set_key(ctx, "serverNonce", "OA6MG9tEQGm2hh");
+ heim_digest_set_key(ctx, "serverQOP", "auth,auth-int");
+
+ challenge = heim_digest_generate_challenge(ctx);
+ if (challenge == NULL)
+ abort();
+
+ if (heim_digest_parse_challenge(ctx, challenge))
+ abort();
+
+ /* check that server detects changing QOP */
+ if (!heim_digest_parse_response(ctx, "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth-conf"))
+ abort();
+
+ if (heim_digest_parse_response(ctx, "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth"))
+ abort();
+
+ heim_digest_set_key(ctx, "password", "secret");
+
+ if (heim_digest_verify(ctx, &r))
+ abort();
+
+ if (strcmp(r, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0)
+ abort();
+
+ free(r);
+
+ heim_digest_release(ctx);
+
+ /*
+ * Validate heim_digest_service_response()
+ */
+
+ if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL)
+ abort();
+
+ heim_digest_set_key(ctx, "clientNonce", "OA6MHXh6VqTrRk");
+ heim_digest_set_key(ctx, "clientQOP", "auth");
+ heim_digest_set_key(ctx, "clientNC", "00000001");
+ heim_digest_set_key(ctx, "serverNonce", "OA6MG9tEQGm2hh");
+ heim_digest_set_key(ctx, "clientURI", "imap/elwood.innosoft.com");
+ heim_digest_set_key(ctx, "serverRealm", "elwood.innosoft.com");
+ heim_digest_set_key(ctx, "serverNonce", "OA6MG9tEQGm2hh");
+ heim_digest_set_key(ctx, "H(A1)", "a2549853149b0536f01f0b850c643c57");
+
+ resp = heim_digest_server_response(ctx);
+
+ if (resp == NULL || strcmp(resp, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0)
+ abort();
+
+ heim_digest_release(ctx);
+
+ if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL)
+ abort();
+
+ heim_digest_set_key(ctx, "clientNonce", "OA6MHXh6VqTrRk");
+ heim_digest_set_key(ctx, "clientQOP", "auth");
+ heim_digest_set_key(ctx, "clientNC", "00000001");
+ heim_digest_set_key(ctx, "serverNonce", "OA6MG9tEQGm2hh");
+ heim_digest_set_key(ctx, "clientURI", "imap/elwood.innosoft.com");
+ heim_digest_set_key(ctx, "serverRealm", "elwood.innosoft.com");
+ heim_digest_set_key(ctx, "serverNonce", "OA6MG9tEQGm2hh");
+ heim_digest_set_key(ctx, "password", "secret");
+ heim_digest_set_key(ctx, "username", "chris");
+
+ resp = heim_digest_server_response(ctx);
+
+ if (resp == NULL || strcmp(resp, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0)
+ abort();
+
+ heim_digest_release(ctx);
+
+ return 0;
+}
+
+static int
+test_http_digest_md5(void)
+{
+ heim_digest_t ctx, ctx2;
+ const char *user, *chal, *resp;
+ char *serverresp, *serverresp2;
+
+ if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_AUTO)) == NULL)
+ abort();
+
+ if (heim_digest_parse_challenge(ctx, "realm=\"testrealm@host.com\","
+ "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
+ "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""))
+ abort();
+
+ if (heim_digest_parse_response(ctx, "username=\"Mufasa\","
+ "realm=\"testrealm@host.com\","
+ "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
+ "uri=\"/dir/index.html\","
+ "response=\"1949323746fe6a43ef61f9606e7febea\","
+ "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""))
+ abort();
+
+ if ((user = heim_digest_get_key(ctx, "username")) == NULL)
+ abort();
+ if (strcmp(user, "Mufasa") != 0)
+ abort();
+
+ if ((user = heim_digest_get_key(ctx, "clientUsername")) == NULL)
+ abort();
+ if (strcmp(user, "Mufasa") != 0)
+ abort();
+
+ heim_digest_set_key(ctx, "password", "CircleOfLife");
+
+ if (heim_digest_verify(ctx, NULL))
+ abort();
+
+ /* Verify failure */
+
+ heim_digest_set_key(ctx, "username", "Oskar");
+
+ if (heim_digest_verify(ctx, NULL) == 0)
+ abort();
+
+ heim_digest_release(ctx);
+
+ /*
+ * Check myself
+ */
+
+ /* server */
+ if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL)
+ abort();
+
+ heim_digest_set_key(ctx, "serverRealm", "myrealmhahaha");
+ heim_digest_set_key(ctx, "serverQOP", "auth,auth-int");
+
+ chal = heim_digest_generate_challenge(ctx);
+ if (chal == NULL)
+ abort();
+
+ /* client */
+ if ((ctx2 = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL)
+ abort();
+
+ if (heim_digest_parse_challenge(ctx2, chal))
+ abort();
+
+ heim_digest_set_key(ctx2, "username", "lha");
+ heim_digest_set_key(ctx2, "password", "passw0rd");
+ heim_digest_set_key(ctx2, "uri", "/uri");
+
+ resp = heim_digest_create_response(ctx2, &serverresp);
+ if (resp == NULL)
+ abort();
+
+ /* server */
+ if (heim_digest_parse_response(ctx, resp))
+ abort();
+
+ heim_digest_set_key(ctx, "password", "passw0rd");
+ heim_digest_verify(ctx, &serverresp2);
+
+
+ /* client */
+ if (strcmp(serverresp, serverresp2) != 0)
+ abort();
+
+ heim_digest_release(ctx);
+ heim_digest_release(ctx2);
+
+ /*
+ * check prefix
+ */
+
+ if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_AUTO)) == NULL)
+ abort();
+
+ if (heim_digest_parse_challenge(ctx, "Digest realm=\"testrealm@host.com\","
+ "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
+ "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""))
+ abort();
+
+ heim_digest_release(ctx);
+
+ /*
+ * check prefix
+ */
+
+ if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_AUTO)) == NULL)
+ abort();
+
+ if (heim_digest_parse_challenge(ctx, "Digest realm=\"testrealm@host.com\","
+ "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
+ "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""))
+ abort();
+
+ heim_digest_release(ctx);
+
+ return 0;
+}
+
+static int
+test_cram_md5(void)
+{
+ const char *chal = "<1896.697170952@postoffice.reston.mci.net>";
+ const char *secret = "tanstaaftanstaaf";
+ const char *resp = "b913a602c7eda7a495b4e6e7334d3890";
+ heim_CRAM_MD5_STATE state;
+ heim_cram_md5 ctx;
+ char *t;
+
+ const uint8_t *prestate = (uint8_t *)
+ "\x87\x1E\x24\x10\xB4\x0C\x72\x5D\xA3\x95\x2D\x5B\x8B\xFC\xDD\xE1"
+ "\x29\x90\xCB\xA7\x66\xF6\xB3\x40\xE8\xAC\x48\x2C\xE4\xE3\xA4\x40";
+
+ /*
+ * Test prebuild blobs
+ */
+
+ if (sizeof(state) != 32)
+ abort();
+
+ heim_cram_md5_export("foo", &state);
+
+ if (memcmp(prestate, &state, 32) != 0)
+ abort();
+
+ /*
+ * Check example
+ */
+
+
+ if (heim_cram_md5_verify(chal, secret, resp) != 0)
+ abort();
+
+
+ /*
+ * Do it ourself
+ */
+
+ t = heim_cram_md5_create(chal, secret);
+ if (t == NULL)
+ abort();
+
+ if (strcmp(resp, t) != 0)
+ abort();
+
+ heim_cram_md5_export(secret, &state);
+ /* here you can store the memcpy-ed version of state somewhere else */
+
+ ctx = heim_cram_md5_import(&state, sizeof(state));
+
+ memset(&state, 0, sizeof(state));
+
+ if (heim_cram_md5_verify_ctx(ctx, chal, resp) != 0)
+ abort();
+
+ heim_cram_md5_free(ctx);
+
+ free(t);
+
+ return 0;
+}
+
+static int
+test_apop(void)
+{
+ const char *chal = "<1896.697170952@dbc.mtview.ca.us>";
+ const char *secret = "tanstaaf";
+ const char *resp = "c4c9334bac560ecc979e58001b3e22fb";
+ char *t;
+
+ t = heim_apop_create(chal, secret);
+ if (t == NULL)
+ abort();
+
+ if (strcmp(resp, t) != 0)
+ abort();
+
+ if (heim_apop_verify(chal, secret, resp) != 0)
+ abort();
+
+ free(t);
+
+ return 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int ret = 0;
+
+ ret |= test_sasl_digest_md5();
+ ret |= test_http_digest_md5();
+ ret |= test_cram_md5();
+ ret |= test_apop();
+
+ return ret;
+}
diff --git a/third_party/heimdal/lib/ntlm/test_ntlm.c b/third_party/heimdal/lib/ntlm/test_ntlm.c
new file mode 100644
index 0000000..e7d7237
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/test_ntlm.c
@@ -0,0 +1,617 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <err.h>
+#include <roken.h>
+#include <getarg.h>
+
+#include <krb5-types.h> /* or <inttypes.h> */
+#include <heimntlm.h>
+
+static int dumpdata_flag;
+
+static int
+test_parse(void)
+{
+ const char *user = "foo",
+ *domain = "mydomain",
+ *hostname = "myhostname",
+ *password = "digestpassword",
+ *target = "DOMAIN";
+ struct ntlm_type1 type1;
+ struct ntlm_type2 type2;
+ struct ntlm_type3 type3;
+ struct ntlm_buf data;
+ int ret, flags;
+
+ memset(&type1, 0, sizeof(type1));
+
+ type1.flags = NTLM_NEG_UNICODE|NTLM_NEG_TARGET|NTLM_NEG_NTLM|NTLM_NEG_VERSION;
+ type1.domain = rk_UNCONST(domain);
+ type1.hostname = rk_UNCONST(hostname);
+ type1.os[0] = 0;
+ type1.os[1] = 0;
+
+ ret = heim_ntlm_encode_type1(&type1, &data);
+ if (ret)
+ errx(1, "heim_ntlm_encode_type1");
+
+ memset(&type1, 0, sizeof(type1));
+
+ if (dumpdata_flag)
+ rk_dumpdata("ntlm-type1", data.data, data.length);
+
+ ret = heim_ntlm_decode_type1(&data, &type1);
+ free(data.data);
+ if (ret)
+ errx(1, "heim_ntlm_encode_type1");
+
+ if (strcmp(type1.domain, domain) != 0)
+ errx(1, "parser got domain wrong: %s", type1.domain);
+
+ if (strcmp(type1.hostname, hostname) != 0)
+ errx(1, "parser got hostname wrong: %s", type1.hostname);
+
+ heim_ntlm_free_type1(&type1);
+
+ /*
+ *
+ */
+
+ memset(&type2, 0, sizeof(type2));
+
+ flags = NTLM_NEG_UNICODE | NTLM_NEG_NTLM | NTLM_TARGET_DOMAIN;
+ type2.flags = flags;
+
+ memset(type2.challenge, 0x7f, sizeof(type2.challenge));
+ type2.targetname = rk_UNCONST(target);
+ type2.targetinfo.data = NULL;
+ type2.targetinfo.length = 0;
+
+ ret = heim_ntlm_encode_type2(&type2, &data);
+ if (ret)
+ errx(1, "heim_ntlm_encode_type2");
+
+ memset(&type2, 0, sizeof(type2));
+
+ if (dumpdata_flag)
+ rk_dumpdata("ntlm-type2", data.data, data.length);
+
+ ret = heim_ntlm_decode_type2(&data, &type2);
+ free(data.data);
+ if (ret)
+ errx(1, "heim_ntlm_decode_type2");
+
+ heim_ntlm_free_type2(&type2);
+
+ /*
+ *
+ */
+
+ memset(&type3, 0, sizeof(type3));
+
+ type3.flags = flags;
+ type3.username = rk_UNCONST(user);
+ type3.targetname = rk_UNCONST(target);
+ type3.ws = rk_UNCONST("workstation");
+
+ {
+ struct ntlm_buf key;
+ heim_ntlm_nt_key(password, &key);
+
+ heim_ntlm_calculate_ntlm1(key.data, key.length,
+ type2.challenge,
+ &type3.ntlm);
+ free(key.data);
+ }
+
+ ret = heim_ntlm_encode_type3(&type3, &data, NULL);
+ if (ret)
+ errx(1, "heim_ntlm_encode_type3");
+
+ free(type3.ntlm.data);
+
+ memset(&type3, 0, sizeof(type3));
+
+ if (dumpdata_flag)
+ rk_dumpdata("ntlm-type3", data.data, data.length);
+
+ ret = heim_ntlm_decode_type3(&data, 1, &type3);
+ free(data.data);
+ if (ret)
+ errx(1, "heim_ntlm_decode_type3");
+
+ if (strcmp("workstation", type3.ws) != 0)
+ errx(1, "type3 ws wrong");
+
+ if (strcmp(target, type3.targetname) != 0)
+ errx(1, "type3 targetname wrong");
+
+ if (strcmp(user, type3.username) != 0)
+ errx(1, "type3 username wrong");
+
+
+ heim_ntlm_free_type3(&type3);
+
+ /*
+ * NTLMv2
+ */
+
+ memset(&type2, 0, sizeof(type2));
+
+ flags = NTLM_NEG_UNICODE | NTLM_NEG_NTLM | NTLM_TARGET_DOMAIN;
+ type2.flags = flags;
+
+ memset(type2.challenge, 0x7f, sizeof(type2.challenge));
+ type2.targetname = rk_UNCONST(target);
+ type2.targetinfo.data = "\x00\x00";
+ type2.targetinfo.length = 2;
+
+ ret = heim_ntlm_encode_type2(&type2, &data);
+ if (ret)
+ errx(1, "heim_ntlm_encode_type2");
+
+ memset(&type2, 0, sizeof(type2));
+
+ ret = heim_ntlm_decode_type2(&data, &type2);
+ free(data.data);
+ if (ret)
+ errx(1, "heim_ntlm_decode_type2");
+
+ heim_ntlm_free_type2(&type2);
+
+ return 0;
+}
+
+static int
+test_keys(void)
+{
+ const char
+ *username = "test",
+ *password = "test1234",
+ *target = "TESTNT";
+ const unsigned char
+ serverchallenge[8] = "\x67\x7f\x1c\x55\x7a\x5e\xe9\x6c";
+ struct ntlm_buf infotarget, infotarget2, answer, key;
+ unsigned char ntlmv2[16], ntlmv2_1[16];
+ int ret;
+
+ infotarget.length = 70;
+ infotarget.data =
+ "\x02\x00\x0c\x00\x54\x00\x45\x00\x53\x00\x54\x00\x4e\x00\x54\x00"
+ "\x01\x00\x0c\x00\x4d\x00\x45\x00\x4d\x00\x42\x00\x45\x00\x52\x00"
+ "\x03\x00\x1e\x00\x6d\x00\x65\x00\x6d\x00\x62\x00\x65\x00\x72\x00"
+ "\x2e\x00\x74\x00\x65\x00\x73\x00\x74\x00\x2e\x00\x63\x00\x6f"
+ "\x00\x6d\x00"
+ "\x00\x00\x00\x00";
+
+ answer.length = 0;
+ answer.data = NULL;
+
+ heim_ntlm_nt_key(password, &key);
+
+ ret = heim_ntlm_calculate_ntlm2(key.data,
+ key.length,
+ username,
+ target,
+ serverchallenge,
+ &infotarget,
+ ntlmv2,
+ &answer);
+ if (ret)
+ errx(1, "heim_ntlm_calculate_ntlm2");
+
+ ret = heim_ntlm_verify_ntlm2(key.data,
+ key.length,
+ username,
+ target,
+ 0,
+ serverchallenge,
+ &answer,
+ &infotarget2,
+ ntlmv2_1);
+ if (ret)
+ errx(1, "heim_ntlm_verify_ntlm2");
+
+ if (memcmp(ntlmv2, ntlmv2_1, sizeof(ntlmv2)) != 0)
+ errx(1, "ntlm master key not same");
+
+ if (infotarget.length > infotarget2.length)
+ errx(1, "infotarget length");
+
+ if (memcmp(infotarget.data, infotarget2.data, infotarget.length) != 0)
+ errx(1, "infotarget not the same");
+
+ free(key.data);
+ free(answer.data);
+ free(infotarget2.data);
+
+ return 0;
+}
+
+static int
+test_ntlm2_session_resp(void)
+{
+ int ret;
+ struct ntlm_buf lm, ntlm;
+
+ const unsigned char lm_resp[24] =
+ "\xff\xff\xff\x00\x11\x22\x33\x44"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00";
+ const unsigned char ntlm2_sess_resp[24] =
+ "\x10\xd5\x50\x83\x2d\x12\xb2\xcc"
+ "\xb7\x9d\x5a\xd1\xf4\xee\xd3\xdf"
+ "\x82\xac\xa4\xc3\x68\x1d\xd4\x55";
+
+ const unsigned char client_nonce[8] =
+ "\xff\xff\xff\x00\x11\x22\x33\x44";
+ const unsigned char server_challenge[8] =
+ "\x01\x23\x45\x67\x89\xab\xcd\xef";
+
+ const unsigned char ntlm_hash[16] =
+ "\xcd\x06\xca\x7c\x7e\x10\xc9\x9b"
+ "\x1d\x33\xb7\x48\x5a\x2e\xd8\x08";
+
+ ret = heim_ntlm_calculate_ntlm2_sess(client_nonce,
+ server_challenge,
+ ntlm_hash,
+ &lm,
+ &ntlm);
+ if (ret)
+ errx(1, "heim_ntlm_calculate_ntlm2_sess_resp");
+
+ if (lm.length != 24 || memcmp(lm.data, lm_resp, 24) != 0)
+ errx(1, "lm_resp wrong");
+ if (ntlm.length != 24 || memcmp(ntlm.data, ntlm2_sess_resp, 24) != 0)
+ errx(1, "ntlm2_sess_resp wrong");
+
+ free(lm.data);
+ free(ntlm.data);
+
+
+ return 0;
+}
+
+static int
+test_ntlmv2(void)
+{
+ unsigned char type3[413] =
+ "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x03\x00\x00\x00\x18\x00\x18\x00"
+ "\x80\x00\x00\x00\x9e\x00\x9e\x00\x98\x00\x00\x00\x14\x00\x14\x00"
+ "\x48\x00\x00\x00\x10\x00\x10\x00\x5c\x00\x00\x00\x14\x00\x14\x00"
+ "\x6c\x00\x00\x00\x00\x00\x00\x00\x36\x01\x00\x00\x05\x82\x88\xa2"
+ "\x05\x01\x28\x0a\x00\x00\x00\x0f\x43\x00\x4f\x00\x4c\x00\x4c\x00"
+ "\x45\x00\x59\x00\x2d\x00\x58\x00\x50\x00\x34\x00\x54\x00\x45\x00"
+ "\x53\x00\x54\x00\x55\x00\x53\x00\x45\x00\x52\x00\x43\x00\x4f\x00"
+ "\x4c\x00\x4c\x00\x45\x00\x59\x00\x2d\x00\x58\x00\x50\x00\x34\x00"
+ "\x2f\x96\xec\x0a\xf7\x9f\x2e\x24\xba\x09\x48\x10\xa5\x22\xd4\xe1"
+ "\x16\x6a\xca\x58\x74\x9a\xc1\x4f\x54\x6f\xee\x40\x96\xce\x43\x6e"
+ "\xdf\x99\x20\x71\x6c\x9a\xda\x2a\x01\x01\x00\x00\x00\x00\x00\x00"
+ "\x8d\xc0\x57\xc9\x79\x5e\xcb\x01\x16\x6a\xca\x58\x74\x9a\xc1\x4f"
+ "\x00\x00\x00\x00\x02\x00\x14\x00\x4e\x00\x55\x00\x54\x00\x43\x00"
+ "\x52\x00\x41\x00\x43\x00\x4b\x00\x45\x00\x52\x00\x01\x00\x14\x00"
+ "\x4e\x00\x55\x00\x54\x00\x43\x00\x52\x00\x41\x00\x43\x00\x4b\x00"
+ "\x45\x00\x52\x00\x04\x00\x12\x00\x61\x00\x70\x00\x70\x00\x6c\x00"
+ "\x65\x00\x2e\x00\x63\x00\x6f\x00\x6d\x00\x03\x00\x20\x00\x68\x00"
+ "\x75\x00\x6d\x00\x6d\x00\x65\x00\x6c\x00\x2e\x00\x61\x00\x70\x00"
+ "\x70\x00\x6c\x00\x65\x00\x2e\x00\x63\x00\x6f\x00\x6d\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f"
+ "\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20"
+ "\x00\x53\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63\x00\x65\x00\x20"
+ "\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x33\x00\x20\x00\x32"
+ "\x00\x36\x00\x30\x00\x30\x00\x00\x00\x57\x00\x69\x00\x6e\x00\x64"
+ "\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32"
+ "\x00\x20\x00\x35\x00\x2e\x00\x31\x00\x00\x00\x00\x00";
+ const unsigned char challenge[8] =
+ "\xe4\x9c\x6a\x12\xe1\xbd\xde\x6a";
+ unsigned char sessionkey[16];
+
+ const char key[16] = "\xD1\x83\x98\x3E\xAE\xA7\xBE\x99\x59\xC8\xF4\xC1\x98\xED\x0E\x68";
+
+ struct ntlm_buf data;
+ struct ntlm_type3 t3;
+ int ret;
+
+ struct ntlm_targetinfo ti;
+
+ unsigned char timsg[114] =
+ "\002\000\024\000N\000U\000T\000C\000R\000A\000C\000K\000E\000R\000\001\000\024\000N\000U\000T\000C\000R\000A\000C\000K\000E\000R\000\004\000\022\000a\000p\000p\000l\000e\000.\000c\000o\000m\000\003\000 \000h\000u\000m\000m\000e\000l\000.\000a\000p\000p\000l\000e\000.\000c\000o\000m\000\000\000\000\000\000\000\000";
+
+
+ data.data = type3;
+ data.length = sizeof(type3);
+
+ ret = heim_ntlm_decode_type3(&data, 1, &t3);
+ if (ret)
+ errx(1, "heim_ntlm_decode_type3");
+
+ memset(&ti, 0, sizeof(ti));
+
+ data.data = timsg;
+ data.length = sizeof(timsg);
+
+ ret = heim_ntlm_decode_targetinfo(&data, 1, &ti);
+ if (ret)
+ return ret;
+
+ ret = heim_ntlm_verify_ntlm2(key, sizeof(key),
+ t3.username,
+ t3.targetname,
+ 1285615547,
+ challenge,
+ &t3.ntlm,
+ &data,
+ sessionkey);
+ if (ret)
+ errx(1, "verify_ntlmv2");
+
+ if (sizeof(timsg) != data.length || memcmp(timsg, data.data, sizeof(timsg)) != 0)
+ errx(1, "target info wrong: %d != %d",
+ (int)sizeof(timsg), (int)data.length);
+
+ heim_ntlm_free_type3(&t3);
+ heim_ntlm_free_targetinfo(&ti);
+
+ return 0;
+}
+
+static int
+test_targetinfo(void)
+{
+ struct ntlm_targetinfo ti;
+ struct ntlm_buf buf;
+ const char *dnsservername = "dnsservername";
+ const char *targetname = "targetname";
+ const char z16[16] = { 0 };
+ int ret;
+
+ memset(&ti, 0, sizeof(ti));
+
+ ti.dnsservername = rk_UNCONST(dnsservername);
+ ti.avflags = 1;
+ ti.targetname = rk_UNCONST(targetname);
+ ti.channel_bindings.data = rk_UNCONST(z16);
+ ti.channel_bindings.length = sizeof(z16);
+
+ ret = heim_ntlm_encode_targetinfo(&ti, 1, &buf);
+ if (ret)
+ return ret;
+
+ memset(&ti, 0, sizeof(ti));
+
+ ret = heim_ntlm_decode_targetinfo(&buf, 1, &ti);
+ if (ret)
+ return ret;
+
+ if (ti.dnsservername == NULL ||
+ strcmp(ti.dnsservername, dnsservername) != 0)
+ errx(1, "ti.dnshostname != %s", dnsservername);
+ if (ti.avflags != 1)
+ errx(1, "ti.avflags != 1");
+ if (ti.targetname == NULL ||
+ strcmp(ti.targetname, targetname) != 0)
+ errx(1, "ti.targetname != %s", targetname);
+
+ if (ti.channel_bindings.length != sizeof(z16) ||
+ memcmp(ti.channel_bindings.data, z16, sizeof(z16)) != 0)
+ errx(1, "ti.channel_bindings != Z(16)");
+
+ heim_ntlm_free_targetinfo(&ti);
+
+ return 0;
+}
+
+static int
+test_string2key(void)
+{
+ const char *pw = "山田";
+ struct ntlm_buf buf;
+
+ unsigned char key[16] = {
+ 0xc6, 0x5d, 0xc7, 0x61, 0xa1, 0x34, 0x17, 0xa1,
+ 0x17, 0x08, 0x9c, 0x1b, 0xb0, 0x0d, 0x0f, 0x19
+ };
+
+ if (heim_ntlm_nt_key(pw, &buf) != 0)
+ errx(1, "heim_ntlmv_nt_key(jp)");
+
+ if (buf.length != 16 || memcmp(buf.data, key, 16) != 0)
+ errx(1, "compare failed");
+
+ heim_ntlm_free_buf(&buf);
+
+ return 0;
+}
+
+static int
+test_jp(void)
+{
+ char buf2[220] =
+ "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x02\x00\x00\x00\x06\x00\x06\x00"
+ "\x38\x00\x00\x00\x05\x02\x89\x62\x62\x94\xb1\xf3\x56\x80\xb0\xf9"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x9e\x00\x9e\x00\x3e\x00\x00\x00"
+ "\x06\x01\xb0\x1d\x00\x00\x00\x0f\x43\x00\x4f\x00\x53\x00\x02\x00"
+ "\x06\x00\x43\x00\x4f\x00\x53\x00\x01\x00\x12\x00\x43\x00\x4f\x00"
+ "\x53\x00\x57\x00\x49\x00\x4e\x00\x37\x00\x4a\x00\x50\x00\x04\x00"
+ "\x1a\x00\x63\x00\x6f\x00\x73\x00\x2e\x00\x61\x00\x70\x00\x70\x00"
+ "\x6c\x00\x65\x00\x2e\x00\x63\x00\x6f\x00\x6d\x00\x03\x00\x2e\x00"
+ "\x63\x00\x6f\x00\x73\x00\x77\x00\x69\x00\x6e\x00\x37\x00\x6a\x00"
+ "\x70\x00\x2e\x00\x63\x00\x6f\x00\x73\x00\x2e\x00\x61\x00\x70\x00"
+ "\x70\x00\x6c\x00\x65\x00\x2e\x00\x63\x00\x6f\x00\x6d\x00\x05\x00"
+ "\x1a\x00\x63\x00\x6f\x00\x73\x00\x2e\x00\x61\x00\x70\x00\x70\x00"
+ "\x6c\x00\x65\x00\x2e\x00\x63\x00\x6f\x00\x6d\x00\x07\x00\x08\x00"
+ "\x94\x51\xf0\xbd\xdc\x61\xcb\x01\x00\x00\x00\x00";
+
+ char buf3[362] =
+ "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x03\x00\x00\x00\x18\x00\x18\x00"
+ "\x74\x00\x00\x00\xce\x00\xce\x00\x8c\x00\x00\x00\x1a\x00\x1a\x00"
+ "\x40\x00\x00\x00\x04\x00\x04\x00\x5a\x00\x00\x00\x16\x00\x16\x00"
+ "\x5e\x00\x00\x00\x10\x00\x10\x00\x5a\x01\x00\x00\x05\x02\x89\x62"
+ "\x31\x00\x37\x00\x2e\x00\x32\x00\x30\x00\x31\x00\x2e\x00\x35\x00"
+ "\x37\x00\x2e\x00\x31\x00\x32\x00\x31\x00\x71\x5c\x30\x75\x77\x00"
+ "\x6f\x00\x72\x00\x6b\x00\x73\x00\x74\x00\x61\x00\x74\x00\x69\x00"
+ "\x6f\x00\x6e\x00\xab\xad\xeb\x72\x01\xd4\x5f\xdf\x59\x07\x5f\xa9"
+ "\xfd\x54\x98\x2d\xfa\x17\xbb\xf1\x3c\x8f\xf5\x20\xe6\x8f\xd7\x0a"
+ "\xc9\x19\x3e\x94\x61\x31\xdb\x0f\x55\xe8\xe2\x53\x01\x01\x00\x00"
+ "\x00\x00\x00\x00\x00\x06\x3e\x30\xe4\x61\xcb\x01\x71\x98\x10\x6b"
+ "\x4c\x82\xec\xb3\x00\x00\x00\x00\x02\x00\x06\x00\x43\x00\x4f\x00"
+ "\x53\x00\x01\x00\x12\x00\x43\x00\x4f\x00\x53\x00\x57\x00\x49\x00"
+ "\x4e\x00\x37\x00\x4a\x00\x50\x00\x04\x00\x1a\x00\x63\x00\x6f\x00"
+ "\x73\x00\x2e\x00\x61\x00\x70\x00\x70\x00\x6c\x00\x65\x00\x2e\x00"
+ "\x63\x00\x6f\x00\x6d\x00\x03\x00\x2e\x00\x63\x00\x6f\x00\x73\x00"
+ "\x77\x00\x69\x00\x6e\x00\x37\x00\x6a\x00\x70\x00\x2e\x00\x63\x00"
+ "\x6f\x00\x73\x00\x2e\x00\x61\x00\x70\x00\x70\x00\x6c\x00\x65\x00"
+ "\x2e\x00\x63\x00\x6f\x00\x6d\x00\x05\x00\x1a\x00\x63\x00\x6f\x00"
+ "\x73\x00\x2e\x00\x61\x00\x70\x00\x70\x00\x6c\x00\x65\x00\x2e\x00"
+ "\x63\x00\x6f\x00\x6d\x00\x07\x00\x08\x00\xab\xec\xcc\x30\xe4\x61"
+ "\xcb\x01\x00\x00\x00\x00\x00\x00\x00\x00\xbc\x2e\xba\x3f\xd1\xb1"
+ "\xa7\x70\x00\x9d\x55\xa0\x59\x74\x2b\x78";
+
+
+ struct ntlm_type2 type2;
+ struct ntlm_type3 type3;
+ struct ntlm_buf data;
+ int ret;
+
+ data.length = sizeof(buf2);
+ data.data = buf2;
+
+ memset(&type2, 0, sizeof(type2));
+
+ ret = heim_ntlm_decode_type2(&data, &type2);
+ if (ret)
+ errx(1, "heim_ntlm_decode_type2(jp): %d", ret);
+
+ data.data = NULL;
+ data.length = 0;
+
+ ret = heim_ntlm_encode_type2(&type2, &data);
+ if (ret)
+ errx(1, "heim_ntlm_encode_type2(jp): %d", ret);
+
+ heim_ntlm_free_type2(&type2);
+ heim_ntlm_free_buf(&data);
+
+ data.length = sizeof(buf3);
+ data.data = buf3;
+
+ memset(&type3, 0, sizeof(type3));
+
+ ret = heim_ntlm_decode_type3(&data, 1, &type3);
+ if (ret)
+ errx(1, "heim_ntlm_decode_type2(jp): %d", ret);
+
+ data.data = NULL;
+ data.length = 0;
+
+ ret = heim_ntlm_encode_type3(&type3, &data, NULL);
+ if (ret)
+ errx(1, "heim_ntlm_decode_type2(jp): %d", ret);
+
+ heim_ntlm_free_type3(&type3);
+ heim_ntlm_free_buf(&data);
+
+ return 0;
+}
+
+
+static int verbose_flag = 0;
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"verbose", 0, arg_flag, &verbose_flag, "verbose printing", NULL },
+ {"version", 0, arg_flag, &version_flag, "print version", NULL },
+ {"help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args, sizeof(args)/sizeof(*args),
+ NULL, "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ret = 0, optidx = 0;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ if (verbose_flag)
+ printf("test_parse\n");
+ ret |= test_parse();
+
+ if (verbose_flag)
+ printf("test_keys\n");
+ ret |= test_keys();
+
+ if (verbose_flag)
+ printf("test_ntlm2_session_resp\n");
+ ret |= test_ntlm2_session_resp();
+
+ if (verbose_flag)
+ printf("test_targetinfo\n");
+ ret |= test_targetinfo();
+
+ if (verbose_flag)
+ printf("test_ntlmv2\n");
+ ret |= test_ntlmv2();
+
+ if (verbose_flag)
+ printf("test_string2key\n");
+ ret |= test_string2key();
+
+ if (verbose_flag)
+ printf("test_jp\n");
+ ret |= test_jp();
+
+ return ret;
+}
diff --git a/third_party/heimdal/lib/ntlm/version-script.map b/third_party/heimdal/lib/ntlm/version-script.map
new file mode 100644
index 0000000..6fe67a6
--- /dev/null
+++ b/third_party/heimdal/lib/ntlm/version-script.map
@@ -0,0 +1,30 @@
+# $Id$
+
+HEIMDAL_NTLM_1.0 {
+ global:
+ heim_ntlm_build_ntlm1_master;
+ heim_ntlm_calculate_lm2;
+ heim_ntlm_calculate_ntlm1;
+ heim_ntlm_calculate_ntlm2;
+ heim_ntlm_calculate_ntlm2_sess;
+ heim_ntlm_decode_targetinfo;
+ heim_ntlm_decode_type1;
+ heim_ntlm_decode_type2;
+ heim_ntlm_decode_type3;
+ heim_ntlm_encode_targetinfo;
+ heim_ntlm_encode_type1;
+ heim_ntlm_encode_type2;
+ heim_ntlm_encode_type3;
+ heim_ntlm_free_buf;
+ heim_ntlm_free_targetinfo;
+ heim_ntlm_free_type1;
+ heim_ntlm_free_type2;
+ heim_ntlm_free_type3;
+ heim_ntlm_nt_key;
+ heim_ntlm_ntlmv2_key;
+ heim_ntlm_verify_ntlm2;
+ heim_ntlm_unparse_flags;
+ initialize_ntlm_error_table_r;
+ local:
+ *;
+};