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