[Pkg-freeipa-devel] [Git][freeipa-team/gss-ntlmssp][upstream] 34 commits: Fix make distcheck

Timo Aaltonen (@tjaalton) gitlab at salsa.debian.org
Thu Feb 16 13:57:32 GMT 2023



Timo Aaltonen pushed to branch upstream at FreeIPA packaging / gss-ntlmssp


Commits:
1867f86e by Simo Sorce at 2021-02-17T17:43:29-05:00
Fix make distcheck

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
c4ca445a by sashan at 2021-02-22T15:16:27-05:00
make HOST_NAME_MAX an alias of MAXHOSTNAMELEN

HOST_NAME_MAX is not available on Solaris/*BSD systems. However we
can use MAXHOSTNAMELEN on those platforms instead.

- - - - -
048b2540 by sashan at 2021-02-22T15:18:07-05:00
let automake to use correct libdir, when building ntlmssptest

We build 32-bit and 64-bit version of gss-ntlmssp. Each version is
configured with its own --libdir location to pickup correct shared
libraries. Command here configures 32-bit verson:

```
./configure --prefix=/usr \
	--mandir=/usr/share/man \
	--bindir=/usr/bin \
	--sbindir=/usr/sbin \
	--libdir=/usr/lib/ \
	--with-wbclient=no \
	--disable-dependency-tracking
```
this is configure comand for 64-bit version:
```
./configure --prefix=/usr \
	--mandir=/usr/share/man \
	--bindir=/usr/bin \
	--sbindir=/usr/sbin \
	--libdir=/usr/lib/amd64 \
	--with-wbclient=no \
	--disable-dependency-tracking
```

However linker fails to pick up correct location of libunistring, when
build 64-bit version.

The most important chancge in this pull request is introduction
of:

```
ntlmssptest_LDFLAGS = \
    -L$(libdir)
```

The other changes are just matter of taste as I want to make check
for libunistring dependency more obvious.

- - - - -
403cfd2d by sashan at 2021-02-23T09:13:09-05:00
check for ucred does not seem to be required

I'm trying to build gss-ntlmssp on Solaris. Autoconf configure script
fails saying ucred.ucred_gid, ucred.ucred_uid members are missing.
Using grep(1) on sources indicates those members/ucred structure is
nowhere used. Removing this check makes code to build on Solaris.

- - - - -
9b6493b1 by sashan at 2021-03-15T15:02:53+00:00
gssntlmssp_la_LDFLAGS also requires libdir to pick up right libraries (64-bit vs 32-bit)

without this diff 64-bit build completes with warning below:
```
libtool: link:  /usr/gcc/10/bin/gcc -shared  -fPIC -DPIC -Wl,-z -Wl,text -Wl,-M -Wl,.libs/gssntlmssp.so.exp -Wl,-h -Wl,gssntlmssp.so -o .libs/gssntlmssp.so  src/.libs/gssntlmssp_la-crypto.o src/.libs/gssntlmssp_la-ntlm_crypto.o src/.libs/gssntlmssp_la-ntlm.o src/.libs/gssntlmssp_la-debug.o src/.libs/gssntlmssp_la-gss_err.o src/.libs/gssntlmssp_la-gss_spi.o src/.libs/gssntlmssp_la-gss_names.o src/.libs/gssntlmssp_la-gss_creds.o src/.libs/gssntlmssp_la-gss_sec_ctx.o src/.libs/gssntlmssp_la-gss_signseal.o src/.libs/gssntlmssp_la-gss_serialize.o src/.libs/gssntlmssp_la-external.o src/.libs/gssntlmssp_la-gss_auth.o src/.libs/gssntlmssp_la-gss_ntlmssp.o   -lkrb5 -lk5crypto -lcom_err -lssl -lcrypto -L/usr/lib -lz -lunistring -lgssapi_krb5  -m64 -O0 -m64 -pthreads -m64 -O0   -pthreads
ld: warning: file /usr/lib/libunistring.a(locale-language.o): wrong ELF class: ELFCLASS32
libtool: link: rm -f .libs/gssntlmssp.so.exp
libtool: link: ( cd ".libs" && rm -f "gssntlmssp.la" && ln -s "../gssntlmssp.la" "gssntlmssp.la" )
make[3]: Leaving directory '/export/home/sashan/userland/components/gss-ntlmssp/build/amd64'
make[2]: Leaving directory '/export/home/sashan/userland/components/gss-ntlmssp/build/amd64'
make[1]: Leaving directory '/export/home/sashan/userland/components/gss-ntlmssp/build/amd64'
/usr/bin/touch /export/home/sashan/userland/components/gss-ntlmssp/build/amd64/.built
```

the resulting plugin fails to load due to missing symbol `u8_conv_to_encoding`,
which is supposed to be provided by `usr/lib/libunistring.a`

- - - - -
9787e63e by simmon at 2021-04-15T09:10:58-04:00
Translated using Weblate (Korean)

Currently translated at 100.0% (30 of 30 strings)

Added translation using Weblate (Korean)

Co-authored-by: simmon <simmon at nplob.com>
Translate-URL: https://translate.fedoraproject.org/projects/gss-ntlmssp/master/ko/
Translation: gss-ntlmssp/main

- - - - -
c0a02166 by Simo Sorce at 2021-05-05T16:10:24-04:00
Add gssspi_mech_invoke method to turn on debugging

This allows to set a file of own chosing as well as turn on and off
debugging as needed.

Mostly thread safe, and applies to all threads at once.
There is a very small window for a race condition where a thread
modifies the debug_fd at the same time another thread is actually trying
to print a debug message.

In order to handle this case without the performance hit of a mutex on
every debug message, we change the implementation to use file
descriptors instead. printing to a closed file descriptor or to a -1
file descriptor returns an error but does not crash the process as it
happens when a file stream is NULL or pointing to freed memory.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
f23b5716 by Simo Sorce at 2021-06-14T08:26:28-04:00
Fix setting INTEG/CONF on ISC call.

When SEAL is negotiated we are supposed to se INTEG and CONF flags, but
instead of using a | to set both a & was used ending up setting nothing.

Fixes #56

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
1cbc124f by Simo Sorce at 2021-08-03T15:53:53-04:00
Handle the case where username is NULL

This is never permissible except with anonymous authentication which we
do not support yet.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
98cd7440 by Simo Sorce at 2022-02-24T14:29:43-05:00
Store the full SPN within a server gssntlm_name

The SPN is used to fill the Traget Name Attribute in the Target Info
array. This means we need to preserver the SPN as passed to us (via
conversion from a GSS Name).

This patch adds an spn field to the server union part of gssntlm_name
structure.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
782837d7 by Simo Sorce at 2022-02-24T14:29:43-05:00
Use the SPN for Target Info

When we encode/decode/process target_info use the new stored SPN.
Also mark the SPN as unverified, because we never know if the calling
code speaks authoritatively, and may be passing an incorrect name.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
89111763 by Simo Sorce at 2022-02-24T14:29:43-05:00
Fix serialization to export also the server spn

This ups both context and credentials export versions as the size and
content of the serilized structires change.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
fde48787 by Simo Sorce at 2022-02-24T15:00:35-05:00
Always include a version field in NTLMSSP packets

It turns out that the version field structure must always be included
in packets, the flag only controls whether the structure is filled or
all zeros.
Add it directly to the wire structures, and change the encoding
functions accordingly.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
a898099d by Simo Sorce at 2022-02-24T15:00:35-05:00
Always set NTLMSSP_NEGOTIATE_VERSION

Setting the NTLMSSP_NEGOTIATE_VERSION flag makes most clients
generate a MIC correctly, while lack of version causes some clients to
omit a required empty version struct on the wire, which in turn causes
MIC checks to fail.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
734e522c by Simo Sorce at 2022-03-15T19:03:08-04:00
Revert the MSVAVFLAGS_UNVERIFIED_SPN flag default

By default SSPI does not set this flag, and setting it causes servers
with restrictive policy to fail authentication. Given no MS client sets
this flag by default, neither should we until there is a clear need.
We trust our calling applications to do the right thing here in any
case just like SSPI trusts their own calling applications.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
ab13d5f3 by Simo Sorce at 2022-04-01T08:59:43-04:00
Add support for loading openssl legacy provider

This is needed to use algorithms like EVP_md4() that
are not loaded by default in OpenSSL 3.0

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
7ddbfb60 by Simo Sorce at 2022-04-01T08:59:43-04:00
Fix distcheck

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
b7812c51 by Omair Majid at 2022-04-01T12:38:51-04:00
Add more OS to CI matrix

- - - - -
80520459 by Simo Sorce at 2022-04-01T14:57:58-04:00
Release version 1.1.0

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
fe6c5fe8 by Simo Sorce at 2022-04-19T20:53:40-04:00
Document environment variables used by gss-ntlmssp


Signed-off-by: Simo Sorce <simo at redhat.com>
- - - - -
4bea27ef by Simo Sorce at 2022-06-23T08:01:55-04:00
Add basic gss_set_cred_option SPI

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
8184bfc2 by Simo Sorce at 2022-06-23T08:01:55-04:00
Add option to set the default NEGOTIATE flags

This requires to explicitly acquire credentials before calling into
the ISC or ASC functions. Then setting the new default flags on the
credntial. It is not elegant to mix credentials and context flags,
but it is the only way to do it in GSSAPI currently, and there is
precedent in other mechanisms like KRB5.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
81494356 by Simo Sorce at 2022-06-23T08:01:55-04:00
Add test to check gss_set_cred_option

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
ea20b62d by Simo Sorce at 2022-08-02T04:50:09-04:00
Allow ntlm_seal even withon NEGOTIATE_SEAL

So according to Issue #77 we have an interop issue if we prevent the use
of gss_wrap when sealing has not been negotiated. On the technical side,
whether we negotiate sealing or not we always create a seal handle with
RC4.

Change behavior to allow applications to still wrap/unwrap data if they
want, even though the negotiation marked sealing as not selected.
The worst thing that can happen is that the peer application does no
like sealed content and bails.

Applications that need to avoid seeling should already just use
gss_get_mic() anyway and they can check the returned GSS flags to see if
sealing was negotiated (returned as GSS_CONF_FLAG), so applications
still have all they need to make their choice and be compatible with
whatever peer they need to speak to.

Thanks to Filip Navara for finding this.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
d7d6d828 by Simo Sorce at 2022-09-30T15:19:58-04:00
Move HMAC code to OpenSSL EVP API

This effectively drops support for OpenSSL 1.0

Signed-off-by: Simo Sorce <simo at samba.org>

- - - - -
4084b586 by Simo Sorce at 2022-12-25T16:10:56-05:00
Fix crash bug when acceptor credentials are NULL

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
a2517e50 by Temuri Doghonadze at 2022-12-29T11:47:14-05:00
Translated using Weblate (Georgian)

Currently translated at 100.0% (30 of 30 strings)

Added translation using Weblate (Georgian)

Co-authored-by: Temuri Doghonadze <temuri.doghonadze at gmail.com>
Translate-URL: https://translate.fedoraproject.org/projects/gss-ntlmssp/master/ka/
Translation: gss-ntlmssp/main

- - - - -
0f4889ab by Simo Sorce at 2023-02-12T10:59:30-05:00
Make gssntlm_import_name_by_mech more robust

This was reported by the GitHub Security Lab team as:
- Memory corruption when importing host-based service names (GHSL-2023-014)

However the only caller that could cause the issue report in a way that
culd lead to memory corruption is the application itself calling
gss_import_name with an invalid name buffer containing embedded zeros.

This is an invalid name for any GSSAPI mechanism and an incorrect use or
incorrect validation on the application side. As such I do not believe
this is a security or DoS issue in the gss-ntlmssp code, but will adopt
the proposed mitigation anyway as it makes the code more robust in the
face of application mistakes.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
97c62c61 by Simo Sorce at 2023-02-12T10:59:30-05:00
Out-of-bounds read in multiple decode functions

These were reported as:
- Out-of-bounds read in ntlm_decode_oem_str (GHSL-2023-019)
- Out-of-bounds read in ntlm_decode_u16l_str_hdr (GHSL-2023-020)
- Out-of-bounds read in ntlm_decode_field (GHSL-2023-021)

These are lall basically the same identical error replicated in 3
separate functions.

Fixes defects GHSL-2023-019, GHSL-2023-020, GHSL-2023-021 found by
the GitHub Security Lab team via oss-fuzz.

A 32-bit integer overflow condition can lead to incorrect checks of
consistency of length of internal buffers. This leads to a DoS
as the service may end up reading from unmapped memory and crashing.

Although most applications will error out before accepting a singe input
buffer of 4GB in lenght this could theoretically happen, and therefore
we fix it.

Fixes CVE-2023-25563

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
c753000e by Simo Sorce at 2023-02-12T11:02:55-05:00
GHSL-2023-013: Memory corruption decoding UTF16

Memory corruption when decoding UTF16 strings (GHSL-2023-013)

Fixes defect GHSL-2023-013 found by the GitHub Security Lab team via
oss-fuzz.

The variable outlen was not initialized and could cause writing a zero
to an arbitrary place in memory if ntlm_str_convert() were to fail,
which would leave outlen uninitialized.

This can lead to a DoS if the write hits unmapped memory or randomly
corrupting a byte in the application memory space.

Make sure to zero out only if ntlm_str_convert() succeeds, but for good
measure also initialize outlen to 0.

Fixes CVE-2023-25564

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
c16100f6 by Simo Sorce at 2023-02-12T11:04:14-05:00
GHSL-2023-012: Incorrect free when decoding target

Incorrect free when decoding target information (GHSL-2023-012)

Fixes defect GHSL-2023-012 found by the GitHub Security Lab team via
oss-fuzz.

The error condition incorrectly assumed the cb and sh buffers would
contain a copy of the data that needed to freed. However that is not the
case.

This will generally cause an assertion when trying to free a pointer
that was never allocated, and potentially memory corruption depending on
the contents fo the target_info buffer.

This may cause a DoS condition.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
025fbb75 by Simo Sorce at 2023-02-12T11:06:10-05:00
GHSL-2023-011: Out-of-bounds read when decoding

Out-of-bounds read when decoding target information (GHSL-2023-011)

Fixes defect GHSL-2023-011 found by the GitHub Security Lab team via
oss-fuzz.

The lenght of the av_pair is not checked properly for two of the
elements. In case the lenght is shorter than requires this may cause an
out-of-bound read that either reads garbage or may cause a crash by
reading unmapped memory.

This can be exploited to crash the service causing a DoS.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
8660fb16 by Simo Sorce at 2023-02-12T11:07:18-05:00
GHSL-2023-010: Memory leak when parsing usernames

Memory leak when parsing usernames (GHSL-2023-010)

Fixes defect GHSL-2023-010 found by the GitHub Security Lab team via
oss-fuzz.

The domain portion may be overridden causing an allocated memory area
the size of the domain name to be lost. This could be used to mount a
DoS by depleeting the server memory.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
3c3077e2 by Simo Sorce at 2023-02-12T11:11:03-05:00
Release version 1.2.0

- - - - -


27 changed files:

- .github/workflows/ccpp.yml
- Makefile.am
- configure.ac
- external/platform.m4
- man/gssntlmssp.8.xml
- po/LINGUAS
- + po/ka.po
- + po/ko.po
- src/crypto.c
- src/debug.c
- src/debug.h
- src/gss_auth.c
- src/gss_creds.c
- src/gss_names.c
- src/gss_ntlmssp.c
- src/gss_ntlmssp.h
- src/gss_sec_ctx.c
- src/gss_serialize.c
- src/gss_spi.c
- src/gssapi_ntlmssp.h
- src/ntlm.c
- src/ntlm_common.h
- src/ntlm_crypto.c
- tests/env1.sh
- tests/env2.sh
- tests/ntlmssptest.c
- version.m4


Changes:

=====================================
.github/workflows/ccpp.yml
=====================================
@@ -7,16 +7,51 @@ jobs:
 
     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 at v2
-    - name: update repositories
-      run: sudo apt-get update
-    - name: install dependency
-      run: sudo apt-get install libkrb5-dev libunistring-dev gettext xsltproc libxml2-utils docbook-xml  docbook-xsl
     - name: autoreconf
       run: autoreconf -fi
     - name: configure
-      run: ./configure --with-wbclient=no
+      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


=====================================
Makefile.am
=====================================
@@ -48,7 +48,10 @@ check_PROGRAMS = \
 gssntlmssp_LTLIBRARIES = \
     gssntlmssp.la
 
-dist_noinst_SCRIPTS = tests/scripts/dlopen.sh
+dist_noinst_SCRIPTS = \
+    tests/scripts/dlopen.sh \
+    tests/env1.sh \
+    tests/env2.sh
 
 dist_noinst_DATA =
 
@@ -70,8 +73,6 @@ AM_CPPFLAGS = \
     -DSYSCONFDIR=\"$(sysconfdir)\" \
     -DLOCALEDIR=\"$(localedir)\"
 
-#EXTRA_DIST = build/config.rpath
-
 GN_MECHGLUE_LIBS = $(GSSAPI_LIBS) $(CRYPTO_LIBS) $(WBC_LIBS)
 
 GN_MECHGLUE_OBJ = \
@@ -118,6 +119,7 @@ gssntlmssp_la_CFLAGS = \
     $(WBC_CFLAGS) \
     $(AM_CFLAGS)
 gssntlmssp_la_LDFLAGS = \
+    -L$(libdir) \
     $(GN_MECHGLUE_LIBS) \
     -export-symbols-regex '^gss(spi|)_' \
     -avoid-version \
@@ -129,10 +131,11 @@ ntlmssptest_SOURCES = \
 ntlmssptest_CFLAGS = \
     $(WBC_CFLAGS) \
     $(AM_CFLAGS)
+ntlmssptest_LDFLAGS = \
+    -L$(libdir)
 ntlmssptest_LDADD = \
-    $(WBC_LIBS) \
-    $(GSSAPI_LIBS) \
-    $(CRYPTO_LIBS)
+    $(GN_MECHGLUE_LIBS) \
+    $(UNISTRING_LIBS)
 
 dist_noinst_DATA += \
     m4
@@ -222,7 +225,15 @@ 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


=====================================
configure.ac
=====================================
@@ -94,6 +94,8 @@ 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"
@@ -136,6 +138,10 @@ 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


=====================================
external/platform.m4
=====================================
@@ -36,13 +36,3 @@ AM_CONDITIONAL([HAVE_SUSE], [test x"$osname" = xsuse])
 AM_CONDITIONAL([HAVE_DEBIAN], [test x"$osname" = xdebian])
 AM_CONDITIONAL([HAVE_GENTOO], [test x"$osname" = xgentoo])
 
-AC_CHECK_MEMBERS([struct ucred.pid, struct ucred.uid, struct ucred.gid], , ,
-                 [[#include <sys/socket.h>]])
-
-if test x"$ac_cv_member_struct_ucred_pid" = xyes -a \
-        x"$ac_cv_member_struct_ucred_uid" = xyes -a \
-        x"$ac_cv_member_struct_ucred_gid" = xyes ; then
-    AC_DEFINE([HAVE_UCRED], [1], [Define if struct ucred is available])
-else
-    AC_MSG_ERROR([struct ucred is not available])
-fi


=====================================
man/gssntlmssp.8.xml
=====================================
@@ -36,6 +36,68 @@
         </para>
     </refsect1>
 
+    <refsect1 id='envvars'>
+        <title>ENVIRONMENT VARIABLES</title>
+        <variablelist>
+       <varlistentry><term>NTLM_USER_FILE</term>
+            <listitem>
+                <para>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.</para>
+            </listitem>
+        </varlistentry>
+        <varlistentry><term>NETBIOS_COMPUTER_NAME</term>
+            <listitem>
+                <para>Set the NETBIOS (short) Computer name used in NTLM
+                packets</para>
+            </listitem>
+        </varlistentry>
+        <varlistentry><term>NETBIOS_DOMAIN_NAME</term>
+            <listitem>
+                <para>Set the NETBIOS (short) Domain name used in NTLM
+                packets</para>
+            </listitem>
+        </varlistentry>
+        <varlistentry><term>NTLMUSER</term>
+            <listitem>
+                <para>Set the NTLM User name used for authentication on
+                initiation of the security context</para>
+            </listitem>
+        </varlistentry>
+        <varlistentry><term>USER</term>
+            <listitem>
+                <para>The default source for the User name used on
+                initiation of the security context</para>
+            </listitem>
+        </varlistentry>
+        <varlistentry><term>LM_COMPAT_LEVEL</term>
+            <listitem>
+                <para>The LM Compatibility Level enforced during security
+                context negotiation. The default level is 3</para>
+            </listitem>
+        </varlistentry>
+        <varlistentry><term>GSSNTLMSSP_DEBUG</term>
+            <listitem>
+                <para>The name of a file to send debug logs</para>
+            </listitem>
+        </varlistentry>
+        <varlistentry><term>GSSNTLMSSP_WB_TLS_CTX</term>
+            <listitem>
+                <para>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</para>
+            </listitem>
+        </varlistentry>
+        </variablelist>
+    </refsect1>
+
     <refsect1 id='see_also'>
         <title>SEE ALSO</title>
         <para>


=====================================
po/LINGUAS
=====================================
@@ -1 +1,3 @@
 en_GB
+ko
+ka


=====================================
po/ka.po
=====================================
@@ -0,0 +1,169 @@
+# GSS-NTLMSSP Translation Template file.
+# Copyright (C) 2015 Simo Sorce <simo at samba.org>
+# This file is distributed under the same license as the gssntlmssp package.
+# Simo Sorce <simo at samba.org>, 2015.
+# Temuri Doghonadze <temuri.doghonadze at gmail.com>, 2022.
+msgid ""
+msgstr ""
+"Project-Id-Version: gssntlmssp 0.5.0\n"
+"Report-Msgid-Bugs-To: simo at 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 <temuri.doghonadze at gmail.com>\n"
+"Language-Team: Georgian <https://translate.fedoraproject.org/projects/"
+"gss-ntlmssp/master/ka/>\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 "ვერ ვიპოვე მომხმარებელი"


=====================================
po/ko.po
=====================================
@@ -0,0 +1,169 @@
+# GSS-NTLMSSP Translation Template file.
+# Copyright (C) 2015 Simo Sorce <simo at samba.org>
+# This file is distributed under the same license as the gssntlmssp package.
+# Simo Sorce <simo at samba.org>, 2015.
+# simmon <simmon at nplob.com>, 2021.
+msgid ""
+msgstr ""
+"Project-Id-Version: gssntlmssp 0.5.0\n"
+"Report-Msgid-Bugs-To: simo at 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 <simmon at nplob.com>\n"
+"Language-Team: Korean <https://translate.fedoraproject.org/projects/"
+"gss-ntlmssp/master/ko/>\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 "사용자를 찾지 못했습니다"


=====================================
src/crypto.c
=====================================
@@ -1,4 +1,4 @@
-/* Copyright 2013 Simo Sorce <simo at samba.org>, see COPYING for license */
+/* Copyright 2013-2022 Simo Sorce <simo at samba.org>, see COPYING for license */
 
 #include <errno.h>
 #include <string.h>
@@ -6,36 +6,15 @@
 #include <openssl/des.h>
 #include <openssl/rc4.h>
 #include <openssl/evp.h>
-#include <openssl/hmac.h>
 #include <openssl/rand.h>
 #include <zlib.h>
 
 #include "crypto.h"
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-HMAC_CTX *HMAC_CTX_new(void)
-{
-    HMAC_CTX *ctx;
-
-    ctx = OPENSSL_malloc(sizeof(HMAC_CTX));
-    if (!ctx) return NULL;
-
-    HMAC_CTX_init(ctx);
-
-    return ctx;
-}
-
-void HMAC_CTX_free(HMAC_CTX *ctx)
-{
-    if (ctx == NULL) return;
-
-    HMAC_CTX_cleanup(ctx);
-    OPENSSL_free(ctx);
-}
-
-#define EVP_MD_CTX_new EVP_MD_CTX_create
-#define EVP_MD_CTX_free EVP_MD_CTX_destroy
-
+/* legacy provider with openssl 3.0 */
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#  include <openssl/provider.h>
+#  include <openssl/crypto.h>
 #endif
 
 int RAND_BUFFER(struct ntlm_buffer *random)
@@ -53,35 +32,42 @@ int HMAC_MD5_IOV(struct ntlm_buffer *key,
                  struct ntlm_iov *iov,
                  struct ntlm_buffer *result)
 {
-    HMAC_CTX *hmac_ctx;
-    unsigned int len;
+    EVP_MD_CTX* ctx = NULL;
+    EVP_PKEY* pkey = NULL;
     size_t i;
     int ret = 0;
 
     if (result->length != 16) return EINVAL;
 
-    hmac_ctx = HMAC_CTX_new();
-    if (!hmac_ctx) {
+    pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key->data, key->length);
+    if (!pkey) {
         ret = ERR_CRYPTO;
         goto done;
     }
 
-    ret = HMAC_Init_ex(hmac_ctx, key->data, key->length, EVP_md5(), NULL);
-    if (ret == 0) {
+    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 = HMAC_Update(hmac_ctx, iov->data[i]->data, iov->data[i]->length);
-        if (ret == 0) {
+        ret = EVP_DigestSignUpdate(ctx, iov->data[i]->data,
+                                   iov->data[i]->length);
+        if (ret != 1) {
             ret = ERR_CRYPTO;
             goto done;
         }
     }
 
-    ret = HMAC_Final(hmac_ctx, result->data, &len);
-    if (ret == 0) {
+    ret = EVP_DigestSignFinal(ctx, result->data, &result->length);
+    if (ret != 1) {
         ret = ERR_CRYPTO;
         goto done;
     }
@@ -89,7 +75,8 @@ int HMAC_MD5_IOV(struct ntlm_buffer *key,
     ret = 0;
 
 done:
-    HMAC_CTX_free(hmac_ctx);
+    EVP_MD_CTX_free(ctx);
+    EVP_PKEY_free(pkey);
     return ret;
 }
 
@@ -104,6 +91,43 @@ int HMAC_MD5(struct ntlm_buffer *key,
     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 ossl3_context_t *init_ossl3_ctx()
+{
+    ossl3_context_t *ctx = OPENSSL_malloc(sizeof(ossl3_context_t));
+    if (!ctx) return NULL;
+
+    ctx->libctx = OSSL_LIB_CTX_new();
+    if (!ctx->libctx) {
+        OPENSSL_free(ctx);
+        return NULL;
+    }
+
+    /* 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");
+    return ctx;
+}
+
+static void free_ossl3_ctx(ossl3_context_t *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)
@@ -149,7 +173,32 @@ done:
 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 = init_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:
+    free_ossl3_ctx(ossl3_ctx);
+    return ret;
+#else
     return mdx_hash(EVP_md4(), payload, result);
+#endif
+
 }
 
 int MD5_HASH(struct ntlm_buffer *payload,


=====================================
src/debug.c
=====================================
@@ -1,33 +1,123 @@
 /* Copyright (C) 2014 GSS-NTLMSSP contributors, see COPYING for license */
 
 #define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
+#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;
-bool gssntlm_debug_enabled = false;
-static FILE *debug_fd = NULL;
+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) {
-        debug_fd = fopen(env, "a");
-        if (debug_fd) gssntlm_debug_enabled = true;
+        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);
-    vfprintf(debug_fd, fmt, ap);
+    vdprintf(gssntlm_debug_fd, fmt, ap);
     va_end(ap);
-    fflush(debug_fd);
+    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);
 }


=====================================
src/debug.h
=====================================
@@ -6,8 +6,9 @@
 #include <stdbool.h>
 #include <time.h>
 
+extern gss_OID_desc gssntlm_debug_oid;
 extern bool gssntlm_debug_initialized;
-extern bool gssntlm_debug_enabled;
+extern int gssntlm_debug_fd;
 
 void gssntlm_debug_init(void);
 void gssntlm_debug_printf(const char *fmt, ...);
@@ -23,7 +24,7 @@ static inline int debug_gss_errors(const char *function,
     if (unlikely(gssntlm_debug_initialized == false)) {
         gssntlm_debug_init();
     }
-    if (unlikely(gssntlm_debug_enabled == true)) {
+    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",
@@ -34,4 +35,6 @@ static inline int debug_gss_errors(const char *function,
 #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_ */


=====================================
src/gss_auth.c
=====================================
@@ -84,7 +84,7 @@ uint32_t gssntlm_cli_auth(uint32_t *minor_status,
 
                 retmin = ntlm_process_target_info(
                                             ctx->ntlm, protect, target_info,
-                                            ctx->target_name.data.server.name,
+                                            ctx->target_name.data.server.spn,
                                             &cb, &client_target_info,
                                             &srv_time, add_mic_ptr);
                 if (retmin) {


=====================================
src/gss_creds.c
=====================================
@@ -716,3 +716,58 @@ uint32_t gssntlm_inquire_cred_by_mech(uint32_t *minor_status,
     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;
+}


=====================================
src/gss_names.c
=====================================
@@ -1,4 +1,4 @@
-/* Copyright 2013 Simo Sorce <simo at samba.org>, see COPYING for license */
+/* Copyright 2013-2022 Simo Sorce <simo at samba.org>, see COPYING for license */
 
 #define _GNU_SOURCE
 
@@ -17,6 +17,11 @@
 
 #include "gss_ntlmssp.h"
 
+#ifndef	HOST_NAME_MAX
+#include <sys/param.h>
+#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)
@@ -131,7 +136,6 @@ static uint32_t parse_user_name(uint32_t *minor_status,
         /* we may have an enterprise name here */
         char strbuf[len + 1];
         char *buf = strbuf;
-        bool domain_handled = false;
 
         /* copy buf to manipulate it */
         memcpy(buf, str, len);
@@ -155,9 +159,6 @@ static uint32_t parse_user_name(uint32_t *minor_status,
         }
 
         if (sep) {
-            /* leading domain, copy if domain name is not empty */
-            domain_handled = true;
-
             /* 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
@@ -175,7 +176,7 @@ static uint32_t parse_user_name(uint32_t *minor_status,
 
         for (at = strchr(buf, '@'); at != NULL; at = strchr(at, '@')) {
             if (*(at - 1) == '\\') {
-                if (domain_handled) {
+                if (*domain) {
                     /* Invalid forms like DOM\foo\@bar or foo at bar\@baz */
                     free(*domain);
                     *domain = NULL;
@@ -184,7 +185,7 @@ static uint32_t parse_user_name(uint32_t *minor_status,
                 }
                 /* remove escape, moving all including terminating '\0' */
                 memmove(at - 1, at, len - (at - buf) + 1);
-            } else if (!domain_handled) {
+            } else if (!*domain) {
                 /* an '@' without escape and no previous
                  * domain was split out.
                  * the rest of the string is the domain */
@@ -261,9 +262,6 @@ uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status,
                                      gss_OID input_name_type,
                                      gss_name_t *output_name)
 {
-    char hostname[HOST_NAME_MAX + 1] = { 0 };
-    char struid[12] = { 0 };
-    uid_t uid;
     struct gssntlm_name *name = NULL;
     uint32_t retmaj;
     uint32_t retmin;
@@ -286,30 +284,74 @@ uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status,
 
     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;
 
-        retmaj = string_split(&retmin, '@',
-                              input_name_buffer->value,
-                              input_name_buffer->length,
-                              NULL, &name->data.server.name);
-        if ((retmaj == GSS_S_COMPLETE) ||
-            (retmaj != GSS_S_UNAVAILABLE)) {
-            goto done;
+        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;
+            }
         }
 
-        /* no seprator, assume only service is provided and try to source
-         * the local host name */
-        retmin = gethostname(hostname, HOST_NAME_MAX);
-        if (retmin) {
-            set_GSSERR(retmin);
-            goto done;
-        }
-        hostname[HOST_NAME_MAX] = '\0';
-        name->data.server.name = strdup(hostname);
-        if (!name->data.server.name) {
-            set_GSSERR(ENOMEM);
+        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 = <service> + </> + <hostname> + <\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)) {
@@ -321,6 +363,7 @@ uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status,
                                  &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;
@@ -328,6 +371,8 @@ uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status,
         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;
@@ -449,7 +494,7 @@ void gssntlm_release_attrs(struct gssntlm_name_attribute **attrs)
 
 int gssntlm_copy_name(struct gssntlm_name *src, struct gssntlm_name *dst)
 {
-    char *dom = NULL, *usr = NULL, *srv = NULL;
+    char *dom = NULL, *usr = NULL, *spn = NULL, *srv = NULL;
     int ret;
     dst->type = src->type;
     switch (src->type) {
@@ -475,6 +520,14 @@ int gssntlm_copy_name(struct gssntlm_name *src, struct gssntlm_name *dst)
         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) {
@@ -494,6 +547,7 @@ done:
     if (ret) {
         safefree(dom);
         safefree(usr);
+        safefree(spn);
         safefree(srv);
     }
     return ret;
@@ -555,6 +609,7 @@ void gssntlm_int_release_name(struct gssntlm_name *name)
         safefree(name->data.user.name);
         break;
     case GSSNTLM_NAME_SERVER:
+        safefree(name->data.server.spn);
         safefree(name->data.server.name);
         break;
     }
@@ -630,7 +685,7 @@ uint32_t gssntlm_display_name(uint32_t *minor_status,
         }
         break;
     case GSSNTLM_NAME_SERVER:
-        out->value = strdup(in->data.server.name);
+        out->value = strdup(in->data.server.spn);
         if (!out->value) {
             set_GSSERR(ENOMEM);
             goto done;


=====================================
src/gss_ntlmssp.c
=====================================
@@ -178,3 +178,36 @@ int gssntlm_get_lm_compatibility_level(void)
     /* 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();
+}


=====================================
src/gss_ntlmssp.h
=====================================
@@ -18,7 +18,8 @@
                 NTLMSSP_NEGOTIATE_NTLM | \
                 NTLMSSP_REQUEST_TARGET | \
                 NTLMSSP_NEGOTIATE_OEM | \
-                NTLMSSP_NEGOTIATE_UNICODE)
+                NTLMSSP_NEGOTIATE_UNICODE | \
+                NTLMSSP_NEGOTIATE_VERSION)
 
 #define NTLMSSP_DEFAULT_SERVER_FLAGS ( \
                 NTLMSSP_NEGOTIATE_ALWAYS_SIGN | \
@@ -59,6 +60,7 @@ struct gssntlm_name {
             char *name;
         } user;
         struct {
+            char *spn;
             char *name;
         } server;
     } data;
@@ -93,6 +95,12 @@ struct gssntlm_cred {
             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 {
@@ -176,6 +184,11 @@ uint32_t gssntlm_context_is_valid(struct gssntlm_ctx *ctx,
 
 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);
 
@@ -446,4 +459,8 @@ uint32_t gssntlm_inquire_attrs_for_mech(uint32_t *minor_status,
                                         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_ */


=====================================
src/gss_sec_ctx.c
=====================================
@@ -26,7 +26,6 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,
     struct gssntlm_ctx *ctx;
     struct gssntlm_name *server = NULL;
     struct gssntlm_cred *cred = NULL;
-    char *computer_name = NULL;
     char *nb_computer_name = NULL;
     char *nb_domain_name = NULL;
     struct gssntlm_name *client_name = NULL;
@@ -117,6 +116,10 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,
         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
@@ -165,13 +168,8 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,
             if (retmaj) goto done;
         }
 
-        computer_name = strdup(client_name->data.server.name);
-        if (!computer_name) {
-            set_GSSERR(ENOMEM);
-            goto done;
-        }
-
-        retmin = netbios_get_names(ctx->external_context, computer_name,
+        retmin = netbios_get_names(ctx->external_context,
+                                   client_name->data.server.name,
                                    &nb_computer_name, &nb_domain_name);
         if (retmin) {
             set_GSSERR(retmin);
@@ -398,7 +396,7 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,
             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->gss_flags |= GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
         }
 
         ctx->stage = NTLMSSP_STAGE_DONE;
@@ -433,7 +431,6 @@ done:
         gssntlm_release_cred(&tmpmin, (gss_cred_id_t *)&cred);
     }
     gssntlm_release_name(&tmpmin, (gss_name_t *)&client_name);
-    safefree(computer_name);
     safefree(nb_computer_name);
     safefree(nb_domain_name);
     safefree(trgt_name);
@@ -532,7 +529,6 @@ uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
     int lm_compat_lvl = -1;
     struct ntlm_buffer challenge = { 0 };
     struct gssntlm_name *server_name = NULL;
-    char *computer_name = NULL;
     char *nb_computer_name = NULL;
     char *nb_domain_name = NULL;
     char *chal_target_name;
@@ -618,13 +614,8 @@ uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
             goto done;
         }
 
-        computer_name = strdup(server_name->data.server.name);
-        if (!computer_name) {
-            set_GSSERR(ENOMEM);
-            goto done;
-        }
-
-        retmin = netbios_get_names(ctx->external_context, computer_name,
+        retmin = netbios_get_names(ctx->external_context,
+                                   server_name->data.server.name,
                                    &nb_computer_name, &nb_domain_name);
         if (retmin) {
             set_GSSERR(retmin);
@@ -648,6 +639,11 @@ uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
         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;
@@ -686,6 +682,18 @@ uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
 
             /* 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 | \
@@ -731,15 +739,27 @@ uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
             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,
-                                         computer_name,
+                                         server_name->data.server.name,
                                          NULL, NULL,
-                                         NULL, &timestamp,
-                                         NULL, NULL, NULL,
+                                         &av_flags, &timestamp,
+                                         NULL,
+                                         server_name->data.server.spn,
+                                         NULL,
                                          &target_info);
         if (retmin) {
             set_GSSERR(retmin);
@@ -835,15 +855,19 @@ uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
             goto done;
         }
 
-        if (((usr_name == NULL) || (usr_name[0] == '\0')) &&
-            (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;
-
+        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];
@@ -1024,7 +1048,6 @@ done:
     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(computer_name);
     safefree(nb_computer_name);
     safefree(nb_domain_name);
     safefree(usr_name);


=====================================
src/gss_serialize.c
=====================================
@@ -26,7 +26,7 @@ struct export_attrs {
 
 struct export_name {
     uint8_t type;
-    struct relmem domain;
+    struct relmem dom_or_spn;
     struct relmem name;
     struct export_attrs attrs;
 };
@@ -38,7 +38,7 @@ struct export_keys {
     uint32_t seq_num;
 };
 
-#define EXPORT_CTX_VER 0x0004
+#define EXPORT_CTX_VER 0x0005
 struct export_ctx {
     uint16_t version;
     uint8_t role;
@@ -211,7 +211,7 @@ static int export_name(struct export_state *state,
         if (name->data.user.domain) {
             ret = export_data_buffer(state, name->data.user.domain,
                                      strlen(name->data.user.domain),
-                                     &exp_name->domain);
+                                     &exp_name->dom_or_spn);
             if (ret) {
                 return ret;
             }
@@ -227,6 +227,14 @@ static int export_name(struct export_state *state,
         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),
@@ -599,10 +607,10 @@ static uint32_t import_name(uint32_t *minor_status,
     case EXP_NAME_USER:
         imp_name->type = GSSNTLM_NAME_USER;
         dest = NULL;
-        if (name->domain.len > 0) {
+        if (name->dom_or_spn.len > 0) {
             retmaj = import_data_buffer(&retmin, state,
                                      &dest, NULL, true,
-                                     &name->domain, true);
+                                     &name->dom_or_spn, true);
             if (retmaj != GSS_S_COMPLETE) goto done;
         }
         imp_name->data.user.domain = (char *)dest;
@@ -619,6 +627,14 @@ static uint32_t import_name(uint32_t *minor_status,
     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,
@@ -878,9 +894,11 @@ done:
     return GSSERR();
 }
 
+#define EXPORT_CRED_VER 0x0002
+
 #pragma pack(push, 1)
 struct export_cred {
-    uint16_t version;    /* 0x00 0x02 */
+    uint16_t version;
     uint16_t type;
 
     struct export_name name;    /* user or server name */
@@ -933,7 +951,7 @@ uint32_t gssntlm_export_cred(uint32_t *minor_status,
     state.exp_data = (uint8_t *)&ecred.data - (uint8_t *)&ecred;
     state.exp_len = state.exp_data;
 
-    ecred.version = htole16(1);
+    ecred.version = htole16(EXPORT_CRED_VER);
 
     switch (cred->type) {
     case GSSNTLM_CRED_NONE:
@@ -1052,7 +1070,7 @@ uint32_t gssntlm_import_cred(uint32_t *minor_status,
     ecred = (struct export_cred *)state.exp_struct;
     state.exp_data = (char *)ecred->data - (char *)ecred;
 
-    if (ecred->version != le16toh(1)) {
+    if (ecred->version != le16toh(EXPORT_CRED_VER)) {
         set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN);
         goto done;
     }


=====================================
src/gss_spi.c
=====================================
@@ -443,3 +443,21 @@ OM_uint32 gss_inquire_attrs_for_mech(OM_uint32 *minor_status,
     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);
+}


=====================================
src/gssapi_ntlmssp.h
=====================================
@@ -53,6 +53,20 @@ extern "C" {
 #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"


=====================================
src/ntlm.c
=====================================
@@ -205,7 +205,6 @@ static int ntlm_str_convert(iconv_t cd,
     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)
@@ -256,6 +255,7 @@ static int ntlm_decode_oem_str(struct wire_field_hdr *str_hdr,
     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;
     }
@@ -299,7 +299,7 @@ static int ntlm_decode_u16l_str_hdr(struct ntlm_ctx *ctx,
     char *in, *out = NULL;
     uint16_t str_len;
     uint32_t str_offs;
-    size_t outlen;
+    size_t outlen = 0;
     int ret = 0;
 
     str_len = le16toh(str_hdr->len);
@@ -308,6 +308,7 @@ static int ntlm_decode_u16l_str_hdr(struct ntlm_ctx *ctx,
     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;
     }
@@ -319,13 +320,14 @@ static int ntlm_decode_u16l_str_hdr(struct ntlm_ctx *ctx,
 
     ret = ntlm_str_convert(ctx->to_oem, in, out, str_len, &outlen);
 
-    /* make sure to terminate output string */
-    out[outlen] = '\0';
-
 done:
     if (ret) {
         safefree(out);
+    } else {
+        /* make sure to terminate output string */
+        out[outlen] = '\0';
     }
+
     *str = out;
     return ret;
 }
@@ -349,15 +351,14 @@ void ntlm_internal_set_version(uint8_t major, uint8_t minor,
 
 static int ntlm_encode_version(struct ntlm_ctx *ctx,
                                struct ntlm_buffer *buffer,
-                               size_t *data_offs)
+                               size_t data_offs)
 {
-    if (*data_offs + sizeof(struct wire_version) > buffer->length) {
+    if (data_offs + sizeof(struct wire_version) > buffer->length) {
         return ERR_ENCODE;
     }
 
-    memcpy(&buffer->data[*data_offs], &ntlmssp_version,
+    memcpy(&buffer->data[data_offs], &ntlmssp_version,
            sizeof(struct wire_version));
-    *data_offs += sizeof(struct wire_version);
     return 0;
 }
 
@@ -394,6 +395,7 @@ static int ntlm_decode_field(struct wire_field_hdr *hdr,
     offs = le32toh(hdr->offset);
     if ((offs < payload_offs) ||
         (offs > buffer->length) ||
+        (UINT32_MAX - offs < len) ||
         (offs + len > buffer->length)) {
         return ERR_DECODE;
     }
@@ -683,11 +685,19 @@ int ntlm_decode_target_info(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer,
             break;
         case MSV_AV_TIMESTAMP:
             if (!av_timestamp) continue;
+            if (av_len < sizeof(timestamp)) {
+                ret = ERR_DECODE;
+                goto done;
+            }
             memcpy(&timestamp, 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;
@@ -729,8 +739,6 @@ int ntlm_decode_target_info(struct ntlm_ctx *ctx, struct ntlm_buffer *buffer,
 
 done:
     if (ret) {
-        ntlm_free_buffer_data(&sh);
-        ntlm_free_buffer_data(&cb);
         safefree(nb_computer);
         safefree(nb_domain);
         safefree(dns_computer);
@@ -754,7 +762,7 @@ done:
 
 int ntlm_process_target_info(struct ntlm_ctx *ctx, bool protect,
                              struct ntlm_buffer *in,
-                             const char *server,
+                             const char *spn,
                              struct ntlm_buffer *unhashed_cb,
                              struct ntlm_buffer *out,
                              uint64_t *out_srv_time,
@@ -786,8 +794,9 @@ int ntlm_process_target_info(struct ntlm_ctx *ctx, bool protect,
         goto done;
     }
 
-    if (server && av_target_name) {
-        if (strcasecmp(server, av_target_name) != 0) {
+    if (spn && av_target_name &&
+        ((av_flags & MSVAVFLAGS_UNVERIFIED_SPN) == 0)) {
+        if (strcasecmp(spn, av_target_name) != 0) {
             ret = EINVAL;
             goto done;
         }
@@ -808,15 +817,13 @@ int ntlm_process_target_info(struct ntlm_ctx *ctx, bool protect,
         if (ret) goto done;
     }
 
-    if (!av_target_name && server) {
-        av_target_name = strdup(server);
+    if (!av_target_name && spn) {
+        av_target_name = strdup(spn);
         if (!av_target_name) {
             ret = ENOMEM;
             goto done;
         }
     }
-    /* TODO: add way to tell if the target name is verified o not,
-     * if not set av_flags |= MSVAVFLAGS_UNVERIFIED_SPN; */
 
     ret = ntlm_encode_target_info(ctx,
                                   nb_computer_name, nb_domain_name,
@@ -932,6 +939,12 @@ int ntlm_encode_neg_msg(struct ntlm_ctx *ctx, uint32_t flags,
         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);
@@ -959,6 +972,14 @@ int ntlm_decode_neg_msg(struct ntlm_ctx *ctx,
 
     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,
@@ -1003,10 +1024,6 @@ int ntlm_encode_chal_msg(struct ntlm_ctx *ctx,
 
     buffer.length = sizeof(struct wire_chal_msg);
 
-    if (flags & NTLMSSP_NEGOTIATE_VERSION) {
-        buffer.length += sizeof(struct wire_version);
-    }
-
     if ((flags & NTLMSSP_TARGET_TYPE_SERVER)
         || (flags & NTLMSSP_TARGET_TYPE_DOMAIN)) {
         if (!target_name) return EINVAL;
@@ -1035,7 +1052,8 @@ int ntlm_encode_chal_msg(struct ntlm_ctx *ctx,
 
     /* this must be first as it pushes the payload further down */
     if (flags & NTLMSSP_NEGOTIATE_VERSION) {
-        ret = ntlm_encode_version(ctx, &buffer, &data_offs);
+        ret = ntlm_encode_version(ctx, &buffer,
+                                  (char *)&msg->version - (char *)msg);
         if (ret) goto done;
     }
 
@@ -1077,6 +1095,7 @@ int ntlm_decode_chal_msg(struct ntlm_ctx *ctx,
 {
     struct wire_chal_msg *msg;
     size_t payload_offs;
+    size_t base_chal_size;
     uint32_t flags;
     char *trg = NULL;
     int ret = 0;
@@ -1089,6 +1108,16 @@ int ntlm_decode_chal_msg(struct ntlm_ctx *ctx,
     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)) {
@@ -1107,7 +1136,7 @@ int ntlm_decode_chal_msg(struct ntlm_ctx *ctx,
 
     /* if we allowed a broken short challenge message from an old
      * server we must stop here */
-    if (buffer->length < sizeof(struct wire_chal_msg)) {
+    if (buffer->length < base_chal_size) {
         if (flags & NTLMSSP_NEGOTIATE_TARGET_INFO) {
             ret = ERR_DECODE;
         }
@@ -1190,9 +1219,6 @@ int ntlm_encode_auth_msg(struct ntlm_ctx *ctx,
     if (enc_sess_key) {
         buffer.length += enc_sess_key->length;
     }
-    if (flags & NTLMSSP_NEGOTIATE_VERSION) {
-        buffer.length += sizeof(struct wire_version);
-    }
     if (mic) {
         buffer.length += 16;
     }
@@ -1207,11 +1233,12 @@ int ntlm_encode_auth_msg(struct ntlm_ctx *ctx,
 
     /* this must be first as it pushes the payload further down */
     if (flags & NTLMSSP_NEGOTIATE_VERSION) {
-        ret = ntlm_encode_version(ctx, &buffer, &data_offs);
+        ret = ntlm_encode_version(ctx, &buffer,
+                                  (char *)&msg->version - (char *)msg);
         if (ret) goto done;
     }
 
-    /* this must be second as it pushes the payload further down */
+    /* 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
@@ -1293,6 +1320,7 @@ int ntlm_decode_auth_msg(struct ntlm_ctx *ctx,
                          struct ntlm_buffer *mic)
 {
     struct wire_auth_msg *msg;
+    uint32_t neg_flags;
     size_t payload_offs;
     char *dom = NULL;
     char *usr = NULL;
@@ -1308,10 +1336,13 @@ int ntlm_decode_auth_msg(struct ntlm_ctx *ctx,
     msg = (struct wire_auth_msg *)buffer->data;
     payload_offs = (char *)msg->payload - (char *)msg;
 
-    /* this must be first as it pushes the payload further down */
-    if (flags & NTLMSSP_NEGOTIATE_VERSION) {
-        /* skip version for now */
-        payload_offs += sizeof(struct wire_version);
+    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
@@ -1319,12 +1350,24 @@ int ntlm_decode_auth_msg(struct ntlm_ctx *ctx,
      * 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;
-        /* mic is at payload_offs right now */
-        if (buffer->length - payload_offs < 16) return ERR_DECODE;
-        memcpy(mic->data, &buffer->data[payload_offs], 16);
-        /* NOTE: we do not push down the payload because we do not know that
-         * the MIC is actually present yet for real */
+
+        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) {


=====================================
src/ntlm_common.h
=====================================
@@ -88,12 +88,26 @@ struct wire_field_hdr {
 };
 #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)
@@ -106,6 +120,7 @@ struct wire_chal_msg {
     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)
@@ -131,23 +146,11 @@ struct wire_auth_msg {
     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)
 
-/* 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)
-
 /* ln/ntlm response, v1 or v2 */
 #pragma pack(push, 1)
 union wire_ntlm_response {


=====================================
src/ntlm_crypto.c
=====================================
@@ -860,9 +860,8 @@ int ntlm_seal(uint32_t flags,
 
     h = &state->send;
 
-    if (!(flags & NTLMSSP_NEGOTIATE_SEAL) ||
-        (h->seal_handle == NULL)) {
-        return ENOTSUP;
+    if (h->seal_handle == NULL) {
+        return EINVAL;
     }
 
     ret = RC4_UPDATE(h->seal_handle, message, output);
@@ -902,9 +901,8 @@ int ntlm_unseal(uint32_t flags,
         h = &state->recv;
     }
 
-    if (!(flags & NTLMSSP_NEGOTIATE_SEAL) ||
-        (h->seal_handle == NULL)) {
-        return ENOTSUP;
+    if (h->seal_handle == NULL) {
+        return EINVAL;
     }
 
     ret = RC4_UPDATE(h->seal_handle, message, output);
@@ -972,9 +970,17 @@ int ntlm_verify_mic(struct ntlm_key *key,
 
     /* flags must be checked as they may push the payload further down */
     flags = le32toh(msg->neg_flags);
-    if (flags & NTLMSSP_NEGOTIATE_VERSION) {
-        /* skip version for now */
-        payload_offs += sizeof(struct wire_version);
+    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) {


=====================================
tests/env1.sh
=====================================
@@ -1,5 +1,7 @@
 #!/bin/sh
 
-export NTLM_USER_FILE="examples/test_user_file2.txt"
+EXAMPLES=$(dirname "$0")/../examples
+
+export NTLM_USER_FILE="${EXAMPLES}/test_user_file2.txt"
 export TEST_USER_NAME="testuser"
 ./ntlmssptest


=====================================
tests/env2.sh
=====================================
@@ -1,5 +1,14 @@
 #!/bin/sh
 
-export NTLM_USER_FILE="examples/test_user_file3.txt"
+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


=====================================
tests/ntlmssptest.c
=====================================
@@ -1,14 +1,21 @@
-/* Copyright 2013 Simo Sorce <simo at samba.org>, see COPYING for license */
+/* Copyright 2013-2022 Simo Sorce <simo at samba.org>, see COPYING for license */
 
 #include <ctype.h>
 #include <errno.h>
+#include <limits.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <time.h>
+#include <unistd.h>
 
 #include "config.h"
 
+#ifndef	HOST_NAME_MAX
+#include <sys/param.h>
+#define	HOST_NAME_MAX	MAXHOSTNAMELEN
+#endif
+
 #include "../src/gssapi_ntlmssp.h"
 #include "../src/gss_ntlmssp.h"
 
@@ -1338,7 +1345,7 @@ done:
     return ret;
 }
 
-#define TEST_USER_FILE "examples/test_user_file.txt"
+#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)
@@ -1723,100 +1730,80 @@ int test_gssapi_1(bool user_env_file, bool use_cb, bool no_seal, bool use_cs)
 
     gss_release_buffer(&retmin, &srv_token);
 
-    if (no_seal) {
-        retmaj = gssntlm_wrap(&retmin, cli_ctx, 1, 0, &message, NULL,
-                              &cli_token);
-        if ((retmaj != GSS_S_FAILURE) && (retmin != ENOTSUP)) {
-            fprintf(stderr, "WARN: gssntlm_wrap(cli) did not fail!\n");
-            fflush(stderr);
-            ret = EINVAL;
-            goto done;
-        }
-
-        retmaj = gssntlm_wrap(&retmin, srv_ctx, 1, 0, &message, NULL,
-                              &srv_token);
-        if ((retmaj != GSS_S_FAILURE) && (retmin != ENOTSUP)) {
-            fprintf(stderr, "WARN: gssntlm_wrap(srv) did not fail!\n");
-            fflush(stderr);
-            ret = EINVAL;
-            goto done;
-        }
-    } else {
-        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 (conf_state == 0) {
-            fprintf(stderr, "WARN: gssntlm_wrap(srv) gave 0 conf_state!\n");
-            fflush(stderr);
-            ret = EINVAL;
-            goto done;
-        }
+    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;
+    }
 
-        gss_release_buffer(&retmin, &cli_token);
-        gss_release_buffer(&retmin, &srv_token);
+    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;
+    }
 
-        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;
-        }
+    gss_release_buffer(&retmin, &cli_token);
+    gss_release_buffer(&retmin, &srv_token);
 
-        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 (conf_state == 0) {
-            fprintf(stderr, "WARN: gssntlm_wrap(cli) gave 0 conf_state!\n");
-            fflush(stderr);
-            ret = EINVAL;
-            goto done;
-        }
+    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;
+    }
 
-        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;
-        }
+    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;
+    }
 
-        gss_release_buffer(&retmin, &cli_token);
-        gss_release_buffer(&retmin, &srv_token);
+    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);
 
@@ -1885,6 +1872,256 @@ done:
     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 at 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;
@@ -2790,14 +3027,121 @@ int test_import_name(void)
     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 at 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 main(int argc, const char *argv[])
 {
     struct ntlm_ctx *ctx;
     int gret = 0;
     int ret;
 
-    /* enable trace debugging by dfault in tests */
-    setenv("GSSNTLMSSP_DEBUG", "tests-trace.log", 0);
+    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();
@@ -3033,6 +3377,16 @@ int main(int argc, const char *argv[])
     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;


=====================================
version.m4
=====================================
@@ -1,5 +1,5 @@
 # Primary version number
-m4_define([VERSION_NUMBER], [1.0.0])
+m4_define([VERSION_NUMBER], [1.2.0])
 
 # If the PRERELEASE_VERSION_NUMBER is set, we'll append
 # it to the release tag when creating an RPM or SRPM



View it on GitLab: https://salsa.debian.org/freeipa-team/gss-ntlmssp/-/compare/3718f86a6af815db103785602956a621c91626ca...3c3077e2a24b47db1b4c459fb3173ba7f75b9b44

-- 
View it on GitLab: https://salsa.debian.org/freeipa-team/gss-ntlmssp/-/compare/3718f86a6af815db103785602956a621c91626ca...3c3077e2a24b47db1b4c459fb3173ba7f75b9b44
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-freeipa-devel/attachments/20230216/5a25b8b2/attachment-0001.htm>


More information about the Pkg-freeipa-devel mailing list