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