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