s4-python: Fix formatting.
[mat/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-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 #
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 #
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
16 #   
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 # GNU General Public License for more details.
21 #   
22 # You should have received a copy of the GNU General Public License
23 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 #
25
26 """Functions for setting up a Samba configuration."""
27
28 from base64 import b64encode
29 import os
30 import pwd
31 import grp
32 import time
33 import uuid
34 import socket
35 import param
36 import registry
37 import urllib
38
39 import ldb
40
41 from samba.auth import system_session, admin_session
42 from samba import glue, version, Ldb, substitute_var, valid_netbios_name
43 from samba import check_all_substituted, read_and_sub_file, setup_file
44 from samba import DS_DOMAIN_FUNCTION_2003, DS_DC_FUNCTION_2008
45 from samba.dcerpc import security
46 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
47 from samba.idmap import IDmapDB
48 from samba.ntacls import setntacl, dsacl2fsacl
49 from samba.ndr import ndr_pack,ndr_unpack
50 from samba.schema import Schema
51 from ms_display_specifiers import read_ms_ldif
52 from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
53 from provisionexceptions import ProvisioningError, InvalidNetbiosName
54
55 __docformat__ = "restructuredText"
56
57 def find_setup_dir():
58     """Find the setup directory used by provision."""
59     dirname = os.path.dirname(__file__)
60     if "/site-packages/" in dirname:
61         prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
62         for suffix in ["share/setup", "share/samba/setup", "setup"]:
63             ret = os.path.join(prefix, suffix)
64             if os.path.isdir(ret):
65                 return ret
66     # In source tree
67     ret = os.path.join(dirname, "../../../setup")
68     if os.path.isdir(ret):
69         return ret
70     raise Exception("Unable to find setup directory.")
71
72 # descriptors of the naming contexts
73 # hard coded at this point, but will probably be changed when
74 # we enable different fsmo roles
75
76 def get_config_descriptor(domain_sid):
77     sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
78            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
79            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
81            "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82            "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83            "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
84            "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
85            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
86            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
87            "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
88            "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
89            "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
90            "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
91            "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
92     sec = security.descriptor.from_sddl(sddl, domain_sid)
93     return ndr_pack(sec)
94
95 def get_domain_descriptor(domain_sid):
96     sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
97         "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
98     "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
99     "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
100     "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
101     "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
102     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
103     "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
104     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
105     "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
106     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
107     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
108     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
109     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
110     "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
111     "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
112     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
113     "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
114     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115     "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117     "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
118     "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
119     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
120     "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
121     "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
122     "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
123     "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
124     "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
125     "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
126     "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
127     "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
128     "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
129     "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130     "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131     "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
132     "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
133     "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
134     "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
135     "(A;;RPRC;;;RU)" \
136     "(A;CI;LC;;;RU)" \
137     "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
138     "(A;;RP;;;WD)" \
139     "(A;;RPLCLORC;;;ED)" \
140     "(A;;RPLCLORC;;;AU)" \
141     "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
142     "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
143     "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
144     "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
145     sec = security.descriptor.from_sddl(sddl, domain_sid)
146     return ndr_pack(sec)
147
148 DEFAULTSITE = "Default-First-Site-Name"
149
150 # Exception classes
151
152 class ProvisionPaths(object):
153     def __init__(self):
154         self.shareconf = None
155         self.hklm = None
156         self.hkcu = None
157         self.hkcr = None
158         self.hku = None
159         self.hkpd = None
160         self.hkpt = None
161         self.samdb = None
162         self.idmapdb = None
163         self.secrets = None
164         self.keytab = None
165         self.dns_keytab = None
166         self.dns = None
167         self.winsdb = None
168         self.private_dir = None
169         self.ldapdir = None
170         self.slapdconf = None
171         self.modulesconf = None
172         self.memberofconf = None
173         self.olmmron = None
174         self.olmmrserveridsconf = None
175         self.olmmrsyncreplconf = None
176         self.olcdir = None
177         self.olslapd = None
178         self.olcseedldif = None
179
180
181 class ProvisionNames(object):
182     def __init__(self):
183         self.rootdn = None
184         self.domaindn = None
185         self.configdn = None
186         self.schemadn = None
187         self.ldapmanagerdn = None
188         self.dnsdomain = None
189         self.realm = None
190         self.netbiosname = None
191         self.domain = None
192         self.hostname = None
193         self.sitename = None
194         self.smbconf = None
195     
196
197 class ProvisionResult(object):
198     def __init__(self):
199         self.paths = None
200         self.domaindn = None
201         self.lp = None
202         self.samdb = None
203
204 def check_install(lp, session_info, credentials):
205     """Check whether the current install seems ok.
206     
207     :param lp: Loadparm context
208     :param session_info: Session information
209     :param credentials: Credentials
210     """
211     if lp.get("realm") == "":
212         raise Exception("Realm empty")
213     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
214             credentials=credentials, lp=lp)
215     if len(ldb.search("(cn=Administrator)")) != 1:
216         raise ProvisioningError("No administrator account found")
217
218
219 def findnss(nssfn, names):
220     """Find a user or group from a list of possibilities.
221     
222     :param nssfn: NSS Function to try (should raise KeyError if not found)
223     :param names: Names to check.
224     :return: Value return by first names list.
225     """
226     for name in names:
227         try:
228             return nssfn(name)
229         except KeyError:
230             pass
231     raise KeyError("Unable to find user/group %r" % names)
232
233
234 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
235 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
236
237
238 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
239     """Setup a ldb in the private dir.
240     
241     :param ldb: LDB file to import data into
242     :param ldif_path: Path of the LDIF file to load
243     :param subst_vars: Optional variables to subsitute in LDIF.
244     :param nocontrols: Optional list of controls, can be None for no controls
245     """
246     assert isinstance(ldif_path, str)
247     data = read_and_sub_file(ldif_path, subst_vars)
248     ldb.add_ldif(data,controls)
249
250
251 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
252     """Modify a ldb in the private dir.
253     
254     :param ldb: LDB object.
255     :param ldif_path: LDIF file path.
256     :param subst_vars: Optional dictionary with substitution variables.
257     """
258     data = read_and_sub_file(ldif_path, subst_vars)
259
260     ldb.modify_ldif(data)
261
262
263 def setup_ldb(ldb, ldif_path, subst_vars):
264     """Import a LDIF a file into a LDB handle, optionally substituting variables.
265
266     :note: Either all LDIF data will be added or none (using transactions).
267
268     :param ldb: LDB file to import into.
269     :param ldif_path: Path to the LDIF file.
270     :param subst_vars: Dictionary with substitution variables.
271     """
272     assert ldb is not None
273     ldb.transaction_start()
274     try:
275         setup_add_ldif(ldb, ldif_path, subst_vars)
276     except:
277         ldb.transaction_cancel()
278         raise
279     ldb.transaction_commit()
280
281
282 def provision_paths_from_lp(lp, dnsdomain):
283     """Set the default paths for provisioning.
284
285     :param lp: Loadparm context.
286     :param dnsdomain: DNS Domain name
287     """
288     paths = ProvisionPaths()
289     paths.private_dir = lp.get("private dir")
290     paths.dns_keytab = "dns.keytab"
291
292     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
293     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
294     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
295     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
296     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
297     paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
298     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
299     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
300     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
301     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
302     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
303     paths.phpldapadminconfig = os.path.join(paths.private_dir, 
304                                             "phpldapadmin-config.php")
305     paths.ldapdir = os.path.join(paths.private_dir, 
306                                  "ldap")
307     paths.slapdconf = os.path.join(paths.ldapdir, 
308                                    "slapd.conf")
309     paths.slapdpid = os.path.join(paths.ldapdir, 
310                                    "slapd.pid")
311     paths.modulesconf = os.path.join(paths.ldapdir, 
312                                      "modules.conf")
313     paths.memberofconf = os.path.join(paths.ldapdir, 
314                                       "memberof.conf")
315     paths.olmmrserveridsconf = os.path.join(paths.ldapdir, 
316                                             "mmr_serverids.conf")
317     paths.olmmrsyncreplconf = os.path.join(paths.ldapdir, 
318                                            "mmr_syncrepl.conf")
319     paths.olcdir = os.path.join(paths.ldapdir, 
320                                  "slapd.d")
321     paths.olcseedldif = os.path.join(paths.ldapdir, 
322                                  "olc_seed.ldif")
323     paths.hklm = "hklm.ldb"
324     paths.hkcr = "hkcr.ldb"
325     paths.hkcu = "hkcu.ldb"
326     paths.hku = "hku.ldb"
327     paths.hkpd = "hkpd.ldb"
328     paths.hkpt = "hkpt.ldb"
329
330     paths.sysvol = lp.get("path", "sysvol")
331
332     paths.netlogon = lp.get("path", "netlogon")
333
334     paths.smbconf = lp.configfile
335
336     return paths
337
338
339 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
340                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
341                 schemadn=None, serverdn=None, sitename=None):
342     """Guess configuration settings to use."""
343
344     if hostname is None:
345         hostname = socket.gethostname().split(".")[0]
346
347     netbiosname = lp.get("netbios name")
348     if netbiosname is None:
349         netbiosname = hostname
350     assert netbiosname is not None
351     netbiosname = netbiosname.upper()
352     if not valid_netbios_name(netbiosname):
353         raise InvalidNetbiosName(netbiosname)
354
355     if dnsdomain is None:
356         dnsdomain = lp.get("realm")
357         if dnsdomain is None or dnsdomain == "":
358             raise ProvisioningError("guess_names: 'realm' not specified in supplied smb.conf!")
359
360     dnsdomain = dnsdomain.lower()
361
362     if serverrole is None:
363         serverrole = lp.get("server role")
364         if serverrole is None:
365             raise ProvisioningError("guess_names: 'server role' not specified in supplied smb.conf!")
366
367     serverrole = serverrole.lower()
368
369     realm = dnsdomain.upper()
370
371     if lp.get("realm").upper() != realm:
372         raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
373
374     if lp.get("server role").lower() != serverrole:
375         raise ProvisioningError("guess_names: server role '%s' in smb.conf must match chosen server role '%s'!", lp.get("server role").upper(), serverrole)
376
377     if serverrole == "domain controller":
378         if domain is None:
379             domain = lp.get("workgroup")
380         if domain is None:
381             raise ProvisioningError("guess_names: 'workgroup' not specified in supplied smb.conf!")
382         domain = domain.upper()
383
384         if lp.get("workgroup").upper() != domain:
385             raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
386
387         if domaindn is None:
388             domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
389     else:
390         domain = netbiosname
391         if domaindn is None:
392             domaindn = "DC=" + netbiosname
393         
394     if not valid_netbios_name(domain):
395         raise InvalidNetbiosName(domain)
396         
397     if hostname.upper() == realm:
398         raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
399     if netbiosname == realm:
400         raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
401     if domain == realm:
402         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
403
404     if rootdn is None:
405        rootdn = domaindn
406        
407     if configdn is None:
408         configdn = "CN=Configuration," + rootdn
409     if schemadn is None:
410         schemadn = "CN=Schema," + configdn
411
412     if sitename is None:
413         sitename=DEFAULTSITE
414
415     names = ProvisionNames()
416     names.rootdn = rootdn
417     names.domaindn = domaindn
418     names.configdn = configdn
419     names.schemadn = schemadn
420     names.ldapmanagerdn = "CN=Manager," + rootdn
421     names.dnsdomain = dnsdomain
422     names.domain = domain
423     names.realm = realm
424     names.netbiosname = netbiosname
425     names.hostname = hostname
426     names.sitename = sitename
427     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
428  
429     return names
430     
431
432 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
433                  targetdir, sid_generator,eadb):
434     """Create a new smb.conf file based on a couple of basic settings.
435     """
436     assert smbconf is not None
437     if hostname is None:
438         hostname = socket.gethostname().split(".")[0]
439     netbiosname = hostname.upper()
440
441     if serverrole is None:
442         serverrole = "standalone"
443
444     assert serverrole in ("domain controller", "member server", "standalone")
445     if serverrole == "domain controller":
446         smbconfsuffix = "dc"
447     elif serverrole == "member server":
448         smbconfsuffix = "member"
449     elif serverrole == "standalone":
450         smbconfsuffix = "standalone"
451
452     if sid_generator is None:
453         sid_generator = "internal"
454
455     assert domain is not None
456     domain = domain.upper()
457
458     assert realm is not None
459     realm = realm.upper()
460
461     default_lp = param.LoadParm()
462     #Load non-existant file
463     if os.path.exists(smbconf):
464         default_lp.load(smbconf)
465     if eadb:
466         posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(os.path.join(targetdir, "private"),"eadb.tdb"))
467     else:
468         posixeadb_line = ""
469
470     if targetdir is not None:
471         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
472         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
473
474         default_lp.set("lock dir", os.path.abspath(targetdir))
475     else:
476         privatedir_line = ""
477         lockdir_line = ""
478
479     if sid_generator == "internal":
480         sid_generator_line = ""
481     else:
482         sid_generator_line = "sid generator = " + sid_generator
483
484     sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
485     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
486
487     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), 
488                smbconf, {
489             "NETBIOS_NAME": netbiosname,
490             "DOMAIN": domain,
491             "REALM": realm,
492             "SERVERROLE": serverrole,
493             "NETLOGONPATH": netlogon,
494             "SYSVOLPATH": sysvol,
495             "SIDGENERATOR_LINE": sid_generator_line,
496             "PRIVATEDIR_LINE": privatedir_line,
497             "LOCKDIR_LINE": lockdir_line,
498                         "POSIXEADB_LINE": posixeadb_line
499             })
500
501
502 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
503                         users_gid, wheel_gid):
504     """setup reasonable name mappings for sam names to unix names.
505
506     :param samdb: SamDB object.
507     :param idmap: IDmap db object.
508     :param sid: The domain sid.
509     :param domaindn: The domain DN.
510     :param root_uid: uid of the UNIX root user.
511     :param nobody_uid: uid of the UNIX nobody user.
512     :param users_gid: gid of the UNIX users group.
513     :param wheel_gid: gid of the UNIX wheel group."""
514
515     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
516     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
517     
518     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
519     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
520
521 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
522                            provision_backend, names, schema,
523                            serverrole, 
524                            erase=False):
525     """Setup the partitions for the SAM database. 
526     
527     Alternatively, provision() may call this, and then populate the database.
528     
529     :note: This will wipe the Sam Database!
530     
531     :note: This function always removes the local SAM LDB file. The erase 
532         parameter controls whether to erase the existing data, which 
533         may not be stored locally but in LDAP.
534
535     """
536     assert session_info is not None
537
538     old_partitions = None
539     new_partitions = None
540
541     # We use options=["modules:"] to stop the modules loading - we
542     # just want to wipe and re-initialise the database, not start it up
543
544     try:
545         os.unlink(samdb_path)
546     except OSError:
547         pass
548
549     samdb = Ldb(url=samdb_path, session_info=session_info, 
550                 lp=lp, options=["modules:"])
551
552     ldap_backend_line = "# No LDAP backend"
553     if provision_backend.type is not "ldb":
554         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
555
556     samdb.transaction_start()
557     try:
558         message("Setting up sam.ldb partitions and settings")
559         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
560                 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(), 
561                 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
562                 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
563                 "LDAP_BACKEND_LINE": ldap_backend_line,
564         })
565
566         
567         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
568                 "BACKEND_TYPE": provision_backend.type,
569                 "SERVER_ROLE": serverrole
570                 })
571
572         message("Setting up sam.ldb rootDSE")
573         setup_samdb_rootdse(samdb, setup_path, names)
574
575     except:
576         samdb.transaction_cancel()
577         raise
578
579     samdb.transaction_commit()
580
581         
582 def secretsdb_self_join(secretsdb, domain, 
583                         netbiosname, machinepass, domainsid=None,
584                         realm=None, dnsdomain=None,
585                         keytab_path=None, 
586                         key_version_number=1,
587                         secure_channel_type=SEC_CHAN_WKSTA):
588     """Add domain join-specific bits to a secrets database.
589     
590     :param secretsdb: Ldb Handle to the secrets database
591     :param machinepass: Machine password
592     """
593     attrs=["whenChanged",
594            "secret",
595            "priorSecret",
596            "priorChanged",
597            "krb5Keytab",
598            "privateKeytab"]
599     
600
601     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
602     msg["secureChannelType"] = str(secure_channel_type)
603     msg["flatname"] = [domain]
604     msg["objectClass"] = ["top", "primaryDomain"]
605     if realm is not None:
606       if dnsdomain is None:
607         dnsdomain = realm.lower()
608       msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
609       msg["realm"] = realm
610       msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
611       msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
612       msg["privateKeytab"] = ["secrets.keytab"];
613
614
615     msg["secret"] = [machinepass]
616     msg["samAccountName"] = ["%s$" % netbiosname]
617     msg["secureChannelType"] = [str(secure_channel_type)]
618     if domainsid is not None:
619         msg["objectSid"] = [ndr_pack(domainsid)]
620     
621     res = secretsdb.search(base="cn=Primary Domains", 
622                            attrs=attrs, 
623                            expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))), 
624                            scope=ldb.SCOPE_ONELEVEL)
625     
626     for del_msg in res:
627       if del_msg.dn is not msg.dn:
628         secretsdb.delete(del_msg.dn)
629
630     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
631
632     if len(res) == 1:
633       msg["priorSecret"] = res[0]["secret"]
634       msg["priorWhenChanged"] = res[0]["whenChanged"]
635
636       if res["privateKeytab"] is not None:
637         msg["privateKeytab"] = res[0]["privateKeytab"]
638
639       if res["krb5Keytab"] is not None:
640         msg["krb5Keytab"] = res[0]["krb5Keytab"]
641
642       for el in msg:
643         el.set_flags(ldb.FLAG_MOD_REPLACE)
644         secretsdb.modify(msg)
645     else:
646       secretsdb.add(msg)
647
648
649 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain, 
650                         dns_keytab_path, dnspass):
651     """Add DNS specific bits to a secrets database.
652     
653     :param secretsdb: Ldb Handle to the secrets database
654     :param setup_path: Setup path function
655     :param machinepass: Machine password
656     """
657     setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { 
658             "REALM": realm,
659             "DNSDOMAIN": dnsdomain,
660             "DNS_KEYTAB": dns_keytab_path,
661             "DNSPASS_B64": b64encode(dnspass),
662             })
663
664
665 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
666     """Setup the secrets database.
667
668     :param path: Path to the secrets database.
669     :param setup_path: Get the path to a setup file.
670     :param session_info: Session info.
671     :param credentials: Credentials
672     :param lp: Loadparm context
673     :return: LDB handle for the created secrets database
674     """
675     if os.path.exists(path):
676         os.unlink(path)
677     secrets_ldb = Ldb(path, session_info=session_info, 
678                       lp=lp)
679     secrets_ldb.erase()
680     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
681     secrets_ldb = Ldb(path, session_info=session_info, 
682                       lp=lp)
683     secrets_ldb.transaction_start()
684     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
685
686     if backend_credentials is not None and backend_credentials.authentication_requested():
687         if backend_credentials.get_bind_dn() is not None:
688             setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
689                     "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
690                     "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
691                     })
692         else:
693             setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
694                     "LDAPADMINUSER": backend_credentials.get_username(),
695                     "LDAPADMINREALM": backend_credentials.get_realm(),
696                     "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
697                     })
698
699     return secrets_ldb
700
701 def setup_privileges(path, setup_path, session_info, lp):
702     """Setup the privileges database.
703
704     :param path: Path to the privileges database.
705     :param setup_path: Get the path to a setup file.
706     :param session_info: Session info.
707     :param credentials: Credentials
708     :param lp: Loadparm context
709     :return: LDB handle for the created secrets database
710     """
711     if os.path.exists(path):
712         os.unlink(path)
713     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
714     privilege_ldb.erase()
715     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
716
717
718 def setup_registry(path, setup_path, session_info, lp):
719     """Setup the registry.
720     
721     :param path: Path to the registry database
722     :param setup_path: Function that returns the path to a setup.
723     :param session_info: Session information
724     :param credentials: Credentials
725     :param lp: Loadparm context
726     """
727     reg = registry.Registry()
728     hive = registry.open_ldb(path, session_info=session_info, 
729                          lp_ctx=lp)
730     reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
731     provision_reg = setup_path("provision.reg")
732     assert os.path.exists(provision_reg)
733     reg.diff_apply(provision_reg)
734
735
736 def setup_idmapdb(path, setup_path, session_info, lp):
737     """Setup the idmap database.
738
739     :param path: path to the idmap database
740     :param setup_path: Function that returns a path to a setup file
741     :param session_info: Session information
742     :param credentials: Credentials
743     :param lp: Loadparm context
744     """
745     if os.path.exists(path):
746         os.unlink(path)
747
748     idmap_ldb = IDmapDB(path, session_info=session_info,
749                         lp=lp)
750
751     idmap_ldb.erase()
752     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
753     return idmap_ldb
754
755
756 def setup_samdb_rootdse(samdb, setup_path, names):
757     """Setup the SamDB rootdse.
758
759     :param samdb: Sam Database handle
760     :param setup_path: Obtain setup path
761     """
762     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
763         "SCHEMADN": names.schemadn, 
764         "NETBIOSNAME": names.netbiosname,
765         "DNSDOMAIN": names.dnsdomain,
766         "REALM": names.realm,
767         "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
768         "DOMAINDN": names.domaindn,
769         "ROOTDN": names.rootdn,
770         "CONFIGDN": names.configdn,
771         "SERVERDN": names.serverdn,
772         })
773         
774
775 def setup_self_join(samdb, names,
776                     machinepass, dnspass, 
777                     domainsid, invocationid, setup_path,
778                     policyguid, policyguid_dc, domainControllerFunctionality,
779                     ntdsguid):
780     """Join a host to its own domain."""
781     assert isinstance(invocationid, str)
782     if ntdsguid is not None:
783         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
784     else:
785         ntdsguid_line = ""
786     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
787               "CONFIGDN": names.configdn, 
788               "SCHEMADN": names.schemadn,
789               "DOMAINDN": names.domaindn,
790               "SERVERDN": names.serverdn,
791               "INVOCATIONID": invocationid,
792               "NETBIOSNAME": names.netbiosname,
793               "DEFAULTSITE": names.sitename,
794               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
795               "MACHINEPASS_B64": b64encode(machinepass),
796               "REALM": names.realm,
797               "DOMAIN": names.domain,
798               "DOMAINSID": str(domainsid),
799               "DNSDOMAIN": names.dnsdomain,
800               "SAMBA_VERSION_STRING": version,
801               "NTDSGUID": ntdsguid_line,
802               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
803
804     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
805               "POLICYGUID": policyguid,
806               "POLICYGUID_DC": policyguid_dc,
807               "DNSDOMAIN": names.dnsdomain,
808               "DOMAINSID": str(domainsid),
809               "DOMAINDN": names.domaindn})
810     
811     # add the NTDSGUID based SPNs
812     ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
813     names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
814                                      expression="", scope=ldb.SCOPE_BASE)
815     assert isinstance(names.ntdsguid, str)
816
817     # Setup fSMORoleOwner entries to point at the newly created DC entry
818     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
819               "DOMAIN": names.domain,
820               "DNSDOMAIN": names.dnsdomain,
821               "DOMAINDN": names.domaindn,
822               "CONFIGDN": names.configdn,
823               "SCHEMADN": names.schemadn, 
824               "DEFAULTSITE": names.sitename,
825               "SERVERDN": names.serverdn,
826               "NETBIOSNAME": names.netbiosname,
827               "NTDSGUID": names.ntdsguid,
828               "DNSPASS_B64": b64encode(dnspass),
829               })
830
831
832 def setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid):
833     policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
834                                "{" + policyguid + "}")
835     os.makedirs(policy_path, 0755)
836     open(os.path.join(policy_path, "GPT.INI"), 'w').write(
837                       "[General]\r\nVersion=65543")
838     os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
839     os.makedirs(os.path.join(policy_path, "USER"), 0755)
840
841     policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
842                                   "{" + policyguid_dc + "}")
843     os.makedirs(policy_path_dc, 0755)
844     open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
845                       "[General]\r\nVersion=2")
846     os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
847     os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
848
849
850 def setup_samdb(path, setup_path, session_info, provision_backend, lp, 
851                 names, message, 
852                 domainsid, domainguid, policyguid, policyguid_dc,
853                 fill, adminpass, krbtgtpass, 
854                 machinepass, invocationid, dnspass, ntdsguid,
855                 serverrole, dom_for_fun_level=None,
856                 schema=None):
857     """Setup a complete SAM Database.
858     
859     :note: This will wipe the main SAM database file!
860     """
861
862     # ATTENTION: Do NOT change these default values without discussion with the
863     # team and/or release manager. They have a big impact on the whole program!
864     domainControllerFunctionality = DS_DC_FUNCTION_2008
865
866     if dom_for_fun_level is None:
867         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
868     if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
869         message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
870
871     if dom_for_fun_level > domainControllerFunctionality:
872         raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008). This won't work!")
873
874     domainFunctionality = dom_for_fun_level
875     forestFunctionality = dom_for_fun_level
876
877     # Also wipes the database
878     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
879                            provision_backend=provision_backend, session_info=session_info,
880                            names=names, 
881                            serverrole=serverrole, schema=schema)
882
883     if (schema == None):
884         schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
885
886     # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
887     samdb = Ldb(session_info=session_info, 
888                 credentials=provision_backend.credentials, lp=lp)
889
890     message("Pre-loading the Samba 4 and AD schema")
891
892     # Load the schema from the one we computed earlier
893     samdb.set_schema_from_ldb(schema.ldb)
894
895     # And now we can connect to the DB - the schema won't be loaded from the DB
896     samdb.connect(path)
897
898     if fill == FILL_DRS:
899         return samdb
900         
901     samdb.transaction_start()
902     try:
903         # Set the domain functionality levels onto the database.
904         # Various module (the password_hash module in particular) need
905         # to know what level of AD we are emulating.
906
907         # These will be fixed into the database via the database
908         # modifictions below, but we need them set from the start.
909         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
910         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
911         samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
912
913         samdb.set_domain_sid(str(domainsid))
914         samdb.set_invocation_id(invocationid)
915
916         message("Adding DomainDN: %s" % names.domaindn)
917
918 #impersonate domain admin
919         admin_session_info = admin_session(lp, str(domainsid))
920         samdb.set_session_info(admin_session_info)
921         if domainguid is not None:
922             domainguid_line = "objectGUID: %s\n-" % domainguid
923         else:
924             domainguid_line = ""
925
926         descr = b64encode(get_domain_descriptor(domainsid))
927         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
928                 "DOMAINDN": names.domaindn,
929                 "DOMAINGUID": domainguid_line,
930                 "DESCRIPTOR": descr
931                 })
932
933
934         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
935             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
936             "DOMAINSID": str(domainsid),
937             "SCHEMADN": names.schemadn, 
938             "NETBIOSNAME": names.netbiosname,
939             "DEFAULTSITE": names.sitename,
940             "CONFIGDN": names.configdn,
941             "SERVERDN": names.serverdn,
942             "POLICYGUID": policyguid,
943             "DOMAINDN": names.domaindn,
944             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
945             "SAMBA_VERSION_STRING": version
946             })
947
948         message("Adding configuration container")
949         descr = b64encode(get_config_descriptor(domainsid))
950         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
951             "CONFIGDN": names.configdn, 
952             "DESCRIPTOR": descr,
953             })
954
955         # The LDIF here was created when the Schema object was constructed
956         message("Setting up sam.ldb schema")
957         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
958         samdb.modify_ldif(schema.schema_dn_modify)
959         samdb.write_prefixes_from_schema()
960         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
961         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
962                        {"SCHEMADN": names.schemadn})
963
964         message("Reopening sam.ldb with new schema");
965         samdb.transaction_commit()
966         samdb = Ldb(session_info=admin_session_info,
967                     credentials=provision_backend.credentials, lp=lp)
968         samdb.connect(path)
969         samdb.transaction_start()
970         samdb.set_invocation_id(invocationid)
971
972         message("Setting up sam.ldb configuration data")
973         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
974             "CONFIGDN": names.configdn,
975             "NETBIOSNAME": names.netbiosname,
976             "DEFAULTSITE": names.sitename,
977             "DNSDOMAIN": names.dnsdomain,
978             "DOMAIN": names.domain,
979             "SCHEMADN": names.schemadn,
980             "DOMAINDN": names.domaindn,
981             "SERVERDN": names.serverdn,
982             "FOREST_FUNCTIONALALITY": str(forestFunctionality)
983             })
984
985         message("Setting up display specifiers")
986         display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
987         display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
988         check_all_substituted(display_specifiers_ldif)
989         samdb.add_ldif(display_specifiers_ldif)
990
991         message("Adding users container")
992         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
993                 "DOMAINDN": names.domaindn})
994         message("Modifying users container")
995         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
996                 "DOMAINDN": names.domaindn})
997         message("Adding computers container")
998         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
999                 "DOMAINDN": names.domaindn})
1000         message("Modifying computers container")
1001         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1002                 "DOMAINDN": names.domaindn})
1003         message("Setting up sam.ldb data")
1004         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1005             "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1006             "DOMAINDN": names.domaindn,
1007             "NETBIOSNAME": names.netbiosname,
1008             "DEFAULTSITE": names.sitename,
1009             "CONFIGDN": names.configdn,
1010             "SERVERDN": names.serverdn,
1011             "POLICYGUID_DC": policyguid_dc
1012             })
1013
1014         setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1015                 "DOMAINDN": names.domaindn})
1016
1017         setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1018                 "CONFIGDN": names.configdn,
1019                 "SCHEMADN": names.schemadn})
1020         if fill == FILL_FULL:
1021             message("Setting up sam.ldb users and groups")
1022             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1023                 "DOMAINDN": names.domaindn,
1024                 "DOMAINSID": str(domainsid),
1025                 "CONFIGDN": names.configdn,
1026                 "ADMINPASS_B64": b64encode(adminpass),
1027                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1028                 })
1029
1030             message("Setting up self join")
1031             setup_self_join(samdb, names=names, invocationid=invocationid,
1032                             dnspass=dnspass,
1033                             machinepass=machinepass,
1034                             domainsid=domainsid, policyguid=policyguid,
1035                             policyguid_dc=policyguid_dc,
1036                             setup_path=setup_path,
1037                             domainControllerFunctionality=domainControllerFunctionality,
1038                             ntdsguid=ntdsguid)
1039
1040             ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1041             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1042                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1043             assert isinstance(names.ntdsguid, str)
1044
1045     except:
1046         samdb.transaction_cancel()
1047         raise
1048
1049     samdb.transaction_commit()
1050     return samdb
1051
1052
1053 FILL_FULL = "FULL"
1054 FILL_NT4SYNC = "NT4SYNC"
1055 FILL_DRS = "DRS"
1056 SYSVOL_ACL = "O:${DOMAINSID}-500G:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;S-1-5-32-549)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1057 POLICIES_ACL = "O:${DOMAINSID}-500G:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;S-1-5-32-549)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;${DOMAINSID}-520)"
1058
1059 def set_gpo_acl(path,acl,lp,domsid):
1060         setntacl(lp,path,acl,domsid)
1061         for root, dirs, files in os.walk(path, topdown=False):
1062                 for name in files:
1063                         setntacl(lp,os.path.join(root, name),acl,domsid)
1064                 for name in dirs:
1065                         setntacl(lp,os.path.join(root, name),acl,domsid)
1066
1067 def setsysvolacl(samdb,names,netlogon,sysvol,gid,domainsid,lp):
1068         canchown = 1
1069         acl = SYSVOL_ACL.replace("${DOMAINSID}",str(domainsid))
1070         try:
1071                 os.chown(sysvol,-1,gid)
1072         except:
1073                 canchown = 0
1074
1075         setntacl(lp,sysvol,acl,str(domainsid))
1076         for root, dirs, files in os.walk(sysvol, topdown=False):
1077                 for name in files:
1078                         if canchown:
1079                                 os.chown(os.path.join(root, name),-1,gid)
1080                         setntacl(lp,os.path.join(root, name),acl,str(domainsid))
1081                 for name in dirs:
1082                         if canchown:
1083                                 os.chown(os.path.join(root, name),-1,gid)
1084                         setntacl(lp,os.path.join(root, name),acl,str(domainsid))
1085
1086         # Set ACL for GPO
1087         policy_path = os.path.join(sysvol, names.dnsdomain, "Policies")
1088         acl = POLICIES_ACL.replace("${DOMAINSID}",str(domainsid))
1089         set_gpo_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
1090         res = samdb.search(base="CN=Policies,CN=System,%s"%(names.domaindn),
1091                                                 attrs=["cn","nTSecurityDescriptor"],
1092                                                 expression="", scope=ldb.SCOPE_ONELEVEL)
1093         for policy in res:
1094                 acl = ndr_unpack(security.descriptor,str(policy["nTSecurityDescriptor"])).as_sddl()
1095                 policy_path = os.path.join(sysvol, names.dnsdomain, "Policies",
1096                                                                          str(policy["cn"]))
1097                 set_gpo_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
1098
1099
1100
1101 def provision(setup_dir, message, session_info, 
1102               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1103               realm=None, 
1104               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
1105               serverdn=None,
1106               domain=None, hostname=None, hostip=None, hostip6=None, 
1107               domainsid=None, adminpass=None, ldapadminpass=None, 
1108               krbtgtpass=None, domainguid=None, 
1109               policyguid=None, policyguid_dc=None, invocationid=None,
1110               machinepass=None, ntdsguid=None,
1111               dnspass=None, root=None, nobody=None, users=None, 
1112               wheel=None, backup=None, aci=None, serverrole=None,
1113               dom_for_fun_level=None,
1114               ldap_backend_extra_port=None, backend_type=None,
1115               sitename=None,
1116               ol_mmr_urls=None, ol_olc=None, 
1117               setup_ds_path=None, slapd_path=None, nosync=False,
1118               ldap_dryrun_mode=False,useeadb=False):
1119     """Provision samba4
1120     
1121     :note: caution, this wipes all existing data!
1122     """
1123
1124     def setup_path(file):
1125       return os.path.join(setup_dir, file)
1126
1127     if domainsid is None:
1128       domainsid = security.random_sid()
1129     else:
1130       domainsid = security.dom_sid(domainsid)
1131
1132     # create/adapt the group policy GUIDs
1133     if policyguid is None:
1134         policyguid = str(uuid.uuid4())
1135     policyguid = policyguid.upper()
1136     if policyguid_dc is None:
1137         policyguid_dc = str(uuid.uuid4())
1138     policyguid_dc = policyguid_dc.upper()
1139
1140     if adminpass is None:
1141         adminpass = glue.generate_random_str(12)
1142     if krbtgtpass is None:
1143         krbtgtpass = glue.generate_random_str(12)
1144     if machinepass is None:
1145         machinepass  = glue.generate_random_str(12)
1146     if dnspass is None:
1147         dnspass = glue.generate_random_str(12)
1148     if ldapadminpass is None:
1149         #Make a new, random password between Samba and it's LDAP server
1150         ldapadminpass=glue.generate_random_str(12)        
1151
1152     if backend_type is None:
1153         backend_type = "ldb"
1154
1155     sid_generator = "internal"
1156     if backend_type == "fedora-ds":
1157         sid_generator = "backend"
1158
1159     root_uid = findnss_uid([root or "root"])
1160     nobody_uid = findnss_uid([nobody or "nobody"])
1161     users_gid = findnss_gid([users or "users"])
1162     if wheel is None:
1163         wheel_gid = findnss_gid(["wheel", "adm"])
1164     else:
1165         wheel_gid = findnss_gid([wheel])
1166
1167     if targetdir is not None:
1168         if (not os.path.exists(os.path.join(targetdir, "etc"))):
1169             os.makedirs(os.path.join(targetdir, "etc"))
1170         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1171     elif smbconf is None:
1172         smbconf = param.default_path()
1173
1174     # only install a new smb.conf if there isn't one there already
1175     if not os.path.exists(smbconf):
1176         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
1177                      targetdir, sid_generator,useeadb)
1178
1179     lp = param.LoadParm()
1180     lp.load(smbconf)
1181
1182     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1183                         dnsdomain=realm, serverrole=serverrole,
1184                         domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1185                         serverdn=serverdn, sitename=sitename)
1186
1187     paths = provision_paths_from_lp(lp, names.dnsdomain)
1188
1189     if hostip is None:
1190         try:
1191             hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1192         except socket.gaierror, (socket.EAI_NODATA, msg):
1193             hostip = None
1194
1195     if hostip6 is None:
1196         try:
1197             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1198         except socket.gaierror, (socket.EAI_NODATA, msg): 
1199             hostip6 = None
1200
1201     if serverrole is None:
1202         serverrole = lp.get("server role")
1203
1204     assert serverrole in ("domain controller", "member server", "standalone")
1205     if invocationid is None:
1206         invocationid = str(uuid.uuid4())
1207
1208     if not os.path.exists(paths.private_dir):
1209         os.mkdir(paths.private_dir)
1210     if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1211         os.mkdir(os.path.join(paths.private_dir,"tls"))
1212
1213     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1214  
1215     schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1216     
1217     if backend_type == "ldb":
1218         provision_backend = LDBBackend(backend_type,
1219                                          paths=paths, setup_path=setup_path,
1220                                          lp=lp, credentials=credentials, 
1221                                          names=names,
1222                                          message=message)
1223     elif backend_type == "existing":
1224         provision_backend = ExistingBackend(backend_type,
1225                                          paths=paths, setup_path=setup_path,
1226                                          lp=lp, credentials=credentials, 
1227                                          names=names,
1228                                          message=message)
1229     elif backend_type == "fedora-ds":
1230         provision_backend = FDSBackend(backend_type,
1231                                          paths=paths, setup_path=setup_path,
1232                                          lp=lp, credentials=credentials, 
1233                                          names=names,
1234                                          message=message,
1235                                          domainsid=domainsid,
1236                                          schema=schema,
1237                                          hostname=hostname,
1238                                          ldapadminpass=ldapadminpass,
1239                                          slapd_path=slapd_path,
1240                                          ldap_backend_extra_port=ldap_backend_extra_port,
1241                                          ldap_dryrun_mode=ldap_dryrun_mode,
1242                                          root=root,
1243                                          setup_ds_path=setup_ds_path)
1244     elif backend_type == "openldap":
1245         provision_backend = OpenLDAPBackend(backend_type,
1246                                          paths=paths, setup_path=setup_path,
1247                                          lp=lp, credentials=credentials, 
1248                                          names=names,
1249                                          message=message,
1250                                          domainsid=domainsid,
1251                                          schema=schema,
1252                                          hostname=hostname,
1253                                          ldapadminpass=ldapadminpass,
1254                                          slapd_path=slapd_path,
1255                                          ldap_backend_extra_port=ldap_backend_extra_port,
1256                                          ldap_dryrun_mode=ldap_dryrun_mode,
1257                                          ol_mmr_urls=ol_mmr_urls, 
1258                                          nosync=nosync)
1259     else:
1260         raise ProvisioningError("Unknown LDAP backend type selected")
1261
1262     provision_backend.init()
1263     provision_backend.start()
1264
1265     # only install a new shares config db if there is none
1266     if not os.path.exists(paths.shareconf):
1267         message("Setting up share.ldb")
1268         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
1269                         lp=lp)
1270         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1271
1272      
1273     message("Setting up secrets.ldb")
1274     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
1275                                   session_info=session_info, 
1276                                   backend_credentials=provision_backend.secrets_credentials, lp=lp)
1277
1278     message("Setting up the registry")
1279     setup_registry(paths.hklm, setup_path, session_info, 
1280                    lp=lp)
1281
1282     message("Setting up the privileges database")
1283     setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1284
1285     message("Setting up idmap db")
1286     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1287                           lp=lp)
1288
1289     message("Setting up SAM db")
1290     samdb = setup_samdb(paths.samdb, setup_path, session_info, 
1291                         provision_backend, lp, names,
1292                         message, 
1293                         domainsid=domainsid, 
1294                         schema=schema, domainguid=domainguid,
1295                         policyguid=policyguid, policyguid_dc=policyguid_dc,
1296                         fill=samdb_fill, 
1297                         adminpass=adminpass, krbtgtpass=krbtgtpass,
1298                         invocationid=invocationid, 
1299                         machinepass=machinepass, dnspass=dnspass, 
1300                         ntdsguid=ntdsguid, serverrole=serverrole,
1301                         dom_for_fun_level=dom_for_fun_level)
1302
1303     if serverrole == "domain controller":
1304         if paths.netlogon is None:
1305             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1306             message("Please either remove %s or see the template at %s" % 
1307                     ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1308             assert(paths.netlogon is not None)
1309
1310         if paths.sysvol is None:
1311             message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1312             message("Please either remove %s or see the template at %s" % 
1313                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
1314             assert(paths.sysvol is not None)            
1315             
1316
1317         if not os.path.isdir(paths.netlogon):
1318             os.makedirs(paths.netlogon, 0755)
1319
1320     if samdb_fill == FILL_FULL:
1321         setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1322                             root_uid=root_uid, nobody_uid=nobody_uid,
1323                             users_gid=users_gid, wheel_gid=wheel_gid)
1324
1325         if serverrole == "domain controller":
1326             # Set up group policies (domain policy and domain controller policy)
1327             setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid)
1328             setsysvolacl(samdb,names,paths.netlogon,paths.sysvol,wheel_gid,domainsid,lp)
1329
1330         message("Setting up sam.ldb rootDSE marking as synchronized")
1331         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1332
1333         secretsdb_self_join(secrets_ldb, domain=names.domain,
1334                             realm=names.realm,
1335                             dnsdomain=names.dnsdomain,
1336                             netbiosname=names.netbiosname,
1337                             domainsid=domainsid, 
1338                             machinepass=machinepass,
1339                             secure_channel_type=SEC_CHAN_BDC)
1340
1341         if serverrole == "domain controller":
1342             secretsdb_setup_dns(secrets_ldb, setup_path, 
1343                                 realm=names.realm, dnsdomain=names.dnsdomain,
1344                                 dns_keytab_path=paths.dns_keytab,
1345                                 dnspass=dnspass)
1346
1347             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1348             assert isinstance(domainguid, str)
1349
1350             # Only make a zone file on the first DC, it should be replicated
1351             # with DNS replication
1352             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1353                              hostip=hostip,
1354                              hostip6=hostip6, hostname=names.hostname,
1355                              realm=names.realm,
1356                              domainguid=domainguid, ntdsguid=names.ntdsguid)
1357
1358             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1359                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1360
1361             create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1362                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1363                               keytab_name=paths.dns_keytab)
1364             message("See %s for an example configuration include file for BIND" % paths.namedconf)
1365             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1366
1367             create_krb5_conf(paths.krb5conf, setup_path,
1368                              dnsdomain=names.dnsdomain, hostname=names.hostname,
1369                              realm=names.realm)
1370             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1371
1372     provision_backend.post_setup()
1373     provision_backend.shutdown()
1374     
1375     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
1376                                ldapi_url)
1377
1378     #Now commit the secrets.ldb to disk
1379     secrets_ldb.transaction_commit()
1380
1381     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1382
1383     message("Once the above files are installed, your Samba4 server will be ready to use")
1384     message("Server Role:           %s" % serverrole)
1385     message("Hostname:              %s" % names.hostname)
1386     message("NetBIOS Domain:        %s" % names.domain)
1387     message("DNS Domain:            %s" % names.dnsdomain)
1388     message("DOMAIN SID:            %s" % str(domainsid))
1389     if samdb_fill == FILL_FULL:
1390         message("Admin password:        %s" % adminpass)
1391     if provision_backend.type is not "ldb":
1392         if provision_backend.credentials.get_bind_dn() is not None:
1393             message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1394         else:
1395             message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
1396
1397         message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
1398
1399         if provision_backend.slapd_command_escaped is not None:
1400             # now display slapd_command_file.txt to show how slapd must be started next time
1401             message("Use later the following commandline to start slapd, then Samba:")
1402             message(provision_backend.slapd_command_escaped)
1403             message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1404
1405
1406     result = ProvisionResult()
1407     result.domaindn = domaindn
1408     result.paths = paths
1409     result.lp = lp
1410     result.samdb = samdb
1411     return result
1412
1413
1414
1415 def provision_become_dc(setup_dir=None,
1416                         smbconf=None, targetdir=None, realm=None, 
1417                         rootdn=None, domaindn=None, schemadn=None,
1418                         configdn=None, serverdn=None,
1419                         domain=None, hostname=None, domainsid=None, 
1420                         adminpass=None, krbtgtpass=None, domainguid=None, 
1421                         policyguid=None, policyguid_dc=None, invocationid=None,
1422                         machinepass=None, 
1423                         dnspass=None, root=None, nobody=None, users=None, 
1424                         wheel=None, backup=None, serverrole=None, 
1425                         ldap_backend=None, ldap_backend_type=None,
1426                         sitename=None, debuglevel=1):
1427
1428     def message(text):
1429         """print a message if quiet is not set."""
1430         print text
1431
1432     glue.set_debug_level(debuglevel)
1433
1434     return provision(setup_dir, message, system_session(), None,
1435               smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1436               realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1437               configdn=configdn, serverdn=serverdn, domain=domain,
1438               hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1439               machinepass=machinepass, serverrole="domain controller",
1440               sitename=sitename)
1441
1442
1443 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1444     """Create a PHP LDAP admin configuration file.
1445
1446     :param path: Path to write the configuration to.
1447     :param setup_path: Function to generate setup paths.
1448     """
1449     setup_file(setup_path("phpldapadmin-config.php"), path, 
1450             {"S4_LDAPI_URI": ldapi_uri})
1451
1452
1453 def create_zone_file(path, setup_path, dnsdomain, 
1454                      hostip, hostip6, hostname, realm, domainguid,
1455                      ntdsguid):
1456     """Write out a DNS zone file, from the info in the current database.
1457
1458     :param path: Path of the new zone file.
1459     :param setup_path: Setup path function.
1460     :param dnsdomain: DNS Domain name
1461     :param domaindn: DN of the Domain
1462     :param hostip: Local IPv4 IP
1463     :param hostip6: Local IPv6 IP
1464     :param hostname: Local hostname
1465     :param realm: Realm name
1466     :param domainguid: GUID of the domain.
1467     :param ntdsguid: GUID of the hosts nTDSDSA record.
1468     """
1469     assert isinstance(domainguid, str)
1470
1471     if hostip6 is not None:
1472         hostip6_base_line = "            IN AAAA    " + hostip6
1473         hostip6_host_line = hostname + "        IN AAAA    " + hostip6
1474     else:
1475         hostip6_base_line = ""
1476         hostip6_host_line = ""
1477
1478     if hostip is not None:
1479         hostip_base_line = "            IN A    " + hostip
1480         hostip_host_line = hostname + "        IN A    " + hostip
1481     else:
1482         hostip_base_line = ""
1483         hostip_host_line = ""
1484
1485     setup_file(setup_path("provision.zone"), path, {
1486             "HOSTNAME": hostname,
1487             "DNSDOMAIN": dnsdomain,
1488             "REALM": realm,
1489             "HOSTIP_BASE_LINE": hostip_base_line,
1490             "HOSTIP_HOST_LINE": hostip_host_line,
1491             "DOMAINGUID": domainguid,
1492             "DATESTRING": time.strftime("%Y%m%d%H"),
1493             "DEFAULTSITE": DEFAULTSITE,
1494             "NTDSGUID": ntdsguid,
1495             "HOSTIP6_BASE_LINE": hostip6_base_line,
1496             "HOSTIP6_HOST_LINE": hostip6_host_line,
1497         })
1498
1499
1500 def create_named_conf(path, setup_path, realm, dnsdomain,
1501                       private_dir):
1502     """Write out a file containing zone statements suitable for inclusion in a
1503     named.conf file (including GSS-TSIG configuration).
1504     
1505     :param path: Path of the new named.conf file.
1506     :param setup_path: Setup path function.
1507     :param realm: Realm name
1508     :param dnsdomain: DNS Domain name
1509     :param private_dir: Path to private directory
1510     :param keytab_name: File name of DNS keytab file
1511     """
1512
1513     setup_file(setup_path("named.conf"), path, {
1514             "DNSDOMAIN": dnsdomain,
1515             "REALM": realm,
1516             "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1517             "PRIVATE_DIR": private_dir
1518             })
1519
1520 def create_named_txt(path, setup_path, realm, dnsdomain,
1521                       private_dir, keytab_name):
1522     """Write out a file containing zone statements suitable for inclusion in a
1523     named.conf file (including GSS-TSIG configuration).
1524     
1525     :param path: Path of the new named.conf file.
1526     :param setup_path: Setup path function.
1527     :param realm: Realm name
1528     :param dnsdomain: DNS Domain name
1529     :param private_dir: Path to private directory
1530     :param keytab_name: File name of DNS keytab file
1531     """
1532
1533     setup_file(setup_path("named.txt"), path, {
1534             "DNSDOMAIN": dnsdomain,
1535             "REALM": realm,
1536             "DNS_KEYTAB": keytab_name,
1537             "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1538             "PRIVATE_DIR": private_dir
1539         })
1540
1541 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1542     """Write out a file containing zone statements suitable for inclusion in a
1543     named.conf file (including GSS-TSIG configuration).
1544     
1545     :param path: Path of the new named.conf file.
1546     :param setup_path: Setup path function.
1547     :param dnsdomain: DNS Domain name
1548     :param hostname: Local hostname
1549     :param realm: Realm name
1550     """
1551
1552     setup_file(setup_path("krb5.conf"), path, {
1553             "DNSDOMAIN": dnsdomain,
1554             "HOSTNAME": hostname,
1555             "REALM": realm,
1556         })
1557
1558