11dd819ad3a57a78765a81d6171bae743760a305
[ira/wip.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     """Setup the secrets database.
293
294     :param path: Path to the secrets database.
295     :param setup_path: Get the path to a setup file.
296     :param session_info: Session info.
297     :param credentials: Credentials
298     :param lp: Loadparm context
299     """
300     if os.path.exists(path):
301         os.unlink(path)
302     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials, lp=lp)
303     secrets_ldb.erase()
304     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
305     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
306     return secrets_ldb
307
308
309 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
310     """Setup the templates database.
311
312     :param path: Path to the database.
313     :param setup_path: Function for obtaining the path to setup files.
314     :param session_info: Session info
315     :param credentials: Credentials
316     :param lp: Loadparm context
317     """
318     templates_ldb = SamDB(path, session_info=session_info,
319                         credentials=credentials, lp=lp)
320     templates_ldb.erase()
321     templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
322
323
324 def setup_registry(path, setup_path, session_info, credentials, lp):
325     """Setup the registry.
326     
327     :param path: Path to the registry database
328     :param setup_path: Function that returns the path to a setup.
329     :param session_info: Session information
330     :param credentials: Credentials
331     :param lp: Loadparm context
332     """
333     reg = registry.Registry()
334     hive = registry.open_ldb(path, session_info=session_info, 
335                          credentials=credentials, lp_ctx=lp)
336     reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
337     provision_reg = setup_path("provision.reg")
338     assert os.path.exists(provision_reg)
339     reg.diff_apply(provision_reg)
340
341
342 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname, 
343                         dnsdomain, realm, rootdn, configdn, netbiosname):
344     """Setup the SamDB rootdse.
345
346     :param samdb: Sam Database handle
347     :param setup_path: Obtain setup path
348     ...
349     """
350     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
351         "SCHEMADN": schemadn, 
352         "NETBIOSNAME": netbiosname,
353         "DNSDOMAIN": dnsdomain,
354         "DEFAULTSITE": DEFAULTSITE,
355         "REALM": realm,
356         "DNSNAME": "%s.%s" % (hostname, dnsdomain),
357         "DOMAINDN": domaindn,
358         "ROOTDN": rootdn,
359         "CONFIGDN": configdn,
360         "VERSION": samba.version(),
361         })
362
363
364 def setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn):
365     """Setup SAM database partitions.
366
367     :param samdb: Sam Database handle
368     :param setup_path: Setup path function
369     :param schemadn: Schema DN.
370     :param configdn: Configuration DN.
371     :param domaindn: Domain DN.
372     """
373     #Add modules to the list to activate them by default
374     #beware often order is important
375     #
376     # Some Known ordering constraints:
377     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
378     # - objectclass must be before password_hash, because password_hash checks
379     #   that the objectclass is of type person (filled in by objectclass
380     #   module when expanding the objectclass list)
381     # - partition must be last
382     # - each partition has its own module list then
383     modules_list = ["rootdse",
384                     "paged_results",
385                     "ranged_results",
386                     "anr",
387                     "server_sort",
388                     "extended_dn",
389                     "asq",
390                     "samldb",
391                     "rdn_name",
392                     "objectclass",
393                     "kludge_acl",
394                     "operational"]
395     tdb_modules_list = [
396                     "subtree_rename",
397                     "subtree_delete",
398                     "linked_attributes"]
399     modules_list2 = ["show_deleted",
400                     "partition"]
401  
402     setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
403         "SCHEMADN": schemadn, 
404         "SCHEMADN_LDB": "schema.ldb",
405         "SCHEMADN_MOD2": ",objectguid",
406         "CONFIGDN": configdn,
407         "CONFIGDN_LDB": "configuration.ldb",
408         "DOMAINDN": domaindn,
409         "DOMAINDN_LDB": "users.ldb",
410         "SCHEMADN_MOD": "schema_fsmo",
411         "CONFIGDN_MOD": "naming_fsmo",
412         "CONFIGDN_MOD2": ",objectguid",
413         "DOMAINDN_MOD": "pdc_fsmo,password_hash",
414         "DOMAINDN_MOD2": ",objectguid",
415         "MODULES_LIST": ",".join(modules_list),
416         "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
417         "MODULES_LIST2": ",".join(modules_list2),
418         })
419
420
421 def setup_self_join(samdb, configdn, schemadn, domaindn, 
422                     netbiosname, hostname, dnsdomain, machinepass, dnspass, 
423                     realm, domainname, domainsid, invocationid, setup_path,
424                     policyguid, hostguid=None):
425     if hostguid is not None:
426         hostguid_add = "objectGUID: %s" % hostguid
427     else:
428         hostguid_add = ""
429
430     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
431               "CONFIGDN": configdn, 
432               "SCHEMADN": schemadn,
433               "DOMAINDN": domaindn,
434               "INVOCATIONID": invocationid,
435               "NETBIOSNAME": netbiosname,
436               "DEFAULTSITE": DEFAULTSITE,
437               "DNSNAME": "%s.%s" % (hostname, dnsdomain),
438               "MACHINEPASS_B64": b64encode(machinepass),
439               "DNSPASS_B64": b64encode(dnspass),
440               "REALM": realm,
441               "DOMAIN": domainname,
442               "HOSTGUID_ADD": hostguid_add,
443               "DNSDOMAIN": dnsdomain})
444     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
445               "POLICYGUID": policyguid,
446               "DNSDOMAIN": dnsdomain,
447               "DOMAINSID": str(domainsid),
448               "DOMAINDN": domaindn})
449
450
451 def setup_samdb(path, setup_path, session_info, credentials, lp, 
452                 schemadn, configdn, domaindn, dnsdomain, realm, 
453                 netbiosname, message, hostname, rootdn, erase, 
454                 domainsid, aci, rdn_dc, domainguid, policyguid, 
455                 domainname, blank, adminpass, krbtgtpass, 
456                 machinepass, hostguid, invocationid, dnspass):
457     # Also wipes the database
458     message("Setting up sam.ldb")
459     samdb = SamDB(path, session_info=session_info, 
460                   credentials=credentials, lp=lp)
461
462     message("Setting up sam.ldb partitions")
463     setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn)
464
465     samdb = SamDB(path, session_info=session_info, 
466                   credentials=credentials, lp=lp)
467
468     samdb.transaction_start()
469     try:
470         message("Setting up sam.ldb attributes")
471         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
472
473         message("Setting up sam.ldb rootDSE")
474         setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, 
475                             hostname, dnsdomain, realm, rootdn, configdn, 
476                             netbiosname)
477
478         if erase:
479             message("Erasing data from partitions")
480             samdb.erase_partitions()
481     except:
482         samdb.transaction_cancel()
483         raise
484
485     samdb.transaction_commit()
486
487     message("Pre-loading the Samba 4 and AD schema")
488     samdb = SamDB(path, session_info=session_info, 
489                   credentials=credentials, lp=lp)
490     samdb.set_domain_sid(domainsid)
491     load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
492
493     samdb.transaction_start()
494         
495     try:
496         message("Adding DomainDN: %s (permitted to fail)" % domaindn)
497         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
498             "DOMAINDN": domaindn,
499             "ACI": aci,
500             "RDN_DC": rdn_dc,
501             })
502
503         message("Modifying DomainDN: " + domaindn + "")
504         if domainguid is not None:
505             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
506         else:
507             domainguid_mod = ""
508
509         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
510             "RDN_DC": rdn_dc,
511             "LDAPTIME": timestring(int(time.time())),
512             "DOMAINSID": str(domainsid),
513             "SCHEMADN": schemadn, 
514             "NETBIOSNAME": netbiosname,
515             "DEFAULTSITE": DEFAULTSITE,
516             "CONFIGDN": configdn,
517             "POLICYGUID": policyguid,
518             "DOMAINDN": domaindn,
519             "DOMAINGUID_MOD": domainguid_mod,
520             })
521
522         message("Adding configuration container (permitted to fail)")
523         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
524             "CONFIGDN": configdn, 
525             "ACI": aci,
526             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
527             })
528         message("Modifying configuration container")
529         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
530             "CONFIGDN": configdn, 
531             "SCHEMADN": schemadn,
532             })
533
534         message("Adding schema container (permitted to fail)")
535         setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
536             "SCHEMADN": schemadn,
537             "ACI": aci,
538             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
539             })
540         message("Modifying schema container")
541         setup_modify_ldif(samdb, setup_path("provision_schema_basedn_modify.ldif"), {
542             "SCHEMADN": schemadn,
543             "NETBIOSNAME": netbiosname,
544             "DEFAULTSITE": DEFAULTSITE,
545             "CONFIGDN": configdn,
546             })
547
548         message("Setting up sam.ldb Samba4 schema")
549         setup_add_ldif(samdb, setup_path("schema_samba4.ldif"), 
550                        {"SCHEMADN": schemadn })
551         message("Setting up sam.ldb AD schema")
552         setup_add_ldif(samdb, setup_path("schema.ldif"), 
553                        {"SCHEMADN": schemadn})
554
555         message("Setting up sam.ldb configuration data")
556         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
557             "CONFIGDN": configdn,
558             "NETBIOSNAME": netbiosname,
559             "DEFAULTSITE": DEFAULTSITE,
560             "DNSDOMAIN": dnsdomain,
561             "DOMAIN": domainname,
562             "SCHEMADN": schemadn,
563             "DOMAINDN": domaindn,
564             })
565
566         message("Setting up display specifiers")
567         setup_add_ldif(samdb, setup_path("display_specifiers.ldif"), 
568                        {"CONFIGDN": configdn})
569
570         message("Adding users container (permitted to fail)")
571         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
572             "DOMAINDN": domaindn})
573         message("Modifying users container")
574         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
575             "DOMAINDN": domaindn})
576         message("Adding computers container (permitted to fail)")
577         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
578             "DOMAINDN": domaindn})
579         message("Modifying computers container")
580         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
581             "DOMAINDN": domaindn})
582         message("Setting up sam.ldb data")
583         setup_add_ldif(samdb, setup_path("provision.ldif"), {
584             "DOMAINDN": domaindn,
585             "NETBIOSNAME": netbiosname,
586             "DEFAULTSITE": DEFAULTSITE,
587             "CONFIGDN": configdn,
588             })
589
590         if not blank:
591             message("Setting up sam.ldb users and groups")
592             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
593                 "DOMAINDN": domaindn,
594                 "DOMAINSID": str(domainsid),
595                 "CONFIGDN": configdn,
596                 "ADMINPASS_B64": b64encode(adminpass),
597                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
598                 })
599
600             if lp.get("server role") == "domain controller":
601                 message("Setting up self join")
602                 setup_self_join(samdb, configdn=configdn, schemadn=schemadn, 
603                                 domaindn=domaindn, invocationid=invocationid, 
604                                 dnspass=dnspass, netbiosname=netbiosname, 
605                                 dnsdomain=dnsdomain, realm=realm, 
606                                 machinepass=machinepass, domainname=domainname, 
607                                 domainsid=domainsid, policyguid=policyguid,
608                                 hostname=hostname, hostguid=hostguid, 
609                                 setup_path=setup_path)
610
611         message("Setting up sam.ldb index")
612         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
613
614         message("Setting up sam.ldb rootDSE marking as synchronized")
615         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
616     except:
617         samdb.transaction_cancel()
618         raise
619
620     samdb.transaction_commit()
621     return samdb
622
623
624 def provision(lp, setup_dir, message, blank, paths, session_info, 
625               credentials, ldapbackend, realm=None, domain=None, hostname=None, 
626               hostip=None, domainsid=None, hostguid=None, adminpass=None, 
627               krbtgtpass=None, domainguid=None, policyguid=None, 
628               invocationid=None, machinepass=None, dnspass=None, root=None,
629               nobody=None, nogroup=None, users=None, wheel=None, backup=None, 
630               aci=None, serverrole=None):
631     """Provision samba4
632     
633     :note: caution, this wipes all existing data!
634     """
635
636     def setup_path(file):
637         return os.path.join(setup_dir, file)
638
639     erase = False
640
641     if domainsid is None:
642         domainsid = security.random_sid()
643     if policyguid is None:
644         policyguid = uuid.random()
645     if invocationid is None:
646         invocationid = uuid.random()
647     if adminpass is None:
648         adminpass = misc.random_password(12)
649     if krbtgtpass is None:
650         krbtgtpass = misc.random_password(12)
651     if machinepass is None:
652         machinepass  = misc.random_password(12)
653     if dnspass is None:
654         dnspass = misc.random_password(12)
655     if root is None:
656         root = findnss(pwd.getpwnam, "root")[4]
657     if nobody is None:
658         nobody = findnss(pwd.getpwnam, "nobody")[4]
659     if nogroup is None:
660         nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[2]
661     if users is None:
662         users = findnss(grp.getgrnam, "users", "guest", "other", "unknown", 
663                         "usr")[2]
664     if wheel is None:
665         wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[2]
666     if backup is None:
667         backup = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[2]
668     if aci is None:
669         aci = "# no aci for local ldb"
670     if serverrole is None:
671         serverrole = lp.get("server role")
672
673     if realm is None:
674         realm = lp.get("realm")
675     else:
676         if lp.get("realm").upper() != realm.upper():
677             raise Exception("realm '%s' in smb.conf must match chosen realm '%s'\n" %
678                 (lp.get("realm"), realm))
679
680     assert realm is not None
681     realm = realm.upper()
682
683     if domain is None:
684         domain = lp.get("workgroup")
685     else:
686         if lp.get("workgroup").upper() != domain.upper():
687             raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'\n",
688                 lp.get("workgroup"), domain)
689
690     assert domain is not None
691     domain = domain.upper()
692     if not valid_netbios_name(domain):
693         raise InvalidNetbiosName(domain)
694
695     if hostname is None:
696         hostname = gethostname().split(".")[0].lower()
697
698     if hostip is None:
699         hostip = gethostbyname(hostname)
700
701     netbiosname = hostname.upper()
702     if not valid_netbios_name(netbiosname):
703         raise InvalidNetbiosName(netbiosname)
704
705     dnsdomain    = realm.lower()
706     domaindn     = "DC=" + dnsdomain.replace(".", ",DC=")
707     rootdn       = domaindn
708     configdn     = "CN=Configuration," + rootdn
709     schemadn     = "CN=Schema," + configdn
710
711     rdn_dc = domaindn.split(",")[0][len("DC="):]
712
713     message("set DOMAIN SID: %s" % str(domainsid))
714     message("Provisioning for %s in realm %s" % (domain, realm))
715     message("Using administrator password: %s" % adminpass)
716
717     assert paths.smbconf is not None
718
719     # only install a new smb.conf if there isn't one there already
720     if not os.path.exists(paths.smbconf):
721         message("Setting up smb.conf")
722         if serverrole == "domain controller":
723             smbconfsuffix = "dc"
724         elif serverrole == "member":
725             smbconfsuffix = "member"
726         else:
727             assert "Invalid server role setting: %s" % serverrole
728         setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), paths.smbconf, {
729             "HOSTNAME": hostname,
730             "DOMAIN_CONF": domain,
731             "REALM_CONF": realm,
732             "SERVERROLE": serverrole,
733             "NETLOGONPATH": paths.netlogon,
734             "SYSVOLPATH": paths.sysvol,
735             })
736         lp.reload()
737
738     # only install a new shares config db if there is none
739     if not os.path.exists(paths.shareconf):
740         message("Setting up share.ldb")
741         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
742                         credentials=credentials, lp=lp)
743         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
744
745     message("Setting up secrets.ldb")
746     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
747                                   session_info=session_info, 
748                                   credentials=credentials, lp=lp)
749
750     message("Setting up the registry")
751     setup_registry(paths.hklm, setup_path, session_info, 
752                    credentials=credentials, lp=lp)
753
754     message("Setting up templates db")
755     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
756                       credentials=credentials, lp=lp)
757
758     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, credentials=credentials,
759                         lp=lp, schemadn=schemadn, configdn=configdn, domaindn=domaindn,
760                         dnsdomain=dnsdomain, netbiosname=netbiosname, realm=realm, message=message,
761                         hostname=hostname, rootdn=rootdn, erase=erase, domainsid=domainsid, aci=aci,
762                         rdn_dc=rdn_dc, domainguid=domainguid, policyguid=policyguid, 
763                         domainname=domain, blank=blank, adminpass=adminpass, krbtgtpass=krbtgtpass,
764                         hostguid=hostguid, invocationid=invocationid, machinepass=machinepass,
765                         dnspass=dnspass)
766
767     if lp.get("server role") == "domain controller":
768         os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}"), 0755)
769         os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "Machine"), 0755)
770         os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "User"), 0755)
771         if not os.path.isdir(paths.netlogon):
772             os.makedirs(paths.netlogon, 0755)
773         secrets_ldb = Ldb(paths.secrets, session_info=session_info, credentials=credentials, lp=lp)
774         setup_ldb(secrets_ldb, setup_path("secrets_dc.ldif"), { 
775             "MACHINEPASS_B64": b64encode(machinepass),
776             "DOMAIN": domain,
777             "REALM": realm,
778             "LDAPTIME": timestring(int(time.time())),
779             "DNSDOMAIN": dnsdomain,
780             "DOMAINSID": str(domainsid),
781             "SECRETS_KEYTAB": paths.keytab,
782             "NETBIOSNAME": netbiosname,
783             "SAM_LDB": paths.samdb,
784             "DNS_KEYTAB": paths.dns_keytab,
785             "DNSPASS_B64": b64encode(dnspass),
786             })
787
788     if not blank:
789         setup_name_mappings(samdb, str(domainsid), 
790                         domaindn, root=root, nobody=nobody, 
791                         nogroup=nogroup, wheel=wheel, users=users,
792                         backup=backup)
793
794     message("Setting up phpLDAPadmin configuration")
795     create_phplpapdadmin_config(paths.phpldapadminconfig, setup_path, paths.s4_ldapi_path)
796
797     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
798
799     if lp.get("server role") == "domain controller":
800         samdb = SamDB(paths.samdb, session_info=session_info, 
801                       credentials=credentials, lp=lp)
802
803         domainguid = samdb.searchone(domaindn, "objectGUID")
804         assert isinstance(domainguid, str)
805         hostguid = samdb.searchone(domaindn, "objectGUID",
806                 expression="(&(objectClass=computer)(cn=%s))" % hostname,
807                 scope=SCOPE_SUBTREE)
808         assert isinstance(hostguid, str)
809
810         message("Setting up DNS zone: %s" % dnsdomain)
811         create_zone_file(paths.dns, setup_path, samdb, 
812                       hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
813                       domaindn=domaindn, dnspass=dnspass, realm=realm, 
814                       domainguid=domainguid, hostguid=hostguid)
815         message("Please install the zone located in %s into your DNS server" % paths.dns)
816
817     return domaindn
818
819 def create_phplpapdadmin_config(path, setup_path, s4_ldapi_path):
820     """Create a PHP LDAP admin configuration file.
821
822     :param path: Path to write the configuration to.
823     :param setup_path: Function to generate setup paths.
824     :param s4_ldapi_path: Path to Samba 4 LDAPI socket.
825     """
826     setup_file(setup_path("phpldapadmin-config.php"), 
827                path, {"S4_LDAPI_URI": "ldapi://%s" % s4_ldapi_path.replace("/", "%2F")})
828
829
830 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, 
831                   hostip, hostname, dnspass, realm, domainguid, hostguid):
832     """Write out a DNS zone file, from the info in the current database.
833     
834     :param path: Path of the new file.
835     :param setup_path": Setup path function.
836     :param samdb: SamDB object
837     :param dnsdomain: DNS Domain name
838     :param domaindn: DN of the Domain
839     :param hostip: Local IP
840     :param hostname: Local hostname
841     :param dnspass: Password for DNS
842     :param realm: Realm name
843     :param domainguid: GUID of the domain.
844     :param hostguid: GUID of the host.
845     """
846
847     setup_file(setup_path("provision.zone"), path, {
848             "DNSPASS_B64": b64encode(dnspass),
849             "HOSTNAME": hostname,
850             "DNSDOMAIN": dnsdomain,
851             "REALM": realm,
852             "HOSTIP": hostip,
853             "DOMAINGUID": domainguid,
854             "DATESTRING": time.strftime("%Y%m%d%H"),
855             "DEFAULTSITE": DEFAULTSITE,
856             "HOSTGUID": hostguid,
857         })
858
859
860 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
861     """Load schema.
862     
863     :param samdb: Load a schema into a SamDB.
864     :param setup_path: Setup path function.
865     :param schemadn: DN of the schema
866     :param netbiosname: NetBIOS name of the host.
867     :param configdn: DN of the configuration
868     """
869     schema_data = open(setup_path("schema.ldif"), 'r').read()
870     schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
871     schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
872     head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
873     head_data = substitute_var(head_data, {
874                     "SCHEMADN": schemadn,
875                     "NETBIOSNAME": netbiosname,
876                     "CONFIGDN": configdn,
877                     "DEFAULTSITE": DEFAULTSITE})
878     samdb.attach_schema_from_ldif(head_data, schema_data)
879