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