pax_global_header00006660000000000000000000000064145673616110014524gustar00rootroot0000000000000052 comment=3a24e4579c4187f6aadff1243610890c9e6d690e gss-ntlmssp-1.3.1/000077500000000000000000000000001456736161100140205ustar00rootroot00000000000000gss-ntlmssp-1.3.1/.github/000077500000000000000000000000001456736161100153605ustar00rootroot00000000000000gss-ntlmssp-1.3.1/.github/workflows/000077500000000000000000000000001456736161100174155ustar00rootroot00000000000000gss-ntlmssp-1.3.1/.github/workflows/ccpp.yml000066400000000000000000000044731456736161100210750ustar00rootroot00000000000000name: Build GSSNTLMSSP on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: container: - docker.io/library/alpine:latest - docker.io/library/alpine:edge - docker.io/library/archlinux:latest - docker.io/library/debian:oldstable - docker.io/library/debian:stable - docker.io/library/debian:unstable - docker.io/library/debian:testing - docker.io/library/ubuntu:latest - docker.io/library/ubuntu:rolling - docker.io/library/ubuntu:devel # - quay.io/centos/centos:stream9 # - registry.fedoraproject.org/fedora:latest - registry.fedoraproject.org/fedora:rawhide fail-fast: false container: image: ${{ matrix.container }} steps: - name: update and install packages run: | if command -v apk; then apk add automake autoconf docbook-xml docbook-xsl doxygen krb5-dev libtool libunistring-dev libxslt gettext-dev gcc libxml2 m4 make musl-dev openssl-dev zlib-dev elif command -v apt; then export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y autotools-dev autoconf build-essential libtool libkrb5-dev libssl-dev libunistring-dev gettext xsltproc libxml2-utils docbook-xml docbook-xsl zlib1g-dev elif command -v dnf; then dnf install -y autoconf automake docbook-style-xsl doxygen gettext-devel krb5-devel libtool libunistring-devel libxml2 libxslt m4 make openssl-devel pkgconfig 'pkgconfig(wbclient)' zlib-devel elif command -v pacman; then pacman -Sy --noconfirm automake autoconf docbook-xml docbook-xsl doxygen libtool libxslt gcc libxml2 m4 make zlib fi - uses: actions/checkout@v3 - name: autoreconf run: autoreconf -fi - name: configure run: | # Alpine needs extra flags. See https://gitlab.alpinelinux.org/alpine/aports/-/issues/13285 if command -v apk; then export LDFLAGS=-lintl fi ./configure --with-wbclient=no - name: make run: make - name: make check run: make check - name: Upload logs uses: actions/upload-artifact@v3 if: failure() with: name: testlogs ${{ matrix.container }} path: test-suite.log gss-ntlmssp-1.3.1/.gitignore000066400000000000000000000005131456736161100160070ustar00rootroot00000000000000.deps/ .libs/ Makefile Makefile.in *.o *.la *.lo *.8 aclocal.m4 autom4te.cache/ ar-lib compile config.guess config.sub depcomp install-sh ltmain.sh missing config.h config.h.in config.h.in~ config.log config.status configure configure.in *.spec dict.symbols libtool m4/ ntlmssptest .dirstamp stamp-h1 examples/mech.ntlmssp po/*.mo gss-ntlmssp-1.3.1/BUILD.txt000066400000000000000000000006351456736161100154240ustar00rootroot00000000000000 BUILDING ======== Usual tools of the trade: $ autoreconf -f -i $ ./configure $ make optionally (for easy installation on Fedora): $ make rpms In order to build gss-ntlmssp the following development packages are needed: autoconf automake docbook-style-xsl doxygen findutils krb5-devel libtool libxml2 libxslt libunistring-devel m4 pkgconfig openssl-devel gss-ntlmssp-1.3.1/COPYING000066400000000000000000000013661456736161100150610ustar00rootroot00000000000000Copyright 2013-2020 Simo Sorce and GSS-NTLMSSP Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. gss-ntlmssp-1.3.1/Makefile.am000066400000000000000000000124231456736161100160560ustar00rootroot00000000000000SUBDIRS = #SUBDIRS += po if HAVE_MANPAGES SUBDIRS += man endif if HAVE_NLS SUBDIRS += po endif # Some old versions of automake don't define builddir builddir ?= . DOXYGEN = @DOXYGEN@ DISTSETUPOPTS = if HAVE_DEBIAN DISTSETUPOPTS += --install-layout=deb endif gssconfdir = $(sysconfdir)/gss gssntlmsspdir = $(libdir)/gssntlmssp localedir = @localedir@ pkgconfigdir = $(libdir)/pkgconfig AM_CFLAGS = if WANT_AUX_INFO AM_CFLAGS += -aux-info $@.X endif if HAVE_GCC AM_CFLAGS += -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith \ -Wcast-qual -Wcast-align -Wwrite-strings \ -Werror-implicit-function-declaration \ -fno-strict-aliasing endif if HAVE_NLS AM_CFLAGS += -DLOCALEDIR="\"$(localedir)\"" endif dist_pkgconfig_DATA = ACLOCAL_AMFLAGS = -I m4 -I . check_PROGRAMS = \ ntlmssptest gssntlmssp_LTLIBRARIES = \ gssntlmssp.la dist_noinst_SCRIPTS = \ tests/scripts/dlopen.sh \ tests/env1.sh \ tests/env2.sh dist_noinst_DATA = ############################### # Global compilation settings # ############################### AM_CPPFLAGS = \ -Wall \ -Iinclude \ -I.. \ -I$(srcdir)/include \ -I$(srcdir) \ -Iinclude \ -I. \ -DLIBDIR=\"$(libdir)\" \ -DVARDIR=\"$(localstatedir)\" \ -DSHLIBEXT=\"$(SHLIBEXT)\" \ -DSYSCONFDIR=\"$(sysconfdir)\" \ -DLOCALEDIR=\"$(localedir)\" GN_MECHGLUE_LIBS = $(GSSAPI_LIBS) $(CRYPTO_LIBS) $(WBC_LIBS) GN_MECHGLUE_OBJ = \ src/crypto.c \ src/ntlm_crypto.c \ src/ntlm.c \ src/debug.c \ src/gss_err.c \ src/gss_spi.c \ src/gss_names.c \ src/gss_creds.c \ src/gss_sec_ctx.c \ src/gss_signseal.c \ src/gss_serialize.c \ src/external.c \ src/gss_auth.c \ src/gss_ntlmssp.c if BUILD_WBCLIENT GN_MECHGLUE_OBJ += src/winbind.c endif dist_noinst_HEADERS = \ src/crypto.h \ src/ntlm_common.h \ src/ntlm.h \ src/debug.h \ src/gss_ntlmssp.h \ src/gss_ntlmssp_winbind.h gssapidir = $(includedir)/gssapi dist_gssapi_HEADERS = \ src/gssapi_ntlmssp.h POTFILES = src/gss_err.c #################### # Program Binaries # #################### gssntlmssp_la_SOURCES = \ $(GN_MECHGLUE_OBJ) gssntlmssp_la_CFLAGS = \ $(WBC_CFLAGS) \ $(AM_CFLAGS) gssntlmssp_la_LDFLAGS = \ -L$(libdir) \ $(GN_MECHGLUE_LIBS) \ -export-symbols-regex '^gss(spi|)_' \ -avoid-version \ -module ntlmssptest_SOURCES = \ $(GN_MECHGLUE_OBJ) \ tests/ntlmssptest.c ntlmssptest_CFLAGS = \ $(WBC_CFLAGS) \ $(AM_CFLAGS) ntlmssptest_LDFLAGS = \ -L$(libdir) ntlmssptest_LDADD = \ $(GN_MECHGLUE_LIBS) \ $(UNISTRING_LIBS) dist_noinst_DATA += \ m4 noinst_PROGRAMS = ntlmssptest ################ # TRANSLATIONS # ################ update-po: if HAVE_MANPAGES $(MAKE) -C man update-po endif # $(MAKE) -C po update-po ####################### # Installation Extras # ####################### installgssntlmsspdirs:: mkdir -p \ $(DESTDIR)$(libdir)/gssntlmssp \ $(DESTDIR)$(mandir) CLEANFILES = *.X */*.X */*/*.X tests: all $(check_PROGRAMS) # RPM-related tasks RPMBUILD ?= $(PWD)/rpmbuild dist_noinst_DATA += \ m4 \ contrib/gssntlmssp.spec.in \ BUILD.txt \ COPYING rpmroot: mkdir -p $(RPMBUILD)/BUILD mkdir -p $(RPMBUILD)/RPMS mkdir -p $(RPMBUILD)/SOURCES mkdir -p $(RPMBUILD)/SPECS mkdir -p $(RPMBUILD)/SRPMS po/$(PACKAGE).pot: $(POTFILES) @echo "Regenerating $@" ; rm -f $@ && \ xgettext --directory=$(top_srcdir) --from-code=UTF-8 \ --sort-by-file --add-comments --keyword=_ --keyword=N_ \ --package-name="@PACKAGE@" --package-version="@VERSION@" \ --msgid-bugs-address="simo@samba.org" \ -o $@ $(POTFILES) rpms: dist-gzip rpmroot cp $(builddir)/contrib/gssntlmssp.spec $(RPMBUILD)/SPECS cp $(distdir).tar.gz $(RPMBUILD)/SOURCES cd $(RPMBUILD); \ rpmbuild --define "_topdir $(RPMBUILD)" -ba SPECS/gssntlmssp.spec if GIT_CHECKOUT prerelease-rpms: cp $(srcdir)/version.m4 $(srcdir)/version.m4.orig sed -e "s/m4_define(\[PRERELEASE_VERSION_NUMBER\], \[.*\])/m4_define(\[PRERELEASE_VERSION_NUMBER\], \[.`date +%Y%m%d.%H%M`.git`git log -1 --pretty=format:%h`\])/" < $(srcdir)/version.m4.orig > $(srcdir)/version.m4 $(MAKE) rpms mv $(srcdir)/version.m4.orig $(srcdir)/version.m4 endif # make srpms will use the old digest algorithm to be compatible # with RHEL5 srpm: dist-gzip rpmroot cp $(builddir)/contrib/gssntlmssp.spec $(RPMBUILD)/SPECS cp $(distdir).tar.gz $(RPMBUILD)/SOURCES cd $(RPMBUILD); \ rpmbuild --define "_topdir $(RPMBUILD)" \ --define _source_filedigest_algorithm=1 \ -bs SPECS/gssntlmssp.spec if GIT_CHECKOUT prerelease-srpm: cp $(srcdir)/version.m4 $(srcdir)/version.m4.orig sed -e "s/m4_define(\[PRERELEASE_VERSION_NUMBER\], \[.*\])/m4_define(\[PRERELEASE_VERSION_NUMBER\], \[.`date +%Y%m%d.%H%M`.git`git log -1 --pretty=format:%h`\])/" < $(srcdir)/version.m4.orig > $(srcdir)/version.m4 $(MAKE) srpm mv $(srcdir)/version.m4.orig $(srcdir)/version.m4 endif #################### # Testing # #################### EXTRA_DIST = \ examples/test_user_file.txt \ examples/test_user_file2.txt \ examples/test_user_file3.txt TESTS = ntlmssptest tests/env1.sh tests/env2.sh test_gssntlmssp: TMPDIR=tests/scripts/ ./tests/scripts/dlopen.sh ./.libs/gssntlmssp.so || exit 1 distclean-local: rm -f tests*.log gss-ntlmssp-1.3.1/README.md000066400000000000000000000017501456736161100153020ustar00rootroot00000000000000![Build GSSNTLMSSP](https://github.com/gssapi/gss-ntlmssp/workflows/Build%20GSSNTLMSSP/badge.svg) GSS-NTLMSSP =========== This is a mechglue plugin for the GSSAPI library that implements NTLM authentication. So far it has been built and tested only with the libgssapi implementation that comes with MIT Kerberos (Versions 1.11 and above) ### Project Information The project is currently hosted on [github](https://github.com/gssapi/gss-ntlmssp) Project related information (releases, hot to contribute, coding style, etc.. is available in the [wiki](https://github.com/gssapi/gss-ntlmssp/wiki) ### Acknowlegments ##### Protocol Documentation: This project has been made much simpler thanks to the work done by the Samba Team first in Samba and Microsoft later by releasing documents such as [MS-NLMP](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NLMP/[MS-NLMP].pdf). ##### OID Space: The Samba Project kindly donated this OID space: 1.3.6.1.4.1.7165.655.1.x gss-ntlmssp-1.3.1/TESTING.txt000066400000000000000000000034441456736161100157030ustar00rootroot00000000000000TESTING ======= Testsuite: ---------- Run ./ntlmssptest at your leisure, it just insures that the crypto is working correctly. Real testing: ------------- There are exactly 2 configuration knobs at this point, and both need to be set right. 1. The gss configruation file. In order to load the mechanism into GSSAPI copy the content of the file examples/mech.ntlmssp into /etc/gss/mech If you are installing in a non standard path check that the location of the shared object matches where you installed it in your system 2. The credentials file Set the environment variable NTLM_USER_FILE to a path to a file with your NTLM cedentials in it. The file format is the same as the one used by the gss ntlm mechanism that can be found in Heimdal. Super simple, one or more lines with: DOMAIN:USERNAME:PASSWORD as elements separated by ':' For example: ADDOM:Administrator:Passw0rd Testing Application: -------------------- So far the only application that seem to properly use GSSAPI and therfore will work unmodified is Firefox. I tried also Curl, but even after making some patches to let it use the builtin SPNEGO implementation of GSSAPI it seem that the code is hardcoded to believe there will always only ever be one roundtrip. This is not necessarily true with the krb5 mechanism although it works with that with current implementations. I will need more patches for curl, meanwhile use firefox. The server: I am using a Windows Server with IIS installed and Windows Authentication enabled. In Firefox go in about:config and set the string list named network.negotiate-auth.trusted-uris to your Windows server domain name suffix. This is necessary otherwise Firefox will not even attempt to perform negotiation, regardles of the Mechanism used. Example: network.negotiate-auth.trusted-uris = .addom.example.com gss-ntlmssp-1.3.1/build_macros.m4000066400000000000000000000015251456736161100167300ustar00rootroot00000000000000AC_DEFUN([BUILD_WITH_SHARED_BUILD_DIR], [ AC_ARG_WITH([shared-build-dir], [AC_HELP_STRING([--with-shared-build-dir=DIR], [temporary build directory where libraries are installed [$srcdir/sharedbuild]])]) sharedbuilddir="$srcdir/sharedbuild" if test x"$with_shared_build_dir" != x; then sharedbuilddir=$with_shared_build_dir CFLAGS="$CFLAGS -I$with_shared_build_dir/include" CPPFLAGS="$CPPFLAGS -I$with_shared_build_dir/include" LDFLAGS="$LDFLAGS -L$with_shared_build_dir/lib" fi AC_SUBST(sharedbuilddir) ]) AC_DEFUN([BUILD_WITH_AUX_INFO], [ AC_ARG_WITH([aux-info], [AC_HELP_STRING([--with-aux-info], [Build with -aux-info output])]) ]) AM_CONDITIONAL([WANT_AUX_INFO], [test x$with_aux_info = xyes]) gss-ntlmssp-1.3.1/conf_macros.m4000066400000000000000000000073041456736161100165570ustar00rootroot00000000000000AC_DEFUN([WITH_DISTRO_VERSION], [ AC_ARG_WITH([distro-version], [AC_HELP_STRING([--with-distro-version=VERSION], [Distro version number []] ) ] ) AC_DEFINE_UNQUOTED(DISTRO_VERSION, "$with_distro_version", [Distro version number]) ]) AC_DEFUN([WITH_MANPAGES], [ AC_ARG_WITH([manpages], [AC_HELP_STRING([--with-manpages], [Whether to regenerate man pages from DocBook sources [yes]] ) ], [], with_manpages=yes ) if test x"$with_manpages" = xyes; then HAVE_MANPAGES=1 AC_SUBST(HAVE_MANPAGES) fi ]) AM_CONDITIONAL([BUILD_MANPAGES], [test x$with_manpages = xyes]) AC_DEFUN([WITH_XML_CATALOG], [ AC_ARG_WITH([xml-catalog-path], [AC_HELP_STRING([--with-xml-catalog-path=PATH], [Where to look for XML catalog [/etc/xml/catalog]] ) ] ) SGML_CATALOG_FILES="/etc/xml/catalog" if test x"$with_xml_catalog_path" != x; then SGML_CATALOG_FILES="$with_xml_catalog_path" fi AC_SUBST([SGML_CATALOG_FILES]) ]) AC_DEFUN([WITH_TEST_DIR], [ AC_ARG_WITH([test-dir], [AC_HELP_STRING([--with-test-dir=PATH], [Directory used for make check temporary files [$builddir]] ) ] ) TEST_DIR=$with_test_dir AC_SUBST(TEST_DIR) AC_DEFINE_UNQUOTED(TEST_DIR, "$with_test_dir", [Directory used for 'make check' temporary files]) ]) AC_ARG_ENABLE([nls], [AS_HELP_STRING([--disable-nls], [do not use Native Language Support])], [use_nls=$enableval], [use_nls=yes]) if test x"$use_nls" = "xyes"; then HAVE_NLS=1 AC_SUBST(HAVE_NLS) AC_DEFINE_UNQUOTED(HAVE_NLS, 1, [Buils with Native Language Support]) fi AM_CONDITIONAL([HAVE_NLS], [test x"$use_nls" = xyes]) AC_ARG_ENABLE([all-experimental-features], [AS_HELP_STRING([--enable-all-experimental-features], [build all experimental features])], [build_all_experimental_features=$enableval], [build_all_experimental_features=no]) AC_DEFUN([WITH_WBCLIENT], [AC_ARG_WITH([wbclient], [AC_HELP_STRING([--with-wbclient], [Build with WBClient support [yes]]) ], [], with_wbclient=yes) if test x"$with_wbclient" = xyes; then HAVE_WBCLIENT=1 AC_SUBST(HAVE_WBCLIENT) AC_DEFINE_UNQUOTED(HAVE_WBCLIENT, 1, [Build with WBClient support]) fi AM_CONDITIONAL([BUILD_WBCLIENT], [test x"$with_wbclient" = xyes]) ]) AC_DEFUN([WITH_WINBIND_TLS_CONTEXT], [ AC_ARG_WITH([winbind-tls-context], [AC_HELP_STRING([--with-winbind-tls-context], [Whether to default to thread local storage for winbind contexts [no]]) ], [with_winbind_tls_context=$withval], with_winbind_tls_context=no) if test x"$with_winbind_tls_context" = xyes; then AC_DEFINE(DEFAULT_WB_TLS_CTX, 1, [whether to default to thread local storage for winbind contexts S]) fi AM_CONDITIONAL([DEFAULT_WB_TLS_CTX], [test x"$with_winbind_tls_context" = xyes]) ]) gss-ntlmssp-1.3.1/configure.ac000066400000000000000000000106441456736161100163130ustar00rootroot00000000000000AC_PREREQ(2.59) m4_include([version.m4]) AC_INIT([gssntlmssp], VERSION_NUMBER, [simo@samba.org]) m4_ifdef([AC_USE_SYSTEM_EXTENSIONS], [AC_USE_SYSTEM_EXTENSIONS], [AC_GNU_SOURCE]) CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE" AM_INIT_AUTOMAKE([-Wall foreign subdir-objects tar-pax]) AM_PROG_CC_C_O AM_PROG_AR AC_PROG_INSTALL LT_INIT([disable-static]) AC_SUBST([PRERELEASE_VERSION], PRERELEASE_VERSION_NUMBER) AC_DEFINE([PRERELEASE_VERSION], "PRERELEASE_VERSION_NUMBER", [Prerelease version number of package]) AM_CONDITIONAL([GIT_CHECKOUT], [git log -1 &>/dev/null]) m4_pattern_allow([AM_SILENT_RULES]) AM_SILENT_RULES AM_CONDITIONAL([HAVE_GCC], [test "$ac_cv_prog_gcc" = yes]) AC_CHECK_HEADERS(stdint.h dlfcn.h) AC_CONFIG_HEADER(config.h) AC_CHECK_TYPES([errno_t], [], [], [[#include ]]) m4_include([build_macros.m4]) BUILD_WITH_SHARED_BUILD_DIR AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;]])], [AC_DEFINE([HAVE_PTHREAD], [1], [Pthread mutexes available.])], [AC_MSG_WARN([Pthread library not found! Clients will not be thread safe...])]) #Include here cause WITH_INIT_DIR requires $osname set in platform.m4 m4_include([external/platform.m4]) m4_include(conf_macros.m4) WITH_DISTRO_VERSION WITH_TEST_DIR WITH_MANPAGES WITH_XML_CATALOG WITH_WBCLIENT WITH_WINBIND_TLS_CONTEXT m4_include([external/pkg.m4]) m4_include([external/docbook.m4]) m4_include([external/sizes.m4]) m4_include([external/ax_pthread.m4]) m4_include([external/m4_ax_check_openssl.m4]) m4_include([external/ax_check_zlib.m4]) m4_include([external/wbclient.m4]) AX_PTHREAD(,[AC_MSG_ERROR([Could not find Pthreads support])]) LIBS="$PTHREAD_LIBS $LIBS" LIBS="$PTHREAD_CFLAGS $CFLAGS" CC="$PTHREAD_CC" AC_CHECK_HEADERS([gssapi/gssapi.h],,[AC_MSG_ERROR([Could not find GSSAPI headers])]) AC_PATH_PROG(KRB5_CONFIG, krb5-config, failed) if test x$KRB5_CONFIG = xfailed; then AC_MSG_ERROR([Could not find GSSAPI development libraries]) else GSSAPI_CFLAGS="`$KRB5_CONFIG --cflags gssapi`" GSSAPI_LIBS="`$KRB5_CONFIG --libs gssapi`" fi AC_CHECK_LIB(gssapi_krb5, gss_import_cred,, [AC_MSG_ERROR([GSSAPI library does not support gss_import_cred])], [$GSSAPI_LIBS]) AC_CHECK_LIB(gssapi_krb5, gss_export_cred,, [AC_MSG_ERROR([GSSAPI library does not support gss_export_cred])], [$GSSAPI_LIBS]) AC_SUBST([GSSAPI_CFLAGS]) AC_SUBST([GSSAPI_LIBS]) AC_CHECK_HEADERS([unicase.h uniconv.h],, [AC_MSG_ERROR([Could not find libunistring headers])]) UNISTRING_LIBS="-lunistring" AC_CHECK_LIB(unistring, u8_toupper,, [AC_MSG_ERROR([libunistring does not support u8_toupper])], [$UNISTRING_LIBS]) AC_SUBST([UNISTRING_LIBS]) AX_CHECK_OPENSSL(,[AC_MSG_ERROR([Could not find OpenSSL support])]) CRYPTO_LIBS="$OPENSSL_LIBS" AC_SUBST([CRYPTO_LIBS]) AX_CHECK_ZLIB(,[AC_MSG_ERROR([Could not find Zlib support])]) if test x$HAVE_WBCLIENT != x; then AM_CHECK_WBCLIENT fi if test x$HAVE_NLS != x; then AC_CHECK_FUNC(dgettext, [], AC_CHECK_LIB(intl, dgettext, [], AC_ERROR([Could not find dgettext() function]))) AC_PATH_PROG(MSGFMT, msgfmt) if test x$MSGFMT = x; then AC_ERROR([Could not find msgfmt]) fi fi RAWLINGUAS=`sed -e "/^#/d" -e "s/#.*//" "${srcdir}/po/LINGUAS"` # Remove newlines LINGUAS=`echo $RAWLINGUAS` AC_SUBST(LINGUAS) if test x$HAVE_MANPAGES != x; then CHECK_XML_TOOLS CHECK_STYLESHEET([$SGML_CATALOG_FILES], [http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl], [Docbook XSL templates]) AC_CHECK_PROG([PO4A],[po4a],[po4a],[no]) fi AM_CONDITIONAL([HAVE_MANPAGES], [test "x$HAVE_MANPAGES" != "x"]) AM_CONDITIONAL([HAVE_PO4A], [test "x$PO4A" != "xno"]) AC_PATH_PROG([DOXYGEN], [doxygen], [false]) AM_CONDITIONAL([HAVE_DOXYGEN], [test x$DOXYGEN != xfalse ]) abs_build_dir=`pwd` AC_DEFINE_UNQUOTED([ABS_BUILD_DIR], ["$abs_build_dir"], [Absolute path to the build directory]) AC_SUBST([abs_builddir], $abs_build_dir) abs_src_dir=`cd $srcdir && pwd` AC_DEFINE_UNQUOTED([ABS_SRC_DIR], ["$abs_src_dir"], [Absolute path to the source directory]) AC_SUBST([abs_srcdir], $abs_src_dir) AC_CONFIG_FILES([Makefile man/Makefile po/Makefile contrib/gssntlmssp.spec examples/mech.ntlmssp]) AC_OUTPUT gss-ntlmssp-1.3.1/contrib/000077500000000000000000000000001456736161100154605ustar00rootroot00000000000000gss-ntlmssp-1.3.1/contrib/gssntlmssp.spec.in000066400000000000000000000032351456736161100211610ustar00rootroot00000000000000Name: @PACKAGE_NAME@ Version: @PACKAGE_VERSION@ Release: 0@PRERELEASE_VERSION@%{?dist} Summary: GSSAPI NTLMSSP Mechanism Group: System Environment/Libraries License: ISC URL: https://fedorahosted.org/gss-ntlmssp Source0: https://fedorahosted.org/released/gss-ntlmssp/%{name}-%{version}.tar.gz Requires: krb5-libs%{?_isa} >= 1.11.5 BuildRequires: autoconf BuildRequires: automake BuildRequires: libtool BuildRequires: m4 BuildRequires: libxslt BuildRequires: libxml2 BuildRequires: docbook-style-xsl BuildRequires: doxygen BuildRequires: gettext-devel BuildRequires: pkgconfig BuildRequires: krb5-devel >= 1.11.5 BuildRequires: libunistring-devel BuildRequires: openssl-devel BuildRequires: pkgconfig(wbclient) %description A GSSAPI Mechanism that implements NTLMSSP %package devel Summary: Development header for GSSAPI NTLMSSP Group: Development/Libraries License: ISC %description devel Adds a header file with definition for custom GSSAPI extensions for NTLMSSP %prep %setup -q %build autoreconf -fiv %configure \ --with-wbclient \ --disable-static \ --disable-rpath make %{?_smp_mflags} all %install %make_install rm -f %{buildroot}%{_libdir}/gssntlmssp/gssntlmssp.la mkdir -p %{buildroot}%{_sysconfdir}/gss/mech.d install -pm644 examples/mech.ntlmssp %{buildroot}%{_sysconfdir}/gss/mech.d/ntlmssp.conf %{find_lang} %{name} %check make test_gssntlmssp %files -f %{name}.lang %config(noreplace) %{_sysconfdir}/gss/mech.d/ntlmssp.conf %{_libdir}/gssntlmssp/ %{_mandir}/man8/gssntlmssp.8* %doc COPYING %files devel %{_includedir}/gssapi/gssapi_ntlmssp.h %changelog * Sat Jun 22 2013 Simo Sorce - @PACKAGE_VERSION@-0@PRERELEASE_VERSION@ - Start gss-ntlmssp-1.3.1/doc/000077500000000000000000000000001456736161100145655ustar00rootroot00000000000000gss-ntlmssp-1.3.1/doc/compatibility.md000066400000000000000000000027221456736161100177630ustar00rootroot00000000000000Due to the difference between how the Krb5 and NTLM challenge-response mechanisms work, not all software using GSSAPI successfully works yet. [SIPE](http://sipe.sourceforge.net/) 1.18.x and later are GSS-NTLMSSP compatible and in the process of making it work many bugs have been fixed on all sides. Many thanks to Stefan Becker and David Woodhouse for the collaboration and making this possible. [Firefox](https://mozilla.org/firefox) (multiple versions) has been tested and seem to work without issues. [Curl](http://curl.haxx.se/) instead seem to assume that the GSSAPI conversation will always be completed in one roundtrip so it fails to work with GSS-NTLMSSP as the NTLM challenge-response protocol requires two or more roundtrips unlike the Krb5 mechanism. UPDATE: Fixed in curl git as of 2014-07-16 just after the 7.37.1 release. [Cyrus-SASL](http://cyrusimap.org/)'s GSS-SPNEGO support is equally broken with GSS-NTLMSSP, the actual authentication works fine, but then it fails to correctly negotiate the SASL SSF properties due again to the incorrect assumption that the authentication negotiation always terminates with the last message being sent from the server to the client. In NTLMSSP usually the last message is from the client back to the server. UPDATE: The GSS-SPNEGO mechanism in Cyrus-SASL has been recently fixed to work correctly with the reference implementation (MS Windows), at least with the Krb5 mechanism, additional testing for GSS-NTLMSSP is needed now. gss-ntlmssp-1.3.1/doc/release-process.md000066400000000000000000000016141456736161100202050ustar00rootroot00000000000000# Release Process for GSS-NTLMSSP The process is currently quite simple and requires write access to the project's git repository. ## Prepare the sources ### Version and Tag the release * Commit a change in version.m4 with the new version number (ex. 0.1.0) * Test locally with "make rpms" that everything builds fine * Tag the release in master like this: ``` git tag v0.1.0 ``` This will apply the tag to the last commit * Push the tag: ``` git push origin v0.1.0 ``` ### Create a release tarball and SHA hash * Run the following commands (on a git clean tree, please): ``` autoreconf -f -i ./configure make dist ``` ... will generate a tarball named like: gssntlmssp-0.1.0.tar.gz ``` sha512sum gssntlmssp-0.1.0.tar.gz > gssntlmssp-0.1.0.tar.gz.sha512sum.txt ``` ... will generate a file with a sha512 checksum ### Publish the release * Upload the tarball and checksum on the release page gss-ntlmssp-1.3.1/examples/000077500000000000000000000000001456736161100156365ustar00rootroot00000000000000gss-ntlmssp-1.3.1/examples/mech.ntlmssp.in000066400000000000000000000005671456736161100206110ustar00rootroot00000000000000# NTLMSSP mechanism plugin # # NOTE: to activate the NTLM SSP mechanism do the following # * FOR krb5 < 1.12: copy the content of this file in /etc/gss/mech # * FOR krb5 >= 1.12: copy this file in /etc/gss/mech.d # # Mechanism Name Object Identifier Shared Library Path Other Options gssntlmssp_v1 1.3.6.1.4.1.311.2.2.10 @libdir@/gssntlmssp/gssntlmssp.so gss-ntlmssp-1.3.1/examples/test_user_file.txt000066400000000000000000000000361456736161100214120ustar00rootroot00000000000000TESTDOM:testuser:testpassword gss-ntlmssp-1.3.1/examples/test_user_file2.txt000066400000000000000000000001551456736161100214760ustar00rootroot00000000000000testuser:1000:3ae6ccce2a2a253f76fde78389be2ce2:d32a2901011176349b41d406dcc95a90:[U ]:LCT-1589398321 gss-ntlmssp-1.3.1/examples/test_user_file3.txt000066400000000000000000000001651456736161100215000ustar00rootroot00000000000000TESTDOM\testuser:1000:3ae6ccce2a2a253f76fde78389be2ce2:d32a2901011176349b41d406dcc95a90:[U ]:LCT-1589398321 gss-ntlmssp-1.3.1/external/000077500000000000000000000000001456736161100156425ustar00rootroot00000000000000gss-ntlmssp-1.3.1/external/ax_check_zlib.m4000066400000000000000000000124501456736161100206730ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_zlib.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_ZLIB([action-if-found], [action-if-not-found]) # # DESCRIPTION # # This macro searches for an installed zlib library. If nothing was # specified when calling configure, it searches first in /usr/local and # then in /usr, /opt/local and /sw. If the --with-zlib=DIR is specified, # it will try to find it in DIR/include/zlib.h and DIR/lib/libz.a. If # --without-zlib is specified, the library is not searched at all. # # If either the header file (zlib.h) or the library (libz) is not found, # shell commands 'action-if-not-found' is run. If 'action-if-not-found' is # not specified, the configuration exits on error, asking for a valid zlib # installation directory or --without-zlib. # # If both header file and library are found, shell commands # 'action-if-found' is run. If 'action-if-found' is not specified, the # default action appends '-I${ZLIB_HOME}/include' to CPFLAGS, appends # '-L$ZLIB_HOME}/lib' to LDFLAGS, prepends '-lz' to LIBS, and calls # AC_DEFINE(HAVE_LIBZ). You should use autoheader to include a definition # for this symbol in a config.h file. Sample usage in a C/C++ source is as # follows: # # #ifdef HAVE_LIBZ # #include # #endif /* HAVE_LIBZ */ # # LICENSE # # Copyright (c) 2008 Loic Dachary # Copyright (c) 2010 Bastien Chevreux # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 14 AU_ALIAS([CHECK_ZLIB], [AX_CHECK_ZLIB]) AC_DEFUN([AX_CHECK_ZLIB], # # Handle user hints # [AC_MSG_CHECKING(if zlib is wanted) zlib_places="/usr/local /usr /opt/local /sw" AC_ARG_WITH([zlib], [ --with-zlib=DIR root directory path of zlib installation @<:@defaults to /usr/local or /usr if not found in /usr/local@:>@ --without-zlib to disable zlib usage completely], [if test "$withval" != no ; then AC_MSG_RESULT(yes) if test -d "$withval" then zlib_places="$withval $zlib_places" else AC_MSG_WARN([Sorry, $withval does not exist, checking usual places]) fi else zlib_places= AC_MSG_RESULT(no) fi], [AC_MSG_RESULT(yes)]) # # Locate zlib, if wanted # if test -n "${zlib_places}" then # check the user supplied or any other more or less 'standard' place: # Most UNIX systems : /usr/local and /usr # MacPorts / Fink on OSX : /opt/local respectively /sw for ZLIB_HOME in ${zlib_places} ; do if test -f "${ZLIB_HOME}/include/zlib.h"; then break; fi ZLIB_HOME="" done ZLIB_OLD_LDFLAGS=$LDFLAGS ZLIB_OLD_CPPFLAGS=$CPPFLAGS if test -n "${ZLIB_HOME}"; then LDFLAGS="$LDFLAGS -L${ZLIB_HOME}/lib" CPPFLAGS="$CPPFLAGS -I${ZLIB_HOME}/include" fi AC_LANG_SAVE AC_LANG_C AC_CHECK_LIB([z], [inflateEnd], [zlib_cv_libz=yes], [zlib_cv_libz=no]) AC_CHECK_HEADER([zlib.h], [zlib_cv_zlib_h=yes], [zlib_cv_zlib_h=no]) AC_LANG_RESTORE if test "$zlib_cv_libz" = "yes" && test "$zlib_cv_zlib_h" = "yes" then # # If both library and header were found, action-if-found # m4_ifblank([$1],[ CPPFLAGS="$CPPFLAGS -I${ZLIB_HOME}/include" LDFLAGS="$LDFLAGS -L${ZLIB_HOME}/lib" LIBS="-lz $LIBS" AC_DEFINE([HAVE_LIBZ], [1], [Define to 1 if you have `z' library (-lz)]) ],[ # Restore variables LDFLAGS="$ZLIB_OLD_LDFLAGS" CPPFLAGS="$ZLIB_OLD_CPPFLAGS" $1 ]) else # # If either header or library was not found, action-if-not-found # m4_default([$2],[ AC_MSG_ERROR([either specify a valid zlib installation with --with-zlib=DIR or disable zlib usage with --without-zlib]) ]) fi fi ]) gss-ntlmssp-1.3.1/external/ax_pthread.m4000066400000000000000000000304401456736161100202240ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also link it with them as well. e.g. you should link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threads programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 17 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) AC_MSG_RESULT($ax_pthread_ok) if test x"$ax_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" ;; *-darwin*) ax_pthread_flags="-pthread $ax_pthread_flags" ;; esac if test x"$ax_pthread_ok" = xno; then for flag in $ax_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) if test x"$ax_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([#include static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($ax_pthread_ok) if test "x$ax_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $attr; return attr /* ; */])], [attr_name=$attr; break], []) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *-osf* | *-hpux*) flag="-D_REENTRANT";; *solaris*) if test "$GCC" = "yes"; then flag="-D_REENTRANT" else flag="-mt -D_REENTRANT" fi ;; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], ax_cv_PTHREAD_PRIO_INHERIT, [ AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) else PTHREAD_CC=$CC fi else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$ax_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD gss-ntlmssp-1.3.1/external/docbook.m4000066400000000000000000000017101456736161100175230ustar00rootroot00000000000000dnl Checks for tools needed to generate manual pages AC_DEFUN([CHECK_XML_TOOLS], [ AC_PATH_PROG([XSLTPROC], [xsltproc]) if test ! -x "$XSLTPROC"; then AC_MSG_ERROR([Could not find xsltproc]) fi AC_PATH_PROG([XMLLINT], [xmllint]) if test ! -x "$XMLLINT"; then AC_MSG_ERROR([Could not find xmllint]) fi AC_PATH_PROG([XMLCATALOG], [xmlcatalog]) if test ! -x "$XMLCATALOG"; then AC_MSG_ERROR([Could not find xmlcatalog]) fi ]) dnl Usage: dnl CHECK_STYLESHEET_URI(FILE, URI, [FRIENDLY-NAME]) dnl Checks if the XML catalog given by FILE exists and dnl if a particular URI appears in the XML catalog AC_DEFUN([CHECK_STYLESHEET], [ AC_CHECK_FILE($1, [], [AC_MSG_ERROR([could not find XML catalog])]) AC_MSG_CHECKING([for ifelse([$3],,[$2],[$3]) in XML catalog]) if AC_RUN_LOG([$XMLCATALOG --noout "$1" "$2" >&2]); then AC_MSG_RESULT([yes]) else AC_MSG_ERROR([could not find ifelse([$3],,[$2],[$3]) in XML catalog]) fi ]) gss-ntlmssp-1.3.1/external/m4_ax_check_openssl.m4000066400000000000000000000101321456736161100220110ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_openssl.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_OPENSSL([action-if-found[, action-if-not-found]]) # # DESCRIPTION # # Look for OpenSSL in a number of default spots, or in a user-selected # spot (via --with-openssl). Sets # # OPENSSL_INCLUDES to the include directives required # OPENSSL_LIBS to the -l directives required # OPENSSL_LDFLAGS to the -L or -R flags required # # and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately # # This macro sets OPENSSL_INCLUDES such that source files should use the # openssl/ directory in include directives: # # #include # # LICENSE # # Copyright (c) 2009,2010 Zmanda Inc. # Copyright (c) 2009,2010 Dustin J. Mitchell # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 8 AU_ALIAS([CHECK_SSL], [AX_CHECK_OPENSSL]) AC_DEFUN([AX_CHECK_OPENSSL], [ found=false AC_ARG_WITH([openssl], [AS_HELP_STRING([--with-openssl=DIR], [root of the OpenSSL directory])], [ case "$withval" in "" | y | ye | yes | n | no) AC_MSG_ERROR([Invalid --with-openssl value]) ;; *) ssldirs="$withval" ;; esac ], [ # if pkg-config is installed and openssl has installed a .pc file, # then use that information and don't search ssldirs AC_PATH_PROG([PKG_CONFIG], [pkg-config]) if test x"$PKG_CONFIG" != x""; then OPENSSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null` if test $? = 0; then OPENSSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null` OPENSSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null` found=true fi fi # no such luck; use some default ssldirs if ! $found; then ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr" fi ] ) # note that we #include , so the OpenSSL headers have to be in # an 'openssl' subdirectory if ! $found; then OPENSSL_INCLUDES= for ssldir in $ssldirs; do AC_MSG_CHECKING([for openssl/ssl.h in $ssldir]) if test -f "$ssldir/include/openssl/ssl.h"; then OPENSSL_INCLUDES="-I$ssldir/include" OPENSSL_LDFLAGS="-L$ssldir/lib" OPENSSL_LIBS="-lssl -lcrypto" found=true AC_MSG_RESULT([yes]) break else AC_MSG_RESULT([no]) fi done # if the file wasn't found, well, go ahead and try the link anyway -- maybe # it will just work! fi # try the preprocessor and linker with our new flags, # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS AC_MSG_CHECKING([whether compiling and linking against OpenSSL works]) echo "Trying link with OPENSSL_LDFLAGS=$OPENSSL_LDFLAGS;" \ "OPENSSL_LIBS=$OPENSSL_LIBS; OPENSSL_INCLUDES=$OPENSSL_INCLUDES" >&AS_MESSAGE_LOG_FD save_LIBS="$LIBS" save_LDFLAGS="$LDFLAGS" save_CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" LIBS="$OPENSSL_LIBS $LIBS" CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS" AC_LINK_IFELSE( [AC_LANG_PROGRAM([#include ], [SSL_new(NULL)])], [ AC_MSG_RESULT([yes]) $1 ], [ AC_MSG_RESULT([no]) $2 ]) CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" AC_SUBST([OPENSSL_INCLUDES]) AC_SUBST([OPENSSL_LIBS]) AC_SUBST([OPENSSL_LDFLAGS]) ]) gss-ntlmssp-1.3.1/external/pkg.m4000066400000000000000000000120541456736161100166670ustar00rootroot00000000000000# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # # Copyright © 2004 Scott James Remnant . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])# PKG_PROG_PKG_CONFIG # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # Check to see whether a particular set of modules exists. Similar # to PKG_CHECK_MODULES(), but does not set variables or print errors. # # # Similar to PKG_CHECK_MODULES, make sure that the first instance of # this or PKG_CHECK_MODULES is called, or make sure to call # PKG_CHECK_EXISTS manually # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_ifval([$2], [$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) # --------------------------------------------- m4_define([_PKG_CONFIG], [if test -n "$PKG_CONFIG"; then if test -n "$$1"; then pkg_cv_[]$1="$$1" else PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], [pkg_failed=yes]) fi else pkg_failed=untried fi[]dnl ])# _PKG_CONFIG # _PKG_SHORT_ERRORS_SUPPORTED # ----------------------------- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])# _PKG_SHORT_ERRORS_SUPPORTED # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES might not happen, you should be sure to include an # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac # # # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"` else $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD ifelse([$4], , [AC_MSG_ERROR(dnl [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT ])], [AC_MSG_RESULT([no]) $4]) elif test $pkg_failed = untried; then ifelse([$4], , [AC_MSG_FAILURE(dnl [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])], [$4]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) ifelse([$3], , :, [$3]) fi[]dnl ])# PKG_CHECK_MODULES gss-ntlmssp-1.3.1/external/platform.m4000066400000000000000000000023061456736161100177310ustar00rootroot00000000000000AC_ARG_WITH([os], [AC_HELP_STRING([--with-os=OS_TYPE], [Type of your operation system (fedora|redhat|suse|gentoo)])] ) osname="" if test x"$with_os" != x ; then if test x"$with_os" = xfedora || \ test x"$with_os" = xredhat || \ test x"$with_os" = xsuse || \ test x"$with_os" = xgentoo || \ test x"$with_os" = xdebian ; then osname=$with_os else AC_MSG_ERROR([Illegal value -$with_os- for option --with-os]) fi fi if test x"$osname" = x ; then if test -f /etc/fedora-release ; then osname="fedora" elif test -f /etc/redhat-release ; then osname="redhat" elif test -f /etc/SuSE-release ; then osname="suse" elif test -f /etc/debian_version ; then osname="debian" elif test -f /etc/gentoo-release ; then osname="gentoo" fi AC_MSG_NOTICE([Detected operating system type: $osname]) fi AM_CONDITIONAL([HAVE_FEDORA], [test x"$osname" = xfedora]) AM_CONDITIONAL([HAVE_REDHAT], [test x"$osname" = xredhat]) AM_CONDITIONAL([HAVE_SUSE], [test x"$osname" = xsuse]) AM_CONDITIONAL([HAVE_DEBIAN], [test x"$osname" = xdebian]) AM_CONDITIONAL([HAVE_GENTOO], [test x"$osname" = xgentoo]) gss-ntlmssp-1.3.1/external/sizes.m4000066400000000000000000000020551456736161100172430ustar00rootroot00000000000000# Solaris needs HAVE_LONG_LONG defined AC_CHECK_TYPES(long long) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(char) AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long long) if test $ac_cv_sizeof_long_long -lt 8 ; then AC_MSG_ERROR([SSSD requires long long of 64-bits]) fi AC_CHECK_TYPE(uint_t, unsigned int) AC_CHECK_TYPE(int8_t, char) AC_CHECK_TYPE(uint8_t, unsigned char) AC_CHECK_TYPE(int16_t, short) AC_CHECK_TYPE(uint16_t, unsigned short) if test $ac_cv_sizeof_int -eq 4 ; then AC_CHECK_TYPE(int32_t, int) AC_CHECK_TYPE(uint32_t, unsigned int) elif test $ac_cv_size_long -eq 4 ; then AC_CHECK_TYPE(int32_t, long) AC_CHECK_TYPE(uint32_t, unsigned long) else AC_MSG_ERROR([LIBREPLACE no 32-bit type found]) fi AC_CHECK_TYPE(int64_t, long long) AC_CHECK_TYPE(uint64_t, unsigned long long) AC_CHECK_TYPE(size_t, unsigned int) AC_CHECK_TYPE(ssize_t, int) AC_CHECK_SIZEOF(off_t) AC_CHECK_SIZEOF(size_t) AC_CHECK_SIZEOF(ssize_t) AC_CHECK_TYPE(intptr_t, long long) AC_CHECK_TYPE(uintptr_t, unsigned long long) AC_CHECK_TYPE(ptrdiff_t, unsigned long long) gss-ntlmssp-1.3.1/external/wbclient.m4000066400000000000000000000007121456736161100177130ustar00rootroot00000000000000dnl A macro to check the availability of Winbind client libraries AC_DEFUN([AM_CHECK_WBCLIENT], [ PKG_CHECK_MODULES(WBC, wbclient, [AC_DEFINE([HAVE_WBCLIENT], [1], [Wbclient support is available]) ], AC_MSG_ERROR("wbclient headers not found")) AC_SUBST(WBC_CFLAGS) AC_SUBST(WBC_LIBS) ]) gss-ntlmssp-1.3.1/man/000077500000000000000000000000001456736161100145735ustar00rootroot00000000000000gss-ntlmssp-1.3.1/man/Makefile.am000066400000000000000000000100601456736161100166240ustar00rootroot00000000000000# The following variable is dependent on placement of this file top_builddir = .. ############ # MANPAGES # ############ #Special Rules: export SGML_CATALOG_FILES DOCBOOK_XSLT = http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl XMLLINT_FLAGS = --catalogs --postvalid --nonet --xinclude --noout XSLTPROC_FLAGS = --catalogs --xinclude --nonet man_MANS = \ gssntlmssp.8 EXTRA_DIST = $(man_MANS:%=%.xml) $(wildcard $(srcdir)/include/*.xml) SUFFIXES = .1.xml .1 .3.xml .3 .5.xml .5 .8.xml .8 .1.xml.1: $(XMLLINT) $(XMLLINT_FLAGS) $< $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(DOCBOOK_XSLT) $< .3.xml.3: $(XMLLINT) $(XMLLINT_FLAGS) $< $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(DOCBOOK_XSLT) $< .5.xml.5: $(XMLLINT) $(XMLLINT_FLAGS) $< $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(DOCBOOK_XSLT) $< .8.xml.8: $(XMLLINT) $(XMLLINT_FLAGS) $< $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(DOCBOOK_XSLT) $< clean-local: rm -f $(man_MANS) ######################## # MANPAGE TRANSLATIONS # ######################## # #PO4A=@PO4A@ #SED=@SED@ # #PACKAGE_DOC=gssproxy-docs # #POTFILE = po/$(PACKAGE_DOC).pot #PO4A_CONFIG = po/po4a.cfg # ## Extract the list of languages from the po4a config file. #LINGUAS_DIST = `$(SED) -ne 's/^.*\[po4a_langs\] \(.*\)$$/\1/p' $(srcdir)/$(PO4A_CONFIG)` # ## If the user has not defined it let's use the default. #LINGUAS ?= $(LINGUAS_DIST) # #PO4A_COMMON_OPTS = --option doctype=docbook \ # --package-name $(PACKAGE_DOC) \ # --variable builddir=$(CURDIR) \ # --package-version $(PACKAGE_VERSION) \ # --msgid-bugs-address simo@samba.org \ # --copyright-holder "Simo Sorce`" # #PO4A_BUILD_OPTS = $(PO4A_COMMON_OPTS) --no-backups # #EXTRA_DIST += \ # $(POTFILE)\ # $(PO4A_CONFIG) # #XML_DOC = $(wildcard $(srcdir)/*.xml) $(wildcard $(srcdir)/include/*.xml) # #if HAVE_PO4A # ## FIXME: Use a stamp file until po4a supports them internally. #man.stamp: $(XML_DOC) $(POTFILE) $(PO4A_CONFIG) # cd $(srcdir) && \ # $(PO4A) $(PO4A_BUILD_OPTS) $(PO4A_CONFIG) # touch $@ # #update-po: # cd $(srcdir) && \ # $(PO4A) $(PO4A_BUILD_OPTS) --force $(PO4A_CONFIG) # #dist-hook: man.stamp # if [ -f man.stamp ]; then \ # cp man.stamp $(distdir); \ # for lang in $(LINGUAS_DIST); do \ # cp $(srcdir)/po/$$lang.po $(distdir)/po; \ # $(mkdir_p) $(distdir)/$$lang; \ # cp -r $(builddir)/$$lang $(distdir)/; \ # done; \ # else \ # cp $(srcdir)/man.stamp $(distdir); \ # for lang in $(LINGUAS_DIST); do \ # cp $(srcdir)/po/$$lang.po $(distdir)/po; \ # $(mkdir_p) $(distdir)/$$lang; \ # cp -r $(srcdir)/$$lang $(distdir)/; \ # done; \ # fi # # #clean-local-no: #clean-local-yes: # for lang in $(LINGUAS); do \ # if [ -d $$lang ]; then \ # rm -rf $$lang; \ # fi \ # done # rm -f $(man_MANS) # rm -f man.stamp # #else # #man.stamp: $(XML_DOC) # touch $@ # #clean-local-no: #clean-local-yes: # rm -f $(man_MANS) # rm -f man.stamp # #endif # #clean-local: clean-local-@USE_NLS@ #distclean-local: clean-local-@USE_NLS@ #mostlyclean-local: clean-local-@USE_NLS@ #maintainer-clean-local: clean-local-@USE_NLS@ # ## Generate translated manual pages #all-local: all-local-@USE_NLS@ #all-local-no: #all-local-yes: man.stamp # if [ -z $$recursion ]; then \ # for lang in $(LINGUAS); do \ # if [ -d $$lang ]; then \ # sources=$$(ls -1 $$lang/*.xml); \ # manpages=$$(echo $$sources | $(SED) 's/\.xml//g'); \ # $(MAKE) recursion=1 man_MANS="$$manpages"; \ # fi \ # done \ # fi # #install-data-local: install-data-local-@USE_NLS@ #install-data-local-no: #install-data-local-yes: # for lang in $(LINGUAS); do \ # if [ -d $$lang ]; then \ # sources=$$(ls -1 $$lang/*.xml); \ # manpages=$$(echo $$sources | $(SED) 's/\.xml//g'); \ # $(MAKE) install-man \ # mandir="$(mandir)/$$lang" \ # man_MANS="$$manpages"; \ # fi \ # done # #uninstall-local: uninstall-local-@USE_NLS@ #uninstall-local-no: #uninstall-local-yes: # for lang in $(LINGUAS); do \ # if [ -d $$lang ]; then \ # sources=$$(ls -1 $$lang/*.xml); \ # manpages=$$(echo $$sources | $(SED) 's/\.xml//g'); \ # $(MAKE) uninstall-man \ # mandir="$(mandir)/$$lang" \ # man_MANS="$$manpages"; \ # fi \ # done gss-ntlmssp-1.3.1/man/gssntlmssp.8.xml000066400000000000000000000101431456736161100176770ustar00rootroot00000000000000 GSSAPI NTLM mechanism manual page GSS-NTLMSSP GSS-NTLMSSP - http://ssimo.org/gss-ntlmssp gssntlmssp 8 gssntlmssp GSSAPI NTLM Security Service Provider mechanism gssntlmssp_v1 2.16.840.1.113730.3.8.15.X /usr/lib64/gssntlmssp/gssntlmssp.so options DESCRIPTION A GSSAPI NTLM mechanism that allows to perform NTLM authentication in GSSAPI programs. ENVIRONMENT VARIABLES NTLM_USER_FILE A file containing users and their password hashes. The file formats supported are the one used by Heimdal for its NTLM backend, and the samba password file format. NETBIOS_COMPUTER_NAME Set the NETBIOS (short) Computer name used in NTLM packets NETBIOS_DOMAIN_NAME Set the NETBIOS (short) Domain name used in NTLM packets NTLMUSER Set the NTLM User name used for authentication on initiation of the security context USER The default source for the User name used on initiation of the security context LM_COMPAT_LEVEL The LM Compatibility Level enforced during security context negotiation. The default level is 3 GSSNTLMSSP_DEBUG The name of a file to send debug logs GSSNTLMSSP_WB_TLS_CTX This variable is used to control whether thread local contexts are used for winbindd communication or whether a single process context is used. Setting the value of this variable to 0 disables per-thread contexts. Because creating Winbindd contexts is an expensive operation this is used as an optimization in applications that can insure a single thread is used, or already handle their own locking. The default behavior is to create a context for each thread, this behavior can be restored by setting the environment variable back to the value 1 SEE ALSO gssapi8 . gss-ntlmssp-1.3.1/po/000077500000000000000000000000001456736161100144365ustar00rootroot00000000000000gss-ntlmssp-1.3.1/po/LINGUAS000066400000000000000000000000141456736161100154560ustar00rootroot00000000000000en_GB ko ka gss-ntlmssp-1.3.1/po/Makefile.am000066400000000000000000000016541456736161100165000ustar00rootroot00000000000000 LINGUAS = @LINGUAS@ MOFILES = $(LINGUAS:%=%.mo) POFILES = $(LINGUAS:%=%.po) noinst_DATA = $(MOFILES) SUFFIXES = .mo .po.mo: rm -f && $(MSGFMT) -o $@ $< clean-local: rm -f $(MOFILES) install-data-hook: all linguas="$(LINGUAS)"; \ for l in $$linguas; do \ dir="$(DESTDIR)$(localedir)/$$l/LC_MESSAGES"; \ $(mkdir_p) $$dir; \ echo Installing $$l.mo to $$dir/$(PACKAGE).mo ; \ $(INSTALL_DATA) $$l.mo $$dir/$(PACKAGE).mo; \ done uninstall-hook: linguas="$(LINGUAS)"; \ for l in $$linguas; do \ file="$(DESTDIR)$(localedir)/$$l/LC_MESSAGES/$(PACKAGE).mo"; \ if [ -r "$$file" ]; then \ echo "Removing $$file"; rm -f "$$file"; \ fi ; \ done # $(PACKAGE).pot is built by a rule in the parent directory Makefile # This rule isn't needed but is here for convenience if manually invoked .PHONY: $(PACKAGE).pot $(PACKAGE).pot: $(MAKE) -C .. po/$@ EXTRA_DIST = $(POFILES) LINGUAS DISTCLEANFILES=$(PACKAGE).pot gss-ntlmssp-1.3.1/po/en_GB.po000066400000000000000000000071721456736161100157570ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: gssntlmssp 0.5.0\n" "Report-Msgid-Bugs-To: simo@samba.org\n" "POT-Creation-Date: 2014-08-11 10:01+0100\n" "PO-Revision-Date: 2014-08-11 09:50+0100\n" "Last-Translator: David Woodhouse \n" "Language: en_GB\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: src/gss_err.c:24 msgid "Unknown Error" msgstr "Unknown Error" #. ERR_DECODE #: src/gss_err.c:25 msgid "Failed to decode data" msgstr "Failed to decode data" #. ERR_ENCODE #: src/gss_err.c:26 msgid "Failed to encode data" msgstr "Failed to encode data" #. ERR_CRYPTO #: src/gss_err.c:27 msgid "Crypto routine failure" msgstr "Crypto routine failure" #. ERR_NOARG #: src/gss_err.c:28 msgid "A required argument is missing" msgstr "A required argument is missing" #. ERR_BADARG #: src/gss_err.c:29 msgid "Invalid value in argument" msgstr "Invalid value in argument" #. ERR_NONAME #: src/gss_err.c:30 msgid "Name is empty" msgstr "Name is empty" #. ERR_NOSRVNAME #: src/gss_err.c:31 msgid "Not a server name" msgstr "Not a server name" #. ERR_NOUSRNAME #: src/gss_err.c:32 msgid "Not a user name" msgstr "Not a user name" #. ERR_BADLMLEVEL #: src/gss_err.c:33 msgid "Bad LM compatibility Level" msgstr "Bad LM compatibility Level" #. ERR_IMPOSSIBLE #: src/gss_err.c:34 msgid "An impossible error occurred" msgstr "An impossible error occurred" #. ERR_BADCTX #: src/gss_err.c:35 msgid "Invalid or incomplete context" msgstr "Invalid or incomplete context" #. ERR_WRONGCTX #: src/gss_err.c:36 msgid "Wrong context type" msgstr "Wrong context type" #. ERR_WRONGMSG #: src/gss_err.c:37 msgid "Wrong message type" msgstr "Wrong message type" #. ERR_REQNEGFLAG #: src/gss_err.c:38 msgid "A required Negotiate flag was not provided" msgstr "A required Negotiate flag was not provided" #. ERR_FAILNEGFLAGS #: src/gss_err.c:39 msgid "Failed to negotiate a common set of flags" msgstr "Failed to negotiate a common set of flags" #. ERR_BADNEGFLAGS #: src/gss_err.c:40 msgid "Invalid combinations of negotiate flags" msgstr "Invalid combinations of negotiate flags" #. ERR_NOSRVCRED #: src/gss_err.c:41 msgid "Not a server credential type" msgstr "Not a server credential type" #. ERR_NOUSRCRED #: src/gss_err.c:42 msgid "Not a user credential type" msgstr "Not a user credential type" #. ERR_BADCRED #: src/gss_err.c:43 msgid "Invalid or unknown credential" msgstr "Invalid or unknown credential" #. ERR_NOTOKEN #: src/gss_err.c:44 msgid "Empty or missing token" msgstr "Empty or missing token" #. ERR_NOTSUPPORTED #: src/gss_err.c:45 msgid "Feature not supported" msgstr "Feature not supported" #. ERR_NOTAVAIL #: src/gss_err.c:46 msgid "Feature not available" msgstr "Feature not available" #. ERR_NAMETOOLONG #: src/gss_err.c:47 msgid "Name is too long" msgstr "Name is too long" #. ERR_NOBINDINGS #: src/gss_err.c:48 msgid "Required channel bingings are not available" msgstr "Required channel bingings are not available" #. ERR_TIMESKEW #: src/gss_err.c:49 msgid "Server and client clocks are too far apart" msgstr "Server and client clocks are too far apart" #. ERR_EXPIRED #: src/gss_err.c:50 msgid "Expired" msgstr "Expired" #. ERR_KEYLEN #: src/gss_err.c:51 msgid "Invalid key length" msgstr "Invalid key length" #. ERR_NONTLMV1 #: src/gss_err.c:52 msgid "NTLM version 1 not allowed" msgstr "NTLM version 1 not allowed" #. ERR_NOUSRFOUND #: src/gss_err.c:53 msgid "User not found" msgstr "User not found" gss-ntlmssp-1.3.1/po/gssntlmssp.pot000066400000000000000000000057061456736161100174070ustar00rootroot00000000000000# GSS-NTLMSSP Translation Template file. # Copyright (C) 2015 Simo Sorce # This file is distributed under the same license as the gssntlmssp package. # Simo Sorce , 2015. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: gssntlmssp 0.5.0\n" "Report-Msgid-Bugs-To: simo@samba.org\n" "POT-Creation-Date: 2015-02-20 09:49-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: src/gss_err.c:24 msgid "Unknown Error" msgstr "" #. ERR_DECODE #: src/gss_err.c:25 msgid "Failed to decode data" msgstr "" #. ERR_ENCODE #: src/gss_err.c:26 msgid "Failed to encode data" msgstr "" #. ERR_CRYPTO #: src/gss_err.c:27 msgid "Crypto routine failure" msgstr "" #. ERR_NOARG #: src/gss_err.c:28 msgid "A required argument is missing" msgstr "" #. ERR_BADARG #: src/gss_err.c:29 msgid "Invalid value in argument" msgstr "" #. ERR_NONAME #: src/gss_err.c:30 msgid "Name is empty" msgstr "" #. ERR_NOSRVNAME #: src/gss_err.c:31 msgid "Not a server name" msgstr "" #. ERR_NOUSRNAME #: src/gss_err.c:32 msgid "Not a user name" msgstr "" #. ERR_BADLMLEVEL #: src/gss_err.c:33 msgid "Bad LM compatibility Level" msgstr "" #. ERR_IMPOSSIBLE #: src/gss_err.c:34 msgid "An impossible error occurred" msgstr "" #. ERR_BADCTX #: src/gss_err.c:35 msgid "Invalid or incomplete context" msgstr "" #. ERR_WRONGCTX #: src/gss_err.c:36 msgid "Wrong context type" msgstr "" #. ERR_WRONGMSG #: src/gss_err.c:37 msgid "Wrong message type" msgstr "" #. ERR_REQNEGFLAG #: src/gss_err.c:38 msgid "A required Negotiate flag was not provided" msgstr "" #. ERR_FAILNEGFLAGS #: src/gss_err.c:39 msgid "Failed to negotiate a common set of flags" msgstr "" #. ERR_BADNEGFLAGS #: src/gss_err.c:40 msgid "Invalid combinations of negotiate flags" msgstr "" #. ERR_NOSRVCRED #: src/gss_err.c:41 msgid "Not a server credential type" msgstr "" #. ERR_NOUSRCRED #: src/gss_err.c:42 msgid "Not a user credential type" msgstr "" #. ERR_BADCRED #: src/gss_err.c:43 msgid "Invalid or unknown credential" msgstr "" #. ERR_NOTOKEN #: src/gss_err.c:44 msgid "Empty or missing token" msgstr "" #. ERR_NOTSUPPORTED #: src/gss_err.c:45 msgid "Feature not supported" msgstr "" #. ERR_NOTAVAIL #: src/gss_err.c:46 msgid "Feature not available" msgstr "" #. ERR_NAMETOOLONG #: src/gss_err.c:47 msgid "Name is too long" msgstr "" #. ERR_NOBINDINGS #: src/gss_err.c:48 msgid "Required channel bingings are not available" msgstr "" #. ERR_TIMESKEW #: src/gss_err.c:49 msgid "Server and client clocks are too far apart" msgstr "" #. ERR_EXPIRED #: src/gss_err.c:50 msgid "Expired" msgstr "" #. ERR_KEYLEN #: src/gss_err.c:51 msgid "Invalid key length" msgstr "" #. ERR_NONTLMV1 #: src/gss_err.c:52 msgid "NTLM version 1 not allowed" msgstr "" #. ERR_NOUSRFOUND #: src/gss_err.c:53 msgid "User not found" msgstr "" gss-ntlmssp-1.3.1/po/ka.po000066400000000000000000000132241456736161100153730ustar00rootroot00000000000000# GSS-NTLMSSP Translation Template file. # Copyright (C) 2015 Simo Sorce # This file is distributed under the same license as the gssntlmssp package. # Simo Sorce , 2015. # Temuri Doghonadze , 2022. msgid "" msgstr "" "Project-Id-Version: gssntlmssp 0.5.0\n" "Report-Msgid-Bugs-To: simo@samba.org\n" "POT-Creation-Date: 2015-02-20 09:49-0500\n" "PO-Revision-Date: 2022-12-29 11:19+0000\n" "Last-Translator: Temuri Doghonadze \n" "Language-Team: Georgian \n" "Language: ka\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.15\n" #: src/gss_err.c:24 msgid "Unknown Error" msgstr "უცნობი შეცდომა" #. ERR_DECODE #: src/gss_err.c:25 msgid "Failed to decode data" msgstr "მონაცემების გაშიფვრის შეცდომა" #. ERR_ENCODE #: src/gss_err.c:26 msgid "Failed to encode data" msgstr "მონაცემების დაშიფვრის შეცდომა" #. ERR_CRYPTO #: src/gss_err.c:27 msgid "Crypto routine failure" msgstr "კრიპტოფუნქციის შეცდომა" #. ERR_NOARG #: src/gss_err.c:28 msgid "A required argument is missing" msgstr "აუცილებელი არგუმენტი ვერ ვიპოვე" #. ERR_BADARG #: src/gss_err.c:29 msgid "Invalid value in argument" msgstr "არგუმენტის არასწორი მნიშვნელობა" #. ERR_NONAME #: src/gss_err.c:30 msgid "Name is empty" msgstr "სახელი ცარიელია" #. ERR_NOSRVNAME #: src/gss_err.c:31 msgid "Not a server name" msgstr "არ წარმოადგენს სერვერის სახელს" #. ERR_NOUSRNAME #: src/gss_err.c:32 msgid "Not a user name" msgstr "არ წარმოადგენს მომხმარებლის სახელს" #. ERR_BADLMLEVEL #: src/gss_err.c:33 msgid "Bad LM compatibility Level" msgstr "LM-ის თავსებადობის არასწორი დონე" #. ERR_IMPOSSIBLE #: src/gss_err.c:34 msgid "An impossible error occurred" msgstr "დაფიქსირდა შეუძლებელი შეცდომა" #. ERR_BADCTX #: src/gss_err.c:35 msgid "Invalid or incomplete context" msgstr "არასწორი ან დაუსრულებელი კონტექსტი" #. ERR_WRONGCTX #: src/gss_err.c:36 msgid "Wrong context type" msgstr "კონტექსტის არასწორი ტიპი" #. ERR_WRONGMSG #: src/gss_err.c:37 msgid "Wrong message type" msgstr "შეტყობინების არასწორი ტიპი" #. ERR_REQNEGFLAG #: src/gss_err.c:38 msgid "A required Negotiate flag was not provided" msgstr "აუცილებელი ალამი Negotiate მოწოდებული არ იყო" #. ERR_FAILNEGFLAGS #: src/gss_err.c:39 msgid "Failed to negotiate a common set of flags" msgstr "ალმების ზოგადი ნაკრების მიმოცვლის შეცდომა" #. ERR_BADNEGFLAGS #: src/gss_err.c:40 msgid "Invalid combinations of negotiate flags" msgstr "მიმოცვლის ალმების არასწორი კომბინაციები" #. ERR_NOSRVCRED #: src/gss_err.c:41 msgid "Not a server credential type" msgstr "არ წარმოადგენს სერვერის ავტორიზაციის დეტალის ტიპს" #. ERR_NOUSRCRED #: src/gss_err.c:42 msgid "Not a user credential type" msgstr "არ წარმოადგენს მომხმარებლის ავტორიზაციის დეტალის ტიპს" #. ERR_BADCRED #: src/gss_err.c:43 msgid "Invalid or unknown credential" msgstr "არასწორი ან უცნობი ავტორიზაციის დეტალი" #. ERR_NOTOKEN #: src/gss_err.c:44 msgid "Empty or missing token" msgstr "ცარიელი ან ნაკლული კოდი" #. ERR_NOTSUPPORTED #: src/gss_err.c:45 msgid "Feature not supported" msgstr "თვისება მხარდაუჭერელია" #. ERR_NOTAVAIL #: src/gss_err.c:46 msgid "Feature not available" msgstr "თვისება მიუწვდომელია" #. ERR_NAMETOOLONG #: src/gss_err.c:47 msgid "Name is too long" msgstr "სახელი ძალიან გრძელია" #. ERR_NOBINDINGS #: src/gss_err.c:48 msgid "Required channel bingings are not available" msgstr "აუცილებელი არხის მიბმები მიუწვდომელია" #. ERR_TIMESKEW #: src/gss_err.c:49 msgid "Server and client clocks are too far apart" msgstr "სერვერის და კლიენტის დროები ძალიან განსხვავდება" #. ERR_EXPIRED #: src/gss_err.c:50 msgid "Expired" msgstr "ვადაგასული" #. ERR_KEYLEN #: src/gss_err.c:51 msgid "Invalid key length" msgstr "გასაღების არასწორი სიგრძე" #. ERR_NONTLMV1 #: src/gss_err.c:52 msgid "NTLM version 1 not allowed" msgstr "NTLM-ის პირველი ვერსიის გამოყენება დაუშვებელია" #. ERR_NOUSRFOUND #: src/gss_err.c:53 msgid "User not found" msgstr "ვერ ვიპოვე მომხმარებელი" gss-ntlmssp-1.3.1/po/ko.po000066400000000000000000000102161456736161100154070ustar00rootroot00000000000000# GSS-NTLMSSP Translation Template file. # Copyright (C) 2015 Simo Sorce # This file is distributed under the same license as the gssntlmssp package. # Simo Sorce , 2015. # simmon , 2021. msgid "" msgstr "" "Project-Id-Version: gssntlmssp 0.5.0\n" "Report-Msgid-Bugs-To: simo@samba.org\n" "POT-Creation-Date: 2015-02-20 09:49-0500\n" "PO-Revision-Date: 2021-04-14 07:02+0000\n" "Last-Translator: simmon \n" "Language-Team: Korean \n" "Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.5.3\n" #: src/gss_err.c:24 msgid "Unknown Error" msgstr "알 수 없는 오류" #. ERR_DECODE #: src/gss_err.c:25 msgid "Failed to decode data" msgstr "자료를 디코드하는데 실패하였습니다" #. ERR_ENCODE #: src/gss_err.c:26 msgid "Failed to encode data" msgstr "자료를 인코드하는데 실패하였습니다" #. ERR_CRYPTO #: src/gss_err.c:27 msgid "Crypto routine failure" msgstr "암호화 루틴 실패" #. ERR_NOARG #: src/gss_err.c:28 msgid "A required argument is missing" msgstr "필요한 인수가 누락되었습니다" #. ERR_BADARG #: src/gss_err.c:29 msgid "Invalid value in argument" msgstr "인수에 잘못된 값" #. ERR_NONAME #: src/gss_err.c:30 msgid "Name is empty" msgstr "이름이 비었습니다" #. ERR_NOSRVNAME #: src/gss_err.c:31 msgid "Not a server name" msgstr "서버 이름이 없습니다" #. ERR_NOUSRNAME #: src/gss_err.c:32 msgid "Not a user name" msgstr "사용자 이름이 없습니다" #. ERR_BADLMLEVEL #: src/gss_err.c:33 msgid "Bad LM compatibility Level" msgstr "나쁜 LM 호환성 레벨" #. ERR_IMPOSSIBLE #: src/gss_err.c:34 msgid "An impossible error occurred" msgstr "불가능한 오류가 발생했습니다" #. ERR_BADCTX #: src/gss_err.c:35 msgid "Invalid or incomplete context" msgstr "잘못된 또는 완전하지 않은 내용" #. ERR_WRONGCTX #: src/gss_err.c:36 msgid "Wrong context type" msgstr "잘못된 내용 유형" #. ERR_WRONGMSG #: src/gss_err.c:37 msgid "Wrong message type" msgstr "잘못된 메세지 유형" #. ERR_REQNEGFLAG #: src/gss_err.c:38 msgid "A required Negotiate flag was not provided" msgstr "필수 협상 플래그가 제공되지 않습니다" #. ERR_FAILNEGFLAGS #: src/gss_err.c:39 msgid "Failed to negotiate a common set of flags" msgstr "플래그 공통 설정 협상에 실패하였습니다" #. ERR_BADNEGFLAGS #: src/gss_err.c:40 msgid "Invalid combinations of negotiate flags" msgstr "협상 플래그의 유효하지 않은 조합" #. ERR_NOSRVCRED #: src/gss_err.c:41 msgid "Not a server credential type" msgstr "서버 인증 유형이 아닙니다" #. ERR_NOUSRCRED #: src/gss_err.c:42 msgid "Not a user credential type" msgstr "사용자 인증 유형이 아닙니다" #. ERR_BADCRED #: src/gss_err.c:43 msgid "Invalid or unknown credential" msgstr "잘못된 또는 알지 못하는 인증" #. ERR_NOTOKEN #: src/gss_err.c:44 msgid "Empty or missing token" msgstr "비었거나 누락된 토큰" #. ERR_NOTSUPPORTED #: src/gss_err.c:45 msgid "Feature not supported" msgstr "기능을 지원하지 않습니다" #. ERR_NOTAVAIL #: src/gss_err.c:46 msgid "Feature not available" msgstr "사용 할 수 없는 상태" #. ERR_NAMETOOLONG #: src/gss_err.c:47 msgid "Name is too long" msgstr "이름이 너무 길어요" #. ERR_NOBINDINGS #: src/gss_err.c:48 msgid "Required channel bingings are not available" msgstr "필요한 채널을 과도하게 사용 할 수 없습니다" #. ERR_TIMESKEW #: src/gss_err.c:49 msgid "Server and client clocks are too far apart" msgstr "서버와 클라이언트 시간이 너무 차이나 있습니다" #. ERR_EXPIRED #: src/gss_err.c:50 msgid "Expired" msgstr "만료됨" #. ERR_KEYLEN #: src/gss_err.c:51 msgid "Invalid key length" msgstr "잘못된 키 길이" #. ERR_NONTLMV1 #: src/gss_err.c:52 msgid "NTLM version 1 not allowed" msgstr "NTML 버전 1은 허용하지 않습니다" #. ERR_NOUSRFOUND #: src/gss_err.c:53 msgid "User not found" msgstr "사용자를 찾지 못했습니다" gss-ntlmssp-1.3.1/po/zanata.xml000066400000000000000000000060221456736161100164360ustar00rootroot00000000000000 https://fedora.zanata.org/ gss-ntlmssp master gettext . . sq ar as ast bal eu bn bn-IN brx bs br bg ca zh-CN zh-HK zh-TW kw kw-GB cs da nl en-GB eo et fi fr gl ka de el gu he hi hu is id ia it ja kn kk km ky ko lt nds mk mai ms ml mr mn ne nb nn or pa fa pl pt pt-BR ro ru sr sr@latin si sk sl es sv tg ta te bo tr uk ur wba cy lv kw@uccor kw@kkcor af am be hr de-CH th vi zu ilo nso tw yo anp gss-ntlmssp-1.3.1/src/000077500000000000000000000000001456736161100146075ustar00rootroot00000000000000gss-ntlmssp-1.3.1/src/crypto.c000066400000000000000000000215111456736161100162730ustar00rootroot00000000000000/* Copyright 2013-2022 Simo Sorce , see COPYING for license */ #include #include #include #include #include #include #include #include "crypto.h" /* legacy provider with openssl 3.0 */ #if OPENSSL_VERSION_NUMBER >= 0x30000000L # include # include #endif int RAND_BUFFER(struct ntlm_buffer *random) { int ret; ret = RAND_bytes(random->data, random->length); if (ret != 1) { return ERR_CRYPTO; } return 0; } int HMAC_MD5_IOV(struct ntlm_buffer *key, struct ntlm_iov *iov, struct ntlm_buffer *result) { EVP_MD_CTX* ctx = NULL; EVP_PKEY* pkey = NULL; size_t i; int ret = 0; if (result->length != 16) return EINVAL; pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key->data, key->length); if (!pkey) { ret = ERR_CRYPTO; goto done; } ctx = EVP_MD_CTX_new(); if (!ctx) { ret = ERR_CRYPTO; goto done; } ret = EVP_DigestSignInit(ctx, NULL, EVP_md5(), NULL, pkey); if (ret != 1) { ret = ERR_CRYPTO; goto done; } for (i = 0; i < iov->num; i++) { ret = EVP_DigestSignUpdate(ctx, iov->data[i]->data, iov->data[i]->length); if (ret != 1) { ret = ERR_CRYPTO; goto done; } } ret = EVP_DigestSignFinal(ctx, result->data, &result->length); if (ret != 1) { ret = ERR_CRYPTO; goto done; } ret = 0; done: EVP_MD_CTX_free(ctx); EVP_PKEY_free(pkey); return ret; } int HMAC_MD5(struct ntlm_buffer *key, struct ntlm_buffer *payload, struct ntlm_buffer *result) { struct ntlm_iov iov; iov.num = 1; iov.data = &payload; return HMAC_MD5_IOV(key, &iov, result); } #if OPENSSL_VERSION_NUMBER >= 0x30000000L typedef struct ossl3_library_context { OSSL_LIB_CTX *libctx; OSSL_PROVIDER *legacy_provider; OSSL_PROVIDER *default_provider; } ossl3_context_t; static pthread_once_t global_ossl3_ctx_init = PTHREAD_ONCE_INIT; static ossl3_context_t *global_ossl3_ctx = NULL; static void init_global_ossl3_ctx(void) { ossl3_context_t *ctx = OPENSSL_malloc(sizeof(ossl3_context_t)); if (!ctx) return; ctx->libctx = OSSL_LIB_CTX_new(); if (!ctx->libctx) { OPENSSL_free(ctx); return; } /* Load both legacy and default provider as both may be needed */ /* if they fail keep going and an error will be raised when we try to * fetch the cipher later */ ctx->legacy_provider = OSSL_PROVIDER_load(ctx->libctx, "legacy"); ctx->default_provider = OSSL_PROVIDER_load(ctx->libctx, "default"); global_ossl3_ctx = ctx; } static ossl3_context_t *get_ossl3_ctx() { int ret; ret = pthread_once(&global_ossl3_ctx_init, init_global_ossl3_ctx); if (ret != 0) { return NULL; } return global_ossl3_ctx; } __attribute__((destructor)) static void free_ossl3_ctx() { ossl3_context_t *ctx = global_ossl3_ctx; if (ctx == NULL) return; if (ctx->legacy_provider) OSSL_PROVIDER_unload(ctx->legacy_provider); if (ctx->default_provider) OSSL_PROVIDER_unload(ctx->default_provider); if (ctx->libctx) OSSL_LIB_CTX_free(ctx->libctx); OPENSSL_free(ctx); } #endif static int mdx_hash(const EVP_MD *type, struct ntlm_buffer *payload, struct ntlm_buffer *result) { EVP_MD_CTX *ctx; unsigned int len; int ret; if (result->length != 16) return EINVAL; ctx = EVP_MD_CTX_new(); if (!ctx) { ret = ERR_CRYPTO; goto done; } EVP_MD_CTX_init(ctx); ret = EVP_DigestInit_ex(ctx, type, NULL); if (ret == 0) { ret = ERR_CRYPTO; goto done; } ret = EVP_DigestUpdate(ctx, payload->data, payload->length); if (ret == 0) { ret = ERR_CRYPTO; goto done; } ret = EVP_DigestFinal_ex(ctx, result->data, &len); if (ret == 0) { ret = ERR_CRYPTO; goto done; } ret = 0; done: if (ctx) EVP_MD_CTX_free(ctx); return ret; } int MD4_HASH(struct ntlm_buffer *payload, struct ntlm_buffer *result) { #if OPENSSL_VERSION_NUMBER >= 0x30000000L ossl3_context_t *ossl3_ctx = NULL; EVP_MD *md; int ret; ossl3_ctx = get_ossl3_ctx(); if (ossl3_ctx == NULL) { ret = ERR_CRYPTO; goto done; } md = EVP_MD_fetch(ossl3_ctx->libctx, "MD4", ""); if (md == NULL) { ret = ERR_CRYPTO; goto done; } ret = mdx_hash(md, payload, result); done: return ret; #else return mdx_hash(EVP_md4(), payload, result); #endif } int MD5_HASH(struct ntlm_buffer *payload, struct ntlm_buffer *result) { return mdx_hash(EVP_md5(), payload, result); } struct ntlm_rc4_handle { RC4_KEY key; }; int RC4_INIT(struct ntlm_buffer *rc4_key, enum ntlm_cipher_mode mode, struct ntlm_rc4_handle **out) { struct ntlm_rc4_handle *handle; handle = malloc(sizeof(struct ntlm_rc4_handle)); if (!handle) return ENOMEM; RC4_set_key(&handle->key, rc4_key->length, rc4_key->data); *out = handle; return 0; } int RC4_UPDATE(struct ntlm_rc4_handle *handle, struct ntlm_buffer *in, struct ntlm_buffer *out) { if (out->length < in->length) return EINVAL; if (in->length > 0) { RC4(&handle->key, in->length, in->data, out->data); } out->length = in->length; return 0; } void RC4_FREE(struct ntlm_rc4_handle **handle) { if (!handle || !*handle) return; safezero((uint8_t *)(&((*handle)->key)), sizeof(RC4_KEY)); safefree(*handle); } int RC4_EXPORT(struct ntlm_rc4_handle *handle, struct ntlm_buffer *out) { RC4_INT *data = (RC4_INT *)out->data; int len = 258 * sizeof(RC4_INT); if (out->length < len) return EINVAL; data[0] = handle->key.x; data[1] = handle->key.y; memcpy(&data[2], handle->key.data, sizeof(RC4_INT) * 256); out->length = len; return 0; } int RC4_IMPORT(struct ntlm_rc4_handle **_handle, struct ntlm_buffer *in) { struct ntlm_rc4_handle *handle; RC4_INT *data = (RC4_INT *)in->data; int len = 258 * sizeof(RC4_INT); if (in->length != len) return EINVAL; handle = malloc(sizeof(struct ntlm_rc4_handle)); if (!handle) return ENOMEM; handle->key.x = data[0]; handle->key.y = data[1]; memcpy(handle->key.data, &data[2], sizeof(RC4_INT) * 256); *_handle = handle; return 0; } int RC4K(struct ntlm_buffer *key, enum ntlm_cipher_mode mode, struct ntlm_buffer *payload, struct ntlm_buffer *result) { struct ntlm_rc4_handle *handle; int ret; if (result->length < payload->length) return EINVAL; ret = RC4_INIT(key, mode, &handle); if (ret) return ret; ret = RC4_UPDATE(handle, payload, result); RC4_FREE(&handle); return ret; } int WEAK_DES(struct ntlm_buffer *key, struct ntlm_buffer *payload, struct ntlm_buffer *result) { DES_key_schedule schedule; DES_cblock key8; if ((key->length != 7) || (payload->length != 8) || (result->length != 8)) { return EINVAL; } /* Undocumented shuffle needed before calling DES_set_key_unchecked */ key8[0] = key->data[0]; key8[1] = (key->data[0] << 7) | (key->data[1] >> 1); key8[2] = (key->data[1] << 6) | (key->data[2] >> 2); key8[3] = (key->data[2] << 5) | (key->data[3] >> 3); key8[4] = (key->data[3] << 4) | (key->data[4] >> 4); key8[5] = (key->data[4] << 3) | (key->data[5] >> 5); key8[6] = (key->data[5] << 2) | (key->data[6] >> 6); key8[7] = (key->data[6] << 1); DES_set_key_unchecked(&key8, &schedule); DES_ecb_encrypt((DES_cblock *)payload->data, (DES_cblock *)result->data, &schedule, 1); return 0; } int DESL(struct ntlm_buffer *key, struct ntlm_buffer *payload, struct ntlm_buffer *result) { uint8_t buf7[7]; struct ntlm_buffer key7; struct ntlm_buffer res8; if ((key->length != 16) || (payload->length != 8) || (result->length != 24)) { return EINVAL; } /* part 1 */ key7.data = key->data; key7.length = 7; res8.data = result->data; res8.length = 8; WEAK_DES(&key7, payload, &res8); /* part 2 */ key7.data = &key->data[7]; key7.length = 7; res8.data = &result->data[8]; res8.length = 8; WEAK_DES(&key7, payload, &res8); /* part 3 */ memcpy(buf7, &key->data[14], 2); memset(&buf7[2], 0, 5); key7.data = buf7; key7.length = 7; res8.data = &result->data[16]; res8.length = 8; WEAK_DES(&key7, payload, &res8); return 0; } uint32_t CRC32(uint32_t crc, struct ntlm_buffer *payload) { return crc32(crc, payload->data, payload->length); } gss-ntlmssp-1.3.1/src/crypto.h000066400000000000000000000113611456736161100163020ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ #ifndef _SRC_CRYPTO_H_ #define _SRC_CRYPTO_H_ #include #include "ntlm_common.h" /** * @brief Fills the provided preallocated buffer with random data * * @param random A preallocated buffer, length determines the amount of * random bytes the function will return. * * @return 0 for success or error otherwise */ int RAND_BUFFER(struct ntlm_buffer *random); /** * @brief HMAC-MD5 function * * @param key The authentication key * @param payload The payload to be authenticated * @param result A preallocated 16 byte buffer * * @return 0 on success or ERR_CRYPTO */ int HMAC_MD5(struct ntlm_buffer *key, struct ntlm_buffer *payload, struct ntlm_buffer *result); /** * @brief HMAC-MD5 function that operats on multiple buffers * * @param key The authentication key * @param iov The IOVec of the payloads to authenticate * @param result A preallocated 16 byte buffer * * @return 0 on success or ERR_CRYPTO */ int HMAC_MD5_IOV(struct ntlm_buffer *key, struct ntlm_iov *iov, struct ntlm_buffer *result); /** * @brief MD4 Hash Function * * @param payload The payoad to hash * @param result The resulting Hash (preallocated, length must be 16) * * @return 0 on success or an error */ int MD4_HASH(struct ntlm_buffer *payload, struct ntlm_buffer *result); /** * @brief MD5 Hash Function * * @param payload The payoad to hash * @param result The resulting Hash (preallocated, length must be 16) * * @return 0 on success or an error */ int MD5_HASH(struct ntlm_buffer *payload, struct ntlm_buffer *result); /** * @brief RC4 engine initialization * * @param rc4_key The encryption/decryption key * @param mode The cipher mode * @param state Allocated ntlm_rc4_state structure * * @return 0 on success or error */ int RC4_INIT(struct ntlm_buffer *rc4_key, enum ntlm_cipher_mode mode, struct ntlm_rc4_handle **handle); /** * @brief RC4 encrypt/decrypt function * * @param state The state initialized by RC4_INIT * @param in Input buffer (plaintext for enc or ciphertext for dec) * @param out Resulting buffer. Must be preallocated. * * @return 0 on success or error */ int RC4_UPDATE(struct ntlm_rc4_handle *handle, struct ntlm_buffer *in, struct ntlm_buffer *out); /** * @brief Release an rc4 handle * * @param state A pointer to the rc4 handle */ void RC4_FREE(struct ntlm_rc4_handle **handle); /** * @brief Exports the RC4 state * * @param handle The RC4 handle to export from * @param out A buffer at least 258 bytes long * * @return 0 on success or EAGAIN if the buffer is too small */ int RC4_EXPORT(struct ntlm_rc4_handle *handle, struct ntlm_buffer *out); /** * @brief Import an RC4 state * * @param handle A new ntlm_rc4_handle on success * @param in A buffer containing an exported state * * @return 0 on success or EINVAL if the buffer is not an exported state */ int RC4_IMPORT(struct ntlm_rc4_handle **handle, struct ntlm_buffer *in); /** * @brief RC4 encryption/decryption all in one * * @param key The encryption/decryption key * @param mode The cipher mode * @param payload Input buffer (plaintext for enc or ciphertext for dec) * @param result Resulting buffer. Must be preallocated. * * @return 0 on success or error */ int RC4K(struct ntlm_buffer *key, enum ntlm_cipher_mode mode, struct ntlm_buffer *payload, struct ntlm_buffer *result); /** * @brief Extreely weak DES encryption * * @param key The encryption/decryption key (must be 8 bytes) * @param payload Input buffer (must be 8 bytes) * @param result Output buffer (must be 8 bytes) * * @return 0 on success or EINVAL if any buffer is not 8 in length */ int WEAK_DES(struct ntlm_buffer *key, struct ntlm_buffer *payload, struct ntlm_buffer *result); /** * @brief A sad weak encryption/expansion scheme needed by NTLMv1 * * @param key The encryption/decryption key (must be 16 bytes) * @param payload Input buffer (must be 8 bytes) * @param result Output buffer (must be 24 bytes) * * @return 0 on success or EINVAL if any buffer is not of proper length */ int DESL(struct ntlm_buffer *key, struct ntlm_buffer *payload, struct ntlm_buffer *result); /** * @brief The CRC32 checksum * * @param crc Initial crc, usually 0 * @param payload The data to checksum * * @return The resulting CRC. */ uint32_t CRC32(uint32_t crc, struct ntlm_buffer *payload); #endif /* _SRC_CRYPTO_H_ */ gss-ntlmssp-1.3.1/src/debug.c000066400000000000000000000047701456736161100160510ustar00rootroot00000000000000/* Copyright (C) 2014 GSS-NTLMSSP contributors, see COPYING for license */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "gssapi_ntlmssp.h" #define OPEN_FLAGS O_WRONLY | O_CREAT | O_APPEND| O_CLOEXEC #define discard_const(ptr) ((void *)((uintptr_t)(ptr))) static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER; bool gssntlm_debug_initialized = false; int gssntlm_debug_fd = -1; void gssntlm_debug_init(void) { char *env; if (gssntlm_debug_initialized) return; pthread_mutex_lock(&debug_mutex); env = secure_getenv("GSSNTLMSSP_DEBUG"); if (env) { gssntlm_debug_fd = open(env, OPEN_FLAGS, 0660); } gssntlm_debug_initialized = true; pthread_mutex_unlock(&debug_mutex); } void gssntlm_debug_printf(const char *fmt, ...) { va_list ap; if (gssntlm_debug_fd == -1) return; va_start(ap, fmt); vdprintf(gssntlm_debug_fd, fmt, ap); va_end(ap); fdatasync(gssntlm_debug_fd); } static int gssntlm_debug_enable(const char *filename) { int old_debug_fd = gssntlm_debug_fd; int new_debug_fd = -1; int ret = 0; pthread_mutex_lock(&debug_mutex); gssntlm_debug_initialized = true; new_debug_fd = open(filename, OPEN_FLAGS, 0660); if (new_debug_fd == -1) { ret = errno; } gssntlm_debug_fd = new_debug_fd; if (old_debug_fd != -1) { close(old_debug_fd); } pthread_mutex_unlock(&debug_mutex); return ret; } static int gssntlm_debug_disable(void) { int old_debug_fd = gssntlm_debug_fd; int ret = 0; pthread_mutex_lock(&debug_mutex); gssntlm_debug_fd = -1; if (old_debug_fd != -1) { ret = close(old_debug_fd); } pthread_mutex_unlock(&debug_mutex); return ret; } gss_OID_desc gssntlm_debug_oid = { GSS_NTLMSSP_DEBUG_OID_LENGTH, discard_const(GSS_NTLMSSP_DEBUG_OID_STRING) }; int gssntlm_debug_invoke(gss_buffer_t value) { char filename[PATH_MAX] = { 0 }; if (value->length > PATH_MAX - 1) { return EINVAL; } if ((value->length != 0) && (((char *)value->value)[0] != '\0')) { memcpy(filename, value->value, value->length); filename[value->length] = '\0'; } if (filename[0] == '\0') { return gssntlm_debug_disable(); } return gssntlm_debug_enable(filename); } gss-ntlmssp-1.3.1/src/debug.h000066400000000000000000000023611456736161100160500ustar00rootroot00000000000000/* Copyright (C) 2014 GSS-NTLMSSP contributors, see COPYING for license */ #ifndef _GSSNTLMSSP_DEBUG_H_ #define _GSSNTLMSSP_DEBUG_H_ #include #include extern gss_OID_desc gssntlm_debug_oid; extern bool gssntlm_debug_initialized; extern int gssntlm_debug_fd; void gssntlm_debug_init(void); void gssntlm_debug_printf(const char *fmt, ...); #define unlikely(x) __builtin_expect(!!(x), 0) static inline int debug_gss_errors(const char *function, const char *file, unsigned int line, unsigned int maj, unsigned int min) { if (unlikely(gssntlm_debug_initialized == false)) { gssntlm_debug_init(); } if (unlikely(gssntlm_debug_fd != -1)) { gssntlm_debug_printf("[%ld] %s: %s() @ %s:%u [%u:%u]\n", (long)time(NULL), GSS_ERROR(maj) ? "ERROR" : "ALLOK", function, file, line, maj, min); } return 0; } #define DEBUG_GSS_ERRORS(maj, min) \ debug_gss_errors(__FUNCTION__, __FILE__, __LINE__, maj, min) int gssntlm_debug_invoke(gss_buffer_t value); #endif /* _GSSNTLMSSP_DEBUG_H_ */ gss-ntlmssp-1.3.1/src/external.c000066400000000000000000000062331456736161100166010ustar00rootroot00000000000000/* Copyright (C) 2014 GSS-NTLMSSP contributors, see COPYING for License */ #include "config.h" #include #include "gss_ntlmssp.h" #if HAVE_WBCLIENT #include "gss_ntlmssp_winbind.h" #endif void *external_get_context(void) { #if HAVE_WBCLIENT return winbind_get_context(); #else return NULL; #endif } void external_free_context(void *ctx) { #if HAVE_WBCLIENT winbind_free_context(ctx); #else return; #endif } uint32_t external_netbios_get_names(void *ctx, char **computer, char **domain) { #if HAVE_WBCLIENT return winbind_get_names(ctx, computer, domain); #else return ERR_NOTAVAIL; #endif } uint32_t external_get_creds(void *ctx, struct gssntlm_name *name, struct gssntlm_cred *cred) { void *ectx = NULL; uint32_t ret; if (ctx == NULL) { ectx = external_get_context(); } else { ectx = ctx; } #if HAVE_WBCLIENT ret = winbind_get_creds(ectx, name, cred); #else ret = ERR_NOTAVAIL; #endif if (ctx == NULL) { external_free_context(ectx); } return ret; } uint32_t external_cli_auth(struct gssntlm_ctx *ctx, struct gssntlm_cred *cred, uint32_t in_flags, gss_channel_bindings_t input_chan_bindings) { #if HAVE_WBCLIENT return winbind_cli_auth(ctx->external_context, cred->cred.external.user.data.user.name, cred->cred.external.user.data.user.domain, input_chan_bindings, in_flags, &ctx->neg_flags, &ctx->nego_msg, &ctx->chal_msg, &ctx->auth_msg, &ctx->exported_session_key); #else return ERR_NOTAVAIL; #endif } uint32_t external_srv_auth(struct gssntlm_ctx *ctx, struct gssntlm_cred *cred, struct ntlm_buffer *nt_chal_resp, struct ntlm_buffer *lm_chal_resp, struct ntlm_key *session_base_key) { #if HAVE_WBCLIENT uint8_t challenge[8]; uint8_t *chal_ptr; /* NOTE: in the ntlmv1 extended security case, winbindd wants a * pre-digested challenge, this is arguably a bug as it has all * the data needed to compute it by itself ... just cope */ if (is_ntlm_v1(nt_chal_resp) && (ctx->neg_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) ) { int ret; ret = ntlm_compute_ext_sec_challenge(ctx->server_chal, lm_chal_resp->data, challenge); if (ret) return ret; chal_ptr = challenge; } else { chal_ptr = ctx->server_chal; } return winbind_srv_auth(ctx->external_context, cred->cred.external.user.data.user.name, cred->cred.external.user.data.user.domain, ctx->workstation, chal_ptr, nt_chal_resp, lm_chal_resp, session_base_key, &ctx->source_name.attrs); #else return ERR_NOTAVAIL; #endif } gss-ntlmssp-1.3.1/src/gss_auth.c000066400000000000000000000374301456736161100165770ustar00rootroot00000000000000/* Copyright (C) 2014 GSS-NTLMSSP contributors, see COPYING for License */ #include #include #include "gss_ntlmssp.h" uint32_t gssntlm_cli_auth(uint32_t *minor_status, struct gssntlm_ctx *ctx, struct gssntlm_cred *cred, struct ntlm_buffer *target_info, uint32_t in_flags, gss_channel_bindings_t input_chan_bindings) { struct ntlm_buffer nt_chal_resp = { 0 }; struct ntlm_buffer lm_chal_resp = { 0 }; struct ntlm_buffer client_target_info = { 0 }; struct ntlm_key key_exchange_key = { .length = 16 }; struct ntlm_key encrypted_random_session_key = { .length = 16 }; struct ntlm_buffer enc_sess_key = { 0 }; struct ntlm_buffer auth_mic = { NULL, 16 }; uint8_t micbuf[16]; struct ntlm_buffer mic = { micbuf, 16 }; bool add_mic = false; bool key_exch; uint32_t retmaj; uint32_t retmin; switch (cred->type) { case GSSNTLM_CRED_USER: if (ctx->gss_flags & GSS_C_ANON_FLAG) { /* Anonymous auth, empty responses */ memset(&nt_chal_resp, 0, sizeof(nt_chal_resp)); lm_chal_resp.data = malloc(1); if (!lm_chal_resp.data) { set_GSSERR(ENOMEM); goto done; } lm_chal_resp.data[0] = 0; lm_chal_resp.length = 1; } else if (gssntlm_sec_v2_ok(ctx)) { /* ### NTLMv2 ### */ uint8_t client_chal[8]; struct ntlm_buffer cli_chal = { client_chal, 8 }; struct ntlm_key ntlmv2_key = { .length = 16 }; struct ntlm_buffer nt_proof = { 0 }; struct ntlm_buffer cb = { 0 }; uint64_t srv_time = 0; if (target_info->length == 0 && input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) { set_GSSERRS(ERR_NOBINDINGS, GSS_S_BAD_BINDINGS); goto done; } if (target_info->length > 0) { bool *add_mic_ptr = NULL; bool protect; if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) { if (input_chan_bindings->initiator_addrtype != 0 || input_chan_bindings->initiator_address.length != 0 || input_chan_bindings->acceptor_addrtype != 0 || input_chan_bindings->acceptor_address.length != 0 || input_chan_bindings->application_data.length == 0) { set_GSSERRS(ERR_BADARG, GSS_S_BAD_BINDINGS); goto done; } cb.length = input_chan_bindings->application_data.length; cb.data = input_chan_bindings->application_data.value; } protect = in_flags & (NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL); if (protect) { if (ctx->int_flags & NTLMSSP_CTX_FLAG_SPNEGO_CAN_MIC) { add_mic_ptr = &add_mic; } } retmin = ntlm_process_target_info( ctx->ntlm, protect, target_info, ctx->target_name.data.server.spn, &cb, &client_target_info, &srv_time, add_mic_ptr); if (retmin) { set_GSSERR(retmin); goto done; } if (srv_time != 0) { long int tdiff; tdiff = ntlm_timestamp_now() - srv_time; if ((tdiff / 10000000) > MAX_CHALRESP_LIFETIME) { set_GSSERRS(ERR_TIMESKEW, GSS_S_CONTEXT_EXPIRED); goto done; } } } /* Random client challenge */ retmin = RAND_BUFFER(&cli_chal); if (retmin) { set_GSSERR(retmin); goto done; } /* NTLMv2 Key */ retmin = NTOWFv2(ctx->ntlm, &cred->cred.user.nt_hash, cred->cred.user.user.data.user.name, cred->cred.user.user.data.user.domain, &ntlmv2_key); if (retmin) { set_GSSERR(retmin); goto done; } /* NTLMv2 Response */ retmin = ntlmv2_compute_nt_response(&ntlmv2_key, ctx->server_chal, client_chal, srv_time, &client_target_info, &nt_chal_resp); if (retmin) { set_GSSERR(retmin); goto done; } if (target_info->length == 0) { /* LMv2 Response * (only sent if challenge response has no target_info) */ retmin = ntlmv2_compute_lm_response(&ntlmv2_key, ctx->server_chal, client_chal, &lm_chal_resp); if (retmin) { set_GSSERR(retmin); goto done; } } /* The NT proof is the first 16 bytes */ nt_proof.data = nt_chal_resp.data; nt_proof.length = 16; /* The Session Base Key */ /* In NTLMv2 the Key Exchange Key is the Session Base Key */ retmin = ntlmv2_session_base_key(&ntlmv2_key, &nt_proof, &key_exchange_key); if (retmin) { set_GSSERR(retmin); goto done; } } else { /* ### NTLMv1 ### */ uint8_t client_chal[8]; struct ntlm_buffer cli_chal = { client_chal, 8 }; struct ntlm_key session_base_key = { .length = 16 }; bool NoLMResponseNTLMv1 = !gssntlm_sec_lm_ok(ctx); bool ext_sec; nt_chal_resp.length = 24; nt_chal_resp.data = calloc(1, nt_chal_resp.length); lm_chal_resp.length = 24; lm_chal_resp.data = calloc(1, lm_chal_resp.length); if (!nt_chal_resp.data || !lm_chal_resp.data) { set_GSSERR(ENOMEM); goto done; } /* Random client challenge */ retmin = RAND_BUFFER(&cli_chal); if (retmin) { set_GSSERR(retmin); goto done; } ext_sec = (in_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY); retmin = ntlm_compute_nt_response(&cred->cred.user.nt_hash, ext_sec, ctx->server_chal, client_chal, &nt_chal_resp); if (retmin) { set_GSSERR(retmin); goto done; } if (!ext_sec && NoLMResponseNTLMv1) { memcpy(lm_chal_resp.data, nt_chal_resp.data, 24); } else { retmin = ntlm_compute_lm_response(&cred->cred.user.lm_hash, ext_sec, ctx->server_chal, client_chal, &lm_chal_resp); if (retmin) { set_GSSERR(retmin); goto done; } } retmin = ntlm_session_base_key(&cred->cred.user.nt_hash, &session_base_key); if (retmin) { set_GSSERR(retmin); goto done; } retmin = KXKEY(ctx->ntlm, ext_sec, (in_flags & NTLMSSP_NEGOTIATE_LM_KEY), (in_flags & NTLMSSP_REQUEST_NON_NT_SESSION_KEY), ctx->server_chal, &cred->cred.user.lm_hash, &session_base_key, &lm_chal_resp, &key_exchange_key); if (retmin) { set_GSSERR(retmin); goto done; } } key_exch = (in_flags & NTLMSSP_NEGOTIATE_KEY_EXCH); retmin = ntlm_exported_session_key(&key_exchange_key, key_exch, &ctx->exported_session_key); if (retmin) { set_GSSERR(retmin); goto done; } if (key_exch) { retmin = ntlm_encrypted_session_key(&key_exchange_key, &ctx->exported_session_key, &encrypted_random_session_key); if (retmin) { set_GSSERR(retmin); goto done; } } /* in_flags all verified, assign as current flags */ ctx->neg_flags |= in_flags; enc_sess_key.data = encrypted_random_session_key.data; enc_sess_key.length = encrypted_random_session_key.length; retmin = ntlm_encode_auth_msg(ctx->ntlm, ctx->neg_flags, &lm_chal_resp, &nt_chal_resp, cred->cred.user.user.data.user.domain, cred->cred.user.user.data.user.name, ctx->workstation, &enc_sess_key, add_mic ? &auth_mic : NULL, &ctx->auth_msg); if (retmin) { set_GSSERR(retmin); goto done; } /* Now we need to calculate the MIC, because the MIC is part of the * message it protects, ntlm_encode_auth_msg() always add a zeroeth * buffer, however it returns in data_mic the pointer to the actual * area in the auth_msg that points at the mic, so we can backfill */ if (add_mic) { retmin = ntlm_mic(&ctx->exported_session_key, &ctx->nego_msg, &ctx->chal_msg, &ctx->auth_msg, &mic); if (retmin) { set_GSSERR(retmin); goto done; } /* now that we have the mic, copy it into the auth message */ memcpy(auth_mic.data, mic.data, 16); /* Make sure SPNEGO gets to know it has to add mechlistMIC too */ ctx->int_flags |= NTLMSSP_CTX_FLAG_AUTH_WITH_MIC; } set_GSSERRS(0, GSS_S_COMPLETE); break; case GSSNTLM_CRED_EXTERNAL: retmin = external_cli_auth(ctx, cred, in_flags, input_chan_bindings); if (retmin) { set_GSSERR(retmin); goto done; } set_GSSERRS(0, GSS_S_COMPLETE); break; default: set_GSSERR(ERR_NOUSRCRED); } done: ntlm_free_buffer_data(&client_target_info); ntlm_free_buffer_data(&nt_chal_resp); ntlm_free_buffer_data(&lm_chal_resp); return GSSERR(); } bool is_ntlm_v1(struct ntlm_buffer *nt_chal_resp) { return (nt_chal_resp->length == 24); } uint32_t gssntlm_srv_auth(uint32_t *minor_status, struct gssntlm_ctx *ctx, struct gssntlm_cred *cred, struct ntlm_buffer *nt_chal_resp, struct ntlm_buffer *lm_chal_resp, struct ntlm_key *key_exchange_key) { struct ntlm_key session_base_key = { .length = 16 }; struct ntlm_key ntlmv2_key = { .length = 16 }; struct ntlm_buffer nt_proof = { 0 }; uint32_t retmaj, retmin; const char *domstr; bool ntlm_v1; bool ext_sec; int retries; if (key_exchange_key->length != 16) { return GSSERRS(ERR_KEYLEN, GSS_S_FAILURE); } ntlm_v1 = is_ntlm_v1(nt_chal_resp); if (ntlm_v1 && !gssntlm_sec_lm_ok(ctx) && !gssntlm_sec_ntlm_ok(ctx)) { return GSSERRS(ERR_NONTLMV1, GSS_S_FAILURE); } ext_sec = (ctx->neg_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY); switch (cred->type) { case GSSNTLM_CRED_USER: if (ntlm_v1) { uint8_t client_chal[8] = { 0 }; if (ext_sec) { memcpy(client_chal, lm_chal_resp->data, 8); } retmin = ntlm_verify_nt_response(nt_chal_resp, &cred->cred.user.nt_hash, ext_sec, ctx->server_chal, client_chal); if (retmin && gssntlm_sec_lm_ok(ctx)) { retmin = ntlm_verify_lm_response(lm_chal_resp, &cred->cred.user.lm_hash, ext_sec, ctx->server_chal, client_chal); } } else for (retries = 2; retries > 0; retries--) { if (retries == 2) { domstr = cred->cred.user.user.data.user.domain; } else { domstr = NULL; } /* NTLMv2 Key */ retmin = NTOWFv2(ctx->ntlm, &cred->cred.user.nt_hash, cred->cred.user.user.data.user.name, domstr, &ntlmv2_key); if (retmin) { set_GSSERR(retmin); goto done; } /* NTLMv2 Response */ retmin = ntlmv2_verify_nt_response(nt_chal_resp, &ntlmv2_key, ctx->server_chal); if (retmin && gssntlm_sec_lm_ok(ctx)) { /* LMv2 Response */ retmin = ntlmv2_verify_lm_response(lm_chal_resp, &ntlmv2_key, ctx->server_chal); } if (retmin == 0) break; } if (retmin) { set_GSSERR(retmin); goto done; } if (ntlm_v1) { retmin = ntlm_session_base_key(&cred->cred.user.nt_hash, &session_base_key); if (retmin) { set_GSSERR(retmin); goto done; } break; } /* The NT proof is the first 16 bytes */ nt_proof.data = nt_chal_resp->data; nt_proof.length = 16; /* The Session Base Key */ /* In NTLMv2 the Key Exchange Key is the Session Base Key */ retmin = ntlmv2_session_base_key(&ntlmv2_key, &nt_proof, &session_base_key); if (retmin) { set_GSSERR(retmin); goto done; } break; case GSSNTLM_CRED_EXTERNAL: retmin = external_srv_auth(ctx, cred, nt_chal_resp, lm_chal_resp, &session_base_key); if (retmin) { set_GSSERR(retmin); goto done; } break; default: set_GSSERR(ERR_NOUSRCRED); goto done; } if (ntlm_v1) { retmin = KXKEY(ctx->ntlm, ext_sec, (ctx->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY), (ctx->neg_flags & NTLMSSP_REQUEST_NON_NT_SESSION_KEY), ctx->server_chal, &cred->cred.user.lm_hash, &session_base_key, lm_chal_resp, key_exchange_key); if (retmin) { set_GSSERR(retmin); goto done; } } else { memcpy(key_exchange_key->data, session_base_key.data, session_base_key.length); } set_GSSERRS(0, GSS_S_COMPLETE); done: return GSSERR(); } gss-ntlmssp-1.3.1/src/gss_creds.c000066400000000000000000000574161456736161100167440ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ #include #include #include #include #include #include #include "gss_ntlmssp.h" static int hex_to_key(const char *hex, struct ntlm_key *key) { size_t len = strlen(hex); if (len != 32) return ERR_KEYLEN; for (int i = 0; i < 16; i++) { key->data[i] = 0; for (int j = 0; j < 2; j++) { uint8_t c = hex[i*2+j]; if (c >= '0' && c <= '9') { c -= '0'; } else if (c >= 'A' && c <= 'F') { c -= 'A' - 10; } else if (c >= 'a' && c <= 'f') { c -= 'a' - 10; } else { return ERR_BADARG; } key->data[i] |= (c << ((1 - j) * 4)); } } key->length = 16; return 0; } static char *get_user_file_envvar(void) { const char *envvar; /* use the same var used by Heimdal */ envvar = getenv("NTLM_USER_FILE"); if (envvar == NULL) return NULL; return strdup(envvar); } static int get_user_file_creds(const char *filename, struct gssntlm_name *name, struct gssntlm_cred *cred) { struct gssntlm_ctx *ctx; int lm_compat_lvl = -1; char line[1024]; char *field1, *field2, *field3, *field4; char *dom, *usr, *pwd, *lm, *nt; char *p; bool found = false; FILE *f; int ret = 0; ctx = calloc(1, sizeof(struct gssntlm_ctx)); if (!ctx) return ENOMEM; lm_compat_lvl = gssntlm_get_lm_compatibility_level(); if (!gssntlm_required_security(lm_compat_lvl, ctx)) { ret = ERR_BADLMLVL; goto done; } /* Use the same file format used by Heimdal in hope to achieve * some compatibility between implementations: * Each line is one entry like the following: * DOMAIN:USERNAME:PASSWORD */ /* **OR** */ /* Use the smbpasswd file format for Samba compatibility. * Each line is one entry like the following: * NAME:UID:LM_HASH:NT_HASH:ACCT_FLAGS:TIMESTAMP * Fields after NT_HASH are ignored * * The smbpasswd format is extended to allow domain qualified names. */ /* The second format is distinguished from the first based on * the number of fields encountered. It is technically possible * to mix both formats in a single file though it is not * recommended. */ f = fopen(filename, "r"); if (!f) { ret = errno; goto done; } while(fgets(line, 1024, f)) { p = line; if (*p == '#') continue; field1 = p; p = strchr(field1, ':'); if (!p) continue; *p++ = '\0'; field2 = p; p = strchr(field2, ':'); if (!p) continue; *p++ = '\0'; field3 = p; p = strchr(field3, ':'); if (!p) { /* Assume Heimdal file format */ p = field3; strsep(&p, "\r\n"); dom = field1; usr = field2; pwd = field3; lm = NULL; nt = NULL; } else { *p++ = '\0'; field4 = p; p = strchr(field4, ':'); if (!p) continue; *p++ = '\0'; /* Assume smbpasswd file format */ dom = NULL; usr = field1; pwd = NULL; lm = field3; nt = field4; /* check if username is domain qualified */ p = strchr(usr, '\\'); if (p) { dom = usr; *p++ = '\0'; usr = p; } } /* if no name is specified use the first found */ if (name == NULL) { found = true; break; } if (name->data.user.domain && dom) { if (!ntlm_casecmp(dom, name->data.user.domain)) continue; } if (name->data.user.name) { if (!ntlm_casecmp(usr, name->data.user.name)) continue; } /* all matched (NULLs in name are wildcards) */ found = true; break; } fclose(f); if (!found) { ret = ENOENT; goto done; } cred->type = GSSNTLM_CRED_USER; cred->cred.user.user.type = GSSNTLM_NAME_USER; if (dom) { free(cred->cred.user.user.data.user.domain); cred->cred.user.user.data.user.domain = strdup(dom); if (!cred->cred.user.user.data.user.domain) { ret = ENOMEM; goto done; } } free(cred->cred.user.user.data.user.name); cred->cred.user.user.data.user.name = strdup(usr); if (!cred->cred.user.user.data.user.name) { ret = ENOMEM; goto done; } if (pwd) { cred->cred.user.nt_hash.length = 16; ret = NTOWFv1(pwd, &cred->cred.user.nt_hash); if (ret) goto done; if (gssntlm_sec_lm_ok(ctx)) { cred->cred.user.lm_hash.length = 16; ret = LMOWFv1(pwd, &cred->cred.user.lm_hash); if (ret) goto done; } } if (lm && nt) { ret = hex_to_key(nt, &cred->cred.user.nt_hash); if (ret) goto done; if (gssntlm_sec_lm_ok(ctx)) { ret = hex_to_key(lm, &cred->cred.user.lm_hash); if (ret) goto done; } } done: free(ctx); return ret; } static int get_server_creds(struct gssntlm_name *name, struct gssntlm_cred *cred) { gss_name_t gssname = NULL; gss_buffer_desc tmpbuf; uint32_t retmaj; uint32_t retmin; int ret; if (name == NULL) { tmpbuf.value = discard_const(""); tmpbuf.length = 0; ret = 0; retmaj = gssntlm_import_name_by_mech(&retmin, &gssntlm_oid, &tmpbuf, GSS_C_NT_HOSTBASED_SERVICE, &gssname); if (retmaj) return retmin; name = (struct gssntlm_name *)gssname; } cred->type = GSSNTLM_CRED_SERVER; ret = gssntlm_copy_name(name, &cred->cred.server.name); gssntlm_int_release_name((struct gssntlm_name *)gssname); return ret; } #define GENERIC_CS_PASSWORD "password" /* To support in future, RC4 Key is NT hash */ #define KRB5_CS_CLI_KEYTAB_URN "client_keytab" #define KRB5_CS_KEYTAB_URN "keytab" static int get_creds_from_store(struct gssntlm_name *name, struct gssntlm_cred *cred, gss_const_key_value_set_t cred_store) { uint32_t i; int ret; if (name) { /* special case to let server creds carry a keyfile */ if (name->type == GSSNTLM_NAME_SERVER) { const char *keyfile = NULL; cred->type = GSSNTLM_CRED_SERVER; ret = gssntlm_copy_name(name, &cred->cred.server.name); if (ret) return ret; for (i = 0; i < cred_store->count; i++) { if (strcmp(cred_store->elements[i].key, GSS_NTLMSSP_CS_KEYFILE) == 0) { keyfile = cred_store->elements[i].value; } } if (keyfile) { cred->cred.server.keyfile = strdup(keyfile); if (cred->cred.server.keyfile == NULL) { return errno; } } return 0; } if (name->type != GSSNTLM_NAME_USER) return ENOENT; ret = gssntlm_copy_name(name, &cred->cred.user.user); if (ret) return ret; } /* so far only user options can be defined in the cred_store */ cred->type = GSSNTLM_CRED_USER; for (i = 0; i < cred_store->count; i++) { if (strcmp(cred_store->elements[i].key, GSS_NTLMSSP_CS_DOMAIN) == 0) { free(cred->cred.user.user.data.user.domain); cred->cred.user.user.data.user.domain = strdup(cred_store->elements[i].value); if (!cred->cred.user.user.data.user.domain) return ENOMEM; } if (strcmp(cred_store->elements[i].key, GSS_NTLMSSP_CS_NTHASH) == 0) { ret = hex_to_key(cred_store->elements[i].value, &cred->cred.user.nt_hash); if (ret) return ret; } if ((strcmp(cred_store->elements[i].key, GSS_NTLMSSP_CS_PASSWORD) == 0) || (strcmp(cred_store->elements[i].key, GENERIC_CS_PASSWORD) == 0)) { cred->cred.user.nt_hash.length = 16; ret = NTOWFv1(cred_store->elements[i].value, &cred->cred.user.nt_hash); if (gssntlm_get_lm_compatibility_level() < 3) { cred->cred.user.lm_hash.length = 16; ret = LMOWFv1(cred_store->elements[i].value, &cred->cred.user.lm_hash); if (ret) return ret; } if (ret) return ret; } if (strcmp(cred_store->elements[i].key, GSS_NTLMSSP_CS_KEYFILE) == 0) { ret = get_user_file_creds(cred_store->elements[i].value, name, cred); if (ret) return ret; } } /* now check if a user name was found, if not error out */ if (cred->cred.user.user.data.user.name == NULL) return ENOENT; return 0; } static void gssntlm_copy_key(struct ntlm_key *dest, struct ntlm_key *src) { memcpy(dest->data, src->data, src->length); dest->length = src->length; } int gssntlm_copy_creds(struct gssntlm_cred *in, struct gssntlm_cred *out) { char *dom = NULL, *usr = NULL, *srv = NULL; int ret = 0; out->type = GSSNTLM_CRED_NONE; switch (in->type) { case GSSNTLM_CRED_NONE: break; case GSSNTLM_CRED_ANON: out->cred.anon.dummy = 1; break; case GSSNTLM_CRED_USER: ret = gssntlm_copy_name(&in->cred.user.user, &out->cred.user.user); if (ret) goto done; gssntlm_copy_key(&out->cred.user.nt_hash, &in->cred.user.nt_hash); gssntlm_copy_key(&out->cred.user.lm_hash, &in->cred.user.lm_hash); break; case GSSNTLM_CRED_SERVER: ret = gssntlm_copy_name(&in->cred.server.name, &out->cred.server.name); if (ret) goto done; break; case GSSNTLM_CRED_EXTERNAL: ret = gssntlm_copy_name(&in->cred.external.user, &out->cred.external.user); if (ret) goto done; break; } out->type = in->type; done: if (ret) { safefree(dom); safefree(usr); safefree(srv); } return ret; } void gssntlm_int_release_cred(struct gssntlm_cred *cred) { if (!cred) return; switch (cred->type) { case GSSNTLM_CRED_NONE: break; case GSSNTLM_CRED_ANON: cred->cred.anon.dummy = 0; break; case GSSNTLM_CRED_USER: gssntlm_int_release_name(&cred->cred.user.user); safezero(cred->cred.user.nt_hash.data, 16); cred->cred.user.nt_hash.length = 0; safezero(cred->cred.user.lm_hash.data, 16); cred->cred.user.lm_hash.length = 0; break; case GSSNTLM_CRED_SERVER: gssntlm_int_release_name(&cred->cred.server.name); safefree(cred->cred.server.keyfile); break; case GSSNTLM_CRED_EXTERNAL: gssntlm_int_release_name(&cred->cred.external.user); break; } } uint32_t gssntlm_acquire_cred_from(uint32_t *minor_status, void *external_context, gss_name_t desired_name, uint32_t time_req, gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_const_key_value_set_t cred_store, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, uint32_t *time_rec) { struct gssntlm_cred *cred; struct gssntlm_name *name; uint32_t retmaj; uint32_t retmin; name = (struct gssntlm_name *)desired_name; cred = calloc(1, sizeof(struct gssntlm_cred)); if (!cred) { return GSSERRS(errno, GSS_S_FAILURE); } /* FIXME: should we split the cred union and allow GSS_C_BOTH ? * It may be possible to specify get server name from env and/or * user creds from cred store at the same time, etc .. */ if (cred_usage == GSS_C_BOTH) { if (name == NULL) { cred_usage = GSS_C_ACCEPT; } else { switch (name->type) { case GSSNTLM_NAME_SERVER: cred_usage = GSS_C_ACCEPT; break; case GSSNTLM_NAME_USER: case GSSNTLM_NAME_ANON: cred_usage = GSS_C_INITIATE; break; default: set_GSSERRS(ERR_BADCRED, GSS_S_CRED_UNAVAIL); goto done; } } } if (cred_usage == GSS_C_INITIATE) { if (name != NULL && name->type != GSSNTLM_NAME_USER) { set_GSSERRS(ERR_NOUSRNAME, GSS_S_BAD_NAMETYPE); goto done; } if (cred_store != GSS_C_NO_CRED_STORE) { retmin = get_creds_from_store(name, cred, cred_store); } else { char *filename; filename = get_user_file_envvar(); if (filename) { retmin = get_user_file_creds(filename, name, cred); free(filename); } else { retmin = ENOENT; } if (retmin) { uint32_t ret; ret = external_get_creds(external_context, name, cred); if (ret != ERR_NOTAVAIL) { retmin = ret; } } } if (retmin) { set_GSSERRS(retmin, GSS_S_CRED_UNAVAIL); goto done; } } else if (cred_usage == GSS_C_ACCEPT) { if (name != NULL && name->type != GSSNTLM_NAME_SERVER) { set_GSSERRS(ERR_NOSRVNAME, GSS_S_BAD_NAMETYPE); goto done; } if (cred_store != GSS_C_NO_CRED_STORE) { retmin = get_creds_from_store(name, cred, cred_store); } else { retmin = get_server_creds(name, cred); if (retmin) { set_GSSERR(retmin); goto done; } } } else if (cred_usage == GSS_C_BOTH) { set_GSSERRS(ERR_NOTSUPPORTED, GSS_S_CRED_UNAVAIL); goto done; } else { set_GSSERRS(ERR_BADARG, GSS_S_CRED_UNAVAIL); goto done; } set_GSSERRS(0, GSS_S_COMPLETE); done: if (retmaj) { uint32_t tmpmin; gssntlm_release_cred(&tmpmin, (gss_cred_id_t *)&cred); } else { *output_cred_handle = (gss_cred_id_t)cred; if (time_rec) *time_rec = GSS_C_INDEFINITE; } return GSSERR(); } uint32_t gssntlm_acquire_cred(uint32_t *minor_status, gss_name_t desired_name, uint32_t time_req, gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, uint32_t *time_rec) { return gssntlm_acquire_cred_from(minor_status, NULL, desired_name, time_req, desired_mechs, cred_usage, GSS_C_NO_CRED_STORE, output_cred_handle, actual_mechs, time_rec); } uint32_t gssntlm_release_cred(uint32_t *minor_status, gss_cred_id_t *cred_handle) { *minor_status = 0; if (!cred_handle) return GSS_S_COMPLETE; gssntlm_int_release_cred((struct gssntlm_cred *)*cred_handle); safefree(*cred_handle); return GSS_S_COMPLETE; } uint32_t gssntlm_acquire_cred_with_password(uint32_t *minor_status, gss_name_t desired_name, gss_buffer_t password, uint32_t time_req, gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, uint32_t *time_rec) { gss_key_value_element_desc element; gss_key_value_set_desc cred_store; element.key = GENERIC_CS_PASSWORD; element.value = (const char *)password->value; cred_store.count = 1; cred_store.elements = &element; return gssntlm_acquire_cred_from(minor_status, NULL, desired_name, time_req, desired_mechs, cred_usage, &cred_store, output_cred_handle, actual_mechs, time_rec); } uint32_t gssntlm_inquire_cred(uint32_t *minor_status, gss_cred_id_t cred_handle, gss_name_t *name, uint32_t *lifetime, gss_cred_usage_t *cred_usage, gss_OID_set *mechanisms) { struct gssntlm_cred *cred = (struct gssntlm_cred *)GSS_C_NO_CREDENTIAL; uint32_t retmin, retmaj; uint32_t maj, min; if (cred_handle == GSS_C_NO_CREDENTIAL) { maj = gssntlm_acquire_cred_from(&min, NULL, NULL, GSS_C_INDEFINITE, NULL, GSS_C_INITIATE, GSS_C_NO_CRED_STORE, (gss_cred_id_t *)&cred, NULL, NULL); if (maj) { set_GSSERRS(0, GSS_S_NO_CRED); goto done; } } else { cred = (struct gssntlm_cred *)cred_handle; } if (cred->type == GSSNTLM_CRED_NONE) { set_GSSERRS(ERR_BADARG, GSS_S_NO_CRED); goto done; } if (name) { switch (cred->type) { case GSSNTLM_CRED_NONE: case GSSNTLM_CRED_ANON: *name = GSS_C_NO_NAME; break; case GSSNTLM_CRED_USER: maj = gssntlm_duplicate_name(&min, (gss_name_t)&cred->cred.user.user, name); if (maj != GSS_S_COMPLETE) { set_GSSERRS(min, maj); goto done; } break; case GSSNTLM_CRED_SERVER: maj = gssntlm_duplicate_name(&min, (gss_name_t)&cred->cred.server.name, name); if (maj != GSS_S_COMPLETE) { set_GSSERRS(min, maj); goto done; } break; case GSSNTLM_CRED_EXTERNAL: maj = gssntlm_duplicate_name(&min, (gss_name_t)&cred->cred.external.user, name); if (maj != GSS_S_COMPLETE) { set_GSSERRS(min, maj); goto done; } break; } } if (lifetime) *lifetime = GSS_C_INDEFINITE; if (cred_usage) { if (cred->type == GSSNTLM_CRED_SERVER) { *cred_usage = GSS_C_ACCEPT; } else { *cred_usage = GSS_C_INITIATE; } } if (mechanisms) { maj = gss_create_empty_oid_set(&min, mechanisms); if (maj != GSS_S_COMPLETE) { set_GSSERRS(min, maj); gss_release_name(&min, name); goto done; } maj = gss_add_oid_set_member(&min, discard_const(&gssntlm_oid), mechanisms); if (maj != GSS_S_COMPLETE) { set_GSSERRS(min, maj); gss_release_oid_set(&min, mechanisms); gss_release_name(&min, name); goto done; } } set_GSSERRS(0, GSS_S_COMPLETE); done: if (cred_handle == GSS_C_NO_CREDENTIAL) { gssntlm_release_cred(&min, (gss_cred_id_t *)&cred); } return GSSERR(); } uint32_t gssntlm_inquire_cred_by_mech(uint32_t *minor_status, gss_cred_id_t cred_handle, gss_OID mech_type, gss_name_t *name, uint32_t *initiator_lifetime, uint32_t *acceptor_lifetime, gss_cred_usage_t *cred_usage) { gss_cred_usage_t usage; uint32_t lifetime; uint32_t retmaj; uint32_t retmin; uint32_t maj, min; maj = gssntlm_inquire_cred(&min, cred_handle, name, &lifetime, &usage, NULL); if (maj != GSS_S_COMPLETE) return GSSERRS(min, maj); switch (usage) { case GSS_C_INITIATE: if (initiator_lifetime) *initiator_lifetime = lifetime; if (acceptor_lifetime) *acceptor_lifetime = 0; break; case GSS_C_ACCEPT: if (initiator_lifetime) *initiator_lifetime = 0; if (acceptor_lifetime) *acceptor_lifetime = lifetime; break; case GSS_C_BOTH: if (initiator_lifetime) *initiator_lifetime = lifetime; if (acceptor_lifetime) *acceptor_lifetime = lifetime; break; default: return GSSERRS(ERR_BADARG, GSS_S_FAILURE); } if (cred_usage) *cred_usage = usage; return GSSERRS(0, GSS_S_COMPLETE); } gss_OID_desc gssntlm_neg_flags_oid = { GSS_NTLMSSP_NEG_FLAGS_OID_LENGTH, discard_const(GSS_NTLMSSP_NEG_FLAGS_OID_STRING) }; static uint32_t gssntlm_set_cred_neg_flags(uint32_t *minor_status, struct gssntlm_cred *cred, const gss_buffer_t value) { if (cred == NULL || value == NULL) { *minor_status = EINVAL; return GSS_S_CALL_INACCESSIBLE_READ; } if (value->length == 0) { /* special to reset to library defaults */ if (cred->type == GSSNTLM_CRED_SERVER) { cred->neg_flags = NTLMSSP_DEFAULT_SERVER_FLAGS; } else { cred->neg_flags = NTLMSSP_DEFAULT_CLIENT_FLAGS; } } else if (value->length == sizeof(uint32_t)) { cred->neg_flags = *(uint32_t *)value->value; } else { *minor_status = EINVAL; return GSS_S_FAILURE; } *minor_status = 0; return GSS_S_COMPLETE; } uint32_t gssntlm_set_cred_option(uint32_t *minor_status, gss_cred_id_t *cred_handle, const gss_OID desired_object, const gss_buffer_t value) { struct gssntlm_cred *cred; if (minor_status == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; *minor_status = 0; if (cred_handle == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; cred = (struct gssntlm_cred *)*cred_handle; if (desired_object == GSS_C_NO_OID) return GSS_S_CALL_INACCESSIBLE_READ; if (gss_oid_equal(desired_object, &gssntlm_neg_flags_oid)) { return gssntlm_set_cred_neg_flags(minor_status, cred, value); } *minor_status = EINVAL; return GSS_S_UNAVAILABLE; } gss-ntlmssp-1.3.1/src/gss_err.c000066400000000000000000000112021456736161100164130ustar00rootroot00000000000000/* Copyright (C) 2014 GSS-NTLMSSP contributors, see COPYING for license */ #include "config.h" #include #include #include #include #include #include "gss_ntlmssp.h" #ifdef HAVE_NLS #include #define _(s) dgettext(PACKAGE, (s)) #else #define _(s) (s) #endif #define N_(s) s /* the order is determined by ntlm_err_code order */ static const char *err_strs[] = { N_("Unknown Error"), /* ERR_DECODE */ N_("Failed to decode data"), /* ERR_ENCODE */ N_("Failed to encode data"), /* ERR_CRYPTO */ N_("Crypto routine failure"), /* ERR_NOARG */ N_("A required argument is missing"), /* ERR_BADARG */ N_("Invalid value in argument"), /* ERR_NONAME */ N_("Name is empty"), /* ERR_NOSRVNAME */ N_("Not a server name"), /* ERR_NOUSRNAME */ N_("Not a user name"), /* ERR_BADLMLEVEL */ N_("Bad LM compatibility Level"), /* ERR_IMPOSSIBLE */ N_("An impossible error occurred"), /* ERR_BADCTX */ N_("Invalid or incomplete context"), /* ERR_WRONGCTX */ N_("Wrong context type"), /* ERR_WRONGMSG */ N_("Wrong message type"), /* ERR_REQNEGFLAG */ N_("A required Negotiate flag was not provided"), /* ERR_FAILNEGFLAGS */ N_("Failed to negotiate a common set of flags"), /* ERR_BADNEGFLAGS */ N_("Invalid combinations of negotiate flags"), /* ERR_NOSRVCRED */ N_("Not a server credential type"), /* ERR_NOUSRCRED */ N_("Not a user credential type"), /* ERR_BADCRED */ N_("Invalid or unknown credential"), /* ERR_NOTOKEN */ N_("Empty or missing token"), /* ERR_NOTSUPPORTED */ N_("Feature not supported"), /* ERR_NOTAVAIL */ N_("Feature not available"), /* ERR_NAMETOOLONG */ N_("Name is too long"), /* ERR_NOBINDINGS */ N_("Required channel bingings are not available"), /* ERR_TIMESKEW */ N_("Server and client clocks are too far apart"), /* ERR_EXPIRED */ N_("Expired"), /* ERR_KEYLEN */ N_("Invalid key length"), /* ERR_NONTLMV1 */ N_("NTLM version 1 not allowed"), /* ERR_NOUSRFOUND */ N_("User not found"), }; #define UNKNOWN_ERROR err_strs[0] uint32_t gssntlm_display_status(uint32_t *minor_status, uint32_t status_value, int status_type, gss_OID mech_type, uint32_t *message_context, gss_buffer_t status_string) { uint32_t retmaj; uint32_t retmin; /* if you can't say it in ~6 lines of text we don't bother */ char buf[512]; int err; if (!status_string) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } if (status_type != GSS_C_MECH_CODE) { return GSSERRS(ERR_BADARG, GSS_S_BAD_STATUS); } *minor_status = 0; *message_context = 0; status_string->length = 0; status_string->value = NULL; if (!status_value) { /* There must have been *some* error. No point saying 'Success' */ goto done; } if (status_value > ERR_BASE && status_value < ERR_LAST) { status_string->value = strdup(_(err_strs[status_value - ERR_BASE])); if (!status_string->value) { return GSSERRS(ENOMEM, GSS_S_FAILURE); } goto done; } /* handle both XSI and GNU specific varints of strerror_r */ errno = 0; #if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) /* XSI version */ err = strerror_r(status_value, buf, 400); /* The XSI-compliant strerror_r() function returns 0 on success. * On error, a (positive) error number is returned (since glibc * 2.13), or -1 is returned and errno is set to indicate the * error (glibc versions before 2.13). */ #else { char *ret; ret = strerror_r(status_value, buf, 400); if (ret == NULL) { err = errno; } else { if (ret != buf) { memmove(buf, ret, strlen(ret) + 1); } err = 0; } } #endif if (err == -1) err = errno; switch (err) { case ERANGE: /* Screw it, they can have a truncated version */ case 0: status_string->value = strdup(buf); break; default: break; } done: if (!status_string->value) { status_string->value = strdup(_(UNKNOWN_ERROR)); if (!status_string->value) { return GSSERRS(ENOMEM, GSS_S_FAILURE); } } status_string->length = strlen(status_string->value); return GSSERRS(0, GSS_S_COMPLETE); } gss-ntlmssp-1.3.1/src/gss_names.c000066400000000000000000000760751456736161100167510ustar00rootroot00000000000000/* Copyright 2013-2022 Simo Sorce , see COPYING for license */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "gss_ntlmssp.h" #ifndef HOST_NAME_MAX #include #define HOST_NAME_MAX MAXHOSTNAMELEN #endif static uint32_t string_split(uint32_t *minor_status, char sep, const char *str, size_t len, char **s1, char **s2) { uint32_t retmaj; uint32_t retmin; char *r1 = NULL; char *r2 = NULL; const char *p; size_t l; p = memchr(str, sep, len); if (!p) return GSSERRS(0, GSS_S_UNAVAILABLE); /* left side */ l = p - str; if (s1 && l != 0) { r1 = strndup(str, l); if (!r1) { set_GSSERR(ENOMEM); goto done; } } /* right side */ p++; l = len - (p - str); if (s2 && l != 0) { r2 = strndup(p, l); if (!r2) { set_GSSERR(ENOMEM); goto done; } } set_GSSERRS(0, GSS_S_COMPLETE); done: if (retmaj) { free(r1); free(r2); } else { if (s1) *s1 = r1; if (s2) *s2 = r2; } return GSSERR(); } /* Form of names allowed in GSSNTLMSSP now: * * Standard Forms: * foo * USERNAME: foo * DOMAIN: * * BAR\foo * USERNAME: foo * DOMAIN: BAR * * foo@BAR * USERNAME: foo * DOMAIN: BAR * * Enterprise name forms: * foo\@bar.example.com * USERNAME: foo@bar.example.com * DOMAIN: * * foo\@bar.example.com@BAR * USERNAME: foo@bar.example.com * DOMAIN: BAR * * \foo@bar.example.com * USERNAME: foo@bar.example.com * DOMAIN: * * BAR\foo@bar.example.com * USERNAME: foo@bar.example.com * DOMAIN: BAR * * BAR@dom\foo@bar.example.com * USERNAME: foo@bar.example.com * DOMAIN: BAR@dom * * Invalid forms: * BAR@dom\@foo.. * DOM\foo\@bar * foo@bar\@baz */ #define MAX_NAME_LEN 1024 static uint32_t parse_user_name(uint32_t *minor_status, const char *str, size_t len, char **domain, char **username) { uint32_t retmaj; uint32_t retmin; char *at, *sep; if (len > MAX_NAME_LEN) { return GSSERRS(ERR_NAMETOOLONG, GSS_S_BAD_NAME); } *username = NULL; *domain = NULL; /* let's check if there are '@' or '\' signs */ at = memchr(str, '@', len); sep = memchr(str, '\\', len); /* Check if enterprise name first */ if (at && sep) { /* we may have an enterprise name here */ char strbuf[len + 1]; char *buf = strbuf; /* copy buf to manipulate it */ memcpy(buf, str, len); buf[len] = '\0'; /* adjust pointers relative to new buffer */ sep = buf + (sep - str); at = buf + (at - str); if (sep > at) { /* domain name contains an '@' sign ... */ if (*(sep + 1) == '@') { /* invalid case of XXX@YYY\@ZZZ*/ set_GSSERR(EINVAL); goto done; } } else if (at - sep == 1) { /* it's just a '\@' escape */ /* no leading domain */ sep = NULL; } if (sep) { /* terminate and copy domain, even if empty */ /* NOTE: this is important for the Windbind integration case * where we need to tell the machinery to *not* add the default * domain name, it happens when the domain is NULL. */ *sep = '\0'; *domain = strdup(buf); if (NULL == *domain) { set_GSSERR(ENOMEM); goto done; } /* point buf at username part */ len = len - (sep - buf) - 1; buf = sep + 1; } for (at = strchr(buf, '@'); at != NULL; at = strchr(at, '@')) { if (*(at - 1) == '\\') { if (*domain) { /* Invalid forms like DOM\foo\@bar or foo@bar\@baz */ free(*domain); *domain = NULL; set_GSSERR(EINVAL); goto done; } /* remove escape, moving all including terminating '\0' */ memmove(at - 1, at, len - (at - buf) + 1); } else if (!*domain) { /* an '@' without escape and no previous * domain was split out. * the rest of the string is the domain */ *at = '\0'; *domain = strdup(at + 1); if (NULL == *domain) { set_GSSERR(ENOMEM); goto done; } /* note we continue the loop to check if any invalid * \@ escapes is found in the domain part */ } at += 1; } *username = strdup(buf); if (NULL == *username) { set_GSSERR(ENOMEM); goto done; } /* we got an enterprise name, return */ set_GSSERRS(0, GSS_S_COMPLETE); goto done; } /* Check if in classic DOMAIN\User windows format */ if (sep) { retmaj = string_split(&retmin, '\\', str, len, domain, username); goto done; } /* else accept a user@domain format too */ if (at) { retmaj = string_split(&retmin, '@', str, len, username, domain); goto done; } /* finally, take string as simple user name */ *username = strndup(str, len); if (NULL == *username) { set_GSSERR(ENOMEM); } set_GSSERRS(0, GSS_S_COMPLETE); done: return GSSERR(); } static uint32_t uid_to_name(uint32_t *minor_status, uid_t uid, char **name) { uint32_t retmaj; uint32_t retmin; struct passwd *pw; pw = getpwuid(uid); if (pw) { return GSSERRS(ERR_NOUSRFOUND, GSS_S_FAILURE); } *name = strdup(pw->pw_name); if (!*name) { set_GSSERR(ENOMEM); goto done; } set_GSSERRS(0, GSS_S_COMPLETE); done: return GSSERR(); } uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status, gss_const_OID mech_type, gss_buffer_t input_name_buffer, gss_OID input_name_type, gss_name_t *output_name) { struct gssntlm_name *name = NULL; uint32_t retmaj; uint32_t retmin; /* TODO: check mech_type == gssntlm_oid */ if (mech_type == GSS_C_NO_OID) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } name = calloc(1, sizeof(struct gssntlm_name)); if (!name) { set_GSSERR(ENOMEM); goto done; } /* treat null OID like NT_USER_NAME */ if (input_name_type == GSS_C_NULL_OID) { input_name_type = GSS_C_NT_USER_NAME; } if (gss_oid_equal(input_name_type, GSS_C_NT_HOSTBASED_SERVICE) || gss_oid_equal(input_name_type, GSS_C_NT_HOSTBASED_SERVICE_X)) { char *spn = NULL; char *p = NULL; name->type = GSSNTLM_NAME_SERVER; if (input_name_buffer->length > 0) { spn = strndup(input_name_buffer->value, input_name_buffer->length); if (!spn) { set_GSSERR(ENOMEM); goto done; } p = strchr(spn, '@'); if (p && input_name_buffer->length == 1) { free(spn); spn = p = NULL; } } if (p) { /* Windows expects a SPN not a GSS Name */ if (p != spn) { *p = '/'; name->data.server.spn = spn; spn = NULL; } p += 1; name->data.server.name = strdup(p); if (!name->data.server.name) { free(spn); set_GSSERR(ENOMEM); goto done; } } else { char hostname[HOST_NAME_MAX + 1] = { 0 }; size_t l, r; /* no seprator, assume only service is provided and try to * source the local host name */ retmin = gethostname(hostname, HOST_NAME_MAX); if (retmin) { free(spn); set_GSSERR(retmin); goto done; } hostname[HOST_NAME_MAX] = '\0'; if (spn != NULL) { /* spn = + + + <\0> */ l = strlen(spn) + 1 + strlen(hostname) + 1; name->data.server.spn = malloc(l); if (!name->data.server.spn) { free(spn); set_GSSERR(ENOMEM); goto done; } r = snprintf(name->data.server.spn, l, "%s/%s", spn, hostname); if (r != l - 1) { free(spn); set_GSSERR(ENOMEM); goto done; } } name->data.server.name = strdup(hostname); if (!name->data.server.name) { free(spn); set_GSSERR(ENOMEM); goto done; } } free(spn); set_GSSERRS(0, GSS_S_COMPLETE); } else if (gss_oid_equal(input_name_type, GSS_C_NT_USER_NAME)) { name->type = GSSNTLM_NAME_USER; retmaj = parse_user_name(&retmin, input_name_buffer->value, input_name_buffer->length, &name->data.user.domain, &name->data.user.name); } else if (gss_oid_equal(input_name_type, GSS_C_NT_MACHINE_UID_NAME)) { uid_t uid; name->type = GSSNTLM_NAME_USER; name->data.user.domain = NULL; uid = *(uid_t *)input_name_buffer->value; retmaj = uid_to_name(&retmin, uid, &name->data.user.name); } else if (gss_oid_equal(input_name_type, GSS_C_NT_STRING_UID_NAME)) { char struid[12] = { 0 }; uid_t uid; name->type = GSSNTLM_NAME_USER; name->data.user.domain = NULL; if (input_name_buffer->length > 12) { set_GSSERR(ERR_BADARG); goto done; } memcpy(struid, input_name_buffer->value, input_name_buffer->length); struid[11] = '\0'; errno = 0; uid = strtol(struid, NULL, 10); if (errno) { set_GSSERR(ERR_BADARG); goto done; } retmaj = uid_to_name(&retmin, uid, &name->data.user.name); } else if (gss_oid_equal(input_name_type, GSS_C_NT_ANONYMOUS)) { name->type = GSSNTLM_NAME_ANON; set_GSSERRS(0, GSS_S_COMPLETE); } else if (gss_oid_equal(input_name_type, GSS_C_NT_EXPORT_NAME)) { /* TODO */ set_GSSERRS(ERR_NOTSUPPORTED, GSS_S_BAD_NAMETYPE); } else { set_GSSERRS(ERR_BADARG, GSS_S_BAD_NAMETYPE); } done: if (retmaj != GSS_S_COMPLETE) { uint32_t tmpmin; gssntlm_release_name(&tmpmin, (gss_name_t *)&name); } else { *output_name = (gss_name_t)name; } return GSSERR(); } uint32_t gssntlm_import_name(uint32_t *minor_status, gss_buffer_t input_name_buffer, gss_OID input_name_type, gss_name_t *output_name) { return gssntlm_import_name_by_mech(minor_status, discard_const(&gssntlm_oid), input_name_buffer, input_name_type, output_name); } size_t gssntlm_get_attrs_count(const struct gssntlm_name_attribute *attrs) { size_t c; for (c = 0; attrs && attrs[c].attr_name != NULL; c++) ; return c; } int gssntlm_copy_attrs(const struct gssntlm_name_attribute *src, struct gssntlm_name_attribute **dst) { struct gssntlm_name_attribute *copied_attrs; size_t attrs_count = gssntlm_get_attrs_count(src); *dst = NULL; if (attrs_count == 0) { return 0; } copied_attrs = calloc(attrs_count + 1, /* +1 for terminator entry */ sizeof(struct gssntlm_name_attribute)); if (copied_attrs == NULL) { return ENOMEM; } for (size_t i = 0; i < attrs_count; i++) { copied_attrs[i].attr_name = strdup(src[i].attr_name); if (copied_attrs[i].attr_name == NULL) { gssntlm_release_attrs(&copied_attrs); return ENOMEM; } copied_attrs[i].attr_value.length = src[i].attr_value.length; copied_attrs[i].attr_value.value = malloc(src[i].attr_value.length); if (copied_attrs[i].attr_value.value == NULL) { gssntlm_release_attrs(&copied_attrs); return ENOMEM; } memcpy(copied_attrs[i].attr_value.value, src[i].attr_value.value, src[i].attr_value.length); } /* terminator entry is filled with zeroes by calloc */ *dst = copied_attrs; return 0; } struct gssntlm_name_attribute *gssntlm_find_attr( struct gssntlm_name_attribute *attrs, const char *attr_name, size_t attr_name_len) { for (size_t i = 0; attrs && (attrs[i].attr_name != NULL); i++) { /* We store attr_name as a zero-terminated string, so * it is always zero-terminated */ if (attr_name_len == strlen(attrs[i].attr_name) && strncasecmp(attrs[i].attr_name, attr_name, attr_name_len) == 0) { return &attrs[i]; } } return NULL; } void gssntlm_release_attrs(struct gssntlm_name_attribute **attrs) { for (size_t i = 0; *attrs && (*attrs)[i].attr_name != NULL; i++) { free((*attrs)[i].attr_name); free((*attrs)[i].attr_value.value); } safefree(*attrs); } int gssntlm_copy_name(struct gssntlm_name *src, struct gssntlm_name *dst) { char *dom = NULL, *usr = NULL, *spn = NULL, *srv = NULL; int ret; dst->type = src->type; switch (src->type) { case GSSNTLM_NAME_NULL: case GSSNTLM_NAME_ANON: break; case GSSNTLM_NAME_USER: if (src->data.user.domain) { dom = strdup(src->data.user.domain); if (!dom) { ret = ENOMEM; goto done; } } if (src->data.user.name) { usr = strdup(src->data.user.name); if (!usr) { ret = ENOMEM; goto done; } } dst->data.user.domain = dom; dst->data.user.name = usr; break; case GSSNTLM_NAME_SERVER: if (src->data.server.spn) { spn = strdup(src->data.server.spn); if (!spn) { ret = ENOMEM; goto done; } } dst->data.server.spn = spn; if (src->data.server.name) { srv = strdup(src->data.server.name); if (!srv) { ret = ENOMEM; goto done; } } dst->data.server.name = srv; break; } ret = gssntlm_copy_attrs(src->attrs, &dst->attrs); if (ret) goto done; ret = 0; done: if (ret) { safefree(dom); safefree(usr); safefree(spn); safefree(srv); } return ret; } uint32_t gssntlm_duplicate_name(uint32_t *minor_status, const gss_name_t input_name, gss_name_t *dest_name) { struct gssntlm_name *in; struct gssntlm_name *out; uint32_t retmin; uint32_t retmaj; if (input_name == GSS_C_NO_NAME || dest_name == NULL) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } in = (struct gssntlm_name *)input_name; if (in->type == GSSNTLM_NAME_NULL) { *dest_name = GSS_C_NO_NAME; return GSSERRS(0, GSS_S_COMPLETE); } out = calloc(1, sizeof(struct gssntlm_name)); if (!out) { set_GSSERR(ENOMEM); goto done; } retmin = gssntlm_copy_name(in, out); if (retmin) { set_GSSERR(retmin); goto done; } set_GSSERRS(0, GSS_S_COMPLETE); done: if (retmaj) { safefree(out); } *dest_name = (gss_name_t)out; return GSSERR(); } void gssntlm_int_release_name(struct gssntlm_name *name) { if (!name) return; switch (name->type) { case GSSNTLM_NAME_NULL: return; case GSSNTLM_NAME_ANON: break; case GSSNTLM_NAME_USER: safefree(name->data.user.domain); safefree(name->data.user.name); break; case GSSNTLM_NAME_SERVER: safefree(name->data.server.spn); safefree(name->data.server.name); break; } gssntlm_release_attrs(&name->attrs); name->type = GSSNTLM_NAME_NULL; } uint32_t gssntlm_release_name(uint32_t *minor_status, gss_name_t *input_name) { uint32_t retmaj; uint32_t retmin; if (!input_name) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } gssntlm_int_release_name((struct gssntlm_name *)*input_name); safefree(*input_name); return GSSERRS(0, GSS_S_COMPLETE); } uint32_t gssntlm_display_name(uint32_t *minor_status, gss_name_t input_name, gss_buffer_t output_name_buffer, gss_OID *output_name_type) { struct gssntlm_name *in; gss_buffer_t out; uint32_t retmaj; uint32_t retmin; int ret; if (input_name == GSS_C_NO_NAME || output_name_buffer == NULL) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } in = (struct gssntlm_name *)input_name; out = output_name_buffer; switch (in->type) { case GSSNTLM_NAME_NULL: return GSSERRS(ERR_BADARG, GSS_S_BAD_NAME); case GSSNTLM_NAME_ANON: out->value = strdup("NT AUTHORITY\\ANONYMOUS LOGON"); if (!out->value) { set_GSSERR(ENOMEM); goto done; } out->length = strlen(out->value) + 1; if (output_name_type) { *output_name_type = GSS_C_NT_ANONYMOUS; } break; case GSSNTLM_NAME_USER: if (in->data.user.domain) { ret = asprintf((char **)&out->value, "%s\\%s", in->data.user.domain, in->data.user.name); if (ret == -1) { out->value = NULL; } } else { out->value = strdup(in->data.user.name); } if (!out->value) { set_GSSERR(ENOMEM); goto done; } out->length = strlen(out->value) + 1; if (output_name_type) { *output_name_type = GSS_C_NT_USER_NAME; } break; case GSSNTLM_NAME_SERVER: out->value = strdup(in->data.server.spn); if (!out->value) { set_GSSERR(ENOMEM); goto done; } out->length = strlen(out->value) + 1; if (output_name_type) { *output_name_type = GSS_C_NT_HOSTBASED_SERVICE; } break; } set_GSSERRS(0, GSS_S_COMPLETE); done: return GSSERR(); } #define PWBUFLEN 1024 uint32_t gssntlm_localname(uint32_t *minor_status, const gss_name_t name, gss_const_OID mech_type, gss_buffer_t localname) { struct gssntlm_name *in; char *uname = NULL; char pwbuf[PWBUFLEN]; struct passwd pw, *res; uint32_t retmaj; uint32_t retmin; int ret; in = (struct gssntlm_name *)name; if (in->type != GSSNTLM_NAME_USER) { set_GSSERRS(ERR_BADARG, GSS_S_BAD_NAME); goto done; } /* TODO: hook up with winbindd/sssd for name resolution ? */ if (in->data.user.domain) { ret = asprintf(&uname, "%s\\%s", in->data.user.domain, in->data.user.name); if (ret == -1) { set_GSSERR(ENOMEM); goto done; } ret = getpwnam_r(uname, &pw, pwbuf, PWBUFLEN, &res); if (ret) { set_GSSERR(ret); goto done; } safefree(uname); if (res) { uname = strdup(res->pw_name); } } if (uname == NULL) { ret = getpwnam_r(in->data.user.name, &pw, pwbuf, PWBUFLEN, &res); if (ret != 0 || res == NULL) { set_GSSERR(ret); goto done; } uname = strdup(res->pw_name); } if (!uname) { set_GSSERR(ENOMEM); goto done; } set_GSSERRS(0, GSS_S_COMPLETE); done: if (retmaj) { safefree(uname); } else { localname->value = uname; localname->length = strlen(uname) + 1; } return GSSERR(); } uint32_t netbios_get_names(void *ctx, char *computer_name, char **netbios_host, char **netbios_domain) { char *nb_computer_name = NULL; char *nb_domain_name = NULL; char *env_name; uint32_t ret; env_name = getenv("NETBIOS_COMPUTER_NAME"); if (env_name) { nb_computer_name = strdup(env_name); if (!nb_computer_name) { ret = ENOMEM; goto done; } } env_name = getenv("NETBIOS_DOMAIN_NAME"); if (env_name) { nb_domain_name = strdup(env_name); if (!nb_domain_name) { ret = ENOMEM; goto done; } } if (!nb_computer_name || !nb_domain_name) { /* fetch only mising ones */ ret = external_netbios_get_names(ctx, nb_computer_name ? NULL : &nb_computer_name, nb_domain_name ? NULL : &nb_domain_name); if ((ret != 0) && (ret != ENOENT) && (ret != ERR_NOTAVAIL)) { goto done; } } if (!nb_computer_name) { char *p; p = strchr(computer_name, '.'); if (p) { nb_computer_name = strndup(computer_name, p - computer_name); } else { nb_computer_name = strdup(computer_name); } for (p = nb_computer_name; p && *p; p++) { /* Can only be ASCII, so toupper is safe */ *p = toupper(*p); } if (!nb_computer_name) { ret = ENOMEM; goto done; } } if (!nb_domain_name) { nb_domain_name = strdup(DEF_NB_DOMAIN); if (!nb_domain_name) { ret = ENOMEM; goto done; } } ret = 0; done: if (ret) { safefree(nb_computer_name); safefree(nb_domain_name); } *netbios_domain = nb_domain_name; *netbios_host = nb_computer_name; return ret; } uint32_t gssntlm_inquire_name(uint32_t *minor_status, gss_name_t name, int *name_is_MN, gss_OID *MN_mech, gss_buffer_set_t *attrs) { uint32_t retmin = 0; uint32_t retmaj = 0; uint32_t tmpmin; const struct gssntlm_name *in = (const struct gssntlm_name *)name; if (!attrs) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE); } *attrs = GSS_C_NO_BUFFER_SET; if (name == GSS_C_NO_NAME) { return GSSERRS(GSS_S_BAD_NAME, GSS_S_CALL_INACCESSIBLE_READ); } for (size_t i = 0; in->attrs && in->attrs[i].attr_name != NULL; i++) { struct gssntlm_name_attribute *attr = &in->attrs[i]; size_t attr_name_len = strlen(attr->attr_name); gss_buffer_desc buf; gss_buffer_t attr_value = &attr->attr_value; /* +1 for '=' separator and +1 for EOL */ size_t full_string_len = attr_value->length + attr_name_len + 2; size_t offset = 0; char *attr_string = malloc(full_string_len); if (attr_string == NULL) { set_GSSERR(ENOMEM); goto done; } /* Construct 'attr_name=\0' string */ memcpy(attr_string, attr->attr_name, attr_name_len); offset += attr_name_len; attr_string[offset++] = '='; memcpy(attr_string + offset, attr_value->value, attr_value->length); offset += attr_value->length; attr_string[offset] = 0; /* now add a buffer to output set */ buf.length = full_string_len; buf.value = attr_string; retmaj = gss_add_buffer_set_member(&retmin, &buf, attrs); free(attr_string); if (retmaj != GSS_S_COMPLETE) goto done; } done: if (retmaj) { (void)gss_release_buffer_set(&tmpmin, attrs); } return GSSERRS(retmin, retmaj); } /* RFC6680 - GSSAPI Naming Extensions */ uint32_t gssntlm_get_name_attribute(uint32_t *minor_status, gss_name_t name, gss_buffer_t attr, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { uint32_t retmin; uint32_t retmaj; const struct gssntlm_name *in = (const struct gssntlm_name *)name; struct gssntlm_name_attribute *found_attr; if (name == GSS_C_NO_NAME) { return GSSERRS(GSS_S_BAD_NAME, GSS_S_CALL_INACCESSIBLE_READ); } if (attr == NULL) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } if (display_value) { display_value->value = NULL; display_value->length = 0; } if (more) { *more = 0; } if (authenticated) { *authenticated = 0; } if (complete) { *complete = 0; } found_attr = gssntlm_find_attr(in->attrs, attr->value, attr->length); if (!found_attr) { return GSSERRS(ENOENT, GSS_S_UNAVAILABLE); } if (authenticated) { *authenticated = 1; } if (complete) { *complete = 1; } if (value) { gss_buffer_t attr_value = &found_attr->attr_value; value->value = malloc(attr_value->length); if (!value->value) { return GSSERRS(ENOMEM, GSS_S_FAILURE); } memcpy(value->value, attr_value->value, attr_value->length); value->length = attr_value->length; } return GSSERRS(0, GSS_S_COMPLETE); } /* RFC5801 Extensions */ #define GS2_NTLM_SASL_NAME "GS2-NTLM" #define GS2_NTLM_SASL_NAME_LEN (sizeof(GS2_NTLM_SASL_NAME) - 1) uint32_t gssntlm_inquire_saslname_for_mech(OM_uint32 *minor_status, const gss_OID desired_mech, gss_buffer_t sasl_mech_name, gss_buffer_t mech_name, gss_buffer_t mech_description) { if (desired_mech && !gss_oid_equal(desired_mech, &gssntlm_oid)) { *minor_status = ENOENT; return GSS_S_BAD_MECH; } sasl_mech_name->value = NULL; mech_name->value = NULL; mech_description->value = NULL; *minor_status = ENOMEM; sasl_mech_name->value = strdup(GS2_NTLM_SASL_NAME); if (sasl_mech_name->value == NULL) { goto done; } sasl_mech_name->length = strlen(sasl_mech_name->value); mech_name->value = strdup("NTLM"); if (mech_name->value == NULL) { goto done; } mech_name->length = strlen(mech_name->value); mech_description->value = strdup("NTLM Mechanism"); if (mech_name->value == NULL) { goto done; } mech_description->length = strlen(mech_description->value); *minor_status = 0; done: if (*minor_status != 0) { free(sasl_mech_name->value); free(mech_name->value); free(mech_description->value); return GSS_S_FAILURE; } return GSS_S_COMPLETE; } uint32_t gssntlm_inquire_mech_for_saslname(OM_uint32 *minor_status, const gss_buffer_t sasl_mech_name, gss_OID *mech_type) { if (sasl_mech_name->length == GS2_NTLM_SASL_NAME_LEN && memcmp(sasl_mech_name->value, GS2_NTLM_SASL_NAME, GS2_NTLM_SASL_NAME_LEN) == 0) { if (mech_type != NULL) { *mech_type = discard_const(&gssntlm_oid); } *minor_status = 0; return GSS_S_COMPLETE; } *minor_status = ENOENT; return GSS_S_BAD_MECH; } static uint32_t make_ma_oid_set(uint32_t *minor_status, gss_OID_set *ma_set, int supported) { gss_const_OID known_mech_attrs[] = { GSS_C_MA_MECH_CONCRETE, GSS_C_MA_MECH_PSEUDO, GSS_C_MA_MECH_COMPOSITE, GSS_C_MA_MECH_NEGO, GSS_C_MA_MECH_GLUE, GSS_C_MA_NOT_MECH, GSS_C_MA_DEPRECATED, GSS_C_MA_NOT_DFLT_MECH, GSS_C_MA_ITOK_FRAMED, GSS_C_MA_AUTH_INIT, GSS_C_MA_AUTH_TARG, GSS_C_MA_AUTH_INIT_INIT, GSS_C_MA_AUTH_TARG_INIT, GSS_C_MA_AUTH_INIT_ANON, GSS_C_MA_AUTH_TARG_ANON, GSS_C_MA_DELEG_CRED, GSS_C_MA_INTEG_PROT, GSS_C_MA_CONF_PROT, GSS_C_MA_MIC, GSS_C_MA_WRAP, GSS_C_MA_PROT_READY, GSS_C_MA_REPLAY_DET, GSS_C_MA_OOS_DET, GSS_C_MA_CBINDINGS, GSS_C_MA_PFS, GSS_C_MA_COMPRESS, GSS_C_MA_CTX_TRANS, NULL }; gss_const_OID supported_mech_attrs[] = { GSS_C_MA_MECH_CONCRETE, GSS_C_MA_AUTH_INIT, GSS_C_MA_INTEG_PROT, GSS_C_MA_CONF_PROT, GSS_C_MA_MIC, GSS_C_MA_WRAP, GSS_C_MA_OOS_DET, GSS_C_MA_CBINDINGS, GSS_C_MA_CTX_TRANS, NULL }; uint32_t maj = 0; uint32_t min = 0; gss_const_OID *array = known_mech_attrs; if (supported) { array = supported_mech_attrs; } maj = gss_create_empty_oid_set(&min, ma_set); if (maj != GSS_S_COMPLETE) { goto done; } for (int i = 0; array[i] != NULL; i++) { maj = gss_add_oid_set_member(&min, discard_const(array[i]), ma_set); if (maj != GSS_S_COMPLETE) { goto done; } } done: *minor_status = min; return maj; } uint32_t gssntlm_inquire_attrs_for_mech(uint32_t *minor_status, gss_const_OID mech_oid, gss_OID_set *mech_attrs, gss_OID_set *known_mech_attrs) { gss_OID_set s_ma = GSS_C_NULL_OID_SET; gss_OID_set k_ma = GSS_C_NULL_OID_SET; uint32_t maj = GSS_S_COMPLETE; uint32_t min = 0; if (mech_oid && !gss_oid_equal(mech_oid, &gssntlm_oid)) { *minor_status = ENOENT; return GSS_S_BAD_MECH; } if (mech_attrs != NULL) { maj = make_ma_oid_set(&min, &s_ma, 1); if (maj != GSS_S_COMPLETE) { goto done; } } if (known_mech_attrs != NULL) { maj = make_ma_oid_set(&min, &k_ma, 0); if (maj != GSS_S_COMPLETE) { goto done; } } done: if (maj != GSS_S_COMPLETE) { gss_release_oid_set(&min, &s_ma); gss_release_oid_set(&min, &k_ma); } if (mech_attrs != NULL) { *mech_attrs = s_ma; } if (known_mech_attrs != NULL) { *known_mech_attrs = k_ma; } *minor_status = min; return maj; } gss-ntlmssp-1.3.1/src/gss_ntlmssp.c000066400000000000000000000117521456736161100173350ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ #include #include #include #include #include "gssapi_ntlmssp.h" #include "gss_ntlmssp.h" #define SEC_LEVEL_MIN 0 #define SEC_LEVEL_MAX 5 #define SEC_LM_OK 0x01 #define SEC_NTLM_OK 0x02 #define SEC_EXT_SEC_OK 0x04 #define SEC_V2_OK 0x08 #define SEC_DC_LM_OK 0x10 #define SEC_DC_NTLM_OK 0x20 #define SEC_DC_V2_OK 0x40 const gss_OID_desc gssntlm_oid = { .length = GSS_NTLMSSP_OID_LENGTH, .elements = discard_const(GSS_NTLMSSP_OID_STRING) }; bool gssntlm_required_security(int security_level, struct gssntlm_ctx *ctx) { uint8_t resp; /* DC defaults */ resp = SEC_DC_LM_OK | SEC_DC_NTLM_OK | SEC_DC_V2_OK; switch (security_level) { case 0: resp |= SEC_LM_OK | SEC_NTLM_OK; break; case 1: resp |= SEC_LM_OK | SEC_NTLM_OK | SEC_EXT_SEC_OK; break; case 2: resp |= SEC_NTLM_OK | SEC_EXT_SEC_OK; break; case 3: resp |= SEC_V2_OK | SEC_EXT_SEC_OK; break; case 4: if (ctx->role == GSSNTLM_DOMAIN_CONTROLLER) resp &= ~SEC_DC_LM_OK; resp |= SEC_V2_OK | SEC_EXT_SEC_OK; break; case 5: if (ctx->role == GSSNTLM_DOMAIN_CONTROLLER) resp = SEC_DC_V2_OK; resp |= SEC_V2_OK | SEC_EXT_SEC_OK; break; default: return false; } ctx->sec_req = resp; return true; } void gssntlm_set_role(struct gssntlm_ctx *ctx, int desired, char *nb_domain_name) { if (desired == GSSNTLM_CLIENT) { ctx->role = GSSNTLM_CLIENT; } else if (nb_domain_name && *nb_domain_name && strcmp(nb_domain_name, DEF_NB_DOMAIN) != 0) { ctx->role = GSSNTLM_DOMAIN_SERVER; } else { ctx->role = GSSNTLM_SERVER; } } bool gssntlm_role_is_client(struct gssntlm_ctx *ctx) { return (ctx->role == GSSNTLM_CLIENT); } bool gssntlm_role_is_server(struct gssntlm_ctx *ctx) { switch (ctx->role) { case GSSNTLM_SERVER: case GSSNTLM_DOMAIN_SERVER: case GSSNTLM_DOMAIN_CONTROLLER: return true; default: break; } return false; } bool gssntlm_role_is_domain_member(struct gssntlm_ctx *ctx) { switch (ctx->role) { case GSSNTLM_DOMAIN_SERVER: case GSSNTLM_DOMAIN_CONTROLLER: return true; default: break; } return false; } bool gssntlm_sec_lm_ok(struct gssntlm_ctx *ctx) { switch (ctx->role) { case GSSNTLM_CLIENT: case GSSNTLM_SERVER: return (ctx->sec_req & SEC_LM_OK); case GSSNTLM_DOMAIN_SERVER: return true; /* defer decision to DC */ case GSSNTLM_DOMAIN_CONTROLLER: return (ctx->sec_req & SEC_DC_LM_OK); } return false; } bool gssntlm_sec_ntlm_ok(struct gssntlm_ctx *ctx) { switch (ctx->role) { case GSSNTLM_CLIENT: case GSSNTLM_SERVER: return (ctx->sec_req & SEC_NTLM_OK); case GSSNTLM_DOMAIN_SERVER: return true; /* defer decision to DC */ case GSSNTLM_DOMAIN_CONTROLLER: return (ctx->sec_req & SEC_DC_NTLM_OK); } return false; } bool gssntlm_sec_v2_ok(struct gssntlm_ctx *ctx) { switch (ctx->role) { case GSSNTLM_CLIENT: case GSSNTLM_SERVER: return (ctx->sec_req & SEC_V2_OK); case GSSNTLM_DOMAIN_SERVER: return true; /* defer decision to DC */ case GSSNTLM_DOMAIN_CONTROLLER: return (ctx->sec_req & SEC_DC_V2_OK); } return false; } bool gssntlm_ext_sec_ok(struct gssntlm_ctx *ctx) { return (ctx->sec_req & SEC_EXT_SEC_OK); } uint32_t gssntlm_context_is_valid(struct gssntlm_ctx *ctx, time_t *time_now) { time_t now; if (!ctx) return GSS_S_NO_CONTEXT; if (!(ctx->int_flags & NTLMSSP_CTX_FLAG_ESTABLISHED)) { return GSS_S_NO_CONTEXT; } now = time(NULL); if (now > ctx->expiration_time) return GSS_S_CONTEXT_EXPIRED; if (time_now) *time_now = now; return GSS_S_COMPLETE; } int gssntlm_get_lm_compatibility_level(void) { const char *envvar; envvar = getenv("LM_COMPAT_LEVEL"); if (envvar != NULL) { return atoi(envvar); } /* use 3 by default for better compatibility */ return 3; } uint32_t gssntlm_mech_invoke(uint32_t *minor_status, const gss_OID desired_mech, const gss_OID desired_object, gss_buffer_t value) { uint32_t retmaj = GSS_S_COMPLETE; uint32_t retmin = 0; if (minor_status == NULL) { return GSS_S_CALL_INACCESSIBLE_WRITE; } if (desired_mech != GSS_C_NO_OID && !gss_oid_equal(desired_mech, &gssntlm_oid)) { return GSSERRS(0, GSS_S_BAD_MECH); } if (desired_object == GSS_C_NO_OID) { return GSSERRS(0, GSS_S_CALL_INACCESSIBLE_READ); } if (!gss_oid_equal(desired_object, &gssntlm_debug_oid)) { return GSSERRS(EINVAL, GSS_S_UNAVAILABLE); } retmin = gssntlm_debug_invoke(value); if (retmin != 0) { retmaj = GSS_S_UNAVAILABLE; } return GSSERR(); } gss-ntlmssp-1.3.1/src/gss_ntlmssp.h000066400000000000000000000447341456736161100173500ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ #ifndef _GSS_NTLMSSP_H_ #define _GSS_NTLMSSP_H_ #include "ntlm.h" #include "crypto.h" #include "gssapi_ntlmssp.h" #include "debug.h" #define DEF_NB_DOMAIN "WORKSTATION" #define MAX_CHALRESP_LIFETIME 36 * 60 * 60 /* 36 hours in seconds */ #define NTLMSSP_DEFAULT_CLIENT_FLAGS ( \ NTLMSSP_NEGOTIATE_ALWAYS_SIGN | \ NTLMSSP_NEGOTIATE_128 | \ NTLMSSP_NEGOTIATE_56 | \ NTLMSSP_NEGOTIATE_NTLM | \ NTLMSSP_REQUEST_TARGET | \ NTLMSSP_NEGOTIATE_OEM | \ NTLMSSP_NEGOTIATE_UNICODE | \ NTLMSSP_NEGOTIATE_VERSION) #define NTLMSSP_DEFAULT_SERVER_FLAGS ( \ NTLMSSP_NEGOTIATE_ALWAYS_SIGN | \ NTLMSSP_NEGOTIATE_56 | \ NTLMSSP_NEGOTIATE_KEY_EXCH | \ NTLMSSP_NEGOTIATE_128 | \ NTLMSSP_NEGOTIATE_VERSION | \ NTLMSSP_NEGOTIATE_ALWAYS_SIGN | \ NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED | \ NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED | \ NTLMSSP_NEGOTIATE_NTLM | \ NTLMSSP_NEGOTIATE_SEAL | \ NTLMSSP_NEGOTIATE_SIGN | \ NTLMSSP_REQUEST_TARGET | \ NTLMSSP_NEGOTIATE_OEM | \ NTLMSSP_NEGOTIATE_UNICODE) #define NTLMSSP_CTX_FLAG_ESTABLISHED 0x01 /* context was established */ #define NTLMSSP_CTX_FLAG_SPNEGO_CAN_MIC 0x02 /* SPNEGO asks for MIC */ #define NTLMSSP_CTX_FLAG_AUTH_WITH_MIC 0x04 /* Auth MIC was created */ struct gssntlm_name_attribute { char *attr_name; /* NULL indicates array termination */ gss_buffer_desc attr_value; }; struct gssntlm_name { enum ntlm_name_type { GSSNTLM_NAME_NULL, GSSNTLM_NAME_ANON, GSSNTLM_NAME_USER, GSSNTLM_NAME_SERVER } type; union { struct { char *domain; char *name; } user; struct { char *spn; char *name; } server; } data; struct gssntlm_name_attribute *attrs; /* Array of name attributes */ }; struct gssntlm_cred { enum ntlm_cred_type { GSSNTLM_CRED_NONE, GSSNTLM_CRED_ANON, GSSNTLM_CRED_USER, GSSNTLM_CRED_SERVER, GSSNTLM_CRED_EXTERNAL, } type; union { struct { int dummy; } anon; struct { struct gssntlm_name user; struct ntlm_key nt_hash; struct ntlm_key lm_hash; } user; struct { struct gssntlm_name name; char *keyfile; } server; struct { struct gssntlm_name user; bool creds_in_cache; } external; } cred; /* set cred options provided default flags * this is currently intentionally not imported/exported * as it is considered an ephemeral local status */ uint32_t neg_flags; }; struct gssntlm_ctx { enum gssntlm_role { GSSNTLM_CLIENT, GSSNTLM_SERVER, GSSNTLM_DOMAIN_SERVER, GSSNTLM_DOMAIN_CONTROLLER } role; enum { NTLMSSP_STAGE_INIT = 0, NTLMSSP_STAGE_NEGOTIATE, NTLMSSP_STAGE_CHALLENGE, NTLMSSP_STAGE_AUTHENTICATE, NTLMSSP_STAGE_DONE } stage; uint8_t sec_req; char *workstation; struct ntlm_ctx *ntlm; struct ntlm_buffer nego_msg; struct ntlm_buffer chal_msg; struct ntlm_buffer auth_msg; struct gssntlm_name source_name; struct gssntlm_name target_name; uint8_t server_chal[8]; /* requested gss fags */ uint32_t gss_flags; /* negotiated flags */ uint32_t neg_flags; struct ntlm_key exported_session_key; struct ntlm_signseal_state crypto_state; uint32_t int_flags; time_t expiration_time; void *external_context; }; #define set_GSSERRS(min, maj) \ (void)DEBUG_GSS_ERRORS((retmaj = (maj)), (retmin = (min))) #define set_GSSERR(min) set_GSSERRS((min), GSS_S_FAILURE) static inline uint32_t gssntlmssp_ret_err(uint32_t *s, uint32_t n, uint32_t j) { if (!s) return GSS_S_CALL_INACCESSIBLE_WRITE; *s = n; return j; } #define GSSERR() gssntlmssp_ret_err(minor_status, retmin, retmaj) #define GSSERRS(min, maj) \ DEBUG_GSS_ERRORS((retmaj = (maj)), (retmin = (min))) ? 0 : \ gssntlmssp_ret_err(minor_status, retmin, retmaj) /* Static const name attribute for sids list */ extern const char gssntlmssp_sids_urn[]; bool gssntlm_required_security(int security_level, struct gssntlm_ctx *ctx); void gssntlm_set_role(struct gssntlm_ctx *ctx, int desired, char *nb_domain_name); bool gssntlm_role_is_client(struct gssntlm_ctx *ctx); bool gssntlm_role_is_server(struct gssntlm_ctx *ctx); bool gssntlm_role_is_domain_member(struct gssntlm_ctx *ctx); bool gssntlm_sec_lm_ok(struct gssntlm_ctx *ctx); bool gssntlm_sec_ntlm_ok(struct gssntlm_ctx *ctx); bool gssntlm_sec_v2_ok(struct gssntlm_ctx *ctx); bool gssntlm_ext_sec_ok(struct gssntlm_ctx *ctx); uint32_t gssntlm_context_is_valid(struct gssntlm_ctx *ctx, time_t *time_now); int gssntlm_get_lm_compatibility_level(void); uint32_t gssntlm_mech_invoke(uint32_t *minor_status, const gss_OID desired_mech, const gss_OID desired_object, gss_buffer_t value); void gssntlm_int_release_name(struct gssntlm_name *name); void gssntlm_int_release_cred(struct gssntlm_cred *cred); size_t gssntlm_get_attrs_count(const struct gssntlm_name_attribute *attrs); int gssntlm_copy_attrs(const struct gssntlm_name_attribute *src, struct gssntlm_name_attribute **dst); struct gssntlm_name_attribute *gssntlm_find_attr( struct gssntlm_name_attribute *attrs, const char *attr_name, size_t attr_name_len); void gssntlm_release_attrs(struct gssntlm_name_attribute **attrs); int gssntlm_copy_name(struct gssntlm_name *src, struct gssntlm_name *dst); int gssntlm_copy_creds(struct gssntlm_cred *in, struct gssntlm_cred *out); void *external_get_context(void); void external_free_context(void *ctx); uint32_t external_netbios_get_names(void *ctx, char **computer, char **domain); uint32_t external_get_creds(void *ctx, struct gssntlm_name *name, struct gssntlm_cred *cred); uint32_t external_cli_auth(struct gssntlm_ctx *ctx, struct gssntlm_cred *cred, uint32_t in_flags, gss_channel_bindings_t input_chan_bindings); uint32_t external_srv_auth(struct gssntlm_ctx *ctx, struct gssntlm_cred *cred, struct ntlm_buffer *nt_chal_resp, struct ntlm_buffer *lm_chal_resp, struct ntlm_key *session_base_key); uint32_t netbios_get_names(void *ctx, char *computer_name, char **netbios_host, char **netbios_domain); bool is_ntlm_v1(struct ntlm_buffer *nt_chal_resp); uint32_t gssntlm_cli_auth(uint32_t *minor, struct gssntlm_ctx *ctx, struct gssntlm_cred *cred, struct ntlm_buffer *target_info, uint32_t in_flags, gss_channel_bindings_t input_chan_bindings); uint32_t gssntlm_srv_auth(uint32_t *minor, struct gssntlm_ctx *ctx, struct gssntlm_cred *cred, struct ntlm_buffer *nt_chal_resp, struct ntlm_buffer *lm_chal_resp, struct ntlm_key *key_exchange_key); extern const gss_OID_desc gssntlm_oid; uint32_t gssntlm_acquire_cred(uint32_t *minor_status, gss_name_t desired_name, uint32_t time_req, gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, uint32_t *time_rec); uint32_t gssntlm_acquire_cred_from(uint32_t *minor_status, void *external_context, gss_name_t desired_name, uint32_t time_req, gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_const_key_value_set_t cred_store, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, uint32_t *time_rec); uint32_t gssntlm_acquire_cred_with_password(uint32_t *minor_status, gss_name_t desired_name, gss_buffer_t password, uint32_t time_req, gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, uint32_t *time_rec); uint32_t gssntlm_release_cred(uint32_t *minor_status, gss_cred_id_t *cred_handle); uint32_t gssntlm_import_name(uint32_t *minor_status, gss_buffer_t input_name_buffer, gss_OID input_name_type, gss_name_t *output_name); uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status, gss_const_OID mech_type, gss_buffer_t input_name_buffer, gss_OID input_name_type, gss_name_t *output_name); uint32_t gssntlm_duplicate_name(uint32_t *minor_status, const gss_name_t input_name, gss_name_t *dest_name); uint32_t gssntlm_release_name(uint32_t *minor_status, gss_name_t *input_name); uint32_t gssntlm_init_sec_context(uint32_t *minor_status, gss_cred_id_t claimant_cred_handle, gss_ctx_id_t *context_handle, gss_name_t target_name, gss_OID mech_type, uint32_t req_flags, uint32_t time_req, gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, gss_OID *actual_mech_type, gss_buffer_t output_token, uint32_t *ret_flags, uint32_t *time_rec); uint32_t gssntlm_delete_sec_context(uint32_t *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t output_token); uint32_t gssntlm_context_time(uint32_t *minor_status, gss_ctx_id_t context_handle, uint32_t *time_rec); uint32_t gssntlm_accept_sec_context(uint32_t *minor_status, gss_ctx_id_t *context_handle, gss_cred_id_t acceptor_cred_handle, gss_buffer_t input_token_buffer, gss_channel_bindings_t input_chan_bindings, gss_name_t *src_name, gss_OID *mech_type, gss_buffer_t output_token, uint32_t *ret_flags, uint32_t *time_rec, gss_cred_id_t *delegated_cred_handle); uint32_t gssntlm_set_sec_context_option(uint32_t *minor_status, gss_ctx_id_t *context_handle, const gss_OID desired_object, const gss_buffer_t value); uint32_t gssntlm_inquire_sec_context_by_oid(uint32_t *minor_status, const gss_ctx_id_t context_handle, const gss_OID desired_object, gss_buffer_set_t *data_set); uint32_t gssntlm_get_mic(uint32_t *minor_status, gss_ctx_id_t context_handle, gss_qop_t qop_req, gss_buffer_t message_buffer, gss_buffer_t message_token); uint32_t gssntlm_verify_mic(uint32_t *minor_status, gss_ctx_id_t context_handle, gss_buffer_t message_buffer, gss_buffer_t message_token, gss_qop_t *qop_state); uint32_t gssntlm_wrap(uint32_t *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, gss_buffer_t input_message_buffer, int *conf_state, gss_buffer_t output_message_buffer); uint32_t gssntlm_unwrap(uint32_t *minor_status, gss_ctx_id_t context_handle, gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int *conf_state, gss_qop_t *qop_state); uint32_t gssntlm_wrap_size_limit(uint32_t *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, uint32_t req_output_size, uint32_t *max_input_size); uint32_t gssntlm_inquire_context(uint32_t *minor_status, gss_ctx_id_t context_handle, gss_name_t *src_name, gss_name_t *targ_name, uint32_t *lifetime_rec, gss_OID *mech_type, uint32_t *ctx_flags, int *locally_initiated, int *open); uint32_t gssntlm_display_name(uint32_t *minor_status, gss_name_t input_name, gss_buffer_t output_name_buffer, gss_OID *output_name_type); uint32_t gssntlm_localname(uint32_t *minor_status, const gss_name_t name, gss_const_OID mech_type, gss_buffer_t localname); uint32_t gssntlm_inquire_cred(uint32_t *minor_status, gss_cred_id_t cred_handle, gss_name_t *name, uint32_t *lifetime, gss_cred_usage_t *cred_usage, gss_OID_set *mechanisms); uint32_t gssntlm_inquire_cred_by_mech(uint32_t *minor_status, gss_cred_id_t cred_handle, gss_OID mech_type, gss_name_t *name, uint32_t *initiator_lifetime, uint32_t *acceptor_lifetime, gss_cred_usage_t *cred_usage); uint32_t gssntlm_export_sec_context(uint32_t *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t interprocess_token); uint32_t gssntlm_import_sec_context(uint32_t *minor_status, gss_buffer_t interprocess_token, gss_ctx_id_t *context_handle); uint32_t gssntlm_export_cred(uint32_t *minor_status, gss_cred_id_t cred_handle, gss_buffer_t token); uint32_t gssntlm_import_cred(uint32_t *minor_status, gss_buffer_t token, gss_cred_id_t *cred_handle); uint32_t gssntlm_display_status(uint32_t *minor_status, uint32_t status_value, int status_type, gss_OID mech_type, uint32_t *message_context, gss_buffer_t status_string); uint32_t gssntlm_get_name_attribute(uint32_t *minor_status, gss_name_t name, gss_buffer_t attr, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more); uint32_t gssntlm_inquire_name(uint32_t *minor_status, gss_name_t name, int *name_is_MN, gss_OID *MN_mech, gss_buffer_set_t *attrs); uint32_t gssntlm_inquire_saslname_for_mech(OM_uint32 *minor_status, const gss_OID desired_mech, gss_buffer_t sasl_mech_name, gss_buffer_t mech_name, gss_buffer_t mech_description); uint32_t gssntlm_inquire_mech_for_saslname(OM_uint32 *minor_status, const gss_buffer_t sasl_mech_name, gss_OID *mech_type); uint32_t gssntlm_inquire_attrs_for_mech(uint32_t *minor_status, gss_const_OID mech_oid, gss_OID_set *mech_attrs, gss_OID_set *known_mech_attrs); uint32_t gssntlm_set_cred_option(uint32_t *minor_status, gss_cred_id_t *cred_handle, const gss_OID desired_object, const gss_buffer_t value); #endif /* _GSS_NTLMSSP_H_ */ gss-ntlmssp-1.3.1/src/gss_ntlmssp_winbind.h000066400000000000000000000022671456736161100210550ustar00rootroot00000000000000/* Copyright (C) 2014 GSS-NTLMSSP contributors, see COPYING for License */ void *winbind_get_context(void); void winbind_free_context(void *ectx); uint32_t winbind_get_names(void *ectx, char **computer, char **domain); uint32_t winbind_get_creds(void *ectx, struct gssntlm_name *name, struct gssntlm_cred *cred); uint32_t winbind_cli_auth(void *ectx, char *user, char *domain, gss_channel_bindings_t input_chan_bindings, uint32_t in_flags, uint32_t *neg_flags, struct ntlm_buffer *nego_msg, struct ntlm_buffer *chal_msg, struct ntlm_buffer *auth_msg, struct ntlm_key *exported_session_key); uint32_t winbind_srv_auth(void *ectx, char *user, char *domain, char *workstation, uint8_t *challenge, struct ntlm_buffer *nt_chal_resp, struct ntlm_buffer *lm_chal_resp, struct ntlm_key *ntlmv2_key, struct gssntlm_name_attribute **auth_attrs); gss-ntlmssp-1.3.1/src/gss_sec_ctx.c000066400000000000000000001351341456736161100172660ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ #include #include #include #include #include #include "gssapi_ntlmssp.h" #include "gss_ntlmssp.h" uint32_t gssntlm_init_sec_context(uint32_t *minor_status, gss_cred_id_t claimant_cred_handle, gss_ctx_id_t *context_handle, gss_name_t target_name, gss_OID mech_type, uint32_t req_flags, uint32_t time_req, gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, gss_OID *actual_mech_type, gss_buffer_t output_token, uint32_t *ret_flags, uint32_t *time_rec) { struct gssntlm_ctx *ctx; struct gssntlm_name *server = NULL; struct gssntlm_cred *cred = NULL; char *nb_computer_name = NULL; char *nb_domain_name = NULL; struct gssntlm_name *client_name = NULL; uint32_t in_flags; uint32_t msg_type; char *trgt_name = NULL; struct ntlm_buffer challenge = { 0 }; struct ntlm_buffer target_info = { 0 }; int lm_compat_lvl; uint32_t tmpmin; uint32_t retmin = 0; uint32_t retmaj = 0; ctx = (struct gssntlm_ctx *)(*context_handle); /* reset return values */ if (actual_mech_type) *actual_mech_type = NULL; if (ret_flags) *ret_flags = 0; if (time_rec) *time_rec = 0; if (output_token == GSS_C_NO_BUFFER) { return GSSERRS(0, GSS_S_CALL_INACCESSIBLE_WRITE); } if (target_name) { server = (struct gssntlm_name *)target_name; if (server->type != GSSNTLM_NAME_SERVER) { return GSSERRS(ERR_NOSRVNAME, GSS_S_BAD_NAMETYPE); } if (!server->data.server.name || !server->data.server.name[0]) { return GSSERRS(ERR_NONAME, GSS_S_BAD_NAME); } } if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { if (req_flags & GSS_C_ANON_FLAG) { set_GSSERRS(ERR_NOARG, GSS_S_UNAVAILABLE); goto done; } else { retmaj = gssntlm_acquire_cred(&retmin, NULL, time_req, NULL, GSS_C_INITIATE, (gss_cred_id_t *)&cred, NULL, time_rec); if (retmaj) goto done; } } else { cred = (struct gssntlm_cred *)claimant_cred_handle; if (cred->type != GSSNTLM_CRED_USER && cred->type != GSSNTLM_CRED_EXTERNAL) { set_GSSERRS(ERR_NOARG, GSS_S_CRED_UNAVAIL); goto done; } if (cred->type == GSSNTLM_CRED_EXTERNAL && cred->cred.external.creds_in_cache == 0) { set_GSSERRS(ERR_NOARG, GSS_S_CRED_UNAVAIL); goto done; } } if (ctx == NULL) { /* first call */ ctx = calloc(1, sizeof(struct gssntlm_ctx)); if (!ctx) { set_GSSERR(ENOMEM); goto done; } ctx->external_context = external_get_context(); retmin = gssntlm_copy_name(&cred->cred.user.user, &ctx->source_name); if (retmin) { set_GSSERR(retmin); goto done; } if (server) { retmin = gssntlm_copy_name(server, &ctx->target_name); if (retmin) { set_GSSERR(retmin); goto done; } } ctx->gss_flags = req_flags; ctx->neg_flags = NTLMSSP_DEFAULT_CLIENT_FLAGS; /* override neg_flags default if requested */ if (cred->neg_flags) { ctx->neg_flags = cred->neg_flags; } /* * we ignore unsupported flags for now * * GSS_C_DELEG_FLAG * GSS_C_MUTUAL_FLAG * GSS_C_PROT_READY_FLAG * GSS_C_TRANS_FLAG * GSS_C_DELEG_POLICY_FLAG * GSS_C_DCE_STYLE * GSS_C_EXTENDED_ERROR_FLAG */ if ((req_flags & GSS_C_INTEG_FLAG) || (req_flags & GSS_C_REPLAY_FLAG) || (req_flags & GSS_C_SEQUENCE_FLAG)) { ctx->neg_flags |= NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_KEY_EXCH; } if (req_flags & GSS_C_CONF_FLAG) { ctx->neg_flags |= NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_LM_KEY | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY; } if (req_flags & GSS_C_ANON_FLAG) { ctx->neg_flags |= NTLMSSP_ANONYMOUS; } if (req_flags & GSS_C_IDENTIFY_FLAG) { ctx->neg_flags |= NTLMSSP_NEGOTIATE_IDENTIFY; } if (req_flags & GSS_C_DATAGRAM_FLAG) { ctx->neg_flags |= NTLMSSP_NEGOTIATE_DATAGRAM | NTLMSSP_NEGOTIATE_KEY_EXCH; } /* acquire our own name */ if (!client_name) { gss_buffer_desc tmpbuf; tmpbuf.value = discard_const(""); tmpbuf.length = 0; retmaj = gssntlm_import_name_by_mech(&retmin, &gssntlm_oid, &tmpbuf, GSS_C_NT_HOSTBASED_SERVICE, (gss_name_t *)&client_name); if (retmaj) goto done; } retmin = netbios_get_names(ctx->external_context, client_name->data.server.name, &nb_computer_name, &nb_domain_name); if (retmin) { set_GSSERR(retmin); goto done; } ctx->workstation = strdup(nb_computer_name); if (!ctx->workstation) { set_GSSERR(ENOMEM); goto done; } gssntlm_set_role(ctx, GSSNTLM_CLIENT, nb_domain_name); lm_compat_lvl = gssntlm_get_lm_compatibility_level(); if (!gssntlm_required_security(lm_compat_lvl, ctx)) { set_GSSERR(ERR_BADLMLVL); goto done; } if (!gssntlm_sec_lm_ok(ctx)) { ctx->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; ctx->neg_flags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY; } if (!gssntlm_ext_sec_ok(ctx)) { ctx->neg_flags &= ~NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY; } retmin = ntlm_init_ctx(&ctx->ntlm); if (retmin) { set_GSSERR(retmin); goto done; } /* only in connectionless mode we may receive an input buffer * on the the first call, if DATAGRAM is not selected and * we have a buffer here, somethings wrong */ if (ctx->neg_flags & NTLMSSP_NEGOTIATE_DATAGRAM) { if ((input_token == GSS_C_NO_BUFFER) || (input_token->length == 0)) { /* in connectionless mode we return an empty buffer here: * see MS-NLMP 1.3.1.3 and 1.7 */ output_token->value = NULL; output_token->length = 0; /* and return the ball */ ctx->stage = NTLMSSP_STAGE_NEGOTIATE; set_GSSERRS(0, GSS_S_CONTINUE_NEEDED); goto done; } } else { if (input_token && input_token->length != 0) { set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); goto done; } retmin = ntlm_encode_neg_msg(ctx->ntlm, ctx->neg_flags, NULL, NULL, &ctx->nego_msg); if (retmin) { set_GSSERR(retmin); goto done; } output_token->value = malloc(ctx->nego_msg.length); if (!output_token->value) { set_GSSERR(ENOMEM); goto done; } memcpy(output_token->value, ctx->nego_msg.data, ctx->nego_msg.length); output_token->length = ctx->nego_msg.length; ctx->stage = NTLMSSP_STAGE_NEGOTIATE; set_GSSERRS(0, GSS_S_CONTINUE_NEEDED); goto done; } /* If we get here we are in connectionless mode and where called * with a chalenge message in the input buffer */ ctx->stage = NTLMSSP_STAGE_NEGOTIATE; } if (ctx == NULL) { /* this should not happen */ set_GSSERR(ERR_IMPOSSIBLE); goto done; } else { if (!gssntlm_role_is_client(ctx)) { set_GSSERRS(ERR_WRONGCTX, GSS_S_NO_CONTEXT); goto done; } ctx->chal_msg.data = malloc(input_token->length); if (!ctx->chal_msg.data) { set_GSSERR(ENOMEM); goto done; } memcpy(ctx->chal_msg.data, input_token->value, input_token->length); ctx->chal_msg.length = input_token->length; retmin = ntlm_decode_msg_type(ctx->ntlm, &ctx->chal_msg, &msg_type); if (retmin) { set_GSSERRS(retmin, GSS_S_DEFECTIVE_TOKEN); goto done; } if (msg_type != CHALLENGE_MESSAGE || ctx->stage != NTLMSSP_STAGE_NEGOTIATE) { set_GSSERRS(ERR_WRONGMSG, GSS_S_NO_CONTEXT); goto done; } /* store challenge in ctx */ challenge.data = ctx->server_chal; challenge.length = 8; retmin = ntlm_decode_chal_msg(ctx->ntlm, &ctx->chal_msg, &in_flags, &trgt_name, &challenge, &target_info); if (retmin) { set_GSSERRS(retmin, GSS_S_DEFECTIVE_TOKEN); goto done; } /* mask unacceptable flags */ if (!gssntlm_sec_lm_ok(ctx)) { in_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; } if (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_56)) { in_flags &= ~NTLMSSP_NEGOTIATE_56; } if (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_128)) { in_flags &= ~NTLMSSP_NEGOTIATE_128; } if (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) { in_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH; } if (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_OEM)) { in_flags &= ~NTLMSSP_NEGOTIATE_OEM; } if (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_UNICODE)) { in_flags &= ~NTLMSSP_NEGOTIATE_UNICODE; } /* check required flags */ if ((ctx->neg_flags & NTLMSSP_NEGOTIATE_128) && (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_56)) && (!(in_flags & NTLMSSP_NEGOTIATE_128))) { set_GSSERR(ERR_REQNEGFLAG); goto done; } if ((ctx->neg_flags & NTLMSSP_NEGOTIATE_SEAL) && (!(in_flags & NTLMSSP_NEGOTIATE_SEAL))) { set_GSSERR(ERR_REQNEGFLAG); goto done; } if ((ctx->neg_flags & NTLMSSP_NEGOTIATE_SIGN) && (!(in_flags & NTLMSSP_NEGOTIATE_SIGN))) { set_GSSERR(ERR_REQNEGFLAG); goto done; } if (!(in_flags & (NTLMSSP_NEGOTIATE_OEM | NTLMSSP_NEGOTIATE_UNICODE))) { /* no common understanding */ set_GSSERR(ERR_FAILNEGFLAGS); goto done; } if (ctx->gss_flags & GSS_C_DATAGRAM_FLAG) { if (!(in_flags & NTLMSSP_NEGOTIATE_DATAGRAM)) { /* no common understanding */ set_GSSERR(ERR_FAILNEGFLAGS); goto done; } if (!(in_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) { /* no common understanding */ set_GSSERR(ERR_FAILNEGFLAGS); goto done; } if ((in_flags & NTLMSSP_NEGOTIATE_OEM) && (in_flags & NTLMSSP_NEGOTIATE_UNICODE)) { /* prefer Unicode */ in_flags &= ~NTLMSSP_NEGOTIATE_OEM; } } else { in_flags &= ~NTLMSSP_NEGOTIATE_DATAGRAM; if ((in_flags & NTLMSSP_NEGOTIATE_OEM) && (in_flags & NTLMSSP_NEGOTIATE_UNICODE)) { /* server sent both?? This is broken, proceed only if there * are no strings set in the challenge packet and downgrade * to OEM charset hoping the server will cope */ if (in_flags & (NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_TARGET_TYPE_SERVER | NTLMSSP_TARGET_TYPE_DOMAIN)) { set_GSSERR(ERR_BADNEGFLAGS); goto done; } else { in_flags &= ~NTLMSSP_NEGOTIATE_UNICODE; } } } /* Now that everything has been checked clear non * negotiated flags */ ctx->neg_flags &= in_flags; retmaj = gssntlm_cli_auth(&retmin, ctx, cred, &target_info, in_flags, input_chan_bindings); if (retmaj) goto done; if (in_flags & (NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL)) { retmin = ntlm_signseal_keys(in_flags, true, &ctx->exported_session_key, &ctx->crypto_state); if (retmin) { set_GSSERR(retmin); goto done; } } if (ctx->neg_flags & NTLMSSP_NEGOTIATE_SIGN) { ctx->gss_flags |= GSS_C_INTEG_FLAG; } if (ctx->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { ctx->gss_flags |= GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG; } ctx->stage = NTLMSSP_STAGE_DONE; output_token->value = malloc(ctx->auth_msg.length); if (!output_token->value) { set_GSSERR(ENOMEM); goto done; } memcpy(output_token->value, ctx->auth_msg.data, ctx->auth_msg.length); output_token->length = ctx->auth_msg.length; /* For now use the same as the challenge/response lifetime (36h) */ ctx->expiration_time = time(NULL) + MAX_CHALRESP_LIFETIME; ctx->int_flags |= NTLMSSP_CTX_FLAG_ESTABLISHED; set_GSSERRS(0, GSS_S_COMPLETE); } done: if ((retmaj != GSS_S_COMPLETE) && (retmaj != GSS_S_CONTINUE_NEEDED)) { gssntlm_delete_sec_context(&tmpmin, (gss_ctx_id_t *)&ctx, NULL); } else { if (actual_mech_type) *actual_mech_type = discard_const(&gssntlm_oid); if (ret_flags) *ret_flags = ctx->gss_flags; if (time_rec) *time_rec = GSS_C_INDEFINITE; } *context_handle = (gss_ctx_id_t)ctx; if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) { /* do not leak it, if not passed in */ gssntlm_release_cred(&tmpmin, (gss_cred_id_t *)&cred); } gssntlm_release_name(&tmpmin, (gss_name_t *)&client_name); safefree(nb_computer_name); safefree(nb_domain_name); safefree(trgt_name); ntlm_free_buffer_data(&target_info); return GSSERR(); } uint32_t gssntlm_delete_sec_context(uint32_t *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t output_token) { struct gssntlm_ctx *ctx; uint32_t retmin; uint32_t retmaj; int ret; if (!context_handle) { set_GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); goto done; } if (*context_handle == NULL) { set_GSSERRS(ERR_NOARG, GSS_S_NO_CONTEXT); goto done; } ctx = (struct gssntlm_ctx *)*context_handle; safefree(ctx->workstation); ret = ntlm_free_ctx(&ctx->ntlm); safefree(ctx->nego_msg.data); safefree(ctx->chal_msg.data); safefree(ctx->auth_msg.data); ctx->nego_msg.length = 0; ctx->chal_msg.length = 0; ctx->auth_msg.length = 0; gssntlm_int_release_name(&ctx->source_name); gssntlm_int_release_name(&ctx->target_name); ntlm_release_rc4_state(&ctx->crypto_state); external_free_context(ctx->external_context); safezero((uint8_t *)ctx, sizeof(struct gssntlm_ctx)); safefree(*context_handle); set_GSSERRS(ret, ret ? GSS_S_FAILURE : GSS_S_COMPLETE); done: return GSSERR(); } uint32_t gssntlm_context_time(uint32_t *minor_status, gss_ctx_id_t context_handle, uint32_t *time_rec) { struct gssntlm_ctx *ctx; time_t now; uint32_t retmin; uint32_t retmaj; if (context_handle == GSS_C_NO_CONTEXT) { set_GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); goto done; } ctx = (struct gssntlm_ctx *)context_handle; retmaj = gssntlm_context_is_valid(ctx, &now); if (retmaj) { set_GSSERRS(ERR_BADCTX, retmaj); goto done; } *time_rec = ctx->expiration_time - now; set_GSSERRS(0, GSS_S_COMPLETE); done: return GSSERR(); } uint32_t gssntlm_accept_sec_context(uint32_t *minor_status, gss_ctx_id_t *context_handle, gss_cred_id_t acceptor_cred_handle, gss_buffer_t input_token, gss_channel_bindings_t input_chan_bindings, gss_name_t *src_name, gss_OID *mech_type, gss_buffer_t output_token, uint32_t *ret_flags, uint32_t *time_rec, gss_cred_id_t *delegated_cred_handle) { struct gssntlm_ctx *ctx; struct gssntlm_cred *cred = NULL; int lm_compat_lvl = -1; struct ntlm_buffer challenge = { 0 }; struct gssntlm_name *server_name = NULL; char *nb_computer_name = NULL; char *nb_domain_name = NULL; char *chal_target_name; uint64_t timestamp; struct ntlm_buffer target_info = { 0 }; struct ntlm_buffer nt_chal_resp = { 0 }; struct ntlm_buffer lm_chal_resp = { 0 }; struct ntlm_buffer enc_sess_key = { 0 }; struct ntlm_key encrypted_random_session_key = { .length = 16 }; struct ntlm_key key_exchange_key = { .length = 16 }; uint8_t micbuf[16]; struct ntlm_buffer mic = { micbuf, 16 }; char *dom_name = NULL; char *usr_name = NULL; char *wks_name = NULL; struct gssntlm_name *gss_usrname = NULL; struct gssntlm_cred *usr_cred = NULL; uint32_t retmin; uint32_t retmaj; uint32_t tmpmin; uint32_t in_flags; uint32_t msg_type; uint32_t av_flags = 0; struct ntlm_buffer unhashed_cb = { 0 }; struct ntlm_buffer av_cb = { 0 }; if (context_handle == NULL) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } if (output_token == GSS_C_NO_BUFFER) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE); } if (src_name) *src_name = GSS_C_NO_NAME; if (mech_type) *mech_type = GSS_C_NO_OID; if (ret_flags) *ret_flags = 0; if (time_rec) *time_rec = 0; if (delegated_cred_handle) *delegated_cred_handle = GSS_C_NO_CREDENTIAL; if (acceptor_cred_handle) { cred = (struct gssntlm_cred *)acceptor_cred_handle; if (cred->type != GSSNTLM_CRED_SERVER) { set_GSSERRS(ERR_NOSRVCRED, GSS_S_DEFECTIVE_CREDENTIAL); goto done; } if (cred->cred.server.name.type != GSSNTLM_NAME_SERVER) { set_GSSERRS(ERR_NOSRVNAME, GSS_S_DEFECTIVE_CREDENTIAL); goto done; } retmaj = gssntlm_duplicate_name(&retmin, (const gss_name_t)&cred->cred.server.name, (gss_name_t *)&server_name); if (retmaj) goto done; } if (*context_handle == GSS_C_NO_CONTEXT) { /* first call */ ctx = calloc(1, sizeof(struct gssntlm_ctx)); if (!ctx) { set_GSSERR(ENOMEM); goto done; } ctx->external_context = external_get_context(); /* acquire our own name */ if (!server_name) { gss_buffer_desc tmpbuf; tmpbuf.value = discard_const(""); tmpbuf.length = 0; retmaj = gssntlm_import_name_by_mech(&retmin, &gssntlm_oid, &tmpbuf, GSS_C_NT_HOSTBASED_SERVICE, (gss_name_t *)&server_name); if (retmaj) goto done; } retmin = gssntlm_copy_name(server_name, &ctx->target_name); if (retmin) { set_GSSERR(retmin); goto done; } retmin = netbios_get_names(ctx->external_context, server_name->data.server.name, &nb_computer_name, &nb_domain_name); if (retmin) { set_GSSERR(retmin); goto done; } ctx->workstation = strdup(nb_computer_name); if (!ctx->workstation) { set_GSSERR(ENOMEM); goto done; } gssntlm_set_role(ctx, GSSNTLM_SERVER, nb_domain_name); lm_compat_lvl = gssntlm_get_lm_compatibility_level(); if (!gssntlm_required_security(lm_compat_lvl, ctx)) { set_GSSERR(ERR_BADLMLVL); goto done; } ctx->neg_flags = NTLMSSP_DEFAULT_SERVER_FLAGS; /* Fixme: How do we allow anonymous negotition ? */ /* override neg_flags default if requested */ if (cred && cred->neg_flags) { ctx->neg_flags = cred->neg_flags; } if (gssntlm_sec_lm_ok(ctx)) { ctx->neg_flags |= NTLMSSP_REQUEST_NON_NT_SESSION_KEY; ctx->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY; } if (gssntlm_ext_sec_ok(ctx)) { ctx->neg_flags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY; } retmin = ntlm_init_ctx(&ctx->ntlm); if (retmin) { set_GSSERR(retmin); goto done; } if (input_token && input_token->length != 0) { ctx->nego_msg.data = malloc(input_token->length); if (!ctx->nego_msg.data) { set_GSSERR(ENOMEM); goto done; } memcpy(ctx->nego_msg.data, input_token->value, input_token->length); ctx->nego_msg.length = input_token->length; retmin = ntlm_decode_msg_type(ctx->ntlm, &ctx->nego_msg, &msg_type); if (retmin || (msg_type != NEGOTIATE_MESSAGE)) { set_GSSERRS(retmin, GSS_S_DEFECTIVE_TOKEN); goto done; } retmin = ntlm_decode_neg_msg(ctx->ntlm, &ctx->nego_msg, &in_flags, NULL, NULL); if (retmin) { set_GSSERRS(retmin, GSS_S_DEFECTIVE_TOKEN); goto done; } /* leave only the crossing between requested and allowed flags */ ctx->neg_flags &= in_flags; /* Try to force the use of NTLMSSP_NEGOTIATE_VERSION even if the * client did not advertize it in their negotiate message, but * should be capable of providing it. * This is what Windows Server 2022 also does, and addresses * issues with older clients that incorrectly deal with MIC * calculations in absence of this flag. */ if ((ctx->neg_flags & (NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN))) { ctx->neg_flags |= NTLMSSP_NEGOTIATE_VERSION; } } else { /* If there is no negotiate message set datagram mode */ ctx->neg_flags |= NTLMSSP_NEGOTIATE_DATAGRAM | \ NTLMSSP_NEGOTIATE_KEY_EXCH; } /* TODO: Check some minimum required flags ? */ /* TODO: Check MS-NLMP ServerRequire128bitEncryption */ if (ctx->neg_flags & NTLMSSP_NEGOTIATE_UNICODE) { /* Choose unicode in preferemce if both are set */ ctx->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM; } else if (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_OEM)) { /* no agreement */ set_GSSERR(ERR_FAILNEGFLAGS); goto done; } if (ctx->neg_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) { ctx->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; } if (ctx->neg_flags & NTLMSSP_REQUEST_TARGET) { ctx->neg_flags |= NTLMSSP_NEGOTIATE_TARGET_INFO; } if (ctx->neg_flags & NTLMSSP_NEGOTIATE_SIGN) { ctx->gss_flags |= GSS_C_INTEG_FLAG; } if (ctx->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { ctx->gss_flags |= GSS_C_CONF_FLAG; } if (ctx->neg_flags & NTLMSSP_NEGOTIATE_DATAGRAM) { ctx->gss_flags |= GSS_C_DATAGRAM_FLAG; } /* Random server challenge */ challenge.data = ctx->server_chal; challenge.length = 8; retmin = RAND_BUFFER(&challenge); if (retmin) { set_GSSERR(retmin); goto done; } /* TODO: allow client applications to set a context option to * provide an av_flags default value so that flags like * MSVAVFLAGS_UNVERIFIED_SPN can be set. By default SSPI does * not set this flag, and setting it causes servers with restrictive * policy to fail authentication. Given no MS client set this flag * by default, neither should we until there is a clear need. We * trust our calling applications to do the right thing here. * av_flags = MSVAVFLAGS_UNVERIFIED_SPN; */ timestamp = ntlm_timestamp_now(); retmin = ntlm_encode_target_info(ctx->ntlm, nb_computer_name, nb_domain_name, server_name->data.server.name, NULL, NULL, av_flags ? &av_flags : NULL, /* don't include empty MsvAvFlags */ ×tamp, NULL, server_name->data.server.spn, NULL, &target_info); if (retmin) { set_GSSERR(retmin); goto done; } if (gssntlm_role_is_domain_member(ctx)) { chal_target_name = nb_domain_name; ctx->neg_flags |= NTLMSSP_TARGET_TYPE_DOMAIN; } else { chal_target_name = nb_computer_name; ctx->neg_flags |= NTLMSSP_TARGET_TYPE_SERVER; } retmin = ntlm_encode_chal_msg(ctx->ntlm, ctx->neg_flags, chal_target_name, &challenge, &target_info, &ctx->chal_msg); if (retmin) { set_GSSERR(retmin); goto done; } ctx->stage = NTLMSSP_STAGE_CHALLENGE; output_token->value = malloc(ctx->chal_msg.length); if (!output_token->value) { set_GSSERR(ENOMEM); goto done; } memcpy(output_token->value, ctx->chal_msg.data, ctx->chal_msg.length); output_token->length = ctx->chal_msg.length; retmaj = GSS_S_CONTINUE_NEEDED; } else { ctx = (struct gssntlm_ctx *)(*context_handle); if (!gssntlm_role_is_server(ctx)) { set_GSSERRS(ERR_WRONGCTX, GSS_S_NO_CONTEXT); goto done; } if ((input_token == GSS_C_NO_BUFFER) || (input_token->length == 0)) { set_GSSERRS(ERR_NOTOKEN, GSS_S_DEFECTIVE_TOKEN); goto done; } ctx->auth_msg.data = malloc(input_token->length); if (!ctx->auth_msg.data) { set_GSSERR(ENOMEM); goto done; } memcpy(ctx->auth_msg.data, input_token->value, input_token->length); ctx->auth_msg.length = input_token->length; retmin = ntlm_decode_msg_type(ctx->ntlm, &ctx->auth_msg, &msg_type); if (retmin) { set_GSSERRS(retmin, GSS_S_DEFECTIVE_TOKEN); goto done; } if (msg_type != AUTHENTICATE_MESSAGE || ctx->stage != NTLMSSP_STAGE_CHALLENGE) { set_GSSERRS(ERR_WRONGMSG, GSS_S_NO_CONTEXT); goto done; } retmin = ntlm_decode_auth_msg(ctx->ntlm, &ctx->auth_msg, ctx->neg_flags, &lm_chal_resp, &nt_chal_resp, &dom_name, &usr_name, &wks_name, &enc_sess_key, &target_info, &mic); if (retmin) { set_GSSERRS(retmin, GSS_S_DEFECTIVE_TOKEN); goto done; } if (target_info.length > 0) { retmin = ntlm_decode_target_info(ctx->ntlm, &target_info, NULL, NULL, NULL, NULL, NULL, NULL, &av_flags, NULL, NULL, &av_cb); if (retmin) { set_GSSERR(retmin); goto done; } } if ((ctx->neg_flags & NTLMSSP_NEGOTIATE_DATAGRAM) && !(ctx->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) { set_GSSERRS(ERR_BADNEGFLAGS, GSS_S_DEFECTIVE_TOKEN); goto done; } if ((usr_name == NULL) || (usr_name[0] == '\0')) { if ((nt_chal_resp.length == 0) && (((lm_chal_resp.length == 1) && (lm_chal_resp.data[0] == '\0')) || (lm_chal_resp.length == 0))) { /* Anonymous auth */ /* FIXME: not supported for now */ set_GSSERR(ERR_NOTSUPPORTED); goto done; } else { set_GSSERR(ERR_NOUSRFOUND); goto done; } } else { char useratdom[1024]; size_t ulen, dlen, uadlen; gss_buffer_desc usrname; gss_const_key_value_set_t cred_store = GSS_C_NO_CRED_STORE; gss_key_value_set_desc cs; gss_key_value_element_desc cs_el; if (!dom_name) { dom_name = strdup(""); if (!dom_name) { set_GSSERR(ENOMEM); goto done; } } /* Use domain\username format as that allows to pass in * enterprise names without the need to escape them */ ulen = strlen(usr_name); dlen = strlen(dom_name); if (ulen + dlen + 2 > 1024) { set_GSSERR(ERR_NAMETOOLONG); goto done; } uadlen = dlen; if (dlen) { memcpy(useratdom, dom_name, dlen); } /* always add the domain separator, this way if the username * is an enteprise name (user@email.domain form) it will be * correctly recognized by gssntlm_import_name() as such */ useratdom[uadlen] = '\\'; uadlen++; /* finally add usernmae part */ memcpy(&useratdom[uadlen], usr_name, ulen); uadlen += ulen; useratdom[uadlen] = '\0'; usrname.value = useratdom; usrname.length = uadlen; retmaj = gssntlm_import_name(&retmin, &usrname, GSS_C_NT_USER_NAME, (gss_name_t *)&gss_usrname); if (retmaj) goto done; if (cred && cred->cred.server.keyfile) { cs_el.key = GSS_NTLMSSP_CS_KEYFILE; cs_el.value = cred->cred.server.keyfile; cs.count = 1; cs.elements = &cs_el; cred_store = &cs; } retmaj = gssntlm_acquire_cred_from(&retmin, ctx->external_context, (gss_name_t)gss_usrname, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, cred_store, (gss_cred_id_t *)&usr_cred, NULL, NULL); if (retmaj) goto done; /* We can't handle winbind credentials yet */ if (usr_cred->type != GSSNTLM_CRED_USER && usr_cred->type != GSSNTLM_CRED_EXTERNAL) { set_GSSERRS(ERR_NOUSRCRED, GSS_S_DEFECTIVE_CREDENTIAL); goto done; } retmin = gssntlm_copy_name(gss_usrname, &ctx->source_name); if (retmin) { set_GSSERR(retmin); goto done; } retmaj = gssntlm_srv_auth(&retmin, ctx, usr_cred, &nt_chal_resp, &lm_chal_resp, &key_exchange_key); if (retmaj) goto done; } if (ctx->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { memcpy(encrypted_random_session_key.data, enc_sess_key.data, 16); ctx->exported_session_key.length = 16; retmin = ntlm_encrypted_session_key(&key_exchange_key, &encrypted_random_session_key, &ctx->exported_session_key); if (retmin) { set_GSSERR(retmin); goto done; } } else { ctx->exported_session_key = key_exchange_key; } /* check if MIC was sent */ if (av_flags & MSVAVFLAGS_MIC_PRESENT) { retmin = ntlm_verify_mic(&ctx->exported_session_key, &ctx->nego_msg, &ctx->chal_msg, &ctx->auth_msg, &mic); if (retmin) { set_GSSERRS(retmin, GSS_S_DEFECTIVE_TOKEN); goto done; } } if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) { uint8_t zero_cb[16] = { 0 }; if (input_chan_bindings->initiator_addrtype != 0 || input_chan_bindings->initiator_address.length != 0 || input_chan_bindings->acceptor_addrtype != 0 || input_chan_bindings->acceptor_address.length != 0 || input_chan_bindings->application_data.length == 0) { set_GSSERRS(ERR_BADARG, GSS_S_BAD_BINDINGS); goto done; } unhashed_cb.length = input_chan_bindings->application_data.length; unhashed_cb.data = input_chan_bindings->application_data.value; if (av_cb.length && (av_cb.length != 16)) { set_GSSERRS(retmin, GSS_S_DEFECTIVE_TOKEN); goto done; } if (av_cb.length && (memcmp(av_cb.data, zero_cb, 16) != 0)) { retmin = ntlm_verify_channel_bindings(&unhashed_cb, &av_cb); if (retmin) { set_GSSERRS(retmin, GSS_S_DEFECTIVE_TOKEN); goto done; /* This flag has been introduced only recently in MIT krb5 */ #ifdef GSS_C_CHANNEL_BOUND_FLAG } else { ctx->gss_flags |= GSS_C_CHANNEL_BOUND_FLAG; #endif /* GSS_C_CHANNEL_BOUND_FLAG */ } } } if (ctx->neg_flags & (NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL)) { retmin = ntlm_signseal_keys(ctx->neg_flags, false, &ctx->exported_session_key, &ctx->crypto_state); if (retmin) { set_GSSERR(retmin); goto done; } } if (src_name) { retmaj = gssntlm_duplicate_name(&retmin, (gss_name_t)&ctx->source_name, src_name); if (retmaj) goto done; } ctx->stage = NTLMSSP_STAGE_DONE; ctx->expiration_time = time(NULL) + MAX_CHALRESP_LIFETIME; ctx->int_flags |= NTLMSSP_CTX_FLAG_ESTABLISHED; set_GSSERRS(0, GSS_S_COMPLETE); } done: if ((retmaj != GSS_S_COMPLETE) && (retmaj != GSS_S_CONTINUE_NEEDED)) { gssntlm_delete_sec_context(&tmpmin, (gss_ctx_id_t *)&ctx, NULL); } else { if (mech_type) *mech_type = discard_const(&gssntlm_oid); if (ret_flags) *ret_flags = ctx->gss_flags; if (time_rec) *time_rec = GSS_C_INDEFINITE; } *context_handle = (gss_ctx_id_t)ctx; gssntlm_release_name(&tmpmin, (gss_name_t *)&server_name); gssntlm_release_name(&tmpmin, (gss_name_t *)&gss_usrname); gssntlm_release_cred(&tmpmin, (gss_cred_id_t *)&usr_cred); safefree(nb_computer_name); safefree(nb_domain_name); safefree(usr_name); safefree(dom_name); safefree(wks_name); ntlm_free_buffer_data(&nt_chal_resp); ntlm_free_buffer_data(&lm_chal_resp); ntlm_free_buffer_data(&enc_sess_key); ntlm_free_buffer_data(&target_info); return GSSERR(); } uint32_t gssntlm_inquire_context(uint32_t *minor_status, gss_ctx_id_t context_handle, gss_name_t *src_name, gss_name_t *targ_name, uint32_t *lifetime_rec, gss_OID *mech_type, uint32_t *ctx_flags, int *locally_initiated, int *open) { struct gssntlm_ctx *ctx; uint32_t retmaj; uint32_t retmin; time_t now; ctx = (struct gssntlm_ctx *)context_handle; if (!ctx) { return GSSERRS(ERR_NOARG, GSS_S_NO_CONTEXT); } if (src_name) { retmaj = gssntlm_duplicate_name(&retmin, (gss_name_t)&ctx->source_name, src_name); if (retmaj) goto done; } if (targ_name) { retmaj = gssntlm_duplicate_name(&retmin, (gss_name_t)&ctx->target_name, targ_name); if (retmaj) goto done; } if (mech_type) { *mech_type = discard_const(&gssntlm_oid); } if (ctx_flags) { *ctx_flags = ctx->gss_flags; } if (locally_initiated) { if (gssntlm_role_is_client(ctx)) { *locally_initiated = 1; } else { *locally_initiated = 0; } } if (ctx->int_flags & NTLMSSP_CTX_FLAG_ESTABLISHED) { if (lifetime_rec) { now = time(NULL); if (ctx->expiration_time > now) { *lifetime_rec = 0; } else { *lifetime_rec = ctx->expiration_time - now; } } if (open) { *open = 1; } } else { if (lifetime_rec) { *lifetime_rec = 0; } if (open) { *open = 0; } } set_GSSERRS(0, GSS_S_COMPLETE); done: return GSSERR(); } gss_OID_desc set_seq_num_oid = { GSS_NTLMSSP_SET_SEQ_NUM_OID_LENGTH, discard_const(GSS_NTLMSSP_SET_SEQ_NUM_OID_STRING) }; uint32_t gssntlm_set_seq_num(uint32_t *minor_status, struct gssntlm_ctx *ctx, const gss_buffer_t value) { uint32_t retmin; uint32_t retmaj; if (ctx->gss_flags & GSS_C_DATAGRAM_FLAG) { if (value->length != 4) { return GSSERRS(ERR_BADARG, GSS_S_FAILURE); } memcpy(&ctx->crypto_state.recv.seq_num, value->value, value->length); ctx->crypto_state.send.seq_num = ctx->crypto_state.recv.seq_num; } else { return GSSERRS(ERR_WRONGCTX, GSS_S_FAILURE); } return GSSERRS(0, GSS_S_COMPLETE); } gss_OID_desc reset_crypto_oid = { GSS_NTLMSSP_RESET_CRYPTO_OID_LENGTH, discard_const(GSS_NTLMSSP_RESET_CRYPTO_OID_STRING) }; uint32_t gssntlm_reset_crypto(uint32_t *minor_status, struct gssntlm_ctx *ctx, const gss_buffer_t value) { uint32_t retmin; uint32_t retmaj; if (value->length != 4) { return GSSERRS(ERR_BADARG, GSS_S_FAILURE); } /* reset crypto state */ if (ctx->neg_flags & (NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL)) { uint32_t val; memcpy(&val, value->value, value->length); /* A val of 1 means we want to reset the verifier handle, * which is the receive handle for NTLM, otherwise we reset * the send handle. */ retmin = ntlm_reset_rc4_state(ctx->neg_flags, (val == 1), &ctx->exported_session_key, &ctx->crypto_state); if (retmin) { return GSSERRS(retmin, GSS_S_FAILURE); } } return GSSERRS(0, GSS_S_COMPLETE); } uint32_t gssntlm_set_sec_context_option(uint32_t *minor_status, gss_ctx_id_t *context_handle, const gss_OID desired_object, const gss_buffer_t value) { struct gssntlm_ctx *ctx; uint32_t retmin; uint32_t retmaj; if (context_handle == NULL || *context_handle == NULL) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } if (desired_object == GSS_C_NO_OID) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } ctx = (struct gssntlm_ctx *)*context_handle; /* set seq num */ if (gss_oid_equal(desired_object, &set_seq_num_oid)) { return gssntlm_set_seq_num(minor_status, ctx, value); } else if (gss_oid_equal(desired_object, &reset_crypto_oid)) { return gssntlm_reset_crypto(minor_status, ctx, value); } return GSSERRS(ERR_BADARG, GSS_S_UNAVAILABLE); } gss_OID_desc spnego_req_mic_oid = { GSS_SPNEGO_REQUIRE_MIC_OID_LENGTH, discard_const(GSS_SPNEGO_REQUIRE_MIC_OID_STRING) }; uint32_t gssntlm_spnego_req_mic(uint32_t *minor_status, struct gssntlm_ctx *ctx, gss_buffer_set_t *data_set) { gss_buffer_desc mic_buf; uint32_t retmin; uint32_t retmaj; uint32_t tmpmin; uint8_t mic_set; /* the simple fact the spnego layer is asking means it can handle * forcing mechlistMIC if we add a MIC to the Authenticate packet. * We expect this to be called before the authenticate token is * generated to set this flag ... */ ctx->int_flags |= NTLMSSP_CTX_FLAG_SPNEGO_CAN_MIC; /* ... and then again after, in which case if we actually did add * a MIC we can tell spnego to add a mechlistMIC */ if (ctx->int_flags & NTLMSSP_CTX_FLAG_AUTH_WITH_MIC) { mic_set = 1; } else { mic_set = 0; } mic_buf.value = &mic_set; mic_buf.length = sizeof(mic_set); retmaj = gss_add_buffer_set_member(&retmin, &mic_buf, data_set); if (retmaj != GSS_S_COMPLETE) { (void)gss_release_buffer_set(&tmpmin, data_set); } return GSSERRS(retmin, retmaj); } static const gss_OID_desc sasl_ssf_oid = { 11, discard_const("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0f") }; static uint32_t gssntlm_sasl_ssf(uint32_t *minor_status, struct gssntlm_ctx *ctx, gss_buffer_set_t *data_set) { uint32_t retmin; uint32_t retmaj; uint32_t tmpmin; gss_buffer_desc ssf_buf; uint32_t ssf = 0; /* Handwaving a bit here but this is what SSF is all about */ if (ctx->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { if (ctx->neg_flags & NTLMSSP_NEGOTIATE_128) { /* Technically we use RC4 with a 128 bit key, but we * consider the RC4 strenght degraded so we assign * it a value of 64, this is consistent with what * the krb5 mechanism does for the Rc4-HMAC enctype */ ssf = 64; } else if (ctx->neg_flags & NTLMSSP_NEGOTIATE_56) { ssf = 56; } else { ssf = 40; } } else if (ctx->neg_flags & NTLMSSP_NEGOTIATE_SIGN) { ssf = 1; } ssf = htobe32(ssf); ssf_buf.value = &ssf; ssf_buf.length = 4; retmaj = gss_add_buffer_set_member(&retmin, &ssf_buf, data_set); if (retmaj != GSS_S_COMPLETE) { (void)gss_release_buffer_set(&tmpmin, data_set); } return GSSERRS(retmin, retmaj); } static uint32_t gssntlm_sspi_session_key(uint32_t *minor_status, struct gssntlm_ctx *ctx, gss_buffer_set_t *data_set) { uint32_t retmin; uint32_t retmaj; uint32_t tmpmin; gss_buffer_desc session_key_buf; if (ctx->exported_session_key.length == 0) { return GSSERRS(ERR_NOTAVAIL, GSS_S_UNAVAILABLE); } session_key_buf.length = ctx->exported_session_key.length; session_key_buf.value = ctx->exported_session_key.data; retmaj = gss_add_buffer_set_member(&retmin, &session_key_buf, data_set); if (retmaj != GSS_S_COMPLETE) { (void)gss_release_buffer_set(&tmpmin, data_set); } return GSSERRS(retmin, retmaj); } uint32_t gssntlm_inquire_sec_context_by_oid(uint32_t *minor_status, const gss_ctx_id_t context_handle, const gss_OID desired_object, gss_buffer_set_t *data_set) { struct gssntlm_ctx *ctx; uint32_t retmin; uint32_t retmaj; if (context_handle == NULL) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } if (desired_object == GSS_C_NO_OID) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } if (!data_set) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE); } ctx = (struct gssntlm_ctx *)context_handle; *data_set = GSS_C_NO_BUFFER_SET; if (gss_oid_equal(desired_object, &spnego_req_mic_oid)) { return gssntlm_spnego_req_mic(minor_status, ctx, data_set); } else if (gss_oid_equal(desired_object, &sasl_ssf_oid)){ return gssntlm_sasl_ssf(minor_status, ctx, data_set); } else if (gss_oid_equal(desired_object, GSS_C_INQ_SSPI_SESSION_KEY)) { return gssntlm_sspi_session_key(minor_status, ctx, data_set); } return GSSERRS(ERR_NOTSUPPORTED, GSS_S_UNAVAILABLE); } gss-ntlmssp-1.3.1/src/gss_serialize.c000066400000000000000000001016121456736161100176170ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ #include #include #include #include #include #include #include "gssapi_ntlmssp.h" #include "gss_ntlmssp.h" /* each integer in the export format is a little endian integer */ #pragma pack(push, 1) struct relmem { uint32_t ptr; uint32_t len; }; struct export_attrs { uint16_t count; /* for each count there is a pair of name/value buffers * that we'll pack in a single buffer */ struct relmem buffers; }; struct export_name { uint8_t type; struct relmem dom_or_spn; struct relmem name; struct export_attrs attrs; }; struct export_keys { struct relmem sign_key; struct relmem seal_key; struct relmem rc4_state; uint32_t seq_num; }; #define EXPORT_CTX_VER 0x0005 struct export_ctx { uint16_t version; uint8_t role; uint8_t stage; uint8_t sec_req; struct relmem workstation; struct relmem nego_msg; struct relmem chal_msg; struct relmem auth_msg; struct export_name source; struct export_name target; uint8_t server_chal[8]; uint32_t gss_flags; uint32_t neg_flags; struct relmem exported_session_key; struct export_keys send; struct export_keys recv; uint8_t int_flags; uint64_t expration_time; uint8_t data[]; }; #pragma pack(pop) #define EXP_CTX_CLIENT 1 #define EXP_CTX_SERVER 2 #define EXP_CTX_DOMSRV 3 #define EXP_CTX_DOMCTR 4 #define EXP_STG_INIT 1 #define EXP_STG_NEGO 2 #define EXP_STG_CHAL 3 #define EXP_STG_AUTH 4 #define EXP_STG_DONE 5 #define EXP_NAME_NONE 0 #define EXP_NAME_ANON 1 #define EXP_NAME_USER 2 #define EXP_NAME_SERV 3 #define INC_EXP_SIZE 0x001000 /* 4K */ #define MAX_EXP_SIZE 0x100000 /* 1M */ #define NEW_SIZE(s, n) \ ((((s) + (n) + (INC_EXP_SIZE-1)) / INC_EXP_SIZE) * INC_EXP_SIZE) struct export_state { uint8_t *exp_struct; size_t exp_size; size_t exp_data; size_t exp_len; }; #define RELMEM_PTR(state, rm) \ ((state)->exp_struct + (state)->exp_data + (rm)->ptr) #define RELMEM_ZERO(rm) \ memset((rm), 0, sizeof(struct relmem)) static int export_data_allocate(struct export_state *state, size_t length, struct relmem *rm) { size_t new_size; void *tmp; if (length > MAX_EXP_SIZE) { return E2BIG; } if (length > state->exp_size - state->exp_len) { new_size = NEW_SIZE(state->exp_len, length); if ((new_size < state->exp_size) || new_size > MAX_EXP_SIZE) { return E2BIG; } tmp = realloc(state->exp_struct, new_size); if (!tmp) { return ENOMEM; } state->exp_struct = tmp; state->exp_size = new_size; } rm->ptr = state->exp_len - state->exp_data; rm->len = length; state->exp_len += length; return 0; } static int export_data_buffer(struct export_state *state, void *data, size_t length, struct relmem *rm) { int ret; if (length == 0) { RELMEM_ZERO(rm); return 0; } ret = export_data_allocate(state, length, rm); if (ret) return ret; memcpy(RELMEM_PTR(state, rm), data, length); return 0; } static int export_attrs(struct export_state *state, struct gssntlm_name_attribute *attrs, struct export_attrs *exp_attrs) { size_t count = gssntlm_get_attrs_count(attrs); size_t ptr_array_size = 0; int ret; if (count == 0) return 0; if (count > UINT16_MAX) return E2BIG; exp_attrs->count = count; /* reserve data space in state->exp_struct for pointers */ ptr_array_size = count * 2 * sizeof(struct relmem); ret = export_data_allocate(state, ptr_array_size, &exp_attrs->buffers); if (ret) return ret; /* exp_attrs->buffers may be reallocated as part of data structure * expansion in export_data_buffer() so we need to recompute the * buffers pointer after each use of export_data_buffer */ for (size_t i = 0; i < count; i++) { struct relmem *buffers; struct relmem buffer; /* name */ ret = export_data_buffer(state, attrs[i].attr_name, strlen(attrs[i].attr_name), &buffer); if (ret) return ret; buffers = (struct relmem *)RELMEM_PTR(state, &exp_attrs->buffers); memcpy(&buffers[i * 2], &buffer, sizeof(struct relmem)); /* value */ ret = export_data_buffer(state, attrs[i].attr_value.value, attrs[i].attr_value.length, &buffer); if (ret) return ret; buffers = (struct relmem *)RELMEM_PTR(state, &exp_attrs->buffers); memcpy(&buffers[i * 2 + 1], &buffer, sizeof(struct relmem)); } return 0; } static int export_name(struct export_state *state, struct gssntlm_name *name, struct export_name *exp_name) { int ret; memset(exp_name, 0, sizeof(struct export_name)); switch (name->type) { case GSSNTLM_NAME_NULL: break; case GSSNTLM_NAME_ANON: exp_name->type = EXP_NAME_ANON; break; case GSSNTLM_NAME_USER: exp_name->type = EXP_NAME_USER; if (name->data.user.domain) { ret = export_data_buffer(state, name->data.user.domain, strlen(name->data.user.domain), &exp_name->dom_or_spn); if (ret) { return ret; } } if (name->data.user.name) { ret = export_data_buffer(state, name->data.user.name, strlen(name->data.user.name), &exp_name->name); if (ret) { return ret; } } break; case GSSNTLM_NAME_SERVER: exp_name->type = EXP_NAME_SERV; if (name->data.server.spn) { ret = export_data_buffer(state, name->data.server.spn, strlen(name->data.server.spn), &exp_name->dom_or_spn); if (ret) { return ret; } } if (name->data.server.name) { ret = export_data_buffer(state, name->data.server.name, strlen(name->data.server.name), &exp_name->name); if (ret) { return ret; } } break; default: return EINVAL; } return export_attrs(state, name->attrs, &exp_name->attrs); } static int export_keys(struct export_state *state, struct ntlm_signseal_handle *keys, struct export_keys *exp_keys) { uint8_t buf[258*sizeof(uint32_t)]; struct ntlm_buffer out = { .data=buf, .length=sizeof(buf) }; int ret; memset(exp_keys, 0, sizeof(struct export_keys)); if (keys->sign_key.length > 0) { ret = export_data_buffer(state, keys->sign_key.data, keys->sign_key.length, &exp_keys->sign_key); if (ret) return ret; } if (keys->seal_key.length > 0) { ret = export_data_buffer(state, keys->seal_key.data, keys->seal_key.length, &exp_keys->seal_key); if (ret) return ret; } if (keys->seal_handle) { ret = RC4_EXPORT(keys->seal_handle, &out); if (ret) return ret; ret = export_data_buffer(state, buf, sizeof(buf), &exp_keys->rc4_state); safezero(buf, sizeof(buf)); if (ret) return ret; } exp_keys->seq_num = htole32(keys->seq_num); return 0; } uint32_t gssntlm_export_sec_context(uint32_t *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t interprocess_token) { struct gssntlm_ctx *ctx; struct export_state state = { 0 }; struct export_ctx ectx = { 0 }; uint64_t expiration; uint32_t retmaj; uint32_t retmin; int ret; if (context_handle == NULL) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } if (interprocess_token == NULL) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE); } ctx = (struct gssntlm_ctx *)*context_handle; if (ctx == NULL) return GSSERRS(ERR_BADARG, GSS_S_NO_CONTEXT); if (ctx->expiration_time && ctx->expiration_time < time(NULL)) { return GSSERRS(ERR_EXPIRED, GSS_S_CONTEXT_EXPIRED); } /* we want to leave space to add the basic context structure in the buffer * however we want a memory stable structure we can refernce via memory * pointers while we run export functions for all the "static" context * data, so we allocate space but we use a stack allocated struct until * the very end. */ state.exp_size = NEW_SIZE(0, sizeof(struct export_ctx)); state.exp_struct = malloc(state.exp_size); if (!state.exp_struct) { set_GSSERR(ENOMEM); goto done; } state.exp_data = (uint8_t *)&ectx.data - (uint8_t *)&ectx; state.exp_len = state.exp_data; ectx.version = htole16(EXPORT_CTX_VER); switch(ctx->role) { case GSSNTLM_CLIENT: ectx.role = EXP_CTX_CLIENT; break; case GSSNTLM_SERVER: ectx.role = EXP_CTX_SERVER; break; case GSSNTLM_DOMAIN_SERVER: ectx.role = EXP_CTX_DOMSRV; break; case GSSNTLM_DOMAIN_CONTROLLER: ectx.role = EXP_CTX_DOMCTR; break; } switch(ctx->stage) { case NTLMSSP_STAGE_INIT: ectx.stage = EXP_STG_INIT; break; case NTLMSSP_STAGE_NEGOTIATE: ectx.stage = EXP_STG_NEGO; break; case NTLMSSP_STAGE_CHALLENGE: ectx.stage = EXP_STG_CHAL; break; case NTLMSSP_STAGE_AUTHENTICATE: ectx.stage = EXP_STG_AUTH; break; case NTLMSSP_STAGE_DONE: ectx.stage = EXP_STG_DONE; break; } ectx.sec_req = ctx->sec_req; if (!ctx->workstation) { RELMEM_ZERO(&ectx.workstation); } else { ret = export_data_buffer(&state, ctx->workstation, strlen(ctx->workstation), &ectx.workstation); if (ret) { set_GSSERR(ret); goto done; } } if (ctx->nego_msg.length > 0) { ret = export_data_buffer(&state, ctx->nego_msg.data, ctx->nego_msg.length, &ectx.nego_msg); if (ret) { set_GSSERR(ret); goto done; } } else { RELMEM_ZERO(&ectx.nego_msg); } if (ctx->chal_msg.length > 0) { ret = export_data_buffer(&state, ctx->chal_msg.data, ctx->chal_msg.length, &ectx.chal_msg); if (ret) { set_GSSERR(ret); goto done; } } else { RELMEM_ZERO(&ectx.chal_msg); } if (ctx->auth_msg.length > 0) { ret = export_data_buffer(&state, ctx->auth_msg.data, ctx->auth_msg.length, &ectx.auth_msg); if (ret) { set_GSSERR(ret); goto done; } } else { RELMEM_ZERO(&ectx.auth_msg); } ret = export_name(&state, &ctx->source_name, &ectx.source); if (ret) { set_GSSERR(ret); goto done; } ret = export_name(&state, &ctx->target_name, &ectx.target); if (ret) { set_GSSERR(ret); goto done; } memcpy(ectx.server_chal, ctx->server_chal, 8); ectx.gss_flags = htole32(ctx->gss_flags); ectx.neg_flags = htole32(ctx->neg_flags); ret = export_data_buffer(&state, ctx->exported_session_key.data, ctx->exported_session_key.length, &ectx.exported_session_key); if (ret) { set_GSSERR(ret); goto done; } ret = export_keys(&state, &ctx->crypto_state.send, &ectx.send); if (ret) { set_GSSERR(ret); goto done; } ret = export_keys(&state, &ctx->crypto_state.recv, &ectx.recv); if (ret) { set_GSSERR(ret); goto done; } ectx.int_flags = ctx->int_flags; expiration = ctx->expiration_time; ectx.expration_time = htole64(expiration); /* finally copy ectx into the allocated buffer */ memcpy(state.exp_struct, &ectx, state.exp_data); set_GSSERRS(0, GSS_S_COMPLETE); done: if (retmaj) { free(state.exp_struct); } else { uint32_t min; interprocess_token->value = state.exp_struct; interprocess_token->length = state.exp_len; /* Invalidate the current context once successfully exported */ gssntlm_delete_sec_context(&min, context_handle, NULL); } return GSSERR(); } static uint32_t import_data_buffer(uint32_t *minor_status, struct export_state *state, uint8_t **dest, size_t *len, bool alloc, struct relmem *rm, bool str) { uint32_t retmaj; uint32_t retmin; void *ptr; if (str && !alloc) { return EINVAL; } if (rm->len == 0) { if (alloc) { *dest = NULL; } set_GSSERRS(0, GSS_S_COMPLETE); goto done; } if (state->exp_data + rm->ptr + rm->len > state->exp_len) { set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN); goto done; } ptr = RELMEM_PTR(state, rm); if (alloc) { if (str) { *dest = (uint8_t *)strndup((const char *)ptr, rm->len); } else { *dest = malloc(rm->len); if (*dest) { memcpy(*dest, ptr, rm->len); } } if (!*dest) { set_GSSERR(ENOMEM); goto done; } } else { if (!*len) { set_GSSERR(ERR_BADARG); goto done; } if (rm->len > *len) { set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); goto done; } memcpy(*dest, ptr, rm->len); } set_GSSERRS(0, GSS_S_COMPLETE); done: if (retmaj == GSS_S_COMPLETE) { if (len) *len = rm->len; } return GSSERR(); } static uint32_t import_attrs(uint32_t *minor_status, struct export_state *state, struct export_attrs *attrs, struct gssntlm_name_attribute **imp_attrs) { struct gssntlm_name_attribute *a; uint32_t retmaj = GSS_S_COMPLETE; uint32_t retmin = 0; uint8_t *cursor; if (attrs->count == 0) goto done; a = calloc(attrs->count + 1, sizeof(struct gssntlm_name_attribute)); if (a == NULL) { set_GSSERR(ENOMEM); goto done; } *imp_attrs = a; cursor = RELMEM_PTR(state, &attrs->buffers); for (size_t i = 0; i < attrs->count; i++) { struct relmem name; struct relmem value; memcpy(&name, cursor, sizeof(struct relmem)); cursor += sizeof(struct relmem); memcpy(&value, cursor, sizeof(struct relmem)); cursor += sizeof(struct relmem); retmaj = import_data_buffer(&retmin, state, (uint8_t **)&a[i].attr_name, NULL, true, &name, true); if (retmaj != GSS_S_COMPLETE) goto done; retmaj = import_data_buffer(&retmin, state, (uint8_t **)&a[i].attr_value.value, &a[i].attr_value.length, true, &value, false); if (retmaj != GSS_S_COMPLETE) goto done; } set_GSSERRS(0, GSS_S_COMPLETE); done: return GSSERR(); } static uint32_t import_name(uint32_t *minor_status, struct export_state *state, struct export_name *name, struct gssntlm_name *imp_name) { uint32_t retmaj; uint32_t retmin; uint8_t *dest; switch (name->type) { case EXP_NAME_NONE: memset(imp_name, 0, sizeof(struct gssntlm_name)); break; case EXP_NAME_ANON: memset(imp_name, 0, sizeof(struct gssntlm_name)); imp_name->type = GSSNTLM_NAME_ANON; break; case EXP_NAME_USER: imp_name->type = GSSNTLM_NAME_USER; dest = NULL; if (name->dom_or_spn.len > 0) { retmaj = import_data_buffer(&retmin, state, &dest, NULL, true, &name->dom_or_spn, true); if (retmaj != GSS_S_COMPLETE) goto done; } imp_name->data.user.domain = (char *)dest; dest = NULL; if (name->name.len > 0) { retmaj = import_data_buffer(&retmin, state, &dest, NULL, true, &name->name, true); if (retmaj != GSS_S_COMPLETE) goto done; } imp_name->data.user.name = (char *)dest; break; case EXP_NAME_SERV: imp_name->type = GSSNTLM_NAME_SERVER; dest = NULL; if (name->dom_or_spn.len > 0) { retmaj = import_data_buffer(&retmin, state, &dest, NULL, true, &name->dom_or_spn, true); if (retmaj != GSS_S_COMPLETE) goto done; } imp_name->data.server.spn = (char *)dest; dest = NULL; if (name->name.len > 0) { retmaj = import_data_buffer(&retmin, state, &dest, NULL, true, &name->name, true); if (retmaj != GSS_S_COMPLETE) goto done; } imp_name->data.server.name = (char *)dest; break; default: set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); break; } retmaj = import_attrs(minor_status, state, &name->attrs, &imp_name->attrs); if (retmaj != GSS_S_COMPLETE) goto done; set_GSSERRS(0, GSS_S_COMPLETE); done: return GSSERR(); } static uint32_t import_keys(uint32_t *minor_status, struct export_state *state, struct export_keys *keys, struct ntlm_signseal_handle *imp_keys) { struct ntlm_buffer in; uint8_t *dest; uint32_t retmaj; uint32_t retmin; int ret; if (keys->sign_key.len > 0) { imp_keys->sign_key.length = 16; /* buf max size */ dest = imp_keys->sign_key.data; retmaj = import_data_buffer(&retmin, state, &dest, &imp_keys->sign_key.length, false, &keys->sign_key, false); if (retmaj != GSS_S_COMPLETE) goto done; } else { memset(&imp_keys->sign_key, 0, sizeof(struct ntlm_key)); } if (keys->seal_key.len > 0) { imp_keys->seal_key.length = 16; /* buf max size */ dest = imp_keys->seal_key.data; retmaj = import_data_buffer(&retmin, state, &dest, &imp_keys->seal_key.length, false, &keys->seal_key, false); if (retmaj != GSS_S_COMPLETE) goto done; } else { memset(&imp_keys->seal_key, 0, sizeof(struct ntlm_key)); } if (keys->rc4_state.len > 0) { retmaj = import_data_buffer(&retmin, state, &in.data, &in.length, true, &keys->rc4_state, false); if (retmaj != GSS_S_COMPLETE) goto done; ret = RC4_IMPORT(&imp_keys->seal_handle, &in); safezero(in.data, in.length); safefree(in.data); if (ret) { set_GSSERR(ret); goto done; } } else { imp_keys->seal_handle = NULL; } imp_keys->seq_num = le32toh(keys->seq_num); set_GSSERRS(0, GSS_S_COMPLETE); done: return GSSERR(); } uint32_t gssntlm_import_sec_context(uint32_t *minor_status, gss_buffer_t interprocess_token, gss_ctx_id_t *context_handle) { struct gssntlm_ctx *ctx = NULL; struct export_state state; struct export_ctx *ectx; uint8_t *dest; uint64_t time; uint32_t retmaj; uint32_t retmin; if (interprocess_token == NULL) { return GSSERRS(0, GSS_S_CALL_INACCESSIBLE_READ); } if (interprocess_token->length < sizeof(struct export_ctx)) { return GSSERRS(0, GSS_S_DEFECTIVE_TOKEN); } if (context_handle == NULL) { return GSSERRS(0, GSS_S_CALL_INACCESSIBLE_WRITE); } ctx = calloc(1, sizeof(struct gssntlm_ctx)); if (!ctx) { set_GSSERR(ENOMEM); goto done; } retmin = ntlm_init_ctx(&ctx->ntlm); if (retmin) { set_GSSERR(retmin); goto done; } state.exp_struct = interprocess_token->value; state.exp_len = interprocess_token->length; ectx = (struct export_ctx *)state.exp_struct; state.exp_data = (uint8_t *)ectx->data - (uint8_t *)ectx; if (ectx->version != le16toh(EXPORT_CTX_VER)) { set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN); goto done; } switch (ectx->role) { case EXP_CTX_CLIENT: ctx->role = GSSNTLM_CLIENT; break; case EXP_CTX_SERVER: ctx->role = GSSNTLM_SERVER; break; case EXP_CTX_DOMSRV: ctx->role = GSSNTLM_DOMAIN_SERVER; break; case EXP_CTX_DOMCTR: ctx->role = GSSNTLM_DOMAIN_CONTROLLER; break; default: set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN); goto done; } switch (ectx->stage) { case EXP_STG_INIT: ctx->stage = NTLMSSP_STAGE_INIT; break; case EXP_STG_NEGO: ctx->stage = NTLMSSP_STAGE_NEGOTIATE; break; case EXP_STG_CHAL: ctx->stage = NTLMSSP_STAGE_CHALLENGE; break; case EXP_STG_AUTH: ctx->stage = NTLMSSP_STAGE_AUTHENTICATE; break; case EXP_STG_DONE: ctx->stage = NTLMSSP_STAGE_DONE; break; default: set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN); goto done; } ctx->sec_req = ectx->sec_req; dest = NULL; if (ectx->workstation.len > 0) { retmaj = import_data_buffer(&retmin, &state, &dest, NULL, true, &ectx->workstation, true); if (retmaj != GSS_S_COMPLETE) goto done; } ctx->workstation = (char *)dest; if (ectx->nego_msg.len > 0) { retmaj = import_data_buffer(&retmin, &state, &ctx->nego_msg.data, &ctx->nego_msg.length, true, &ectx->nego_msg, false); if (retmaj != GSS_S_COMPLETE) goto done; } else { ctx->nego_msg.data = NULL; ctx->nego_msg.length = 0; } if (ectx->chal_msg.len > 0) { retmaj = import_data_buffer(&retmin, &state, &ctx->chal_msg.data, &ctx->chal_msg.length, true, &ectx->chal_msg, false); if (retmaj != GSS_S_COMPLETE) goto done; } else { ctx->chal_msg.data = NULL; ctx->chal_msg.length = 0; } if (ectx->auth_msg.len > 0) { retmaj = import_data_buffer(&retmin, &state, &ctx->auth_msg.data, &ctx->auth_msg.length, true, &ectx->auth_msg, false); if (retmaj != GSS_S_COMPLETE) goto done; } else { ctx->auth_msg.data = NULL; ctx->auth_msg.length = 0; } retmaj = import_name(&retmin, &state, &ectx->source, &ctx->source_name); if (retmaj != GSS_S_COMPLETE) goto done; retmaj = import_name(&retmin, &state, &ectx->target, &ctx->target_name); if (retmaj != GSS_S_COMPLETE) goto done; memcpy(ctx->server_chal, ectx->server_chal, 8); ctx->gss_flags = le32toh(ectx->gss_flags); ctx->neg_flags = le32toh(ectx->neg_flags); if (ectx->exported_session_key.len > 0) { ctx->exported_session_key.length = 16; /* buf max size */ dest = ctx->exported_session_key.data; retmaj = import_data_buffer(&retmin, &state, &dest, &ctx->exported_session_key.length, false, &ectx->exported_session_key, false); if (retmaj != GSS_S_COMPLETE) goto done; } else { memset(&ctx->exported_session_key, 0, sizeof(struct ntlm_key)); } retmaj = import_keys(&retmin, &state, &ectx->send, &ctx->crypto_state.send); if (retmaj != GSS_S_COMPLETE) goto done; retmaj = import_keys(&retmin, &state, &ectx->recv, &ctx->crypto_state.recv); if (retmaj != GSS_S_COMPLETE) goto done; /* We need to restoer also the general crypto status flags */ ctx->crypto_state.ext_sec = (ctx->neg_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY); ctx->crypto_state.datagram = (ctx->neg_flags & NTLMSSP_NEGOTIATE_DATAGRAM); ctx->int_flags = ectx->int_flags; time = le64toh(ectx->expration_time); ctx->expiration_time = time; set_GSSERRS(0, GSS_S_COMPLETE); done: if (retmaj == GSS_S_COMPLETE) { *context_handle = (gss_ctx_id_t)ctx; } else { uint32_t min; gssntlm_delete_sec_context(&min, (gss_ctx_id_t *)&ctx, NULL); } return GSSERR(); } #define EXPORT_CRED_VER 0x0002 #pragma pack(push, 1) struct export_cred { uint16_t version; uint16_t type; struct export_name name; /* user or server name */ struct relmem nt_hash; /* empty for dummy or server */ struct relmem lm_hash; /* empty for dummy or server */ struct relmem keyfile; uint8_t ext_cached; uint8_t data[]; }; #pragma pack(pop) #define EXP_CRED_NONE 0 #define EXP_CRED_ANON 1 #define EXP_CRED_USER 2 #define EXP_CRED_SERVER 3 #define EXP_CRED_EXTERNAL 4 uint32_t gssntlm_export_cred(uint32_t *minor_status, gss_cred_id_t cred_handle, gss_buffer_t token) { struct gssntlm_cred *cred; struct export_state state = { 0 }; struct export_cred ecred = { 0 }; uint32_t retmaj; uint32_t retmin; int ret; if (token == NULL) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE); } cred = (struct gssntlm_cred *)cred_handle; if (cred_handle == NULL) { return GSSERRS(ERR_NOARG, GSS_S_NO_CRED); } /* we want to leave space to add the basic creds structure in the buffer * however we want a memory stable structure we can refernce via memory * pointers while we run export functions for all the "static" context * data, so we allocate space but we use a stack allocated struct until * the very end. */ state.exp_size = NEW_SIZE(0, sizeof(struct export_cred)); state.exp_struct = calloc(1, state.exp_size); if (!state.exp_struct) { set_GSSERR(ENOMEM); goto done; } state.exp_data = (uint8_t *)&ecred.data - (uint8_t *)&ecred; state.exp_len = state.exp_data; ecred.version = htole16(EXPORT_CRED_VER); switch (cred->type) { case GSSNTLM_CRED_NONE: ecred.type = EXP_CRED_NONE; break; case GSSNTLM_CRED_ANON: ecred.type = EXP_CRED_ANON; break; case GSSNTLM_CRED_USER: ecred.type = EXP_CRED_USER; ret = export_name(&state, &cred->cred.user.user, &ecred.name); if (ret) { set_GSSERR(ret); goto done; } ret = export_data_buffer(&state, cred->cred.user.nt_hash.data, cred->cred.user.nt_hash.length, &ecred.nt_hash); if (ret) { set_GSSERR(ret); goto done; } ret = export_data_buffer(&state, cred->cred.user.lm_hash.data, cred->cred.user.lm_hash.length, &ecred.lm_hash); if (ret) { set_GSSERR(ret); goto done; } break; case GSSNTLM_CRED_SERVER: ecred.type = EXP_CRED_SERVER; ret = export_name(&state, &cred->cred.server.name, &ecred.name); if (ret) { set_GSSERR(ret); goto done; } if (cred->cred.server.keyfile) { ret = export_data_buffer(&state, cred->cred.server.keyfile, strlen(cred->cred.server.keyfile), &ecred.keyfile); if (ret) { set_GSSERR(ret); goto done; } } break; case GSSNTLM_CRED_EXTERNAL: ecred.type = EXP_CRED_EXTERNAL; ret = export_name(&state, &cred->cred.external.user, &ecred.name); if (ret) { set_GSSERR(ret); goto done; } if (cred->cred.external.creds_in_cache) { ecred.ext_cached = 1; } break; } /* finally copy ecred into the allocated buffer */ memcpy(state.exp_struct, &ecred, state.exp_data); set_GSSERRS(0, GSS_S_COMPLETE); done: if (retmaj) { free(state.exp_struct); } else { token->value = state.exp_struct; token->length = state.exp_len; } return GSSERR(); } uint32_t gssntlm_import_cred(uint32_t *minor_status, gss_buffer_t token, gss_cred_id_t *cred_handle) { struct gssntlm_cred *cred; struct export_state state = { 0 }; struct export_cred *ecred; uint32_t retmaj; uint32_t retmin; if (token == NULL) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } if (token->length < sizeof(struct export_cred)) { return GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); } if (cred_handle == NULL) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE); } cred = calloc(1, sizeof(struct gssntlm_cred)); if (!cred) { set_GSSERR(ENOMEM); goto done; } state.exp_struct = token->value; state.exp_len = token->length; ecred = (struct export_cred *)state.exp_struct; state.exp_data = (char *)ecred->data - (char *)ecred; if (ecred->version != le16toh(EXPORT_CRED_VER)) { set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); goto done; } switch (ecred->type) { case EXP_CRED_NONE: cred->type = GSSNTLM_CRED_NONE; break; case EXP_CRED_ANON: cred->type = GSSNTLM_CRED_ANON; break; case EXP_CRED_USER: cred->type = GSSNTLM_CRED_USER; retmaj = import_name(&retmin, &state, &ecred->name, &cred->cred.user.user); if (retmaj != GSS_S_COMPLETE) goto done; if (ecred->nt_hash.len > 16 || ecred->lm_hash.len > 16) { set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); goto done; } retmaj = import_data_buffer(&retmin, &state, (uint8_t **)&cred->cred.user.nt_hash.data, &cred->cred.user.nt_hash.length, false, &ecred->nt_hash, false); if (retmaj != GSS_S_COMPLETE) goto done; retmaj = import_data_buffer(&retmin, &state, (uint8_t **)&cred->cred.user.lm_hash.data, &cred->cred.user.lm_hash.length, false, &ecred->lm_hash, false); if (retmaj != GSS_S_COMPLETE) goto done; break; case EXP_CRED_SERVER: cred->type = GSSNTLM_CRED_SERVER; retmaj = import_name(&retmin, &state, &ecred->name, &cred->cred.server.name); if (retmaj != GSS_S_COMPLETE) goto done; if (ecred->keyfile.len > 0) { retmaj = import_data_buffer(&retmin, &state, (uint8_t **)&cred->cred.server.keyfile, NULL, true, &ecred->keyfile, true); if (retmaj != GSS_S_COMPLETE) goto done; } break; case EXP_CRED_EXTERNAL: cred->type = GSSNTLM_CRED_EXTERNAL; retmaj = import_name(&retmin, &state, &ecred->name, &cred->cred.external.user); if (retmaj != GSS_S_COMPLETE) goto done; cred->cred.external.creds_in_cache = (ecred->ext_cached == 1); break; default: set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); break; } set_GSSERRS(0, GSS_S_COMPLETE); done: if (retmaj == GSS_S_COMPLETE) { *cred_handle = (gss_cred_id_t)cred; } else { uint32_t min; gssntlm_release_cred(&min, (gss_cred_id_t *)&cred); } return GSSERR(); } gss-ntlmssp-1.3.1/src/gss_signseal.c000066400000000000000000000172611456736161100174430ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ #include #include #include #include #include "gssapi_ntlmssp.h" #include "gss_ntlmssp.h" uint32_t gssntlm_get_mic(uint32_t *minor_status, gss_ctx_id_t context_handle, gss_qop_t qop_req, gss_buffer_t message_buffer, gss_buffer_t message_token) { struct gssntlm_ctx *ctx; struct ntlm_buffer message; struct ntlm_buffer signature; uint32_t retmaj, retmin; ctx = (struct gssntlm_ctx *)context_handle; retmaj = gssntlm_context_is_valid(ctx, NULL); if (retmaj != GSS_S_COMPLETE) { return GSSERRS(ERR_BADCTX, retmaj); } if (qop_req != GSS_C_QOP_DEFAULT) { return GSSERRS(ERR_BADARG, GSS_S_BAD_QOP); } if (!message_buffer->value || message_buffer->length == 0) { return GSSERRS(ERR_BADARG, GSS_S_CALL_INACCESSIBLE_READ); } message_token->value = malloc(NTLM_SIGNATURE_SIZE); if (!message_token->value) { return GSSERRS(ENOMEM, GSS_S_FAILURE); } message_token->length = NTLM_SIGNATURE_SIZE; message.data = message_buffer->value; message.length = message_buffer->length; signature.data = message_token->value; signature.length = message_token->length; retmin = ntlm_sign(ctx->neg_flags, NTLM_SEND, &ctx->crypto_state, &message, &signature); if (retmin) { safefree(message_token->value); return GSSERRS(retmin, GSS_S_FAILURE); } return GSSERRS(0, GSS_S_COMPLETE); } uint32_t gssntlm_verify_mic(uint32_t *minor_status, gss_ctx_id_t context_handle, gss_buffer_t message_buffer, gss_buffer_t message_token, gss_qop_t *qop_state) { struct gssntlm_ctx *ctx; struct ntlm_buffer message; uint8_t token[16]; struct ntlm_buffer signature = { token, NTLM_SIGNATURE_SIZE }; uint32_t retmaj, retmin; ctx = (struct gssntlm_ctx *)context_handle; retmaj = gssntlm_context_is_valid(ctx, NULL); if (retmaj != GSS_S_COMPLETE) { return GSSERRS(ERR_BADCTX, retmaj); } if (!message_buffer->value || message_buffer->length == 0) { return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); } if (qop_state) { *qop_state = GSS_C_QOP_DEFAULT; } message.data = message_buffer->value; message.length = message_buffer->length; retmin = ntlm_sign(ctx->neg_flags, NTLM_RECV, &ctx->crypto_state, &message, &signature); if (retmin) { return GSSERRS(retmin, GSS_S_FAILURE); } if (memcmp(signature.data, message_token->value, NTLM_SIGNATURE_SIZE) != 0) { return GSSERRS(0, GSS_S_BAD_SIG); } return GSSERRS(0, GSS_S_COMPLETE); } uint32_t gssntlm_wrap(uint32_t *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, gss_buffer_t input_message_buffer, int *conf_state, gss_buffer_t output_message_buffer) { struct gssntlm_ctx *ctx; struct ntlm_buffer message; struct ntlm_buffer output; struct ntlm_buffer signature; uint32_t retmaj, retmin; ctx = (struct gssntlm_ctx *)context_handle; retmaj = gssntlm_context_is_valid(ctx, NULL); if (retmaj != GSS_S_COMPLETE) { return GSSERRS(ERR_BADCTX, retmaj); } if (qop_req != GSS_C_QOP_DEFAULT) { return GSSERRS(ERR_BADARG, GSS_S_BAD_QOP); } if (!input_message_buffer->value || input_message_buffer->length == 0) { return GSSERRS(ERR_BADARG, GSS_S_CALL_INACCESSIBLE_READ); } if (conf_state) { *conf_state = 0; } if (conf_req_flag == 0) { /* ignore, always seal */ } output_message_buffer->length = input_message_buffer->length + NTLM_SIGNATURE_SIZE; output_message_buffer->value = malloc(output_message_buffer->length); if (!output_message_buffer->value) { return GSSERRS(ENOMEM, GSS_S_FAILURE); } message.data = input_message_buffer->value; message.length = input_message_buffer->length; signature.data = output_message_buffer->value; signature.length = NTLM_SIGNATURE_SIZE; output.data = (uint8_t *)output_message_buffer->value + NTLM_SIGNATURE_SIZE; output.length = input_message_buffer->length; retmin = ntlm_seal(ctx->neg_flags, &ctx->crypto_state, &message, &output, &signature); if (retmin) { safefree(output_message_buffer->value); return GSSERRS(retmin, GSS_S_FAILURE); } if (conf_state) { *conf_state = 1; } return GSSERRS(0, GSS_S_COMPLETE); } uint32_t gssntlm_unwrap(uint32_t *minor_status, gss_ctx_id_t context_handle, gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int *conf_state, gss_qop_t *qop_state) { struct gssntlm_ctx *ctx; struct ntlm_buffer message; struct ntlm_buffer output; uint8_t sig[16]; struct ntlm_buffer signature = { sig, NTLM_SIGNATURE_SIZE }; uint32_t retmaj, retmin; ctx = (struct gssntlm_ctx *)context_handle; retmaj = gssntlm_context_is_valid(ctx, NULL); if (retmaj != GSS_S_COMPLETE) { return GSSERRS(ERR_BADCTX, retmaj); } if (!input_message_buffer->value || input_message_buffer->length == 0) { return GSSERRS(ERR_BADARG, GSS_S_CALL_INACCESSIBLE_READ); } if (conf_state) { *conf_state = 0; } if (qop_state) { *qop_state = GSS_C_QOP_DEFAULT; } output_message_buffer->length = input_message_buffer->length - NTLM_SIGNATURE_SIZE; output_message_buffer->value = malloc(output_message_buffer->length); if (!output_message_buffer->value) { return GSSERRS(ENOMEM, GSS_S_FAILURE); } message.data = (uint8_t *)input_message_buffer->value + NTLM_SIGNATURE_SIZE; message.length = input_message_buffer->length - NTLM_SIGNATURE_SIZE; output.data = output_message_buffer->value; output.length = output_message_buffer->length; retmin = ntlm_unseal(ctx->neg_flags, &ctx->crypto_state, &message, &output, &signature); if (retmin) { safefree(output_message_buffer->value); return GSSERRS(retmin, GSS_S_FAILURE); } if (memcmp(input_message_buffer->value, signature.data, NTLM_SIGNATURE_SIZE) != 0) { safefree(output_message_buffer->value); return GSSERRS(0, GSS_S_BAD_SIG); } if (conf_state) { *conf_state = 1; } return GSSERRS(0, GSS_S_COMPLETE); } uint32_t gssntlm_wrap_size_limit(uint32_t *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, uint32_t req_output_size, uint32_t *max_input_size) { struct gssntlm_ctx *ctx; uint32_t retmaj, retmin; ctx = (struct gssntlm_ctx *)context_handle; retmaj = gssntlm_context_is_valid(ctx, NULL); if (retmaj != GSS_S_COMPLETE) { return GSSERRS(ERR_BADCTX, retmaj); } if (qop_req != GSS_C_QOP_DEFAULT) { return GSSERRS(ERR_BADARG, GSS_S_BAD_QOP); } if (req_output_size < 16) { *max_input_size = 0; } else { *max_input_size = req_output_size - NTLM_SIGNATURE_SIZE; } return GSSERRS(0, GSS_S_COMPLETE); } gss-ntlmssp-1.3.1/src/gss_spi.c000066400000000000000000000454351456736161100164350ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ #include #include #include "gss_ntlmssp.h" OM_uint32 gss_init_sec_context(OM_uint32 *minor_status, gss_cred_id_t claimant_cred_handle, gss_ctx_id_t *context_handle, gss_name_t target_name, gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, gss_OID *actual_mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec) { return gssntlm_init_sec_context(minor_status, claimant_cred_handle, context_handle, target_name, mech_type, req_flags, time_req, input_chan_bindings, input_token, actual_mech_type, output_token, ret_flags, time_rec); } OM_uint32 gss_delete_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t output_token) { return gssntlm_delete_sec_context(minor_status, context_handle, output_token); } OM_uint32 gss_acquire_cred_from(OM_uint32 *minor_status, gss_name_t desired_name, OM_uint32 time_req, gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_const_key_value_set_t cred_store, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec) { return gssntlm_acquire_cred_from(minor_status, NULL, desired_name, time_req, desired_mechs, cred_usage, cred_store, output_cred_handle, actual_mechs, time_rec); } OM_uint32 gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name, OM_uint32 time_req, gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec) { return gssntlm_acquire_cred(minor_status, desired_name, time_req, desired_mechs, cred_usage, output_cred_handle, actual_mechs, time_rec); } OM_uint32 gssspi_acquire_cred_with_password(OM_uint32 *minor_status, gss_name_t desired_name, gss_buffer_t password, OM_uint32 time_req, gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *time_rec) { return gssntlm_acquire_cred_with_password(minor_status, desired_name, password, time_req, desired_mechs, cred_usage, output_cred_handle, actual_mechs, time_rec); } OM_uint32 gss_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle) { return gssntlm_release_cred(minor_status, cred_handle); } OM_uint32 gss_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer, gss_OID input_name_type, gss_name_t *output_name) { return gssntlm_import_name(minor_status, input_name_buffer, input_name_type, output_name); } OM_uint32 gssspi_import_name_by_mech(OM_uint32 *minor_status, gss_OID mech_type, gss_buffer_t input_name_buffer, gss_OID input_name_type, gss_name_t *output_name) { return gssntlm_import_name(minor_status, input_name_buffer, input_name_type, output_name); } OM_uint32 gss_duplicate_name(OM_uint32 *minor_status, const gss_name_t input_name, gss_name_t *dest_name) { return gssntlm_duplicate_name(minor_status, input_name, dest_name); } OM_uint32 gss_release_name(OM_uint32 *minor_status, gss_name_t *input_name) { return gssntlm_release_name(minor_status, input_name); } OM_uint32 gss_get_name_attribute(OM_uint32 *minor_status, gss_name_t name, gss_buffer_t attr, int *authenticated, int *complete, gss_buffer_t value, gss_buffer_t display_value, int *more) { return gssntlm_get_name_attribute(minor_status, name, attr, authenticated, complete, value, display_value, more); } OM_uint32 gss_context_time(OM_uint32 *minor_status, gss_ctx_id_t context_handle, OM_uint32 *time_rec) { return gssntlm_context_time(minor_status, context_handle, time_rec); } OM_uint32 gss_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_cred_id_t acceptor_cred_handle, gss_buffer_t input_token_buffer, gss_channel_bindings_t input_chan_bindings, gss_name_t *src_name, gss_OID *mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec, gss_cred_id_t *delegated_cred_handle) { return gssntlm_accept_sec_context(minor_status, context_handle, acceptor_cred_handle, input_token_buffer, input_chan_bindings, src_name, mech_type, output_token, ret_flags, time_rec, delegated_cred_handle); } OM_uint32 gss_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_qop_t qop_req, gss_buffer_t message_buffer, gss_buffer_t message_token) { return gssntlm_get_mic(minor_status, context_handle, qop_req, message_buffer, message_token); } OM_uint32 gss_verify_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_buffer_t message_buffer, gss_buffer_t message_token, gss_qop_t *qop_state) { return gssntlm_verify_mic(minor_status, context_handle, message_buffer, message_token, qop_state); } OM_uint32 gss_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, gss_buffer_t input_message_buffer, int *conf_state, gss_buffer_t output_message_buffer) { return gssntlm_wrap(minor_status, context_handle, conf_req_flag, qop_req, input_message_buffer, conf_state, output_message_buffer); } OM_uint32 gss_unwrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int *conf_state, gss_qop_t *qop_state) { return gssntlm_unwrap(minor_status, context_handle, input_message_buffer, output_message_buffer, conf_state, qop_state); } OM_uint32 gss_wrap_size_limit(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, OM_uint32 req_output_size, OM_uint32 *max_input_size) { return gssntlm_wrap_size_limit(minor_status, context_handle, conf_req_flag, qop_req, req_output_size, max_input_size); } OM_uint32 gss_inquire_context(OM_uint32 *minor_status, gss_ctx_id_t context_handle, gss_name_t *src_name, gss_name_t *targ_name, OM_uint32 *lifetime_rec, gss_OID *mech_type, OM_uint32 *ctx_flags, int *locally_initiated, int *open) { return gssntlm_inquire_context(minor_status, context_handle, src_name, targ_name, lifetime_rec, mech_type, ctx_flags, locally_initiated, open); } OM_uint32 gss_display_name(OM_uint32 *minor_status, gss_name_t input_name, gss_buffer_t output_name_buffer, gss_OID *output_name_type) { return gssntlm_display_name(minor_status, input_name, output_name_buffer, output_name_type); } OM_uint32 gss_localname(OM_uint32 *minor_status, const gss_name_t name, gss_const_OID mech_type, gss_buffer_t localname) { return gssntlm_localname(minor_status, name, mech_type, localname); } OM_uint32 gss_set_sec_context_option(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, const gss_OID desired_object, const gss_buffer_t value) { return gssntlm_set_sec_context_option(minor_status, context_handle, desired_object, value); } OM_uint32 gss_inquire_sec_context_by_oid(OM_uint32 *minor_status, const gss_ctx_id_t context_handle, const gss_OID desired_object, gss_buffer_set_t *data_set) { return gssntlm_inquire_sec_context_by_oid(minor_status, context_handle, desired_object, data_set); } OM_uint32 gss_inquire_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle, gss_name_t *name, OM_uint32 *lifetime, gss_cred_usage_t *cred_usage, gss_OID_set *mechanisms) { return gssntlm_inquire_cred(minor_status, cred_handle, name, lifetime, cred_usage, mechanisms); } OM_uint32 gss_inquire_cred_by_mech(OM_uint32 *minor_status, gss_cred_id_t cred_handle, gss_OID mech_type, gss_name_t *name, OM_uint32 *initiator_lifetime, OM_uint32 *acceptor_lifetime, gss_cred_usage_t *cred_usage) { return gssntlm_inquire_cred_by_mech(minor_status, cred_handle, mech_type, name, initiator_lifetime, acceptor_lifetime, cred_usage); } OM_uint32 gss_export_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t interprocess_token) { return gssntlm_export_sec_context(minor_status, context_handle, interprocess_token); } OM_uint32 gss_import_sec_context(OM_uint32 *minor_status, gss_buffer_t interprocess_token, gss_ctx_id_t *context_handle) { return gssntlm_import_sec_context(minor_status, interprocess_token, context_handle); } OM_uint32 gss_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle, gss_buffer_t token) { return gssntlm_export_cred(minor_status, cred_handle, token); } OM_uint32 gss_import_cred(OM_uint32 *minor_status, gss_buffer_t token, gss_cred_id_t *cred_handle) { return gssntlm_import_cred(minor_status, token, cred_handle); } OM_uint32 gss_display_status(OM_uint32 *minor_status, OM_uint32 status_value, int status_type, gss_OID mech_type, OM_uint32 *message_context, gss_buffer_t status_string) { return gssntlm_display_status(minor_status, status_value, status_type, mech_type, message_context, status_string); } OM_uint32 gss_inquire_name(OM_uint32 *minor_status, gss_name_t name, int *name_is_MN, gss_OID *MN_mech, gss_buffer_set_t *attrs) { return gssntlm_inquire_name(minor_status, name, name_is_MN, MN_mech, attrs); } OM_uint32 gss_inquire_saslname_for_mech(OM_uint32 *minor_status, const gss_OID desired_mech, gss_buffer_t sasl_mech_name, gss_buffer_t mech_name, gss_buffer_t mech_description) { return gssntlm_inquire_saslname_for_mech(minor_status, desired_mech, sasl_mech_name, mech_name, mech_description); } OM_uint32 gss_inquire_mech_for_saslname(OM_uint32 *minor_status, const gss_buffer_t sasl_mech_name, gss_OID *mech_type) { return gssntlm_inquire_mech_for_saslname(minor_status, sasl_mech_name, mech_type); } OM_uint32 gss_inquire_attrs_for_mech(OM_uint32 *minor_status, gss_const_OID mech_oid, gss_OID_set *mech_attrs, gss_OID_set *known_mech_attrs) { return gssntlm_inquire_attrs_for_mech(minor_status, mech_oid, mech_attrs, known_mech_attrs); } OM_uint32 gssspi_mech_invoke(OM_uint32 *minor_status, const gss_OID desired_mech, const gss_OID desired_object, gss_buffer_t value) { return gssntlm_mech_invoke(minor_status, desired_mech, desired_object, value); } OM_uint32 gssspi_set_cred_option(OM_uint32 *minor_status, gss_cred_id_t *cred_handle, const gss_OID desired_object, const gss_buffer_t value) { return gssntlm_set_cred_option(minor_status, cred_handle, desired_object, value); } gss-ntlmssp-1.3.1/src/gssapi_ntlmssp.h000066400000000000000000000062351456736161100200340ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ #ifndef _GSSAPI_NTLMSSP_H_ #define _GSSAPI_NTLMSSP_H_ #include #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* NTLMSSP OID: 1.3.6.1.4.1.311.2.2.10 */ #define GSS_NTLMSSP_OID_STRING "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" #define GSS_NTLMSSP_OID_LENGTH 10 /* add a new GSSPAPI req flag, it is technically a sort of SSPI * extension as Microsoft's SSPI may change behavior on datagram * oriented connections and has a ISC_REQ_DATAGRAM flag for that */ #define GSS_C_DATAGRAM_FLAG 0x10000 /* OID space kindly donated by Samba Project: 1.3.6.1.4.1.7165.655.1 */ #define GSS_NTLMSSP_BASE_OID_STRING "\x2b\x06\x01\x04\x01\xb7\x7d\x85\x0f\x01" #define GSS_NTLMSSP_BASE_OID_LENGTH 10 /* Set Seq Num OID * OID to be used to be used with gss_set_sec_context_option() * the value buffer is a uint32_t in host order and is used * to force a specific sequence number. This operation is allowed * only if GSS_C_DATAGRAM_FLAG was used. */ #define GSS_NTLMSSP_SET_SEQ_NUM_OID_STRING GSS_NTLMSSP_BASE_OID_STRING "\x01" #define GSS_NTLMSSP_SET_SEQ_NUM_OID_LENGTH GSS_NTLMSSP_BASE_OID_LENGTH + 1 /* SPNEGO Require MIC OID * When the NTLMSSP mechanism produces a MIC in the authenticate message, * the SPNEGO mechanism also must produce a mechlistMIC token otherwise * Windows servers get confused and fail the authentication. * This OID is queried by the SPNEGO mechanism after each token is generated. * After the Negotiate token is produced, a query for this context property * signals us that the SPNEGO implementation knows how to deal with the MIC, * After the Authenticate token is produced we return whether a MIC was * produced or not */ #define GSS_SPNEGO_REQUIRE_MIC_OID_STRING GSS_NTLMSSP_BASE_OID_STRING "\x02" #define GSS_SPNEGO_REQUIRE_MIC_OID_LENGTH GSS_NTLMSSP_BASE_OID_LENGTH + 1 /* SPNEGO Reset Crypto OID * MS-SPNG 3.3.5.1 warns hat the NTLM mechanism requires to reset the * crypto engine when the SPNEGO layer uses a MechListMIC. * This OID is queried by the SPNEGO mechanism after a MIC processing to * cause the crypto engine to be reset. */ #define GSS_NTLMSSP_RESET_CRYPTO_OID_STRING GSS_NTLMSSP_BASE_OID_STRING "\x03" #define GSS_NTLMSSP_RESET_CRYPTO_OID_LENGTH GSS_NTLMSSP_BASE_OID_LENGTH + 1 /* Debug OID for mech_invoke * Use this with gsspi_mech_invoke, to pass a file name and enable debugging. */ #define GSS_NTLMSSP_DEBUG_OID_STRING GSS_NTLMSSP_BASE_OID_STRING "\x04" #define GSS_NTLMSSP_DEBUG_OID_LENGTH GSS_NTLMSSP_BASE_OID_LENGTH + 1 /* Set Default Neg Flags Cred Option OID * Use this with gss_set_cred_option to provide a set of NEGOTIATE flags * to override the default selection on context initialization. */ #define GSS_NTLMSSP_NEG_FLAGS_OID_STRING GSS_NTLMSSP_BASE_OID_STRING "\x05" #define GSS_NTLMSSP_NEG_FLAGS_OID_LENGTH GSS_NTLMSSP_BASE_OID_LENGTH + 1 #define GSS_NTLMSSP_CS_DOMAIN "ntlmssp_domain" #define GSS_NTLMSSP_CS_NTHASH "ntlmssp_nthash" #define GSS_NTLMSSP_CS_PASSWORD "ntlmssp_password" #define GSS_NTLMSSP_CS_KEYFILE "ntlmssp_keyfile" #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _GSSAPI_NTLMSSP_H_ */ gss-ntlmssp-1.3.1/src/ntlm.c000066400000000000000000001264141456736161100157350ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ /* This File implements the NTLM protocol as specified by: * [MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol * * Additional cross checking with: * http://davenport.sourceforge.net/ntlm.html */ #include #include #include #include #include #include #include #include #include "ntlm.h" #pragma pack(push, 1) struct wire_av_pair { uint16_t av_id; uint16_t av_len; uint8_t value[]; /* variable */ }; #pragma pack(pop) enum msv_av_ids { MSV_AV_EOL = 0, MSV_AV_NB_COMPUTER_NAME, MSV_AV_NB_DOMAIN_NAME, MSV_AV_DNS_COMPUTER_NAME, MSV_AV_DNS_DOMAIN_NAME, MSV_AV_DNS_TREE_NAME, MSV_AV_FLAGS, MSV_AV_TIMESTAMP, MSV_AV_SINGLE_HOST, MSV_AV_TARGET_NAME, MSV_AV_CHANNEL_BINDINGS }; /* Used only on the same host */ #pragma pack(push, 1) struct wire_single_host_data { uint32_t size; uint32_t Z4; uint32_t data_present; uint32_t custom_data; uint8_t machine_id[32]; }; #pragma pack(pop) #pragma pack(push, 1) struct wire_ntlm_cli_chal { uint8_t resp_type; uint8_t hi_resp_type; uint16_t reserved1; uint32_t reserved2; uint64_t timestamp; uint8_t cli_chal[8]; uint32_t reserved3; uint8_t av_pairs[]; /* variable */ }; #pragma pack(pop) struct ntlm_ctx { iconv_t from_oem; iconv_t to_oem; }; int ntlm_init_ctx(struct ntlm_ctx **ctx) { struct ntlm_ctx *_ctx; int ret = 0; _ctx = calloc(1, sizeof(struct ntlm_ctx)); if (!_ctx) return ENOMEM; _ctx->from_oem = iconv_open("UTF-16LE", "UTF-8"); if (_ctx->from_oem == (iconv_t) -1) { ret = errno; } _ctx->to_oem = iconv_open("UTF-8", "UTF-16LE"); if (_ctx->to_oem == (iconv_t) -1) { iconv_close(_ctx->from_oem); ret = errno; } if (ret) { safefree(_ctx); } else { *ctx = _ctx; } return ret; } int ntlm_free_ctx(struct ntlm_ctx **ctx) { int ret; if (!ctx || !*ctx) return 0; if ((*ctx)->from_oem) { ret = iconv_close((*ctx)->from_oem); if (ret) goto done; } if ((*ctx)->to_oem) { ret = iconv_close((*ctx)->to_oem); } done: if (ret) ret = errno; safefree(*ctx); return ret; } void ntlm_free_buffer_data(struct ntlm_buffer *buf) { if (!buf) return; safefree(buf->data); buf->length = 0; } /* A FILETIME structure is effectively a little endian 64 bit integer * with the time from January 1, 1601 UTC with 10s of microsecond resolution. */ #define FILETIME_EPOCH_VALUE 116444736000000000LL uint64_t ntlm_timestamp_now(void) { struct timeval tv; uint64_t filetime; gettimeofday(&tv, NULL); /* set filetime to the time representing the eopch */ filetime = FILETIME_EPOCH_VALUE; /* add the number of seconds since the epoch */ filetime += (uint64_t)tv.tv_sec * 10000000; /* add the number of microseconds since the epoch */ filetime += tv.tv_usec * 10; return filetime; } bool ntlm_casecmp(const char *s1, const char *s2) { size_t s1_len, s2_len; int ret, res; if (s1 == s2) return true; if (!s1 || !s2) return false; s1_len = strlen(s1); s2_len = strlen(s2); ret = ulc_casecmp(s1, s1_len, s2, s2_len, uc_locale_language(), NULL, &res); if (ret || res != 0) return false; return true; } /** * @brief Converts a string using the provided iconv context. * This function is ok only to convert utf8<->utf16le * * @param cd The iconv context * @param in Input buffer * @param out Output buffer * @param baselen Input length * @param outlen Returned length of out buffer * * NOTE: out must be preallocated to a size of baselen * 2 * * @return 0 on success or a standard error value on error. */ static int ntlm_str_convert(iconv_t cd, const char *in, char *out, size_t baselen, size_t *outlen) { char *_in; size_t inleft, outleft; size_t ret; ret = iconv(cd, NULL, NULL, NULL, NULL); if (ret == -1) return errno; _in = discard_const(in); inleft = baselen; /* conservative max_size calculation in case lots of octects end up * being multiple bytes in length (in both directions) */ outleft = baselen * 2; ret = iconv(cd, &_in, &inleft, &out, &outleft); if (ret == -1) return errno; if (outlen) { *outlen = baselen * 2 - outleft; } return 0; } uint8_t ntlmssp_sig[8] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0}; static void ntlm_encode_header(struct wire_msg_hdr *hdr, uint32_t msg_type) { memcpy(hdr->signature, ntlmssp_sig, 8); hdr->msg_type = htole32(msg_type); } static int ntlm_decode_header(struct wire_msg_hdr *hdr, uint32_t *msg_type) { if (memcmp(hdr->signature, ntlmssp_sig, 8) != 0) { return ERR_DECODE; } *msg_type = le32toh(hdr->msg_type); return 0; } static int ntlm_encode_oem_str(struct wire_field_hdr *hdr, struct ntlm_buffer *buffer, size_t *data_offs, const char *str, int str_len) { if (*data_offs + str_len > buffer->length) { return ERR_ENCODE; } memcpy(&buffer->data[*data_offs], str, str_len); hdr->len = htole16(str_len); hdr->max_len = htole16(str_len); hdr->offset = htole32(*data_offs); *data_offs += str_len; return 0; } static int ntlm_decode_oem_str(struct wire_field_hdr *str_hdr, struct ntlm_buffer *buffer, size_t payload_offs, char **_str) { uint16_t str_len; uint32_t str_offs; char *str = NULL; str_len = le16toh(str_hdr->len); if (str_len == 0) goto done; str_offs = le32toh(str_hdr->offset); if ((str_offs < payload_offs) || (str_offs > buffer->length) || (UINT32_MAX - str_offs < str_len) || (str_offs + str_len > buffer->length)) { return ERR_DECODE; } str = strndup((const char *)&buffer->data[str_offs], str_len); if (!str) return ENOMEM; done: *_str = str; return 0; } static int ntlm_encode_u16l_str_hdr(struct ntlm_ctx *ctx, struct wire_field_hdr *hdr, struct ntlm_buffer *buffer, size_t *data_offs, const char *str, int str_len) { char *out; size_t outlen; int ret; out = (char *)&buffer->data[*data_offs]; ret = ntlm_str_convert(ctx->from_oem, str, out, str_len, &outlen); if (ret) return ret; hdr->len = htole16(outlen); hdr->max_len = htole16(outlen); hdr->offset = htole32(*data_offs); *data_offs += outlen; return 0; } static int ntlm_decode_u16l_str_hdr(struct ntlm_ctx *ctx, struct wire_field_hdr *str_hdr, struct ntlm_buffer *buffer, size_t payload_offs, char **str) { char *in, *out = NULL; uint16_t str_len; uint32_t str_offs; size_t outlen = 0; int ret = 0; str_len = le16toh(str_hdr->len); if (str_len == 0) goto done; str_offs = le32toh(str_hdr->offset); if ((str_offs < payload_offs) || (str_offs > buffer->length) || (UINT32_MAX - str_offs < str_len) || (str_offs + str_len > buffer->length)) { return ERR_DECODE; } in = (char *)&buffer->data[str_offs]; out = malloc(str_len * 2 + 1); if (!out) return ENOMEM; ret = ntlm_str_convert(ctx->to_oem, in, out, str_len, &outlen); done: if (ret) { safefree(out); } else { /* make sure to terminate output string */ if (out) { out[outlen] = '\0'; } } *str = out; return ret; } struct wire_version ntlmssp_version = { NTLMSSP_VERSION_MAJOR, NTLMSSP_VERSION_MINOR, NTLMSSP_VERSION_BUILD, /* 0 is always 0 even in LE */ { 0, 0, 0 }, NTLMSSP_VERSION_REV }; void ntlm_internal_set_version(uint8_t major, uint8_t minor, uint16_t build, uint8_t revision) { ntlmssp_version.major = major; ntlmssp_version.minor = minor; ntlmssp_version.build = htole16(build); ntlmssp_version.revision = revision; } static int ntlm_encode_version(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer, size_t data_offs) { if (data_offs + sizeof(struct wire_version) > buffer->length) { return ERR_ENCODE; } memcpy(&buffer->data[data_offs], &ntlmssp_version, sizeof(struct wire_version)); return 0; } static int ntlm_encode_field(struct wire_field_hdr *hdr, struct ntlm_buffer *buffer, size_t *data_offs, struct ntlm_buffer *field) { if (*data_offs + field->length > buffer->length) { return ERR_ENCODE; } memcpy(&buffer->data[*data_offs], field->data, field->length); hdr->len = htole16(field->length); hdr->max_len = hdr->len; hdr->offset = htole32(*data_offs); *data_offs += field->length; return 0; } static int ntlm_decode_field(struct wire_field_hdr *hdr, struct ntlm_buffer *buffer, size_t payload_offs, struct ntlm_buffer *field) { struct ntlm_buffer b = { NULL, 0 }; uint32_t offs; uint16_t len; len = le16toh(hdr->len); if (len == 0) goto done; offs = le32toh(hdr->offset); if ((offs < payload_offs) || (offs > buffer->length) || (UINT32_MAX - offs < len) || (offs + len > buffer->length)) { return ERR_DECODE; } b.data = malloc(len); if (!b.data) return ENOMEM; b.length = len; memcpy(b.data, &buffer->data[offs], b.length); done: *field = b; return 0; } static int ntlm_encode_av_pair_u16l_str(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer, size_t *data_offs, enum msv_av_ids av_id, const char *str, size_t str_len) { struct wire_av_pair *av_pair; char *out; size_t outlen; int ret; if (*data_offs + 4 + str_len > buffer->length) { return ERR_ENCODE; } av_pair = (struct wire_av_pair *)&buffer->data[*data_offs]; out = (char *)av_pair->value; ret = ntlm_str_convert(ctx->from_oem, str, out, str_len, &outlen); if (ret) return ret; av_pair->av_len = htole16(outlen); av_pair->av_id = htole16(av_id); *data_offs += av_pair->av_len + 4; return 0; } static int ntlm_decode_av_pair_u16l_str(struct ntlm_ctx *ctx, struct wire_av_pair *av_pair, char **str) { char *in, *out; size_t inlen, outlen; int ret; in = (char *)av_pair->value; inlen = le16toh(av_pair->av_len); out = malloc(inlen * 2 + 1); ret = ntlm_str_convert(ctx->to_oem, in, out, inlen, &outlen); if (ret) { safefree(out); return ret; } /* terminate out string for sure */ out[outlen] = '\0'; *str = out; return 0; } static int ntlm_encode_av_pair_value(struct ntlm_buffer *buffer, size_t *data_offs, enum msv_av_ids av_id, struct ntlm_buffer *value) { struct wire_av_pair *av_pair; if (*data_offs + 4 + value->length > buffer->length) { return ERR_ENCODE; } av_pair = (struct wire_av_pair *)&buffer->data[*data_offs]; av_pair->av_id = htole16(av_id); av_pair->av_len = htole16(value->length); if (value->length) { memcpy(av_pair->value, value->data, value->length); } *data_offs += value->length + 4; return 0; } int ntlm_encode_target_info(struct ntlm_ctx *ctx, char *nb_computer_name, char *nb_domain_name, char *dns_computer_name, char *dns_domain_name, char *dns_tree_name, uint32_t *av_flags, uint64_t *av_timestamp, struct ntlm_buffer *av_single_host, char *av_target_name, struct ntlm_buffer *av_cb, struct ntlm_buffer *target_info) { struct ntlm_buffer buffer; size_t data_offs; size_t max_size; size_t nb_computer_name_len = 0; size_t nb_domain_name_len = 0; size_t dns_computer_name_len = 0; size_t dns_domain_name_len = 0; size_t dns_tree_name_len = 0; size_t av_target_name_len = 0; struct ntlm_buffer value; int ret = 0; max_size = 4; /* MSV_AV_EOL */ if (nb_computer_name) { nb_computer_name_len = strlen(nb_computer_name); max_size += 4 + nb_computer_name_len * 2; } if (nb_domain_name) { nb_domain_name_len = strlen(nb_domain_name); max_size += 4 + nb_domain_name_len * 2; } if (dns_computer_name) { dns_computer_name_len = strlen(dns_computer_name); max_size += 4 + dns_computer_name_len * 2; } if (dns_domain_name) { dns_domain_name_len = strlen(dns_domain_name); max_size += 4 + dns_domain_name_len * 2; } if (dns_tree_name) { dns_tree_name_len = strlen(dns_tree_name); max_size += 4 + dns_tree_name_len * 2; } if (av_flags) { max_size += 4 + 4; } if (av_timestamp) { max_size += 4 + 8; } if (av_single_host) { max_size += 4 + av_single_host->length; } if (av_target_name) { av_target_name_len = strlen(av_target_name); max_size += 4 + av_target_name_len * 2; } if (av_cb && av_cb->length > 0) { max_size += 4 + av_cb->length; } data_offs = 0; buffer.length = max_size; buffer.data = calloc(1, buffer.length); if (!buffer.data) return ENOMEM; if (nb_computer_name) { ret = ntlm_encode_av_pair_u16l_str(ctx, &buffer, &data_offs, MSV_AV_NB_COMPUTER_NAME, nb_computer_name, nb_computer_name_len); if (ret) goto done; } if (nb_domain_name) { ret = ntlm_encode_av_pair_u16l_str(ctx, &buffer, &data_offs, MSV_AV_NB_DOMAIN_NAME, nb_domain_name, nb_domain_name_len); if (ret) goto done; } if (dns_computer_name) { ret = ntlm_encode_av_pair_u16l_str(ctx, &buffer, &data_offs, MSV_AV_DNS_COMPUTER_NAME, dns_computer_name, dns_computer_name_len); if (ret) goto done; } if (dns_domain_name) { ret = ntlm_encode_av_pair_u16l_str(ctx, &buffer, &data_offs, MSV_AV_DNS_DOMAIN_NAME, dns_domain_name, dns_domain_name_len); if (ret) goto done; } if (dns_tree_name) { ret = ntlm_encode_av_pair_u16l_str(ctx, &buffer, &data_offs, MSV_AV_DNS_TREE_NAME, dns_tree_name, dns_tree_name_len); if (ret) goto done; } if (av_flags) { uint32_t flags = htole32(*av_flags); value.data = (uint8_t *)&flags; value.length = 4; ret = ntlm_encode_av_pair_value(&buffer, &data_offs, MSV_AV_FLAGS, &value); if (ret) goto done; } if (av_timestamp) { uint64_t timestamp = htole64(*av_timestamp); value.data = (uint8_t *)×tamp; value.length = 8; ret = ntlm_encode_av_pair_value(&buffer, &data_offs, MSV_AV_TIMESTAMP, &value); if (ret) goto done; } if (av_single_host) { ret = ntlm_encode_av_pair_value(&buffer, &data_offs, MSV_AV_SINGLE_HOST, av_single_host); if (ret) goto done; } if (av_target_name) { ret = ntlm_encode_av_pair_u16l_str(ctx, &buffer, &data_offs, MSV_AV_TARGET_NAME, av_target_name, av_target_name_len); if (ret) goto done; } if (av_cb && av_cb->length > 0) { ret = ntlm_encode_av_pair_value(&buffer, &data_offs, MSV_AV_CHANNEL_BINDINGS, av_cb); if (ret) goto done; } value.length = 0; value.data = NULL; ret = ntlm_encode_av_pair_value(&buffer, &data_offs, MSV_AV_EOL, &value); buffer.length = data_offs; done: if (ret) { safefree(buffer.data); } else { *target_info = buffer; } return ret; } int ntlm_decode_target_info(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer, char **nb_computer_name, char **nb_domain_name, char **dns_computer_name, char **dns_domain_name, char **dns_tree_name, char **av_target_name, uint32_t *av_flags, uint64_t *av_timestamp, struct ntlm_buffer *av_single_host, struct ntlm_buffer *av_cb) { struct wire_av_pair *av_pair; uint16_t av_id = (uint16_t)-1; uint16_t av_len = (uint16_t)-1; struct ntlm_buffer sh = { NULL, 0 }; struct ntlm_buffer cb = { NULL, 0 }; char *nb_computer = NULL; char *nb_domain = NULL; char *dns_computer = NULL; char *dns_domain = NULL; char *dns_tree = NULL; char *av_target = NULL; size_t data_offs = 0; uint64_t timestamp = 0; uint32_t flags = 0; int ret = 0; while (data_offs + 4 <= buffer->length) { av_pair = (struct wire_av_pair *)&buffer->data[data_offs]; data_offs += 4; av_id = le16toh(av_pair->av_id); av_len = le16toh(av_pair->av_len); if (av_len > buffer->length - data_offs) { ret = ERR_DECODE; goto done; } data_offs += av_len; switch (av_id) { case MSV_AV_CHANNEL_BINDINGS: if (!av_cb) continue; cb.data = av_pair->value; cb.length = av_len; break; case MSV_AV_TARGET_NAME: if (!av_target_name) continue; ret = ntlm_decode_av_pair_u16l_str(ctx, av_pair, &av_target); if (ret) goto done; break; case MSV_AV_SINGLE_HOST: if (!av_single_host) continue; sh.data = av_pair->value; sh.length = av_len; break; case MSV_AV_TIMESTAMP: if (!av_timestamp) continue; if (av_len < sizeof(timestamp)) { ret = ERR_DECODE; goto done; } memcpy(×tamp, av_pair->value, sizeof(timestamp)); timestamp = le64toh(timestamp); break; case MSV_AV_FLAGS: if (!av_flags) continue; if (av_len < sizeof(flags)) { ret = ERR_DECODE; goto done; } memcpy(&flags, av_pair->value, sizeof(flags)); flags = le32toh(flags); break; case MSV_AV_DNS_TREE_NAME: if (!dns_tree_name) continue; ret = ntlm_decode_av_pair_u16l_str(ctx, av_pair, &dns_tree); if (ret) goto done; break; case MSV_AV_DNS_DOMAIN_NAME: if (!dns_domain_name) continue; ret = ntlm_decode_av_pair_u16l_str(ctx, av_pair, &dns_domain); if (ret) goto done; break; case MSV_AV_DNS_COMPUTER_NAME: if (!dns_computer_name) continue; ret = ntlm_decode_av_pair_u16l_str(ctx, av_pair, &dns_computer); if (ret) goto done; break; case MSV_AV_NB_DOMAIN_NAME: if (!nb_domain_name) continue; ret = ntlm_decode_av_pair_u16l_str(ctx, av_pair, &nb_domain); if (ret) goto done; break; case MSV_AV_NB_COMPUTER_NAME: if (!nb_computer_name) continue; ret = ntlm_decode_av_pair_u16l_str(ctx, av_pair, &nb_computer); if (ret) goto done; break; default: /* unknown av_pair, or EOL */ break; } if (av_id == MSV_AV_EOL) break; } if (av_id != MSV_AV_EOL || av_len != 0) { ret = ERR_DECODE; } done: if (ret) { safefree(nb_computer); safefree(nb_domain); safefree(dns_computer); safefree(dns_domain); safefree(dns_tree); safefree(av_target); } else { if (nb_computer_name) *nb_computer_name = nb_computer; if (nb_domain_name) *nb_domain_name = nb_domain; if (dns_computer_name) *dns_computer_name = dns_computer; if (dns_domain_name) *dns_domain_name = dns_domain; if (dns_tree_name) *dns_tree_name = dns_tree; if (av_target_name) *av_target_name = av_target; if (av_timestamp) *av_timestamp = timestamp; if (av_single_host) *av_single_host = sh; if (av_flags) *av_flags = flags; if (av_cb) *av_cb = cb; } return ret; } int ntlm_process_target_info(struct ntlm_ctx *ctx, bool protect, struct ntlm_buffer *in, const char *spn, struct ntlm_buffer *unhashed_cb, struct ntlm_buffer *out, uint64_t *out_srv_time, bool *add_mic) { char *nb_computer_name = NULL; char *nb_domain_name = NULL; char *dns_computer_name = NULL; char *dns_domain_name = NULL; char *dns_tree_name = NULL; char *av_target_name = NULL; uint32_t av_flags = 0; uint64_t srv_time = 0; uint8_t cb[16] = { 0 }; struct ntlm_buffer av_cb = { NULL, 0 }; int ret = 0; /* TODO: check that returned netbios/dns names match ? */ /* TODO: support SingleHost buffers */ ret = ntlm_decode_target_info(ctx, in, &nb_computer_name, &nb_domain_name, &dns_computer_name, &dns_domain_name, &dns_tree_name, &av_target_name, &av_flags, &srv_time, NULL, NULL); if (ret) goto done; if (protect && (!nb_computer_name || nb_computer_name[0] == '\0')) { ret = EINVAL; goto done; } if (spn && av_target_name && ((av_flags & MSVAVFLAGS_UNVERIFIED_SPN) == 0)) { if (strcasecmp(spn, av_target_name) != 0) { ret = EINVAL; goto done; } } /* the server did not send the timestamp, use current time */ if (srv_time == 0) { srv_time = ntlm_timestamp_now(); } else if (add_mic) { av_flags |= MSVAVFLAGS_MIC_PRESENT; *add_mic = true; } if (unhashed_cb->length > 0) { av_cb.data = cb; av_cb.length = 16; ret = ntlm_hash_channel_bindings(unhashed_cb, &av_cb); if (ret) goto done; } if (!av_target_name && spn) { av_target_name = strdup(spn); if (!av_target_name) { ret = ENOMEM; goto done; } } ret = ntlm_encode_target_info(ctx, nb_computer_name, nb_domain_name, dns_computer_name, dns_domain_name, dns_tree_name, &av_flags, &srv_time, NULL, av_target_name, &av_cb, out); done: safefree(nb_computer_name); safefree(nb_domain_name); safefree(dns_computer_name); safefree(dns_domain_name); safefree(dns_tree_name); safefree(av_target_name); *out_srv_time = srv_time; return ret; } int ntlm_decode_msg_type(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer, uint32_t *type) { struct wire_neg_msg *msg; uint32_t msg_type; int ret; if (!ctx) return EINVAL; if (buffer->length < sizeof(struct wire_msg_hdr)) { return ERR_DECODE; } msg = (struct wire_neg_msg *)buffer->data; ret = ntlm_decode_header(&msg->header, &msg_type); if (ret) goto done; switch (msg_type) { case NEGOTIATE_MESSAGE: if (buffer->length < sizeof(struct wire_neg_msg)) { return ERR_DECODE; } break; case CHALLENGE_MESSAGE: if (buffer->length < sizeof(struct wire_chal_msg) && buffer->length != sizeof(struct wire_chal_msg_old)) { return ERR_DECODE; } break; case AUTHENTICATE_MESSAGE: if (buffer->length < sizeof(struct wire_auth_msg)) { return ERR_DECODE; } break; default: ret = ERR_DECODE; break; } done: if (ret == 0) { *type = msg_type; } return ret; } int ntlm_encode_neg_msg(struct ntlm_ctx *ctx, uint32_t flags, const char *domain, const char *workstation, struct ntlm_buffer *message) { struct wire_neg_msg *msg; struct ntlm_buffer buffer; size_t data_offs; size_t dom_len = 0; size_t wks_len = 0; int ret = 0; if (!ctx) return EINVAL; buffer.length = sizeof(struct wire_neg_msg); /* Strings MUST use OEM charset in negotiate message */ if (flags & NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED) { if (!domain) return EINVAL; dom_len = strlen(domain); buffer.length += dom_len; } if (flags & NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED) { if (!workstation) return EINVAL; wks_len = strlen(workstation); buffer.length += wks_len; } buffer.data = calloc(1, buffer.length); if (!buffer.data) return ENOMEM; msg = (struct wire_neg_msg *)buffer.data; data_offs = (char *)msg->payload - (char *)msg; ntlm_encode_header(&msg->header, NEGOTIATE_MESSAGE); msg->neg_flags = htole32(flags); if (dom_len) { ret = ntlm_encode_oem_str(&msg->domain_name, &buffer, &data_offs, domain, dom_len); if (ret) goto done; } if (wks_len) { ret = ntlm_encode_oem_str(&msg->workstation_name, &buffer, &data_offs, workstation, wks_len); if (ret) goto done; } if (flags & NTLMSSP_NEGOTIATE_VERSION) { ret = ntlm_encode_version(ctx, &buffer, (char *)&msg->version - (char *)msg); if (ret) goto done; } done: if (ret) { safefree(buffer.data); } else { *message = buffer; } return ret; } int ntlm_decode_neg_msg(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer, uint32_t *flags, char **domain, char **workstation) { struct wire_neg_msg *msg; size_t payload_offs; uint32_t neg_flags; char *dom = NULL; char *wks = NULL; int ret = 0; if (!ctx) return EINVAL; msg = (struct wire_neg_msg *)buffer->data; payload_offs = (char *)msg->payload - (char *)msg; neg_flags = le32toh(msg->neg_flags); if ((neg_flags & NTLMSSP_NEGOTIATE_VERSION) == 0) { /* adjust the payload offset to point to the * version field, for compatibility with older * clients that completely omitted the structure * on the wire */ payload_offs -= sizeof(struct wire_version); } if (domain && (neg_flags & NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED)) { ret = ntlm_decode_oem_str(&msg->domain_name, buffer, payload_offs, &dom); if (ret) goto done; } if (workstation && (neg_flags & NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED)) { ret = ntlm_decode_oem_str(&msg->workstation_name, buffer, payload_offs, &wks); if (ret) goto done; } done: if (ret) { safefree(dom); safefree(wks); } else { *flags = neg_flags; if (domain) *domain = dom; if (workstation) *workstation = wks; } return ret; } int ntlm_encode_chal_msg(struct ntlm_ctx *ctx, uint32_t flags, const char *target_name, struct ntlm_buffer *challenge, struct ntlm_buffer *target_info, struct ntlm_buffer *message) { struct wire_chal_msg *msg; struct ntlm_buffer buffer; size_t data_offs; size_t target_len = 0; int ret = 0; if (!ctx) return EINVAL; if (!challenge || challenge->length != 8) return EINVAL; buffer.length = sizeof(struct wire_chal_msg); if ((flags & NTLMSSP_TARGET_TYPE_SERVER) || (flags & NTLMSSP_TARGET_TYPE_DOMAIN)) { if (!target_name) return EINVAL; target_len = strlen(target_name); if (flags & NTLMSSP_NEGOTIATE_UNICODE) { buffer.length += target_len * 2; } else { buffer.length += target_len; } } if (flags & NTLMSSP_NEGOTIATE_TARGET_INFO) { if (!target_info) return EINVAL; buffer.length += target_info->length; } buffer.data = calloc(1, buffer.length); if (!buffer.data) return ENOMEM; msg = (struct wire_chal_msg *)buffer.data; data_offs = (char *)msg->payload - (char *)msg; ntlm_encode_header(&msg->header, CHALLENGE_MESSAGE); /* this must be first as it pushes the payload further down */ if (flags & NTLMSSP_NEGOTIATE_VERSION) { ret = ntlm_encode_version(ctx, &buffer, (char *)&msg->version - (char *)msg); if (ret) goto done; } if ((flags & NTLMSSP_TARGET_TYPE_SERVER) || (flags & NTLMSSP_TARGET_TYPE_DOMAIN)) { if (flags & NTLMSSP_NEGOTIATE_UNICODE) { ret = ntlm_encode_u16l_str_hdr(ctx, &msg->target_name, &buffer, &data_offs, target_name, target_len); } else { ret = ntlm_encode_oem_str(&msg->target_name, &buffer, &data_offs, target_name, target_len); } if (ret) goto done; } msg->neg_flags = htole32(flags); memcpy(msg->server_challenge, challenge->data, 8); if (flags & NTLMSSP_NEGOTIATE_TARGET_INFO) { ret = ntlm_encode_field(&msg->target_info, &buffer, &data_offs, target_info); if (ret) goto done; } done: if (ret) { safefree(buffer.data); } else { *message = buffer; } return ret; } int ntlm_decode_chal_msg(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer, uint32_t *_flags, char **target_name, struct ntlm_buffer *challenge, struct ntlm_buffer *target_info) { struct wire_chal_msg *msg; size_t payload_offs; size_t base_chal_size; uint32_t flags; char *trg = NULL; int ret = 0; if (!ctx) return EINVAL; if (challenge->length < 8) return EINVAL; msg = (struct wire_chal_msg *)buffer->data; payload_offs = (char *)msg->payload - (char *)msg; flags = le32toh(msg->neg_flags); base_chal_size = sizeof(struct wire_chal_msg); if ((flags & NTLMSSP_NEGOTIATE_VERSION) == 0) { /* adjust the payload offset to point to the * version field, for compatibility with older * clients that completely omitted the structure * on the wire */ payload_offs -= sizeof(struct wire_version); base_chal_size -= sizeof(struct wire_version); } if ((flags & NTLMSSP_TARGET_TYPE_SERVER) || (flags & NTLMSSP_TARGET_TYPE_DOMAIN)) { if (flags & NTLMSSP_NEGOTIATE_UNICODE) { ret = ntlm_decode_u16l_str_hdr(ctx, &msg->target_name, buffer, payload_offs, &trg); } else { ret = ntlm_decode_oem_str(&msg->target_name, buffer, payload_offs, &trg); } if (ret) goto done; } memcpy(challenge->data, msg->server_challenge, 8); challenge->length = 8; /* if we allowed a broken short challenge message from an old * server we must stop here */ if (buffer->length < base_chal_size) { if (flags & NTLMSSP_NEGOTIATE_TARGET_INFO) { ret = ERR_DECODE; } goto done; } if (flags & NTLMSSP_NEGOTIATE_TARGET_INFO) { ret = ntlm_decode_field(&msg->target_info, buffer, payload_offs, target_info); if (ret) goto done; } done: if (ret) { safefree(trg); } else { *_flags = flags; *target_name = trg; } return ret; } int ntlm_encode_auth_msg(struct ntlm_ctx *ctx, uint32_t flags, struct ntlm_buffer *lm_chalresp, struct ntlm_buffer *nt_chalresp, char *domain_name, char *user_name, char *workstation, struct ntlm_buffer *enc_sess_key, struct ntlm_buffer *mic, struct ntlm_buffer *message) { struct wire_auth_msg *msg; struct ntlm_buffer buffer; struct ntlm_buffer empty_chalresp = { 0 }; size_t data_offs; size_t domain_name_len = 0; size_t user_name_len = 0; size_t workstation_len = 0; int ret = 0; if (!ctx) return EINVAL; buffer.length = sizeof(struct wire_auth_msg); if (lm_chalresp) { buffer.length += lm_chalresp->length; } else { lm_chalresp = &empty_chalresp; } if (nt_chalresp) { buffer.length += nt_chalresp->length; } else { nt_chalresp = &empty_chalresp; } if (domain_name) { domain_name_len = strlen(domain_name); if (flags & NTLMSSP_NEGOTIATE_UNICODE) { buffer.length += domain_name_len * 2; } else { buffer.length += domain_name_len; } } if (user_name) { user_name_len = strlen(user_name); if (flags & NTLMSSP_NEGOTIATE_UNICODE) { buffer.length += user_name_len * 2; } else { buffer.length += user_name_len; } } if (workstation) { workstation_len = strlen(workstation); if (flags & NTLMSSP_NEGOTIATE_UNICODE) { buffer.length += workstation_len * 2; } else { buffer.length += workstation_len; } } if (enc_sess_key) { buffer.length += enc_sess_key->length; } if (mic) { buffer.length += 16; } buffer.data = calloc(1, buffer.length); if (!buffer.data) return ENOMEM; msg = (struct wire_auth_msg *)buffer.data; data_offs = (char *)msg->payload - (char *)msg; ntlm_encode_header(&msg->header, AUTHENTICATE_MESSAGE); /* this must be first as it pushes the payload further down */ if (flags & NTLMSSP_NEGOTIATE_VERSION) { ret = ntlm_encode_version(ctx, &buffer, (char *)&msg->version - (char *)msg); if (ret) goto done; } /* this pushes the payload further down */ if (mic) { memset(&buffer.data[data_offs], 0, mic->length); /* return the actual pointer back in the mic, as it will * be backfilled later by the caller */ mic->data = &buffer.data[data_offs]; data_offs += mic->length; } ret = ntlm_encode_field(&msg->lm_chalresp, &buffer, &data_offs, lm_chalresp); if (ret) goto done; ret = ntlm_encode_field(&msg->nt_chalresp, &buffer, &data_offs, nt_chalresp); if (ret) goto done; if (domain_name_len) { if (flags & NTLMSSP_NEGOTIATE_UNICODE) { ret = ntlm_encode_u16l_str_hdr(ctx, &msg->domain_name, &buffer, &data_offs, domain_name, domain_name_len); } else { ret = ntlm_encode_oem_str(&msg->domain_name, &buffer, &data_offs, domain_name, domain_name_len); } if (ret) goto done; } if (user_name_len) { if (flags & NTLMSSP_NEGOTIATE_UNICODE) { ret = ntlm_encode_u16l_str_hdr(ctx, &msg->user_name, &buffer, &data_offs, user_name, user_name_len); } else { ret = ntlm_encode_oem_str(&msg->user_name, &buffer, &data_offs, user_name, user_name_len); } if (ret) goto done; } if (workstation_len) { if (flags & NTLMSSP_NEGOTIATE_UNICODE) { ret = ntlm_encode_u16l_str_hdr(ctx, &msg->workstation, &buffer, &data_offs, workstation, workstation_len); } else { ret = ntlm_encode_oem_str(&msg->workstation, &buffer, &data_offs, workstation, workstation_len); } if (ret) goto done; } if (enc_sess_key) { ret = ntlm_encode_field(&msg->enc_sess_key, &buffer, &data_offs, enc_sess_key); if (ret) goto done; } msg->neg_flags = htole32(flags); done: if (ret) { safefree(buffer.data); } else { *message = buffer; } return ret; } int ntlm_decode_auth_msg(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer, uint32_t flags, struct ntlm_buffer *lm_chalresp, struct ntlm_buffer *nt_chalresp, char **domain_name, char **user_name, char **workstation, struct ntlm_buffer *enc_sess_key, struct ntlm_buffer *target_info, struct ntlm_buffer *mic) { struct wire_auth_msg *msg; uint32_t neg_flags; size_t payload_offs; char *dom = NULL; char *usr = NULL; char *wks = NULL; int ret = 0; if (!ctx) return EINVAL; if (lm_chalresp) lm_chalresp->data = NULL; if (nt_chalresp) nt_chalresp->data = NULL; if (enc_sess_key) enc_sess_key->data = NULL; msg = (struct wire_auth_msg *)buffer->data; payload_offs = (char *)msg->payload - (char *)msg; neg_flags = le32toh(msg->neg_flags); if ((neg_flags & NTLMSSP_NEGOTIATE_VERSION) == 0) { /* adjust the payload offset to point to the * version field, for compatibility with older * clients that completely omitted the structure * on the wire */ payload_offs -= sizeof(struct wire_version); } /* Unconditionally copy 16 bytes for the MIC, if it was really * added by the client it will be flagged in the AV_PAIR contained * in the NT Response, that will be fully decoded later by the caller * and the MIC checked otherwise these 16 bytes will just be ignored */ if (mic) { size_t mic_offs = payload_offs; if (mic->length < 16) return ERR_DECODE; if ((neg_flags & NTLMSSP_NEGOTIATE_VERSION) == 0) { struct wire_version zver = {0}; /* mic is at payload_offs right now, but this offset may be * wrongly reduced for compatibility with older clients, * if all bytes are zeroed, then it means there was an actual * empty version struct */ if (memcmp(&msg->version, &zver, sizeof(struct wire_version)) == 0) { mic_offs += sizeof(struct wire_version); } } if (buffer->length - mic_offs < 16) return ERR_DECODE; memcpy(mic->data, &buffer->data[mic_offs], 16); } if (msg->lm_chalresp.len != 0 && lm_chalresp) { ret = ntlm_decode_field(&msg->lm_chalresp, buffer, payload_offs, lm_chalresp); if (ret) goto done; } if (msg->nt_chalresp.len != 0 && nt_chalresp) { ret = ntlm_decode_field(&msg->nt_chalresp, buffer, payload_offs, nt_chalresp); if (ret) goto done; if (target_info) { union wire_ntlm_response *resp; struct wire_ntlmv2_cli_chal *chal; uint8_t *data; int len; resp = (union wire_ntlm_response *)nt_chalresp->data; chal = (struct wire_ntlmv2_cli_chal *)resp->v2.cli_chal; len = nt_chalresp->length - sizeof(resp->v2.resp) - offsetof(struct wire_ntlmv2_cli_chal, target_info); if (len > 0) { data = chal->target_info; target_info->data = malloc(len); if (!target_info->data) { ret = ENOMEM; goto done; } memcpy(target_info->data, data, len); target_info->length = len; } } } if (msg->domain_name.len != 0 && domain_name) { if (flags & NTLMSSP_NEGOTIATE_UNICODE) { ret = ntlm_decode_u16l_str_hdr(ctx, &msg->domain_name, buffer, payload_offs, &dom); } else { ret = ntlm_decode_oem_str(&msg->domain_name, buffer, payload_offs, &dom); } if (ret) goto done; } if (msg->user_name.len != 0 && user_name) { if (flags & NTLMSSP_NEGOTIATE_UNICODE) { ret = ntlm_decode_u16l_str_hdr(ctx, &msg->user_name, buffer, payload_offs, &usr); } else { ret = ntlm_decode_oem_str(&msg->user_name, buffer, payload_offs, &usr); } if (ret) goto done; } if (msg->workstation.len != 0 && workstation) { if (flags & NTLMSSP_NEGOTIATE_UNICODE) { ret = ntlm_decode_u16l_str_hdr(ctx, &msg->workstation, buffer, payload_offs, &wks); } else { ret = ntlm_decode_oem_str(&msg->workstation, buffer, payload_offs, &wks); } if (ret) goto done; } if (msg->enc_sess_key.len != 0 && enc_sess_key) { ret = ntlm_decode_field(&msg->enc_sess_key, buffer, payload_offs, enc_sess_key); } /* ignore returned flags, our flags are authoritative flags = le32toh(msg->neg_flags); */ done: if (ret) { if (lm_chalresp) safefree(lm_chalresp->data); if (nt_chalresp) safefree(nt_chalresp->data); if (enc_sess_key) safefree(enc_sess_key->data); safefree(dom); safefree(usr); safefree(wks); } else { if (domain_name) *domain_name = dom; if (user_name) *user_name = usr; if (workstation) *workstation = wks; } return ret; } gss-ntlmssp-1.3.1/src/ntlm.h000066400000000000000000000737631456736161100157520ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ #ifndef _NTLM_H_ #define _NTLM_H_ #include #include "ntlm_common.h" /* Negotiate Flags */ #define NTLMSSP_NEGOTIATE_56 (1U << 31) #define NTLMSSP_NEGOTIATE_KEY_EXCH (1U << 30) #define NTLMSSP_NEGOTIATE_128 (1U << 29) #define UNUSED_R1 (1U << 28) #define UNUSED_R2 (1U << 27) #define UNUSED_R3 (1U << 26) #define NTLMSSP_NEGOTIATE_VERSION (1U << 25) #define UNUSED_R4 (1U << 24) #define NTLMSSP_NEGOTIATE_TARGET_INFO (1U << 23) #define NTLMSSP_REQUEST_NON_NT_SESSION_KEY (1U << 22) #define UNUSED_R5 /* Davenport: NEGOTIATE_ACCEPT */ (1U << 21) #define NTLMSSP_NEGOTIATE_IDENTIFY (1U << 20) #define NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY (1U << 19) #define UNUSED_R6 /* Davenport:TARGET_TYPE_SHARE */ (1U << 18) #define NTLMSSP_TARGET_TYPE_SERVER (1U << 17) #define NTLMSSP_TARGET_TYPE_DOMAIN (1U << 16) #define NTLMSSP_NEGOTIATE_ALWAYS_SIGN (1U << 15) #define UNUSED_R7 /* Davenport:LOCAL_CALL */ (1U << 14) #define NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED (1U << 13) #define NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED (1U << 12) #define NTLMSSP_ANONYMOUS (1U << 11) #define UNUSED_R8 (1U << 10) #define NTLMSSP_NEGOTIATE_NTLM (1U << 9) #define UNUSED_R9 (1U << 8) #define NTLMSSP_NEGOTIATE_LM_KEY (1U << 7) #define NTLMSSP_NEGOTIATE_DATAGRAM (1U << 6) #define NTLMSSP_NEGOTIATE_SEAL (1U << 5) #define NTLMSSP_NEGOTIATE_SIGN (1U << 4) #define UNUSED_R10 (1U << 3) #define NTLMSSP_REQUEST_TARGET (1U << 2) #define NTLMSSP_NEGOTIATE_OEM (1U << 1) #define NTLMSSP_NEGOTIATE_UNICODE (1U << 0) /* (2.2.2.10 VERSION) */ #define WINDOWS_MAJOR_VERSION_5 0x05 #define WINDOWS_MAJOR_VERSION_6 0x06 #define WINDOWS_MAJOR_VERSION_10 0x0A #define WINDOWS_MINOR_VERSION_0 0x00 #define WINDOWS_MINOR_VERSION_1 0x01 #define WINDOWS_MINOR_VERSION_2 0x02 #define WINDOWS_MINOR_VERSION_3 0x03 #define NTLMSSP_REVISION_W2K3 0x0F #define NTLMSSP_VERSION_MAJOR WINDOWS_MAJOR_VERSION_6 #define NTLMSSP_VERSION_MINOR WINDOWS_MINOR_VERSION_2 #define NTLMSSP_VERSION_BUILD 0 #define NTLMSSP_VERSION_REV NTLMSSP_REVISION_W2K3 #define NTLMSSP_MESSAGE_SIGNATURE_VERSION 0x00000001 #define NEGOTIATE_MESSAGE 0x00000001 #define CHALLENGE_MESSAGE 0x00000002 #define AUTHENTICATE_MESSAGE 0x00000003 #define MSVAVFLAGS_AUTH_CONSTRAINED 0x01 #define MSVAVFLAGS_MIC_PRESENT 0x02 #define MSVAVFLAGS_UNVERIFIED_SPN 0x04 #define NTLM_SIGNATURE_SIZE 16 struct ntlm_ctx; /** * @brief Create a ntlm_ctx initialized to the initial state * * @param ctx The returned context * * @return 0 if successful, an error otherwise */ int ntlm_init_ctx(struct ntlm_ctx **ctx); /** * @brief Frees a ntlm_ctx * * @param ctx Pointer to a context to be freed * * @return 0 if successful, an error otherwise * NOTE: even if an error is returned the contetx is freed and NULLed */ int ntlm_free_ctx(struct ntlm_ctx **ctx); void ntlm_free_buffer_data(struct ntlm_buffer *buf); uint64_t ntlm_timestamp_now(void); bool ntlm_casecmp(const char *s1, const char *s2); /** * @brief Sets the NTLMSSP version * Mostly used to emulate Windows versions for test vectors * * @param major major version number (ex. 6) * @param minor minor version number (ex. 1) * @param build build version number (ex. 7600) * @param revision revision version number (ex. 16) */ void ntlm_internal_set_version(uint8_t major, uint8_t minor, uint16_t build, uint8_t revision); /* ############### CRYPTO FUNCTIONS ################ */ struct ntlm_key { uint8_t data[16]; /* up to 16 bytes (128 bits) */ size_t length; }; struct ntlm_signseal_handle { struct ntlm_key sign_key; struct ntlm_key seal_key; struct ntlm_rc4_handle *seal_handle; uint32_t seq_num; }; struct ntlm_signseal_state { struct ntlm_signseal_handle send; struct ntlm_signseal_handle recv; bool datagram; bool ext_sec; }; #define NTLM_SEND 1 #define NTLM_RECV 2 /** * @brief Turns a utf8 password into an NT Hash * * @param password The password * @param result The returned hash * * @return 0 on success or an error; */ int NTOWFv1(const char *password, struct ntlm_key *result); /** * @brief Turns a utf8 password into an LM Hash * * @param password The password * @param result The returned hash * * @return 0 on success or an error; */ int LMOWFv1(const char *password, struct ntlm_key *result); /** * @brief Generate the challenge used in NTLMv1 w/ Extended Security * * @param server_chal An 8 byte long buffer w/ the server challenge * @param client_chal An 8 byte long buffer w/ the client challenge * @param result_chal An 8 byte long buffer w/ for the result * * @return 0 on success or ERR_CRYPTO */ int ntlm_compute_ext_sec_challenge(uint8_t *server_chal, uint8_t *client_chal, uint8_t *result_chal); /** * @brief Generates a v1 NT Response * * @param nt_key The NTLMv1 key computed by NTOWFv1() * @param ext_sec Whether Extended Security has been negotiated * @param server_chal[8] The server challenge * @param client_chal[8] The client challenge (only with Extended Security) * @param nt_response The output buffer (must be 24 bytes preallocated) * * @return 0 on success or ERR_CRYPTO */ int ntlm_compute_nt_response(struct ntlm_key *nt_key, bool ext_sec, uint8_t server_chal[8], uint8_t client_chal[8], struct ntlm_buffer *nt_response); /** * @brief Generates a v1 LM Response * * @param lm_key The LMv1 key computed by LMOWFv1() * @param ext_sec Whether Extended Security has been negotiated * @param server_chal[8] The server challenge * @param client_chal[8] The client challenge (only with Extended Security) * @param lm_response The output buffer (must be 24 bytes preallocated) * * @return 0 on success or ERR_CRYPTO */ int ntlm_compute_lm_response(struct ntlm_key *lm_key, bool ext_sec, uint8_t server_chal[8], uint8_t client_chal[8], struct ntlm_buffer *lm_response); /** * @brief Returns the v1 session key * * @param nt_key The NTLMv1 key computed by NTOWFv1() * @param session_base_key The output buffer (must be 16 bytes preallocated) * * @return 0 on success or ERR_CRYPTO */ int ntlm_session_base_key(struct ntlm_key *nt_key, struct ntlm_key *session_base_key); /** * @brief V1 Key Exchange Key calculation * * @param ctx An ntlm context * @param ext_sec Whether Extended Security has been negotiated * @param neg_lm_key Whether LM KEY has been negotiated * @param non_nt_sess_key Whether non NT Session Key has been negotiated * @param server_chal The server challenge (only with Extended Security) * @param lm_key The LMv1 key computed by LMOWFv1() * @param session_base_key The Session Base Key * @param lm_response The LM v1 Response * @param key_exchange_key The output buffer (must be 16 bytes preallocated) * * @return 0 on success or ERR_CRYPTO */ int KXKEY(struct ntlm_ctx *ctx, bool ext_sec, bool neg_lm_key, bool non_nt_sess_key, uint8_t server_chal[8], struct ntlm_key *lm_key, struct ntlm_key *session_base_key, struct ntlm_buffer *lm_response, struct ntlm_key *key_exchange_key); /** * @brief Generates a NTLMv2 Response Key * * @param ctx An ntlm context * @param nt_hash The NT Hash of the user password * @param user The user name * @param domain The user's domain * @param result The resulting key * (must be a preallocated 16 bytes buffer) * * @return 0 on success or ERR_CRYPTO */ int NTOWFv2(struct ntlm_ctx *ctx, struct ntlm_key *nt_hash, const char *user, const char *domain, struct ntlm_key *result); /** * @brief Computes The NTLMv2 Response * * @param ntlmv2_key The NTLMv2 key computed by NTOWFv2() * @param server_chal[8] The server provided challenge * @param client_chal[8] A client generated random challenge * @param timestamp A FILETIME timestamp * @param target_info The target info * @param nt_response The resulting nt_response buffer * * @return 0 on success or error. */ int ntlmv2_compute_nt_response(struct ntlm_key *ntlmv2_key, uint8_t server_chal[8], uint8_t client_chal[8], uint64_t timestamp, struct ntlm_buffer *target_info, struct ntlm_buffer *nt_response); /** * @brief Computes The LMv2 Response * * @param ntlmv2_key The NTLMv2 key computed by NTOWFv2() * @param server_chal[8] The server provided challenge * @param client_chal[8] A client generated random challenge * @param lm_response The resulting lm_response buffer * * @return 0 on success or error. */ int ntlmv2_compute_lm_response(struct ntlm_key *ntlmv2_key, uint8_t server_chal[8], uint8_t client_chal[8], struct ntlm_buffer *lm_response); /** * @brief Computes the NTLMv2 SessionBaseKey * * @param ntlmv2_key The NTLMv2 key computed by NTOWFv2() * @param nt_response The NTLMv2 response * @param session_base_key The resulting session key * * @return 0 on success or error. */ int ntlmv2_session_base_key(struct ntlm_key *ntlmv2_key, struct ntlm_buffer *nt_response, struct ntlm_key *session_base_key); /** * @brief Comutes the NTLM session key * * @param key_exchange_key[16] The Key Exchange Key * @param key_exch KEY_EXCH has been negotited * @param exported_session_key[16] Resulting exported session key * * @return 0 on success or error. */ int ntlm_exported_session_key(struct ntlm_key *key_exchange_key, bool key_exch, struct ntlm_key *exported_session_key); /** * @brief Encrypts or Decrypts the NTLM session key using RC4K * * @param key_exchange_key[16] The Key Exchange Key * @param exported_session_key[16] Resulting exported session key * @param encrypted_random_session_key Resulting encrypted session key * * @return 0 on success or error. */ int ntlm_encrypted_session_key(struct ntlm_key *key, struct ntlm_key *in, struct ntlm_key *out); /** * @brief Computes the extended security keys from the session key * * @param flags Incoming challenge/authenticate flags * @param client Wheter this ia a client or a server * @param session_key The session key * @param signseal_state Sign and seal keys and state * * @return 0 on success or error. */ int ntlm_signseal_keys(uint32_t flags, bool client, struct ntlm_key *session_key, struct ntlm_signseal_state *signseal_state); /** * @brief Resets the RC4 state for the send or receive handle * * @param flags Incoming challenge/authenticate flags * @param recv Wheter to reset the send or recive buffer * @param session_key The session key * @param signseal_state Sign and seal keys and state * * @return 0 on success or error. */ int ntlm_reset_rc4_state(uint32_t flags, bool recv, struct ntlm_key *session_key, struct ntlm_signseal_state *state); /** * @brief Release the RC4 state * * @param signseal_state Sign and seal keys and state */ void ntlm_release_rc4_state(struct ntlm_signseal_state *state); /** * @brief Verifies a NTLM v1 NT Response * * @param nt_response The NT Response buffer * @param nt_key The NTLMv1 NT Key * @param ext_sec Whether Extended Security was negotiated * @param server_chal[8] The Server Challenge * @param client_chal[8] The Client Challenge * * @return 0 on success, or an error */ int ntlm_verify_nt_response(struct ntlm_buffer *nt_response, struct ntlm_key *nt_key, bool ext_sec, uint8_t server_chal[8], uint8_t client_chal[8]); /** * @brief Verifies a NTLM v1 LM Response * * @param lm_response The LM Response buffer * @param lm_key The NTLMv1 LM Key * @param ext_sec Whether Extended Security was negotiated * @param server_chal[8] The Server Challenge * @param client_chal[8] The Client Challenge * * @return 0 on success, or an error */ int ntlm_verify_lm_response(struct ntlm_buffer *lm_response, struct ntlm_key *lm_key, bool ext_sec, uint8_t server_chal[8], uint8_t client_chal[8]); /** * @brief Verifies a NTLM v1 NT Response * * @param nt_response The NT Response buffer * @param nt_key The NTLMv1 NT Key * @param ext_sec Whether Extended Security was negotiated * @param server_chal[8] The Server Challenge * @param client_chal[8] The Client Challenge * * @return 0 on success, or an error */ int ntlm_verify_nt_response(struct ntlm_buffer *nt_response, struct ntlm_key *nt_key, bool ext_sec, uint8_t server_chal[8], uint8_t client_chal[8]); /** * @brief Verifies a NTLM v1 LM Response * * @param lm_response The LM Response buffer * @param lm_key The NTLMv1 LM Key * @param ext_sec Whether Extended Security was negotiated * @param server_chal[8] The Server Challenge * @param client_chal[8] The Client Challenge * * @return 0 on success, or an error */ int ntlm_verify_lm_response(struct ntlm_buffer *lm_response, struct ntlm_key *lm_key, bool ext_sec, uint8_t server_chal[8], uint8_t client_chal[8]); /** * @brief Verifies a 16 bit NT Response * * @param nt_response The NT Response buffer including client challenge * @param ntlmv2_key The NTLMv2 key * @param server_chal[8] The server challenge used to compute the response * * @return 0 on success, or an error */ int ntlmv2_verify_nt_response(struct ntlm_buffer *nt_response, struct ntlm_key *ntlmv2_key, uint8_t server_chal[8]); /** * @brief Verifies a 16 bit LM Response * * @param nt_response The LM Response buffer including client challenge * @param ntlmv2_key The NTLMv2 key * @param server_chal[8] The server challenge used to compute the response * * @return 0 on success, or an error */ int ntlmv2_verify_lm_response(struct ntlm_buffer *nt_response, struct ntlm_key *ntlmv2_key, uint8_t server_chal[8]); /** * @brief Create NTLM signature for the provided message * * @param flags Negotiated flags * @param state Sign and seal keys and state * @param direction Direction (true for send) * @param message Message buffer * @param signature Preallocated byffer of 16 bytes for signature * * @return 0 on success, or an error */ int ntlm_sign(uint32_t flags, int direction, struct ntlm_signseal_state *state, struct ntlm_buffer *message, struct ntlm_buffer *signature); /** * @brief NTLM seal the provided message * * @param flags Negotiated flags * @param state Sign and seal keys and state * @param message Message buffer * @param output Output buffer * @param signature Signature * * @return 0 on success, or an error */ int ntlm_seal(uint32_t flags, struct ntlm_signseal_state *state, struct ntlm_buffer *message, struct ntlm_buffer *output, struct ntlm_buffer *signature); /** * @brief NTLM unseal the provided message * * @param flags Negotiated flags * @param state Sign and seal keys and state * @param message Message buffer * @param output Output buffer * @param signature Signature * * @return 0 on success, or an error */ int ntlm_unseal(uint32_t flags, struct ntlm_signseal_state *state, struct ntlm_buffer *message, struct ntlm_buffer *output, struct ntlm_buffer *signature); /** * @brief Creates a NTLM MIC * * @param exported_session_key The Exported Session Key * @param negotiate_message The NTLM Negotiate Message (or empty) * @param challenge_message The NTLM Challenge Message * @param authenticate_message The NTLM Authenticate Message * @param mic Preallocated byffer of 16 bytes * * @return 0 on success, or an error */ int ntlm_mic(struct ntlm_key *exported_session_key, struct ntlm_buffer *negotiate_message, struct ntlm_buffer *challenge_message, struct ntlm_buffer *authenticate_message, struct ntlm_buffer *mic); /** * @brief Verifies a MIC * * @param key The keys used to generate the original MIC * @param negotiate_message The NTLM Negotiate Message (or empty) * @param challenge_message The NTLM Challenge Message * @param authenticate_message The NTLM Authenticate Message * @param mic The original MIC * * NOTE: This function zeros the area of memory where the MIC is held in the * Authenticate Message * * @return 0 on success, EACCES if the MIC fails to verify, or an error */ int ntlm_verify_mic(struct ntlm_key *key, struct ntlm_buffer *negotiate_message, struct ntlm_buffer *challenge_message, struct ntlm_buffer *authenticate_message, struct ntlm_buffer *mic); /** * @brief NTLM hash client channel binding unhashed data * * @param unhashed The unhashed channel bindings data * @param signature The MD5 signature * * @return 0 on success, or an error */ int ntlm_hash_channel_bindings(struct ntlm_buffer *unhashed, struct ntlm_buffer *signature); /** * @brief Verifies Channel binding signature from unhashed data. * * @param unhashed The unhashed channel bindings data * @param signature The recieved MD5 signature to check against * * @return 0 on success, EACCES if the CBT fails to verify, or an error */ int ntlm_verify_channel_bindings(struct ntlm_buffer *unhashed, struct ntlm_buffer *signature); /* ############## ENCODING / DECODING ############## */ /** * @brief A utility function to construct a target_info structure * * @param ctx The ntlm context * @param nb_computer_name The NetBIOS Computer Name * @param nb_domain_name The NetBIOS Domain Name * @param dns_computer_name The DNS Fully Qualified Computer Name * @param dns_domain_name The DNS Fully Qualified Domain Name * @param dns_tree_name The DNS Tree Name * @param av_flags The av flags * @param av_timestamp A 64 bit FILETIME timestamp * @param av_single_host A ntlm_buffer with the single host data * @param av_target_name The target name * @param av_cb A ntlm_buffer with channel binding data * @param target_info The buffer in which target_info is returned. * * NOTE: The caller is responsible for free()ing the buffer * * @return 0 if everyting parses correctly, or an error code */ int ntlm_encode_target_info(struct ntlm_ctx *ctx, char *nb_computer_name, char *nb_domain_name, char *dns_computer_name, char *dns_domain_name, char *dns_tree_name, uint32_t *av_flags, uint64_t *av_timestamp, struct ntlm_buffer *av_single_host, char *av_target_name, struct ntlm_buffer *av_cb, struct ntlm_buffer *target_info); /** * @brief A utility function to parse a target_info structure * * @param ctx The ntlm context * @param buffer A ntlm_buffer containing the info to be parsed * @param nb_computer_name The NetBIOS Computer Name * @param nb_domain_name The NetBIOS Domain Name * @param dns_computer_name The DNS Fully Qualified Computer Name * @param dns_domain_name The DNS Fully Qualified Domain Name * @param dns_tree_name The DNS Tree Name * @param av_flags The av flags * @param av_timestamp A 64 bit FILETIME timestamp * @param av_single_host A ntlm_buffer with the single host data * @param av_target_name The target name * @param av_cb A ntlm_buffer with channel binding data * * NOTE: The caller is responsible for free()ing all strings, while the * ntlm_buffer types point directly at data in the provided buffer. * * @return 0 if everyting parses correctly, or an error code */ int ntlm_decode_target_info(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer, char **nb_computer_name, char **nb_domain_name, char **dns_computer_name, char **dns_domain_name, char **dns_tree_name, char **av_target_name, uint32_t *av_flags, uint64_t *av_timestamp, struct ntlm_buffer *av_single_host, struct ntlm_buffer *av_cb); /** * @brief A utility function to process a target_info structure * * @param ctx The ntlm context * @param protect Set if signing or sealing has been requested * @param in A ntlm_buffer containing the received info * @param server The Client Supplied Server Name if available * @param unhashed_cb A ntlm_buffer with channel binding data * @param out The processed target_info buffer * @param out_srv_time A 64 bit FILETIME timestamp * @param add_mic A pointer to a boolean. If NULL MIC flags will * not be set, otherwise if allowed the MIC flag * will be set and true will be returned. * * @return 0 if everyting parses correctly, or an error code */ int ntlm_process_target_info(struct ntlm_ctx *ctx, bool protect, struct ntlm_buffer *in, const char *server, struct ntlm_buffer *unhashed_cb, struct ntlm_buffer *out, uint64_t *out_srv_time, bool *add_mic); /** * @brief Verifies the message signature is valid and the message * in sequence with the expected state * * @param ctx The conversation context. * @param buffer A ntlm_buffer containing the raw NTLMSSP packet * * @return 0 if everyting parses correctly, or an error code * * NOTE: Always use ntlm_detect_msg_type before calling other functions, * so that the signature and message type are checked, and the state is * validated. */ int ntlm_decode_msg_type(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer, uint32_t *type); /** * @brief This function encodes a NEGTIATE_MESSAGE which is the first message * a client will send to a server. It also updates the stage in the context. * * @param ctx A fresh ntlm context. * @param flags Requested flags * @param domain Optional Domain Name * @param workstation Optional Workstation Name * @param message A ntlm_buffer containing the returned message * * NOTE: the caller is responsible for free()ing the message buffer. * * @return 0 if everyting encodes correctly, or an error code */ int ntlm_encode_neg_msg(struct ntlm_ctx *ctx, uint32_t flags, const char *domain, const char *workstation, struct ntlm_buffer *message); /** * @brief This function decodes a NTLMSSP NEGTIATE_MESSAGE. * * @param ctx A fresh ntlm context * @param buffer A ntlm_buffer containing the raw NTLMSSP packet * @param flags Returns the flags requested by the client * @param domain Returns the domain provided by the client if any * @param workstation Returns the workstation provided by the client if any * * @return 0 if everyting parses correctly, or an error code * */ int ntlm_decode_neg_msg(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer, uint32_t *flags, char **domain, char **workstation); /** * @brief This function encodes a CHALLENGE_MESSAGE which is the first message * a server will send to a client. It also updates the stage in the context. * * @param ctx The ntlm context * @param flags The challenge flags * @param target_name The target name * @param challenge A 64 bit value with a challenge * @param target_info A buffer containing target_info data * @param message A ntlm_buffer containing the encoded message * * NOTE: the caller is responsible for free()ing the message buffer * * @return 0 if everyting encodes correctly, or an error code */ int ntlm_encode_chal_msg(struct ntlm_ctx *ctx, uint32_t flags, const char *target_name, struct ntlm_buffer *challenge, struct ntlm_buffer *target_info, struct ntlm_buffer *message); /** * @brief This function decodes a NTLMSSP CHALLENGE_MESSAGE. * * @param ctx The ntlm context * @param buffer A ntlm_buffer containing the raw NTLMSSP packet * @param flags The challenge flags * @param target_name The target name * @param challenge A 64 bit value with the server challenge * The caller MUST provide a preallocated buffer of * appropriate length (8 bytes) * @param target_info A buffer containing returned target_info data * * @return 0 if everyting encodes correctly, or an error code */ int ntlm_decode_chal_msg(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer, uint32_t *flags, char **target_name, struct ntlm_buffer *challenge, struct ntlm_buffer *target_info); /** * @brief This function encodes a AUTHENTICATE_MESSAGE which is the second * message a client will send to a serve. * It also updates the stage in the context. * * @param ctx The ntlm context * @param flags The flags * @param lm_chalresp A LM or LMv2 response * @param nt_chalresp A NTLM or NTLMv2 response * @param domain_name The Domain name * @param user_name The User name * @param workstation The Workstation name * @param enc_sess_key The session key * @param mic A MIC of the messages * @param message A ntlm_buffer containing the encoded message * * @return 0 if everyting encodes correctly, or an error code */ int ntlm_encode_auth_msg(struct ntlm_ctx *ctx, uint32_t flags, struct ntlm_buffer *lm_chalresp, struct ntlm_buffer *nt_chalresp, char *domain_name, char *user_name, char *workstation, struct ntlm_buffer *enc_sess_key, struct ntlm_buffer *mic, struct ntlm_buffer *message); /** * @brief This function decodes a NTLMSSP AUTHENTICATE_MESSAGE. * * @param ctx The ntlm context * @param buffer A ntlm_buffer containing the raw NTLMSSP packet * @param flags The negotiated flags * @param lm_chalresp A LM or LMv2 response * @param nt_chalresp A NTLM or NTLMv2 response * @param domain_name The Domain name * @param user_name The User name * @param workstation The Workstation name * @param enc_sess_key The session key * @param target_info The target_info AV_PAIR embedded in the NT Response * @param mic A MIC of the messages * Passing a pointer to a mic means the caller has * previously requested the presence of a MIC field from * the peer. If a MIC is not returned by the peer the * secoding will fail. If not MIC ha sbeen previously * requested set this pointer to NULL. * The caller must provide a preallocated buffer of * appropriate length (16 bytes) * * NOTE: the caller is reponsible for freeing all allocated buffers * on success. * * @return 0 if everyting encodes correctly, or an error code */ int ntlm_decode_auth_msg(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer, uint32_t flags, struct ntlm_buffer *lm_chalresp, struct ntlm_buffer *nt_chalresp, char **domain_name, char **user_name, char **workstation, struct ntlm_buffer *enc_sess_key, struct ntlm_buffer *target_info, struct ntlm_buffer *mic); #endif /* _NTLM_H_ */ gss-ntlmssp-1.3.1/src/ntlm_common.h000066400000000000000000000102011456736161100172740ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ #ifndef _NTLM_COMMON_H_ #define _NTLM_COMMON_H_ #include #include enum ntlm_err_code { ERR_BASE = 0x4E540000, /* base error space at 'NT00' */ ERR_DECODE, ERR_ENCODE, ERR_CRYPTO, ERR_NOARG, ERR_BADARG, ERR_NONAME, ERR_NOSRVNAME, ERR_NOUSRNAME, ERR_BADLMLVL, ERR_IMPOSSIBLE, ERR_BADCTX, ERR_WRONGCTX, ERR_WRONGMSG, ERR_REQNEGFLAG, ERR_FAILNEGFLAGS, ERR_BADNEGFLAGS, ERR_NOSRVCRED, ERR_NOUSRCRED, ERR_BADCRED, ERR_NOTOKEN, ERR_NOTSUPPORTED, ERR_NOTAVAIL, ERR_NAMETOOLONG, ERR_NOBINDINGS, ERR_TIMESKEW, ERR_EXPIRED, ERR_KEYLEN, ERR_NONTLMV1, ERR_NOUSRFOUND, ERR_LAST }; #define NTLM_ERR_MASK 0x4E54FFFF #define IS_NTLM_ERR_CODE(x) (((x) & NTLM_ERR_MASK) ? true : false) #define discard_const(ptr) ((void *)((uintptr_t)(ptr))) #define safefree(x) do { free(x); x = NULL; } while(0) #define safezero(x, s) do { \ volatile uint8_t *p = (x); \ size_t size = (s); \ while (size--) { *p++ = 0; } \ } while(0) struct ntlm_buffer { uint8_t *data; size_t length; }; struct ntlm_iov { struct ntlm_buffer **data; size_t num; }; struct ntlm_rc4_handle; enum ntlm_cipher_mode { NTLM_CIPHER_IGNORE, NTLM_CIPHER_ENCRYPT, NTLM_CIPHER_DECRYPT, }; #pragma pack(push, 1) struct wire_msg_hdr { uint8_t signature[8]; uint32_t msg_type; }; #pragma pack(pop) /* A wire string, the offset is relative to the mesage and must fall into the * payload section. * max_len should be set equal to len and ignored by servers. */ #pragma pack(push, 1) struct wire_field_hdr { uint16_t len; uint16_t max_len; uint32_t offset; }; #pragma pack(pop) /* Version information. * Used only for debugging and usually placed as the head of the payload when * used */ #pragma pack(push, 1) struct wire_version { uint8_t major; uint8_t minor; uint16_t build; uint8_t reserved[3]; uint8_t revision; }; #pragma pack(pop) #pragma pack(push, 1) struct wire_neg_msg { struct wire_msg_hdr header; uint32_t neg_flags; struct wire_field_hdr domain_name; struct wire_field_hdr workstation_name; struct wire_version version; uint8_t payload[]; /* variable */ }; #pragma pack(pop) #pragma pack(push, 1) struct wire_chal_msg { struct wire_msg_hdr header; struct wire_field_hdr target_name; uint32_t neg_flags; uint8_t server_challenge[8]; uint8_t reserved[8]; struct wire_field_hdr target_info; struct wire_version version; uint8_t payload[]; /* variable */ }; #pragma pack(pop) /* We have evidence of at least one old broken server * that send shorter CHALLENGE msgs like this: */ #pragma pack(push, 1) struct wire_chal_msg_old { struct wire_msg_hdr header; struct wire_field_hdr target_name; uint32_t neg_flags; uint8_t server_challenge[8]; }; #pragma pack(pop) #pragma pack(push, 1) struct wire_auth_msg { struct wire_msg_hdr header; struct wire_field_hdr lm_chalresp; struct wire_field_hdr nt_chalresp; struct wire_field_hdr domain_name; struct wire_field_hdr user_name; struct wire_field_hdr workstation; struct wire_field_hdr enc_sess_key; uint32_t neg_flags; struct wire_version version; uint8_t payload[]; /* variable */ }; #pragma pack(pop) /* ln/ntlm response, v1 or v2 */ #pragma pack(push, 1) union wire_ntlm_response { struct { uint8_t resp[24]; } v1; struct { uint8_t resp[16]; uint8_t cli_chal[]; } v2; }; #pragma pack(pop) #pragma pack(push, 1) struct wire_ntlmv2_cli_chal { uint8_t resp_version; uint8_t hi_resp_version; uint8_t zero_6[6]; uint64_t timestamp; uint8_t client_chal[8]; uint8_t zero_4[4]; uint8_t target_info[]; /* NOTE: the target_info array must terminate with 4 zero bytes. * This is consistent with just copying the target_info array * returned in the challenge message as the last AV_PAIR there is * always MSV_AV_EOL which happens to be 4 bytes of zeros */ }; #pragma pack(pop) #endif /* _NTLM_COMMON_H_ */ gss-ntlmssp-1.3.1/src/ntlm_crypto.c000066400000000000000000000751171456736161100173400ustar00rootroot00000000000000/* Copyright 2013 Simo Sorce , see COPYING for license */ /* This File implements the NTLM protocol as specified by: * [MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol * * Additional cross checking with: * http://davenport.sourceforge.net/ntlm.html */ #include #include #include #include #include #include "ntlm.h" #include "crypto.h" /* signature structure, v1 or v2 */ #pragma pack(push, 1) union wire_msg_signature { struct { uint32_t version; uint32_t random_pad; uint32_t checksum; uint32_t seq_num; } v1; struct { uint32_t version; uint64_t checksum; uint32_t seq_num; } v2; }; #pragma pack(pop) /* the max username is 20 chars, max NB domain len is 15, so 128 should be * plenty including conversion to UTF8 using max lenght for each code point */ #define MAX_USER_DOM_LEN 512 int NTOWFv1(const char *password, struct ntlm_key *result) { struct ntlm_buffer payload; struct ntlm_buffer hash; char *retstr; size_t out; size_t len; int ret; len = strlen(password); retstr = u8_conv_to_encoding("UTF-16LE", iconveh_error, (const uint8_t *)password, len, NULL, NULL, &out); if (!retstr) return ERR_CRYPTO; payload.data = (uint8_t *)retstr; payload.length = out; hash.data = result->data; hash.length = result->length; ret = MD4_HASH(&payload, &hash); free(retstr); return ret; } #define DES_CONST "KGS!@#$%" int LMOWFv1(const char *password, struct ntlm_key *result) { struct ntlm_buffer key; struct ntlm_buffer plain; struct ntlm_buffer cipher; char upcased[15]; char *retstr; size_t out; size_t len; int ret; if (result->length != 16) return EINVAL; len = strlen(password); if (len > 14) { memset(result->data, 0, result->length); return 0; } out = 15; retstr = (char *)u8_toupper((const uint8_t *)password, len, NULL, NULL, (uint8_t *)upcased, &out); if (!retstr) return ERR_CRYPTO; if (retstr != upcased) { free(retstr); ret = EINVAL; } memset(&upcased[len], 0, 15 - len); /* part1 */ key.data = (uint8_t *)upcased; key.length = 7; plain.data = discard_const(DES_CONST); plain.length = 8; cipher.data = result->data; cipher.length = 8; ret = WEAK_DES(&key, &plain, &cipher); if (ret) return ret; /* part2 */ key.data = (uint8_t *)&upcased[7]; key.length = 7; plain.data = discard_const(DES_CONST); plain.length = 8; cipher.data = &result->data[8]; cipher.length = 8; return WEAK_DES(&key, &plain, &cipher); } int ntlm_compute_ext_sec_challenge(uint8_t *server_chal, uint8_t *client_chal, uint8_t *result_chal) { uint8_t scbuf[16]; uint8_t mdbuf[16]; struct ntlm_buffer challenges = { scbuf, 16 }; struct ntlm_buffer msgdigest = { mdbuf, 16 }; int ret; memcpy(scbuf, server_chal, 8); memcpy(&scbuf[8], client_chal, 8); ret = MD5_HASH(&challenges, &msgdigest); if (ret) return ret; memcpy(result_chal, mdbuf, 8); return 0; } int ntlm_compute_nt_response(struct ntlm_key *nt_key, bool ext_sec, uint8_t server_chal[8], uint8_t client_chal[8], struct ntlm_buffer *nt_response) { struct ntlm_buffer key = { nt_key->data, nt_key->length }; uint8_t chal[8]; struct ntlm_buffer payload = { chal, 8}; int ret; if (ext_sec) { ret = ntlm_compute_ext_sec_challenge(server_chal, client_chal, chal); if (ret) return ret; } else { memcpy(chal, server_chal, 8); } return DESL(&key, &payload, nt_response); } int ntlm_compute_lm_response(struct ntlm_key *lm_key, bool ext_sec, uint8_t server_chal[8], uint8_t client_chal[8], struct ntlm_buffer *lm_response) { struct ntlm_buffer key = { lm_key->data, lm_key->length }; struct ntlm_buffer payload = { server_chal, 8 }; if (ext_sec) { memcpy(lm_response->data, client_chal, 8); memset(&lm_response->data[8], 0, 16); return 0; } return DESL(&key, &payload, lm_response); } int ntlm_session_base_key(struct ntlm_key *nt_key, struct ntlm_key *session_base_key) { struct ntlm_buffer payload = { nt_key->data, nt_key->length }; struct ntlm_buffer hash = { session_base_key->data, session_base_key->length }; return MD4_HASH(&payload, &hash); } int KXKEY(struct ntlm_ctx *ctx, bool ext_sec, bool neg_lm_key, bool non_nt_sess_key, uint8_t server_chal[8], struct ntlm_key *lm_key, struct ntlm_key *session_base_key, struct ntlm_buffer *lm_response, struct ntlm_key *key_exchange_key) { struct ntlm_buffer payload; struct ntlm_buffer result; struct ntlm_buffer key; uint8_t buf[16]; int ret = 0; if (ext_sec) { key.data = session_base_key->data; key.length = session_base_key->length; memcpy(buf, server_chal, 8); memcpy(&buf[8], lm_response->data, 8); payload.data = buf; payload.length = 16; result.data = key_exchange_key->data; result.length = key_exchange_key->length; ret = HMAC_MD5(&key, &payload, &result); } else if (neg_lm_key) { payload.data = lm_response->data; payload.length = 8; key.data = lm_key->data; key.length = 7; result.data = key_exchange_key->data; result.length = 8; ret = WEAK_DES(&key, &payload, &result); if (ret) return ret; buf[0] = lm_key->data[7]; memset(&buf[1], 0xbd, 6); key.data = buf; result.data = &key_exchange_key->data[8]; result.length = 8; ret = WEAK_DES(&key, &payload, &result); } else if (non_nt_sess_key) { memcpy(key_exchange_key->data, lm_key, 8); memset(&key_exchange_key->data[8], 0, 8); } else { memcpy(key_exchange_key->data, session_base_key->data, 16); } return ret; } int NTOWFv2(struct ntlm_ctx *ctx, struct ntlm_key *nt_hash, const char *user, const char *domain, struct ntlm_key *result) { struct ntlm_buffer key = { nt_hash->data, nt_hash->length }; struct ntlm_buffer hmac = { result->data, result->length }; struct ntlm_buffer payload; uint8_t upcased[MAX_USER_DOM_LEN]; uint8_t *retstr; size_t offs; size_t out; size_t len; int ret; len = strlen(user); out = MAX_USER_DOM_LEN; retstr = u8_toupper((const uint8_t *)user, len, NULL, NULL, upcased, &out); if (!retstr) return ERR_CRYPTO; offs = out; if (domain) { len = strlen(domain); memcpy(&upcased[offs], domain, len); offs += len; } retstr = (uint8_t *)u8_conv_to_encoding("UTF-16LE", iconveh_error, upcased, offs, NULL, NULL, &out); if (!retstr) return ERR_CRYPTO; payload.data = (uint8_t *)retstr; payload.length = out; ret = HMAC_MD5(&key, &payload, &hmac); free(retstr); return ret; } int ntlmv2_compute_nt_response(struct ntlm_key *ntlmv2_key, uint8_t server_chal[8], uint8_t client_chal[8], uint64_t timestamp, struct ntlm_buffer *target_info, struct ntlm_buffer *nt_response) { union wire_ntlm_response *nt_resp = NULL; struct wire_ntlmv2_cli_chal *r; struct ntlm_buffer key = { ntlmv2_key->data, ntlmv2_key->length }; struct ntlm_buffer payload; struct ntlm_buffer nt_proof; size_t r_len; int ret; /* add additional 4 0s trailing target_info */ r_len = sizeof(struct wire_ntlmv2_cli_chal) + target_info->length + 4; nt_resp = calloc(1, sizeof(nt_resp->v2) + r_len); if (!nt_resp) return ENOMEM; r = (struct wire_ntlmv2_cli_chal *)nt_resp->v2.cli_chal; r->resp_version = 1; r->hi_resp_version = 1; r->timestamp = htole64(timestamp); memcpy(r->client_chal, client_chal, 8); memcpy(r->target_info, target_info->data, target_info->length); /* use nt_resp as a buffer to calculate the NT proof as they share * the cli_chal part */ payload.data = &nt_resp->v2.resp[8]; payload.length = 8 + r_len; memcpy(payload.data, server_chal, 8); nt_proof.data = nt_resp->v2.resp; nt_proof.length = 16; ret = HMAC_MD5(&key, &payload, &nt_proof); if (ret) { safefree(nt_resp); } else { nt_response->data = (uint8_t *)nt_resp; nt_response->length = 16 + r_len; } return ret; } int ntlmv2_compute_lm_response(struct ntlm_key *ntlmv2_key, uint8_t server_chal[8], uint8_t client_chal[8], struct ntlm_buffer *lm_response) { union wire_ntlm_response *lm_resp = NULL; struct ntlm_buffer key = { ntlmv2_key->data, ntlmv2_key->length }; uint8_t payload_buf[16]; struct ntlm_buffer payload = { payload_buf, 16 }; struct ntlm_buffer lm_proof; int ret; /* now caluclate the LM Proof */ lm_resp = malloc(sizeof(union wire_ntlm_response)); if (!lm_resp) { ret = ENOMEM; goto done; } memcpy(payload.data, server_chal, 8); memcpy(&payload.data[8], client_chal, 8); lm_proof.data = lm_resp->v2.resp; lm_proof.length = 16; ret = HMAC_MD5(&key, &payload, &lm_proof); done: if (ret) { safefree(lm_resp); } else { memcpy(lm_resp->v2.cli_chal, client_chal, 8); lm_response->data = (uint8_t *)lm_resp; lm_response->length = 24; } return ret; } int ntlmv2_session_base_key(struct ntlm_key *ntlmv2_key, struct ntlm_buffer *nt_response, struct ntlm_key *session_base_key) { struct ntlm_buffer key = { ntlmv2_key->data, ntlmv2_key->length }; struct ntlm_buffer hmac = { session_base_key->data, session_base_key->length }; if (session_base_key->length != 16) return EINVAL; return HMAC_MD5(&key, nt_response, &hmac); } int ntlm_exported_session_key(struct ntlm_key *key_exchange_key, bool key_exch, struct ntlm_key *exported_session_key) { struct ntlm_buffer nonce; if (!key_exch) { *exported_session_key = *key_exchange_key; return 0; } exported_session_key->length = 16; nonce.data = exported_session_key->data; nonce.length = exported_session_key->length; return RAND_BUFFER(&nonce); } int ntlm_encrypted_session_key(struct ntlm_key *key, struct ntlm_key *in, struct ntlm_key *out) { struct ntlm_buffer _key = { key->data, key->length }; struct ntlm_buffer data = { in->data, in->length }; struct ntlm_buffer result = { out->data, out->length }; return RC4K(&_key, NTLM_CIPHER_ENCRYPT, &data, &result); } static int ntlm_key_derivation_function(struct ntlm_key *key, const char *magic_constant, struct ntlm_key *derived_key) { uint8_t buf[80]; /* key + constant is never larger than 80 */ struct ntlm_buffer payload = { buf, 0 }; struct ntlm_buffer result = { derived_key->data, 16 }; size_t len; int ret; if (key->length > 16) return ERR_CRYPTO; len = strlen(magic_constant) + 1; if (len > 64) return ERR_CRYPTO; payload.length = key->length; memcpy(payload.data, key->data, key->length); memcpy(&payload.data[payload.length], magic_constant, len); payload.length += len; ret = MD5_HASH(&payload, &result); if (ret == 0) { derived_key->length = 16; } return ret; } #define NTLM_MODE_CLIENT true #define NTLM_MODE_SERVER false static int ntlm_signkey(bool mode, struct ntlm_key *session_key, struct ntlm_key *signing_key) { const char *mc; if (mode == NTLM_MODE_CLIENT) { mc = "session key to client-to-server signing key magic constant"; } else { mc = "session key to server-to-client signing key magic constant"; } return ntlm_key_derivation_function(session_key, mc, signing_key); } static int ntlm_sealkey(uint32_t flags, bool mode, struct ntlm_key *session_key, struct ntlm_key *sealing_key) { struct ntlm_key key; const char *mc; if (flags & NTLMSSP_NEGOTIATE_128) { key.length = 16; } else if (flags & NTLMSSP_NEGOTIATE_56) { key.length = 7; } else { key.length = 5; } memcpy(key.data, session_key->data, key.length); if (mode == NTLM_MODE_CLIENT) { mc = "session key to client-to-server sealing key magic constant"; } else { mc = "session key to server-to-client sealing key magic constant"; } return ntlm_key_derivation_function(&key, mc, sealing_key); } static void no_ext_sec_sealkey(uint32_t flags, struct ntlm_key *session_key, struct ntlm_buffer *sealing_key) { if (flags & NTLMSSP_NEGOTIATE_LM_KEY) { if (flags & NTLMSSP_NEGOTIATE_56) { memcpy(sealing_key->data, session_key->data, 7); sealing_key->data[7] = 0xA0; } else { memcpy(sealing_key->data, session_key->data, 5); sealing_key->data[5] = 0xE5; sealing_key->data[6] = 0x38; sealing_key->data[7] = 0xB0; } sealing_key->length = 8; } else { memcpy(sealing_key->data, session_key->data, 16); sealing_key->length = session_key->length; } } static int no_ext_sec_handle(uint32_t flags, struct ntlm_key *session_key, struct ntlm_rc4_handle **seal_handle) { uint8_t skbuf[16]; struct ntlm_buffer sealing_key = { skbuf, 16 }; no_ext_sec_sealkey(flags, session_key, &sealing_key); return RC4_INIT(&sealing_key, NTLM_CIPHER_ENCRYPT, seal_handle); } static int ext_sec_keys(uint32_t flags, bool client, struct ntlm_key *session_key, struct ntlm_signseal_state *state) { struct ntlm_buffer rc4_key; bool mode; int ret; state->ext_sec = true; if (flags & NTLMSSP_NEGOTIATE_DATAGRAM) { state->datagram = true; } /* send key */ mode = client ? NTLM_MODE_CLIENT : NTLM_MODE_SERVER; ret = ntlm_signkey(mode, session_key, &state->send.sign_key); if (ret) return ret; /* recv key */ mode = client ? NTLM_MODE_SERVER : NTLM_MODE_CLIENT; ret = ntlm_signkey(mode, session_key, &state->recv.sign_key); if (ret) return ret; /* send key */ mode = client ? NTLM_MODE_CLIENT : NTLM_MODE_SERVER; ret = ntlm_sealkey(flags, mode, session_key, &state->send.seal_key); if (ret) return ret; /* recv key */ mode = client ? NTLM_MODE_SERVER : NTLM_MODE_CLIENT; ret = ntlm_sealkey(flags, mode, session_key, &state->recv.seal_key); if (ret) return ret; rc4_key.data = state->send.seal_key.data; rc4_key.length = state->send.seal_key.length; ret = RC4_INIT(&rc4_key, NTLM_CIPHER_ENCRYPT, &state->send.seal_handle); if (ret) return ret; rc4_key.data = state->recv.seal_key.data; rc4_key.length = state->recv.seal_key.length; ret = RC4_INIT(&rc4_key, NTLM_CIPHER_DECRYPT, &state->recv.seal_handle); if (ret) return ret; return 0; } int ntlm_signseal_keys(uint32_t flags, bool client, struct ntlm_key *session_key, struct ntlm_signseal_state *state) { memset(state, 0, sizeof(struct ntlm_signseal_state)); if (flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) { state->datagram = (flags & NTLMSSP_NEGOTIATE_DATAGRAM); return ext_sec_keys(flags, client, session_key, state); } else { return no_ext_sec_handle(flags, session_key, &state->send.seal_handle); } } int ntlm_reset_rc4_state(uint32_t flags, bool recv, struct ntlm_key *session_key, struct ntlm_signseal_state *state) { struct ntlm_buffer rc4_key; int ret; if (!(flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)) { return no_ext_sec_handle(flags, session_key, &state->send.seal_handle); } if (recv) { RC4_FREE(&state->recv.seal_handle); rc4_key.data = state->recv.seal_key.data; rc4_key.length = state->recv.seal_key.length; ret = RC4_INIT(&rc4_key, NTLM_CIPHER_DECRYPT, &state->recv.seal_handle); } else { RC4_FREE(&state->send.seal_handle); rc4_key.data = state->send.seal_key.data; rc4_key.length = state->send.seal_key.length; ret = RC4_INIT(&rc4_key, NTLM_CIPHER_ENCRYPT, &state->send.seal_handle); } return ret; } void ntlm_release_rc4_state(struct ntlm_signseal_state *state) { RC4_FREE(&state->recv.seal_handle); RC4_FREE(&state->send.seal_handle); } static int ntlm_seal_regen(struct ntlm_signseal_handle *h) { struct ntlm_buffer payload; struct ntlm_buffer result; uint8_t inbuf[20]; uint8_t outbuf[16]; uint32_t le; int ret; RC4_FREE(&h->seal_handle); memcpy(inbuf, h->seal_key.data, h->seal_key.length); le = htole32(h->seq_num); memcpy(&inbuf[h->seal_key.length], &le, 4); payload.data = inbuf; payload.length = h->seal_key.length + 4; result.data = outbuf; result.length = 16; ret = MD5_HASH(&payload, &result); if (ret) return ret; ret = RC4_INIT(&result, NTLM_CIPHER_ENCRYPT, &h->seal_handle); return ret; } int ntlm_verify_nt_response(struct ntlm_buffer *nt_response, struct ntlm_key *nt_key, bool ext_sec, uint8_t server_chal[8], uint8_t client_chal[8]) { uint8_t buf[24]; struct ntlm_buffer expected_response = { buf, 24 }; int ret; ret = ntlm_compute_nt_response(nt_key, ext_sec, server_chal, client_chal, &expected_response); if (ret) return ret; ret = EINVAL; if (memcmp(nt_response->data, expected_response.data, 24) == 0) { ret = 0; } return ret; } int ntlm_verify_lm_response(struct ntlm_buffer *lm_response, struct ntlm_key *lm_key, bool ext_sec, uint8_t server_chal[8], uint8_t client_chal[8]) { uint8_t buf[24]; struct ntlm_buffer expected_response = { buf, 24 }; int ret; ret = ntlm_compute_lm_response(lm_key, ext_sec, server_chal, client_chal, &expected_response); if (ret) return ret; ret = EINVAL; if (memcmp(lm_response->data, expected_response.data, 24) == 0) { ret = 0; } return ret; } int ntlmv2_verify_nt_response(struct ntlm_buffer *nt_response, struct ntlm_key *ntlmv2_key, uint8_t server_chal[8]) { union wire_ntlm_response *nt_resp = NULL; struct ntlm_buffer key = { ntlmv2_key->data, ntlmv2_key->length }; uint8_t proof[16]; struct ntlm_buffer nt_proof = { proof, 16 }; struct ntlm_buffer payload; int ret; if (nt_response->length < 24) return EINVAL; nt_resp = (union wire_ntlm_response *)nt_response->data; payload.length = nt_response->length - sizeof(nt_resp->v2.resp) + 8; payload.data = malloc(payload.length); if (!payload.data) return ENOMEM; memcpy(payload.data, server_chal, 8); memcpy(&payload.data[8], nt_resp->v2.cli_chal, payload.length - 8); ret = HMAC_MD5(&key, &payload, &nt_proof); if (ret) goto done; ret = EINVAL; if (memcmp(nt_resp->v2.resp, proof, 16) == 0) { ret = 0; } done: safefree(payload.data); return ret; } int ntlmv2_verify_lm_response(struct ntlm_buffer *lm_response, struct ntlm_key *ntlmv2_key, uint8_t server_chal[8]) { struct ntlm_buffer key = { ntlmv2_key->data, ntlmv2_key->length }; union wire_ntlm_response *lm_resp = NULL; uint8_t payload_buf[16]; struct ntlm_buffer payload = { payload_buf, 16 }; uint8_t proof[16]; struct ntlm_buffer lm_proof = { proof, 16 }; int ret; if (lm_response->length != 24) return EINVAL; /* now caluclate the LM Proof */ lm_resp = (union wire_ntlm_response *)lm_response->data; memcpy(payload.data, server_chal, 8); memcpy(&payload.data[8], lm_resp->v2.cli_chal, 8); ret = HMAC_MD5(&key, &payload, &lm_proof); if (ret) return ret; if (memcmp(lm_resp->v2.resp, proof, 16) == 0) return 0; return EINVAL; } static int ntlmv2_sign(struct ntlm_key *sign_key, uint32_t seq_num, struct ntlm_rc4_handle *handle, bool keyex, struct ntlm_buffer *message, struct ntlm_buffer *signature) { struct ntlm_buffer key = { sign_key->data, sign_key->length }; union wire_msg_signature *msg_sig; uint32_t le_seq; uint8_t le8seq[8]; struct ntlm_buffer seq = { le8seq, 4 }; struct ntlm_buffer *data[2]; struct ntlm_iov iov; uint8_t hmac_sig[NTLM_SIGNATURE_SIZE]; struct ntlm_buffer hmac = { hmac_sig, NTLM_SIGNATURE_SIZE }; struct ntlm_buffer rc4buf; struct ntlm_buffer rc4res; int ret; msg_sig = (union wire_msg_signature *)signature->data; if (signature->length != NTLM_SIGNATURE_SIZE) { return EINVAL; } le_seq = htole32(seq_num); memcpy(seq.data, &le_seq, 4); data[0] = &seq; data[1] = message; iov.data = data; iov.num = 2; ret = HMAC_MD5_IOV(&key, &iov, &hmac); if (ret) return ret; /* put version */ msg_sig->v2.version = htole32(NTLMSSP_MESSAGE_SIGNATURE_VERSION); /* put actual MAC */ if (keyex) { /* encrypt truncated hmac */ rc4buf.data = hmac.data; rc4buf.length = 8; /* and put it in the middle of the output signature */ rc4res.data = (uint8_t *)&msg_sig->v2.checksum; rc4res.length = 8; ret = RC4_UPDATE(handle, &rc4buf, &rc4res); if (ret) return ret; } else { memcpy(&msg_sig->v2.checksum, hmac.data, 8); } /* put used seq_num */ msg_sig->v2.seq_num = le_seq; return 0; } static int ntlmv1_sign(struct ntlm_rc4_handle *handle, uint32_t random_pad, uint32_t seq_num, struct ntlm_buffer *message, struct ntlm_buffer *signature) { union wire_msg_signature *msg_sig; uint32_t rc4buf[3]; struct ntlm_buffer payload; struct ntlm_buffer result; int ret; msg_sig = (union wire_msg_signature *)signature->data; if (signature->length != NTLM_SIGNATURE_SIZE) { return EINVAL; } rc4buf[0] = random_pad; rc4buf[1] = htole32(CRC32(0, message)); rc4buf[2] = htole32(seq_num); payload.data = (uint8_t *)rc4buf; payload.length = 12; result.data = (uint8_t *)&msg_sig->v1.random_pad; result.length = 12; ret = RC4_UPDATE(handle, &payload, &result); if (ret) return ret; msg_sig->v1.version = htole32(NTLMSSP_MESSAGE_SIGNATURE_VERSION); msg_sig->v1.random_pad = 0; return 0; } int ntlm_sign(uint32_t flags, int direction, struct ntlm_signseal_state *state, struct ntlm_buffer *message, struct ntlm_buffer *signature) { struct ntlm_signseal_handle *h; int ret; if (direction == NTLM_SEND || !state->ext_sec) { h = &state->send; } else { h = &state->recv; } if (flags & NTLMSSP_NEGOTIATE_SIGN) { if (state->ext_sec) { if (state->datagram) { ret = ntlm_seal_regen(h); if (ret) return ret; } ret = ntlmv2_sign(&h->sign_key, h->seq_num, h->seal_handle, (flags & NTLMSSP_NEGOTIATE_KEY_EXCH), message, signature); } else { ret = ntlmv1_sign(h->seal_handle, 0, h->seq_num, message, signature); } if (ret) return ret; if (!state->datagram) { h->seq_num++; } return 0; } else if (flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) { uint32_t sig_ver = htole32(NTLMSSP_MESSAGE_SIGNATURE_VERSION); memcpy(signature->data, &sig_ver, 4); memset(&signature->data[4], 0, 12); return 0; } return ENOTSUP; } int ntlm_seal(uint32_t flags, struct ntlm_signseal_state *state, struct ntlm_buffer *message, struct ntlm_buffer *output, struct ntlm_buffer *signature) { struct ntlm_signseal_handle *h; int ret; h = &state->send; if (h->seal_handle == NULL) { return EINVAL; } ret = RC4_UPDATE(h->seal_handle, message, output); if (ret) return ret; if (state->ext_sec) { if (state->datagram) { ret = ntlm_seal_regen(h); if (ret) return ret; } ret = ntlmv2_sign(&h->sign_key, h->seq_num, h->seal_handle, (flags & NTLMSSP_NEGOTIATE_KEY_EXCH), message, signature); } else { ret = ntlmv1_sign(h->seal_handle, 0, h->seq_num, message, signature); } if (ret) return ret; if (!state->datagram) { h->seq_num++; } return 0; } int ntlm_unseal(uint32_t flags, struct ntlm_signseal_state *state, struct ntlm_buffer *message, struct ntlm_buffer *output, struct ntlm_buffer *signature) { struct ntlm_signseal_handle *h; int ret; if (!state->ext_sec) { h = &state->send; } else { h = &state->recv; } if (h->seal_handle == NULL) { return EINVAL; } ret = RC4_UPDATE(h->seal_handle, message, output); if (ret) return ret; if (state->ext_sec) { if (state->datagram) { ret = ntlm_seal_regen(h); if (ret) return ret; } ret = ntlmv2_sign(&h->sign_key, h->seq_num, h->seal_handle, (flags & NTLMSSP_NEGOTIATE_KEY_EXCH), output, signature); } else { ret = ntlmv1_sign(h->seal_handle, 0, h->seq_num, output, signature); } if (ret) return ret; if (!state->datagram) { h->seq_num++; } return 0; } int ntlm_mic(struct ntlm_key *exported_session_key, struct ntlm_buffer *negotiate_message, struct ntlm_buffer *challenge_message, struct ntlm_buffer *authenticate_message, struct ntlm_buffer *mic) { struct ntlm_buffer key = { exported_session_key->data, exported_session_key->length }; struct ntlm_buffer *data[3] = { negotiate_message, challenge_message, authenticate_message }; struct ntlm_iov iov; if (negotiate_message->length == 0) { /* connectionless case */ iov.data = &data[1]; iov.num = 2; } else { iov.data = data; iov.num = 3; } return HMAC_MD5_IOV(&key, &iov, mic); } int ntlm_verify_mic(struct ntlm_key *key, struct ntlm_buffer *negotiate_message, struct ntlm_buffer *challenge_message, struct ntlm_buffer *authenticate_message, struct ntlm_buffer *mic) { uint8_t micbuf[NTLM_SIGNATURE_SIZE]; struct ntlm_buffer check_mic = { micbuf, NTLM_SIGNATURE_SIZE }; struct wire_auth_msg *msg; size_t payload_offs; uint32_t flags; int ret; msg = (struct wire_auth_msg *)authenticate_message->data; payload_offs = offsetof(struct wire_auth_msg, payload); /* flags must be checked as they may push the payload further down */ flags = le32toh(msg->neg_flags); if ((flags & NTLMSSP_NEGOTIATE_VERSION) == 0) { struct wire_version zver = {0}; /* mic is at payload_offs right now, but this offset may * need to be reduced if the sender completely omitted * the version struct, as some older clients do */ if (memcmp(&msg->version, &zver, sizeof(struct wire_version)) != 0) { /* version struct is not all zeros, this indicates the actual * struct was omitted and payload is shifted down */ payload_offs -= sizeof(struct wire_version); } } if (payload_offs + NTLM_SIGNATURE_SIZE > authenticate_message->length) { return EINVAL; } /* payload_offs now points at the MIC buffer, clear it off in order * to be able to calculate the original chcksum */ memset(&authenticate_message->data[payload_offs], 0, NTLM_SIGNATURE_SIZE); ret = ntlm_mic(key, negotiate_message, challenge_message, authenticate_message, &check_mic); if (ret) return ret; if (memcmp(mic->data, check_mic.data, NTLM_SIGNATURE_SIZE) != 0) { return EACCES; } return 0; } int ntlm_hash_channel_bindings(struct ntlm_buffer *unhashed, struct ntlm_buffer *signature) { struct ntlm_buffer input; uint32_t ulen; int ret; /* The channel bindings are calculated according to RFC4121, 4.1.1.2, * with a all initiator and acceptor fields zeroed, so we need 4 zeroed * 32bit fields, and one little endian length field to include in the * MD5 calculation */ input.length = sizeof(uint32_t) * 5 + unhashed->length; input.data = malloc(input.length); if (!input.data) return EINVAL; memset(input.data, 0, sizeof(uint32_t) * 4); ulen = unhashed->length; ulen = htole32(ulen); memcpy(&input.data[sizeof(uint32_t) * 4], &ulen, sizeof(uint32_t)); memcpy(&input.data[sizeof(uint32_t) * 5], unhashed->data, unhashed->length); ret = MD5_HASH(&input, signature); safefree(input.data); return ret; } int ntlm_verify_channel_bindings(struct ntlm_buffer *unhashed, struct ntlm_buffer *signature) { uint8_t cbbuf[16]; struct ntlm_buffer cb = { cbbuf, 16 }; int ret; if (signature->length != 16) return EINVAL; ret = ntlm_hash_channel_bindings(unhashed, &cb); if (ret) return ret; if (memcmp(cb.data, signature->data, 16) != 0) return EACCES; return 0; } gss-ntlmssp-1.3.1/src/winbind.c000066400000000000000000000323001456736161100164030ustar00rootroot00000000000000/* Copyright (C) 2014 GSS-NTLMSSP contributors, see COPYING for License */ #include "config.h" #include #include #include "gss_ntlmssp.h" #include "gss_ntlmssp_winbind.h" #include #ifdef HAVE_PTHREAD #include static pthread_key_t key; static pthread_once_t key_once = PTHREAD_ONCE_INIT; /* note that the destructor is given directly the content of the key in * ptr, not the key itself. */ static void key_destructor(void *ptr) { wbcCtxFree((struct wbcContext *)ptr); } static void key_create(void) { /* we have no way to report errors ... */ (void)pthread_key_create(&key, key_destructor); } static struct wbcContext *winbind_pthread_context(void) { struct wbcContext *ctx; int ret; ret = pthread_once(&key_once, key_create); if (ret != 0) { return NULL; } ctx = pthread_getspecific(key); if (ctx == NULL) { ctx = wbcCtxCreate(); ret = pthread_setspecific(key, ctx); if (ret != 0) { wbcCtxFree(ctx); ctx = NULL; } } return ctx; } /* This variable is used to indicate that the context to be used * is the thread local context. We need a single pointer signaling * the routines need to use a TLS context. We cannot return the actual * TLS context pointer because we have no assurance the caller will * hold the pointer within the same thread. We calso cannot use NULL * because a NULL indicates a failure to allocate the context when * non-tls storage is used (in which case we return an allocated * context) */ static long use_tls_ctx = 0; #endif /* HAVE_PTHREAD */ void *winbind_get_context(void) { #ifdef HAVE_PTHREAD const char *envvar; #ifdef DEFAULT_WB_TLS_CTX int tls_ctx = 1; #else int tls_ctx = 0; #endif /* we return use_tls_ctx if we are told to use per thread * context, the code knows how to cope correctly */ envvar = getenv("GSSNTLMSSP_WB_TLS_CTX"); if (envvar != NULL) { tls_ctx = atoi(envvar); } if (tls_ctx == 1) { return &use_tls_ctx; } #endif /* HAVE_PTHREAD */ return wbcCtxCreate(); } static struct wbcContext *winbind_fetch_context(void *ectx) { #ifdef HAVE_PTHREAD if (ectx == &use_tls_ctx) { return winbind_pthread_context(); } #endif /* HAVE_PTHREAD */ return (struct wbcContext *)ectx; } void winbind_free_context(void *ectx) { #ifdef HAVE_PTHREAD if (ectx == &use_tls_ctx) { return; } #endif /* HAVE_PTHREAD */ return wbcCtxFree((struct wbcContext *)ectx); } uint32_t winbind_get_names(void *ectx, char **computer, char **domain) { struct wbcContext *ctx; struct wbcInterfaceDetails *details = NULL; wbcErr wbc_status; int ret = ERR_NOTAVAIL; ctx = winbind_fetch_context(ectx); if (ctx == NULL) { ret = ERR_BADCTX; goto done; } wbc_status = wbcCtxInterfaceDetails(ctx, &details); if (!WBC_ERROR_IS_OK(wbc_status)) goto done; if (computer && details->netbios_name && (details->netbios_name[0] != 0)) { *computer = strdup(details->netbios_name); if (!*computer) { ret = ENOMEM; goto done; } } if (domain && details->netbios_domain && (details->netbios_domain[0] != 0)) { *domain = strdup(details->netbios_domain); if (!*domain) { ret = ENOMEM; goto done; } } ret = 0; done: if (ret) { if (computer) safefree(*computer); } wbcFreeMemory(details); return ret; } uint32_t winbind_get_creds(void *ectx, struct gssntlm_name *name, struct gssntlm_cred *cred) { struct wbcContext *ctx; struct wbcCredentialCacheParams params; struct wbcCredentialCacheInfo *result; struct wbcInterfaceDetails *details = NULL; wbcErr wbc_status; bool cached = false; int ret = ERR_NOTAVAIL; ctx = winbind_fetch_context(ectx); if (ctx == NULL) { ret = ERR_BADCTX; goto done; } if (name && name->data.user.domain) { params.domain_name = name->data.user.domain; } else { wbc_status = wbcCtxInterfaceDetails(ctx, &details); if (!WBC_ERROR_IS_OK(wbc_status)) goto done; params.domain_name = details->netbios_domain; } if (name && name->data.user.name) { params.account_name = name->data.user.name; } else { params.account_name = getenv("NTLMUSER"); if (!params.account_name) { params.account_name = getenv("USER"); } if (!params.account_name) goto done; } params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP; params.num_blobs = 0; params.blobs = NULL; wbc_status = wbcCtxCredentialCache(ctx, ¶ms, &result, NULL); if (WBC_ERROR_IS_OK(wbc_status)) { /* Yes, winbind seems to think it has credentials for us */ cached = true; } wbcFreeMemory(result); cred->type = GSSNTLM_CRED_EXTERNAL; cred->cred.external.user.type = GSSNTLM_NAME_USER; cred->cred.external.user.data.user.domain = strdup(params.domain_name); if (!cred->cred.external.user.data.user.domain) { ret = ENOMEM; goto done; } cred->cred.external.user.data.user.name = strdup(params.account_name); if (!cred->cred.external.user.data.user.name) { ret = ENOMEM; goto done; } cred->cred.external.creds_in_cache = cached; ret = 0; done: wbcFreeMemory(details); return ret; } uint32_t winbind_cli_auth(void *ectx, char *user, char *domain, gss_channel_bindings_t input_chan_bindings, uint32_t in_flags, uint32_t *neg_flags, struct ntlm_buffer *nego_msg, struct ntlm_buffer *chal_msg, struct ntlm_buffer *auth_msg, struct ntlm_key *exported_session_key) { /* Get responses and session key from winbind */ struct wbcContext *ctx; struct wbcCredentialCacheParams params; struct wbcCredentialCacheInfo *result = NULL; struct wbcNamedBlob *sesskey_blob = NULL; struct wbcNamedBlob *auth_blob = NULL; struct wire_auth_msg *w_auth_msg; struct wire_chal_msg *w_chal_msg; wbcErr wbc_status; int ret; int i; ctx = winbind_fetch_context(ectx); if (ctx == NULL) { ret = ERR_BADCTX; goto done; } if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) { /* Winbind doesn't support this (yet). We'd want to pass our * own client_target_info in with the request. */ ret = ERR_NOTSUPPORTED; goto done; } params.account_name = user; params.domain_name= domain; params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP; params.num_blobs = 0; params.blobs = NULL; wbc_status = wbcAddNamedBlob(¶ms.num_blobs, ¶ms.blobs, "challenge_blob", 0, chal_msg->data, chal_msg->length); if (!WBC_ERROR_IS_OK(wbc_status)) { ret = ENOMEM; goto done; } /* If we've masked out flags in in_flags, don't let * winbind see them in the challenge */ w_chal_msg = (struct wire_chal_msg *)params.blobs[0].blob.data; w_chal_msg->neg_flags = htole32(in_flags); /* Put this in second. * https://bugzilla.samba.org/show_bug.cgi?id=10692 */ if (nego_msg->length) { wbc_status = wbcAddNamedBlob(¶ms.num_blobs, ¶ms.blobs, "initial_blob", 0, nego_msg->data, nego_msg->length); if (!WBC_ERROR_IS_OK(wbc_status)) { ret = ENOMEM; goto done; } } wbc_status = wbcCtxCredentialCache(ctx, ¶ms, &result, NULL); if (!WBC_ERROR_IS_OK(wbc_status)) { ret = ERR_NOTAVAIL; goto done; } for (i = 0; i < result->num_blobs; i++) { if (strcmp(result->blobs[i].name, "auth_blob") == 0) { auth_blob = &result->blobs[i]; } else if (strcmp(result->blobs[i].name, "session_key") == 0) { sesskey_blob = &result->blobs[i]; } } if (!auth_blob || auth_blob->blob.length < sizeof(*auth_msg) || !sesskey_blob || sesskey_blob->blob.length != 16 ) { ret = ERR_KEYLEN; goto done; } /* We need to 'correct' the flags in the auth message that * winbind generates. In datagram mode they do matter. * Winbind leaves out the DATAGRAM and SEAL flags, amongst * others. Thankfully winbind also doesn't support MIC so * we can tamper as much as we like... */ w_auth_msg = (struct wire_auth_msg *)auth_blob->blob.data; *neg_flags |= in_flags; w_auth_msg->neg_flags = htole32(*neg_flags); auth_msg->length = auth_blob->blob.length; auth_msg->data = auth_blob->blob.data; auth_blob->blob.data = NULL; exported_session_key->length = sesskey_blob->blob.length; memcpy(exported_session_key->data, sesskey_blob->blob.data, sesskey_blob->blob.length); ret = 0; done: wbcFreeMemory(params.blobs); wbcFreeMemory(result); return ret; } const char gssntlmssp_sids_urn[] = "urn:gssntlmssp:sids"; static uint32_t format_sids_as_name_attribute( const struct wbcAuthUserInfo *wbc_info, struct gssntlm_name_attribute **auth_attrs) { size_t worst_sids_list_len = WBC_SID_STRING_BUFLEN * wbc_info->num_sids; struct gssntlm_name_attribute *attrs = NULL; char *sids_buf_realloced; char *sids_buf = NULL; char *name = NULL; size_t offset = 0; int ret = EFAULT; /* Allocate buffers */ name = strdup(gssntlmssp_sids_urn); if (name == NULL) { ret = ENOMEM; goto done; } /* 1 for returned attribute +1 for termiator entry */ attrs = calloc(2, sizeof(struct gssntlm_name_attribute)); if (attrs == NULL) { ret = ENOMEM; goto done; } /* sids buffer is allocated with the worst-case size */ sids_buf = malloc(worst_sids_list_len); if (sids_buf == NULL) { ret = ENOMEM; goto done; } /* Construct name attributes string */ for (uint32_t i = 0; i < wbc_info->num_sids; i++) { offset += wbcSidToStringBuf(&wbc_info->sids[i].sid, sids_buf + offset, worst_sids_list_len - offset); if (i < wbc_info->num_sids - 1) { /* Replace EOL by separator for non-last SID */ sids_buf[offset] = ','; } offset++; } /* Usually average SID has ~5 sub_authorities out of 15 possible so * about 60% of worst-case string size is unused. Having 100 SIDs * in ACCESS_TOKEN, the space waste is about 11k out of 18k. * Optimization: to save the space, we shrink sids_buf here */ sids_buf_realloced = realloc(sids_buf, offset); /* If realloc() fails, the original block is left untouched; * it is not freed or moved */ if (sids_buf_realloced) { sids_buf = sids_buf_realloced; } ret = 0; done: if (ret) { free(name); free(attrs); free(sids_buf); return ret; } attrs[0].attr_name = name; attrs[0].attr_value.length = offset; attrs[0].attr_value.value = sids_buf; /* attrs[1] was zeroed by calloc */ *auth_attrs = attrs; return 0; } uint32_t winbind_srv_auth(void *ectx, char *user, char *domain, char *workstation, uint8_t *challenge, struct ntlm_buffer *nt_chal_resp, struct ntlm_buffer *lm_chal_resp, struct ntlm_key *ntlmv2_key, struct gssntlm_name_attribute **auth_attrs) { struct wbcContext *ctx; struct wbcAuthUserParams wbc_params = { 0 }; struct wbcAuthUserInfo *wbc_info = NULL; struct wbcAuthErrorInfo *wbc_err = NULL; uint32_t res; wbcErr wbc_status; if (ntlmv2_key->length != 16) { return ERR_KEYLEN; } ctx = winbind_fetch_context(ectx); if (ctx == NULL) { return ERR_BADCTX; } wbc_params.account_name = user; wbc_params.domain_name = domain; wbc_params.workstation_name = workstation; wbc_params.flags = 0; wbc_params.parameter_control = WBC_MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | WBC_MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT; wbc_params.level = WBC_AUTH_USER_LEVEL_RESPONSE; memcpy(wbc_params.password.response.challenge, challenge, 8); wbc_params.password.response.nt_length = nt_chal_resp->length; wbc_params.password.response.nt_data = nt_chal_resp->data; wbc_params.password.response.lm_length = lm_chal_resp->length; wbc_params.password.response.lm_data = lm_chal_resp->data; wbc_status = wbcCtxAuthenticateUserEx(ctx, &wbc_params, &wbc_info, &wbc_err); if (!WBC_ERROR_IS_OK(wbc_status)) { /* TODO: use wbcErrorString, to save error message */ wbcFreeMemory(wbc_err); return EACCES; } memcpy(ntlmv2_key->data, wbc_info->user_session_key, ntlmv2_key->length); res = format_sids_as_name_attribute(wbc_info, auth_attrs); wbcFreeMemory(wbc_info); return res; } gss-ntlmssp-1.3.1/tests/000077500000000000000000000000001456736161100151625ustar00rootroot00000000000000gss-ntlmssp-1.3.1/tests/env1.sh000077500000000000000000000002301456736161100163650ustar00rootroot00000000000000#!/bin/sh EXAMPLES=$(dirname "$0")/../examples export NTLM_USER_FILE="${EXAMPLES}/test_user_file2.txt" export TEST_USER_NAME="testuser" ./ntlmssptest gss-ntlmssp-1.3.1/tests/env2.sh000077500000000000000000000004511456736161100163730ustar00rootroot00000000000000#!/bin/sh EXAMPLES=$(dirname "$0")/../examples export NTLMSSP_TEST_DEBUG="tests-trace-2.log" export NTLM_USER_FILE="${EXAMPLES}/test_user_file3.txt" export TEST_USER_NAME="TESTDOM\\testuser" ./ntlmssptest if [ ! -f "tests-trace-2.log" ]; then echo "Debug trace file not found!" exit -1 fi gss-ntlmssp-1.3.1/tests/ntlmssptest.c000066400000000000000000003461721456736161100177430ustar00rootroot00000000000000/* Copyright 2013-2022 Simo Sorce , see COPYING for license */ #include #include #include #include #include #include #include #include #include "config.h" #ifndef HOST_NAME_MAX #include #define HOST_NAME_MAX MAXHOSTNAMELEN #endif #include "../src/gssapi_ntlmssp.h" #include "../src/gss_ntlmssp.h" const char *hex_to_dump(const uint8_t *d, size_t s) { static char hex_to_dump_str[1536]; char format[] = " %02x"; size_t t, i, j, k, p; bool print_trail = false; bool next_line = false; if (s > 256) t = 256; else t = s; for (i = 0, p = 0; i < t; i++) { snprintf(&hex_to_dump_str[p], 4, format, d[i]); p += 3; k = (i + 1) % 16; if (i + 1 == t) { print_trail = true; next_line = false; } else if (k == 0) { print_trail = true; next_line = true; } if (print_trail) { for (j = 16 - k + 1; j > 0; j--) { hex_to_dump_str[p++] = ' '; hex_to_dump_str[p++] = ' '; hex_to_dump_str[p++] = ' '; } hex_to_dump_str[p++] = '|'; if (k == 0) k = 16; for (j = 0; j < 16; j++) { if (k > 0) { if (isalnum(d[j])) hex_to_dump_str[p++] = d[j]; else hex_to_dump_str[p++] = '.'; k--; } else hex_to_dump_str[p++] = ' '; } hex_to_dump_str[p++] = '|'; print_trail = false; } if (next_line) { hex_to_dump_str[p++] = '\n'; hex_to_dump_str[p] = '\0'; next_line = false; } } if (t < s) { snprintf(&hex_to_dump_str[p], 7, " [..]\n"); } else if (hex_to_dump_str[p] != '\n') { hex_to_dump_str[p] = '\n'; hex_to_dump_str[p + 1] = '\0'; } return hex_to_dump_str; } static int test_difference(const char *text, const void *expected, size_t expected_len, const void *obtained, size_t obtained_len) { if (expected_len == 0) expected_len = strlen((const char *)expected); if (obtained_len == 0) obtained_len = strlen((const char *)obtained); if ((expected_len != obtained_len) || (memcmp(expected, obtained, expected_len) != 0)) { fprintf(stderr, "%s differ!\n", text); fprintf(stderr, "expected\n%s", hex_to_dump(expected, expected_len)); fprintf(stderr, "obtained\n%s", hex_to_dump(obtained, obtained_len)); return EINVAL; } return 0; } static int test_buffers(const char *text, struct ntlm_buffer *expected, struct ntlm_buffer *obtained) { return test_difference(text, expected->data, expected->length, obtained->data, obtained->length); } static int test_keys(const char *text, struct ntlm_key *expected, struct ntlm_key *obtained) { return test_difference(text, expected->data, expected->length, obtained->data, obtained->length); } /* Test Data as per para 4.2 of MS-NLMP */ const char *T_User = "User"; const char *T_UserDom = "Domain"; const char *T_Passwd = "Password"; const char *T_Server_Name = "Server"; const char *T_Workstation = "COMPUTER"; uint8_t T_RandomSessionKey[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }; uint64_t T_time = 0; uint8_t T_ClientChallenge[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }; uint8_t T_ServerChallenge[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; /* NTLMv1 Auth Test Data */ struct { struct ntlm_key ResponseKeyLM; struct ntlm_key ResponseKeyNT; struct ntlm_key SessionBaseKey; uint8_t LMv1Response[24]; uint8_t NTLMv1Response[24]; struct ntlm_key KeyExchangeKey; struct ntlm_key EncryptedSessionKey1; struct ntlm_key EncryptedSessionKey2; struct ntlm_key EncryptedSessionKey3; uint32_t ChallengeFlags; uint8_t ChallengeMessage[0x44]; uint8_t AuthenticateMessage[0xAC]; } T_NTLMv1 = { { .data = { 0xe5, 0x2c, 0xac, 0x67, 0x41, 0x9a, 0x9a, 0x22, 0x4a, 0x3b, 0x10, 0x8f, 0x3f, 0xa6, 0xcb, 0x6d }, .length = 16 }, { .data = { 0xa4, 0xf4, 0x9c, 0x40, 0x65, 0x10, 0xbd, 0xca, 0xb6, 0x82, 0x4e, 0xe7, 0xc3, 0x0f, 0xd8, 0x52 }, .length = 16 }, { .data = { 0xd8, 0x72, 0x62, 0xb0, 0xcd, 0xe4, 0xb1, 0xcb, 0x74, 0x99, 0xbe, 0xcc, 0xcd, 0xf1, 0x07, 0x84 }, .length = 16 }, { 0x98, 0xde, 0xf7, 0xb8, 0x7f, 0x88, 0xaa, 0x5d, 0xaf, 0xe2, 0xdf, 0x77, 0x96, 0x88, 0xa1, 0x72, 0xde, 0xf1, 0x1c, 0x7d, 0x5c, 0xcd, 0xef, 0x13 }, { 0x67, 0xc4, 0x30, 0x11, 0xf3, 0x02, 0x98, 0xa2, 0xad, 0x35, 0xec, 0xe6, 0x4f, 0x16, 0x33, 0x1c, 0x44, 0xbd, 0xbe, 0xd9, 0x27, 0x84, 0x1f, 0x94 }, { .data = { 0xb0, 0x9e, 0x37, 0x9f, 0x7f, 0xbe, 0xcb, 0x1e, 0xaf, 0x0a, 0xfd, 0xcb, 0x03, 0x83, 0xc8, 0xa0 }, .length = 16 }, { .data = { 0x51, 0x88, 0x22, 0xb1, 0xb3, 0xf3, 0x50, 0xc8, 0x95, 0x86, 0x82, 0xec, 0xbb, 0x3e, 0x3c, 0xb7 }, .length = 16 }, { .data = { 0x74, 0x52, 0xca, 0x55, 0xc2, 0x25, 0xa1, 0xca, 0x04, 0xb4, 0x8f, 0xae, 0x32, 0xcf, 0x56, 0xfc }, .length = 16 }, { .data = { 0x4c, 0xd7, 0xbb, 0x57, 0xd6, 0x97, 0xef, 0x9b, 0x54, 0x9f, 0x02, 0xb8, 0xf9, 0xb3, 0x78, 0x64 }, .length = 16 }, ( NTLMSSP_NEGOTIATE_56 | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_VERSION | NTLMSSP_TARGET_TYPE_SERVER | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_OEM | NTLMSSP_NEGOTIATE_UNICODE ), { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x38, 0x00, 0x00, 0x00, 0x33, 0x82, 0x02, 0xe2, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x70, 0x17, 0x00, 0x00, 0x00, 0x0f, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00 }, { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x84, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x48, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x54, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x35, 0x82, 0x80, 0xe2, 0x05, 0x01, 0x28, 0x0a, 0x00, 0x00, 0x00, 0x0f, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x50, 0x00, 0x55, 0x00, 0x54, 0x00, 0x45, 0x00, 0x52, 0x00, 0x98, 0xde, 0xf7, 0xb8, 0x7f, 0x88, 0xaa, 0x5d, 0xaf, 0xe2, 0xdf, 0x77, 0x96, 0x88, 0xa1, 0x72, 0xde, 0xf1, 0x1c, 0x7d, 0x5c, 0xcd, 0xef, 0x13, 0x67, 0xc4, 0x30, 0x11, 0xf3, 0x02, 0x98, 0xa2, 0xad, 0x35, 0xec, 0xe6, 0x4f, 0x16, 0x33, 0x1c, 0x44, 0xbd, 0xbe, 0xd9, 0x27, 0x84, 0x1f, 0x94, 0x51, 0x88, 0x22, 0xb1, 0xb3, 0xf3, 0x50, 0xc8, 0x95, 0x86, 0x82, 0xec, 0xbb, 0x3e, 0x3c, 0xb7 } }; /* NTLMv2 Auth Test Data */ struct { uint32_t ChallengeFlags; uint8_t TargetInfo[36]; struct ntlm_key ResponseKeyNT; struct ntlm_key SessionBaseKey; uint8_t LMv2Response[16]; uint8_t NTLMv2Response[16]; struct ntlm_key EncryptedSessionKey; uint8_t ChallengeMessage[0x68]; uint8_t AuthenticateMessage[0xE8]; } T_NTLMv2 = { ( NTLMSSP_NEGOTIATE_56 | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_VERSION | NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | NTLMSSP_TARGET_TYPE_SERVER | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_OEM | NTLMSSP_NEGOTIATE_UNICODE ), { /* MSV_AV_NB_DOMAIN_NAME, 12 "D.o.m.a.i.n." */ 0x02, 0x00, 0x0c, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, /* MSV_AV_NB_COMPUTER_NAME, 12 "S.e.r.v.e.r." */ 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, /* MSV_AV_EOL, 0 */ 0x00, 0x00, 0x00, 0x00 }, { .data = { 0x0c, 0x86, 0x8a, 0x40, 0x3b, 0xfd, 0x7a, 0x93, 0xa3, 0x00, 0x1e, 0xf2, 0x2e, 0xf0, 0x2e, 0x3f }, .length = 16 }, { .data = { 0x8d, 0xe4, 0x0c, 0xca, 0xdb, 0xc1, 0x4a, 0x82, 0xf1, 0x5c, 0xb0, 0xad, 0x0d, 0xe9, 0x5c, 0xa3 }, .length = 16 }, { 0x86, 0xc3, 0x50, 0x97, 0xac, 0x9c, 0xec, 0x10, 0x25, 0x54, 0x76, 0x4a, 0x57, 0xcc, 0xcc, 0x19 }, { 0x68, 0xcd, 0x0a, 0xb8, 0x51, 0xe5, 0x1c, 0x96, 0xaa, 0xbc, 0x92, 0x7b, 0xeb, 0xef, 0x6a, 0x1c }, { .data = { 0xc5, 0xda, 0xd2, 0x54, 0x4f, 0xc9, 0x79, 0x90, 0x94, 0xce, 0x1c, 0xe9, 0x0b, 0xc9, 0xd0, 0x3e }, .length = 16 }, { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x38, 0x00, 0x00, 0x00, 0x33, 0x82, 0x8a, 0xe2, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x24, 0x00, 0x44, 0x00, 0x00, 0x00, 0x06, 0x00, 0x70, 0x17, 0x00, 0x00, 0x00, 0x0f, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x54, 0x00, 0x54, 0x00, 0x84, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x48, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x54, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x35, 0x82, 0x88, 0xe2, 0x05, 0x01, 0x28, 0x0a, 0x00, 0x00, 0x00, 0x0f, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x50, 0x00, 0x55, 0x00, 0x54, 0x00, 0x45, 0x00, 0x52, 0x00, 0x86, 0xc3, 0x50, 0x97, 0xac, 0x9c, 0xec, 0x10, 0x25, 0x54, 0x76, 0x4a, 0x57, 0xcc, 0xcc, 0x19, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x68, 0xcd, 0x0a, 0xb8, 0x51, 0xe5, 0x1c, 0x96, 0xaa, 0xbc, 0x92, 0x7b, 0xeb, 0xef, 0x6a, 0x1c, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xda, 0xd2, 0x54, 0x4f, 0xc9, 0x79, 0x90, 0x94, 0xce, 0x1c, 0xe9, 0x0b, 0xc9, 0xd0, 0x3e } }; /* NTLMv2 Auth with Channel Bindings Test Data */ struct { uint32_t ChallengeFlags; const char *User; const char *Password; const char *Domain; const char *Workstation; const char *Server; const char *DnsDomain; const char *DnsServer; const char *Forest; uint64_t ServerTime; uint8_t ServerChallenge[8]; struct ntlm_key NTLMHash; uint8_t TargetInfo[0xb6]; uint8_t ChallengeMessage[0xfe]; uint8_t AuthenticateMessage[0x228]; uint8_t MIC[16]; uint8_t CBSum[16]; } T_NTLMv2_CBT = { ( NTLMSSP_NEGOTIATE_56 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_VERSION | NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | NTLMSSP_TARGET_TYPE_DOMAIN | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_UNICODE ), "Administrator", "P@ssw0rd", "WS2008R2", "WIN7-2-PC", "DC-WS2008R2", "ws2008r2.local", "DC-ws2008r2.ws2008r2.local", "ws2008r2.local", 0x01cdde0bc33fe77b, { 0xa2, 0xc5, 0xe8, 0xca, 0x30, 0x84, 0xaa, 0x72 }, { .data = { 0xe1, 0x9c, 0xcf, 0x75, 0xee, 0x54, 0xe0, 0x6b, 0x06, 0xa5, 0x90, 0x7a, 0xf1, 0x3c, 0xef, 0x42 }, .length = 16 }, { 0x02, 0x00, 0x10, 0x00, 0x57, 0x00, 0x53, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x01, 0x00, 0x16, 0x00, 0x44, 0x00, 0x43, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x53, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x03, 0x00, 0x34, 0x00, 0x44, 0x00, 0x43, 0x00, 0x2d, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x7b, 0xe7, 0x3f, 0xc3, 0x0b, 0xde, 0xcd, 0x01, 0x00, 0x00, 0x00, 0x00 }, { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x38, 0x00, 0x00, 0x00, 0x05, 0x82, 0x89, 0xa2, 0xa2, 0xc5, 0xe8, 0xca, 0x30, 0x84, 0xaa, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00, 0xb6, 0x00, 0x48, 0x00, 0x00, 0x00, 0x06, 0x01, 0xb0, 0x1d, 0x00, 0x00, 0x00, 0x0f, 0x57, 0x00, 0x53, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x02, 0x00, 0x10, 0x00, 0x57, 0x00, 0x53, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x01, 0x00, 0x16, 0x00, 0x44, 0x00, 0x43, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x53, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x03, 0x00, 0x34, 0x00, 0x44, 0x00, 0x43, 0x00, 0x2d, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x7b, 0xe7, 0x3f, 0xc3, 0x0b, 0xde, 0xcd, 0x01, 0x00, 0x00, 0x00, 0x00 }, { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x94, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x7c, 0x01, 0xac, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x58, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x68, 0x00, 0x00, 0x00, 0x12, 0x00, 0x12, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x05, 0x82, 0x88, 0xa2, 0x06, 0x01, 0xb0, 0x1d, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x54, 0xa5, 0x42, 0xb0, 0x90, 0xb6, 0x6c, 0x1f, 0xea, 0x1a, 0x2c, 0xc8, 0x2e, 0x93, 0x0b, 0x57, 0x00, 0x53, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x50, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x6a, 0x21, 0xae, 0x1a, 0x44, 0xc0, 0x44, 0x69, 0x3e, 0xee, 0x59, 0xfc, 0x5d, 0x81, 0xe0, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xe7, 0x3f, 0xc3, 0x0b, 0xde, 0xcd, 0x01, 0x27, 0xfc, 0x11, 0x80, 0x82, 0xc2, 0xfb, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x57, 0x00, 0x53, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x01, 0x00, 0x16, 0x00, 0x44, 0x00, 0x43, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x53, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x03, 0x00, 0x34, 0x00, 0x44, 0x00, 0x43, 0x00, 0x2d, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x07, 0x00, 0x08, 0x00, 0x7b, 0xe7, 0x3f, 0xc3, 0x0b, 0xde, 0xcd, 0x01, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x4d, 0x6b, 0x0d, 0x27, 0x54, 0x10, 0x22, 0xf5, 0xff, 0xa6, 0x73, 0xda, 0x2b, 0xfc, 0xfd, 0xf1, 0x94, 0x2f, 0x25, 0x7b, 0xe1, 0x1a, 0x49, 0xc9, 0x54, 0x19, 0x7a, 0xca, 0x8a, 0xaf, 0x2e, 0xaf, 0x0a, 0x00, 0x10, 0x00, 0x65, 0x86, 0xe9, 0x9d, 0x81, 0xc2, 0xfc, 0x98, 0x4e, 0x47, 0x17, 0x2f, 0xd4, 0xdd, 0x03, 0x10, 0x09, 0x00, 0x3e, 0x00, 0x48, 0x00, 0x54, 0x00, 0x54, 0x00, 0x50, 0x00, 0x2f, 0x00, 0x64, 0x00, 0x63, 0x00, 0x2d, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x77, 0x00, 0x73, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x72, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0xf0, 0x54, 0xa5, 0x42, 0xb0, 0x90, 0xb6, 0x6c, 0x1f, 0xea, 0x1a, 0x2c, 0xc8, 0x2e, 0x93, 0x0b }, { 0x65, 0x86, 0xE9, 0x9D, 0x81, 0xC2, 0xFC, 0x98, 0x4E, 0x47, 0x17, 0x2F, 0xD4, 0xDD, 0x03, 0x10 }, }; struct t_gsswrapex_data { uint32_t flags; struct ntlm_buffer Plaintext; struct ntlm_key KeyExchangeKey; struct ntlm_key ClientSealKey; struct ntlm_key ClientSignKey; struct ntlm_buffer Ciphertext; struct ntlm_buffer Signature; }; /* Basic GSS_WrapEx V1 Test Data */ uint8_t T_GSSWRAPv1noESS_Plaintext_data[18] = { 0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x78, 0x00, 0x74, 0x00 }; uint8_t T_GSSWRAPv1noESS_Ciphertext_data[18] = { 0x56, 0xfe, 0x04, 0xd8, 0x61, 0xf9, 0x31, 0x9a, 0xf0, 0xd7, 0x23, 0x8a, 0x2e, 0x3b, 0x4d, 0x45, 0x7f, 0xb8 }; uint8_t T_GSSWRAPv1noESS_Signature_data[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xdc, 0xd1, 0xdf, 0x2e, 0x45, 0x9d, 0x36 }; struct t_gsswrapex_data T_GSSWRAPv1noESS = { ( NTLMSSP_NEGOTIATE_56 | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL ), { .data = T_GSSWRAPv1noESS_Plaintext_data, .length = sizeof(T_GSSWRAPv1noESS_Plaintext_data) }, { .data = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }, .length = 16 }, { .data = { 0 }, .length = 0 }, { .data = { 0 }, .length = 0 }, { .data = T_GSSWRAPv1noESS_Ciphertext_data, .length = sizeof(T_GSSWRAPv1noESS_Ciphertext_data) }, { .data = T_GSSWRAPv1noESS_Signature_data, .length = sizeof(T_GSSWRAPv1noESS_Signature_data) }, }; /* GSS_WrapEx V1 Extended Session Security Test Data */ uint8_t T_GSSWRAPEXv1_Plaintext_data[18] = { 0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x78, 0x00, 0x74, 0x00 }; uint8_t T_GSSWRAPEXv1_Ciphertext_data[18] = { 0xa0, 0x23, 0x72, 0xf6, 0x53, 0x02, 0x73, 0xf3, 0xaa, 0x1e, 0xb9, 0x01, 0x90, 0xce, 0x52, 0x00, 0xc9, 0x9d }; uint8_t T_GSSWRAPEXv1_Signature_data[16] = { 0x01, 0x00, 0x00, 0x00, 0xff, 0x2a, 0xeb, 0x52, 0xf6, 0x81, 0x79, 0x3a, 0x00, 0x00, 0x00, 0x00 }; struct t_gsswrapex_data T_GSSWRAPEXv1 = { ( NTLMSSP_NEGOTIATE_56 | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY ), { .data = T_GSSWRAPEXv1_Plaintext_data, .length = sizeof(T_GSSWRAPEXv1_Plaintext_data) }, { .data = { 0xeb, 0x93, 0x42, 0x9a, 0x8b, 0xd9, 0x52, 0xf8, 0xb8, 0x9c, 0x55, 0xb8, 0x7f, 0x47, 0x5e, 0xdc }, .length = 16 }, { .data = { 0x04, 0xdd, 0x7f, 0x01, 0x4d, 0x85, 0x04, 0xd2, 0x65, 0xa2, 0x5c, 0xc8, 0x6a, 0x3a, 0x7c, 0x06 }, .length = 16 }, { .data = { 0x60, 0xe7, 0x99, 0xbe, 0x5c, 0x72, 0xfc, 0x92, 0x92, 0x2a, 0xe8, 0xeb, 0xe9, 0x61, 0xfb, 0x8d }, .length = 16 }, { .data = T_GSSWRAPEXv1_Ciphertext_data, .length = sizeof(T_GSSWRAPEXv1_Ciphertext_data) }, { .data = T_GSSWRAPEXv1_Signature_data, .length = sizeof(T_GSSWRAPEXv1_Signature_data) }, }; /* GSS_WrapEx V2 Test Data */ uint8_t T_GSSWRAPEXv2_Plaintext_data[18] = { 0x50, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x78, 0x00, 0x74, 0x00 }; uint8_t T_GSSWRAPEXv2_Ciphertext_data[18] = { 0x54, 0xe5, 0x01, 0x65, 0xbf, 0x19, 0x36, 0xdc, 0x99, 0x60, 0x20, 0xc1, 0x81, 0x1b, 0x0f, 0x06, 0xfb, 0x5f }; uint8_t T_GSSWRAPEXv2_Signature_data[16] = { 0x01, 0x00, 0x00, 0x00, 0x7f, 0xb3, 0x8e, 0xc5, 0xc5, 0x5d, 0x49, 0x76, 0x00, 0x00, 0x00, 0x00 }; struct t_gsswrapex_data T_GSSWRAPEXv2 = { ( NTLMSSP_NEGOTIATE_56 | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY ), { .data = T_GSSWRAPEXv2_Plaintext_data, .length = sizeof(T_GSSWRAPEXv2_Plaintext_data) }, { .data = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }, .length = 16 }, { .data = { 0x59, 0xf6, 0x00, 0x97, 0x3c, 0xc4, 0x96, 0x0a, 0x25, 0x48, 0x0a, 0x7c, 0x19, 0x6e, 0x4c, 0x58 }, .length = 16 }, { .data = { 0x47, 0x88, 0xdc, 0x86, 0x1b, 0x47, 0x82, 0xf3, 0x5d, 0x43, 0xfd, 0x98, 0xfe, 0x1a, 0x2d, 0x39 }, .length = 16 }, { .data = T_GSSWRAPEXv2_Ciphertext_data, .length = sizeof(T_GSSWRAPEXv2_Ciphertext_data) }, { .data = T_GSSWRAPEXv2_Signature_data, .length = sizeof(T_GSSWRAPEXv2_Signature_data) }, }; int test_LMOWFv1(struct ntlm_ctx *ctx) { struct ntlm_key result = { .length = 16 }; int ret; ret = LMOWFv1(T_Passwd, &result); if (ret) return ret; return test_keys("results", &T_NTLMv1.ResponseKeyLM, &result); } int test_NTOWFv1(struct ntlm_ctx *ctx) { struct ntlm_key result = { .length = 16 }; int ret; ret = NTOWFv1(T_Passwd, &result); if (ret) return ret; return test_keys("results", &T_NTLMv1.ResponseKeyNT, &result); } int test_SessionBaseKeyV1(struct ntlm_ctx *ctx) { struct ntlm_key session_base_key = { .length = 16 }; int ret; ret = ntlm_session_base_key(&T_NTLMv1.ResponseKeyNT, &session_base_key); if (ret) return ret; return test_keys("results", &T_NTLMv1.SessionBaseKey, &session_base_key); } int test_LMResponseV1(struct ntlm_ctx *ctx) { uint8_t buf[24]; struct ntlm_buffer result = { buf, 24 }; int ret; ret = ntlm_compute_lm_response(&T_NTLMv1.ResponseKeyLM, false, T_ServerChallenge, T_ClientChallenge, &result); if (ret) return ret; return test_difference("results", T_NTLMv1.LMv1Response, sizeof(T_NTLMv1.LMv1Response), result.data, result.length); } int test_NTResponseV1(struct ntlm_ctx *ctx) { uint8_t buf[24]; struct ntlm_buffer result = { buf, 24 }; int ret; ret = ntlm_compute_nt_response(&T_NTLMv1.ResponseKeyNT, false, T_ServerChallenge, T_ClientChallenge, &result); if (ret) return ret; return test_difference("results", T_NTLMv1.NTLMv1Response, sizeof(T_NTLMv1.NTLMv1Response), result.data, result.length); } int test_LM_KeyExchangeKey(struct ntlm_ctx *ctx) { struct ntlm_key result = { .length = 16 }; struct ntlm_buffer lm_response = { .data = T_NTLMv1.LMv1Response, .length = sizeof(T_NTLMv1.LMv1Response) }; int ret; ret = KXKEY(ctx, false, true, false, T_ServerChallenge, &T_NTLMv1.ResponseKeyLM, &T_NTLMv1.SessionBaseKey, &lm_response, &result); if (ret) return ret; return test_keys("results", &T_NTLMv1.KeyExchangeKey, &result); } int test_NTOWFv2(struct ntlm_ctx *ctx) { struct ntlm_key nt_hash = { .length = 16 }; struct ntlm_key result = { .length = 16 }; int ret; ret = NTOWFv1(T_Passwd, &nt_hash); if (ret) return ret; ret = NTOWFv2(ctx, &nt_hash, T_User, T_UserDom, &result); if (ret) return ret; return test_keys("results", &T_NTLMv2.ResponseKeyNT, &result); } int test_LMResponseV2(struct ntlm_ctx *ctx) { struct ntlm_buffer result; int ret; ret = ntlmv2_compute_lm_response(&T_NTLMv2.ResponseKeyNT, T_ServerChallenge, T_ClientChallenge, &result); if (ret) return ret; ret = test_difference("results", T_NTLMv2.LMv2Response, sizeof(T_NTLMv2.LMv2Response), result.data, sizeof(T_NTLMv2.LMv2Response)); free(result.data); return ret; } int test_NTResponseV2(struct ntlm_ctx *ctx) { struct ntlm_buffer target_info = { T_NTLMv2.TargetInfo, 36 }; struct ntlm_buffer result; int ret; ret = ntlmv2_compute_nt_response(&T_NTLMv2.ResponseKeyNT, T_ServerChallenge, T_ClientChallenge, T_time, &target_info, &result); if (ret) return ret; ret = test_difference("results", T_NTLMv2.NTLMv2Response, sizeof(T_NTLMv2.NTLMv2Response), result.data, sizeof(T_NTLMv2.NTLMv2Response)); free(result.data); return ret; } int test_SessionBaseKeyV2(struct ntlm_ctx *ctx) { struct ntlm_buffer nt_response = { T_NTLMv2.NTLMv2Response, 16 }; struct ntlm_key session_base_key = { .length = 16 }; int ret; ret = ntlmv2_session_base_key(&T_NTLMv2.ResponseKeyNT, &nt_response, &session_base_key); if (ret) return ret; return test_keys("results", &T_NTLMv2.SessionBaseKey, &session_base_key); } int test_EncryptedSessionKey(struct ntlm_ctx *ctx, struct ntlm_key *key_exchange_key, struct ntlm_key *encrypted_session_key) { struct ntlm_key exported_session_key = { .length = 16 }; struct ntlm_key encrypted_random_session_key = { .length = 16 }; int ret; memcpy(exported_session_key.data, T_RandomSessionKey, 16); ret = ntlm_encrypted_session_key(key_exchange_key, &exported_session_key, &encrypted_random_session_key); if (ret) return ret; return test_keys("results", encrypted_session_key, &encrypted_random_session_key); } int test_EncryptedSessionKey1(struct ntlm_ctx *ctx) { struct ntlm_buffer lm_response = { T_NTLMv1.LMv1Response, 24 }; struct ntlm_key key_exchnage_key = { .length = 16 }; int ret; ret = KXKEY(ctx, false, false, false, T_ServerChallenge, &T_NTLMv1.ResponseKeyLM, &T_NTLMv1.SessionBaseKey, &lm_response, &key_exchnage_key); if (ret) return ret; return test_EncryptedSessionKey(ctx, &key_exchnage_key, &T_NTLMv1.EncryptedSessionKey1); } int test_EncryptedSessionKey2(struct ntlm_ctx *ctx) { struct ntlm_buffer lm_response = { T_NTLMv1.LMv1Response, 24 }; struct ntlm_key key_exchnage_key = { .length = 16 }; int ret; ret = KXKEY(ctx, false, false, true, T_ServerChallenge, &T_NTLMv1.ResponseKeyLM, &T_NTLMv1.SessionBaseKey, &lm_response, &key_exchnage_key); if (ret) return ret; return test_EncryptedSessionKey(ctx, &key_exchnage_key, &T_NTLMv1.EncryptedSessionKey2); } int test_EncryptedSessionKey3(struct ntlm_ctx *ctx) { struct ntlm_buffer lm_response = { T_NTLMv1.LMv1Response, 24 }; struct ntlm_key key_exchnage_key = { .length = 16 }; int ret; ret = KXKEY(ctx, false, true, false, T_ServerChallenge, &T_NTLMv1.ResponseKeyLM, &T_NTLMv1.SessionBaseKey, &lm_response, &key_exchnage_key); if (ret) return ret; return test_EncryptedSessionKey(ctx, &key_exchnage_key, &T_NTLMv1.EncryptedSessionKey3); } int test_DecodeChallengeMessageV1(struct ntlm_ctx *ctx) { struct ntlm_buffer chal_msg = { T_NTLMv1.ChallengeMessage, 0x68 }; uint32_t type; uint32_t flags; char *target_name = NULL; uint8_t chal[8]; struct ntlm_buffer challenge = { chal, 8 }; int err; int ret; ret = ntlm_decode_msg_type(ctx, &chal_msg, &type); if (ret) return ret; if (type != 2) return EINVAL; ret = ntlm_decode_chal_msg(ctx, &chal_msg, &flags, &target_name, &challenge, NULL); if (ret) return ret; err = test_difference("flags", &T_NTLMv1.ChallengeFlags, 4, &flags, 4); if (err) ret = err; err = test_difference("Target Names", T_Server_Name, 0, target_name, 0); if (err) ret = err; err = test_difference("Challenges", T_ServerChallenge, 8, chal, 8); if (err) ret = err; free(target_name); return ret; } int test_EncodeChallengeMessageV1(struct ntlm_ctx *ctx) { struct ntlm_buffer challenge = { T_ServerChallenge, 8 }; struct ntlm_buffer message = { 0 }; int ret; ret = ntlm_encode_chal_msg(ctx, T_NTLMv1.ChallengeFlags, T_Server_Name, &challenge, NULL, &message); if (ret) return ret; ret = test_difference("Challenge Messages", T_NTLMv1.ChallengeMessage, sizeof(T_NTLMv1.ChallengeMessage), message.data, message.length); free(message.data); return ret; } int test_DecodeChallengeMessageV2(struct ntlm_ctx *ctx) { struct ntlm_buffer chal_msg = { T_NTLMv2.ChallengeMessage, sizeof(T_NTLMv2.ChallengeMessage) }; uint32_t type; uint32_t flags; char *target_name = NULL; uint8_t chal[8]; struct ntlm_buffer challenge = { chal, 8 }; struct ntlm_buffer target_info = { 0 }; int err; int ret; ret = ntlm_decode_msg_type(ctx, &chal_msg, &type); if (ret) return ret; if (type != 2) return EINVAL; ret = ntlm_decode_chal_msg(ctx, &chal_msg, &flags, &target_name, &challenge, &target_info); if (ret) return ret; err = test_difference("flags", &T_NTLMv2.ChallengeFlags, 4, &flags, 4); if (err) ret = err; err = test_difference("Target Names", T_Server_Name, 0, target_name, 0); if (err) ret = err; err = test_difference("Challenges", T_ServerChallenge, 8, chal, 8); if (err) ret = err; err = test_difference("Target Infos", T_NTLMv2.TargetInfo, 36, target_info.data, target_info.length); if (err) ret = err; free(target_name); free(target_info.data); return ret; } int test_EncodeChallengeMessageV2(struct ntlm_ctx *ctx) { struct ntlm_buffer challenge = { T_ServerChallenge, 8 }; struct ntlm_buffer target_info = { T_NTLMv2.TargetInfo, 36 }; struct ntlm_buffer message = { 0 }; int ret; ret = ntlm_encode_chal_msg(ctx, T_NTLMv2.ChallengeFlags, T_Server_Name, &challenge, &target_info, &message); if (ret) return ret; ret = test_difference("Challenge Messages", T_NTLMv2.ChallengeMessage, sizeof(T_NTLMv2.ChallengeMessage), message.data, message.length); free(message.data); return ret; } int test_DecodeAuthenticateMessageV2(struct ntlm_ctx *ctx) { struct ntlm_buffer auth_msg = { T_NTLMv2.AuthenticateMessage, 0xE8 }; uint32_t type; struct ntlm_buffer lm_chalresp = { 0 }; struct ntlm_buffer nt_chalresp = { 0 }; char *dom = NULL; char *usr = NULL; char *wks = NULL; struct ntlm_buffer enc_sess_key = { 0 }; int err; int ret; ret = ntlm_decode_msg_type(ctx, &auth_msg, &type); if (ret) return ret; if (type != 3) return EINVAL; ret = ntlm_decode_auth_msg(ctx, &auth_msg, T_NTLMv2.ChallengeFlags, &lm_chalresp, &nt_chalresp, &dom, &usr, &wks, &enc_sess_key, NULL, NULL); if (ret) return ret; if (lm_chalresp.length != 24) { fprintf(stderr, "Expected a 24 bytes long LM Challenge\n"); fprintf(stderr, "Obtained %s", hex_to_dump(lm_chalresp.data, lm_chalresp.length)); ret = EINVAL; } else { err = test_difference("LM Challenges", T_NTLMv2.LMv2Response, sizeof(T_NTLMv2.LMv2Response), lm_chalresp.data, sizeof(T_NTLMv2.LMv2Response)); if (err) ret = err; } if (nt_chalresp.length != 84) { fprintf(stderr, "Expected a 84 bytes long NT Challenge\n"); fprintf(stderr, "Obtained %s", hex_to_dump(nt_chalresp.data, nt_chalresp.length)); ret = EINVAL; } else { err = test_difference("NT Challenges", T_NTLMv2.NTLMv2Response, sizeof(T_NTLMv2.LMv2Response), nt_chalresp.data, sizeof(T_NTLMv2.LMv2Response)); if (err) ret = err; } err = test_difference("Domain Names", T_UserDom, 0, dom, 0); if (err) ret = err; err = test_difference("User Names", T_User, 0, usr, 0); if (err) ret = err; err = test_difference("Workstation Names", T_Workstation, 0, wks, 0); if (err) ret = err; err = test_difference("EncryptedSessionKey", T_NTLMv2.EncryptedSessionKey.data, T_NTLMv2.EncryptedSessionKey.length, enc_sess_key.data, enc_sess_key.length); if (err) ret = err; free(lm_chalresp.data); free(nt_chalresp.data); free(dom); free(usr); free(wks); free(enc_sess_key.data); return ret; } int test_EncodeAuthenticateMessageV2(struct ntlm_ctx *ctx) { int ret = 0; return ret; } int test_DecodeChallengeMessageV2CBT(struct ntlm_ctx *ctx) { struct ntlm_buffer chal_msg = { T_NTLMv2_CBT.ChallengeMessage, sizeof(T_NTLMv2_CBT.ChallengeMessage) }; uint32_t type; uint32_t flags; char *target_name = NULL; uint8_t chal[8]; struct ntlm_buffer challenge = { chal, 8 }; struct ntlm_buffer target_info = { 0 }; int err; int ret; ret = ntlm_decode_msg_type(ctx, &chal_msg, &type); if (ret) return ret; if (type != 2) return EINVAL; ret = ntlm_decode_chal_msg(ctx, &chal_msg, &flags, &target_name, &challenge, &target_info); if (ret) return ret; err = test_difference("flags", &T_NTLMv2_CBT.ChallengeFlags, 4, &flags, 4); if (err) ret = err; err = test_difference("Target Names", T_NTLMv2_CBT.Domain, 0, target_name, 0); if (err) ret = err; err = test_difference("Challenges", T_NTLMv2_CBT.ServerChallenge, sizeof(T_NTLMv2_CBT.ServerChallenge), chal, 8); if (err) ret = err; err = test_difference("Target Infos", T_NTLMv2_CBT.TargetInfo, sizeof(T_NTLMv2_CBT.TargetInfo), target_info.data, target_info.length); if (err) ret = err; free(target_name); free(target_info.data); return ret; } int test_EncodeChallengeMessageV2CBT(struct ntlm_ctx *ctx) { struct ntlm_buffer challenge = { T_NTLMv2_CBT.ServerChallenge, 8 }; struct ntlm_buffer target_info = { T_NTLMv2_CBT.TargetInfo, sizeof(T_NTLMv2_CBT.TargetInfo) }; struct ntlm_buffer message = { 0 }; int ret; ret = ntlm_encode_chal_msg(ctx, T_NTLMv2_CBT.ChallengeFlags, T_NTLMv2_CBT.Domain, &challenge, &target_info, &message); if (ret) return ret; ret = test_difference("Challenge Messages", T_NTLMv2_CBT.ChallengeMessage, sizeof(T_NTLMv2_CBT.ChallengeMessage), message.data, message.length); free(message.data); return ret; } int test_DecodeAuthenticateMessageV2CBT(struct ntlm_ctx *ctx) { struct ntlm_buffer auth_msg = { T_NTLMv2_CBT.AuthenticateMessage, sizeof(T_NTLMv2_CBT.AuthenticateMessage) }; uint32_t type; struct ntlm_buffer lm_chalresp = { 0 }; struct ntlm_buffer nt_chalresp = { 0 }; char *dom = NULL; char *usr = NULL; char *wks = NULL; struct ntlm_buffer enc_sess_key = { 0 }; uint8_t micdata[16]; struct ntlm_buffer mic = { micdata, 16 }; struct ntlm_key ntlmv2_key = { .length = 16 }; struct ntlm_buffer target_info = { 0 }; struct ntlm_buffer cb = { 0 }; int ret, c; int err; ret = ntlm_decode_msg_type(ctx, &auth_msg, &type); if (ret) return ret; if (type != 3) return EINVAL; ret = ntlm_decode_auth_msg(ctx, &auth_msg, T_NTLMv2_CBT.ChallengeFlags, &lm_chalresp, &nt_chalresp, &dom, &usr, &wks, &enc_sess_key, &target_info, &mic); if (ret) return ret; for (c = 1; lm_chalresp.length > c; c++) { lm_chalresp.data[0] |= lm_chalresp.data[c]; } if ((lm_chalresp.length != 24) || (lm_chalresp.data[0] != 0)) { fprintf(stderr, "LM Challenge too short[%zd] or not all zeros!\n", lm_chalresp.length); ret = EINVAL; } err = test_difference("Domain Names", T_NTLMv2_CBT.Domain, 0, dom, 0); if (err) ret = err; err = test_difference("User Names", T_NTLMv2_CBT.User, 0, usr, 0); if (err) ret = err; err = test_difference("Workstation Names", T_NTLMv2_CBT.Workstation, 0, wks, 0); if (err) ret = err; if (enc_sess_key.length != 0) { fprintf(stderr, "Encrypted Random Session Key not null (%zd)!\n", enc_sess_key.length); ret = EINVAL; } err = test_difference("MIC", T_NTLMv2_CBT.MIC, sizeof(T_NTLMv2_CBT.MIC), mic.data, mic.length); if (err) ret = err; ret = NTOWFv2(ctx, &T_NTLMv2_CBT.NTLMHash, T_NTLMv2_CBT.User, T_NTLMv2_CBT.Domain, &ntlmv2_key); if (ret) { fprintf(stderr, "NTLMv2 key generation failed!\n"); goto done; } ret = ntlmv2_verify_nt_response(&nt_chalresp, &ntlmv2_key, T_NTLMv2_CBT.ServerChallenge); if (ret) { fprintf(stderr, "NTLMv2 Verification failed!\n"); goto done; } ret = ntlm_decode_target_info(ctx, &target_info, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cb); if (ret) { fprintf(stderr, "NTLMv2 failed to decode target info!\n"); goto done; } err = test_difference("CBTs", T_NTLMv2_CBT.CBSum, sizeof(T_NTLMv2_CBT.CBSum), cb.data, cb.length); if (err) ret = err; done: free(lm_chalresp.data); free(nt_chalresp.data); free(dom); free(usr); free(wks); free(enc_sess_key.data); free(target_info.data); return ret; } int test_GSS_Wrap_EX(struct ntlm_ctx *ctx, struct t_gsswrapex_data *data) { struct ntlm_signseal_state state; uint8_t outbuf[data->Ciphertext.length]; uint8_t signbuf[16]; struct ntlm_buffer output = { outbuf, data->Ciphertext.length }; struct ntlm_buffer signature = { signbuf, 16 }; int ret; int err; ret = ntlm_signseal_keys(data->flags, true, &data->KeyExchangeKey, &state); if (ret) goto done; if (data->ClientSealKey.length) { err = test_keys("Client Sealing Keys", &data->ClientSealKey, &state.send.seal_key); if (err) ret = err; } if (data->ClientSignKey.length) { err = test_keys("Client Signing Keys", &data->ClientSignKey, &state.send.sign_key); if (err) ret = err; } if (ret) goto done; ret = ntlm_seal(data->flags, &state, &data->Plaintext, &output, &signature); if (ret) { fprintf(stderr, "Sealing failed\n"); goto done; } err = test_buffers("Ciphertext", &data->Ciphertext, &output); if (err) ret = err; err = test_buffers("Signature", &data->Signature, &signature); if (err) ret = err; done: ntlm_release_rc4_state(&state); return ret; } #define TEST_USER_FILE ABS_SRC_DIR"/examples/test_user_file.txt" long seed = 0; static size_t repeatable_rand(uint8_t *buf, size_t max) { char *env_seed; size_t len; int i; if (seed == 0) { env_seed = getenv("NTLMSSPTEST_SEED"); if (env_seed) { seed = strtol(env_seed, NULL, 0); } else { seed = time(NULL); fprintf(stdout, "repeatable_rand seed = %ld\n", seed); } srandom(seed); } len = random() % max; if (len < 5) len = 5; for (i = 0; i < len; i++) { buf[i] = random(); } return len; } #define CASE(X) case X: return #X const char *gss_maj_to_str(uint32_t err) { switch (err) { CASE(GSS_S_COMPLETE); /* caling errors */ CASE(GSS_S_CALL_INACCESSIBLE_READ); CASE(GSS_S_CALL_INACCESSIBLE_WRITE); CASE(GSS_S_CALL_BAD_STRUCTURE); /* routine errors */ CASE(GSS_S_BAD_MECH); CASE(GSS_S_BAD_NAME); CASE(GSS_S_BAD_NAMETYPE); CASE(GSS_S_BAD_BINDINGS); CASE(GSS_S_BAD_STATUS); CASE(GSS_S_BAD_SIG); CASE(GSS_S_NO_CRED); CASE(GSS_S_NO_CONTEXT); CASE(GSS_S_DEFECTIVE_TOKEN); CASE(GSS_S_CREDENTIALS_EXPIRED); CASE(GSS_S_CONTEXT_EXPIRED); CASE(GSS_S_BAD_QOP); CASE(GSS_S_UNAUTHORIZED); CASE(GSS_S_UNAVAILABLE); CASE(GSS_S_DUPLICATE_ELEMENT); CASE(GSS_S_NAME_NOT_MN); CASE(GSS_S_BAD_MECH_ATTR); /* supplementary info */ CASE(GSS_S_CONTINUE_NEEDED); CASE(GSS_S_DUPLICATE_TOKEN); CASE(GSS_S_OLD_TOKEN); CASE(GSS_S_UNSEQ_TOKEN); CASE(GSS_S_GAP_TOKEN); default: return "Unknown Error"; } } static void print_min_status(uint32_t err) { gss_buffer_desc buf; uint32_t msgctx = 0; uint32_t retmaj; uint32_t retmin; do { retmaj = gssntlm_display_status(&retmin, err, GSS_C_MECH_CODE, NULL, &msgctx, &buf); if (retmaj) { fprintf(stderr, "!!gssntlm_display_status failed for err=%d", err); msgctx = 0; } else { fprintf(stderr, "%.*s%.*s", (int)buf.length, (char *)buf.value, msgctx, " "); (void)gss_release_buffer(&retmin, &buf); } } while (msgctx); } int test_Errors(void) { int i; for (i = ERR_BASE; i < ERR_LAST; i++) { fprintf(stderr, "%x: ", i); print_min_status(i); fprintf(stderr, "\n"); } return 0; } static void print_gss_error(const char *text, uint32_t maj, uint32_t min) { fprintf(stderr, "%s Major Error: [%s] Minor Error: [", text, gss_maj_to_str(maj)); print_min_status(min); fprintf(stderr, "]\n"); fflush(stderr); } int test_gssapi_1(bool user_env_file, bool use_cb, bool no_seal, bool use_cs) { gss_ctx_id_t cli_ctx = GSS_C_NO_CONTEXT; gss_ctx_id_t srv_ctx = GSS_C_NO_CONTEXT; gss_buffer_desc cli_token = { 0 }; gss_buffer_desc srv_token = { 0 }; gss_cred_id_t cli_cred = GSS_C_NO_CREDENTIAL; gss_cred_id_t srv_cred = GSS_C_NO_CREDENTIAL; const char *username; const char *password = "testpassword"; const char *srvname = "test@testserver"; gss_name_t gss_username = NULL; gss_name_t gss_srvname = NULL; gss_buffer_desc pwbuf; gss_buffer_desc nbuf; uint32_t retmin, retmaj; const char *msg = "Sample, payload checking, message."; gss_buffer_desc message = { strlen(msg), discard_const(msg) }; gss_buffer_desc ctx_token; gss_OID actual_mech = GSS_C_NO_OID; uint8_t rand_cb[128]; struct gss_channel_bindings_struct cbts = { 0 }; gss_channel_bindings_t cbt = GSS_C_NO_CHANNEL_BINDINGS; gss_buffer_set_t data_set = NULL; gss_OID_desc sasl_ssf_oid = { 11, discard_const("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0f") }; gss_key_value_element_desc cs_el; gss_key_value_set_desc cs; gss_const_key_value_set_t cred_store = GSS_C_NO_CRED_STORE; uint32_t ssf, expect_ssf; uint32_t req_flags; int conf_state; int ret; if (use_cs) { /* always use the default test file and name in this mode for now */ cs_el.key = GSS_NTLMSSP_CS_KEYFILE; cs_el.value = TEST_USER_FILE; cs.count = 1; cs.elements = &cs_el; cred_store = &cs; username = NULL; } else { setenv("NTLM_USER_FILE", TEST_USER_FILE, 0); username = getenv("TEST_USER_NAME"); } if (username == NULL) { username = "TESTDOM\\testuser"; } nbuf.value = discard_const(username); nbuf.length = strlen(username); retmaj = gssntlm_import_name(&retmin, &nbuf, GSS_C_NT_USER_NAME, &gss_username); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_import_name(username) failed!", retmaj, retmin); return EINVAL; } if (user_env_file || use_cs) { retmaj = gssntlm_acquire_cred_from(&retmin, NULL, (gss_name_t)gss_username, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, cred_store, &cli_cred, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_acquire_cred(username) failed!", retmaj, retmin); ret = EINVAL; goto done; } } else { pwbuf.value = discard_const(password); pwbuf.length = strlen(password); retmaj = gssntlm_acquire_cred_with_password(&retmin, (gss_name_t)gss_username, (gss_buffer_t)&pwbuf, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &cli_cred, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_acquire_cred_with_password failed!", retmaj, retmin); ret = EINVAL; goto done; } } retmaj = gssntlm_inquire_cred_by_mech(&retmin, cli_cred, GSS_C_NO_OID, NULL, NULL, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_import_cred_by_mech failed!", retmaj, retmin); return EINVAL; } nbuf.value = discard_const(srvname); nbuf.length = strlen(srvname); retmaj = gssntlm_import_name(&retmin, &nbuf, GSS_C_NT_HOSTBASED_SERVICE, &gss_srvname); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_import_name(srvname) failed!", retmaj, retmin); return EINVAL; } retmaj = gssntlm_acquire_cred_from(&retmin, NULL, (gss_name_t)gss_srvname, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, cred_store, &srv_cred, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_acquire_cred(srvname) failed!", retmaj, retmin); ret = EINVAL; goto done; } if (use_cb) { /* generate random cb */ cbts.application_data.length = repeatable_rand(rand_cb, 128); cbts.application_data.value = rand_cb; cbt = &cbts; } if (no_seal) { req_flags = GSS_C_INTEG_FLAG; } else { req_flags = GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG; } retmaj = gssntlm_init_sec_context(&retmin, cli_cred, &cli_ctx, gss_srvname, GSS_C_NO_OID, req_flags, 0, cbt, GSS_C_NO_BUFFER, NULL, &cli_token, NULL, NULL); if (retmaj != GSS_S_CONTINUE_NEEDED) { print_gss_error("gssntlm_init_sec_context 1 failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_accept_sec_context(&retmin, &srv_ctx, srv_cred, &cli_token, cbt, NULL, NULL, &srv_token, NULL, NULL, NULL); if (retmaj != GSS_S_CONTINUE_NEEDED) { print_gss_error("gssntlm_accept_sec_context 1 failed!", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &cli_token); /* test importing and exporting context before it is fully estabished */ retmaj = gssntlm_export_sec_context(&retmin, &srv_ctx, &ctx_token); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_export_sec_context 1 failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_import_sec_context(&retmin, &ctx_token, &srv_ctx); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_import_sec_context 1 failed!", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &ctx_token); retmaj = gssntlm_init_sec_context(&retmin, cli_cred, &cli_ctx, gss_srvname, GSS_C_NO_OID, req_flags, 0, cbt, &srv_token, &actual_mech, &cli_token, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_init_sec_context 2 failed!", retmaj, retmin); ret = EINVAL; goto done; } if (!actual_mech) { fprintf(stderr, "Expected actual mech to be returned!\n"); ret = EINVAL; goto done; } actual_mech = GSS_C_NO_OID; gss_release_buffer(&retmin, &srv_token); retmaj = gssntlm_accept_sec_context(&retmin, &srv_ctx, srv_cred, &cli_token, cbt, NULL, &actual_mech, &srv_token, NULL, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_accept_sec_context 2 failed!", retmaj, retmin); ret = EINVAL; goto done; } if (!actual_mech) { fprintf(stderr, "Expected actual mech to be returned!\n"); ret = EINVAL; goto done; } actual_mech = GSS_C_NO_OID; gss_release_buffer(&retmin, &cli_token); gss_release_buffer(&retmin, &srv_token); /* test importing and exporting context after it is fully estabished */ retmaj = gssntlm_export_sec_context(&retmin, &cli_ctx, &ctx_token); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_export_sec_context 2 failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_import_sec_context(&retmin, &ctx_token, &cli_ctx); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_import_sec_context 2 failed!", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &ctx_token); retmaj = gssntlm_get_mic(&retmin, cli_ctx, 0, &message, &cli_token); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_get_mic(cli) failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_verify_mic(&retmin, srv_ctx, &message, &cli_token, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_verify_mic(srv) failed!", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &cli_token); retmaj = gssntlm_get_mic(&retmin, srv_ctx, 0, &message, &srv_token); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_get_mic(srv) failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_verify_mic(&retmin, cli_ctx, &message, &srv_token, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_verify_mic(cli) failed!", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &srv_token); retmaj = gssntlm_wrap(&retmin, cli_ctx, 1, 0, &message, &conf_state, &cli_token); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_wrap(cli) failed!", retmaj, retmin); ret = EINVAL; goto done; } if (conf_state == 0) { fprintf(stderr, "WARN: gssntlm_wrap(cli) gave 0 conf_state!\n"); fflush(stderr); ret = EINVAL; goto done; } retmaj = gssntlm_unwrap(&retmin, srv_ctx, &cli_token, &srv_token, &conf_state, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_unwrap(srv) failed!", retmaj, retmin); ret = EINVAL; goto done; } if (!no_seal && conf_state == 0) { fprintf(stderr, "WARN: gssntlm_wrap(srv) gave 0 conf_state!\n"); fflush(stderr); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &cli_token); gss_release_buffer(&retmin, &srv_token); retmaj = gssntlm_wrap(&retmin, srv_ctx, 1, 0, &message, &conf_state, &srv_token); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_wrap(srv) failed!", retmaj, retmin); ret = EINVAL; goto done; } if (conf_state == 0) { fprintf(stderr, "WARN: gssntlm_wrap(srv) gave 0 conf_state!\n"); fflush(stderr); ret = EINVAL; goto done; } retmaj = gssntlm_unwrap(&retmin, cli_ctx, &srv_token, &cli_token, &conf_state, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_unwrap(cli) failed!", retmaj, retmin); ret = EINVAL; goto done; } if (!no_seal && conf_state == 0) { fprintf(stderr, "WARN: gssntlm_wrap(cli) gave 0 conf_state!\n"); fflush(stderr); ret = EINVAL; goto done; } if (memcmp(message.value, cli_token.value, cli_token.length) != 0) { print_gss_error("sealing and unsealing failed to return the " "same result", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &cli_token); gss_release_buffer(&retmin, &srv_token); gssntlm_release_name(&retmin, &gss_username); gssntlm_release_name(&retmin, &gss_srvname); retmaj = gssntlm_inquire_context(&retmin, srv_ctx, &gss_username, &gss_srvname, NULL, NULL, NULL, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_inquire_context failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_display_name(&retmin, gss_username, &nbuf, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_display_name failed!", retmaj, retmin); ret = EINVAL; goto done; } if (strcmp(nbuf.value, username) != 0) { fprintf(stderr, "Expected username of [%s] but got [%s] instead!\n", username, (char *)nbuf.value); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &nbuf); retmaj = gssntlm_inquire_sec_context_by_oid(&retmin, srv_ctx, &sasl_ssf_oid, &data_set); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_inquire_sec_context_by_oid failed!", retmaj, retmin); ret = EINVAL; goto done; } if (data_set == NULL || data_set->count != 1) { fprintf(stderr, "Expected 1 data_set element\n"); return EINVAL; } ssf = be32toh(*(uint32_t *)data_set->elements[0].value); expect_ssf = 64; if (no_seal) { expect_ssf = 1; } if (ssf != expect_ssf) { fprintf(stderr, "Expected SSF of %u, got: %u\n", (unsigned int)expect_ssf, (unsigned int)ssf); return EINVAL; } ret = 0; done: gssntlm_delete_sec_context(&retmin, &cli_ctx, GSS_C_NO_BUFFER); gssntlm_delete_sec_context(&retmin, &srv_ctx, GSS_C_NO_BUFFER); gssntlm_release_name(&retmin, &gss_username); gssntlm_release_name(&retmin, &gss_srvname); gssntlm_release_cred(&retmin, &cli_cred); gssntlm_release_cred(&retmin, &srv_cred); gss_release_buffer(&retmin, &cli_token); gss_release_buffer(&retmin, &srv_token); gss_release_buffer_set(&retmin, &data_set); return ret; } int inner_setup_channel(gss_cred_id_t cli_cred, gss_ctx_id_t *cli_ctx, gss_cred_id_t srv_cred, gss_ctx_id_t *srv_ctx, gss_name_t gss_srvname, int *step) { gss_buffer_desc cli_token = { 0 }; gss_buffer_desc srv_token = { 0 }; uint32_t retmin, retmaj; uint32_t req_flags = 0; int ret; *step = 1; retmaj = gssntlm_init_sec_context(&retmin, cli_cred, cli_ctx, gss_srvname, GSS_C_NO_OID, req_flags, 0, GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER, NULL, &cli_token, NULL, NULL); if (retmaj != GSS_S_CONTINUE_NEEDED) { print_gss_error("gssntlm_init_sec_context 1 failed!", retmaj, retmin); ret = EINVAL; goto done; } *step = 2; retmaj = gssntlm_accept_sec_context(&retmin, srv_ctx, srv_cred, &cli_token, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &srv_token, NULL, NULL, NULL); if (retmaj != GSS_S_CONTINUE_NEEDED) { print_gss_error("gssntlm_accept_sec_context 1 failed!", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &cli_token); *step = 3; retmaj = gssntlm_init_sec_context(&retmin, cli_cred, cli_ctx, gss_srvname, GSS_C_NO_OID, req_flags, 0, GSS_C_NO_CHANNEL_BINDINGS, &srv_token, NULL, &cli_token, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_init_sec_context 2 failed!", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &srv_token); *step = 4; retmaj = gssntlm_accept_sec_context(&retmin, srv_ctx, srv_cred, &cli_token, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &srv_token, NULL, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_accept_sec_context 2 failed!", retmaj, retmin); ret = EINVAL; goto done; } ret = 0; done: gss_release_buffer(&retmin, &cli_token); gss_release_buffer(&retmin, &srv_token); return ret; } int test_gssapi_neg_flags(void) { gss_ctx_id_t cli_ctx = GSS_C_NO_CONTEXT; gss_ctx_id_t srv_ctx = GSS_C_NO_CONTEXT; gss_cred_id_t cli_cred = GSS_C_NO_CREDENTIAL; gss_cred_id_t srv_cred = GSS_C_NO_CREDENTIAL; gss_OID_desc gssntlm_neg_flags_oid = { GSS_NTLMSSP_NEG_FLAGS_OID_LENGTH, discard_const(GSS_NTLMSSP_NEG_FLAGS_OID_STRING) }; const char *username; const char *password = "testpassword"; const char *srvname = "test@testserver"; gss_name_t gss_username = NULL; gss_name_t gss_srvname = NULL; gss_buffer_desc pwbuf; gss_buffer_desc nbuf; gss_buffer_desc value; uint32_t neg_flags; uint32_t retmin, retmaj; int step; int ret; setenv("NTLM_USER_FILE", TEST_USER_FILE, 0); username = getenv("TEST_USER_NAME"); if (username == NULL) { username = "TESTDOM\\testuser"; } nbuf.value = discard_const(username); nbuf.length = strlen(username); retmaj = gssntlm_import_name(&retmin, &nbuf, GSS_C_NT_USER_NAME, &gss_username); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_import_name(username) failed!", retmaj, retmin); return EINVAL; } pwbuf.value = discard_const(password); pwbuf.length = strlen(password); retmaj = gssntlm_acquire_cred_with_password(&retmin, (gss_name_t)gss_username, (gss_buffer_t)&pwbuf, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &cli_cred, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_acquire_cred_with_password failed!", retmaj, retmin); ret = EINVAL; goto done; } nbuf.value = discard_const(srvname); nbuf.length = strlen(srvname); retmaj = gssntlm_import_name(&retmin, &nbuf, GSS_C_NT_HOSTBASED_SERVICE, &gss_srvname); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_import_name(srvname) failed!", retmaj, retmin); return EINVAL; } retmaj = gssntlm_acquire_cred(&retmin, (gss_name_t)gss_srvname, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &srv_cred, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_acquire_cred(srvname) failed!", retmaj, retmin); ret = EINVAL; goto done; } ret = inner_setup_channel(cli_cred, &cli_ctx, srv_cred, &srv_ctx, gss_srvname, &step); if (ret != 0) { goto done; } gssntlm_delete_sec_context(&retmin, &cli_ctx, GSS_C_NO_BUFFER); gssntlm_delete_sec_context(&retmin, &srv_ctx, GSS_C_NO_BUFFER); /* test again with different neg flags */ neg_flags = NTLMSSP_NEGOTIATE_128 \ | NTLMSSP_NEGOTIATE_NTLM \ | NTLMSSP_NEGOTIATE_UNICODE; value.value = &neg_flags; value.length = sizeof(neg_flags); retmaj = gssntlm_set_cred_option(&retmin, &cli_cred, &gssntlm_neg_flags_oid, &value); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_set_cred_option(cli_cred) failed!", retmaj, retmin); ret = EINVAL; goto done; } ret = inner_setup_channel(cli_cred, &cli_ctx, srv_cred, &srv_ctx, gss_srvname, &step); if (ret != 0) { goto done; } fprintf(stderr, "1 "); gssntlm_delete_sec_context(&retmin, &cli_ctx, GSS_C_NO_BUFFER); gssntlm_delete_sec_context(&retmin, &srv_ctx, GSS_C_NO_BUFFER); /* test again with incompatible neg flags */ neg_flags = NTLMSSP_NEGOTIATE_56; value.value = &neg_flags; value.length = sizeof(neg_flags); retmaj = gssntlm_set_cred_option(&retmin, &srv_cred, &gssntlm_neg_flags_oid, &value); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_set_cred_option(srv_cred) failed!", retmaj, retmin); ret = EINVAL; goto done; } ret = inner_setup_channel(cli_cred, &cli_ctx, srv_cred, &srv_ctx, gss_srvname, &step); if (!(ret == 22 && step == 2)) { fprintf(stderr, "Expected Negotiataion failure (%d, %d)\n", ret, step); ret = EINVAL; goto done; } fprintf(stderr, "2 "); gssntlm_delete_sec_context(&retmin, &cli_ctx, GSS_C_NO_BUFFER); gssntlm_delete_sec_context(&retmin, &srv_ctx, GSS_C_NO_BUFFER); /* test again with reset flags */ value.value = NULL; value.length = 0; retmaj = gssntlm_set_cred_option(&retmin, &cli_cred, &gssntlm_neg_flags_oid, &value); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_set_cred_option(cli_cred) failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_set_cred_option(&retmin, &srv_cred, &gssntlm_neg_flags_oid, &value); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_set_cred_option(srv_cred) failed!", retmaj, retmin); ret = EINVAL; goto done; } ret = inner_setup_channel(cli_cred, &cli_ctx, srv_cred, &srv_ctx, gss_srvname, &step); if (ret != 0) { goto done; } fprintf(stderr, "3 "); ret = 0; done: gssntlm_delete_sec_context(&retmin, &cli_ctx, GSS_C_NO_BUFFER); gssntlm_delete_sec_context(&retmin, &srv_ctx, GSS_C_NO_BUFFER); gssntlm_release_name(&retmin, &gss_username); gssntlm_release_name(&retmin, &gss_srvname); gssntlm_release_cred(&retmin, &cli_cred); gssntlm_release_cred(&retmin, &srv_cred); return ret; } int test_gssapi_cl(void) { gss_ctx_id_t cli_ctx = GSS_C_NO_CONTEXT; gss_ctx_id_t srv_ctx = GSS_C_NO_CONTEXT; gss_buffer_desc cli_token = { 0 }; gss_buffer_desc srv_token = { 0 }; gss_cred_id_t cli_cred = GSS_C_NO_CREDENTIAL; gss_cred_id_t srv_cred = GSS_C_NO_CREDENTIAL; const char *username; const char *password = "testpassword"; const char *srvname = "test@testserver"; gss_name_t gss_username = NULL; gss_name_t gss_srvname = NULL; gss_buffer_desc pwbuf; gss_buffer_desc nbuf; gss_OID_desc set_seqnum_oid = { GSS_NTLMSSP_SET_SEQ_NUM_OID_LENGTH, discard_const(GSS_NTLMSSP_SET_SEQ_NUM_OID_STRING) }; gss_buffer_desc set_seqnum_buf; uint32_t app_seq_num; uint32_t retmin, retmaj; const char *msg = "Sample, signature checking, message."; gss_buffer_desc message = { strlen(msg), discard_const(msg) }; int ret; setenv("NTLM_USER_FILE", TEST_USER_FILE, 0); username = getenv("TEST_USER_NAME"); if (username == NULL) { username = "TESTDOM\\testuser"; } nbuf.value = discard_const(username); nbuf.length = strlen(username); retmaj = gssntlm_import_name(&retmin, &nbuf, GSS_C_NT_USER_NAME, &gss_username); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_import_name(username) failed!", retmaj, retmin); return EINVAL; } pwbuf.value = discard_const(password); pwbuf.length = strlen(password); retmaj = gssntlm_acquire_cred_with_password(&retmin, (gss_name_t)gss_username, (gss_buffer_t)&pwbuf, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &cli_cred, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_acquire_cred_with_password failed!", retmaj, retmin); return EINVAL; } nbuf.value = discard_const(srvname); nbuf.length = strlen(srvname); retmaj = gssntlm_import_name(&retmin, &nbuf, GSS_C_NT_HOSTBASED_SERVICE, &gss_srvname); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_import_name(srvname) failed!", retmaj, retmin); return EINVAL; } retmaj = gssntlm_acquire_cred(&retmin, (gss_name_t)gss_srvname, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &srv_cred, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_acquire_cred(srvname) failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_accept_sec_context(&retmin, &srv_ctx, srv_cred, &cli_token, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &srv_token, NULL, NULL, NULL); if (retmaj != GSS_S_CONTINUE_NEEDED) { print_gss_error("gssntlm_accept_sec_context 1 failed!", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &cli_token); retmaj = gssntlm_init_sec_context(&retmin, cli_cred, &cli_ctx, gss_srvname, GSS_C_NO_OID, GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_DATAGRAM_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &srv_token, NULL, &cli_token, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_init_sec_context 1 failed!", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &srv_token); retmaj = gssntlm_accept_sec_context(&retmin, &srv_ctx, srv_cred, &cli_token, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &srv_token, NULL, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_accept_sec_context 2 failed!", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &cli_token); gss_release_buffer(&retmin, &srv_token); /* arbitrary seq number forced on the context */ app_seq_num = 10; set_seqnum_buf.value = &app_seq_num; set_seqnum_buf.length = 4; retmaj = gssntlm_set_sec_context_option(&retmin, (gss_ctx_id_t *)&cli_ctx, &set_seqnum_oid, &set_seqnum_buf); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_set_sec_context_option(cli) failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_set_sec_context_option(&retmin, (gss_ctx_id_t *)&srv_ctx, &set_seqnum_oid, &set_seqnum_buf); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_set_sec_context_option(srv) failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_get_mic(&retmin, cli_ctx, 0, &message, &cli_token); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_get_mic(cli) failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_verify_mic(&retmin, srv_ctx, &message, &cli_token, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_verify_mic(srv) failed!", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &cli_token); retmaj = gssntlm_get_mic(&retmin, srv_ctx, 0, &message, &srv_token); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_get_mic(srv) failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_verify_mic(&retmin, cli_ctx, &message, &srv_token, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_verify_mic(cli) failed!", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &srv_token); retmaj = gssntlm_wrap(&retmin, cli_ctx, 1, 0, &message, NULL, &cli_token); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_wrap(cli) failed!", retmaj, retmin); ret = EINVAL; goto done; } retmaj = gssntlm_unwrap(&retmin, srv_ctx, &cli_token, &srv_token, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_unwrap(srv) failed!", retmaj, retmin); ret = EINVAL; goto done; } if (memcmp(message.value, srv_token.value, srv_token.length) != 0) { print_gss_error("sealing and unsealing failed to return the " "same result", retmaj, retmin); ret = EINVAL; goto done; } gss_release_buffer(&retmin, &cli_token); gss_release_buffer(&retmin, &srv_token); gssntlm_release_name(&retmin, &gss_username); gssntlm_release_name(&retmin, &gss_srvname); ret = 0; done: gssntlm_delete_sec_context(&retmin, &cli_ctx, GSS_C_NO_BUFFER); gssntlm_delete_sec_context(&retmin, &srv_ctx, GSS_C_NO_BUFFER); gssntlm_release_name(&retmin, &gss_username); gssntlm_release_name(&retmin, &gss_srvname); gssntlm_release_cred(&retmin, &cli_cred); gssntlm_release_cred(&retmin, &srv_cred); gss_release_buffer(&retmin, &cli_token); gss_release_buffer(&retmin, &srv_token); return ret; } int test_gssapi_rfc5801(void) { gss_buffer_desc sasl_name = { 8, discard_const("GS2-NTLM") }; gss_buffer_desc mech_name; gss_buffer_desc mech_desc; gss_OID oid; uint32_t retmin, retmaj; retmaj = gssntlm_inquire_mech_for_saslname(&retmin, &sasl_name, &oid); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_inquire_mech_for_saslname() failed!", retmaj, retmin); return EINVAL; } retmaj = gssntlm_inquire_saslname_for_mech(&retmin, oid, &sasl_name, &mech_name, &mech_desc); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_inquire_saslname_for_mech() failed!", retmaj, retmin); return EINVAL; } if (strncmp(sasl_name.value, "GS2-NTLM", 8)) { fprintf(stderr, "Expected 'GS2-NTLM', got: '%.*s'\n", (int)sasl_name.length, (char *)sasl_name.value); return EINVAL; } if (strncmp(mech_name.value, "NTLM", 8)) { fprintf(stderr, "Expected 'NTLM', got: '%.*s'\n", (int)mech_name.length, (char *)mech_name.value); return EINVAL; } if (strncmp(mech_desc.value, "NTLM Mechanism", 8)) { fprintf(stderr, "Expected 'NTLM Mechanism', got: '%.*s'\n", (int)mech_desc.length, (char *)mech_desc.value); return EINVAL; } gss_release_buffer(&retmaj, &sasl_name); gss_release_buffer(&retmaj, &mech_name); gss_release_buffer(&retmaj, &mech_desc); return 0; } int test_gssapi_rfc5587(void) { int ret = 0; gss_OID_set mech_attrs; gss_OID_set known_mech_attrs; uint32_t retmin, retmaj; retmaj = gssntlm_inquire_attrs_for_mech(&retmin, &gssntlm_oid, &mech_attrs, &known_mech_attrs); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_inquire_attrs_for_mech() failed!", retmaj, retmin); ret = EINVAL; goto done; } if (mech_attrs == GSS_C_NULL_OID_SET) { fprintf(stderr, "mech_attrs returned empty\n"); ret = EINVAL; goto done; } if (known_mech_attrs == GSS_C_NULL_OID_SET) { fprintf(stderr, "known_mech_attrs returned empty\n"); ret = EINVAL; goto done; } if (mech_attrs->count != 9) { fprintf(stderr, "expected 9 mech_attr oids, got %lu\n", mech_attrs->count); ret = EINVAL; goto done; } #define CHECK_MA(A, X) \ do { \ int i; \ for (i = 0; i < A->count; i++) { \ if ((A->elements[i].length == X->length) && \ (memcmp(A->elements[i].elements, \ X->elements, X->length) == 0)) break; \ } \ if (i >= A->count) { \ fprintf(stderr, #X " is missing from " #A " set\n"); \ ret = EINVAL; \ goto done; \ } \ } while(0) CHECK_MA(mech_attrs, GSS_C_MA_MECH_CONCRETE); CHECK_MA(mech_attrs, GSS_C_MA_AUTH_INIT); CHECK_MA(mech_attrs, GSS_C_MA_INTEG_PROT); CHECK_MA(mech_attrs, GSS_C_MA_CONF_PROT); CHECK_MA(mech_attrs, GSS_C_MA_MIC); CHECK_MA(mech_attrs, GSS_C_MA_WRAP); CHECK_MA(mech_attrs, GSS_C_MA_OOS_DET); CHECK_MA(mech_attrs, GSS_C_MA_CBINDINGS); CHECK_MA(mech_attrs, GSS_C_MA_CTX_TRANS); if (known_mech_attrs->count != 27) { fprintf(stderr, "expected 27 known_mech_attr oids, got %lu\n", known_mech_attrs->count); ret = EINVAL; goto done; } CHECK_MA(known_mech_attrs, GSS_C_MA_MECH_CONCRETE); CHECK_MA(known_mech_attrs, GSS_C_MA_MECH_PSEUDO); CHECK_MA(known_mech_attrs, GSS_C_MA_MECH_COMPOSITE); CHECK_MA(known_mech_attrs, GSS_C_MA_MECH_NEGO); CHECK_MA(known_mech_attrs, GSS_C_MA_MECH_GLUE); CHECK_MA(known_mech_attrs, GSS_C_MA_NOT_MECH); CHECK_MA(known_mech_attrs, GSS_C_MA_DEPRECATED); CHECK_MA(known_mech_attrs, GSS_C_MA_NOT_DFLT_MECH); CHECK_MA(known_mech_attrs, GSS_C_MA_ITOK_FRAMED); CHECK_MA(known_mech_attrs, GSS_C_MA_AUTH_INIT); CHECK_MA(known_mech_attrs, GSS_C_MA_AUTH_TARG); CHECK_MA(known_mech_attrs, GSS_C_MA_AUTH_INIT_INIT); CHECK_MA(known_mech_attrs, GSS_C_MA_AUTH_TARG_INIT); CHECK_MA(known_mech_attrs, GSS_C_MA_AUTH_INIT_ANON); CHECK_MA(known_mech_attrs, GSS_C_MA_AUTH_TARG_ANON); CHECK_MA(known_mech_attrs, GSS_C_MA_DELEG_CRED); CHECK_MA(known_mech_attrs, GSS_C_MA_INTEG_PROT); CHECK_MA(known_mech_attrs, GSS_C_MA_CONF_PROT); CHECK_MA(known_mech_attrs, GSS_C_MA_MIC); CHECK_MA(known_mech_attrs, GSS_C_MA_WRAP); CHECK_MA(known_mech_attrs, GSS_C_MA_PROT_READY); CHECK_MA(known_mech_attrs, GSS_C_MA_REPLAY_DET); CHECK_MA(known_mech_attrs, GSS_C_MA_OOS_DET); CHECK_MA(known_mech_attrs, GSS_C_MA_CBINDINGS); CHECK_MA(known_mech_attrs, GSS_C_MA_PFS); CHECK_MA(known_mech_attrs, GSS_C_MA_COMPRESS); CHECK_MA(known_mech_attrs, GSS_C_MA_CTX_TRANS); done: gss_release_oid_set(&retmin, &mech_attrs); gss_release_oid_set(&retmin, &known_mech_attrs); return ret; } static size_t random_in_range(size_t min_count, size_t max_count) { return (random() % (max_count - min_count + 1) ) + min_count; } static size_t generate_random_sid_str(char *dst) { /* At least 1, 15 at the most - according to SID format */ size_t sub_auth_count = random_in_range(1, 15); size_t offs = sprintf(dst, "S-1-5"); for (size_t i = 0; i < sub_auth_count; i++) { offs += sprintf(dst + offs, "-%lu", (unsigned long int)random()); } return offs; } static size_t generate_random_sids_list(char *dst, size_t min_count, size_t max_count) { size_t offs = 0; size_t count = random_in_range(min_count, max_count); for (size_t i = 0; i < count; i++) { offs += generate_random_sid_str(dst + offs); if (i < count - 1) { dst[offs] = ','; } offs++; } return offs; } static void generate_random_binary_blob(char *dst, size_t bytes_count) { for (size_t i = 0; i < bytes_count; i++) { dst[i] = (char) (random() % 32); /* use non-printable characters */ } } /* Low-level func with buffer ownership transfer * name - allocated string with the name of the attribute * value - allocated buffer with a value of size 'length' */ static int gssntlm_append_attr(const char *name, void *value, size_t length, struct gssntlm_name *dst) { size_t prev_attrs_count = gssntlm_get_attrs_count(dst->attrs); /* 1 for new attribute +1 for terminator entry */ size_t new_attrs_count = prev_attrs_count + 2; struct gssntlm_name_attribute *attrs; if (!name || !value || !length || !dst) { return ERR_NOARG; } /* Increase buffer - if there was no any attributes before, * realloc is identical to malloc */ attrs = realloc(dst->attrs, new_attrs_count * sizeof(struct gssntlm_name_attribute)); if (attrs == NULL) { return ENOMEM; } dst->attrs = attrs; attrs[prev_attrs_count].attr_name = strdup(name); if (attrs[prev_attrs_count].attr_name == NULL) { return ENOMEM; } attrs[prev_attrs_count].attr_value.value = malloc(length); if (attrs[prev_attrs_count].attr_value.value == NULL) { safefree(attrs[prev_attrs_count].attr_name); return ENOMEM; } memcpy(attrs[prev_attrs_count].attr_value.value, value, length); attrs[prev_attrs_count].attr_value.length = length; /* terminate array */ attrs[prev_attrs_count + 1].attr_name = NULL; return 0; } static int append_sids_list_attr(const char *urn, size_t min_count, size_t max_count, struct gssntlm_name *name, size_t *length) { int ret; /* WBC_SID_STRING_BUFLEN is defined in wbclient.h * we don't want to include here, so we use any value larger than that */ const size_t sid_str_alloc_size = 256; char *str = malloc(max_count * sid_str_alloc_size); if (!str) { return ENOMEM; } *length = generate_random_sids_list(str, min_count, max_count); /* append zero terminated string for ease of string handling */ ret = gssntlm_append_attr(urn, str, strlen(str) + 1, name); free(str); return ret; } static int append_binary_attr(const char *urn, size_t length, struct gssntlm_name *name) { char rndbuf[length]; int ret; generate_random_binary_blob(rndbuf, length); ret = gssntlm_append_attr(urn, rndbuf, length, name); return ret; } int test_gssapi_rfc6680(void) { static const char *urns[] = { "urn:gssntlmssp:sids", "a", "attr1", "attr2", "attr", /* should not match attrN */ "urn:test", "urn:even:more"}; const size_t urns_count = sizeof(urns) / sizeof(*urns); const char *username; gss_buffer_desc nbuf, abuf, vbuf; gss_name_t gss_username = NULL; gss_name_t gss_username_copy = NULL; gss_buffer_set_t aset = NULL; uint32_t retmin, retmaj; int ret = 0; size_t generated_len[urns_count]; memset(generated_len, 0, sizeof(generated_len)); struct gssntlm_cred user_cred = { 0 }; struct gssntlm_cred *imp_cred = NULL; gss_buffer_desc exp_buffer = { 0 }; setenv("NTLM_USER_FILE", TEST_USER_FILE, 0); username = getenv("TEST_USER_NAME"); if (username == NULL) { username = "TESTDOM\\testuser"; } nbuf.value = discard_const(username); nbuf.length = strlen(username); retmaj = gssntlm_import_name(&retmin, &nbuf, GSS_C_NT_USER_NAME, &gss_username); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_import_name(username) failed!", retmaj, retmin); ret = EINVAL; goto done; } /* This test is designed to check memory allocation/relase and copying * for attached attributes in gssntlm_name structure */ for (size_t i = 0; i < urns_count; i++) { /* Copy gss_name before new attribute is attached */ retmaj = gss_duplicate_name(&retmin, gss_username, &gss_username_copy); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gss_duplicate_name(username->" "username_noattr_copy) failed!", retmaj, retmin); ret = EINVAL; goto done; } /* Release original name to make sure we don't have any references * from copy to original instance */ retmaj = gss_release_name(&retmin, &gss_username); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gss_release_name(username) failed!", retmaj, retmin); ret = EINVAL; goto done; } /* Just swap the names */ gss_username = gss_username_copy; gss_username_copy = NULL; /* Test both textual (sids_list) and binary attributes */ if (i % 2 == 0) { static const int MAX_SIDS_IN_LIST_COUNT = 1000; size_t min_count = i + 1; /* Use progressive size of attribute, starting from small * and finishing with maximal possible size */ size_t max_count = min_count + (i * (MAX_SIDS_IN_LIST_COUNT - min_count)) / (urns_count - 1); ret = append_sids_list_attr(urns[i], min_count, max_count, (struct gssntlm_name *)gss_username, &generated_len[i]); } else { /* some prime numbers to make allocation ill-aligned */ generated_len[i] = 1021 + i * 997; ret = append_binary_attr(urns[i], generated_len[i], (struct gssntlm_name *)gss_username); } if (ret) goto done; /* Check gss_get_name_attribute API */ for (size_t j = 0; j < urns_count; j++) { abuf.length = strlen(urns[j]); abuf.value = discard_const(urns[j]); retmaj = gss_get_name_attribute(&retmin, gss_username, &abuf, NULL, NULL, &vbuf, NULL, NULL); /* All attrs up to i-th should be found, the rest - absent */ if ((j <= i) && (retmaj != GSS_S_COMPLETE)) { print_gss_error("gss_get_name_attribute(username) failed!", retmaj, retmin); ret = EINVAL; goto done; } else if (( j > i) && ((retmaj != GSS_S_UNAVAILABLE) || (retmin != ENOENT))) { print_gss_error("gss_get_name_attribute(username) not failed " "properly for undefined attr!", retmaj, retmin); ret = EINVAL; goto done; } if (retmaj == GSS_S_COMPLETE) { /* Check the length of found attrs */ if (vbuf.length != generated_len[j]) { fprintf(stderr, "gss_get_name_attribute() returned " "mismatched attr len for attr=%s\n", urns[j]); ret = EINVAL; goto done; } retmaj = gss_release_buffer(&retmin, &vbuf); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gss_release_buffer(vbuf) failed!", retmaj, retmin); ret = EINVAL; goto done; } } } /* Check gss_inquire_name API */ retmaj = gss_inquire_name(&retmin, gss_username, NULL, NULL, &aset); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gss_inquire_name(username) failed!", retmaj, retmin); ret = EINVAL; goto done; } /* Check amount of returned attributes */ if (aset->count != i + 1) { fprintf(stderr, "gss_inquire_name() returned " "wrong attrs count=%lu for pass #%lu\n", aset->count, i + 1); ret = EINVAL; goto done; } /* Check that all returned buffers can be read properly */ for (size_t k = 0; k < aset->count; k++) { for (size_t offs = 0; offs < aset->elements[k].length; offs++) { char c = ((char*)aset->elements[k].value)[offs]; (void)c; } } /* Basic check for all returned attrs by matching their size */ for (size_t k = 0; k < aset->count; k++) { int attr_found = 0; for (size_t m = 0; m <= i; m++) { size_t urn_m_len = strlen(urns[m]); if (aset->elements[k].length > urn_m_len && strncmp(urns[m], aset->elements[k].value, urn_m_len) == 0 && ((char*)aset->elements[k].value)[urn_m_len] == '=') { if (aset->elements[k].length != urn_m_len + generated_len[m] + 2) { fprintf(stderr, "gss_inquire_name() returned wrong attr len=%lu" "for attr=%s\n", aset->elements[k].length, urns[m]); ret = EINVAL; goto done; } else { attr_found = 1; break; } } } if (!attr_found) { fprintf(stderr, "gss_inquire_name() returned " "unextected attr[%lu]!", k); ret = EINVAL; goto done; } } retmaj = gss_release_buffer_set(&retmin, &aset); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gss_release_buffer_set(aset) failed!", retmaj, retmin); ret = EINVAL; goto done; } } /* test import/export of name with attributes via import/export of * crafted user credentials */ user_cred.type = GSSNTLM_CRED_USER; user_cred.cred.user.user = *(struct gssntlm_name *)gss_username; retmaj = gssntlm_export_cred(&retmin, (gss_cred_id_t)&user_cred, &exp_buffer); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_export_cred() failed!", retmaj, retmin); ret = EINVAL; goto done; } /* and now try to import back */ retmaj = gssntlm_import_cred(&retmin, &exp_buffer, (gss_cred_id_t *)&imp_cred); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_import_cred() failed!", retmaj, retmin); ret = EINVAL; goto done; } /* Check gss_inquire_name API still returns the right number of * attribnutes */ retmaj = gss_inquire_name(&retmin, (gss_name_t)&imp_cred->cred.user.user, NULL, NULL, &aset); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gss_inquire_name(imp_cred->name) failed!", retmaj, retmin); ret = EINVAL; goto done; } /* Check amount of returned attributes */ if (aset->count != urns_count) { fprintf(stderr, "gss_inquire_name() returned " "wrong attrs count=%lu for pass #%lu\n", aset->count, urns_count); ret = EINVAL; goto done; } done: gssntlm_release_name(&retmin, &gss_username); gssntlm_release_name(&retmin, &gss_username_copy); gss_release_buffer(&retmin, &vbuf); gss_release_buffer_set(&retmin, &aset); gss_release_buffer(&retmin, &exp_buffer); gssntlm_release_cred(&retmin, (gss_cred_id_t *)&imp_cred); return ret; } /* test with data from Jordan Borean, the DC apparently has a zero key */ int test_ZERO_LMKEY(struct ntlm_ctx *ctx) { struct ntlm_key lmowf = { .data = {0}, .length = 16 }; struct ntlm_key ntowf = { .length = 16 }; struct ntlm_key sessionkey = { .length = 16 }; struct ntlm_key result = { .length = 16 }; const char *password = "VagrantPass1"; uint8_t serverChallenge[] = { 0x45, 0x56, 0xB5, 0x69, 0xC9, 0x53, 0x6A, 0x31 }; struct ntlm_key MS_SessionKey = { .data = { 0x5F, 0xFA, 0x2B, 0xF7, 0x27, 0xAD, 0xD1, 0x01, 0xC2, 0x6C, 0xF2, 0xE6, 0xC1, 0x13, 0xBD, 0x6D }, .length = 16 }; uint8_t LM_Response[] = { 0x8B, 0xFC, 0xFE, 0xD5, 0xA3, 0x6D, 0x25, 0x13, 0x86, 0xCC, 0x38, 0xDE, 0x78, 0xBA, 0xE1, 0x62, 0x24, 0xC5, 0x2F, 0xD7, 0x35, 0x35, 0x5E, 0x24 }; struct ntlm_buffer lm_response = { .data = LM_Response, .length = 24 }; int ret; ret = NTOWFv1(password, &ntowf); if (ret) return ret; ret = ntlm_session_base_key(&ntowf, &sessionkey); if (ret) return ret; ret = KXKEY(ctx, false, true, false, serverChallenge, &lmowf, &sessionkey, &lm_response, &result); if (ret) return ret; return test_keys("results", &MS_SessionKey, &result); } int test_NTOWF_UTF16(struct ntlm_ctx *ctx) { const char *passwd = "Pass\xF0\x9D\x84\x9E"; struct ntlm_key expected = { .data = { 0x0d, 0x72, 0xdd, 0xde, 0xdd, 0xba, 0xe5, 0xff, 0x24, 0x48, 0x20, 0xe0, 0x72, 0x41, 0x3b, 0x90 }, .length = 16 }; struct ntlm_key result = { .length = 16 }; int ret; ret = NTOWFv1(passwd, &result); if (ret) return ret; return test_keys("results", &expected, &result); } int test_ACQ_NO_NAME(void) { gss_cred_id_t cli_cred = GSS_C_NO_CREDENTIAL; gss_key_value_element_desc cred_file = { .key = GSS_NTLMSSP_CS_KEYFILE, .value = TEST_USER_FILE }; gss_key_value_set_desc cred_store = { .elements = &cred_file, .count = 1 }; uint32_t retmin, retmaj; int ret; retmaj = gssntlm_acquire_cred_from(&retmin, NULL, GSS_C_NO_NAME, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &cred_store, &cli_cred, NULL, NULL); if (retmaj != GSS_S_COMPLETE) { print_gss_error("gssntlm_acquire_cred_from(cred_store) failed!", retmaj, retmin); ret = EINVAL; goto done; } ret = 0; done: gssntlm_release_cred(&retmin, &cli_cred); return ret; } int test_import_name(void) { struct { const char *name; const char *username; const char *domain; uint32_t error; } name_test[] = { { "foo", "foo", NULL, 0 }, { "BAR\\foo", "foo", "BAR", 0 }, { "foo@BAR", "foo", "BAR", 0 }, { "foo\\@bar.baz", "foo@bar.baz", NULL, 0 }, { "foo\\@bar.baz@BAR", "foo@bar.baz", "BAR", 0 }, { "\\foo@bar.baz", "foo@bar.baz", "", 0 }, { "BAR\\foo@bar.baz", "foo@bar.baz", "BAR", 0 }, { "BAR@baz\\foo@bar.baz", "foo@bar.baz", "BAR@baz", 0 }, { "BAR@baz\\@foo@bar.baz", NULL, NULL, GSS_S_FAILURE }, { "BAR\\foo\\@bar.baz", NULL, NULL, GSS_S_FAILURE }, { "foo@bar\\@baz", NULL, NULL, GSS_S_FAILURE }, { NULL, NULL, NULL, 0 } }; int ret = 0; for (int i = 0; name_test[i].name != NULL; i++) { struct gssntlm_name *gss_username = NULL; gss_buffer_desc username; uint32_t retmin, retmaj; bool failed = false; username.value = discard_const(name_test[i].name); username.length = strlen(username.value); retmaj = gssntlm_import_name(&retmin, &username, GSS_C_NT_USER_NAME, (gss_name_t *)&gss_username); if (retmaj != name_test[i].error) { failed = true; } else if (retmaj == GSS_S_COMPLETE) { if ((gss_username->data.user.name == NULL && name_test[i].username != NULL) || (name_test[i].username == NULL && gss_username->data.user.name != NULL) || (gss_username->data.user.name != NULL && name_test[i].username != NULL && strcmp(gss_username->data.user.name, name_test[i].username) != 0)) { failed = true; } if ((gss_username->data.user.domain == NULL && name_test[i].domain != NULL) || (name_test[i].domain == NULL && gss_username->data.user.domain != NULL) || (gss_username->data.user.domain != NULL && name_test[i].domain != NULL && strcmp(gss_username->data.user.domain, name_test[i].domain) != 0)) { failed = true; } } if (failed) { fprintf(stderr, "gssntlm_import_name(%s) failed!\n", name_test[i].name); fprintf(stderr, "Expected: [%s]\\[%s]\n", name_test[i].domain, name_test[i].username); if (gss_username) { fprintf(stderr, "Obtained: [%s]\\[%s]\n", gss_username->data.user.domain, gss_username->data.user.name); } if (retmaj) { print_gss_error("Function returned error.", retmaj, retmin); } else { fprintf(stderr, "Expected error: %d", (int)name_test[i].error); fflush(stderr); } ret++; } gssntlm_release_name(&retmin, (gss_name_t *)&gss_username); } return ret; } int test_hostbased_name(void) { char hostname[HOST_NAME_MAX + 1] = { 0 }; struct { const char *input; const char *name; const char *spn_svc; const char *spn_name; size_t spn_svc_len; } hostbased_test[] = { { "HTTP", hostname, "HTTP/", hostname, 5 }, { "HTTP@foo.bar", "foo.bar", "HTTP/", "foo.bar", 5 }, { "@foo.bar", "foo.bar", NULL, NULL, 0 }, { "@", hostname, NULL, NULL, 0 }, { "", hostname, NULL, NULL, 0 }, { NULL, NULL, NULL, NULL, 0 } }; int ret = 0; /* get hostname to verify results */ ret = gethostname(hostname, HOST_NAME_MAX); if (ret) { fprintf(stderr, "Test: test_hostbased_name failed to get hostname\n"); } for (int i = 0; hostbased_test[i].input != NULL; i++) { struct gssntlm_name *gss_host_name = NULL; gss_buffer_desc host_name; uint32_t retmin, retmaj; bool failed = false; host_name.value = discard_const(hostbased_test[i].input); host_name.length = strlen(host_name.value); retmaj = gssntlm_import_name(&retmin, &host_name, GSS_C_NT_HOSTBASED_SERVICE, (gss_name_t *)&gss_host_name); if (retmaj == GSS_S_COMPLETE) { if ((gss_host_name->type != GSSNTLM_NAME_SERVER) || (strcmp(hostbased_test[i].name, gss_host_name->data.server.name) != 0)) { failed = true; } if (hostbased_test[i].spn_svc_len != 0) { if ((strncmp(hostbased_test[i].spn_svc, gss_host_name->data.server.spn, hostbased_test[i].spn_svc_len) != 0) || (strcmp(hostbased_test[i].spn_name, gss_host_name->data.server.spn + hostbased_test[i].spn_svc_len) != 0)) { failed = true; } } } else { failed = true; } if (failed) { fprintf(stderr, "gssntlm_import_name(%s) failed!\n", hostbased_test[i].input); fprintf(stderr, "Expected: [%s%s]\n", hostbased_test[i].spn_svc, hostbased_test[i].spn_name); if (gss_host_name) { fprintf(stderr, "Obtained: [%s]+[%s]\n", gss_host_name->data.server.spn, gss_host_name->data.server.name); } if (retmaj != GSS_S_COMPLETE) { print_gss_error("Function returned error.", retmaj, retmin); } fflush(stderr); ret++; } gssntlm_release_name(&retmin, (gss_name_t *)&gss_host_name); } return ret; } int test_debug(void) { char *test_env; uint32_t maj, min; test_env = getenv("NTLMSSP_TEST_DEBUG"); if (test_env) { fprintf(stderr, "%s\n", test_env); gss_buffer_desc val = { strlen(test_env), test_env }; maj = gssntlm_mech_invoke(&min, discard_const(&gssntlm_oid), discard_const(&gssntlm_debug_oid), &val); if (maj != GSS_S_COMPLETE) { fprintf(stderr, "%d %d\n", maj, min); return 1; } return 0; } /* enable trace debugging by default in tests */ setenv("GSSNTLMSSP_DEBUG", "tests-trace.log", 0); return 0; } int test_bad_challenge(struct ntlm_ctx *ctx) { struct ntlm_buffer challenge = { T_ServerChallenge, 8 }; struct ntlm_buffer message = { 0 }; struct wire_chal_msg *msg; uint32_t type; uint32_t flags; char *target_name = NULL; uint8_t chal[8]; struct ntlm_buffer rchallenge = { chal, 8 }; int ret; /* check we can decode encode/decode NULL target_name */ flags = T_NTLMv1.ChallengeFlags & ~(NTLMSSP_TARGET_TYPE_SERVER | NTLMSSP_TARGET_TYPE_DOMAIN); flags |= NTLMSSP_NEGOTIATE_UNICODE; ret = ntlm_encode_chal_msg(ctx, flags, NULL, &challenge, NULL, &message); if (ret) return ret; /* Doctor the message to set back NTLMSSP_TARGET_TYPE_SERVER */ msg = (struct wire_chal_msg *)message.data; msg->neg_flags |= NTLMSSP_TARGET_TYPE_SERVER; ret = ntlm_decode_msg_type(ctx, &message, &type); if (ret) return ret; if (type != 2) return EINVAL; ret = ntlm_decode_chal_msg(ctx, &message, &flags, &target_name, &rchallenge, NULL); if (ret) return ret; if (target_name != NULL) { ret = EINVAL; free(target_name); } free(message.data); return ret; } int main(int argc, const char *argv[]) { struct ntlm_ctx *ctx; int gret = 0; int ret; fprintf(stderr, "Test setup debug\n"); ret = test_debug(); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test errors\n"); ret = test_Errors(); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; ret = ntlm_init_ctx(&ctx); if (ret) goto done; fprintf(stderr, "Test LMOWFv1\n"); ret = test_LMOWFv1(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test NTOWFv1\n"); ret = test_NTOWFv1(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test LMResponse v1\n"); ret = test_LMResponseV1(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test NTResponse v1\n"); ret = test_NTResponseV1(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test SessionBaseKey v1\n"); ret = test_SessionBaseKeyV1(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test LM KeyExchangeKey\n"); ret = test_LM_KeyExchangeKey(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test EncryptedSessionKey v1 (1)\n"); ret = test_EncryptedSessionKey1(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test EncryptedSessionKey v1 (2)\n"); ret = test_EncryptedSessionKey2(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test EncryptedSessionKey v1 (3)\n"); ret = test_EncryptedSessionKey3(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; /* override internal version for V1 test vector */ ntlm_internal_set_version(6, 0, 6000, 15); fprintf(stderr, "Test decoding ChallengeMessage v1\n"); ret = test_DecodeChallengeMessageV1(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test encoding ChallengeMessage v1\n"); ret = test_EncodeChallengeMessageV1(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test LMResponse v2\n"); ret = test_LMResponseV2(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test NTResponse v2\n"); ret = test_NTResponseV2(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test SessionBaseKey v2\n"); ret = test_SessionBaseKeyV2(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test EncryptedSessionKey v2\n"); ret = test_EncryptedSessionKey(ctx, &T_NTLMv2.SessionBaseKey, &T_NTLMv2.EncryptedSessionKey); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; /* override internal version for V2 test vector */ ntlm_internal_set_version(6, 0, 6000, 15); fprintf(stderr, "Test decoding ChallengeMessage v2\n"); ret = test_DecodeChallengeMessageV2(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test encoding ChallengeMessage v2\n"); ret = test_EncodeChallengeMessageV2(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test decoding AuthenticateMessage v2\n"); ret = test_DecodeAuthenticateMessageV2(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test encoding AuthenticateMessage v2\n"); ret = test_EncodeAuthenticateMessageV2(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; /* override internal version for CBT test vector */ ntlm_internal_set_version(6, 1, 7600, 15); fprintf(stderr, "Test decoding ChallengeMessage v2 with CBT\n"); ret = test_DecodeChallengeMessageV2CBT(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test encoding ChallengeMessage v2 with CBT\n"); ret = test_EncodeChallengeMessageV2CBT(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test decoding AuthenticateMessage v2 with CBT\n"); ret = test_DecodeAuthenticateMessageV2CBT(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test sealing a Message with No Extended Security\n"); ret = test_GSS_Wrap_EX(ctx, &T_GSSWRAPv1noESS); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test sealing a Message with NTLMv1 Extended Security\n"); ret = test_GSS_Wrap_EX(ctx, &T_GSSWRAPEXv1); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test sealing a Message with NTLMv2 Extended Security\n"); ret = test_GSS_Wrap_EX(ctx, &T_GSSWRAPEXv2); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, " *** Test with NTLMv1 auth\n"); setenv("LM_COMPAT_LEVEL", "0", 1); fprintf(stderr, "Test GSSAPI conversation (user env file)\n"); ret = test_gssapi_1(true, false, false, false); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test GSSAPI conversation (cred store)\n"); ret = test_gssapi_1(true, false, false, true); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test GSSAPI conversation (no SEAL)\n"); ret = test_gssapi_1(true, false, true, false); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test GSSAPI conversation (with password)\n"); ret = test_gssapi_1(false, false, false, false); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test Connectionless exchange\n"); ret = test_gssapi_cl(); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, " *** Again forcing NTLMv2 auth\n"); setenv("LM_COMPAT_LEVEL", "5", 1); fprintf(stderr, "Test GSSAPI conversation (user env file)\n"); ret = test_gssapi_1(true, false, false, false); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test GSSAPI conversation (no SEAL)\n"); ret = test_gssapi_1(true, false, true, false); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test GSSAPI conversation (with password)\n"); ret = test_gssapi_1(false, false, false, false); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test GSSAPI conversation (with CB)\n"); ret = test_gssapi_1(false, true, false, false); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test Connectionless exchange\n"); ret = test_gssapi_cl(); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test RFC5801 SPI\n"); ret = test_gssapi_rfc5801(); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test RFC5587 SPI\n"); ret = test_gssapi_rfc5587(); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test RFC6680 SPI\n"); ret = test_gssapi_rfc6680(); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test ZERO LM_KEY\n"); ret = test_ZERO_LMKEY(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test NTOWF with UTF16\n"); ret = test_NTOWF_UTF16(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test Bad Challenge Message\n"); ret = test_bad_challenge(ctx); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test Acquired cred from with no name\n"); ret = test_ACQ_NO_NAME(); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; fprintf(stderr, "Test importing different name forms\n"); ret = test_import_name(); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret += ret; fprintf(stderr, "Test importing different hostbased name forms\n"); ret = test_hostbased_name(); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret += ret; fprintf(stderr, "Test Negotiate flags variations\n"); ret = test_gssapi_neg_flags(); fprintf(stderr, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); if (ret) gret++; done: ntlm_free_ctx(&ctx); return gret; } gss-ntlmssp-1.3.1/tests/scripts/000077500000000000000000000000001456736161100166515ustar00rootroot00000000000000gss-ntlmssp-1.3.1/tests/scripts/dlopen.sh000077500000000000000000000025301456736161100204710ustar00rootroot00000000000000#!/bin/sh test -n "$TMPDIR" || exit 1 tempdir="$TMPDIR/dlopentest" mkdir -p $tempdir cat >> $tempdir/dlopen.c << _EOF #include #include #include #include /* Simple program to see if dlopen() would succeed. */ int main(int argc, char **argv) { int i; struct stat st; char buf[PATH_MAX]; for (i = 1; i < argc; i++) { if (dlopen(argv[i], RTLD_NOW)) { fprintf(stdout, "dlopen() of \"%s\" succeeded.\n", argv[i]); } else { snprintf(buf, sizeof(buf), "./%s", argv[i]); if ((stat(buf, &st) == 0) && dlopen(buf, RTLD_NOW)) { fprintf(stdout, "dlopen() of \"./%s\" " "succeeded.\n", argv[i]); } else { fprintf(stdout, "dlopen() of \"%s\" failed: " "%s\n", argv[i], dlerror()); return 1; } } } return 0; } _EOF for arg in $@ ; do case "$arg" in "") ;; -I*|-D*|-f*|-m*|-g*|-O*|-W*) cflags="$cflags $arg" ;; -l*|-L*) ldflags="$ldflags $arg" ;; /*) modules="$modules $arg" ;; *) modules="$modules $arg" ;; esac done ${CC:-gcc} $RPM_OPT_FLAGS $CFLAGS -o $tempdir/dlopen $cflags $tempdir/dlopen.c $ldflags -ldl retval=0 for module in $modules ; do case "$module" in "") ;; /*) $tempdir/dlopen "$module" retval=$? ;; *) $tempdir/dlopen ./"$module" retval=$? ;; esac done rm -f $tempdir/dlopen $tempdir/dlopen.c rmdir $tempdir exit $retval gss-ntlmssp-1.3.1/version.m4000066400000000000000000000003301456736161100157430ustar00rootroot00000000000000# Primary version number m4_define([VERSION_NUMBER], [1.3.1]) # If the PRERELEASE_VERSION_NUMBER is set, we'll append # it to the release tag when creating an RPM or SRPM m4_define([PRERELEASE_VERSION_NUMBER], [])