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