f3fc138e6bb3df7fb200daedb2d8d46dfa7d4fbd
[ira/wip.git] / source / scripting / python / samba / provision.py
1 #
2 #    backend code for provisioning a Samba4 server
3 #    Released under the GNU GPL v2 or later
4 #    Copyright Jelmer Vernooij 2007
5 #
6 # Based on the original in EJS:
7 #    Copyright Andrew Tridgell 2005
8 #
9
10 from base64 import b64encode
11 import os
12 import pwd
13 import grp
14 import time
15 import uuid, misc
16 from socket import gethostname, gethostbyname
17 import param
18 import registry
19 import samba
20 from samba import Ldb, substitute_var, valid_netbios_name
21 from samba.samdb import SamDB
22 import security
23 from ldb import Dn, SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
24         LDB_ERR_NO_SUCH_OBJECT, timestring
25
26
27 DEFAULTSITE = "Default-First-Site-Name"
28
29 class InvalidNetbiosName(Exception):
30     def __init__(self, name):
31         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
32
33
34 class ProvisionPaths:
35     def __init__(self):
36         self.smbconf = None
37         self.shareconf = None
38         self.hklm = None
39         self.hkcu = None
40         self.hkcr = None
41         self.hku = None
42         self.hkpd = None
43         self.hkpt = None
44         self.samdb = None
45         self.secrets = None
46         self.keytab = None
47         self.dns_keytab = None
48         self.dns = None
49         self.winsdb = None
50         self.ldap_basedn_ldif = None
51         self.ldap_config_basedn_ldif = None
52         self.ldap_schema_basedn_ldif = None
53
54
55 def install_ok(lp, session_info, credentials):
56     """Check whether the current install seems ok."""
57     if lp.get("realm") == "":
58         return False
59     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
60             credentials=credentials, lp=lp)
61     if len(ldb.search("(cn=Administrator)")) != 1:
62         return False
63     return True
64
65
66 def findnss(nssfn, *names):
67     """Find a user or group from a list of possibilities."""
68     for name in names:
69         try:
70             return nssfn(name)
71         except KeyError:
72             pass
73     raise Exception("Unable to find user/group for %s" % arguments[1])
74
75
76 def open_ldb(session_info, credentials, lp, dbname):
77     assert session_info is not None
78     try:
79         return Ldb(dbname, session_info=session_info, credentials=credentials, 
80                    lp=lp)
81     except LdbError, e:
82         print e
83         os.unlink(dbname)
84         return Ldb(dbname, session_info=session_info, credentials=credentials,
85                    lp=lp)
86
87
88 def setup_add_ldif(ldb, setup_dir, ldif, subst_vars=None):
89     """Setup a ldb in the private dir."""
90     assert isinstance(ldif, str)
91     assert isinstance(setup_dir, str)
92     src = os.path.join(setup_dir, ldif)
93
94     data = open(src, 'r').read()
95     if subst_vars is not None:
96         data = substitute_var(data, subst_vars)
97
98     assert "${" not in data
99
100     for msg in ldb.parse_ldif(data):
101         ldb.add(msg[1])
102
103
104 def setup_modify_ldif(ldb, setup_dir, ldif, substvars=None):
105     """Modify a ldb in the private dir.
106     
107     :param ldb: LDB object.
108     :param setup_dir: Setup directory.
109     :param ldif: LDIF file path.
110     :param substvars: Optional dictionary with substitution variables.
111     """
112     src = os.path.join(setup_dir, ldif)
113
114     data = open(src, 'r').read()
115     if substvars is not None:
116         data = substitute_var(data, substvars)
117
118     assert "${" not in data
119
120     for (changetype, msg) in ldb.parse_ldif(data):
121         ldb.modify(msg)
122
123
124 def setup_ldb(ldb, setup_dir, ldif, subst_vars=None):
125     assert ldb is not None
126     ldb.transaction_start()
127     try:
128         setup_add_ldif(ldb, setup_dir, ldif, subst_vars)
129     except:
130         ldb.transaction_cancel()
131         raise
132     ldb.transaction_commit()
133
134
135 def setup_file(setup_dir, template, fname, substvars):
136     """Setup a file in the private dir."""
137     f = fname
138     src = os.path.join(setup_dir, template)
139
140     if os.path.exists(f):
141         os.unlink(f)
142
143     data = open(src, 'r').read()
144     if substvars:
145         data = substitute_var(data, substvars)
146     assert not "${" in data
147
148     open(f, 'w').write(data)
149
150
151 def provision_default_paths(lp, dnsdomain):
152     """Set the default paths for provisioning.
153
154     :param lp: Loadparm context.
155     :param dnsdomain: DNS Domain name
156     """
157     paths = ProvisionPaths()
158     private_dir = lp.get("private dir")
159     paths.shareconf = os.path.join(private_dir, "share.ldb")
160     paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb")
161     paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb")
162     paths.templates = os.path.join(private_dir, "templates.ldb")
163     paths.keytab = os.path.join(private_dir, "secrets.keytab")
164     paths.dns_keytab = os.path.join(private_dir, "dns.keytab")
165     paths.dns = os.path.join(private_dir, dnsdomain + ".zone")
166     paths.winsdb = os.path.join(private_dir, "wins.ldb")
167     paths.ldap_basedn_ldif = os.path.join(private_dir, 
168                                           dnsdomain + ".ldif")
169     paths.ldap_config_basedn_ldif = os.path.join(private_dir, 
170                                              dnsdomain + "-config.ldif")
171     paths.ldap_schema_basedn_ldif = os.path.join(private_dir, 
172                                               dnsdomain + "-schema.ldif")
173     paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
174     paths.phpldapadminconfig = os.path.join(private_dir, 
175                                             "phpldapadmin-config.php")
176     paths.hklm = os.path.join(private_dir, "hklm.ldb")
177     paths.sysvol = lp.get("sysvol", "path")
178     if paths.sysvol is None:
179         paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
180
181     paths.netlogon = lp.get("netlogon", "path")
182     if paths.netlogon is None:
183         paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
184
185     return paths
186
187
188 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users, 
189                         wheel, backup):
190     """setup reasonable name mappings for sam names to unix names."""
191     # add some foreign sids if they are not present already
192     ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
193     ldb.add_foreign(domaindn, "S-1-1-0", "World")
194     ldb.add_foreign(domaindn, "S-1-5-2", "Network")
195     ldb.add_foreign(domaindn, "S-1-5-18", "System")
196     ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
197
198     # some well known sids
199     ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
200     ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
201     ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
202     ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
203     ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
204     ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
205     ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
206     ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
207     ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
208
209     # and some well known domain rids
210     ldb.setup_name_mapping(domaindn, sid + "-500", root)
211     ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
212     ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
213     ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
214     ldb.setup_name_mapping(domaindn, sid + "-513", users)
215     ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
216
217
218 def provision_become_dc(setup_dir, message, paths, lp, session_info, 
219                         credentials):
220     assert session_info is not None
221
222     message("Setting up templates into %s" % paths.templates)
223     setup_templatesdb(paths.templates, setup_dir, session_info, 
224                       credentials, lp)
225
226     # Also wipes the database
227     message("Setting up samdb")
228     os.path.unlink(paths.samdb)
229     samdb = SamDB(paths.samdb, credentials=credentials, 
230                   session_info=session_info, lp=lp)
231     samdb.erase()
232
233     message("Setting up %s partitions" % paths.samdb)
234     setup_samdb_partitions(samdb, setup_dir, schemadn, 
235                            configdn, domaindn)
236
237     samdb = SamDB(paths.samdb, credentials=credentials, 
238                   session_info=session_info, lp=lp)
239
240     ldb.transaction_start()
241     try:
242         message("Setting up %s attributes" % paths.samdb)
243         setup_add_ldif(samdb, setup_dir, "provision_init.ldif")
244
245         message("Setting up %s rootDSE" % paths.samdb)
246         setup_samdb_rootdse(samdb, setup_dir, schemadn, domaindn, 
247                             hostname, dnsdomain, realm, rootdn, configdn, 
248                             netbiosname)
249
250         message("Erasing data from partitions")
251         ldb_erase_partitions(domaindn, message, samdb, None)
252
253         message("Setting up %s indexes" % paths.samdb)
254         setup_add_ldif(samdb, setup_dir, "provision_index.ldif")
255     except:
256         samdb.transaction_cancel()
257         raise
258
259     samdb.transaction_commit()
260
261     message("Setting up %s" % paths.secrets)
262     secrets_ldb = setup_secretsdb(paths.secrets, setup_dir, session_info, credentials, lp)
263     setup_ldb(secrets_ldb, setup_dir, "secrets_dc.ldif", 
264               { "MACHINEPASS_B64": b64encode(machinepass) })
265
266
267 def setup_secretsdb(path, setup_dir, session_info, credentials, lp):
268     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials, lp=lp)
269     secrets_ldb.erase()
270     setup_ldb(secrets_ldb, setup_dir, "secrets_init.ldif")
271     setup_ldb(secrets_ldb, setup_dir, "secrets.ldif")
272     return secrets_ldb
273
274
275 def setup_templatesdb(path, setup_dir, session_info, credentials, lp):
276     templates_ldb = Ldb(path, session_info=session_info,
277                         credentials=credentials, lp=lp)
278     templates_ldb.erase()
279     setup_ldb(templates_ldb, setup_dir, "provision_templates.ldif", None)
280
281
282 def setup_registry(path, setup_dir, session_info, credentials, lp):
283     reg = registry.Registry()
284     hive = registry.Hive(path, session_info=session_info, 
285                          credentials=credentials, lp_ctx=lp)
286     reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
287     provision_reg = os.path.join(setup_dir, "provision.reg")
288     assert os.path.exists(provision_reg)
289     reg.apply_patchfile(provision_reg)
290
291
292 def setup_samdb_rootdse(samdb, setup_dir, schemadn, domaindn, hostname, 
293                         dnsdomain, realm, rootdn, configdn, netbiosname):
294     setup_add_ldif(samdb, setup_dir, "provision_rootdse_add.ldif", {
295         "SCHEMADN": schemadn, 
296         "NETBIOSNAME": netbiosname,
297         "DNSDOMAIN": dnsdomain,
298         "DEFAULTSITE": DEFAULTSITE,
299         "REALM": realm,
300         "DNSNAME": "%s.%s" % (hostname, dnsdomain),
301         "DOMAINDN": domaindn,
302         "ROOTDN": rootdn,
303         "CONFIGDN": configdn,
304         "VERSION": samba.version(),
305         })
306
307
308 def setup_samdb_partitions(samdb, setup_dir, schemadn, configdn, domaindn):
309     #Add modules to the list to activate them by default
310     #beware often order is important
311     #
312     # Some Known ordering constraints:
313     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
314     # - objectclass must be before password_hash, because password_hash checks
315     #   that the objectclass is of type person (filled in by objectclass
316     #   module when expanding the objectclass list)
317     # - partition must be last
318     # - each partition has its own module list then
319     modules_list = ["rootdse",
320                     "paged_results",
321                     "ranged_results",
322                     "anr",
323                     "server_sort",
324                     "extended_dn",
325                     "asq",
326                     "samldb",
327                     "rdn_name",
328                     "objectclass",
329                     "kludge_acl",
330                     "operational"]
331     tdb_modules_list = [
332                     "subtree_rename",
333                     "subtree_delete",
334                     "linked_attributes"]
335     modules_list2 = ["show_deleted",
336                     "partition"]
337  
338     setup_ldb(samdb, setup_dir, "provision_partitions.ldif", {
339         "SCHEMADN": schemadn, 
340         "SCHEMADN_LDB": "schema.ldb",
341         "SCHEMADN_MOD2": ",objectguid",
342         "CONFIGDN": configdn,
343         "CONFIGDN_LDB": "configuration.ldb",
344         "DOMAINDN": domaindn,
345         "DOMAINDN_LDB": "users.ldb",
346         "SCHEMADN_MOD": "schema_fsmo",
347         "CONFIGDN_MOD": "naming_fsmo",
348         "CONFIGDN_MOD2": ",objectguid",
349         "DOMAINDN_MOD": "pdc_fsmo,password_hash",
350         "DOMAINDN_MOD2": ",objectguid",
351         "MODULES_LIST": ",".join(modules_list),
352         "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
353         "MODULES_LIST2": ",".join(modules_list2),
354         })
355
356
357
358 def provision(lp, setup_dir, message, blank, paths, session_info, 
359               credentials, ldapbackend, realm=None, domain=None, hostname=None, 
360               hostip=None, domainsid=None, hostguid=None, adminpass=None, 
361               krbtgtpass=None, domainguid=None, policyguid=None, 
362               invocationid=None, machinepass=None, dnspass=None, root=None,
363               nobody=None, nogroup=None, users=None, wheel=None, backup=None, 
364               aci=None, serverrole=None):
365     """Provision samba4
366     
367     :note: caution, this wipes all existing data!
368     """
369
370     if domainsid is None:
371         domainsid = security.random_sid()
372     if policyguid is None:
373         policyguid = uuid.random()
374     if invocationid is None:
375         invocationid = uuid.random()
376     if adminpass is None:
377         adminpass = misc.random_password(12)
378     if krbtgtpass is None:
379         krbtgtpass = misc.random_password(12)
380     if machinepass is None:
381         machinepass  = misc.random_password(12)
382     if dnspass is None:
383         dnspass = misc.random_password(12)
384     if root is None:
385         root = findnss(pwd.getpwnam, "root")[4]
386     if nobody is None:
387         nobody = findnss(pwd.getpwnam, "nobody")[4]
388     if nogroup is None:
389         nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[2]
390     if users is None:
391         users = findnss(grp.getgrnam, "users", "guest", "other", "unknown", "usr")[2]
392     if wheel is None:
393         wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[2]
394     if backup is None:
395         backup = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[2]
396     if aci is None:
397         aci = "# no aci for local ldb"
398     if serverrole is None:
399         serverrole = lp.get("server role")
400
401     if realm is None:
402         realm = lp.get("realm")
403     else:
404         if lp.get("realm").upper() != realm.upper():
405             raise Error("realm '%s' in smb.conf must match chosen realm '%s'\n" %
406                 (lp.get("realm"), realm))
407
408     assert realm is not None
409     realm = realm.upper()
410
411     if domain is None:
412         domain = lp.get("workgroup")
413     else:
414         if lp.get("workgroup").upper() != domain.upper():
415             raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'\n",
416                 lp.get("workgroup"), domain)
417
418     assert domain is not None
419     domain = domain.upper()
420     if not valid_netbios_name(domain):
421         raise InvalidNetbiosName(domain)
422
423     if hostname is None:
424         hostname = gethostname().split(".")[0].lower()
425
426     if hostip is None:
427         hostip = gethostbyname(hostname)
428
429     netbiosname = hostname.upper()
430     if not valid_netbios_name(netbiosname):
431         raise InvalidNetbiosName(netbiosname)
432
433     dnsdomain    = realm.lower()
434     domaindn     = "DC=" + dnsdomain.replace(".", ",DC=")
435     rootdn       = domaindn
436     configdn     = "CN=Configuration," + rootdn
437     schemadn     = "CN=Schema," + configdn
438
439     rdn_dc = domaindn.split(",")[0][len("DC="):]
440
441     message("Provisioning for %s in realm %s" % (domain, realm))
442     message("Using administrator password: %s" % adminpass)
443
444     assert paths.smbconf is not None
445
446     # only install a new smb.conf if there isn't one there already
447     if not os.path.exists(paths.smbconf):
448         message("Setting up smb.conf")
449         if serverrole == "domain controller":
450             smbconfsuffix = "dc"
451         elif serverrole == "member":
452             smbconfsuffix = "member"
453         else:
454             assert "Invalid server role setting: %s" % serverrole
455         setup_file(setup_dir, "provision.smb.conf.%s" % smbconfsuffix, paths.smbconf)
456         lp.reload()
457
458     # only install a new shares config db if there is none
459     if not os.path.exists(paths.shareconf):
460         message("Setting up share.ldb")
461         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
462                         credentials=credentials, lp=lp)
463         setup_ldb(share_ldb, setup_dir, "share.ldif")
464
465     message("Setting up %s" % paths.secrets)
466     secrets_ldb = setup_secretsdb(paths.secrets, setup_dir, session_info=session_info, 
467                     credentials=credentials, lp=lp)
468
469     message("Setting up registry")
470     # FIXME: Still fails for some reason
471     #setup_registry(paths.hklm, setup_dir, session_info, 
472     #               credentials=credentials, lp=lp)
473
474     message("Setting up templates into %s" % paths.templates)
475     setup_templatesdb(paths.templates, setup_dir, session_info=session_info, 
476                       credentials=credentials, lp=lp)
477
478     samdb = SamDB(paths.samdb, session_info=session_info, 
479                   credentials=credentials, lp=lp)
480     samdb.erase()
481
482     message("Setting up sam.ldb partitions")
483     setup_samdb_partitions(samdb, setup_dir, schemadn, configdn, domaindn)
484
485     samdb = SamDB(paths.samdb, session_info=session_info, 
486                   credentials=credentials, lp=lp)
487
488     samdb.transaction_start()
489     try:
490         message("Setting up sam.ldb attributes")
491         setup_add_ldif(samdb, setup_dir, "provision_init.ldif")
492
493         message("Setting up sam.ldb rootDSE")
494         setup_samdb_rootdse(samdb, setup_dir, schemadn, domaindn, 
495                             hostname, dnsdomain, realm, rootdn, configdn, 
496                             netbiosname)
497
498         message("Erasing data from partitions")
499         ldb_erase_partitions(domaindn, message, samdb, ldapbackend)
500     except:
501         samdb.transaction_cancel()
502         raise
503
504     samdb.transaction_commit()
505
506     message("Pre-loading the Samba 4 and AD schema")
507     samdb = SamDB(paths.samdb, session_info=session_info, 
508                   credentials=credentials, lp=lp)
509     samdb.set_domain_sid(domainsid)
510     load_schema(setup_dir, samdb, schemadn, netbiosname, configdn)
511
512     samdb.transaction_start()
513         
514     try:
515         message("Adding DomainDN: %s (permitted to fail)" % domaindn)
516         setup_add_ldif(samdb, setup_dir, "provision_basedn.ldif", {
517             "DOMAINDN": domaindn,
518             "ACI": aci,
519             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
520             "RDN_DC": rdn_dc,
521             })
522
523         message("Modifying DomainDN: " + domaindn + "")
524         if domainguid is not None:
525             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
526         else:
527             domainguid_mod = ""
528
529         setup_modify_ldif(samdb, setup_dir, "provision_basedn_modify.ldif", {
530             "RDN_DC": rdn_dc,
531             "LDAPTIME": timestring(int(time.time())),
532             "DOMAINSID": str(domainsid),
533             "SCHEMADN": schemadn, 
534             "NETBIOSNAME": netbiosname,
535             "DEFAULTSITE": DEFAULTSITE,
536             "CONFIGDN": configdn,
537             "POLICYGUID": policyguid,
538             "DOMAINDN": domaindn,
539             "DOMAINGUID_MOD": domainguid_mod,
540             })
541
542         message("Adding configuration container (permitted to fail)")
543         setup_add_ldif(samdb, setup_dir, "provision_configuration_basedn.ldif", {
544             "CONFIGDN": configdn, 
545             "ACI": aci,
546             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
547             })
548         message("Modifying configuration container")
549         setup_modify_ldif(samdb, setup_dir, "provision_configuration_basedn_modify.ldif", {
550             "CONFIGDN": configdn, 
551             "SCHEMADN": schemadn,
552             })
553
554         message("Adding schema container (permitted to fail)")
555         setup_add_ldif(samdb, setup_dir, "provision_schema_basedn.ldif", {
556             "SCHEMADN": schemadn,
557             "ACI": aci,
558             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
559             })
560         message("Modifying schema container")
561         setup_modify_ldif(samdb, setup_dir, "provision_schema_basedn_modify.ldif", {
562             "SCHEMADN": schemadn,
563             "NETBIOSNAME": netbiosname,
564             "DEFAULTSITE": DEFAULTSITE,
565             "CONFIGDN": configdn,
566             })
567
568         message("Setting up sam.ldb Samba4 schema")
569         setup_add_ldif(samdb, setup_dir, "schema_samba4.ldif", 
570                        {"SCHEMADN": schemadn })
571         message("Setting up sam.ldb AD schema")
572         setup_add_ldif(samdb, setup_dir, "schema.ldif", 
573                        {"SCHEMADN": schemadn})
574
575         message("Setting up sam.ldb configuration data")
576         setup_add_ldif(samdb, setup_dir, "provision_configuration.ldif", {
577             "CONFIGDN": configdn,
578             "NETBIOSNAME": netbiosname,
579             "DEFAULTSITE": DEFAULTSITE,
580             "DNSDOMAIN": dnsdomain,
581             "DOMAIN": domain,
582             "SCHEMADN": schemadn,
583             "DOMAINDN": domaindn,
584             })
585
586         message("Setting up display specifiers")
587         setup_add_ldif(samdb, setup_dir, "display_specifiers.ldif", {"CONFIGDN": configdn})
588
589         message("Adding users container (permitted to fail)")
590         setup_add_ldif(samdb, setup_dir, "provision_users_add.ldif", {
591             "DOMAINDN": domaindn})
592         message("Modifying users container")
593         setup_modify_ldif(samdb, setup_dir, "provision_users_modify.ldif", {
594             "DOMAINDN": domaindn})
595         message("Adding computers container (permitted to fail)")
596         setup_add_ldif(samdb, setup_dir, "provision_computers_add.ldif", {
597             "DOMAINDN": domaindn})
598         message("Modifying computers container")
599         setup_modify_ldif(samdb, setup_dir, "provision_computers_modify.ldif", {
600             "DOMAINDN": domaindn})
601         message("Setting up sam.ldb data")
602         setup_add_ldif(samdb, setup_dir, "provision.ldif", {
603             "DOMAINDN": domaindn,
604             "NETBIOSNAME": netbiosname,
605             "DEFAULTSITE": DEFAULTSITE,
606             "CONFIGDN": configdn,
607             })
608
609         if not blank:
610
611     #    message("Activate schema module")
612     #    setup_modify_ldif("schema_activation.ldif", info, samdb, False)
613     #
614     #    // (hack) Reload, now we have the schema loaded.  
615     #    commit_ok = samdb.transaction_commit()
616     #    if (!commit_ok) {
617     #        message("samdb commit failed: " + samdb.errstring() + "\n")
618     #        assert(commit_ok)
619     #    }
620     #    samdb.close()
621     #
622     #    samdb = open_ldb(info, paths.samdb, False)
623     #
624             message("Setting up sam.ldb users and groups")
625             setup_add_ldif(samdb, setup_dir, "provision_users.ldif", {
626                 "DOMAINDN": domaindn,
627                 "DOMAINSID": str(domainsid),
628                 "CONFIGDN": configdn,
629                 "ADMINPASS_B64": b64encode(adminpass),
630                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
631                 })
632
633             if lp.get("server role") == "domain controller":
634                 message("Setting up self join")
635                 if hostguid is not None:
636                     hostguid_add = "objectGUID: %s" % hostguid
637                 else:
638                     hostguid_add = ""
639
640                 setup_add_ldif(samdb, setup_dir, "provision_self_join.ldif", { 
641                           "CONFIGDN": configdn, 
642                           "SCHEMADN": schemadn,
643                           "DOMAINDN": domaindn,
644                           "INVOCATIONID": invocationid,
645                           "NETBIOSNAME": netbiosname,
646                           "DEFAULTSITE": DEFAULTSITE,
647                           "DNSNAME": "%s.%s" % (hostname, dnsdomain),
648                           "MACHINEPASS_B64": b64encode(machinepass),
649                           "DNSPASS_B64": b64encode(dnspass),
650                           "REALM": realm,
651                           "DOMAIN": domain,
652                           "HOSTGUID_ADD": hostguid_add,
653                           "DNSDOMAIN": dnsdomain})
654                 setup_add_ldif(samdb, setup_dir, "provision_group_policy.ldif", { 
655                           "POLICYGUID": policyguid,
656                           "DNSDOMAIN": dnsdomain,
657                           "DOMAINSID": str(domainsid),
658                           "DOMAINDN": domaindn})
659
660                 os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}"), 0755)
661                 os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "Machine"), 0755)
662                 os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "User"), 0755)
663                 if not os.path.isdir(paths.netlogon):
664                     os.makedirs(paths.netlogon, 0755)
665                 setup_ldb(secrets_ldb, setup_dir, "secrets_dc.ldif", { 
666                     "MACHINEPASS_B64": b64encode(machinepass),
667                     "DOMAIN": domain,
668                     "REALM": realm,
669                     "LDAPTIME": timestring(int(time.time())),
670                     "DNSDOMAIN": dnsdomain,
671                     "DOMAINSID": str(domainsid),
672                     "SECRETS_KEYTAB": paths.keytab,
673                     "NETBIOSNAME": netbiosname,
674                     "SAM_LDB": paths.samdb,
675                     "DNS_KEYTAB": paths.dns_keytab,
676                     "DNSPASS_B64": b64encode(dnspass),
677                     })
678
679             setup_name_mappings(samdb, str(domainsid), 
680                                 domaindn, root=root, nobody=nobody, 
681                                 nogroup=nogroup, wheel=wheel, users=users,
682                                 backup=backup)
683
684         message("Setting up sam.ldb index")
685         setup_add_ldif(samdb, setup_dir, "provision_index.ldif")
686
687         message("Setting up sam.ldb rootDSE marking as syncronized")
688         setup_modify_ldif(samdb, setup_dir, "provision_rootdse_modify.ldif")
689     except:
690         samdb.transaction_cancel()
691         raise
692
693     samdb.transaction_commit()
694
695     message("Setting up phpLDAPadmin configuration")
696     create_phplpapdadmin_config(paths.phpldapadminconfig, setup_dir, paths.s4_ldapi_path)
697
698     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
699
700     message("Setting up DNS zone: %s" % dnsdomain)
701     provision_dns(setup_dir, paths, session_info, credentials, lp,
702                   hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
703                   domaindn=domaindn, dnspass=dnspass, realm=realm)
704     message("Please install the zone located in %s into your DNS server" % paths.dns)
705
706 def create_phplpapdadmin_config(path, setup_dir, s4_ldapi_path):
707     setup_file(setup_dir, "phpldapadmin-config.php", 
708                path, {"S4_LDAPI_URI": "ldapi://%s" % s4_ldapi_path.replace("/", "%2F")})
709
710
711 def provision_dns(setup_dir, paths, session_info, 
712                   credentials, lp, dnsdomain, domaindn, 
713                   hostip, hostname, dnspass, realm):
714     """Write out a DNS zone file, from the info in the current database."""
715
716     # connect to the sam
717     ldb = SamDB(paths.samdb, session_info=session_info, credentials=credentials,
718                 lp=lp)
719
720     # These values may have changed, due to an incoming SamSync,
721     # or may not have been specified, so fetch them from the database
722     domainguid = str(ldb.searchone(Dn(ldb, domaindn), "objectGUID"))
723
724     hostguid = str(ldb.searchone(Dn(ldb, domaindn), "objectGUID" ,
725                                  expression="(&(objectClass=computer)(cn=%s))" % hostname))
726
727     setup_file(setup_dir, "provision.zone", paths.dns, {
728             "DNSPASS_B64": b64encode(dnspass),
729             "HOSTNAME": hostname,
730             "DNSDOMAIN": dnsdomain,
731             "REALM": realm,
732             "HOSTIP": hostip,
733             "DOMAINGUID": domainguid,
734             "DATESTRING": time.strftime("%Y%m%d%H"),
735             "DEFAULTSITE": DEFAULTSITE,
736             "HOSTGUID": hostguid,
737         })
738
739
740 def provision_ldapbase(setup_dir, message, paths):
741     """Write out a DNS zone file, from the info in the current database."""
742     message("Setting up LDAP base entry: %s" % domaindn)
743     rdns = domaindn.split(",")
744
745     rdn_dc = rdns[0][len("DC="):]
746
747     setup_file(setup_dir, "provision_basedn.ldif", 
748            paths.ldap_basedn_ldif)
749
750     setup_file(setup_dir, "provision_configuration_basedn.ldif", 
751            paths.ldap_config_basedn_ldif)
752
753     setup_file(setup_dir, "provision_schema_basedn.ldif", 
754            paths.ldap_schema_basedn_ldif, {
755             "SCHEMADN": schemadn,
756             "ACI": "# no aci for local ldb",
757             "EXTENSIBLEOBJECT": "objectClass: extensibleObject"})
758
759     message("Please install the LDIF located in " + paths.ldap_basedn_ldif + ", " + paths.ldap_config_basedn_ldif + " and " + paths.ldap_schema_basedn_ldif + " into your LDAP server, and re-run with --ldap-backend=ldap://my.ldap.server")
760
761
762 def load_schema(setup_dir, samdb, schemadn, netbiosname, configdn):
763     """Load schema."""
764     src = os.path.join(setup_dir, "schema.ldif")
765     schema_data = open(src, 'r').read()
766     src = os.path.join(setup_dir, "schema_samba4.ldif")
767     schema_data += open(src, 'r').read()
768     schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
769     src = os.path.join(setup_dir, "provision_schema_basedn_modify.ldif")
770     head_data = open(src, 'r').read()
771     head_data = substitute_var(head_data, {
772                     "SCHEMADN": schemadn,
773                     "NETBIOSNAME": netbiosname,
774                     "CONFIGDN": configdn,
775                     "DEFAULTSITE": DEFAULTSITE})
776     samdb.attach_schema_from_ldif(head_data, schema_data)
777
778
779 def join_domain(domain, netbios_name, join_type, creds):
780     ctx = NetContext(creds)
781     joindom = object()
782     joindom.domain = domain
783     joindom.join_type = join_type
784     joindom.netbios_name = netbios_name
785     if not ctx.JoinDomain(joindom):
786         raise Exception("Domain Join failed: " + joindom.error_string)
787
788
789 def vampire(domain, session_info, credentials, message):
790     """Vampire a remote domain.  
791     
792     Session info and credentials are required for for
793     access to our local database (might be remote ldap)
794     """
795     ctx = NetContext(credentials)
796     machine_creds = Credentials()
797     machine_creds.set_domain(form.domain)
798     if not machine_creds.set_machine_account():
799         raise Exception("Failed to access domain join information!")
800     vampire_ctx.machine_creds = machine_creds
801     vampire_ctx.session_info = session_info
802     if not ctx.SamSyncLdb(vampire_ctx):
803         raise Exception("Migration of remote domain to Samba failed: %s " % vampire_ctx.error_string)
804
805
806 def ldb_erase_partitions(domaindn, message, ldb, ldapbackend):
807     """Erase an ldb, removing all records."""
808     assert ldb is not None
809     res = ldb.search(Dn(ldb, ""), SCOPE_BASE, "(objectClass=*)", 
810                      ["namingContexts"])
811     assert len(res) == 1
812     if not "namingContexts" in res[0]:
813         return
814     for basedn in res[0]["namingContexts"]:
815         anything = "(|(objectclass=*)(dn=*))"
816         previous_remaining = 1
817         current_remaining = 0
818
819         if ldapbackend and (basedn == domaindn):
820             # Only delete objects that were created by provision
821             anything = "(objectcategory=*)"
822
823         k = 0
824         while ++k < 10 and (previous_remaining != current_remaining):
825             # and the rest
826             res2 = ldb.search(Dn(ldb, basedn), SCOPE_SUBTREE, anything, ["dn"])
827             previous_remaining = current_remaining
828             current_remaining = len(res2)
829             for msg in res2:
830                 try:
831                     ldb.delete(msg.dn)
832                 except LdbError, (_, text):
833                     message("Unable to delete %s: %s" % (msg.dn, text))
834
835