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