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