python/provision: remove unused imports
[nivanova/samba-autobuild/.git] / python / samba / provision / __init__.py
1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba AD server
3
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
7 #
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 #
24
25 """Functions for setting up a Samba configuration."""
26
27 __docformat__ = "restructuredText"
28
29 from samba.compat import urllib_quote
30 from samba.compat import string_types
31 from samba.compat import binary_type
32 from base64 import b64encode
33 import errno
34 import os
35 import stat
36 import re
37 import pwd
38 import grp
39 import logging
40 import time
41 import uuid
42 import socket
43 import tempfile
44 import samba.dsdb
45
46 import ldb
47
48 from samba.auth import system_session, admin_session
49 import samba
50 from samba import auth
51 from samba.samba3 import smbd, passdb
52 from samba.samba3 import param as s3param
53 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
54 from samba import (
55     Ldb,
56     MAX_NETBIOS_NAME_LEN,
57     check_all_substituted,
58     is_valid_netbios_char,
59     setup_file,
60     substitute_var,
61     valid_netbios_name,
62     version,
63     is_heimdal_built,
64 )
65 from samba.dcerpc import security, misc
66 from samba.dcerpc.misc import (
67     SEC_CHAN_BDC,
68     SEC_CHAN_WKSTA,
69 )
70 from samba.dsdb import (
71     DS_DOMAIN_FUNCTION_2003,
72     DS_DOMAIN_FUNCTION_2008_R2,
73     ENC_ALL_TYPES,
74 )
75 from samba.idmap import IDmapDB
76 from samba.ms_display_specifiers import read_ms_ldif
77 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
78 from samba.ndr import ndr_pack, ndr_unpack
79 from samba.provision.backend import (
80     ExistingBackend,
81     FDSBackend,
82     LDBBackend,
83     OpenLDAPBackend,
84 )
85 from samba.descriptor import (
86     get_empty_descriptor,
87     get_config_descriptor,
88     get_config_partitions_descriptor,
89     get_config_sites_descriptor,
90     get_config_ntds_quotas_descriptor,
91     get_config_delete_protected1_descriptor,
92     get_config_delete_protected1wd_descriptor,
93     get_config_delete_protected2_descriptor,
94     get_domain_descriptor,
95     get_domain_infrastructure_descriptor,
96     get_domain_builtin_descriptor,
97     get_domain_computers_descriptor,
98     get_domain_users_descriptor,
99     get_domain_controllers_descriptor,
100     get_domain_delete_protected1_descriptor,
101     get_domain_delete_protected2_descriptor,
102     get_dns_partition_descriptor,
103     get_dns_forest_microsoft_dns_descriptor,
104     get_dns_domain_microsoft_dns_descriptor,
105     get_managed_service_accounts_descriptor,
106 )
107 from samba.provision.common import (
108     setup_path,
109     setup_add_ldif,
110     setup_modify_ldif,
111     FILL_FULL,
112     FILL_SUBDOMAIN,
113     FILL_NT4SYNC,
114     FILL_DRS
115 )
116 from samba.provision.sambadns import (
117     get_dnsadmins_sid,
118     setup_ad_dns,
119     create_dns_update_list
120 )
121
122 import samba.param
123 import samba.registry
124 from samba.schema import Schema
125 from samba.samdb import SamDB
126 from samba.dbchecker import dbcheck
127 from samba.provision.kerberos import create_kdc_conf
128 from samba.samdb import get_default_backend_store
129
130 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
131 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04FB984F9"
132 DEFAULTSITE = "Default-First-Site-Name"
133 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
134
135 DEFAULT_MIN_PWD_LENGTH = 7
136
137
138 class ProvisionPaths(object):
139
140     def __init__(self):
141         self.shareconf = None
142         self.hklm = None
143         self.hkcu = None
144         self.hkcr = None
145         self.hku = None
146         self.hkpd = None
147         self.hkpt = None
148         self.samdb = None
149         self.idmapdb = None
150         self.secrets = None
151         self.keytab = None
152         self.dns_keytab = None
153         self.dns = None
154         self.winsdb = None
155         self.private_dir = None
156         self.binddns_dir = None
157         self.state_dir = None
158
159
160 class ProvisionNames(object):
161
162     def __init__(self):
163         self.ncs = None
164         self.rootdn = None
165         self.domaindn = None
166         self.configdn = None
167         self.schemadn = None
168         self.dnsforestdn = None
169         self.dnsdomaindn = None
170         self.ldapmanagerdn = None
171         self.dnsdomain = None
172         self.realm = None
173         self.netbiosname = None
174         self.domain = None
175         self.hostname = None
176         self.sitename = None
177         self.smbconf = None
178         self.domainsid = None
179         self.forestsid = None
180         self.domainguid = None
181         self.name_map = {}
182
183
184 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
185                                   lp):
186     """Get key provision parameters (realm, domain, ...) from a given provision
187
188     :param samdb: An LDB object connected to the sam.ldb file
189     :param secretsdb: An LDB object connected to the secrets.ldb file
190     :param idmapdb: An LDB object connected to the idmap.ldb file
191     :param paths: A list of path to provision object
192     :param smbconf: Path to the smb.conf file
193     :param lp: A LoadParm object
194     :return: A list of key provision parameters
195     """
196     names = ProvisionNames()
197     names.adminpass = None
198
199     # NT domain, kerberos realm, root dn, domain dn, domain dns name
200     names.domain = lp.get("workgroup").upper()
201     names.realm = lp.get("realm")
202     names.dnsdomain = names.realm.lower()
203     basedn = samba.dn_from_dns_name(names.dnsdomain)
204     names.realm = names.realm.upper()
205     # netbiosname
206     # Get the netbiosname first (could be obtained from smb.conf in theory)
207     res = secretsdb.search(expression="(flatname=%s)" %
208                            names.domain, base="CN=Primary Domains",
209                            scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
210     names.netbiosname = str(res[0]["sAMAccountName"]).replace("$", "")
211
212     names.smbconf = smbconf
213
214     # That's a bit simplistic but it's ok as long as we have only 3
215     # partitions
216     current = samdb.search(expression="(objectClass=*)",
217                            base="", scope=ldb.SCOPE_BASE,
218                            attrs=["defaultNamingContext", "schemaNamingContext",
219                                   "configurationNamingContext", "rootDomainNamingContext",
220                                   "namingContexts"])
221
222     names.configdn = str(current[0]["configurationNamingContext"][0])
223     names.schemadn = str(current[0]["schemaNamingContext"][0])
224     if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
225                                              current[0]["defaultNamingContext"][0].decode('utf8')))):
226         raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
227                                  "is not the same ..." % (paths.samdb,
228                                                           str(current[0]["defaultNamingContext"][0].decode('utf8')),
229                                                           paths.smbconf, basedn)))
230
231     names.domaindn = str(current[0]["defaultNamingContext"][0])
232     names.rootdn = str(current[0]["rootDomainNamingContext"][0])
233     names.ncs = current[0]["namingContexts"]
234     names.dnsforestdn = None
235     names.dnsdomaindn = None
236
237     for i in range(0, len(names.ncs)):
238         nc = str(names.ncs[i])
239
240         dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
241         if nc == dnsforestdn:
242             names.dnsforestdn = dnsforestdn
243             continue
244
245         dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
246         if nc == dnsdomaindn:
247             names.dnsdomaindn = dnsdomaindn
248             continue
249
250     # default site name
251     res3 = samdb.search(expression="(objectClass=site)",
252                         base="CN=Sites," + str(names.configdn), scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
253     names.sitename = str(res3[0]["cn"])
254
255     # dns hostname and server dn
256     res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
257                         base="OU=Domain Controllers,%s" % basedn,
258                         scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
259     if len(res4) == 0:
260         raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
261
262     names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
263
264     server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
265                               attrs=[], base=names.configdn)
266     names.serverdn = str(server_res[0].dn)
267
268     # invocation id/objectguid
269     res5 = samdb.search(expression="(objectClass=*)",
270                         base="CN=NTDS Settings,%s" % str(names.serverdn),
271                         scope=ldb.SCOPE_BASE,
272                         attrs=["invocationID", "objectGUID"])
273     names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
274     names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
275
276     # domain guid/sid
277     res6 = samdb.search(expression="(objectClass=*)", base=basedn,
278                         scope=ldb.SCOPE_BASE, attrs=["objectGUID",
279                                                      "objectSid", "msDS-Behavior-Version"])
280     names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
281     names.domainsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
282     names.forestsid = ndr_unpack(security.dom_sid, res6[0]["objectSid"][0])
283     if res6[0].get("msDS-Behavior-Version") is None or \
284             int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
285         names.domainlevel = DS_DOMAIN_FUNCTION_2000
286     else:
287         names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
288
289     # policy guid
290     res7 = samdb.search(expression="(name={%s})" % DEFAULT_POLICY_GUID,
291                         base="CN=Policies,CN=System," + basedn,
292                         scope=ldb.SCOPE_ONELEVEL, attrs=["cn", "displayName"])
293     names.policyid = str(res7[0]["cn"]).replace("{", "").replace("}", "")
294     # dc policy guid
295     res8 = samdb.search(expression="(name={%s})" % DEFAULT_DC_POLICY_GUID,
296                         base="CN=Policies,CN=System," + basedn,
297                         scope=ldb.SCOPE_ONELEVEL,
298                         attrs=["cn", "displayName"])
299     if len(res8) == 1:
300         names.policyid_dc = str(res8[0]["cn"]).replace("{", "").replace("}", "")
301     else:
302         names.policyid_dc = None
303
304     res9 = idmapdb.search(expression="(cn=%s-%s)" %
305                           (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
306                           attrs=["xidNumber", "type"])
307     if len(res9) != 1:
308         raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
309     if str(res9[0]["type"][0]) == "ID_TYPE_BOTH":
310         names.root_gid = int(res9[0]["xidNumber"][0])
311     else:
312         names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
313
314     res10 = samdb.search(expression="(samaccountname=dns)",
315                          scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
316                          controls=["search_options:1:2"])
317     if (len(res10) > 0):
318         has_legacy_dns_account = True
319     else:
320         has_legacy_dns_account = False
321
322     res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
323                          scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
324                          controls=["search_options:1:2"])
325     if (len(res11) > 0):
326         has_dns_account = True
327     else:
328         has_dns_account = False
329
330     if names.dnsdomaindn is not None:
331         if has_dns_account:
332             names.dns_backend = 'BIND9_DLZ'
333         else:
334             names.dns_backend = 'SAMBA_INTERNAL'
335     elif has_dns_account or has_legacy_dns_account:
336         names.dns_backend = 'BIND9_FLATFILE'
337     else:
338         names.dns_backend = 'NONE'
339
340     dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
341     names.name_map['DnsAdmins'] = str(dns_admins_sid)
342
343     return names
344
345
346 def update_provision_usn(samdb, low, high, id, replace=False):
347     """Update the field provisionUSN in sam.ldb
348
349     This field is used to track range of USN modified by provision and
350     upgradeprovision.
351     This value is used afterward by next provision to figure out if
352     the field have been modified since last provision.
353
354     :param samdb: An LDB object connect to sam.ldb
355     :param low: The lowest USN modified by this upgrade
356     :param high: The highest USN modified by this upgrade
357     :param id: The invocation id of the samba's dc
358     :param replace: A boolean indicating if the range should replace any
359                     existing one or appended (default)
360     """
361
362     tab = []
363     if not replace:
364         entry = samdb.search(base="@PROVISION",
365                              scope=ldb.SCOPE_BASE,
366                              attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
367         for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
368             if not re.search(';', str(e)):
369                 e = "%s;%s" % (str(e), id)
370             tab.append(str(e))
371
372     tab.append("%s-%s;%s" % (low, high, id))
373     delta = ldb.Message()
374     delta.dn = ldb.Dn(samdb, "@PROVISION")
375     delta[LAST_PROVISION_USN_ATTRIBUTE] = \
376         ldb.MessageElement(tab,
377                            ldb.FLAG_MOD_REPLACE,
378                            LAST_PROVISION_USN_ATTRIBUTE)
379     entry = samdb.search(expression='provisionnerID=*',
380                          base="@PROVISION", scope=ldb.SCOPE_BASE,
381                          attrs=["provisionnerID"])
382     if len(entry) == 0 or len(entry[0]) == 0:
383         delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
384     samdb.modify(delta)
385
386
387 def set_provision_usn(samdb, low, high, id):
388     """Set the field provisionUSN in sam.ldb
389     This field is used to track range of USN modified by provision and
390     upgradeprovision.
391     This value is used afterward by next provision to figure out if
392     the field have been modified since last provision.
393
394     :param samdb: An LDB object connect to sam.ldb
395     :param low: The lowest USN modified by this upgrade
396     :param high: The highest USN modified by this upgrade
397     :param id: The invocationId of the provision"""
398
399     tab = []
400     tab.append("%s-%s;%s" % (low, high, id))
401
402     delta = ldb.Message()
403     delta.dn = ldb.Dn(samdb, "@PROVISION")
404     delta[LAST_PROVISION_USN_ATTRIBUTE] = \
405         ldb.MessageElement(tab,
406                            ldb.FLAG_MOD_ADD,
407                            LAST_PROVISION_USN_ATTRIBUTE)
408     samdb.add(delta)
409
410
411 def get_max_usn(samdb, basedn):
412     """ This function return the biggest USN present in the provision
413
414     :param samdb: A LDB object pointing to the sam.ldb
415     :param basedn: A string containing the base DN of the provision
416                     (ie. DC=foo, DC=bar)
417     :return: The biggest USN in the provision"""
418
419     res = samdb.search(expression="objectClass=*", base=basedn,
420                        scope=ldb.SCOPE_SUBTREE, attrs=["uSNChanged"],
421                        controls=["search_options:1:2",
422                                  "server_sort:1:1:uSNChanged",
423                                  "paged_results:1:1"])
424     return res[0]["uSNChanged"]
425
426
427 def get_last_provision_usn(sam):
428     """Get USNs ranges modified by a provision or an upgradeprovision
429
430     :param sam: An LDB object pointing to the sam.ldb
431     :return: a dictionary which keys are invocation id and values are an array
432              of integer representing the different ranges
433     """
434     try:
435         entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
436                            base="@PROVISION", scope=ldb.SCOPE_BASE,
437                            attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
438     except ldb.LdbError as e1:
439         (ecode, emsg) = e1.args
440         if ecode == ldb.ERR_NO_SUCH_OBJECT:
441             return None
442         raise
443     if len(entry) > 0:
444         myids = []
445         range = {}
446         p = re.compile(r'-')
447         if entry[0].get("provisionnerID"):
448             for e in entry[0]["provisionnerID"]:
449                 myids.append(str(e))
450         for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
451             tab1 = str(r).split(';')
452             if len(tab1) == 2:
453                 id = tab1[1]
454             else:
455                 id = "default"
456             if (len(myids) > 0 and id not in myids):
457                 continue
458             tab2 = p.split(tab1[0])
459             if range.get(id) is None:
460                 range[id] = []
461             range[id].append(tab2[0])
462             range[id].append(tab2[1])
463         return range
464     else:
465         return None
466
467
468 class ProvisionResult(object):
469     """Result of a provision.
470
471     :ivar server_role: The server role
472     :ivar paths: ProvisionPaths instance
473     :ivar domaindn: The domain dn, as string
474     """
475
476     def __init__(self):
477         self.server_role = None
478         self.paths = None
479         self.domaindn = None
480         self.lp = None
481         self.samdb = None
482         self.idmap = None
483         self.names = None
484         self.domainsid = None
485         self.adminpass_generated = None
486         self.adminpass = None
487         self.backend_result = None
488
489     def report_logger(self, logger):
490         """Report this provision result to a logger."""
491         logger.info(
492             "Once the above files are installed, your Samba AD server will "
493             "be ready to use")
494         if self.adminpass_generated:
495             logger.info("Admin password:        %s", self.adminpass)
496         logger.info("Server Role:           %s", self.server_role)
497         logger.info("Hostname:              %s", self.names.hostname)
498         logger.info("NetBIOS Domain:        %s", self.names.domain)
499         logger.info("DNS Domain:            %s", self.names.dnsdomain)
500         logger.info("DOMAIN SID:            %s", self.domainsid)
501
502         if self.backend_result:
503             self.backend_result.report_logger(logger)
504
505
506 def check_install(lp, session_info, credentials):
507     """Check whether the current install seems ok.
508
509     :param lp: Loadparm context
510     :param session_info: Session information
511     :param credentials: Credentials
512     """
513     if lp.get("realm") == "":
514         raise Exception("Realm empty")
515     samdb = Ldb(lp.samdb_url(), session_info=session_info,
516                 credentials=credentials, lp=lp)
517     if len(samdb.search("(cn=Administrator)")) != 1:
518         raise ProvisioningError("No administrator account found")
519
520
521 def findnss(nssfn, names):
522     """Find a user or group from a list of possibilities.
523
524     :param nssfn: NSS Function to try (should raise KeyError if not found)
525     :param names: Names to check.
526     :return: Value return by first names list.
527     """
528     for name in names:
529         try:
530             return nssfn(name)
531         except KeyError:
532             pass
533     raise KeyError("Unable to find user/group in %r" % names)
534
535
536 def findnss_uid(names):
537     return findnss(pwd.getpwnam, names)[2]
538
539
540 def findnss_gid(names):
541     return findnss(grp.getgrnam, names)[2]
542
543
544 def provision_paths_from_lp(lp, dnsdomain):
545     """Set the default paths for provisioning.
546
547     :param lp: Loadparm context.
548     :param dnsdomain: DNS Domain name
549     """
550     paths = ProvisionPaths()
551     paths.private_dir = lp.get("private dir")
552     paths.binddns_dir = lp.get("binddns dir")
553     paths.state_dir = lp.get("state directory")
554
555     # This is stored without path prefix for the "privateKeytab" attribute in
556     # "secrets_dns.ldif".
557     paths.dns_keytab = "dns.keytab"
558     paths.keytab = "secrets.keytab"
559
560     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
561     paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
562     paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
563     paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
564     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
565     paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
566     paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
567     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
568     paths.kdcconf = os.path.join(paths.private_dir, "kdc.conf")
569     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
570     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
571     paths.encrypted_secrets_key_path = os.path.join(
572         paths.private_dir,
573         "encrypted_secrets.key")
574
575     paths.dns = os.path.join(paths.binddns_dir, "dns", dnsdomain + ".zone")
576     paths.namedconf = os.path.join(paths.binddns_dir, "named.conf")
577     paths.namedconf_update = os.path.join(paths.binddns_dir, "named.conf.update")
578     paths.namedtxt = os.path.join(paths.binddns_dir, "named.txt")
579
580     paths.hklm = "hklm.ldb"
581     paths.hkcr = "hkcr.ldb"
582     paths.hkcu = "hkcu.ldb"
583     paths.hku = "hku.ldb"
584     paths.hkpd = "hkpd.ldb"
585     paths.hkpt = "hkpt.ldb"
586     paths.sysvol = lp.get("path", "sysvol")
587     paths.netlogon = lp.get("path", "netlogon")
588     paths.smbconf = lp.configfile
589     return paths
590
591
592 def determine_netbios_name(hostname):
593     """Determine a netbios name from a hostname."""
594     # remove forbidden chars and force the length to be <16
595     netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
596     return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
597
598
599 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
600                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
601                 schemadn=None, serverdn=None, sitename=None,
602                 domain_names_forced=False):
603     """Guess configuration settings to use."""
604
605     if hostname is None:
606         hostname = socket.gethostname().split(".")[0]
607
608     netbiosname = lp.get("netbios name")
609     if netbiosname is None:
610         netbiosname = determine_netbios_name(hostname)
611     netbiosname = netbiosname.upper()
612     if not valid_netbios_name(netbiosname):
613         raise InvalidNetbiosName(netbiosname)
614
615     if dnsdomain is None:
616         dnsdomain = lp.get("realm")
617         if dnsdomain is None or dnsdomain == "":
618             raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
619
620     dnsdomain = dnsdomain.lower()
621
622     if serverrole is None:
623         serverrole = lp.get("server role")
624         if serverrole is None:
625             raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
626
627     serverrole = serverrole.lower()
628
629     realm = dnsdomain.upper()
630
631     if lp.get("realm") == "":
632         raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s.  Please remove the smb.conf file and let provision generate it" % lp.configfile)
633
634     if lp.get("realm").upper() != realm:
635         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(), lp.configfile, realm))
636
637     if lp.get("server role").lower() != serverrole:
638         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))
639
640     if serverrole == "active directory domain controller":
641         if domain is None:
642             # This will, for better or worse, default to 'WORKGROUP'
643             domain = lp.get("workgroup")
644         domain = domain.upper()
645
646         if lp.get("workgroup").upper() != domain:
647             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))
648
649         if domaindn is None:
650             domaindn = samba.dn_from_dns_name(dnsdomain)
651
652         if domain == netbiosname:
653             raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
654     else:
655         domain = netbiosname
656         if domaindn is None:
657             domaindn = "DC=" + netbiosname
658
659     if not valid_netbios_name(domain):
660         raise InvalidNetbiosName(domain)
661
662     if hostname.upper() == realm:
663         raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
664     if netbiosname.upper() == realm:
665         raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
666     if domain == realm and not domain_names_forced:
667         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
668
669     if serverrole != "active directory domain controller":
670         #
671         # This is the code path for a domain member
672         # where we provision the database as if we where
673         # on a domain controller, so we should not use
674         # the same dnsdomain as the domain controllers
675         # of our primary domain.
676         #
677         # This will be important if we start doing
678         # SID/name filtering and reject the local
679         # sid and names if they come from a domain
680         # controller.
681         #
682         realm = netbiosname
683         dnsdomain = netbiosname.lower()
684
685     if rootdn is None:
686         rootdn = domaindn
687
688     if configdn is None:
689         configdn = "CN=Configuration," + rootdn
690     if schemadn is None:
691         schemadn = "CN=Schema," + configdn
692
693     if sitename is None:
694         sitename = DEFAULTSITE
695
696     names = ProvisionNames()
697     names.rootdn = rootdn
698     names.domaindn = domaindn
699     names.configdn = configdn
700     names.schemadn = schemadn
701     names.ldapmanagerdn = "CN=Manager," + rootdn
702     names.dnsdomain = dnsdomain
703     names.domain = domain
704     names.realm = realm
705     names.netbiosname = netbiosname
706     names.hostname = hostname
707     names.sitename = sitename
708     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
709         netbiosname, sitename, configdn)
710
711     return names
712
713
714 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
715                  serverrole=None, eadb=False, use_ntvfs=False, lp=None,
716                  global_param=None):
717     """Create a new smb.conf file based on a couple of basic settings.
718     """
719     assert smbconf is not None
720
721     if hostname is None:
722         hostname = socket.gethostname().split(".")[0]
723
724     netbiosname = determine_netbios_name(hostname)
725
726     if serverrole is None:
727         serverrole = "standalone server"
728
729     assert domain is not None
730     domain = domain.upper()
731
732     assert realm is not None
733     realm = realm.upper()
734
735     global_settings = {
736         "netbios name": netbiosname,
737         "workgroup": domain,
738         "realm": realm,
739         "server role": serverrole,
740     }
741
742     if lp is None:
743         lp = samba.param.LoadParm()
744     # Load non-existent file
745     if os.path.exists(smbconf):
746         lp.load(smbconf)
747
748     if global_param is not None:
749         for ent in global_param:
750             if global_param[ent] is not None:
751                 global_settings[ent] = " ".join(global_param[ent])
752
753     if targetdir is not None:
754         global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
755         global_settings["lock dir"] = os.path.abspath(targetdir)
756         global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
757         global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
758         global_settings["binddns dir"] = os.path.abspath(os.path.join(targetdir, "bind-dns"))
759
760         lp.set("lock dir", os.path.abspath(targetdir))
761         lp.set("state directory", global_settings["state directory"])
762         lp.set("cache directory", global_settings["cache directory"])
763         lp.set("binddns dir", global_settings["binddns dir"])
764
765     if eadb:
766         if use_ntvfs and not lp.get("posix:eadb"):
767             if targetdir is not None:
768                 privdir = os.path.join(targetdir, "private")
769             else:
770                 privdir = lp.get("private dir")
771             lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
772         elif not use_ntvfs and not lp.get("xattr_tdb:file"):
773             if targetdir is not None:
774                 statedir = os.path.join(targetdir, "state")
775             else:
776                 statedir = lp.get("state directory")
777             lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
778
779     shares = {}
780     if serverrole == "active directory domain controller":
781         shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
782         shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
783                                           "scripts")
784     else:
785         global_settings["passdb backend"] = "samba_dsdb"
786
787     f = open(smbconf, 'w')
788     try:
789         f.write("[globals]\n")
790         for key, val in global_settings.items():
791             f.write("\t%s = %s\n" % (key, val))
792         f.write("\n")
793
794         for name, path in shares.items():
795             f.write("[%s]\n" % name)
796             f.write("\tpath = %s\n" % path)
797             f.write("\tread only = no\n")
798             f.write("\n")
799     finally:
800         f.close()
801     # reload the smb.conf
802     lp.load(smbconf)
803
804     # and dump it without any values that are the default
805     # this ensures that any smb.conf parameters that were set
806     # on the provision/join command line are set in the resulting smb.conf
807     lp.dump(False, smbconf)
808
809
810 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
811                         users_gid, root_gid):
812     """setup reasonable name mappings for sam names to unix names.
813
814     :param samdb: SamDB object.
815     :param idmap: IDmap db object.
816     :param sid: The domain sid.
817     :param domaindn: The domain DN.
818     :param root_uid: uid of the UNIX root user.
819     :param nobody_uid: uid of the UNIX nobody user.
820     :param users_gid: gid of the UNIX users group.
821     :param root_gid: gid of the UNIX root group.
822     """
823     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
824
825     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
826     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
827
828
829 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
830                            provision_backend, names, serverrole,
831                            erase=False, plaintext_secrets=False,
832                            backend_store=None):
833     """Setup the partitions for the SAM database.
834
835     Alternatively, provision() may call this, and then populate the database.
836
837     :note: This will wipe the Sam Database!
838
839     :note: This function always removes the local SAM LDB file. The erase
840         parameter controls whether to erase the existing data, which
841         may not be stored locally but in LDAP.
842
843     """
844     assert session_info is not None
845
846     # We use options=["modules:"] to stop the modules loading - we
847     # just want to wipe and re-initialise the database, not start it up
848
849     try:
850         os.unlink(samdb_path)
851     except OSError:
852         pass
853
854     samdb = Ldb(url=samdb_path, session_info=session_info,
855                 lp=lp, options=["modules:"])
856
857     ldap_backend_line = "# No LDAP backend"
858     if provision_backend.type != "ldb":
859         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
860
861     required_features = None
862     if not plaintext_secrets:
863         required_features = "requiredFeatures: encryptedSecrets"
864
865     if backend_store is None:
866         backend_store = get_default_backend_store()
867     backend_store_line = "backendStore: %s" % backend_store
868
869     if backend_store == "mdb":
870         if required_features is not None:
871             required_features += "\n"
872         else:
873             required_features = ""
874         required_features += "requiredFeatures: lmdbLevelOne"
875
876     if required_features is None:
877         required_features = "# No required features"
878
879     samdb.transaction_start()
880     try:
881         logger.info("Setting up sam.ldb partitions and settings")
882         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
883                 "LDAP_BACKEND_LINE": ldap_backend_line,
884                 "BACKEND_STORE": backend_store_line
885         })
886
887         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
888                 "BACKEND_TYPE": provision_backend.type,
889                 "SERVER_ROLE": serverrole,
890                 "REQUIRED_FEATURES": required_features
891                 })
892
893         logger.info("Setting up sam.ldb rootDSE")
894         setup_samdb_rootdse(samdb, names)
895     except:
896         samdb.transaction_cancel()
897         raise
898     else:
899         samdb.transaction_commit()
900
901
902 def secretsdb_self_join(secretsdb, domain,
903                         netbiosname, machinepass, domainsid=None,
904                         realm=None, dnsdomain=None,
905                         keytab_path=None,
906                         key_version_number=1,
907                         secure_channel_type=SEC_CHAN_WKSTA):
908     """Add domain join-specific bits to a secrets database.
909
910     :param secretsdb: Ldb Handle to the secrets database
911     :param machinepass: Machine password
912     """
913     attrs = ["whenChanged",
914              "secret",
915              "priorSecret",
916              "priorChanged",
917              "krb5Keytab",
918              "privateKeytab"]
919
920     if realm is not None:
921         if dnsdomain is None:
922             dnsdomain = realm.lower()
923         dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
924     else:
925         dnsname = None
926     shortname = netbiosname.lower()
927
928     # We don't need to set msg["flatname"] here, because rdn_name will handle
929     # it, and it causes problems for modifies anyway
930     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
931     msg["secureChannelType"] = [str(secure_channel_type)]
932     msg["objectClass"] = ["top", "primaryDomain"]
933     if dnsname is not None:
934         msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
935         msg["realm"] = [realm]
936         msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
937         msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
938         msg["privateKeytab"] = ["secrets.keytab"]
939
940     msg["secret"] = [machinepass.encode('utf-8')]
941     msg["samAccountName"] = ["%s$" % netbiosname]
942     msg["secureChannelType"] = [str(secure_channel_type)]
943     if domainsid is not None:
944         msg["objectSid"] = [ndr_pack(domainsid)]
945
946     # This complex expression tries to ensure that we don't have more
947     # than one record for this SID, realm or netbios domain at a time,
948     # but we don't delete the old record that we are about to modify,
949     # because that would delete the keytab and previous password.
950     res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
951                            expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
952                            scope=ldb.SCOPE_ONELEVEL)
953
954     for del_msg in res:
955         secretsdb.delete(del_msg.dn)
956
957     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
958
959     if len(res) == 1:
960         msg["priorSecret"] = [res[0]["secret"][0]]
961         try:
962             msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
963         except KeyError:
964             pass
965
966         try:
967             msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
968         except KeyError:
969             pass
970
971         try:
972             msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
973         except KeyError:
974             pass
975
976         for el in msg:
977             if el != 'dn':
978                 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
979         secretsdb.modify(msg)
980         secretsdb.rename(res[0].dn, msg.dn)
981     else:
982         spn = ['HOST/%s' % shortname]
983         if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
984             # we are a domain controller then we add servicePrincipalName
985             # entries for the keytab code to update.
986             spn.extend(['HOST/%s' % dnsname])
987         msg["servicePrincipalName"] = spn
988
989         secretsdb.add(msg)
990
991
992 def setup_secretsdb(paths, session_info, backend_credentials, lp):
993     """Setup the secrets database.
994
995     :note: This function does not handle exceptions and transaction on purpose,
996        it's up to the caller to do this job.
997
998     :param path: Path to the secrets database.
999     :param session_info: Session info.
1000     :param credentials: Credentials
1001     :param lp: Loadparm context
1002     :return: LDB handle for the created secrets database
1003     """
1004     if os.path.exists(paths.secrets):
1005         os.unlink(paths.secrets)
1006
1007     keytab_path = os.path.join(paths.private_dir, paths.keytab)
1008     if os.path.exists(keytab_path):
1009         os.unlink(keytab_path)
1010
1011     bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1012     if os.path.exists(bind_dns_keytab_path):
1013         os.unlink(bind_dns_keytab_path)
1014
1015     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1016     if os.path.exists(dns_keytab_path):
1017         os.unlink(dns_keytab_path)
1018
1019     path = paths.secrets
1020
1021     secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1022     secrets_ldb.erase()
1023     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1024     secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1025     secrets_ldb.transaction_start()
1026     try:
1027         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1028
1029         if (backend_credentials is not None and
1030             backend_credentials.authentication_requested()):
1031             if backend_credentials.get_bind_dn() is not None:
1032                 setup_add_ldif(secrets_ldb,
1033                                setup_path("secrets_simple_ldap.ldif"), {
1034                                    "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1035                                    "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1036                                })
1037             else:
1038                 setup_add_ldif(secrets_ldb,
1039                                setup_path("secrets_sasl_ldap.ldif"), {
1040                                    "LDAPADMINUSER": backend_credentials.get_username(),
1041                                    "LDAPADMINREALM": backend_credentials.get_realm(),
1042                                    "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1043                                })
1044     except:
1045         secrets_ldb.transaction_cancel()
1046         raise
1047     return secrets_ldb
1048
1049
1050 def setup_privileges(path, session_info, lp):
1051     """Setup the privileges database.
1052
1053     :param path: Path to the privileges database.
1054     :param session_info: Session info.
1055     :param credentials: Credentials
1056     :param lp: Loadparm context
1057     :return: LDB handle for the created secrets database
1058     """
1059     if os.path.exists(path):
1060         os.unlink(path)
1061     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1062     privilege_ldb.erase()
1063     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1064
1065
1066 def setup_encrypted_secrets_key(path):
1067     """Setup the encrypted secrets key file.
1068
1069     Any existing key file will be deleted and a new random key generated.
1070
1071     :param path: Path to the secrets key file.
1072
1073     """
1074     if os.path.exists(path):
1075         os.unlink(path)
1076
1077     flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1078     mode = stat.S_IRUSR | stat.S_IWUSR
1079
1080     umask_original = os.umask(0)
1081     try:
1082         fd = os.open(path, flags, mode)
1083     finally:
1084         os.umask(umask_original)
1085
1086     with os.fdopen(fd, 'wb') as f:
1087         key = samba.generate_random_bytes(16)
1088         f.write(key)
1089
1090
1091 def setup_registry(path, session_info, lp):
1092     """Setup the registry.
1093
1094     :param path: Path to the registry database
1095     :param session_info: Session information
1096     :param credentials: Credentials
1097     :param lp: Loadparm context
1098     """
1099     reg = samba.registry.Registry()
1100     hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1101     reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1102     provision_reg = setup_path("provision.reg")
1103     assert os.path.exists(provision_reg)
1104     reg.diff_apply(provision_reg)
1105
1106
1107 def setup_idmapdb(path, session_info, lp):
1108     """Setup the idmap database.
1109
1110     :param path: path to the idmap database
1111     :param session_info: Session information
1112     :param credentials: Credentials
1113     :param lp: Loadparm context
1114     """
1115     if os.path.exists(path):
1116         os.unlink(path)
1117
1118     idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1119     idmap_ldb.erase()
1120     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1121     return idmap_ldb
1122
1123
1124 def setup_samdb_rootdse(samdb, names):
1125     """Setup the SamDB rootdse.
1126
1127     :param samdb: Sam Database handle
1128     """
1129     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1130         "SCHEMADN": names.schemadn,
1131         "DOMAINDN": names.domaindn,
1132         "ROOTDN": names.rootdn,
1133         "CONFIGDN": names.configdn,
1134         "SERVERDN": names.serverdn,
1135     })
1136
1137
1138 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1139                     dns_backend, dnspass, domainsid, next_rid, invocationid,
1140                     policyguid, policyguid_dc,
1141                     domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1142     """Join a host to its own domain."""
1143     assert isinstance(invocationid, str)
1144     if ntdsguid is not None:
1145         ntdsguid_line = "objectGUID: %s\n" % ntdsguid
1146     else:
1147         ntdsguid_line = ""
1148
1149     if dc_rid is None:
1150         dc_rid = next_rid
1151
1152     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1153               "CONFIGDN": names.configdn,
1154               "SCHEMADN": names.schemadn,
1155               "DOMAINDN": names.domaindn,
1156               "SERVERDN": names.serverdn,
1157               "INVOCATIONID": invocationid,
1158               "NETBIOSNAME": names.netbiosname,
1159               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1160               "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1161               "DOMAINSID": str(domainsid),
1162               "DCRID": str(dc_rid),
1163               "SAMBA_VERSION_STRING": version,
1164               "NTDSGUID": ntdsguid_line,
1165               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1166                   domainControllerFunctionality),
1167               "RIDALLOCATIONSTART": str(next_rid + 100),
1168               "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1169
1170     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1171               "POLICYGUID": policyguid,
1172               "POLICYGUID_DC": policyguid_dc,
1173               "DNSDOMAIN": names.dnsdomain,
1174               "DOMAINDN": names.domaindn})
1175
1176     # If we are setting up a subdomain, then this has been replicated in, so we
1177     # don't need to add it
1178     if fill == FILL_FULL:
1179         setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1180                 "CONFIGDN": names.configdn,
1181                 "SCHEMADN": names.schemadn,
1182                 "DOMAINDN": names.domaindn,
1183                 "SERVERDN": names.serverdn,
1184                 "INVOCATIONID": invocationid,
1185                 "NETBIOSNAME": names.netbiosname,
1186                 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1187                 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1188                 "DOMAINSID": str(domainsid),
1189                 "DCRID": str(dc_rid),
1190                 "SAMBA_VERSION_STRING": version,
1191                 "NTDSGUID": ntdsguid_line,
1192                 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1193                     domainControllerFunctionality)})
1194
1195     # Setup fSMORoleOwner entries to point at the newly created DC entry
1196         setup_modify_ldif(samdb,
1197                           setup_path("provision_self_join_modify_config.ldif"), {
1198                               "CONFIGDN": names.configdn,
1199                               "SCHEMADN": names.schemadn,
1200                               "DEFAULTSITE": names.sitename,
1201                               "NETBIOSNAME": names.netbiosname,
1202                               "SERVERDN": names.serverdn,
1203                           })
1204
1205     system_session_info = system_session()
1206     samdb.set_session_info(system_session_info)
1207     # Setup fSMORoleOwner entries to point at the newly created DC entry to
1208     # modify a serverReference under cn=config when we are a subdomain, we must
1209     # be system due to ACLs
1210     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1211               "DOMAINDN": names.domaindn,
1212               "SERVERDN": names.serverdn,
1213               "NETBIOSNAME": names.netbiosname,
1214               })
1215
1216     samdb.set_session_info(admin_session_info)
1217
1218     if dns_backend != "SAMBA_INTERNAL":
1219         # This is Samba4 specific and should be replaced by the correct
1220         # DNS AD-style setup
1221         setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1222               "DNSDOMAIN": names.dnsdomain,
1223               "DOMAINDN": names.domaindn,
1224               "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1225               "HOSTNAME": names.hostname,
1226               "DNSNAME": '%s.%s' % (
1227                   names.netbiosname.lower(), names.dnsdomain.lower())
1228               })
1229
1230
1231 def getpolicypath(sysvolpath, dnsdomain, guid):
1232     """Return the physical path of policy given its guid.
1233
1234     :param sysvolpath: Path to the sysvol folder
1235     :param dnsdomain: DNS name of the AD domain
1236     :param guid: The GUID of the policy
1237     :return: A string with the complete path to the policy folder
1238     """
1239     if guid[0] != "{":
1240         guid = "{%s}" % guid
1241     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1242     return policy_path
1243
1244
1245 def create_gpo_struct(policy_path):
1246     if not os.path.exists(policy_path):
1247         os.makedirs(policy_path, 0o775)
1248     f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1249     try:
1250         f.write("[General]\r\nVersion=0")
1251     finally:
1252         f.close()
1253     p = os.path.join(policy_path, "MACHINE")
1254     if not os.path.exists(p):
1255         os.makedirs(p, 0o775)
1256     p = os.path.join(policy_path, "USER")
1257     if not os.path.exists(p):
1258         os.makedirs(p, 0o775)
1259
1260
1261 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1262     """Create the default GPO for a domain
1263
1264     :param sysvolpath: Physical path for the sysvol folder
1265     :param dnsdomain: DNS domain name of the AD domain
1266     :param policyguid: GUID of the default domain policy
1267     :param policyguid_dc: GUID of the default domain controler policy
1268     """
1269     policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid)
1270     create_gpo_struct(policy_path)
1271
1272     policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc)
1273     create_gpo_struct(policy_path)
1274
1275
1276 def setup_samdb(path, session_info, provision_backend, lp, names,
1277                 logger, fill, serverrole, schema, am_rodc=False,
1278                 plaintext_secrets=False, backend_store=None):
1279     """Setup a complete SAM Database.
1280
1281     :note: This will wipe the main SAM database file!
1282     """
1283
1284     # Also wipes the database
1285     setup_samdb_partitions(path, logger=logger, lp=lp,
1286                            provision_backend=provision_backend, session_info=session_info,
1287                            names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1288                            backend_store=backend_store)
1289
1290     # Load the database, but don's load the global schema and don't connect
1291     # quite yet
1292     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1293                   credentials=provision_backend.credentials, lp=lp,
1294                   global_schema=False, am_rodc=am_rodc)
1295
1296     logger.info("Pre-loading the Samba 4 and AD schema")
1297
1298     # Load the schema from the one we computed earlier
1299     samdb.set_schema(schema, write_indices_and_attributes=False)
1300
1301     # Set the NTDS settings DN manually - in order to have it already around
1302     # before the provisioned tree exists and we connect
1303     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1304
1305     # And now we can connect to the DB - the schema won't be loaded from the
1306     # DB
1307     try:
1308         samdb.connect(path)
1309     except ldb.LdbError as e2:
1310         (num, string_error) = e2.args
1311         if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1312             raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1313         else:
1314             raise
1315
1316     # But we have to give it one more kick to have it use the schema
1317     # during provision - it needs, now that it is connected, to write
1318     # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1319     samdb.set_schema(schema, write_indices_and_attributes=True)
1320
1321     return samdb
1322
1323
1324 def fill_samdb(samdb, lp, names, logger, policyguid,
1325                policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1326                dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1327                dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1328                backend_store=None):
1329
1330     if next_rid is None:
1331         next_rid = 1000
1332
1333     # Provision does not make much sense values larger than 1000000000
1334     # as the upper range of the rIDAvailablePool is 1073741823 and
1335     # we don't want to create a domain that cannot allocate rids.
1336     if next_rid < 1000 or next_rid > 1000000000:
1337         error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1338         error += "the valid range is %u-%u. The default is %u." % (
1339             1000, 1000000000, 1000)
1340         raise ProvisioningError(error)
1341
1342     # ATTENTION: Do NOT change these default values without discussion with the
1343     # team and/or release manager. They have a big impact on the whole program!
1344     domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1345
1346     if dom_for_fun_level is None:
1347         dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1348
1349     if dom_for_fun_level > domainControllerFunctionality:
1350         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!")
1351
1352     domainFunctionality = dom_for_fun_level
1353     forestFunctionality = dom_for_fun_level
1354
1355     # Set the NTDS settings DN manually - in order to have it already around
1356     # before the provisioned tree exists and we connect
1357     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1358
1359     # Set the domain functionality levels onto the database.
1360     # Various module (the password_hash module in particular) need
1361     # to know what level of AD we are emulating.
1362
1363     # These will be fixed into the database via the database
1364     # modifictions below, but we need them set from the start.
1365     samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1366     samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1367     samdb.set_opaque_integer("domainControllerFunctionality",
1368                              domainControllerFunctionality)
1369
1370     samdb.set_domain_sid(str(names.domainsid))
1371     samdb.set_invocation_id(invocationid)
1372
1373     logger.info("Adding DomainDN: %s" % names.domaindn)
1374
1375     # impersonate domain admin
1376     admin_session_info = admin_session(lp, str(names.domainsid))
1377     samdb.set_session_info(admin_session_info)
1378     if names.domainguid is not None:
1379         domainguid_line = "objectGUID: %s\n-" % names.domainguid
1380     else:
1381         domainguid_line = ""
1382
1383     descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1384     setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1385             "DOMAINDN": names.domaindn,
1386             "DOMAINSID": str(names.domainsid),
1387             "DESCRIPTOR": descr,
1388             "DOMAINGUID": domainguid_line
1389             })
1390
1391     setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1392         "DOMAINDN": names.domaindn,
1393         "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1394         "NEXTRID": str(next_rid),
1395         "DEFAULTSITE": names.sitename,
1396         "CONFIGDN": names.configdn,
1397         "POLICYGUID": policyguid,
1398         "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1399         "SAMBA_VERSION_STRING": version,
1400         "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1401     })
1402
1403     # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1404     if fill == FILL_FULL:
1405         logger.info("Adding configuration container")
1406         descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1407         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1408                 "CONFIGDN": names.configdn,
1409                 "DESCRIPTOR": descr,
1410                 })
1411
1412         # The LDIF here was created when the Schema object was constructed
1413         ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1414         logger.info("Setting up sam.ldb schema")
1415         samdb.add_ldif(schema.schema_dn_add,
1416                        controls=["relax:0", ignore_checks_oid])
1417         samdb.modify_ldif(schema.schema_dn_modify,
1418                           controls=[ignore_checks_oid])
1419         samdb.write_prefixes_from_schema()
1420         samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1421         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1422                        {"SCHEMADN": names.schemadn},
1423                        controls=["relax:0", ignore_checks_oid])
1424
1425     # Now register this container in the root of the forest
1426     msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1427     msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
1428                                         "subRefs")
1429
1430     samdb.invocation_id = invocationid
1431
1432     # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1433     if fill == FILL_FULL:
1434         logger.info("Setting up sam.ldb configuration data")
1435
1436         partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1437         sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1438         ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1439         protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1440         protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1441         protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1442
1443         if "2008" in schema.base_schema:
1444             # exclude 2012-specific changes if we're using a 2008 schema
1445             incl_2012 = "#"
1446         else:
1447             incl_2012 = ""
1448
1449         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1450                 "CONFIGDN": names.configdn,
1451                 "NETBIOSNAME": names.netbiosname,
1452                 "DEFAULTSITE": names.sitename,
1453                 "DNSDOMAIN": names.dnsdomain,
1454                 "DOMAIN": names.domain,
1455                 "SCHEMADN": names.schemadn,
1456                 "DOMAINDN": names.domaindn,
1457                 "SERVERDN": names.serverdn,
1458                 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1459                 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1460                 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1461                 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1462                 "SERVICES_DESCRIPTOR": protected1_descr,
1463                 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1464                 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1465                 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1466                 "PARTITIONS_DESCRIPTOR": partitions_descr,
1467                 "SITES_DESCRIPTOR": sites_descr,
1468                 })
1469
1470         setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1471                 "CONFIGDN": names.configdn,
1472                 "INC2012": incl_2012,
1473                 })
1474
1475         logger.info("Setting up display specifiers")
1476         display_specifiers_ldif = read_ms_ldif(
1477             setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1478         display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1479                                                  {"CONFIGDN": names.configdn})
1480         check_all_substituted(display_specifiers_ldif)
1481         samdb.add_ldif(display_specifiers_ldif)
1482
1483         logger.info("Modifying display specifiers and extended rights")
1484         setup_modify_ldif(samdb,
1485                           setup_path("provision_configuration_modify.ldif"), {
1486                               "CONFIGDN": names.configdn,
1487                               "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1488                           })
1489
1490     logger.info("Adding users container")
1491     users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1492     setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1493             "DOMAINDN": names.domaindn,
1494             "USERS_DESCRIPTOR": users_desc
1495             })
1496     logger.info("Modifying users container")
1497     setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1498             "DOMAINDN": names.domaindn})
1499     logger.info("Adding computers container")
1500     computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1501     setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1502             "DOMAINDN": names.domaindn,
1503             "COMPUTERS_DESCRIPTOR": computers_desc
1504             })
1505     logger.info("Modifying computers container")
1506     setup_modify_ldif(samdb,
1507                       setup_path("provision_computers_modify.ldif"), {
1508                           "DOMAINDN": names.domaindn})
1509     logger.info("Setting up sam.ldb data")
1510     infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1511     lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1512     system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1513     builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1514     controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1515     setup_add_ldif(samdb, setup_path("provision.ldif"), {
1516         "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1517         "DOMAINDN": names.domaindn,
1518         "NETBIOSNAME": names.netbiosname,
1519         "DEFAULTSITE": names.sitename,
1520         "CONFIGDN": names.configdn,
1521         "SERVERDN": names.serverdn,
1522         "RIDAVAILABLESTART": str(next_rid + 600),
1523         "POLICYGUID_DC": policyguid_dc,
1524         "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1525         "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1526         "SYSTEM_DESCRIPTOR": system_desc,
1527         "BUILTIN_DESCRIPTOR": builtin_desc,
1528         "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1529     })
1530
1531     # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1532     if fill == FILL_FULL:
1533         managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1534         setup_modify_ldif(samdb,
1535                           setup_path("provision_configuration_references.ldif"), {
1536                               "CONFIGDN": names.configdn,
1537                               "SCHEMADN": names.schemadn})
1538
1539         logger.info("Setting up well known security principals")
1540         protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1541         setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1542             "CONFIGDN": names.configdn,
1543             "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1544         }, controls=["relax:0", "provision:0"])
1545
1546     if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1547         setup_modify_ldif(samdb,
1548                           setup_path("provision_basedn_references.ldif"), {
1549                               "DOMAINDN": names.domaindn,
1550                               "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1551                           })
1552
1553         logger.info("Setting up sam.ldb users and groups")
1554         setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1555             "DOMAINDN": names.domaindn,
1556             "DOMAINSID": str(names.domainsid),
1557             "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1558             "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1559         }, controls=["relax:0", "provision:0"])
1560
1561         logger.info("Setting up self join")
1562         setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1563                         invocationid=invocationid,
1564                         dns_backend=dns_backend,
1565                         dnspass=dnspass,
1566                         machinepass=machinepass,
1567                         domainsid=names.domainsid,
1568                         next_rid=next_rid,
1569                         dc_rid=dc_rid,
1570                         policyguid=policyguid,
1571                         policyguid_dc=policyguid_dc,
1572                         domainControllerFunctionality=domainControllerFunctionality,
1573                         ntdsguid=ntdsguid)
1574
1575         ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1576         names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1577                                          attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE).decode('utf8')
1578         assert isinstance(names.ntdsguid, string_types)
1579
1580     return samdb
1581
1582
1583 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1584 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)"
1585 SYSVOL_SERVICE = "sysvol"
1586
1587
1588 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1589     setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1590     for root, dirs, files in os.walk(path, topdown=False):
1591         for name in files:
1592             setntacl(lp, os.path.join(root, name), acl, domsid,
1593                      use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1594         for name in dirs:
1595             setntacl(lp, os.path.join(root, name), acl, domsid,
1596                      use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1597
1598
1599 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1600     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1601     folders beneath.
1602
1603     :param sysvol: Physical path for the sysvol folder
1604     :param dnsdomain: The DNS name of the domain
1605     :param domainsid: The SID of the domain
1606     :param domaindn: The DN of the domain (ie. DC=...)
1607     :param samdb: An LDB object on the SAM db
1608     :param lp: an LP object
1609     """
1610
1611     # Set ACL for GPO root folder
1612     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1613     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1614              use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1615
1616     res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1617                        attrs=["cn", "nTSecurityDescriptor"],
1618                        expression="", scope=ldb.SCOPE_ONELEVEL)
1619
1620     for policy in res:
1621         acl = ndr_unpack(security.descriptor,
1622                          policy["nTSecurityDescriptor"][0]).as_sddl()
1623         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1624         set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1625                     str(domainsid), use_ntvfs,
1626                     passdb=passdb)
1627
1628
1629 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1630                  domaindn, lp, use_ntvfs):
1631     """Set the ACL for the sysvol share and the subfolders
1632
1633     :param samdb: An LDB object on the SAM db
1634     :param netlogon: Physical path for the netlogon folder
1635     :param sysvol: Physical path for the sysvol folder
1636     :param uid: The UID of the "Administrator" user
1637     :param gid: The GID of the "Domain adminstrators" group
1638     :param domainsid: The SID of the domain
1639     :param dnsdomain: The DNS name of the domain
1640     :param domaindn: The DN of the domain (ie. DC=...)
1641     """
1642     s4_passdb = None
1643
1644     if not use_ntvfs:
1645         s3conf = s3param.get_context()
1646         s3conf.load(lp.configfile)
1647
1648         file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1649         try:
1650             try:
1651                 smbd.set_simple_acl(file.name, 0o755, gid)
1652             except OSError:
1653                 if not smbd.have_posix_acls():
1654                     # This clue is only strictly correct for RPM and
1655                     # Debian-like Linux systems, but hopefully other users
1656                     # will get enough clue from it.
1657                     raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires.  "
1658                                             "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1659
1660                 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires.  "
1661                                         "Try the mounting the filesystem with the 'acl' option.")
1662             try:
1663                 smbd.chown(file.name, uid, gid)
1664             except OSError:
1665                 raise ProvisioningError("Unable to chown a file on your filesystem.  "
1666                                         "You may not be running provision as root.")
1667         finally:
1668             file.close()
1669
1670         # This will ensure that the smbd code we are running when setting ACLs
1671         # is initialised with the smb.conf
1672         s3conf = s3param.get_context()
1673         s3conf.load(lp.configfile)
1674         # ensure we are using the right samba_dsdb passdb backend, no matter what
1675         s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1676         passdb.reload_static_pdb()
1677
1678         # ensure that we init the samba_dsdb backend, so the domain sid is
1679         # marked in secrets.tdb
1680         s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1681
1682         # now ensure everything matches correctly, to avoid wierd issues
1683         if passdb.get_global_sam_sid() != domainsid:
1684             raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid))
1685
1686         domain_info = s4_passdb.domain_info()
1687         if domain_info["dom_sid"] != domainsid:
1688             raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
1689
1690         if domain_info["dns_domain"].upper() != dnsdomain.upper():
1691             raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
1692
1693     try:
1694         if use_ntvfs:
1695             os.chown(sysvol, -1, gid)
1696     except OSError:
1697         canchown = False
1698     else:
1699         canchown = True
1700
1701     # use admin sid dn as user dn, since admin should own most of the files,
1702     # the operation will be much faster
1703     userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1704
1705     flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1706              auth.AUTH_SESSION_INFO_AUTHENTICATED |
1707              auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1708
1709     session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1710                                      session_info_flags=flags)
1711
1712     def _setntacl(path):
1713         """A helper to reuse args"""
1714         return setntacl(
1715             lp, path, SYSVOL_ACL, str(domainsid),
1716             use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
1717             service=SYSVOL_SERVICE, session_info=session_info)
1718
1719     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1720     _setntacl(sysvol)
1721     for root, dirs, files in os.walk(sysvol, topdown=False):
1722         for name in files:
1723             if use_ntvfs and canchown:
1724                 os.chown(os.path.join(root, name), -1, gid)
1725             _setntacl(os.path.join(root, name))
1726         for name in dirs:
1727             if use_ntvfs and canchown:
1728                 os.chown(os.path.join(root, name), -1, gid)
1729             _setntacl(os.path.join(root, name))
1730
1731     # Set acls on Policy folder and policies folders
1732     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1733
1734
1735 def acl_type(direct_db_access):
1736     if direct_db_access:
1737         return "DB"
1738     else:
1739         return "VFS"
1740
1741
1742 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1743     fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1744     fsacl_sddl = fsacl.as_sddl(domainsid)
1745     if fsacl_sddl != acl:
1746         raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), path, fsacl_sddl, acl))
1747
1748     for root, dirs, files in os.walk(path, topdown=False):
1749         for name in files:
1750             fsacl = getntacl(lp, os.path.join(root, name),
1751                              direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1752             if fsacl is None:
1753                 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1754             fsacl_sddl = fsacl.as_sddl(domainsid)
1755             if fsacl_sddl != acl:
1756                 raise ProvisioningError('%s ACL on GPO file %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl))
1757
1758         for name in dirs:
1759             fsacl = getntacl(lp, os.path.join(root, name),
1760                              direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1761             if fsacl is None:
1762                 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1763             fsacl_sddl = fsacl.as_sddl(domainsid)
1764             if fsacl_sddl != acl:
1765                 raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl))
1766
1767
1768 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1769                    direct_db_access):
1770     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1771     folders beneath.
1772
1773     :param sysvol: Physical path for the sysvol folder
1774     :param dnsdomain: The DNS name of the domain
1775     :param domainsid: The SID of the domain
1776     :param domaindn: The DN of the domain (ie. DC=...)
1777     :param samdb: An LDB object on the SAM db
1778     :param lp: an LP object
1779     """
1780
1781     # Set ACL for GPO root folder
1782     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1783     fsacl = getntacl(lp, root_policy_path,
1784                      direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1785     if fsacl is None:
1786         raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1787     fsacl_sddl = fsacl.as_sddl(domainsid)
1788     if fsacl_sddl != POLICIES_ACL:
1789         raise ProvisioningError('%s ACL on policy root %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), root_policy_path, fsacl_sddl, fsacl))
1790     res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1791                        attrs=["cn", "nTSecurityDescriptor"],
1792                        expression="", scope=ldb.SCOPE_ONELEVEL)
1793
1794     for policy in res:
1795         acl = ndr_unpack(security.descriptor,
1796                          policy["nTSecurityDescriptor"][0]).as_sddl()
1797         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1798         check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1799                       domainsid, direct_db_access)
1800
1801
1802 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1803                    lp):
1804     """Set the ACL for the sysvol share and the subfolders
1805
1806     :param samdb: An LDB object on the SAM db
1807     :param netlogon: Physical path for the netlogon folder
1808     :param sysvol: Physical path for the sysvol folder
1809     :param uid: The UID of the "Administrator" user
1810     :param gid: The GID of the "Domain adminstrators" group
1811     :param domainsid: The SID of the domain
1812     :param dnsdomain: The DNS name of the domain
1813     :param domaindn: The DN of the domain (ie. DC=...)
1814     """
1815
1816     # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1817     s3conf = s3param.get_context()
1818     s3conf.load(lp.configfile)
1819     # ensure we are using the right samba_dsdb passdb backend, no matter what
1820     s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1821     # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1822     s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1823
1824     # now ensure everything matches correctly, to avoid wierd issues
1825     if passdb.get_global_sam_sid() != domainsid:
1826         raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid))
1827
1828     domain_info = s4_passdb.domain_info()
1829     if domain_info["dom_sid"] != domainsid:
1830         raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
1831
1832     if domain_info["dns_domain"].upper() != dnsdomain.upper():
1833         raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
1834
1835     # Ensure we can read this directly, and via the smbd VFS
1836     for direct_db_access in [True, False]:
1837         # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1838         for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1839             fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1840             if fsacl is None:
1841                 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1842             fsacl_sddl = fsacl.as_sddl(domainsid)
1843             if fsacl_sddl != SYSVOL_ACL:
1844                 raise ProvisioningError('%s ACL on sysvol directory %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), dir_path, fsacl_sddl, SYSVOL_ACL))
1845
1846         # Check acls on Policy folder and policies folders
1847         check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1848                        direct_db_access)
1849
1850
1851 def interface_ips_v4(lp):
1852     """return only IPv4 IPs"""
1853     ips = samba.interface_ips(lp, False)
1854     ret = []
1855     for i in ips:
1856         if i.find(':') == -1:
1857             ret.append(i)
1858     return ret
1859
1860
1861 def interface_ips_v6(lp):
1862     """return only IPv6 IPs"""
1863     ips = samba.interface_ips(lp, False)
1864     ret = []
1865     for i in ips:
1866         if i.find(':') != -1:
1867             ret.append(i)
1868     return ret
1869
1870
1871 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1872                    schema=None,
1873                    targetdir=None, samdb_fill=FILL_FULL,
1874                    hostip=None, hostip6=None,
1875                    next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1876                    domainguid=None, policyguid=None, policyguid_dc=None,
1877                    invocationid=None, machinepass=None, ntdsguid=None,
1878                    dns_backend=None, dnspass=None,
1879                    serverrole=None, dom_for_fun_level=None,
1880                    am_rodc=False, lp=None, use_ntvfs=False,
1881                    skip_sysvolacl=False, backend_store=None):
1882     # create/adapt the group policy GUIDs
1883     # Default GUID for default policy are described at
1884     # "How Core Group Policy Works"
1885     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1886     if policyguid is None:
1887         policyguid = DEFAULT_POLICY_GUID
1888     policyguid = policyguid.upper()
1889     if policyguid_dc is None:
1890         policyguid_dc = DEFAULT_DC_POLICY_GUID
1891     policyguid_dc = policyguid_dc.upper()
1892
1893     if invocationid is None:
1894         invocationid = str(uuid.uuid4())
1895
1896     if krbtgtpass is None:
1897         krbtgtpass = samba.generate_random_machine_password(128, 255)
1898     if machinepass is None:
1899         machinepass = samba.generate_random_machine_password(128, 255)
1900     if dnspass is None:
1901         dnspass = samba.generate_random_password(128, 255)
1902
1903     samdb.transaction_start()
1904     try:
1905         samdb = fill_samdb(samdb, lp, names, logger=logger,
1906                            schema=schema,
1907                            policyguid=policyguid, policyguid_dc=policyguid_dc,
1908                            fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1909                            invocationid=invocationid, machinepass=machinepass,
1910                            dns_backend=dns_backend, dnspass=dnspass,
1911                            ntdsguid=ntdsguid, serverrole=serverrole,
1912                            dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1913                            next_rid=next_rid, dc_rid=dc_rid,
1914                            backend_store=backend_store)
1915
1916         # Set up group policies (domain policy and domain controller
1917         # policy)
1918         if serverrole == "active directory domain controller":
1919             create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1920                                policyguid_dc)
1921     except:
1922         samdb.transaction_cancel()
1923         raise
1924     else:
1925         samdb.transaction_commit()
1926
1927     if serverrole == "active directory domain controller":
1928         # Continue setting up sysvol for GPO. This appears to require being
1929         # outside a transaction.
1930         if not skip_sysvolacl:
1931             setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1932                          paths.root_gid, names.domainsid, names.dnsdomain,
1933                          names.domaindn, lp, use_ntvfs)
1934         else:
1935             logger.info("Setting acl on sysvol skipped")
1936
1937         secretsdb_self_join(secrets_ldb, domain=names.domain,
1938                             realm=names.realm, dnsdomain=names.dnsdomain,
1939                             netbiosname=names.netbiosname, domainsid=names.domainsid,
1940                             machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1941
1942         # Now set up the right msDS-SupportedEncryptionTypes into the DB
1943         # In future, this might be determined from some configuration
1944         kerberos_enctypes = str(ENC_ALL_TYPES)
1945
1946         try:
1947             msg = ldb.Message(ldb.Dn(samdb,
1948                                      samdb.searchone("distinguishedName",
1949                                                      expression="samAccountName=%s$" % names.netbiosname,
1950                                                      scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1951             msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1952                 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1953                 name="msDS-SupportedEncryptionTypes")
1954             samdb.modify(msg)
1955         except ldb.LdbError as e:
1956             (enum, estr) = e.args
1957             if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1958                 # It might be that this attribute does not exist in this schema
1959                 raise
1960
1961         setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1962                      hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1963                      dnspass=dnspass, os_level=dom_for_fun_level,
1964                      targetdir=targetdir, fill_level=samdb_fill,
1965                      backend_store=backend_store)
1966
1967         domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1968                                      attribute="objectGUID").decode('utf8')
1969         assert isinstance(domainguid, string_types)
1970
1971     lastProvisionUSNs = get_last_provision_usn(samdb)
1972     maxUSN = get_max_usn(samdb, str(names.rootdn))
1973     if lastProvisionUSNs is not None:
1974         update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1975     else:
1976         set_provision_usn(samdb, 0, maxUSN, invocationid)
1977
1978     logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1979     setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1980                       {'NTDSGUID': names.ntdsguid})
1981
1982     # fix any dangling GUIDs from the provision
1983     logger.info("Fixing provision GUIDs")
1984     chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1985                   quiet=True)
1986     samdb.transaction_start()
1987     try:
1988         # a small number of GUIDs are missing because of ordering issues in the
1989         # provision code
1990         for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1991             chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1992                                scope=ldb.SCOPE_BASE,
1993                                attrs=['defaultObjectCategory'])
1994         chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1995                            scope=ldb.SCOPE_ONELEVEL,
1996                            attrs=['ipsecOwnersReference',
1997                                   'ipsecFilterReference',
1998                                   'ipsecISAKMPReference',
1999                                   'ipsecNegotiationPolicyReference',
2000                                   'ipsecNFAReference'])
2001         if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
2002                               attrs=['attributeId', 'governsId']) != 0:
2003             raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2004     except:
2005         samdb.transaction_cancel()
2006         raise
2007     else:
2008         samdb.transaction_commit()
2009
2010
2011 _ROLES_MAP = {
2012     "ROLE_STANDALONE": "standalone server",
2013     "ROLE_DOMAIN_MEMBER": "member server",
2014     "ROLE_DOMAIN_BDC": "active directory domain controller",
2015     "ROLE_DOMAIN_PDC": "active directory domain controller",
2016     "dc": "active directory domain controller",
2017     "member": "member server",
2018     "domain controller": "active directory domain controller",
2019     "active directory domain controller": "active directory domain controller",
2020     "member server": "member server",
2021     "standalone": "standalone server",
2022     "standalone server": "standalone server",
2023 }
2024
2025
2026 def sanitize_server_role(role):
2027     """Sanitize a server role name.
2028
2029     :param role: Server role
2030     :raise ValueError: If the role can not be interpreted
2031     :return: Sanitized server role (one of "member server",
2032         "active directory domain controller", "standalone server")
2033     """
2034     try:
2035         return _ROLES_MAP[role]
2036     except KeyError:
2037         raise ValueError(role)
2038
2039
2040 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2041                             maxuid, maxgid):
2042     """Create AD entries for the fake ypserver.
2043
2044     This is needed for being able to manipulate posix attrs via ADUC.
2045     """
2046     samdb.transaction_start()
2047     try:
2048         logger.info("Setting up fake yp server settings")
2049         setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2050             "DOMAINDN": domaindn,
2051             "NETBIOSNAME": netbiosname,
2052             "NISDOMAIN": nisdomain,
2053         })
2054     except:
2055         samdb.transaction_cancel()
2056         raise
2057     else:
2058         samdb.transaction_commit()
2059
2060
2061 def directory_create_or_exists(path, mode=0o755):
2062     if not os.path.exists(path):
2063         try:
2064             os.mkdir(path, mode)
2065         except OSError as e:
2066             if e.errno in [errno.EEXIST]:
2067                 pass
2068             else:
2069                 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2070
2071
2072 def determine_host_ip(logger, lp, hostip=None):
2073     if hostip is None:
2074         logger.info("Looking up IPv4 addresses")
2075         hostips = interface_ips_v4(lp)
2076         if len(hostips) > 0:
2077             hostip = hostips[0]
2078             if len(hostips) > 1:
2079                 logger.warning("More than one IPv4 address found. Using %s",
2080                                hostip)
2081     if hostip == "127.0.0.1":
2082         hostip = None
2083     if hostip is None:
2084         logger.warning("No IPv4 address will be assigned")
2085
2086     return hostip
2087
2088
2089 def determine_host_ip6(logger, lp, hostip6=None):
2090     if hostip6 is None:
2091         logger.info("Looking up IPv6 addresses")
2092         hostips = interface_ips_v6(lp)
2093         if hostips:
2094             hostip6 = hostips[0]
2095         if len(hostips) > 1:
2096             logger.warning("More than one IPv6 address found. Using %s", hostip6)
2097     if hostip6 is None:
2098         logger.warning("No IPv6 address will be assigned")
2099
2100     return hostip6
2101
2102
2103 def provision(logger, session_info, smbconf=None,
2104               targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2105               domaindn=None, schemadn=None, configdn=None, serverdn=None,
2106               domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2107               next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2108               krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2109               dns_backend=None, dns_forwarder=None, dnspass=None,
2110               invocationid=None, machinepass=None, ntdsguid=None,
2111               root=None, nobody=None, users=None, backup=None, aci=None,
2112               serverrole=None, dom_for_fun_level=None, backend_type=None,
2113               sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2114               useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2115               use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2116               ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2117               ldap_backend_extra_port=None, base_schema=None,
2118               plaintext_secrets=False, backend_store=None):
2119     """Provision samba4
2120
2121     :note: caution, this wipes all existing data!
2122     """
2123
2124     try:
2125         serverrole = sanitize_server_role(serverrole)
2126     except ValueError:
2127         raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2128
2129     if ldapadminpass is None:
2130         # Make a new, random password between Samba and it's LDAP server
2131         ldapadminpass = samba.generate_random_password(128, 255)
2132
2133     if backend_type is None:
2134         backend_type = "ldb"
2135     if backend_store is None:
2136         backend_store = get_default_backend_store()
2137
2138     if domainsid is None:
2139         domainsid = security.random_sid()
2140
2141     root_uid = findnss_uid([root or "root"])
2142     nobody_uid = findnss_uid([nobody or "nobody"])
2143     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2144     root_gid = pwd.getpwuid(root_uid).pw_gid
2145
2146     try:
2147         bind_gid = findnss_gid(["bind", "named"])
2148     except KeyError:
2149         bind_gid = None
2150
2151     if targetdir is not None:
2152         smbconf = os.path.join(targetdir, "etc", "smb.conf")
2153     elif smbconf is None:
2154         smbconf = samba.param.default_path()
2155     if not os.path.exists(os.path.dirname(smbconf)):
2156         os.makedirs(os.path.dirname(smbconf))
2157
2158     server_services = []
2159     global_param = {}
2160     if use_rfc2307:
2161         global_param["idmap_ldb:use rfc2307"] = ["yes"]
2162
2163     if dns_backend != "SAMBA_INTERNAL":
2164         server_services.append("-dns")
2165     else:
2166         if dns_forwarder is not None:
2167             global_param["dns forwarder"] = [dns_forwarder]
2168
2169     if use_ntvfs:
2170         server_services.append("+smb")
2171         server_services.append("-s3fs")
2172         global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2173
2174     if len(server_services) > 0:
2175         global_param["server services"] = server_services
2176
2177     # only install a new smb.conf if there isn't one there already
2178     if os.path.exists(smbconf):
2179         # if Samba Team members can't figure out the weird errors
2180         # loading an empty smb.conf gives, then we need to be smarter.
2181         # Pretend it just didn't exist --abartlet
2182         f = open(smbconf, 'r')
2183         try:
2184             data = f.read().lstrip()
2185         finally:
2186             f.close()
2187         if data is None or data == "":
2188             make_smbconf(smbconf, hostname, domain, realm,
2189                          targetdir, serverrole=serverrole,
2190                          eadb=useeadb, use_ntvfs=use_ntvfs,
2191                          lp=lp, global_param=global_param)
2192     else:
2193         make_smbconf(smbconf, hostname, domain, realm, targetdir,
2194                      serverrole=serverrole,
2195                      eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2196
2197     if lp is None:
2198         lp = samba.param.LoadParm()
2199     lp.load(smbconf)
2200     names = guess_names(lp=lp, hostname=hostname, domain=domain,
2201                         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2202                         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2203                         sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2204     paths = provision_paths_from_lp(lp, names.dnsdomain)
2205
2206     paths.bind_gid = bind_gid
2207     paths.root_uid = root_uid
2208     paths.root_gid = root_gid
2209
2210     hostip = determine_host_ip(logger, lp, hostip)
2211     hostip6 = determine_host_ip6(logger, lp, hostip6)
2212     names.hostip = hostip
2213     names.hostip6 = hostip6
2214     names.domainguid = domainguid
2215     names.domainsid = domainsid
2216     names.forestsid = domainsid
2217
2218     if serverrole is None:
2219         serverrole = lp.get("server role")
2220
2221     directory_create_or_exists(paths.private_dir, 0o700)
2222     directory_create_or_exists(paths.binddns_dir, 0o770)
2223     directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2224     directory_create_or_exists(paths.state_dir)
2225     if not plaintext_secrets:
2226         setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2227
2228     if paths.sysvol and not os.path.exists(paths.sysvol):
2229         os.makedirs(paths.sysvol, 0o775)
2230
2231     ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
2232
2233     schema = Schema(domainsid, invocationid=invocationid,
2234                     schemadn=names.schemadn, base_schema=base_schema)
2235
2236     if backend_type == "ldb":
2237         provision_backend = LDBBackend(backend_type, paths=paths,
2238                                        lp=lp,
2239                                        names=names, logger=logger)
2240     elif backend_type == "existing":
2241         # If support for this is ever added back, then the URI will need to be
2242         # specified again
2243         provision_backend = ExistingBackend(backend_type, paths=paths,
2244                                             lp=lp,
2245                                             names=names, logger=logger,
2246                                             ldap_backend_forced_uri=ldap_backend_forced_uri)
2247     elif backend_type == "fedora-ds":
2248         provision_backend = FDSBackend(backend_type, paths=paths,
2249                                        lp=lp,
2250                                        names=names, logger=logger, domainsid=domainsid,
2251                                        schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2252                                        slapd_path=slapd_path,
2253                                        root=root)
2254     elif backend_type == "openldap":
2255         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2256                                             lp=lp,
2257                                             names=names, logger=logger, domainsid=domainsid,
2258                                             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2259                                             slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2260                                             ldap_backend_extra_port=ldap_backend_extra_port,
2261                                             ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2262                                             ldap_backend_forced_uri=ldap_backend_forced_uri)
2263     else:
2264         raise ValueError("Unknown LDAP backend type selected")
2265
2266     provision_backend.init()
2267     provision_backend.start()
2268
2269     # only install a new shares config db if there is none
2270     if not os.path.exists(paths.shareconf):
2271         logger.info("Setting up share.ldb")
2272         share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2273         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2274
2275     logger.info("Setting up secrets.ldb")
2276     secrets_ldb = setup_secretsdb(paths,
2277                                   session_info=session_info,
2278                                   backend_credentials=provision_backend.credentials, lp=lp)
2279
2280     try:
2281         logger.info("Setting up the registry")
2282         setup_registry(paths.hklm, session_info, lp=lp)
2283
2284         logger.info("Setting up the privileges database")
2285         setup_privileges(paths.privilege, session_info, lp=lp)
2286
2287         logger.info("Setting up idmap db")
2288         idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2289
2290         setup_name_mappings(idmap, sid=str(domainsid),
2291                             root_uid=root_uid, nobody_uid=nobody_uid,
2292                             users_gid=users_gid, root_gid=root_gid)
2293
2294         logger.info("Setting up SAM db")
2295         samdb = setup_samdb(paths.samdb, session_info,
2296                             provision_backend, lp, names, logger=logger,
2297                             serverrole=serverrole,
2298                             schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2299                             plaintext_secrets=plaintext_secrets,
2300                             backend_store=backend_store)
2301
2302         if serverrole == "active directory domain controller":
2303             if paths.netlogon is None:
2304                 raise MissingShareError("netlogon", paths.smbconf)
2305
2306             if paths.sysvol is None:
2307                 raise MissingShareError("sysvol", paths.smbconf)
2308
2309             if not os.path.isdir(paths.netlogon):
2310                 os.makedirs(paths.netlogon, 0o755)
2311
2312         if adminpass is None:
2313             adminpass = samba.generate_random_password(12, 32)
2314             adminpass_generated = True
2315         else:
2316             if isinstance(adminpass, binary_type):
2317                 adminpass = adminpass.decode('utf-8')
2318             adminpass_generated = False
2319
2320         if samdb_fill == FILL_FULL:
2321             provision_fill(samdb, secrets_ldb, logger, names, paths,
2322                            schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2323                            hostip=hostip, hostip6=hostip6,
2324                            next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2325                            krbtgtpass=krbtgtpass,
2326                            policyguid=policyguid, policyguid_dc=policyguid_dc,
2327                            invocationid=invocationid, machinepass=machinepass,
2328                            ntdsguid=ntdsguid, dns_backend=dns_backend,
2329                            dnspass=dnspass, serverrole=serverrole,
2330                            dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2331                            lp=lp, use_ntvfs=use_ntvfs,
2332                            skip_sysvolacl=skip_sysvolacl,
2333                            backend_store=backend_store)
2334
2335         if not is_heimdal_built():
2336             create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2337             logger.info("The Kerberos KDC configuration for Samba AD is "
2338                         "located at %s", paths.kdcconf)
2339
2340         create_krb5_conf(paths.krb5conf,
2341                          dnsdomain=names.dnsdomain, hostname=names.hostname,
2342                          realm=names.realm)
2343         logger.info("A Kerberos configuration suitable for Samba AD has been "
2344                     "generated at %s", paths.krb5conf)
2345         logger.info("Merge the contents of this file with your system "
2346                     "krb5.conf or replace it with this one. Do not create a "
2347                     "symlink!")
2348
2349         if serverrole == "active directory domain controller":
2350             create_dns_update_list(lp, logger, paths)
2351
2352         backend_result = provision_backend.post_setup()
2353         provision_backend.shutdown()
2354
2355     except:
2356         secrets_ldb.transaction_cancel()
2357         raise
2358
2359     # Now commit the secrets.ldb to disk
2360     secrets_ldb.transaction_commit()
2361
2362     # the commit creates the dns.keytab in the private directory
2363     private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2364     bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2365
2366     if os.path.isfile(private_dns_keytab_path):
2367         if os.path.isfile(bind_dns_keytab_path):
2368             try:
2369                 os.unlink(bind_dns_keytab_path)
2370             except OSError as e:
2371                 logger.error("Failed to remove %s: %s" %
2372                              (bind_dns_keytab_path, e.strerror))
2373
2374         # link the dns.keytab to the bind-dns directory
2375         try:
2376             os.link(private_dns_keytab_path, bind_dns_keytab_path)
2377         except OSError as e:
2378             logger.error("Failed to create link %s -> %s: %s" %
2379                          (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2380
2381         # chown the dns.keytab in the bind-dns directory
2382         if paths.bind_gid is not None:
2383             try:
2384                 os.chmod(paths.binddns_dir, 0o770)
2385                 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2386             except OSError:
2387                 if 'SAMBA_SELFTEST' not in os.environ:
2388                     logger.info("Failed to chown %s to bind gid %u",
2389                                 paths.binddns_dir, paths.bind_gid)
2390
2391             try:
2392                 os.chmod(bind_dns_keytab_path, 0o640)
2393                 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2394             except OSError:
2395                 if 'SAMBA_SELFTEST' not in os.environ:
2396                     logger.info("Failed to chown %s to bind gid %u",
2397                                 bind_dns_keytab_path, paths.bind_gid)
2398
2399     result = ProvisionResult()
2400     result.server_role = serverrole
2401     result.domaindn = domaindn
2402     result.paths = paths
2403     result.names = names
2404     result.lp = lp
2405     result.samdb = samdb
2406     result.idmap = idmap
2407     result.domainsid = str(domainsid)
2408
2409     if samdb_fill == FILL_FULL:
2410         result.adminpass_generated = adminpass_generated
2411         result.adminpass = adminpass
2412     else:
2413         result.adminpass_generated = False
2414         result.adminpass = None
2415
2416     result.backend_result = backend_result
2417
2418     if use_rfc2307:
2419         provision_fake_ypserver(logger=logger, samdb=samdb,
2420                                 domaindn=names.domaindn, netbiosname=names.netbiosname,
2421                                 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2422
2423     return result
2424
2425
2426 def provision_become_dc(smbconf=None, targetdir=None,
2427                         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2428                         serverdn=None, domain=None, hostname=None, domainsid=None,
2429                         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2430                         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2431                         dns_backend=None, root=None, nobody=None, users=None,
2432                         backup=None, serverrole=None, ldap_backend=None,
2433                         ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2434
2435     logger = logging.getLogger("provision")
2436     samba.set_debug_level(debuglevel)
2437
2438     res = provision(logger, system_session(),
2439                     smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2440                     realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2441                     configdn=configdn, serverdn=serverdn, domain=domain,
2442                     hostname=hostname, hostip=None, domainsid=domainsid,
2443                     machinepass=machinepass,
2444                     serverrole="active directory domain controller",
2445                     sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2446                     use_ntvfs=use_ntvfs)
2447     res.lp.set("debuglevel", str(debuglevel))
2448     return res
2449
2450
2451 def create_krb5_conf(path, dnsdomain, hostname, realm):
2452     """Write out a file containing a valid krb5.conf file
2453
2454     :param path: Path of the new krb5.conf file.
2455     :param dnsdomain: DNS Domain name
2456     :param hostname: Local hostname
2457     :param realm: Realm name
2458     """
2459     setup_file(setup_path("krb5.conf"), path, {
2460             "DNSDOMAIN": dnsdomain,
2461             "HOSTNAME": hostname,
2462             "REALM": realm,
2463     })
2464
2465
2466 class ProvisioningError(Exception):
2467     """A generic provision error."""
2468
2469     def __init__(self, value):
2470         self.value = value
2471
2472     def __str__(self):
2473         return "ProvisioningError: " + self.value
2474
2475
2476 class InvalidNetbiosName(Exception):
2477     """A specified name was not a valid NetBIOS name."""
2478
2479     def __init__(self, name):
2480         super(InvalidNetbiosName, self).__init__(
2481             "The name '%r' is not a valid NetBIOS name" % name)
2482
2483
2484 class MissingShareError(ProvisioningError):
2485
2486     def __init__(self, name, smbconf):
2487         super(MissingShareError, self).__init__(
2488             "Existing smb.conf does not have a [%s] share, but you are "
2489             "configuring a DC. Please remove %s or add the share manually." %
2490             (name, smbconf))