pyldb: avoid segfault when adding an element with no name
[kai/samba-autobuild/.git] / selftest / target / Samba4.pm
index 842cd1e1bb6ed793a3e081ed6d671b3a32d1b713..6f4a31d357af22aa247a35ecf08f5ea752c7db5d 100755 (executable)
@@ -3,6 +3,9 @@
 # Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org>
 # Published under the GNU GPL, v3 or later.
 
+# NOTE: Refer to the README for more details about the various testenvs,
+# and tips about adding new testenvs.
+
 package Samba4;
 
 use strict;
@@ -12,6 +15,8 @@ use POSIX;
 use SocketWrapper;
 use target::Samba;
 use target::Samba3;
+use Archive::Tar;
+use File::Path 'make_path';
 
 sub new($$$$$) {
        my ($classname, $bindir, $ldap, $srcdir, $server_maxtime) = @_;
@@ -141,6 +146,7 @@ sub check_or_start($$$)
                } else {
                        $ENV{RESOLV_WRAPPER_HOSTS} = $env_vars->{RESOLV_WRAPPER_HOSTS};
                }
+               $ENV{RESOLV_CONF} = $env_vars->{RESOLV_CONF};
 
                $ENV{UID_WRAPPER} = "1";
                $ENV{UID_WRAPPER_ROOT} = "1";
@@ -155,10 +161,13 @@ sub check_or_start($$$)
                        @preargs = split(/ /,$ENV{SAMBA_VALGRIND});
                }
 
+               if (defined($process_model)) {
+                       push @optargs, ("-M", $process_model);
+               }
                close($env_vars->{STDIN_PIPE});
                open STDIN, ">&", $STDIN_READER or die "can't dup STDIN_READER to STDIN: $!";
 
-               exec(@preargs, Samba::bindir_path($self, "samba"), "-M", $process_model, "-i", "--no-process-group", "--maximum-runtime=$self->{server_maxtime}", $env_vars->{CONFIGURATION}, @optargs) or die("Unable to start samba: $!");
+               exec(@preargs, Samba::bindir_path($self, "samba"), "-i", "--no-process-group", "--maximum-runtime=$self->{server_maxtime}", $env_vars->{CONFIGURATION}, @optargs) or die("Unable to start samba: $!");
        }
        $env_vars->{SAMBA_PID} = $pid;
        print "DONE ($pid)\n";
@@ -208,21 +217,13 @@ sub wait_for_start($$)
                $count++;
        } while ($ret != 0 && $count < 20);
        if ($count == 20) {
-               warn("nbt not reachable after 20 retries\n");
                teardown_env($self, $testenv_vars);
-               return 0;
+               warn("nbt not reachable after 20 retries\n");
+               return -1;
        }
 
        # Ensure we have the first RID Set before we start tests.  This makes the tests more reliable.
        if ($testenv_vars->{SERVER_ROLE} eq "domain controller") {
-               # Add hosts file for name lookups
-               $ENV{NSS_WRAPPER_HOSTS} = $testenv_vars->{NSS_WRAPPER_HOSTS};
-               if (defined($testenv_vars->{RESOLV_WRAPPER_CONF})) {
-                       $ENV{RESOLV_WRAPPER_CONF} = $testenv_vars->{RESOLV_WRAPPER_CONF};
-               } else {
-                       $ENV{RESOLV_WRAPPER_HOSTS} = $testenv_vars->{RESOLV_WRAPPER_HOSTS};
-               }
-
                print "waiting for working LDAP and a RID Set to be allocated\n";
                my $ldbsearch = Samba::bindir_path($self, "ldbsearch");
                my $count = 0;
@@ -234,14 +235,30 @@ sub wait_for_start($$)
                        $search_dn = "cn=RID Set,cn=$testenv_vars->{NETBIOSNAME},ou=domain controllers,$base_dn";
                }
                my $max_wait = 60;
-               my $cmd = "$ldbsearch $testenv_vars->{CONFIGURATION} -H ldap://$testenv_vars->{SERVER} -U$testenv_vars->{USERNAME}%$testenv_vars->{PASSWORD} -s base -b \"$search_dn\"";
+
+               # 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}' ";
+
+               $cmd .= "$ldbsearch ";
+               $cmd .= "$testenv_vars->{CONFIGURATION} ";
+               $cmd .= "-H ldap://$testenv_vars->{SERVER} ";
+               $cmd .= "-U$testenv_vars->{USERNAME}%$testenv_vars->{PASSWORD} ";
+               $cmd .= "-s base ";
+               $cmd .= "-b '$search_dn' ";
                while (system("$cmd >/dev/null") != 0) {
                        $count++;
                        if ($count > $max_wait) {
+                               teardown_env($self, $testenv_vars);
                                warn("Timed out ($max_wait sec) waiting for working LDAP and a RID Set to be allocated by $testenv_vars->{NETBIOSNAME} PID $testenv_vars->{SAMBA_PID}");
-                               $ret = -1;
-                               last;
+                               return -1;
                        }
+                       print "Waiting for working LDAP...\n";
                        sleep(1);
                }
        }
@@ -253,7 +270,7 @@ sub wait_for_start($$)
                my $cmd = "NSS_WRAPPER_PASSWD=$testenv_vars->{NSS_WRAPPER_PASSWD} ";
                $cmd .= "NSS_WRAPPER_GROUP=$testenv_vars->{NSS_WRAPPER_GROUP} ";
                $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=$testenv_vars->{SELFTEST_WINBINDD_SOCKET_DIR} ";
-               $cmd .= "$wbinfo -p";
+               $cmd .= "$wbinfo -P";
                $ret = system($cmd);
 
                if ($ret != 0) {
@@ -262,14 +279,45 @@ sub wait_for_start($$)
                $count++;
        } while ($ret != 0 && $count < 20);
        if ($count == 20) {
-               warn("winbind not reachable after 20 retries\n");
                teardown_env($self, $testenv_vars);
-               return 0;
+               warn("winbind not reachable after 20 retries\n");
+               return -1;
+       }
+
+       # Ensure we registered all our names
+       if ($testenv_vars->{SERVER_ROLE} eq "domain controller") {
+               my $max_wait = 60;
+               print "Waiting for dns_update_cache to be created.\n";
+               $count = 0;
+               while (not -e "$testenv_vars->{PRIVATEDIR}/dns_update_cache") {
+                       $count++;
+                       if ($count > $max_wait) {
+                               teardown_env($self, $testenv_vars);
+                               warn("Timed out ($max_wait sec) waiting for dns_update_cache PID $testenv_vars->{SAMBA_PID}");
+                               return -1;
+                       }
+                       print "Waiting for dns_update_cache to be created...\n";
+                       sleep(1);
+               }
+               print "Waiting for dns_update_cache to be filled.\n";
+               $count = 0;
+               while ((-s "$testenv_vars->{PRIVATEDIR}/dns_update_cache") == 0) {
+                       $count++;
+                       if ($count > $max_wait) {
+                               teardown_env($self, $testenv_vars);
+                               warn("Timed out ($max_wait sec) waiting for dns_update_cache PID $testenv_vars->{SAMBA_PID}");
+                               return -1;
+                       }
+                       print "Waiting for dns_update_cache to be filled...\n";
+                       sleep(1);
+               }
        }
 
        print $self->getlog_env($testenv_vars);
 
-       return $ret
+       print "READY ($testenv_vars->{SAMBA_PID})\n";
+
+       return 0
 }
 
 sub write_ldb_file($$$)
@@ -285,12 +333,13 @@ sub write_ldb_file($$$)
 sub add_wins_config($$)
 {
        my ($self, $privatedir) = @_;
+       my $client_ip = Samba::get_ipv4_addr("client");
 
        return $self->write_ldb_file("$privatedir/wins_config.ldb", "
 dn: name=TORTURE_11,CN=PARTNERS
 objectClass: wreplPartner
 name: TORTURE_11
-address: 127.0.0.11
+address: $client_ip
 pullInterval: 0
 pushChangeCount: 0
 type: 0x3
@@ -319,6 +368,129 @@ sub mk_openldap($$)
        return ($slapd_conf_d, $pidfile);
 }
 
+sub setup_dns_hub_internal($$$)
+{
+       my ($self, $hostname, $prefix) = @_;
+       my $STDIN_READER;
+
+       unless(-d $prefix or make_path($prefix, 0777)) {
+               warn("Unable to create $prefix");
+               return undef;
+       }
+       my $prefix_abs = abs_path($prefix);
+
+       die ("prefix=''") if $prefix_abs eq "";
+       die ("prefix='/'") if $prefix_abs eq "/";
+
+       unless (system("rm -rf $prefix_abs/*") == 0) {
+               warn("Unable to clean up");
+       }
+
+       my $env = undef;
+       $env->{NETBIOSNAME} = $hostname;
+
+       $env->{SERVER_IP} = Samba::get_ipv4_addr($hostname);
+       $env->{SERVER_IPV6} = Samba::get_ipv6_addr($hostname);
+
+       $env->{DNS_HUB_LOG} = "$prefix_abs/dns_hub.log";
+
+       $env->{RESOLV_CONF} = "$prefix_abs/resolv.conf";
+
+       open(RESOLV_CONF, ">$env->{RESOLV_CONF}");
+       print RESOLV_CONF "nameserver $env->{SERVER_IP}\n";
+       print RESOLV_CONF "nameserver $env->{SERVER_IPV6}\n";
+       close(RESOLV_CONF);
+
+       # use a pipe for stdin in the child processes. This allows
+       # those processes to monitor the pipe for EOF to ensure they
+       # exit when the test script exits
+       pipe($STDIN_READER, $env->{STDIN_PIPE});
+
+       print "STARTING rootdnsforwarder...\n";
+       my $pid = fork();
+       if ($pid == 0) {
+               # we want out from samba to go to the log file, but also
+               # to the users terminal when running 'make test' on the command
+               # line. This puts it on stderr on the terminal
+               open STDOUT, "| tee $env->{DNS_HUB_LOG} 1>&2";
+               open STDERR, '>&STDOUT';
+
+               my $swiface = Samba::get_interface($hostname);
+               SocketWrapper::set_default_iface($swiface);
+               my $pcap_file = "$ENV{SOCKET_WRAPPER_PCAP_DIR}/env-$hostname$.pcap";
+               SocketWrapper::setup_pcap($pcap_file);
+
+               my @preargs = ();
+               my @args = ();
+               my @optargs = ();
+               if (!defined($ENV{PYTHON})) {
+                   push (@preargs, "env");
+                   push (@preargs, "python");
+               } else {
+                   push (@preargs, $ENV{PYTHON});
+               }
+               $ENV{MAKE_TEST_BINARY} = "$self->{srcdir}/selftest/target/dns_hub.py";
+               push (@args, "$self->{server_maxtime}");
+               push (@args, "$env->{SERVER_IP}");
+               push (@args, Samba::realm_to_ip_mappings());
+               close($env->{STDIN_PIPE});
+               open STDIN, ">&", $STDIN_READER or die "can't dup STDIN_READER to STDIN: $!";
+
+               exec(@preargs, $ENV{MAKE_TEST_BINARY}, @args, @optargs)
+                       or die("Unable to start $ENV{MAKE_TEST_BINARY}: $!");
+       }
+       $env->{SAMBA_PID} = $pid;
+       $env->{KRB5_CONFIG} = "$prefix_abs/no_krb5.conf";
+       close($STDIN_READER);
+
+       print "DONE\n";
+       return $env;
+}
+
+sub setup_dns_hub
+{
+       my ($self, $prefix) = @_;
+
+       my $hostname = "rootdnsforwarder";
+
+       my $env = $self->setup_dns_hub_internal("$hostname", "$prefix/$hostname");
+
+       $self->{dns_hub_env} = $env;
+
+       return $env;
+}
+
+sub get_dns_hub_env($)
+{
+       my ($self, $prefix) = @_;
+
+       if (defined($self->{dns_hub_env})) {
+               return $self->{dns_hub_env};
+       }
+
+       die("get_dns_hub_env() not setup 'dns_hub_env'");
+       return undef;
+}
+
+# Returns the environmental variables that we pass to samba-tool commands
+sub get_cmd_env_vars
+{
+       my ($self, $localenv) = @_;
+
+       my $cmd_env = "NSS_WRAPPER_HOSTS='$localenv->{NSS_WRAPPER_HOSTS}' ";
+       $cmd_env .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$localenv->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
+       if (defined($localenv->{RESOLV_WRAPPER_CONF})) {
+               $cmd_env .= "RESOLV_WRAPPER_CONF=\"$localenv->{RESOLV_WRAPPER_CONF}\" ";
+       } else {
+               $cmd_env .= "RESOLV_WRAPPER_HOSTS=\"$localenv->{RESOLV_WRAPPER_HOSTS}\" ";
+       }
+       $cmd_env .= " KRB5_CONFIG=\"$localenv->{KRB5_CONFIG}\" ";
+       $cmd_env .= "KRB5CCNAME=\"$localenv->{KRB5_CCACHE}\" ";
+       $cmd_env .= "RESOLV_CONF=\"$localenv->{RESOLV_CONF}\" ";
+
+       return $cmd_env;
+}
+
 sub setup_namespaces($$:$$)
 {
        my ($self, $localenv, $upn_array, $spn_array) = @_;
@@ -337,15 +509,7 @@ sub setup_namespaces($$:$$)
 
        my $samba_tool =  Samba::bindir_path($self, "samba-tool");
 
-       my $cmd_env = "";
-       $cmd_env .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$localenv->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-       if (defined($localenv->{RESOLV_WRAPPER_CONF})) {
-               $cmd_env .= "RESOLV_WRAPPER_CONF=\"$localenv->{RESOLV_WRAPPER_CONF}\" ";
-       } else {
-               $cmd_env .= "RESOLV_WRAPPER_HOSTS=\"$localenv->{RESOLV_WRAPPER_HOSTS}\" ";
-       }
-       $cmd_env .= " KRB5_CONFIG=\"$localenv->{KRB5_CONFIG}\" ";
-       $cmd_env .= "KRB5CCNAME=\"$localenv->{KRB5_CCACHE}\" ";
+       my $cmd_env = $self->get_cmd_env_vars($localenv);
 
        my $cmd_config = " $localenv->{CONFIGURATION}";
 
@@ -365,9 +529,7 @@ sub setup_trust($$$$$)
        my ($self, $localenv, $remoteenv, $type, $extra_args) = @_;
 
        $localenv->{TRUST_SERVER} = $remoteenv->{SERVER};
-       $localenv->{TRUST_SERVER_IP} = $remoteenv->{SERVER_IP};
-       $localenv->{TRUST_SERVER_IPV6} = $remoteenv->{SERVER_IPV6};
-       $localenv->{TRUST_NETBIOSNAME} = $remoteenv->{NETBIOSNAME};
+
        $localenv->{TRUST_USERNAME} = $remoteenv->{USERNAME};
        $localenv->{TRUST_PASSWORD} = $remoteenv->{PASSWORD};
        $localenv->{TRUST_DOMAIN} = $remoteenv->{DOMAIN};
@@ -375,16 +537,9 @@ sub setup_trust($$$$$)
        $localenv->{TRUST_DOMSID} = $remoteenv->{DOMSID};
 
        my $samba_tool =  Samba::bindir_path($self, "samba-tool");
+
        # setup the trust
-       my $cmd_env = "";
-       $cmd_env .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$localenv->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-       if (defined($localenv->{RESOLV_WRAPPER_CONF})) {
-               $cmd_env .= "RESOLV_WRAPPER_CONF=\"$localenv->{RESOLV_WRAPPER_CONF}\" ";
-       } else {
-               $cmd_env .= "RESOLV_WRAPPER_HOSTS=\"$localenv->{RESOLV_WRAPPER_HOSTS}\" ";
-       }
-       $cmd_env .= " KRB5_CONFIG=\"$localenv->{KRB5_CONFIG}\" ";
-       $cmd_env .= "KRB5CCNAME=\"$localenv->{KRB5_CCACHE}\" ";
+       my $cmd_env = $self->get_cmd_env_vars($localenv);
 
        my $cmd_config = " $localenv->{CONFIGURATION}";
        my $cmd_creds = $cmd_config;
@@ -422,6 +577,11 @@ sub provision_raw_prepare($$$$$$$$$$$$)
            $domain, $realm, $samsid, $functional_level,
            $password, $kdc_ipv4, $kdc_ipv6) = @_;
        my $ctx;
+       my $python_cmd = "";
+       if (defined $ENV{PYTHON}) {
+               $python_cmd = $ENV{PYTHON} . " ";
+       }
+       $ctx->{python} = $python_cmd;
        my $netbiosname = uc($hostname);
 
        unless(-d $prefix or mkdir($prefix, 0777)) {
@@ -498,17 +658,20 @@ sub provision_raw_prepare($$$$$$$$$$$$)
        if ($ENV{SAMBA_DNS_FAKING}) {
                $ctx->{dns_host_file} = "$ENV{SELFTEST_PREFIX}/dns_host_file";
                $ctx->{samba_dnsupdate} = "$ENV{SRCDIR_ABS}/source4/scripting/bin/samba_dnsupdate -s $ctx->{smb_conf} --all-interfaces --use-file=$ctx->{dns_host_file}";
+               $ctx->{samba_dnsupdate} = $python_cmd .  $ctx->{samba_dnsupdate};
        } else {
                $ctx->{samba_dnsupdate} = "$ENV{SRCDIR_ABS}/source4/scripting/bin/samba_dnsupdate -s $ctx->{smb_conf} --all-interfaces";
+               $ctx->{samba_dnsupdate} = $python_cmd .  $ctx->{samba_dnsupdate};
                $ctx->{use_resolv_wrapper} = 1;
        }
-       $ctx->{resolv_conf} = "$ctx->{etcdir}/resolv.conf";
+
+       my $dns_hub = $self->get_dns_hub_env();
+       $ctx->{resolv_conf} = $dns_hub->{RESOLV_CONF};
 
        $ctx->{tlsdir} = "$ctx->{privatedir}/tls";
 
-       $ctx->{ipv4} = "127.0.0.$swiface";
-       $ctx->{ipv6} = sprintf("fd00:0000:0000:0000:0000:0000:5357:5f%02x", $swiface);
-       $ctx->{interfaces} = "$ctx->{ipv4}/8 $ctx->{ipv6}/64";
+       $ctx->{ipv4} = Samba::get_ipv4_addr($hostname);
+       $ctx->{ipv6} = Samba::get_ipv6_addr($hostname);
 
        push(@{$ctx->{directories}}, $ctx->{privatedir});
        push(@{$ctx->{directories}}, $ctx->{binddnsdir});
@@ -530,6 +693,7 @@ sub provision_raw_prepare($$$$$$$$$$$$)
        push (@provision_options, "NSS_WRAPPER_HOSTNAME=\"$ctx->{nsswrap_hostname}\"");
        if (defined($ctx->{use_resolv_wrapper})) {
                push (@provision_options, "RESOLV_WRAPPER_CONF=\"$ctx->{resolv_conf}\"");
+               push (@provision_options, "RESOLV_CONF=\"$ctx->{resolv_conf}\"");
        } else {
                push (@provision_options, "RESOLV_WRAPPER_HOSTS=\"$ctx->{dns_host_file}\"");
        }
@@ -547,10 +711,10 @@ sub provision_raw_prepare($$$$$$$$$$$$)
                    push (@provision_options, "python");
                }
        }
-       if (defined($ENV{PYTHON})) {
-               push (@provision_options, $ENV{PYTHON});
-       }
-       push (@provision_options, Samba::bindir_path($self, "samba-tool"));
+
+       my $samba_tool =  Samba::bindir_path($self, "samba-tool");
+
+       push (@provision_options, $samba_tool);
        push (@provision_options, "domain");
        push (@provision_options, "provision");
        push (@provision_options, "--configfile=$ctx->{smb_conf}");
@@ -574,6 +738,16 @@ sub provision_raw_prepare($$$$$$$$$$$$)
        return $ctx;
 }
 
+sub has_option
+{
+       my ($self, $keyword, @options_list) = @_;
+
+       # convert the options-list to a hash-map for easy keyword lookup
+       my %options_dict = map { $_ => 1 } @options_list;
+
+       return exists $options_dict{$keyword};
+}
+
 #
 # Step1 creates the basic configuration
 #
@@ -598,6 +772,15 @@ sub provision_raw_step1($$)
        my $crlfile = "$ctx->{tlsdir}/crl.pem";
        $crlfile = "" unless -e ${crlfile};
 
+       # work out which file server to use. Default to source3 smbd (s3fs),
+       # unless the source4 NTVFS (smb) file server has been specified
+       my $services = "-smb +s3fs";
+       if ($self->has_option("--use-ntvfs", @{$ctx->{provision_options}})) {
+               $services = "+smb -s3fs";
+       }
+
+       my $interfaces = Samba::get_interfaces_config($ctx->{netbiosname});
+
        print CONFFILE "
 [global]
        netbios name = $ctx->{netbiosname}
@@ -614,14 +797,14 @@ sub provision_raw_step1($$)
        winbindd socket directory = $ctx->{winbindd_socket_dir}
        ntp signd socket directory = $ctx->{ntp_signd_socket_dir}
        winbind separator = /
-       interfaces = $ctx->{interfaces}
+       interfaces = $interfaces
        tls dh params file = $ctx->{tlsdir}/dhparms.pem
        tls crlfile = ${crlfile}
        tls verify peer = no_check
        panic action = $RealBin/gdb_backtrace \%d
        wins support = yes
        server role = $ctx->{server_role}
-       server services = +echo +smb -s3fs
+       server services = +echo $services
         dcerpc endpoint servers = +winreg +srvsvc
        notify:inotify = false
        ldb:nosync = true
@@ -634,13 +817,12 @@ sub provision_raw_step1($$)
        ntlm auth = Yes
        rndc command = true
        dns update command = $ctx->{samba_dnsupdate}
-       spn update command = $ENV{SRCDIR_ABS}/source4/scripting/bin/samba_spnupdate -s $ctx->{smb_conf}
-       gpo update command = $ENV{SRCDIR_ABS}/source4/scripting/bin/samba_gpoupdate -s $ctx->{smb_conf} -H $ctx->{privatedir}/sam.ldb --machine
+       spn update command = $ctx->{python} $ENV{SRCDIR_ABS}/source4/scripting/bin/samba_spnupdate -s $ctx->{smb_conf}
+       gpo update command = $ctx->{python} $ENV{SRCDIR_ABS}/source4/scripting/bin/samba-gpupdate -s $ctx->{smb_conf} --target=Computer
+       samba kcc command = $ctx->{python} $ENV{SRCDIR_ABS}/source4/scripting/bin/samba_kcc
        dreplsrv:periodic_startup_interval = 0
        dsdb:schema update allowed = yes
 
-        prefork children = 4
-
         vfs objects = dfs_samba4 acl_xattr fake_acls xattr_tdb streams_depot
 
         idmap_ldb:use rfc2307=yes
@@ -648,6 +830,7 @@ sub provision_raw_step1($$)
        winbind enum groups = yes
 
         rpc server port:netlogon = 1026
+       include system krb5 conf = no
 
 ";
 
@@ -708,13 +891,6 @@ nogroup:x:65534:nobody
        }
        close(HOSTS);
 
-       if (defined($ctx->{resolv_conf})) {
-               open(RESOLV_CONF, ">$ctx->{resolv_conf}");
-               print RESOLV_CONF "nameserver $ctx->{kdc_ipv4}\n";
-               print RESOLV_CONF "nameserver $ctx->{kdc_ipv6}\n";
-               close(RESOLV_CONF);
-       }
-
        my $configuration = "--configfile=$ctx->{smb_conf}";
 
 #Ensure the config file is valid before we start
@@ -729,20 +905,30 @@ nogroup:x:65534:nobody
                return undef;
        }
 
+       # Return the environment variables for the new testenv DC.
+       # 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 = {
                KRB5_CONFIG => $ctx->{krb5_conf},
                KRB5_CCACHE => $ctx->{krb5_ccache},
                MITKDC_CONFIG => $ctx->{mitkdc_conf},
                PIDDIR => $ctx->{piddir},
                SERVER => $ctx->{hostname},
+               DC_SERVER => $ctx->{hostname},
                SERVER_IP => $ctx->{ipv4},
+               DC_SERVER_IP => $ctx->{ipv4},
                SERVER_IPV6 => $ctx->{ipv6},
+               DC_SERVER_IPV6 => $ctx->{ipv6},
                NETBIOSNAME => $ctx->{netbiosname},
+               DC_NETBIOSNAME => $ctx->{netbiosname},
                DOMAIN => $ctx->{domain},
                USERNAME => $ctx->{username},
+               DC_USERNAME => $ctx->{username},
                REALM => $ctx->{realm},
+               DNSNAME => $ctx->{dnsname},
                SAMSID => $ctx->{samsid},
                PASSWORD => $ctx->{password},
+               DC_PASSWORD => $ctx->{password},
                LDAPDIR => $ctx->{ldapdir},
                LDAP_INSTANCE => $ctx->{ldap_instance},
                SELFTEST_WINBINDD_SOCKET_DIR => $ctx->{winbindd_socket_dir},
@@ -812,6 +998,7 @@ sub provision_raw_step2($$$)
        $ldbmodify .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
        $ldbmodify .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
        $ldbmodify .= Samba::bindir_path($self, "ldbmodify");
+       $ldbmodify .=  " --configfile=$ctx->{smb_conf}";
        my $base_dn = "DC=".join(",DC=", split(/\./, $ctx->{realm}));
 
        if ($ctx->{server_role} ne "domain controller") {
@@ -860,6 +1047,28 @@ userPrincipalName: testdenied_upn\@$ctx->{realm}.upn
 ";
        close(LDIF);
 
+       $samba_tool_cmd = "";
+       $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} testupnspn $ctx->{password}";
+       unless (system($samba_tool_cmd) == 0) {
+               warn("Unable to add testupnspn user: \n$samba_tool_cmd\n");
+               return undef;
+       }
+
+       my $user_dn = "cn=testupnspn,cn=users,$base_dn";
+       open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb");
+       print LDIF "dn: $user_dn
+changetype: modify
+replace: userPrincipalName
+userPrincipalName: http/testupnspn.$ctx->{dnsname}\@$ctx->{realm}
+replace: servicePrincipalName
+servicePrincipalName: http/testupnspn.$ctx->{dnsname}
+-
+";
+       close(LDIF);
+
        $samba_tool_cmd = "";
        $samba_tool_cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
        $samba_tool_cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
@@ -871,7 +1080,7 @@ userPrincipalName: testdenied_upn\@$ctx->{realm}.upn
        }
 
        # Create to users alice and bob!
-       my $user_account_array = ["alice", "bob"];
+       my $user_account_array = ["alice", "bob", "jane"];
 
        foreach my $user_account (@{$user_account_array}) {
                my $samba_tool_cmd = "";
@@ -886,6 +1095,23 @@ userPrincipalName: testdenied_upn\@$ctx->{realm}.upn
                }
        }
 
+       my $ldbmodify = "";
+       $ldbmodify .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
+       $ldbmodify .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+       $ldbmodify .= Samba::bindir_path($self, "ldbmodify");
+       $ldbmodify .=  " --configfile=$ctx->{smb_conf}";
+       my $base_dn = "DC=".join(",DC=", split(/\./, $ctx->{realm}));
+       my $user_dn = "cn=jane,cn=users,$base_dn";
+
+       open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb");
+       print LDIF "dn: $user_dn
+changetype: modify
+replace: userPrincipalName
+userPrincipalName: jane.doe\@$ctx->{realm}
+-
+";
+       close(LDIF);
+
        return $ret;
 }
 
@@ -907,8 +1133,6 @@ sub provision($$$$$$$$$$)
 
        if (defined($extra_provision_options)) {
                push (@{$ctx->{provision_options}}, @{$extra_provision_options});
-       } else {
-               push (@{$ctx->{provision_options}}, "--use-ntvfs");
        }
 
        $ctx->{share} = "$ctx->{prefix_abs}/share";
@@ -1049,6 +1273,22 @@ $extra_smbconf_shares
        return $self->provision_raw_step2($ctx, $ret);
 }
 
+# For multi-DC testenvs, we want $DC_SERVER to always be the PDC (i.e. the
+# original DC) in the testenv. $SERVER is always the joined DC that we are
+# actually running the test against
+sub set_pdc_env_vars
+{
+       my ($self, $env, $dcvars) = @_;
+
+       $env->{DC_SERVER} = $dcvars->{DC_SERVER};
+       $env->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP};
+       $env->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6};
+       $env->{DC_SERVERCONFFILE} = $dcvars->{SERVERCONFFILE};
+       $env->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME};
+       $env->{DC_USERNAME} = $dcvars->{DC_USERNAME};
+       $env->{DC_PASSWORD} = $dcvars->{DC_PASSWORD};
+}
+
 sub provision_s4member($$$$$)
 {
        my ($self, $prefix, $dcvars, $hostname, $more_conf) = @_;
@@ -1074,6 +1314,7 @@ rpc_server:tcpip = no
        if ($more_conf) {
                $extra_smb_conf = $extra_smb_conf . $more_conf . "\n";
        }
+       my $extra_provision_options = ["--use-ntvfs"];
        my $ret = $self->provision($prefix,
                                   "member server",
                                   $hostname,
@@ -1083,21 +1324,14 @@ rpc_server:tcpip = no
                                   "locMEMpass3",
                                   $dcvars->{SERVER_IP},
                                   $dcvars->{SERVER_IPV6},
-                                  $extra_smb_conf, "", undef);
+                                  $extra_smb_conf, "",
+                                  $extra_provision_options);
        unless ($ret) {
                return undef;
        }
 
        my $samba_tool =  Samba::bindir_path($self, "samba-tool");
-       my $cmd = "";
-       $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-       if (defined($ret->{RESOLV_WRAPPER_CONF})) {
-               $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" ";
-       } else {
-               $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" ";
-       }
-       $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-       $cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+       my $cmd = $self->get_cmd_env_vars($ret);
        $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} member";
        $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
        $cmd .= " --machinepass=machine$ret->{PASSWORD}";
@@ -1107,20 +1341,8 @@ rpc_server:tcpip = no
                return undef;
        }
 
-       $ret->{MEMBER_SERVER} = $ret->{SERVER};
-       $ret->{MEMBER_SERVER_IP} = $ret->{SERVER_IP};
-       $ret->{MEMBER_SERVER_IPV6} = $ret->{SERVER_IPV6};
-       $ret->{MEMBER_NETBIOSNAME} = $ret->{NETBIOSNAME};
-       $ret->{MEMBER_USERNAME} = $ret->{USERNAME};
-       $ret->{MEMBER_PASSWORD} = $ret->{PASSWORD};
-
        $ret->{DOMSID} = $dcvars->{DOMSID};
-       $ret->{DC_SERVER} = $dcvars->{DC_SERVER};
-       $ret->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP};
-       $ret->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6};
-       $ret->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME};
-       $ret->{DC_USERNAME} = $dcvars->{DC_USERNAME};
-       $ret->{DC_PASSWORD} = $dcvars->{DC_PASSWORD};
+       $self->set_pdc_env_vars($ret, $dcvars);
 
        return $ret;
 }
@@ -1137,6 +1359,7 @@ sub provision_rpc_proxy($$$)
        dcerpc_remote:binding = ncacn_ip_tcp:$dcvars->{SERVER}
        dcerpc endpoint servers = epmapper, remote
        dcerpc_remote:interfaces = rpcecho
+       dcerpc_remote:allow_anonymous_fallback = yes
 
 [cifs_to_dc]
        path = /tmp/_ignore_cifs_to_dc_/_none_
@@ -1151,6 +1374,7 @@ sub provision_rpc_proxy($$$)
 
 ";
 
+       my $extra_provision_options = ["--use-ntvfs"];
        my $ret = $self->provision($prefix,
                                   "member server",
                                   "localrpcproxy",
@@ -1160,7 +1384,8 @@ sub provision_rpc_proxy($$$)
                                   "locRPCproxypass4",
                                   $dcvars->{SERVER_IP},
                                   $dcvars->{SERVER_IPV6},
-                                  $extra_smbconf_options, "", undef);
+                                  $extra_smbconf_options, "",
+                                  $extra_provision_options);
        unless ($ret) {
                return undef;
        }
@@ -1168,15 +1393,7 @@ sub provision_rpc_proxy($$$)
        my $samba_tool =  Samba::bindir_path($self, "samba-tool");
 
        # The joind runs in the context of the rpc_proxy/member for now
-       my $cmd = "";
-       $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-       if (defined($ret->{RESOLV_WRAPPER_CONF})) {
-               $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" ";
-       } else {
-               $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" ";
-       }
-       $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-       $cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+       my $cmd = $self->get_cmd_env_vars($ret);
        $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} member";
        $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
        $cmd .= " --machinepass=machine$ret->{PASSWORD}";
@@ -1191,6 +1408,7 @@ sub provision_rpc_proxy($$$)
        $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 .= "$samba_tool delegation for-any-protocol '$ret->{NETBIOSNAME}\$' on";
         $cmd .= " $dcvars->{CONFIGURATION}";
         print $cmd;
@@ -1205,6 +1423,7 @@ sub provision_rpc_proxy($$$)
        $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 .= "$samba_tool delegation add-service '$ret->{NETBIOSNAME}\$' cifs/$dcvars->{SERVER}";
         $cmd .= " $dcvars->{CONFIGURATION}";
 
@@ -1213,20 +1432,8 @@ sub provision_rpc_proxy($$$)
                return undef;
        }
 
-       $ret->{RPC_PROXY_SERVER} = $ret->{SERVER};
-       $ret->{RPC_PROXY_SERVER_IP} = $ret->{SERVER_IP};
-       $ret->{RPC_PROXY_SERVER_IPV6} = $ret->{SERVER_IPV6};
-       $ret->{RPC_PROXY_NETBIOSNAME} = $ret->{NETBIOSNAME};
-       $ret->{RPC_PROXY_USERNAME} = $ret->{USERNAME};
-       $ret->{RPC_PROXY_PASSWORD} = $ret->{PASSWORD};
-
        $ret->{DOMSID} = $dcvars->{DOMSID};
-       $ret->{DC_SERVER} = $dcvars->{DC_SERVER};
-       $ret->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP};
-       $ret->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6};
-       $ret->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME};
-       $ret->{DC_USERNAME} = $dcvars->{DC_USERNAME};
-       $ret->{DC_PASSWORD} = $dcvars->{DC_PASSWORD};
+       $self->set_pdc_env_vars($ret, $dcvars);
 
        return $ret;
 }
@@ -1271,15 +1478,7 @@ sub provision_promoted_dc($$$)
        }
 
        my $samba_tool =  Samba::bindir_path($self, "samba-tool");
-       my $cmd = "";
-       $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-       if (defined($ret->{RESOLV_WRAPPER_CONF})) {
-               $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" ";
-       } else {
-               $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" ";
-       }
-       $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-       $cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+       my $cmd = $self->get_cmd_env_vars($ret);
        $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} MEMBER --realm=$dcvars->{REALM}";
        $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
        $cmd .= " --machinepass=machine$ret->{PASSWORD}";
@@ -1290,10 +1489,7 @@ sub provision_promoted_dc($$$)
        }
 
        my $samba_tool =  Samba::bindir_path($self, "samba-tool");
-       my $cmd = "";
-       $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-       $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-       $cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+       my $cmd = $self->get_cmd_env_vars($ret);
        $cmd .= "$samba_tool domain dcpromo $ret->{CONFIGURATION} $dcvars->{REALM} DC --realm=$dcvars->{REALM}";
        $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
        $cmd .= " --machinepass=machine$ret->{PASSWORD} --use-ntvfs --dns-backend=BIND9_DLZ";
@@ -1303,17 +1499,7 @@ sub provision_promoted_dc($$$)
                return undef;
        }
 
-       $ret->{PROMOTED_DC_SERVER} = $ret->{SERVER};
-       $ret->{PROMOTED_DC_SERVER_IP} = $ret->{SERVER_IP};
-       $ret->{PROMOTED_DC_SERVER_IPV6} = $ret->{SERVER_IPV6};
-       $ret->{PROMOTED_DC_NETBIOSNAME} = $ret->{NETBIOSNAME};
-
-       $ret->{DC_SERVER} = $dcvars->{DC_SERVER};
-       $ret->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP};
-       $ret->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6};
-       $ret->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME};
-       $ret->{DC_USERNAME} = $dcvars->{DC_USERNAME};
-       $ret->{DC_PASSWORD} = $dcvars->{DC_PASSWORD};
+       $self->set_pdc_env_vars($ret, $dcvars);
 
        return $ret;
 }
@@ -1368,41 +1554,18 @@ sub provision_vampire_dc($$$)
        }
 
        my $samba_tool =  Samba::bindir_path($self, "samba-tool");
-       my $cmd = "";
-       $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-       if (defined($ret->{RESOLV_WRAPPER_CONF})) {
-               $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" ";
-       } else {
-               $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" ";
-       }
-       $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-       $cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+       my $cmd = $self->get_cmd_env_vars($ret);
        $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} DC --realm=$dcvars->{REALM}";
        $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD} --domain-critical-only";
        $cmd .= " --machinepass=machine$ret->{PASSWORD} --use-ntvfs";
+       $cmd .= " --backend-store=mdb";
 
        unless (system($cmd) == 0) {
                warn("Join failed\n$cmd");
                return undef;
        }
 
-        if ($fl == "2000") {
-               $ret->{VAMPIRE_2000_DC_SERVER} = $ret->{SERVER};
-               $ret->{VAMPIRE_2000_DC_SERVER_IP} = $ret->{SERVER_IP};
-               $ret->{VAMPIRE_2000_DC_SERVER_IPV6} = $ret->{SERVER_IPV6};
-               $ret->{VAMPIRE_2000_DC_NETBIOSNAME} = $ret->{NETBIOSNAME};
-        } else {
-               $ret->{VAMPIRE_DC_SERVER} = $ret->{SERVER};
-               $ret->{VAMPIRE_DC_SERVER_IP} = $ret->{SERVER_IP};
-               $ret->{VAMPIRE_DC_SERVER_IPV6} = $ret->{SERVER_IPV6};
-               $ret->{VAMPIRE_DC_NETBIOSNAME} = $ret->{NETBIOSNAME};
-        }
-       $ret->{DC_SERVER} = $dcvars->{DC_SERVER};
-       $ret->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP};
-       $ret->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6};
-       $ret->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME};
-       $ret->{DC_USERNAME} = $dcvars->{DC_USERNAME};
-       $ret->{DC_PASSWORD} = $dcvars->{DC_PASSWORD};
+       $self->set_pdc_env_vars($ret, $dcvars);
        $ret->{DC_REALM} = $dcvars->{DC_REALM};
 
        return $ret;
@@ -1449,15 +1612,7 @@ sub provision_subdom_dc($$$)
        Samba::mk_mitkdc_conf($ctx, abs_path(Samba::bindir_path($self, "shared")));
 
        my $samba_tool =  Samba::bindir_path($self, "samba-tool");
-       my $cmd = "";
-       $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-       if (defined($ret->{RESOLV_WRAPPER_CONF})) {
-               $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" ";
-       } else {
-               $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" ";
-       }
-       $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-       $cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+       my $cmd = $self->get_cmd_env_vars($ret);
        $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $ctx->{dnsname} subdomain ";
        $cmd .= "--parent-domain=$dcvars->{REALM} -U$dcvars->{DC_USERNAME}\@$dcvars->{REALM}\%$dcvars->{DC_PASSWORD}";
        $cmd .= " --machinepass=machine$ret->{PASSWORD} --use-ntvfs";
@@ -1469,16 +1624,8 @@ sub provision_subdom_dc($$$)
        }
 
        $ret->{SUBDOM_DC_SERVER} = $ret->{SERVER};
-       $ret->{SUBDOM_DC_SERVER_IP} = $ret->{SERVER_IP};
-       $ret->{SUBDOM_DC_SERVER_IPV6} = $ret->{SERVER_IPV6};
-       $ret->{SUBDOM_DC_NETBIOSNAME} = $ret->{NETBIOSNAME};
 
-       $ret->{DC_SERVER} = $dcvars->{DC_SERVER};
-       $ret->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP};
-       $ret->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6};
-       $ret->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME};
-       $ret->{DC_USERNAME} = $dcvars->{DC_USERNAME};
-       $ret->{DC_PASSWORD} = $dcvars->{DC_PASSWORD};
+       $self->set_pdc_env_vars($ret, $dcvars);
 
        return $ret;
 }
@@ -1500,8 +1647,12 @@ sub provision_ad_dc_ntvfs($$)
        lsa over netlogon = yes
         rpc server port = 1027
         auth event notification = true
+       dsdb event notification = true
+       dsdb password event notification = true
+       dsdb group change notification = true
        server schannel = auto
        ";
+       my $extra_provision_options = ["--use-ntvfs"];
        my $ret = $self->provision($prefix,
                                   "domain controller",
                                   "localdc",
@@ -1513,7 +1664,7 @@ sub provision_ad_dc_ntvfs($$)
                                   undef,
                                   $extra_conf_options,
                                   "",
-                                  undef);
+                                  $extra_provision_options);
        unless ($ret) {
                return undef;
        }
@@ -1523,12 +1674,6 @@ sub provision_ad_dc_ntvfs($$)
                return undef;
        }
        $ret->{NETBIOSALIAS} = "localdc1-a";
-       $ret->{DC_SERVER} = $ret->{SERVER};
-       $ret->{DC_SERVER_IP} = $ret->{SERVER_IP};
-       $ret->{DC_SERVER_IPV6} = $ret->{SERVER_IPV6};
-       $ret->{DC_NETBIOSNAME} = $ret->{NETBIOSNAME};
-       $ret->{DC_USERNAME} = $ret->{USERNAME};
-       $ret->{DC_PASSWORD} = $ret->{PASSWORD};
        $ret->{DC_REALM} = $ret->{REALM};
 
        return $ret;
@@ -1543,7 +1688,7 @@ sub provision_fl2000dc($$)
        spnego:simulate_w2k=yes
        ntlmssp_server:force_old_spnego=yes
 ";
-       my $extra_provision_options = undef;
+       my $extra_provision_options = ["--use-ntvfs"];
        # This environment uses plain text secrets
        # i.e. secret attributes are not encrypted on disk.
        # This allows testing of the --plaintext-secrets option for
@@ -1569,12 +1714,6 @@ sub provision_fl2000dc($$)
                warn("Unable to add wins configuration");
                return undef;
        }
-       $ret->{DC_SERVER} = $ret->{SERVER};
-       $ret->{DC_SERVER_IP} = $ret->{SERVER_IP};
-       $ret->{DC_SERVER_IPV6} = $ret->{SERVER_IPV6};
-       $ret->{DC_NETBIOSNAME} = $ret->{NETBIOSNAME};
-       $ret->{DC_USERNAME} = $ret->{USERNAME};
-       $ret->{DC_PASSWORD} = $ret->{PASSWORD};
        $ret->{DC_REALM} = $ret->{REALM};
 
        return $ret;
@@ -1583,13 +1722,15 @@ sub provision_fl2000dc($$)
 sub provision_fl2003dc($$$)
 {
        my ($self, $prefix, $dcvars) = @_;
-       my $swiface1 = Samba::get_interface("fakednsforwarder1");
-       my $swiface2 = Samba::get_interface("fakednsforwarder2");
+       my $ip_addr1 = Samba::get_ipv4_addr("fakednsforwarder1");
+       my $ip_addr2 = Samba::get_ipv4_addr("fakednsforwarder2");
 
        print "PROVISIONING DC WITH FOREST LEVEL 2003...\n";
        my $extra_conf_options = "allow dns updates = nonsecure and secure
        dcesrv:header signing = no
-       dns forwarder = 127.0.0.$swiface1 127.0.0.$swiface2";
+       dcesrv:max auth states = 0
+       dns forwarder = $ip_addr1 $ip_addr2";
+       my $extra_provision_options = ["--use-ntvfs"];
        my $ret = $self->provision($prefix,
                                   "domain controller",
                                   "dc6",
@@ -1601,19 +1742,13 @@ sub provision_fl2003dc($$$)
                                   undef,
                                   $extra_conf_options,
                                   "",
-                                  undef);
+                                  $extra_provision_options);
        unless (defined $ret) {
                return undef;
        }
 
-       $ret->{DC_SERVER} = $ret->{SERVER};
-       $ret->{DC_SERVER_IP} = $ret->{SERVER_IP};
-       $ret->{DC_SERVER_IPV6} = $ret->{SERVER_IPV6};
-       $ret->{DC_NETBIOSNAME} = $ret->{NETBIOSNAME};
-       $ret->{DC_USERNAME} = $ret->{USERNAME};
-       $ret->{DC_PASSWORD} = $ret->{PASSWORD};
-       $ret->{DNS_FORWARDER1} = "127.0.0.$swiface1";
-       $ret->{DNS_FORWARDER2} = "127.0.0.$swiface2";
+       $ret->{DNS_FORWARDER1} = $ip_addr1;
+       $ret->{DNS_FORWARDER2} = $ip_addr2;
 
        my @samba_tool_options;
        push (@samba_tool_options, Samba::bindir_path($self, "samba-tool"));
@@ -1645,6 +1780,7 @@ sub provision_fl2008r2dc($$$)
 
        print "PROVISIONING DC WITH FOREST LEVEL 2008r2...\n";
         my $extra_conf_options = "ldap server require strong auth = no";
+       my $extra_provision_options = ["--use-ntvfs"];
        my $ret = $self->provision($prefix,
                                   "domain controller",
                                   "dc7",
@@ -1656,7 +1792,7 @@ sub provision_fl2008r2dc($$$)
                                   undef,
                                   $extra_conf_options,
                                   "",
-                                  undef);
+                                  $extra_provision_options);
        unless (defined $ret) {
                return undef;
        }
@@ -1665,12 +1801,6 @@ sub provision_fl2008r2dc($$$)
                warn("Unable to add wins configuration");
                return undef;
        }
-       $ret->{DC_SERVER} = $ret->{SERVER};
-       $ret->{DC_SERVER_IP} = $ret->{SERVER_IP};
-       $ret->{DC_SERVER_IPV6} = $ret->{SERVER_IPV6};
-       $ret->{DC_NETBIOSNAME} = $ret->{NETBIOSNAME};
-       $ret->{DC_USERNAME} = $ret->{USERNAME};
-       $ret->{DC_PASSWORD} = $ret->{PASSWORD};
        $ret->{DC_REALM} = $ret->{REALM};
 
        return $ret;
@@ -1729,15 +1859,7 @@ sub provision_rodc($$$)
        }
 
        my $samba_tool =  Samba::bindir_path($self, "samba-tool");
-       my $cmd = "";
-       $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-       if (defined($ret->{RESOLV_WRAPPER_CONF})) {
-               $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" ";
-       } else {
-               $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" ";
-       }
-       $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-       $cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+       my $cmd = $self->get_cmd_env_vars($ret);
        $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} RODC";
        $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
        $cmd .= " --server=$dcvars->{DC_SERVER} --use-ntvfs";
@@ -1752,6 +1874,7 @@ sub provision_rodc($$$)
        my $testallowed_account = "testallowed account";
        $cmd = "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
        $cmd .= "KRB5CCNAME=\"$ret->{KRB5_CCACHE}\" ";
+       $cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" ";
        $cmd .= "$samba_tool rodc preload '$testallowed_account' $ret->{CONFIGURATION}";
        $cmd .= " --server=$dcvars->{DC_SERVER}";
 
@@ -1768,17 +1891,7 @@ sub provision_rodc($$$)
        Samba::mk_krb5_conf($ctx);
        Samba::mk_mitkdc_conf($ctx, abs_path(Samba::bindir_path($self, "shared")));
 
-       $ret->{RODC_DC_SERVER} = $ret->{SERVER};
-       $ret->{RODC_DC_SERVER_IP} = $ret->{SERVER_IP};
-       $ret->{RODC_DC_SERVER_IPV6} = $ret->{SERVER_IPV6};
-       $ret->{RODC_DC_NETBIOSNAME} = $ret->{NETBIOSNAME};
-
-       $ret->{DC_SERVER} = $dcvars->{DC_SERVER};
-       $ret->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP};
-       $ret->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6};
-       $ret->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME};
-       $ret->{DC_USERNAME} = $dcvars->{DC_USERNAME};
-       $ret->{DC_PASSWORD} = $dcvars->{DC_PASSWORD};
+       $self->set_pdc_env_vars($ret, $dcvars);
 
        return $ret;
 }
@@ -1806,7 +1919,8 @@ sub read_config_h($)
 
 sub provision_ad_dc($$$$$$)
 {
-       my ($self, $prefix, $hostname, $domain, $realm, $smbconf_args) = @_;
+       my ($self, $prefix, $hostname, $domain, $realm, $smbconf_args,
+               $extra_provision_options) = @_;
 
        my $prefix_abs = abs_path($prefix);
 
@@ -1827,7 +1941,6 @@ sub provision_ad_dc($$$$$$)
        $password_hash_gpg_key_ids = "" unless defined($config_h->{HAVE_GPGME});
 
        my $extra_smbconf_options = "
-        server services = -smb +s3fs
         xattr_tdb:file = $prefix_abs/statedir/xattr.tdb
 
        dbwrap_tdb_mutexes:* = yes
@@ -1872,6 +1985,9 @@ sub provision_ad_dc($$$$$$)
 
        server schannel = auto
         auth event notification = true
+       dsdb event notification = true
+       dsdb password event notification = true
+       dsdb group change notification = true
         $smbconf_args
 ";
 
@@ -1914,6 +2030,7 @@ sub provision_ad_dc($$$$$$)
        copy = print1
 ";
 
+       push (@{$extra_provision_options}, "--backend-store=mdb");
        print "PROVISIONING AD DC...\n";
        my $ret = $self->provision($prefix,
                                   "domain controller",
@@ -1926,7 +2043,7 @@ sub provision_ad_dc($$$$$$)
                                   undef,
                                   $extra_smbconf_options,
                                   $extra_smbconf_shares,
-                                  undef);
+                                  $extra_provision_options);
        unless (defined $ret) {
                return undef;
        }
@@ -1936,13 +2053,6 @@ sub provision_ad_dc($$$$$$)
                return undef;
        }
 
-       $ret->{DC_SERVER} = $ret->{SERVER};
-       $ret->{DC_SERVER_IP} = $ret->{SERVER_IP};
-       $ret->{DC_SERVER_IPV6} = $ret->{SERVER_IPV6};
-       $ret->{DC_NETBIOSNAME} = $ret->{NETBIOSNAME};
-       $ret->{DC_USERNAME} = $ret->{USERNAME};
-       $ret->{DC_PASSWORD} = $ret->{PASSWORD};
-
        return $ret;
 }
 
@@ -1951,10 +2061,15 @@ sub provision_chgdcpass($$)
        my ($self, $prefix) = @_;
 
        print "PROVISIONING CHGDCPASS...\n";
-       my $extra_provision_options = undef;
        # This environment disallows the use of this password
        # (and also removes the default AD complexity checks)
        my $unacceptable_password = "widk3Dsle32jxdBdskldsk55klASKQ";
+       my $extra_smb_conf = "
+       check password script = $self->{srcdir}/selftest/checkpassword_arg1.sh ${unacceptable_password}
+       allow dcerpc auth level connect:lsarpc = yes
+       dcesrv:max auth states = 8
+";
+       my $extra_provision_options = ["--use-ntvfs"];
        push (@{$extra_provision_options}, "--dns-backend=BIND9_DLZ");
        my $ret = $self->provision($prefix,
                                   "domain controller",
@@ -1965,7 +2080,7 @@ sub provision_chgdcpass($$)
                                   "chgDCpass1",
                                   undef,
                                   undef,
-                                  "check password script = sed -e '/$unacceptable_password/{;q1}; /$unacceptable_password/!{q0}'\n",
+                                  $extra_smb_conf,
                                   "",
                                   $extra_provision_options);
        unless (defined $ret) {
@@ -1984,13 +2099,7 @@ sub provision_chgdcpass($$)
                warn("Unable to remove $ret->{PRIVATEDIR}/secrets.tdb added during provision");
                return undef;
        }
-           
-       $ret->{DC_SERVER} = $ret->{SERVER};
-       $ret->{DC_SERVER_IP} = $ret->{SERVER_IP};
-       $ret->{DC_SERVER_IPV6} = $ret->{SERVER_IPV6};
-       $ret->{DC_NETBIOSNAME} = $ret->{NETBIOSNAME};
-       $ret->{DC_USERNAME} = $ret->{USERNAME};
-       $ret->{DC_PASSWORD} = $ret->{PASSWORD};
+
        $ret->{UNACCEPTABLE_PASSWORD} = $unacceptable_password;
 
        return $ret;
@@ -2001,6 +2110,15 @@ sub teardown_env_terminate($$)
        my ($self, $envvars) = @_;
        my $pid;
 
+       # This should cause samba to terminate gracefully
+       my $smbcontrol = Samba::bindir_path($self, "smbcontrol");
+       my $cmd = "";
+       $cmd .= "$smbcontrol samba shutdown $envvars->{CONFIGURATION}";
+       my $ret = system($cmd);
+       if ($ret != 0) {
+               warn "'$cmd' failed with '$ret'\n";
+       }
+
        # This should cause samba to terminate gracefully
        close($envvars->{STDIN_PIPE});
 
@@ -2103,17 +2221,21 @@ sub check_env($$)
 # Declare the environments Samba4 makes available.
 # To be set up, they will be called as
 #   samba4->setup_$envname($self, $path, $dep_1_vars, $dep_2_vars, ...)
+# The interdependencies between the testenvs are declared below. Some testenvs
+# are dependent on another testenv running first, e.g. vampire_dc is dependent
+# on ad_dc_ntvfs because vampire_dc joins ad_dc_ntvfs's domain. All DCs are
+# dependent on dns_hub, which handles resolving DNS queries for the realm.
 %Samba4::ENV_DEPS = (
        # name               => [dep_1, dep_2, ...],
-       ad_dc_ntvfs          => [],
-       ad_dc                => [],
-       ad_dc_no_nss         => [],
-       ad_dc_no_ntlm        => [],
-       ad_dc_ntvfs          => [],
+       dns_hub              => [],
+       ad_dc_ntvfs          => ["dns_hub"],
+       ad_dc                => ["dns_hub"],
+       ad_dc_no_nss         => ["dns_hub"],
+       ad_dc_no_ntlm        => ["dns_hub"],
 
        fl2008r2dc           => ["ad_dc"],
        fl2003dc             => ["ad_dc"],
-       fl2000dc             => [],
+       fl2000dc             => ["dns_hub"],
 
        vampire_2000_dc      => ["fl2000dc"],
        vampire_dc           => ["ad_dc_ntvfs"],
@@ -2122,14 +2244,71 @@ sub check_env($$)
 
        rodc                 => ["ad_dc_ntvfs"],
        rpc_proxy            => ["ad_dc_ntvfs"],
-       chgdcpass            => [],
+       chgdcpass            => ["dns_hub"],
 
        s4member_dflt_domain => ["ad_dc_ntvfs"],
        s4member             => ["ad_dc_ntvfs"],
 
+       # envs that test the server process model
+       proclimitdc          => ["dns_hub"],
+       preforkrestartdc     => ["dns_hub"],
+
+       # backup/restore testenvs
+       backupfromdc         => ["dns_hub"],
+       customdc             => ["dns_hub"],
+       restoredc            => ["backupfromdc"],
+       renamedc             => ["backupfromdc"],
+       offlinebackupdc      => ["backupfromdc"],
+       labdc                => ["backupfromdc"],
+
+       # aliases in order to split autbuild tasks
+       fl2008dc             => ["ad_dc_ntvfs"],
+       ad_dc_default        => ["ad_dc_ntvfs"],
+       ad_dc_slowtests      => ["ad_dc_ntvfs"],
+       ad_dc_backup         => ["ad_dc"],
+
+       schema_dc      => ["dns_hub"],
+       schema_pair_dc => ["schema_dc"],
+
        none                 => [],
 );
 
+%Samba4::ENV_DEPS_POST = (
+       schema_dc => ["schema_pair_dc"],
+);
+
+sub return_alias_env
+{
+       my ($self, $path, $env) = @_;
+
+       # just an alias
+       return $env;
+}
+
+sub setup_fl2008dc
+{
+       my ($self, $path, $dep_env) = @_;
+       return $self->return_alias_env($path, $dep_env)
+}
+
+sub setup_ad_dc_default
+{
+       my ($self, $path, $dep_env) = @_;
+       return $self->return_alias_env($path, $dep_env)
+}
+
+sub setup_ad_dc_slowtests
+{
+       my ($self, $path, $dep_env) = @_;
+       return $self->return_alias_env($path, $dep_env)
+}
+
+sub setup_ad_dc_backup
+{
+       my ($self, $path, $dep_env) = @_;
+       return $self->return_alias_env($path, $dep_env)
+}
+
 sub setup_s4member
 {
        my ($self, $path, $dc_vars) = @_;
@@ -2282,15 +2461,7 @@ sub setup_generic_vampire_dc
                # 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}));
-               my $cmd = "";
-               $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$env->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-               if (defined($env->{RESOLV_WRAPPER_CONF})) {
-                       $cmd .= "RESOLV_WRAPPER_CONF=\"$env->{RESOLV_WRAPPER_CONF}\" ";
-               } else {
-                       $cmd .= "RESOLV_WRAPPER_HOSTS=\"$env->{RESOLV_WRAPPER_HOSTS}\" ";
-               }
-               $cmd .= " KRB5_CONFIG=\"$env->{KRB5_CONFIG}\"";
-               $cmd .= "KRB5CCNAME=\"$env->{KRB5_CCACHE}\" ";
+               my $cmd = $self->get_cmd_env_vars($env);
                $cmd .= " $samba_tool drs replicate $env->{DC_SERVER} $env->{SERVER}";
                $cmd .= " $dc_vars->{CONFIGURATION}";
                $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
@@ -2309,15 +2480,7 @@ sub setup_generic_vampire_dc
 
                # Pull in a full set of changes from the main DC
                my $base_dn = "DC=".join(",DC=", split(/\./, $dc_vars->{REALM}));
-               $cmd = "";
-               $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$env->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
-               if (defined($env->{RESOLV_WRAPPER_CONF})) {
-                       $cmd .= "RESOLV_WRAPPER_CONF=\"$env->{RESOLV_WRAPPER_CONF}\" ";
-               } else {
-                       $cmd .= "RESOLV_WRAPPER_HOSTS=\"$env->{RESOLV_WRAPPER_HOSTS}\" ";
-               }
-               $cmd .= " KRB5_CONFIG=\"$env->{KRB5_CONFIG}\"";
-               $cmd .= "KRB5CCNAME=\"$env->{KRB5_CCACHE}\" ";
+               $cmd = $self->get_cmd_env_vars($env);
                $cmd .= " $samba_tool drs replicate $env->{SERVER} $env->{DC_SERVER}";
                $cmd .= " $dc_vars->{CONFIGURATION}";
                $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
@@ -2352,13 +2515,14 @@ 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 = "";
+               my $cmd = "NSS_WRAPPER_HOSTS='$env->{NSS_WRAPPER_HOSTS}' ";
                # 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}";
@@ -2393,7 +2557,7 @@ sub setup_subdom_dc
                # force replicated DC to update repsTo/repsFrom
                # for primary domain partitions
                my $samba_tool =  Samba::bindir_path($self, "samba-tool");
-               my $cmd = "";
+               my $cmd = "NSS_WRAPPER_HOSTS='$env->{NSS_WRAPPER_HOSTS}' ";
                # as 'subdomain' dc may add data in its local replica
                # we need to synchronize data between DCs
                my $base_dn = "DC=".join(",DC=", split(/\./, $env->{REALM}));
@@ -2401,6 +2565,7 @@ sub setup_subdom_dc
                $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->{SUBDOM_DC_SERVER}";
                $cmd .= " $dc_vars->{CONFIGURATION}";
                $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD} --realm=$dc_vars->{DC_REALM}";
@@ -2439,9 +2604,11 @@ sub setup_rodc
        my $cmd = "";
 
        my $base_dn = "DC=".join(",DC=", split(/\./, $dc_vars->{REALM}));
-       $cmd = "SOCKET_WRAPPER_DEFAULT_IFACE=\"$env->{SOCKET_WRAPPER_DEFAULT_IFACE}\"";
+       $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}";
@@ -2471,12 +2638,12 @@ sub setup_ad_dc
        }
 
        my $env = $self->provision_ad_dc($path, "addc", "ADDOMAIN",
-                                        "addom.samba.example.com", "");
+                                        "addom.samba.example.com", "", undef);
        unless ($env) {
                return undef;
        }
 
-       if (not defined($self->check_or_start($env, "single"))) {
+       if (not defined($self->check_or_start($env, "prefork"))) {
            return undef;
        }
 
@@ -2498,7 +2665,7 @@ sub setup_ad_dc_no_nss
        }
 
        my $env = $self->provision_ad_dc($path, "addc_no_nss", "ADNONSSDOMAIN",
-                                        "adnonssdom.samba.example.com", "");
+                                        "adnonssdom.samba.example.com", "", undef);
        unless ($env) {
                return undef;
        }
@@ -2529,7 +2696,7 @@ sub setup_ad_dc_no_ntlm
 
        my $env = $self->provision_ad_dc($path, "addc_no_ntlm", "ADNONTLMDOMAIN",
                                         "adnontlmdom.samba.example.com",
-                                        "ntlm auth = disabled");
+                                        "ntlm auth = disabled", undef);
        unless ($env) {
                return undef;
        }
@@ -2546,6 +2713,666 @@ sub setup_ad_dc_no_ntlm
        return $env;
 }
 
+#
+# AD DC test environment used solely to test pre-fork process restarts.
+# As processes get killed off and restarted it should not be used for other
+sub setup_preforkrestartdc
+{
+       my ($self, $path) = @_;
+
+       # If we didn't build with ADS, pretend this env was never available
+       if (not $self->{target3}->have_ads()) {
+              return "UNKNOWN";
+       }
+
+       # note DC name must be <= 15 chars so we use 'prockill' instead of
+       # 'preforkrestart'
+       my $env = $self->provision_ad_dc(
+               $path,
+               "prockilldc",
+               "PROCKILLDOMAIN",
+               "prockilldom.samba.example.com",
+               "prefork backoff increment = 5\nprefork maximum backoff=10");
+       unless ($env) {
+               return undef;
+       }
+
+       $env->{NSS_WRAPPER_MODULE_SO_PATH} = undef;
+       $env->{NSS_WRAPPER_MODULE_FN_PREFIX} = undef;
+
+       if (not defined($self->check_or_start($env, "prefork"))) {
+           return undef;
+       }
+
+       my $upn_array = ["$env->{REALM}.upn"];
+       my $spn_array = ["$env->{REALM}.spn"];
+
+       $self->setup_namespaces($env, $upn_array, $spn_array);
+
+       return $env;
+}
+
+#
+# ad_dc test environment used solely to test standard process model connection
+# process limits. As the limit is set artificially low it should not be used
+# for other tests.
+sub setup_proclimitdc
+{
+       my ($self, $path) = @_;
+
+       # If we didn't build with ADS, pretend this env was never available
+       if (not $self->{target3}->have_ads()) {
+              return "UNKNOWN";
+       }
+
+       my $env = $self->provision_ad_dc(
+               $path,
+               "proclimitdc",
+               "PROCLIMITDOM",
+               "proclimit.samba.example.com",
+               "max smbd processes = 20");
+       unless ($env) {
+               return undef;
+       }
+
+       $env->{NSS_WRAPPER_MODULE_SO_PATH} = undef;
+       $env->{NSS_WRAPPER_MODULE_FN_PREFIX} = undef;
+
+       if (not defined($self->check_or_start($env, "standard"))) {
+           return undef;
+       }
+
+       my $upn_array = ["$env->{REALM}.upn"];
+       my $spn_array = ["$env->{REALM}.spn"];
+
+       $self->setup_namespaces($env, $upn_array, $spn_array);
+
+       return $env;
+}
+
+# Used to test a live upgrade of the schema on a 2 DC network.
+sub setup_schema_dc
+{
+       my ($self, $path) = @_;
+
+       # provision the PDC using an older base schema
+       my $provision_args = ["--base-schema=2008_R2", "--backend-store=mdb"];
+
+       my $env = $self->provision_ad_dc($path, "liveupgrade1dc", "SCHEMADOMAIN",
+                                        "schema.samba.example.com",
+                                        "drs: max link sync = 2",
+                                        $provision_args);
+       unless ($env) {
+               return undef;
+       }
+
+       if (not defined($self->check_or_start($env, "prefork"))) {
+           return undef;
+       }
+
+       my $upn_array = ["$env->{REALM}.upn"];
+       my $spn_array = ["$env->{REALM}.spn"];
+
+       $self->setup_namespaces($env, $upn_array, $spn_array);
+
+       return $env;
+}
+
+# the second DC in the live schema upgrade pair
+sub setup_schema_pair_dc
+{
+       # note: dcvars contains the env info for the dependent testenv ('schema_dc')
+       my ($self, $prefix, $dcvars) = @_;
+       print "Preparing SCHEMA UPGRADE PAIR DC...\n";
+
+       my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "liveupgrade2dc",
+                                                   $dcvars->{DOMAIN},
+                                                   $dcvars->{REALM},
+                                                   $dcvars->{PASSWORD},
+                                                   "");
+
+       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 $join_cmd = $cmd_vars;
+       $join_cmd .= "$samba_tool domain join $env->{CONFIGURATION} $dcvars->{REALM} DC --realm=$dcvars->{REALM}";
+       $join_cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD} ";
+       $join_cmd .= " --backend-store=mdb";
+
+       my $upgrade_cmd = $cmd_vars;
+       $upgrade_cmd .= "$samba_tool domain schemaupgrade $dcvars->{CONFIGURATION}";
+       $upgrade_cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}";
+
+       my $repl_cmd = $cmd_vars;
+       $repl_cmd .= "$samba_tool drs replicate $env->{SERVER} $dcvars->{SERVER}";
+        $repl_cmd .= " CN=Schema,CN=Configuration,DC=schema,DC=samba,DC=example,DC=com";
+       $repl_cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
+
+       unless (system($join_cmd) == 0) {
+               warn("Join failed\n$join_cmd");
+               return undef;
+       }
+
+       $env->{DC_SERVER} = $dcvars->{SERVER};
+       $env->{DC_SERVER_IP} = $dcvars->{SERVER_IP};
+       $env->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6};
+       $env->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME};
+
+       # start samba for the new DC
+       if (not defined($self->check_or_start($env, "standard"))) {
+           return undef;
+       }
+
+       unless (system($upgrade_cmd) == 0) {
+               warn("Schema upgrade failed\n$upgrade_cmd");
+               return undef;
+       }
+
+       unless (system($repl_cmd) == 0) {
+               warn("Post-update schema replication failed\n$repl_cmd");
+               return undef;
+       }
+
+       return $env;
+}
+
+# Sets up a DC that's solely used to do a domain backup from. We then use the
+# backupfrom-DC to create the restore-DC - this proves that the backup/restore
+# process will create a Samba DC that will actually start up.
+# We don't use the backup-DC for anything else because its domain will conflict
+# with the restore DC.
+sub setup_backupfromdc
+{
+       my ($self, $path) = @_;
+
+       # If we didn't build with ADS, pretend this env was never available
+       if (not $self->{target3}->have_ads()) {
+              return "UNKNOWN";
+       }
+
+       my $provision_args = ["--site=Backup-Site"];
+
+       my $env = $self->provision_ad_dc($path, "backupfromdc", "BACKUPDOMAIN",
+                                        "backupdom.samba.example.com",
+                                        "samba kcc command = /bin/true",
+                                        $provision_args);
+       unless ($env) {
+               return undef;
+       }
+
+       if (not defined($self->check_or_start($env))) {
+           return undef;
+       }
+
+       my $upn_array = ["$env->{REALM}.upn"];
+       my $spn_array = ["$env->{REALM}.spn"];
+
+       $self->setup_namespaces($env, $upn_array, $spn_array);
+
+       return $env;
+}
+
+# returns the server/user-auth params needed to run an online backup cmd
+sub get_backup_server_args
+{
+       # dcvars contains the env info for the backup DC testenv
+       my ($self, $dcvars) = @_;
+       my $server = $dcvars->{DC_SERVER_IP};
+       my $server_args = "--server=$server ";
+       $server_args .= "-U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
+       $server_args .= " $dcvars->{CONFIGURATION}";
+
+       return $server_args;
+}
+
+# Creates a backup of a running testenv DC
+sub create_backup
+{
+       # note: dcvars contains the env info for the backup DC testenv
+       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}\" ";
+       }
+       # 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}\" ";
+
+       # use samba-tool to create a backup from the 'backupfromdc' DC
+       my $cmd = "";
+       my $samba_tool = Samba::bindir_path($self, "samba-tool");
+
+       $cmd .= "$cmd_env $samba_tool domain backup $backup_cmd";
+       $cmd .= " --targetdir=$backupdir";
+
+       print "Executing: $cmd\n";
+       unless(system($cmd) == 0) {
+               warn("Failed to create backup using: \n$cmd");
+               return undef;
+       }
+
+       # get the name of the backup file created
+       opendir(DIR, $backupdir);
+       my @files = grep(/\.tar/, readdir(DIR));
+       closedir(DIR);
+
+       if(scalar @files != 1) {
+               warn("Backup file not found in directory $backupdir\n");
+               return undef;
+       }
+       my $backup_file = "$backupdir/$files[0]";
+       print "Using backup file $backup_file...\n";
+
+       return $backup_file;
+}
+
+# Restores a backup-file to populate a testenv for a new DC
+sub restore_backup_file
+{
+       my ($self, $backup_file, $restore_opts, $restoredir, $smbconf) = @_;
+
+       # pass the restore command the testenv's smb.conf that we've already
+       # generated. But move it to a temp-dir first, so that the restore doesn't
+       # overwrite it
+       my $tmpdir = File::Temp->newdir();
+       my $tmpconf = "$tmpdir/smb.conf";
+       my $cmd = "cp $smbconf $tmpconf";
+       unless(system($cmd) == 0) {
+               warn("Failed to backup smb.conf using: \n$cmd");
+               return -1;
+       }
+
+       my $samba_tool = Samba::bindir_path($self, "samba-tool");
+       $cmd = "$samba_tool domain backup restore --backup-file=$backup_file";
+       $cmd .= " --targetdir=$restoredir $restore_opts --configfile=$tmpconf";
+
+       print "Executing: $cmd\n";
+       unless(system($cmd) == 0) {
+               warn("Failed to restore backup using: \n$cmd");
+               return -1;
+       }
+
+       print "Restore complete\n";
+       return 0
+}
+
+# sets up the initial directory and returns the new testenv's env info
+# (without actually doing a 'domain join')
+sub prepare_dc_testenv
+{
+       my ($self, $prefix, $dcname, $domain, $realm,
+               $password, $conf_options) = @_;
+
+       my $ctx = $self->provision_raw_prepare($prefix, "domain controller",
+                                              $dcname,
+                                              $domain,
+                                              $realm,
+                                              undef,
+                                              "2008",
+                                              $password,
+                                              undef,
+                                              undef);
+
+       # the restore uses a slightly different state-dir location to other testenvs
+       $ctx->{statedir} = "$ctx->{prefix_abs}/state";
+       push(@{$ctx->{directories}}, "$ctx->{statedir}");
+
+       # add support for sysvol/netlogon/tmp shares
+       $ctx->{share} = "$ctx->{prefix_abs}/share";
+       push(@{$ctx->{directories}}, "$ctx->{share}");
+       push(@{$ctx->{directories}}, "$ctx->{share}/test1");
+
+       $ctx->{smb_conf_extra_options} = "
+       $conf_options
+       max xmit = 32K
+       server max protocol = SMB2
+       samba kcc command = /bin/true
+       xattr_tdb:file = $ctx->{statedir}/xattr.tdb
+
+[sysvol]
+       path = $ctx->{statedir}/sysvol
+       read only = no
+
+[netlogon]
+       path = $ctx->{statedir}/sysvol/$ctx->{dnsname}/scripts
+       read only = no
+
+[tmp]
+       path = $ctx->{share}
+       read only = no
+       posix:sharedelay = 10000
+       posix:oplocktimeout = 3
+       posix:writetimeupdatedelay = 50000
+
+[test1]
+       path = $ctx->{share}/test1
+       read only = no
+       posix:sharedelay = 100000
+       posix:oplocktimeout = 3
+       posix:writetimeupdatedelay = 500000
+";
+
+       my $env = $self->provision_raw_step1($ctx);
+
+    return ($env, $ctx);
+}
+
+
+# Set up a DC testenv solely by using the samba-tool domain backup/restore
+# commands. This proves that we can backup an online DC ('backupfromdc') and
+# use the backup file to create a valid, working samba DC.
+sub setup_restoredc
+{
+       # note: dcvars contains the env info for the dependent testenv ('backupfromdc')
+       my ($self, $prefix, $dcvars) = @_;
+       print "Preparing RESTORE DC...\n";
+
+       # we arbitrarily designate the restored DC as having SMBv1 disabled
+       my $extra_conf = "
+       server min protocol = SMB2
+       client min protocol = SMB2
+       prefork children = 1";
+
+       my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "restoredc",
+                                                   $dcvars->{DOMAIN},
+                                                   $dcvars->{REALM},
+                                                   $dcvars->{PASSWORD},
+                                                   $extra_conf);
+
+       # create a backup of the 'backupfromdc'
+       my $backupdir = File::Temp->newdir();
+       my $server_args = $self->get_backup_server_args($dcvars);
+       my $backup_args = "online $server_args";
+       my $backup_file = $self->create_backup($env, $dcvars, $backupdir,
+                                              $backup_args);
+       unless($backup_file) {
+               return undef;
+       }
+
+       # restore the backup file to populate the restore-DC testenv
+       my $restore_dir = abs_path($prefix);
+       my $ret = $self->restore_backup_file($backup_file,
+                                            "--newservername=$env->{SERVER}",
+                                            $restore_dir, $env->{SERVERCONFFILE});
+       unless ($ret == 0) {
+               return undef;
+       }
+
+       # start samba for the restored DC
+       if (not defined($self->check_or_start($env))) {
+           return undef;
+       }
+
+       return $env;
+}
+
+# Set up a DC testenv solely by using the 'samba-tool domain backup rename' and
+# restore commands. This proves that we can backup and rename an online DC
+# ('backupfromdc') and use the backup file to create a valid, working samba DC.
+sub setup_renamedc
+{
+       # note: dcvars contains the env info for the dependent testenv ('backupfromdc')
+       my ($self, $prefix, $dcvars) = @_;
+       print "Preparing RENAME DC...\n";
+       my $extra_conf = "prefork children = 1";
+
+       my $realm = "renamedom.samba.example.com";
+       my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "renamedc",
+                                                   "RENAMEDOMAIN", $realm,
+                                                   $dcvars->{PASSWORD}, $extra_conf);
+
+       # create a backup of the 'backupfromdc' which renames the domain
+       my $backupdir = File::Temp->newdir();
+       my $server_args = $self->get_backup_server_args($dcvars);
+       my $backup_args = "rename $env->{DOMAIN} $env->{REALM} $server_args";
+       $backup_args .= " --backend-store=tdb";
+       my $backup_file = $self->create_backup($env, $dcvars, $backupdir,
+                                              $backup_args);
+       unless($backup_file) {
+               return undef;
+       }
+
+       # restore the backup file to populate the rename-DC testenv
+       my $restore_dir = abs_path($prefix);
+       my $restore_opts =  "--newservername=$env->{SERVER} --host-ip=$env->{SERVER_IP}";
+       my $ret = $self->restore_backup_file($backup_file, $restore_opts,
+                                            $restore_dir, $env->{SERVERCONFFILE});
+       unless ($ret == 0) {
+               return undef;
+       }
+
+       # start samba for the restored DC
+       if (not defined($self->check_or_start($env))) {
+           return undef;
+       }
+
+       my $upn_array = ["$env->{REALM}.upn"];
+       my $spn_array = ["$env->{REALM}.spn"];
+
+       $self->setup_namespaces($env, $upn_array, $spn_array);
+
+       return $env;
+}
+
+# Set up a DC testenv solely by using the 'samba-tool domain backup offline' and
+# restore commands. This proves that we do an offline backup of a local DC
+# ('backupfromdc') and use the backup file to create a valid, working samba DC.
+sub setup_offlinebackupdc
+{
+       # note: dcvars contains the env info for the dependent testenv ('backupfromdc')
+       my ($self, $prefix, $dcvars) = @_;
+       print "Preparing OFFLINE BACKUP DC...\n";
+       my $extra_conf = "prefork children = 1";
+
+       my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "offlinebackupdc",
+                                                   $dcvars->{DOMAIN},
+                                                   $dcvars->{REALM},
+                                                   $dcvars->{PASSWORD}, $extra_conf);
+
+       # create an offline backup of the 'backupfromdc' target
+       my $backupdir = File::Temp->newdir();
+       my $cmd = "offline -s $dcvars->{SERVERCONFFILE}";
+       my $backup_file = $self->create_backup($env, $dcvars,
+                                              $backupdir, $cmd);
+
+       unless($backup_file) {
+               return undef;
+       }
+
+       # restore the backup file to populate the rename-DC testenv
+       my $restore_dir = abs_path($prefix);
+       my $restore_opts =  "--newservername=$env->{SERVER} --host-ip=$env->{SERVER_IP}";
+       my $ret = $self->restore_backup_file($backup_file, $restore_opts,
+                                            $restore_dir, $env->{SERVERCONFFILE});
+       unless ($ret == 0) {
+               return undef;
+       }
+
+       # re-create the testenv's krb5.conf (the restore may have overwritten it)
+       Samba::mk_krb5_conf($ctx);
+
+       # start samba for the restored DC
+       if (not defined($self->check_or_start($env))) {
+           return undef;
+       }
+
+       return $env;
+}
+
+# Set up a DC testenv solely by using the samba-tool 'domain backup rename' and
+# restore commands, using the --no-secrets option. This proves that we can
+# create a realistic lab environment from an online DC ('backupfromdc').
+sub setup_labdc
+{
+       # note: dcvars contains the env info for the dependent testenv ('backupfromdc')
+       my ($self, $prefix, $dcvars) = @_;
+       print "Preparing LAB-DOMAIN DC...\n";
+       my $extra_conf = "prefork children = 1";
+
+       my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "labdc",
+                                                   "LABDOMAIN",
+                                                   "labdom.samba.example.com",
+                                                   $dcvars->{PASSWORD}, $extra_conf);
+
+       # create a backup of the 'backupfromdc' which renames the domain and uses
+       # the --no-secrets option to scrub any sensitive info
+       my $backupdir = File::Temp->newdir();
+       my $server_args = $self->get_backup_server_args($dcvars);
+       my $backup_args = "rename $env->{DOMAIN} $env->{REALM} $server_args";
+       $backup_args .= " --no-secrets --backend-store=mdb";
+       my $backup_file = $self->create_backup($env, $dcvars, $backupdir,
+                                              $backup_args);
+       unless($backup_file) {
+               return undef;
+       }
+
+       # restore the backup file to populate the lab-DC testenv
+       my $restore_dir = abs_path($prefix);
+       my $restore_opts =  "--newservername=$env->{SERVER} --host-ip=$env->{SERVER_IP}";
+       my $ret = $self->restore_backup_file($backup_file, $restore_opts,
+                                            $restore_dir, $env->{SERVERCONFFILE});
+       unless ($ret == 0) {
+               return undef;
+       }
+
+       # because we don't include any secrets in the backup, we need to reset the
+       # admin user's password back to what the testenv expects
+       my $samba_tool = Samba::bindir_path($self, "samba-tool");
+       my $cmd = "$samba_tool user setpassword $env->{USERNAME} ";
+       $cmd .= "--newpassword=$env->{PASSWORD} -H $restore_dir/private/sam.ldb";
+       $cmd .= " $env->{CONFIGURATION}";
+
+       unless(system($cmd) == 0) {
+               warn("Failed to reset admin's password: \n$cmd");
+               return undef;
+       }
+
+       # start samba for the restored DC
+       if (not defined($self->check_or_start($env))) {
+           return undef;
+       }
+
+       my $upn_array = ["$env->{REALM}.upn"];
+       my $spn_array = ["$env->{REALM}.spn"];
+
+       $self->setup_namespaces($env, $upn_array, $spn_array);
+
+       return $env;
+}
+
+# Inspects a backup *.tar.bz2 file and determines the realm/domain it contains
+sub get_backup_domain_realm
+{
+       my ($self, $backup_file) = @_;
+
+       print "Determining REALM/DOMAIN values in backup...\n";
+
+       # The backup will have the correct domain/realm values in the smb.conf.
+       # So we can work out the env variables the testenv should use based on
+       # that. Let's start by extracting the smb.conf
+       my $tar = Archive::Tar->new($backup_file);
+       my $tmpdir = File::Temp->newdir();
+       my $smbconf = "$tmpdir/smb.conf";
+
+       # note that the filepaths within the tar-file differ slightly for online
+       # and offline backups
+       if ($tar->contains_file("etc/smb.conf")) {
+               $tar->extract_file("etc/smb.conf", $smbconf);
+       } elsif ($tar->contains_file("./etc/smb.conf")) {
+               $tar->extract_file("./etc/smb.conf", $smbconf);
+       } else {
+               warn("Could not find smb.conf in $backup_file");
+               return undef, undef;
+       }
+
+       # now use testparm to read the values we're interested in
+       my $testparm = Samba::bindir_path($self, "testparm");
+       my $domain = `$testparm $smbconf -sl --parameter-name=WORKGROUP`;
+       my $realm = `$testparm $smbconf -sl --parameter-name=REALM`;
+       chomp $realm;
+       chomp $domain;
+       print "Backup-file REALM is $realm, DOMAIN is $domain\n";
+
+       return ($domain, $realm);
+}
+
+# This spins up a custom testenv that can be based on any backup-file you want.
+# This is just intended for manual testing (rather than automated test-cases)
+sub setup_customdc
+{
+       my ($self, $prefix) = @_;
+       print "Preparing CUSTOM RESTORE DC...\n";
+       my $dc_name = "customdc";
+       my $password = "locDCpass1";
+       my $backup_file = $ENV{'BACKUP_FILE'};
+
+       # user must specify a backup file to restore via an ENV variable, i.e.
+       # BACKUP_FILE=backup-blah.tar.bz2 SELFTEST_TESTENV=customdc make testenv
+       if (not defined($backup_file)) {
+               warn("Please specify BACKUP_FILE");
+               return undef;
+       }
+
+       # work out the correct domain/realm env values from the backup-file
+       my ($domain, $realm) = $self->get_backup_domain_realm($backup_file);
+
+       # create a placeholder directory and smb.conf, as well as the env vars.
+       my ($env, $ctx) = $self->prepare_dc_testenv($prefix, $dc_name,
+                                                   $domain, $realm, $password, "");
+
+       # restore the specified backup file to populate the testenv
+       my $restore_dir = abs_path($prefix);
+       my $ret = $self->restore_backup_file($backup_file,
+                                            "--newservername=$env->{SERVER}",
+                                            $restore_dir, $env->{SERVERCONFFILE});
+       unless ($ret == 0) {
+               return undef;
+       }
+
+       # Change the admin password to the testenv default, just in case it's
+       # different, or in case this was a --no-secrets backup
+       my $samba_tool = Samba::bindir_path($self, "samba-tool");
+       my $cmd = "$samba_tool user setpassword $env->{USERNAME} ";
+       $cmd .= "--newpassword=$password -H $restore_dir/private/sam.ldb";
+       $cmd .= " $env->{CONFIGURATION}";
+
+       unless(system($cmd) == 0) {
+               warn("Failed to reset admin's password: \n$cmd");
+               return undef;
+       }
+
+       # re-create the testenv's krb5.conf (the restore may have overwritten it,
+       # if the backup-file was an offline backup)
+       Samba::mk_krb5_conf($ctx);
+
+       # start samba for the restored DC
+       if (not defined($self->check_or_start($env))) {
+           return undef;
+       }
+
+       # if this was a backup-rename, then we may need to setup namespaces
+       my $upn_array = ["$env->{REALM}.upn"];
+       my $spn_array = ["$env->{REALM}.spn"];
+
+       $self->setup_namespaces($env, $upn_array, $spn_array);
+
+       return $env;
+}
+
 sub setup_none
 {
        my ($self, $path) = @_;