wintest: use --add-ref for RODC replication
[nivanova/samba-autobuild/.git] / wintest / test-s4-howto.py
1 #!/usr/bin/env python
2
3 '''automated testing of the steps of the Samba4 HOWTO'''
4
5 import sys, os
6 import wintest, pexpect, time, subprocess
7
8 def check_prerequesites(t):
9     t.info("Checking prerequesites")
10     t.setvar('HOSTNAME', t.cmd_output("hostname -s").strip())
11     if os.getuid() != 0:
12         raise Exception("You must run this script as root")
13     t.run_cmd('ifconfig ${INTERFACE} ${INTERFACE_NET} up')
14     if t.getvar('INTERFACE_IPV6'):
15         t.run_cmd('ifconfig ${INTERFACE} inet6 del ${INTERFACE_IPV6}/64', checkfail=False)
16         t.run_cmd('ifconfig ${INTERFACE} inet6 add ${INTERFACE_IPV6}/64 up')
17
18 def build_s4(t):
19     '''build samba4'''
20     t.info('Building s4')
21     t.chdir('${SOURCETREE}/source4')
22     t.putenv('CC', 'ccache gcc')
23     t.run_cmd('make reconfigure || ./configure --enable-auto-reconfigure --enable-developer --prefix=${PREFIX} -C')
24     t.run_cmd('make -j')
25     t.run_cmd('rm -rf ${PREFIX}')
26     t.run_cmd('make -j install')
27
28
29 def provision_s4(t, func_level="2008"):
30     '''provision s4 as a DC'''
31     t.info('Provisioning s4')
32     t.chdir('${PREFIX}')
33     t.del_files(["var", "private"])
34     t.run_cmd("rm -f etc/smb.conf")
35     provision=['sbin/provision',
36                '--realm=${LCREALM}',
37                '--domain=${DOMAIN}',
38                '--adminpass=${PASSWORD1}',
39                '--server-role=domain controller',
40                '--function-level=%s' % func_level,
41                '-d${DEBUGLEVEL}',
42                '--option=interfaces=${INTERFACE}',
43                '--host-ip=${INTERFACE_IP}',
44                '--option=bind interfaces only=yes',
45                '--option=rndc command=${RNDC} -c${PREFIX}/etc/rndc.conf']
46     if t.getvar('INTERFACE_IPV6'):
47         provision.append('--host-ip6=${INTERFACE_IPV6}')
48     t.run_cmd(provision)
49     t.run_cmd('bin/samba-tool newuser testallowed ${PASSWORD1}')
50     t.run_cmd('bin/samba-tool newuser testdenied ${PASSWORD1}')
51     t.run_cmd('bin/samba-tool group addmembers "Allowed RODC Password Replication Group" testallowed')
52
53
54 def start_s4(t):
55     '''startup samba4'''
56     t.info('Starting Samba4')
57     t.chdir("${PREFIX}")
58     t.run_cmd('killall -9 -q samba smbd nmbd winbindd', checkfail=False)
59     t.run_cmd(['sbin/samba',
60              '--option', 'panic action=gnome-terminal -e "gdb --pid %PID%"'])
61     t.port_wait("${INTERFACE_IP}", 139)
62
63 def stop_vms(t):
64     '''Shut down any existing alive VMs, so they do not collide with what we are doing'''
65     t.info('Shutting down any of our VMs already running')
66     vms = t.get_vms()
67     for v in vms:
68         t.vm_poweroff(v, checkfail=False)
69
70 def test_smbclient(t):
71     '''test smbclient'''
72     t.info('Testing smbclient')
73     t.chdir('${PREFIX}')
74     t.cmd_contains("bin/smbclient --version", ["Version 4.0"])
75     t.retry_cmd('bin/smbclient -L ${INTERFACE_IP} -U%', ["netlogon", "sysvol", "IPC Service"])
76     child = t.pexpect_spawn('bin/smbclient //${INTERFACE_IP}/netlogon -Uadministrator%${PASSWORD1}')
77     child.expect("smb:")
78     child.sendline("dir")
79     child.expect("blocks available")
80     child.sendline("mkdir testdir")
81     child.expect("smb:")
82     child.sendline("cd testdir")
83     child.expect('testdir')
84     child.sendline("cd ..")
85     child.sendline("rmdir testdir")
86
87
88 def create_shares(t):
89     '''create some test shares'''
90     t.info("Adding test shares")
91     t.chdir('${PREFIX}')
92     t.write_file("etc/smb.conf", '''
93 [test]
94        path = ${PREFIX}/test
95        read only = no
96 [profiles]
97        path = ${PREFIX}/var/profiles
98        read only = no
99     ''',
100                  mode='a')
101     t.run_cmd("mkdir -p test")
102     t.run_cmd("mkdir -p var/profiles")
103
104
105 def set_nameserver(t, nameserver):
106     '''set the nameserver in resolv.conf'''
107     t.write_file("/etc/resolv.conf.wintest", '''
108 # Generated by wintest, the Samba v Windows automated testing system
109 nameserver %s
110
111 # your original resolv.conf appears below:
112 ''' % t.substitute(nameserver))
113     child = t.pexpect_spawn("cat /etc/resolv.conf", crlf=False)
114     i = child.expect(['your original resolv.conf appears below:', pexpect.EOF])
115     if i == 0:
116         child.expect(pexpect.EOF)
117     contents = child.before.lstrip().replace('\r', '')
118     t.write_file('/etc/resolv.conf.wintest', contents, mode='a')
119     t.write_file('/etc/resolv.conf.wintest-bak', contents)
120     t.run_cmd("mv -f /etc/resolv.conf.wintest /etc/resolv.conf")
121     t.resolv_conf_backup = '/etc/resolv.conf.wintest-bak';
122
123
124 def restore_resolv_conf(t):
125     '''restore the /etc/resolv.conf after testing is complete'''
126     if getattr(t, 'resolv_conf_backup', False):
127         t.info("restoring /etc/resolv.conf")
128         t.run_cmd("mv -f %s /etc/resolv.conf" % t.resolv_conf_backup)
129
130
131 def rndc_cmd(t, cmd, checkfail=True):
132     '''run a rndc command'''
133     t.run_cmd("${RNDC} -c ${PREFIX}/etc/rndc.conf %s" % cmd, checkfail=checkfail)
134
135 def named_supports_gssapi_keytab(t):
136     '''see if named supports tkey-gssapi-keytab'''
137     t.write_file("${PREFIX}/named.conf.test",
138                  'options { tkey-gssapi-keytab "test"; };')
139     try:
140         t.run_cmd("${NAMED_CHECKCONF} ${PREFIX}/named.conf.test")
141     except subprocess.CalledProcessError:
142         return False
143     return True
144
145
146 def configure_bind(t):
147     t.chdir('${PREFIX}')
148
149     nameserver = t.get_nameserver()
150     if nameserver == t.getvar('INTERFACE_IP'):
151         raise RuntimeError("old /etc/resolv.conf must not contain %s as a nameserver, this will create loops with the generated dns configuration" % nameserver)
152     t.setvar('DNSSERVER', nameserver)
153
154     if t.getvar('INTERFACE_IPV6'):
155         ipv6_listen = 'listen-on-v6 port 53 { ${INTERFACE_IPV6}; };'
156     else:
157         ipv6_listen = ''
158     t.setvar('BIND_LISTEN_IPV6', ipv6_listen)
159
160     if named_supports_gssapi_keytab(t):
161         t.setvar("NAMED_TKEY_OPTION",
162                  'tkey-gssapi-keytab "${PREFIX}/private/dns.keytab";')
163     else:
164         t.info("LCREALM=${LCREALM}")
165         t.setvar("NAMED_TKEY_OPTION",
166                  '''tkey-gssapi-credential "DNS/${LCREALM}";
167                  tkey-domain "${LCREALM}";
168                  ''')
169         t.putenv("KRB5_CONFIG", '${PREFIX}/private/krb5.conf')
170         t.putenv('KEYTAB_FILE', '${PREFIX}/private/dns.keytab')
171         t.putenv('KRB5_KTNAME', '${PREFIX}/private/dns.keytab')
172
173     t.write_file("etc/named.conf", '''
174 options {
175         listen-on port 53 { ${INTERFACE_IP};  };
176         ${BIND_LISTEN_IPV6}
177         directory       "${PREFIX}/var/named";
178         dump-file       "${PREFIX}/var/named/data/cache_dump.db";
179         pid-file        "${PREFIX}/var/named/named.pid";
180         statistics-file "${PREFIX}/var/named/data/named_stats.txt";
181         memstatistics-file "${PREFIX}/var/named/data/named_mem_stats.txt";
182         allow-query     { any; };
183         recursion yes;
184         ${NAMED_TKEY_OPTION}
185         max-cache-ttl 10;
186         max-ncache-ttl 10;
187
188         forward only;
189         forwarders {
190                   ${DNSSERVER};
191         };
192
193 };
194
195 key "rndc-key" {
196         algorithm hmac-md5;
197         secret "lA/cTrno03mt5Ju17ybEYw==";
198 };
199  
200 controls {
201         inet ${INTERFACE_IP} port 953
202         allow { any; } keys { "rndc-key"; };
203 };
204
205 include "${PREFIX}/private/named.conf";
206 ''')
207
208     # add forwarding for the windows domains
209     domains = t.get_domains()
210     for d in domains:
211         t.write_file('etc/named.conf',
212                      '''
213 zone "%s" IN {
214       type forward;
215       forward only;
216       forwarders {
217          %s;
218       };
219 };
220 ''' % (d, domains[d]),
221                      mode='a')
222
223
224     t.write_file("etc/rndc.conf", '''
225 # Start of rndc.conf
226 key "rndc-key" {
227         algorithm hmac-md5;
228         secret "lA/cTrno03mt5Ju17ybEYw==";
229 };
230
231 options {
232         default-key "rndc-key";
233         default-server  ${INTERFACE_IP};
234         default-port 953;
235 };
236 ''')
237
238     set_nameserver(t, t.getvar('INTERFACE_IP'))
239
240
241 def stop_bind(t):
242     '''Stop our private BIND from listening and operating'''
243     rndc_cmd(t, "stop", checkfail=False)
244     t.port_wait("${INTERFACE_IP}", 53, wait_for_fail=True)
245
246     t.run_cmd("rm -rf var/named")
247
248
249 def start_bind(t):
250     '''restart the test environment version of bind'''
251     t.info("Restarting bind9")
252     t.chdir('${PREFIX}')
253
254     set_nameserver(t, t.getvar('INTERFACE_IP'))
255
256     t.run_cmd("mkdir -p var/named/data")
257     t.run_cmd("chown -R ${BIND_USER} var/named")
258
259     t.bind_child = t.run_child("${BIND9} -u ${BIND_USER} -n 1 -c ${PREFIX}/etc/named.conf -g")
260
261     t.port_wait("${INTERFACE_IP}", 53)
262     rndc_cmd(t, "flush")
263
264 def restart_bind(t):
265     configure_bind(t)
266     stop_bind(t)
267     start_bind(t)
268
269 def test_dns(t):
270     '''test that DNS is OK'''
271     t.info("Testing DNS")
272     t.cmd_contains("host -t SRV _ldap._tcp.${LCREALM}.",
273                  ['_ldap._tcp.${LCREALM} has SRV record 0 100 389 ${HOSTNAME}.${LCREALM}'])
274     t.cmd_contains("host -t SRV  _kerberos._udp.${LCREALM}.",
275                  ['_kerberos._udp.${LCREALM} has SRV record 0 100 88 ${HOSTNAME}.${LCREALM}'])
276     t.cmd_contains("host -t A ${HOSTNAME}.${LCREALM}",
277                  ['${HOSTNAME}.${LCREALM} has address'])
278
279 def test_kerberos(t):
280     '''test that kerberos is OK'''
281     t.info("Testing kerberos")
282     t.run_cmd("kdestroy")
283     t.kinit("administrator@${REALM}", "${PASSWORD1}")
284     # this copes with the differences between MIT and Heimdal klist
285     t.cmd_contains("klist", ["rincipal", "administrator@${REALM}"])
286
287
288 def test_dyndns(t):
289     '''test that dynamic DNS is working'''
290     t.chdir('${PREFIX}')
291     t.run_cmd("sbin/samba_dnsupdate --fail-immediately")
292     rndc_cmd(t, "flush")
293
294
295 def run_winjoin(t, vm):
296     '''join a windows box to our domain'''
297     t.setwinvars(vm)
298
299     t.info("Joining a windows box to the domain")
300     t.vm_poweroff("${WIN_VM}", checkfail=False)
301     t.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
302     child = t.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True)
303     child.sendline("netdom join ${WIN_HOSTNAME} /Domain:${LCREALM} /PasswordD:${PASSWORD1} /UserD:administrator")
304     child.expect("The command completed successfully")
305     child.expect("C:")
306     child.sendline("shutdown /r -t 0")
307     t.wait_reboot()
308     child = t.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True)
309     child.sendline("ipconfig /registerdns")
310     child.expect("Registration of the DNS resource records for all adapters of this computer has been initiated. Any errors will be reported in the Event Viewer")
311     child.expect("C:")
312
313 def test_winjoin(t, vm):
314     t.info("Checking the windows join is OK")
315     t.chdir('${PREFIX}')
316     t.port_wait("${WIN_IP}", 139)
317     t.retry_cmd('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Uadministrator@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"], retries=100)
318     t.cmd_contains("host -t A ${WIN_HOSTNAME}.${LCREALM}.", ['has address'])
319     t.cmd_contains('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utestallowed@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
320     t.cmd_contains('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -k no -Utestallowed@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
321     t.cmd_contains('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -k yes -Utestallowed@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
322     child = t.open_telnet("${WIN_HOSTNAME}", "${DOMAIN}\\administrator", "${PASSWORD1}")
323     child.sendline("net use t: \\\\${HOSTNAME}.${LCREALM}\\test")
324     child.expect("The command completed successfully")
325     t.vm_poweroff("${WIN_VM}")
326
327
328 def run_dcpromo(t, vm):
329     '''run a dcpromo on windows'''
330     t.setwinvars(vm)
331
332     t.info("Joining a windows VM ${WIN_VM} to the domain as a DC using dcpromo")
333     t.vm_poweroff("${WIN_VM}", checkfail=False)
334     t.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
335     child = t.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_ip=True)
336     child.sendline("copy /Y con answers.txt")
337     child.sendline('''
338 [DCINSTALL]
339 RebootOnSuccess=Yes
340 RebootOnCompletion=Yes
341 ReplicaOrNewDomain=Replica
342 ReplicaDomainDNSName=${LCREALM}
343 SiteName=Default-First-Site-Name
344 InstallDNS=No
345 ConfirmGc=Yes
346 CreateDNSDelegation=No
347 UserDomain=${LCREALM}
348 UserName=${LCREALM}\\administrator
349 Password=${PASSWORD1}
350 DatabasePath="C:\Windows\NTDS"
351 LogPath="C:\Windows\NTDS"
352 SYSVOLPath="C:\Windows\SYSVOL"
353 SafeModeAdminPassword=${PASSWORD1}
354 \1a
355 ''')
356     child.expect("copied.")
357     child.expect("C:")
358     child.expect("C:")
359     child.sendline("dcpromo /answer:answers.txt")
360     i = child.expect(["You must restart this computer", "failed", "Active Directory Domain Services was not installed", "C:"], timeout=120)
361     if i == 1 or i == 2:
362         raise Exception("dcpromo failed")
363     t.wait_reboot()
364
365
366 def test_dcpromo(t, vm):
367     '''test that dcpromo worked'''
368     t.info("Checking the dcpromo join is OK")
369     t.chdir('${PREFIX}')
370     t.port_wait("${WIN_IP}", 139)
371     t.retry_cmd("host -t A ${WIN_HOSTNAME}.${LCREALM}. ${INTERFACE_IP}",
372                 ['${WIN_HOSTNAME}.${LCREALM} has address'],
373                 retries=30, delay=10, casefold=True)
374     t.retry_cmd('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Uadministrator@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
375     t.cmd_contains("host -t A ${WIN_HOSTNAME}.${LCREALM}.", ['has address'])
376     t.cmd_contains('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utestallowed@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
377
378     t.cmd_contains("bin/samba-tool drs kcc ${HOSTNAME}.${LCREALM} -Uadministrator@${LCREALM}%${PASSWORD1}", ['Consistency check', 'successful'])
379     t.retry_cmd("bin/samba-tool drs kcc ${WIN_HOSTNAME}.${LCREALM} -Uadministrator@${LCREALM}%${PASSWORD1}", ['Consistency check', 'successful'])
380
381     t.kinit("administrator@${REALM}", "${PASSWORD1}")
382
383     # the first replication will transfer the dnsHostname attribute
384     t.cmd_contains("bin/samba-tool drs replicate ${HOSTNAME}.${LCREALM} ${WIN_HOSTNAME} CN=Configuration,${BASEDN} -k yes", ["was successful"])
385
386     for nc in [ '${BASEDN}', 'CN=Configuration,${BASEDN}', 'CN=Schema,CN=Configuration,${BASEDN}' ]:
387         t.cmd_contains("bin/samba-tool drs replicate ${HOSTNAME}.${LCREALM} ${WIN_HOSTNAME}.${LCREALM} %s -k yes" % nc, ["was successful"])
388         t.cmd_contains("bin/samba-tool drs replicate ${WIN_HOSTNAME}.${LCREALM} ${HOSTNAME}.${LCREALM} %s -k yes" % nc, ["was successful"])
389
390     t.cmd_contains("bin/samba-tool drs showrepl ${HOSTNAME}.${LCREALM} -k yes",
391                  [ "INBOUND NEIGHBORS",
392                    "${BASEDN}",
393                    "Last attempt .* was successful",
394                    "CN=Configuration,${BASEDN}",
395                    "Last attempt .* was successful",
396                    "CN=Configuration,${BASEDN}", # cope with either order
397                    "Last attempt .* was successful",
398                    "OUTBOUND NEIGHBORS",
399                    "${BASEDN}",
400                    "Last success",
401                    "CN=Configuration,${BASEDN}",
402                    "Last success",
403                    "CN=Configuration,${BASEDN}",
404                    "Last success"],
405                    ordered=True,
406                    regex=True)
407
408     t.cmd_contains("bin/samba-tool drs showrepl ${WIN_HOSTNAME}.${LCREALM} -k yes",
409                  [ "INBOUND NEIGHBORS",
410                    "${BASEDN}",
411                    "Last attempt .* was successful",
412                    "CN=Configuration,${BASEDN}",
413                    "Last attempt .* was successful",
414                    "CN=Configuration,${BASEDN}",
415                    "Last attempt .* was successful",
416                    "OUTBOUND NEIGHBORS",
417                    "${BASEDN}",
418                    "Last success",
419                    "CN=Configuration,${BASEDN}",
420                    "Last success",
421                    "CN=Configuration,${BASEDN}",
422                    "Last success" ],
423                    ordered=True,
424                    regex=True)
425
426     child = t.open_telnet("${WIN_HOSTNAME}", "${DOMAIN}\\administrator", "${PASSWORD1}", set_time=True)
427     child.sendline("net use t: \\\\${HOSTNAME}.${LCREALM}\\test")
428
429     retries = 10
430     i = child.expect(["The command completed successfully", "The network path was not found"])
431     while i == 1 and retries > 0:
432         child.expect("C:")
433         time.sleep(2)
434         child.sendline("net use t: \\\\${HOSTNAME}.${LCREALM}\\test")
435         i = child.expect(["The command completed successfully", "The network path was not found"])
436         retries -=1
437
438     t.run_net_time(child)
439
440     t.info("Checking if showrepl is happy")
441     child.sendline("repadmin /showrepl")
442     child.expect("${BASEDN}")
443     child.expect("was successful")
444     child.expect("CN=Configuration,${BASEDN}")
445     child.expect("was successful")
446     child.expect("CN=Schema,CN=Configuration,${BASEDN}")
447     child.expect("was successful")
448
449     t.info("Checking if new users propogate to windows")
450     t.retry_cmd('bin/samba-tool newuser test2 ${PASSWORD2}', ["created successfully"])
451     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k no", ['Sharename', 'Remote IPC'])
452     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k yes", ['Sharename', 'Remote IPC'])
453
454     t.info("Checking if new users on windows propogate to samba")
455     child.sendline("net user test3 ${PASSWORD3} /add")
456     while True:
457         i = child.expect(["The command completed successfully",
458                           "The directory service was unable to allocate a relative identifier"])
459         if i == 0:
460             break
461         time.sleep(2)
462
463     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${LCREALM} -Utest3%${PASSWORD3} -k no", ['Sharename', 'IPC'])
464     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${LCREALM} -Utest3%${PASSWORD3} -k yes", ['Sharename', 'IPC'])
465
466     t.info("Checking propogation of user deletion")
467     t.run_cmd('bin/samba-tool user delete test2 -Uadministrator@${LCREALM}%${PASSWORD1}')
468     child.sendline("net user test3 /del")
469     child.expect("The command completed successfully")
470
471     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k no", ['LOGON_FAILURE'])
472     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${LCREALM} -Utest3%${PASSWORD3} -k no", ['LOGON_FAILURE'])
473     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k yes", ['LOGON_FAILURE'])
474     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${LCREALM} -Utest3%${PASSWORD3} -k yes", ['LOGON_FAILURE'])
475     t.vm_poweroff("${WIN_VM}")
476
477
478 def run_dcpromo_rodc(t, vm):
479     '''run a RODC dcpromo to join a windows DC to the samba domain'''
480     t.setwinvars(vm)
481     t.info("Joining a w2k8 box to the domain as a RODC")
482     t.vm_poweroff("${WIN_VM}", checkfail=False)
483     t.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
484     child = t.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_ip=True)
485     child.sendline("copy /Y con answers.txt")
486     child.sendline('''
487 [DCInstall]
488 ReplicaOrNewDomain=ReadOnlyReplica
489 ReplicaDomainDNSName=${LCREALM}
490 PasswordReplicationDenied="BUILTIN\Administrators"
491 PasswordReplicationDenied="BUILTIN\Server Operators"
492 PasswordReplicationDenied="BUILTIN\Backup Operators"
493 PasswordReplicationDenied="BUILTIN\Account Operators"
494 PasswordReplicationDenied="${DOMAIN}\Denied RODC Password Replication Group"
495 PasswordReplicationAllowed="${DOMAIN}\Allowed RODC Password Replication Group"
496 DelegatedAdmin="${DOMAIN}\\Administrator"
497 SiteName=Default-First-Site-Name
498 InstallDNS=No
499 ConfirmGc=Yes
500 CreateDNSDelegation=No
501 UserDomain=${LCREALM}
502 UserName=${LCREALM}\\administrator
503 Password=${PASSWORD1}
504 DatabasePath="C:\Windows\NTDS"
505 LogPath="C:\Windows\NTDS"
506 SYSVOLPath="C:\Windows\SYSVOL"
507 SafeModeAdminPassword=${PASSWORD1}
508 RebootOnCompletion=No
509 \1a
510 ''')
511     child.expect("copied.")
512     child.sendline("dcpromo /answer:answers.txt")
513     i = child.expect(["You must restart this computer", "failed"], timeout=120)
514     if i != 0:
515         raise Exception("dcpromo failed")
516     child.sendline("shutdown -r -t 0")
517     t.wait_reboot()
518
519
520
521 def test_dcpromo_rodc(t, vm):
522     '''test the RODC dcpromo worked'''
523     t.info("Checking the w2k8 RODC join is OK")
524     t.chdir('${PREFIX}')
525     t.port_wait("${WIN_IP}", 139)
526     child = t.open_telnet("${WIN_HOSTNAME}", "${DOMAIN}\\administrator", "${PASSWORD1}", set_time=True)
527     child.sendline("ipconfig /registerdns")
528     t.retry_cmd('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Uadministrator@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
529     t.cmd_contains("host -t A ${WIN_HOSTNAME}.${LCREALM}.", ['has address'])
530     t.cmd_contains('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utestallowed@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
531     child.sendline("net use t: \\\\${HOSTNAME}.${LCREALM}\\test")
532     child.expect("The command completed successfully")
533
534     t.info("Checking if showrepl is happy")
535     child.sendline("repadmin /showrepl")
536     child.expect("${BASEDN}")
537     child.expect("was successful")
538     child.expect("CN=Configuration,${BASEDN}")
539     child.expect("was successful")
540     child.expect("CN=Configuration,${BASEDN}")
541     child.expect("was successful")
542
543     for nc in [ '${BASEDN}', 'CN=Configuration,${BASEDN}', 'CN=Schema,CN=Configuration,${BASEDN}' ]:
544         t.cmd_contains("bin/samba-tool drs replicate --add-ref ${WIN_HOSTNAME}.${LCREALM} ${HOSTNAME}.${LCREALM} %s" % nc, ["was successful"])
545
546     t.cmd_contains("bin/samba-tool drs showrepl ${HOSTNAME}.${LCREALM}",
547                  [ "INBOUND NEIGHBORS",
548                    "OUTBOUND NEIGHBORS",
549                    "${BASEDN}",
550                    "Last attempt.*was successful",
551                    "CN=Configuration,${BASEDN}",
552                    "Last attempt.*was successful",
553                    "CN=Configuration,${BASEDN}",
554                    "Last attempt.*was successful" ],
555                    ordered=True,
556                    regex=True)
557
558     t.info("Checking if new users are available on windows")
559     t.run_cmd('bin/samba-tool newuser test2 ${PASSWORD2}')
560     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k yes", ['Sharename', 'Remote IPC'])
561     t.retry_cmd("bin/samba-tool drs replicate ${WIN_HOSTNAME}.${LCREALM} ${HOSTNAME}.${LCREALM} ${BASEDN}", ["was successful"])
562     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k no", ['Sharename', 'Remote IPC'])
563     t.run_cmd('bin/samba-tool user delete test2 -Uadministrator@${LCREALM}%${PASSWORD1}')
564     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k yes", ['LOGON_FAILURE'])
565     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k no", ['LOGON_FAILURE'])
566     t.vm_poweroff("${WIN_VM}")
567
568
569 def prep_join_as_dc(t, vm):
570     '''start VM and shutdown Samba in preperation to join a windows domain as a DC'''
571     t.setwinvars(vm)
572     t.info("Starting VMs for joining ${WIN_VM} as a second DC using samba-tool join DC")
573     t.chdir('${PREFIX}')
574     t.run_cmd('killall -9 -q samba smbd nmbd winbindd', checkfail=False)
575     t.vm_poweroff("${WIN_VM}", checkfail=False)
576     t.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
577     rndc_cmd(t, 'flush')
578     t.run_cmd("rm -rf etc/smb.conf private")
579     child = t.open_telnet("${WIN_HOSTNAME}", "${WIN_DOMAIN}\\administrator", "${WIN_PASS}", set_time=True)
580     t.get_ipconfig(child)
581
582 def join_as_dc(t, vm):
583     '''join a windows domain as a DC'''
584     t.setwinvars(vm)
585     t.info("Joining ${WIN_VM} as a second DC using samba-tool join DC")
586     t.port_wait("${WIN_IP}", 389)
587     t.retry_cmd("host -t SRV _ldap._tcp.${WIN_REALM} ${WIN_IP}", ['has SRV record'] )
588
589     t.retry_cmd("bin/samba-tool drs showrepl ${WIN_HOSTNAME}.${WIN_REALM} -Uadministrator%${WIN_PASS}", ['INBOUND NEIGHBORS'] )
590     t.run_cmd('bin/samba-tool join ${WIN_REALM} DC -Uadministrator%${WIN_PASS} -d${DEBUGLEVEL} --option=interfaces=${INTERFACE}')
591     t.run_cmd('bin/samba-tool drs kcc ${WIN_HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}')
592
593
594 def test_join_as_dc(t, vm):
595     '''test the join of a windows domain as a DC'''
596     t.info("Checking the DC join is OK")
597     t.chdir('${PREFIX}')
598     t.retry_cmd('bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}', ["C$", "IPC$", "Sharename"])
599     t.cmd_contains("host -t A ${HOSTNAME}.${WIN_REALM}.", ['has address'])
600     child = t.open_telnet("${WIN_HOSTNAME}", "${WIN_DOMAIN}\\administrator", "${WIN_PASS}", set_time=True)
601
602     t.info("Forcing kcc runs, and replication")
603     t.run_cmd('bin/samba-tool drs kcc ${WIN_HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}')
604     t.run_cmd('bin/samba-tool drs kcc ${HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}')
605
606     t.kinit("administrator@${WIN_REALM}", "${WIN_PASS}")
607     for nc in [ '${WIN_BASEDN}', 'CN=Configuration,${WIN_BASEDN}', 'CN=Schema,CN=Configuration,${WIN_BASEDN}' ]:
608         t.cmd_contains("bin/samba-tool drs replicate ${HOSTNAME}.${WIN_REALM} ${WIN_HOSTNAME}.${WIN_REALM} %s -k yes" % nc, ["was successful"])
609         t.cmd_contains("bin/samba-tool drs replicate ${WIN_HOSTNAME}.${WIN_REALM} ${HOSTNAME}.${WIN_REALM} %s -k yes" % nc, ["was successful"])
610
611     retries = 10
612     i = 1
613     while i == 1 and retries > 0:
614         child.sendline("net use t: \\\\${HOSTNAME}.${WIN_REALM}\\test")
615         i = child.expect(["The command completed successfully", "The network path was not found"])
616         child.expect("C:")
617         if i == 1:
618             time.sleep(2)
619         retries -=1
620
621     t.info("Checking if showrepl is happy")
622     child.sendline("repadmin /showrepl")
623     child.expect("${WIN_BASEDN}")
624     child.expect("was successful")
625     child.expect("CN=Configuration,${WIN_BASEDN}")
626     child.expect("was successful")
627     child.expect("CN=Configuration,${WIN_BASEDN}")
628     child.expect("was successful")
629
630     t.info("Checking if new users propogate to windows")
631     t.retry_cmd('bin/samba-tool newuser test2 ${PASSWORD2}', ["created successfully"])
632     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${WIN_REALM} -Utest2%${PASSWORD2} -k no", ['Sharename', 'Remote IPC'])
633     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${WIN_REALM} -Utest2%${PASSWORD2} -k yes", ['Sharename', 'Remote IPC'])
634
635     t.info("Checking if new users on windows propogate to samba")
636     child.sendline("net user test3 ${PASSWORD3} /add")
637     child.expect("The command completed successfully")
638     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k no", ['Sharename', 'IPC'])
639     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k yes", ['Sharename', 'IPC'])
640
641     t.info("Checking propogation of user deletion")
642     t.run_cmd('bin/samba-tool user delete test2 -Uadministrator@${WIN_REALM}%${WIN_PASS}')
643     child.sendline("net user test3 /del")
644     child.expect("The command completed successfully")
645
646     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${WIN_REALM} -Utest2%${PASSWORD2} -k no", ['LOGON_FAILURE'])
647     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k no", ['LOGON_FAILURE'])
648     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${WIN_REALM} -Utest2%${PASSWORD2} -k yes", ['LOGON_FAILURE'])
649     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k yes", ['LOGON_FAILURE'])
650     t.vm_poweroff("${WIN_VM}")
651
652
653 def join_as_rodc(t, vm):
654     '''join a windows domain as a RODC'''
655     t.setwinvars(vm)
656     t.info("Joining ${WIN_VM} as a RODC using samba-tool join DC")
657     t.port_wait("${WIN_IP}", 389)
658     t.retry_cmd("host -t SRV _ldap._tcp.${WIN_REALM} ${WIN_IP}", ['has SRV record'] )
659     t.retry_cmd("bin/samba-tool drs showrepl ${WIN_HOSTNAME}.${WIN_REALM} -Uadministrator%${WIN_PASS}", ['INBOUND NEIGHBORS'] )
660     t.run_cmd('bin/samba-tool join ${WIN_REALM} RODC -Uadministrator%${WIN_PASS} -d${DEBUGLEVEL} --option=interfaces=${INTERFACE}')
661     t.run_cmd('bin/samba-tool drs kcc ${WIN_HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}')
662
663
664 def test_join_as_rodc(t, vm):
665     '''test a windows domain RODC join'''
666     t.info("Checking the RODC join is OK")
667     t.chdir('${PREFIX}')
668     t.retry_cmd('bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}', ["C$", "IPC$", "Sharename"])
669     t.cmd_contains("host -t A ${HOSTNAME}.${WIN_REALM}.", ['has address'])
670     child = t.open_telnet("${WIN_HOSTNAME}", "${WIN_DOMAIN}\\administrator", "${WIN_PASS}", set_time=True)
671
672     t.info("Forcing kcc runs, and replication")
673     t.run_cmd('bin/samba-tool drs kcc ${HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}')
674     t.run_cmd('bin/samba-tool drs kcc ${WIN_HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}')
675
676     t.kinit("administrator@${WIN_REALM}", "${WIN_PASS}")
677     for nc in [ '${WIN_BASEDN}', 'CN=Configuration,${WIN_BASEDN}', 'CN=Schema,CN=Configuration,${WIN_BASEDN}' ]:
678         t.cmd_contains("bin/samba-tool drs replicate ${HOSTNAME}.${WIN_REALM} ${WIN_HOSTNAME}.${WIN_REALM} %s -k yes" % nc, ["was successful"])
679
680     retries = 10
681     i = 1
682     while i == 1 and retries > 0:
683         child.sendline("net use t: \\\\${HOSTNAME}.${WIN_REALM}\\test")
684         i = child.expect(["The command completed successfully", "The network path was not found"])
685         child.expect("C:")
686         if i == 1:
687             time.sleep(2)
688         retries -=1
689
690     t.info("Checking if showrepl is happy")
691     child.sendline("repadmin /showrepl")
692     child.expect("DSA invocationID")
693
694     t.cmd_contains("bin/samba-tool drs showrepl ${WIN_HOSTNAME}.${WIN_REALM} -k yes",
695                  [ "INBOUND NEIGHBORS",
696                    "OUTBOUND NEIGHBORS",
697                    "${WIN_BASEDN}",
698                    "Last attempt .* was successful",
699                    "CN=Configuration,${WIN_BASEDN}",
700                    "Last attempt .* was successful",
701                    "CN=Configuration,${WIN_BASEDN}",
702                    "Last attempt .* was successful" ],
703                    ordered=True,
704                    regex=True)
705
706     t.info("Checking if new users on windows propogate to samba")
707     child.sendline("net user test3 ${PASSWORD3} /add")
708     child.expect("The command completed successfully")
709     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k no", ['Sharename', 'IPC'])
710     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k yes", ['Sharename', 'IPC'])
711
712     # should this work?
713     t.info("Checking if new users propogate to windows")
714     t.cmd_contains('bin/samba-tool newuser test2 ${PASSWORD2}', ['No RID Set DN'])
715
716     t.info("Checking propogation of user deletion")
717     child.sendline("net user test3 /del")
718     child.expect("The command completed successfully")
719
720     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k no", ['LOGON_FAILURE'])
721     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k yes", ['LOGON_FAILURE'])
722     t.vm_poweroff("${WIN_VM}")
723
724
725 def run_dcpromo_as_first_dc(t, vm, func_level=None):
726     t.setwinvars(vm)
727     t.info("Configuring a windows VM ${WIN_VM} at the first DC in the domain using dcpromo")
728     child = t.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_time=True)
729     if t.get_is_dc(child):
730         return
731
732     if func_level == '2008r2':
733         t.setvar("FUNCTION_LEVEL_INT", str(4))
734     elif func_level == '2003':
735         t.setvar("FUNCTION_LEVEL_INT", str(1))
736     else:
737         t.setvar("FUNCTION_LEVEL_INT", str(0))
738
739     child = t.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_ip=True)
740
741     """This server must therefore not yet be a directory server, so we must promote it"""
742     child.sendline("copy /Y con answers.txt")
743     child.sendline('''
744 [DCInstall]
745 ; New forest promotion
746 ReplicaOrNewDomain=Domain
747 NewDomain=Forest
748 NewDomainDNSName=${WIN_REALM}
749 ForestLevel=${FUNCTION_LEVEL_INT}
750 DomainNetbiosName=${WIN_DOMAIN}
751 DomainLevel=${FUNCTION_LEVEL_INT}
752 InstallDNS=Yes
753 ConfirmGc=Yes
754 CreateDNSDelegation=No
755 DatabasePath="C:\Windows\NTDS"
756 LogPath="C:\Windows\NTDS"
757 SYSVOLPath="C:\Windows\SYSVOL"
758 ; Set SafeModeAdminPassword to the correct value prior to using the unattend file
759 SafeModeAdminPassword=${WIN_PASS}
760 ; Run-time flags (optional)
761 RebootOnCompletion=No
762 \1a
763 ''')
764     child.expect("copied.")
765     child.expect("C:")
766     child.expect("C:")
767     child.sendline("dcpromo /answer:answers.txt")
768     i = child.expect(["You must restart this computer", "failed", "Active Directory Domain Services was not installed", "C:"], timeout=120)
769     if i == 1 or i == 2:
770         raise Exception("dcpromo failed")
771     child.sendline("shutdown -r -t 0")
772     t.port_wait("${WIN_IP}", 139, wait_for_fail=True)
773     t.port_wait("${WIN_IP}", 139)
774
775
776 def test_howto(t):
777     '''test the Samba4 howto'''
778
779     check_prerequesites(t)
780
781     # we don't need fsync safety in these tests
782     t.putenv('TDB_NO_FSYNC', '1')
783
784     if not t.skip("configure_bind"):
785         configure_bind(t)
786     if not t.skip("stop_bind"):
787         stop_bind(t)
788     if not t.skip("stop_vms"):
789         stop_vms(t)
790
791     if not t.skip("build"):
792         build_s4(t)
793
794     if not t.skip("provision"):
795         provision_s4(t)
796
797     if not t.skip("create-shares"):
798         create_shares(t)
799
800     if not t.skip("starts4"):
801         start_s4(t)
802     if not t.skip("smbclient"):
803         test_smbclient(t)
804     if not t.skip("configure_bind2"):
805         configure_bind(t)
806     if not t.skip("start_bind"):
807         start_bind(t)
808     if not t.skip("dns"):
809         test_dns(t)
810     if not t.skip("kerberos"):
811         test_kerberos(t)
812     if not t.skip("dyndns"):
813         test_dyndns(t)
814
815     if t.have_vm('WINDOWS7') and not t.skip("windows7"):
816         run_winjoin(t, "WINDOWS7")
817         test_winjoin(t, "WINDOWS7")
818
819     if t.have_vm('WINXP') and not t.skip("winxp"):
820         run_winjoin(t, "WINXP")
821         test_winjoin(t, "WINXP")
822
823     if t.have_vm('W2K8R2C') and not t.skip("dcpromo_rodc"):
824         t.info("Testing w2k8r2 RODC dcpromo")
825         run_dcpromo_rodc(t, "W2K8R2C")
826         test_dcpromo_rodc(t, "W2K8R2C")
827
828     if t.have_vm('W2K8R2B') and not t.skip("dcpromo_w2k8r2"):
829         t.info("Testing w2k8r2 dcpromo")
830         run_dcpromo(t, "W2K8R2B")
831         test_dcpromo(t, "W2K8R2B")
832
833     if t.have_vm('W2K8B') and not t.skip("dcpromo_w2k8"):
834         t.info("Testing w2k8 dcpromo")
835         run_dcpromo(t, "W2K8B")
836         test_dcpromo(t, "W2K8B")
837
838     if t.have_vm('W2K3B') and not t.skip("dcpromo_w2k3"):
839         t.info("Testing w2k3 dcpromo")
840         t.info("Changing to 2003 functional level")
841         provision_s4(t, func_level='2003')
842         create_shares(t)
843         start_s4(t)
844         test_smbclient(t)
845         restart_bind(t)
846         test_dns(t)
847         test_kerberos(t)
848         test_dyndns(t)
849         run_dcpromo(t, "W2K3B")
850         test_dcpromo(t, "W2K3B")
851
852     if t.have_vm('W2K8R2A') and not t.skip("join_w2k8r2"):
853         prep_join_as_dc(t, "W2K8R2A")
854         run_dcpromo_as_first_dc(t, "W2K8R2A", func_level='2008r2')
855         join_as_dc(t, "W2K8R2A")
856         create_shares(t)
857         start_s4(t)
858         test_dyndns(t)
859         test_join_as_dc(t, "W2K8R2A")
860
861     if t.have_vm('W2K8R2A') and not t.skip("join_rodc"):
862         prep_join_as_dc(t, "W2K8R2A")
863         run_dcpromo_as_first_dc(t, "W2K8R2A", func_level='2008r2')
864         join_as_rodc(t, "W2K8R2A")
865         create_shares(t)
866         start_s4(t)
867         test_dyndns(t)
868         test_join_as_rodc(t, "W2K8R2A")
869
870     if t.have_vm('W2K3A') and not t.skip("join_w2k3"):
871         prep_join_as_dc(t, "W2K3A")
872         run_dcpromo_as_first_dc(t, "W2K3A", func_level='2003')
873         join_as_dc(t, "W2K3A")
874         create_shares(t)
875         start_s4(t)
876         test_dyndns(t)
877         test_join_as_dc(t, "W2K3A")
878
879     t.info("Howto test: All OK")
880
881
882 def test_cleanup(t):
883     '''cleanup after tests'''
884     t.info("Cleaning up ...")
885     restore_resolv_conf(t)
886     if getattr(t, 'bind_child', False):
887         t.bind_child.kill()
888
889
890 if __name__ == '__main__':
891     t = wintest.wintest()
892
893     t.setup("test-s4-howto.py", "source4")
894
895     try:
896         test_howto(t)
897     except:
898         if not t.opts.nocleanup:
899             test_cleanup(t)
900         raise
901
902     if not t.opts.nocleanup:
903         test_cleanup(t)
904     t.info("S4 howto test: All OK")