s3:rpc_server: Check query level according to MS-NRPC
[samba.git] / selftest / target / Samba4.pm
1 #!/usr/bin/perl
2 # Bootstrap Samba and run a number of tests against it.
3 # Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org>
4 # Published under the GNU GPL, v3 or later.
5
6 # NOTE: Refer to the README for more details about the various testenvs,
7 # and tips about adding new testenvs.
8
9 package Samba4;
10
11 use strict;
12 use warnings;
13 use Cwd qw(abs_path);
14 use FindBin qw($RealBin);
15 use POSIX;
16 use SocketWrapper;
17 use target::Samba;
18 use target::Samba3;
19 use Archive::Tar;
20
21 sub new($$$$$) {
22         my ($classname, $SambaCtx, $bindir, $srcdir, $server_maxtime, $default_ldb_backend) = @_;
23
24         my $self = {
25                 vars => {},
26                 SambaCtx => $SambaCtx,
27                 bindir => $bindir,
28                 srcdir => $srcdir,
29                 server_maxtime => $server_maxtime,
30                 target3 => new Samba3($SambaCtx, $bindir, $srcdir, $server_maxtime),
31                 default_ldb_backend => $default_ldb_backend,
32         };
33         bless $self;
34         return $self;
35 }
36
37 sub scriptdir_path($$) {
38         my ($self, $path) = @_;
39         return "$self->{srcdir}/source4/scripting/$path";
40 }
41
42 sub check_or_start($$$)
43 {
44         my ($self, $env_vars, $process_model) = @_;
45         my $STDIN_READER;
46
47         my $env_ok = $self->check_env($env_vars);
48         if ($env_ok) {
49                 return $env_vars->{SAMBA_PID};
50         } elsif (defined($env_vars->{SAMBA_PID})) {
51                 warn("SAMBA PID $env_vars->{SAMBA_PID} is not running (died)");
52                 return undef;
53         }
54
55         # use a pipe for stdin in the child processes. This allows
56         # those processes to monitor the pipe for EOF to ensure they
57         # exit when the test script exits
58         pipe($STDIN_READER, $env_vars->{STDIN_PIPE});
59
60         # build up the command to run samba
61         my @preargs = ();
62         my @optargs = ();
63         if (defined($ENV{SAMBA_OPTIONS})) {
64                 @optargs = split(/ /, $ENV{SAMBA_OPTIONS});
65         }
66         if(defined($ENV{SAMBA_VALGRIND})) {
67                 @preargs = split(/ /,$ENV{SAMBA_VALGRIND});
68         }
69
70         if (defined($process_model)) {
71                 push @optargs, ("-M", $process_model);
72         }
73         my $binary = Samba::bindir_path($self, "samba");
74         my @full_cmd = (@preargs, $binary, "-i",
75                         "--no-process-group", "--maximum-runtime=$self->{server_maxtime}",
76                         $env_vars->{CONFIGURATION}, @optargs);
77
78         # the samba process takes some additional env variables (compared to s3)
79         my $samba_envs = Samba::get_env_for_process("samba", $env_vars);
80         if (defined($ENV{MITKRB5})) {
81                 $samba_envs->{KRB5_KDC_PROFILE} = $env_vars->{MITKDC_CONFIG};
82         }
83
84         # fork a child process and exec() samba
85         my $daemon_ctx = {
86                 NAME => "samba",
87                 BINARY_PATH => $binary,
88                 FULL_CMD => [ @full_cmd ],
89                 LOG_FILE => $env_vars->{SAMBA_TEST_LOG},
90                 TEE_STDOUT => 1,
91                 PCAP_FILE => "env-$ENV{ENVNAME}-samba",
92                 ENV_VARS => $samba_envs,
93         };
94         my $pid = Samba::fork_and_exec($self, $env_vars, $daemon_ctx, $STDIN_READER);
95
96         $env_vars->{SAMBA_PID} = $pid;
97
98         # close the parent's read-end of the pipe
99         close($STDIN_READER);
100
101         if ($self->wait_for_start($env_vars) != 0) {
102             warn("Samba $pid failed to start up");
103             return undef;
104         }
105
106         return $pid;
107 }
108
109 sub wait_for_start($$)
110 {
111         my ($self, $testenv_vars) = @_;
112         my $count = 0;
113         my $ret = 0;
114
115         if (not $self->check_env($testenv_vars)) {
116             warn("unable to confirm Samba $testenv_vars->{SAMBA_PID} is running");
117             return -1;
118         }
119
120         # This will return quickly when things are up, but be slow if we
121         # need to wait for (eg) SSL init
122         my $nmblookup =  Samba::bindir_path($self, "nmblookup4");
123
124         do {
125                 $ret = system("$nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{SERVER}");
126                 if ($ret != 0) {
127                         sleep(1);
128                 } else {
129                         system("$nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{SERVER}");
130                         system("$nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{NETBIOSNAME}");
131                         system("$nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{NETBIOSNAME}");
132                         system("$nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{NETBIOSNAME}");
133                         system("$nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{NETBIOSNAME}");
134                         system("$nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{SERVER}");
135                         system("$nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{SERVER}");
136                         system("$nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{NETBIOSNAME}");
137                         system("$nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{NETBIOSNAME}");
138                         system("$nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{NETBIOSNAME}");
139                         system("$nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{NETBIOSNAME}");
140                 }
141                 $count++;
142         } while ($ret != 0 && $count < 20);
143         if ($count == 20) {
144                 teardown_env($self, $testenv_vars);
145                 warn("nbt not reachable after 20 retries\n");
146                 return -1;
147         }
148
149         # Ensure we have the first RID Set before we start tests.  This makes the tests more reliable.
150         if ($testenv_vars->{SERVER_ROLE} eq "domain controller") {
151                 print "waiting for working LDAP and a RID Set to be allocated\n";
152                 my $ldbsearch = Samba::bindir_path($self, "ldbsearch");
153                 my $count = 0;
154                 my $base_dn = "DC=".join(",DC=", split(/\./, $testenv_vars->{REALM}));
155
156                 my $search_dn = $base_dn;
157                 if ($testenv_vars->{NETBIOSNAME} ne "RODC") {
158                         # TODO currently no check for actual rIDAllocationPool
159                         $search_dn = "cn=RID Set,cn=$testenv_vars->{NETBIOSNAME},ou=domain controllers,$base_dn";
160                 }
161                 my $max_wait = 60;
162
163                 # Add hosts file for name lookups
164                 my $cmd = $self->get_cmd_env_vars($testenv_vars);
165
166                 $cmd .= "$ldbsearch ";
167                 $cmd .= "$testenv_vars->{CONFIGURATION} ";
168                 $cmd .= "-H ldap://$testenv_vars->{SERVER} ";
169                 $cmd .= "-U$testenv_vars->{USERNAME}%$testenv_vars->{PASSWORD} ";
170                 $cmd .= "--scope base ";
171                 $cmd .= "-b '$search_dn' ";
172                 while (system("$cmd >/dev/null") != 0) {
173                         $count++;
174                         if ($count > $max_wait) {
175                                 teardown_env($self, $testenv_vars);
176                                 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}");
177                                 return -1;
178                         }
179                         print "Waiting for working LDAP...\n";
180                         sleep(1);
181                 }
182         }
183
184         my $wbinfo =  Samba::bindir_path($self, "wbinfo");
185
186         $count = 0;
187         do {
188                 my $cmd = "NSS_WRAPPER_PASSWD=$testenv_vars->{NSS_WRAPPER_PASSWD} ";
189                 $cmd .= "NSS_WRAPPER_GROUP=$testenv_vars->{NSS_WRAPPER_GROUP} ";
190                 $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=$testenv_vars->{SELFTEST_WINBINDD_SOCKET_DIR} ";
191                 $cmd .= "$wbinfo -P";
192                 $ret = system($cmd);
193
194                 if ($ret != 0) {
195                         sleep(1);
196                 }
197                 $count++;
198         } while ($ret != 0 && $count < 20);
199         if ($count == 20) {
200                 teardown_env($self, $testenv_vars);
201                 warn("winbind not reachable after 20 retries\n");
202                 return -1;
203         }
204
205         # Ensure we registered all our names
206         if ($testenv_vars->{SERVER_ROLE} eq "domain controller") {
207                 my $max_wait = 120;
208                 my $dns_update_cache = "$testenv_vars->{PRIVATEDIR}/dns_update_cache";
209                 print "Waiting for $dns_update_cache to be created.\n";
210                 $count = 0;
211                 while (not -e $dns_update_cache) {
212                         $count++;
213                         if ($count > $max_wait) {
214                                 teardown_env($self, $testenv_vars);
215                                 warn("Timed out ($max_wait sec) waiting for $dns_update_cache PID $testenv_vars->{SAMBA_PID}");
216                                 return -1;
217                         }
218                         print "Waiting for $dns_update_cache to be created...\n";
219                         sleep(1);
220                 }
221                 print "Waiting for $dns_update_cache to be filled.\n";
222                 $count = 0;
223                 while ((-s "$dns_update_cache") == 0) {
224                         $count++;
225                         if ($count > $max_wait) {
226                                 teardown_env($self, $testenv_vars);
227                                 warn("Timed out ($max_wait sec) waiting for $dns_update_cache PID $testenv_vars->{SAMBA_PID}");
228                                 return -1;
229                         }
230                         print "Waiting for $dns_update_cache to be filled...\n";
231                         sleep(1);
232                 }
233         }
234
235         print $self->getlog_env($testenv_vars);
236
237         print "READY ($testenv_vars->{SAMBA_PID})\n";
238
239         return 0
240 }
241
242 sub write_ldb_file($$$)
243 {
244         my ($self, $file, $ldif_in) = @_;
245
246         my $ldbadd =  Samba::bindir_path($self, "ldbadd");
247         open(my $ldif, "|$ldbadd -H $file > /dev/null")
248             or die "Failed to run $ldbadd: $!";
249         print $ldif $ldif_in;
250         close($ldif);
251
252         unless ($? == 0) {
253             warn("$ldbadd failed: $?");
254             return undef;
255         }
256         return 1;
257 }
258
259 sub add_wins_config($$)
260 {
261         my ($self, $privatedir) = @_;
262         my $client_ip = Samba::get_ipv4_addr("client");
263
264         return $self->write_ldb_file("$privatedir/wins_config.ldb", "
265 dn: name=TORTURE_11,CN=PARTNERS
266 objectClass: wreplPartner
267 name: TORTURE_11
268 address: $client_ip
269 pullInterval: 0
270 pushChangeCount: 0
271 type: 0x3
272 ");
273 }
274
275 sub setup_dns_hub_internal($$$)
276 {
277         my ($self, $hostname, $prefix) = @_;
278         my $STDIN_READER;
279
280         unless(-d $prefix or mkdir($prefix, 0777)) {
281                 warn("Unable to create $prefix");
282                 return undef;
283         }
284         my $prefix_abs = abs_path($prefix);
285
286         die ("prefix=''") if $prefix_abs eq "";
287         die ("prefix='/'") if $prefix_abs eq "/";
288
289         unless (system("rm -rf $prefix_abs/*") == 0) {
290                 warn("Unable to clean up");
291         }
292
293         my $env = undef;
294         $env->{NETBIOSNAME} = $hostname;
295
296         $env->{SERVER_IP} = Samba::get_ipv4_addr($hostname);
297         $env->{SERVER_IPV6} = Samba::get_ipv6_addr($hostname);
298         $env->{SOCKET_WRAPPER_DEFAULT_IFACE} = Samba::get_interface($hostname);
299         $env->{DNS_HUB_LOG} = "$prefix_abs/dns_hub.log";
300         $env->{RESOLV_CONF} = "$prefix_abs/resolv.conf";
301         $env->{TESTENV_DIR} = $prefix_abs;
302
303         my $ctx = undef;
304         $ctx->{resolv_conf} = $env->{RESOLV_CONF};
305         $ctx->{dns_ipv4} = $env->{SERVER_IP};
306         $ctx->{dns_ipv6} = $env->{SERVER_IPV6};
307         Samba::mk_resolv_conf($ctx);
308
309         my @preargs = ();
310         my @args = ();
311         if (!defined($ENV{PYTHON})) {
312             push (@preargs, "env");
313             push (@preargs, "python");
314         } else {
315             push (@preargs, $ENV{PYTHON});
316         }
317         my $binary = "$self->{srcdir}/selftest/target/dns_hub.py";
318         push (@args, "$self->{server_maxtime}");
319         push (@args, "$env->{SERVER_IP},$env->{SERVER_IPV6}");
320         push (@args, Samba::realm_to_ip_mappings());
321         my @full_cmd = (@preargs, $binary, @args);
322
323         my $daemon_ctx = {
324                 NAME => "dnshub",
325                 BINARY_PATH => $binary,
326                 FULL_CMD => [ @full_cmd ],
327                 LOG_FILE => $env->{DNS_HUB_LOG},
328                 TEE_STDOUT => 1,
329                 PCAP_FILE => "env-$ENV{ENVNAME}-dns_hub",
330                 ENV_VARS => {},
331         };
332
333         # use a pipe for stdin in the child processes. This allows
334         # those processes to monitor the pipe for EOF to ensure they
335         # exit when the test script exits
336         pipe($STDIN_READER, $env->{STDIN_PIPE});
337
338         my $pid = Samba::fork_and_exec($self, $env, $daemon_ctx, $STDIN_READER);
339
340         $env->{SAMBA_PID} = $pid;
341         $env->{KRB5_CONFIG} = "$prefix_abs/no_krb5.conf";
342
343         # close the parent's read-end of the pipe
344         close($STDIN_READER);
345
346         return $env;
347 }
348
349 sub setup_dns_hub
350 {
351         my ($self, $prefix) = @_;
352
353         my $hostname = "rootdnsforwarder";
354
355         unless(-d $prefix or mkdir($prefix, 0777)) {
356                 warn("Unable to create $prefix");
357                 return undef;
358         }
359         my $env = $self->setup_dns_hub_internal("$hostname", "$prefix/$hostname");
360
361         $self->{dns_hub_env} = $env;
362
363         return $env;
364 }
365
366 sub get_dns_hub_env($)
367 {
368         my ($self, $prefix) = @_;
369
370         if (defined($self->{dns_hub_env})) {
371                 return $self->{dns_hub_env};
372         }
373
374         die("get_dns_hub_env() not setup 'dns_hub_env'");
375         return undef;
376 }
377
378 sub return_env_value
379 {
380         my ($env, $overwrite, $key) = @_;
381
382         if (defined($overwrite) and defined($overwrite->{$key})) {
383                 return $overwrite->{$key};
384         }
385
386         if (defined($env->{$key})) {
387                 return $env->{$key};
388         }
389
390         return undef;
391 }
392
393 # Returns the environmental variables that we pass to samba-tool commands
394 sub get_cmd_env_vars
395 {
396         my ($self, $givenenv, $overwrite) = @_;
397
398         my @keys = (
399                 "NSS_WRAPPER_HOSTS",
400                 "SOCKET_WRAPPER_DEFAULT_IFACE",
401                 "RESOLV_CONF",
402                 "RESOLV_WRAPPER_CONF",
403                 "RESOLV_WRAPPER_HOSTS",
404                 "GNUTLS_FORCE_FIPS_MODE",
405                 "OPENSSL_FORCE_FIPS_MODE",
406                 "KRB5_CONFIG",
407                 "KRB5_CCACHE",
408                 "GNUPGHOME",
409         );
410
411         my $localenv = undef;
412         foreach my $key (@keys) {
413                 my $v = return_env_value($givenenv, $overwrite, $key);
414                 $localenv->{$key} = $v if defined($v);
415         }
416
417         my $cmd_env = "NSS_WRAPPER_HOSTS='$localenv->{NSS_WRAPPER_HOSTS}' ";
418         $cmd_env .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$localenv->{SOCKET_WRAPPER_DEFAULT_IFACE}\" ";
419         if (defined($localenv->{RESOLV_WRAPPER_CONF})) {
420                 $cmd_env .= "RESOLV_WRAPPER_CONF=\"$localenv->{RESOLV_WRAPPER_CONF}\" ";
421         } else {
422                 $cmd_env .= "RESOLV_WRAPPER_HOSTS=\"$localenv->{RESOLV_WRAPPER_HOSTS}\" ";
423         }
424         if (defined($localenv->{GNUTLS_FORCE_FIPS_MODE})) {
425                 $cmd_env .= "GNUTLS_FORCE_FIPS_MODE=$localenv->{GNUTLS_FORCE_FIPS_MODE} ";
426         }
427         if (defined($localenv->{OPENSSL_FORCE_FIPS_MODE})) {
428                 $cmd_env .= "OPENSSL_FORCE_FIPS_MODE=$localenv->{OPENSSL_FORCE_FIPS_MODE} ";
429         }
430         $cmd_env .= "KRB5_CONFIG=\"$localenv->{KRB5_CONFIG}\" ";
431         $cmd_env .= "KRB5CCNAME=\"$localenv->{KRB5_CCACHE}\" ";
432         $cmd_env .= "RESOLV_CONF=\"$localenv->{RESOLV_CONF}\" ";
433         $cmd_env .= "GNUPGHOME=\"$localenv->{GNUPGHOME}\" ";
434
435         return $cmd_env;
436 }
437
438 # Sets up a forest trust namespace.
439 # (Note this is different to kernel namespaces, setup by the
440 # USE_NAMESPACES=1 option)
441 sub setup_namespaces
442 {
443         my ($self, $localenv, $upn_array, $spn_array) = @_;
444
445         @{$upn_array} = [] unless defined($upn_array);
446         my $upn_args = "";
447         foreach my $upn (@{$upn_array}) {
448                 $upn_args .= " --add-upn-suffix=$upn";
449         }
450
451         @{$spn_array} = [] unless defined($spn_array);
452         my $spn_args = "";
453         foreach my $spn (@{$spn_array}) {
454                 $spn_args .= " --add-spn-suffix=$spn";
455         }
456
457         my $samba_tool =  Samba::bindir_path($self, "samba-tool");
458
459         my $cmd_env = $self->get_cmd_env_vars($localenv);
460
461         my $cmd_config = " $localenv->{CONFIGURATION}";
462
463         my $namespaces = $cmd_env;
464         $namespaces .= " $samba_tool domain trust namespaces $upn_args $spn_args";
465         $namespaces .= $cmd_config;
466         unless (system($namespaces) == 0) {
467                 warn("Failed to add namespaces \n$namespaces");
468                 return -1;
469         }
470
471         return 0;
472 }
473
474 sub setup_trust($$$$$)
475 {
476         my ($self, $localenv, $remoteenv, $type, $extra_args) = @_;
477
478         $localenv->{TRUST_SERVER} = $remoteenv->{SERVER};
479         $localenv->{TRUST_SERVER_IP} = $remoteenv->{SERVER_IP};
480         $localenv->{TRUST_DNSNAME} = $remoteenv->{DNSNAME};
481
482         $localenv->{TRUST_USERNAME} = $remoteenv->{USERNAME};
483         $localenv->{TRUST_PASSWORD} = $remoteenv->{PASSWORD};
484         $localenv->{TRUST_DOMAIN} = $remoteenv->{DOMAIN};
485         $localenv->{TRUST_REALM} = $remoteenv->{REALM};
486         $localenv->{TRUST_DOMSID} = $remoteenv->{DOMSID};
487
488         # Add trusted domain realms to krb5.conf
489         Samba::append_krb5_conf_trust_realms($localenv);
490
491         my $samba_tool =  Samba::bindir_path($self, "samba-tool");
492
493         # setup the trust
494         my $cmd_env = $self->get_cmd_env_vars($localenv);
495
496         my $cmd_config = " $localenv->{CONFIGURATION}";
497         my $cmd_creds = $cmd_config;
498         $cmd_creds .= " -U$localenv->{TRUST_DOMAIN}\\\\$localenv->{TRUST_USERNAME}\%$localenv->{TRUST_PASSWORD}";
499
500         my $create = $cmd_env;
501         $create .= " $samba_tool domain trust create --type=${type} $localenv->{TRUST_REALM}";
502         $create .= " $extra_args";
503         $create .= $cmd_creds;
504         unless (system($create) == 0) {
505                 warn("Failed to create trust \n$create");
506                 return undef;
507         }
508
509         my $groupname = "g_$localenv->{TRUST_DOMAIN}";
510         my $groupadd = $cmd_env;
511         $groupadd .= " $samba_tool group add '$groupname' --group-scope=Domain $cmd_config";
512         unless (system($groupadd) == 0) {
513                 warn("Failed to create group \n$groupadd");
514                 return undef;
515         }
516         my $groupmem = $cmd_env;
517         $groupmem .= " $samba_tool group addmembers '$groupname' '$localenv->{TRUST_DOMSID}-513' $cmd_config";
518         unless (system($groupmem) == 0) {
519                 warn("Failed to add group member \n$groupmem");
520                 return undef;
521         }
522
523         return $localenv
524 }
525
526 sub provision_raw_prepare($$$$$$$$$$$$$$)
527 {
528         my ($self,
529             $prefix,
530             $server_role,
531             $hostname,
532             $domain,
533             $realm,
534             $samsid,
535             $functional_level,
536             $password,
537             $kdc_ipv4,
538             $kdc_ipv6,
539             $force_fips_mode,
540             $extra_provision_options) = @_;
541         my $ctx;
542         my $python_cmd = "";
543         if (defined $ENV{PYTHON}) {
544                 $python_cmd = $ENV{PYTHON} . " ";
545         }
546         $ctx->{python} = $python_cmd;
547         my $netbiosname = uc($hostname);
548
549         unless(-d $prefix or mkdir($prefix, 0777)) {
550                 warn("Unable to create $prefix");
551                 return undef;
552         }
553         my $prefix_abs = abs_path($prefix);
554
555         die ("prefix=''") if $prefix_abs eq "";
556         die ("prefix='/'") if $prefix_abs eq "/";
557
558         unless (system("rm -rf $prefix_abs/*") == 0) {
559                 warn("Unable to clean up");
560         }
561
562
563         my $swiface = Samba::get_interface($hostname);
564
565         $ctx->{prefix} = $prefix;
566         $ctx->{prefix_abs} = $prefix_abs;
567
568         $ctx->{server_role} = $server_role;
569         $ctx->{hostname} = $hostname;
570         $ctx->{netbiosname} = $netbiosname;
571         $ctx->{swiface} = $swiface;
572         $ctx->{password} = $password;
573         $ctx->{kdc_ipv4} = $kdc_ipv4;
574         $ctx->{kdc_ipv6} = $kdc_ipv6;
575         $ctx->{force_fips_mode} = $force_fips_mode;
576         $ctx->{krb5_ccname} = "$prefix_abs/krb5cc_%{uid}";
577         if ($functional_level eq "2000") {
578                 $ctx->{supported_enctypes} = "arcfour-hmac-md5 des-cbc-md5 des-cbc-crc";
579         }
580
581 #
582 # Set smbd log level here.
583 #
584         $ctx->{server_loglevel} =$ENV{SERVER_LOG_LEVEL} || 1;
585         $ctx->{username} = "Administrator";
586         $ctx->{domain} = $domain;
587         $ctx->{realm} = uc($realm);
588         $ctx->{dnsname} = lc($realm);
589         $ctx->{samsid} = $samsid;
590         $ctx->{domain_admin} = "Administrator";
591         $ctx->{domain_admin_password} = $password;
592         $ctx->{domain_user} = "alice";
593         $ctx->{domain_user_password} = "Secret007";
594
595         $ctx->{functional_level} = $functional_level;
596
597         my $unix_name = ($ENV{USER} or $ENV{LOGNAME} or `whoami`);
598         chomp $unix_name;
599         $ctx->{unix_name} = $unix_name;
600         $ctx->{unix_uid} = $>;
601         my @mygid = split(" ", $();
602         $ctx->{unix_gid} = $mygid[0];
603         $ctx->{unix_gids_str} = $);
604         @{$ctx->{unix_gids}} = split(" ", $ctx->{unix_gids_str});
605
606         $ctx->{etcdir} = "$prefix_abs/etc";
607         $ctx->{piddir} = "$prefix_abs/pid";
608         $ctx->{smb_conf} = "$ctx->{etcdir}/smb.conf";
609         $ctx->{krb5_conf} = "$ctx->{etcdir}/krb5.conf";
610         $ctx->{krb5_ccache} = "$prefix_abs/krb5_ccache";
611         $ctx->{mitkdc_conf} = "$ctx->{etcdir}/mitkdc.conf";
612         $ctx->{gnupghome} = "$prefix_abs/gnupg";
613         $ctx->{privatedir} = "$prefix_abs/private";
614         $ctx->{binddnsdir} = "$prefix_abs/bind-dns";
615         $ctx->{ncalrpcdir} = "$prefix_abs/ncalrpc";
616         $ctx->{lockdir} = "$prefix_abs/lockdir";
617         $ctx->{logdir} = "$prefix_abs/logs";
618         $ctx->{statedir} = "$prefix_abs/statedir";
619         $ctx->{cachedir} = "$prefix_abs/cachedir";
620         $ctx->{winbindd_socket_dir} = "$prefix_abs/wbsock";
621         $ctx->{nmbd_socket_dir} = "$prefix_abs/nmbsock";
622         $ctx->{ntp_signd_socket_dir} = "$prefix_abs/ntp_signd_socket";
623         $ctx->{nsswrap_passwd} = "$ctx->{etcdir}/passwd";
624         $ctx->{nsswrap_group} = "$ctx->{etcdir}/group";
625         $ctx->{nsswrap_hosts} = "$ENV{SELFTEST_PREFIX}/hosts";
626         $ctx->{nsswrap_hostname} = "$ctx->{hostname}.$ctx->{dnsname}";
627         if ($ENV{SAMBA_DNS_FAKING}) {
628                 $ctx->{dns_host_file} = "$ENV{SELFTEST_PREFIX}/dns_host_file";
629                 $ctx->{samba_dnsupdate} = "$ENV{SRCDIR_ABS}/source4/scripting/bin/samba_dnsupdate --configfile=$ctx->{smb_conf} --all-interfaces --use-file=$ctx->{dns_host_file}";
630                 $ctx->{samba_dnsupdate} = $python_cmd .  $ctx->{samba_dnsupdate};
631         } else {
632                 $ctx->{samba_dnsupdate} = "$ENV{SRCDIR_ABS}/source4/scripting/bin/samba_dnsupdate --configfile=$ctx->{smb_conf} --all-interfaces";
633                 $ctx->{samba_dnsupdate} = $python_cmd .  $ctx->{samba_dnsupdate};
634                 $ctx->{use_resolv_wrapper} = 1;
635         }
636
637         my $dns_hub = $self->get_dns_hub_env();
638         $ctx->{resolv_conf} = $dns_hub->{RESOLV_CONF};
639
640         $ctx->{tlsdir} = "$ctx->{privatedir}/tls";
641
642         $ctx->{ipv4} = Samba::get_ipv4_addr($hostname);
643         $ctx->{ipv6} = Samba::get_ipv6_addr($hostname);
644
645         push(@{$ctx->{directories}}, $ctx->{privatedir});
646         push(@{$ctx->{directories}}, $ctx->{binddnsdir});
647         push(@{$ctx->{directories}}, $ctx->{etcdir});
648         push(@{$ctx->{directories}}, $ctx->{piddir});
649         push(@{$ctx->{directories}}, $ctx->{lockdir});
650         push(@{$ctx->{directories}}, $ctx->{logdir});
651         push(@{$ctx->{directories}}, $ctx->{statedir});
652         push(@{$ctx->{directories}}, $ctx->{cachedir});
653
654         $ctx->{smb_conf_extra_options} = "";
655
656         my @provision_options = ();
657         push (@provision_options, "GNUPGHOME=\"$ctx->{gnupghome}\"");
658         push (@provision_options, "KRB5_CONFIG=\"$ctx->{krb5_conf}\"");
659         push (@provision_options, "KRB5CCNAME=\"$ctx->{krb5_ccache}\"");
660         push (@provision_options, "NSS_WRAPPER_PASSWD=\"$ctx->{nsswrap_passwd}\"");
661         push (@provision_options, "NSS_WRAPPER_GROUP=\"$ctx->{nsswrap_group}\"");
662         push (@provision_options, "NSS_WRAPPER_HOSTS=\"$ctx->{nsswrap_hosts}\"");
663         push (@provision_options, "NSS_WRAPPER_HOSTNAME=\"$ctx->{nsswrap_hostname}\"");
664         if (defined($ctx->{use_resolv_wrapper})) {
665                 push (@provision_options, "RESOLV_WRAPPER_CONF=\"$ctx->{resolv_conf}\"");
666                 push (@provision_options, "RESOLV_CONF=\"$ctx->{resolv_conf}\"");
667         } else {
668                 push (@provision_options, "RESOLV_WRAPPER_HOSTS=\"$ctx->{dns_host_file}\"");
669         }
670         if (defined($ctx->{force_fips_mode})) {
671                 push (@provision_options, "GNUTLS_FORCE_FIPS_MODE=1");
672                 push (@provision_options, "OPENSSL_FORCE_FIPS_MODE=1");
673         }
674
675         if (defined($ENV{GDB_PROVISION})) {
676                 push (@provision_options, "gdb --args");
677                 if (!defined($ENV{PYTHON})) {
678                     push (@provision_options, "env");
679                     push (@provision_options, "python");
680                 }
681         }
682         if (defined($ENV{VALGRIND_PROVISION})) {
683                 push (@provision_options, "valgrind");
684                 if (!defined($ENV{PYTHON})) {
685                     push (@provision_options, "env");
686                     push (@provision_options, "python");
687                 }
688         }
689
690         my $samba_tool =  Samba::bindir_path($self, "samba-tool");
691
692         push (@provision_options, $samba_tool);
693         push (@provision_options, "domain");
694         push (@provision_options, "provision");
695         push (@provision_options, "--configfile=$ctx->{smb_conf}");
696         push (@provision_options, "--host-name=$ctx->{hostname}");
697         push (@provision_options, "--host-ip=$ctx->{ipv4}");
698         push (@provision_options, "--quiet");
699         push (@provision_options, "--domain=$ctx->{domain}");
700         push (@provision_options, "--realm=$ctx->{realm}");
701         if (defined($ctx->{samsid})) {
702                 push (@provision_options, "--domain-sid=$ctx->{samsid}");
703         }
704         push (@provision_options, "--adminpass=$ctx->{password}");
705         push (@provision_options, "--krbtgtpass=krbtgt$ctx->{password}");
706         push (@provision_options, "--machinepass=machine$ctx->{password}");
707         push (@provision_options, "--root=$ctx->{unix_name}");
708         push (@provision_options, "--server-role=\"$ctx->{server_role}\"");
709         push (@provision_options, "--function-level=\"$ctx->{functional_level}\"");
710
711         @{$ctx->{provision_options}} = @provision_options;
712
713         if (defined($extra_provision_options)) {
714                 push (@{$ctx->{provision_options}}, @{$extra_provision_options});
715         }
716
717         return $ctx;
718 }
719
720 sub has_option
721 {
722         my ($self, $keyword, @options_list) = @_;
723
724         # convert the options-list to a hash-map for easy keyword lookup
725         my %options_dict = map { $_ => 1 } @options_list;
726
727         return exists $options_dict{$keyword};
728 }
729
730 #
731 # Step1 creates the basic configuration
732 #
733 sub provision_raw_step1($$)
734 {
735         my ($self, $ctx) = @_;
736
737         mkdir($_, 0777) foreach (@{$ctx->{directories}});
738
739         ##
740         ## lockdir and piddir must be 0755
741         ##
742         chmod 0755, $ctx->{lockdir};
743         chmod 0755, $ctx->{piddir};
744
745         unless (open(CONFFILE, ">$ctx->{smb_conf}")) {
746                 warn("can't open $ctx->{smb_conf}$?");
747                 return undef;
748         }
749
750         Samba::copy_gnupg_home($ctx);
751         Samba::prepare_keyblobs($ctx);
752         my $crlfile = "$ctx->{tlsdir}/crl.pem";
753         $crlfile = "" unless -e ${crlfile};
754
755         # work out which file server to use. Default to source3 smbd (s3fs),
756         # unless the source4 NTVFS (smb) file server has been specified
757         my $services = "-smb +s3fs";
758         if ($self->has_option("--use-ntvfs", @{$ctx->{provision_options}})) {
759                 $services = "+smb -s3fs";
760         }
761
762         my $interfaces = Samba::get_interfaces_config($ctx->{netbiosname});
763
764         print CONFFILE "
765 [global]
766         netbios name = $ctx->{netbiosname}
767         posix:eadb = $ctx->{statedir}/eadb.tdb
768         workgroup = $ctx->{domain}
769         realm = $ctx->{realm}
770         private dir = $ctx->{privatedir}
771         binddns dir = $ctx->{binddnsdir}
772         pid directory = $ctx->{piddir}
773         ncalrpc dir = $ctx->{ncalrpcdir}
774         lock dir = $ctx->{lockdir}
775         state directory = $ctx->{statedir}
776         cache directory = $ctx->{cachedir}
777         winbindd socket directory = $ctx->{winbindd_socket_dir}
778         nmbd:socket dir = $ctx->{nmbd_socket_dir}
779         ntp signd socket directory = $ctx->{ntp_signd_socket_dir}
780         winbind separator = /
781         interfaces = $interfaces
782         tls dh params file = $ctx->{tlsdir}/dhparms.pem
783         tls crlfile = ${crlfile}
784         tls verify peer = no_check
785         panic action = $RealBin/gdb_backtrace \%d
786         smbd:suicide mode = yes
787         smbd:FSCTL_SMBTORTURE = yes
788         smbd:validate_oplock_types = yes
789         wins support = yes
790         server role = $ctx->{server_role}
791         server services = +echo $services
792         dcerpc endpoint servers = +winreg +srvsvc +rpcecho
793         notify:inotify = false
794         ldb:nosync = true
795         ldap server require strong auth = yes
796         log file = $ctx->{logdir}/log.\%m
797         log level = $ctx->{server_loglevel}
798         lanman auth = Yes
799         ntlm auth = Yes
800         client min protocol = SMB2_02
801         server min protocol = SMB2_02
802         mangled names = yes
803         dns update command = $ctx->{samba_dnsupdate}
804         spn update command = $ctx->{python} $ENV{SRCDIR_ABS}/source4/scripting/bin/samba_spnupdate --configfile $ctx->{smb_conf}
805         gpo update command = $ctx->{python} $ENV{SRCDIR_ABS}/source4/scripting/bin/samba-gpupdate --configfile $ctx->{smb_conf} --target=Computer
806         samba kcc command = $ctx->{python} $ENV{SRCDIR_ABS}/source4/scripting/bin/samba_kcc
807         dreplsrv:periodic_startup_interval = 0
808         dsdb:schema update allowed = yes
809
810         vfs objects = dfs_samba4 acl_xattr fake_acls xattr_tdb streams_depot
811
812         idmap_ldb:use rfc2307=yes
813         winbind enum users = yes
814         winbind enum groups = yes
815
816         rpc server port:netlogon = 1026
817         include system krb5 conf = no
818
819         debug syslog format = always
820         debug hires timestamp = yes
821
822 ";
823
824         print CONFFILE "
825
826         # Begin extra options
827         $ctx->{smb_conf_extra_options}
828         # End extra options
829 ";
830         close(CONFFILE);
831
832         #Default the KDC IP to the server's IP
833         if (not defined($ctx->{kdc_ipv4})) {
834                 $ctx->{kdc_ipv4} = $ctx->{ipv4};
835         }
836         if (not defined($ctx->{kdc_ipv6})) {
837                 $ctx->{kdc_ipv6} = $ctx->{ipv6};
838         }
839
840         Samba::mk_krb5_conf($ctx);
841         Samba::mk_mitkdc_conf($ctx, abs_path(Samba::bindir_path($self, "shared")));
842
843         open(PWD, ">$ctx->{nsswrap_passwd}");
844         if ($ctx->{unix_uid} != 0) {
845                 print PWD "root:x:0:0:root gecos:$ctx->{prefix_abs}:/bin/false\n";
846         }
847         print PWD "$ctx->{unix_name}:x:$ctx->{unix_uid}:65531:$ctx->{unix_name} gecos:$ctx->{prefix_abs}:/bin/false\n";
848         print PWD "nobody:x:65534:65533:nobody gecos:$ctx->{prefix_abs}:/bin/false
849 pdbtest:x:65533:65533:pdbtest gecos:$ctx->{prefix_abs}:/bin/false
850 pdbtest2:x:65532:65533:pdbtest gecos:$ctx->{prefix_abs}:/bin/false
851 pdbtest3:x:65531:65533:pdbtest gecos:$ctx->{prefix_abs}:/bin/false
852 pdbtest4:x:65530:65533:pdbtest gecos:$ctx->{prefix_abs}:/bin/false
853 ";
854         close(PWD);
855         my $uid_rfc2307test = 65533;
856
857         open(GRP, ">$ctx->{nsswrap_group}");
858         if ($ctx->{unix_gid} != 0) {
859                 print GRP "root:x:0:\n";
860         }
861         print GRP "$ctx->{unix_name}:x:$ctx->{unix_gid}:\n";
862         print GRP "wheel:x:10:
863 users:x:65531:
864 nobody:x:65533:
865 nogroup:x:65534:nobody
866 ";
867         close(GRP);
868         my $gid_rfc2307test = 65532;
869
870         my $hostname = lc($ctx->{hostname});
871         open(HOSTS, ">>$ctx->{nsswrap_hosts}");
872         if ($hostname eq "localdc") {
873                 print HOSTS "$ctx->{ipv4} ${hostname}.$ctx->{dnsname} $ctx->{dnsname} ${hostname}\n";
874                 print HOSTS "$ctx->{ipv6} ${hostname}.$ctx->{dnsname} $ctx->{dnsname} ${hostname}\n";
875         } else {
876                 print HOSTS "$ctx->{ipv4} ${hostname}.$ctx->{dnsname} ${hostname}\n";
877                 print HOSTS "$ctx->{ipv6} ${hostname}.$ctx->{dnsname} ${hostname}\n";
878         }
879         close(HOSTS);
880
881         my $configuration = "--configfile=$ctx->{smb_conf}";
882
883 #Ensure the config file is valid before we start
884         my $testparm = Samba::bindir_path($self, "samba-tool") . " testparm";
885         if (system("$testparm $configuration -v --suppress-prompt >/dev/null 2>&1") != 0) {
886                 system("$testparm -v --suppress-prompt $configuration >&2");
887                 warn("Failed to create a valid smb.conf configuration $testparm!");
888                 return undef;
889         }
890         unless (system("($testparm $configuration -v --suppress-prompt --parameter-name=\"netbios name\" --section-name=global 2> /dev/null | grep -i \"^$ctx->{netbiosname}\" ) >/dev/null 2>&1") == 0) {
891                 warn("Failed to create a valid smb.conf configuration! $testparm $configuration -v --suppress-prompt --parameter-name=\"netbios name\" --section-name=global");
892                 return undef;
893         }
894
895         # Return the environment variables for the new testenv DC.
896         # Note that we have SERVER_X and DC_SERVER_X variables (which have the same
897         # value initially). In a 2 DC setup, $DC_SERVER_X will always be the PDC.
898         my $ret = {
899                 GNUPGHOME => $ctx->{gnupghome},
900                 KRB5_CONFIG => $ctx->{krb5_conf},
901                 KRB5_CCACHE => $ctx->{krb5_ccache},
902                 MITKDC_CONFIG => $ctx->{mitkdc_conf},
903                 PIDDIR => $ctx->{piddir},
904                 SERVER => $ctx->{hostname},
905                 DC_SERVER => $ctx->{hostname},
906                 SERVER_IP => $ctx->{ipv4},
907                 DC_SERVER_IP => $ctx->{ipv4},
908                 SERVER_IPV6 => $ctx->{ipv6},
909                 DC_SERVER_IPV6 => $ctx->{ipv6},
910                 NETBIOSNAME => $ctx->{netbiosname},
911                 DC_NETBIOSNAME => $ctx->{netbiosname},
912                 DOMAIN => $ctx->{domain},
913                 USERNAME => $ctx->{username},
914                 DC_USERNAME => $ctx->{username},
915                 DOMAIN_ADMIN => $ctx->{domain_admin},
916                 DOMAIN_ADMIN_PASSWORD => $ctx->{domain_admin_password},
917                 DOMAIN_USER => $ctx->{domain_user},
918                 DOMAIN_USER_PASSWORD => $ctx->{domain_user_password},
919                 REALM => $ctx->{realm},
920                 DNSNAME => $ctx->{dnsname},
921                 SAMSID => $ctx->{samsid},
922                 PASSWORD => $ctx->{password},
923                 DC_PASSWORD => $ctx->{password},
924                 LDAPDIR => $ctx->{ldapdir},
925                 LDAP_INSTANCE => $ctx->{ldap_instance},
926                 SELFTEST_WINBINDD_SOCKET_DIR => $ctx->{winbindd_socket_dir},
927                 NCALRPCDIR => $ctx->{ncalrpcdir},
928                 LOCKDIR => $ctx->{lockdir},
929                 STATEDIR => $ctx->{statedir},
930                 CACHEDIR => $ctx->{cachedir},
931                 PRIVATEDIR => $ctx->{privatedir},
932                 BINDDNSDIR => $ctx->{binddnsdir},
933                 SERVERCONFFILE => $ctx->{smb_conf},
934                 TESTENV_DIR => $ctx->{prefix_abs},
935                 CONFIGURATION => $configuration,
936                 SOCKET_WRAPPER_DEFAULT_IFACE => $ctx->{swiface},
937                 NSS_WRAPPER_PASSWD => $ctx->{nsswrap_passwd},
938                 NSS_WRAPPER_GROUP => $ctx->{nsswrap_group},
939                 NSS_WRAPPER_HOSTS => $ctx->{nsswrap_hosts},
940                 NSS_WRAPPER_HOSTNAME => $ctx->{nsswrap_hostname},
941                 SAMBA_TEST_FIFO => "$ctx->{prefix}/samba_test.fifo",
942                 SAMBA_TEST_LOG => "$ctx->{prefix}/samba_test.log",
943                 SAMBA_TEST_LOG_POS => 0,
944                 NSS_WRAPPER_MODULE_SO_PATH => Samba::nss_wrapper_winbind_so_path($self),
945                 NSS_WRAPPER_MODULE_FN_PREFIX => "winbind",
946                 LOCAL_PATH => $ctx->{share},
947                 UID_RFC2307TEST => $uid_rfc2307test,
948                 GID_RFC2307TEST => $gid_rfc2307test,
949                 SERVER_ROLE => $ctx->{server_role},
950                 RESOLV_CONF => $ctx->{resolv_conf},
951                 KRB5_CRL_FILE => $crlfile,
952         };
953
954         if (defined($ctx->{use_resolv_wrapper})) {
955                 $ret->{RESOLV_WRAPPER_CONF} = $ctx->{resolv_conf};
956         } else {
957                 $ret->{RESOLV_WRAPPER_HOSTS} = $ctx->{dns_host_file};
958         }
959         if (defined($ctx->{force_fips_mode})) {
960                 $ret->{GNUTLS_FORCE_FIPS_MODE} = "1",
961                 $ret->{OPENSSL_FORCE_FIPS_MODE} = "1",
962         }
963
964         if ($ctx->{server_role} eq "domain controller") {
965                 $ret->{DOMSID} = $ret->{SAMSID};
966         }
967
968         return $ret;
969 }
970
971 #
972 # Step2 runs the provision script
973 #
974 sub provision_raw_step2($$$)
975 {
976         my ($self, $ctx, $ret) = @_;
977
978         my $ldif;
979
980         my $provision_cmd = join(" ", @{$ctx->{provision_options}});
981         unless (system($provision_cmd) == 0) {
982                 warn("Unable to provision: \n$provision_cmd\n");
983                 return undef;
984         }
985
986         my $cmd_env = $self->get_cmd_env_vars($ret);
987
988         my $testallowed_account = "testallowed";
989         my $samba_tool_cmd = ${cmd_env};
990         $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
991             . " user create --configfile=$ctx->{smb_conf} $testallowed_account $ctx->{password}";
992         unless (system($samba_tool_cmd) == 0) {
993                 warn("Unable to add testallowed user: \n$samba_tool_cmd\n");
994                 return undef;
995         }
996
997         my $srv_account = "srv_account";
998         $samba_tool_cmd = ${cmd_env};
999         $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
1000             . " user create --configfile=$ctx->{smb_conf} $srv_account $ctx->{password}";
1001         unless (system($samba_tool_cmd) == 0) {
1002                 warn("Unable to add $srv_account user: \n$samba_tool_cmd\n");
1003                 return undef;
1004         }
1005
1006         $samba_tool_cmd = ${cmd_env};
1007         $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
1008             . " spn add HOST/$srv_account --configfile=$ctx->{smb_conf} $srv_account";
1009         unless (system($samba_tool_cmd) == 0) {
1010                 warn("Unable to add spn for $srv_account: \n$samba_tool_cmd\n");
1011                 return undef;
1012         }
1013
1014         my $ldbmodify = ${cmd_env};
1015         $ldbmodify .= Samba::bindir_path($self, "ldbmodify");
1016         $ldbmodify .=  " --configfile=$ctx->{smb_conf}";
1017         my $base_dn = "DC=".join(",DC=", split(/\./, $ctx->{realm}));
1018
1019         if ($ctx->{server_role} ne "domain controller") {
1020                 $base_dn = "DC=$ctx->{netbiosname}";
1021         }
1022
1023         my $user_dn = "cn=$testallowed_account,cn=users,$base_dn";
1024         $testallowed_account = "testallowed account";
1025         open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb")
1026             or die "Failed to run $ldbmodify: $!";
1027         print $ldif "dn: $user_dn
1028 changetype: modify
1029 replace: samAccountName
1030 samAccountName: $testallowed_account
1031 -
1032 ";
1033         close($ldif);
1034         unless ($? == 0) {
1035             warn("$ldbmodify failed: $?");
1036             return undef;
1037         }
1038
1039         open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb")
1040             or die "Failed to run $ldbmodify: $!";
1041         print $ldif "dn: $user_dn
1042 changetype: modify
1043 replace: userPrincipalName
1044 userPrincipalName: testallowed upn\@$ctx->{realm}
1045 replace: servicePrincipalName
1046 servicePrincipalName: host/testallowed
1047 -
1048 ";
1049         close($ldif);
1050         unless ($? == 0) {
1051             warn("$ldbmodify failed: $?");
1052             return undef;
1053         }
1054
1055         $samba_tool_cmd = ${cmd_env};
1056         $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
1057             . " user create --configfile=$ctx->{smb_conf} testdenied $ctx->{password}";
1058         unless (system($samba_tool_cmd) == 0) {
1059                 warn("Unable to add testdenied user: \n$samba_tool_cmd\n");
1060                 return undef;
1061         }
1062
1063         $user_dn = "cn=testdenied,cn=users,$base_dn";
1064         open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb")
1065             or die "Failed to run $ldbmodify: $!";
1066         print $ldif "dn: $user_dn
1067 changetype: modify
1068 replace: userPrincipalName
1069 userPrincipalName: testdenied_upn\@$ctx->{realm}.upn
1070 -
1071 ";
1072         close($ldif);
1073         unless ($? == 0) {
1074             warn("$ldbmodify failed: $?");
1075             return undef;
1076         }
1077
1078         $samba_tool_cmd = ${cmd_env};
1079         $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
1080             . " user create --configfile=$ctx->{smb_conf} testupnspn $ctx->{password}";
1081         unless (system($samba_tool_cmd) == 0) {
1082                 warn("Unable to add testupnspn user: \n$samba_tool_cmd\n");
1083                 return undef;
1084         }
1085
1086         $user_dn = "cn=testupnspn,cn=users,$base_dn";
1087         open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb")
1088             or die "Failed to run $ldbmodify: $!";
1089         print $ldif "dn: $user_dn
1090 changetype: modify
1091 replace: userPrincipalName
1092 userPrincipalName: http/testupnspn.$ctx->{dnsname}\@$ctx->{realm}
1093 replace: servicePrincipalName
1094 servicePrincipalName: http/testupnspn.$ctx->{dnsname}
1095 -
1096 ";
1097         close($ldif);
1098         unless ($? == 0) {
1099             warn("$ldbmodify failed: $?");
1100             return undef;
1101         }
1102
1103         $samba_tool_cmd = ${cmd_env};
1104         $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
1105             . " group addmembers --configfile=$ctx->{smb_conf} 'Allowed RODC Password Replication Group' '$testallowed_account'";
1106         unless (system($samba_tool_cmd) == 0) {
1107                 warn("Unable to add '$testallowed_account' user to 'Allowed RODC Password Replication Group': \n$samba_tool_cmd\n");
1108                 return undef;
1109         }
1110
1111         # Create two users alice and bob!
1112         my $user_account_array = ["alice", "bob", "jane", "joe"];
1113
1114         foreach my $user_account (@{$user_account_array}) {
1115                 my $samba_tool_cmd = ${cmd_env};
1116
1117                 $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
1118                     . " user create --configfile=$ctx->{smb_conf} $user_account Secret007";
1119                 unless (system($samba_tool_cmd) == 0) {
1120                         warn("Unable to create user: $user_account\n$samba_tool_cmd\n");
1121                         return undef;
1122                 }
1123         }
1124
1125         my $group_array = ["Samba Users"];
1126
1127         foreach my $group (@{$group_array}) {
1128                 my $samba_tool_cmd = ${cmd_env};
1129
1130                 $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
1131                     . " group add --configfile=$ctx->{smb_conf} \"$group\"";
1132                 unless (system($samba_tool_cmd) == 0) {
1133                         warn("Unable to create group: $group\n$samba_tool_cmd\n");
1134                         return undef;
1135                 }
1136         }
1137
1138         # Add user joe to group "Samba Users"
1139         my $group = "Samba Users";
1140         my $user_account = "joe";
1141
1142         $samba_tool_cmd = ${cmd_env};
1143         $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
1144             . " group addmembers --configfile=$ctx->{smb_conf} \"$group\" $user_account";
1145         unless (system($samba_tool_cmd) == 0) {
1146                 warn("Unable to add " . $user_account . "to group group : $group\n$samba_tool_cmd\n");
1147                 return undef;
1148         }
1149
1150         $group = "Samba Users";
1151         $user_account = "joe";
1152
1153         $samba_tool_cmd = ${cmd_env};
1154         $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool")
1155             . " user setprimarygroup --configfile=$ctx->{smb_conf} $user_account \"$group\"";
1156         unless (system($samba_tool_cmd) == 0) {
1157                 warn("Unable to set primary group of user: $user_account\n$samba_tool_cmd\n");
1158                 return undef;
1159         }
1160
1161         # Change the userPrincipalName for jane
1162         $user_dn = "cn=jane,cn=users,$base_dn";
1163
1164         open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb")
1165             or die "Failed to run $ldbmodify: $!";
1166         print $ldif "dn: $user_dn
1167 changetype: modify
1168 replace: userPrincipalName
1169 userPrincipalName: jane.doe\@$ctx->{realm}
1170 -
1171 ";
1172         close($ldif);
1173         unless ($? == 0) {
1174             warn("$ldbmodify failed: $?");
1175             return undef;
1176         }
1177
1178         return $ret;
1179 }
1180
1181 sub provision($$$$$$$$$$$)
1182 {
1183         my ($self,
1184             $prefix,
1185             $server_role,
1186             $hostname,
1187             $domain,
1188             $realm,
1189             $functional_level,
1190             $password,
1191             $kdc_ipv4,
1192             $kdc_ipv6,
1193             $force_fips_mode,
1194             $extra_smbconf_options,
1195             $extra_smbconf_shares,
1196             $extra_provision_options) = @_;
1197
1198         my $samsid = Samba::random_domain_sid();
1199
1200         my $ctx = $self->provision_raw_prepare($prefix, $server_role,
1201                                                $hostname,
1202                                                $domain, $realm,
1203                                                $samsid,
1204                                                $functional_level,
1205                                                $password,
1206                                                $kdc_ipv4,
1207                                                $kdc_ipv6,
1208                                                $force_fips_mode,
1209                                                $extra_provision_options);
1210
1211         $ctx->{share} = "$ctx->{prefix_abs}/share";
1212         push(@{$ctx->{directories}}, "$ctx->{share}");
1213         push(@{$ctx->{directories}}, "$ctx->{share}/test1");
1214         push(@{$ctx->{directories}}, "$ctx->{share}/test2");
1215
1216         # precreate directories for printer drivers
1217         push(@{$ctx->{directories}}, "$ctx->{share}/W32X86");
1218         push(@{$ctx->{directories}}, "$ctx->{share}/x64");
1219         push(@{$ctx->{directories}}, "$ctx->{share}/WIN40");
1220
1221         my $msdfs = "no";
1222         $msdfs = "yes" if ($server_role eq "domain controller");
1223         $ctx->{smb_conf_extra_options} = "
1224
1225         max xmit = 32K
1226         server max protocol = SMB2
1227         host msdfs = $msdfs
1228         lanman auth = yes
1229
1230         # fruit:copyfile is a global option
1231         fruit:copyfile = yes
1232
1233         $extra_smbconf_options
1234
1235 [tmp]
1236         path = $ctx->{share}
1237         read only = no
1238         posix:sharedelay = 100000
1239         posix:oplocktimeout = 3
1240         posix:writetimeupdatedelay = 500000
1241
1242 [xcopy_share]
1243         path = $ctx->{share}
1244         read only = no
1245         posix:sharedelay = 100000
1246         posix:oplocktimeout = 3
1247         posix:writetimeupdatedelay = 500000
1248         create mask = 777
1249         force create mode = 777
1250
1251 [posix_share]
1252         path = $ctx->{share}
1253         read only = no
1254         create mask = 0777
1255         force create mode = 0
1256         directory mask = 0777
1257         force directory mode = 0
1258
1259 [test1]
1260         path = $ctx->{share}/test1
1261         read only = no
1262         posix:sharedelay = 100000
1263         posix:oplocktimeout = 3
1264         posix:writetimeupdatedelay = 500000
1265
1266 [test2]
1267         path = $ctx->{share}/test2
1268         read only = no
1269         posix:sharedelay = 100000
1270         posix:oplocktimeout = 3
1271         posix:writetimeupdatedelay = 500000
1272
1273 [cifs]
1274         path = $ctx->{share}/_ignore_cifs_
1275         read only = no
1276         ntvfs handler = cifs
1277         cifs:server = $ctx->{netbiosname}
1278         cifs:share = tmp
1279         cifs:use-s4u2proxy = yes
1280         # There is no username specified here, instead the client is expected
1281         # to log in with kerberos, and the serverwill use delegated credentials.
1282         # Or the server tries s4u2self/s4u2proxy to impersonate the client
1283
1284 [simple]
1285         path = $ctx->{share}
1286         read only = no
1287         ntvfs handler = simple
1288
1289 [sysvol]
1290         path = $ctx->{statedir}/sysvol
1291         read only = no
1292
1293 [netlogon]
1294         path = $ctx->{statedir}/sysvol/$ctx->{dnsname}/scripts
1295         read only = no
1296
1297 [cifsposix]
1298         copy = simple
1299         ntvfs handler = cifsposix
1300
1301 [vfs_fruit]
1302         path = $ctx->{share}
1303         vfs objects = catia fruit streams_xattr acl_xattr
1304         ea support = yes
1305         fruit:resource = file
1306         fruit:metadata = netatalk
1307         fruit:locking = netatalk
1308         fruit:encoding = native
1309
1310 [xattr]
1311         path = $ctx->{share}
1312         # This can be used for testing real fs xattr stuff
1313         vfs objects = streams_xattr acl_xattr
1314
1315 $extra_smbconf_shares
1316 ";
1317
1318         my $ret = $self->provision_raw_step1($ctx);
1319         unless (defined $ret) {
1320                 return undef;
1321         }
1322
1323         return $self->provision_raw_step2($ctx, $ret);
1324 }
1325
1326 # For multi-DC testenvs, we want $DC_SERVER to always be the PDC (i.e. the
1327 # original DC) in the testenv. $SERVER is always the joined DC that we are
1328 # actually running the test against
1329 sub set_pdc_env_vars
1330 {
1331         my ($self, $env, $dcvars) = @_;
1332
1333         $env->{DC_SERVER} = $dcvars->{DC_SERVER};
1334         $env->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP};
1335         $env->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6};
1336         $env->{DC_SERVERCONFFILE} = $dcvars->{SERVERCONFFILE};
1337         $env->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME};
1338         $env->{DC_USERNAME} = $dcvars->{DC_USERNAME};
1339         $env->{DC_PASSWORD} = $dcvars->{DC_PASSWORD};
1340 }
1341
1342 sub provision_s4member($$$$$)
1343 {
1344         my ($self, $prefix, $dcvars, $hostname, $more_conf) = @_;
1345         print "PROVISIONING MEMBER...\n";
1346         my $extra_smb_conf = "
1347         passdb backend = samba_dsdb
1348 winbindd:use external pipes = true
1349
1350 # the source4 smb server doesn't allow signing by default
1351 server signing = enabled
1352 raw NTLMv2 auth = yes
1353
1354 # override the new SMB2 only default
1355 client min protocol = CORE
1356 server min protocol = LANMAN1
1357 ";
1358         if ($more_conf) {
1359                 $extra_smb_conf = $extra_smb_conf . $more_conf . "\n";
1360         }
1361         my $extra_provision_options = ["--use-ntvfs"];
1362         my $ret = $self->provision($prefix,
1363                                    "member server",
1364                                    $hostname,
1365                                    $dcvars->{DOMAIN},
1366                                    $dcvars->{REALM},
1367                                    "2008",
1368                                    "locMEMpass3",
1369                                    $dcvars->{SERVER_IP},
1370                                    $dcvars->{SERVER_IPV6},
1371                                    undef,
1372                                    $extra_smb_conf, "",
1373                                    $extra_provision_options);
1374         unless ($ret) {
1375                 return undef;
1376         }
1377
1378         my $samba_tool =  Samba::bindir_path($self, "samba-tool");
1379         my $cmd = $self->get_cmd_env_vars($ret);
1380         $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} --experimental-s4-member member";
1381         $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
1382         $cmd .= " --machinepass=machine$ret->{PASSWORD}";
1383
1384         unless (system($cmd) == 0) {
1385                 warn("Join failed\n$cmd");
1386                 return undef;
1387         }
1388
1389         $ret->{DOMSID} = $dcvars->{DOMSID};
1390         $self->set_pdc_env_vars($ret, $dcvars);
1391
1392         return $ret;
1393 }
1394
1395 sub provision_rpc_proxy($$$)
1396 {
1397         my ($self, $prefix, $dcvars) = @_;
1398         print "PROVISIONING RPC PROXY...\n";
1399
1400         my $extra_smbconf_options = "
1401         passdb backend = samba_dsdb
1402
1403         # rpc_proxy
1404         dcerpc_remote:binding = ncacn_ip_tcp:$dcvars->{SERVER}
1405         dcerpc endpoint servers = epmapper, remote
1406         dcerpc_remote:interfaces = rpcecho
1407         dcerpc_remote:allow_anonymous_fallback = yes
1408         # override the new SMB2 only default
1409         client min protocol = CORE
1410         server min protocol = LANMAN1
1411 [cifs_to_dc]
1412         path = /tmp/_ignore_cifs_to_dc_/_none_
1413         read only = no
1414         ntvfs handler = cifs
1415         cifs:server = $dcvars->{SERVER}
1416         cifs:share = cifs
1417         cifs:use-s4u2proxy = yes
1418         # There is no username specified here, instead the client is expected
1419         # to log in with kerberos, and the serverwill use delegated credentials.
1420         # Or the server tries s4u2self/s4u2proxy to impersonate the client
1421
1422 ";
1423
1424         my $extra_provision_options = ["--use-ntvfs"];
1425         my $ret = $self->provision($prefix,
1426                                    "member server",
1427                                    "localrpcproxy",
1428                                    $dcvars->{DOMAIN},
1429                                    $dcvars->{REALM},
1430                                    "2008",
1431                                    "locRPCproxypass4",
1432                                    $dcvars->{SERVER_IP},
1433                                    $dcvars->{SERVER_IPV6},
1434                                    undef,
1435                                    $extra_smbconf_options, "",
1436                                    $extra_provision_options);
1437         unless ($ret) {
1438                 return undef;
1439         }
1440
1441         my $samba_tool =  Samba::bindir_path($self, "samba-tool");
1442
1443         # The joind runs in the context of the rpc_proxy/member for now
1444         my $cmd = $self->get_cmd_env_vars($ret);
1445         $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} --experimental-s4-member member";
1446         $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
1447         $cmd .= " --machinepass=machine$ret->{PASSWORD}";
1448
1449         unless (system($cmd) == 0) {
1450                 warn("Join failed\n$cmd");
1451                 return undef;
1452         }
1453
1454         # Prepare a context of the DC, but using the local CCACHE.
1455         my $overwrite = undef;
1456         $overwrite->{KRB5_CCACHE} = $ret->{KRB5_CCACHE};
1457         my $dc_cmd_env = $self->get_cmd_env_vars($dcvars, $overwrite);
1458
1459         # Setting up delegation runs in the context of the DC for now
1460         $cmd = $dc_cmd_env;
1461         $cmd .= "$samba_tool delegation for-any-protocol '$ret->{NETBIOSNAME}\$' on";
1462         $cmd .= " $dcvars->{CONFIGURATION}";
1463         print $cmd;
1464
1465         unless (system($cmd) == 0) {
1466                 warn("Delegation failed\n$cmd");
1467                 return undef;
1468         }
1469
1470         # Setting up delegation runs in the context of the DC for now
1471         $cmd = $dc_cmd_env;
1472         $cmd .= "$samba_tool delegation add-service '$ret->{NETBIOSNAME}\$' cifs/$dcvars->{SERVER}";
1473         $cmd .= " $dcvars->{CONFIGURATION}";
1474
1475         unless (system($cmd) == 0) {
1476                 warn("Delegation failed\n$cmd");
1477                 return undef;
1478         }
1479
1480         $ret->{DOMSID} = $dcvars->{DOMSID};
1481         $self->set_pdc_env_vars($ret, $dcvars);
1482
1483         return $ret;
1484 }
1485
1486 sub provision_promoted_dc($$$)
1487 {
1488         my ($self, $prefix, $dcvars) = @_;
1489         print "PROVISIONING PROMOTED DC...\n";
1490
1491         # We do this so that we don't run the provision.  That's the job of 'samba-tool domain dcpromo'.
1492         my $ctx = $self->provision_raw_prepare($prefix, "domain controller",
1493                                                "promotedvdc",
1494                                                $dcvars->{DOMAIN},
1495                                                $dcvars->{REALM},
1496                                                $dcvars->{SAMSID},
1497                                                "2008",
1498                                                $dcvars->{PASSWORD},
1499                                                $dcvars->{SERVER_IP},
1500                                                $dcvars->{SERVER_IPV6});
1501
1502         $ctx->{smb_conf_extra_options} = "
1503         max xmit = 32K
1504         server max protocol = SMB2
1505
1506         ntlm auth = ntlmv2-only
1507
1508         kdc force enable rc4 weak session keys = yes
1509
1510 [sysvol]
1511         path = $ctx->{statedir}/sysvol
1512         read only = yes
1513
1514 [netlogon]
1515         path = $ctx->{statedir}/sysvol/$ctx->{dnsname}/scripts
1516         read only = no
1517
1518 ";
1519
1520         my $ret = $self->provision_raw_step1($ctx);
1521         unless ($ret) {
1522                 return undef;
1523         }
1524
1525         my $samba_tool =  Samba::bindir_path($self, "samba-tool");
1526         my $cmd = $self->get_cmd_env_vars($ret);
1527         $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} --experimental-s4-member MEMBER --realm=$dcvars->{REALM}";
1528         $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
1529         $cmd .= " --machinepass=machine$ret->{PASSWORD}";
1530
1531         unless (system($cmd) == 0) {
1532                 warn("Join failed\n$cmd");
1533                 return undef;
1534         }
1535
1536         $samba_tool =  Samba::bindir_path($self, "samba-tool");
1537         $cmd = $self->get_cmd_env_vars($ret);
1538         $cmd .= "$samba_tool domain dcpromo $ret->{CONFIGURATION} $dcvars->{REALM} DC --realm=$dcvars->{REALM}";
1539         $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
1540         $cmd .= " --machinepass=machine$ret->{PASSWORD} --dns-backend=BIND9_DLZ";
1541
1542         unless (system($cmd) == 0) {
1543                 warn("Join failed\n$cmd");
1544                 return undef;
1545         }
1546
1547         $self->set_pdc_env_vars($ret, $dcvars);
1548
1549         return $ret;
1550 }
1551
1552 sub provision_vampire_dc($$$)
1553 {
1554         my ($self, $prefix, $dcvars, $fl) = @_;
1555         print "PROVISIONING VAMPIRE DC @ FL $fl...\n";
1556         my $name = "localvampiredc";
1557         my $extra_conf = "";
1558
1559         if ($fl == "2000") {
1560                 $name = "vampire2000dc";
1561         } else {
1562                 $extra_conf = "drs: immediate link sync = yes
1563                        drs: max link sync = 250";
1564         }
1565
1566         # We do this so that we don't run the provision.  That's the job of 'net vampire'.
1567         my $ctx = $self->provision_raw_prepare($prefix, "domain controller",
1568                                                $name,
1569                                                $dcvars->{DOMAIN},
1570                                                $dcvars->{REALM},
1571                                                $dcvars->{DOMSID},
1572                                                $fl,
1573                                                $dcvars->{PASSWORD},
1574                                                $dcvars->{SERVER_IP},
1575                                                $dcvars->{SERVER_IPV6});
1576
1577         $ctx->{smb_conf_extra_options} = "
1578         max xmit = 32K
1579         server max protocol = SMB2
1580
1581         ntlm auth = mschapv2-and-ntlmv2-only
1582         $extra_conf
1583
1584 [sysvol]
1585         path = $ctx->{statedir}/sysvol
1586         read only = yes
1587
1588 [netlogon]
1589         path = $ctx->{statedir}/sysvol/$ctx->{dnsname}/scripts
1590         read only = no
1591
1592 ";
1593
1594         my $ret = $self->provision_raw_step1($ctx);
1595         unless ($ret) {
1596                 return undef;
1597         }
1598
1599         my $samba_tool =  Samba::bindir_path($self, "samba-tool");
1600         my $cmd = $self->get_cmd_env_vars($ret);
1601         $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} DC --realm=$dcvars->{REALM}";
1602         $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD} --domain-critical-only";
1603         $cmd .= " --machinepass=machine$ret->{PASSWORD}";
1604         $cmd .= " --backend-store=$self->{default_ldb_backend}";
1605
1606         unless (system($cmd) == 0) {
1607                 warn("Join failed\n$cmd");
1608                 return undef;
1609         }
1610
1611         $self->set_pdc_env_vars($ret, $dcvars);
1612         $ret->{DC_REALM} = $dcvars->{DC_REALM};
1613
1614         return $ret;
1615 }
1616
1617 sub provision_ad_dc_ntvfs($$$)
1618 {
1619         my ($self, $prefix, $extra_provision_options) = @_;
1620
1621         # We keep the old 'winbind' name here in server services to
1622         # ensure upgrades which used that name still work with the now
1623         # alias.
1624
1625         print "PROVISIONING AD DC (NTVFS)...\n";
1626         my $extra_conf_options = "netbios aliases = localDC1-a
1627         server services = +winbind -winbindd
1628         ldap server require strong auth = allow_sasl_without_tls_channel_bindings
1629         raw NTLMv2 auth = yes
1630         lsa over netlogon = yes
1631         rpc server port = 1027
1632         auth event notification = true
1633         dsdb event notification = true
1634         dsdb password event notification = true
1635         dsdb group change notification = true
1636         # override the new SMB2 only default
1637         client min protocol = CORE
1638         server min protocol = LANMAN1
1639
1640         CVE_2020_1472:warn_about_unused_debug_level = 3
1641         CVE_2022_38023:warn_about_unused_debug_level = 3
1642         allow nt4 crypto:torturetest\$ = yes
1643         server reject md5 schannel:schannel2\$ = no
1644         server reject md5 schannel:schannel3\$ = no
1645         server reject md5 schannel:schannel8\$ = no
1646         server reject md5 schannel:schannel9\$ = no
1647         server reject md5 schannel:torturetest\$ = no
1648         server reject md5 schannel:tests4u2proxywk\$ = no
1649         server reject md5 schannel:tests4u2selfbdc\$ = no
1650         server reject md5 schannel:tests4u2selfwk\$ = no
1651         server reject md5 schannel:torturepacbdc\$ = no
1652         server reject md5 schannel:torturepacwksta\$ = no
1653         server require schannel:schannel0\$ = no
1654         server require schannel:schannel1\$ = no
1655         server require schannel:schannel2\$ = no
1656         server require schannel:schannel3\$ = no
1657         server require schannel:schannel4\$ = no
1658         server require schannel:schannel5\$ = no
1659         server require schannel:schannel6\$ = no
1660         server require schannel:schannel7\$ = no
1661         server require schannel:schannel8\$ = no
1662         server require schannel:schannel9\$ = no
1663         server require schannel:schannel10\$ = no
1664         server require schannel:schannel11\$ = no
1665         server require schannel:torturetest\$ = no
1666         server schannel require seal:schannel0\$ = no
1667         server schannel require seal:schannel1\$ = no
1668         server schannel require seal:schannel2\$ = no
1669         server schannel require seal:schannel3\$ = no
1670         server schannel require seal:schannel4\$ = no
1671         server schannel require seal:schannel5\$ = no
1672         server schannel require seal:schannel6\$ = no
1673         server schannel require seal:schannel7\$ = no
1674         server schannel require seal:schannel8\$ = no
1675         server schannel require seal:schannel9\$ = no
1676         server schannel require seal:schannel10\$ = no
1677         server schannel require seal:schannel11\$ = no
1678         server schannel require seal:torturetest\$ = no
1679
1680         # needed for 'samba.tests.auth_log' tests
1681         server require schannel:LOCALDC\$ = no
1682         server schannel require seal:LOCALDC\$ = no
1683         ";
1684         push (@{$extra_provision_options},
1685               "--base-schema=2008_R2",
1686               "--use-ntvfs");
1687         my $ret = $self->provision($prefix,
1688                                    "domain controller",
1689                                    "localdc",
1690                                    "SAMBADOMAIN",
1691                                    "samba.example.com",
1692                                    "2008",
1693                                    "locDCpass1",
1694                                    undef,
1695                                    undef,
1696                                    undef,
1697                                    $extra_conf_options,
1698                                    "",
1699                                    $extra_provision_options);
1700         unless ($ret) {
1701                 return undef;
1702         }
1703
1704         unless($self->add_wins_config("$prefix/private")) {
1705                 warn("Unable to add wins configuration");
1706                 return undef;
1707         }
1708         $ret->{NETBIOSALIAS} = "localdc1-a";
1709         $ret->{DC_REALM} = $ret->{REALM};
1710
1711         return $ret;
1712 }
1713
1714 sub provision_fl2000dc($$)
1715 {
1716         my ($self, $prefix) = @_;
1717
1718         print "PROVISIONING DC WITH FOREST LEVEL 2000...\n";
1719         my $extra_conf_options = "
1720         kdc enable fast = no
1721         spnego:simulate_w2k=yes
1722         ntlmssp_server:force_old_spnego=yes
1723
1724         CVE_2022_38023:warn_about_unused_debug_level = 3
1725         server reject md5 schannel:tests4u2proxywk\$ = no
1726         server reject md5 schannel:tests4u2selfbdc\$ = no
1727         server reject md5 schannel:tests4u2selfwk\$ = no
1728         server reject md5 schannel:torturepacbdc\$ = no
1729         server reject md5 schannel:torturepacwksta\$ = no
1730 ";
1731         my $extra_provision_options = ["--base-schema=2008_R2"];
1732         # This environment uses plain text secrets
1733         # i.e. secret attributes are not encrypted on disk.
1734         # This allows testing of the --plaintext-secrets option for
1735         # provision
1736         push (@{$extra_provision_options}, "--plaintext-secrets");
1737         my $ret = $self->provision($prefix,
1738                                    "domain controller",
1739                                    "dc5",
1740                                    "SAMBA2000",
1741                                    "samba2000.example.com",
1742                                    "2000",
1743                                    "locDCpass5",
1744                                    undef,
1745                                    undef,
1746                                    undef,
1747                                    $extra_conf_options,
1748                                    "",
1749                                    $extra_provision_options);
1750         unless ($ret) {
1751                 return undef;
1752         }
1753
1754         unless($self->add_wins_config("$prefix/private")) {
1755                 warn("Unable to add wins configuration");
1756                 return undef;
1757         }
1758         $ret->{DC_REALM} = $ret->{REALM};
1759
1760         return $ret;
1761 }
1762
1763 sub provision_fl2003dc($$$)
1764 {
1765         my ($self, $prefix, $dcvars) = @_;
1766         my $ip_addr1 = Samba::get_ipv4_addr("fakednsforwarder1");
1767         my $ip_addr2 = Samba::get_ipv6_addr("fakednsforwarder2");
1768
1769         print "PROVISIONING DC WITH FOREST LEVEL 2003...\n";
1770         my $extra_conf_options = "
1771         allow dns updates = nonsecure and secure
1772
1773         kdc enable fast = no
1774         dcesrv:header signing = no
1775         dcesrv:max auth states = 0
1776
1777         dns forwarder = $ip_addr1 [$ip_addr2]:54
1778
1779         CVE_2022_38023:warn_about_unused_debug_level = 3
1780         server reject md5 schannel:tests4u2proxywk\$ = no
1781         server reject md5 schannel:tests4u2selfbdc\$ = no
1782         server reject md5 schannel:tests4u2selfwk\$ = no
1783         server reject md5 schannel:torturepacbdc\$ = no
1784         server reject md5 schannel:torturepacwksta\$ = no
1785 ";
1786
1787         my $extra_provision_options = ["--base-schema=2008_R2"];
1788         my $ret = $self->provision($prefix,
1789                                    "domain controller",
1790                                    "dc6",
1791                                    "SAMBA2003",
1792                                    "samba2003.example.com",
1793                                    "2003",
1794                                    "locDCpass6",
1795                                    undef,
1796                                    undef,
1797                                    undef,
1798                                    $extra_conf_options,
1799                                    "",
1800                                    $extra_provision_options);
1801         unless (defined $ret) {
1802                 return undef;
1803         }
1804
1805         $ret->{DNS_FORWARDER1} = $ip_addr1;
1806         $ret->{DNS_FORWARDER2} = $ip_addr2;
1807
1808         my @samba_tool_options;
1809         push (@samba_tool_options, Samba::bindir_path($self, "samba-tool"));
1810         push (@samba_tool_options, "domain");
1811         push (@samba_tool_options, "passwordsettings");
1812         push (@samba_tool_options, "set");
1813         push (@samba_tool_options, "--configfile=$ret->{SERVERCONFFILE}");
1814         push (@samba_tool_options, "--min-pwd-age=0");
1815         push (@samba_tool_options, "--history-length=1");
1816
1817         my $samba_tool_cmd = join(" ", @samba_tool_options);
1818
1819         unless (system($samba_tool_cmd) == 0) {
1820                 warn("Unable to set min password age to 0: \n$samba_tool_cmd\n");
1821                 return undef;
1822         }
1823
1824         unless($self->add_wins_config("$prefix/private")) {
1825                 warn("Unable to add wins configuration");
1826                 return undef;
1827         }
1828
1829         return $ret;
1830 }
1831
1832 sub provision_fl2008r2dc($$$)
1833 {
1834         my ($self, $prefix, $dcvars) = @_;
1835
1836         print "PROVISIONING DC WITH FOREST LEVEL 2008r2...\n";
1837         my $extra_conf_options = "
1838         ldap server require strong auth = no
1839         # delay by 10 seconds, 10^7 usecs
1840         ldap_server:delay_expire_disconnect = 10000
1841
1842         CVE_2022_38023:warn_about_unused_debug_level = 3
1843         server reject md5 schannel:tests4u2proxywk\$ = no
1844         server reject md5 schannel:tests4u2selfbdc\$ = no
1845         server reject md5 schannel:tests4u2selfwk\$ = no
1846         server reject md5 schannel:torturepacbdc\$ = no
1847         server reject md5 schannel:torturepacwksta\$ = no
1848 ";
1849         my $extra_provision_options = ["--base-schema=2008_R2"];
1850         my $ret = $self->provision($prefix,
1851                                    "domain controller",
1852                                    "dc7",
1853                                    "SAMBA2008R2",
1854                                    "samba2008R2.example.com",
1855                                    "2008_R2",
1856                                    "locDCpass7",
1857                                    undef,
1858                                    undef,
1859                                    undef,
1860                                    $extra_conf_options,
1861                                    "",
1862                                    $extra_provision_options);
1863         unless (defined $ret) {
1864                 return undef;
1865         }
1866
1867         unless ($self->add_wins_config("$prefix/private")) {
1868                 warn("Unable to add wins configuration");
1869                 return undef;
1870         }
1871         $ret->{DC_REALM} = $ret->{REALM};
1872
1873         return $ret;
1874 }
1875
1876
1877 sub provision_rodc($$$)
1878 {
1879         my ($self, $prefix, $dcvars) = @_;
1880         print "PROVISIONING RODC...\n";
1881
1882         # We do this so that we don't run the provision.  That's the job of 'net join RODC'.
1883         my $ctx = $self->provision_raw_prepare($prefix, "domain controller",
1884                                                "rodc",
1885                                                $dcvars->{DOMAIN},
1886                                                $dcvars->{REALM},
1887                                                $dcvars->{DOMSID},
1888                                                "2008",
1889                                                $dcvars->{PASSWORD},
1890                                                $dcvars->{SERVER_IP},
1891                                                $dcvars->{SERVER_IPV6});
1892         unless ($ctx) {
1893                 return undef;
1894         }
1895
1896         $ctx->{share} = "$ctx->{prefix_abs}/share";
1897         push(@{$ctx->{directories}}, "$ctx->{share}");
1898
1899         $ctx->{smb_conf_extra_options} = "
1900         max xmit = 32K
1901         server max protocol = SMB2
1902         password server = $dcvars->{DC_SERVER}
1903
1904 [sysvol]
1905         path = $ctx->{statedir}/sysvol
1906         read only = yes
1907
1908 [netlogon]
1909         path = $ctx->{statedir}/sysvol/$ctx->{dnsname}/scripts
1910         read only = yes
1911
1912 [tmp]
1913         path = $ctx->{share}
1914         read only = no
1915         posix:sharedelay = 10000
1916         posix:oplocktimeout = 3
1917         posix:writetimeupdatedelay = 50000
1918
1919 ";
1920
1921         my $ret = $self->provision_raw_step1($ctx);
1922         unless ($ret) {
1923                 return undef;
1924         }
1925
1926         my $samba_tool =  Samba::bindir_path($self, "samba-tool");
1927         my $cmd = $self->get_cmd_env_vars($ret);
1928         $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} RODC";
1929         $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
1930         $cmd .= " --server=$dcvars->{DC_SERVER}";
1931
1932         unless (system($cmd) == 0) {
1933                 warn("RODC join failed\n$cmd");
1934                 return undef;
1935         }
1936
1937         # This ensures deterministic behaviour for tests that want to have the 'testallowed account'
1938         # user password verified on the RODC
1939         my $testallowed_account = "testallowed account";
1940         $cmd = $self->get_cmd_env_vars($ret);
1941         $cmd .= "$samba_tool rodc preload '$testallowed_account' $ret->{CONFIGURATION}";
1942         $cmd .= " --server=$dcvars->{DC_SERVER}";
1943
1944         unless (system($cmd) == 0) {
1945                 warn("RODC join failed\n$cmd");
1946                 return undef;
1947         }
1948
1949         # we overwrite the kdc after the RODC join
1950         # so that use the RODC as kdc and test
1951         # the proxy code
1952         $ctx->{kdc_ipv4} = $ret->{SERVER_IP};
1953         $ctx->{kdc_ipv6} = $ret->{SERVER_IPV6};
1954         Samba::mk_krb5_conf($ctx);
1955         Samba::mk_mitkdc_conf($ctx, abs_path(Samba::bindir_path($self, "shared")));
1956
1957         $self->set_pdc_env_vars($ret, $dcvars);
1958
1959         return $ret;
1960 }
1961
1962 sub read_config_h($)
1963 {
1964         my ($name) = @_;
1965         my %ret;
1966         open(LF, "<$name") or die("unable to read $name: $!");
1967         while (<LF>) {
1968                 chomp;
1969                 next if not (/^#define /);
1970                 if (/^#define (.*?)[ \t]+(.*?)$/) {
1971                         $ret{$1} = $2;
1972                         next;
1973                 }
1974                 if (/^#define (.*?)[ \t]+$/) {
1975                         $ret{$1} = 1;;
1976                         next;
1977                 }
1978         }
1979         close(LF);
1980         return \%ret;
1981 }
1982
1983 sub provision_ad_dc()
1984 {
1985         my ($self,
1986             $prefix,
1987             $hostname,
1988             $domain,
1989             $realm,
1990             $force_fips_mode,
1991             $smbconf_args,
1992             $extra_provision_options,
1993             $functional_level) = @_;
1994
1995         my $prefix_abs = abs_path($prefix);
1996
1997         my $bindir_abs = abs_path($self->{bindir});
1998         my $lockdir="$prefix_abs/lockdir";
1999         my $conffile="$prefix_abs/etc/smb.conf";
2000
2001         my $require_mutexes = "dbwrap_tdb_require_mutexes:* = yes";
2002         if ($ENV{SELFTEST_DONT_REQUIRE_TDB_MUTEX_SUPPORT} // '' eq "1") {
2003                 $require_mutexes = "";
2004         }
2005
2006         my $config_h = {};
2007
2008         if (!defined($functional_level)) {
2009                 $functional_level = "2016";
2010         }
2011
2012         # If we choose to have distinct environments for experimental
2013         # 2012 as well as the experimental 2016 support, we should
2014         # extend what we match here.
2015         if ($functional_level eq "2016") {
2016                 $smbconf_args = "$smbconf_args
2017
2018 [global]
2019         ad dc functional level = 2016
2020 ";
2021         }
2022         if (defined($ENV{CONFIG_H})) {
2023                 $config_h = read_config_h($ENV{CONFIG_H});
2024         }
2025
2026         my $password_hash_gpg_key_ids = "password hash gpg key ids = 4952E40301FAB41A";
2027         $password_hash_gpg_key_ids = "" unless defined($config_h->{HAVE_GPGME});
2028
2029         my $extra_smbconf_options = "
2030         xattr_tdb:file = $prefix_abs/statedir/xattr.tdb
2031
2032         dbwrap_tdb_mutexes:* = yes
2033         ${require_mutexes}
2034
2035         ${password_hash_gpg_key_ids}
2036
2037         kernel oplocks = no
2038         kernel change notify = no
2039         smb2 leases = no
2040         smb2 disable oplock break retry = yes
2041         server multi channel support = yes
2042
2043         logging = file
2044         printing = bsd
2045         printcap name = /dev/null
2046
2047         max protocol = SMB3
2048         read only = no
2049
2050         smbd:sharedelay = 100000
2051         smbd:writetimeupdatedelay = 500000
2052         create mask = 755
2053         dos filemode = yes
2054         check parent directory delete on close = yes
2055
2056         dcerpc endpoint servers = -winreg -srvsvc
2057
2058         printcap name = /dev/null
2059
2060         addprinter command = $ENV{SRCDIR_ABS}/source3/script/tests/printing/modprinter.pl -a -s $conffile --
2061         deleteprinter command = $ENV{SRCDIR_ABS}/source3/script/tests/printing/modprinter.pl -d -s $conffile --
2062
2063         printing = vlp
2064         print command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb print %p %s
2065         lpq command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb lpq %p
2066         lp rm command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb lprm %p %j
2067         lp pause command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb lppause %p %j
2068         lp resume command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb lpresume %p %j
2069         queue pause command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb queuepause %p
2070         queue resume command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb queueresume %p
2071         lpq cache time = 0
2072         print notify backchannel = yes
2073
2074         CVE_2020_1472:warn_about_unused_debug_level = 3
2075         CVE_2022_38023:warn_about_unused_debug_level = 3
2076         CVE_2022_38023:error_debug_level = 2
2077         server reject md5 schannel:schannel2\$ = no
2078         server reject md5 schannel:schannel3\$ = no
2079         server reject md5 schannel:schannel8\$ = no
2080         server reject md5 schannel:schannel9\$ = no
2081         server reject md5 schannel:torturetest\$ = no
2082         server reject md5 schannel:tests4u2proxywk\$ = no
2083         server reject md5 schannel:tests4u2selfbdc\$ = no
2084         server reject md5 schannel:tests4u2selfwk\$ = no
2085         server reject md5 schannel:torturepacbdc\$ = no
2086         server reject md5 schannel:torturepacwksta\$ = no
2087         server reject md5 schannel:samlogontest\$ = no
2088         server require schannel:schannel0\$ = no
2089         server require schannel:schannel1\$ = no
2090         server require schannel:schannel2\$ = no
2091         server require schannel:schannel3\$ = no
2092         server require schannel:schannel4\$ = no
2093         server require schannel:schannel5\$ = no
2094         server require schannel:schannel6\$ = no
2095         server require schannel:schannel7\$ = no
2096         server require schannel:schannel8\$ = no
2097         server require schannel:schannel9\$ = no
2098         server require schannel:schannel10\$ = no
2099         server require schannel:schannel11\$ = no
2100         server require schannel:torturetest\$ = no
2101         server schannel require seal:schannel0\$ = no
2102         server schannel require seal:schannel1\$ = no
2103         server schannel require seal:schannel2\$ = no
2104         server schannel require seal:schannel3\$ = no
2105         server schannel require seal:schannel4\$ = no
2106         server schannel require seal:schannel5\$ = no
2107         server schannel require seal:schannel6\$ = no
2108         server schannel require seal:schannel7\$ = no
2109         server schannel require seal:schannel8\$ = no
2110         server schannel require seal:schannel9\$ = no
2111         server schannel require seal:schannel10\$ = no
2112         server schannel require seal:schannel11\$ = no
2113         server schannel require seal:torturetest\$ = no
2114
2115         auth event notification = true
2116         dsdb event notification = true
2117         dsdb password event notification = true
2118         dsdb group change notification = true
2119         $smbconf_args
2120 ";
2121
2122         my $extra_smbconf_shares = "
2123
2124 [tmpenc]
2125         copy = tmp
2126         smb encrypt = required
2127
2128 [tmpcase]
2129         copy = tmp
2130         case sensitive = yes
2131
2132 [tmpguest]
2133         copy = tmp
2134         guest ok = yes
2135
2136 [hideunread]
2137         copy = tmp
2138         hide unreadable = yes
2139
2140 [durable]
2141         copy = tmp
2142         kernel share modes = no
2143         kernel oplocks = no
2144         posix locking = no
2145
2146 [print\$]
2147         copy = tmp
2148
2149 [print1]
2150         copy = tmp
2151         printable = yes
2152
2153 [print2]
2154         copy = print1
2155 [print3]
2156         copy = print1
2157 [print4]
2158         copy = print1
2159         guest ok = yes
2160 [lp]
2161         copy = print1
2162 ";
2163
2164         push (@{$extra_provision_options}, "--backend-store=$self->{default_ldb_backend}");
2165         print "PROVISIONING AD DC...\n";
2166         my $ret = $self->provision($prefix,
2167                                    "domain controller",
2168                                    $hostname,
2169                                    $domain,
2170                                    $realm,
2171                                    $functional_level,
2172                                    "locDCpass1",
2173                                    undef,
2174                                    undef,
2175                                    $force_fips_mode,
2176                                    $extra_smbconf_options,
2177                                    $extra_smbconf_shares,
2178                                    $extra_provision_options);
2179         unless (defined $ret) {
2180                 return undef;
2181         }
2182
2183         unless($self->add_wins_config("$prefix/private")) {
2184                 warn("Unable to add wins configuration");
2185                 return undef;
2186         }
2187
2188         return $ret;
2189 }
2190
2191 sub provision_chgdcpass($$)
2192 {
2193         my ($self, $prefix) = @_;
2194
2195         print "PROVISIONING CHGDCPASS...\n";
2196         # This environment disallows the use of this password
2197         # (and also removes the default AD complexity checks)
2198         my $unacceptable_password = "Paßßword-widk3Dsle32jxdBdskldsk55klASKQ";
2199
2200         # This environment also sets some settings that are unusual,
2201         # to test specific behaviours.  In particular, this
2202         # environment fails to correctly support DRSUAPI_DRS_GET_ANC
2203         # like Samba before 4.5 and DRSUAPI_DRS_GET_TGT before 4.8
2204         #
2205         # Additionally, disabling DRSUAPI_DRS_GET_TGT causes all links
2206         # to be sent last (in the final chunk), which is like Samba
2207         # before 4.8.
2208
2209         my $extra_smb_conf = "
2210         check password script = $self->{srcdir}/selftest/checkpassword_arg1.sh ${unacceptable_password}
2211         allow dcerpc auth level connect:lsarpc = yes
2212         dcesrv:max auth states = 8
2213         drs:broken_samba_4.5_get_anc_emulation = true
2214         drs:get_tgt_support = false
2215 ";
2216         my $extra_provision_options = ["--dns-backend=BIND9_DLZ"];
2217         my $ret = $self->provision($prefix,
2218                                    "domain controller",
2219                                    "chgdcpass",
2220                                    "CHDCDOMAIN",
2221                                    "chgdcpassword.samba.example.com",
2222                                    "2008",
2223                                    "chgDCpass1",
2224                                    undef,
2225                                    undef,
2226                                    undef,
2227                                    $extra_smb_conf,
2228                                    "",
2229                                    $extra_provision_options);
2230         unless (defined $ret) {
2231                 return undef;
2232         }
2233
2234         unless($self->add_wins_config("$prefix/private")) {
2235                 warn("Unable to add wins configuration");
2236                 return undef;
2237         }
2238
2239         # Remove secrets.tdb from this environment to test that we
2240         # still start up on systems without the new matching
2241         # secrets.tdb records.
2242         unless (unlink("$ret->{PRIVATEDIR}/secrets.tdb") || unlink("$ret->{PRIVATEDIR}/secrets.ntdb")) {
2243                 warn("Unable to remove $ret->{PRIVATEDIR}/secrets.tdb added during provision");
2244                 return undef;
2245         }
2246
2247         $ret->{UNACCEPTABLE_PASSWORD} = $unacceptable_password;
2248
2249         return $ret;
2250 }
2251
2252 sub teardown_env_terminate($$)
2253 {
2254         my ($self, $envvars) = @_;
2255         my $pid;
2256
2257         # This should cause samba to terminate gracefully
2258         my $smbcontrol = Samba::bindir_path($self, "smbcontrol");
2259         my $cmd = "";
2260         $cmd .= "$smbcontrol samba shutdown $envvars->{CONFIGURATION}";
2261         my $ret = system($cmd);
2262         if ($ret != 0) {
2263                 warn "'$cmd' failed with '$ret'\n";
2264         }
2265
2266         # This should cause samba to terminate gracefully
2267         close($envvars->{STDIN_PIPE});
2268
2269         $pid = $envvars->{SAMBA_PID};
2270         my $count = 0;
2271         my $childpid;
2272
2273         # This should give it time to write out the gcov data
2274         until ($count > 15) {
2275             if (Samba::cleanup_child($pid, "samba") != 0) {
2276                 return;
2277             }
2278             sleep(1);
2279             $count++;
2280         }
2281
2282         # After 15 Seconds, work out why this thing is still alive
2283         warn "server process $pid took more than $count seconds to exit, showing backtrace:\n";
2284         system("$self->{srcdir}/selftest/gdb_backtrace $pid");
2285
2286         until ($count > 30) {
2287             if (Samba::cleanup_child($pid, "samba") != 0) {
2288                 return;
2289             }
2290             sleep(1);
2291             $count++;
2292         }
2293
2294         if (kill(0, $pid)) {
2295             warn "server process $pid took more than $count seconds to exit, sending SIGTERM\n";
2296             kill "TERM", $pid;
2297         }
2298
2299         until ($count > 40) {
2300             if (Samba::cleanup_child($pid, "samba") != 0) {
2301                 return;
2302             }
2303             sleep(1);
2304             $count++;
2305         }
2306         # If it is still around, kill it
2307         if (kill(0, $pid)) {
2308             warn "server process $pid took more than $count seconds to exit, killing\n with SIGKILL\n";
2309             kill 9, $pid;
2310         }
2311         return;
2312 }
2313
2314 sub teardown_env($$)
2315 {
2316         my ($self, $envvars) = @_;
2317         teardown_env_terminate($self, $envvars);
2318
2319         print $self->getlog_env($envvars);
2320
2321         return;
2322 }
2323
2324 sub getlog_env($$)
2325 {
2326         my ($self, $envvars) = @_;
2327         my $title = "SAMBA LOG of: $envvars->{NETBIOSNAME} pid $envvars->{SAMBA_PID}\n";
2328         my $out = $title;
2329
2330         open(LOG, "<$envvars->{SAMBA_TEST_LOG}");
2331
2332         seek(LOG, $envvars->{SAMBA_TEST_LOG_POS}, SEEK_SET);
2333         while (<LOG>) {
2334                 $out .= $_;
2335         }
2336         $envvars->{SAMBA_TEST_LOG_POS} = tell(LOG);
2337         close(LOG);
2338
2339         return "" if $out eq $title;
2340
2341         return $out;
2342 }
2343
2344 sub check_env($$)
2345 {
2346         my ($self, $envvars) = @_;
2347         my $samba_pid = $envvars->{SAMBA_PID};
2348
2349         if (not defined($samba_pid)) {
2350             return 0;
2351         } elsif ($samba_pid > 0) {
2352             my $childpid = Samba::cleanup_child($samba_pid, "samba");
2353
2354             if ($childpid == 0) {
2355                 return 1;
2356             }
2357             return 0;
2358         } else {
2359             return 1;
2360         }
2361 }
2362
2363 # Declare the environments Samba4 makes available.
2364 # To be set up, they will be called as
2365 #   samba4->setup_$envname($self, $path, $dep_1_vars, $dep_2_vars, ...)
2366 # The interdependencies between the testenvs are declared below. Some testenvs
2367 # are dependent on another testenv running first, e.g. vampire_dc is dependent
2368 # on ad_dc_ntvfs because vampire_dc joins ad_dc_ntvfs's domain. All DCs are
2369 # dependent on dns_hub, which handles resolving DNS queries for the realm.
2370 %Samba4::ENV_DEPS = (
2371         # name               => [dep_1, dep_2, ...],
2372         dns_hub              => [],
2373         ad_dc_ntvfs          => ["dns_hub"],
2374         ad_dc_fips           => ["dns_hub"],
2375         ad_dc                => ["dns_hub"],
2376         ad_dc_smb1           => ["dns_hub"],
2377         ad_dc_smb1_done      => ["ad_dc_smb1"],
2378         ad_dc_no_nss         => ["dns_hub"],
2379         ad_dc_no_ntlm        => ["dns_hub"],
2380
2381         fl2008r2dc           => ["ad_dc", "nt4_dc"],
2382         fl2003dc             => ["ad_dc"],
2383         fl2000dc             => ["ad_dc"],
2384
2385         vampire_2000_dc      => ["fl2000dc"],
2386         vampire_dc           => ["ad_dc_ntvfs"],
2387         promoted_dc          => ["ad_dc_ntvfs"],
2388
2389         rodc                 => ["ad_dc_ntvfs"],
2390         rpc_proxy            => ["ad_dc_ntvfs"],
2391         chgdcpass            => ["dns_hub"],
2392
2393         s4member_dflt_domain => ["ad_dc_ntvfs"],
2394         s4member             => ["ad_dc_ntvfs"],
2395
2396         # envs that test the server process model
2397         proclimitdc          => ["dns_hub"],
2398         preforkrestartdc     => ["dns_hub"],
2399
2400         # backup/restore testenvs
2401         backupfromdc         => ["dns_hub"],
2402         customdc             => ["dns_hub"],
2403         restoredc            => ["backupfromdc"],
2404         renamedc             => ["backupfromdc"],
2405         offlinebackupdc      => ["backupfromdc"],
2406         labdc                => ["backupfromdc"],
2407
2408         # aliases in order to split autobuild tasks
2409         fl2008dc             => ["ad_dc_ntvfs"],
2410         ad_dc_default        => ["ad_dc"],
2411         ad_dc_default_smb1   => ["ad_dc_smb1"],
2412         ad_dc_default_smb1_done   => ["ad_dc_default_smb1"],
2413         ad_dc_slowtests      => ["ad_dc"],
2414         ad_dc_backup         => ["ad_dc"],
2415
2416         schema_dc      => ["dns_hub"],
2417         schema_pair_dc => ["schema_dc"],
2418
2419         none                 => [],
2420 );
2421
2422 %Samba4::ENV_DEPS_POST = (
2423         schema_dc => ["schema_pair_dc"],
2424 );
2425
2426 sub return_alias_env
2427 {
2428         my ($self, $path, $env) = @_;
2429
2430         # just an alias
2431         return $env;
2432 }
2433
2434 sub setup_fl2008dc
2435 {
2436         my ($self, $path, $dep_env) = @_;
2437         return $self->return_alias_env($path, $dep_env)
2438 }
2439
2440 sub setup_ad_dc_default
2441 {
2442         my ($self, $path, $dep_env) = @_;
2443         return $self->return_alias_env($path, $dep_env)
2444 }
2445
2446 sub setup_ad_dc_default_smb1
2447 {
2448         my ($self, $path, $dep_env) = @_;
2449         return $self->return_alias_env($path, $dep_env)
2450 }
2451
2452 sub setup_ad_dc_default_smb1_done
2453 {
2454         my ($self, $path, $dep_env) = @_;
2455         return $self->return_alias_env($path, $dep_env)
2456 }
2457
2458 sub setup_ad_dc_slowtests
2459 {
2460         my ($self, $path, $dep_env) = @_;
2461         return $self->return_alias_env($path, $dep_env)
2462 }
2463
2464 sub setup_ad_dc_backup
2465 {
2466         my ($self, $path, $dep_env) = @_;
2467         return $self->return_alias_env($path, $dep_env)
2468 }
2469
2470 sub setup_s4member
2471 {
2472         my ($self, $path, $dc_vars) = @_;
2473
2474         my $env = $self->provision_s4member($path, $dc_vars, "s4member");
2475
2476         if (defined $env) {
2477                 if (not defined($self->check_or_start($env, "standard"))) {
2478                         return undef;
2479                 }
2480         }
2481
2482         return $env;
2483 }
2484
2485 sub setup_s4member_dflt_domain
2486 {
2487         my ($self, $path, $dc_vars) = @_;
2488
2489         my $env = $self->provision_s4member($path, $dc_vars, "s4member_dflt",
2490                                             "winbind use default domain = yes");
2491
2492         if (defined $env) {
2493                 if (not defined($self->check_or_start($env, "standard"))) {
2494                         return undef;
2495                 }
2496         }
2497
2498         return $env;
2499 }
2500
2501 sub setup_rpc_proxy
2502 {
2503         my ($self, $path, $dc_vars) = @_;
2504
2505         my $env = $self->provision_rpc_proxy($path, $dc_vars);
2506
2507         if (defined $env) {
2508                 if (not defined($self->check_or_start($env, "standard"))) {
2509                         return undef;
2510                 }
2511         }
2512         return $env;
2513 }
2514
2515 sub setup_ad_dc_ntvfs
2516 {
2517         my ($self, $path) = @_;
2518
2519         my $env = $self->provision_ad_dc_ntvfs($path, undef);
2520         if (defined $env) {
2521                 if (not defined($self->check_or_start($env, "standard"))) {
2522                     warn("Failed to start ad_dc_ntvfs");
2523                         return undef;
2524                 }
2525         }
2526         return $env;
2527 }
2528
2529 sub setup_chgdcpass
2530 {
2531         my ($self, $path) = @_;
2532
2533         my $env = $self->provision_chgdcpass($path);
2534         if (defined $env) {
2535                 if (not defined($self->check_or_start($env, "standard"))) {
2536                         return undef;
2537                 }
2538         }
2539         return $env;
2540 }
2541
2542 sub setup_fl2000dc
2543 {
2544         my ($self, $path, $dc_vars) = @_;
2545
2546         my $env = $self->provision_fl2000dc($path);
2547         if (defined $env) {
2548                 if (not defined($self->check_or_start($env, "standard"))) {
2549                         return undef;
2550                 }
2551
2552                 $env = $self->setup_trust($env, $dc_vars, "external", "--no-aes-keys --direction=outgoing");
2553         }
2554
2555         return $env;
2556 }
2557
2558 sub setup_fl2003dc
2559 {
2560         my ($self, $path, $dc_vars) = @_;
2561
2562         my $env = $self->provision_fl2003dc($path);
2563
2564         if (defined $env) {
2565                 if (not defined($self->check_or_start($env, "standard"))) {
2566                         return undef;
2567                 }
2568
2569                 $env = $self->setup_trust($env, $dc_vars, "external", "--no-aes-keys");
2570         }
2571         return $env;
2572 }
2573
2574 sub setup_fl2008r2dc
2575 {
2576         my ($self, $path, $ad_dc_vars, $nt4_dc_vars) = @_;
2577
2578         my $env = $self->provision_fl2008r2dc($path);
2579
2580         if (!defined $env) {
2581             return $env;
2582         }
2583
2584         if (not defined($self->check_or_start($env, "standard"))) {
2585             return undef;
2586         }
2587
2588         my $upn_array = ["$env->{REALM}.upn"];
2589         my $spn_array = ["$env->{REALM}.spn"];
2590
2591         if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) {
2592             return undef;
2593         }
2594
2595         $env = $self->setup_trust($env, $ad_dc_vars, "forest", "");
2596         if (!defined $env) {
2597             return undef;
2598         }
2599
2600         my $net = Samba::bindir_path($self, "net");
2601         my $smbcontrol = Samba::bindir_path($self, "smbcontrol");
2602
2603         my $trustpw = "TrUsTpW";
2604         $trustpw .= "$env->{SOCKET_WRAPPER_DEFAULT_IFACE}";
2605         $trustpw .= "$nt4_dc_vars->{SOCKET_WRAPPER_DEFAULT_IFACE}";
2606
2607         my $cmd_env = $self->get_cmd_env_vars($env);
2608         my $cmd = $cmd_env;
2609         $cmd .= "$net rpc trust create ";
2610         $cmd .= "otherdomainsid=$nt4_dc_vars->{SAMSID} ";
2611         $cmd .= "otherdomain=$nt4_dc_vars->{DOMAIN} ";
2612         $cmd .= "other_netbios_domain=$nt4_dc_vars->{DOMAIN} ";
2613         $cmd .= "trustpw=$trustpw ";
2614         $cmd .= "$env->{CONFIGURATION} ";
2615         $cmd .= "-U $env->{DOMAIN}/$env->{USERNAME}\%$env->{PASSWORD} ";
2616
2617         if (system($cmd) != 0) {
2618                 warn("net rpc trust create failed\n$cmd");
2619                 return undef;
2620         }
2621
2622         my $nt4_cmd_env = $self->get_cmd_env_vars($nt4_dc_vars);
2623         $cmd = $nt4_cmd_env;
2624         $cmd .= "$net rpc trustdom establish $env->{DOMAIN} -U/%$trustpw $nt4_dc_vars->{CONFIGURATION}";
2625
2626         if (system($cmd) != 0) {
2627                 warn("add failed\n$cmd");
2628                 return undef;
2629         }
2630
2631         # Reload trusts
2632         $cmd = $nt4_cmd_env;
2633         $cmd .= "$smbcontrol winbindd reload-config $nt4_dc_vars->{CONFIGURATION}";
2634
2635         if (system($cmd) != 0) {
2636                 warn("add failed\n$cmd");
2637                 return undef;
2638         }
2639
2640         $env->{NT4_TRUST_SERVER} = $nt4_dc_vars->{SERVER};
2641         $env->{NT4_TRUST_SERVER_IP} = $nt4_dc_vars->{SERVER_IP};
2642         $env->{NT4_TRUST_DOMAIN} = $nt4_dc_vars->{DOMAIN};
2643         $env->{NT4_TRUST_DOMSID} = $nt4_dc_vars->{DOMSID};
2644
2645         return $env;
2646 }
2647
2648 sub setup_vampire_dc
2649 {
2650         return setup_generic_vampire_dc(@_, "2008");
2651 }
2652
2653 sub setup_vampire_2000_dc
2654 {
2655         return setup_generic_vampire_dc(@_, "2000");
2656 }
2657
2658 sub setup_generic_vampire_dc
2659 {
2660         my ($self, $path, $dc_vars, $fl) = @_;
2661
2662         my $env = $self->provision_vampire_dc($path, $dc_vars, $fl);
2663
2664         if (defined $env) {
2665                 if (not defined($self->check_or_start($env, "single"))) {
2666                         return undef;
2667                 }
2668
2669                 # force replicated DC to update repsTo/repsFrom
2670                 # for vampired partitions
2671                 my $samba_tool =  Samba::bindir_path($self, "samba-tool");
2672
2673                 # as 'vampired' dc may add data in its local replica
2674                 # we need to synchronize data between DCs
2675                 my $base_dn = "DC=".join(",DC=", split(/\./, $dc_vars->{REALM}));
2676                 my $cmd = $self->get_cmd_env_vars($env);
2677                 $cmd .= " $samba_tool drs replicate $env->{DC_SERVER} $env->{SERVER}";
2678                 $cmd .= " $dc_vars->{CONFIGURATION}";
2679                 $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
2680                 # replicate Configuration NC
2681                 my $cmd_repl = "$cmd \"CN=Configuration,$base_dn\"";
2682                 unless(system($cmd_repl) == 0) {
2683                         warn("Failed to replicate\n$cmd_repl");
2684                         return undef;
2685                 }
2686                 # replicate Default NC
2687                 $cmd_repl = "$cmd \"$base_dn\"";
2688                 unless(system($cmd_repl) == 0) {
2689                         warn("Failed to replicate\n$cmd_repl");
2690                         return undef;
2691                 }
2692
2693                 # Pull in a full set of changes from the main DC
2694                 $base_dn = "DC=".join(",DC=", split(/\./, $dc_vars->{REALM}));
2695                 $cmd = $self->get_cmd_env_vars($env);
2696                 $cmd .= " $samba_tool drs replicate $env->{SERVER} $env->{DC_SERVER}";
2697                 $cmd .= " $dc_vars->{CONFIGURATION}";
2698                 $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
2699                 # replicate Configuration NC
2700                 $cmd_repl = "$cmd \"CN=Configuration,$base_dn\"";
2701                 unless(system($cmd_repl) == 0) {
2702                         warn("Failed to replicate\n$cmd_repl");
2703                         return undef;
2704                 }
2705                 # replicate Default NC
2706                 $cmd_repl = "$cmd \"$base_dn\"";
2707                 unless(system($cmd_repl) == 0) {
2708                         warn("Failed to replicate\n$cmd_repl");
2709                         return undef;
2710                 }
2711         }
2712
2713         return $env;
2714 }
2715
2716 sub setup_promoted_dc
2717 {
2718         my ($self, $path, $dc_vars) = @_;
2719
2720         my $env = $self->provision_promoted_dc($path, $dc_vars);
2721
2722         if (defined $env) {
2723                 if (not defined($self->check_or_start($env, "single"))) {
2724                         return undef;
2725                 }
2726
2727                 # force source and replicated DC to update repsTo/repsFrom
2728                 # for vampired partitions
2729                 my $samba_tool =  Samba::bindir_path($self, "samba-tool");
2730                 my $cmd = $self->get_cmd_env_vars($env);
2731                 # as 'vampired' dc may add data in its local replica
2732                 # we need to synchronize data between DCs
2733                 my $base_dn = "DC=".join(",DC=", split(/\./, $dc_vars->{REALM}));
2734                 $cmd .= " $samba_tool drs replicate $env->{DC_SERVER} $env->{SERVER}";
2735                 $cmd .= " $dc_vars->{CONFIGURATION}";
2736                 $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
2737                 # replicate Configuration NC
2738                 my $cmd_repl = "$cmd \"CN=Configuration,$base_dn\"";
2739                 unless(system($cmd_repl) == 0) {
2740                         warn("Failed to replicate\n$cmd_repl");
2741                         return undef;
2742                 }
2743                 # replicate Default NC
2744                 $cmd_repl = "$cmd \"$base_dn\"";
2745                 unless(system($cmd_repl) == 0) {
2746                         warn("Failed to replicate\n$cmd_repl");
2747                         return undef;
2748                 }
2749         }
2750
2751         return $env;
2752 }
2753
2754 sub setup_rodc
2755 {
2756         my ($self, $path, $dc_vars) = @_;
2757
2758         my $env = $self->provision_rodc($path, $dc_vars);
2759
2760         unless ($env) {
2761                 return undef;
2762         }
2763
2764         if (not defined($self->check_or_start($env, "standard"))) {
2765             return undef;
2766         }
2767
2768         my $samba_tool =  Samba::bindir_path($self, "samba-tool");
2769         my $cmd = $self->get_cmd_env_vars($env);
2770
2771         my $base_dn = "DC=".join(",DC=", split(/\./, $dc_vars->{REALM}));
2772         $cmd .= " $samba_tool drs replicate $env->{SERVER} $env->{DC_SERVER}";
2773         $cmd .= " $dc_vars->{CONFIGURATION}";
2774         $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
2775         # replicate Configuration NC
2776         my $cmd_repl = "$cmd \"CN=Configuration,$base_dn\"";
2777         unless(system($cmd_repl) == 0) {
2778             warn("Failed to replicate\n$cmd_repl");
2779             return undef;
2780         }
2781         # replicate Default NC
2782         $cmd_repl = "$cmd \"$base_dn\"";
2783         unless(system($cmd_repl) == 0) {
2784             warn("Failed to replicate\n$cmd_repl");
2785             return undef;
2786         }
2787
2788         return $env;
2789 }
2790
2791 sub _setup_ad_dc
2792 {
2793         my ($self, $path, $conf_opts, $server, $dom, $functional_level) = @_;
2794
2795         # If we didn't build with ADS, pretend this env was never available
2796         if (not $self->{target3}->have_ads()) {
2797                return "UNKNOWN";
2798         }
2799
2800         if (!defined($conf_opts)) {
2801                 $conf_opts = "";
2802         }
2803         if (!defined($server)) {
2804                 $server = "addc";
2805         }
2806         if (!defined($dom)) {
2807                 $dom = "addom.samba.example.com";
2808         }
2809         my $env = $self->provision_ad_dc($path, $server, "ADDOMAIN",
2810                                          $dom,
2811                                          undef,
2812                                          $conf_opts,
2813                                          undef,
2814                                          $functional_level);
2815         unless ($env) {
2816                 return undef;
2817         }
2818
2819         if (not defined($self->check_or_start($env, "prefork"))) {
2820             return undef;
2821         }
2822
2823         my $upn_array = ["$env->{REALM}.upn"];
2824         my $spn_array = ["$env->{REALM}.spn"];
2825
2826         if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) {
2827                 return undef;
2828         }
2829
2830         return $env;
2831 }
2832
2833 sub setup_ad_dc
2834 {
2835         my ($self, $path) = @_;
2836         return _setup_ad_dc($self, $path, undef, undef, undef);
2837 }
2838
2839 sub setup_ad_dc_smb1
2840 {
2841         my ($self, $path) = @_;
2842         my $conf_opts = "
2843 [global]
2844         client min protocol = CORE
2845         server min protocol = LANMAN1
2846
2847         # needed for 'samba.tests.auth_log' tests
2848         server require schannel:ADDCSMB1\$ = no
2849         server schannel require seal:ADDCSMB1\$ = no
2850 ";
2851         return _setup_ad_dc($self, $path, $conf_opts, "addcsmb1", "addom2.samba.example.com");
2852 }
2853
2854 sub setup_ad_dc_smb1_done
2855 {
2856         my ($self, $path, $dep_env) = @_;
2857         return $self->return_alias_env($path, $dep_env);
2858 }
2859
2860 sub setup_ad_dc_no_nss
2861 {
2862         my ($self, $path) = @_;
2863
2864         # If we didn't build with ADS, pretend this env was never available
2865         if (not $self->{target3}->have_ads()) {
2866                return "UNKNOWN";
2867         }
2868
2869         my $env = $self->provision_ad_dc($path,
2870                                          "addc_no_nss",
2871                                          "ADNONSSDOMAIN",
2872                                          "adnonssdom.samba.example.com",
2873                                          undef,
2874                                          "",
2875                                          undef);
2876         unless ($env) {
2877                 return undef;
2878         }
2879
2880         $env->{NSS_WRAPPER_MODULE_SO_PATH} = undef;
2881         $env->{NSS_WRAPPER_MODULE_FN_PREFIX} = undef;
2882
2883         if (not defined($self->check_or_start($env, "single"))) {
2884             return undef;
2885         }
2886
2887         my $upn_array = ["$env->{REALM}.upn"];
2888         my $spn_array = ["$env->{REALM}.spn"];
2889
2890         if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) {
2891                 return undef;
2892         }
2893
2894         return $env;
2895 }
2896
2897 sub setup_ad_dc_no_ntlm
2898 {
2899         my ($self, $path) = @_;
2900
2901         # If we didn't build with ADS, pretend this env was never available
2902         if (not $self->{target3}->have_ads()) {
2903                return "UNKNOWN";
2904         }
2905
2906         my $env = $self->provision_ad_dc($path,
2907                                          "addc_no_ntlm",
2908                                          "ADNONTLMDOMAIN",
2909                                          "adnontlmdom.samba.example.com",
2910                                          undef,
2911                                          "ntlm auth = disabled\nnt hash store = never",
2912                                          undef);
2913         unless ($env) {
2914                 return undef;
2915         }
2916
2917         if (not defined($self->check_or_start($env, "prefork"))) {
2918             return undef;
2919         }
2920
2921         my $upn_array = ["$env->{REALM}.upn"];
2922         my $spn_array = ["$env->{REALM}.spn"];
2923
2924         if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) {
2925                 return undef;
2926         }
2927
2928         return $env;
2929 }
2930
2931 sub setup_ad_dc_fips
2932 {
2933         my ($self, $path) = @_;
2934
2935         # If we didn't build with ADS, pretend this env was never available
2936         if (not $self->{target3}->have_ads()) {
2937                return "UNKNOWN";
2938         }
2939
2940         my $env = $self->provision_ad_dc($path,
2941                                          "fipsdc",
2942                                          "FIPSDOMAIN",
2943                                          "fips.samba.example.com",
2944                                          1,
2945                                          "",
2946                                          undef);
2947         unless ($env) {
2948                 return undef;
2949         }
2950
2951         if (not defined($self->check_or_start($env, "prefork"))) {
2952             return undef;
2953         }
2954
2955         my $upn_array = ["$env->{REALM}.upn"];
2956         my $spn_array = ["$env->{REALM}.spn"];
2957
2958         if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) {
2959                 return undef;
2960         }
2961
2962         return $env;
2963 }
2964
2965 #
2966 # AD DC test environment used solely to test pre-fork process restarts.
2967 # As processes get killed off and restarted it should not be used for other
2968 sub setup_preforkrestartdc
2969 {
2970         my ($self, $path) = @_;
2971
2972         # If we didn't build with ADS, pretend this env was never available
2973         if (not $self->{target3}->have_ads()) {
2974                return "UNKNOWN";
2975         }
2976
2977         # note DC name must be <= 15 chars so we use 'prockill' instead of
2978         # 'preforkrestart'
2979         my $env = $self->provision_ad_dc($path,
2980                                          "prockilldc",
2981                                          "PROCKILLDOMAIN",
2982                                          "prockilldom.samba.example.com",
2983                                          undef,
2984                                          "prefork backoff increment = 5\nprefork maximum backoff=10",
2985                                          undef);
2986         unless ($env) {
2987                 return undef;
2988         }
2989
2990         # We treat processes in this environment cruelly, sometimes
2991         # sending them SIGSEGV signals. We don't need gdb_backtrace
2992         # dissecting these fake crashes in precise detail.
2993         $env->{PLEASE_NO_GDB_BACKTRACE} = '1';
2994
2995         $env->{NSS_WRAPPER_MODULE_SO_PATH} = undef;
2996         $env->{NSS_WRAPPER_MODULE_FN_PREFIX} = undef;
2997
2998         if (not defined($self->check_or_start($env, "prefork"))) {
2999             return undef;
3000         }
3001
3002         my $upn_array = ["$env->{REALM}.upn"];
3003         my $spn_array = ["$env->{REALM}.spn"];
3004
3005         if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) {
3006                 return undef;
3007         }
3008
3009         return $env;
3010 }
3011
3012 #
3013 # ad_dc test environment used solely to test standard process model connection
3014 # process limits. As the limit is set artificially low it should not be used
3015 # for other tests.
3016 sub setup_proclimitdc
3017 {
3018         my ($self, $path) = @_;
3019
3020         # If we didn't build with ADS, pretend this env was never available
3021         if (not $self->{target3}->have_ads()) {
3022                return "UNKNOWN";
3023         }
3024
3025         my $env = $self->provision_ad_dc($path,
3026                                          "proclimitdc",
3027                                          "PROCLIMITDOM",
3028                                          "proclimit.samba.example.com",
3029                                          undef,
3030                                          "max smbd processes = 20",
3031                                          undef);
3032         unless ($env) {
3033                 return undef;
3034         }
3035
3036         $env->{NSS_WRAPPER_MODULE_SO_PATH} = undef;
3037         $env->{NSS_WRAPPER_MODULE_FN_PREFIX} = undef;
3038
3039         if (not defined($self->check_or_start($env, "standard"))) {
3040             return undef;
3041         }
3042
3043         my $upn_array = ["$env->{REALM}.upn"];
3044         my $spn_array = ["$env->{REALM}.spn"];
3045
3046         if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) {
3047                 return undef;
3048         }
3049
3050         return $env;
3051 }
3052
3053 # Used to test a live upgrade of the schema on a 2 DC network.
3054 sub setup_schema_dc
3055 {
3056         my ($self, $path) = @_;
3057
3058         # provision the PDC using an older base schema
3059         my $provision_args = ["--base-schema=2008_R2", "--backend-store=$self->{default_ldb_backend}"];
3060
3061         # We set the functional level to 2008_R2 to match the older
3062         # base-schema (to allow schema upgrade to be tested)
3063         my $env = $self->provision_ad_dc($path,
3064                                          "liveupgrade1dc",
3065                                          "SCHEMADOMAIN",
3066                                          "schema.samba.example.com",
3067                                          undef,
3068                                          "drs: max link sync = 2",
3069                                          $provision_args,
3070                                          "2008_R2");
3071         unless ($env) {
3072                 return undef;
3073         }
3074
3075         if (not defined($self->check_or_start($env, "prefork"))) {
3076             return undef;
3077         }
3078
3079         my $upn_array = ["$env->{REALM}.upn"];
3080         my $spn_array = ["$env->{REALM}.spn"];
3081
3082         if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) {
3083                 return undef;
3084         }
3085
3086         return $env;
3087 }
3088
3089 # the second DC in the live schema upgrade pair
3090 sub setup_schema_pair_dc
3091 {
3092         # note: dcvars contains the env info for the dependent testenv ('schema_dc')
3093         my ($self, $prefix, $dcvars) = @_;
3094         print "Preparing SCHEMA UPGRADE PAIR DC...\n";
3095
3096         my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "liveupgrade2dc",
3097                                                     $dcvars->{DOMAIN},
3098                                                     $dcvars->{REALM},
3099                                                     $dcvars->{PASSWORD},
3100                                                     "");
3101
3102         my $samba_tool =  Samba::bindir_path($self, "samba-tool");
3103         my $cmd_vars = $self->get_cmd_env_vars($env);
3104
3105         my $join_cmd = $cmd_vars;
3106         $join_cmd .= "$samba_tool domain join $env->{CONFIGURATION} $dcvars->{REALM} DC --realm=$dcvars->{REALM}";
3107         $join_cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD} ";
3108         $join_cmd .= " --backend-store=$self->{default_ldb_backend}";
3109
3110         my $upgrade_cmd = $cmd_vars;
3111         $upgrade_cmd .= "$samba_tool domain schemaupgrade $dcvars->{CONFIGURATION}";
3112         $upgrade_cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}";
3113
3114         my $repl_cmd = $cmd_vars;
3115         $repl_cmd .= "$samba_tool drs replicate $env->{SERVER} $dcvars->{SERVER}";
3116         $repl_cmd .= " CN=Schema,CN=Configuration,DC=schema,DC=samba,DC=example,DC=com";
3117         $repl_cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
3118
3119         unless (system($join_cmd) == 0) {
3120                 warn("Join failed\n$join_cmd");
3121                 return undef;
3122         }
3123
3124         $env->{DC_SERVER} = $dcvars->{SERVER};
3125         $env->{DC_SERVER_IP} = $dcvars->{SERVER_IP};
3126         $env->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6};
3127         $env->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME};
3128
3129         # start samba for the new DC
3130         if (not defined($self->check_or_start($env, "standard"))) {
3131             return undef;
3132         }
3133
3134         unless (system($upgrade_cmd) == 0) {
3135                 warn("Schema upgrade failed\n$upgrade_cmd");
3136                 return undef;
3137         }
3138
3139         unless (system($repl_cmd) == 0) {
3140                 warn("Post-update schema replication failed\n$repl_cmd");
3141                 return undef;
3142         }
3143
3144         return $env;
3145 }
3146
3147 # Sets up a DC that's solely used to do a domain backup from. We then use the
3148 # backupfrom-DC to create the restore-DC - this proves that the backup/restore
3149 # process will create a Samba DC that will actually start up.
3150 # We don't use the backup-DC for anything else because its domain will conflict
3151 # with the restore DC.
3152 sub setup_backupfromdc
3153 {
3154         my ($self, $path) = @_;
3155
3156         # If we didn't build with ADS, pretend this env was never available
3157         if (not $self->{target3}->have_ads()) {
3158                return "UNKNOWN";
3159         }
3160
3161         my $provision_args = ["--site=Backup-Site"];
3162
3163         my $env = $self->provision_ad_dc($path,
3164                                          "backupfromdc",
3165                                          "BACKUPDOMAIN",
3166                                          "backupdom.samba.example.com",
3167                                          undef,
3168                                          "samba kcc command = /bin/true",
3169                                          $provision_args);
3170         unless ($env) {
3171                 return undef;
3172         }
3173
3174         if (not defined($self->check_or_start($env))) {
3175             return undef;
3176         }
3177
3178         my $upn_array = ["$env->{REALM}.upn"];
3179         my $spn_array = ["$env->{REALM}.spn"];
3180
3181         if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) {
3182                 return undef;
3183         }
3184
3185         # Set up a dangling forward link to an expunged object
3186         #
3187         # We need this to ensure that the "samba-tool domain backup rename"
3188         # that is part of the creation of the labdc environment can
3189         # cope with this situation on the source DC.
3190
3191         if (not $self->write_ldb_file("$env->{PRIVATEDIR}/sam.ldb", "
3192 dn: ou=linktest,dc=backupdom,dc=samba,dc=example,dc=com
3193 objectclass: organizationalUnit
3194 -
3195
3196 dn: cn=linkto,ou=linktest,dc=backupdom,dc=samba,dc=example,dc=com
3197 objectclass: msExchConfigurationContainer
3198 -
3199
3200 dn: cn=linkfrom,ou=linktest,dc=backupdom,dc=samba,dc=example,dc=com
3201 objectclass: msExchConfigurationContainer
3202 addressBookRoots: cn=linkto,ou=linktest,dc=backupdom,dc=samba,dc=example,dc=com
3203 -
3204
3205 ")) {
3206             return undef;
3207         }
3208         my $ldbdel = Samba::bindir_path($self, "ldbdel");
3209         my $cmd = "$ldbdel -H $env->{PRIVATEDIR}/sam.ldb cn=linkto,ou=linktest,dc=backupdom,dc=samba,dc=example,dc=com";
3210
3211         unless(system($cmd) == 0) {
3212                 warn("Failed to delete link target: \n$cmd");
3213                 return undef;
3214         }
3215
3216         # Expunge will ensure that linkto is totally wiped from the DB
3217         my $samba_tool = Samba::bindir_path($self, "samba-tool");
3218         $cmd = "$samba_tool  domain tombstones expunge --tombstone-lifetime=0 $env->{CONFIGURATION}";
3219
3220         unless(system($cmd) == 0) {
3221                 warn("Failed to expunge link target: \n$cmd");
3222                 return undef;
3223         }
3224         return $env;
3225 }
3226
3227 # returns the server/user-auth params needed to run an online backup cmd
3228 sub get_backup_server_args
3229 {
3230         # dcvars contains the env info for the backup DC testenv
3231         my ($self, $dcvars) = @_;
3232         my $server = $dcvars->{DC_SERVER_IP};
3233         my $server_args = "--server=$server ";
3234         $server_args .= "-U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
3235         $server_args .= " $dcvars->{CONFIGURATION}";
3236
3237         return $server_args;
3238 }
3239
3240 # Creates a backup of a running testenv DC
3241 sub create_backup
3242 {
3243         # note: dcvars contains the env info for the backup DC testenv
3244         my ($self, $env, $dcvars, $backupdir, $backup_cmd) = @_;
3245
3246         # get all the env variables we pass in with the samba-tool command
3247         # Note: use the backupfrom-DC's krb5.conf to do the backup
3248         my $overwrite = undef;
3249         $overwrite->{KRB5_CONFIG} = $dcvars->{KRB5_CONFIG};
3250         my $cmd_env = $self->get_cmd_env_vars($env, $overwrite);
3251
3252         # use samba-tool to create a backup from the 'backupfromdc' DC
3253         my $cmd = "";
3254         my $samba_tool = Samba::bindir_path($self, "samba-tool");
3255
3256         $cmd .= "$cmd_env $samba_tool domain backup $backup_cmd";
3257         $cmd .= " --targetdir=$backupdir";
3258
3259         print "Executing: $cmd\n";
3260         unless(system($cmd) == 0) {
3261                 warn("Failed to create backup using: \n$cmd");
3262                 return undef;
3263         }
3264
3265         # get the name of the backup file created
3266         opendir(DIR, $backupdir);
3267         my @files = grep(/\.tar/, readdir(DIR));
3268         closedir(DIR);
3269
3270         if(scalar @files != 1) {
3271                 warn("Backup file not found in directory $backupdir\n");
3272                 return undef;
3273         }
3274         my $backup_file = "$backupdir/$files[0]";
3275         print "Using backup file $backup_file...\n";
3276
3277         return $backup_file;
3278 }
3279
3280 # Restores a backup-file to populate a testenv for a new DC
3281 sub restore_backup_file
3282 {
3283         my ($self, $backup_file, $restore_opts, $restoredir, $smbconf) = @_;
3284
3285         # pass the restore command the testenv's smb.conf that we've already
3286         # generated. But move it to a temp-dir first, so that the restore doesn't
3287         # overwrite it
3288         my $tmpdir = File::Temp->newdir();
3289         my $tmpconf = "$tmpdir/smb.conf";
3290         my $cmd = "cp $smbconf $tmpconf";
3291         unless(system($cmd) == 0) {
3292                 warn("Failed to backup smb.conf using: \n$cmd");
3293                 return -1;
3294         }
3295
3296         my $samba_tool = Samba::bindir_path($self, "samba-tool");
3297         $cmd = "$samba_tool domain backup restore --backup-file=$backup_file";
3298         $cmd .= " --targetdir=$restoredir $restore_opts --configfile=$tmpconf";
3299
3300         print "Executing: $cmd\n";
3301         unless(system($cmd) == 0) {
3302                 warn("Failed to restore backup using: \n$cmd");
3303                 return -1;
3304         }
3305
3306         print "Restore complete\n";
3307         return 0
3308 }
3309
3310 # sets up the initial directory and returns the new testenv's env info
3311 # (without actually doing a 'domain join')
3312 sub prepare_dc_testenv
3313 {
3314         my ($self, $prefix, $dcname, $domain, $realm,
3315                 $password, $conf_options, $dnsupdate_options) = @_;
3316
3317         my $ctx = $self->provision_raw_prepare($prefix, "domain controller",
3318                                                $dcname,
3319                                                $domain,
3320                                                $realm,
3321                                                undef,
3322                                                "2008",
3323                                                $password,
3324                                                undef,
3325                                                undef);
3326
3327         # the restore uses a slightly different state-dir location to other testenvs
3328         $ctx->{statedir} = "$ctx->{prefix_abs}/state";
3329         push(@{$ctx->{directories}}, "$ctx->{statedir}");
3330
3331         # add support for sysvol/netlogon/tmp shares
3332         $ctx->{share} = "$ctx->{prefix_abs}/share";
3333         push(@{$ctx->{directories}}, "$ctx->{share}");
3334         push(@{$ctx->{directories}}, "$ctx->{share}/test1");
3335
3336         if (defined($dnsupdate_options)) {
3337                 $ctx->{samba_dnsupdate} .= $dnsupdate_options;
3338         }
3339
3340         $ctx->{smb_conf_extra_options} = "
3341         $conf_options
3342
3343         # Some of the DCs based on this will be in FL 2016 domains, so
3344         # claim FL 2016 DC capability
3345         ad dc functional level = 2016
3346
3347         max xmit = 32K
3348         server max protocol = SMB2
3349         samba kcc command = /bin/true
3350         xattr_tdb:file = $ctx->{statedir}/xattr.tdb
3351
3352 [sysvol]
3353         path = $ctx->{statedir}/sysvol
3354         read only = no
3355
3356 [netlogon]
3357         path = $ctx->{statedir}/sysvol/$ctx->{dnsname}/scripts
3358         read only = no
3359
3360 [tmp]
3361         path = $ctx->{share}
3362         read only = no
3363         posix:sharedelay = 10000
3364         posix:oplocktimeout = 3
3365         posix:writetimeupdatedelay = 50000
3366
3367 [test1]
3368         path = $ctx->{share}/test1
3369         read only = no
3370         posix:sharedelay = 100000
3371         posix:oplocktimeout = 3
3372         posix:writetimeupdatedelay = 500000
3373 ";
3374
3375         my $env = $self->provision_raw_step1($ctx);
3376
3377     return ($env, $ctx);
3378 }
3379
3380
3381 # Set up a DC testenv solely by using the samba-tool domain backup/restore
3382 # commands. This proves that we can backup an online DC ('backupfromdc') and
3383 # use the backup file to create a valid, working samba DC.
3384 sub setup_restoredc
3385 {
3386         # note: dcvars contains the env info for the dependent testenv ('backupfromdc')
3387         my ($self, $prefix, $dcvars) = @_;
3388         print "Preparing RESTORE DC...\n";
3389
3390         # we arbitrarily designate the restored DC as having SMBv1 disabled
3391         my $extra_conf = "
3392         server min protocol = SMB2
3393         client min protocol = SMB2
3394         prefork children = 1";
3395         my $dnsupdate_options = " --use-samba-tool --no-credentials";
3396
3397         my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "restoredc",
3398                                                     $dcvars->{DOMAIN},
3399                                                     $dcvars->{REALM},
3400                                                     $dcvars->{PASSWORD},
3401                                                     $extra_conf,
3402                                                     $dnsupdate_options);
3403
3404         # create a backup of the 'backupfromdc'
3405         my $backupdir = File::Temp->newdir();
3406         my $server_args = $self->get_backup_server_args($dcvars);
3407         my $backup_args = "online $server_args";
3408         my $backup_file = $self->create_backup($env, $dcvars, $backupdir,
3409                                                $backup_args);
3410         unless($backup_file) {
3411                 return undef;
3412         }
3413
3414         # restore the backup file to populate the restore-DC testenv
3415         my $restore_dir = abs_path($prefix);
3416         my $ret = $self->restore_backup_file($backup_file,
3417                                              "--newservername=$env->{SERVER}",
3418                                              $restore_dir, $env->{SERVERCONFFILE});
3419         unless ($ret == 0) {
3420                 return undef;
3421         }
3422
3423         #
3424         # As we create the same domain as a clone
3425         # we need a separate resolv.conf!
3426         #
3427         $ctx->{resolv_conf} = "$ctx->{etcdir}/resolv.conf";
3428         $ctx->{dns_ipv4} = $ctx->{ipv4};
3429         $ctx->{dns_ipv6} = $ctx->{ipv6};
3430         Samba::mk_resolv_conf($ctx);
3431         $env->{RESOLV_CONF} = $ctx->{resolv_conf};
3432
3433         # start samba for the restored DC
3434         if (not defined($self->check_or_start($env))) {
3435             return undef;
3436         }
3437
3438         return $env;
3439 }
3440
3441 # Set up a DC testenv solely by using the 'samba-tool domain backup rename' and
3442 # restore commands. This proves that we can backup and rename an online DC
3443 # ('backupfromdc') and use the backup file to create a valid, working samba DC.
3444 sub setup_renamedc
3445 {
3446         # note: dcvars contains the env info for the dependent testenv ('backupfromdc')
3447         my ($self, $prefix, $dcvars) = @_;
3448         print "Preparing RENAME DC...\n";
3449         my $extra_conf = "prefork children = 1";
3450
3451         my $realm = "renamedom.samba.example.com";
3452         my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "renamedc",
3453                                                     "RENAMEDOMAIN", $realm,
3454                                                     $dcvars->{PASSWORD}, $extra_conf);
3455
3456         # create a backup of the 'backupfromdc' which renames the domain
3457         my $backupdir = File::Temp->newdir();
3458         my $server_args = $self->get_backup_server_args($dcvars);
3459         my $backup_args = "rename $env->{DOMAIN} $env->{REALM} $server_args";
3460         $backup_args .= " --backend-store=tdb";
3461         my $backup_file = $self->create_backup($env, $dcvars, $backupdir,
3462                                                $backup_args);
3463         unless($backup_file) {
3464                 return undef;
3465         }
3466
3467         # restore the backup file to populate the rename-DC testenv
3468         my $restore_dir = abs_path($prefix);
3469         my $restore_opts =  "--newservername=$env->{SERVER} --host-ip=$env->{SERVER_IP}";
3470         my $ret = $self->restore_backup_file($backup_file, $restore_opts,
3471                                              $restore_dir, $env->{SERVERCONFFILE});
3472         unless ($ret == 0) {
3473                 return undef;
3474         }
3475
3476         # start samba for the restored DC
3477         if (not defined($self->check_or_start($env))) {
3478             return undef;
3479         }
3480
3481         my $upn_array = ["$env->{REALM}.upn"];
3482         my $spn_array = ["$env->{REALM}.spn"];
3483
3484         if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) {
3485                 return undef;
3486         }
3487
3488         return $env;
3489 }
3490
3491 # Set up a DC testenv solely by using the 'samba-tool domain backup offline' and
3492 # restore commands. This proves that we do an offline backup of a local DC
3493 # ('backupfromdc') and use the backup file to create a valid, working samba DC.
3494 sub setup_offlinebackupdc
3495 {
3496         # note: dcvars contains the env info for the dependent testenv ('backupfromdc')
3497         my ($self, $prefix, $dcvars) = @_;
3498         print "Preparing OFFLINE BACKUP DC...\n";
3499         my $extra_conf = "prefork children = 1";
3500         my $dnsupdate_options = " --use-samba-tool --no-credentials";
3501
3502         my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "offlinebackupdc",
3503                                                     $dcvars->{DOMAIN},
3504                                                     $dcvars->{REALM},
3505                                                     $dcvars->{PASSWORD},
3506                                                     $extra_conf,
3507                                                     $dnsupdate_options);
3508
3509         # create an offline backup of the 'backupfromdc' target
3510         my $backupdir = File::Temp->newdir();
3511         my $cmd = "offline --configfile $dcvars->{SERVERCONFFILE}";
3512         my $backup_file = $self->create_backup($env, $dcvars,
3513                                                $backupdir, $cmd);
3514
3515         unless($backup_file) {
3516                 return undef;
3517         }
3518
3519         # restore the backup file to populate the rename-DC testenv
3520         my $restore_dir = abs_path($prefix);
3521         my $restore_opts =  "--newservername=$env->{SERVER} --host-ip=$env->{SERVER_IP}";
3522         my $ret = $self->restore_backup_file($backup_file, $restore_opts,
3523                                              $restore_dir, $env->{SERVERCONFFILE});
3524         unless ($ret == 0) {
3525                 return undef;
3526         }
3527
3528         #
3529         # As we create the same domain as a clone
3530         # we need a separate resolv.conf!
3531         #
3532         $ctx->{resolv_conf} = "$ctx->{etcdir}/resolv.conf";
3533         $ctx->{dns_ipv4} = $ctx->{ipv4};
3534         $ctx->{dns_ipv6} = $ctx->{ipv6};
3535         Samba::mk_resolv_conf($ctx);
3536         $env->{RESOLV_CONF} = $ctx->{resolv_conf};
3537
3538         # re-create the testenv's krb5.conf (the restore may have overwritten it)
3539         Samba::mk_krb5_conf($ctx);
3540
3541         # start samba for the restored DC
3542         if (not defined($self->check_or_start($env))) {
3543             return undef;
3544         }
3545
3546         return $env;
3547 }
3548
3549 # Set up a DC testenv solely by using the samba-tool 'domain backup rename' and
3550 # restore commands, using the --no-secrets option. This proves that we can
3551 # create a realistic lab environment from an online DC ('backupfromdc').
3552 sub setup_labdc
3553 {
3554         # note: dcvars contains the env info for the dependent testenv ('backupfromdc')
3555         my ($self, $prefix, $dcvars) = @_;
3556         print "Preparing LAB-DOMAIN DC...\n";
3557         my $extra_conf = "prefork children = 1";
3558
3559         my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "labdc",
3560                                                     "LABDOMAIN",
3561                                                     "labdom.samba.example.com",
3562                                                     $dcvars->{PASSWORD}, $extra_conf);
3563
3564         # create a backup of the 'backupfromdc' which renames the domain and uses
3565         # the --no-secrets option to scrub any sensitive info
3566         my $backupdir = File::Temp->newdir();
3567         my $server_args = $self->get_backup_server_args($dcvars);
3568         my $backup_args = "rename $env->{DOMAIN} $env->{REALM} $server_args";
3569         $backup_args .= " --no-secrets --backend-store=$self->{default_ldb_backend}";
3570         my $backup_file = $self->create_backup($env, $dcvars, $backupdir,
3571                                                $backup_args);
3572         unless($backup_file) {
3573                 return undef;
3574         }
3575
3576         # restore the backup file to populate the lab-DC testenv
3577         my $restore_dir = abs_path($prefix);
3578         my $restore_opts =  "--newservername=$env->{SERVER} --host-ip=$env->{SERVER_IP}";
3579         my $ret = $self->restore_backup_file($backup_file, $restore_opts,
3580                                              $restore_dir, $env->{SERVERCONFFILE});
3581         unless ($ret == 0) {
3582                 return undef;
3583         }
3584
3585         # because we don't include any secrets in the backup, we need to reset the
3586         # admin user's password back to what the testenv expects
3587         my $samba_tool = Samba::bindir_path($self, "samba-tool");
3588         my $cmd = "$samba_tool user setpassword $env->{USERNAME} ";
3589         $cmd .= "--newpassword=$env->{PASSWORD} -H $restore_dir/private/sam.ldb";
3590         $cmd .= " $env->{CONFIGURATION}";
3591
3592         unless(system($cmd) == 0) {
3593                 warn("Failed to reset admin's password: \n$cmd");
3594                 return undef;
3595         }
3596
3597         # start samba for the restored DC
3598         if (not defined($self->check_or_start($env))) {
3599             return undef;
3600         }
3601
3602         my $upn_array = ["$env->{REALM}.upn"];
3603         my $spn_array = ["$env->{REALM}.spn"];
3604
3605         if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) {
3606                 return undef;
3607         }
3608
3609         return $env;
3610 }
3611
3612 # Inspects a backup *.tar.bz2 file and determines the realm/domain it contains
3613 sub get_backup_domain_realm
3614 {
3615         my ($self, $backup_file) = @_;
3616
3617         print "Determining REALM/DOMAIN values in backup...\n";
3618
3619         # The backup will have the correct domain/realm values in the smb.conf.
3620         # So we can work out the env variables the testenv should use based on
3621         # that. Let's start by extracting the smb.conf
3622         my $tar = Archive::Tar->new($backup_file);
3623         my $tmpdir = File::Temp->newdir();
3624         my $smbconf = "$tmpdir/smb.conf";
3625
3626         # note that the filepaths within the tar-file differ slightly for online
3627         # and offline backups
3628         if ($tar->contains_file("etc/smb.conf")) {
3629                 $tar->extract_file("etc/smb.conf", $smbconf);
3630         } elsif ($tar->contains_file("./etc/smb.conf")) {
3631                 $tar->extract_file("./etc/smb.conf", $smbconf);
3632         } else {
3633                 warn("Could not find smb.conf in $backup_file");
3634                 return undef, undef;
3635         }
3636
3637         # make sure we don't try to create locks/sockets in the default install
3638         # location (i.e. /usr/local/samba/)
3639         my $options = "--option=\"private dir = $tmpdir\"";
3640         $options .=  " --option=\"lock dir = $tmpdir\"";
3641
3642         # now use testparm to read the values we're interested in
3643         my $testparm = Samba::bindir_path($self, "testparm");
3644         my $domain = `$testparm $smbconf -sl --parameter-name=WORKGROUP $options`;
3645         my $realm = `$testparm $smbconf -sl --parameter-name=REALM $options`;
3646         chomp $realm;
3647         chomp $domain;
3648         print "Backup-file REALM is $realm, DOMAIN is $domain\n";
3649
3650         return ($domain, $realm);
3651 }
3652
3653 # This spins up a custom testenv that can be based on any backup-file you want.
3654 # This is just intended for manual testing (rather than automated test-cases)
3655 sub setup_customdc
3656 {
3657         my ($self, $prefix) = @_;
3658         print "Preparing CUSTOM RESTORE DC...\n";
3659         my $dc_name = "customdc";
3660         my $password = "locDCpass1";
3661         my $backup_file = $ENV{'BACKUP_FILE'};
3662         my $dnsupdate_options = " --use-samba-tool --no-credentials";
3663
3664         # user must specify a backup file to restore via an ENV variable, i.e.
3665         # BACKUP_FILE=backup-blah.tar.bz2 SELFTEST_TESTENV=customdc make testenv
3666         if (not defined($backup_file)) {
3667                 warn("Please specify BACKUP_FILE");
3668                 return undef;
3669         }
3670
3671         # work out the correct domain/realm env values from the backup-file
3672         my ($domain, $realm) = $self->get_backup_domain_realm($backup_file);
3673         if ($domain eq '' or $realm eq '') {
3674                 warn("Could not determine domain or realm");
3675                 return undef;
3676         }
3677
3678         # create a placeholder directory and smb.conf, as well as the env vars.
3679         my ($env, $ctx) = $self->prepare_dc_testenv($prefix, $dc_name,
3680                                                     $domain, $realm, $password, "",
3681                                                     $dnsupdate_options);
3682
3683         # restore the specified backup file to populate the testenv
3684         my $restore_dir = abs_path($prefix);
3685         my $ret = $self->restore_backup_file($backup_file,
3686                                              "--newservername=$env->{SERVER}",
3687                                              $restore_dir, $env->{SERVERCONFFILE});
3688         unless ($ret == 0) {
3689                 return undef;
3690         }
3691
3692         #
3693         # As we create the same domain as a clone
3694         # we need a separate resolv.conf!
3695         #
3696         $ctx->{resolv_conf} = "$ctx->{etcdir}/resolv.conf";
3697         $ctx->{dns_ipv4} = $ctx->{ipv4};
3698         $ctx->{dns_ipv6} = $ctx->{ipv6};
3699         Samba::mk_resolv_conf($ctx);
3700         $env->{RESOLV_CONF} = $ctx->{resolv_conf};
3701
3702         # Change the admin password to the testenv default, just in case it's
3703         # different, or in case this was a --no-secrets backup
3704         my $samba_tool = Samba::bindir_path($self, "samba-tool");
3705         my $cmd = "$samba_tool user setpassword $env->{USERNAME} ";
3706         $cmd .= "--newpassword=$password -H $restore_dir/private/sam.ldb";
3707         $cmd .= " $env->{CONFIGURATION}";
3708
3709         unless(system($cmd) == 0) {
3710                 warn("Failed to reset admin's password: \n$cmd");
3711                 return undef;
3712         }
3713
3714         # re-create the testenv's krb5.conf (the restore may have overwritten it,
3715         # if the backup-file was an offline backup)
3716         Samba::mk_krb5_conf($ctx);
3717
3718         # start samba for the restored DC
3719         if (not defined($self->check_or_start($env))) {
3720             return undef;
3721         }
3722
3723         # if this was a backup-rename, then we may need to setup namespaces
3724         my $upn_array = ["$env->{REALM}.upn"];
3725         my $spn_array = ["$env->{REALM}.spn"];
3726
3727         return $env;
3728 }
3729
3730 sub setup_none
3731 {
3732         my ($self, $path) = @_;
3733
3734         my $ret = {
3735                 KRB5_CONFIG => abs_path($path) . "/no_krb5.conf",
3736                 SAMBA_PID => -1,
3737         }
3738 }
3739
3740 1;