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