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