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