s4:provision: add the possibility to provision "server services" in smb.conf
[nivanova/samba-autobuild/.git] / source4 / scripting / python / samba / provision / __init__.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-2010
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 __docformat__ = "restructuredText"
29
30 from base64 import b64encode
31 import os
32 import re
33 import pwd
34 import grp
35 import logging
36 import time
37 import uuid
38 import socket
39 import urllib
40 import shutil
41 import string
42
43 import ldb
44
45 from samba.auth import system_session, admin_session
46 import samba
47 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
48 from samba import (
49     Ldb,
50     check_all_substituted,
51     setup_file,
52     substitute_var,
53     valid_netbios_name,
54     version,
55     )
56 from samba.dcerpc import security, misc
57 from samba.dcerpc.misc import (
58     SEC_CHAN_BDC,
59     SEC_CHAN_WKSTA,
60     )
61 from samba.dsdb import (
62     DS_DOMAIN_FUNCTION_2003,
63     DS_DOMAIN_FUNCTION_2008_R2,
64     ENC_ALL_TYPES,
65     )
66 from samba.idmap import IDmapDB
67 from samba.ms_display_specifiers import read_ms_ldif
68 from samba.ntacls import setntacl, dsacl2fsacl
69 from samba.ndr import ndr_pack, ndr_unpack
70 from samba.provision.backend import (
71     ExistingBackend,
72     FDSBackend,
73     LDBBackend,
74     OpenLDAPBackend,
75     )
76 from samba.provision.descriptor import (
77     get_config_descriptor,
78     get_domain_descriptor
79     )
80 from samba.provision.common import (
81     setup_path,
82     setup_add_ldif,
83     setup_modify_ldif,
84     setup_ldb
85     )
86 from samba.provision.sambadns import (
87     setup_ad_dns,
88     create_dns_update_list
89     )
90
91 import samba.param
92 import samba.registry
93 from samba.schema import Schema
94 from samba.samdb import SamDB
95 from samba.dbchecker import dbcheck
96
97
98 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
99 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
100 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
101 DEFAULTSITE = "Default-First-Site-Name"
102 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
103
104
105 class ProvisionPaths(object):
106
107     def __init__(self):
108         self.shareconf = None
109         self.hklm = None
110         self.hkcu = None
111         self.hkcr = None
112         self.hku = None
113         self.hkpd = None
114         self.hkpt = None
115         self.samdb = None
116         self.idmapdb = None
117         self.secrets = None
118         self.keytab = None
119         self.dns_keytab = None
120         self.dns = None
121         self.winsdb = None
122         self.private_dir = None
123
124
125 class ProvisionNames(object):
126
127     def __init__(self):
128         self.rootdn = None
129         self.domaindn = None
130         self.configdn = None
131         self.schemadn = None
132         self.ldapmanagerdn = None
133         self.dnsdomain = None
134         self.realm = None
135         self.netbiosname = None
136         self.domain = None
137         self.hostname = None
138         self.sitename = None
139         self.smbconf = None
140
141 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
142     """Get key provision parameters (realm, domain, ...) from a given provision
143
144     :param samdb: An LDB object connected to the sam.ldb file
145     :param secretsdb: An LDB object connected to the secrets.ldb file
146     :param idmapdb: An LDB object connected to the idmap.ldb file
147     :param paths: A list of path to provision object
148     :param smbconf: Path to the smb.conf file
149     :param lp: A LoadParm object
150     :return: A list of key provision parameters
151     """
152     names = ProvisionNames()
153     names.adminpass = None
154
155     # NT domain, kerberos realm, root dn, domain dn, domain dns name
156     names.domain = string.upper(lp.get("workgroup"))
157     names.realm = lp.get("realm")
158     names.dnsdomain = names.realm.lower()
159     basedn = samba.dn_from_dns_name(names.dnsdomain)
160     names.realm = string.upper(names.realm)
161     # netbiosname
162     # Get the netbiosname first (could be obtained from smb.conf in theory)
163     res = secretsdb.search(expression="(flatname=%s)" %
164                             names.domain,base="CN=Primary Domains",
165                             scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
166     names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
167
168     names.smbconf = smbconf
169
170     # That's a bit simplistic but it's ok as long as we have only 3
171     # partitions
172     current = samdb.search(expression="(objectClass=*)",
173         base="", scope=ldb.SCOPE_BASE,
174         attrs=["defaultNamingContext", "schemaNamingContext",
175                "configurationNamingContext","rootDomainNamingContext"])
176
177     names.configdn = current[0]["configurationNamingContext"]
178     configdn = str(names.configdn)
179     names.schemadn = current[0]["schemaNamingContext"]
180     if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
181                                        current[0]["defaultNamingContext"][0]))):
182         raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
183                                  "is not the same ..." % (paths.samdb,
184                                     str(current[0]["defaultNamingContext"][0]),
185                                     paths.smbconf, basedn)))
186
187     names.domaindn=current[0]["defaultNamingContext"]
188     names.rootdn=current[0]["rootDomainNamingContext"]
189     # default site name
190     res3 = samdb.search(expression="(objectClass=site)",
191         base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
192     names.sitename = str(res3[0]["cn"])
193
194     # dns hostname and server dn
195     res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
196                             base="OU=Domain Controllers,%s" % basedn,
197                             scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
198     names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
199
200     server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
201                                 attrs=[], base=configdn)
202     names.serverdn = server_res[0].dn
203
204     # invocation id/objectguid
205     res5 = samdb.search(expression="(objectClass=*)",
206             base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
207             attrs=["invocationID", "objectGUID"])
208     names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
209     names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
210
211     # domain guid/sid
212     res6 = samdb.search(expression="(objectClass=*)", base=basedn,
213             scope=ldb.SCOPE_BASE, attrs=["objectGUID",
214                 "objectSid","msDS-Behavior-Version" ])
215     names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
216     names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
217     if res6[0].get("msDS-Behavior-Version") is None or \
218         int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
219         names.domainlevel = DS_DOMAIN_FUNCTION_2000
220     else:
221         names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
222
223     # policy guid
224     res7 = samdb.search(expression="(displayName=Default Domain Policy)",
225                         base="CN=Policies,CN=System," + basedn,
226                         scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
227     names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
228     # dc policy guid
229     res8 = samdb.search(expression="(displayName=Default Domain Controllers"
230                                    " Policy)",
231                             base="CN=Policies,CN=System," + basedn,
232                             scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
233     if len(res8) == 1:
234         names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
235     else:
236         names.policyid_dc = None
237     res9 = idmapdb.search(expression="(cn=%s)" %
238                             (security.SID_BUILTIN_ADMINISTRATORS),
239                             attrs=["xidNumber"])
240     if len(res9) == 1:
241         names.wheel_gid = res9[0]["xidNumber"]
242     else:
243         raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
244     return names
245
246 def update_provision_usn(samdb, low, high, id, replace=False):
247     """Update the field provisionUSN in sam.ldb
248
249     This field is used to track range of USN modified by provision and
250     upgradeprovision.
251     This value is used afterward by next provision to figure out if
252     the field have been modified since last provision.
253
254     :param samdb: An LDB object connect to sam.ldb
255     :param low: The lowest USN modified by this upgrade
256     :param high: The highest USN modified by this upgrade
257     :param id: The invocation id of the samba's dc
258     :param replace: A boolean indicating if the range should replace any
259                     existing one or appended (default)
260     """
261
262     tab = []
263     if not replace:
264         entry = samdb.search(base="@PROVISION",
265                              scope=ldb.SCOPE_BASE,
266                              attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
267         for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
268             if not re.search(';', e):
269                 e = "%s;%s" % (e, id)
270             tab.append(str(e))
271
272     tab.append("%s-%s;%s" % (low, high, id))
273     delta = ldb.Message()
274     delta.dn = ldb.Dn(samdb, "@PROVISION")
275     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
276         ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
277     entry = samdb.search(expression='provisionnerID=*',
278                          base="@PROVISION", scope=ldb.SCOPE_BASE,
279                          attrs=["provisionnerID"])
280     if len(entry) == 0 or len(entry[0]) == 0:
281         delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
282     samdb.modify(delta)
283
284
285 def set_provision_usn(samdb, low, high, id):
286     """Set the field provisionUSN in sam.ldb
287     This field is used to track range of USN modified by provision and
288     upgradeprovision.
289     This value is used afterward by next provision to figure out if
290     the field have been modified since last provision.
291
292     :param samdb: An LDB object connect to sam.ldb
293     :param low: The lowest USN modified by this upgrade
294     :param high: The highest USN modified by this upgrade
295     :param id: The invocationId of the provision"""
296
297     tab = []
298     tab.append("%s-%s;%s" % (low, high, id))
299
300     delta = ldb.Message()
301     delta.dn = ldb.Dn(samdb, "@PROVISION")
302     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
303         ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
304     samdb.add(delta)
305
306
307 def get_max_usn(samdb,basedn):
308     """ This function return the biggest USN present in the provision
309
310     :param samdb: A LDB object pointing to the sam.ldb
311     :param basedn: A string containing the base DN of the provision
312                     (ie. DC=foo, DC=bar)
313     :return: The biggest USN in the provision"""
314
315     res = samdb.search(expression="objectClass=*",base=basedn,
316                          scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
317                          controls=["search_options:1:2",
318                                    "server_sort:1:1:uSNChanged",
319                                    "paged_results:1:1"])
320     return res[0]["uSNChanged"]
321
322
323 def get_last_provision_usn(sam):
324     """Get USNs ranges modified by a provision or an upgradeprovision
325
326     :param sam: An LDB object pointing to the sam.ldb
327     :return: a dictionnary which keys are invocation id and values are an array
328              of integer representing the different ranges
329     """
330     try:
331         entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
332                            base="@PROVISION", scope=ldb.SCOPE_BASE,
333                            attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
334     except ldb.LdbError, (ecode, emsg):
335         if ecode == ldb.ERR_NO_SUCH_OBJECT:
336             return None
337         raise
338     if len(entry):
339         myids = []
340         range = {}
341         p = re.compile(r'-')
342         if entry[0].get("provisionnerID"):
343             for e in entry[0]["provisionnerID"]:
344                 myids.append(str(e))
345         for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
346             tab1 = str(r).split(';')
347             if len(tab1) == 2:
348                 id = tab1[1]
349             else:
350                 id = "default"
351             if (len(myids) > 0 and id not in myids):
352                 continue
353             tab2 = p.split(tab1[0])
354             if range.get(id) == None:
355                 range[id] = []
356             range[id].append(tab2[0])
357             range[id].append(tab2[1])
358         return range
359     else:
360         return None
361
362
363 class ProvisionResult(object):
364
365     def __init__(self):
366         self.paths = None
367         self.domaindn = None
368         self.lp = None
369         self.samdb = None
370         self.idmap = None
371         self.names = None
372
373
374 def check_install(lp, session_info, credentials):
375     """Check whether the current install seems ok.
376
377     :param lp: Loadparm context
378     :param session_info: Session information
379     :param credentials: Credentials
380     """
381     if lp.get("realm") == "":
382         raise Exception("Realm empty")
383     samdb = Ldb(lp.samdb_url(), session_info=session_info,
384             credentials=credentials, lp=lp)
385     if len(samdb.search("(cn=Administrator)")) != 1:
386         raise ProvisioningError("No administrator account found")
387
388
389 def findnss(nssfn, names):
390     """Find a user or group from a list of possibilities.
391
392     :param nssfn: NSS Function to try (should raise KeyError if not found)
393     :param names: Names to check.
394     :return: Value return by first names list.
395     """
396     for name in names:
397         try:
398             return nssfn(name)
399         except KeyError:
400             pass
401     raise KeyError("Unable to find user/group in %r" % names)
402
403
404 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
405 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
406
407
408 def provision_paths_from_lp(lp, dnsdomain):
409     """Set the default paths for provisioning.
410
411     :param lp: Loadparm context.
412     :param dnsdomain: DNS Domain name
413     """
414     paths = ProvisionPaths()
415     paths.private_dir = lp.get("private dir")
416
417     # This is stored without path prefix for the "privateKeytab" attribute in
418     # "secrets_dns.ldif".
419     paths.dns_keytab = "dns.keytab"
420     paths.keytab = "secrets.keytab"
421
422     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
423     paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
424     paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
425     paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
426     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
427     paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
428     paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
429     paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
430     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
431     paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
432     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
433     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
434     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
435     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
436     paths.phpldapadminconfig = os.path.join(paths.private_dir,
437                                             "phpldapadmin-config.php")
438     paths.hklm = "hklm.ldb"
439     paths.hkcr = "hkcr.ldb"
440     paths.hkcu = "hkcu.ldb"
441     paths.hku = "hku.ldb"
442     paths.hkpd = "hkpd.ldb"
443     paths.hkpt = "hkpt.ldb"
444     paths.sysvol = lp.get("path", "sysvol")
445     paths.netlogon = lp.get("path", "netlogon")
446     paths.smbconf = lp.configfile
447     return paths
448
449
450 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
451                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
452                 schemadn=None, serverdn=None, sitename=None):
453     """Guess configuration settings to use."""
454
455     if hostname is None:
456         hostname = socket.gethostname().split(".")[0]
457
458     netbiosname = lp.get("netbios name")
459     if netbiosname is None:
460         netbiosname = hostname
461         # remove forbidden chars
462         newnbname = ""
463         for x in netbiosname:
464             if x.isalnum() or x in VALID_NETBIOS_CHARS:
465                 newnbname = "%s%c" % (newnbname, x)
466         # force the length to be <16
467         netbiosname = newnbname[0:15]
468     assert netbiosname is not None
469     netbiosname = netbiosname.upper()
470     if not valid_netbios_name(netbiosname):
471         raise InvalidNetbiosName(netbiosname)
472
473     if dnsdomain is None:
474         dnsdomain = lp.get("realm")
475         if dnsdomain is None or dnsdomain == "":
476             raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
477
478     dnsdomain = dnsdomain.lower()
479
480     if serverrole is None:
481         serverrole = lp.get("server role")
482         if serverrole is None:
483             raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
484
485     serverrole = serverrole.lower()
486
487     realm = dnsdomain.upper()
488
489     if lp.get("realm") == "":
490         raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s.  Please remove the smb.conf file and let provision generate it" % lp.configfile)
491
492     if lp.get("realm").upper() != realm:
493         raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
494
495     if lp.get("server role").lower() != serverrole:
496         raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), serverrole, lp.configfile))
497
498     if serverrole == "domain controller":
499         if domain is None:
500             # This will, for better or worse, default to 'WORKGROUP'
501             domain = lp.get("workgroup")
502         domain = domain.upper()
503
504         if lp.get("workgroup").upper() != domain:
505             raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!  Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
506
507         if domaindn is None:
508             domaindn = samba.dn_from_dns_name(dnsdomain)
509
510         if domain == netbiosname:
511             raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
512     else:
513         domain = netbiosname
514         if domaindn is None:
515             domaindn = "DC=" + netbiosname
516
517     if not valid_netbios_name(domain):
518         raise InvalidNetbiosName(domain)
519
520     if hostname.upper() == realm:
521         raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
522     if netbiosname.upper() == realm:
523         raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
524     if domain == realm:
525         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
526
527     if rootdn is None:
528        rootdn = domaindn
529
530     if configdn is None:
531         configdn = "CN=Configuration," + rootdn
532     if schemadn is None:
533         schemadn = "CN=Schema," + configdn
534
535     if sitename is None:
536         sitename=DEFAULTSITE
537
538     names = ProvisionNames()
539     names.rootdn = rootdn
540     names.domaindn = domaindn
541     names.configdn = configdn
542     names.schemadn = schemadn
543     names.ldapmanagerdn = "CN=Manager," + rootdn
544     names.dnsdomain = dnsdomain
545     names.domain = domain
546     names.realm = realm
547     names.netbiosname = netbiosname
548     names.hostname = hostname
549     names.sitename = sitename
550     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
551         netbiosname, sitename, configdn)
552
553     return names
554
555
556 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
557                  targetdir, sid_generator="internal", eadb=False, lp=None,
558                  server_services=None):
559     """Create a new smb.conf file based on a couple of basic settings.
560     """
561     assert smbconf is not None
562     if hostname is None:
563         hostname = socket.gethostname().split(".")[0]
564         netbiosname = hostname.upper()
565         # remove forbidden chars
566         newnbname = ""
567         for x in netbiosname:
568             if x.isalnum() or x in VALID_NETBIOS_CHARS:
569                 newnbname = "%s%c" % (newnbname, x)
570         #force the length to be <16
571         netbiosname = newnbname[0:15]
572     else:
573         netbiosname = hostname.upper()
574
575     if serverrole is None:
576         serverrole = "standalone"
577
578     assert serverrole in ("domain controller", "member server", "standalone")
579     if serverrole == "domain controller":
580         smbconfsuffix = "dc"
581     elif serverrole == "member server":
582         smbconfsuffix = "member"
583     elif serverrole == "standalone":
584         smbconfsuffix = "standalone"
585
586     if sid_generator is None:
587         sid_generator = "internal"
588
589     assert domain is not None
590     domain = domain.upper()
591
592     assert realm is not None
593     realm = realm.upper()
594
595     if lp is None:
596         lp = samba.param.LoadParm()
597     #Load non-existant file
598     if os.path.exists(smbconf):
599         lp.load(smbconf)
600     if eadb and not lp.get("posix:eadb"):
601         if targetdir is not None:
602             privdir = os.path.join(targetdir, "private")
603         else:
604             privdir = lp.get("private dir")
605         lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
606
607     if server_services is not None:
608         server_services_line = "server services = " + " ".join(server_services)
609     else:
610         server_services_line = ""
611
612     if targetdir is not None:
613         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
614         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
615         statedir_line = "state directory = " + os.path.abspath(targetdir)
616         cachedir_line = "cache directory = " + os.path.abspath(targetdir)
617
618         lp.set("lock dir", os.path.abspath(targetdir))
619         lp.set("state directory", os.path.abspath(targetdir))
620         lp.set("cache directory", os.path.abspath(targetdir))
621     else:
622         privatedir_line = ""
623         lockdir_line = ""
624         statedir_line = ""
625         cachedir_line = ""
626
627     sysvol = os.path.join(lp.get("state directory"), "sysvol")
628     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
629
630     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
631                smbconf, {
632             "NETBIOS_NAME": netbiosname,
633             "DOMAIN": domain,
634             "REALM": realm,
635             "SERVERROLE": serverrole,
636             "NETLOGONPATH": netlogon,
637             "SYSVOLPATH": sysvol,
638             "PRIVATEDIR_LINE": privatedir_line,
639             "LOCKDIR_LINE": lockdir_line,
640             "STATEDIR_LINE": statedir_line,
641             "CACHEDIR_LINE": cachedir_line,
642             "SERVER_SERVICES_LINE": server_services_line
643             })
644
645     # reload the smb.conf
646     lp.load(smbconf)
647
648     # and dump it without any values that are the default
649     # this ensures that any smb.conf parameters that were set
650     # on the provision/join command line are set in the resulting smb.conf
651     f = open(smbconf, mode='w')
652     lp.dump(f, False)
653     f.close()
654
655
656
657 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
658                         users_gid, wheel_gid):
659     """setup reasonable name mappings for sam names to unix names.
660
661     :param samdb: SamDB object.
662     :param idmap: IDmap db object.
663     :param sid: The domain sid.
664     :param domaindn: The domain DN.
665     :param root_uid: uid of the UNIX root user.
666     :param nobody_uid: uid of the UNIX nobody user.
667     :param users_gid: gid of the UNIX users group.
668     :param wheel_gid: gid of the UNIX wheel group.
669     """
670     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
671     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
672
673     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
674     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
675
676
677 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
678                            provision_backend, names, schema, serverrole,
679                            erase=False):
680     """Setup the partitions for the SAM database.
681
682     Alternatively, provision() may call this, and then populate the database.
683
684     :note: This will wipe the Sam Database!
685
686     :note: This function always removes the local SAM LDB file. The erase
687         parameter controls whether to erase the existing data, which
688         may not be stored locally but in LDAP.
689
690     """
691     assert session_info is not None
692
693     # We use options=["modules:"] to stop the modules loading - we
694     # just want to wipe and re-initialise the database, not start it up
695
696     try:
697         os.unlink(samdb_path)
698     except OSError:
699         pass
700
701     samdb = Ldb(url=samdb_path, session_info=session_info,
702                 lp=lp, options=["modules:"])
703
704     ldap_backend_line = "# No LDAP backend"
705     if provision_backend.type is not "ldb":
706         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
707
708     samdb.transaction_start()
709     try:
710         logger.info("Setting up sam.ldb partitions and settings")
711         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
712                 "LDAP_BACKEND_LINE": ldap_backend_line
713         })
714
715
716         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
717                 "BACKEND_TYPE": provision_backend.type,
718                 "SERVER_ROLE": serverrole
719                 })
720
721         logger.info("Setting up sam.ldb rootDSE")
722         setup_samdb_rootdse(samdb, names)
723     except Exception:
724         samdb.transaction_cancel()
725         raise
726     else:
727         samdb.transaction_commit()
728
729
730 def secretsdb_self_join(secretsdb, domain,
731                         netbiosname, machinepass, domainsid=None,
732                         realm=None, dnsdomain=None,
733                         keytab_path=None,
734                         key_version_number=1,
735                         secure_channel_type=SEC_CHAN_WKSTA):
736     """Add domain join-specific bits to a secrets database.
737
738     :param secretsdb: Ldb Handle to the secrets database
739     :param machinepass: Machine password
740     """
741     attrs = ["whenChanged",
742            "secret",
743            "priorSecret",
744            "priorChanged",
745            "krb5Keytab",
746            "privateKeytab"]
747
748     if realm is not None:
749         if dnsdomain is None:
750             dnsdomain = realm.lower()
751         dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
752     else:
753         dnsname = None
754     shortname = netbiosname.lower()
755
756     # We don't need to set msg["flatname"] here, because rdn_name will handle
757     # it, and it causes problems for modifies anyway
758     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
759     msg["secureChannelType"] = [str(secure_channel_type)]
760     msg["objectClass"] = ["top", "primaryDomain"]
761     if dnsname is not None:
762         msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
763         msg["realm"] = [realm]
764         msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
765         msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
766         msg["privateKeytab"] = ["secrets.keytab"]
767
768     msg["secret"] = [machinepass]
769     msg["samAccountName"] = ["%s$" % netbiosname]
770     msg["secureChannelType"] = [str(secure_channel_type)]
771     if domainsid is not None:
772         msg["objectSid"] = [ndr_pack(domainsid)]
773
774     # This complex expression tries to ensure that we don't have more
775     # than one record for this SID, realm or netbios domain at a time,
776     # but we don't delete the old record that we are about to modify,
777     # because that would delete the keytab and previous password.
778     res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
779         expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
780         scope=ldb.SCOPE_ONELEVEL)
781
782     for del_msg in res:
783         secretsdb.delete(del_msg.dn)
784
785     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
786
787     if len(res) == 1:
788         msg["priorSecret"] = [res[0]["secret"][0]]
789         msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
790
791         try:
792             msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
793         except KeyError:
794             pass
795
796         try:
797             msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
798         except KeyError:
799             pass
800
801         for el in msg:
802             if el != 'dn':
803                 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
804         secretsdb.modify(msg)
805         secretsdb.rename(res[0].dn, msg.dn)
806     else:
807         spn = [ 'HOST/%s' % shortname ]
808         if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
809             # we are a domain controller then we add servicePrincipalName
810             # entries for the keytab code to update.
811             spn.extend([ 'HOST/%s' % dnsname ])
812         msg["servicePrincipalName"] = spn
813
814         secretsdb.add(msg)
815
816
817 def setup_secretsdb(paths, session_info, backend_credentials, lp):
818     """Setup the secrets database.
819
820    :note: This function does not handle exceptions and transaction on purpose,
821        it's up to the caller to do this job.
822
823     :param path: Path to the secrets database.
824     :param session_info: Session info.
825     :param credentials: Credentials
826     :param lp: Loadparm context
827     :return: LDB handle for the created secrets database
828     """
829     if os.path.exists(paths.secrets):
830         os.unlink(paths.secrets)
831
832     keytab_path = os.path.join(paths.private_dir, paths.keytab)
833     if os.path.exists(keytab_path):
834         os.unlink(keytab_path)
835
836     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
837     if os.path.exists(dns_keytab_path):
838         os.unlink(dns_keytab_path)
839
840     path = paths.secrets
841
842     secrets_ldb = Ldb(path, session_info=session_info,
843                       lp=lp)
844     secrets_ldb.erase()
845     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
846     secrets_ldb = Ldb(path, session_info=session_info,
847                       lp=lp)
848     secrets_ldb.transaction_start()
849     try:
850         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
851
852         if (backend_credentials is not None and
853             backend_credentials.authentication_requested()):
854             if backend_credentials.get_bind_dn() is not None:
855                 setup_add_ldif(secrets_ldb,
856                     setup_path("secrets_simple_ldap.ldif"), {
857                         "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
858                         "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
859                         })
860             else:
861                 setup_add_ldif(secrets_ldb,
862                     setup_path("secrets_sasl_ldap.ldif"), {
863                         "LDAPADMINUSER": backend_credentials.get_username(),
864                         "LDAPADMINREALM": backend_credentials.get_realm(),
865                         "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
866                         })
867     except Exception:
868         secrets_ldb.transaction_cancel()
869         raise
870     return secrets_ldb
871
872
873
874 def setup_privileges(path, session_info, lp):
875     """Setup the privileges database.
876
877     :param path: Path to the privileges database.
878     :param session_info: Session info.
879     :param credentials: Credentials
880     :param lp: Loadparm context
881     :return: LDB handle for the created secrets database
882     """
883     if os.path.exists(path):
884         os.unlink(path)
885     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
886     privilege_ldb.erase()
887     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
888
889
890 def setup_registry(path, session_info, lp):
891     """Setup the registry.
892
893     :param path: Path to the registry database
894     :param session_info: Session information
895     :param credentials: Credentials
896     :param lp: Loadparm context
897     """
898     reg = samba.registry.Registry()
899     hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
900     reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
901     provision_reg = setup_path("provision.reg")
902     assert os.path.exists(provision_reg)
903     reg.diff_apply(provision_reg)
904
905
906 def setup_idmapdb(path, session_info, lp):
907     """Setup the idmap database.
908
909     :param path: path to the idmap database
910     :param session_info: Session information
911     :param credentials: Credentials
912     :param lp: Loadparm context
913     """
914     if os.path.exists(path):
915         os.unlink(path)
916
917     idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
918     idmap_ldb.erase()
919     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
920     return idmap_ldb
921
922
923 def setup_samdb_rootdse(samdb, names):
924     """Setup the SamDB rootdse.
925
926     :param samdb: Sam Database handle
927     """
928     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
929         "SCHEMADN": names.schemadn,
930         "DOMAINDN": names.domaindn,
931         "ROOTDN"  : names.rootdn,
932         "CONFIGDN": names.configdn,
933         "SERVERDN": names.serverdn,
934         })
935
936
937 def setup_self_join(samdb, admin_session_info, names, fill, machinepass, dnspass,
938                     domainsid, next_rid, invocationid,
939                     policyguid, policyguid_dc, domainControllerFunctionality,
940                     ntdsguid, dc_rid=None):
941     """Join a host to its own domain."""
942     assert isinstance(invocationid, str)
943     if ntdsguid is not None:
944         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
945     else:
946         ntdsguid_line = ""
947
948     if dc_rid is None:
949         dc_rid = next_rid
950
951     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
952               "CONFIGDN": names.configdn,
953               "SCHEMADN": names.schemadn,
954               "DOMAINDN": names.domaindn,
955               "SERVERDN": names.serverdn,
956               "INVOCATIONID": invocationid,
957               "NETBIOSNAME": names.netbiosname,
958               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
959               "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
960               "DOMAINSID": str(domainsid),
961               "DCRID": str(dc_rid),
962               "SAMBA_VERSION_STRING": version,
963               "NTDSGUID": ntdsguid_line,
964               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
965                   domainControllerFunctionality),
966               "RIDALLOCATIONSTART": str(next_rid + 100),
967               "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
968
969     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
970               "POLICYGUID": policyguid,
971               "POLICYGUID_DC": policyguid_dc,
972               "DNSDOMAIN": names.dnsdomain,
973               "DOMAINDN": names.domaindn})
974
975     # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
976     if fill == FILL_FULL:
977         setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
978                 "CONFIGDN": names.configdn,
979                 "SCHEMADN": names.schemadn,
980                 "DOMAINDN": names.domaindn,
981                 "SERVERDN": names.serverdn,
982                 "INVOCATIONID": invocationid,
983                 "NETBIOSNAME": names.netbiosname,
984                 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
985                 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
986                 "DOMAINSID": str(domainsid),
987                 "DCRID": str(dc_rid),
988                 "SAMBA_VERSION_STRING": version,
989                 "NTDSGUID": ntdsguid_line,
990                 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
991                     domainControllerFunctionality)})
992
993     # Setup fSMORoleOwner entries to point at the newly created DC entry
994         setup_modify_ldif(samdb, setup_path("provision_self_join_modify_config.ldif"), {
995                 "CONFIGDN": names.configdn,
996                 "SCHEMADN": names.schemadn,
997                 "DEFAULTSITE": names.sitename,
998                 "NETBIOSNAME": names.netbiosname,
999                 "SERVERDN": names.serverdn,
1000                 })
1001
1002     system_session_info = system_session()
1003     samdb.set_session_info(system_session_info)
1004     # Setup fSMORoleOwner entries to point at the newly created DC entry
1005
1006     # to modify a serverReference under cn=config when we are a subdomain, we must
1007     # be system due to ACLs
1008     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1009               "DOMAINDN": names.domaindn,
1010               "SERVERDN": names.serverdn,
1011               "NETBIOSNAME": names.netbiosname,
1012               })
1013
1014     samdb.set_session_info(admin_session_info)
1015
1016     # This is Samba4 specific and should be replaced by the correct
1017     # DNS AD-style setup
1018     setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1019               "DNSDOMAIN": names.dnsdomain,
1020               "DOMAINDN": names.domaindn,
1021               "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1022               "HOSTNAME" : names.hostname,
1023               "DNSNAME" : '%s.%s' % (
1024                   names.netbiosname.lower(), names.dnsdomain.lower())
1025               })
1026
1027
1028 def getpolicypath(sysvolpath, dnsdomain, guid):
1029     """Return the physical path of policy given its guid.
1030
1031     :param sysvolpath: Path to the sysvol folder
1032     :param dnsdomain: DNS name of the AD domain
1033     :param guid: The GUID of the policy
1034     :return: A string with the complete path to the policy folder
1035     """
1036
1037     if guid[0] != "{":
1038         guid = "{%s}" % guid
1039     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1040     return policy_path
1041
1042
1043 def create_gpo_struct(policy_path):
1044     if not os.path.exists(policy_path):
1045         os.makedirs(policy_path, 0775)
1046     open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1047                       "[General]\r\nVersion=0")
1048     p = os.path.join(policy_path, "MACHINE")
1049     if not os.path.exists(p):
1050         os.makedirs(p, 0775)
1051     p = os.path.join(policy_path, "USER")
1052     if not os.path.exists(p):
1053         os.makedirs(p, 0775)
1054
1055
1056 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1057     """Create the default GPO for a domain
1058
1059     :param sysvolpath: Physical path for the sysvol folder
1060     :param dnsdomain: DNS domain name of the AD domain
1061     :param policyguid: GUID of the default domain policy
1062     :param policyguid_dc: GUID of the default domain controler policy
1063     """
1064     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1065     create_gpo_struct(policy_path)
1066
1067     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1068     create_gpo_struct(policy_path)
1069
1070
1071 def setup_samdb(path, session_info, provision_backend, lp, names,
1072         logger, fill, serverrole, schema, am_rodc=False):
1073     """Setup a complete SAM Database.
1074
1075     :note: This will wipe the main SAM database file!
1076     """
1077
1078     # Also wipes the database
1079     setup_samdb_partitions(path, logger=logger, lp=lp,
1080         provision_backend=provision_backend, session_info=session_info,
1081         names=names, serverrole=serverrole, schema=schema)
1082
1083     # Load the database, but don's load the global schema and don't connect
1084     # quite yet
1085     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1086                   credentials=provision_backend.credentials, lp=lp,
1087                   global_schema=False, am_rodc=am_rodc)
1088
1089     logger.info("Pre-loading the Samba 4 and AD schema")
1090
1091     # Load the schema from the one we computed earlier
1092     samdb.set_schema(schema)
1093
1094     # Set the NTDS settings DN manually - in order to have it already around
1095     # before the provisioned tree exists and we connect
1096     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1097
1098     # And now we can connect to the DB - the schema won't be loaded from the
1099     # DB
1100     samdb.connect(path)
1101
1102     return samdb
1103
1104 def fill_samdb(samdb, lp, names,
1105         logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1106         adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1107         serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1108         next_rid=None, dc_rid=None):
1109
1110     if next_rid is None:
1111         next_rid = 1000
1112
1113     # Provision does not make much sense values larger than 1000000000
1114     # as the upper range of the rIDAvailablePool is 1073741823 and
1115     # we don't want to create a domain that cannot allocate rids.
1116     if next_rid < 1000 or next_rid > 1000000000:
1117         error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1118         error += "the valid range is %u-%u. The default is %u." % (
1119             1000, 1000000000, 1000)
1120         raise ProvisioningError(error)
1121
1122     # ATTENTION: Do NOT change these default values without discussion with the
1123     # team and/or release manager. They have a big impact on the whole program!
1124     domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1125
1126     if dom_for_fun_level is None:
1127         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1128
1129     if dom_for_fun_level > domainControllerFunctionality:
1130         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_R2). This won't work!")
1131
1132     domainFunctionality = dom_for_fun_level
1133     forestFunctionality = dom_for_fun_level
1134
1135     # Set the NTDS settings DN manually - in order to have it already around
1136     # before the provisioned tree exists and we connect
1137     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1138
1139     samdb.transaction_start()
1140     try:
1141         # Set the domain functionality levels onto the database.
1142         # Various module (the password_hash module in particular) need
1143         # to know what level of AD we are emulating.
1144
1145         # These will be fixed into the database via the database
1146         # modifictions below, but we need them set from the start.
1147         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1148         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1149         samdb.set_opaque_integer("domainControllerFunctionality",
1150             domainControllerFunctionality)
1151
1152         samdb.set_domain_sid(str(domainsid))
1153         samdb.set_invocation_id(invocationid)
1154
1155         logger.info("Adding DomainDN: %s" % names.domaindn)
1156
1157         # impersonate domain admin
1158         admin_session_info = admin_session(lp, str(domainsid))
1159         samdb.set_session_info(admin_session_info)
1160         if domainguid is not None:
1161             domainguid_line = "objectGUID: %s\n-" % domainguid
1162         else:
1163             domainguid_line = ""
1164
1165         descr = b64encode(get_domain_descriptor(domainsid))
1166         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1167                 "DOMAINDN": names.domaindn,
1168                 "DOMAINSID": str(domainsid),
1169                 "DESCRIPTOR": descr,
1170                 "DOMAINGUID": domainguid_line
1171                 })
1172
1173         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1174             "DOMAINDN": names.domaindn,
1175             "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1176             "NEXTRID": str(next_rid),
1177             "DEFAULTSITE": names.sitename,
1178             "CONFIGDN": names.configdn,
1179             "POLICYGUID": policyguid,
1180             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1181             "SAMBA_VERSION_STRING": version
1182             })
1183
1184         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1185         if fill == FILL_FULL:
1186             logger.info("Adding configuration container")
1187             descr = b64encode(get_config_descriptor(domainsid))
1188             setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1189                     "CONFIGDN": names.configdn,
1190                     "DESCRIPTOR": descr,
1191                     })
1192
1193             # The LDIF here was created when the Schema object was constructed
1194             logger.info("Setting up sam.ldb schema")
1195             samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1196             samdb.modify_ldif(schema.schema_dn_modify)
1197             samdb.write_prefixes_from_schema()
1198             samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1199             setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1200                            {"SCHEMADN": names.schemadn})
1201
1202         # Now register this container in the root of the forest
1203         msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1204         msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1205                     "subRefs")
1206
1207     except Exception:
1208         samdb.transaction_cancel()
1209         raise
1210     else:
1211         samdb.transaction_commit()
1212
1213     samdb.transaction_start()
1214     try:
1215         samdb.invocation_id = invocationid
1216
1217         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1218         if fill == FILL_FULL:
1219             logger.info("Setting up sam.ldb configuration data")
1220             setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1221                     "CONFIGDN": names.configdn,
1222                     "NETBIOSNAME": names.netbiosname,
1223                     "DEFAULTSITE": names.sitename,
1224                     "DNSDOMAIN": names.dnsdomain,
1225                     "DOMAIN": names.domain,
1226                     "SCHEMADN": names.schemadn,
1227                     "DOMAINDN": names.domaindn,
1228                     "SERVERDN": names.serverdn,
1229                     "FOREST_FUNCTIONALITY": str(forestFunctionality),
1230                     "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1231                     })
1232
1233             logger.info("Setting up display specifiers")
1234             display_specifiers_ldif = read_ms_ldif(
1235                 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1236             display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1237                                                      {"CONFIGDN": names.configdn})
1238             check_all_substituted(display_specifiers_ldif)
1239             samdb.add_ldif(display_specifiers_ldif)
1240
1241         logger.info("Adding users container")
1242         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1243                 "DOMAINDN": names.domaindn})
1244         logger.info("Modifying users container")
1245         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1246                 "DOMAINDN": names.domaindn})
1247         logger.info("Adding computers container")
1248         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1249                 "DOMAINDN": names.domaindn})
1250         logger.info("Modifying computers container")
1251         setup_modify_ldif(samdb,
1252             setup_path("provision_computers_modify.ldif"), {
1253                 "DOMAINDN": names.domaindn})
1254         logger.info("Setting up sam.ldb data")
1255         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1256             "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1257             "DOMAINDN": names.domaindn,
1258             "NETBIOSNAME": names.netbiosname,
1259             "DEFAULTSITE": names.sitename,
1260             "CONFIGDN": names.configdn,
1261             "SERVERDN": names.serverdn,
1262             "RIDAVAILABLESTART": str(next_rid + 600),
1263             "POLICYGUID_DC": policyguid_dc
1264             })
1265
1266         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1267         if fill == FILL_FULL:
1268             setup_modify_ldif(samdb,
1269                               setup_path("provision_configuration_references.ldif"), {
1270                     "CONFIGDN": names.configdn,
1271                     "SCHEMADN": names.schemadn})
1272
1273             logger.info("Setting up well known security principals")
1274             setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1275                 "CONFIGDN": names.configdn,
1276                 })
1277
1278         if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1279             setup_modify_ldif(samdb,
1280                               setup_path("provision_basedn_references.ldif"),
1281                               {"DOMAINDN": names.domaindn})
1282
1283             logger.info("Setting up sam.ldb users and groups")
1284             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1285                 "DOMAINDN": names.domaindn,
1286                 "DOMAINSID": str(domainsid),
1287                 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1288                 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1289                 })
1290
1291             logger.info("Setting up self join")
1292             setup_self_join(samdb, admin_session_info, names=names, fill=fill, invocationid=invocationid,
1293                             dnspass=dnspass,
1294                             machinepass=machinepass,
1295                             domainsid=domainsid,
1296                             next_rid=next_rid,
1297                             dc_rid=dc_rid,
1298                             policyguid=policyguid,
1299                             policyguid_dc=policyguid_dc,
1300                             domainControllerFunctionality=domainControllerFunctionality,
1301                             ntdsguid=ntdsguid)
1302
1303             ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1304             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1305                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1306             assert isinstance(names.ntdsguid, str)
1307     except Exception:
1308         samdb.transaction_cancel()
1309         raise
1310     else:
1311         samdb.transaction_commit()
1312         return samdb
1313
1314
1315 FILL_FULL = "FULL"
1316 FILL_SUBDOMAIN = "SUBDOMAIN"
1317 FILL_NT4SYNC = "NT4SYNC"
1318 FILL_DRS = "DRS"
1319 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1320 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1321
1322
1323 def set_dir_acl(path, acl, lp, domsid):
1324     setntacl(lp, path, acl, domsid)
1325     for root, dirs, files in os.walk(path, topdown=False):
1326         for name in files:
1327             setntacl(lp, os.path.join(root, name), acl, domsid)
1328         for name in dirs:
1329             setntacl(lp, os.path.join(root, name), acl, domsid)
1330
1331
1332 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1333     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1334     folders beneath.
1335
1336     :param sysvol: Physical path for the sysvol folder
1337     :param dnsdomain: The DNS name of the domain
1338     :param domainsid: The SID of the domain
1339     :param domaindn: The DN of the domain (ie. DC=...)
1340     :param samdb: An LDB object on the SAM db
1341     :param lp: an LP object
1342     """
1343
1344     # Set ACL for GPO root folder
1345     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1346     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1347
1348     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1349                         attrs=["cn", "nTSecurityDescriptor"],
1350                         expression="", scope=ldb.SCOPE_ONELEVEL)
1351
1352     for policy in res:
1353         acl = ndr_unpack(security.descriptor,
1354                          str(policy["nTSecurityDescriptor"])).as_sddl()
1355         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1356         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1357                     str(domainsid))
1358
1359
1360 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1361     lp):
1362     """Set the ACL for the sysvol share and the subfolders
1363
1364     :param samdb: An LDB object on the SAM db
1365     :param netlogon: Physical path for the netlogon folder
1366     :param sysvol: Physical path for the sysvol folder
1367     :param gid: The GID of the "Domain adminstrators" group
1368     :param domainsid: The SID of the domain
1369     :param dnsdomain: The DNS name of the domain
1370     :param domaindn: The DN of the domain (ie. DC=...)
1371     """
1372
1373     try:
1374         os.chown(sysvol, -1, gid)
1375     except OSError:
1376         canchown = False
1377     else:
1378         canchown = True
1379
1380     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1381     setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1382     for root, dirs, files in os.walk(sysvol, topdown=False):
1383         for name in files:
1384             if canchown:
1385                 os.chown(os.path.join(root, name), -1, gid)
1386             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1387         for name in dirs:
1388             if canchown:
1389                 os.chown(os.path.join(root, name), -1, gid)
1390             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1391
1392     # Set acls on Policy folder and policies folders
1393     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1394
1395
1396 def interface_ips_v4(lp):
1397     '''return only IPv4 IPs'''
1398     ips = samba.interface_ips(lp, False)
1399     ret = []
1400     for i in ips:
1401         if i.find(':') == -1:
1402             ret.append(i)
1403     return ret
1404
1405 def interface_ips_v6(lp, linklocal=False):
1406     '''return only IPv6 IPs'''
1407     ips = samba.interface_ips(lp, False)
1408     ret = []
1409     for i in ips:
1410         if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1411             ret.append(i)
1412     return ret
1413
1414
1415 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1416                    domainsid, schema=None,
1417                    targetdir=None, samdb_fill=FILL_FULL,
1418                    hostip=None, hostip6=None,
1419                    next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1420                    domainguid=None, policyguid=None, policyguid_dc=None,
1421                    invocationid=None, machinepass=None, ntdsguid=None,
1422                    dns_backend=None, dnspass=None,
1423                    serverrole=None, dom_for_fun_level=None,
1424                    am_rodc=False, lp=None):
1425     # create/adapt the group policy GUIDs
1426     # Default GUID for default policy are described at
1427     # "How Core Group Policy Works"
1428     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1429     if policyguid is None:
1430         policyguid = DEFAULT_POLICY_GUID
1431     policyguid = policyguid.upper()
1432     if policyguid_dc is None:
1433         policyguid_dc = DEFAULT_DC_POLICY_GUID
1434     policyguid_dc = policyguid_dc.upper()
1435
1436     if invocationid is None:
1437         invocationid = str(uuid.uuid4())
1438
1439     if adminpass is None:
1440         adminpass = samba.generate_random_password(12, 32)
1441     if krbtgtpass is None:
1442         krbtgtpass = samba.generate_random_password(128, 255)
1443     if machinepass is None:
1444         machinepass  = samba.generate_random_password(128, 255)
1445     if dnspass is None:
1446         dnspass = samba.generate_random_password(128, 255)
1447
1448     samdb = fill_samdb(samdb, lp, names, logger=logger,
1449                        domainsid=domainsid, schema=schema, domainguid=domainguid,
1450                        policyguid=policyguid, policyguid_dc=policyguid_dc,
1451                        fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1452                        invocationid=invocationid, machinepass=machinepass,
1453                        dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1454                        dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1455                        next_rid=next_rid, dc_rid=dc_rid)
1456
1457     if serverrole == "domain controller":
1458         # Set up group policies (domain policy and domain controller
1459         # policy)
1460         create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1461                            policyguid_dc)
1462         setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
1463                      domainsid, names.dnsdomain, names.domaindn, lp)
1464
1465         secretsdb_self_join(secrets_ldb, domain=names.domain,
1466                             realm=names.realm, dnsdomain=names.dnsdomain,
1467                             netbiosname=names.netbiosname, domainsid=domainsid,
1468                             machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1469
1470         # Now set up the right msDS-SupportedEncryptionTypes into the DB
1471         # In future, this might be determined from some configuration
1472         kerberos_enctypes = str(ENC_ALL_TYPES)
1473
1474         try:
1475             msg = ldb.Message(ldb.Dn(samdb,
1476                                      samdb.searchone("distinguishedName",
1477                                                      expression="samAccountName=%s$" % names.netbiosname,
1478                                                      scope=ldb.SCOPE_SUBTREE)))
1479             msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1480                 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1481                 name="msDS-SupportedEncryptionTypes")
1482             samdb.modify(msg)
1483         except ldb.LdbError, (enum, estr):
1484             if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1485                 # It might be that this attribute does not exist in this schema
1486                 raise
1487
1488         setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1489                      hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1490                      dnspass=dnspass, os_level=dom_for_fun_level,
1491                      targetdir=targetdir, site=DEFAULTSITE)
1492
1493         domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1494                                      attribute="objectGUID")
1495         assert isinstance(domainguid, str)
1496
1497     lastProvisionUSNs = get_last_provision_usn(samdb)
1498     maxUSN = get_max_usn(samdb, str(names.rootdn))
1499     if lastProvisionUSNs is not None:
1500         update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1501     else:
1502         set_provision_usn(samdb, 0, maxUSN, invocationid)
1503
1504     logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1505     setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1506                       { 'NTDSGUID' : names.ntdsguid })
1507
1508     # fix any dangling GUIDs from the provision
1509     logger.info("Fixing provision GUIDs")
1510     chk = dbcheck(samdb, samdb_schema=samdb,  verbose=False, fix=True, yes=True, quiet=True)
1511     samdb.transaction_start()
1512     # a small number of GUIDs are missing because of ordering issues in the
1513     # provision code
1514     for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1515         chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1516                            scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1517     chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1518                        scope=ldb.SCOPE_ONELEVEL,
1519                        attrs=['ipsecOwnersReference',
1520                               'ipsecFilterReference',
1521                               'ipsecISAKMPReference',
1522                               'ipsecNegotiationPolicyReference',
1523                               'ipsecNFAReference'])
1524     samdb.transaction_commit()
1525
1526 def provision(logger, session_info, credentials, smbconf=None,
1527         targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1528         domaindn=None, schemadn=None, configdn=None, serverdn=None,
1529         domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1530         next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1531         domainguid=None, policyguid=None, policyguid_dc=None,
1532         dns_backend=None, dnspass=None,
1533         invocationid=None, machinepass=None, ntdsguid=None,
1534         root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1535         serverrole=None, dom_for_fun_level=None, 
1536         backend_type=None, sitename=None,
1537         ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1538         useeadb=False, am_rodc=False,
1539         lp=None):
1540     """Provision samba4
1541
1542     :note: caution, this wipes all existing data!
1543     """
1544
1545     roles = {}
1546     roles["ROLE_STANDALONE"] = "standalone"
1547     roles["ROLE_DOMAIN_MEMBER"] = "member server"
1548     roles["ROLE_DOMAIN_BDC"] = "domain controller"
1549     roles["ROLE_DOMAIN_PDC"] = "domain controller"
1550     roles["dc"] = "domain controller"
1551     roles["member"] = "member server"
1552     roles["domain controller"] = "domain controller"
1553     roles["member server"] = "member server"
1554     roles["standalone"] = "standalone"
1555
1556     try:
1557         serverrole = roles[serverrole]
1558     except KeyError:
1559         raise ProvisioningError('server role (%s) should be one of "domain controller", "member server", "standalone"' % serverrole)
1560
1561     if ldapadminpass is None:
1562         # Make a new, random password between Samba and it's LDAP server
1563         ldapadminpass=samba.generate_random_password(128, 255)
1564
1565     if backend_type is None:
1566         backend_type = "ldb"
1567
1568     if domainsid is None:
1569         domainsid = security.random_sid()
1570     else:
1571         domainsid = security.dom_sid(domainsid)
1572
1573     sid_generator = "internal"
1574     if backend_type == "fedora-ds":
1575         sid_generator = "backend"
1576
1577     root_uid = findnss_uid([root or "root"])
1578     nobody_uid = findnss_uid([nobody or "nobody"])
1579     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1580     if wheel is None:
1581         wheel_gid = findnss_gid(["wheel", "adm"])
1582     else:
1583         wheel_gid = findnss_gid([wheel])
1584     try:
1585         bind_gid = findnss_gid(["bind", "named"])
1586     except KeyError:
1587         bind_gid = None
1588
1589     if targetdir is not None:
1590         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1591     elif smbconf is None:
1592         smbconf = samba.param.default_path()
1593     if not os.path.exists(os.path.dirname(smbconf)):
1594         os.makedirs(os.path.dirname(smbconf))
1595
1596     # only install a new smb.conf if there isn't one there already
1597     if os.path.exists(smbconf):
1598         # if Samba Team members can't figure out the weird errors
1599         # loading an empty smb.conf gives, then we need to be smarter.
1600         # Pretend it just didn't exist --abartlet
1601         data = open(smbconf, 'r').read()
1602         data = data.lstrip()
1603         if data is None or data == "":
1604             make_smbconf(smbconf, hostname, domain, realm,
1605                          serverrole, targetdir, sid_generator, useeadb,
1606                          lp=lp)
1607     else:
1608         make_smbconf(smbconf, hostname, domain, realm, serverrole,
1609                      targetdir, sid_generator, useeadb, lp=lp)
1610
1611     if lp is None:
1612         lp = samba.param.LoadParm()
1613     lp.load(smbconf)
1614     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1615         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1616         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1617         sitename=sitename, rootdn=rootdn)
1618     paths = provision_paths_from_lp(lp, names.dnsdomain)
1619
1620     paths.bind_gid = bind_gid
1621     paths.wheel_gid = wheel_gid
1622
1623     if hostip is None:
1624         logger.info("Looking up IPv4 addresses")
1625         hostips = interface_ips_v4(lp)
1626         if len(hostips) > 0:
1627             hostip = hostips[0]
1628             if len(hostips) > 1:
1629                 logger.warning("More than one IPv4 address found. Using %s",
1630                     hostip)
1631     if hostip == "127.0.0.1":
1632         hostip = None
1633     if hostip is None:
1634         logger.warning("No IPv4 address will be assigned")
1635
1636     if hostip6 is None:
1637         logger.info("Looking up IPv6 addresses")
1638         hostips = interface_ips_v6(lp, linklocal=False)
1639         if hostips:
1640             hostip6 = hostips[0]
1641         if len(hostips) > 1:
1642             logger.warning("More than one IPv6 address found. Using %s", hostip6)
1643     if hostip6 is None:
1644         logger.warning("No IPv6 address will be assigned")
1645
1646     names.hostip = hostip
1647     names.hostip6 = hostip6
1648
1649     if serverrole is None:
1650         serverrole = lp.get("server role")
1651
1652     if not os.path.exists(paths.private_dir):
1653         os.mkdir(paths.private_dir)
1654     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1655         os.mkdir(os.path.join(paths.private_dir, "tls"))
1656
1657     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1658
1659     schema = Schema(domainsid, invocationid=invocationid,
1660         schemadn=names.schemadn)
1661
1662     if backend_type == "ldb":
1663         provision_backend = LDBBackend(backend_type, paths=paths,
1664             lp=lp, credentials=credentials,
1665             names=names, logger=logger)
1666     elif backend_type == "existing":
1667         # If support for this is ever added back, then the URI will need to be specified again
1668         provision_backend = ExistingBackend(backend_type, paths=paths,
1669             lp=lp, credentials=credentials,
1670             names=names, logger=logger,
1671             ldap_backend_forced_uri=None)
1672     elif backend_type == "fedora-ds":
1673         provision_backend = FDSBackend(backend_type, paths=paths,
1674             lp=lp, credentials=credentials,
1675             names=names, logger=logger, domainsid=domainsid,
1676             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1677             slapd_path=slapd_path,
1678             root=root)
1679     elif backend_type == "openldap":
1680         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1681             lp=lp, credentials=credentials,
1682             names=names, logger=logger, domainsid=domainsid,
1683             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1684             slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1685     else:
1686         raise ValueError("Unknown LDAP backend type selected")
1687
1688     provision_backend.init()
1689     provision_backend.start()
1690
1691     # only install a new shares config db if there is none
1692     if not os.path.exists(paths.shareconf):
1693         logger.info("Setting up share.ldb")
1694         share_ldb = Ldb(paths.shareconf, session_info=session_info,
1695                         lp=lp)
1696         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1697
1698     logger.info("Setting up secrets.ldb")
1699     secrets_ldb = setup_secretsdb(paths,
1700         session_info=session_info,
1701         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1702
1703     try:
1704         logger.info("Setting up the registry")
1705         setup_registry(paths.hklm, session_info,
1706                        lp=lp)
1707
1708         logger.info("Setting up the privileges database")
1709         setup_privileges(paths.privilege, session_info, lp=lp)
1710
1711         logger.info("Setting up idmap db")
1712         idmap = setup_idmapdb(paths.idmapdb,
1713             session_info=session_info, lp=lp)
1714
1715         setup_name_mappings(idmap, sid=str(domainsid),
1716                             root_uid=root_uid, nobody_uid=nobody_uid,
1717                             users_gid=users_gid, wheel_gid=wheel_gid)
1718
1719         logger.info("Setting up SAM db")
1720         samdb = setup_samdb(paths.samdb, session_info,
1721                             provision_backend, lp, names, logger=logger,
1722                             serverrole=serverrole,
1723                             schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1724
1725         if serverrole == "domain controller":
1726             if paths.netlogon is None:
1727                 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1728                 logger.info("Please either remove %s or see the template at %s" %
1729                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1730                 assert paths.netlogon is not None
1731
1732             if paths.sysvol is None:
1733                 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1734                         " are configuring a DC.")
1735                 logger.info("Please either remove %s or see the template at %s" %
1736                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1737                 assert paths.sysvol is not None
1738
1739             if not os.path.isdir(paths.netlogon):
1740                 os.makedirs(paths.netlogon, 0755)
1741
1742         if samdb_fill == FILL_FULL:
1743             provision_fill(samdb, secrets_ldb, logger,
1744                            names, paths, schema=schema, targetdir=targetdir,
1745                            samdb_fill=samdb_fill, hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1746                            next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1747                            krbtgtpass=krbtgtpass, domainguid=domainguid,
1748                            policyguid=policyguid, policyguid_dc=policyguid_dc,
1749                            invocationid=invocationid, machinepass=machinepass,
1750                            ntdsguid=ntdsguid, dns_backend=dns_backend, dnspass=dnspass,
1751                            serverrole=serverrole, dom_for_fun_level=dom_for_fun_level,
1752                            am_rodc=am_rodc, lp=lp)
1753
1754         create_krb5_conf(paths.krb5conf,
1755                          dnsdomain=names.dnsdomain, hostname=names.hostname,
1756                          realm=names.realm)
1757         logger.info("A Kerberos configuration suitable for Samba 4 has been "
1758                     "generated at %s", paths.krb5conf)
1759
1760         if serverrole == "domain controller":
1761             create_dns_update_list(lp, logger, paths)
1762
1763         provision_backend.post_setup()
1764         provision_backend.shutdown()
1765
1766         create_phpldapadmin_config(paths.phpldapadminconfig,
1767                                    ldapi_url)
1768     except Exception:
1769         secrets_ldb.transaction_cancel()
1770         raise
1771
1772     # Now commit the secrets.ldb to disk
1773     secrets_ldb.transaction_commit()
1774
1775     # the commit creates the dns.keytab, now chown it
1776     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1777     if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1778         try:
1779             os.chmod(dns_keytab_path, 0640)
1780             os.chown(dns_keytab_path, -1, paths.bind_gid)
1781         except OSError:
1782             if not os.environ.has_key('SAMBA_SELFTEST'):
1783                 logger.info("Failed to chown %s to bind gid %u",
1784                             dns_keytab_path, paths.bind_gid)
1785
1786     logger.info("A phpLDAPadmin configuration file suitable for administering the Samba 4 LDAP server has been created in %s .",
1787             paths.phpldapadminconfig)
1788
1789     logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1790     logger.info("Server Role:           %s" % serverrole)
1791     logger.info("Hostname:              %s" % names.hostname)
1792     logger.info("NetBIOS Domain:        %s" % names.domain)
1793     logger.info("DNS Domain:            %s" % names.dnsdomain)
1794     logger.info("DOMAIN SID:            %s" % str(domainsid))
1795     if samdb_fill == FILL_FULL:
1796         logger.info("Admin password:        %s" % adminpass)
1797     if provision_backend.type is not "ldb":
1798         if provision_backend.credentials.get_bind_dn() is not None:
1799             logger.info("LDAP Backend Admin DN: %s" %
1800                 provision_backend.credentials.get_bind_dn())
1801         else:
1802             logger.info("LDAP Admin User:       %s" %
1803                 provision_backend.credentials.get_username())
1804
1805         logger.info("LDAP Admin Password:   %s" %
1806             provision_backend.credentials.get_password())
1807
1808         if provision_backend.slapd_command_escaped is not None:
1809             # now display slapd_command_file.txt to show how slapd must be
1810             # started next time
1811             logger.info("Use later the following commandline to start slapd, then Samba:")
1812             logger.info(provision_backend.slapd_command_escaped)
1813             logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1814                     provision_backend.ldapdir)
1815
1816     result = ProvisionResult()
1817     result.domaindn = domaindn
1818     result.paths = paths
1819     result.names = names
1820     result.lp = lp
1821     result.samdb = samdb
1822     result.idmap = idmap
1823     return result
1824
1825
1826 def provision_become_dc(smbconf=None, targetdir=None,
1827         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1828         serverdn=None, domain=None, hostname=None, domainsid=None,
1829         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1830         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1831         dns_backend=None, root=None, nobody=None, users=None, wheel=None, backup=None,
1832         serverrole=None, ldap_backend=None, ldap_backend_type=None,
1833         sitename=None, debuglevel=1):
1834
1835     logger = logging.getLogger("provision")
1836     samba.set_debug_level(debuglevel)
1837
1838     res = provision(logger, system_session(), None,
1839         smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1840         realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1841         configdn=configdn, serverdn=serverdn, domain=domain,
1842         hostname=hostname, hostip=None, domainsid=domainsid,
1843         machinepass=machinepass, serverrole="domain controller",
1844         sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
1845     res.lp.set("debuglevel", str(debuglevel))
1846     return res
1847
1848
1849 def create_phpldapadmin_config(path, ldapi_uri):
1850     """Create a PHP LDAP admin configuration file.
1851
1852     :param path: Path to write the configuration to.
1853     """
1854     setup_file(setup_path("phpldapadmin-config.php"), path,
1855             {"S4_LDAPI_URI": ldapi_uri})
1856
1857
1858 def create_krb5_conf(path, dnsdomain, hostname, realm):
1859     """Write out a file containing zone statements suitable for inclusion in a
1860     named.conf file (including GSS-TSIG configuration).
1861
1862     :param path: Path of the new named.conf file.
1863     :param dnsdomain: DNS Domain name
1864     :param hostname: Local hostname
1865     :param realm: Realm name
1866     """
1867     setup_file(setup_path("krb5.conf"), path, {
1868             "DNSDOMAIN": dnsdomain,
1869             "HOSTNAME": hostname,
1870             "REALM": realm,
1871         })
1872
1873
1874 class ProvisioningError(Exception):
1875     """A generic provision error."""
1876
1877     def __init__(self, value):
1878         self.value = value
1879
1880     def __str__(self):
1881         return "ProvisioningError: " + self.value
1882
1883
1884 class InvalidNetbiosName(Exception):
1885     """A specified name was not a valid NetBIOS name."""
1886     def __init__(self, name):
1887         super(InvalidNetbiosName, self).__init__(
1888             "The name '%r' is not a valid NetBIOS name" % name)