diff options
Diffstat (limited to 'selftest/target/Samba4.pm')
-rwxr-xr-x | selftest/target/Samba4.pm | 3743 |
1 files changed, 3743 insertions, 0 deletions
diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm new file mode 100755 index 0000000..2d449e4 --- /dev/null +++ b/selftest/target/Samba4.pm @@ -0,0 +1,3743 @@ +#!/usr/bin/perl +# Bootstrap Samba and run a number of tests against it. +# Copyright (C) 2005-2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU GPL, v3 or later. + +# NOTE: Refer to the README for more details about the various testenvs, +# and tips about adding new testenvs. + +package Samba4; + +use strict; +use warnings; +use Cwd qw(abs_path); +use FindBin qw($RealBin); +use POSIX; +use SocketWrapper; +use target::Samba; +use target::Samba3; +use Archive::Tar; + +sub new($$$$$) { + my ($classname, $SambaCtx, $bindir, $srcdir, $server_maxtime, $default_ldb_backend) = @_; + + my $self = { + vars => {}, + SambaCtx => $SambaCtx, + bindir => $bindir, + srcdir => $srcdir, + server_maxtime => $server_maxtime, + target3 => new Samba3($SambaCtx, $bindir, $srcdir, $server_maxtime), + default_ldb_backend => $default_ldb_backend, + }; + bless $self; + return $self; +} + +sub scriptdir_path($$) { + my ($self, $path) = @_; + return "$self->{srcdir}/source4/scripting/$path"; +} + +sub check_or_start($$$) +{ + my ($self, $env_vars, $process_model) = @_; + my $STDIN_READER; + + my $env_ok = $self->check_env($env_vars); + if ($env_ok) { + return $env_vars->{SAMBA_PID}; + } elsif (defined($env_vars->{SAMBA_PID})) { + warn("SAMBA PID $env_vars->{SAMBA_PID} is not running (died)"); + return undef; + } + + # use a pipe for stdin in the child processes. This allows + # those processes to monitor the pipe for EOF to ensure they + # exit when the test script exits + pipe($STDIN_READER, $env_vars->{STDIN_PIPE}); + + # build up the command to run samba + my @preargs = (); + my @optargs = (); + if (defined($ENV{SAMBA_OPTIONS})) { + @optargs = split(/ /, $ENV{SAMBA_OPTIONS}); + } + if(defined($ENV{SAMBA_VALGRIND})) { + @preargs = split(/ /,$ENV{SAMBA_VALGRIND}); + } + + if (defined($process_model)) { + push @optargs, ("-M", $process_model); + } + my $binary = Samba::bindir_path($self, "samba"); + my @full_cmd = (@preargs, $binary, "-i", + "--no-process-group", "--maximum-runtime=$self->{server_maxtime}", + $env_vars->{CONFIGURATION}, @optargs); + + # the samba process takes some additional env variables (compared to s3) + my $samba_envs = Samba::get_env_for_process("samba", $env_vars); + if (defined($ENV{MITKRB5})) { + $samba_envs->{KRB5_KDC_PROFILE} = $env_vars->{MITKDC_CONFIG}; + } + + # fork a child process and exec() samba + my $daemon_ctx = { + NAME => "samba", + BINARY_PATH => $binary, + FULL_CMD => [ @full_cmd ], + LOG_FILE => $env_vars->{SAMBA_TEST_LOG}, + TEE_STDOUT => 1, + PCAP_FILE => "env-$ENV{ENVNAME}-samba", + ENV_VARS => $samba_envs, + }; + my $pid = Samba::fork_and_exec($self, $env_vars, $daemon_ctx, $STDIN_READER); + + $env_vars->{SAMBA_PID} = $pid; + + # close the parent's read-end of the pipe + close($STDIN_READER); + + if ($self->wait_for_start($env_vars) != 0) { + warn("Samba $pid failed to start up"); + return undef; + } + + return $pid; +} + +sub wait_for_start($$) +{ + my ($self, $testenv_vars) = @_; + my $count = 0; + my $ret = 0; + + if (not $self->check_env($testenv_vars)) { + warn("unable to confirm Samba $testenv_vars->{SAMBA_PID} is running"); + return -1; + } + + # This will return quickly when things are up, but be slow if we + # need to wait for (eg) SSL init + my $nmblookup = Samba::bindir_path($self, "nmblookup4"); + + do { + $ret = system("$nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{SERVER}"); + if ($ret != 0) { + sleep(1); + } else { + system("$nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{SERVER}"); + system("$nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{NETBIOSNAME}"); + system("$nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{NETBIOSNAME}"); + system("$nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{NETBIOSNAME}"); + system("$nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{NETBIOSNAME}"); + system("$nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{SERVER}"); + system("$nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{SERVER}"); + system("$nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{NETBIOSNAME}"); + system("$nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{NETBIOSNAME}"); + system("$nmblookup $testenv_vars->{CONFIGURATION} $testenv_vars->{NETBIOSNAME}"); + system("$nmblookup $testenv_vars->{CONFIGURATION} -U $testenv_vars->{SERVER_IP} $testenv_vars->{NETBIOSNAME}"); + } + $count++; + } while ($ret != 0 && $count < 20); + if ($count == 20) { + teardown_env($self, $testenv_vars); + warn("nbt not reachable after 20 retries\n"); + return -1; + } + + # Ensure we have the first RID Set before we start tests. This makes the tests more reliable. + if ($testenv_vars->{SERVER_ROLE} eq "domain controller") { + print "waiting for working LDAP and a RID Set to be allocated\n"; + my $ldbsearch = Samba::bindir_path($self, "ldbsearch"); + my $count = 0; + my $base_dn = "DC=".join(",DC=", split(/\./, $testenv_vars->{REALM})); + + my $search_dn = $base_dn; + if ($testenv_vars->{NETBIOSNAME} ne "RODC") { + # TODO currently no check for actual rIDAllocationPool + $search_dn = "cn=RID Set,cn=$testenv_vars->{NETBIOSNAME},ou=domain controllers,$base_dn"; + } + my $max_wait = 60; + + # Add hosts file for name lookups + my $cmd = $self->get_cmd_env_vars($testenv_vars); + + $cmd .= "$ldbsearch "; + $cmd .= "$testenv_vars->{CONFIGURATION} "; + $cmd .= "-H ldap://$testenv_vars->{SERVER} "; + $cmd .= "-U$testenv_vars->{USERNAME}%$testenv_vars->{PASSWORD} "; + $cmd .= "--scope base "; + $cmd .= "-b '$search_dn' "; + while (system("$cmd >/dev/null") != 0) { + $count++; + if ($count > $max_wait) { + teardown_env($self, $testenv_vars); + warn("Timed out ($max_wait sec) waiting for working LDAP and a RID Set to be allocated by $testenv_vars->{NETBIOSNAME} PID $testenv_vars->{SAMBA_PID}"); + return -1; + } + print "Waiting for working LDAP...\n"; + sleep(1); + } + } + + my $wbinfo = Samba::bindir_path($self, "wbinfo"); + + $count = 0; + do { + my $cmd = "NSS_WRAPPER_PASSWD=$testenv_vars->{NSS_WRAPPER_PASSWD} "; + $cmd .= "NSS_WRAPPER_GROUP=$testenv_vars->{NSS_WRAPPER_GROUP} "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=$testenv_vars->{SELFTEST_WINBINDD_SOCKET_DIR} "; + $cmd .= "$wbinfo -P"; + $ret = system($cmd); + + if ($ret != 0) { + sleep(1); + } + $count++; + } while ($ret != 0 && $count < 20); + if ($count == 20) { + teardown_env($self, $testenv_vars); + warn("winbind not reachable after 20 retries\n"); + return -1; + } + + # Ensure we registered all our names + if ($testenv_vars->{SERVER_ROLE} eq "domain controller") { + my $max_wait = 120; + my $dns_update_cache = "$testenv_vars->{PRIVATEDIR}/dns_update_cache"; + print "Waiting for $dns_update_cache to be created.\n"; + $count = 0; + while (not -e $dns_update_cache) { + $count++; + if ($count > $max_wait) { + teardown_env($self, $testenv_vars); + warn("Timed out ($max_wait sec) waiting for $dns_update_cache PID $testenv_vars->{SAMBA_PID}"); + return -1; + } + print "Waiting for $dns_update_cache to be created...\n"; + sleep(1); + } + print "Waiting for $dns_update_cache to be filled.\n"; + $count = 0; + while ((-s "$dns_update_cache") == 0) { + $count++; + if ($count > $max_wait) { + teardown_env($self, $testenv_vars); + warn("Timed out ($max_wait sec) waiting for $dns_update_cache PID $testenv_vars->{SAMBA_PID}"); + return -1; + } + print "Waiting for $dns_update_cache to be filled...\n"; + sleep(1); + } + } + + print $self->getlog_env($testenv_vars); + + print "READY ($testenv_vars->{SAMBA_PID})\n"; + + return 0 +} + +sub write_ldb_file($$$) +{ + my ($self, $file, $ldif_in) = @_; + + my $ldbadd = Samba::bindir_path($self, "ldbadd"); + open(my $ldif, "|$ldbadd -H $file > /dev/null") + or die "Failed to run $ldbadd: $!"; + print $ldif $ldif_in; + close($ldif); + + unless ($? == 0) { + warn("$ldbadd failed: $?"); + return undef; + } + return 1; +} + +sub add_wins_config($$) +{ + my ($self, $privatedir) = @_; + my $client_ip = Samba::get_ipv4_addr("client"); + + return $self->write_ldb_file("$privatedir/wins_config.ldb", " +dn: name=TORTURE_11,CN=PARTNERS +objectClass: wreplPartner +name: TORTURE_11 +address: $client_ip +pullInterval: 0 +pushChangeCount: 0 +type: 0x3 +"); +} + +sub setup_dns_hub_internal($$$) +{ + my ($self, $hostname, $prefix) = @_; + my $STDIN_READER; + + unless(-d $prefix or mkdir($prefix, 0777)) { + warn("Unable to create $prefix"); + return undef; + } + my $prefix_abs = abs_path($prefix); + + die ("prefix=''") if $prefix_abs eq ""; + die ("prefix='/'") if $prefix_abs eq "/"; + + unless (system("rm -rf $prefix_abs/*") == 0) { + warn("Unable to clean up"); + } + + my $env = undef; + $env->{NETBIOSNAME} = $hostname; + + $env->{SERVER_IP} = Samba::get_ipv4_addr($hostname); + $env->{SERVER_IPV6} = Samba::get_ipv6_addr($hostname); + $env->{SOCKET_WRAPPER_DEFAULT_IFACE} = Samba::get_interface($hostname); + $env->{DNS_HUB_LOG} = "$prefix_abs/dns_hub.log"; + $env->{RESOLV_CONF} = "$prefix_abs/resolv.conf"; + $env->{TESTENV_DIR} = $prefix_abs; + + my $ctx = undef; + $ctx->{resolv_conf} = $env->{RESOLV_CONF}; + $ctx->{dns_ipv4} = $env->{SERVER_IP}; + $ctx->{dns_ipv6} = $env->{SERVER_IPV6}; + Samba::mk_resolv_conf($ctx); + + my @preargs = (); + my @args = (); + if (!defined($ENV{PYTHON})) { + push (@preargs, "env"); + push (@preargs, "python"); + } else { + push (@preargs, $ENV{PYTHON}); + } + my $binary = "$self->{srcdir}/selftest/target/dns_hub.py"; + push (@args, "$self->{server_maxtime}"); + push (@args, "$env->{SERVER_IP},$env->{SERVER_IPV6}"); + push (@args, Samba::realm_to_ip_mappings()); + my @full_cmd = (@preargs, $binary, @args); + + my $daemon_ctx = { + NAME => "dnshub", + BINARY_PATH => $binary, + FULL_CMD => [ @full_cmd ], + LOG_FILE => $env->{DNS_HUB_LOG}, + TEE_STDOUT => 1, + PCAP_FILE => "env-$ENV{ENVNAME}-dns_hub", + ENV_VARS => {}, + }; + + # use a pipe for stdin in the child processes. This allows + # those processes to monitor the pipe for EOF to ensure they + # exit when the test script exits + pipe($STDIN_READER, $env->{STDIN_PIPE}); + + my $pid = Samba::fork_and_exec($self, $env, $daemon_ctx, $STDIN_READER); + + $env->{SAMBA_PID} = $pid; + $env->{KRB5_CONFIG} = "$prefix_abs/no_krb5.conf"; + + # close the parent's read-end of the pipe + close($STDIN_READER); + + return $env; +} + +sub setup_dns_hub +{ + my ($self, $prefix) = @_; + + my $hostname = "rootdnsforwarder"; + + unless(-d $prefix or mkdir($prefix, 0777)) { + warn("Unable to create $prefix"); + return undef; + } + my $env = $self->setup_dns_hub_internal("$hostname", "$prefix/$hostname"); + + $self->{dns_hub_env} = $env; + + return $env; +} + +sub get_dns_hub_env($) +{ + my ($self, $prefix) = @_; + + if (defined($self->{dns_hub_env})) { + return $self->{dns_hub_env}; + } + + die("get_dns_hub_env() not setup 'dns_hub_env'"); + return undef; +} + +sub return_env_value +{ + my ($env, $overwrite, $key) = @_; + + if (defined($overwrite) and defined($overwrite->{$key})) { + return $overwrite->{$key}; + } + + if (defined($env->{$key})) { + return $env->{$key}; + } + + return undef; +} + +# Returns the environmental variables that we pass to samba-tool commands +sub get_cmd_env_vars +{ + my ($self, $givenenv, $overwrite) = @_; + + my @keys = ( + "NSS_WRAPPER_HOSTS", + "SOCKET_WRAPPER_DEFAULT_IFACE", + "RESOLV_CONF", + "RESOLV_WRAPPER_CONF", + "RESOLV_WRAPPER_HOSTS", + "GNUTLS_FORCE_FIPS_MODE", + "OPENSSL_FORCE_FIPS_MODE", + "KRB5_CONFIG", + "KRB5_CCACHE", + "GNUPGHOME", + ); + + my $localenv = undef; + foreach my $key (@keys) { + my $v = return_env_value($givenenv, $overwrite, $key); + $localenv->{$key} = $v if defined($v); + } + + my $cmd_env = "NSS_WRAPPER_HOSTS='$localenv->{NSS_WRAPPER_HOSTS}' "; + $cmd_env .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$localenv->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + if (defined($localenv->{RESOLV_WRAPPER_CONF})) { + $cmd_env .= "RESOLV_WRAPPER_CONF=\"$localenv->{RESOLV_WRAPPER_CONF}\" "; + } else { + $cmd_env .= "RESOLV_WRAPPER_HOSTS=\"$localenv->{RESOLV_WRAPPER_HOSTS}\" "; + } + if (defined($localenv->{GNUTLS_FORCE_FIPS_MODE})) { + $cmd_env .= "GNUTLS_FORCE_FIPS_MODE=$localenv->{GNUTLS_FORCE_FIPS_MODE} "; + } + if (defined($localenv->{OPENSSL_FORCE_FIPS_MODE})) { + $cmd_env .= "OPENSSL_FORCE_FIPS_MODE=$localenv->{OPENSSL_FORCE_FIPS_MODE} "; + } + $cmd_env .= "KRB5_CONFIG=\"$localenv->{KRB5_CONFIG}\" "; + $cmd_env .= "KRB5CCNAME=\"$localenv->{KRB5_CCACHE}\" "; + $cmd_env .= "RESOLV_CONF=\"$localenv->{RESOLV_CONF}\" "; + $cmd_env .= "GNUPGHOME=\"$localenv->{GNUPGHOME}\" "; + + return $cmd_env; +} + +# Sets up a forest trust namespace. +# (Note this is different to kernel namespaces, setup by the +# USE_NAMESPACES=1 option) +sub setup_namespaces +{ + my ($self, $localenv, $upn_array, $spn_array) = @_; + + @{$upn_array} = [] unless defined($upn_array); + my $upn_args = ""; + foreach my $upn (@{$upn_array}) { + $upn_args .= " --add-upn-suffix=$upn"; + } + + @{$spn_array} = [] unless defined($spn_array); + my $spn_args = ""; + foreach my $spn (@{$spn_array}) { + $spn_args .= " --add-spn-suffix=$spn"; + } + + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + + my $cmd_env = $self->get_cmd_env_vars($localenv); + + my $cmd_config = " $localenv->{CONFIGURATION}"; + + my $namespaces = $cmd_env; + $namespaces .= " $samba_tool domain trust namespaces $upn_args $spn_args"; + $namespaces .= $cmd_config; + unless (system($namespaces) == 0) { + warn("Failed to add namespaces \n$namespaces"); + return -1; + } + + return 0; +} + +sub setup_trust($$$$$) +{ + my ($self, $localenv, $remoteenv, $type, $extra_args) = @_; + + $localenv->{TRUST_SERVER} = $remoteenv->{SERVER}; + $localenv->{TRUST_SERVER_IP} = $remoteenv->{SERVER_IP}; + $localenv->{TRUST_DNSNAME} = $remoteenv->{DNSNAME}; + + $localenv->{TRUST_USERNAME} = $remoteenv->{USERNAME}; + $localenv->{TRUST_PASSWORD} = $remoteenv->{PASSWORD}; + $localenv->{TRUST_DOMAIN} = $remoteenv->{DOMAIN}; + $localenv->{TRUST_REALM} = $remoteenv->{REALM}; + $localenv->{TRUST_DOMSID} = $remoteenv->{DOMSID}; + + # Add trusted domain realms to krb5.conf + Samba::append_krb5_conf_trust_realms($localenv); + + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + + # setup the trust + my $cmd_env = $self->get_cmd_env_vars($localenv); + + my $cmd_config = " $localenv->{CONFIGURATION}"; + my $cmd_creds = $cmd_config; + $cmd_creds .= " -U$localenv->{TRUST_DOMAIN}\\\\$localenv->{TRUST_USERNAME}\%$localenv->{TRUST_PASSWORD}"; + + my $create = $cmd_env; + $create .= " $samba_tool domain trust create --type=${type} $localenv->{TRUST_REALM}"; + $create .= " $extra_args"; + $create .= $cmd_creds; + unless (system($create) == 0) { + warn("Failed to create trust \n$create"); + return undef; + } + + my $groupname = "g_$localenv->{TRUST_DOMAIN}"; + my $groupadd = $cmd_env; + $groupadd .= " $samba_tool group add '$groupname' --group-scope=Domain $cmd_config"; + unless (system($groupadd) == 0) { + warn("Failed to create group \n$groupadd"); + return undef; + } + my $groupmem = $cmd_env; + $groupmem .= " $samba_tool group addmembers '$groupname' '$localenv->{TRUST_DOMSID}-513' $cmd_config"; + unless (system($groupmem) == 0) { + warn("Failed to add group member \n$groupmem"); + return undef; + } + + return $localenv +} + +sub provision_raw_prepare($$$$$$$$$$$$$$) +{ + my ($self, + $prefix, + $server_role, + $hostname, + $domain, + $realm, + $samsid, + $functional_level, + $password, + $kdc_ipv4, + $kdc_ipv6, + $force_fips_mode, + $extra_provision_options) = @_; + my $ctx; + my $python_cmd = ""; + if (defined $ENV{PYTHON}) { + $python_cmd = $ENV{PYTHON} . " "; + } + $ctx->{python} = $python_cmd; + my $netbiosname = uc($hostname); + + unless(-d $prefix or mkdir($prefix, 0777)) { + warn("Unable to create $prefix"); + return undef; + } + my $prefix_abs = abs_path($prefix); + + die ("prefix=''") if $prefix_abs eq ""; + die ("prefix='/'") if $prefix_abs eq "/"; + + unless (system("rm -rf $prefix_abs/*") == 0) { + warn("Unable to clean up"); + } + + + my $swiface = Samba::get_interface($hostname); + + $ctx->{prefix} = $prefix; + $ctx->{prefix_abs} = $prefix_abs; + + $ctx->{server_role} = $server_role; + $ctx->{hostname} = $hostname; + $ctx->{netbiosname} = $netbiosname; + $ctx->{swiface} = $swiface; + $ctx->{password} = $password; + $ctx->{kdc_ipv4} = $kdc_ipv4; + $ctx->{kdc_ipv6} = $kdc_ipv6; + $ctx->{force_fips_mode} = $force_fips_mode; + $ctx->{krb5_ccname} = "$prefix_abs/krb5cc_%{uid}"; + if ($functional_level eq "2000") { + $ctx->{supported_enctypes} = "arcfour-hmac-md5 des-cbc-md5 des-cbc-crc"; + } + +# +# Set smbd log level here. +# + $ctx->{server_loglevel} =$ENV{SERVER_LOG_LEVEL} || 1; + $ctx->{username} = "Administrator"; + $ctx->{domain} = $domain; + $ctx->{realm} = uc($realm); + $ctx->{dnsname} = lc($realm); + $ctx->{samsid} = $samsid; + $ctx->{domain_admin} = "Administrator"; + $ctx->{domain_admin_password} = $password; + $ctx->{domain_user} = "alice"; + $ctx->{domain_user_password} = "Secret007"; + + $ctx->{functional_level} = $functional_level; + + my $unix_name = ($ENV{USER} or $ENV{LOGNAME} or `whoami`); + chomp $unix_name; + $ctx->{unix_name} = $unix_name; + $ctx->{unix_uid} = $>; + my @mygid = split(" ", $(); + $ctx->{unix_gid} = $mygid[0]; + $ctx->{unix_gids_str} = $); + @{$ctx->{unix_gids}} = split(" ", $ctx->{unix_gids_str}); + + $ctx->{etcdir} = "$prefix_abs/etc"; + $ctx->{piddir} = "$prefix_abs/pid"; + $ctx->{smb_conf} = "$ctx->{etcdir}/smb.conf"; + $ctx->{krb5_conf} = "$ctx->{etcdir}/krb5.conf"; + $ctx->{krb5_ccache} = "$prefix_abs/krb5_ccache"; + $ctx->{mitkdc_conf} = "$ctx->{etcdir}/mitkdc.conf"; + $ctx->{gnupghome} = "$prefix_abs/gnupg"; + $ctx->{privatedir} = "$prefix_abs/private"; + $ctx->{binddnsdir} = "$prefix_abs/bind-dns"; + $ctx->{ncalrpcdir} = "$prefix_abs/ncalrpc"; + $ctx->{lockdir} = "$prefix_abs/lockdir"; + $ctx->{logdir} = "$prefix_abs/logs"; + $ctx->{statedir} = "$prefix_abs/statedir"; + $ctx->{cachedir} = "$prefix_abs/cachedir"; + $ctx->{winbindd_socket_dir} = "$prefix_abs/wbsock"; + $ctx->{ntp_signd_socket_dir} = "$prefix_abs/ntp_signd_socket"; + $ctx->{nsswrap_passwd} = "$ctx->{etcdir}/passwd"; + $ctx->{nsswrap_group} = "$ctx->{etcdir}/group"; + $ctx->{nsswrap_hosts} = "$ENV{SELFTEST_PREFIX}/hosts"; + $ctx->{nsswrap_hostname} = "$ctx->{hostname}.$ctx->{dnsname}"; + if ($ENV{SAMBA_DNS_FAKING}) { + $ctx->{dns_host_file} = "$ENV{SELFTEST_PREFIX}/dns_host_file"; + $ctx->{samba_dnsupdate} = "$ENV{SRCDIR_ABS}/source4/scripting/bin/samba_dnsupdate --configfile=$ctx->{smb_conf} --all-interfaces --use-file=$ctx->{dns_host_file}"; + $ctx->{samba_dnsupdate} = $python_cmd . $ctx->{samba_dnsupdate}; + } else { + $ctx->{samba_dnsupdate} = "$ENV{SRCDIR_ABS}/source4/scripting/bin/samba_dnsupdate --configfile=$ctx->{smb_conf} --all-interfaces"; + $ctx->{samba_dnsupdate} = $python_cmd . $ctx->{samba_dnsupdate}; + $ctx->{use_resolv_wrapper} = 1; + } + + my $dns_hub = $self->get_dns_hub_env(); + $ctx->{resolv_conf} = $dns_hub->{RESOLV_CONF}; + + $ctx->{tlsdir} = "$ctx->{privatedir}/tls"; + + $ctx->{ipv4} = Samba::get_ipv4_addr($hostname); + $ctx->{ipv6} = Samba::get_ipv6_addr($hostname); + + push(@{$ctx->{directories}}, $ctx->{privatedir}); + push(@{$ctx->{directories}}, $ctx->{binddnsdir}); + push(@{$ctx->{directories}}, $ctx->{etcdir}); + push(@{$ctx->{directories}}, $ctx->{piddir}); + push(@{$ctx->{directories}}, $ctx->{lockdir}); + push(@{$ctx->{directories}}, $ctx->{logdir}); + push(@{$ctx->{directories}}, $ctx->{statedir}); + push(@{$ctx->{directories}}, $ctx->{cachedir}); + + $ctx->{smb_conf_extra_options} = ""; + + my @provision_options = (); + push (@provision_options, "GNUPGHOME=\"$ctx->{gnupghome}\""); + push (@provision_options, "KRB5_CONFIG=\"$ctx->{krb5_conf}\""); + push (@provision_options, "KRB5CCNAME=\"$ctx->{krb5_ccache}\""); + push (@provision_options, "NSS_WRAPPER_PASSWD=\"$ctx->{nsswrap_passwd}\""); + push (@provision_options, "NSS_WRAPPER_GROUP=\"$ctx->{nsswrap_group}\""); + push (@provision_options, "NSS_WRAPPER_HOSTS=\"$ctx->{nsswrap_hosts}\""); + push (@provision_options, "NSS_WRAPPER_HOSTNAME=\"$ctx->{nsswrap_hostname}\""); + if (defined($ctx->{use_resolv_wrapper})) { + push (@provision_options, "RESOLV_WRAPPER_CONF=\"$ctx->{resolv_conf}\""); + push (@provision_options, "RESOLV_CONF=\"$ctx->{resolv_conf}\""); + } else { + push (@provision_options, "RESOLV_WRAPPER_HOSTS=\"$ctx->{dns_host_file}\""); + } + if (defined($ctx->{force_fips_mode})) { + push (@provision_options, "GNUTLS_FORCE_FIPS_MODE=1"); + push (@provision_options, "OPENSSL_FORCE_FIPS_MODE=1"); + } + + if (defined($ENV{GDB_PROVISION})) { + push (@provision_options, "gdb --args"); + if (!defined($ENV{PYTHON})) { + push (@provision_options, "env"); + push (@provision_options, "python"); + } + } + if (defined($ENV{VALGRIND_PROVISION})) { + push (@provision_options, "valgrind"); + if (!defined($ENV{PYTHON})) { + push (@provision_options, "env"); + push (@provision_options, "python"); + } + } + + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + + push (@provision_options, $samba_tool); + push (@provision_options, "domain"); + push (@provision_options, "provision"); + push (@provision_options, "--configfile=$ctx->{smb_conf}"); + push (@provision_options, "--host-name=$ctx->{hostname}"); + push (@provision_options, "--host-ip=$ctx->{ipv4}"); + push (@provision_options, "--quiet"); + push (@provision_options, "--domain=$ctx->{domain}"); + push (@provision_options, "--realm=$ctx->{realm}"); + if (defined($ctx->{samsid})) { + push (@provision_options, "--domain-sid=$ctx->{samsid}"); + } + push (@provision_options, "--adminpass=$ctx->{password}"); + push (@provision_options, "--krbtgtpass=krbtgt$ctx->{password}"); + push (@provision_options, "--machinepass=machine$ctx->{password}"); + push (@provision_options, "--root=$ctx->{unix_name}"); + push (@provision_options, "--server-role=\"$ctx->{server_role}\""); + push (@provision_options, "--function-level=\"$ctx->{functional_level}\""); + + @{$ctx->{provision_options}} = @provision_options; + + if (defined($extra_provision_options)) { + push (@{$ctx->{provision_options}}, @{$extra_provision_options}); + } + + return $ctx; +} + +sub has_option +{ + my ($self, $keyword, @options_list) = @_; + + # convert the options-list to a hash-map for easy keyword lookup + my %options_dict = map { $_ => 1 } @options_list; + + return exists $options_dict{$keyword}; +} + +# +# Step1 creates the basic configuration +# +sub provision_raw_step1($$) +{ + my ($self, $ctx) = @_; + + mkdir($_, 0777) foreach (@{$ctx->{directories}}); + + ## + ## lockdir and piddir must be 0755 + ## + chmod 0755, $ctx->{lockdir}; + chmod 0755, $ctx->{piddir}; + + unless (open(CONFFILE, ">$ctx->{smb_conf}")) { + warn("can't open $ctx->{smb_conf}$?"); + return undef; + } + + Samba::copy_gnupg_home($ctx); + Samba::prepare_keyblobs($ctx); + my $crlfile = "$ctx->{tlsdir}/crl.pem"; + $crlfile = "" unless -e ${crlfile}; + + # work out which file server to use. Default to source3 smbd (s3fs), + # unless the source4 NTVFS (smb) file server has been specified + my $services = "-smb +s3fs"; + if ($self->has_option("--use-ntvfs", @{$ctx->{provision_options}})) { + $services = "+smb -s3fs"; + } + + my $interfaces = Samba::get_interfaces_config($ctx->{netbiosname}); + + print CONFFILE " +[global] + netbios name = $ctx->{netbiosname} + posix:eadb = $ctx->{statedir}/eadb.tdb + workgroup = $ctx->{domain} + realm = $ctx->{realm} + private dir = $ctx->{privatedir} + binddns dir = $ctx->{binddnsdir} + pid directory = $ctx->{piddir} + ncalrpc dir = $ctx->{ncalrpcdir} + lock dir = $ctx->{lockdir} + state directory = $ctx->{statedir} + cache directory = $ctx->{cachedir} + winbindd socket directory = $ctx->{winbindd_socket_dir} + ntp signd socket directory = $ctx->{ntp_signd_socket_dir} + winbind separator = / + interfaces = $interfaces + tls dh params file = $ctx->{tlsdir}/dhparms.pem + tls crlfile = ${crlfile} + tls verify peer = no_check + panic action = $RealBin/gdb_backtrace \%d + smbd:suicide mode = yes + smbd:FSCTL_SMBTORTURE = yes + smbd:validate_oplock_types = yes + wins support = yes + server role = $ctx->{server_role} + server services = +echo $services + dcerpc endpoint servers = +winreg +srvsvc +rpcecho + notify:inotify = false + ldb:nosync = true + ldap server require strong auth = yes + log file = $ctx->{logdir}/log.\%m + log level = $ctx->{server_loglevel} + lanman auth = Yes + ntlm auth = Yes + client min protocol = SMB2_02 + server min protocol = SMB2_02 + mangled names = yes + dns update command = $ctx->{samba_dnsupdate} + spn update command = $ctx->{python} $ENV{SRCDIR_ABS}/source4/scripting/bin/samba_spnupdate --configfile $ctx->{smb_conf} + gpo update command = $ctx->{python} $ENV{SRCDIR_ABS}/source4/scripting/bin/samba-gpupdate --configfile $ctx->{smb_conf} --target=Computer + samba kcc command = $ctx->{python} $ENV{SRCDIR_ABS}/source4/scripting/bin/samba_kcc + dreplsrv:periodic_startup_interval = 0 + dsdb:schema update allowed = yes + + vfs objects = dfs_samba4 acl_xattr fake_acls xattr_tdb streams_depot + + idmap_ldb:use rfc2307=yes + winbind enum users = yes + winbind enum groups = yes + + rpc server port:netlogon = 1026 + include system krb5 conf = no + + debug syslog format = always + debug hires timestamp = yes + +"; + + print CONFFILE " + + # Begin extra options + $ctx->{smb_conf_extra_options} + # End extra options +"; + close(CONFFILE); + + #Default the KDC IP to the server's IP + if (not defined($ctx->{kdc_ipv4})) { + $ctx->{kdc_ipv4} = $ctx->{ipv4}; + } + if (not defined($ctx->{kdc_ipv6})) { + $ctx->{kdc_ipv6} = $ctx->{ipv6}; + } + + Samba::mk_krb5_conf($ctx); + Samba::mk_mitkdc_conf($ctx, abs_path(Samba::bindir_path($self, "shared"))); + + open(PWD, ">$ctx->{nsswrap_passwd}"); + if ($ctx->{unix_uid} != 0) { + print PWD "root:x:0:0:root gecos:$ctx->{prefix_abs}:/bin/false\n"; + } + print PWD "$ctx->{unix_name}:x:$ctx->{unix_uid}:65531:$ctx->{unix_name} gecos:$ctx->{prefix_abs}:/bin/false\n"; + print PWD "nobody:x:65534:65533:nobody gecos:$ctx->{prefix_abs}:/bin/false +pdbtest:x:65533:65533:pdbtest gecos:$ctx->{prefix_abs}:/bin/false +pdbtest2:x:65532:65533:pdbtest gecos:$ctx->{prefix_abs}:/bin/false +pdbtest3:x:65531:65533:pdbtest gecos:$ctx->{prefix_abs}:/bin/false +pdbtest4:x:65530:65533:pdbtest gecos:$ctx->{prefix_abs}:/bin/false +"; + close(PWD); + my $uid_rfc2307test = 65533; + + open(GRP, ">$ctx->{nsswrap_group}"); + if ($ctx->{unix_gid} != 0) { + print GRP "root:x:0:\n"; + } + print GRP "$ctx->{unix_name}:x:$ctx->{unix_gid}:\n"; + print GRP "wheel:x:10: +users:x:65531: +nobody:x:65533: +nogroup:x:65534:nobody +"; + close(GRP); + my $gid_rfc2307test = 65532; + + my $hostname = lc($ctx->{hostname}); + open(HOSTS, ">>$ctx->{nsswrap_hosts}"); + if ($hostname eq "localdc") { + print HOSTS "$ctx->{ipv4} ${hostname}.$ctx->{dnsname} $ctx->{dnsname} ${hostname}\n"; + print HOSTS "$ctx->{ipv6} ${hostname}.$ctx->{dnsname} $ctx->{dnsname} ${hostname}\n"; + } else { + print HOSTS "$ctx->{ipv4} ${hostname}.$ctx->{dnsname} ${hostname}\n"; + print HOSTS "$ctx->{ipv6} ${hostname}.$ctx->{dnsname} ${hostname}\n"; + } + close(HOSTS); + + my $configuration = "--configfile=$ctx->{smb_conf}"; + +#Ensure the config file is valid before we start + my $testparm = Samba::bindir_path($self, "samba-tool") . " testparm"; + if (system("$testparm $configuration -v --suppress-prompt >/dev/null 2>&1") != 0) { + system("$testparm -v --suppress-prompt $configuration >&2"); + warn("Failed to create a valid smb.conf configuration $testparm!"); + return undef; + } + 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) { + warn("Failed to create a valid smb.conf configuration! $testparm $configuration -v --suppress-prompt --parameter-name=\"netbios name\" --section-name=global"); + return undef; + } + + # Return the environment variables for the new testenv DC. + # Note that we have SERVER_X and DC_SERVER_X variables (which have the same + # value initially). In a 2 DC setup, $DC_SERVER_X will always be the PDC. + my $ret = { + GNUPGHOME => $ctx->{gnupghome}, + KRB5_CONFIG => $ctx->{krb5_conf}, + KRB5_CCACHE => $ctx->{krb5_ccache}, + MITKDC_CONFIG => $ctx->{mitkdc_conf}, + PIDDIR => $ctx->{piddir}, + SERVER => $ctx->{hostname}, + DC_SERVER => $ctx->{hostname}, + SERVER_IP => $ctx->{ipv4}, + DC_SERVER_IP => $ctx->{ipv4}, + SERVER_IPV6 => $ctx->{ipv6}, + DC_SERVER_IPV6 => $ctx->{ipv6}, + NETBIOSNAME => $ctx->{netbiosname}, + DC_NETBIOSNAME => $ctx->{netbiosname}, + DOMAIN => $ctx->{domain}, + USERNAME => $ctx->{username}, + DC_USERNAME => $ctx->{username}, + DOMAIN_ADMIN => $ctx->{domain_admin}, + DOMAIN_ADMIN_PASSWORD => $ctx->{domain_admin_password}, + DOMAIN_USER => $ctx->{domain_user}, + DOMAIN_USER_PASSWORD => $ctx->{domain_user_password}, + REALM => $ctx->{realm}, + DNSNAME => $ctx->{dnsname}, + SAMSID => $ctx->{samsid}, + PASSWORD => $ctx->{password}, + DC_PASSWORD => $ctx->{password}, + LDAPDIR => $ctx->{ldapdir}, + LDAP_INSTANCE => $ctx->{ldap_instance}, + SELFTEST_WINBINDD_SOCKET_DIR => $ctx->{winbindd_socket_dir}, + NCALRPCDIR => $ctx->{ncalrpcdir}, + LOCKDIR => $ctx->{lockdir}, + STATEDIR => $ctx->{statedir}, + CACHEDIR => $ctx->{cachedir}, + PRIVATEDIR => $ctx->{privatedir}, + BINDDNSDIR => $ctx->{binddnsdir}, + SERVERCONFFILE => $ctx->{smb_conf}, + TESTENV_DIR => $ctx->{prefix_abs}, + CONFIGURATION => $configuration, + SOCKET_WRAPPER_DEFAULT_IFACE => $ctx->{swiface}, + NSS_WRAPPER_PASSWD => $ctx->{nsswrap_passwd}, + NSS_WRAPPER_GROUP => $ctx->{nsswrap_group}, + NSS_WRAPPER_HOSTS => $ctx->{nsswrap_hosts}, + NSS_WRAPPER_HOSTNAME => $ctx->{nsswrap_hostname}, + SAMBA_TEST_FIFO => "$ctx->{prefix}/samba_test.fifo", + SAMBA_TEST_LOG => "$ctx->{prefix}/samba_test.log", + SAMBA_TEST_LOG_POS => 0, + NSS_WRAPPER_MODULE_SO_PATH => Samba::nss_wrapper_winbind_so_path($self), + NSS_WRAPPER_MODULE_FN_PREFIX => "winbind", + LOCAL_PATH => $ctx->{share}, + UID_RFC2307TEST => $uid_rfc2307test, + GID_RFC2307TEST => $gid_rfc2307test, + SERVER_ROLE => $ctx->{server_role}, + RESOLV_CONF => $ctx->{resolv_conf}, + KRB5_CRL_FILE => $crlfile, + }; + + if (defined($ctx->{use_resolv_wrapper})) { + $ret->{RESOLV_WRAPPER_CONF} = $ctx->{resolv_conf}; + } else { + $ret->{RESOLV_WRAPPER_HOSTS} = $ctx->{dns_host_file}; + } + if (defined($ctx->{force_fips_mode})) { + $ret->{GNUTLS_FORCE_FIPS_MODE} = "1", + $ret->{OPENSSL_FORCE_FIPS_MODE} = "1", + } + + if ($ctx->{server_role} eq "domain controller") { + $ret->{DOMSID} = $ret->{SAMSID}; + } + + return $ret; +} + +# +# Step2 runs the provision script +# +sub provision_raw_step2($$$) +{ + my ($self, $ctx, $ret) = @_; + + my $ldif; + + my $provision_cmd = join(" ", @{$ctx->{provision_options}}); + unless (system($provision_cmd) == 0) { + warn("Unable to provision: \n$provision_cmd\n"); + return undef; + } + + my $cmd_env = $self->get_cmd_env_vars($ret); + + my $testallowed_account = "testallowed"; + my $samba_tool_cmd = ${cmd_env}; + $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") + . " user create --configfile=$ctx->{smb_conf} $testallowed_account $ctx->{password}"; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to add testallowed user: \n$samba_tool_cmd\n"); + return undef; + } + + my $srv_account = "srv_account"; + $samba_tool_cmd = ${cmd_env}; + $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") + . " user create --configfile=$ctx->{smb_conf} $srv_account $ctx->{password}"; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to add $srv_account user: \n$samba_tool_cmd\n"); + return undef; + } + + $samba_tool_cmd = ${cmd_env}; + $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") + . " spn add HOST/$srv_account --configfile=$ctx->{smb_conf} $srv_account"; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to add spn for $srv_account: \n$samba_tool_cmd\n"); + return undef; + } + + my $ldbmodify = ${cmd_env}; + $ldbmodify .= Samba::bindir_path($self, "ldbmodify"); + $ldbmodify .= " --configfile=$ctx->{smb_conf}"; + my $base_dn = "DC=".join(",DC=", split(/\./, $ctx->{realm})); + + if ($ctx->{server_role} ne "domain controller") { + $base_dn = "DC=$ctx->{netbiosname}"; + } + + my $user_dn = "cn=$testallowed_account,cn=users,$base_dn"; + $testallowed_account = "testallowed account"; + open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb") + or die "Failed to run $ldbmodify: $!"; + print $ldif "dn: $user_dn +changetype: modify +replace: samAccountName +samAccountName: $testallowed_account +- +"; + close($ldif); + unless ($? == 0) { + warn("$ldbmodify failed: $?"); + return undef; + } + + open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb") + or die "Failed to run $ldbmodify: $!"; + print $ldif "dn: $user_dn +changetype: modify +replace: userPrincipalName +userPrincipalName: testallowed upn\@$ctx->{realm} +replace: servicePrincipalName +servicePrincipalName: host/testallowed +- +"; + close($ldif); + unless ($? == 0) { + warn("$ldbmodify failed: $?"); + return undef; + } + + $samba_tool_cmd = ${cmd_env}; + $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") + . " user create --configfile=$ctx->{smb_conf} testdenied $ctx->{password}"; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to add testdenied user: \n$samba_tool_cmd\n"); + return undef; + } + + $user_dn = "cn=testdenied,cn=users,$base_dn"; + open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb") + or die "Failed to run $ldbmodify: $!"; + print $ldif "dn: $user_dn +changetype: modify +replace: userPrincipalName +userPrincipalName: testdenied_upn\@$ctx->{realm}.upn +- +"; + close($ldif); + unless ($? == 0) { + warn("$ldbmodify failed: $?"); + return undef; + } + + $samba_tool_cmd = ${cmd_env}; + $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") + . " user create --configfile=$ctx->{smb_conf} testupnspn $ctx->{password}"; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to add testupnspn user: \n$samba_tool_cmd\n"); + return undef; + } + + $user_dn = "cn=testupnspn,cn=users,$base_dn"; + open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb") + or die "Failed to run $ldbmodify: $!"; + print $ldif "dn: $user_dn +changetype: modify +replace: userPrincipalName +userPrincipalName: http/testupnspn.$ctx->{dnsname}\@$ctx->{realm} +replace: servicePrincipalName +servicePrincipalName: http/testupnspn.$ctx->{dnsname} +- +"; + close($ldif); + unless ($? == 0) { + warn("$ldbmodify failed: $?"); + return undef; + } + + $samba_tool_cmd = ${cmd_env}; + $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") + . " group addmembers --configfile=$ctx->{smb_conf} 'Allowed RODC Password Replication Group' '$testallowed_account'"; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to add '$testallowed_account' user to 'Allowed RODC Password Replication Group': \n$samba_tool_cmd\n"); + return undef; + } + + # Create two users alice and bob! + my $user_account_array = ["alice", "bob", "jane", "joe"]; + + foreach my $user_account (@{$user_account_array}) { + my $samba_tool_cmd = ${cmd_env}; + + $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") + . " user create --configfile=$ctx->{smb_conf} $user_account Secret007"; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to create user: $user_account\n$samba_tool_cmd\n"); + return undef; + } + } + + my $group_array = ["Samba Users"]; + + foreach my $group (@{$group_array}) { + my $samba_tool_cmd = ${cmd_env}; + + $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") + . " group add --configfile=$ctx->{smb_conf} \"$group\""; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to create group: $group\n$samba_tool_cmd\n"); + return undef; + } + } + + # Add user joe to group "Samba Users" + my $group = "Samba Users"; + my $user_account = "joe"; + + $samba_tool_cmd = ${cmd_env}; + $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") + . " group addmembers --configfile=$ctx->{smb_conf} \"$group\" $user_account"; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to add " . $user_account . "to group group : $group\n$samba_tool_cmd\n"); + return undef; + } + + $group = "Samba Users"; + $user_account = "joe"; + + $samba_tool_cmd = ${cmd_env}; + $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") + . " user setprimarygroup --configfile=$ctx->{smb_conf} $user_account \"$group\""; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to set primary group of user: $user_account\n$samba_tool_cmd\n"); + return undef; + } + + # Change the userPrincipalName for jane + $user_dn = "cn=jane,cn=users,$base_dn"; + + open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb") + or die "Failed to run $ldbmodify: $!"; + print $ldif "dn: $user_dn +changetype: modify +replace: userPrincipalName +userPrincipalName: jane.doe\@$ctx->{realm} +- +"; + close($ldif); + unless ($? == 0) { + warn("$ldbmodify failed: $?"); + return undef; + } + + return $ret; +} + +sub provision($$$$$$$$$$$) +{ + my ($self, + $prefix, + $server_role, + $hostname, + $domain, + $realm, + $functional_level, + $password, + $kdc_ipv4, + $kdc_ipv6, + $force_fips_mode, + $extra_smbconf_options, + $extra_smbconf_shares, + $extra_provision_options) = @_; + + my $samsid = Samba::random_domain_sid(); + + my $ctx = $self->provision_raw_prepare($prefix, $server_role, + $hostname, + $domain, $realm, + $samsid, + $functional_level, + $password, + $kdc_ipv4, + $kdc_ipv6, + $force_fips_mode, + $extra_provision_options); + + $ctx->{share} = "$ctx->{prefix_abs}/share"; + push(@{$ctx->{directories}}, "$ctx->{share}"); + push(@{$ctx->{directories}}, "$ctx->{share}/test1"); + push(@{$ctx->{directories}}, "$ctx->{share}/test2"); + + # precreate directories for printer drivers + push(@{$ctx->{directories}}, "$ctx->{share}/W32X86"); + push(@{$ctx->{directories}}, "$ctx->{share}/x64"); + push(@{$ctx->{directories}}, "$ctx->{share}/WIN40"); + + my $msdfs = "no"; + $msdfs = "yes" if ($server_role eq "domain controller"); + $ctx->{smb_conf_extra_options} = " + + max xmit = 32K + server max protocol = SMB2 + host msdfs = $msdfs + lanman auth = yes + + # fruit:copyfile is a global option + fruit:copyfile = yes + + $extra_smbconf_options + +[tmp] + path = $ctx->{share} + read only = no + posix:sharedelay = 100000 + posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 500000 + +[xcopy_share] + path = $ctx->{share} + read only = no + posix:sharedelay = 100000 + posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 500000 + create mask = 777 + force create mode = 777 + +[posix_share] + path = $ctx->{share} + read only = no + create mask = 0777 + force create mode = 0 + directory mask = 0777 + force directory mode = 0 + +[test1] + path = $ctx->{share}/test1 + read only = no + posix:sharedelay = 100000 + posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 500000 + +[test2] + path = $ctx->{share}/test2 + read only = no + posix:sharedelay = 100000 + posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 500000 + +[cifs] + path = $ctx->{share}/_ignore_cifs_ + read only = no + ntvfs handler = cifs + cifs:server = $ctx->{netbiosname} + cifs:share = tmp + cifs:use-s4u2proxy = yes + # There is no username specified here, instead the client is expected + # to log in with kerberos, and the serverwill use delegated credentials. + # Or the server tries s4u2self/s4u2proxy to impersonate the client + +[simple] + path = $ctx->{share} + read only = no + ntvfs handler = simple + +[sysvol] + path = $ctx->{statedir}/sysvol + read only = no + +[netlogon] + path = $ctx->{statedir}/sysvol/$ctx->{dnsname}/scripts + read only = no + +[cifsposix] + copy = simple + ntvfs handler = cifsposix + +[vfs_fruit] + path = $ctx->{share} + vfs objects = catia fruit streams_xattr acl_xattr + ea support = yes + fruit:resource = file + fruit:metadata = netatalk + fruit:locking = netatalk + fruit:encoding = native + +[xattr] + path = $ctx->{share} + # This can be used for testing real fs xattr stuff + vfs objects = streams_xattr acl_xattr + +$extra_smbconf_shares +"; + + my $ret = $self->provision_raw_step1($ctx); + unless (defined $ret) { + return undef; + } + + return $self->provision_raw_step2($ctx, $ret); +} + +# For multi-DC testenvs, we want $DC_SERVER to always be the PDC (i.e. the +# original DC) in the testenv. $SERVER is always the joined DC that we are +# actually running the test against +sub set_pdc_env_vars +{ + my ($self, $env, $dcvars) = @_; + + $env->{DC_SERVER} = $dcvars->{DC_SERVER}; + $env->{DC_SERVER_IP} = $dcvars->{DC_SERVER_IP}; + $env->{DC_SERVER_IPV6} = $dcvars->{DC_SERVER_IPV6}; + $env->{DC_SERVERCONFFILE} = $dcvars->{SERVERCONFFILE}; + $env->{DC_NETBIOSNAME} = $dcvars->{DC_NETBIOSNAME}; + $env->{DC_USERNAME} = $dcvars->{DC_USERNAME}; + $env->{DC_PASSWORD} = $dcvars->{DC_PASSWORD}; +} + +sub provision_s4member($$$$$) +{ + my ($self, $prefix, $dcvars, $hostname, $more_conf) = @_; + print "PROVISIONING MEMBER...\n"; + my $extra_smb_conf = " + passdb backend = samba_dsdb +winbindd:use external pipes = true + +# the source4 smb server doesn't allow signing by default +server signing = enabled +raw NTLMv2 auth = yes + +# override the new SMB2 only default +client min protocol = CORE +server min protocol = LANMAN1 +"; + if ($more_conf) { + $extra_smb_conf = $extra_smb_conf . $more_conf . "\n"; + } + my $extra_provision_options = ["--use-ntvfs"]; + my $ret = $self->provision($prefix, + "member server", + $hostname, + $dcvars->{DOMAIN}, + $dcvars->{REALM}, + "2008", + "locMEMpass3", + $dcvars->{SERVER_IP}, + $dcvars->{SERVER_IPV6}, + undef, + $extra_smb_conf, "", + $extra_provision_options); + unless ($ret) { + return undef; + } + + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + my $cmd = $self->get_cmd_env_vars($ret); + $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} --experimental-s4-member member"; + $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}"; + $cmd .= " --machinepass=machine$ret->{PASSWORD}"; + + unless (system($cmd) == 0) { + warn("Join failed\n$cmd"); + return undef; + } + + $ret->{DOMSID} = $dcvars->{DOMSID}; + $self->set_pdc_env_vars($ret, $dcvars); + + return $ret; +} + +sub provision_rpc_proxy($$$) +{ + my ($self, $prefix, $dcvars) = @_; + print "PROVISIONING RPC PROXY...\n"; + + my $extra_smbconf_options = " + passdb backend = samba_dsdb + + # rpc_proxy + dcerpc_remote:binding = ncacn_ip_tcp:$dcvars->{SERVER} + dcerpc endpoint servers = epmapper, remote + dcerpc_remote:interfaces = rpcecho + dcerpc_remote:allow_anonymous_fallback = yes + # override the new SMB2 only default + client min protocol = CORE + server min protocol = LANMAN1 +[cifs_to_dc] + path = /tmp/_ignore_cifs_to_dc_/_none_ + read only = no + ntvfs handler = cifs + cifs:server = $dcvars->{SERVER} + cifs:share = cifs + cifs:use-s4u2proxy = yes + # There is no username specified here, instead the client is expected + # to log in with kerberos, and the serverwill use delegated credentials. + # Or the server tries s4u2self/s4u2proxy to impersonate the client + +"; + + my $extra_provision_options = ["--use-ntvfs"]; + my $ret = $self->provision($prefix, + "member server", + "localrpcproxy", + $dcvars->{DOMAIN}, + $dcvars->{REALM}, + "2008", + "locRPCproxypass4", + $dcvars->{SERVER_IP}, + $dcvars->{SERVER_IPV6}, + undef, + $extra_smbconf_options, "", + $extra_provision_options); + unless ($ret) { + return undef; + } + + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + + # The joind runs in the context of the rpc_proxy/member for now + my $cmd = $self->get_cmd_env_vars($ret); + $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} --experimental-s4-member member"; + $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}"; + $cmd .= " --machinepass=machine$ret->{PASSWORD}"; + + unless (system($cmd) == 0) { + warn("Join failed\n$cmd"); + return undef; + } + + # Prepare a context of the DC, but using the local CCACHE. + my $overwrite = undef; + $overwrite->{KRB5_CCACHE} = $ret->{KRB5_CCACHE}; + my $dc_cmd_env = $self->get_cmd_env_vars($dcvars, $overwrite); + + # Setting up delegation runs in the context of the DC for now + $cmd = $dc_cmd_env; + $cmd .= "$samba_tool delegation for-any-protocol '$ret->{NETBIOSNAME}\$' on"; + $cmd .= " $dcvars->{CONFIGURATION}"; + print $cmd; + + unless (system($cmd) == 0) { + warn("Delegation failed\n$cmd"); + return undef; + } + + # Setting up delegation runs in the context of the DC for now + $cmd = $dc_cmd_env; + $cmd .= "$samba_tool delegation add-service '$ret->{NETBIOSNAME}\$' cifs/$dcvars->{SERVER}"; + $cmd .= " $dcvars->{CONFIGURATION}"; + + unless (system($cmd) == 0) { + warn("Delegation failed\n$cmd"); + return undef; + } + + $ret->{DOMSID} = $dcvars->{DOMSID}; + $self->set_pdc_env_vars($ret, $dcvars); + + return $ret; +} + +sub provision_promoted_dc($$$) +{ + my ($self, $prefix, $dcvars) = @_; + print "PROVISIONING PROMOTED DC...\n"; + + # We do this so that we don't run the provision. That's the job of 'samba-tool domain dcpromo'. + my $ctx = $self->provision_raw_prepare($prefix, "domain controller", + "promotedvdc", + $dcvars->{DOMAIN}, + $dcvars->{REALM}, + $dcvars->{SAMSID}, + "2008", + $dcvars->{PASSWORD}, + $dcvars->{SERVER_IP}, + $dcvars->{SERVER_IPV6}); + + $ctx->{smb_conf_extra_options} = " + max xmit = 32K + server max protocol = SMB2 + + ntlm auth = ntlmv2-only + + kdc force enable rc4 weak session keys = yes + +[sysvol] + path = $ctx->{statedir}/sysvol + read only = yes + +[netlogon] + path = $ctx->{statedir}/sysvol/$ctx->{dnsname}/scripts + read only = no + +"; + + my $ret = $self->provision_raw_step1($ctx); + unless ($ret) { + return undef; + } + + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + my $cmd = $self->get_cmd_env_vars($ret); + $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} --experimental-s4-member MEMBER --realm=$dcvars->{REALM}"; + $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}"; + $cmd .= " --machinepass=machine$ret->{PASSWORD}"; + + unless (system($cmd) == 0) { + warn("Join failed\n$cmd"); + return undef; + } + + $samba_tool = Samba::bindir_path($self, "samba-tool"); + $cmd = $self->get_cmd_env_vars($ret); + $cmd .= "$samba_tool domain dcpromo $ret->{CONFIGURATION} $dcvars->{REALM} DC --realm=$dcvars->{REALM}"; + $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}"; + $cmd .= " --machinepass=machine$ret->{PASSWORD} --dns-backend=BIND9_DLZ"; + + unless (system($cmd) == 0) { + warn("Join failed\n$cmd"); + return undef; + } + + $self->set_pdc_env_vars($ret, $dcvars); + + return $ret; +} + +sub provision_vampire_dc($$$) +{ + my ($self, $prefix, $dcvars, $fl) = @_; + print "PROVISIONING VAMPIRE DC @ FL $fl...\n"; + my $name = "localvampiredc"; + my $extra_conf = ""; + + if ($fl == "2000") { + $name = "vampire2000dc"; + } else { + $extra_conf = "drs: immediate link sync = yes + drs: max link sync = 250"; + } + + # We do this so that we don't run the provision. That's the job of 'net vampire'. + my $ctx = $self->provision_raw_prepare($prefix, "domain controller", + $name, + $dcvars->{DOMAIN}, + $dcvars->{REALM}, + $dcvars->{DOMSID}, + $fl, + $dcvars->{PASSWORD}, + $dcvars->{SERVER_IP}, + $dcvars->{SERVER_IPV6}); + + $ctx->{smb_conf_extra_options} = " + max xmit = 32K + server max protocol = SMB2 + + ntlm auth = mschapv2-and-ntlmv2-only + $extra_conf + +[sysvol] + path = $ctx->{statedir}/sysvol + read only = yes + +[netlogon] + path = $ctx->{statedir}/sysvol/$ctx->{dnsname}/scripts + read only = no + +"; + + my $ret = $self->provision_raw_step1($ctx); + unless ($ret) { + return undef; + } + + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + my $cmd = $self->get_cmd_env_vars($ret); + $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} DC --realm=$dcvars->{REALM}"; + $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD} --domain-critical-only"; + $cmd .= " --machinepass=machine$ret->{PASSWORD}"; + $cmd .= " --backend-store=$self->{default_ldb_backend}"; + + unless (system($cmd) == 0) { + warn("Join failed\n$cmd"); + return undef; + } + + $self->set_pdc_env_vars($ret, $dcvars); + $ret->{DC_REALM} = $dcvars->{DC_REALM}; + + return $ret; +} + +sub provision_ad_dc_ntvfs($$$) +{ + my ($self, $prefix, $extra_provision_options) = @_; + + # We keep the old 'winbind' name here in server services to + # ensure upgrades which used that name still work with the now + # alias. + + print "PROVISIONING AD DC (NTVFS)...\n"; + my $extra_conf_options = "netbios aliases = localDC1-a + server services = +winbind -winbindd + ldap server require strong auth = allow_sasl_over_tls + raw NTLMv2 auth = yes + lsa over netlogon = yes + rpc server port = 1027 + auth event notification = true + dsdb event notification = true + dsdb password event notification = true + dsdb group change notification = true + # override the new SMB2 only default + client min protocol = CORE + server min protocol = LANMAN1 + + CVE_2020_1472:warn_about_unused_debug_level = 3 + CVE_2022_38023:warn_about_unused_debug_level = 3 + allow nt4 crypto:torturetest\$ = yes + server reject md5 schannel:schannel2\$ = no + server reject md5 schannel:schannel3\$ = no + server reject md5 schannel:schannel8\$ = no + server reject md5 schannel:schannel9\$ = no + server reject md5 schannel:torturetest\$ = no + server reject md5 schannel:tests4u2proxywk\$ = no + server reject md5 schannel:tests4u2selfbdc\$ = no + server reject md5 schannel:tests4u2selfwk\$ = no + server reject md5 schannel:torturepacbdc\$ = no + server reject md5 schannel:torturepacwksta\$ = no + server require schannel:schannel0\$ = no + server require schannel:schannel1\$ = no + server require schannel:schannel2\$ = no + server require schannel:schannel3\$ = no + server require schannel:schannel4\$ = no + server require schannel:schannel5\$ = no + server require schannel:schannel6\$ = no + server require schannel:schannel7\$ = no + server require schannel:schannel8\$ = no + server require schannel:schannel9\$ = no + server require schannel:schannel10\$ = no + server require schannel:schannel11\$ = no + server require schannel:torturetest\$ = no + server schannel require seal:schannel0\$ = no + server schannel require seal:schannel1\$ = no + server schannel require seal:schannel2\$ = no + server schannel require seal:schannel3\$ = no + server schannel require seal:schannel4\$ = no + server schannel require seal:schannel5\$ = no + server schannel require seal:schannel6\$ = no + server schannel require seal:schannel7\$ = no + server schannel require seal:schannel8\$ = no + server schannel require seal:schannel9\$ = no + server schannel require seal:schannel10\$ = no + server schannel require seal:schannel11\$ = no + server schannel require seal:torturetest\$ = no + + # needed for 'samba.tests.auth_log' tests + server require schannel:LOCALDC\$ = no + server schannel require seal:LOCALDC\$ = no + "; + push (@{$extra_provision_options}, + "--base-schema=2008_R2", + "--use-ntvfs"); + my $ret = $self->provision($prefix, + "domain controller", + "localdc", + "SAMBADOMAIN", + "samba.example.com", + "2008", + "locDCpass1", + undef, + undef, + undef, + $extra_conf_options, + "", + $extra_provision_options); + unless ($ret) { + return undef; + } + + unless($self->add_wins_config("$prefix/private")) { + warn("Unable to add wins configuration"); + return undef; + } + $ret->{NETBIOSALIAS} = "localdc1-a"; + $ret->{DC_REALM} = $ret->{REALM}; + + return $ret; +} + +sub provision_fl2000dc($$) +{ + my ($self, $prefix) = @_; + + print "PROVISIONING DC WITH FOREST LEVEL 2000...\n"; + my $extra_conf_options = " + kdc enable fast = no + spnego:simulate_w2k=yes + ntlmssp_server:force_old_spnego=yes + + CVE_2022_38023:warn_about_unused_debug_level = 3 + server reject md5 schannel:tests4u2proxywk\$ = no + server reject md5 schannel:tests4u2selfbdc\$ = no + server reject md5 schannel:tests4u2selfwk\$ = no + server reject md5 schannel:torturepacbdc\$ = no + server reject md5 schannel:torturepacwksta\$ = no +"; + my $extra_provision_options = ["--base-schema=2008_R2"]; + # This environment uses plain text secrets + # i.e. secret attributes are not encrypted on disk. + # This allows testing of the --plaintext-secrets option for + # provision + push (@{$extra_provision_options}, "--plaintext-secrets"); + my $ret = $self->provision($prefix, + "domain controller", + "dc5", + "SAMBA2000", + "samba2000.example.com", + "2000", + "locDCpass5", + undef, + undef, + undef, + $extra_conf_options, + "", + $extra_provision_options); + unless ($ret) { + return undef; + } + + unless($self->add_wins_config("$prefix/private")) { + warn("Unable to add wins configuration"); + return undef; + } + $ret->{DC_REALM} = $ret->{REALM}; + + return $ret; +} + +sub provision_fl2003dc($$$) +{ + my ($self, $prefix, $dcvars) = @_; + my $ip_addr1 = Samba::get_ipv4_addr("fakednsforwarder1"); + my $ip_addr2 = Samba::get_ipv6_addr("fakednsforwarder2"); + + print "PROVISIONING DC WITH FOREST LEVEL 2003...\n"; + my $extra_conf_options = " + allow dns updates = nonsecure and secure + + kdc enable fast = no + dcesrv:header signing = no + dcesrv:max auth states = 0 + + dns forwarder = $ip_addr1 [$ip_addr2]:54 + + CVE_2022_38023:warn_about_unused_debug_level = 3 + server reject md5 schannel:tests4u2proxywk\$ = no + server reject md5 schannel:tests4u2selfbdc\$ = no + server reject md5 schannel:tests4u2selfwk\$ = no + server reject md5 schannel:torturepacbdc\$ = no + server reject md5 schannel:torturepacwksta\$ = no +"; + + my $extra_provision_options = ["--base-schema=2008_R2"]; + my $ret = $self->provision($prefix, + "domain controller", + "dc6", + "SAMBA2003", + "samba2003.example.com", + "2003", + "locDCpass6", + undef, + undef, + undef, + $extra_conf_options, + "", + $extra_provision_options); + unless (defined $ret) { + return undef; + } + + $ret->{DNS_FORWARDER1} = $ip_addr1; + $ret->{DNS_FORWARDER2} = $ip_addr2; + + my @samba_tool_options; + push (@samba_tool_options, Samba::bindir_path($self, "samba-tool")); + push (@samba_tool_options, "domain"); + push (@samba_tool_options, "passwordsettings"); + push (@samba_tool_options, "set"); + push (@samba_tool_options, "--configfile=$ret->{SERVERCONFFILE}"); + push (@samba_tool_options, "--min-pwd-age=0"); + push (@samba_tool_options, "--history-length=1"); + + my $samba_tool_cmd = join(" ", @samba_tool_options); + + unless (system($samba_tool_cmd) == 0) { + warn("Unable to set min password age to 0: \n$samba_tool_cmd\n"); + return undef; + } + + unless($self->add_wins_config("$prefix/private")) { + warn("Unable to add wins configuration"); + return undef; + } + + return $ret; +} + +sub provision_fl2008r2dc($$$) +{ + my ($self, $prefix, $dcvars) = @_; + + print "PROVISIONING DC WITH FOREST LEVEL 2008r2...\n"; + my $extra_conf_options = " + ldap server require strong auth = no + # delay by 10 seconds, 10^7 usecs + ldap_server:delay_expire_disconnect = 10000 + + CVE_2022_38023:warn_about_unused_debug_level = 3 + server reject md5 schannel:tests4u2proxywk\$ = no + server reject md5 schannel:tests4u2selfbdc\$ = no + server reject md5 schannel:tests4u2selfwk\$ = no + server reject md5 schannel:torturepacbdc\$ = no + server reject md5 schannel:torturepacwksta\$ = no +"; + my $extra_provision_options = ["--base-schema=2008_R2"]; + my $ret = $self->provision($prefix, + "domain controller", + "dc7", + "SAMBA2008R2", + "samba2008R2.example.com", + "2008_R2", + "locDCpass7", + undef, + undef, + undef, + $extra_conf_options, + "", + $extra_provision_options); + unless (defined $ret) { + return undef; + } + + unless ($self->add_wins_config("$prefix/private")) { + warn("Unable to add wins configuration"); + return undef; + } + $ret->{DC_REALM} = $ret->{REALM}; + + return $ret; +} + + +sub provision_rodc($$$) +{ + my ($self, $prefix, $dcvars) = @_; + print "PROVISIONING RODC...\n"; + + # We do this so that we don't run the provision. That's the job of 'net join RODC'. + my $ctx = $self->provision_raw_prepare($prefix, "domain controller", + "rodc", + $dcvars->{DOMAIN}, + $dcvars->{REALM}, + $dcvars->{DOMSID}, + "2008", + $dcvars->{PASSWORD}, + $dcvars->{SERVER_IP}, + $dcvars->{SERVER_IPV6}); + unless ($ctx) { + return undef; + } + + $ctx->{share} = "$ctx->{prefix_abs}/share"; + push(@{$ctx->{directories}}, "$ctx->{share}"); + + $ctx->{smb_conf_extra_options} = " + max xmit = 32K + server max protocol = SMB2 + password server = $dcvars->{DC_SERVER} + +[sysvol] + path = $ctx->{statedir}/sysvol + read only = yes + +[netlogon] + path = $ctx->{statedir}/sysvol/$ctx->{dnsname}/scripts + read only = yes + +[tmp] + path = $ctx->{share} + read only = no + posix:sharedelay = 10000 + posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 50000 + +"; + + my $ret = $self->provision_raw_step1($ctx); + unless ($ret) { + return undef; + } + + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + my $cmd = $self->get_cmd_env_vars($ret); + $cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} RODC"; + $cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}"; + $cmd .= " --server=$dcvars->{DC_SERVER}"; + + unless (system($cmd) == 0) { + warn("RODC join failed\n$cmd"); + return undef; + } + + # This ensures deterministic behaviour for tests that want to have the 'testallowed account' + # user password verified on the RODC + my $testallowed_account = "testallowed account"; + $cmd = $self->get_cmd_env_vars($ret); + $cmd .= "$samba_tool rodc preload '$testallowed_account' $ret->{CONFIGURATION}"; + $cmd .= " --server=$dcvars->{DC_SERVER}"; + + unless (system($cmd) == 0) { + warn("RODC join failed\n$cmd"); + return undef; + } + + # we overwrite the kdc after the RODC join + # so that use the RODC as kdc and test + # the proxy code + $ctx->{kdc_ipv4} = $ret->{SERVER_IP}; + $ctx->{kdc_ipv6} = $ret->{SERVER_IPV6}; + Samba::mk_krb5_conf($ctx); + Samba::mk_mitkdc_conf($ctx, abs_path(Samba::bindir_path($self, "shared"))); + + $self->set_pdc_env_vars($ret, $dcvars); + + return $ret; +} + +sub read_config_h($) +{ + my ($name) = @_; + my %ret; + open(LF, "<$name") or die("unable to read $name: $!"); + while (<LF>) { + chomp; + next if not (/^#define /); + if (/^#define (.*?)[ \t]+(.*?)$/) { + $ret{$1} = $2; + next; + } + if (/^#define (.*?)[ \t]+$/) { + $ret{$1} = 1;; + next; + } + } + close(LF); + return \%ret; +} + +sub provision_ad_dc() +{ + my ($self, + $prefix, + $hostname, + $domain, + $realm, + $force_fips_mode, + $smbconf_args, + $extra_provision_options, + $functional_level) = @_; + + my $prefix_abs = abs_path($prefix); + + my $bindir_abs = abs_path($self->{bindir}); + my $lockdir="$prefix_abs/lockdir"; + my $conffile="$prefix_abs/etc/smb.conf"; + + my $require_mutexes = "dbwrap_tdb_require_mutexes:* = yes"; + if ($ENV{SELFTEST_DONT_REQUIRE_TDB_MUTEX_SUPPORT} // '' eq "1") { + $require_mutexes = ""; + } + + my $config_h = {}; + + if (!defined($functional_level)) { + $functional_level = "2016"; + } + + # If we choose to have distinct environments for experimental + # 2012 as well as the experimental 2016 support, we should + # extend what we match here. + if ($functional_level eq "2016") { + $smbconf_args = "$smbconf_args + +[global] + ad dc functional level = 2016 +"; + } + if (defined($ENV{CONFIG_H})) { + $config_h = read_config_h($ENV{CONFIG_H}); + } + + my $password_hash_gpg_key_ids = "password hash gpg key ids = 4952E40301FAB41A"; + $password_hash_gpg_key_ids = "" unless defined($config_h->{HAVE_GPGME}); + + my $extra_smbconf_options = " + xattr_tdb:file = $prefix_abs/statedir/xattr.tdb + + dbwrap_tdb_mutexes:* = yes + ${require_mutexes} + + ${password_hash_gpg_key_ids} + + kernel oplocks = no + kernel change notify = no + smb2 leases = no + smb2 disable oplock break retry = yes + server multi channel support = yes + + logging = file + printing = bsd + printcap name = /dev/null + + max protocol = SMB3 + read only = no + + smbd:sharedelay = 100000 + smbd:writetimeupdatedelay = 500000 + create mask = 755 + dos filemode = yes + check parent directory delete on close = yes + + dcerpc endpoint servers = -winreg -srvsvc + + printcap name = /dev/null + + addprinter command = $ENV{SRCDIR_ABS}/source3/script/tests/printing/modprinter.pl -a -s $conffile -- + deleteprinter command = $ENV{SRCDIR_ABS}/source3/script/tests/printing/modprinter.pl -d -s $conffile -- + + printing = vlp + print command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb print %p %s + lpq command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb lpq %p + lp rm command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb lprm %p %j + lp pause command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb lppause %p %j + lp resume command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb lpresume %p %j + queue pause command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb queuepause %p + queue resume command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb queueresume %p + lpq cache time = 0 + print notify backchannel = yes + + CVE_2020_1472:warn_about_unused_debug_level = 3 + CVE_2022_38023:warn_about_unused_debug_level = 3 + CVE_2022_38023:error_debug_level = 2 + server reject md5 schannel:schannel2\$ = no + server reject md5 schannel:schannel3\$ = no + server reject md5 schannel:schannel8\$ = no + server reject md5 schannel:schannel9\$ = no + server reject md5 schannel:torturetest\$ = no + server reject md5 schannel:tests4u2proxywk\$ = no + server reject md5 schannel:tests4u2selfbdc\$ = no + server reject md5 schannel:tests4u2selfwk\$ = no + server reject md5 schannel:torturepacbdc\$ = no + server reject md5 schannel:torturepacwksta\$ = no + server reject md5 schannel:samlogontest\$ = no + server require schannel:schannel0\$ = no + server require schannel:schannel1\$ = no + server require schannel:schannel2\$ = no + server require schannel:schannel3\$ = no + server require schannel:schannel4\$ = no + server require schannel:schannel5\$ = no + server require schannel:schannel6\$ = no + server require schannel:schannel7\$ = no + server require schannel:schannel8\$ = no + server require schannel:schannel9\$ = no + server require schannel:schannel10\$ = no + server require schannel:schannel11\$ = no + server require schannel:torturetest\$ = no + server schannel require seal:schannel0\$ = no + server schannel require seal:schannel1\$ = no + server schannel require seal:schannel2\$ = no + server schannel require seal:schannel3\$ = no + server schannel require seal:schannel4\$ = no + server schannel require seal:schannel5\$ = no + server schannel require seal:schannel6\$ = no + server schannel require seal:schannel7\$ = no + server schannel require seal:schannel8\$ = no + server schannel require seal:schannel9\$ = no + server schannel require seal:schannel10\$ = no + server schannel require seal:schannel11\$ = no + server schannel require seal:torturetest\$ = no + + auth event notification = true + dsdb event notification = true + dsdb password event notification = true + dsdb group change notification = true + $smbconf_args +"; + + my $extra_smbconf_shares = " + +[tmpenc] + copy = tmp + smb encrypt = required + +[tmpcase] + copy = tmp + case sensitive = yes + +[tmpguest] + copy = tmp + guest ok = yes + +[hideunread] + copy = tmp + hide unreadable = yes + +[durable] + copy = tmp + kernel share modes = no + kernel oplocks = no + posix locking = no + +[print\$] + copy = tmp + +[print1] + copy = tmp + printable = yes + +[print2] + copy = print1 +[print3] + copy = print1 +[print4] + copy = print1 + guest ok = yes +[lp] + copy = print1 +"; + + push (@{$extra_provision_options}, "--backend-store=$self->{default_ldb_backend}"); + print "PROVISIONING AD DC...\n"; + my $ret = $self->provision($prefix, + "domain controller", + $hostname, + $domain, + $realm, + $functional_level, + "locDCpass1", + undef, + undef, + $force_fips_mode, + $extra_smbconf_options, + $extra_smbconf_shares, + $extra_provision_options); + unless (defined $ret) { + return undef; + } + + unless($self->add_wins_config("$prefix/private")) { + warn("Unable to add wins configuration"); + return undef; + } + + return $ret; +} + +sub provision_chgdcpass($$) +{ + my ($self, $prefix) = @_; + + print "PROVISIONING CHGDCPASS...\n"; + # This environment disallows the use of this password + # (and also removes the default AD complexity checks) + my $unacceptable_password = "Paßßword-widk3Dsle32jxdBdskldsk55klASKQ"; + + # This environment also sets some settings that are unusual, + # to test specific behaviours. In particular, this + # environment fails to correctly support DRSUAPI_DRS_GET_ANC + # like Samba before 4.5 and DRSUAPI_DRS_GET_TGT before 4.8 + # + # Additionally, disabling DRSUAPI_DRS_GET_TGT causes all links + # to be sent last (in the final chunk), which is like Samba + # before 4.8. + + my $extra_smb_conf = " + check password script = $self->{srcdir}/selftest/checkpassword_arg1.sh ${unacceptable_password} + allow dcerpc auth level connect:lsarpc = yes + dcesrv:max auth states = 8 + drs:broken_samba_4.5_get_anc_emulation = true + drs:get_tgt_support = false +"; + my $extra_provision_options = ["--dns-backend=BIND9_DLZ"]; + my $ret = $self->provision($prefix, + "domain controller", + "chgdcpass", + "CHDCDOMAIN", + "chgdcpassword.samba.example.com", + "2008", + "chgDCpass1", + undef, + undef, + undef, + $extra_smb_conf, + "", + $extra_provision_options); + unless (defined $ret) { + return undef; + } + + unless($self->add_wins_config("$prefix/private")) { + warn("Unable to add wins configuration"); + return undef; + } + + # Remove secrets.tdb from this environment to test that we + # still start up on systems without the new matching + # secrets.tdb records. + unless (unlink("$ret->{PRIVATEDIR}/secrets.tdb") || unlink("$ret->{PRIVATEDIR}/secrets.ntdb")) { + warn("Unable to remove $ret->{PRIVATEDIR}/secrets.tdb added during provision"); + return undef; + } + + $ret->{UNACCEPTABLE_PASSWORD} = $unacceptable_password; + + return $ret; +} + +sub teardown_env_terminate($$) +{ + my ($self, $envvars) = @_; + my $pid; + + # This should cause samba to terminate gracefully + my $smbcontrol = Samba::bindir_path($self, "smbcontrol"); + my $cmd = ""; + $cmd .= "$smbcontrol samba shutdown $envvars->{CONFIGURATION}"; + my $ret = system($cmd); + if ($ret != 0) { + warn "'$cmd' failed with '$ret'\n"; + } + + # This should cause samba to terminate gracefully + close($envvars->{STDIN_PIPE}); + + $pid = $envvars->{SAMBA_PID}; + my $count = 0; + my $childpid; + + # This should give it time to write out the gcov data + until ($count > 15) { + if (Samba::cleanup_child($pid, "samba") != 0) { + return; + } + sleep(1); + $count++; + } + + # After 15 Seconds, work out why this thing is still alive + warn "server process $pid took more than $count seconds to exit, showing backtrace:\n"; + system("$self->{srcdir}/selftest/gdb_backtrace $pid"); + + until ($count > 30) { + if (Samba::cleanup_child($pid, "samba") != 0) { + return; + } + sleep(1); + $count++; + } + + if (kill(0, $pid)) { + warn "server process $pid took more than $count seconds to exit, sending SIGTERM\n"; + kill "TERM", $pid; + } + + until ($count > 40) { + if (Samba::cleanup_child($pid, "samba") != 0) { + return; + } + sleep(1); + $count++; + } + # If it is still around, kill it + if (kill(0, $pid)) { + warn "server process $pid took more than $count seconds to exit, killing\n with SIGKILL\n"; + kill 9, $pid; + } + return; +} + +sub teardown_env($$) +{ + my ($self, $envvars) = @_; + teardown_env_terminate($self, $envvars); + + print $self->getlog_env($envvars); + + return; +} + +sub getlog_env($$) +{ + my ($self, $envvars) = @_; + my $title = "SAMBA LOG of: $envvars->{NETBIOSNAME} pid $envvars->{SAMBA_PID}\n"; + my $out = $title; + + open(LOG, "<$envvars->{SAMBA_TEST_LOG}"); + + seek(LOG, $envvars->{SAMBA_TEST_LOG_POS}, SEEK_SET); + while (<LOG>) { + $out .= $_; + } + $envvars->{SAMBA_TEST_LOG_POS} = tell(LOG); + close(LOG); + + return "" if $out eq $title; + + return $out; +} + +sub check_env($$) +{ + my ($self, $envvars) = @_; + my $samba_pid = $envvars->{SAMBA_PID}; + + if (not defined($samba_pid)) { + return 0; + } elsif ($samba_pid > 0) { + my $childpid = Samba::cleanup_child($samba_pid, "samba"); + + if ($childpid == 0) { + return 1; + } + return 0; + } else { + return 1; + } +} + +# Declare the environments Samba4 makes available. +# To be set up, they will be called as +# samba4->setup_$envname($self, $path, $dep_1_vars, $dep_2_vars, ...) +# The interdependencies between the testenvs are declared below. Some testenvs +# are dependent on another testenv running first, e.g. vampire_dc is dependent +# on ad_dc_ntvfs because vampire_dc joins ad_dc_ntvfs's domain. All DCs are +# dependent on dns_hub, which handles resolving DNS queries for the realm. +%Samba4::ENV_DEPS = ( + # name => [dep_1, dep_2, ...], + dns_hub => [], + ad_dc_ntvfs => ["dns_hub"], + ad_dc_fips => ["dns_hub"], + ad_dc => ["dns_hub"], + ad_dc_smb1 => ["dns_hub"], + ad_dc_smb1_done => ["ad_dc_smb1"], + ad_dc_no_nss => ["dns_hub"], + ad_dc_no_ntlm => ["dns_hub"], + + fl2008r2dc => ["ad_dc", "nt4_dc"], + fl2003dc => ["ad_dc"], + fl2000dc => ["ad_dc"], + + vampire_2000_dc => ["fl2000dc"], + vampire_dc => ["ad_dc_ntvfs"], + promoted_dc => ["ad_dc_ntvfs"], + + rodc => ["ad_dc_ntvfs"], + rpc_proxy => ["ad_dc_ntvfs"], + chgdcpass => ["dns_hub"], + + s4member_dflt_domain => ["ad_dc_ntvfs"], + s4member => ["ad_dc_ntvfs"], + + # envs that test the server process model + proclimitdc => ["dns_hub"], + preforkrestartdc => ["dns_hub"], + + # backup/restore testenvs + backupfromdc => ["dns_hub"], + customdc => ["dns_hub"], + restoredc => ["backupfromdc"], + renamedc => ["backupfromdc"], + offlinebackupdc => ["backupfromdc"], + labdc => ["backupfromdc"], + + # aliases in order to split autobuild tasks + fl2008dc => ["ad_dc_ntvfs"], + ad_dc_default => ["ad_dc"], + ad_dc_default_smb1 => ["ad_dc_smb1"], + ad_dc_default_smb1_done => ["ad_dc_default_smb1"], + ad_dc_slowtests => ["ad_dc"], + ad_dc_backup => ["ad_dc"], + + schema_dc => ["dns_hub"], + schema_pair_dc => ["schema_dc"], + + none => [], +); + +%Samba4::ENV_DEPS_POST = ( + schema_dc => ["schema_pair_dc"], +); + +sub return_alias_env +{ + my ($self, $path, $env) = @_; + + # just an alias + return $env; +} + +sub setup_fl2008dc +{ + my ($self, $path, $dep_env) = @_; + return $self->return_alias_env($path, $dep_env) +} + +sub setup_ad_dc_default +{ + my ($self, $path, $dep_env) = @_; + return $self->return_alias_env($path, $dep_env) +} + +sub setup_ad_dc_default_smb1 +{ + my ($self, $path, $dep_env) = @_; + return $self->return_alias_env($path, $dep_env) +} + +sub setup_ad_dc_default_smb1_done +{ + my ($self, $path, $dep_env) = @_; + return $self->return_alias_env($path, $dep_env) +} + +sub setup_ad_dc_slowtests +{ + my ($self, $path, $dep_env) = @_; + return $self->return_alias_env($path, $dep_env) +} + +sub setup_ad_dc_backup +{ + my ($self, $path, $dep_env) = @_; + return $self->return_alias_env($path, $dep_env) +} + +sub setup_s4member +{ + my ($self, $path, $dc_vars) = @_; + + my $env = $self->provision_s4member($path, $dc_vars, "s4member"); + + if (defined $env) { + if (not defined($self->check_or_start($env, "standard"))) { + return undef; + } + } + + return $env; +} + +sub setup_s4member_dflt_domain +{ + my ($self, $path, $dc_vars) = @_; + + my $env = $self->provision_s4member($path, $dc_vars, "s4member_dflt", + "winbind use default domain = yes"); + + if (defined $env) { + if (not defined($self->check_or_start($env, "standard"))) { + return undef; + } + } + + return $env; +} + +sub setup_rpc_proxy +{ + my ($self, $path, $dc_vars) = @_; + + my $env = $self->provision_rpc_proxy($path, $dc_vars); + + if (defined $env) { + if (not defined($self->check_or_start($env, "standard"))) { + return undef; + } + } + return $env; +} + +sub setup_ad_dc_ntvfs +{ + my ($self, $path) = @_; + + my $env = $self->provision_ad_dc_ntvfs($path, undef); + if (defined $env) { + if (not defined($self->check_or_start($env, "standard"))) { + warn("Failed to start ad_dc_ntvfs"); + return undef; + } + } + return $env; +} + +sub setup_chgdcpass +{ + my ($self, $path) = @_; + + my $env = $self->provision_chgdcpass($path); + if (defined $env) { + if (not defined($self->check_or_start($env, "standard"))) { + return undef; + } + } + return $env; +} + +sub setup_fl2000dc +{ + my ($self, $path, $dc_vars) = @_; + + my $env = $self->provision_fl2000dc($path); + if (defined $env) { + if (not defined($self->check_or_start($env, "standard"))) { + return undef; + } + + $env = $self->setup_trust($env, $dc_vars, "external", "--no-aes-keys --direction=outgoing"); + } + + return $env; +} + +sub setup_fl2003dc +{ + my ($self, $path, $dc_vars) = @_; + + my $env = $self->provision_fl2003dc($path); + + if (defined $env) { + if (not defined($self->check_or_start($env, "standard"))) { + return undef; + } + + $env = $self->setup_trust($env, $dc_vars, "external", "--no-aes-keys"); + } + return $env; +} + +sub setup_fl2008r2dc +{ + my ($self, $path, $ad_dc_vars, $nt4_dc_vars) = @_; + + my $env = $self->provision_fl2008r2dc($path); + + if (!defined $env) { + return $env; + } + + if (not defined($self->check_or_start($env, "standard"))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) { + return undef; + } + + $env = $self->setup_trust($env, $ad_dc_vars, "forest", ""); + if (!defined $env) { + return undef; + } + + my $net = Samba::bindir_path($self, "net"); + my $smbcontrol = Samba::bindir_path($self, "smbcontrol"); + + my $trustpw = "TrUsTpW"; + $trustpw .= "$env->{SOCKET_WRAPPER_DEFAULT_IFACE}"; + $trustpw .= "$nt4_dc_vars->{SOCKET_WRAPPER_DEFAULT_IFACE}"; + + my $cmd = ""; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$env->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$env->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$net rpc trust create "; + $cmd .= "otherdomainsid=$nt4_dc_vars->{SAMSID} "; + $cmd .= "otherdomain=$nt4_dc_vars->{DOMAIN} "; + $cmd .= "other_netbios_domain=$nt4_dc_vars->{DOMAIN} "; + $cmd .= "trustpw=$trustpw "; + $cmd .= "$env->{CONFIGURATION} "; + $cmd .= "-U $env->{DOMAIN}/$env->{USERNAME}\%$env->{PASSWORD} "; + + if (system($cmd) != 0) { + warn("net rpc trust create failed\n$cmd"); + return undef; + } + + $cmd = ""; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$nt4_dc_vars->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$nt4_dc_vars->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$net rpc trustdom establish $env->{DOMAIN} -U/%$trustpw $nt4_dc_vars->{CONFIGURATION}"; + + if (system($cmd) != 0) { + warn("add failed\n$cmd"); + return undef; + } + + # Reload trusts + $cmd = "$smbcontrol winbindd reload-config $nt4_dc_vars->{CONFIGURATION}"; + + if (system($cmd) != 0) { + warn("add failed\n$cmd"); + return undef; + } + + $env->{NT4_TRUST_SERVER} = $nt4_dc_vars->{SERVER}; + $env->{NT4_TRUST_SERVER_IP} = $nt4_dc_vars->{SERVER_IP}; + $env->{NT4_TRUST_DOMAIN} = $nt4_dc_vars->{DOMAIN}; + $env->{NT4_TRUST_DOMSID} = $nt4_dc_vars->{DOMSID}; + + return $env; +} + +sub setup_vampire_dc +{ + return setup_generic_vampire_dc(@_, "2008"); +} + +sub setup_vampire_2000_dc +{ + return setup_generic_vampire_dc(@_, "2000"); +} + +sub setup_generic_vampire_dc +{ + my ($self, $path, $dc_vars, $fl) = @_; + + my $env = $self->provision_vampire_dc($path, $dc_vars, $fl); + + if (defined $env) { + if (not defined($self->check_or_start($env, "single"))) { + return undef; + } + + # force replicated DC to update repsTo/repsFrom + # for vampired partitions + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + + # as 'vampired' dc may add data in its local replica + # we need to synchronize data between DCs + my $base_dn = "DC=".join(",DC=", split(/\./, $dc_vars->{REALM})); + my $cmd = $self->get_cmd_env_vars($env); + $cmd .= " $samba_tool drs replicate $env->{DC_SERVER} $env->{SERVER}"; + $cmd .= " $dc_vars->{CONFIGURATION}"; + $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}"; + # replicate Configuration NC + my $cmd_repl = "$cmd \"CN=Configuration,$base_dn\""; + unless(system($cmd_repl) == 0) { + warn("Failed to replicate\n$cmd_repl"); + return undef; + } + # replicate Default NC + $cmd_repl = "$cmd \"$base_dn\""; + unless(system($cmd_repl) == 0) { + warn("Failed to replicate\n$cmd_repl"); + return undef; + } + + # Pull in a full set of changes from the main DC + $base_dn = "DC=".join(",DC=", split(/\./, $dc_vars->{REALM})); + $cmd = $self->get_cmd_env_vars($env); + $cmd .= " $samba_tool drs replicate $env->{SERVER} $env->{DC_SERVER}"; + $cmd .= " $dc_vars->{CONFIGURATION}"; + $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}"; + # replicate Configuration NC + $cmd_repl = "$cmd \"CN=Configuration,$base_dn\""; + unless(system($cmd_repl) == 0) { + warn("Failed to replicate\n$cmd_repl"); + return undef; + } + # replicate Default NC + $cmd_repl = "$cmd \"$base_dn\""; + unless(system($cmd_repl) == 0) { + warn("Failed to replicate\n$cmd_repl"); + return undef; + } + } + + return $env; +} + +sub setup_promoted_dc +{ + my ($self, $path, $dc_vars) = @_; + + my $env = $self->provision_promoted_dc($path, $dc_vars); + + if (defined $env) { + if (not defined($self->check_or_start($env, "single"))) { + return undef; + } + + # force source and replicated DC to update repsTo/repsFrom + # for vampired partitions + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + my $cmd = $self->get_cmd_env_vars($env); + # as 'vampired' dc may add data in its local replica + # we need to synchronize data between DCs + my $base_dn = "DC=".join(",DC=", split(/\./, $dc_vars->{REALM})); + $cmd .= " $samba_tool drs replicate $env->{DC_SERVER} $env->{SERVER}"; + $cmd .= " $dc_vars->{CONFIGURATION}"; + $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}"; + # replicate Configuration NC + my $cmd_repl = "$cmd \"CN=Configuration,$base_dn\""; + unless(system($cmd_repl) == 0) { + warn("Failed to replicate\n$cmd_repl"); + return undef; + } + # replicate Default NC + $cmd_repl = "$cmd \"$base_dn\""; + unless(system($cmd_repl) == 0) { + warn("Failed to replicate\n$cmd_repl"); + return undef; + } + } + + return $env; +} + +sub setup_rodc +{ + my ($self, $path, $dc_vars) = @_; + + my $env = $self->provision_rodc($path, $dc_vars); + + unless ($env) { + return undef; + } + + if (not defined($self->check_or_start($env, "standard"))) { + return undef; + } + + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + my $cmd = $self->get_cmd_env_vars($env); + + my $base_dn = "DC=".join(",DC=", split(/\./, $dc_vars->{REALM})); + $cmd .= " $samba_tool drs replicate $env->{SERVER} $env->{DC_SERVER}"; + $cmd .= " $dc_vars->{CONFIGURATION}"; + $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}"; + # replicate Configuration NC + my $cmd_repl = "$cmd \"CN=Configuration,$base_dn\""; + unless(system($cmd_repl) == 0) { + warn("Failed to replicate\n$cmd_repl"); + return undef; + } + # replicate Default NC + $cmd_repl = "$cmd \"$base_dn\""; + unless(system($cmd_repl) == 0) { + warn("Failed to replicate\n$cmd_repl"); + return undef; + } + + return $env; +} + +sub _setup_ad_dc +{ + my ($self, $path, $conf_opts, $server, $dom, $functional_level) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->{target3}->have_ads()) { + return "UNKNOWN"; + } + + if (!defined($conf_opts)) { + $conf_opts = ""; + } + if (!defined($server)) { + $server = "addc"; + } + if (!defined($dom)) { + $dom = "addom.samba.example.com"; + } + my $env = $self->provision_ad_dc($path, $server, "ADDOMAIN", + $dom, + undef, + $conf_opts, + undef, + $functional_level); + unless ($env) { + return undef; + } + + if (not defined($self->check_or_start($env, "prefork"))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) { + return undef; + } + + return $env; +} + +sub setup_ad_dc +{ + my ($self, $path) = @_; + return _setup_ad_dc($self, $path, undef, undef, undef); +} + +sub setup_ad_dc_smb1 +{ + my ($self, $path) = @_; + my $conf_opts = " +[global] + client min protocol = CORE + server min protocol = LANMAN1 + + # needed for 'samba.tests.auth_log' tests + server require schannel:ADDCSMB1\$ = no + server schannel require seal:ADDCSMB1\$ = no +"; + return _setup_ad_dc($self, $path, $conf_opts, "addcsmb1", "addom2.samba.example.com"); +} + +sub setup_ad_dc_smb1_done +{ + my ($self, $path, $dep_env) = @_; + return $self->return_alias_env($path, $dep_env); +} + +sub setup_ad_dc_no_nss +{ + my ($self, $path) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->{target3}->have_ads()) { + return "UNKNOWN"; + } + + my $env = $self->provision_ad_dc($path, + "addc_no_nss", + "ADNONSSDOMAIN", + "adnonssdom.samba.example.com", + undef, + "", + undef); + unless ($env) { + return undef; + } + + $env->{NSS_WRAPPER_MODULE_SO_PATH} = undef; + $env->{NSS_WRAPPER_MODULE_FN_PREFIX} = undef; + + if (not defined($self->check_or_start($env, "single"))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) { + return undef; + } + + return $env; +} + +sub setup_ad_dc_no_ntlm +{ + my ($self, $path) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->{target3}->have_ads()) { + return "UNKNOWN"; + } + + my $env = $self->provision_ad_dc($path, + "addc_no_ntlm", + "ADNONTLMDOMAIN", + "adnontlmdom.samba.example.com", + undef, + "ntlm auth = disabled\nnt hash store = never", + undef); + unless ($env) { + return undef; + } + + if (not defined($self->check_or_start($env, "prefork"))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) { + return undef; + } + + return $env; +} + +sub setup_ad_dc_fips +{ + my ($self, $path) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->{target3}->have_ads()) { + return "UNKNOWN"; + } + + my $env = $self->provision_ad_dc($path, + "fipsdc", + "FIPSDOMAIN", + "fips.samba.example.com", + 1, + "", + undef); + unless ($env) { + return undef; + } + + if (not defined($self->check_or_start($env, "prefork"))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) { + return undef; + } + + return $env; +} + +# +# AD DC test environment used solely to test pre-fork process restarts. +# As processes get killed off and restarted it should not be used for other +sub setup_preforkrestartdc +{ + my ($self, $path) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->{target3}->have_ads()) { + return "UNKNOWN"; + } + + # note DC name must be <= 15 chars so we use 'prockill' instead of + # 'preforkrestart' + my $env = $self->provision_ad_dc($path, + "prockilldc", + "PROCKILLDOMAIN", + "prockilldom.samba.example.com", + undef, + "prefork backoff increment = 5\nprefork maximum backoff=10", + undef); + unless ($env) { + return undef; + } + + # We treat processes in this environment cruelly, sometimes + # sending them SIGSEGV signals. We don't need gdb_backtrace + # dissecting these fake crashes in precise detail. + $env->{PLEASE_NO_GDB_BACKTRACE} = '1'; + + $env->{NSS_WRAPPER_MODULE_SO_PATH} = undef; + $env->{NSS_WRAPPER_MODULE_FN_PREFIX} = undef; + + if (not defined($self->check_or_start($env, "prefork"))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) { + return undef; + } + + return $env; +} + +# +# ad_dc test environment used solely to test standard process model connection +# process limits. As the limit is set artificially low it should not be used +# for other tests. +sub setup_proclimitdc +{ + my ($self, $path) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->{target3}->have_ads()) { + return "UNKNOWN"; + } + + my $env = $self->provision_ad_dc($path, + "proclimitdc", + "PROCLIMITDOM", + "proclimit.samba.example.com", + undef, + "max smbd processes = 20", + undef); + unless ($env) { + return undef; + } + + $env->{NSS_WRAPPER_MODULE_SO_PATH} = undef; + $env->{NSS_WRAPPER_MODULE_FN_PREFIX} = undef; + + if (not defined($self->check_or_start($env, "standard"))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) { + return undef; + } + + return $env; +} + +# Used to test a live upgrade of the schema on a 2 DC network. +sub setup_schema_dc +{ + my ($self, $path) = @_; + + # provision the PDC using an older base schema + my $provision_args = ["--base-schema=2008_R2", "--backend-store=$self->{default_ldb_backend}"]; + + # We set the functional level to 2008_R2 to match the older + # base-schema (to allow schema upgrade to be tested) + my $env = $self->provision_ad_dc($path, + "liveupgrade1dc", + "SCHEMADOMAIN", + "schema.samba.example.com", + undef, + "drs: max link sync = 2", + $provision_args, + "2008_R2"); + unless ($env) { + return undef; + } + + if (not defined($self->check_or_start($env, "prefork"))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) { + return undef; + } + + return $env; +} + +# the second DC in the live schema upgrade pair +sub setup_schema_pair_dc +{ + # note: dcvars contains the env info for the dependent testenv ('schema_dc') + my ($self, $prefix, $dcvars) = @_; + print "Preparing SCHEMA UPGRADE PAIR DC...\n"; + + my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "liveupgrade2dc", + $dcvars->{DOMAIN}, + $dcvars->{REALM}, + $dcvars->{PASSWORD}, + ""); + + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + my $cmd_vars = $self->get_cmd_env_vars($env); + + my $join_cmd = $cmd_vars; + $join_cmd .= "$samba_tool domain join $env->{CONFIGURATION} $dcvars->{REALM} DC --realm=$dcvars->{REALM}"; + $join_cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD} "; + $join_cmd .= " --backend-store=$self->{default_ldb_backend}"; + + my $upgrade_cmd = $cmd_vars; + $upgrade_cmd .= "$samba_tool domain schemaupgrade $dcvars->{CONFIGURATION}"; + $upgrade_cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}"; + + my $repl_cmd = $cmd_vars; + $repl_cmd .= "$samba_tool drs replicate $env->{SERVER} $dcvars->{SERVER}"; + $repl_cmd .= " CN=Schema,CN=Configuration,DC=schema,DC=samba,DC=example,DC=com"; + $repl_cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}"; + + unless (system($join_cmd) == 0) { + warn("Join failed\n$join_cmd"); + return undef; + } + + $env->{DC_SERVER} = $dcvars->{SERVER}; + $env->{DC_SERVER_IP} = $dcvars->{SERVER_IP}; + $env->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6}; + $env->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME}; + + # start samba for the new DC + if (not defined($self->check_or_start($env, "standard"))) { + return undef; + } + + unless (system($upgrade_cmd) == 0) { + warn("Schema upgrade failed\n$upgrade_cmd"); + return undef; + } + + unless (system($repl_cmd) == 0) { + warn("Post-update schema replication failed\n$repl_cmd"); + return undef; + } + + return $env; +} + +# Sets up a DC that's solely used to do a domain backup from. We then use the +# backupfrom-DC to create the restore-DC - this proves that the backup/restore +# process will create a Samba DC that will actually start up. +# We don't use the backup-DC for anything else because its domain will conflict +# with the restore DC. +sub setup_backupfromdc +{ + my ($self, $path) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->{target3}->have_ads()) { + return "UNKNOWN"; + } + + my $provision_args = ["--site=Backup-Site"]; + + my $env = $self->provision_ad_dc($path, + "backupfromdc", + "BACKUPDOMAIN", + "backupdom.samba.example.com", + undef, + "samba kcc command = /bin/true", + $provision_args); + unless ($env) { + return undef; + } + + if (not defined($self->check_or_start($env))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) { + return undef; + } + + # Set up a dangling forward link to an expunged object + # + # We need this to ensure that the "samba-tool domain backup rename" + # that is part of the creation of the labdc environment can + # cope with this situation on the source DC. + + if (not $self->write_ldb_file("$env->{PRIVATEDIR}/sam.ldb", " +dn: ou=linktest,dc=backupdom,dc=samba,dc=example,dc=com +objectclass: organizationalUnit +- + +dn: cn=linkto,ou=linktest,dc=backupdom,dc=samba,dc=example,dc=com +objectclass: msExchConfigurationContainer +- + +dn: cn=linkfrom,ou=linktest,dc=backupdom,dc=samba,dc=example,dc=com +objectclass: msExchConfigurationContainer +addressBookRoots: cn=linkto,ou=linktest,dc=backupdom,dc=samba,dc=example,dc=com +- + +")) { + return undef; + } + my $ldbdel = Samba::bindir_path($self, "ldbdel"); + my $cmd = "$ldbdel -H $env->{PRIVATEDIR}/sam.ldb cn=linkto,ou=linktest,dc=backupdom,dc=samba,dc=example,dc=com"; + + unless(system($cmd) == 0) { + warn("Failed to delete link target: \n$cmd"); + return undef; + } + + # Expunge will ensure that linkto is totally wiped from the DB + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + $cmd = "$samba_tool domain tombstones expunge --tombstone-lifetime=0 $env->{CONFIGURATION}"; + + unless(system($cmd) == 0) { + warn("Failed to expunge link target: \n$cmd"); + return undef; + } + return $env; +} + +# returns the server/user-auth params needed to run an online backup cmd +sub get_backup_server_args +{ + # dcvars contains the env info for the backup DC testenv + my ($self, $dcvars) = @_; + my $server = $dcvars->{DC_SERVER_IP}; + my $server_args = "--server=$server "; + $server_args .= "-U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}"; + $server_args .= " $dcvars->{CONFIGURATION}"; + + return $server_args; +} + +# Creates a backup of a running testenv DC +sub create_backup +{ + # note: dcvars contains the env info for the backup DC testenv + my ($self, $env, $dcvars, $backupdir, $backup_cmd) = @_; + + # get all the env variables we pass in with the samba-tool command + # Note: use the backupfrom-DC's krb5.conf to do the backup + my $overwrite = undef; + $overwrite->{KRB5_CONFIG} = $dcvars->{KRB5_CONFIG}; + my $cmd_env = $self->get_cmd_env_vars($env, $overwrite); + + # use samba-tool to create a backup from the 'backupfromdc' DC + my $cmd = ""; + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + + $cmd .= "$cmd_env $samba_tool domain backup $backup_cmd"; + $cmd .= " --targetdir=$backupdir"; + + print "Executing: $cmd\n"; + unless(system($cmd) == 0) { + warn("Failed to create backup using: \n$cmd"); + return undef; + } + + # get the name of the backup file created + opendir(DIR, $backupdir); + my @files = grep(/\.tar/, readdir(DIR)); + closedir(DIR); + + if(scalar @files != 1) { + warn("Backup file not found in directory $backupdir\n"); + return undef; + } + my $backup_file = "$backupdir/$files[0]"; + print "Using backup file $backup_file...\n"; + + return $backup_file; +} + +# Restores a backup-file to populate a testenv for a new DC +sub restore_backup_file +{ + my ($self, $backup_file, $restore_opts, $restoredir, $smbconf) = @_; + + # pass the restore command the testenv's smb.conf that we've already + # generated. But move it to a temp-dir first, so that the restore doesn't + # overwrite it + my $tmpdir = File::Temp->newdir(); + my $tmpconf = "$tmpdir/smb.conf"; + my $cmd = "cp $smbconf $tmpconf"; + unless(system($cmd) == 0) { + warn("Failed to backup smb.conf using: \n$cmd"); + return -1; + } + + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + $cmd = "$samba_tool domain backup restore --backup-file=$backup_file"; + $cmd .= " --targetdir=$restoredir $restore_opts --configfile=$tmpconf"; + + print "Executing: $cmd\n"; + unless(system($cmd) == 0) { + warn("Failed to restore backup using: \n$cmd"); + return -1; + } + + print "Restore complete\n"; + return 0 +} + +# sets up the initial directory and returns the new testenv's env info +# (without actually doing a 'domain join') +sub prepare_dc_testenv +{ + my ($self, $prefix, $dcname, $domain, $realm, + $password, $conf_options, $dnsupdate_options) = @_; + + my $ctx = $self->provision_raw_prepare($prefix, "domain controller", + $dcname, + $domain, + $realm, + undef, + "2008", + $password, + undef, + undef); + + # the restore uses a slightly different state-dir location to other testenvs + $ctx->{statedir} = "$ctx->{prefix_abs}/state"; + push(@{$ctx->{directories}}, "$ctx->{statedir}"); + + # add support for sysvol/netlogon/tmp shares + $ctx->{share} = "$ctx->{prefix_abs}/share"; + push(@{$ctx->{directories}}, "$ctx->{share}"); + push(@{$ctx->{directories}}, "$ctx->{share}/test1"); + + if (defined($dnsupdate_options)) { + $ctx->{samba_dnsupdate} .= $dnsupdate_options; + } + + $ctx->{smb_conf_extra_options} = " + $conf_options + + # Some of the DCs based on this will be in FL 2016 domains, so + # claim FL 2016 DC capability + ad dc functional level = 2016 + + max xmit = 32K + server max protocol = SMB2 + samba kcc command = /bin/true + xattr_tdb:file = $ctx->{statedir}/xattr.tdb + +[sysvol] + path = $ctx->{statedir}/sysvol + read only = no + +[netlogon] + path = $ctx->{statedir}/sysvol/$ctx->{dnsname}/scripts + read only = no + +[tmp] + path = $ctx->{share} + read only = no + posix:sharedelay = 10000 + posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 50000 + +[test1] + path = $ctx->{share}/test1 + read only = no + posix:sharedelay = 100000 + posix:oplocktimeout = 3 + posix:writetimeupdatedelay = 500000 +"; + + my $env = $self->provision_raw_step1($ctx); + + return ($env, $ctx); +} + + +# Set up a DC testenv solely by using the samba-tool domain backup/restore +# commands. This proves that we can backup an online DC ('backupfromdc') and +# use the backup file to create a valid, working samba DC. +sub setup_restoredc +{ + # note: dcvars contains the env info for the dependent testenv ('backupfromdc') + my ($self, $prefix, $dcvars) = @_; + print "Preparing RESTORE DC...\n"; + + # we arbitrarily designate the restored DC as having SMBv1 disabled + my $extra_conf = " + server min protocol = SMB2 + client min protocol = SMB2 + prefork children = 1"; + my $dnsupdate_options = " --use-samba-tool --no-credentials"; + + my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "restoredc", + $dcvars->{DOMAIN}, + $dcvars->{REALM}, + $dcvars->{PASSWORD}, + $extra_conf, + $dnsupdate_options); + + # create a backup of the 'backupfromdc' + my $backupdir = File::Temp->newdir(); + my $server_args = $self->get_backup_server_args($dcvars); + my $backup_args = "online $server_args"; + my $backup_file = $self->create_backup($env, $dcvars, $backupdir, + $backup_args); + unless($backup_file) { + return undef; + } + + # restore the backup file to populate the restore-DC testenv + my $restore_dir = abs_path($prefix); + my $ret = $self->restore_backup_file($backup_file, + "--newservername=$env->{SERVER}", + $restore_dir, $env->{SERVERCONFFILE}); + unless ($ret == 0) { + return undef; + } + + # + # As we create the same domain as a clone + # we need a separate resolv.conf! + # + $ctx->{resolv_conf} = "$ctx->{etcdir}/resolv.conf"; + $ctx->{dns_ipv4} = $ctx->{ipv4}; + $ctx->{dns_ipv6} = $ctx->{ipv6}; + Samba::mk_resolv_conf($ctx); + $env->{RESOLV_CONF} = $ctx->{resolv_conf}; + + # start samba for the restored DC + if (not defined($self->check_or_start($env))) { + return undef; + } + + return $env; +} + +# Set up a DC testenv solely by using the 'samba-tool domain backup rename' and +# restore commands. This proves that we can backup and rename an online DC +# ('backupfromdc') and use the backup file to create a valid, working samba DC. +sub setup_renamedc +{ + # note: dcvars contains the env info for the dependent testenv ('backupfromdc') + my ($self, $prefix, $dcvars) = @_; + print "Preparing RENAME DC...\n"; + my $extra_conf = "prefork children = 1"; + + my $realm = "renamedom.samba.example.com"; + my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "renamedc", + "RENAMEDOMAIN", $realm, + $dcvars->{PASSWORD}, $extra_conf); + + # create a backup of the 'backupfromdc' which renames the domain + my $backupdir = File::Temp->newdir(); + my $server_args = $self->get_backup_server_args($dcvars); + my $backup_args = "rename $env->{DOMAIN} $env->{REALM} $server_args"; + $backup_args .= " --backend-store=tdb"; + my $backup_file = $self->create_backup($env, $dcvars, $backupdir, + $backup_args); + unless($backup_file) { + return undef; + } + + # restore the backup file to populate the rename-DC testenv + my $restore_dir = abs_path($prefix); + my $restore_opts = "--newservername=$env->{SERVER} --host-ip=$env->{SERVER_IP}"; + my $ret = $self->restore_backup_file($backup_file, $restore_opts, + $restore_dir, $env->{SERVERCONFFILE}); + unless ($ret == 0) { + return undef; + } + + # start samba for the restored DC + if (not defined($self->check_or_start($env))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) { + return undef; + } + + return $env; +} + +# Set up a DC testenv solely by using the 'samba-tool domain backup offline' and +# restore commands. This proves that we do an offline backup of a local DC +# ('backupfromdc') and use the backup file to create a valid, working samba DC. +sub setup_offlinebackupdc +{ + # note: dcvars contains the env info for the dependent testenv ('backupfromdc') + my ($self, $prefix, $dcvars) = @_; + print "Preparing OFFLINE BACKUP DC...\n"; + my $extra_conf = "prefork children = 1"; + my $dnsupdate_options = " --use-samba-tool --no-credentials"; + + my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "offlinebackupdc", + $dcvars->{DOMAIN}, + $dcvars->{REALM}, + $dcvars->{PASSWORD}, + $extra_conf, + $dnsupdate_options); + + # create an offline backup of the 'backupfromdc' target + my $backupdir = File::Temp->newdir(); + my $cmd = "offline --configfile $dcvars->{SERVERCONFFILE}"; + my $backup_file = $self->create_backup($env, $dcvars, + $backupdir, $cmd); + + unless($backup_file) { + return undef; + } + + # restore the backup file to populate the rename-DC testenv + my $restore_dir = abs_path($prefix); + my $restore_opts = "--newservername=$env->{SERVER} --host-ip=$env->{SERVER_IP}"; + my $ret = $self->restore_backup_file($backup_file, $restore_opts, + $restore_dir, $env->{SERVERCONFFILE}); + unless ($ret == 0) { + return undef; + } + + # + # As we create the same domain as a clone + # we need a separate resolv.conf! + # + $ctx->{resolv_conf} = "$ctx->{etcdir}/resolv.conf"; + $ctx->{dns_ipv4} = $ctx->{ipv4}; + $ctx->{dns_ipv6} = $ctx->{ipv6}; + Samba::mk_resolv_conf($ctx); + $env->{RESOLV_CONF} = $ctx->{resolv_conf}; + + # re-create the testenv's krb5.conf (the restore may have overwritten it) + Samba::mk_krb5_conf($ctx); + + # start samba for the restored DC + if (not defined($self->check_or_start($env))) { + return undef; + } + + return $env; +} + +# Set up a DC testenv solely by using the samba-tool 'domain backup rename' and +# restore commands, using the --no-secrets option. This proves that we can +# create a realistic lab environment from an online DC ('backupfromdc'). +sub setup_labdc +{ + # note: dcvars contains the env info for the dependent testenv ('backupfromdc') + my ($self, $prefix, $dcvars) = @_; + print "Preparing LAB-DOMAIN DC...\n"; + my $extra_conf = "prefork children = 1"; + + my ($env, $ctx) = $self->prepare_dc_testenv($prefix, "labdc", + "LABDOMAIN", + "labdom.samba.example.com", + $dcvars->{PASSWORD}, $extra_conf); + + # create a backup of the 'backupfromdc' which renames the domain and uses + # the --no-secrets option to scrub any sensitive info + my $backupdir = File::Temp->newdir(); + my $server_args = $self->get_backup_server_args($dcvars); + my $backup_args = "rename $env->{DOMAIN} $env->{REALM} $server_args"; + $backup_args .= " --no-secrets --backend-store=$self->{default_ldb_backend}"; + my $backup_file = $self->create_backup($env, $dcvars, $backupdir, + $backup_args); + unless($backup_file) { + return undef; + } + + # restore the backup file to populate the lab-DC testenv + my $restore_dir = abs_path($prefix); + my $restore_opts = "--newservername=$env->{SERVER} --host-ip=$env->{SERVER_IP}"; + my $ret = $self->restore_backup_file($backup_file, $restore_opts, + $restore_dir, $env->{SERVERCONFFILE}); + unless ($ret == 0) { + return undef; + } + + # because we don't include any secrets in the backup, we need to reset the + # admin user's password back to what the testenv expects + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + my $cmd = "$samba_tool user setpassword $env->{USERNAME} "; + $cmd .= "--newpassword=$env->{PASSWORD} -H $restore_dir/private/sam.ldb"; + $cmd .= " $env->{CONFIGURATION}"; + + unless(system($cmd) == 0) { + warn("Failed to reset admin's password: \n$cmd"); + return undef; + } + + # start samba for the restored DC + if (not defined($self->check_or_start($env))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) { + return undef; + } + + return $env; +} + +# Inspects a backup *.tar.bz2 file and determines the realm/domain it contains +sub get_backup_domain_realm +{ + my ($self, $backup_file) = @_; + + print "Determining REALM/DOMAIN values in backup...\n"; + + # The backup will have the correct domain/realm values in the smb.conf. + # So we can work out the env variables the testenv should use based on + # that. Let's start by extracting the smb.conf + my $tar = Archive::Tar->new($backup_file); + my $tmpdir = File::Temp->newdir(); + my $smbconf = "$tmpdir/smb.conf"; + + # note that the filepaths within the tar-file differ slightly for online + # and offline backups + if ($tar->contains_file("etc/smb.conf")) { + $tar->extract_file("etc/smb.conf", $smbconf); + } elsif ($tar->contains_file("./etc/smb.conf")) { + $tar->extract_file("./etc/smb.conf", $smbconf); + } else { + warn("Could not find smb.conf in $backup_file"); + return undef, undef; + } + + # make sure we don't try to create locks/sockets in the default install + # location (i.e. /usr/local/samba/) + my $options = "--option=\"private dir = $tmpdir\""; + $options .= " --option=\"lock dir = $tmpdir\""; + + # now use testparm to read the values we're interested in + my $testparm = Samba::bindir_path($self, "testparm"); + my $domain = `$testparm $smbconf -sl --parameter-name=WORKGROUP $options`; + my $realm = `$testparm $smbconf -sl --parameter-name=REALM $options`; + chomp $realm; + chomp $domain; + print "Backup-file REALM is $realm, DOMAIN is $domain\n"; + + return ($domain, $realm); +} + +# This spins up a custom testenv that can be based on any backup-file you want. +# This is just intended for manual testing (rather than automated test-cases) +sub setup_customdc +{ + my ($self, $prefix) = @_; + print "Preparing CUSTOM RESTORE DC...\n"; + my $dc_name = "customdc"; + my $password = "locDCpass1"; + my $backup_file = $ENV{'BACKUP_FILE'}; + my $dnsupdate_options = " --use-samba-tool --no-credentials"; + + # user must specify a backup file to restore via an ENV variable, i.e. + # BACKUP_FILE=backup-blah.tar.bz2 SELFTEST_TESTENV=customdc make testenv + if (not defined($backup_file)) { + warn("Please specify BACKUP_FILE"); + return undef; + } + + # work out the correct domain/realm env values from the backup-file + my ($domain, $realm) = $self->get_backup_domain_realm($backup_file); + if ($domain eq '' or $realm eq '') { + warn("Could not determine domain or realm"); + return undef; + } + + # create a placeholder directory and smb.conf, as well as the env vars. + my ($env, $ctx) = $self->prepare_dc_testenv($prefix, $dc_name, + $domain, $realm, $password, "", + $dnsupdate_options); + + # restore the specified backup file to populate the testenv + my $restore_dir = abs_path($prefix); + my $ret = $self->restore_backup_file($backup_file, + "--newservername=$env->{SERVER}", + $restore_dir, $env->{SERVERCONFFILE}); + unless ($ret == 0) { + return undef; + } + + # + # As we create the same domain as a clone + # we need a separate resolv.conf! + # + $ctx->{resolv_conf} = "$ctx->{etcdir}/resolv.conf"; + $ctx->{dns_ipv4} = $ctx->{ipv4}; + $ctx->{dns_ipv6} = $ctx->{ipv6}; + Samba::mk_resolv_conf($ctx); + $env->{RESOLV_CONF} = $ctx->{resolv_conf}; + + # Change the admin password to the testenv default, just in case it's + # different, or in case this was a --no-secrets backup + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + my $cmd = "$samba_tool user setpassword $env->{USERNAME} "; + $cmd .= "--newpassword=$password -H $restore_dir/private/sam.ldb"; + $cmd .= " $env->{CONFIGURATION}"; + + unless(system($cmd) == 0) { + warn("Failed to reset admin's password: \n$cmd"); + return undef; + } + + # re-create the testenv's krb5.conf (the restore may have overwritten it, + # if the backup-file was an offline backup) + Samba::mk_krb5_conf($ctx); + + # start samba for the restored DC + if (not defined($self->check_or_start($env))) { + return undef; + } + + # if this was a backup-rename, then we may need to setup namespaces + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + if ($self->setup_namespaces($env, $upn_array, $spn_array) != 0) { + return undef; + } + + return $env; +} + +sub setup_none +{ + my ($self, $path) = @_; + + my $ret = { + KRB5_CONFIG => abs_path($path) . "/no_krb5.conf", + SAMBA_PID => -1, + } +} + +1; |