diff options
Diffstat (limited to 'selftest/target')
-rw-r--r-- | selftest/target/README | 137 | ||||
-rw-r--r-- | selftest/target/Samba.pm | 1108 | ||||
-rwxr-xr-x | selftest/target/Samba3.pm | 4182 | ||||
-rwxr-xr-x | selftest/target/Samba4.pm | 3662 | ||||
-rwxr-xr-x | selftest/target/dns_hub.py | 250 |
5 files changed, 9339 insertions, 0 deletions
diff --git a/selftest/target/README b/selftest/target/README new file mode 100644 index 0000000..81d7447 --- /dev/null +++ b/selftest/target/README @@ -0,0 +1,137 @@ +Selftest target environments (testenvs) +======================================= +Samba's integration testing heavily relies on the automatic creation of a Samba +network. This specialized test environment is generally referred to as a Samba +'testenv'. + +A testenv involves starting the Samba server listening on a fake network, which +is established using the socket_wrapper library from cwrap (https://cwrap.org). +All testing is also done as a non-root user using the uid_wrapper library, also +from cwrap. + +Samba's test framework uses many different types of testenv. Each testenv is +customized to test a particular Samba feature or configuration. Using cwrap +allows multiple different Samba servers to run at the same time, without +interference. + +Some of the different testenvs are described in more detail below. + +Important notes if adding a new testenv +--------------------------------------- +- When adding a new testenv, in the Perl code it is recommended to always +explicitly specify the --configfile option in the samba-tool command, i.e. add +"env->{CONFIGURATION}" to the samba-tool command. Otherwise, the samba-tool +can try to load smb.conf from the default install location (i.e. +/usr/local/samba/etc/smb.conf). Loading a host-specific smb.conf that's outside +of the testenv is obviously not ideal and something we want to avoid in a +reliable test framework. + +'local' disambiguation +---------------------- +You may notice some variation in the target testenv that test suites are run +against, for example "ad_dc" and "ad_dc:local". The main difference is the +":local" changes the smb.conf that the testenv uses. By default, the testenvs +use the st/client/client.conf config-file, so that they simulate a client +talking to the Samba server. However, some tests may want to simulate running +a command on the Samba server itself. In these cases, the ":local" is used, +which means the testenv uses the Samba server's smb.conf instead (i.e. +st/ad_dc/etc/smb.conf). + +Note that several of the testenvs also use local in their name, e.g. +'localvampiredc'. In particular, there's the 'localdc', which is the NetBIOS +name of the DC in the 'ad_dc_ntvfs' testenv. + +dns_hub +------- +dns_hub doesn't run a Samba/smbd server like the other testenvs do. It's there +to solve the problem of how to do DNS more nicely in selftest. Running +autobuild can start up a lot of different testenvs, and so we end up with +different DCs running in different domains. Each test suite only wants to talk +to a specific domain at a time. However, by default the tests all use a common +client.conf - essentially the tests are simulating a single client that's +pretending to be in several different domains. The problem is when the test +wants to resolve a DNS host, which DC should it ask? Each DC only knows about its +own realm. dns_hub.py acts as a proxy, so it works out the correct DC to forward +the query to, based on the queried host's realm. + +Vampire DC +---------- +Vampire DC gets its name for historic reasons. It's one of the few testenvs +where 2 DCs are joined together, so it's used for a lot of DRS replication +testing. Basically its main job is to 'suck' the database changes out of +another DC (the 'ad_dc_ntfvs' DC). + +There's also a 'vampire_2000_dc' that joins the 'fl2000dc' DC, although that's +not used very much. + +Backup/restore testenvs +----------------------- +Several testenvs are created to test the domain backup/restore commands. These +testenvs verify that we can backup and restore a domain's database, start +Samba against it, and the restored database is actually functional. There are +several different flavours of backups (to cover different use-cases), so there +are separate testenvs for each one. + +- backupfromdc: A fairly plain AD DC used as the base to generate the + backup-files. These backup-files will then seed the domain database + for the separate testenvs below. + Backupfromdc's other unique feature is that it's the only testenv that gets + provisioned with a non-default site, i.e. Default-First-Site-Name doesn't + exist. +- restoredc: tests the 'backup online' option. Online backups are similar to + doing a DC join. + Restoredc's other unique feature is that is has SMBv1 disabled. +- offlinebackupdc: tests the 'backup offline' option. Offline backups capture + the raw DB files on disk (safely). +- renamedc: tests the 'backup rename' option, where the domain and realm are + renamed. +- labdc: one of the use-cases for the backup tool is to create a realistic + pre-production testbed, based off a production DC. This testenv simulates + that process. It uses the 'backup rename --no-secrets' option. + +customdc testenv +---------------- +The customdc is a special testenv that's only used for manual testing, rather +than the automated tests most testenvs are primarily used for. + +The customdc testenv also uses the backup/restore tool, however, it is quite +special. Instead of the backup-file being automatically generated from a +vanilla AD DC (i.e. backupfromdc), you can specify any backup-file you like. + +To run the testenv, you need to specify a 'BACKUP_FILE' shell variable, e.g. + +BACKUP_FILE=/tmp/samba-backup-50k-dc-0-mdb-50k-offline.tar.bz2 \ + SELFTEST_TESTENV=customdc make testenv + +The main use-case for the customdc is testing changes against a large +database. Adding users is very time-consuming, so it's much quicker to populate +a domain with users once, take a backup, and then you can spin up a testenv +based on the backup multiple times. + +Another use-case is that if you get a database that's corrupted or in a bad +state, then you could save a backup and be able to easily get the database back +into the bad state. This allows you to try different commands to diagnose/fix +the issue, without fear of never seeing the problem again. + +You could even spin up a 'lab DC' inside a testenv, by taking a backup of a +real network DC. + +preforkrestartdc testenv +------------------------ +Used to test killing and restarting processes under the pre-fork model. Due to +the destructive nature of the tests, it's not recommended to use this testenv +for anything else. + +proclimitdc testenv +------------------- +Used to test process limits on the standard model. It sets the number of +allowed processes artificially low, to test that new connections are refused +correctly. Due to the limited number of connections accepted, it's not +recommended to use this testenv for anything else. + +schema_dc +---------------- +This is a 2-DC testenv setup (schema_dc and schema_pair_dc). +We provision the first DC, and join the second, using an older version of the +schema (2008R2), then start-up Samba. Then, we run a schema upgrade (i.e. +'samba-tool domain schemaupgrade') on the PDC. diff --git a/selftest/target/Samba.pm b/selftest/target/Samba.pm new file mode 100644 index 0000000..2131e4a --- /dev/null +++ b/selftest/target/Samba.pm @@ -0,0 +1,1108 @@ +#!/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. + +package Samba; + +use strict; +use warnings; +use target::Samba3; +use target::Samba4; +use POSIX; +use Cwd qw(abs_path); +use IO::Poll qw(POLLIN); + +sub new($$$$$) { + my ($classname, $bindir, $srcdir, $server_maxtime, + $opt_socket_wrapper_pcap, $opt_socket_wrapper_keep_pcap) = @_; + + my $self = { + opt_socket_wrapper_pcap => $opt_socket_wrapper_pcap, + opt_socket_wrapper_keep_pcap => $opt_socket_wrapper_keep_pcap, + }; + $self->{samba3} = new Samba3($self, $bindir, $srcdir, $server_maxtime); + $self->{samba4} = new Samba4($self, $bindir, $srcdir, $server_maxtime); + bless $self; + return $self; +} + +%Samba::ENV_DEPS = (%Samba3::ENV_DEPS, %Samba4::ENV_DEPS); +our %ENV_DEPS; + +%Samba::ENV_DEPS_POST = (%Samba3::ENV_DEPS_POST, %Samba4::ENV_DEPS_POST); +our %ENV_DEPS_POST; + +%Samba::ENV_TARGETS = ( + (map { $_ => "Samba3" } keys %Samba3::ENV_DEPS), + (map { $_ => "Samba4" } keys %Samba4::ENV_DEPS), +); +our %ENV_TARGETS; + +%Samba::ENV_NEEDS_AD_DC = ( + (map { $_ => 1 } keys %Samba4::ENV_DEPS) +); +our %ENV_NEEDS_AD_DC; +foreach my $env (keys %Samba3::ENV_DEPS) { + $ENV_NEEDS_AD_DC{$env} = ($env =~ /^ad_/); +} + +sub setup_pcap($$) +{ + my ($self, $name) = @_; + + return unless ($self->{opt_socket_wrapper_pcap}); + return unless defined($ENV{SOCKET_WRAPPER_PCAP_DIR}); + + my $fname = $name; + $fname =~ s%[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\-]%_%g; + + my $pcap_file = "$ENV{SOCKET_WRAPPER_PCAP_DIR}/$fname.pcap"; + + SocketWrapper::setup_pcap($pcap_file); + + return $pcap_file; +} + +sub cleanup_pcap($$$) +{ + my ($self, $pcap_file, $exitcode) = @_; + + return unless ($self->{opt_socket_wrapper_pcap}); + return if ($self->{opt_socket_wrapper_keep_pcap}); + return unless ($exitcode == 0); + return unless defined($pcap_file); + + unlink($pcap_file); +} + +sub setup_env($$$) +{ + my ($self, $envname, $path) = @_; + + my $targetname = $ENV_TARGETS{$envname}; + if (not defined($targetname)) { + warn("Samba can't provide environment '$envname'"); + return "UNKNOWN"; + } + + my %targetlookup = ( + "Samba3" => $self->{samba3}, + "Samba4" => $self->{samba4} + ); + my $target = $targetlookup{$targetname}; + + if (defined($target->{vars}->{$envname})) { + return $target->{vars}->{$envname}; + } + + $target->{vars}->{$envname} = ""; + + my @dep_vars; + foreach(@{$ENV_DEPS{$envname}}) { + my $vars = $self->setup_env($_, $path); + if (defined($vars)) { + push(@dep_vars, $vars); + } else { + warn("Failed setting up $_ as a dependency of $envname"); + return undef; + } + } + + $ENV{ENVNAME} = $envname; + # Avoid hitting system krb5.conf - + # An env that needs Kerberos will reset this to the real value. + $ENV{KRB5_CONFIG} = "$path/no_krb5.conf"; + $ENV{RESOLV_CONF} = "$path/no_resolv.conf"; + + my $setup_name = $ENV_TARGETS{$envname}."::setup_".$envname; + my $setup_sub = \&$setup_name; + my $setup_pcap_file = $self->setup_pcap("env-$ENV{ENVNAME}-setup"); + my $env = &$setup_sub($target, "$path/$envname", @dep_vars); + $self->cleanup_pcap($setup_pcap_file, not defined($env)); + SocketWrapper::setup_pcap(undef); + + if (not defined($env)) { + warn("failed to start up environment '$envname'"); + return undef; + } + + $target->{vars}->{$envname} = $env; + $target->{vars}->{$envname}->{target} = $target; + + foreach(@{$ENV_DEPS_POST{$envname}}) { + if (not defined $_) { + continue; + } + my $vars = $self->setup_env($_, $path); + if (not defined($vars)) { + return undef; + } + } + + return $env; +} + +sub bindir_path($$) { + my ($object, $path) = @_; + + my $valpath = "$object->{bindir}/$path"; + my $python_cmd = ""; + my $result = $path; + if (defined $ENV{'PYTHON'}) { + $python_cmd = $ENV{'PYTHON'} . " "; + } + + if (-f $valpath or -d $valpath) { + $result = $valpath; + } + # make sure we prepend samba-tool with calling $PYTHON python version + if ($path eq "samba-tool") { + $result = $python_cmd . $result; + } + return $result; +} + +sub nss_wrapper_winbind_so_path($) { + my ($object) = @_; + my $ret = $ENV{NSS_WRAPPER_WINBIND_SO_PATH}; + if (not defined($ret)) { + $ret = bindir_path($object, "plugins/libnss_wrapper_winbind.so.2"); + $ret = abs_path($ret); + } + return $ret; +} + +sub copy_file_content($$) +{ + my ($in, $out) = @_; + open(IN, "${in}") or die("failed to open in[${in}] for reading: $!"); + open(OUT, ">${out}") or die("failed to open out[${out}] for writing: $!"); + while(<IN>) { + print OUT $_; + } + close(OUT); + close(IN); +} + +sub prepare_keyblobs($) +{ + my ($ctx) = @_; + + my $cadir = "$ENV{SRCDIR_ABS}/selftest/manage-ca/CA-samba.example.com"; + my $cacert = "$cadir/Public/CA-samba.example.com-cert.pem"; + my $cacrl_pem = "$cadir/Public/CA-samba.example.com-crl.pem"; + my $dcdnsname = "$ctx->{hostname}.$ctx->{dnsname}"; + my $dcdir = "$cadir/DCs/$dcdnsname"; + my $dccert = "$dcdir/DC-$dcdnsname-cert.pem"; + my $dckey_private = "$dcdir/DC-$dcdnsname-private-key.pem"; + my $adminprincipalname = "administrator\@$ctx->{dnsname}"; + my $admindir = "$cadir/Users/$adminprincipalname"; + my $admincert = "$admindir/USER-$adminprincipalname-cert.pem"; + my $adminkey_private = "$admindir/USER-$adminprincipalname-private-key.pem"; + my $pkinitprincipalname = "pkinit\@$ctx->{dnsname}"; + my $ca_pkinitdir = "$cadir/Users/$pkinitprincipalname"; + my $pkinitcert = "$ca_pkinitdir/USER-$pkinitprincipalname-cert.pem"; + my $pkinitkey_private = "$ca_pkinitdir/USER-$pkinitprincipalname-private-key.pem"; + + my $tlsdir = "$ctx->{tlsdir}"; + my $pkinitdir = "$ctx->{prefix_abs}/pkinit"; + #TLS and PKINIT crypto blobs + my $dhfile = "$tlsdir/dhparms.pem"; + my $cafile = "$tlsdir/ca.pem"; + my $crlfile = "$tlsdir/crl.pem"; + my $certfile = "$tlsdir/cert.pem"; + my $keyfile = "$tlsdir/key.pem"; + my $admincertfile = "$pkinitdir/USER-$adminprincipalname-cert.pem"; + my $adminkeyfile = "$pkinitdir/USER-$adminprincipalname-private-key.pem"; + my $pkinitcertfile = "$pkinitdir/USER-$pkinitprincipalname-cert.pem"; + my $pkinitkeyfile = "$pkinitdir/USER-$pkinitprincipalname-private-key.pem"; + + mkdir($tlsdir, 0700); + mkdir($pkinitdir, 0700); + my $oldumask = umask; + umask 0077; + + # This is specified here to avoid draining entropy on every run + # generate by + # openssl dhparam -out dhparms.pem -text -2 8192 + open(DHFILE, ">$dhfile"); + print DHFILE <<EOF; +-----BEGIN DH PARAMETERS----- +MIIECAKCBAEAlcpjuJptCzC2bIIApLuyFLw2nODQUztqs/peysY9e3LgWh/xrc87 +SWJNSUrqFJFh2m357WH0XGcTdTk0b/8aIYIWjbwEhWR/5hZ+1x2TDrX1awkYayAe +pr0arycmWHaAmhw+m+dBdj2O2jRMe7gn0ha85JALNl+Z3wv2q2eys8TIiQ2dbHPx +XvpMmlAv7QHZnpSpX/XgueQr6T3EYggljppZwk1fe4W2cxBjCv9w/Q83pJXMEVVB +WESEQPZC38v6hVIXIlF4J7jXjV3+NtCLL4nvsy0jrLEntyKz5OB8sNPRzJr0Ju2Y +yXORCSMMXMygP+dxJtQ6txzQYWyaCYN1HqHDZy3cFL9Qy8kTFqIcW56Lti2GsW/p +jSMzEOa1NevhKNFL3dSZJx5m+5ZeMvWXlCqXSptmVdbs5wz5jkMUm/E6pVfM5lyb +Ttlcq2iYPqnJz1jcL5xwhoufID8zSJCPJ7C0jb0Ngy5wLIUZfjXJUXxUyxTnNR9i +N9Sc+UkDvLxnCW+qzjyPXGlQU1SsJwMLWa2ZecL/uYE4bOdcN3g+5WHkevyDnXqR ++yy9x7sGXjBT3bRWK5tVHJWOi6eBu1hp39U6aK8oOJWiUt3vmC2qEdIsT6JaLNNi +YKrSfRGBf19IJBaagen1S19bb3dnmwoU1RaWM0EeJQW1oXOBg7zLisB2yuu5azBn +tse00+0nc+GbH2y+jP0sE7xil1QeilZl+aQ3tX9vL0cnCa+8602kXxU7P5HaX2+d +05pvoHmeZbDV85io36oF976gBYeYN+qAkTUMsIZhuLQDuyn0963XOLyn1Pm6SBrU +OkIZXW7WoKEuO/YSfizUIqXwmAMJjnEMJCWG51MZZKx//9Hsdp1RXSm/bRSbvXB7 +MscjvQYWmfCFnIk8LYnEt3Yey40srEiS9xyZqdrvobxz+sU1XcqR38kpVf4gKASL +xURia64s4emuJF+YHIObyydazQ+6/wX/C+m+nyfhuxSO6j1janPwtYbU+Uj3TzeM +04K1mpPQpZcaMdZZiNiu7i8VJlOPKAz7aJT8TnMMF5GMyzyLpSMpc+NF9L/BSocV +/cUM4wQT2PTHrcyYzmTVH7c9bzBkuxqrwVB1BY1jitDV9LIYIVBglKcX88qrfHIM +XiXPAIwGclD59qm2cG8OdM9NA5pNMI119KuUAIJsUdgPbR1LkT2XTT15YVoHmFSQ +DlaWOXn4td031jr0EisX8QtFR7+/0Nfoni6ydFGs5fNH/L1ckq6FEO4OhgucJw9H +YRmiFlsQBQNny78vNchwZne3ZixkShtGW0hWDdi2n+h7St1peNJCNJjMbEhRsPRx +RmNGWh4AL8rho4RO9OBao0MnUdjbbffD+wIBAg== +-----END DH PARAMETERS----- +EOF + close(DHFILE); + + if (! -e ${dckey_private}) { + umask $oldumask; + return; + } + + copy_file_content(${cacert}, ${cafile}); + copy_file_content(${cacrl_pem}, ${crlfile}); + copy_file_content(${dccert}, ${certfile}); + copy_file_content(${dckey_private}, ${keyfile}); + if (-e ${adminkey_private}) { + copy_file_content(${admincert}, ${admincertfile}); + copy_file_content(${adminkey_private}, ${adminkeyfile}); + } + if (-e ${pkinitkey_private}) { + copy_file_content(${pkinitcert}, ${pkinitcertfile}); + copy_file_content(${pkinitkey_private}, ${pkinitkeyfile}); + } + + # COMPAT stuff to be removed in a later commit + my $kdccertfile = "$tlsdir/kdc.pem"; + copy_file_content(${dccert}, ${kdccertfile}); + + umask $oldumask; +} + +sub copy_gnupg_home($) +{ + my ($ctx) = @_; + + my $gnupg_srcdir = "$ENV{SRCDIR_ABS}/selftest/gnupg"; + my @files = ( + "gpg.conf", + "pubring.gpg", + "secring.gpg", + "trustdb.gpg", + ); + + my $oldumask = umask; + umask 0077; + mkdir($ctx->{gnupghome}, 0777); + umask 0177; + foreach my $file (@files) { + my $srcfile = "${gnupg_srcdir}/${file}"; + my $dstfile = "$ctx->{gnupghome}/${file}"; + copy_file_content(${srcfile}, ${dstfile}); + } + umask $oldumask; +} + +sub mk_krb5_conf($$) +{ + my ($ctx) = @_; + + unless (open(KRB5CONF, ">$ctx->{krb5_conf}")) { + warn("can't open $ctx->{krb5_conf}$?"); + return undef; + } + + my $our_realms_stanza = mk_realms_stanza($ctx->{realm}, + $ctx->{dnsname}, + $ctx->{domain}, + $ctx->{kdc_ipv4}); + print KRB5CONF " +#Generated krb5.conf for $ctx->{realm} + +[libdefaults] + default_realm = $ctx->{realm} + dns_lookup_realm = false + dns_lookup_kdc = true + ticket_lifetime = 24h + forwardable = yes + + # We are running on the same machine, do not correct + # system clock differences + kdc_timesync = 0 + + fcache_strict_checking = false +"; + + if (defined($ENV{MITKRB5})) { + print KRB5CONF " + # Set the grace clocskew to 5 seconds + # This is especially required by samba3.raw.session krb5 and + # reauth tests when not using Heimdal + clockskew = 5 + "; + } + + if (defined($ctx->{krb5_ccname})) { + print KRB5CONF " + default_ccache_name = $ctx->{krb5_ccname} +"; + } + + + if (defined($ctx->{supported_enctypes})) { + print KRB5CONF " + default_etypes = $ctx->{supported_enctypes} + default_as_etypes = $ctx->{supported_enctypes} + default_tgs_enctypes = $ctx->{supported_enctypes} + default_tkt_enctypes = $ctx->{supported_enctypes} + permitted_enctypes = $ctx->{supported_enctypes} +"; + } + + if (defined($ctx->{tlsdir})) { + if (defined($ENV{MITKRB5})) { + print KRB5CONF " + pkinit_anchors = FILE:$ctx->{tlsdir}/ca.pem + pkinit_kdc_hostname = $ctx->{hostname}.$ctx->{dnsname} + +"; + } else { + print KRB5CONF " + +[appdefaults] + pkinit_anchors = FILE:$ctx->{tlsdir}/ca.pem + +[kdc] + enable-pkinit = true + pkinit_identity = FILE:$ctx->{tlsdir}/kdc.pem,$ctx->{tlsdir}/key.pem + pkinit_anchors = FILE:$ctx->{tlsdir}/ca.pem + +"; + } + } + + print KRB5CONF " +[realms] + $our_realms_stanza +"; + + close(KRB5CONF); +} + +sub append_krb5_conf_trust_realms($$) +{ + my ($ctx) = @_; + + unless (open(KRB5CONF, ">>$ctx->{KRB5_CONFIG}")) { + warn("can't open $ctx->{KRB5_CONFIG}$?"); + return undef; + } + + my $trust_realms_stanza = mk_realms_stanza($ctx->{TRUST_REALM}, + $ctx->{TRUST_DNSNAME}, + $ctx->{TRUST_DOMAIN}, + $ctx->{TRUST_SERVER_IP}); + + print KRB5CONF " $trust_realms_stanza"; + + close(KRB5CONF) +} + +sub mk_realms_stanza($$$$) +{ + my ($realm, $dnsname, $domain, $kdc_ipv4) = @_; + my $lc_domain = lc($domain); + + # The pkinit_require_krbtgt_otherName = false + # is just because the certificates we have saved + # do not have the realm in the subjectAltName + # (specially encoded as a principal) + # per + # https://github.com/heimdal/heimdal/wiki/Setting-up-PK-INIT-and-Certificates + my $realms_stanza = " + $realm = { + kdc = $kdc_ipv4:88 + admin_server = $kdc_ipv4:88 + default_domain = $dnsname + pkinit_require_krbtgt_otherName = false + } + $dnsname = { + kdc = $kdc_ipv4:88 + admin_server = $kdc_ipv4:88 + default_domain = $dnsname + pkinit_require_krbtgt_otherName = false + } + $domain = { + kdc = $kdc_ipv4:88 + admin_server = $kdc_ipv4:88 + default_domain = $dnsname + pkinit_require_krbtgt_otherName = false + } + $lc_domain = { + kdc = $kdc_ipv4:88 + admin_server = $kdc_ipv4:88 + default_domain = $dnsname + pkinit_require_krbtgt_otherName = false + } + +"; + return $realms_stanza; +} + +sub mk_mitkdc_conf($$) +{ + # samba_kdb_dir is the path to mit_samba.so + my ($ctx, $samba_kdb_dir) = @_; + + unless (open(KDCCONF, ">$ctx->{mitkdc_conf}")) { + warn("can't open $ctx->{mitkdc_conf}$?"); + return undef; + } + + print KDCCONF " +# Generated kdc.conf for $ctx->{realm} + +[kdcdefaults] + kdc_ports = 88 + kdc_tcp_ports = 88 + restrict_anonymous_to_tgt = true + +[realms] + $ctx->{realm} = { + master_key_type = aes256-cts + default_principal_flags = +preauth + pkinit_identity = FILE:$ctx->{tlsdir}/kdc.pem,$ctx->{tlsdir}/key.pem + pkinit_anchors = FILE:$ctx->{tlsdir}/ca.pem + pkinit_eku_checking = scLogin + pkinit_indicator = pkinit + pkinit_allow_upn = true + } + + $ctx->{dnsname} = { + master_key_type = aes256-cts + default_principal_flags = +preauth + pkinit_identity = FILE:$ctx->{tlsdir}/kdc.pem,$ctx->{tlsdir}/key.pem + pkinit_anchors = FILE:$ctx->{tlsdir}/ca.pem + pkinit_eku_checking = scLogin + pkinit_indicator = pkinit + pkinit_allow_upn = true + } + + $ctx->{domain} = { + master_key_type = aes256-cts + default_principal_flags = +preauth + pkinit_identity = FILE:$ctx->{tlsdir}/kdc.pem,$ctx->{tlsdir}/key.pem + pkinit_anchors = FILE:$ctx->{tlsdir}/ca.pem + pkinit_eku_checking = scLogin + pkinit_indicator = pkinit + pkinit_allow_upn = true + } + +[dbmodules] + db_module_dir = $samba_kdb_dir + + $ctx->{realm} = { + db_library = samba + } + + $ctx->{dnsname} = { + db_library = samba + } + + $ctx->{domain} = { + db_library = samba + } + +[logging] + kdc = FILE:$ctx->{logdir}/mit_kdc.log +"; + + close(KDCCONF); +} + +sub mk_resolv_conf($$) +{ + my ($ctx) = @_; + + unless (open(RESOLV_CONF, ">$ctx->{resolv_conf}")) { + warn("can't open $ctx->{resolv_conf}$?"); + return undef; + } + + print RESOLV_CONF "nameserver $ctx->{dns_ipv4}\n"; + print RESOLV_CONF "nameserver $ctx->{dns_ipv6}\n"; + close(RESOLV_CONF); +} + +sub realm_to_ip_mappings +{ + # this maps the DNS realms for the various testenvs to the corresponding + # PDC (i.e. the first DC created for that realm). + my %realm_to_pdc_mapping = ( + 'adnonssdom.samba.example.com' => 'addc_no_nss', + 'adnontlmdom.samba.example.com' => 'addc_no_ntlm', + 'samba2000.example.com' => 'dc5', + 'samba2003.example.com' => 'dc6', + 'samba2008r2.example.com' => 'dc7', + 'addom.samba.example.com' => 'addc', + 'addom2.samba.example.com' => 'addcsmb1', + 'sub.samba.example.com' => 'localsubdc', + 'chgdcpassword.samba.example.com' => 'chgdcpass', + 'backupdom.samba.example.com' => 'backupfromdc', + 'renamedom.samba.example.com' => 'renamedc', + 'labdom.samba.example.com' => 'labdc', + 'schema.samba.example.com' => 'liveupgrade1dc', + 'prockilldom.samba.example.com' => 'prockilldc', + 'proclimit.samba.example.com' => 'proclimitdc', + 'samba.example.com' => 'localdc', + 'fips.samba.example.com' => 'fipsdc', + ); + + my @mapping = (); + + # convert the hashmap to a list of key=value strings, where key is the + # realm and value is the IP address + foreach my $realm (sort(keys %realm_to_pdc_mapping)) { + my $pdc = $realm_to_pdc_mapping{$realm}; + my $ipaddr = get_ipv4_addr($pdc); + push(@mapping, "$realm=$ipaddr"); + } + # return the mapping as a single comma-separated string + return join(',', @mapping); +} + +sub get_interface($) +{ + my ($netbiosname) = @_; + $netbiosname = lc($netbiosname); + + # this maps the SOCKET_WRAPPER_DEFAULT_IFACE value for each possible + # testenv to the DC's NETBIOS name. This value also corresponds to last + # digit of the DC's IP address. Note that the NETBIOS name may differ from + # the testenv name. + # Note that when adding a DC with a new realm, also update + # get_realm_ip_mappings() above. + my %testenv_iface_mapping = ( + localnt4dc2 => 3, + localnt4member3 => 4, + localshare4 => 5, + # 6 is spare + localktest6 => 7, + maptoguest => 8, + localnt4dc9 => 9, + # 10 is spare + + # 11-16 are used by selftest.pl for the client.conf. Most tests only + # use the first .11 IP. However, some tests (like winsreplication) rely + # on the client having multiple IPs. + client => 11, + + addc_no_nss => 17, + addc_no_ntlm => 18, + idmapadmember => 19, + idmapridmember => 20, + localdc => 21, + localvampiredc => 22, + s4member => 23, + localrpcproxy => 24, + dc5 => 25, + dc6 => 26, + dc7 => 27, + rodc => 28, + localadmember => 29, + addc => 30, + localsubdc => 31, + chgdcpass => 32, + promotedvdc => 33, + rfc2307member => 34, + fileserver => 35, + fakednsforwarder1 => 36, + fakednsforwarder2 => 37, + s4member_dflt => 38, + vampire2000dc => 39, + backupfromdc => 40, + restoredc => 41, + renamedc => 42, + labdc => 43, + offlinebackupdc => 44, + customdc => 45, + prockilldc => 46, + proclimitdc => 47, + liveupgrade1dc => 48, + liveupgrade2dc => 49, + ctdb0 => 50, + ctdb1 => 51, + ctdb2 => 52, + fileserversmb1 => 53, + addcsmb1 => 54, + lclnt4dc2smb1 => 55, + fipsdc => 56, + fipsadmember => 57, + offlineadmem => 58, + s2kmember => 59, + admemidmapnss => 60, + localadmember2 => 61, + admemautorid => 62, + + rootdnsforwarder => 64, + + # Note: that you also need to update dns_hub.py when adding a new + # multi-DC testenv + # update lib/socket_wrapper/socket_wrapper.c + # #define MAX_WRAPPED_INTERFACES 64 + # if you wish to have more than 64 interfaces + ); + + if (not defined($testenv_iface_mapping{$netbiosname})) { + die(); + } + + return $testenv_iface_mapping{$netbiosname}; +} + +sub get_ipv4_addr +{ + my ($hostname, $iface_num) = @_; + my $swiface = Samba::get_interface($hostname); + + # Handle testenvs with multiple different addresses, i.e. IP multihoming. + # Currently only the selftest client has multiple IPv4 addresses. + if (defined($iface_num)) { + $swiface += $iface_num; + } + + return "10.53.57.$swiface"; +} + +sub get_ipv6_addr +{ + (my $hostname) = @_; + my $swiface = Samba::get_interface($hostname); + + return sprintf("fd00:0000:0000:0000:0000:0000:5357:5f%02x", $swiface); +} + +# returns the 'interfaces' setting for smb.conf, i.e. the IPv4/IPv6 +# addresses for testenv +sub get_interfaces_config +{ + my ($hostname, $num_ips) = @_; + my $interfaces = ""; + + # We give the client.conf multiple different IPv4 addresses. + # All other testenvs generally just have one IPv4 address. + if (! defined($num_ips)) { + $num_ips = 1; + } + for (my $i = 0; $i < $num_ips; $i++) { + my $ipv4_addr = Samba::get_ipv4_addr($hostname, $i); + if (use_namespaces()) { + # use a /24 subnet with network namespaces + $interfaces .= "$ipv4_addr/24 "; + } else { + $interfaces .= "$ipv4_addr/8 "; + } + } + + my $ipv6_addr = Samba::get_ipv6_addr($hostname); + $interfaces .= "$ipv6_addr/64"; + + return $interfaces; +} + +sub cleanup_child($$) +{ + my ($pid, $name) = @_; + + if (!defined($pid)) { + print STDERR "cleanup_child: pid not defined ... not calling waitpid\n"; + return -1; + } + + my $childpid = waitpid($pid, WNOHANG); + + if ($childpid == 0) { + } elsif ($childpid < 0) { + printf STDERR "%s child process %d isn't here any more\n", $name, $pid; + return $childpid; + } elsif ($? & 127) { + printf STDERR "%s child process %d, died with signal %d, %s coredump\n", + $name, $childpid, ($? & 127), ($? & 128) ? 'with' : 'without'; + } else { + printf STDERR "%s child process %d exited with value %d\n", $name, $childpid, $? >> 8; + } + return $childpid; +} + +sub random_domain_sid() +{ + my $domain_sid = "S-1-5-21-". int(rand(4294967295)) . "-" . int(rand(4294967295)) . "-" . int(rand(4294967295)); + return $domain_sid; +} + +# sets the environment variables ready for running a given process +sub set_env_for_process +{ + my ($proc_name, $env_vars, $proc_envs) = @_; + + if (not defined($proc_envs)) { + $proc_envs = get_env_for_process($proc_name, $env_vars); + } + + foreach my $key (keys %{ $proc_envs }) { + $ENV{$key} = $proc_envs->{$key}; + } +} + +sub get_env_for_process +{ + my ($proc_name, $env_vars) = @_; + my $proc_envs = { + RESOLV_CONF => $env_vars->{RESOLV_CONF}, + KRB5_CONFIG => $env_vars->{KRB5_CONFIG}, + KRB5CCNAME => "$env_vars->{KRB5_CCACHE}.$proc_name", + GNUPGHOME => $env_vars->{GNUPGHOME}, + SELFTEST_WINBINDD_SOCKET_DIR => $env_vars->{SELFTEST_WINBINDD_SOCKET_DIR}, + NMBD_SOCKET_DIR => $env_vars->{NMBD_SOCKET_DIR}, + NSS_WRAPPER_PASSWD => $env_vars->{NSS_WRAPPER_PASSWD}, + NSS_WRAPPER_GROUP => $env_vars->{NSS_WRAPPER_GROUP}, + NSS_WRAPPER_HOSTS => $env_vars->{NSS_WRAPPER_HOSTS}, + NSS_WRAPPER_HOSTNAME => $env_vars->{NSS_WRAPPER_HOSTNAME}, + NSS_WRAPPER_MODULE_SO_PATH => $env_vars->{NSS_WRAPPER_MODULE_SO_PATH}, + NSS_WRAPPER_MODULE_FN_PREFIX => $env_vars->{NSS_WRAPPER_MODULE_FN_PREFIX}, + UID_WRAPPER_ROOT => "1", + ENVNAME => "$ENV{ENVNAME}.$proc_name", + }; + + if (defined($env_vars->{RESOLV_WRAPPER_CONF})) { + $proc_envs->{RESOLV_WRAPPER_CONF} = $env_vars->{RESOLV_WRAPPER_CONF}; + } else { + $proc_envs->{RESOLV_WRAPPER_HOSTS} = $env_vars->{RESOLV_WRAPPER_HOSTS}; + } + if (defined($env_vars->{GNUTLS_FORCE_FIPS_MODE})) { + $proc_envs->{GNUTLS_FORCE_FIPS_MODE} = $env_vars->{GNUTLS_FORCE_FIPS_MODE}; + } + if (defined($env_vars->{OPENSSL_FORCE_FIPS_MODE})) { + $proc_envs->{OPENSSL_FORCE_FIPS_MODE} = $env_vars->{OPENSSL_FORCE_FIPS_MODE}; + } + return $proc_envs; +} + +sub fork_and_exec +{ + my ($self, $env_vars, $daemon_ctx, $STDIN_READER, $child_cleanup) = @_; + my $SambaCtx = $self; + $SambaCtx = $self->{SambaCtx} if defined($self->{SambaCtx}); + + # we close the child's write-end of the pipe and redirect the + # read-end to its stdin. That way the daemon will receive an + # EOF on stdin when parent selftest process closes its + # write-end. + $child_cleanup //= sub { close($env_vars->{STDIN_PIPE}) }; + + unlink($daemon_ctx->{LOG_FILE}); + print "STARTING $daemon_ctx->{NAME} for $ENV{ENVNAME}..."; + + my $parent_pid = $$; + my $pid = fork(); + + # exec the daemon in the child process + if ($pid == 0) { + my @preargs = (); + + # redirect the daemon's stdout/stderr to a log file + if (defined($daemon_ctx->{TEE_STDOUT})) { + # in some cases, we want out from samba to go to the log file, + # but also to the users terminal when running 'make test' on the + # command line. This puts it on stderr on the terminal + open STDOUT, "| tee $daemon_ctx->{LOG_FILE} 1>&2"; + } else { + open STDOUT, ">$daemon_ctx->{LOG_FILE}"; + } + open STDERR, '>&STDOUT'; + + SocketWrapper::set_default_iface($env_vars->{SOCKET_WRAPPER_DEFAULT_IFACE}); + if (defined($daemon_ctx->{PCAP_FILE})) { + $SambaCtx->setup_pcap("$daemon_ctx->{PCAP_FILE}"); + } + + # setup ENV variables in the child process + set_env_for_process($daemon_ctx->{NAME}, $env_vars, + $daemon_ctx->{ENV_VARS}); + + $child_cleanup->(); + + # not all s3 daemons run in all testenvs (e.g. fileserver doesn't + # run winbindd). In which case, the child process just sleeps + if (defined($daemon_ctx->{SKIP_DAEMON})) { + $SIG{USR1} = $SIG{ALRM} = $SIG{INT} = $SIG{QUIT} = $SIG{TERM} = sub { + my $signame = shift; + print("Skip $daemon_ctx->{NAME} received signal $signame"); + exit 0; + }; + my $poll = IO::Poll->new(); + $poll->mask($STDIN_READER, POLLIN); + $poll->poll($self->{server_maxtime}); + exit 0; + } + + $ENV{MAKE_TEST_BINARY} = $daemon_ctx->{BINARY_PATH}; + + open STDIN, ">&", $STDIN_READER or die "can't dup STDIN_READER to STDIN: $!"; + + # if using kernel namespaces, prepend the command so the process runs in + # its own namespace + if (Samba::use_namespaces()) { + @preargs = ns_exec_preargs($parent_pid, $env_vars); + } + + # the command args are stored as an array reference (because...Perl), + # so convert the reference back to an array + my @full_cmd = @{ $daemon_ctx->{FULL_CMD} }; + + exec(@preargs, @full_cmd) or die("Unable to start $ENV{MAKE_TEST_BINARY}: $!"); + } + + print "DONE ($pid)\n"; + + # if using kernel namespaces, we now establish a connection between the + # main selftest namespace (i.e. this process) and the new child namespace + if (use_namespaces()) { + ns_child_forked($pid, $env_vars); + } + + return $pid; +} + +my @exported_envvars = ( + # domain stuff + "DOMAIN", + "DNSNAME", + "REALM", + "DOMSID", + + # stuff related to a trusted domain + "TRUST_SERVER", + "TRUST_USERNAME", + "TRUST_PASSWORD", + "TRUST_DOMAIN", + "TRUST_REALM", + "TRUST_DOMSID", + + # stuff related to a trusted domain, on a trust_member + # the domain behind a forest trust (two-way) + "TRUST_F_BOTH_SERVER", + "TRUST_F_BOTH_SERVER_IP", + "TRUST_F_BOTH_SERVER_IPV6", + "TRUST_F_BOTH_NETBIOSNAME", + "TRUST_F_BOTH_USERNAME", + "TRUST_F_BOTH_PASSWORD", + "TRUST_F_BOTH_DOMAIN", + "TRUST_F_BOTH_REALM", + + # stuff related to a trusted domain, on a trust_member + # the domain behind an external trust (two-way) + "TRUST_E_BOTH_SERVER", + "TRUST_E_BOTH_SERVER_IP", + "TRUST_E_BOTH_SERVER_IPV6", + "TRUST_E_BOTH_NETBIOSNAME", + "TRUST_E_BOTH_USERNAME", + "TRUST_E_BOTH_PASSWORD", + "TRUST_E_BOTH_DOMAIN", + "TRUST_E_BOTH_REALM", + + # domain controller stuff + "DC_SERVER", + "DC_SERVER_IP", + "DC_SERVER_IPV6", + "DC_NETBIOSNAME", + "DC_NETBIOSALIAS", + + # server stuff + "SERVER", + "SERVER_IP", + "SERVER_IPV6", + "NETBIOSNAME", + "NETBIOSALIAS", + "SAMSID", + + # only use these 2 as a last resort. Some tests need to test both client- + # side and server-side. In this case, run as default client, ans access + # server's smb.conf as needed, typically using: + # param.LoadParm(filename_for_non_global_lp=os.environ['SERVERCONFFILE']) + "SERVERCONFFILE", + "DC_SERVERCONFFILE", + + # user stuff + "USERNAME", + "USERID", + "PASSWORD", + "DC_USERNAME", + "DC_PASSWORD", + + # UID/GID for rfc2307 mapping tests + "UID_RFC2307TEST", + "GID_RFC2307TEST", + + # misc stuff + "KRB5_CONFIG", + "KRB5CCNAME", + "GNUPGHOME", + "SELFTEST_WINBINDD_SOCKET_DIR", + "NMBD_SOCKET_DIR", + "LOCAL_PATH", + "DNS_FORWARDER1", + "DNS_FORWARDER2", + "RESOLV_CONF", + "UNACCEPTABLE_PASSWORD", + "LOCK_DIR", + "SMBD_TEST_LOG", + + # nss_wrapper + "NSS_WRAPPER_PASSWD", + "NSS_WRAPPER_GROUP", + "NSS_WRAPPER_HOSTS", + "NSS_WRAPPER_HOSTNAME", + "NSS_WRAPPER_MODULE_SO_PATH", + "NSS_WRAPPER_MODULE_FN_PREFIX", + + # resolv_wrapper + "RESOLV_WRAPPER_CONF", + "RESOLV_WRAPPER_HOSTS", +); + +sub exported_envvars_str +{ + my ($testenv_vars) = @_; + my $out = ""; + + foreach (@exported_envvars) { + next unless defined($testenv_vars->{$_}); + $out .= $_."=".$testenv_vars->{$_}."\n"; + } + + return $out; +} + +sub clear_exported_envvars +{ + foreach (@exported_envvars) { + delete $ENV{$_}; + } +} + +sub export_envvars +{ + my ($testenv_vars) = @_; + + foreach (@exported_envvars) { + if (defined($testenv_vars->{$_})) { + $ENV{$_} = $testenv_vars->{$_}; + } else { + delete $ENV{$_}; + } + } +} + +sub export_envvars_to_file +{ + my ($filepath, $testenv_vars) = @_; + my $env_str = exported_envvars_str($testenv_vars); + + open(FILE, "> $filepath"); + print FILE "$env_str"; + close(FILE); +} + +# Returns true if kernel namespaces are being used instead of socket-wrapper. +# The default is false. +sub use_namespaces +{ + return defined($ENV{USE_NAMESPACES}); +} + +# returns a given testenv's interface-name (only when USE_NAMESPACES=1) +sub ns_interface_name +{ + my ($hostname) = @_; + + # when using namespaces, each testenv has its own vethX interface, + # where X = Samba::get_interface(testenv_name) + my $iface = get_interface($hostname); + return "veth$iface"; +} + +# Called after a new child namespace has been forked +sub ns_child_forked +{ + my ($child_pid, $env_vars) = @_; + + # we only need to do this for the first child forked for this testenv + if (defined($env_vars->{NS_PID})) { + return; + } + + # store the child PID. It's the only way the main (selftest) namespace can + # access the new child (testenv) namespace. + $env_vars->{NS_PID} = $child_pid; + + # Add the new child namespace's interface to the main selftest bridge. + # This connects together the various testenvs so that selftest can talk to + # them all + my $iface = ns_interface_name($env_vars->{NETBIOSNAME}); + system "$ENV{SRCDIR}/selftest/ns/add_bridge_iface.sh $iface-br selftest0"; +} + +# returns args to prepend to a command in order to execute it the correct +# namespace for the testenv (creating a new namespace if needed). +# This should only used when USE_NAMESPACES=1 is set. +sub ns_exec_preargs +{ + my ($parent_pid, $env_vars) = @_; + + # NS_PID stores the pid of the first child daemon run in this namespace + if (defined($env_vars->{NS_PID})) { + + # the namespace has already been created previously. So we use nsenter + # to execute the command in the given testenv's namespace. We need to + # use the NS_PID to identify this particular namespace + return ("nsenter", "-t", "$env_vars->{NS_PID}", "--net"); + } else { + + # We need to create a new namespace for this daemon (i.e. we're + # setting up a new testenv). First, write the environment variables to + # an exports.sh file for this testenv (for convenient access by the + # namespace scripts). + my $exports_file = "$env_vars->{TESTENV_DIR}/exports.sh"; + export_envvars_to_file($exports_file, $env_vars); + + # when using namespaces, each testenv has its own veth interface + my $interface = ns_interface_name($env_vars->{NETBIOSNAME}); + + # we use unshare to create a new network namespace. The start_in_ns.sh + # helper script gets run first to setup the new namespace's interfaces. + # (This all gets prepended around the actual command to run in the new + # namespace) + return ("unshare", "--net", "$ENV{SRCDIR}/selftest/ns/start_in_ns.sh", + $interface, $exports_file, $parent_pid); + } +} + + +sub check_env { + my ($self, $envvars) = @_; + return 1; +} + +sub teardown_env { + my ($self, $env) = @_; + return 1; +} + + +sub getlog_env { + return ''; +} + +1; diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm new file mode 100755 index 0000000..8f680b7 --- /dev/null +++ b/selftest/target/Samba3.pm @@ -0,0 +1,4182 @@ +#!/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 Samba3; + +use strict; +use warnings; +use Cwd qw(abs_path); +use FindBin qw($RealBin); +use POSIX; +use target::Samba; +use File::Path 'remove_tree'; + +sub return_alias_env +{ + my ($self, $path, $env) = @_; + + # just an alias + return $env; +} + +sub have_ads($) { + my ($self) = @_; + my $found_ads = 0; + my $smbd_build_options = Samba::bindir_path($self, "smbd") . " --configfile=/dev/null -b|"; + open(IN, $smbd_build_options) or die("Unable to run $smbd_build_options: $!"); + + while (<IN>) { + if (/WITH_ADS/) { + $found_ads = 1; + } + } + close IN; + + # If we were not built with ADS support, pretend we were never even available + print "smbd does not have ADS support\n" unless $found_ads; + return $found_ads; +} + +# return smb.conf parameters applicable to @path, based on the underlying +# filesystem type +sub get_fs_specific_conf($$) +{ + my ($self, $path) = @_; + my $mods = ""; + my $stat_out = `stat --file-system $path` or return ""; + + if ($stat_out =~ m/Type:\s+btrfs/) { + $mods .= "streams_xattr btrfs"; + } + + if ($mods) { + return "vfs objects = $mods"; + } + + return ''; +} + +sub new($$) { + my ($classname, $SambaCtx, $bindir, $srcdir, $server_maxtime) = @_; + my $self = { vars => {}, + SambaCtx => $SambaCtx, + bindir => $bindir, + srcdir => $srcdir, + server_maxtime => $server_maxtime + }; + bless $self; + return $self; +} + +sub teardown_env($$) +{ + my ($self, $envvars) = @_; + + if (defined($envvars->{CTDB_PREFIX})) { + $self->teardown_env_ctdb($envvars); + } else { + $self->teardown_env_samba($envvars); + } + + return; +} + +sub teardown_env_samba($$) +{ + my ($self, $envvars) = @_; + my $count = 0; + + # This should cause smbd to terminate gracefully + close($envvars->{STDIN_PIPE}); + + my $smbdpid = $envvars->{SMBD_TL_PID}; + my $nmbdpid = $envvars->{NMBD_TL_PID}; + my $winbinddpid = $envvars->{WINBINDD_TL_PID}; + my $samba_dcerpcdpid = $envvars->{SAMBA_DCERPCD_TL_PID}; + + # This should give it time to write out the gcov data + until ($count > 20) { + my $smbdchild = Samba::cleanup_child($smbdpid, "smbd"); + my $nmbdchild = Samba::cleanup_child($nmbdpid, "nmbd"); + my $winbinddchild = Samba::cleanup_child($winbinddpid, "winbindd"); + my $samba_dcerpcdchild = Samba::cleanup_child( + $samba_dcerpcdpid, "samba-dcerpcd"); + if ($smbdchild == -1 + && $nmbdchild == -1 + && $winbinddchild == -1 + && $samba_dcerpcdpid == -1) { + last; + } + sleep(1); + $count++; + } + + if ($count <= 20 && + kill(0, $smbdpid, $nmbdpid, $winbinddpid, $samba_dcerpcdpid) == 0) { + return; + } + + $self->stop_sig_term($smbdpid); + $self->stop_sig_term($nmbdpid); + $self->stop_sig_term($winbinddpid); + $self->stop_sig_term($samba_dcerpcdpid); + + $count = 0; + until ($count > 10) { + my $smbdchild = Samba::cleanup_child($smbdpid, "smbd"); + my $nmbdchild = Samba::cleanup_child($nmbdpid, "nmbd"); + my $winbinddchild = Samba::cleanup_child($winbinddpid, "winbindd"); + my $samba_dcerpcdpid = Samba::cleanup_child( + $samba_dcerpcdpid, "samba-dcerpcd"); + if ($smbdchild == -1 + && $nmbdchild == -1 + && $winbinddchild == -1 + && $samba_dcerpcdpid == -1) { + last; + } + sleep(1); + $count++; + } + + if ($count <= 10 && + kill(0, $smbdpid, $nmbdpid, $winbinddpid, $samba_dcerpcdpid) == 0) { + return; + } + + warn("timelimit process did not quit on SIGTERM, sending SIGKILL"); + $self->stop_sig_kill($smbdpid); + $self->stop_sig_kill($nmbdpid); + $self->stop_sig_kill($winbinddpid); + $self->stop_sig_kill($samba_dcerpcdpid); + + return 0; +} + +sub teardown_env_ctdb($$) +{ + my ($self, $data) = @_; + + if (defined($data->{SAMBA_NODES})) { + my $num_nodes = $data->{NUM_NODES}; + my $nodes = $data->{SAMBA_NODES}; + + for (my $i = 0; $i < $num_nodes; $i++) { + if (defined($nodes->[$i])) { + $self->teardown_env_samba($nodes->[$i]); + } + } + } + + close($data->{CTDB_STDIN_PIPE}); + + if (not defined($data->{SAMBA_NODES})) { + # Give waiting children time to exit + sleep(5); + } + + return 0; +} + +sub getlog_env_app($$$) +{ + my ($self, $envvars, $name) = @_; + + my $title = "$name LOG of: $envvars->{NETBIOSNAME}\n"; + my $out = $title; + + open(LOG, "<".$envvars->{$name."_TEST_LOG"}); + + seek(LOG, $envvars->{$name."_TEST_LOG_POS"}, SEEK_SET); + while (<LOG>) { + $out .= $_; + } + $envvars->{$name."_TEST_LOG_POS"} = tell(LOG); + close(LOG); + + return "" if $out eq $title; + + return $out; +} + +sub getlog_env($$) +{ + my ($self, $envvars) = @_; + my $ret = ""; + + $ret .= $self->getlog_env_app($envvars, "SMBD"); + $ret .= $self->getlog_env_app($envvars, "NMBD"); + $ret .= $self->getlog_env_app($envvars, "WINBINDD"); + + return $ret; +} + +sub check_env($$) +{ + my ($self, $envvars) = @_; + + my $childpid = waitpid(-1, WNOHANG); + + # TODO ... + return 1; +} + +# Declare the environments Samba3 makes available. +# To be set up, they will be called as +# samba3->setup_$envname($self, $path, $dep_1_vars, $dep_2_vars, ...) +%Samba3::ENV_DEPS = ( + # name => [dep_1, dep_2, ...], + nt4_dc => [], + nt4_dc_smb1 => [], + nt4_dc_smb1_done => ["nt4_dc_smb1"], + nt4_dc_schannel => [], + + simpleserver => [], + fileserver => [], + fileserver_smb1 => [], + fileserver_smb1_done => ["fileserver_smb1"], + maptoguest => [], + ktest => [], + + nt4_member => ["nt4_dc"], + + ad_member => ["ad_dc", "fl2008r2dc", "fl2003dc"], + ad_member_rfc2307 => ["ad_dc_ntvfs"], + ad_member_idmap_rid => ["ad_dc"], + admem_idmap_autorid => ["ad_dc"], + ad_member_idmap_ad => ["fl2008r2dc"], + ad_member_fips => ["ad_dc_fips"], + ad_member_offlogon => ["ad_dc"], + ad_member_oneway => ["fl2000dc"], + ad_member_idmap_nss => ["ad_dc"], + ad_member_s3_join => ["ad_dc"], + + clusteredmember => ["nt4_dc"], +); + +%Samba3::ENV_DEPS_POST = (); + +sub setup_nt4_dc +{ + my ($self, $path, $more_conf, $server) = @_; + + print "PROVISIONING NT4 DC..."; + + my $nt4_dc_options = " + domain master = yes + domain logons = yes + lanman auth = yes + ntlm auth = yes + raw NTLMv2 auth = yes + rpc start on demand helpers = false + + CVE_2020_1472:warn_about_unused_debug_level = 3 + 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 + + vfs_default:VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS = no + + fss: sequence timeout = 1 + check parent directory delete on close = yes +"; + + if (defined($more_conf)) { + $nt4_dc_options = $nt4_dc_options . $more_conf; + } + if (!defined($server)) { + $server = "LOCALNT4DC2"; + } + my $vars = $self->provision( + prefix => $path, + domain => "SAMBA-TEST", + server => $server, + password => "localntdc2pass", + extra_options => $nt4_dc_options); + + $vars or return undef; + + if (not $self->check_or_start( + env_vars => $vars, + samba_dcerpcd => "yes", + nmbd => "yes", + winbindd => "yes", + smbd => "yes")) { + return undef; + } + + $vars->{DOMSID} = $vars->{SAMSID}; + $vars->{DC_SERVER} = $vars->{SERVER}; + $vars->{DC_SERVER_IP} = $vars->{SERVER_IP}; + $vars->{DC_SERVER_IPV6} = $vars->{SERVER_IPV6}; + $vars->{DC_NETBIOSNAME} = $vars->{NETBIOSNAME}; + $vars->{DC_USERNAME} = $vars->{USERNAME}; + $vars->{DC_PASSWORD} = $vars->{PASSWORD}; + + return $vars; +} + +sub setup_nt4_dc_smb1 +{ + my ($self, $path) = @_; + my $conf = " +[global] + client min protocol = CORE + server min protocol = LANMAN1 +"; + return $self->setup_nt4_dc($path, $conf, "LCLNT4DC2SMB1"); +} + +sub setup_nt4_dc_smb1_done +{ + my ($self, $path, $dep_env) = @_; + return $self->return_alias_env($path, $dep_env); +} + +sub setup_nt4_dc_schannel +{ + my ($self, $path) = @_; + + print "PROVISIONING NT4 DC WITH SERVER SCHANNEL ..."; + + my $pdc_options = " + domain master = yes + domain logons = yes + lanman auth = yes + + server schannel = yes + # used to reproduce bug #12772 + server max protocol = SMB2_02 +"; + + my $vars = $self->provision( + prefix => $path, + domain => "NT4SCHANNEL", + server => "LOCALNT4DC9", + password => "localntdc9pass", + extra_options => $pdc_options); + + $vars or return undef; + + if (not $self->check_or_start( + env_vars => $vars, + nmbd => "yes", + winbindd => "yes", + smbd => "yes")) { + return undef; + } + + $vars->{DOMSID} = $vars->{SAMSID}; + $vars->{DC_SERVER} = $vars->{SERVER}; + $vars->{DC_SERVER_IP} = $vars->{SERVER_IP}; + $vars->{DC_SERVER_IPV6} = $vars->{SERVER_IPV6}; + $vars->{DC_NETBIOSNAME} = $vars->{NETBIOSNAME}; + $vars->{DC_USERNAME} = $vars->{USERNAME}; + $vars->{DC_PASSWORD} = $vars->{PASSWORD}; + + return $vars; +} + +sub setup_nt4_member +{ + my ($self, $prefix, $nt4_dc_vars) = @_; + my $count = 0; + my $rc; + + print "PROVISIONING MEMBER..."; + + my $require_mutexes = "dbwrap_tdb_require_mutexes:* = yes"; + if ($ENV{SELFTEST_DONT_REQUIRE_TDB_MUTEX_SUPPORT} // '' eq "1") { + $require_mutexes = ""; + } + + my $member_options = " + security = domain + dbwrap_tdb_mutexes:* = yes + ${require_mutexes} +"; + my $ret = $self->provision( + prefix => $prefix, + domain => $nt4_dc_vars->{DOMAIN}, + server => "LOCALNT4MEMBER3", + password => "localnt4member3pass", + extra_options => $member_options); + + $ret or return undef; + + my $nmblookup = Samba::bindir_path($self, "nmblookup"); + do { + print "Waiting for the LOGON SERVER registration ...\n"; + $rc = system("$nmblookup $ret->{CONFIGURATION} $ret->{DOMAIN}\#1c"); + if ($rc != 0) { + sleep(1); + } + $count++; + } while ($rc != 0 && $count < 10); + if ($count == 10) { + print "NMBD not reachable after 10 retries\n"; + teardown_env($self, $ret); + return 0; + } + + my $net = Samba::bindir_path($self, "net"); + # Add hosts file for name lookups + my $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' "; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$net rpc join $ret->{CONFIGURATION} $nt4_dc_vars->{DOMAIN} member"; + $cmd .= " -U$nt4_dc_vars->{USERNAME}\%$nt4_dc_vars->{PASSWORD}"; + + if (system($cmd) != 0) { + warn("Join failed\n$cmd"); + return undef; + } + + # Add hosts file for name lookups + $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' "; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$net $ret->{CONFIGURATION} primarytrust dumpinfo | grep -q 'REDACTED SECRET VALUES'"; + + if (system($cmd) != 0) { + warn("check failed\n$cmd"); + return undef; + } + + if (not $self->check_or_start( + env_vars => $ret, + nmbd => "yes", + winbindd => "yes", + smbd => "yes")) { + return undef; + } + + $ret->{DOMSID} = $nt4_dc_vars->{DOMSID}; + $ret->{DC_SERVER} = $nt4_dc_vars->{SERVER}; + $ret->{DC_SERVER_IP} = $nt4_dc_vars->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $nt4_dc_vars->{SERVER_IPV6}; + $ret->{DC_NETBIOSNAME} = $nt4_dc_vars->{NETBIOSNAME}; + $ret->{DC_USERNAME} = $nt4_dc_vars->{USERNAME}; + $ret->{DC_PASSWORD} = $nt4_dc_vars->{PASSWORD}; + + return $ret; +} + +sub setup_clusteredmember +{ + my ($self, $prefix, $nt4_dc_vars) = @_; + my $count = 0; + my $rc; + my @retvals = (); + my $ret; + + print "PROVISIONING CLUSTEREDMEMBER...\n"; + + my $prefix_abs = abs_path($prefix); + mkdir($prefix_abs, 0777); + + my $server_name = "CLUSTEREDMEMBER"; + + my $ctdb_data = $self->setup_ctdb($prefix); + + if (not $ctdb_data) { + print "No ctdb data\n"; + return undef; + } + + print "PROVISIONING CLUSTERED SAMBA...\n"; + + my $num_nodes = $ctdb_data->{NUM_NODES}; + my $nodes = $ctdb_data->{CTDB_NODES}; + + # Enable cleanup of earlier nodes if a later node fails + $ctdb_data->{SAMBA_NODES} = \@retvals; + + for (my $i = 0; $i < $num_nodes; $i++) { + my $node = $nodes->[$i]; + my $socket = $node->{SOCKET_FILE}; + my $server_name = $node->{SERVER_NAME}; + my $pub_iface = $node->{SOCKET_WRAPPER_DEFAULT_IFACE}; + my $node_prefix = $node->{NODE_PREFIX}; + + print "NODE_PREFIX=${node_prefix}\n"; + print "SOCKET=${socket}\n"; + + my $require_mutexes = "dbwrap_tdb_require_mutexes:* = yes"; + if ($ENV{SELFTEST_DONT_REQUIRE_TDB_MUTEX_SUPPORT} // '' eq "1") { + $require_mutexes = "" ; + } + + my $member_options = " + security = domain + server signing = on + clustering = yes + ctdbd socket = ${socket} + include = registry + dbwrap_tdb_mutexes:* = yes + ${require_mutexes} +"; + + my $node_ret = $self->provision( + prefix => "$node_prefix", + domain => $nt4_dc_vars->{DOMAIN}, + server => "$server_name", + password => "clustermember8pass", + netbios_name => "CLUSTEREDMEMBER", + share_dir => "${prefix_abs}/shared", + extra_options => $member_options, + no_delete_prefix => 1); + if (not $node_ret) { + print "Provision node $i failed\n"; + teardown_env($self, $ctdb_data); + return undef; + } + + my $registry_share_template = "$node_ret->{SERVERCONFFILE}.registry_share_template"; + unless (open(REGISTRYCONF, ">$registry_share_template")) { + warn("Unable to open $registry_share_template"); + teardown_env($self, $node_ret); + teardown_env($self, $ctdb_data); + return undef; + } + + print REGISTRYCONF " +[registry_share] + copy = tmp + comment = smb username is [%U] +"; + + close(REGISTRYCONF); + + my $net = Samba::bindir_path($self, "net"); + my $cmd = ""; + + $cmd .= "UID_WRAPPER_ROOT=1 "; + $cmd .= "$net conf import $node_ret->{CONFIGURATION} ${registry_share_template}"; + + my $net_ret = system($cmd); + if ($net_ret != 0) { + warn("net conf import failed: $net_ret\n$cmd"); + teardown_env($self, $node_ret); + teardown_env($self, $ctdb_data); + return undef; + } + + my $nmblookup = Samba::bindir_path($self, "nmblookup"); + do { + print "Waiting for the LOGON SERVER registration ...\n"; + $rc = system("$nmblookup $node_ret->{CONFIGURATION} " . + "$node_ret->{DOMAIN}\#1c"); + if ($rc != 0) { + sleep(1); + } + $count++; + } while ($rc != 0 && $count < 10); + + if ($count == 10) { + print "NMBD not reachable after 10 retries\n"; + teardown_env($self, $node_ret); + teardown_env($self, $ctdb_data); + return undef; + } + + push(@retvals, $node_ret); + } + + $ret = {%$ctdb_data, %{$retvals[0]}}; + + my $net = Samba::bindir_path($self, "net"); + my $cmd = ""; + $cmd .= "UID_WRAPPER_ROOT=1 "; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$net join $ret->{CONFIGURATION} $nt4_dc_vars->{DOMAIN} member"; + $cmd .= " -U$nt4_dc_vars->{USERNAME}\%$nt4_dc_vars->{PASSWORD}"; + + if (system($cmd) != 0) { + warn("Join failed\n$cmd"); + teardown_env($self, $ret); + return undef; + } + + for (my $i=0; $i<@retvals; $i++) { + my $node_provision = $retvals[$i]; + my $ok; + $ok = $self->check_or_start( + env_vars => $node_provision, + winbindd => "yes", + smbd => "yes", + child_cleanup => sub { + map { + my $fh = $_->{STDIN_PIPE}; + close($fh) if defined($fh); + } @retvals }); + if (not $ok) { + teardown_env($self, $ret); + return undef; + } + } + + # + # Build a unclist for every share + # + unless (open(NODES, "<$ret->{CTDB_NODES_FILE}")) { + warn("Unable to open CTDB nodes file"); + teardown_env($self, $ret); + return undef; + } + my @nodes = <NODES>; + close(NODES); + chomp @nodes; + + my $conffile = $ret->{SERVERCONFFILE}; + $cmd = ""; + $cmd .= 'sed -n -e \'s|^\[\(.*\)\]$|\1|p\''; + $cmd .= " \"$conffile\""; + $cmd .= " | grep -vx 'global'"; + + my @shares = `$cmd`; + $rc = $?; + if ($rc != 0) { + warn("Listing shares failed\n$cmd"); + teardown_env($self, $ret); + return undef; + } + chomp @shares; + + my $unclistdir = "${prefix_abs}/unclists"; + mkdir($unclistdir, 0777); + foreach my $share (@shares) { + my $l = "${unclistdir}/${share}.txt"; + unless (open(UNCLIST, ">${l}")) { + warn("Unable to open UNC list ${l}"); + teardown_env($self, $ret); + return undef; + } + foreach my $node (@nodes) { + print UNCLIST "//${node}/${share}\n"; + } + close(UNCLIST); + } + + $ret->{DOMSID} = $nt4_dc_vars->{DOMSID}; + $ret->{DC_SERVER} = $nt4_dc_vars->{SERVER}; + $ret->{DC_SERVER_IP} = $nt4_dc_vars->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $nt4_dc_vars->{SERVER_IPV6}; + $ret->{DC_NETBIOSNAME} = $nt4_dc_vars->{NETBIOSNAME}; + $ret->{DC_USERNAME} = $nt4_dc_vars->{USERNAME}; + $ret->{DC_PASSWORD} = $nt4_dc_vars->{PASSWORD}; + + return $ret; +} + +sub provision_ad_member +{ + my ($self, + $prefix, + $machine_account, + $dcvars, + $trustvars_f, + $trustvars_e, + $extra_member_options, + $force_fips_mode, + $offline_logon, + $no_nss_winbind) = @_; + + if (defined($offline_logon) && defined($no_nss_winbind)) { + warn ("Offline logon incompatible with no nss winbind\n"); + return undef; + } + + my $prefix_abs = abs_path($prefix); + my @dirs = (); + + mkdir($prefix_abs, 0777); + + my $share_dir="$prefix_abs/share"; + push(@dirs, $share_dir); + + my $substitution_path = "$share_dir/D_$dcvars->{DOMAIN}"; + push(@dirs, $substitution_path); + + $substitution_path = "$share_dir/D_$dcvars->{DOMAIN}/U_alice"; + push(@dirs, $substitution_path); + + $substitution_path = "$share_dir/D_$dcvars->{DOMAIN}/U_alice/G_domain users"; + push(@dirs, $substitution_path); + + # Using '/' as the winbind separator is a bad idea ... + $substitution_path = "$share_dir/D_$dcvars->{DOMAIN}/u_$dcvars->{DOMAIN}"; + push(@dirs, $substitution_path); + + $substitution_path = "$share_dir/D_$dcvars->{DOMAIN}/u_$dcvars->{DOMAIN}/alice"; + push(@dirs, $substitution_path); + + $substitution_path = "$share_dir/D_$dcvars->{DOMAIN}/u_$dcvars->{DOMAIN}/alice/g_$dcvars->{DOMAIN}"; + push(@dirs, $substitution_path); + + $substitution_path = "$share_dir/D_$dcvars->{DOMAIN}/u_$dcvars->{DOMAIN}/alice/g_$dcvars->{DOMAIN}/domain users"; + push(@dirs, $substitution_path); + + my $option_offline_logon = "no"; + if (defined($offline_logon)) { + $option_offline_logon = "yes"; + } + + my $netbios_aliases = ""; + if ($machine_account eq "LOCALADMEMBER") { + $netbios_aliases = "netbios aliases = foo bar"; + } + + unless (defined($extra_member_options)) { + $extra_member_options = ""; + } + + my $member_options = " + security = ads + workgroup = $dcvars->{DOMAIN} + realm = $dcvars->{REALM} + $netbios_aliases + template homedir = /home/%D/%G/%U + auth event notification = true + password server = $dcvars->{SERVER} + winbind scan trusted domains = no + winbind offline logon = $option_offline_logon + + allow dcerpc auth level connect:lsarpc = yes + dcesrv:max auth states = 8 + rpc start on demand helpers = false + + # Begin extra member options + $extra_member_options + # End extra member options + +[sub_dug] + path = $share_dir/D_%D/U_%U/G_%G + writeable = yes + +[sub_dug2] + path = $share_dir/D_%D/u_%u/g_%g + writeable = yes + +[sub_valid_users] + path = $share_dir + valid users = ADDOMAIN/%U + +[sub_valid_users_domain] + path = $share_dir + valid users = %D/%U + +[sub_valid_users_group] + path = $share_dir + valid users = \@$dcvars->{DOMAIN}/%G + +[valid_users] + path = $share_dir + valid users = $dcvars->{DOMAIN}/$dcvars->{DC_USERNAME} + +[valid_users_group] + path = $share_dir + valid users = \"\@$dcvars->{DOMAIN}/domain users\" + +[valid_users_unix_group] + path = $share_dir + valid users = \"+$dcvars->{DOMAIN}/domain users\" + +[valid_users_nis_group] + path = $share_dir + valid users = \"&$dcvars->{DOMAIN}/domain users\" + +[valid_users_unix_nis_group] + path = $share_dir + valid users = \"+&$dcvars->{DOMAIN}/domain users\" + +[valid_users_nis_unix_group] + path = $share_dir + valid users = \"&+$dcvars->{DOMAIN}/domain users\" + +[invalid_users] + path = $share_dir + invalid users = $dcvars->{DOMAIN}/$dcvars->{DC_USERNAME} + +[valid_and_invalid_users] + path = $share_dir + valid users = $dcvars->{DOMAIN}/$dcvars->{DC_USERNAME} $dcvars->{DOMAIN}/alice + invalid users = $dcvars->{DOMAIN}/$dcvars->{DC_USERNAME} +"; + + my $ret = $self->provision( + prefix => $prefix, + domain => $dcvars->{DOMAIN}, + realm => $dcvars->{REALM}, + server => $machine_account, + password => "loCalMemberPass", + extra_options => $member_options, + resolv_conf => $dcvars->{RESOLV_CONF}); + + $ret or return undef; + + mkdir($_, 0777) foreach(@dirs); + + $ret->{DOMAIN} = $dcvars->{DOMAIN}; + $ret->{REALM} = $dcvars->{REALM}; + $ret->{DOMSID} = $dcvars->{DOMSID}; + + my $ctx; + $ctx = {}; + $ctx->{krb5_conf} = "$prefix_abs/lib/krb5.conf"; + $ctx->{domain} = $dcvars->{DOMAIN}; + $ctx->{realm} = $dcvars->{REALM}; + $ctx->{dnsname} = lc($dcvars->{REALM}); + $ctx->{kdc_ipv4} = $dcvars->{SERVER_IP}; + $ctx->{kdc_ipv6} = $dcvars->{SERVER_IPV6}; + $ctx->{krb5_ccname} = "$prefix_abs/krb5cc_%{uid}"; + Samba::mk_krb5_conf($ctx, ""); + + $ret->{KRB5_CONFIG} = $ctx->{krb5_conf}; + + if (defined($force_fips_mode)) { + $ret->{GNUTLS_FORCE_FIPS_MODE} = "1"; + $ret->{OPENSSL_FORCE_FIPS_MODE} = "1"; + } + + my $net = Samba::bindir_path($self, "net"); + # Add hosts file for name lookups + my $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' "; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + if (defined($ret->{RESOLV_WRAPPER_CONF})) { + $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" "; + } else { + $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" "; + } + if (defined($force_fips_mode)) { + $cmd .= "GNUTLS_FORCE_FIPS_MODE=1 "; + $cmd .= "OPENSSL_FORCE_FIPS_MODE=1 "; + } + $cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" "; + $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$net join $ret->{CONFIGURATION}"; + $cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD} --use-kerberos=required"; + + if (system($cmd) != 0) { + warn("Join failed\n$cmd"); + return undef; + } + + # We need world access to this share, as otherwise the domain + # administrator from the AD domain provided by Samba4 can't + # access the share for tests. + chmod 0777, "$prefix/share"; + + if (defined($offline_logon)) { + my $wbinfo = Samba::bindir_path($self, "wbinfo"); + + if (not $self->check_or_start( + env_vars => $ret, + winbindd => "yes")) { + return undef; + } + + # Fill samlogoncache for alice + $cmd = "NSS_WRAPPER_PASSWD='$ret->{NSS_WRAPPER_PASSWD}' "; + $cmd .= "NSS_WRAPPER_GROUP='$ret->{NSS_WRAPPER_GROUP}' "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$wbinfo --pam-logon=ADDOMAIN/alice%Secret007"; + if (system($cmd) != 0) { + warn("Filling the cache failed\n$cmd"); + return undef; + } + + $cmd = "NSS_WRAPPER_PASSWD='$ret->{NSS_WRAPPER_PASSWD}' "; + $cmd .= "NSS_WRAPPER_GROUP='$ret->{NSS_WRAPPER_GROUP}' "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$wbinfo --ccache-save=ADDOMAIN/alice%Secret007"; + if (system($cmd) != 0) { + warn("Filling the cache failed\n$cmd"); + return undef; + } + + # Fill samlogoncache for bob + $cmd = "NSS_WRAPPER_PASSWD='$ret->{NSS_WRAPPER_PASSWD}' "; + $cmd .= "NSS_WRAPPER_GROUP='$ret->{NSS_WRAPPER_GROUP}' "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$wbinfo --pam-logon=ADDOMAIN/bob%Secret007"; + if (system($cmd) != 0) { + warn("Filling the cache failed\n$cmd"); + return undef; + } + + $cmd = "NSS_WRAPPER_PASSWD='$ret->{NSS_WRAPPER_PASSWD}' "; + $cmd .= "NSS_WRAPPER_GROUP='$ret->{NSS_WRAPPER_GROUP}' "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$wbinfo --ccache-save=ADDOMAIN/bob%Secret007"; + if (system($cmd) != 0) { + warn("Filling the cache failed\n$cmd"); + return undef; + } + + # Set windindd offline + my $smbcontrol = Samba::bindir_path($self, "smbcontrol"); + $cmd = "NSS_WRAPPER_PASSWD='$ret->{NSS_WRAPPER_PASSWD}' "; + $cmd .= "NSS_WRAPPER_GROUP='$ret->{NSS_WRAPPER_GROUP}' "; + $cmd .= "UID_WRAPPER_ROOT='1' "; + $cmd .= "$smbcontrol $ret->{CONFIGURATION} winbindd offline"; + if (system($cmd) != 0) { + warn("Setting winbindd offline failed\n$cmd"); + return undef; + } + + # Validate the offline cache + $cmd = "NSS_WRAPPER_PASSWD='$ret->{NSS_WRAPPER_PASSWD}' "; + $cmd .= "NSS_WRAPPER_GROUP='$ret->{NSS_WRAPPER_GROUP}' "; + $cmd .= "UID_WRAPPER_ROOT='1' "; + $cmd .= "$smbcontrol $ret->{CONFIGURATION} winbindd validate-cache"; + if (system($cmd) != 0) { + warn("Validation of winbind credential cache failed\n$cmd"); + teardown_env($self, $ret); + return undef; + } + + # Shut down winbindd + teardown_env($self, $ret); + + ### Change SOCKET_WRAPPER_DIR so it can't connect to AD + my $swrap_env = $ENV{SOCKET_WRAPPER_DIR}; + $ENV{SOCKET_WRAPPER_DIR} = "$prefix_abs"; + + # Start winbindd in offline mode + if (not $self->check_or_start( + env_vars => $ret, + winbindd => "offline")) { + return undef; + } + + # Set socket dir again + $ENV{SOCKET_WRAPPER_DIR} = $swrap_env; + + } else { + if (defined($no_nss_winbind)) { + $ret->{NSS_WRAPPER_MODULE_SO_PATH} = ""; + $ret->{NSS_WRAPPER_MODULE_FN_PREFIX} = ""; + } + + if (not $self->check_or_start( + env_vars => $ret, + samba_dcerpcd => "yes", + nmbd => "yes", + winbindd => "yes", + smbd => "yes")) { + return undef; + } + } + + $ret->{DC_SERVER} = $dcvars->{SERVER}; + $ret->{DC_SERVER_IP} = $dcvars->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6}; + $ret->{DC_SERVERCONFFILE} = $dcvars->{SERVERCONFFILE}; + $ret->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME}; + $ret->{DC_USERNAME} = $dcvars->{USERNAME}; + $ret->{DC_PASSWORD} = $dcvars->{PASSWORD}; + + # forest trust + $ret->{TRUST_F_BOTH_SERVER} = $trustvars_f->{SERVER}; + $ret->{TRUST_F_BOTH_SERVER_IP} = $trustvars_f->{SERVER_IP}; + $ret->{TRUST_F_BOTH_SERVER_IPV6} = $trustvars_f->{SERVER_IPV6}; + $ret->{TRUST_F_BOTH_NETBIOSNAME} = $trustvars_f->{NETBIOSNAME}; + $ret->{TRUST_F_BOTH_USERNAME} = $trustvars_f->{USERNAME}; + $ret->{TRUST_F_BOTH_PASSWORD} = $trustvars_f->{PASSWORD}; + $ret->{TRUST_F_BOTH_DOMAIN} = $trustvars_f->{DOMAIN}; + $ret->{TRUST_F_BOTH_REALM} = $trustvars_f->{REALM}; + + # external trust + $ret->{TRUST_E_BOTH_SERVER} = $trustvars_e->{SERVER}; + $ret->{TRUST_E_BOTH_SERVER_IP} = $trustvars_e->{SERVER_IP}; + $ret->{TRUST_E_BOTH_SERVER_IPV6} = $trustvars_e->{SERVER_IPV6}; + $ret->{TRUST_E_BOTH_NETBIOSNAME} = $trustvars_e->{NETBIOSNAME}; + $ret->{TRUST_E_BOTH_USERNAME} = $trustvars_e->{USERNAME}; + $ret->{TRUST_E_BOTH_PASSWORD} = $trustvars_e->{PASSWORD}; + $ret->{TRUST_E_BOTH_DOMAIN} = $trustvars_e->{DOMAIN}; + $ret->{TRUST_E_BOTH_REALM} = $trustvars_e->{REALM}; + + return $ret; +} + +sub setup_ad_member +{ + my ($self, + $prefix, + $dcvars, + $trustvars_f, + $trustvars_e) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->have_ads()) { + return "UNKNOWN"; + } + + print "PROVISIONING AD MEMBER..."; + + return $self->provision_ad_member($prefix, + "LOCALADMEMBER", + $dcvars, + $trustvars_f, + $trustvars_e); +} + +sub setup_ad_member_s3_join +{ + my ($self, + $prefix, + $dcvars, + $trustvars_f, + $trustvars_e) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->have_ads()) { + return "UNKNOWN"; + } + + print "PROVISIONING AD MEMBER..."; + + return $self->provision_ad_member($prefix, + "LOCALADMEMBER2", + $dcvars, + $trustvars_f, + $trustvars_e); +} + +sub setup_ad_member_rfc2307 +{ + my ($self, $prefix, $dcvars) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->have_ads()) { + return "UNKNOWN"; + } + + print "PROVISIONING S3 AD MEMBER WITH idmap_rfc2307 config..."; + + my $member_options = " + security = ads + workgroup = $dcvars->{DOMAIN} + realm = $dcvars->{REALM} + idmap cache time = 0 + idmap negative cache time = 0 + idmap config * : backend = autorid + idmap config * : range = 1000000-1999999 + idmap config * : rangesize = 100000 + idmap config $dcvars->{DOMAIN} : backend = rfc2307 + idmap config $dcvars->{DOMAIN} : range = 2000000-2999999 + idmap config $dcvars->{DOMAIN} : ldap_server = ad + idmap config $dcvars->{DOMAIN} : bind_path_user = ou=idmap,dc=samba,dc=example,dc=com + idmap config $dcvars->{DOMAIN} : bind_path_group = ou=idmap,dc=samba,dc=example,dc=com + + password server = $dcvars->{SERVER} +"; + + my $ret = $self->provision( + prefix => $prefix, + domain => $dcvars->{DOMAIN}, + realm => $dcvars->{REALM}, + server => "RFC2307MEMBER", + password => "loCalMemberPass", + extra_options => $member_options, + resolv_conf => $dcvars->{RESOLV_CONF}); + + $ret or return undef; + + $ret->{DOMAIN} = $dcvars->{DOMAIN}; + $ret->{REALM} = $dcvars->{REALM}; + $ret->{DOMSID} = $dcvars->{DOMSID}; + + my $ctx; + my $prefix_abs = abs_path($prefix); + $ctx = {}; + $ctx->{krb5_conf} = "$prefix_abs/lib/krb5.conf"; + $ctx->{domain} = $dcvars->{DOMAIN}; + $ctx->{realm} = $dcvars->{REALM}; + $ctx->{dnsname} = lc($dcvars->{REALM}); + $ctx->{kdc_ipv4} = $dcvars->{SERVER_IP}; + $ctx->{kdc_ipv6} = $dcvars->{SERVER_IPV6}; + $ctx->{krb5_ccname} = "$prefix_abs/krb5cc_%{uid}"; + Samba::mk_krb5_conf($ctx, ""); + + $ret->{KRB5_CONFIG} = $ctx->{krb5_conf}; + + my $net = Samba::bindir_path($self, "net"); + # Add hosts file for name lookups + my $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' "; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + if (defined($ret->{RESOLV_WRAPPER_CONF})) { + $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" "; + } else { + $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" "; + } + $cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" "; + $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$net join $ret->{CONFIGURATION}"; + $cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}"; + + if (system($cmd) != 0) { + warn("Join failed\n$cmd"); + return undef; + } + + # We need world access to this share, as otherwise the domain + # administrator from the AD domain provided by Samba4 can't + # access the share for tests. + chmod 0777, "$prefix/share"; + + if (not $self->check_or_start( + env_vars => $ret, + nmbd => "yes", + winbindd => "yes", + smbd => "yes")) { + return undef; + } + + $ret->{DC_SERVER} = $dcvars->{SERVER}; + $ret->{DC_SERVER_IP} = $dcvars->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6}; + $ret->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME}; + $ret->{DC_USERNAME} = $dcvars->{USERNAME}; + $ret->{DC_PASSWORD} = $dcvars->{PASSWORD}; + + return $ret; +} + +sub setup_admem_idmap_autorid +{ + my ($self, $prefix, $dcvars) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->have_ads()) { + return "UNKNOWN"; + } + + print "PROVISIONING S3 AD MEMBER WITH idmap_autorid config..."; + + my $member_options = " + security = ads + workgroup = $dcvars->{DOMAIN} + realm = $dcvars->{REALM} + idmap config * : backend = autorid + idmap config * : range = 1000000-19999999 + idmap config * : rangesize = 1000000 + + # Prevent overridding the provisioned lib/krb5.conf which sets certain + # values required for tests to succeed + create krb5 conf = no +"; + + my $ret = $self->provision( + prefix => $prefix, + domain => $dcvars->{DOMAIN}, + realm => $dcvars->{REALM}, + server => "ADMEMAUTORID", + password => "loCalMemberPass", + extra_options => $member_options, + resolv_conf => $dcvars->{RESOLV_CONF}); + + $ret or return undef; + + $ret->{DOMAIN} = $dcvars->{DOMAIN}; + $ret->{REALM} = $dcvars->{REALM}; + $ret->{DOMSID} = $dcvars->{DOMSID}; + + my $ctx; + my $prefix_abs = abs_path($prefix); + $ctx = {}; + $ctx->{krb5_conf} = "$prefix_abs/lib/krb5.conf"; + $ctx->{domain} = $dcvars->{DOMAIN}; + $ctx->{realm} = $dcvars->{REALM}; + $ctx->{dnsname} = lc($dcvars->{REALM}); + $ctx->{kdc_ipv4} = $dcvars->{SERVER_IP}; + $ctx->{kdc_ipv6} = $dcvars->{SERVER_IPV6}; + $ctx->{krb5_ccname} = "$prefix_abs/krb5cc_%{uid}"; + Samba::mk_krb5_conf($ctx, ""); + + $ret->{KRB5_CONFIG} = $ctx->{krb5_conf}; + + my $net = Samba::bindir_path($self, "net"); + # Add hosts file for name lookups + my $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' "; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + if (defined($ret->{RESOLV_WRAPPER_CONF})) { + $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" "; + } else { + $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" "; + } + $cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" "; + $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$net join $ret->{CONFIGURATION}"; + $cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}"; + + if (system($cmd) != 0) { + warn("Join failed\n$cmd"); + return undef; + } + + # We need world access to this share, as otherwise the domain + # administrator from the AD domain provided by Samba4 can't + # access the share for tests. + chmod 0777, "$prefix/share"; + + if (not $self->check_or_start( + env_vars => $ret, + nmbd => "yes", + winbindd => "yes", + smbd => "yes")) { + return undef; + } + + $ret->{DC_SERVER} = $dcvars->{SERVER}; + $ret->{DC_SERVER_IP} = $dcvars->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6}; + $ret->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME}; + $ret->{DC_USERNAME} = $dcvars->{USERNAME}; + $ret->{DC_PASSWORD} = $dcvars->{PASSWORD}; + + return $ret; +} + +sub setup_ad_member_idmap_rid +{ + my ($self, $prefix, $dcvars) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->have_ads()) { + return "UNKNOWN"; + } + + print "PROVISIONING S3 AD MEMBER WITH idmap_rid config..."; + + my $member_options = " + security = ads + workgroup = $dcvars->{DOMAIN} + realm = $dcvars->{REALM} + idmap config * : backend = tdb + idmap config * : range = 1000000-1999999 + idmap config $dcvars->{DOMAIN} : backend = rid + idmap config $dcvars->{DOMAIN} : range = 2000000-2999999 + # Prevent overridding the provisioned lib/krb5.conf which sets certain + # values required for tests to succeed + create krb5 conf = no + map to guest = bad user + server signing = required +"; + + my $ret = $self->provision( + prefix => $prefix, + domain => $dcvars->{DOMAIN}, + realm => $dcvars->{REALM}, + server => "IDMAPRIDMEMBER", + password => "loCalMemberPass", + extra_options => $member_options, + resolv_conf => $dcvars->{RESOLV_CONF}); + + $ret or return undef; + + $ret->{DOMAIN} = $dcvars->{DOMAIN}; + $ret->{REALM} = $dcvars->{REALM}; + $ret->{DOMSID} = $dcvars->{DOMSID}; + + my $ctx; + my $prefix_abs = abs_path($prefix); + $ctx = {}; + $ctx->{krb5_conf} = "$prefix_abs/lib/krb5.conf"; + $ctx->{domain} = $dcvars->{DOMAIN}; + $ctx->{realm} = $dcvars->{REALM}; + $ctx->{dnsname} = lc($dcvars->{REALM}); + $ctx->{kdc_ipv4} = $dcvars->{SERVER_IP}; + $ctx->{kdc_ipv6} = $dcvars->{SERVER_IPV6}; + $ctx->{krb5_ccname} = "$prefix_abs/krb5cc_%{uid}"; + Samba::mk_krb5_conf($ctx, ""); + + $ret->{KRB5_CONFIG} = $ctx->{krb5_conf}; + + my $net = Samba::bindir_path($self, "net"); + # Add hosts file for name lookups + my $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' "; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + if (defined($ret->{RESOLV_WRAPPER_CONF})) { + $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" "; + } else { + $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" "; + } + $cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" "; + $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$net join $ret->{CONFIGURATION}"; + $cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}"; + + if (system($cmd) != 0) { + warn("Join failed\n$cmd"); + return undef; + } + + # We need world access to this share, as otherwise the domain + # administrator from the AD domain provided by Samba4 can't + # access the share for tests. + chmod 0777, "$prefix/share"; + + if (not $self->check_or_start( + env_vars => $ret, + nmbd => "yes", + winbindd => "yes", + smbd => "yes")) { + return undef; + } + + $ret->{DC_SERVER} = $dcvars->{SERVER}; + $ret->{DC_SERVER_IP} = $dcvars->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6}; + $ret->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME}; + $ret->{DC_USERNAME} = $dcvars->{USERNAME}; + $ret->{DC_PASSWORD} = $dcvars->{PASSWORD}; + + return $ret; +} + +sub setup_ad_member_idmap_ad +{ + my ($self, $prefix, $dcvars) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->have_ads()) { + return "UNKNOWN"; + } + + print "PROVISIONING S3 AD MEMBER WITH idmap_ad config..."; + + my $member_options = " + security = ads + workgroup = $dcvars->{DOMAIN} + realm = $dcvars->{REALM} + password server = $dcvars->{SERVER} + idmap config * : backend = tdb + idmap config * : range = 1000000-1999999 + idmap config $dcvars->{DOMAIN} : backend = ad + idmap config $dcvars->{DOMAIN} : range = 2000000-2999999 + idmap config $dcvars->{DOMAIN} : unix_primary_group = yes + idmap config $dcvars->{DOMAIN} : unix_nss_info = yes + idmap config $dcvars->{TRUST_DOMAIN} : backend = ad + idmap config $dcvars->{TRUST_DOMAIN} : range = 2000000-2999999 + gensec_gssapi:requested_life_time = 5 +"; + + my $ret = $self->provision( + prefix => $prefix, + domain => $dcvars->{DOMAIN}, + realm => $dcvars->{REALM}, + server => "IDMAPADMEMBER", + password => "loCalMemberPass", + extra_options => $member_options, + resolv_conf => $dcvars->{RESOLV_CONF}); + + $ret or return undef; + + $ret->{DOMAIN} = $dcvars->{DOMAIN}; + $ret->{REALM} = $dcvars->{REALM}; + $ret->{DOMSID} = $dcvars->{DOMSID}; + + my $ctx; + my $prefix_abs = abs_path($prefix); + $ctx = {}; + $ctx->{krb5_conf} = "$prefix_abs/lib/krb5.conf"; + $ctx->{domain} = $dcvars->{DOMAIN}; + $ctx->{realm} = $dcvars->{REALM}; + $ctx->{dnsname} = lc($dcvars->{REALM}); + $ctx->{kdc_ipv4} = $dcvars->{SERVER_IP}; + $ctx->{kdc_ipv6} = $dcvars->{SERVER_IPV6}; + $ctx->{krb5_ccname} = "$prefix_abs/krb5cc_%{uid}"; + Samba::mk_krb5_conf($ctx, ""); + + $ret->{KRB5_CONFIG} = $ctx->{krb5_conf}; + + my $net = Samba::bindir_path($self, "net"); + # Add hosts file for name lookups + my $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' "; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + if (defined($ret->{RESOLV_WRAPPER_CONF})) { + $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" "; + } else { + $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" "; + } + $cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" "; + $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$net join $ret->{CONFIGURATION}"; + $cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}"; + + if (system($cmd) != 0) { + warn("Join failed\n$cmd"); + return undef; + } + + # We need world access to this share, as otherwise the domain + # administrator from the AD domain provided by Samba4 can't + # access the share for tests. + chmod 0777, "$prefix/share"; + + if (not $self->check_or_start( + env_vars => $ret, + nmbd => "yes", + winbindd => "yes", + smbd => "yes")) { + return undef; + } + + $ret->{DC_SERVER} = $dcvars->{SERVER}; + $ret->{DC_SERVER_IP} = $dcvars->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6}; + $ret->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME}; + $ret->{DC_USERNAME} = $dcvars->{USERNAME}; + $ret->{DC_PASSWORD} = $dcvars->{PASSWORD}; + + $ret->{TRUST_SERVER} = $dcvars->{TRUST_SERVER}; + $ret->{TRUST_USERNAME} = $dcvars->{TRUST_USERNAME}; + $ret->{TRUST_PASSWORD} = $dcvars->{TRUST_PASSWORD}; + $ret->{TRUST_DOMAIN} = $dcvars->{TRUST_DOMAIN}; + $ret->{TRUST_REALM} = $dcvars->{TRUST_REALM}; + $ret->{TRUST_DOMSID} = $dcvars->{TRUST_DOMSID}; + + return $ret; +} + +sub setup_ad_member_oneway +{ + my ($self, $prefix, $dcvars) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->have_ads()) { + return "UNKNOWN"; + } + + print "PROVISIONING S3 AD MEMBER WITH one-way trust..."; + + my $member_options = " + security = ads + workgroup = $dcvars->{DOMAIN} + realm = $dcvars->{REALM} + password server = $dcvars->{SERVER} + idmap config * : backend = tdb + idmap config * : range = 1000000-1999999 + gensec_gssapi:requested_life_time = 5 +"; + + my $ret = $self->provision( + prefix => $prefix, + domain => $dcvars->{DOMAIN}, + server => "S2KMEMBER", + password => "loCalS2KMemberPass", + extra_options => $member_options, + resolv_conf => $dcvars->{RESOLV_CONF}); + + $ret or return undef; + + $ret->{DOMAIN} = $dcvars->{DOMAIN}; + $ret->{REALM} = $dcvars->{REALM}; + $ret->{DOMSID} = $dcvars->{DOMSID}; + + my $ctx; + my $prefix_abs = abs_path($prefix); + $ctx = {}; + $ctx->{krb5_conf} = "$prefix_abs/lib/krb5.conf"; + $ctx->{domain} = $dcvars->{DOMAIN}; + $ctx->{realm} = $dcvars->{REALM}; + $ctx->{dnsname} = lc($dcvars->{REALM}); + $ctx->{kdc_ipv4} = $dcvars->{SERVER_IP}; + $ctx->{kdc_ipv6} = $dcvars->{SERVER_IPV6}; + $ctx->{krb5_ccname} = "$prefix_abs/krb5cc_%{uid}"; + Samba::mk_krb5_conf($ctx, ""); + + $ret->{KRB5_CONFIG} = $ctx->{krb5_conf}; + + my $net = Samba::bindir_path($self, "net"); + # Add hosts file for name lookups + my $cmd = "NSS_WRAPPER_HOSTS='$ret->{NSS_WRAPPER_HOSTS}' "; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + if (defined($ret->{RESOLV_WRAPPER_CONF})) { + $cmd .= "RESOLV_WRAPPER_CONF=\"$ret->{RESOLV_WRAPPER_CONF}\" "; + } else { + $cmd .= "RESOLV_WRAPPER_HOSTS=\"$ret->{RESOLV_WRAPPER_HOSTS}\" "; + } + $cmd .= "RESOLV_CONF=\"$ret->{RESOLV_CONF}\" "; + $cmd .= "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$net join $ret->{CONFIGURATION}"; + $cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}"; + + if (system($cmd) != 0) { + warn("Join failed\n$cmd"); + return undef; + } + + if (not $self->check_or_start( + env_vars => $ret, + winbindd => "yes")) { + return undef; + } + + $ret->{DC_SERVER} = $dcvars->{SERVER}; + $ret->{DC_SERVER_IP} = $dcvars->{SERVER_IP}; + $ret->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6}; + $ret->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME}; + $ret->{DC_USERNAME} = $dcvars->{USERNAME}; + $ret->{DC_PASSWORD} = $dcvars->{PASSWORD}; + + $ret->{TRUST_SERVER} = $dcvars->{TRUST_SERVER}; + $ret->{TRUST_USERNAME} = $dcvars->{TRUST_USERNAME}; + $ret->{TRUST_PASSWORD} = $dcvars->{TRUST_PASSWORD}; + $ret->{TRUST_DOMAIN} = $dcvars->{TRUST_DOMAIN}; + $ret->{TRUST_REALM} = $dcvars->{TRUST_REALM}; + $ret->{TRUST_DOMSID} = $dcvars->{TRUST_DOMSID}; + + return $ret; +} + +sub setup_ad_member_fips +{ + my ($self, + $prefix, + $dcvars, + $trustvars_f, + $trustvars_e) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->have_ads()) { + return "UNKNOWN"; + } + + print "PROVISIONING AD FIPS MEMBER..."; + + return $self->provision_ad_member($prefix, + "FIPSADMEMBER", + $dcvars, + $trustvars_f, + $trustvars_e, + undef, + 1); +} + +sub setup_ad_member_offlogon +{ + my ($self, + $prefix, + $dcvars, + $trustvars_f, + $trustvars_e) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->have_ads()) { + return "UNKNOWN"; + } + + print "PROVISIONING AD MEMBER OFFLINE LOGON..."; + + return $self->provision_ad_member($prefix, + "OFFLINEADMEM", + $dcvars, + $trustvars_f, + $trustvars_e, + undef, + undef, + 1); +} + +sub setup_ad_member_idmap_nss +{ + my ($self, + $prefix, + $dcvars, + $trustvars_f, + $trustvars_e) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->have_ads()) { + return "UNKNOWN"; + } + + print "PROVISIONING AD MEMBER WITHOUT NSS WINBIND WITH idmap_nss config..."; + + my $extra_member_options = " + # bob:x:65521:65531:localbob gecos:/:/bin/false + # jane:x:65520:65531:localjane gecos:/:/bin/false + # jackthemapper:x:65519:65531:localjackthemaper gecos:/:/bin/false + # jacknomapper:x:65518:65531:localjacknomaper gecos:/:/bin/false + idmap config $dcvars->{DOMAIN} : backend = nss + idmap config $dcvars->{DOMAIN} : range = 65518-65521 + + # Support SMB1 so that we can use posix_whoami(). + client min protocol = CORE + server min protocol = LANMAN1 + + username map = $prefix/lib/username.map +"; + + my $ret = $self->provision_ad_member($prefix, + "ADMEMIDMAPNSS", + $dcvars, + $trustvars_f, + $trustvars_e, + $extra_member_options, + undef, + undef, + 1); + + open(USERMAP, ">$prefix/lib/username.map") or die("Unable to open $prefix/lib/username.map"); + print USERMAP " +!jacknomapper = \@jackthemappergroup +!root = jacknomappergroup +root = $dcvars->{DOMAIN}/root +bob = $dcvars->{DOMAIN}/bob +"; + close(USERMAP); + + return $ret; +} + +sub setup_simpleserver +{ + my ($self, $path) = @_; + + print "PROVISIONING simple server..."; + + my $prefix_abs = abs_path($path); + mkdir($prefix_abs, 0777); + + my $external_streams_depot="$prefix_abs/external_streams_depot"; + remove_tree($external_streams_depot); + mkdir($external_streams_depot, 0777); + + my $simpleserver_options = " + lanman auth = yes + ntlm auth = yes + vfs objects = xattr_tdb streams_depot + change notify = no + server smb encrypt = off + +[vfs_aio_pthread] + path = $prefix_abs/share + read only = no + vfs objects = aio_pthread + aio_pthread:aio open = yes + smbd async dosmode = no + +[vfs_aio_pthread_async_dosmode_default1] + path = $prefix_abs/share + read only = no + vfs objects = aio_pthread + store dos attributes = yes + aio_pthread:aio open = yes + smbd async dosmode = yes + +[vfs_aio_pthread_async_dosmode_default2] + path = $prefix_abs/share + read only = no + vfs objects = aio_pthread xattr_tdb + store dos attributes = yes + aio_pthread:aio open = yes + smbd async dosmode = yes + +[async_dosmode_shadow_copy2] + path = $prefix_abs/share + read only = no + vfs objects = shadow_copy2 xattr_tdb + smbd async dosmode = yes + +[vfs_aio_fork] + path = $prefix_abs/share + vfs objects = aio_fork + read only = no + vfs_aio_fork:erratic_testing_mode=yes + +[dosmode] + path = $prefix_abs/share + vfs objects = + store dos attributes = yes + hide files = /hidefile/ + hide dot files = yes + +[hidenewfiles] + path = $prefix_abs/share + hide new files timeout = 5 + +[external_streams_depot] + path = $prefix_abs/share + read only = no + streams_depot:directory = $external_streams_depot +"; + + my $vars = $self->provision( + prefix => $path, + domain => "WORKGROUP", + server => "LOCALSHARE4", + password => "local4pass", + extra_options => $simpleserver_options); + + $vars or return undef; + + if (not $self->check_or_start( + env_vars => $vars, + nmbd => "yes", + smbd => "yes")) { + return undef; + } + + return $vars; +} + +sub create_file_chmod($$) +{ + my ($name, $mode) = @_; + my $fh; + + unless (open($fh, '>', $name)) { + warn("Unable to open $name"); + return undef; + } + chmod($mode, $fh); +} + +sub setup_fileserver +{ + my ($self, $path, $more_conf, $server) = @_; + my $prefix_abs = abs_path($path); + my $srcdir_abs = abs_path($self->{srcdir}); + + print "PROVISIONING file server ...\n"; + + my @dirs = (); + + mkdir($prefix_abs, 0777); + + my $usershare_dir="$prefix_abs/lib/usershare"; + + mkdir("$prefix_abs/lib", 0755); + remove_tree($usershare_dir); + mkdir($usershare_dir, 01770); + + my $share_dir="$prefix_abs/share"; + + # Create share directory structure + my $lower_case_share_dir="$share_dir/lower-case"; + push(@dirs, $lower_case_share_dir); + + my $lower_case_share_dir_30000="$share_dir/lower-case-30000"; + push(@dirs, $lower_case_share_dir_30000); + + my $dfree_share_dir="$share_dir/dfree"; + push(@dirs, $dfree_share_dir); + push(@dirs, "$dfree_share_dir/subdir1"); + push(@dirs, "$dfree_share_dir/subdir2"); + push(@dirs, "$dfree_share_dir/subdir3"); + + my $quotadir_dir="$share_dir/quota"; + push(@dirs, $quotadir_dir); + + my $valid_users_sharedir="$share_dir/valid_users"; + push(@dirs,$valid_users_sharedir); + + my $offline_sharedir="$share_dir/offline"; + push(@dirs,$offline_sharedir); + + my $force_user_valid_users_dir = "$share_dir/force_user_valid_users"; + push(@dirs, $force_user_valid_users_dir); + + my $smbget_sharedir="$share_dir/smbget"; + push(@dirs,$smbget_sharedir); + + my $tarmode_sharedir="$share_dir/tarmode"; + push(@dirs,$tarmode_sharedir); + + my $tarmode2_sharedir="$share_dir/tarmode2"; + push(@dirs,$tarmode2_sharedir); + + my $smbcacls_sharedir="$share_dir/smbcacls"; + push(@dirs,$smbcacls_sharedir); + + my $usershare_sharedir="$share_dir/usershares"; + push(@dirs,$usershare_sharedir); + + my $dropbox_sharedir="$share_dir/dropbox"; + push(@dirs,$dropbox_sharedir); + + my $bad_iconv_sharedir="$share_dir/bad_iconv"; + push(@dirs, $bad_iconv_sharedir); + + my $veto_sharedir="$share_dir/veto"; + push(@dirs,$veto_sharedir); + + my $virusfilter_sharedir="$share_dir/virusfilter"; + push(@dirs,$virusfilter_sharedir); + + my $delete_unwrite_sharedir="$share_dir/delete_unwrite"; + push(@dirs,$delete_unwrite_sharedir); + push(@dirs, "$delete_unwrite_sharedir/delete_veto_yes"); + push(@dirs, "$delete_unwrite_sharedir/delete_veto_no"); + + my $volume_serial_number_sharedir="$share_dir/volume_serial_number"; + push(@dirs, $volume_serial_number_sharedir); + + my $ip4 = Samba::get_ipv4_addr("FILESERVER"); + my $fileserver_options = " + kernel change notify = yes + spotlight backend = elasticsearch + elasticsearch:address = $ip4 + elasticsearch:port = 8080 + elasticsearch:mappings = $srcdir_abs/source3/rpc_server/mdssvc/elasticsearch_mappings.json + + usershare path = $usershare_dir + usershare max shares = 10 + usershare allow guests = yes + usershare prefix allow list = $usershare_sharedir + + get quota command = $prefix_abs/getset_quota.py + set quota command = $prefix_abs/getset_quota.py +[tarmode] + path = $tarmode_sharedir + comment = tar test share + xattr_tdb:file = $prefix_abs/tarmode-xattr.tdb +[tarmode2] + path = $tarmode2_sharedir + comment = tar test share + xattr_tdb:file = $prefix_abs/tarmode2-xattr.tdb +[spotlight] + path = $share_dir + spotlight = yes + read only = no +[no_spotlight] + path = $share_dir + spotlight = no + read only = no +[lowercase] + path = $lower_case_share_dir + comment = smb username is [%U] + case sensitive = True + default case = lower + preserve case = no + short preserve case = no +[lowercase-30000] + path = $lower_case_share_dir_30000 + comment = smb username is [%U] + case sensitive = True + default case = lower + preserve case = no + short preserve case = no +[dfree] + path = $dfree_share_dir + comment = smb username is [%U] + dfree command = $srcdir_abs/testprogs/blackbox/dfree.sh +[valid-users-access] + path = $valid_users_sharedir + valid users = +userdup +[offline] + path = $offline_sharedir + vfs objects = offline + +# BUG: https://bugzilla.samba.org/show_bug.cgi?id=9878 +# RH BUG: https://bugzilla.redhat.com/show_bug.cgi?id=1077651 +[force_user_valid_users] + path = $force_user_valid_users_dir + comment = force user with valid users combination test share + valid users = +force_user + force user = force_user + force group = everyone + write list = force_user + +[smbget] + path = $smbget_sharedir + comment = smb username is [%U] + guest ok = yes +[ign_sysacls] + path = $share_dir + comment = ignore system acls + acl_xattr:ignore system acls = yes +[inherit_owner] + path = $share_dir + comment = inherit owner + inherit owner = yes +[inherit_owner_u] + path = $share_dir + comment = inherit only unix owner + inherit owner = unix only + acl_xattr:ignore system acls = yes +# BUG: https://bugzilla.samba.org/show_bug.cgi?id=13690 +[force_group_test] + path = $share_dir + comment = force group test +# force group = everyone + +[create_mode_664] + path = $share_dir + comment = smb username is [%U] + create mask = 0644 + force create mode = 0664 + vfs objects = dirsort + +[dropbox] + path = $dropbox_sharedir + comment = smb username is [%U] + writeable = yes + vfs objects = + +[bad_iconv] + path = $bad_iconv_sharedir + comment = smb username is [%U] + vfs objects = + +[veto_files_nodelete] + path = $veto_sharedir + read only = no + msdfs root = yes + veto files = /veto_name*/ + delete veto files = no + +[veto_files_delete] + path = $veto_sharedir + msdfs root = yes + veto files = /veto_name*/ + delete veto files = yes + +[delete_veto_files_only] + path = $veto_sharedir + delete veto files = yes + +[veto_files_nohidden] + path = $veto_sharedir + veto files = /.*/ + +[veto_files] + path = $veto_sharedir + veto files = /veto_name*/ + +[delete_yes_unwrite] + read only = no + path = $delete_unwrite_sharedir + hide unwriteable files = yes + delete veto files = yes + +[delete_no_unwrite] + read only = no + path = $delete_unwrite_sharedir + hide unwriteable files = yes + delete veto files = no + +[virusfilter] + path = $virusfilter_sharedir + vfs objects = acl_xattr virusfilter + virusfilter:scanner = dummy + virusfilter:min file size = 0 + virusfilter:infected files = *infected* + virusfilter:infected file action = rename + virusfilter:scan on close = yes + vfs_default:VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS = no + +[volumeserialnumber] + path = $volume_serial_number_sharedir + volume serial number = 0xdeadbeef + +[ea_acl_xattr] + path = $share_dir + vfs objects = acl_xattr + acl_xattr:security_acl_name = user.hackme + read only = no + +[homes] + comment = Home directories + browseable = No + read only = No +"; + + if (defined($more_conf)) { + $fileserver_options = $fileserver_options . $more_conf; + } + if (!defined($server)) { + $server = "FILESERVER"; + } + + my $vars = $self->provision( + prefix => $path, + domain => "WORKGROUP", + server => $server, + password => "fileserver", + extra_options => $fileserver_options, + no_delete_prefix => 1); + + $vars or return undef; + + if (not $self->check_or_start( + env_vars => $vars, + nmbd => "yes", + smbd => "yes")) { + return undef; + } + + + mkdir($_, 0777) foreach(@dirs); + + ## Create case sensitive lower case share dir + foreach my $file ('a'..'z') { + my $full_path = $lower_case_share_dir . '/' . $file; + open my $fh, '>', $full_path; + # Add some content to file + print $fh $full_path; + close $fh; + } + + for (my $file = 1; $file < 51; ++$file) { + my $full_path = $lower_case_share_dir . '/' . $file; + open my $fh, '>', $full_path; + # Add some content to file + print $fh $full_path; + close $fh; + } + + # Create content for 30000 share + foreach my $file ('a'..'z') { + my $full_path = $lower_case_share_dir_30000 . '/' . $file; + open my $fh, '>', $full_path; + # Add some content to file + print $fh $full_path; + close $fh; + } + + for (my $file = 1; $file < 30001; ++$file) { + my $full_path = $lower_case_share_dir_30000 . '/' . $file; + open my $fh, '>', $full_path; + # Add some content to file + print $fh $full_path; + close $fh; + } + + ## + ## create a listable file in valid_users_share + ## + create_file_chmod("$valid_users_sharedir/foo", 0644) or return undef; + + ## + ## create a valid utf8 filename which is invalid as a CP850 conversion + ## + create_file_chmod("$bad_iconv_sharedir/\xED\x9F\xBF", 0644) or return undef; + + ## + ## create unwritable files inside inside the delete unwrite veto share dirs. + ## + unlink("$delete_unwrite_sharedir/delete_veto_yes/file_444"); + create_file_chmod("$delete_unwrite_sharedir/delete_veto_yes/file_444", 0444) or return undef; + unlink("$delete_unwrite_sharedir/delete_veto_no/file_444"); + create_file_chmod("$delete_unwrite_sharedir/delete_veto_no/file_444", 0444) or return undef; + + return $vars; +} + +sub setup_fileserver_smb1 +{ + my ($self, $path) = @_; + my $prefix_abs = abs_path($path); + my $conf = " +[global] + client min protocol = CORE + server min protocol = LANMAN1 + +[hidenewfiles] + path = $prefix_abs/share + hide new files timeout = 5 +[vfs_aio_pthread] + path = $prefix_abs/share + read only = no + vfs objects = aio_pthread + aio_pthread:aio open = yes + smbd async dosmode = no + +[vfs_aio_pthread_async_dosmode_default1] + path = $prefix_abs/share + read only = no + vfs objects = aio_pthread + store dos attributes = yes + aio_pthread:aio open = yes + smbd async dosmode = yes + +[vfs_aio_pthread_async_dosmode_default2] + path = $prefix_abs/share + read only = no + vfs objects = aio_pthread xattr_tdb + store dos attributes = yes + aio_pthread:aio open = yes + smbd async dosmode = yes + +[vfs_aio_fork] + path = $prefix_abs/share + vfs objects = aio_fork + read only = no + vfs_aio_fork:erratic_testing_mode=yes +"; + return $self->setup_fileserver($path, $conf, "FILESERVERSMB1"); +} + +sub setup_fileserver_smb1_done +{ + my ($self, $path, $dep_env) = @_; + return $self->return_alias_env($path, $dep_env); +} + +sub setup_ktest +{ + my ($self, $prefix) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->have_ads()) { + return "UNKNOWN"; + } + + print "PROVISIONING server with security=ads..."; + + my $ktest_options = " + workgroup = KTEST + realm = ktest.samba.example.com + security = ads + server signing = required + server min protocol = SMB3_00 + client max protocol = SMB3 + + # This disables NTLM auth against the local SAM, which + # we use can then test this setting by. + ntlm auth = disabled + + idmap config * : backend = autorid + idmap config * : range = 1000000-1999999 + idmap config * : rangesize = 100000 +"; + + my $ret = $self->provision( + prefix => $prefix, + domain => "KTEST", + server => "LOCALKTEST6", + password => "localktest6pass", + extra_options => $ktest_options); + + $ret or return undef; + + my $ctx; + my $prefix_abs = abs_path($prefix); + $ctx = {}; + $ctx->{krb5_conf} = "$prefix_abs/lib/krb5.conf"; + $ctx->{domain} = "KTEST"; + $ctx->{realm} = "KTEST.SAMBA.EXAMPLE.COM"; + $ctx->{dnsname} = lc($ctx->{realm}); + $ctx->{kdc_ipv4} = "0.0.0.0"; + $ctx->{kdc_ipv6} = "::"; + $ctx->{krb5_ccname} = "$prefix_abs/krb5cc_%{uid}"; + Samba::mk_krb5_conf($ctx, ""); + + $ret->{KRB5_CONFIG} = $ctx->{krb5_conf}; + +#This is the secrets.tdb created by 'net ads join' from Samba3 to a +#Samba4 DC with the same parameters as are being used here. The +#domain SID is S-1-5-21-1071277805-689288055-3486227160 + $ret->{SAMSID} = "S-1-5-21-1911091480-1468226576-2729736297"; + $ret->{DOMSID} = "S-1-5-21-1071277805-689288055-3486227160"; + + system("cp $self->{srcdir}/source3/selftest/ktest-secrets.tdb $prefix/private/secrets.tdb"); + chmod 0600, "$prefix/private/secrets.tdb"; + +#Make sure there's no old ntdb file. + system("rm -f $prefix/private/secrets.ntdb"); + +#This uses a pre-calculated krb5 credentials cache, obtained by running Samba4 with: +# "--option=kdc:service ticket lifetime=239232" "--option=kdc:user ticket lifetime=239232" "--option=kdc:renewal lifetime=239232" +# +#and having in krb5.conf: +# ticket_lifetime = 799718400 +# renew_lifetime = 799718400 +# +# The commands for the -2 keytab where were: +# kinit administrator@KTEST.SAMBA.EXAMPLE.COM +# kvno host/localktest6@KTEST.SAMBA.EXAMPLE.COM +# kvno cifs/localktest6@KTEST.SAMBA.EXAMPLE.COM +# kvno host/LOCALKTEST6@KTEST.SAMBA.EXAMPLE.COM +# kvno cifs/LOCALKTEST6@KTEST.SAMBA.EXAMPLE.COM +# +# and then for the -3 keytab, I did +# +# net changetrustpw; kdestroy and the same again. +# +# This creates a credential cache with a very long lifetime (2036 at +# at 2011-04), and shows that running 'net changetrustpw' does not +# break existing logins (for the secrets.tdb method at least). +# + + $ret->{KRB5_CCACHE}="FILE:$prefix/krb5_ccache"; + + system("cp $self->{srcdir}/source3/selftest/ktest-krb5_ccache-2 $prefix/krb5_ccache-2"); + chmod 0600, "$prefix/krb5_ccache-2"; + + system("cp $self->{srcdir}/source3/selftest/ktest-krb5_ccache-3 $prefix/krb5_ccache-3"); + chmod 0600, "$prefix/krb5_ccache-3"; + + # We need world access to this share, as otherwise the domain + # administrator from the AD domain provided by ktest can't + # access the share for tests. + chmod 0777, "$prefix/share"; + + if (not $self->check_or_start( + env_vars => $ret, + nmbd => "yes", + winbindd => "offline", + smbd => "yes")) { + return undef; + } + return $ret; +} + +sub setup_maptoguest +{ + my ($self, $path) = @_; + my $prefix_abs = abs_path($path); + my $libdir="$prefix_abs/lib"; + my $share_dir="$prefix_abs/share"; + my $errorinjectconf="$libdir/error_inject.conf"; + + print "PROVISIONING maptoguest..."; + + my $options = " +map to guest = bad user +ntlm auth = yes +server min protocol = LANMAN1 + +[force_user_error_inject] + path = $share_dir + vfs objects = acl_xattr fake_acls xattr_tdb error_inject + force user = user1 + include = $errorinjectconf +"; + + my $vars = $self->provision( + prefix => $path, + domain => "WORKGROUP", + server => "maptoguest", + password => "maptoguestpass", + extra_options => $options); + + $vars or return undef; + + if (not $self->check_or_start( + env_vars => $vars, + nmbd => "yes", + smbd => "yes")) { + return undef; + } + + return $vars; +} + +sub stop_sig_term($$) { + my ($self, $pid) = @_; + kill("USR1", $pid) or kill("ALRM", $pid) or warn("Unable to kill $pid: $!"); +} + +sub stop_sig_kill($$) { + my ($self, $pid) = @_; + kill("ALRM", $pid) or warn("Unable to kill $pid: $!"); +} + +sub write_pid($$$) +{ + my ($env_vars, $app, $pid) = @_; + + open(PID, ">$env_vars->{PIDDIR}/timelimit.$app.pid"); + print PID $pid; + close(PID); +} + +sub read_pid($$) +{ + my ($env_vars, $app) = @_; + + open(PID, "<$env_vars->{PIDDIR}/timelimit.$app.pid"); + my $pid = <PID>; + close(PID); + return $pid; +} + +# builds up the cmd args to run an s3 binary (i.e. smbd, nmbd, etc) +sub make_bin_cmd +{ + my ($self, $binary, $env_vars, $options, $valgrind, $dont_log_stdout) = @_; + + my @optargs = (); + if (defined($options)) { + @optargs = split(/ /, $options); + } + my @preargs = (Samba::bindir_path($self, "timelimit"), $self->{server_maxtime}); + + if (defined($valgrind)) { + @preargs = split(/ /, $valgrind); + } + my @args = ("-F", "--no-process-group", + "--configfile=$env_vars->{SERVERCONFFILE}", + "-l", $env_vars->{LOGDIR}); + + if (not defined($dont_log_stdout)) { + push(@args, "--debug-stdout"); + } + return (@preargs, $binary, @args, @optargs); +} + +sub check_or_start($$) { + my ($self, %args) = @_; + my $env_vars = $args{env_vars}; + my $nmbd = $args{nmbd} // "no"; + my $winbindd = $args{winbindd} // "no"; + my $smbd = $args{smbd} // "no"; + my $samba_dcerpcd = $args{samba_dcerpcd} // "no"; + my $child_cleanup = $args{child_cleanup}; + + my $STDIN_READER; + + # 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}); + + my $binary = Samba::bindir_path($self, "samba-dcerpcd"); + my @full_cmd = $self->make_bin_cmd( + $binary, + $env_vars, + $ENV{SAMBA_DCERPCD_OPTIONS}, + $ENV{SAMBA_DCERPCD_VALGRIND}, + $ENV{SAMBA_DCERPCD_DONT_LOG_STDOUT}); + push(@full_cmd, '--libexec-rpcds'); + + my $samba_dcerpcd_envs = Samba::get_env_for_process( + "samba_dcerpcd", $env_vars); + + # fork and exec() samba_dcerpcd in the child process + my $daemon_ctx = { + NAME => "samba_dcerpcd", + BINARY_PATH => $binary, + FULL_CMD => [ @full_cmd ], + LOG_FILE => $env_vars->{SAMBA_DCERPCD_TEST_LOG}, + PCAP_FILE => "env-$ENV{ENVNAME}-samba_dcerpcd", + ENV_VARS => $samba_dcerpcd_envs, + }; + if ($samba_dcerpcd ne "yes") { + $daemon_ctx->{SKIP_DAEMON} = 1; + } + + my $pid = Samba::fork_and_exec( + $self, $env_vars, $daemon_ctx, $STDIN_READER, $child_cleanup); + + $env_vars->{SAMBA_DCERPCD_TL_PID} = $pid; + write_pid($env_vars, "samba_dcerpcd", $pid); + + $binary = Samba::bindir_path($self, "nmbd"); + @full_cmd = $self->make_bin_cmd($binary, $env_vars, + $ENV{NMBD_OPTIONS}, $ENV{NMBD_VALGRIND}, + $ENV{NMBD_DONT_LOG_STDOUT}); + my $nmbd_envs = Samba::get_env_for_process("nmbd", $env_vars); + delete $nmbd_envs->{RESOLV_WRAPPER_CONF}; + delete $nmbd_envs->{RESOLV_WRAPPER_HOSTS}; + + # fork and exec() nmbd in the child process + $daemon_ctx = { + NAME => "nmbd", + BINARY_PATH => $binary, + FULL_CMD => [ @full_cmd ], + LOG_FILE => $env_vars->{NMBD_TEST_LOG}, + PCAP_FILE => "env-$ENV{ENVNAME}-nmbd", + ENV_VARS => $nmbd_envs, + }; + if ($nmbd ne "yes") { + $daemon_ctx->{SKIP_DAEMON} = 1; + } + $pid = Samba::fork_and_exec( + $self, $env_vars, $daemon_ctx, $STDIN_READER, $child_cleanup); + + $env_vars->{NMBD_TL_PID} = $pid; + write_pid($env_vars, "nmbd", $pid); + + $binary = Samba::bindir_path($self, "winbindd"); + @full_cmd = $self->make_bin_cmd($binary, $env_vars, + $ENV{WINBINDD_OPTIONS}, + $ENV{WINBINDD_VALGRIND}, + $ENV{WINBINDD_DONT_LOG_STDOUT}); + + # fork and exec() winbindd in the child process + $daemon_ctx = { + NAME => "winbindd", + BINARY_PATH => $binary, + FULL_CMD => [ @full_cmd ], + LOG_FILE => $env_vars->{WINBINDD_TEST_LOG}, + PCAP_FILE => "env-$ENV{ENVNAME}-winbindd", + }; + if ($winbindd ne "yes" and $winbindd ne "offline") { + $daemon_ctx->{SKIP_DAEMON} = 1; + } + + $pid = Samba::fork_and_exec( + $self, $env_vars, $daemon_ctx, $STDIN_READER, $child_cleanup); + + $env_vars->{WINBINDD_TL_PID} = $pid; + write_pid($env_vars, "winbindd", $pid); + + $binary = Samba::bindir_path($self, "smbd"); + @full_cmd = $self->make_bin_cmd($binary, $env_vars, + $ENV{SMBD_OPTIONS}, $ENV{SMBD_VALGRIND}, + $ENV{SMBD_DONT_LOG_STDOUT}); + + # fork and exec() smbd in the child process + $daemon_ctx = { + NAME => "smbd", + BINARY_PATH => $binary, + FULL_CMD => [ @full_cmd ], + LOG_FILE => $env_vars->{SMBD_TEST_LOG}, + PCAP_FILE => "env-$ENV{ENVNAME}-smbd", + }; + if ($smbd ne "yes") { + $daemon_ctx->{SKIP_DAEMON} = 1; + } + + $pid = Samba::fork_and_exec( + $self, $env_vars, $daemon_ctx, $STDIN_READER, $child_cleanup); + + $env_vars->{SMBD_TL_PID} = $pid; + write_pid($env_vars, "smbd", $pid); + + # close the parent's read-end of the pipe + close($STDIN_READER); + + return $self->wait_for_start($env_vars, + $nmbd, + $winbindd, + $smbd, + $samba_dcerpcd); +} + +sub createuser($$$$$) +{ + my ($self, $username, $password, $conffile, $env) = @_; + my $cmd = "UID_WRAPPER_ROOT=1 " . Samba::bindir_path($self, "smbpasswd")." -c $conffile -L -s -a $username > /dev/null"; + + keys %$env; + while(my($var, $val) = each %$env) { + $cmd = "$var=\"$val\" $cmd"; + } + + unless (open(PWD, "|$cmd")) { + warn("Unable to set password for $username account\n$cmd"); + return undef; + } + print PWD "$password\n$password\n"; + unless (close(PWD)) { + warn("Unable to set password for $username account\n$cmd"); + return undef; + } +} + +sub provision($$) +{ + my ($self, %args) = @_; + + my $prefix = $args{prefix}; + my $domain = $args{domain}; + my $realm = $args{realm}; + my $server = $args{server}; + my $password = $args{password}; + my $extra_options = $args{extra_options}; + my $resolv_conf = $args{resolv_conf}; + my $no_delete_prefix= $args{no_delete_prefix}; + my $netbios_name = $args{netbios_name} // $server; + my $server_log_level = $ENV{SERVER_LOG_LEVEL} || 1; + + ## + ## setup the various environment variables we need + ## + + my $samsid = Samba::random_domain_sid(); + my $swiface = Samba::get_interface($server); + my %ret = (); + my %createuser_env = (); + my $server_ip = Samba::get_ipv4_addr($server); + my $server_ipv6 = Samba::get_ipv6_addr($server); + my $dns_domain; + if (defined($realm)) { + $dns_domain = lc($realm); + } else { + $dns_domain = "samba.example.com"; + } + + my $unix_name = ($ENV{USER} or $ENV{LOGNAME} or `PATH=/usr/ucb:$ENV{PATH} whoami`); + chomp $unix_name; + my $unix_uid = $>; + my $unix_gids_str = $); + my @unix_gids = split(" ", $unix_gids_str); + + my $prefix_abs = abs_path($prefix); + my $bindir_abs = abs_path($self->{bindir}); + + my @dirs = (); + + my $shrdir=$args{share_dir} // "$prefix_abs/share"; + push(@dirs,$shrdir); + + my $libdir="$prefix_abs/lib"; + push(@dirs,$libdir); + + my $piddir="$prefix_abs/pid"; + push(@dirs,$piddir); + + my $privatedir="$prefix_abs/private"; + push(@dirs,$privatedir); + + my $cachedir = "$prefix_abs/cachedir"; + push(@dirs, $cachedir); + + my $binddnsdir = "$prefix_abs/bind-dns"; + push(@dirs, $binddnsdir); + + my $lockdir="$prefix_abs/lockdir"; + push(@dirs,$lockdir); + + my $eventlogdir="$prefix_abs/lockdir/eventlog"; + push(@dirs,$eventlogdir); + + my $logdir="$prefix_abs/logs"; + push(@dirs,$logdir); + + my $driver32dir="$shrdir/W32X86"; + push(@dirs,$driver32dir); + + my $driver64dir="$shrdir/x64"; + push(@dirs,$driver64dir); + + my $driver40dir="$shrdir/WIN40"; + push(@dirs,$driver40dir); + + my $ro_shrdir="$shrdir/root-tmp"; + push(@dirs,$ro_shrdir); + + my $noperm_shrdir="$shrdir/noperm-tmp"; + push(@dirs,$noperm_shrdir); + + my $msdfs_shrdir="$shrdir/msdfsshare"; + push(@dirs,$msdfs_shrdir); + + my $msdfs_shrdir2="$shrdir/msdfsshare2"; + push(@dirs,$msdfs_shrdir2); + + my $msdfs_deeppath="$msdfs_shrdir/deeppath"; + push(@dirs,$msdfs_deeppath); + + my $smbcacls_sharedir_dfs="$shrdir/smbcacls_sharedir_dfs"; + push(@dirs,$smbcacls_sharedir_dfs); + + my $smbcacls_share="$shrdir/smbcacls_share"; + push(@dirs,$smbcacls_share); + + my $smbcacls_share_testdir="$shrdir/smbcacls_share/smbcacls"; + push(@dirs,$smbcacls_share_testdir); + + my $badnames_shrdir="$shrdir/badnames"; + push(@dirs,$badnames_shrdir); + + my $lease1_shrdir="$shrdir/dynamic"; + push(@dirs,$lease1_shrdir); + + my $manglenames_shrdir="$shrdir/manglenames"; + push(@dirs,$manglenames_shrdir); + + my $widelinks_shrdir="$shrdir/widelinks"; + push(@dirs,$widelinks_shrdir); + + my $widelinks_linkdir="$shrdir/widelinks_foo"; + push(@dirs,$widelinks_linkdir); + + my $fsrvp_shrdir="$shrdir/fsrvp"; + push(@dirs,$fsrvp_shrdir); + + my $shadow_tstdir="$shrdir/shadow"; + push(@dirs,$shadow_tstdir); + my $shadow_mntdir="$shadow_tstdir/mount"; + push(@dirs,$shadow_mntdir); + my $shadow_basedir="$shadow_mntdir/base"; + push(@dirs,$shadow_basedir); + my $shadow_shrdir="$shadow_basedir/share"; + push(@dirs,$shadow_shrdir); + + my $nosymlinks_shrdir="$shrdir/nosymlinks"; + push(@dirs,$nosymlinks_shrdir); + + my $local_symlinks_shrdir="$shrdir/local_symlinks"; + push(@dirs,$local_symlinks_shrdir); + + my $fruit_resource_stream_shrdir="$shrdir/fruit_resource_stream"; + push(@dirs,$fruit_resource_stream_shrdir); + + # this gets autocreated by winbindd + my $wbsockdir="$prefix_abs/wbsock"; + + my $nmbdsockdir="$prefix_abs/nmbd"; + unlink($nmbdsockdir); + + ## + ## create the test directory layout + ## + die ("prefix_abs = ''") if $prefix_abs eq ""; + die ("prefix_abs = '/'") if $prefix_abs eq "/"; + + mkdir($prefix_abs, 0777); + print "CREATE TEST ENVIRONMENT IN '$prefix'..."; + if (not defined($no_delete_prefix) or not $no_delete_prefix) { + system("rm -rf $prefix_abs/*"); + } + mkdir($_, 0777) foreach(@dirs); + + my $fs_specific_conf = $self->get_fs_specific_conf($shrdir); + + ## + ## lockdir and piddir must be 0755 + ## + chmod 0755, $lockdir; + chmod 0755, $piddir; + + + ## + ## Create a directory without permissions to enter + ## + chmod 0000, $noperm_shrdir; + + ## + ## create ro and msdfs share layout + ## + + chmod 0755, $ro_shrdir; + + create_file_chmod("$ro_shrdir/readable_file", 0644) or return undef; + create_file_chmod("$ro_shrdir/unreadable_file", 0600) or return undef; + + create_file_chmod("$ro_shrdir/msdfs-target", 0600) or return undef; + symlink "msdfs:$server_ip\\ro-tmp,$server_ipv6\\ro-tmp", + "$msdfs_shrdir/msdfs-src1"; + symlink "msdfs:$server_ipv6\\ro-tmp", "$msdfs_shrdir/deeppath/msdfs-src2"; + symlink "msdfs:$server_ip\\smbcacls_sharedir_dfs,$server_ipv6\\smbcacls_sharedir_dfs", + "$msdfs_shrdir/smbcacls_sharedir_dfs"; + + symlink "msdfs:$server_ip\\msdfs-share2,$server_ipv6\\msdfs-share2", "$msdfs_shrdir/dfshop1"; + symlink "msdfs:$server_ip\\tmp,$server_ipv6\\tmp", "$msdfs_shrdir2/dfshop2"; + ## + ## create bad names in $badnames_shrdir + ## + ## (An invalid name, would be mangled to 8.3). + create_file_chmod("$badnames_shrdir/\340|\231\216\377\177", + 0600) or return undef; + + ## (A bad name, would not be mangled to 8.3). + create_file_chmod("$badnames_shrdir/\240\276\346\327\377\177", + 0666) or return undef; + + ## (A bad good name). + create_file_chmod("$badnames_shrdir/blank.txt", + 0666) or return undef; + + ## + ## create mangleable directory names in $manglenames_shrdir + ## + my $manglename_target = "$manglenames_shrdir/foo:bar"; + mkdir($manglename_target, 0777); + + ## + ## create symlinks for widelinks tests. + ## + my $widelinks_target = "$widelinks_linkdir/target"; + create_file_chmod("$widelinks_target", 0666) or return undef; + + ## + ## This link should get an error + ## + symlink "$widelinks_target", "$widelinks_shrdir/source"; + ## + ## This link should be allowed + ## + symlink "$widelinks_shrdir", "$widelinks_shrdir/dot"; + + my $conffile="$libdir/server.conf"; + my $dfqconffile="$libdir/dfq.conf"; + my $errorinjectconf="$libdir/error_inject.conf"; + my $delayinjectconf="$libdir/delay_inject.conf"; + my $globalinjectconf="$libdir/global_inject.conf"; + my $aliceconfdir="$libdir"; + my $aliceconffile="$libdir/alice.conf"; + + my $nss_wrapper_pl = "$ENV{PERL} $self->{srcdir}/third_party/nss_wrapper/nss_wrapper.pl"; + my $nss_wrapper_passwd = "$privatedir/passwd"; + my $nss_wrapper_group = "$privatedir/group"; + my $nss_wrapper_hosts = "$ENV{SELFTEST_PREFIX}/hosts"; + my $dns_host_file = "$ENV{SELFTEST_PREFIX}/dns_host_file"; + + my $mod_printer_pl = "$ENV{PERL} $self->{srcdir}/source3/script/tests/printing/modprinter.pl"; + + my $fake_snap_pl = "$ENV{PERL} $self->{srcdir}/source3/script/tests/fake_snap.pl"; + + my @eventlog_list = ("dns server", "application"); + + ## + ## calculate uids and gids + ## + + my ($max_uid, $max_gid); + my ($uid_nobody, $uid_root, $uid_pdbtest, $uid_pdbtest2, $uid_userdup); + my ($uid_pdbtest_wkn); + my ($uid_smbget); + my ($uid_force_user); + my ($gid_nobody, $gid_nogroup, $gid_root, $gid_domusers, $gid_domadmins); + my ($gid_userdup, $gid_everyone); + my ($gid_force_user); + my ($gid_jackthemapper); + my ($gid_jacknomapper); + my ($uid_user1); + my ($uid_user2); + my ($uid_gooduser); + my ($uid_eviluser); + my ($uid_slashuser); + my ($uid_localbob); + my ($uid_localjane); + my ($uid_localjackthemapper); + my ($uid_localjacknomapper); + + if ($unix_uid < 0xffff - 13) { + $max_uid = 0xffff; + } else { + $max_uid = $unix_uid; + } + + $uid_root = $max_uid - 1; + $uid_nobody = $max_uid - 2; + $uid_pdbtest = $max_uid - 3; + $uid_pdbtest2 = $max_uid - 4; + $uid_userdup = $max_uid - 5; + $uid_pdbtest_wkn = $max_uid - 6; + $uid_force_user = $max_uid - 7; + $uid_smbget = $max_uid - 8; + $uid_user1 = $max_uid - 9; + $uid_user2 = $max_uid - 10; + $uid_gooduser = $max_uid - 11; + $uid_eviluser = $max_uid - 12; + $uid_slashuser = $max_uid - 13; + $uid_localbob = $max_uid - 14; + $uid_localjane = $max_uid - 15; + $uid_localjackthemapper = $max_uid - 16; + $uid_localjacknomapper = $max_uid - 17; + + if ($unix_gids[0] < 0xffff - 8) { + $max_gid = 0xffff; + } else { + $max_gid = $unix_gids[0]; + } + + $gid_nobody = $max_gid - 1; + $gid_nogroup = $max_gid - 2; + $gid_root = $max_gid - 3; + $gid_domusers = $max_gid - 4; + $gid_domadmins = $max_gid - 5; + $gid_userdup = $max_gid - 6; + $gid_everyone = $max_gid - 7; + $gid_force_user = $max_gid - 8; + $gid_jackthemapper = $max_gid - 9; + $gid_jacknomapper = $max_gid - 10; + + ## + ## create conffile + ## + + unless (open(CONF, ">$conffile")) { + warn("Unable to open $conffile"); + return undef; + } + + my $interfaces = Samba::get_interfaces_config($server); + + print CONF " +[global] + dcesrv:fuzz directory = $cachedir/fuzz + netbios name = $netbios_name + interfaces = $interfaces + bind interfaces only = yes + panic action = cd $self->{srcdir} && $self->{srcdir}/selftest/gdb_backtrace %d %\$(MAKE_TEST_BINARY) + smbd:suicide mode = yes + smbd:FSCTL_SMBTORTURE = yes + smbd:validate_oplock_types = yes + + client min protocol = SMB2_02 + server min protocol = SMB2_02 + + server multi channel support = yes + + workgroup = $domain + + private dir = $privatedir + binddns dir = $binddnsdir + pid directory = $piddir + lock directory = $lockdir + log file = $logdir/log.\%m + log level = $server_log_level + debug pid = yes + max log size = 0 + + state directory = $lockdir + cache directory = $lockdir + + passdb backend = tdbsam + + time server = yes + + add user script = $nss_wrapper_pl --passwd_path $nss_wrapper_passwd --type passwd --action add --name %u --gid $gid_nogroup + add group script = $nss_wrapper_pl --group_path $nss_wrapper_group --type group --action add --name %g + add machine script = $nss_wrapper_pl --passwd_path $nss_wrapper_passwd --type passwd --action add --name %u --gid $gid_nogroup + add user to group script = $nss_wrapper_pl --passwd_path $nss_wrapper_passwd --type member --action add --member %u --name %g --group_path $nss_wrapper_group + delete user script = $nss_wrapper_pl --passwd_path $nss_wrapper_passwd --type passwd --action delete --name %u + delete group script = $nss_wrapper_pl --group_path $nss_wrapper_group --type group --action delete --name %g + delete user from group script = $nss_wrapper_pl --passwd_path $nss_wrapper_passwd --type member --action delete --member %u --name %g --group_path $nss_wrapper_group + + addprinter command = $mod_printer_pl -a -s $conffile -- + deleteprinter command = $mod_printer_pl -d -s $conffile -- + + eventlog list = application \"dns server\" + + kernel oplocks = no + kernel change notify = no + + logging = file + printing = bsd + printcap name = /dev/null + + winbindd socket directory = $wbsockdir + nmbd:socket dir = $nmbdsockdir + idmap config * : range = 100000-200000 + winbind enum users = yes + winbind enum groups = yes + winbind separator = / + include system krb5 conf = no + +# min receivefile size = 4000 + + read only = no + + smbd:sharedelay = 100000 + smbd:writetimeupdatedelay = 500000 + map hidden = no + map system = no + map readonly = no + store dos attributes = yes + create mask = 755 + dos filemode = yes + strict rename = yes + strict sync = yes + mangled names = yes + vfs objects = acl_xattr fake_acls xattr_tdb streams_depot time_audit full_audit + + full_audit:syslog = no + full_audit:success = none + full_audit:failure = none + + 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 + + ncalrpc dir = $prefix_abs/ncalrpc + + # The samba3.blackbox.smbclient_s3 test uses this to test that + # sending messages works, and that the %m sub works. + message command = mv %s $shrdir/message.%m + + # fsrvp server requires registry shares + registry shares = yes + + # Used by RPC SRVSVC tests + add share command = $bindir_abs/smbaddshare + change share command = $bindir_abs/smbchangeshare + delete share command = $bindir_abs/smbdeleteshare + + # fruit:copyfile is a global option + fruit:copyfile = yes + + #this does not mean that we use non-secure test env, + #it just means we ALLOW one to be configured. + allow insecure wide links = yes + + include = $globalinjectconf + + # Begin extra options + $extra_options + # End extra options + + #Include user defined custom parameters if set +"; + + if (defined($ENV{INCLUDE_CUSTOM_CONF})) { + print CONF "\t$ENV{INCLUDE_CUSTOM_CONF}\n"; + } + + print CONF " +[smbcacls_share] + path = $smbcacls_share + comment = smb username is [%U] + msdfs root = yes + +[smbcacls_sharedir_dfs] + path = $smbcacls_sharedir_dfs + comment = smb username is [%U] +[tmp] + path = $shrdir + comment = smb username is [%U] +[tmpsort] + path = $shrdir + comment = Load dirsort module + vfs objects = dirsort acl_xattr fake_acls xattr_tdb streams_depot +[tmpenc] + path = $shrdir + comment = encrypt smb username is [%U] + server smb encrypt = required + vfs objects = dirsort +[tmpguest] + path = $shrdir + guest ok = yes +[guestonly] + path = $shrdir + guest only = yes + guest ok = yes +[forceuser] + path = $shrdir + force user = $unix_name + guest ok = yes +[forceuser_unixonly] + comment = force a user with unix user SID and group SID + path = $shrdir + force user = pdbtest + guest ok = yes +[forceuser_wkngroup] + comment = force a user with well-known group SID + path = $shrdir + force user = pdbtest_wkn + guest ok = yes +[forcegroup] + path = $shrdir + force group = nogroup + guest ok = yes +[ro-tmp] + path = $ro_shrdir + guest ok = yes +[noperm] + path = $noperm_shrdir + wide links = yes + guest ok = yes +[write-list-tmp] + path = $shrdir + read only = yes + write list = $unix_name +[valid-users-tmp] + path = $shrdir + valid users = $unix_name + access based share enum = yes +[msdfs-share] + path = $msdfs_shrdir + msdfs root = yes + msdfs shuffle referrals = yes + guest ok = yes +[msdfs-share-wl] + path = $msdfs_shrdir + msdfs root = yes + wide links = yes + guest ok = yes +[msdfs-share2] + path = $msdfs_shrdir2 + msdfs root = yes + guest ok = yes +[hideunread] + copy = tmp + hide unreadable = yes +[tmpcase] + copy = tmp + case sensitive = yes +[hideunwrite] + copy = tmp + hide unwriteable files = yes +[durable] + copy = tmp + kernel share modes = no + kernel oplocks = no + posix locking = no +[fs_specific] + copy = tmp + $fs_specific_conf +[print1] + copy = tmp + printable = yes + +[print2] + copy = print1 +[print3] + copy = print1 + default devmode = no + +[print_var_exp] + copy = print1 + print command = $self->{srcdir}/source3/script/tests/printing/printing_var_exp_lpr_cmd.sh \"Windows user: %U\" \"UNIX user: %u\" \"Domain: %D\" + +[lp] + copy = print1 + +[nfs4acl_simple_40] + path = $shrdir + comment = smb username is [%U] + nfs4:mode = simple + nfs4acl_xattr:version = 40 + vfs objects = nfs4acl_xattr xattr_tdb + +[nfs4acl_special_40] + path = $shrdir + comment = smb username is [%U] + nfs4:mode = special + nfs4acl_xattr:version = 40 + vfs objects = nfs4acl_xattr xattr_tdb + +[nfs4acl_simple_41] + path = $shrdir + comment = smb username is [%U] + nfs4:mode = simple + vfs objects = nfs4acl_xattr xattr_tdb + +[nfs4acl_xdr_40] + path = $shrdir + comment = smb username is [%U] + vfs objects = nfs4acl_xattr xattr_tdb + nfs4:mode = simple + nfs4acl_xattr:encoding = xdr + nfs4acl_xattr:version = 40 + +[nfs4acl_xdr_41] + path = $shrdir + comment = smb username is [%U] + vfs objects = nfs4acl_xattr xattr_tdb + nfs4:mode = simple + nfs4acl_xattr:encoding = xdr + nfs4acl_xattr:version = 41 + +[nfs4acl_nfs_40] + path = $shrdir + comment = smb username is [%U] + vfs objects = nfs4acl_xattr xattr_tdb + nfs4:mode = simple + nfs4acl_xattr:encoding = nfs + nfs4acl_xattr:version = 40 + nfs4acl_xattr:xattr_name = security.nfs4acl_xdr + +[nfs4acl_nfs_41] + path = $shrdir + comment = smb username is [%U] + vfs objects = nfs4acl_xattr xattr_tdb + nfs4:mode = simple + nfs4acl_xattr:encoding = nfs + nfs4acl_xattr:version = 41 + nfs4acl_xattr:xattr_name = security.nfs4acl_xdr + +[xcopy_share] + path = $shrdir + comment = smb username is [%U] + create mask = 777 + force create mode = 777 +[posix_share] + path = $shrdir + comment = smb username is [%U] + create mask = 0777 + force create mode = 0 + directory mask = 0777 + force directory mode = 0 + vfs objects = xattr_tdb streams_depot +[aio] + copy = durable + aio read size = 1 + aio write size = 1 + +[print\$] + copy = tmp + +[vfs_fruit] + path = $shrdir + vfs objects = catia fruit streams_xattr acl_xattr xattr_tdb + fruit:resource = file + fruit:metadata = netatalk + fruit:locking = netatalk + fruit:encoding = native + fruit:veto_appledouble = no + +[vfs_fruit_xattr] + path = $shrdir + # This is used by vfs.fruit tests that require real fs xattr + vfs objects = catia fruit streams_xattr acl_xattr + fruit:resource = file + fruit:metadata = netatalk + fruit:locking = netatalk + fruit:encoding = native + fruit:veto_appledouble = no + +[vfs_fruit_metadata_stream] + path = $shrdir + vfs objects = fruit streams_xattr acl_xattr xattr_tdb + fruit:resource = file + fruit:metadata = stream + fruit:veto_appledouble = no + +[vfs_fruit_stream_depot] + path = $shrdir + vfs objects = fruit streams_depot acl_xattr xattr_tdb + fruit:resource = stream + fruit:metadata = stream + fruit:veto_appledouble = no + +[vfs_wo_fruit] + path = $shrdir + vfs objects = streams_xattr acl_xattr xattr_tdb + +[vfs_wo_fruit_stream_depot] + path = $shrdir + vfs objects = streams_depot acl_xattr xattr_tdb + +[vfs_fruit_timemachine] + path = $shrdir + vfs objects = fruit streams_xattr acl_xattr xattr_tdb + fruit:resource = file + fruit:metadata = stream + fruit:time machine = yes + fruit:time machine max size = 32K + +[vfs_fruit_wipe_intentionally_left_blank_rfork] + path = $shrdir + vfs objects = fruit streams_xattr acl_xattr xattr_tdb + fruit:resource = file + fruit:metadata = stream + fruit:wipe_intentionally_left_blank_rfork = true + fruit:delete_empty_adfiles = false + fruit:veto_appledouble = no + +[vfs_fruit_delete_empty_adfiles] + path = $shrdir + vfs objects = fruit streams_xattr acl_xattr xattr_tdb + fruit:resource = file + fruit:metadata = stream + fruit:wipe_intentionally_left_blank_rfork = true + fruit:delete_empty_adfiles = true + fruit:veto_appledouble = no + +[vfs_fruit_zero_fileid] + path = $shrdir + vfs objects = fruit streams_xattr acl_xattr xattr_tdb + fruit:resource = file + fruit:metadata = stream + fruit:zero_file_id=yes + +[fruit_resource_stream] + path = $fruit_resource_stream_shrdir + vfs objects = fruit streams_xattr acl_xattr xattr_tdb + fruit:resource = stream + fruit:metadata = stream + +[badname-tmp] + path = $badnames_shrdir + guest ok = yes + +[manglenames_share] + path = $manglenames_shrdir + guest ok = yes + +[dynamic_share] + path = $shrdir/dynamic/%t + guest ok = yes + root preexec = mkdir %P + +[widelinks_share] + path = $widelinks_shrdir + wide links = no + guest ok = yes + +[fsrvp_share] + path = $fsrvp_shrdir + comment = fake shapshots using rsync + vfs objects = shell_snap shadow_copy2 + shell_snap:check path command = $fake_snap_pl --check + shell_snap:create command = $fake_snap_pl --create + shell_snap:delete command = $fake_snap_pl --delete + # a relative path here fails, the snapshot dir is no longer found + shadow:snapdir = $fsrvp_shrdir/.snapshots + +[shadow1] + path = $shadow_shrdir + comment = previous versions snapshots under mount point + vfs objects = shadow_copy2 + shadow:mountpoint = $shadow_mntdir + +[shadow2] + path = $shadow_shrdir + comment = previous versions snapshots outside mount point + vfs objects = shadow_copy2 + shadow:mountpoint = $shadow_mntdir + shadow:snapdir = $shadow_tstdir/.snapshots + +[shadow3] + path = $shadow_shrdir + comment = previous versions with subvolume snapshots, snapshots under base dir + vfs objects = shadow_copy2 + shadow:mountpoint = $shadow_mntdir + shadow:basedir = $shadow_basedir + shadow:snapdir = $shadow_basedir/.snapshots + +[shadow4] + path = $shadow_shrdir + comment = previous versions with subvolume snapshots, snapshots outside mount point + vfs objects = shadow_copy2 + shadow:mountpoint = $shadow_mntdir + shadow:basedir = $shadow_basedir + shadow:snapdir = $shadow_tstdir/.snapshots + +[shadow5] + path = $shadow_shrdir + comment = previous versions at volume root snapshots under mount point + vfs objects = shadow_copy2 + shadow:mountpoint = $shadow_shrdir + +[shadow6] + path = $shadow_shrdir + comment = previous versions at volume root snapshots outside mount point + vfs objects = shadow_copy2 + shadow:mountpoint = $shadow_shrdir + shadow:snapdir = $shadow_tstdir/.snapshots + +[shadow7] + path = $shadow_shrdir + comment = previous versions snapshots everywhere + vfs objects = shadow_copy2 + shadow:mountpoint = $shadow_mntdir + shadow:snapdirseverywhere = yes + +[shadow8] + path = $shadow_shrdir + comment = previous versions using snapsharepath + vfs objects = shadow_copy2 + shadow:mountpoint = $shadow_mntdir + shadow:snapdir = $shadow_tstdir/.snapshots + shadow:snapsharepath = share + +[shadow_fmt0] + comment = Testing shadow:format with default option + vfs object = shadow_copy2 + path = $shadow_shrdir + read only = no + guest ok = yes + shadow:mountpoint = $shadow_mntdir + shadow:basedir = $shadow_basedir + shadow:snapdir = $shadow_basedir/.snapshots + shadow:format = \@GMT-%Y.%m.%d-%H.%M.%S + +[shadow_fmt1] + comment = Testing shadow:format with only date component + vfs object = shadow_copy2 + path = $shadow_shrdir + read only = no + guest ok = yes + shadow:mountpoint = $shadow_mntdir + shadow:basedir = $shadow_basedir + shadow:snapdir = $shadow_basedir/.snapshots + shadow:format = \@GMT-%Y-%m-%d + +[shadow_fmt2] + comment = Testing shadow:format with some hardcoded prefix + vfs object = shadow_copy2 + path = $shadow_shrdir + read only = no + guest ok = yes + shadow:mountpoint = $shadow_mntdir + shadow:basedir = $shadow_basedir + shadow:snapdir = $shadow_basedir/.snapshots + shadow:format = snap\@GMT-%Y.%m.%d-%H.%M.%S + +[shadow_fmt3] + comment = Testing shadow:format with modified format + vfs object = shadow_copy2 + path = $shadow_shrdir + read only = no + guest ok = yes + shadow:mountpoint = $shadow_mntdir + shadow:basedir = $shadow_basedir + shadow:snapdir = $shadow_basedir/.snapshots + shadow:format = \@GMT-%Y.%m.%d-%H_%M_%S-snap + +[shadow_fmt4] + comment = Testing shadow:snapprefix regex + vfs object = shadow_copy2 + path = $shadow_shrdir + read only = no + guest ok = yes + shadow:mountpoint = $shadow_mntdir + shadow:basedir = $shadow_basedir + shadow:snapdir = $shadow_basedir/.snapshots + shadow:snapprefix = \^s[a-z]*p\$ + shadow:format = _GMT-%Y.%m.%d-%H.%M.%S + +[shadow_fmt5] + comment = Testing shadow:snapprefix with delim regex + vfs object = shadow_copy2 + path = $shadow_shrdir + read only = no + guest ok = yes + shadow:mountpoint = $shadow_mntdir + shadow:basedir = $shadow_basedir + shadow:snapdir = $shadow_basedir/.snapshots + shadow:delimiter = \@GMT + shadow:snapprefix = [a-z]* + shadow:format = \@GMT-%Y.%m.%d-%H.%M.%S + +[shadow_wl] + path = $shadow_shrdir + comment = previous versions with wide links allowed + vfs objects = shadow_copy2 + shadow:mountpoint = $shadow_mntdir + wide links = yes + +[shadow_write] + path = $shadow_tstdir + comment = previous versions snapshots under mount point + vfs objects = shadow_copy2 streams_xattr error_inject + aio write size = 0 + error_inject:pwrite = EBADF + shadow:mountpoint = $shadow_tstdir + shadow:fixinodes = yes + smbd async dosmode = yes + +[dfq] + path = $shrdir/dfree + vfs objects = acl_xattr fake_acls xattr_tdb fake_dfq + admin users = $unix_name + include = $dfqconffile +[dfq_cache] + path = $shrdir/dfree + vfs objects = acl_xattr fake_acls xattr_tdb fake_dfq + admin users = $unix_name + include = $dfqconffile + dfree cache time = 60 +[dfq_owner] + path = $shrdir/dfree + vfs objects = acl_xattr fake_acls xattr_tdb fake_dfq + inherit owner = yes + include = $dfqconffile +[quotadir] + path = $shrdir/quota + admin users = $unix_name + +[acl_xattr_ign_sysacl_posix] + copy = tmp + acl_xattr:ignore system acls = yes + acl_xattr:default acl style = posix +[acl_xattr_ign_sysacl_windows] + copy = tmp + acl_xattr:ignore system acls = yes + acl_xattr:default acl style = windows + +[mangle_illegal] + copy = tmp + mangled names = illegal + +[nosymlinks] + copy = tmp + path = $nosymlinks_shrdir + follow symlinks = no + +[local_symlinks] + copy = tmp + path = $local_symlinks_shrdir + follow symlinks = yes + +[kernel_oplocks] + copy = tmp + kernel oplocks = yes + vfs objects = streams_xattr xattr_tdb + +[streams_xattr] + copy = tmp + vfs objects = streams_xattr xattr_tdb + +[streams_xattr_nostrict] + copy = tmp + strict rename = no + vfs objects = streams_xattr xattr_tdb + +[acl_streams_xattr] + copy = tmp + vfs objects = acl_xattr streams_xattr fake_acls xattr_tdb + acl_xattr:ignore system acls = yes + acl_xattr:security_acl_name = user.acl + xattr_tdb:ignore_user_xattr = yes + +[compound_find] + copy = tmp + smbd:find async delay usec = 10000 +[error_inject] + copy = tmp + vfs objects = error_inject + include = $errorinjectconf + +[delay_inject] + copy = tmp + vfs objects = delay_inject + kernel share modes = no + kernel oplocks = no + posix locking = no + include = $delayinjectconf + +[aio_delay_inject] + copy = tmp + vfs objects = delay_inject + delay_inject:pread_send = 2000 + delay_inject:pwrite_send = 2000 + +[brl_delay_inject1] + copy = tmp + vfs objects = delay_inject + delay_inject:brl_lock_windows = 90 + delay_inject:brl_lock_windows_use_timer = yes + +[brl_delay_inject2] + copy = tmp + vfs objects = delay_inject + delay_inject:brl_lock_windows = 90 + delay_inject:brl_lock_windows_use_timer = no + +[delete_readonly] + path = $prefix_abs/share + delete readonly = yes + +[enc_desired] + path = $prefix_abs/share + vfs objects = + server smb encrypt = desired + +[enc_off] + path = $prefix_abs/share + vfs objects = + server smb encrypt = off + +[notify_priv] + copy = tmp + honor change notify privilege = yes + +[acls_non_canonical] + copy = tmp + acl flag inherited canonicalization = no + +[full_audit_success_bad_name] + copy = tmp + full_audit:success = badname + +[full_audit_fail_bad_name] + copy = tmp + full_audit:failure = badname + +include = $aliceconfdir/%U.conf + "; + + close(CONF); + + my $net = Samba::bindir_path($self, "net"); + my $cmd = ""; + $cmd .= "UID_WRAPPER_ROOT=1 "; + $cmd .= "SMB_CONF_PATH=\"$conffile\" "; + $cmd .= "$net setlocalsid $samsid"; + + my $net_ret = system($cmd); + if ($net_ret != 0) { + warn("net setlocalsid failed: $net_ret\n$cmd"); + return undef; + } + + unless (open(ERRORCONF, ">$errorinjectconf")) { + warn("Unable to open $errorinjectconf"); + return undef; + } + close(ERRORCONF); + + unless (open(DELAYCONF, ">$delayinjectconf")) { + warn("Unable to open $delayinjectconf"); + return undef; + } + close(DELAYCONF); + + unless (open(DFQCONF, ">$dfqconffile")) { + warn("Unable to open $dfqconffile"); + return undef; + } + close(DFQCONF); + + unless (open(DELAYCONF, ">$globalinjectconf")) { + warn("Unable to open $globalinjectconf"); + return undef; + } + close(DELAYCONF); + + unless (open(ALICECONF, ">$aliceconffile")) { + warn("Unable to open $aliceconffile"); + return undef; + } + + print ALICECONF " +[alice_share] + path = $shrdir + comment = smb username is [%U] + "; + + close(ALICECONF); + + ## + ## create a test account + ## + + unless (open(PASSWD, ">$nss_wrapper_passwd")) { + warn("Unable to open $nss_wrapper_passwd"); + return undef; + } + print PASSWD "nobody:x:$uid_nobody:$gid_nobody:nobody gecos:$prefix_abs:/bin/false +$unix_name:x:$unix_uid:$unix_gids[0]:$unix_name gecos:$prefix_abs:/bin/false +pdbtest:x:$uid_pdbtest:$gid_nogroup:pdbtest gecos:$prefix_abs:/bin/false +pdbtest2:x:$uid_pdbtest2:$gid_nogroup:pdbtest gecos:$prefix_abs:/bin/false +userdup:x:$uid_userdup:$gid_userdup:userdup gecos:$prefix_abs:/bin/false +pdbtest_wkn:x:$uid_pdbtest_wkn:$gid_everyone:pdbtest_wkn gecos:$prefix_abs:/bin/false +force_user:x:$uid_force_user:$gid_force_user:force user gecos:$prefix_abs:/bin/false +smbget_user:x:$uid_smbget:$gid_domusers:smbget_user gecos:$prefix_abs:/bin/false +user1:x:$uid_user1:$gid_nogroup:user1 gecos:$prefix_abs:/bin/false +user2:x:$uid_user2:$gid_nogroup:user2 gecos:$prefix_abs:/bin/false +gooduser:x:$uid_gooduser:$gid_domusers:gooduser gecos:$prefix_abs:/bin/false +eviluser:x:$uid_eviluser:$gid_domusers:eviluser gecos::/bin/false +slashuser:x:$uid_slashuser:$gid_domusers:slashuser gecos:/:/bin/false +bob:x:$uid_localbob:$gid_domusers:localbob gecos:/:/bin/false +jane:x:$uid_localjane:$gid_domusers:localjane gecos:/:/bin/false +jackthemapper:x:$uid_localjackthemapper:$gid_domusers:localjackthemaper gecos:/:/bin/false +jacknomapper:x:$uid_localjacknomapper:$gid_domusers:localjacknomaper gecos:/:/bin/false +"; + if ($unix_uid != 0) { + print PASSWD "root:x:$uid_root:$gid_root:root gecos:$prefix_abs:/bin/false +"; + } + close(PASSWD); + + unless (open(GROUP, ">$nss_wrapper_group")) { + warn("Unable to open $nss_wrapper_group"); + return undef; + } + print GROUP "nobody:x:$gid_nobody: +nogroup:x:$gid_nogroup:nobody +$unix_name-group:x:$unix_gids[0]: +domusers:X:$gid_domusers: +domadmins:X:$gid_domadmins: +userdup:x:$gid_userdup:$unix_name +everyone:x:$gid_everyone: +force_user:x:$gid_force_user: +jackthemappergroup:x:$gid_jackthemapper:jackthemapper +jacknomappergroup:x:$gid_jacknomapper:jacknomapper +"; + if ($unix_gids[0] != 0) { + print GROUP "root:x:$gid_root: +"; + } + + close(GROUP); + + ## hosts + my $hostname = lc($server); + unless (open(HOSTS, ">>$nss_wrapper_hosts")) { + warn("Unable to open $nss_wrapper_hosts"); + return undef; + } + print HOSTS "${server_ip} ${hostname}.${dns_domain} ${hostname}\n"; + print HOSTS "${server_ipv6} ${hostname}.${dns_domain} ${hostname}\n"; + close(HOSTS); + + $resolv_conf = "$privatedir/no_resolv.conf" unless defined($resolv_conf); + + foreach my $evlog (@eventlog_list) { + my $evlogtdb = "$eventlogdir/$evlog.tdb"; + open(EVENTLOG, ">$evlogtdb") or die("Unable to open $evlogtdb"); + close(EVENTLOG); + } + + $createuser_env{NSS_WRAPPER_PASSWD} = $nss_wrapper_passwd; + $createuser_env{NSS_WRAPPER_GROUP} = $nss_wrapper_group; + $createuser_env{NSS_WRAPPER_HOSTS} = $nss_wrapper_hosts; + $createuser_env{NSS_WRAPPER_HOSTNAME} = "${hostname}.samba.example.com"; + if ($ENV{SAMBA_DNS_FAKING}) { + $createuser_env{RESOLV_WRAPPER_HOSTS} = $dns_host_file; + } else { + $createuser_env{RESOLV_WRAPPER_CONF} = $resolv_conf; + } + $createuser_env{RESOLV_CONF} = $resolv_conf; + + createuser($self, $unix_name, $password, $conffile, \%createuser_env) || die("Unable to create user"); + createuser($self, "force_user", $password, $conffile, \%createuser_env) || die("Unable to create force_user"); + createuser($self, "smbget_user", $password, $conffile, \%createuser_env) || die("Unable to create smbget_user"); + createuser($self, "user1", $password, $conffile, \%createuser_env) || die("Unable to create user1"); + createuser($self, "user2", $password, $conffile, \%createuser_env) || die("Unable to create user2"); + createuser($self, "gooduser", $password, $conffile, \%createuser_env) || die("Unable to create gooduser"); + createuser($self, "eviluser", $password, $conffile, \%createuser_env) || die("Unable to create eviluser"); + createuser($self, "slashuser", $password, $conffile, \%createuser_env) || die("Unable to create slashuser"); + createuser($self, "jackthemapper", "mApsEcrEt", $conffile, \%createuser_env) || die("Unable to create jackthemapper"); + createuser($self, "jacknomapper", "nOmApsEcrEt", $conffile, \%createuser_env) || die("Unable to create jacknomapper"); + + open(DNS_UPDATE_LIST, ">$prefix/dns_update_list") or die("Unable to open $$prefix/dns_update_list"); + print DNS_UPDATE_LIST "A $server. $server_ip\n"; + print DNS_UPDATE_LIST "AAAA $server. $server_ipv6\n"; + close(DNS_UPDATE_LIST); + + print "DONE\n"; + + $ret{SERVER_IP} = $server_ip; + $ret{SERVER_IPV6} = $server_ipv6; + $ret{SAMBA_DCERPCD_TEST_LOG} = "$prefix/samba_dcerpcd_test.log"; + $ret{SAMBA_DCERPCD_LOG_POS} = 0; + $ret{NMBD_TEST_LOG} = "$prefix/nmbd_test.log"; + $ret{NMBD_TEST_LOG_POS} = 0; + $ret{WINBINDD_TEST_LOG} = "$prefix/winbindd_test.log"; + $ret{WINBINDD_TEST_LOG_POS} = 0; + $ret{SMBD_TEST_LOG} = "$prefix/smbd_test.log"; + $ret{SMBD_TEST_LOG_POS} = 0; + $ret{SERVERCONFFILE} = $conffile; + $ret{TESTENV_DIR} = $prefix_abs; + $ret{CONFIGURATION} ="--configfile=$conffile"; + $ret{LOCK_DIR} = $lockdir; + $ret{SERVER} = $server; + $ret{USERNAME} = $unix_name; + $ret{USERID} = $unix_uid; + $ret{DOMAIN} = $domain; + $ret{SAMSID} = $samsid; + $ret{NETBIOSNAME} = $server; + $ret{PASSWORD} = $password; + $ret{PIDDIR} = $piddir; + $ret{SELFTEST_WINBINDD_SOCKET_DIR} = $wbsockdir; + $ret{NMBD_SOCKET_DIR} = $nmbdsockdir; + $ret{SOCKET_WRAPPER_DEFAULT_IFACE} = $swiface; + $ret{NSS_WRAPPER_PASSWD} = $nss_wrapper_passwd; + $ret{NSS_WRAPPER_GROUP} = $nss_wrapper_group; + $ret{NSS_WRAPPER_HOSTS} = $nss_wrapper_hosts; + $ret{NSS_WRAPPER_HOSTNAME} = "${hostname}.samba.example.com"; + $ret{NSS_WRAPPER_MODULE_SO_PATH} = Samba::nss_wrapper_winbind_so_path($self); + $ret{NSS_WRAPPER_MODULE_FN_PREFIX} = "winbind"; + if ($ENV{SAMBA_DNS_FAKING}) { + $ret{RESOLV_WRAPPER_HOSTS} = $dns_host_file; + } else { + $ret{RESOLV_WRAPPER_CONF} = $resolv_conf; + } + $ret{RESOLV_CONF} = $resolv_conf; + $ret{LOCAL_PATH} = "$shrdir"; + $ret{LOGDIR} = $logdir; + + # + # Avoid hitting system krb5.conf - + # An env that needs Kerberos will reset this to the real + # value. + # + $ret{KRB5_CONFIG} = abs_path($prefix) . "/no_krb5.conf"; + + # Define KRB5CCNAME for each environment we set up + $ret{KRB5_CCACHE} = abs_path($prefix) . "/krb5ccache"; + $ENV{KRB5CCNAME} = $ret{KRB5_CCACHE}; + + return \%ret; +} + +sub wait_for_start($$$$$) +{ + my ($self, $envvars, $nmbd, $winbindd, $smbd, $samba_dcerpcd) = @_; + my $cmd; + my $netcmd; + my $ret; + + if ($samba_dcerpcd eq "yes") { + my $count = 0; + my $rpcclient = Samba::bindir_path($self, "rpcclient"); + + print "checking for samba_dcerpcd\n"; + + do { + $ret = system("$rpcclient $envvars->{CONFIGURATION} ncalrpc: -c epmmap"); + + if ($ret != 0) { + sleep(1); + } + $count++ + } while ($ret != 0 && $count < 10); + + if ($count == 10) { + print "samba_dcerpcd not reachable after 10 retries\n"; + teardown_env($self, $envvars); + return 0; + } + } + + if ($nmbd eq "yes") { + my $count = 0; + + # give time for nbt server to register its names + print "checking for nmbd\n"; + + # 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, "nmblookup"); + + do { + $ret = system("$nmblookup $envvars->{CONFIGURATION} $envvars->{SERVER}"); + if ($ret != 0) { + sleep(1); + } else { + system("$nmblookup $envvars->{CONFIGURATION} -U $envvars->{SERVER_IP} __SAMBA__"); + system("$nmblookup $envvars->{CONFIGURATION} __SAMBA__"); + system("$nmblookup $envvars->{CONFIGURATION} -U 10.255.255.255 __SAMBA__"); + system("$nmblookup $envvars->{CONFIGURATION} -U $envvars->{SERVER_IP} $envvars->{SERVER}"); + } + $count++; + } while ($ret != 0 && $count < 10); + if ($count == 10) { + print "NMBD not reachable after 10 retries\n"; + teardown_env($self, $envvars); + return 0; + } + } + + if ($winbindd eq "yes" or $winbindd eq "offline") { + print "checking for winbindd\n"; + my $count = 0; + $cmd = "SELFTEST_WINBINDD_SOCKET_DIR='$envvars->{SELFTEST_WINBINDD_SOCKET_DIR}' "; + $cmd .= "NSS_WRAPPER_PASSWD='$envvars->{NSS_WRAPPER_PASSWD}' "; + $cmd .= "NSS_WRAPPER_GROUP='$envvars->{NSS_WRAPPER_GROUP}' "; + if ($winbindd eq "yes") { + $cmd .= Samba::bindir_path($self, "wbinfo") . " --ping-dc"; + } elsif ($winbindd eq "offline") { + $cmd .= Samba::bindir_path($self, "wbinfo") . " --ping"; + } + + do { + $ret = system($cmd); + if ($ret != 0) { + sleep(1); + } + $count++; + } while ($ret != 0 && $count < 20); + if ($count == 20) { + print "WINBINDD not reachable after 20 seconds\n"; + teardown_env($self, $envvars); + return 0; + } + } + + if ($smbd eq "yes") { + # make sure smbd is also up set + print "wait for smbd\n"; + + my $count = 0; + do { + if (defined($envvars->{GNUTLS_FORCE_FIPS_MODE})) { + # We don't have NTLM in FIPS mode, so lets use + # smbcontrol instead of smbclient. + $cmd = Samba::bindir_path($self, "smbcontrol"); + $cmd .= " $envvars->{CONFIGURATION}"; + $cmd .= " smbd ping"; + } else { + # This uses NTLM which is not available in FIPS + $cmd = Samba::bindir_path($self, "smbclient"); + $cmd .= " $envvars->{CONFIGURATION}"; + $cmd .= " -L $envvars->{SERVER}"; + $cmd .= " -U%"; + $cmd .= " -I $envvars->{SERVER_IP}"; + $cmd .= " -p 139"; + } + + $ret = system($cmd); + if ($ret != 0) { + sleep(1); + } + $count++ + } while ($ret != 0 && $count < 20); + if ($count == 20) { + print "SMBD failed to start up in a reasonable time (20sec)\n"; + teardown_env($self, $envvars); + return 0; + } + } + + # Ensure we have domain users mapped. + $netcmd = "NSS_WRAPPER_PASSWD='$envvars->{NSS_WRAPPER_PASSWD}' "; + $netcmd .= "NSS_WRAPPER_GROUP='$envvars->{NSS_WRAPPER_GROUP}' "; + $netcmd .= "UID_WRAPPER_ROOT='1' "; + $netcmd .= Samba::bindir_path($self, "net") ." $envvars->{CONFIGURATION} "; + + $cmd = $netcmd . "groupmap delete ntgroup=domusers"; + $ret = system($cmd); + + $cmd = $netcmd . "groupmap add rid=513 unixgroup=domusers type=domain"; + $ret = system($cmd); + if ($ret != 0) { + print("\"$cmd\" failed\n"); + return 1; + } + + $cmd = $netcmd . "groupmap delete ntgroup=domadmins"; + $ret = system($cmd); + + $cmd = $netcmd . "groupmap add rid=512 unixgroup=domadmins type=domain"; + $ret = system($cmd); + if ($ret != 0) { + print("\"$cmd\" failed\n"); + return 1; + } + + $cmd = $netcmd . "groupmap delete ntgroup=everyone"; + $ret = system($cmd); + + $cmd = $netcmd . "groupmap add sid=S-1-1-0 unixgroup=everyone type=builtin"; + $ret = system($cmd); + if ($ret != 0) { + print("\"$cmd\" failed\n"); + return 1; + } + + # note: creating builtin groups requires winbindd for the + # unix id allocator + my $create_builtin_users = "no"; + if ($winbindd eq "yes") { + $cmd = "SELFTEST_WINBINDD_SOCKET_DIR='$envvars->{SELFTEST_WINBINDD_SOCKET_DIR}' "; + $cmd .= "NSS_WRAPPER_PASSWD='$envvars->{NSS_WRAPPER_PASSWD}' "; + $cmd .= "NSS_WRAPPER_GROUP='$envvars->{NSS_WRAPPER_GROUP}' "; + $cmd .= Samba::bindir_path($self, "wbinfo") . " --sid-to-gid=S-1-5-32-545"; + my $wbinfo_out = qx($cmd 2>&1); + if ($? != 0) { + # wbinfo doesn't give us a better error code then + # WBC_ERR_DOMAIN_NOT_FOUND, but at least that's + # different then WBC_ERR_WINBIND_NOT_AVAILABLE + if ($wbinfo_out !~ /WBC_ERR_DOMAIN_NOT_FOUND/) { + print("Failed to run \"wbinfo --sid-to-gid=S-1-5-32-545\": $wbinfo_out"); + teardown_env($self, $envvars); + return 0; + } + $create_builtin_users = "yes"; + } + } + if ($create_builtin_users eq "yes") { + $cmd = "SELFTEST_WINBINDD_SOCKET_DIR='$envvars->{SELFTEST_WINBINDD_SOCKET_DIR}' "; + $cmd .= "NSS_WRAPPER_PASSWD='$envvars->{NSS_WRAPPER_PASSWD}' "; + $cmd .= "NSS_WRAPPER_GROUP='$envvars->{NSS_WRAPPER_GROUP}' "; + $cmd .= Samba::bindir_path($self, "net") . " $envvars->{CONFIGURATION} "; + $cmd .= "sam createbuiltingroup Users"; + $ret = system($cmd); + if ($ret != 0) { + print "Failed to create BUILTIN\\Users group\n"; + teardown_env($self, $envvars); + return 0; + } + + $cmd = Samba::bindir_path($self, "net") . " $envvars->{CONFIGURATION} "; + $cmd .= "cache del IDMAP/SID2XID/S-1-5-32-545"; + system($cmd); + + $cmd = "SELFTEST_WINBINDD_SOCKET_DIR='$envvars->{SELFTEST_WINBINDD_SOCKET_DIR}' "; + $cmd .= "NSS_WRAPPER_PASSWD='$envvars->{NSS_WRAPPER_PASSWD}' "; + $cmd .= "NSS_WRAPPER_GROUP='$envvars->{NSS_WRAPPER_GROUP}' "; + $cmd .= Samba::bindir_path($self, "wbinfo") . " --sid-to-gid=S-1-5-32-545"; + $ret = system($cmd); + if ($ret != 0) { + print "Missing \"BUILTIN\\Users\", did net sam createbuiltingroup Users fail?\n"; + teardown_env($self, $envvars); + return 0; + } + } + + print $self->getlog_env($envvars); + + return 1; +} + +## +## provision and start of ctdb +## +sub setup_ctdb($$) +{ + my ($self, $prefix) = @_; + my $num_nodes = 3; + + my $data = $self->provision_ctdb($prefix, $num_nodes); + $data or return undef; + + my $rc = $self->check_or_start_ctdb($data); + if (not $rc) { + print("check_or_start_ctdb() failed\n"); + return undef; + } + + $rc = $self->wait_for_start_ctdb($data); + if (not $rc) { + print "Cluster startup failed\n"; + return undef; + } + + return $data; +} + +sub provision_ctdb($$$$) +{ + my ($self, $prefix, $num_nodes, $no_delete_prefix) = @_; + my $rc; + + print "PROVISIONING CTDB...\n"; + + my $prefix_abs = abs_path($prefix); + + # + # check / create directories: + # + die ("prefix_abs = ''") if $prefix_abs eq ""; + die ("prefix_abs = '/'") if $prefix_abs eq "/"; + + mkdir ($prefix_abs, 0777); + + print "CREATE CTDB TEST ENVIRONMENT in '$prefix_abs'...\n"; + + if (not defined($no_delete_prefix) or not $no_delete_prefix) { + system("rm -rf $prefix_abs/*"); + } + + # + # Per-node data + # + my @nodes = (); + for (my $i = 0; $i < $num_nodes; $i++) { + my %node = (); + my $server_name = "ctdb${i}"; + my $pub_iface = Samba::get_interface($server_name); + my $ip = Samba::get_ipv4_addr($server_name); + + $node{NODE_NUMBER} = "$i"; + $node{SERVER_NAME} = "$server_name"; + $node{SOCKET_WRAPPER_DEFAULT_IFACE} = "$pub_iface"; + $node{IP} = "$ip"; + + push(@nodes, \%node); + } + + # + # nodes + # + my $nodes_file = "$prefix/nodes.in"; + unless (open(NODES, ">$nodes_file")) { + warn("Unable to open nodesfile '$nodes_file'"); + return undef; + } + for (my $i = 0; $i < $num_nodes; $i++) { + my $ip = $nodes[$i]->{IP}; + print NODES "${ip}\n"; + } + close(NODES); + + # + # local_daemons.sh setup + # + # Socket wrapper setup is done by selftest.pl, so don't use + # the CTDB-specific setup + # + my $cmd; + $cmd .= "ctdb/tests/local_daemons.sh " . $prefix_abs . " setup"; + $cmd .= " -n " . $num_nodes; + $cmd .= " -N " . $nodes_file; + # CTDB should not attempt to manage public addresses - + # clients should just connect to CTDB private addresses + $cmd .= " -P " . "/dev/null"; + + my $ret = system($cmd); + if ($ret != 0) { + print("\"$cmd\" failed\n"); + return undef; + } + + # + # Unix domain socket and node directory for each daemon + # + for (my $i = 0; $i < $num_nodes; $i++) { + my ($cmd, $ret, $out); + + my $cmd_prefix = "ctdb/tests/local_daemons.sh ${prefix_abs}"; + + # + # socket + # + + $cmd = "${cmd_prefix} print-socket ${i}"; + + $out = `$cmd`; + $ret = $?; + if ($ret != 0) { + print("\"$cmd\" failed\n"); + return undef; + } + chomp $out; + $nodes[$i]->{SOCKET_FILE} = "$out"; + + # + # node directory + # + + $cmd = "${cmd_prefix} onnode ${i} 'echo \$CTDB_BASE'"; + + $out = `$cmd`; + $ret = $?; + if ($ret != 0) { + print("\"$cmd\" failed\n"); + return undef; + } + chomp $out; + $nodes[$i]->{NODE_PREFIX} = "$out"; + } + + my %ret = (); + + $ret{CTDB_PREFIX} = "$prefix"; + $ret{NUM_NODES} = $num_nodes; + $ret{CTDB_NODES} = \@nodes; + $ret{CTDB_NODES_FILE} = $nodes_file; + + return \%ret; +} + +sub check_or_start_ctdb($$) { + my ($self, $data) = @_; + + my $prefix = $data->{CTDB_PREFIX}; + my $num_nodes = $data->{NUM_NODES}; + my $nodes = $data->{CTDB_NODES}; + my $STDIN_READER; + + # Share a single stdin pipe for all nodes + pipe($STDIN_READER, $data->{CTDB_STDIN_PIPE}); + + for (my $i = 0; $i < $num_nodes; $i++) { + my $node = $nodes->[$i]; + + $node->{STDIN_PIPE} = $data->{CTDB_STDIN_PIPE}; + + my $cmd = "ctdb/tests/local_daemons.sh"; + my @full_cmd = ("$cmd", "$prefix", "start", "$i"); + my $daemon_ctx = { + NAME => "ctdbd", + BINARY_PATH => $cmd, + FULL_CMD => [ @full_cmd ], + TEE_STDOUT => 1, + LOG_FILE => "/dev/null", + ENV_VARS => {}, + }; + + print "STARTING CTDBD (node ${i})\n"; + + # This does magic with $STDIN_READER, so use it + my $ret = Samba::fork_and_exec($self, + $node, + $daemon_ctx, + $STDIN_READER); + + if ($ret == 0) { + print("\"$cmd\" failed\n"); + teardown_env_ctdb($self, $data); + return 0; + } + } + + close($STDIN_READER); + + return 1; +} + +sub wait_for_start_ctdb($$) +{ + my ($self, $data) = @_; + + my $prefix = $data->{CTDB_PREFIX}; + + print "Wait for ctdbd...\n"; + + my $ctdb = Samba::bindir_path($self, "ctdb"); + my $cmd; + $cmd .= "ctdb/tests/local_daemons.sh ${prefix} onnode all"; + $cmd .= " ${ctdb} nodestatus all 2>&1"; + + my $count = 0; + my $wait_seconds = 60; + my $out; + + until ($count > $wait_seconds) { + $out = `$cmd`; + my $ret = $?; + if ($ret == 0) { + print "\ncluster became healthy\n"; + last; + } + print "Waiting for CTDB...\n"; + sleep(1); + $count++; + } + + if ($count > $wait_seconds) { + print "\nGiving up to wait for CTDB...\n"; + print "${out}\n\n"; + print "CTDB log:\n"; + $cmd = "ctdb/tests/local_daemons.sh ${prefix} print-log all >&2"; + system($cmd); + teardown_env_ctdb($self, $data); + return 0; + } + + print "\nCTDB initialized\n"; + + return 1; +} + +1; diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm new file mode 100755 index 0000000..7033146 --- /dev/null +++ b/selftest/target/Samba4.pm @@ -0,0 +1,3662 @@ +#!/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) = @_; + + my $self = { + vars => {}, + SambaCtx => $SambaCtx, + bindir => $bindir, + srcdir => $srcdir, + server_maxtime => $server_maxtime, + target3 => new Samba3($SambaCtx, $bindir, $srcdir, $server_maxtime) + }; + 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->{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 + +"; + + 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}, + 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}, + }; + + 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 to 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=mdb"; + + 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}, "--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) = @_; + + 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($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=mdb"); + print "PROVISIONING AD DC...\n"; + my $ret = $self->provision($prefix, + "domain controller", + $hostname, + $domain, + $realm, + "2008", + "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"], + 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 autbuild tasks + fl2008dc => ["ad_dc"], + 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) = @_; + + my $extra_args = ["--base-schema=2008_R2"]; + my $env = $self->provision_ad_dc_ntvfs($path, $extra_args); + if (defined $env) { + if (not defined($self->check_or_start($env, "standard"))) { + warn("Failed to start fl2008dc"); + return undef; + } + } + return $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, $dc_vars) = @_; + + my $env = $self->provision_fl2008r2dc($path); + + if (defined $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, $dc_vars, "forest", ""); + } + + 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) = @_; + + # 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); + 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=mdb"]; + + my $env = $self->provision_ad_dc($path, + "liveupgrade1dc", + "SCHEMADOMAIN", + "schema.samba.example.com", + undef, + "drs: max link sync = 2", + $provision_args); + unless ($env) { + return undef; + } + + if (not defined($self->check_or_start($env, "prefork"))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + 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=mdb"; + + my $upgrade_cmd = $cmd_vars; + $upgrade_cmd .= "$samba_tool domain schemaupgrade $dcvars->{CONFIGURATION}"; + $upgrade_cmd .= " -U$dcvars->{USERNAME}\%$dcvars->{PASSWORD}"; + + my $repl_cmd = $cmd_vars; + $repl_cmd .= "$samba_tool drs replicate $env->{SERVER} $dcvars->{SERVER}"; + $repl_cmd .= " CN=Schema,CN=Configuration,DC=schema,DC=samba,DC=example,DC=com"; + $repl_cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}"; + + unless (system($join_cmd) == 0) { + warn("Join failed\n$join_cmd"); + return undef; + } + + $env->{DC_SERVER} = $dcvars->{SERVER}; + $env->{DC_SERVER_IP} = $dcvars->{SERVER_IP}; + $env->{DC_SERVER_IPV6} = $dcvars->{SERVER_IPV6}; + $env->{DC_NETBIOSNAME} = $dcvars->{NETBIOSNAME}; + + # start samba for the new DC + if (not defined($self->check_or_start($env, "standard"))) { + return undef; + } + + unless (system($upgrade_cmd) == 0) { + warn("Schema upgrade failed\n$upgrade_cmd"); + return undef; + } + + unless (system($repl_cmd) == 0) { + warn("Post-update schema replication failed\n$repl_cmd"); + return undef; + } + + return $env; +} + +# Sets up a DC that's solely used to do a domain backup from. We then use the +# backupfrom-DC to create the restore-DC - this proves that the backup/restore +# process will create a Samba DC that will actually start up. +# We don't use the backup-DC for anything else because its domain will conflict +# with the restore DC. +sub setup_backupfromdc +{ + my ($self, $path) = @_; + + # If we didn't build with ADS, pretend this env was never available + if (not $self->{target3}->have_ads()) { + return "UNKNOWN"; + } + + my $provision_args = ["--site=Backup-Site"]; + + my $env = $self->provision_ad_dc($path, + "backupfromdc", + "BACKUPDOMAIN", + "backupdom.samba.example.com", + 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 + 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 a 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 a 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=mdb"; + my $backup_file = $self->create_backup($env, $dcvars, $backupdir, + $backup_args); + unless($backup_file) { + return undef; + } + + # restore the backup file to populate the lab-DC testenv + my $restore_dir = abs_path($prefix); + my $restore_opts = "--newservername=$env->{SERVER} --host-ip=$env->{SERVER_IP}"; + my $ret = $self->restore_backup_file($backup_file, $restore_opts, + $restore_dir, $env->{SERVERCONFFILE}); + unless ($ret == 0) { + return undef; + } + + # because we don't include any secrets in the backup, we need to reset the + # admin user's password back to what the testenv expects + my $samba_tool = Samba::bindir_path($self, "samba-tool"); + my $cmd = "$samba_tool user setpassword $env->{USERNAME} "; + $cmd .= "--newpassword=$env->{PASSWORD} -H $restore_dir/private/sam.ldb"; + $cmd .= " $env->{CONFIGURATION}"; + + unless(system($cmd) == 0) { + warn("Failed to reset admin's password: \n$cmd"); + return undef; + } + + # start samba for the restored DC + if (not defined($self->check_or_start($env))) { + return undef; + } + + my $upn_array = ["$env->{REALM}.upn"]; + my $spn_array = ["$env->{REALM}.spn"]; + + 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 a 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; diff --git a/selftest/target/dns_hub.py b/selftest/target/dns_hub.py new file mode 100755 index 0000000..993c56e --- /dev/null +++ b/selftest/target/dns_hub.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +# +# Unix SMB/CIFS implementation. +# Copyright (C) Volker Lendecke 2017 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Used by selftest to proxy DNS queries to the correct testenv DC. +# See selftest/target/README for more details. +# Based on the EchoServer example from python docs + +import threading +import sys +import select +import socket +import collections +import time +from samba.dcerpc import dns +import samba.ndr as ndr + +if sys.version_info[0] < 3: + import SocketServer + sserver = SocketServer +else: + import socketserver + sserver = socketserver + +DNS_REQUEST_TIMEOUT = 10 + +# make sure the script dies immediately when hitting control-C, +# rather than raising KeyboardInterrupt. As we do all database +# operations using transactions, this is safe. +import signal +signal.signal(signal.SIGINT, signal.SIG_DFL) + +class DnsHandler(sserver.BaseRequestHandler): + dns_qtype_strings = dict((v, k) for k, v in vars(dns).items() if k.startswith('DNS_QTYPE_')) + def dns_qtype_string(self, qtype): + "Return a readable qtype code" + return self.dns_qtype_strings[qtype] + + dns_rcode_strings = dict((v, k) for k, v in vars(dns).items() if k.startswith('DNS_RCODE_')) + def dns_rcode_string(self, rcode): + "Return a readable error code" + return self.dns_rcode_strings[rcode] + + def dns_transaction_udp(self, packet, host): + "send a DNS query and read the reply" + s = None + flags = socket.AddressInfo.AI_NUMERICHOST + flags |= socket.AddressInfo.AI_NUMERICSERV + flags |= socket.AddressInfo.AI_PASSIVE + addr_info = socket.getaddrinfo(host, int(53), + type=socket.SocketKind.SOCK_DGRAM, + flags=flags) + assert len(addr_info) == 1 + try: + send_packet = ndr.ndr_pack(packet) + s = socket.socket(addr_info[0][0], addr_info[0][1], 0) + s.settimeout(DNS_REQUEST_TIMEOUT) + s.connect(addr_info[0][4]) + s.sendall(send_packet, 0) + recv_packet = s.recv(2048, 0) + return ndr.ndr_unpack(dns.name_packet, recv_packet) + except socket.error as err: + print("Error sending to host %s for name %s: %s\n" % + (host, packet.questions[0].name, err.errno)) + raise + finally: + if s is not None: + s.close() + + def get_pdc_ipv4_addr(self, lookup_name): + """Maps a DNS realm to the IPv4 address of the PDC for that testenv""" + + realm_to_ip_mappings = self.server.realm_to_ip_mappings + + # sort the realms so we find the longest-match first + testenv_realms = sorted(realm_to_ip_mappings.keys(), key=len) + testenv_realms.reverse() + + for realm in testenv_realms: + if lookup_name.endswith(realm): + # return the corresponding IP address for this realm's PDC + return realm_to_ip_mappings[realm] + + return None + + def forwarder(self, name): + lname = name.lower() + + # check for special cases used by tests (e.g. dns_forwarder.py) + if lname.endswith('an-address-that-will-not-resolve'): + return 'ignore' + if lname.endswith('dsfsdfs'): + return 'fail' + if lname.endswith("torture1", 0, len(lname)-2): + # CATCH TORTURE100, TORTURE101, ... + return 'torture' + if lname.endswith('_none_.example.com'): + return 'torture' + if lname.endswith('torturedom.samba.example.com'): + return 'torture' + + # return the testenv PDC matching the realm being requested + return self.get_pdc_ipv4_addr(lname) + + def handle(self): + start = time.monotonic() + data, sock = self.request + query = ndr.ndr_unpack(dns.name_packet, data) + name = query.questions[0].name + forwarder = self.forwarder(name) + response = None + + if forwarder == 'ignore': + return + elif forwarder == 'fail': + pass + elif forwarder in ['torture', None]: + response = query + response.operation |= dns.DNS_FLAG_REPLY + response.operation |= dns.DNS_FLAG_RECURSION_AVAIL + response.operation |= dns.DNS_RCODE_NXDOMAIN + else: + try: + response = self.dns_transaction_udp(query, forwarder) + except OSError as err: + print("dns_hub: Error sending dns query to forwarder[%s] for name[%s]: %s" % + (forwarder, name, err)) + + if response is None: + response = query + response.operation |= dns.DNS_FLAG_REPLY + response.operation |= dns.DNS_FLAG_RECURSION_AVAIL + response.operation |= dns.DNS_RCODE_SERVFAIL + + send_packet = ndr.ndr_pack(response) + + end = time.monotonic() + tdiff = end - start + errcode = response.operation & dns.DNS_RCODE + if tdiff > (DNS_REQUEST_TIMEOUT/5): + debug = True + else: + debug = False + if debug: + print("dns_hub: forwarder[%s] client[%s] name[%s][%s] %s response.operation[0x%x] tdiff[%s]\n" % + (forwarder, self.client_address, name, + self.dns_qtype_string(query.questions[0].question_type), + self.dns_rcode_string(errcode), response.operation, tdiff)) + + try: + sock.sendto(send_packet, self.client_address) + except socket.error as err: + print("dns_hub: Error sending response to client[%s] for name[%s] tdiff[%s]: %s\n" % + (self.client_address, name, tdiff, err)) + + +class server_thread(threading.Thread): + def __init__(self, server, name): + threading.Thread.__init__(self, name=name) + self.server = server + + def run(self): + print("dns_hub[%s]: before serve_forever()" % self.name) + self.server.serve_forever() + print("dns_hub[%s]: after serve_forever()" % self.name) + + def stop(self): + print("dns_hub[%s]: before shutdown()" % self.name) + self.server.shutdown() + print("dns_hub[%s]: after shutdown()" % self.name) + +class UDPV4Server(sserver.UDPServer): + address_family = socket.AF_INET + +class UDPV6Server(sserver.UDPServer): + address_family = socket.AF_INET6 + +def main(): + if len(sys.argv) < 4: + print("Usage: dns_hub.py TIMEOUT LISTENADDRESS[,LISTENADDRESS,...] MAPPING[,MAPPING,...]") + sys.exit(1) + + timeout = int(sys.argv[1]) * 1000 + timeout = min(timeout, 2**31 - 1) # poll with 32-bit int can't take more + # we pass in the listen addresses as a comma-separated string. + listenaddresses = sys.argv[2].split(',') + # we pass in the realm-to-IP mappings as a comma-separated key=value + # string. Convert this back into a dictionary that the DnsHandler can use + realm_mappings = collections.OrderedDict(kv.split('=') for kv in sys.argv[3].split(',')) + + def prepare_server_thread(listenaddress, realm_mappings): + + flags = socket.AddressInfo.AI_NUMERICHOST + flags |= socket.AddressInfo.AI_NUMERICSERV + flags |= socket.AddressInfo.AI_PASSIVE + addr_info = socket.getaddrinfo(listenaddress, int(53), + type=socket.SocketKind.SOCK_DGRAM, + flags=flags) + assert len(addr_info) == 1 + if addr_info[0][0] == socket.AddressFamily.AF_INET6: + server = UDPV6Server(addr_info[0][4], DnsHandler) + else: + server = UDPV4Server(addr_info[0][4], DnsHandler) + + # we pass in the realm-to-IP mappings as a comma-separated key=value + # string. Convert this back into a dictionary that the DnsHandler can use + server.realm_to_ip_mappings = realm_mappings + t = server_thread(server, name="UDP[%s]" % listenaddress) + return t + + print("dns_hub will proxy DNS requests for the following realms:") + for realm, ip in realm_mappings.items(): + print(" {0} ==> {1}".format(realm, ip)) + + print("dns_hub will listen on the following UDP addresses:") + threads = [] + for listenaddress in listenaddresses: + print(" %s" % listenaddress) + t = prepare_server_thread(listenaddress, realm_mappings) + threads.append(t) + + for t in threads: + t.start() + p = select.poll() + stdin = sys.stdin.fileno() + p.register(stdin, select.POLLIN) + p.poll(timeout) + print("dns_hub: after poll()") + for t in threads: + t.stop() + for t in threads: + t.join() + print("dns_hub: before exit()") + sys.exit(0) + +main() |