Package: release.debian.org
User: release.debian....@packages.debian.org
Usertags: unblock
Severity: normal

Please unblock package samba

This is an update from 4.13.4 to 4.13.5 (requested in #984863).

[ Reason ]
This is a stability fixes release.

Full list of changes:
o  Trever L. Adams <trever.ad...@gmail.com>
   * BUG 14634: s3:modules:vfs_virusfilter: Recent talloc changes cause infinite
     start-up failure.

o  Jeremy Allison <j...@samba.org>
   * BUG 13992: s3: libsmb: Add missing cli_tdis() in error path if encryption
     setup failed on temp proxy connection.
   * BUG 14604: smbd: In conn_force_tdis_done() when forcing a connection closed
     force a full reload of services.

o  Andrew Bartlett <abart...@samba.org>
   * BUG 14593: dbcheck: Check Deleted Objects and reduce noise in reports about
     expired tombstones.

o  Ralph Boehme <s...@samba.org
   * BUG 14503: s3: Fix fcntl waf configure check.
   * BUG 14602: s3/auth: Implement "winbind:ignore domains".
   * BUG 14617: smbd: Use fsp->conn->session_info for the initial
     delete-on-close token.

o  Peter Eriksson <p...@lysator.liu.se>
   * BUG 14648: s3: VFS: nfs4_acls. Add missing TALLOC_FREE(frame) in error
     path.

o  Björn Jacke <b...@sernet.de>
   * BUG 14624: classicupgrade: Treat old never expires value right.

o  Volker Lendecke <v...@samba.org>
   * BUG 14636: g_lock: Fix uninitalized variable reads.

o  Stefan Metzmacher <me...@samba.org>
   * BUG 13898: s3:pysmbd: Fix fd leak in py_smbd_create_file().

o  Andreas Schneider <a...@samba.org>
   * BUG 14625: lib:util: Avoid free'ing our own pointer.

o  Paul Wise <pa...@bonedaddy.net>
   * BUG 12505: HEIMDAL: krb5_storage_free(NULL) should work.

[ Impact ]
At least Paul Wise is affected. See:

https://bugzilla.samba.org/show_bug.cgi?id=13992
https://bugzilla.samba.org/show_bug.cgi?id=12505


[ Tests ]
As is every samba release, the testsuite is improved.

I've also tested the package.

[ Risks ]

[ Checklist ]

  [ ] all changes are documented in the d/changelog

I forgot samba was a key package, so the changelog is not complete.
The missing pieces are above.

  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

Attached are releavant changes (removing diff in the doc about the version).


[ Other info ]
I may ask another unblock request before the bullseye release if samba
4.13.6+ is released.

unblock samba/2:4.13.5+dfsg-1
diff --git a/Makefile b/Makefile
index 0b7b0ae8866..7f5960d5191 100644
--- a/Makefile
+++ b/Makefile
@@ -15,6 +15,9 @@ uninstall:
 test:
 	$(WAF) test $(TEST_OPTIONS)
 
+testonly:
+	$(WAF) testonly $(TEST_OPTIONS)
+
 perftest:
 	$(WAF) test --perf-test $(TEST_OPTIONS)
 
diff --git a/VERSION b/VERSION
index 130087004f0..c24df6ba32e 100644
--- a/VERSION
+++ b/VERSION
@@ -25,7 +25,7 @@
 ########################################################
 SAMBA_VERSION_MAJOR=4
 SAMBA_VERSION_MINOR=13
-SAMBA_VERSION_RELEASE=4
+SAMBA_VERSION_RELEASE=5
 
 ########################################################
 # If a official release has a serious bug              #
diff --git a/WHATSNEW.txt b/WHATSNEW.txt
index 544f4377bfd..8b8c349eaa5 100644
--- a/WHATSNEW.txt
+++ b/WHATSNEW.txt
@@ -1,3 +1,79 @@
+                   ==============================
+                   Release Notes for Samba 4.13.5
+                           March 09, 2021
+                   ==============================
+
+
+This is the latest stable release of the Samba 4.13 release series.
+
+
+Changes since 4.13.4
+--------------------
+
+o  Trever L. Adams <trever.ad...@gmail.com>
+   * BUG 14634: s3:modules:vfs_virusfilter: Recent talloc changes cause infinite
+     start-up failure.
+
+o  Jeremy Allison <j...@samba.org>
+   * BUG 13992: s3: libsmb: Add missing cli_tdis() in error path if encryption
+     setup failed on temp proxy connection.
+   * BUG 14604: smbd: In conn_force_tdis_done() when forcing a connection closed
+     force a full reload of services.
+
+o  Andrew Bartlett <abart...@samba.org>
+   * BUG 14593: dbcheck: Check Deleted Objects and reduce noise in reports about
+     expired tombstones.
+
+o  Ralph Boehme <s...@samba.org
+   * BUG 14503: s3: Fix fcntl waf configure check.
+   * BUG 14602: s3/auth: Implement "winbind:ignore domains".
+   * BUG 14617: smbd: Use fsp->conn->session_info for the initial
+     delete-on-close token.
+
+o  Peter Eriksson <p...@lysator.liu.se>
+   * BUG 14648: s3: VFS: nfs4_acls. Add missing TALLOC_FREE(frame) in error
+     path.
+
+o  Björn Jacke <b...@sernet.de>
+   * BUG 14624: classicupgrade: Treat old never expires value right.
+
+o  Volker Lendecke <v...@samba.org>
+   * BUG 14636: g_lock: Fix uninitalized variable reads.
+
+o  Stefan Metzmacher <me...@samba.org>
+   * BUG 13898: s3:pysmbd: Fix fd leak in py_smbd_create_file().
+
+o  Andreas Schneider <a...@samba.org>
+   * BUG 14625: lib:util: Avoid free'ing our own pointer.
+
+o  Paul Wise <pa...@bonedaddy.net>
+   * BUG 12505: HEIMDAL: krb5_storage_free(NULL) should work.
+
+
+#######################################
+Reporting bugs & Development Discussion
+#######################################
+
+Please discuss this release on the samba-technical mailing list or by
+joining the #samba-technical IRC channel on irc.freenode.net.
+
+If you do report problems then please try to send high quality
+feedback. If you don't provide vital information to help us track down
+the problem then you will probably be ignored.  All bug reports should
+be filed under the Samba 4.1 and newer product in the project's Bugzilla
+database (https://bugzilla.samba.org/).
+
+
+======================================================================
+== Our Code, Our Bugs, Our Responsibility.
+== The Samba Team
+======================================================================
+
+
+Release notes for older releases follow:
+----------------------------------------
+
+
                    ==============================
                    Release Notes for Samba 4.13.4
                           January 26, 2021
@@ -65,8 +141,7 @@ database (https://bugzilla.samba.org/).
 ======================================================================
 
 
-Release notes for older releases follow:
-----------------------------------------
+----------------------------------------------------------------------
 
 
                    ==============================
diff --git a/lib/util/memcache.c b/lib/util/memcache.c
index 1e616bd0e9a..7b0b27eaddb 100644
--- a/lib/util/memcache.c
+++ b/lib/util/memcache.c
@@ -223,14 +223,25 @@ static void memcache_delete_element(struct memcache *cache,
 	TALLOC_FREE(e);
 }
 
-static void memcache_trim(struct memcache *cache)
+static void memcache_trim(struct memcache *cache, struct memcache_element *e)
 {
+	struct memcache_element *tail = NULL;
+
 	if (cache->max_size == 0) {
 		return;
 	}
 
-	while ((cache->size > cache->max_size) && DLIST_TAIL(cache->mru)) {
-		memcache_delete_element(cache, DLIST_TAIL(cache->mru));
+	for (tail = DLIST_TAIL(cache->mru);
+	     (cache->size > cache->max_size) && (tail != NULL);
+	     tail = DLIST_TAIL(cache->mru))
+	{
+		if (tail == e) {
+			tail = DLIST_PREV(tail);
+			if (tail == NULL) {
+				break;
+			}
+		}
+		memcache_delete_element(cache, tail);
 	}
 }
 
@@ -351,7 +362,7 @@ void memcache_add(struct memcache *cache, enum memcache_number n,
 		memcpy(&mtv, cache_value.data, sizeof(mtv));
 		cache->size += mtv.len;
 	}
-	memcache_trim(cache);
+	memcache_trim(cache, e);
 }
 
 void memcache_add_talloc(struct memcache *cache, enum memcache_number n,
diff --git a/lib/util/tests/test_memcache.c b/lib/util/tests/test_memcache.c
new file mode 100644
index 00000000000..8a3997817c1
--- /dev/null
+++ b/lib/util/tests/test_memcache.c
@@ -0,0 +1,161 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2021      Andreas Schneider <a...@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "lib/replace/replace.h"
+#include "lib/util/talloc_stack.h"
+#include "lib/util/memcache.h"
+
+static int setup_talloc_context(void **state)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+
+	*state = frame;
+	return 0;
+}
+
+static int teardown_talloc_context(void **state)
+{
+	TALLOC_CTX *frame = *state;
+	TALLOC_FREE(frame);
+	return 0;
+}
+
+static void torture_memcache_init(void **state)
+{
+	TALLOC_CTX *mem_ctx = *state;
+	struct memcache *cache = NULL;
+
+	cache = memcache_init(mem_ctx, 0);
+	assert_non_null(cache);
+
+	TALLOC_FREE(cache);
+
+	cache = memcache_init(mem_ctx, 10);
+	assert_non_null(cache);
+
+	TALLOC_FREE(cache);
+}
+
+static void torture_memcache_add_lookup_delete(void **state)
+{
+	TALLOC_CTX *mem_ctx = *state;
+	struct memcache *cache = NULL;
+	DATA_BLOB key1, key2;
+	char *path1 = NULL, *path2 = NULL;
+
+	cache = memcache_init(mem_ctx, 0);
+	assert_non_null(cache);
+
+	key1 = data_blob_const("key1", 4);
+	path1 = talloc_strdup(mem_ctx, "/tmp/one");
+	assert_non_null(path1);
+
+	key2 = data_blob_const("key2", 4);
+	path2 = talloc_strdup(mem_ctx, "/tmp/two");
+	assert_non_null(path1);
+
+	memcache_add_talloc(cache, GETWD_CACHE, key1, &path1);
+	assert_null(path1);
+
+	memcache_add_talloc(cache, GETWD_CACHE, key2, &path2);
+	assert_null(path2);
+
+	path1 = memcache_lookup_talloc(cache, GETWD_CACHE, key1);
+	assert_non_null(path1);
+	assert_string_equal(path1, "/tmp/one");
+
+	path2 = memcache_lookup_talloc(cache, GETWD_CACHE, key2);
+	assert_non_null(path2);
+	assert_string_equal(path2, "/tmp/two");
+
+	memcache_delete(cache, GETWD_CACHE, key1);
+	path1 = memcache_lookup_talloc(cache, GETWD_CACHE, key1);
+	assert_null(path1);
+
+	memcache_flush(cache, GETWD_CACHE);
+	path2 = memcache_lookup_talloc(cache, GETWD_CACHE, key2);
+	assert_null(path2);
+
+	TALLOC_FREE(path1);
+	TALLOC_FREE(path2);
+	TALLOC_FREE(cache);
+}
+
+static void torture_memcache_add_oversize(void **state)
+{
+	TALLOC_CTX *mem_ctx = *state;
+	struct memcache *cache = NULL;
+	DATA_BLOB key1, key2;
+	char *path1 = NULL, *path2 = NULL;
+
+	cache = memcache_init(mem_ctx, 10);
+	assert_non_null(cache);
+
+	key1 = data_blob_const("key1", 4);
+	path1 = talloc_strdup(mem_ctx, "/tmp/one");
+	assert_non_null(path1);
+
+	key2 = data_blob_const("key2", 4);
+	path2 = talloc_strdup(mem_ctx, "/tmp/two");
+	assert_non_null(path1);
+
+	memcache_add_talloc(cache, GETWD_CACHE, key1, &path1);
+	assert_null(path1);
+
+	memcache_add_talloc(cache, GETWD_CACHE, key2, &path2);
+	assert_null(path2);
+
+	path1 = memcache_lookup_talloc(cache, GETWD_CACHE, key1);
+	assert_null(path1);
+
+	path2 = memcache_lookup_talloc(cache, GETWD_CACHE, key2);
+	assert_non_null(path2);
+	assert_string_equal(path2, "/tmp/two");
+
+	TALLOC_FREE(path1);
+	TALLOC_FREE(path2);
+	TALLOC_FREE(cache);
+}
+
+int main(int argc, char *argv[])
+{
+	int rc;
+	const struct CMUnitTest tests[] = {
+		cmocka_unit_test(torture_memcache_init),
+		cmocka_unit_test(torture_memcache_add_lookup_delete),
+		cmocka_unit_test(torture_memcache_add_oversize),
+	};
+
+	if (argc == 2) {
+		cmocka_set_test_filter(argv[1]);
+	}
+	cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+	rc = cmocka_run_group_tests(tests,
+				    setup_talloc_context,
+				    teardown_talloc_context);
+
+	return rc;
+}
diff --git a/lib/util/wscript_build b/lib/util/wscript_build
index bf3e44bf1d2..5a8a04965ec 100644
--- a/lib/util/wscript_build
+++ b/lib/util/wscript_build
@@ -310,4 +310,10 @@ else:
                      source='tests/test_util.c',
                      deps='cmocka replace talloc samba-util',
                      local_include=False,
-                     for_selftest=True);
+                     for_selftest=True)
+
+    bld.SAMBA_BINARY('test_memcache',
+                     source='tests/test_memcache.c',
+                     deps='cmocka replace talloc samba-util',
+                     local_include=False,
+                     for_selftest=True)
diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py
index 593aa8cf6d2..d12833d9390 100644
--- a/python/samba/dbchecker.py
+++ b/python/samba/dbchecker.py
@@ -1819,6 +1819,11 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base)))
             # old static provision dumps
             return False
 
+        if dn in self.deleted_objects_containers:
+            # The Deleted Objects container will look like an expired
+            # tombstone
+            return False
+
         repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, repl_val)
 
         isDeleted = self.find_repl_attid(repl, drsuapi.DRSUAPI_ATTID_isDeleted)
@@ -1832,7 +1837,25 @@ newSuperior: %s""" % (str(from_dn), str(to_rdn), str(to_base)))
         if delta <= tombstone_delta:
             return False
 
-        self.report("SKIPING: object %s is an expired tombstone" % dn)
+        expunge_time = delete_time + tombstone_delta
+
+        delta_days = delta / (24 * 60 * 60)
+
+        if delta_days <= 2:
+            self.report("SKIPPING additional checks on object "
+                        "%s which very recently "
+                        "became an expired tombstone (normal)" % dn)
+            self.report("INFO: it is expected this will be expunged "
+                        "by the next daily task some time after %s, "
+                        "%d hours ago"
+                        % (time.ctime(expunge_time), delta // (60 * 60)))
+        else:
+            self.report("SKIPPING: object %s is an expired tombstone" % dn)
+            self.report("INFO: it was expected this object would have "
+                        "been expunged soon after"
+                        "%s, %d days ago"
+                        % (time.ctime(expunge_time), delta_days))
+
         self.report("isDeleted: attid=0x%08x version=%d invocation=%s usn=%s (local=%s) at %s" % (
                     isDeleted.attid,
                     isDeleted.version,
diff --git a/python/samba/upgrade.py b/python/samba/upgrade.py
index 8511bed2868..dff856a8d7c 100644
--- a/python/samba/upgrade.py
+++ b/python/samba/upgrade.py
@@ -74,7 +74,7 @@ def import_sam_policy(samdb, policy, logger):
 
     if 'maximum password age' in policy:
         max_pw_age_unix = policy['maximum password age']
-        if max_pw_age_unix == -1 or max_pw_age_unix == 0:
+        if max_pw_age_unix == -1 or max_pw_age_unix == 0 or max_pw_age_unix == 0xFFFFFFFF:
             max_pw_age_nt = -0x8000000000000000
         else:
             max_pw_age_nt = int(-max_pw_age_unix * (1e7))
diff --git a/script/autobuild.py b/script/autobuild.py
index 0ab04eb7c26..0f837d0c109 100755
--- a/script/autobuild.py
+++ b/script/autobuild.py
@@ -4,7 +4,7 @@
 # released under GNU GPL v3 or later
 
 from __future__ import print_function
-from subprocess import call, check_call, check_output, Popen, PIPE
+from subprocess import call, check_call, check_output, Popen, PIPE, CalledProcessError
 import os
 import tarfile
 import sys
@@ -860,6 +860,17 @@ def run_cmd(cmd, dir=".", show=None, output=False, checkfail=True):
     else:
         return call(cmd, shell=True, cwd=dir)
 
+def rmdir_force(dirname, re_raise=True):
+    try:
+        run_cmd("test -d %s && chmod -R +w %s; rm -rf %s" % (
+                dirname, dirname, dirname), output=True, show=True)
+    except CalledProcessError as e:
+        do_print("Failed: '%s'" % (str(e)))
+        run_cmd("tree %s" % dirname, output=True, show=True)
+        if re_raise:
+            raise
+        return False
+    return True
 
 class builder(object):
     '''handle build of one directory'''
@@ -882,8 +893,8 @@ class builder(object):
         self.test_source_dir = "%s/%s" % (testbase, self.tag)
         self.cwd = "%s/%s" % (self.test_source_dir, self.dir)
         self.prefix = "%s/%s" % (test_prefix, self.tag)
-        run_cmd("rm -rf %s" % self.test_source_dir)
-        run_cmd("rm -rf %s" % self.prefix)
+        rmdir_force(self.test_source_dir)
+        rmdir_force(self.prefix)
         if cp:
             run_cmd("cp -R -a -l %s %s" % (test_master, self.test_source_dir), dir=test_master, show=True)
         else:
@@ -893,8 +904,8 @@ class builder(object):
     def start_next(self):
         if self.next == len(self.sequence):
             if not options.nocleanup:
-                run_cmd("rm -rf %s" % self.test_source_dir)
-                run_cmd("rm -rf %s" % self.prefix)
+                rmdir_force(self.test_source_dir)
+                rmdir_force(self.prefix)
             do_print('%s: Completed OK' % self.name)
             self.done = True
             return
@@ -1018,7 +1029,7 @@ class buildlist(object):
                         'df -m %s' % testbase]:
                 try:
                     out = run_cmd(cmd, output=True, checkfail=False)
-                except subprocess.CalledProcessError as e:
+                except CalledProcessError as e:
                     out = "<failed: %s>" % str(e)
                 print('### %s' % cmd, file=f)
                 print(out, file=f)
@@ -1048,14 +1059,23 @@ class buildlist(object):
         self.tail_proc = Popen(cmd, close_fds=True)
 
 
-def cleanup():
+def cleanup(do_raise=False):
     if options.nocleanup:
         return
     run_cmd("stat %s || true" % test_tmpdir, show=True)
     run_cmd("stat %s" % testbase, show=True)
     do_print("Cleaning up %r" % cleanup_list)
     for d in cleanup_list:
-        run_cmd("rm -rf %s" % d)
+        ok = rmdir_force(d, re_raise=False)
+        if ok:
+            continue
+        if os.path.isdir(d):
+            do_print("Killing, waiting and retry")
+            run_cmd("killbysubdir %s > /dev/null 2>&1" % d, checkfail=False)
+        else:
+            do_print("Waiting and retry")
+        time.sleep(1)
+        rmdir_force(d, re_raise=do_raise)
 
 
 def daemonize(logfile):
@@ -1321,7 +1341,7 @@ while True:
         (status, failed_task, failed_stage, failed_tag, errstr) = blist.run()
         if status != 0 or errstr != "retry":
             break
-        cleanup()
+        cleanup(do_raise=True)
     except Exception:
         cleanup()
         raise
diff --git a/selftest/selftest.pl b/selftest/selftest.pl
index d14df92a11c..258a8437922 100755
--- a/selftest/selftest.pl
+++ b/selftest/selftest.pl
@@ -280,7 +280,7 @@ my $bindir_abs = abs_path($bindir);
 my $torture_maxtime = ($ENV{TORTURE_MAXTIME} or 1200);
 
 $prefix =~ s+//+/+;
-$prefix =~ s+/./+/+;
+$prefix =~ s+/\./+/+;
 $prefix =~ s+/$++;
 
 die("using an empty prefix isn't allowed") unless $prefix ne "";
@@ -312,7 +312,6 @@ $ENV{PREFIX} = $prefix;
 $ENV{PREFIX_ABS} = $prefix_abs;
 $ENV{SRCDIR} = $srcdir;
 $ENV{SRCDIR_ABS} = $srcdir_abs;
-$ENV{GNUPGHOME} = "$srcdir_abs/selftest/gnupg";
 $ENV{BINDIR} = $bindir_abs;
 
 my $tls_enabled = not $opt_quick;
@@ -693,6 +692,9 @@ $ENV{RESOLV_CONF} = "${selftest_resolv_conf_path}.global";
 my $selftest_krbt_ccache_path = "$tmpdir_abs/selftest.krb5_ccache";
 $ENV{KRB5CCNAME} = "FILE:${selftest_krbt_ccache_path}.global";
 
+my $selftest_gnupghome_path = "$tmpdir_abs/selftest.no.gnupg";
+$ENV{GNUPGHOME} = "${selftest_gnupghome_path}.global";
+
 my @available = ();
 foreach my $fn (@testlists) {
 	foreach (read_testlist($fn)) {
@@ -829,6 +831,7 @@ sub setup_env($$)
 
 	$ENV{RESOLV_CONF} = "${selftest_resolv_conf_path}.${envname}/ignore";
 	$ENV{KRB5CCNAME} = "FILE:${selftest_krbt_ccache_path}.${envname}/ignore";
+	$ENV{GNUPGHOME} = "${selftest_gnupghome_path}.${envname}/ignore";
 
 	if (defined(get_running_env($envname))) {
 		$testenv_vars = get_running_env($envname);
diff --git a/selftest/target/Samba.pm b/selftest/target/Samba.pm
index 6118f2e243a..d47f933376e 100644
--- a/selftest/target/Samba.pm
+++ b/selftest/target/Samba.pm
@@ -280,6 +280,30 @@ EOF
 	umask $oldumask;
 }
 
+sub copy_gnupg_home($)
+{
+	my ($ctx) = @_;
+
+	my $gnupg_srcdir = "$ENV{SRCDIR_ABS}/selftest/gnupg";
+	my @files = (
+		"gpg.conf",
+		"pubring.gpg",
+		"secring.gpg",
+		"trustdb.gpg",
+	);
+
+	my $oldumask = umask;
+	umask 0077;
+	mkdir($ctx->{gnupghome}, 0777);
+	umask 0177;
+	foreach my $file (@files) {
+		my $srcfile = "${gnupg_srcdir}/${file}";
+		my $dstfile = "$ctx->{gnupghome}/${file}";
+		copy_file_content(${srcfile}, ${dstfile});
+	}
+	umask $oldumask;
+}
+
 sub mk_krb5_conf($$)
 {
 	my ($ctx) = @_;
@@ -672,6 +696,7 @@ sub get_env_for_process
 		RESOLV_CONF => $env_vars->{RESOLV_CONF},
 		KRB5_CONFIG => $env_vars->{KRB5_CONFIG},
 		KRB5CCNAME => "$env_vars->{KRB5_CCACHE}.$proc_name",
+		GNUPGHOME => $env_vars->{GNUPGHOME},
 		SELFTEST_WINBINDD_SOCKET_DIR => $env_vars->{SELFTEST_WINBINDD_SOCKET_DIR},
 		NMBD_SOCKET_DIR => $env_vars->{NMBD_SOCKET_DIR},
 		NSS_WRAPPER_PASSWD => $env_vars->{NSS_WRAPPER_PASSWD},
@@ -857,6 +882,7 @@ my @exported_envvars = (
 	# misc stuff
 	"KRB5_CONFIG",
 	"KRB5CCNAME",
+	"GNUPGHOME",
 	"SELFTEST_WINBINDD_SOCKET_DIR",
 	"NMBD_SOCKET_DIR",
 	"LOCAL_PATH",
diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index e141f102ef1..c15057fa80b 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -723,6 +723,7 @@ sub provision_ad_member
 	my $ret = $self->provision(
 	    prefix => $prefix,
 	    domain => $dcvars->{DOMAIN},
+	    realm => $dcvars->{REALM},
 	    server => "LOCALADMEMBER",
 	    password => "loCalMemberPass",
 	    extra_options => $member_options,
@@ -873,6 +874,7 @@ sub setup_ad_member_rfc2307
 	my $ret = $self->provision(
 	    prefix => $prefix,
 	    domain => $dcvars->{DOMAIN},
+	    realm => $dcvars->{REALM},
 	    server => "RFC2307MEMBER",
 	    password => "loCalMemberPass",
 	    extra_options => $member_options,
@@ -970,6 +972,7 @@ sub setup_ad_member_idmap_rid
 	my $ret = $self->provision(
 	    prefix => $prefix,
 	    domain => $dcvars->{DOMAIN},
+	    realm => $dcvars->{REALM},
 	    server => "IDMAPRIDMEMBER",
 	    password => "loCalMemberPass",
 	    extra_options => $member_options,
@@ -1067,6 +1070,7 @@ sub setup_ad_member_idmap_ad
 	my $ret = $self->provision(
 	    prefix => $prefix,
 	    domain => $dcvars->{DOMAIN},
+	    realm => $dcvars->{REALM},
 	    server => "IDMAPADMEMBER",
 	    password => "loCalMemberPass",
 	    extra_options => $member_options,
@@ -1727,12 +1731,22 @@ $ret->{USERNAME} = KTEST\\Administrator
 sub setup_maptoguest
 {
 	my ($self, $path) = @_;
+	my $prefix_abs = abs_path($path);
+	my $libdir="$prefix_abs/lib";
+	my $share_dir="$prefix_abs/share";
+	my $errorinjectconf="$libdir/error_inject.conf";
 
 	print "PROVISIONING maptoguest...";
 
 	my $options = "
 map to guest = bad user
 ntlm auth = yes
+
+[force_user_error_inject]
+	path = $share_dir
+	vfs objects = acl_xattr fake_acls xattr_tdb error_inject
+	force user = user1
+	include = $errorinjectconf
 ";
 
 	my $vars = $self->provision(
@@ -1930,6 +1944,7 @@ sub provision($$)
 
 	my $prefix = $args{prefix};
 	my $domain = $args{domain};
+	my $realm = $args{realm};
 	my $server = $args{server};
 	my $password = $args{password};
 	my $extra_options = $args{extra_options};
@@ -1947,6 +1962,12 @@ sub provision($$)
 	my %createuser_env = ();
 	my $server_ip = Samba::get_ipv4_addr($server);
 	my $server_ipv6 = Samba::get_ipv6_addr($server);
+	my $dns_domain;
+	if (defined($realm)) {
+	    $dns_domain = lc($realm);
+	} else {
+	    $dns_domain = "samba.example.com";
+	}
 
 	my $unix_name = ($ENV{USER} or $ENV{LOGNAME} or `PATH=/usr/ucb:$ENV{PATH} whoami`);
 	chomp $unix_name;
@@ -2926,8 +2947,8 @@ force_user:x:$gid_force_user:
 		warn("Unable to open $nss_wrapper_hosts");
 		return undef;
 	}
-	print HOSTS "${server_ip} ${hostname}.samba.example.com ${hostname}\n";
-	print HOSTS "${server_ipv6} ${hostname}.samba.example.com ${hostname}\n";
+	print HOSTS "${server_ip} ${hostname}.${dns_domain} ${hostname}\n";
+	print HOSTS "${server_ipv6} ${hostname}.${dns_domain} ${hostname}\n";
 	close(HOSTS);
 
 	$resolv_conf = "$privatedir/no_resolv.conf" unless defined($resolv_conf);
diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index 649e923ff9a..77bd741d476 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -17,7 +17,6 @@ use SocketWrapper;
 use target::Samba;
 use target::Samba3;
 use Archive::Tar;
-use File::Path 'make_path';
 
 sub new($$$$$) {
 	my ($classname, $SambaCtx, $bindir, $srcdir, $server_maxtime) = @_;
@@ -161,19 +160,7 @@ sub wait_for_start($$)
 		my $max_wait = 60;
 
 		# Add hosts file for name lookups
-		my $cmd = "NSS_WRAPPER_HOSTS='$testenv_vars->{NSS_WRAPPER_HOSTS}' ";
-		if (defined($testenv_vars->{RESOLV_WRAPPER_CONF})) {
-			$cmd .= "RESOLV_WRAPPER_CONF='$testenv_vars->{RESOLV_WRAPPER_CONF}' ";
-		} else {
-			$cmd .= "RESOLV_WRAPPER_HOSTS='$testenv_vars->{RESOLV_WRAPPER_HOSTS}' ";
-		}
-		$cmd .= "RESOLV_CONF='$testenv_vars->{RESOLV_CONF}' ";
-		if (defined($testenv_vars->{GNUTLS_FORCE_FIPS_MODE})) {
-			$cmd .= "GNUTLS_FORCE_FIPS_MODE=$testenv_vars->{GNUTLS_FORCE_FIPS_MODE} ";
-		}
-		if (defined($testenv_vars->{OPENSSL_FORCE_FIPS_MODE})) {
-			$cmd .= "OPENSSL_FORCE_FIPS_MODE=$testenv_vars->{OPENSSL_FORCE_FIPS_MODE} ";
-		}
+		my $cmd = $self->get_cmd_env_vars($testenv_vars);
 
 		$cmd .= "$ldbsearch ";
 		$cmd .= "$testenv_vars->{CONFIGURATION} ";
@@ -281,7 +268,7 @@ sub setup_dns_hub_internal($$$)
 	my ($self, $hostname, $prefix) = @_;
 	my $STDIN_READER;
 
-	unless(-d $prefix or make_path($prefix, 0777)) {
+	unless(-d $prefix or mkdir($prefix, 0777)) {
 		warn("Unable to create $prefix");
 		return undef;
 	}
@@ -356,6 +343,10 @@ sub setup_dns_hub
 
 	my $hostname = "rootdnsforwarder";
 
+	unless(-d $prefix or mkdir($prefix, 0777)) {
+		warn("Unable to create $prefix");
+		return undef;
+	}
 	my $env = $self->setup_dns_hub_internal("$hostname", "$prefix/$hostname");
 
 	$self->{dns_hub_env} = $env;
@@ -375,10 +366,44 @@ sub get_dns_hub_env($)
 	return undef;
 }
 
+sub return_env_value
+{
+	my ($env, $overwrite, $key) = @_;
+
+	if (defined($overwrite) and defined($overwrite->{$key})) {
+		return $overwrite->{$key};
+	}
+
+	if (defined($env->{$key})) {
+		return $env->{$key};
+	}
+
+	return undef;
+}
+
 # Returns the environmental variables that we pass to samba-tool commands
 sub get_cmd_env_vars
 {
-	my ($self, $localenv) = @_;
+	my ($self, $givenenv, $overwrite) = @_;
+
+	my @keys = (
+		"NSS_WRAPPER_HOSTS",
+		"SOCKET_WRAPPER_DEFAULT_IFACE",
+		"RESOLV_CONF",
+		"RESOLV_WRAPPER_CONF",
+		"RESOLV_WRAPPER_HOSTS",
+		"GNUTLS_FORCE_FIPS_MODE",
+		"OPENSSL_FORCE_FIPS_MODE",
+		"KRB5_CONFIG",
+		"KRB5_CCACHE",
+		"GNUPGHOME",
+	);
+
+	my $localenv = undef;
+	foreach my $key (@keys) {
+		my $v = return_env_value($givenenv, $overwrite, $key);
+		$localenv->{$key} = $v if defined($v);
+	}
 
 	my $cmd_env = "NSS_WRAPPER_HOSTS='$localenv->{NSS_WRAPPER_HOSTS}' ";
 	$cmd_env .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$localenv->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
@@ -393,9 +418,10 @@ sub get_cmd_env_vars
 	if (defined($localenv->{OPENSSL_FORCE_FIPS_MODE})) {
 		$cmd_env .= "OPENSSL_FORCE_FIPS_MODE=$localenv->{OPENSSL_FORCE_FIPS_MODE} ";
 	}
-	$cmd_env .= " KRB5_CONFIG=\"$localenv->{KRB5_CONFIG}\" ";
+	$cmd_env .= "KRB5_CONFIG=\"$localenv->{KRB5_CONFIG}\" ";
 	$cmd_env .= "KRB5CCNAME=\"$localenv->{KRB5_CCACHE}\" ";
 	$cmd_env .= "RESOLV_CONF=\"$localenv->{RESOLV_CONF}\" ";
+	$cmd_env .= "GNUPGHOME=\"$localenv->{GNUPGHOME}\" ";
 
 	return $cmd_env;
 }
@@ -565,6 +591,7 @@ sub provision_raw_prepare($$$$$$$$$$$$$$)
 	$ctx->{krb5_conf} = "$ctx->{etcdir}/krb5.conf";
 	$ctx->{krb5_ccache} = "$prefix_abs/krb5_ccache";
 	$ctx->{mitkdc_conf} = "$ctx->{etcdir}/mitkdc.conf";
+	$ctx->{gnupghome} = "$prefix_abs/gnupg";
 	$ctx->{privatedir} = "$prefix_abs/private";
 	$ctx->{binddnsdir} = "$prefix_abs/bind-dns";
 	$ctx->{ncalrpcdir} = "$prefix_abs/ncalrpc";
@@ -608,8 +635,9 @@ sub provision_raw_prepare($$$$$$$$$$$$$$)
 	$ctx->{smb_conf_extra_options} = "";
 
 	my @provision_options = ();
+	push (@provision_options, "GNUPGHOME=\"$ctx->{gnupghome}\"");
 	push (@provision_options, "KRB5_CONFIG=\"$ctx->{krb5_conf}\"");
-	push (@provision_options, "KRB5_CCACHE=\"$ctx->{krb5_ccache}\"");
+	push (@provision_options, "KRB5CCNAME=\"$ctx->{krb5_ccache}\"");
 	push (@provision_options, "NSS_WRAPPER_PASSWD=\"$ctx->{nsswrap_passwd}\"");
 	push (@provision_options, "NSS_WRAPPER_GROUP=\"$ctx->{nsswrap_group}\"");
 	push (@provision_options, "NSS_WRAPPER_HOSTS=\"$ctx->{nsswrap_hosts}\"");
@@ -700,6 +728,7 @@ sub provision_raw_step1($$)
 		return undef;
 	}
 
+	Samba::copy_gnupg_home($ctx);
 	Samba::prepare_keyblobs($ctx);
 	my $crlfile = "$ctx->{tlsdir}/crl.pem";
 	$crlfile = "" unless -e ${crlfile};
@@ -843,6 +872,7 @@ nogroup:x:65534:nobody
 	# Note that we have SERVER_X and DC_SERVER_X variables (which have the same
 	# value initially). In a 2 DC setup, $DC_SERVER_X will always be the PDC.
 	my $ret = {
+		GNUPGHOME => $ctx->{gnupghome},
 		KRB5_CONFIG => $ctx->{krb5_conf},
 		KRB5_CCACHE => $ctx->{krb5_ccache},
 		MITKDC_CONFIG => $ctx->{mitkdc_conf},
@@ -922,11 +952,10 @@ sub provision_raw_step2($$$)
 		return undef;
 	}
 
+	my $cmd_env = $self->get_cmd_env_vars($ret);
+
 	my $testallowed_account = "testallowed";
-	my $samba_tool_cmd = "";
-	$samba_tool_cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
-	$samba_tool_cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-	$samba_tool_cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	my $samba_tool_cmd = ${cmd_env};
 	$samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
 	    . " user create --configfile=$ctx->{smb_conf} $testallowed_account $ctx->{password}";
 	unless (system($samba_tool_cmd) == 0) {
@@ -935,10 +964,7 @@ sub provision_raw_step2($$$)
 	}
 
 	my $srv_account = "srv_account";
-	$samba_tool_cmd = "";
-	$samba_tool_cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
-	$samba_tool_cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-	$samba_tool_cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$samba_tool_cmd = ${cmd_env};
 	$samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
 	    . " user create --configfile=$ctx->{smb_conf} $srv_account $ctx->{password}";
 	unless (system($samba_tool_cmd) == 0) {
@@ -946,10 +972,7 @@ sub provision_raw_step2($$$)
 		return undef;
 	}
 
-	$samba_tool_cmd = "";
-	$samba_tool_cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
-	$samba_tool_cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-	$samba_tool_cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$samba_tool_cmd = ${cmd_env};
 	$samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
 	    . " spn add HOST/$srv_account --configfile=$ctx->{smb_conf} $srv_account";
 	unless (system($samba_tool_cmd) == 0) {
@@ -957,10 +980,7 @@ sub provision_raw_step2($$$)
 		return undef;
 	}
 
-	my $ldbmodify = "";
-	$ldbmodify .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
-	$ldbmodify .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-	$ldbmodify .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	my $ldbmodify = ${cmd_env};
 	$ldbmodify .= Samba::bindir_path($self, "ldbmodify");
 	$ldbmodify .=  " --configfile=$ctx->{smb_conf}";
 	my $base_dn = "DC=".join(",DC=", split(/\./, $ctx->{realm}));
@@ -991,10 +1011,7 @@ servicePrincipalName: host/testallowed
 ";
 	close(LDIF);
 
-	$samba_tool_cmd = "";
-	$samba_tool_cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
-	$samba_tool_cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-	$samba_tool_cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$samba_tool_cmd = ${cmd_env};
 	$samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
 	    . " user create --configfile=$ctx->{smb_conf} testdenied $ctx->{password}";
 	unless (system($samba_tool_cmd) == 0) {
@@ -1012,10 +1029,7 @@ userPrincipalName: testdenied_upn\@$ctx->{realm}.upn
 ";
 	close(LDIF);
 
-	$samba_tool_cmd = "";
-	$samba_tool_cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
-	$samba_tool_cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-	$samba_tool_cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$samba_tool_cmd = ${cmd_env};
 	$samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
 	    . " user create --configfile=$ctx->{smb_conf} testupnspn $ctx->{password}";
 	unless (system($samba_tool_cmd) == 0) {
@@ -1035,10 +1049,7 @@ servicePrincipalName: http/testupnspn.$ctx->{dnsname}
 ";
 	close(LDIF);
 
-	$samba_tool_cmd = "";
-	$samba_tool_cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
-	$samba_tool_cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-	$samba_tool_cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$samba_tool_cmd = ${cmd_env};
 	$samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
 	    . " group addmembers --configfile=$ctx->{smb_conf} 'Allowed RODC Password Replication Group' '$testallowed_account'";
 	unless (system($samba_tool_cmd) == 0) {
@@ -1050,11 +1061,8 @@ servicePrincipalName: http/testupnspn.$ctx->{dnsname}
 	my $user_account_array = ["alice", "bob", "jane", "joe"];
 
 	foreach my $user_account (@{$user_account_array}) {
-		my $samba_tool_cmd = "";
+		my $samba_tool_cmd = ${cmd_env};
 
-		$samba_tool_cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
-		$samba_tool_cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-		$samba_tool_cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
 		$samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
 		    . " user create --configfile=$ctx->{smb_conf} $user_account Secret007";
 		unless (system($samba_tool_cmd) == 0) {
@@ -1066,10 +1074,8 @@ servicePrincipalName: http/testupnspn.$ctx->{dnsname}
 	my $group_array = ["Samba Users"];
 
 	foreach my $group (@{$group_array}) {
-		my $samba_tool_cmd = "";
+		my $samba_tool_cmd = ${cmd_env};
 
-		$samba_tool_cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-		$samba_tool_cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
 		$samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
 		    . " group add --configfile=$ctx->{smb_conf} \"$group\"";
 		unless (system($samba_tool_cmd) == 0) {
@@ -1079,12 +1085,10 @@ servicePrincipalName: http/testupnspn.$ctx->{dnsname}
 	}
 
 	# Add user joe to group "Samba Users"
-	$samba_tool_cmd = "";
 	my $group = "Samba Users";
 	my $user_account = "joe";
 
-	$samba_tool_cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-	$samba_tool_cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$samba_tool_cmd = ${cmd_env};
 	$samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
 	    . " group addmembers --configfile=$ctx->{smb_conf} \"$group\" $user_account";
 	unless (system($samba_tool_cmd) == 0) {
@@ -1092,12 +1096,10 @@ servicePrincipalName: http/testupnspn.$ctx->{dnsname}
 		return undef;
 	}
 
-	$samba_tool_cmd = "";
 	$group = "Samba Users";
 	$user_account = "joe";
 
-	$samba_tool_cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-	$samba_tool_cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$samba_tool_cmd = ${cmd_env};
 	$samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
 	    . " user setprimarygroup --configfile=$ctx->{smb_conf} $user_account \"$group\"";
 	unless (system($samba_tool_cmd) == 0) {
@@ -1106,10 +1108,7 @@ servicePrincipalName: http/testupnspn.$ctx->{dnsname}
 	}
 
 	# Change the userPrincipalName for jane
-	$ldbmodify = "";
-	$ldbmodify .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
-	$ldbmodify .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-	$ldbmodify .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+	$ldbmodify = ${cmd_env};
 	$ldbmodify .= Samba::bindir_path($self, "ldbmodify");
 	$ldbmodify .=  " --configfile=$ctx->{smb_conf}";
 	$base_dn = "DC=".join(",DC=", split(/\./, $ctx->{realm}));
@@ -1409,12 +1408,13 @@ sub provision_rpc_proxy($$$)
 		return undef;
 	}
 
+	# Prepare a context of the DC, but using the local CCACHE.
+	my $overwrite = undef;
+	$overwrite->{KRB5_CCACHE} = $ret->{KRB5_CCACHE};
+	my $dc_cmd_env = $self->get_cmd_env_vars($dcvars, $overwrite);
+
 	# Setting up delegation runs in the context of the DC for now
-	$cmd = "";
-	$cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$dcvars->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-	$cmd .= "KRB5_CONFIG=\"$dcvars->{KRB5_CONFIG}\" ";
-	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
-	$cmd .= "RESOLV_CONF=\"$dcvars->{RESOLV_CONF}\" ";
+	$cmd = $dc_cmd_env;
 	$cmd .= "$samba_tool delegation for-any-protocol '$ret->{NETBIOSNAME}\$' on";
         $cmd .= " $dcvars->{CONFIGURATION}";
         print $cmd;
@@ -1425,11 +1425,7 @@ sub provision_rpc_proxy($$$)
 	}
 
 	# Setting up delegation runs in the context of the DC for now
-	$cmd = "";
-	$cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$dcvars->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-	$cmd .= "KRB5_CONFIG=\"$dcvars->{KRB5_CONFIG}\" ";
-	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
-	$cmd .= "RESOLV_CONF=\"$dcvars->{RESOLV_CONF}\" ";
+	$cmd = $dc_cmd_env;
 	$cmd .= "$samba_tool delegation add-service '$ret->{NETBIOSNAME}\$' cifs/$dcvars->{SERVER}";
         $cmd .= " $dcvars->{CONFIGURATION}";
 
@@ -1824,9 +1820,7 @@ sub provision_rodc($$$)
         # This ensures deterministic behaviour for tests that want to have the 'testallowed account'
         # user password verified on the RODC
 	my $testallowed_account = "testallowed account";
-	$cmd = "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-	$cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
-	$cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
+	$cmd = $self->get_cmd_env_vars($ret);
 	$cmd .= "$samba_tool rodc preload '$testallowed_account' $ret->{CONFIGURATION}";
 	$cmd .= " --server=$dcvars->{DC_SERVER}";
 
@@ -2502,14 +2496,10 @@ sub setup_promoted_dc
 		# force source and replicated DC to update repsTo/repsFrom
 		# for vampired partitions
 		my $samba_tool =  Samba::bindir_path($self, "samba-tool");
-		my $cmd = "NSS_WRAPPER_HOSTS='$env->{NSS_WRAPPER_HOSTS}' ";
+		my $cmd = $self->get_cmd_env_vars($env);
 		# as 'vampired' dc may add data in its local replica
 		# we need to synchronize data between DCs
 		my $base_dn = "DC=".join(",DC=", split(/\./, $dc_vars->{REALM}));
-		$cmd = "SOCKET_WRAPPER_DEFAULT_IFACE=\"$env->{SOCKET_WRAPPER_DEFAULT_IFACE}\"";
-		$cmd .= " KRB5_CONFIG=\"$env->{KRB5_CONFIG}\"";
-		$cmd .= "KRB5CCNAME=\"$env->{KRB5_CCACHE}\" ";
-		$cmd .= "RESOLV_CONF=\"$env->{RESOLV_CONF}\" ";
 		$cmd .= " $samba_tool drs replicate $env->{DC_SERVER} $env->{SERVER}";
 		$cmd .= " $dc_vars->{CONFIGURATION}";
 		$cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
@@ -2545,14 +2535,9 @@ sub setup_rodc
 	}
 
 	my $samba_tool =  Samba::bindir_path($self, "samba-tool");
-	my $cmd = "";
+	my $cmd = $self->get_cmd_env_vars($env);
 
 	my $base_dn = "DC=".join(",DC=", split(/\./, $dc_vars->{REALM}));
-	$cmd .= "NSS_WRAPPER_HOSTS='$env->{NSS_WRAPPER_HOSTS}' ";
-	$cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$env->{SOCKET_WRAPPER_DEFAULT_IFACE}\"";
-	$cmd .= " KRB5_CONFIG=\"$env->{KRB5_CONFIG}\"";
-	$cmd .= "KRB5CCNAME=\"$env->{KRB5_CCACHE}\" ";
-	$cmd .= "RESOLV_CONF=\"$env->{RESOLV_CONF}\" ";
 	$cmd .= " $samba_tool drs replicate $env->{SERVER} $env->{DC_SERVER}";
 	$cmd .= " $dc_vars->{CONFIGURATION}";
 	$cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
@@ -2857,16 +2842,7 @@ sub setup_schema_pair_dc
 						    "");
 
 	my $samba_tool =  Samba::bindir_path($self, "samba-tool");
-	my $cmd_vars = "NSS_WRAPPER_HOSTS='$env->{NSS_WRAPPER_HOSTS}' ";
-	$cmd_vars .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$env->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-	if (defined($env->{RESOLV_WRAPPER_CONF})) {
-		$cmd_vars .= "RESOLV_WRAPPER_CONF=\"$env->{RESOLV_WRAPPER_CONF}\" ";
-	} else {
-		$cmd_vars .= "RESOLV_WRAPPER_HOSTS=\"$env->{RESOLV_WRAPPER_HOSTS}\" ";
-	}
-	$cmd_vars .= "KRB5_CONFIG=\"$env->{KRB5_CONFIG}\" ";
-	$cmd_vars .= "KRB5CCNAME=\"$env->{KRB5_CCACHE}\" ";
-	$cmd_vars .= "RESOLV_CONF=\"$env->{RESOLV_CONF}\" ";
+	my $cmd_vars = $self->get_cmd_env_vars($env);
 
 	my $join_cmd = $cmd_vars;
 	$join_cmd .= "$samba_tool domain join $env->{CONFIGURATION} $dcvars->{REALM} DC --realm=$dcvars->{REALM}";
@@ -3008,17 +2984,10 @@ sub create_backup
 	my ($self, $env, $dcvars, $backupdir, $backup_cmd) = @_;
 
 	# get all the env variables we pass in with the samba-tool command
-	my $cmd_env = "NSS_WRAPPER_HOSTS='$env->{NSS_WRAPPER_HOSTS}' ";
-	$cmd_env .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$env->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-	if (defined($env->{RESOLV_WRAPPER_CONF})) {
-		$cmd_env .= "RESOLV_WRAPPER_CONF=\"$env->{RESOLV_WRAPPER_CONF}\" ";
-	} else {
-		$cmd_env .= "RESOLV_WRAPPER_HOSTS=\"$env->{RESOLV_WRAPPER_HOSTS}\" ";
-	}
-	$cmd_env .= "RESOLV_CONF=\"$env->{RESOLV_CONF}\" ";
 	# Note: use the backupfrom-DC's krb5.conf to do the backup
-	$cmd_env .= " KRB5_CONFIG=\"$dcvars->{KRB5_CONFIG}\" ";
-	$cmd_env .= "KRB5CCNAME=\"$env->{KRB5_CCACHE}\" ";
+	my $overwrite = undef;
+	$overwrite->{KRB5_CONFIG} = $dcvars->{KRB5_CONFIG};
+	my $cmd_env = $self->get_cmd_env_vars($env, $overwrite);
 
 	# use samba-tool to create a backup from the 'backupfromdc' DC
 	my $cmd = "";
diff --git a/selftest/tests.py b/selftest/tests.py
index 6918e1306c3..2b65943b2ed 100644
--- a/selftest/tests.py
+++ b/selftest/tests.py
@@ -398,6 +398,8 @@ plantestsuite("samba.unittests.util_paths", "none",
               [os.path.join(bindir(), "default/lib/util/test_util_paths")])
 plantestsuite("samba.unittests.util", "none",
               [os.path.join(bindir(), "default/lib/util/test_util")])
+plantestsuite("samba.unittests.memcache", "none",
+              [os.path.join(bindir(), "default/lib/util/test_memcache")])
 plantestsuite("samba.unittests.ntlm_check", "none",
               [os.path.join(bindir(), "default/libcli/auth/test_ntlm_check")])
 plantestsuite("samba.unittests.gnutls", "none",
diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c
index 9427c05f573..4686b29111e 100644
--- a/source3/auth/auth_util.c
+++ b/source3/auth/auth_util.c
@@ -485,6 +485,14 @@ NTSTATUS create_local_token(TALLOC_CTX *mem_ctx,
 		return NT_STATUS_LOGON_FAILURE;
 	}
 
+	if (!is_allowed_domain(server_info->info3->base.logon_domain.string)) {
+		DBG_NOTICE("Authentication failed for user [%s] "
+			   "from firewalled domain [%s]\n",
+			   server_info->info3->base.account_name.string,
+			   server_info->info3->base.logon_domain.string);
+		return NT_STATUS_AUTHENTICATION_FIREWALL_FAILED;
+	}
+
 	if (server_info->cached_session_info != NULL) {
 		session_info = copy_session_info(mem_ctx,
 				server_info->cached_session_info);
diff --git a/source3/include/proto.h b/source3/include/proto.h
index 12aa392abae..de5d1be5208 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -322,6 +322,7 @@ struct passwd *Get_Pwnam_alloc(TALLOC_CTX *mem_ctx, const char *user);
 /* The following definitions come from lib/util_names.c  */
 const char *get_global_sam_name(void);
 const char *my_sam_name(void);
+bool is_allowed_domain(const char *domain_name);
 
 /* The following definitions come from lib/util.c  */
 
diff --git a/source3/lib/g_lock.c b/source3/lib/g_lock.c
index c36539393e1..36b527706da 100644
--- a/source3/lib/g_lock.c
+++ b/source3/lib/g_lock.c
@@ -646,8 +646,8 @@ static void g_lock_lock_retry(struct tevent_req *subreq)
 	struct g_lock_lock_state *state = tevent_req_data(
 		req, struct g_lock_lock_state);
 	struct g_lock_lock_fn_state fn_state;
-	struct server_id blocker;
-	bool blockerdead;
+	struct server_id blocker = { .pid = 0 };
+	bool blockerdead = false;
 	NTSTATUS status;
 
 	status = dbwrap_watched_watch_recv(subreq, &blockerdead, &blocker);
diff --git a/source3/lib/util_names.c b/source3/lib/util_names.c
index 15236c913df..630a25875c7 100644
--- a/source3/lib/util_names.c
+++ b/source3/lib/util_names.c
@@ -182,3 +182,23 @@ const char *my_sam_name(void)
 
 	return lp_workgroup();
 }
+
+bool is_allowed_domain(const char *domain_name)
+{
+	const char **ignored_domains = NULL;
+	const char **dom = NULL;
+
+	ignored_domains = lp_parm_string_list(-1,
+					      "winbind",
+					      "ignore domains",
+					      NULL);
+
+	for (dom = ignored_domains; dom != NULL && *dom != NULL; dom++) {
+		if (gen_fnmatch(*dom, domain_name) == 0) {
+			DBG_NOTICE("Ignoring domain '%s'\n", domain_name);
+			return false;
+		}
+	}
+
+	return true;
+}
diff --git a/source3/libsmb/clidfs.c b/source3/libsmb/clidfs.c
index 4495a027830..3cc52cc5ac9 100644
--- a/source3/libsmb/clidfs.c
+++ b/source3/libsmb/clidfs.c
@@ -1230,6 +1230,7 @@ bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
 	if (force_encrypt) {
 		status = cli_cm_force_encryption_creds(cli, creds, "IPC$");
 		if (!NT_STATUS_IS_OK(status)) {
+			cli_tdis(cli);
 			cli_state_restore_tcon(cli, orig_tcon);
 			return false;
 		}
diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c
index 4412651f505..8ded372d0ba 100644
--- a/source3/libsmb/clientgen.c
+++ b/source3/libsmb/clientgen.c
@@ -352,11 +352,37 @@ uint32_t cli_state_set_tid(struct cli_state *cli, uint32_t tid)
 
 struct smbXcli_tcon *cli_state_save_tcon(struct cli_state *cli)
 {
+	/*
+	 * Note. This used to make a deep copy of either
+	 * cli->smb2.tcon or cli->smb1.tcon, but this leaves
+	 * the original pointer in place which will then get
+	 * TALLOC_FREE()'d when the new connection is made on
+	 * this cli_state.
+	 *
+	 * As there may be pipes open on the old connection with
+	 * talloc'ed state allocated using the tcon pointer as a
+	 * parent we can't deep copy and then free this as that
+	 * closes the open pipes.
+	 *
+	 * This call is used to temporarily swap out a tcon pointer
+	 * to allow a new tcon on the same cli_state.
+	 *
+	 * Just return the raw pointer and set the old value to NULL.
+	 * We know we MUST be calling cli_state_restore_tcon() below
+	 * to restore before closing the session.
+	 *
+	 * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=13992
+	 */
+	struct smbXcli_tcon *tcon_ret = NULL;
+
 	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-		return smbXcli_tcon_copy(cli, cli->smb2.tcon);
+		tcon_ret = cli->smb2.tcon;
+		cli->smb2.tcon = NULL; /* *Not* TALLOC_FREE(). */
 	} else {
-		return smbXcli_tcon_copy(cli, cli->smb1.tcon);
+		tcon_ret = cli->smb1.tcon;
+		cli->smb1.tcon = NULL; /* *Not* TALLOC_FREE(). */
 	}
+	return tcon_ret;
 }
 
 void cli_state_restore_tcon(struct cli_state *cli, struct smbXcli_tcon *tcon)
diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c
index 1c4d3a42221..d4c27e4d654 100644
--- a/source3/locking/share_mode_lock.c
+++ b/source3/locking/share_mode_lock.c
@@ -2256,7 +2256,7 @@ static bool share_mode_entry_do(
 	struct locking_tdb_data *ltdb = NULL;
 	size_t idx;
 	bool found = false;
-	bool modified;
+	bool modified = false;
 	struct share_mode_entry e;
 	uint8_t *e_ptr = NULL;
 	bool have_share_modes;
diff --git a/source3/modules/nfs4_acls.c b/source3/modules/nfs4_acls.c
index 7f32e681694..c7808037a09 100644
--- a/source3/modules/nfs4_acls.c
+++ b/source3/modules/nfs4_acls.c
@@ -997,6 +997,7 @@ NTSTATUS smb_set_nt_acl_nfs4(vfs_handle_struct *handle, files_struct *fsp,
 	}
 
 	if (security_descriptor_with_ms_nfs(psd)) {
+		TALLOC_FREE(frame);
 		return NT_STATUS_OK;
 	}
 
diff --git a/source3/modules/vfs_error_inject.c b/source3/modules/vfs_error_inject.c
index 04880ffd5ab..d8731c29610 100644
--- a/source3/modules/vfs_error_inject.c
+++ b/source3/modules/vfs_error_inject.c
@@ -30,6 +30,7 @@ struct unix_error_map {
 	{	"ESTALE",	ESTALE	},
 	{	"EBADF",	EBADF	},
 	{	"EINTR",	EINTR	},
+	{	"EACCES",	EACCES	},
 };
 
 static int find_unix_error_from_string(const char *err_str)
@@ -122,10 +123,46 @@ static int vfs_error_inject_openat(struct vfs_handle_struct *handle,
 	return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname, fsp, flags, mode);
 }
 
+static int vfs_error_inject_unlinkat(struct vfs_handle_struct *handle,
+				     struct files_struct *dirfsp,
+				     const struct smb_filename *smb_fname,
+				     int flags)
+{
+	struct smb_filename *parent_fname = NULL;
+	int error = inject_unix_error("unlinkat", handle);
+	int ret;
+	bool ok;
+
+	if (error == 0) {
+		return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags);
+	}
+
+	ok = parent_smb_fname(talloc_tos(), smb_fname, &parent_fname, NULL);
+	if (!ok) {
+		return -1;
+	}
+
+	ret = SMB_VFS_STAT(handle->conn, parent_fname);
+	if (ret != 0) {
+		TALLOC_FREE(parent_fname);
+		return -1;
+	}
+
+	if (parent_fname->st.st_ex_uid == get_current_uid(dirfsp->conn)) {
+		TALLOC_FREE(parent_fname);
+		return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags);
+	}
+
+	TALLOC_FREE(parent_fname);
+	errno = error;
+	return -1;
+}
+
 static struct vfs_fn_pointers vfs_error_inject_fns = {
 	.chdir_fn = vfs_error_inject_chdir,
 	.pwrite_fn = vfs_error_inject_pwrite,
 	.openat_fn = vfs_error_inject_openat,
+	.unlinkat_fn = vfs_error_inject_unlinkat,
 };
 
 static_decl_vfs;
diff --git a/source3/modules/vfs_virusfilter.c b/source3/modules/vfs_virusfilter.c
index dc3f040363d..466aec920be 100644
--- a/source3/modules/vfs_virusfilter.c
+++ b/source3/modules/vfs_virusfilter.c
@@ -267,18 +267,21 @@ static int virusfilter_vfs_connect(
 
 	infected_file_command = lp_parm_const_string(
 		snum, "virusfilter", "infected file command", NULL);
-	config->infected_file_command = talloc_strdup(config, infected_file_command);
-	if (config->infected_file_command == NULL) {
-		DBG_ERR("virusfilter-vfs: out of memory!\n");
-		return -1;
+	if (infected_file_command != NULL) {
+		config->infected_file_command = talloc_strdup(config, infected_file_command);
+		if (config->infected_file_command == NULL) {
+			DBG_ERR("virusfilter-vfs: out of memory!\n");
+			return -1;
+		}
 	}
-
 	scan_error_command = lp_parm_const_string(
 		snum, "virusfilter", "scan error command", NULL);
-	config->scan_error_command = talloc_strdup(config, scan_error_command);
-	if (config->scan_error_command == NULL) {
-		DBG_ERR("virusfilter-vfs: out of memory!\n");
-		return -1;
+	if (scan_error_command != NULL) {
+		config->scan_error_command = talloc_strdup(config, scan_error_command);
+		if (config->scan_error_command == NULL) {
+			DBG_ERR("virusfilter-vfs: out of memory!\n");
+			return -1;
+		}
 	}
 
 	config->block_access_on_error = lp_parm_bool(
@@ -290,10 +293,12 @@ static int virusfilter_vfs_connect(
 	quarantine_dir = lp_parm_const_string(
 		snum, "virusfilter", "quarantine directory",
 		tmp ? tmp : "/tmp/.quarantine");
-	config->quarantine_dir = talloc_strdup(config, quarantine_dir);
-	if (config->quarantine_dir == NULL) {
-		DBG_ERR("virusfilter-vfs: out of memory!\n");
-		return -1;
+	if (quarantine_dir != NULL) {
+		config->quarantine_dir = talloc_strdup(config, quarantine_dir);
+		if (config->quarantine_dir == NULL) {
+			DBG_ERR("virusfilter-vfs: out of memory!\n");
+			return -1;
+		}
 	}
 
 	if (tmp != config->quarantine_dir) {
@@ -311,42 +316,50 @@ static int virusfilter_vfs_connect(
 	quarantine_prefix = lp_parm_const_string(
 		snum, "virusfilter", "quarantine prefix",
 		VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
-	config->quarantine_prefix = talloc_strdup(config, quarantine_prefix);
-	if (config->quarantine_prefix == NULL) {
-		DBG_ERR("virusfilter-vfs: out of memory!\n");
-		return -1;
+	if (quarantine_prefix != NULL) {
+		config->quarantine_prefix = talloc_strdup(config, quarantine_prefix);
+		if (config->quarantine_prefix == NULL) {
+			DBG_ERR("virusfilter-vfs: out of memory!\n");
+			return -1;
+		}
 	}
 
 	quarantine_suffix = lp_parm_const_string(
 		snum, "virusfilter", "quarantine suffix",
 		VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
-	config->quarantine_suffix = talloc_strdup(config, quarantine_suffix);
-	if (config->quarantine_suffix == NULL) {
-		DBG_ERR("virusfilter-vfs: out of memory!\n");
-		return -1;
+	if (quarantine_suffix != NULL) {
+		config->quarantine_suffix = talloc_strdup(config, quarantine_suffix);
+		if (config->quarantine_suffix == NULL) {
+			DBG_ERR("virusfilter-vfs: out of memory!\n");
+			return -1;
+		}
 	}
 
 	/*
 	 * Make sure prefixes and suffixes do not contain directory
 	 * delimiters
 	 */
-	sret = strstr(config->quarantine_prefix, "/");
-	if (sret != NULL) {
-		DBG_ERR("quarantine prefix must not contain directory "
-			"delimiter(s) such as '/' (%s replaced with %s)\n",
-			config->quarantine_prefix,
-			VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
-		config->quarantine_prefix =
-			VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX;
-	}
-	sret = strstr(config->quarantine_suffix, "/");
-	if (sret != NULL) {
-		DBG_ERR("quarantine suffix must not contain directory "
-			"delimiter(s) such as '/' (%s replaced with %s)\n",
-			config->quarantine_suffix,
-			VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
-		config->quarantine_suffix =
-			VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX;
+	if (config->quarantine_prefix != NULL) {
+		sret = strstr(config->quarantine_prefix, "/");
+		if (sret != NULL) {
+			DBG_ERR("quarantine prefix must not contain directory "
+				"delimiter(s) such as '/' (%s replaced with %s)\n",
+				config->quarantine_prefix,
+				VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX);
+			config->quarantine_prefix =
+				VIRUSFILTER_DEFAULT_QUARANTINE_PREFIX;
+		}
+	}
+	if (config->quarantine_suffix != NULL) {
+		sret = strstr(config->quarantine_suffix, "/");
+		if (sret != NULL) {
+			DBG_ERR("quarantine suffix must not contain directory "
+				"delimiter(s) such as '/' (%s replaced with %s)\n",
+				config->quarantine_suffix,
+				VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX);
+			config->quarantine_suffix =
+				VIRUSFILTER_DEFAULT_QUARANTINE_SUFFIX;
+		}
 	}
 
 	config->quarantine_keep_tree = lp_parm_bool(
@@ -358,42 +371,50 @@ static int virusfilter_vfs_connect(
 	rename_prefix = lp_parm_const_string(
 		snum, "virusfilter", "rename prefix",
 		VIRUSFILTER_DEFAULT_RENAME_PREFIX);
-	config->rename_prefix = talloc_strdup(config, rename_prefix);
-	if (config->rename_prefix == NULL) {
-		DBG_ERR("virusfilter-vfs: out of memory!\n");
-		return -1;
+	if (rename_prefix != NULL) {
+		config->rename_prefix = talloc_strdup(config, rename_prefix);
+		if (config->rename_prefix == NULL) {
+			DBG_ERR("virusfilter-vfs: out of memory!\n");
+			return -1;
+		}
 	}
 
 	rename_suffix = lp_parm_const_string(
 		snum, "virusfilter", "rename suffix",
 		VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
-	config->rename_suffix = talloc_strdup(config, rename_suffix);
-	if (config->rename_suffix == NULL) {
-		DBG_ERR("virusfilter-vfs: out of memory!\n");
-		return -1;
+	if (rename_suffix != NULL) {
+		config->rename_suffix = talloc_strdup(config, rename_suffix);
+		if (config->rename_suffix == NULL) {
+			DBG_ERR("virusfilter-vfs: out of memory!\n");
+			return -1;
+		}
 	}
 
 	/*
 	 * Make sure prefixes and suffixes do not contain directory
 	 * delimiters
 	 */
-	sret = strstr(config->rename_prefix, "/");
-	if (sret != NULL) {
-		DBG_ERR("rename prefix must not contain directory "
-			"delimiter(s) such as '/' (%s replaced with %s)\n",
-			config->rename_prefix,
-			VIRUSFILTER_DEFAULT_RENAME_PREFIX);
-		config->rename_prefix =
-			VIRUSFILTER_DEFAULT_RENAME_PREFIX;
-	}
-	sret = strstr(config->rename_suffix, "/");
-	if (sret != NULL) {
-		DBG_ERR("rename suffix must not contain directory "
-			"delimiter(s) such as '/' (%s replaced with %s)\n",
-			config->rename_suffix,
-			VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
-		config->rename_suffix =
-			VIRUSFILTER_DEFAULT_RENAME_SUFFIX;
+	if (config->rename_prefix != NULL) {
+		sret = strstr(config->rename_prefix, "/");
+		if (sret != NULL) {
+			DBG_ERR("rename prefix must not contain directory "
+				"delimiter(s) such as '/' (%s replaced with %s)\n",
+				config->rename_prefix,
+				VIRUSFILTER_DEFAULT_RENAME_PREFIX);
+			config->rename_prefix =
+				VIRUSFILTER_DEFAULT_RENAME_PREFIX;
+		}
+	}
+	if (config->rename_suffix != NULL) {
+		sret = strstr(config->rename_suffix, "/");
+		if (sret != NULL) {
+			DBG_ERR("rename suffix must not contain directory "
+				"delimiter(s) such as '/' (%s replaced with %s)\n",
+				config->rename_suffix,
+				VIRUSFILTER_DEFAULT_RENAME_SUFFIX);
+			config->rename_suffix =
+				VIRUSFILTER_DEFAULT_RENAME_SUFFIX;
+		}
 	}
 
 	config->infected_open_errno = lp_parm_int(
@@ -410,10 +431,12 @@ static int virusfilter_vfs_connect(
 
 	socket_path = lp_parm_const_string(
 		snum, "virusfilter", "socket path", NULL);
-	config->socket_path = talloc_strdup(config, socket_path);
-	if (config->socket_path == NULL) {
-		DBG_ERR("virusfilter-vfs: out of memory!\n");
-		return -1;
+	if (socket_path != NULL) {
+		config->socket_path = talloc_strdup(config, socket_path);
+		if (config->socket_path == NULL) {
+			DBG_ERR("virusfilter-vfs: out of memory!\n");
+			return -1;
+		}
 	}
 
 	/* canonicalize socket_path */
diff --git a/source3/script/tests/test_force_user_unlink.sh b/source3/script/tests/test_force_user_unlink.sh
new file mode 100755
index 00000000000..86076535497
--- /dev/null
+++ b/source3/script/tests/test_force_user_unlink.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# Test unlink on share with "force user"
+#
+# Copyright (C) 2021 Ralph Boehme
+
+incdir=$(dirname $0)/../../../testprogs/blackbox
+. $incdir/subunit.sh
+. $incdir/common_test_fns.inc
+
+smbclient="$BINDIR/smbclient"
+error_inject_conf=$(dirname ${SMB_CONF_PATH})/error_inject.conf
+failed=0
+
+test_forced_user_can_delete() {
+    out=$($smbclient -U $DOMAIN/$USERNAME%$PASSWORD //$SERVER_IP/force_user_error_inject -c "rm dir/file")
+    if [ $? -ne 0 ] ; then
+	echo $out
+	return 1
+    fi
+    tmp=$(echo $out | grep NT_STATUS_ )
+    if [ $? -eq 0 ] ; then
+	return 1
+    fi
+    return 0
+}
+
+echo "error_inject:unlinkat = EACCES" > ${error_inject_conf}
+
+$smbclient -U $DOMAIN/$USERNAME%$PASSWORD //$SERVER_IP/force_user_error_inject -c "mkdir dir" || failed=`expr $failed + 1`
+$smbclient -U $DOMAIN/$USERNAME%$PASSWORD //$SERVER_IP/force_user_error_inject -c "put WHATSNEW.txt dir/file" || failed=`expr $failed + 1`
+
+testit "test_forced_user_can_delete" test_forced_user_can_delete || failed=`expr $failed + 1`
+
+rm ${error_inject_conf}
+
+# Clean up after ourselves.
+$smbclient -U $DOMAIN/$USERNAME%$PASSWORD //$SERVER_IP/force_user_error_inject -c "del dir/file; rmdir dir"
+
+testok $0 $failed
diff --git a/source3/script/tests/test_net_rpc_share_allowedusers.sh b/source3/script/tests/test_net_rpc_share_allowedusers.sh
index 5dd382d4c51..d22c7580681 100755
--- a/source3/script/tests/test_net_rpc_share_allowedusers.sh
+++ b/source3/script/tests/test_net_rpc_share_allowedusers.sh
@@ -26,5 +26,25 @@ testit_grep "net_rpc_share_allowedusers" '^print\$$' $net usersidlist | $VALGRIN
 testit_grep "net_rpc_share_allowedusers" '^print\$$' $net usersidlist | $VALGRIND $net rpc share allowedusers -S$SERVER -U$USERNAME%$PASSWORD $ADDARGS - 'print$' || failed=`expr $failed + 1`
 # Check user "user1" is allowed to read share "tmp".
 testit_grep "net_rpc_share_allowedusers" '^ user1$' $net usersidlist | $VALGRIND $net rpc share allowedusers -S$SERVER -U$USERNAME%$PASSWORD $ADDARGS || failed=`expr $failed + 1`
+#
+# Subtle extra test for bug https://bugzilla.samba.org/show_bug.cgi?id=13992
+#
+# '^ user1$' must appear MORE THAN ONCE, as it can read more than one
+# share. The previous test found user1, but only once as the bug only
+# allows reading the security descriptor for one share, and we were
+# unlucky that the first share security descriptor returned allows
+# user1 to read from it.
+#
+subunit_start_test "net_rpc_share_allowedusers"
+multi_userout=`$net usersidlist | $VALGRIND $net rpc share allowedusers -S$SERVER -U$USERNAME%$PASSWORD $ADDARGS`
+num_matches=`echo "$multi_userout" | grep -c '^ user1$'`
+if [ "$num_matches" -gt "1" ]
+then
+	subunit_pass_test "net_rpc_share_allowedusers"
+else
+	echo "net_rpc_share_allowedusers only found $num_matches shares readable by user1. Should be greater than one.\n"
+	failed=`expr $failed + 1`
+	echo "$multi_userout" | subunit_fail_test "net_rpc_share_allowedusers"
+fi
 
 testok $0 $failed
diff --git a/source3/script/tests/test_winbind_ignore_domains.sh b/source3/script/tests/test_winbind_ignore_domains.sh
new file mode 100755
index 00000000000..adce8abb09c
--- /dev/null
+++ b/source3/script/tests/test_winbind_ignore_domains.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+incdir=`dirname $0`/../../../testprogs/blackbox
+. $incdir/subunit.sh
+. $incdir/common_test_fns.inc
+
+failed=0
+
+smbclient="$BINDIR/smbclient"
+smbcontrol="$BINDIR/smbcontrol"
+ldbmodify="$BINDIR/ldbmodify"
+ldbsearch="$BINDIR/ldbsearch"
+wbinfo="$BINDIR/wbinfo"
+global_inject_conf=$(dirname $SMB_CONF_PATH)/global_inject.conf
+SERVER_FQDN=$(echo "$SERVER.$REALM" | awk '{print tolower($0)}')
+
+TRUST_BASE_DN=$($ldbsearch -H ldap://$TRUST_SERVER -b "" -s base defaultNamingContext | awk '/^defaultNamingContext/ {print $2}')
+if [ $? -ne 0 ] ; then
+    echo "Could not find trusted base DN" | subunit_fail_test "test_idmap_ad"
+    exit 1
+fi
+
+#
+# Add POSIX ids to trusted domain
+#
+add_posix_ids() {
+cat <<EOF | $ldbmodify -H ldap://$TRUST_SERVER \
+       -U "$TRUST_DOMAIN\Administrator%$TRUST_PASSWORD"
+dn: CN=Administrator,CN=Users,$TRUST_BASE_DN
+changetype: modify
+add: uidNumber
+uidNumber: 2500000
+EOF
+
+cat <<EOF | $ldbmodify -H ldap://$TRUST_SERVER \
+       -U "$TRUST_DOMAIN\Administrator%$TRUST_PASSWORD"
+dn: CN=Domain Users,CN=Users,$TRUST_BASE_DN
+changetype: modify
+add: gidNumber
+gidNumber: 2500001
+EOF
+
+cat <<EOF | $ldbmodify -H ldap://$TRUST_SERVER \
+       -U "$TRUST_DOMAIN\Administrator%$TRUST_PASSWORD"
+dn: CN=Domain Admins,CN=Users,$TRUST_BASE_DN
+changetype: modify
+add: gidNumber
+gidNumber: 2500002
+EOF
+}
+
+#
+# Remove POSIX ids from trusted domain
+#
+remove_posix_ids() {
+cat <<EOF | $ldbmodify -H ldap://$TRUST_SERVER \
+       -U "$TRUST_DOMAIN\Administrator%$TRUST_PASSWORD"
+dn: CN=Administrator,CN=Users,$TRUST_BASE_DN
+changetype: modify
+delete: uidNumber
+uidNumber: 2500000
+EOF
+
+cat <<EOF | $ldbmodify -H ldap://$TRUST_SERVER \
+       -U "$TRUST_DOMAIN\Administrator%$TRUST_PASSWORD"
+dn: CN=Domain Users,CN=Users,$TRUST_BASE_DN
+changetype: modify
+delete: gidNumber
+gidNumber: 2500001
+EOF
+
+cat <<EOF | $ldbmodify -H ldap://$TRUST_SERVER \
+       -U "$TRUST_DOMAIN\Administrator%$TRUST_PASSWORD"
+dn: CN=Domain Admins,CN=Users,$TRUST_BASE_DN
+changetype: modify
+delete: gidNumber
+gidNumber: 2500002
+EOF
+}
+
+add_posix_ids
+
+echo "" > $global_inject_conf
+$smbcontrol winbindd reload-config
+$wbinfo -p
+
+test_smbclient "test_winbind_ignore_domains_ok_ntlm_ip" "ls" "//$SERVER_IP/tmp" -U $TRUST_DOMAIN/$TRUST_USERNAME%$TRUST_PASSWORD || failed=`expr $failed + 1`
+test_smbclient "test_winbind_ignore_domains_ok_ntlm_fqdn" "ls" "//$SERVER_FQDN/tmp" -U $TRUST_DOMAIN/$TRUST_USERNAME%$TRUST_PASSWORD || failed=`expr $failed + 1`
+test_smbclient "test_winbind_ignore_domains_ok_krb5" "ls" "//$SERVER_FQDN/tmp" -U $TRUST_USERNAME@$TRUST_REALM%$TRUST_PASSWORD -k || failed=`expr $failed + 1`
+
+echo "winbind:ignore domains = $TRUST_DOMAIN" > $global_inject_conf
+$smbcontrol winbindd reload-config
+$wbinfo -p
+
+test_smbclient_expect_failure "test_winbind_ignore_domains_fail_ntlm_ip" "ls" "//$SERVER_IP/tmp" -U $TRUST_DOMAIN/$TRUST_USERNAME%$TRUST_PASSWORD || failed=`expr $failed + 1`
+test_smbclient_expect_failure "test_winbind_ignore_domains_fail_ntlm_fqdn" "ls" "//$SERVER_FQDN/tmp" -U $TRUST_DOMAIN/$TRUST_USERNAME%$TRUST_PASSWORD || failed=`expr $failed + 1`
+test_smbclient_expect_failure "test_winbind_ignore_domains_fail_krb5" "ls" "//$SERVER_FQDN/tmp" -U $TRUST_USERNAME@$TRUST_REALM%$TRUST_PASSWORD -k || failed=`expr $failed + 1`
+
+echo "" > $global_inject_conf
+$smbcontrol winbindd reload-config
+$wbinfo -p
+remove_posix_ids
+
+testok $0 $failed
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 27dc7587b17..47e914b1009 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -487,13 +487,13 @@ for env in ["fileserver"]:
                   [os.path.join(samba3srcdir, "script/tests/test_smbclient_tarmode.pl"),
                    '-n', '$SERVER', '-i', '$SERVER_IP', '-s', 'tarmode2',
                    '-u', '$USERNAME', '-p', '$PASSWORD', '-l', '$LOCAL_PATH/tarmode2',
-                   '-d', '$PREFIX', '-b', smbclient3,
+                   '-d', 'smbclient_tar.NT1', '-b', smbclient3,
                    '--subunit', '--', configuration, '-mNT1'])
     plantestsuite("samba3.blackbox.smbclient_tar.SMB3", env,
                   [os.path.join(samba3srcdir, "script/tests/test_smbclient_tarmode.pl"),
                    '-n', '$SERVER', '-i', '$SERVER_IP', '-s', 'tarmode2',
                    '-u', '$USERNAME', '-p', '$PASSWORD', '-l', '$LOCAL_PATH/tarmode2',
-                   '-d', '$PREFIX', '-b', smbclient3,
+                   '-d', 'smbclient_tar.SMB3', '-b', smbclient3,
                    '--subunit', '--', configuration, '-mSMB3'])
 
 for env in ["fileserver:local"]:
@@ -991,6 +991,9 @@ plantestsuite("samba3.blackbox.smbd_no_krb5", "ad_member:local",
               [os.path.join(samba3srcdir, "script/tests/test_smbd_no_krb5.sh"),
                smbclient3, '$SERVER', "$DC_USERNAME", "$DC_PASSWORD", "$PREFIX"])
 
+plantestsuite("samba3.blackbox.winbind_ignore_domain", "ad_member_idmap_ad:local",
+              [os.path.join(samba3srcdir, "script/tests/test_winbind_ignore_domains.sh")])
+
 plantestsuite("samba3.blackbox.durable_v2_delay", "simpleserver:local",
               [os.path.join(samba3srcdir, "script/tests/test_durable_handle_reconnect.sh")])
 
@@ -1122,6 +1125,11 @@ plantestsuite(
      "",
      "-b $PREFIX/clusteredmember_smb1/unclists/tmp.txt -N 5 -o 10"])
 
+plantestsuite("samba3.blackbox.force-user-unlink",
+              "maptoguest:local",
+              [os.path.join(samba3srcdir,
+                            "script/tests/test_force_user_unlink.sh")])
+
 def planclusteredmembertestsuite(tname, prefix):
     '''Define a clustered test for the clusteredmember environment'''
 
diff --git a/source3/smbd/close.c b/source3/smbd/close.c
index 9974877edc2..43762555b35 100644
--- a/source3/smbd/close.c
+++ b/source3/smbd/close.c
@@ -341,21 +341,13 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
 
 	if (fsp->fsp_flags.initial_delete_on_close &&
 			!is_delete_on_close_set(lck, fsp->name_hash)) {
-		struct auth_session_info *session_info = NULL;
-
 		/* Initial delete on close was set and no one else
 		 * wrote a real delete on close. */
 
-		status = smbXsrv_session_info_lookup(conn->sconn->client,
-						     fsp->vuid,
-						     &session_info);
-		if (!NT_STATUS_IS_OK(status)) {
-			return NT_STATUS_INTERNAL_ERROR;
-		}
 		fsp->fsp_flags.delete_on_close = true;
 		set_delete_on_close_lck(fsp, lck,
-					session_info->security_token,
-					session_info->unix_token);
+					fsp->conn->session_info->security_token,
+					fsp->conn->session_info->unix_token);
 	}
 
 	delete_file = is_delete_on_close_set(lck, fsp->name_hash) &&
@@ -1176,24 +1168,15 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
 	}
 
 	if (fsp->fsp_flags.initial_delete_on_close) {
-		struct auth_session_info *session_info = NULL;
-
 		/* Initial delete on close was set - for
 		 * directories we don't care if anyone else
 		 * wrote a real delete on close. */
 
-		status = smbXsrv_session_info_lookup(fsp->conn->sconn->client,
-						     fsp->vuid,
-						     &session_info);
-		if (!NT_STATUS_IS_OK(status)) {
-			return NT_STATUS_INTERNAL_ERROR;
-		}
-
 		send_stat_cache_delete_message(fsp->conn->sconn->msg_ctx,
 					       fsp->fsp_name->base_name);
 		set_delete_on_close_lck(fsp, lck,
-					session_info->security_token,
-					session_info->unix_token);
+					fsp->conn->session_info->security_token,
+					fsp->conn->session_info->unix_token);
 		fsp->fsp_flags.delete_on_close = true;
 	}
 
diff --git a/source3/smbd/conn_idle.c b/source3/smbd/conn_idle.c
index ca697383877..56a6ef896fb 100644
--- a/source3/smbd/conn_idle.c
+++ b/source3/smbd/conn_idle.c
@@ -273,5 +273,13 @@ static void conn_force_tdis_done(struct tevent_req *req)
 	* uid in the meantime. Ensure we're still root.
 	*/
 	change_to_root_user();
-	reload_services(sconn, conn_snum_used, true);
+	/*
+	 * Use 'false' in the last parameter (test) to force
+	 * a full reload of services. Prevents
+	 * reload_services caching the fact it's
+	 * been called multiple times in a row.
+	 * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=14604
+	 * for details.
+	 */
+	reload_services(sconn, conn_snum_used, false);
 }
diff --git a/source3/smbd/pysmbd.c b/source3/smbd/pysmbd.c
index dd4a70ca256..2081a75d52c 100644
--- a/source3/smbd/pysmbd.c
+++ b/source3/smbd/pysmbd.c
@@ -1144,9 +1144,12 @@ static PyObject *py_smbd_create_file(PyObject *self, PyObject *args, PyObject *k
 	if (!NT_STATUS_IS_OK(status)) {
 		DBG_ERR("init_files_struct failed: %s\n",
 			nt_errstr(status));
+	} else if (fsp != NULL) {
+		SMB_VFS_CLOSE(fsp);
 	}
 
 	TALLOC_FREE(frame);
+	PyErr_NTSTATUS_NOT_OK_RAISE(status);
 	Py_RETURN_NONE;
 }
 
diff --git a/source3/torture/test_smb2.c b/source3/torture/test_smb2.c
index d5023d88cfd..3e25a562bd6 100644
--- a/source3/torture/test_smb2.c
+++ b/source3/torture/test_smb2.c
@@ -188,11 +188,11 @@ bool run_smb2_basic(int dummy)
 			      cli->timeout,
 			      cli->smb2.session,
 			      cli->smb2.tcon);
+	cli_state_restore_tcon(cli, saved_tcon);
 	if (!NT_STATUS_IS_OK(status)) {
 		printf("smb2cli_tdis returned %s\n", nt_errstr(status));
 		return false;
 	}
-	cli_state_restore_tcon(cli, saved_tcon);
 
 	status = smb2cli_tdis(cli->conn,
 			      cli->timeout,
diff --git a/source3/torture/torture.c b/source3/torture/torture.c
index 2a3133373e9..5e263797730 100644
--- a/source3/torture/torture.c
+++ b/source3/torture/torture.c
@@ -1343,6 +1343,7 @@ static bool run_tcon_test(int dummy)
 	if (!NT_STATUS_IS_OK(status)) {
 		printf("%s refused 2nd tree connect (%s)\n", host,
 		       nt_errstr(status));
+		cli_state_restore_tcon(cli, orig_tcon);
 		cli_shutdown(cli);
 		return False;
 	}
@@ -1395,6 +1396,8 @@ static bool run_tcon_test(int dummy)
 	status = cli_close(cli, fnum1);
 	if (!NT_STATUS_IS_OK(status)) {
 		printf("close failed (%s)\n", nt_errstr(status));
+		cli_state_restore_tcon(cli, orig_tcon);
+		cli_shutdown(cli);
 		return False;
 	}
 
@@ -1403,6 +1406,8 @@ static bool run_tcon_test(int dummy)
 	status = cli_tdis(cli);
 	if (!NT_STATUS_IS_OK(status)) {
 		printf("secondary tdis failed (%s)\n", nt_errstr(status));
+		cli_state_restore_tcon(cli, orig_tcon);
+		cli_shutdown(cli);
 		return False;
 	}
 
@@ -11714,7 +11719,7 @@ static bool run_uid_regression_test(int dummy)
 	int16_t old_vuid;
 	int32_t old_cnum;
 	bool correct = True;
-	struct smbXcli_tcon *orig_tcon = NULL;
+	struct smbXcli_tcon *tcon_copy = NULL;
 	NTSTATUS status;
 
 	printf("starting uid regression test\n");
@@ -11755,8 +11760,20 @@ static bool run_uid_regression_test(int dummy)
 	}
 
 	old_cnum = cli_state_get_tid(cli);
-	orig_tcon = cli_state_save_tcon(cli);
-	if (orig_tcon == NULL) {
+	/*
+	 * This is an SMB1-only test.
+	 * Copy the tcon, not "save/restore".
+	 *
+	 * In SMB1 the cli_tdis() below frees
+	 * cli->smb1.tcon so we need a copy
+	 * of the struct to put back for the
+	 * second tdis call with invalid vuid.
+	 *
+	 * This is a test-only hack. Real client code
+	 * uses cli_state_save_tcon()/cli_state_restore_tcon().
+	 */
+	tcon_copy = smbXcli_tcon_copy(cli, cli->smb1.tcon);
+	if (tcon_copy == NULL) {
 		correct = false;
 		goto out;
 	}
@@ -11772,11 +11789,11 @@ static bool run_uid_regression_test(int dummy)
 	} else {
 		d_printf("First tdis failed (%s)\n", nt_errstr(status));
 		correct = false;
-		cli_state_restore_tcon(cli, orig_tcon);
+		cli->smb1.tcon = tcon_copy;
 		goto out;
 	}
 
-	cli_state_restore_tcon(cli, orig_tcon);
+	cli->smb1.tcon = tcon_copy;
 	cli_state_set_uid(cli, old_vuid);
 	cli_state_set_tid(cli, old_cnum);
 
diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c
index 1e08237905a..bc5aec92385 100644
--- a/source3/winbindd/winbindd.c
+++ b/source3/winbindd/winbindd.c
@@ -102,7 +102,7 @@ struct imessaging_context *winbind_imessaging_context(void)
 
 /* Reload configuration */
 
-static bool reload_services_file(const char *lfile)
+bool winbindd_reload_services_file(const char *lfile)
 {
 	const struct loadparm_substitution *lp_sub =
 		loadparm_s3_global_substitution();
@@ -117,15 +117,15 @@ static bool reload_services_file(const char *lfile)
 		TALLOC_FREE(fname);
 	}
 
+	reopen_logs();
+	ret = lp_load_global(get_dyn_CONFIGFILE());
+
 	/* if this is a child, restore the logfile to the special
 	   name - <domain>, idmap, etc. */
 	if (lfile && *lfile) {
 		lp_set_logfile(lfile);
 	}
 
-	reopen_logs();
-	ret = lp_load_global(get_dyn_CONFIGFILE());
-
 	reopen_logs();
 	load_interfaces();
 	winbindd_setup_max_fds();
@@ -156,7 +156,7 @@ static void winbindd_status(void)
 
 /* Flush client cache */
 
-static void flush_caches(void)
+void winbindd_flush_caches(void)
 {
 	/* We need to invalidate cached user list entries on a SIGHUP 
            otherwise cached access denied errors due to restrict anonymous
@@ -363,7 +363,7 @@ static void winbindd_sig_hup_handler(struct tevent_context *ev,
 
 	DEBUG(1,("Reloading services after SIGHUP\n"));
 	flush_caches_noinit();
-	reload_services_file(file);
+	winbindd_reload_services_file(file);
 }
 
 bool winbindd_setup_sig_hup_handler(const char *lfile)
@@ -447,18 +447,6 @@ static bool winbindd_setup_sig_usr2_handler(void)
 	return true;
 }
 
-/* React on 'smbcontrol winbindd reload-config' in the same way as on SIGHUP*/
-static void msg_reload_services(struct messaging_context *msg,
-				void *private_data,
-				uint32_t msg_type,
-				struct server_id server_id,
-				DATA_BLOB *data)
-{
-        /* Flush various caches */
-	flush_caches();
-	reload_services_file((const char *) private_data);
-}
-
 /* React on 'smbcontrol winbindd shutdown' in the same way as on SIGTERM*/
 static void msg_shutdown(struct messaging_context *msg,
 			 void *private_data,
@@ -1420,7 +1408,8 @@ static void winbindd_register_handlers(struct messaging_context *msg_ctx,
 	/* React on 'smbcontrol winbindd reload-config' in the same way
 	   as to SIGHUP signal */
 	messaging_register(msg_ctx, NULL,
-			   MSG_SMB_CONF_UPDATED, msg_reload_services);
+			   MSG_SMB_CONF_UPDATED,
+			   winbindd_msg_reload_services_parent);
 	messaging_register(msg_ctx, NULL,
 			   MSG_SHUTDOWN, msg_shutdown);
 
@@ -1811,7 +1800,7 @@ int main(int argc, const char **argv)
 		exit(1);
 	}
 
-	if (!reload_services_file(NULL)) {
+	if (!winbindd_reload_services_file(NULL)) {
 		DEBUG(0, ("error opening config file\n"));
 		exit(1);
 	}
diff --git a/source3/winbindd/winbindd_dual.c b/source3/winbindd/winbindd_dual.c
index 47efe988d65..b1c86b2979c 100644
--- a/source3/winbindd/winbindd_dual.c
+++ b/source3/winbindd/winbindd_dual.c
@@ -927,6 +927,39 @@ void winbind_disconnect_dc_parent(struct messaging_context *msg_ctx,
 	forall_children(winbind_msg_relay_fn, &state);
 }
 
+static void winbindd_msg_reload_services_child(struct messaging_context *msg,
+					       void *private_data,
+					       uint32_t msg_type,
+					       struct server_id server_id,
+					       DATA_BLOB *data)
+{
+	DBG_DEBUG("Got reload-config message\n");
+	winbindd_reload_services_file((const char *)private_data);
+}
+
+/* React on 'smbcontrol winbindd reload-config' in the same way as on SIGHUP*/
+void winbindd_msg_reload_services_parent(struct messaging_context *msg,
+					 void *private_data,
+					 uint32_t msg_type,
+					 struct server_id server_id,
+					 DATA_BLOB *data)
+{
+	struct winbind_msg_relay_state state = {
+		.msg_ctx = msg,
+		.msg_type = msg_type,
+		.data = data,
+	};
+
+	DBG_DEBUG("Got reload-config message\n");
+
+        /* Flush various caches */
+	winbindd_flush_caches();
+
+	winbindd_reload_services_file((const char *)private_data);
+
+	forall_children(winbind_msg_relay_fn, &state);
+}
+
 /* Set our domains as offline and forward the offline message to our children. */
 
 struct winbind_msg_on_offline_state {
@@ -1760,6 +1793,10 @@ static bool fork_domain_child(struct winbindd_child *child)
 	messaging_register(global_messaging_context(), NULL,
 			   MSG_WINBIND_DISCONNECT_DC,
 			   winbind_msg_disconnect_dc);
+	messaging_register(global_messaging_context(),
+			   override_logfile ? NULL : child->logfilename,
+			   MSG_SMB_CONF_UPDATED,
+			   winbindd_msg_reload_services_child);
 
 	primary_domain = find_our_domain();
 
diff --git a/source3/winbindd/winbindd_pam.c b/source3/winbindd/winbindd_pam.c
index b5850a33b0f..c49033b375d 100644
--- a/source3/winbindd/winbindd_pam.c
+++ b/source3/winbindd/winbindd_pam.c
@@ -2403,6 +2403,15 @@ process_result:
 			goto done;
 		}
 
+		if (!is_allowed_domain(info3->base.logon_domain.string)) {
+			DBG_NOTICE("Authentication failed for user [%s] "
+				   "from firewalled domain [%s]\n",
+				   info3->base.account_name.string,
+				   info3->base.logon_domain.string);
+			result = NT_STATUS_AUTHENTICATION_FIREWALL_FAILED;
+			goto done;
+		}
+
 		result = append_auth_data(state->mem_ctx, state->response,
 					  state->request->flags,
 					  validation_level,
@@ -2756,6 +2765,16 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
 			goto done;
 		}
 
+		if (!is_allowed_domain(info3->base.logon_domain.string)) {
+			DBG_NOTICE("Authentication failed for user [%s] "
+				   "from firewalled domain [%s]\n",
+				   info3->base.account_name.string,
+				   info3->base.logon_domain.string);
+			state->response->data.auth.authoritative = true;
+			result = NT_STATUS_AUTHENTICATION_FIREWALL_FAILED;
+			goto done;
+		}
+
 		result = append_auth_data(state->mem_ctx, state->response,
 					  state->request->flags,
 					  validation_level,
@@ -2824,6 +2843,14 @@ enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact
 		goto done;
 	}
 
+	if (!is_allowed_domain(domain)) {
+		DBG_NOTICE("Authentication failed for user [%s] "
+			   "from firewalled domain [%s]\n",
+			   user, domain);
+		result = NT_STATUS_AUTHENTICATION_FIREWALL_FAILED;
+		goto done;
+	}
+
 	/* Change password */
 
 	oldpass = state->request->data.chauthtok.oldpass;
@@ -3085,6 +3112,15 @@ enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domai
 		fstrcpy(domain,lp_workgroup());
 	}
 
+	if (!is_allowed_domain(domain)) {
+		DBG_NOTICE("Authentication failed for user [%s] "
+			   "from firewalled domain [%s]\n",
+			   state->request->data.chng_pswd_auth_crap.user,
+			   domain);
+		result = NT_STATUS_AUTHENTICATION_FIREWALL_FAILED;
+		goto done;
+	}
+
 	if(!*user) {
 		fstrcpy(user, state->request->data.chng_pswd_auth_crap.user);
 	}
@@ -3287,6 +3323,14 @@ NTSTATUS winbindd_pam_auth_pac_verify(struct winbindd_cli_state *state,
 		return result;
 	}
 
+	if (!is_allowed_domain(info6->base.logon_domain.string)) {
+		DBG_NOTICE("Authentication failed for user [%s] "
+			   "from firewalled domain [%s]\n",
+			   info6->base.account_name.string,
+			   info6->base.logon_domain.string);
+		return NT_STATUS_AUTHENTICATION_FIREWALL_FAILED;
+	}
+
 	result = map_info6_to_validation(state->mem_ctx,
 					 info6,
 					 &validation_level,
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index 2a829b0171a..6d4ffa726f1 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -31,6 +31,8 @@ bool winbindd_setup_sig_hup_handler(const char *lfile);
 bool winbindd_use_idmap_cache(void);
 bool winbindd_use_cache(void);
 char *get_winbind_priv_pipe_dir(void);
+void winbindd_flush_caches(void);
+bool winbindd_reload_services_file(const char *lfile);
 
 /* The following definitions come from winbindd/winbindd_ads.c  */
 
@@ -341,6 +343,11 @@ void winbind_msg_ip_dropped_parent(struct messaging_context *msg_ctx,
 				   uint32_t msg_type,
 				   struct server_id server_id,
 				   DATA_BLOB *data);
+void winbindd_msg_reload_services_parent(struct messaging_context *msg,
+					 void *private_data,
+					 uint32_t msg_type,
+					 struct server_id server_id,
+					 DATA_BLOB *data);
 NTSTATUS winbindd_reinit_after_fork(const struct winbindd_child *myself,
 				    const char *logfilename);
 struct winbindd_domain *wb_child_domain(void);
diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c
index c2f02b74211..bec706f87de 100644
--- a/source3/winbindd/winbindd_util.c
+++ b/source3/winbindd/winbindd_util.c
@@ -123,8 +123,6 @@ static NTSTATUS add_trusted_domain(const char *domain_name,
 				   struct winbindd_domain **_d)
 {
 	struct winbindd_domain *domain = NULL;
-	const char **ignored_domains = NULL;
-	const char **dom = NULL;
 	int role = lp_server_role();
 	struct dom_sid_buf buf;
 
@@ -133,12 +131,8 @@ static NTSTATUS add_trusted_domain(const char *domain_name,
 		return NT_STATUS_INVALID_PARAMETER;
 	}
 
-	ignored_domains = lp_parm_string_list(-1, "winbind", "ignore domains", NULL);
-	for (dom=ignored_domains; dom && *dom; dom++) {
-		if (gen_fnmatch(*dom, domain_name) == 0) {
-			DEBUG(2,("Ignoring domain '%s'\n", domain_name));
-			return NT_STATUS_NO_SUCH_DOMAIN;
-		}
+	if (!is_allowed_domain(domain_name)) {
+		return NT_STATUS_NO_SUCH_DOMAIN;
 	}
 
 	/*
diff --git a/source3/wscript b/source3/wscript
index 9920432a360..563854c1d23 100644
--- a/source3/wscript
+++ b/source3/wscript
@@ -1244,7 +1244,7 @@ err:
 
 int main(void)
 {
-        uint64_t *hint, get_hint;
+        uint64_t hint, get_hint;
         int fd;
 
         fd = open(DATA, O_RDONLY | O_CREAT | O_EXCL);
@@ -1252,8 +1252,8 @@ int main(void)
             goto err;
         }
 
-        *hint = RWH_WRITE_LIFE_SHORT;
-        int ret = fcntl(fd, F_SET_RW_HINT, hint);
+        hint = RWH_WRITE_LIFE_SHORT;
+        int ret = fcntl(fd, F_SET_RW_HINT, &hint);
         if (ret == -1) {
             goto err;
         }
@@ -1267,8 +1267,8 @@ int main(void)
             goto err;
         }
 
-        *hint = RWH_WRITE_LIFE_EXTREME;
-        ret = fcntl(fd, F_SET_FILE_RW_HINT, hint);
+        hint = RWH_WRITE_LIFE_EXTREME;
+        ret = fcntl(fd, F_SET_FILE_RW_HINT, &hint);
         if (ret == -1) {
             goto err;
         }
diff --git a/source4/heimdal/lib/krb5/store.c b/source4/heimdal/lib/krb5/store.c
index 17de78e9e74..31afb23c983 100644
--- a/source4/heimdal/lib/krb5/store.c
+++ b/source4/heimdal/lib/krb5/store.c
@@ -270,6 +270,8 @@ krb5_storage_get_eof_code(krb5_storage *sp)
 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
 krb5_storage_free(krb5_storage *sp)
 {
+    if (sp == NULL)
+        return 0;
     if(sp->free)
 	(*sp->free)(sp);
     free(sp->data);
diff --git a/source4/selftest/provisions/release-4-5-0-pre1/expected-dbcheck-link-output-lost-deleted-user3.txt b/source4/selftest/provisions/release-4-5-0-pre1/expected-dbcheck-link-output-lost-deleted-user3.txt
index d014bfacae2..ea9b630df08 100644
--- a/source4/selftest/provisions/release-4-5-0-pre1/expected-dbcheck-link-output-lost-deleted-user3.txt
+++ b/source4/selftest/provisions/release-4-5-0-pre1/expected-dbcheck-link-output-lost-deleted-user3.txt
@@ -1,19 +1,19 @@
 Checking 232 objects
-SKIPING: object CN=fred\0ADEL:2301a64c-1122-5566-851e-12d4a711cfb4,OU=removed,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
+SKIPPING: object CN=fred\0ADEL:2301a64c-1122-5566-851e-12d4a711cfb4,OU=removed,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
 isDeleted: attid=0x00020030 version=1 invocation=4e4496a3-7fb8-4f97-8a33-d238db8b5e2d usn=3746 (local=3746) at Wed Jun 29 04:36:39 2016
-SKIPING: object CN=fred\0ADEL:2301a64c-5b42-4ca8-851e-12d4a711cfb4,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
+SKIPPING: object CN=fred\0ADEL:2301a64c-5b42-4ca8-851e-12d4a711cfb4,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
 isDeleted: attid=0x00020030 version=1 invocation=4e4496a3-7fb8-4f97-8a33-d238db8b5e2d usn=3746 (local=3746) at Wed Jun 29 04:36:39 2016
-SKIPING: object CN=dsg\0ADEL:6d66d0ef-cad7-4e5d-b1b6-4a233a21c269,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
+SKIPPING: object CN=dsg\0ADEL:6d66d0ef-cad7-4e5d-b1b6-4a233a21c269,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
 isDeleted: attid=0x00020030 version=1 invocation=4e4496a3-7fb8-4f97-8a33-d238db8b5e2d usn=3734 (local=3734) at Wed Jun 29 04:34:32 2016
-SKIPING: object CN=udg\0ADEL:7cff5537-51b1-4d26-a295-0225dbea8525,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
+SKIPPING: object CN=udg\0ADEL:7cff5537-51b1-4d26-a295-0225dbea8525,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
 isDeleted: attid=0x00020030 version=1 invocation=4e4496a3-7fb8-4f97-8a33-d238db8b5e2d usn=3739 (local=3739) at Wed Jun 29 04:34:34 2016
-SKIPING: object CN=usg\0ADEL:d012e8f5-a4bd-40ea-a2a1-68ff2508847d,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
+SKIPPING: object CN=usg\0ADEL:d012e8f5-a4bd-40ea-a2a1-68ff2508847d,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
 isDeleted: attid=0x00020030 version=1 invocation=4e4496a3-7fb8-4f97-8a33-d238db8b5e2d usn=3736 (local=3736) at Wed Jun 29 04:34:33 2016
-SKIPING: object CN=ddg\0ADEL:fb8c2fe3-5448-43de-99f9-e1d3b9357cfc,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
+SKIPPING: object CN=ddg\0ADEL:fb8c2fe3-5448-43de-99f9-e1d3b9357cfc,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
 isDeleted: attid=0x00020030 version=1 invocation=4e4496a3-7fb8-4f97-8a33-d238db8b5e2d usn=3737 (local=3737) at Wed Jun 29 04:34:34 2016
-SKIPING: object CN=gsg\0ADEL:91aa85cc-fc19-4b8c-9fc7-aaba425439c7,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
+SKIPPING: object CN=gsg\0ADEL:91aa85cc-fc19-4b8c-9fc7-aaba425439c7,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
 isDeleted: attid=0x00020030 version=1 invocation=4e4496a3-7fb8-4f97-8a33-d238db8b5e2d usn=3735 (local=3735) at Wed Jun 29 04:34:33 2016
-SKIPING: object CN=gdg\0ADEL:e0f581e7-14ee-4fc2-839c-8f46f581c72a,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
+SKIPPING: object CN=gdg\0ADEL:e0f581e7-14ee-4fc2-839c-8f46f581c72a,CN=Deleted Objects,DC=release-4-5-0-pre1,DC=samba,DC=corp is an expired tombstone
 isDeleted: attid=0x00020030 version=1 invocation=4e4496a3-7fb8-4f97-8a33-d238db8b5e2d usn=3738 (local=3738) at Wed Jun 29 04:34:34 2016
 NOTICE: found 8 expired tombstones, 'samba' will remove them daily, 'samba-tool domain tombstones expunge' would do that immediately.
 Checked 232 objects (0 errors)
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index 3a903a7eee0..258c9122edc 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -561,7 +561,8 @@ if have_gnutls_crypto_policies:
     plantestsuite("samba3.wbinfo_simple.fips.%s" % t, "ad_member_fips:local", [os.path.join(srcdir(), "nsswitch/tests/test_wbinfo_simple.sh"), t])
     plantestsuite("samba4.wbinfo_name_lookup.fips", "ad_member_fips", [os.path.join(srcdir(), "nsswitch/tests/test_wbinfo_name_lookup.sh"), '$DOMAIN', '$REALM', '$DC_USERNAME'])
 
-plantestsuite_loadlist("samba4.rpc.echo against NetBIOS alias", "ad_dc_ntvfs", [valgrindify(smbtorture4), "$LISTOPT", "$LOADLIST", 'ncacn_np:$NETBIOSALIAS', '-U$DOMAIN/$USERNAME%$PASSWORD', 'rpc.echo'])
+plansmbtorture4testsuite('rpc.echo', "ad_dc_ntvfs", ['ncacn_np:$NETBIOSALIAS', '-U$DOMAIN/$USERNAME%$PASSWORD'], "samba4.rpc.echo against NetBIOS alias")
+
 # json tests hook into ``chgdcpass'' to make them run in contributor CI on
 # gitlab
 planpythontestsuite("chgdcpass", "samba.tests.blackbox.netads_json")
diff --git a/testprogs/blackbox/dbcheck-links.sh b/testprogs/blackbox/dbcheck-links.sh
index ead59d691e0..f00fe46c2de 100755
--- a/testprogs/blackbox/dbcheck-links.sh
+++ b/testprogs/blackbox/dbcheck-links.sh
@@ -42,7 +42,7 @@ dbcheck() {
     if [ "$?" != "$2" ]; then
 	return 1
     fi
-    sort $tmpfile > $tmpfile.sorted
+    sort $tmpfile | grep -v "^INFO:" > $tmpfile.sorted
     sort $release_dir/expected-dbcheck-link-output${1}.txt > $tmpfile.expected
     diff -u $tmpfile.sorted $tmpfile.expected
     if [ "$?" != "0" ]; then
diff --git a/testprogs/blackbox/dbcheck-oldrelease.sh b/testprogs/blackbox/dbcheck-oldrelease.sh
index 9e9924654be..64c08c57981 100755
--- a/testprogs/blackbox/dbcheck-oldrelease.sh
+++ b/testprogs/blackbox/dbcheck-oldrelease.sh
@@ -297,6 +297,17 @@ dbcheck_objectclass() {
     fi
 }
 
+# This should 'fail', because it returns the number of wrong records, which it must if we did not skip the deleted objects
+dbcheck_deleted_objects() {
+    if [ x$RELEASE = x"alpha13" ]; then
+	basedn=$($ldbsearch -H tdb://$PREFIX_ABS/${RELEASE}/private/sam.ldb -s base -b "" defaultNamingContext| grep -i defaultNamingContext| cut -d\  -f 2)
+
+	$PYTHON $BINDIR/samba-tool dbcheck -H tdb://$PREFIX_ABS/${RELEASE}/private/sam.ldb "cn=deleted objects,$basedn" --scope base $@
+    else
+	return 1
+    fi
+}
+
 # This should 'fail', because it returns the number of modified records
 dbcheck() {
        $PYTHON $BINDIR/samba-tool dbcheck --selftest-check-expired-tombstones --cross-ncs --fix --yes -H tdb://$PREFIX_ABS/${RELEASE}/private/sam.ldb $@
@@ -488,6 +499,7 @@ testit $RELEASE undump || failed=`expr $failed + 1`
 testit "reindex" reindex || failed=`expr $failed + 1`
 testit "current_version_mod" do_current_version_mod || failed=`expr $failed + 1`
 testit "check_expected_before_values" check_expected_before_values || failed=`expr $failed + 1`
+testit_expect_failure "dbcheck_deleted_objects" dbcheck_deleted_objects || failed=`expr $failed + 1`
 testit_expect_failure "dbcheck_objectclass" dbcheck_objectclass || failed=`expr $failed + 1`
 testit_expect_failure "dbcheck" dbcheck || failed=`expr $failed + 1`
 testit "check_expected_after_values" check_expected_after_values || failed=`expr $failed + 1`

Reply via email to