[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#985217: marked as done (unblock: samba/2:4.13.5+dfsg-1)



Your message dated Sun, 14 Mar 2021 15:35:40 +0000
with message-id <E1lLSmK-0004uo-5d@respighi.debian.org>
and subject line unblock samba
has caused the Debian Bug report #985217,
regarding unblock: samba/2:4.13.5+dfsg-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
985217: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=985217
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
User: release.debian.org@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.adams@gmail.com>
   * BUG 14634: s3:modules:vfs_virusfilter: Recent talloc changes cause infinite
     start-up failure.

o  Jeremy Allison <jra@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 <abartlet@samba.org>
   * BUG 14593: dbcheck: Check Deleted Objects and reduce noise in reports about
     expired tombstones.

o  Ralph Boehme <slow@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 <pen@lysator.liu.se>
   * BUG 14648: s3: VFS: nfs4_acls. Add missing TALLOC_FREE(frame) in error
     path.

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

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

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

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

o  Paul Wise <pabs3@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.adams@gmail.com>
+   * BUG 14634: s3:modules:vfs_virusfilter: Recent talloc changes cause infinite
+     start-up failure.
+
+o  Jeremy Allison <jra@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 <abartlet@samba.org>
+   * BUG 14593: dbcheck: Check Deleted Objects and reduce noise in reports about
+     expired tombstones.
+
+o  Ralph Boehme <slow@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 <pen@lysator.liu.se>
+   * BUG 14648: s3: VFS: nfs4_acls. Add missing TALLOC_FREE(frame) in error
+     path.
+
+o  Björn Jacke <bj@sernet.de>
+   * BUG 14624: classicupgrade: Treat old never expires value right.
+
+o  Volker Lendecke <vl@samba.org>
+   * BUG 14636: g_lock: Fix uninitalized variable reads.
+
+o  Stefan Metzmacher <metze@samba.org>
+   * BUG 13898: s3:pysmbd: Fix fd leak in py_smbd_create_file().
+
+o  Andreas Schneider <asn@samba.org>
+   * BUG 14625: lib:util: Avoid free'ing our own pointer.
+
+o  Paul Wise <pabs3@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 <asn@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`

--- End Message ---
--- Begin Message ---
Unblocked samba.

--- End Message ---

Reply to: