8c0c37917f3a4e2197cb9369e48f0a9c496a46cd
[nivanova/samba-autobuild/.git] / python / samba / provision / __init__.py
1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba AD server
3
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
7 #
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 #
24
25 """Functions for setting up a Samba configuration."""
26
27 __docformat__ = "restructuredText"
28
29 from samba.compat import urllib_quote
30 from 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         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
887                 "BACKEND_TYPE": provision_backend.type,
888                 "SERVER_ROLE": serverrole,
889                 "REQUIRED_FEATURES": required_features
890                 })
891
892         logger.info("Setting up sam.ldb rootDSE")
893         setup_samdb_rootdse(samdb, names)
894     except:
895         samdb.transaction_cancel()
896         raise
897     else:
898         samdb.transaction_commit()
899
900
901 def secretsdb_self_join(secretsdb, domain,
902                         netbiosname, machinepass, domainsid=None,
903                         realm=None, dnsdomain=None,
904                         keytab_path=None,
905                         key_version_number=1,
906                         secure_channel_type=SEC_CHAN_WKSTA):
907     """Add domain join-specific bits to a secrets database.
908
909     :param secretsdb: Ldb Handle to the secrets database
910     :param machinepass: Machine password
911     """
912     attrs = ["whenChanged",
913              "secret",
914              "priorSecret",
915              "priorChanged",
916              "krb5Keytab",
917              "privateKeytab"]
918
919     if realm is not None:
920         if dnsdomain is None:
921             dnsdomain = realm.lower()
922         dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
923     else:
924         dnsname = None
925     shortname = netbiosname.lower()
926
927     # We don't need to set msg["flatname"] here, because rdn_name will handle
928     # it, and it causes problems for modifies anyway
929     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
930     msg["secureChannelType"] = [str(secure_channel_type)]
931     msg["objectClass"] = ["top", "primaryDomain"]
932     if dnsname is not None:
933         msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
934         msg["realm"] = [realm]
935         msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
936         msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
937         msg["privateKeytab"] = ["secrets.keytab"]
938
939     msg["secret"] = [machinepass.encode('utf-8')]
940     msg["samAccountName"] = ["%s$" % netbiosname]
941     msg["secureChannelType"] = [str(secure_channel_type)]
942     if domainsid is not None:
943         msg["objectSid"] = [ndr_pack(domainsid)]
944
945     # This complex expression tries to ensure that we don't have more
946     # than one record for this SID, realm or netbios domain at a time,
947     # but we don't delete the old record that we are about to modify,
948     # because that would delete the keytab and previous password.
949     res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
950                            expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
951                            scope=ldb.SCOPE_ONELEVEL)
952
953     for del_msg in res:
954         secretsdb.delete(del_msg.dn)
955
956     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
957
958     if len(res) == 1:
959         msg["priorSecret"] = [res[0]["secret"][0]]
960         try:
961             msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
962         except KeyError:
963             pass
964
965         try:
966             msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
967         except KeyError:
968             pass
969
970         try:
971             msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
972         except KeyError:
973             pass
974
975         for el in msg:
976             if el != 'dn':
977                 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
978         secretsdb.modify(msg)
979         secretsdb.rename(res[0].dn, msg.dn)
980     else:
981         spn = ['HOST/%s' % shortname]
982         if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
983             # we are a domain controller then we add servicePrincipalName
984             # entries for the keytab code to update.
985             spn.extend(['HOST/%s' % dnsname])
986         msg["servicePrincipalName"] = spn
987
988         secretsdb.add(msg)
989
990
991 def setup_secretsdb(paths, session_info, backend_credentials, lp):
992     """Setup the secrets database.
993
994     :note: This function does not handle exceptions and transaction on purpose,
995        it's up to the caller to do this job.
996
997     :param path: Path to the secrets database.
998     :param session_info: Session info.
999     :param credentials: Credentials
1000     :param lp: Loadparm context
1001     :return: LDB handle for the created secrets database
1002     """
1003     if os.path.exists(paths.secrets):
1004         os.unlink(paths.secrets)
1005
1006     keytab_path = os.path.join(paths.private_dir, paths.keytab)
1007     if os.path.exists(keytab_path):
1008         os.unlink(keytab_path)
1009
1010     bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
1011     if os.path.exists(bind_dns_keytab_path):
1012         os.unlink(bind_dns_keytab_path)
1013
1014     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1015     if os.path.exists(dns_keytab_path):
1016         os.unlink(dns_keytab_path)
1017
1018     path = paths.secrets
1019
1020     secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1021     secrets_ldb.erase()
1022     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
1023     secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
1024     secrets_ldb.transaction_start()
1025     try:
1026         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
1027
1028         if (backend_credentials is not None and
1029             backend_credentials.authentication_requested()):
1030             if backend_credentials.get_bind_dn() is not None:
1031                 setup_add_ldif(secrets_ldb,
1032                                setup_path("secrets_simple_ldap.ldif"), {
1033                         "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
1034                         "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1035                     })
1036             else:
1037                 setup_add_ldif(secrets_ldb,
1038                                setup_path("secrets_sasl_ldap.ldif"), {
1039                         "LDAPADMINUSER": backend_credentials.get_username(),
1040                         "LDAPADMINREALM": backend_credentials.get_realm(),
1041                         "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()).decode('utf8')
1042                     })
1043     except:
1044         secrets_ldb.transaction_cancel()
1045         raise
1046     return secrets_ldb
1047
1048
1049 def setup_privileges(path, session_info, lp):
1050     """Setup the privileges database.
1051
1052     :param path: Path to the privileges database.
1053     :param session_info: Session info.
1054     :param credentials: Credentials
1055     :param lp: Loadparm context
1056     :return: LDB handle for the created secrets database
1057     """
1058     if os.path.exists(path):
1059         os.unlink(path)
1060     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1061     privilege_ldb.erase()
1062     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1063
1064
1065 def setup_encrypted_secrets_key(path):
1066     """Setup the encrypted secrets key file.
1067
1068     Any existing key file will be deleted and a new random key generated.
1069
1070     :param path: Path to the secrets key file.
1071
1072     """
1073     if os.path.exists(path):
1074         os.unlink(path)
1075
1076     flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1077     mode = stat.S_IRUSR | stat.S_IWUSR
1078
1079     umask_original = os.umask(0)
1080     try:
1081         fd = os.open(path, flags, mode)
1082     finally:
1083         os.umask(umask_original)
1084
1085     with os.fdopen(fd, 'w') as f:
1086         key = samba.generate_random_bytes(16)
1087         f.write(key)
1088
1089
1090 def setup_registry(path, session_info, lp):
1091     """Setup the registry.
1092
1093     :param path: Path to the registry database
1094     :param session_info: Session information
1095     :param credentials: Credentials
1096     :param lp: Loadparm context
1097     """
1098     reg = samba.registry.Registry()
1099     hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1100     reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1101     provision_reg = setup_path("provision.reg")
1102     assert os.path.exists(provision_reg)
1103     reg.diff_apply(provision_reg)
1104
1105
1106 def setup_idmapdb(path, session_info, lp):
1107     """Setup the idmap database.
1108
1109     :param path: path to the idmap database
1110     :param session_info: Session information
1111     :param credentials: Credentials
1112     :param lp: Loadparm context
1113     """
1114     if os.path.exists(path):
1115         os.unlink(path)
1116
1117     idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1118     idmap_ldb.erase()
1119     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1120     return idmap_ldb
1121
1122
1123 def setup_samdb_rootdse(samdb, names):
1124     """Setup the SamDB rootdse.
1125
1126     :param samdb: Sam Database handle
1127     """
1128     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1129         "SCHEMADN": names.schemadn,
1130         "DOMAINDN": names.domaindn,
1131         "ROOTDN": names.rootdn,
1132         "CONFIGDN": names.configdn,
1133         "SERVERDN": names.serverdn,
1134     })
1135
1136
1137 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1138                     dns_backend, dnspass, domainsid, next_rid, invocationid,
1139                     policyguid, policyguid_dc,
1140                     domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1141     """Join a host to its own domain."""
1142     assert isinstance(invocationid, str)
1143     if ntdsguid is not None:
1144         ntdsguid_line = "objectGUID: %s\n" %ntdsguid
1145     else:
1146         ntdsguid_line = ""
1147
1148     if dc_rid is None:
1149         dc_rid = next_rid
1150
1151     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1152               "CONFIGDN": names.configdn,
1153               "SCHEMADN": names.schemadn,
1154               "DOMAINDN": names.domaindn,
1155               "SERVERDN": names.serverdn,
1156               "INVOCATIONID": invocationid,
1157               "NETBIOSNAME": names.netbiosname,
1158               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1159               "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1160               "DOMAINSID": str(domainsid),
1161               "DCRID": str(dc_rid),
1162               "SAMBA_VERSION_STRING": version,
1163               "NTDSGUID": ntdsguid_line,
1164               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1165                   domainControllerFunctionality),
1166               "RIDALLOCATIONSTART": str(next_rid + 100),
1167               "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1168
1169     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1170               "POLICYGUID": policyguid,
1171               "POLICYGUID_DC": policyguid_dc,
1172               "DNSDOMAIN": names.dnsdomain,
1173               "DOMAINDN": names.domaindn})
1174
1175     # If we are setting up a subdomain, then this has been replicated in, so we
1176     # don't need to add it
1177     if fill == FILL_FULL:
1178         setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1179                 "CONFIGDN": names.configdn,
1180                 "SCHEMADN": names.schemadn,
1181                 "DOMAINDN": names.domaindn,
1182                 "SERVERDN": names.serverdn,
1183                 "INVOCATIONID": invocationid,
1184                 "NETBIOSNAME": names.netbiosname,
1185                 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1186                 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')).decode('utf8'),
1187                 "DOMAINSID": str(domainsid),
1188                 "DCRID": str(dc_rid),
1189                 "SAMBA_VERSION_STRING": version,
1190                 "NTDSGUID": ntdsguid_line,
1191                 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1192                     domainControllerFunctionality)})
1193
1194     # Setup fSMORoleOwner entries to point at the newly created DC entry
1195         setup_modify_ldif(samdb,
1196                           setup_path("provision_self_join_modify_config.ldif"), {
1197                 "CONFIGDN": names.configdn,
1198                 "SCHEMADN": names.schemadn,
1199                 "DEFAULTSITE": names.sitename,
1200                 "NETBIOSNAME": names.netbiosname,
1201                 "SERVERDN": names.serverdn,
1202             })
1203
1204     system_session_info = system_session()
1205     samdb.set_session_info(system_session_info)
1206     # Setup fSMORoleOwner entries to point at the newly created DC entry to
1207     # modify a serverReference under cn=config when we are a subdomain, we must
1208     # be system due to ACLs
1209     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1210               "DOMAINDN": names.domaindn,
1211               "SERVERDN": names.serverdn,
1212               "NETBIOSNAME": names.netbiosname,
1213               })
1214
1215     samdb.set_session_info(admin_session_info)
1216
1217     if dns_backend != "SAMBA_INTERNAL":
1218         # This is Samba4 specific and should be replaced by the correct
1219         # DNS AD-style setup
1220         setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1221               "DNSDOMAIN": names.dnsdomain,
1222               "DOMAINDN": names.domaindn,
1223               "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')).decode('utf8'),
1224               "HOSTNAME": names.hostname,
1225               "DNSNAME": '%s.%s' % (
1226                   names.netbiosname.lower(), names.dnsdomain.lower())
1227               })
1228
1229
1230 def getpolicypath(sysvolpath, dnsdomain, guid):
1231     """Return the physical path of policy given its guid.
1232
1233     :param sysvolpath: Path to the sysvol folder
1234     :param dnsdomain: DNS name of the AD domain
1235     :param guid: The GUID of the policy
1236     :return: A string with the complete path to the policy folder
1237     """
1238     if guid[0] != "{":
1239         guid = "{%s}" % guid
1240     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1241     return policy_path
1242
1243
1244 def create_gpo_struct(policy_path):
1245     if not os.path.exists(policy_path):
1246         os.makedirs(policy_path, 0o775)
1247     f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1248     try:
1249         f.write("[General]\r\nVersion=0")
1250     finally:
1251         f.close()
1252     p = os.path.join(policy_path, "MACHINE")
1253     if not os.path.exists(p):
1254         os.makedirs(p, 0o775)
1255     p = os.path.join(policy_path, "USER")
1256     if not os.path.exists(p):
1257         os.makedirs(p, 0o775)
1258
1259
1260 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1261     """Create the default GPO for a domain
1262
1263     :param sysvolpath: Physical path for the sysvol folder
1264     :param dnsdomain: DNS domain name of the AD domain
1265     :param policyguid: GUID of the default domain policy
1266     :param policyguid_dc: GUID of the default domain controler policy
1267     """
1268     policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid)
1269     create_gpo_struct(policy_path)
1270
1271     policy_path = getpolicypath(sysvolpath, dnsdomain, policyguid_dc)
1272     create_gpo_struct(policy_path)
1273
1274
1275 def setup_samdb(path, session_info, provision_backend, lp, names,
1276                 logger, fill, serverrole, schema, am_rodc=False,
1277                 plaintext_secrets=False, backend_store=None):
1278     """Setup a complete SAM Database.
1279
1280     :note: This will wipe the main SAM database file!
1281     """
1282
1283     # Also wipes the database
1284     setup_samdb_partitions(path, logger=logger, lp=lp,
1285                            provision_backend=provision_backend, session_info=session_info,
1286                            names=names, serverrole=serverrole, plaintext_secrets=plaintext_secrets,
1287                            backend_store=backend_store)
1288
1289     # Load the database, but don's load the global schema and don't connect
1290     # quite yet
1291     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1292                   credentials=provision_backend.credentials, lp=lp,
1293                   global_schema=False, am_rodc=am_rodc)
1294
1295     logger.info("Pre-loading the Samba 4 and AD schema")
1296
1297     # Load the schema from the one we computed earlier
1298     samdb.set_schema(schema, write_indices_and_attributes=False)
1299
1300     # Set the NTDS settings DN manually - in order to have it already around
1301     # before the provisioned tree exists and we connect
1302     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1303
1304     # And now we can connect to the DB - the schema won't be loaded from the
1305     # DB
1306     try:
1307         samdb.connect(path)
1308     except ldb.LdbError as e2:
1309         (num, string_error) = e2.args
1310         if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1311             raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1312         else:
1313             raise
1314
1315     # But we have to give it one more kick to have it use the schema
1316     # during provision - it needs, now that it is connected, to write
1317     # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1318     samdb.set_schema(schema, write_indices_and_attributes=True)
1319
1320     return samdb
1321
1322
1323 def fill_samdb(samdb, lp, names, logger, policyguid,
1324                policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1325                dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1326                dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None,
1327                backend_store=None):
1328
1329     if next_rid is None:
1330         next_rid = 1000
1331
1332     # Provision does not make much sense values larger than 1000000000
1333     # as the upper range of the rIDAvailablePool is 1073741823 and
1334     # we don't want to create a domain that cannot allocate rids.
1335     if next_rid < 1000 or next_rid > 1000000000:
1336         error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1337         error += "the valid range is %u-%u. The default is %u." % (
1338             1000, 1000000000, 1000)
1339         raise ProvisioningError(error)
1340
1341     # ATTENTION: Do NOT change these default values without discussion with the
1342     # team and/or release manager. They have a big impact on the whole program!
1343     domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1344
1345     if dom_for_fun_level is None:
1346         dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1347
1348     if dom_for_fun_level > domainControllerFunctionality:
1349         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!")
1350
1351     domainFunctionality = dom_for_fun_level
1352     forestFunctionality = dom_for_fun_level
1353
1354     # Set the NTDS settings DN manually - in order to have it already around
1355     # before the provisioned tree exists and we connect
1356     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1357
1358     # Set the domain functionality levels onto the database.
1359     # Various module (the password_hash module in particular) need
1360     # to know what level of AD we are emulating.
1361
1362     # These will be fixed into the database via the database
1363     # modifictions below, but we need them set from the start.
1364     samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1365     samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1366     samdb.set_opaque_integer("domainControllerFunctionality",
1367                              domainControllerFunctionality)
1368
1369     samdb.set_domain_sid(str(names.domainsid))
1370     samdb.set_invocation_id(invocationid)
1371
1372     logger.info("Adding DomainDN: %s" % names.domaindn)
1373
1374     # impersonate domain admin
1375     admin_session_info = admin_session(lp, str(names.domainsid))
1376     samdb.set_session_info(admin_session_info)
1377     if names.domainguid is not None:
1378         domainguid_line = "objectGUID: %s\n-" % names.domainguid
1379     else:
1380         domainguid_line = ""
1381
1382     descr = b64encode(get_domain_descriptor(names.domainsid)).decode('utf8')
1383     setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1384             "DOMAINDN": names.domaindn,
1385             "DOMAINSID": str(names.domainsid),
1386             "DESCRIPTOR": descr,
1387             "DOMAINGUID": domainguid_line
1388             })
1389
1390     setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1391         "DOMAINDN": names.domaindn,
1392         "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1393         "NEXTRID": str(next_rid),
1394         "DEFAULTSITE": names.sitename,
1395         "CONFIGDN": names.configdn,
1396         "POLICYGUID": policyguid,
1397         "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1398         "SAMBA_VERSION_STRING": version,
1399         "MIN_PWD_LENGTH": str(DEFAULT_MIN_PWD_LENGTH)
1400     })
1401
1402     # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1403     if fill == FILL_FULL:
1404         logger.info("Adding configuration container")
1405         descr = b64encode(get_config_descriptor(names.domainsid)).decode('utf8')
1406         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1407                 "CONFIGDN": names.configdn,
1408                 "DESCRIPTOR": descr,
1409                 })
1410
1411         # The LDIF here was created when the Schema object was constructed
1412         ignore_checks_oid = "local_oid:%s:0" % samba.dsdb.DSDB_CONTROL_SKIP_DUPLICATES_CHECK_OID
1413         logger.info("Setting up sam.ldb schema")
1414         samdb.add_ldif(schema.schema_dn_add,
1415                        controls=["relax:0", ignore_checks_oid])
1416         samdb.modify_ldif(schema.schema_dn_modify,
1417                           controls=[ignore_checks_oid])
1418         samdb.write_prefixes_from_schema()
1419         samdb.add_ldif(schema.schema_data, controls=["relax:0", ignore_checks_oid])
1420         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1421                        {"SCHEMADN": names.schemadn},
1422                        controls=["relax:0", ignore_checks_oid])
1423
1424     # Now register this container in the root of the forest
1425     msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1426     msg["subRefs"] = ldb.MessageElement(names.configdn, ldb.FLAG_MOD_ADD,
1427                                         "subRefs")
1428
1429     samdb.invocation_id = invocationid
1430
1431     # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1432     if fill == FILL_FULL:
1433         logger.info("Setting up sam.ldb configuration data")
1434
1435         partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid)).decode('utf8')
1436         sites_descr = b64encode(get_config_sites_descriptor(names.domainsid)).decode('utf8')
1437         ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid)).decode('utf8')
1438         protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1439         protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1440         protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1441
1442         if "2008" in schema.base_schema:
1443             # exclude 2012-specific changes if we're using a 2008 schema
1444             incl_2012 = "#"
1445         else:
1446             incl_2012 = ""
1447
1448         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1449                 "CONFIGDN": names.configdn,
1450                 "NETBIOSNAME": names.netbiosname,
1451                 "DEFAULTSITE": names.sitename,
1452                 "DNSDOMAIN": names.dnsdomain,
1453                 "DOMAIN": names.domain,
1454                 "SCHEMADN": names.schemadn,
1455                 "DOMAINDN": names.domaindn,
1456                 "SERVERDN": names.serverdn,
1457                 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1458                 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1459                 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1460                 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1461                 "SERVICES_DESCRIPTOR": protected1_descr,
1462                 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1463                 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1464                 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1465                 "PARTITIONS_DESCRIPTOR": partitions_descr,
1466                 "SITES_DESCRIPTOR": sites_descr,
1467                 })
1468
1469         setup_add_ldif(samdb, setup_path("extended-rights.ldif"), {
1470                 "CONFIGDN": names.configdn,
1471                 "INC2012": incl_2012,
1472                 })
1473
1474         logger.info("Setting up display specifiers")
1475         display_specifiers_ldif = read_ms_ldif(
1476             setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1477         display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1478                                                  {"CONFIGDN": names.configdn})
1479         check_all_substituted(display_specifiers_ldif)
1480         samdb.add_ldif(display_specifiers_ldif)
1481
1482         logger.info("Modifying display specifiers and extended rights")
1483         setup_modify_ldif(samdb,
1484                           setup_path("provision_configuration_modify.ldif"), {
1485                 "CONFIGDN": names.configdn,
1486                 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1487             })
1488
1489     logger.info("Adding users container")
1490     users_desc = b64encode(get_domain_users_descriptor(names.domainsid)).decode('utf8')
1491     setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1492             "DOMAINDN": names.domaindn,
1493             "USERS_DESCRIPTOR": users_desc
1494             })
1495     logger.info("Modifying users container")
1496     setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1497             "DOMAINDN": names.domaindn})
1498     logger.info("Adding computers container")
1499     computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid)).decode('utf8')
1500     setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1501             "DOMAINDN": names.domaindn,
1502             "COMPUTERS_DESCRIPTOR": computers_desc
1503             })
1504     logger.info("Modifying computers container")
1505     setup_modify_ldif(samdb,
1506                       setup_path("provision_computers_modify.ldif"), {
1507             "DOMAINDN": names.domaindn})
1508     logger.info("Setting up sam.ldb data")
1509     infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid)).decode('utf8')
1510     lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid)).decode('utf8')
1511     system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid)).decode('utf8')
1512     builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid)).decode('utf8')
1513     controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid)).decode('utf8')
1514     setup_add_ldif(samdb, setup_path("provision.ldif"), {
1515         "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1516         "DOMAINDN": names.domaindn,
1517         "NETBIOSNAME": names.netbiosname,
1518         "DEFAULTSITE": names.sitename,
1519         "CONFIGDN": names.configdn,
1520         "SERVERDN": names.serverdn,
1521         "RIDAVAILABLESTART": str(next_rid + 600),
1522         "POLICYGUID_DC": policyguid_dc,
1523         "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1524         "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1525         "SYSTEM_DESCRIPTOR": system_desc,
1526         "BUILTIN_DESCRIPTOR": builtin_desc,
1527         "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1528     })
1529
1530     # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1531     if fill == FILL_FULL:
1532         managedservice_descr = b64encode(get_managed_service_accounts_descriptor(names.domainsid)).decode('utf8')
1533         setup_modify_ldif(samdb,
1534                           setup_path("provision_configuration_references.ldif"), {
1535                               "CONFIGDN": names.configdn,
1536                               "SCHEMADN": names.schemadn})
1537
1538         logger.info("Setting up well known security principals")
1539         protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid)).decode('utf8')
1540         setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1541             "CONFIGDN": names.configdn,
1542             "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1543         }, controls=["relax:0", "provision:0"])
1544
1545     if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1546         setup_modify_ldif(samdb,
1547                           setup_path("provision_basedn_references.ldif"), {
1548                               "DOMAINDN": names.domaindn,
1549                               "MANAGEDSERVICE_DESCRIPTOR": managedservice_descr
1550                           })
1551
1552         logger.info("Setting up sam.ldb users and groups")
1553         setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1554             "DOMAINDN": names.domaindn,
1555             "DOMAINSID": str(names.domainsid),
1556             "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')).decode('utf8'),
1557             "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')).decode('utf8')
1558         }, controls=["relax:0", "provision:0"])
1559
1560         logger.info("Setting up self join")
1561         setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1562                         invocationid=invocationid,
1563                         dns_backend=dns_backend,
1564                         dnspass=dnspass,
1565                         machinepass=machinepass,
1566                         domainsid=names.domainsid,
1567                         next_rid=next_rid,
1568                         dc_rid=dc_rid,
1569                         policyguid=policyguid,
1570                         policyguid_dc=policyguid_dc,
1571                         domainControllerFunctionality=domainControllerFunctionality,
1572                         ntdsguid=ntdsguid)
1573
1574         ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1575         names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1576                                          attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1577         assert isinstance(names.ntdsguid, str)
1578
1579     return samdb
1580
1581
1582 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1583 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)"
1584 SYSVOL_SERVICE = "sysvol"
1585
1586
1587 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1588     setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1589     for root, dirs, files in os.walk(path, topdown=False):
1590         for name in files:
1591             setntacl(lp, os.path.join(root, name), acl, domsid,
1592                      use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1593         for name in dirs:
1594             setntacl(lp, os.path.join(root, name), acl, domsid,
1595                      use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1596
1597
1598 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1599     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1600     folders beneath.
1601
1602     :param sysvol: Physical path for the sysvol folder
1603     :param dnsdomain: The DNS name of the domain
1604     :param domainsid: The SID of the domain
1605     :param domaindn: The DN of the domain (ie. DC=...)
1606     :param samdb: An LDB object on the SAM db
1607     :param lp: an LP object
1608     """
1609
1610     # Set ACL for GPO root folder
1611     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1612     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1613              use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1614
1615     res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1616                        attrs=["cn", "nTSecurityDescriptor"],
1617                        expression="", scope=ldb.SCOPE_ONELEVEL)
1618
1619     for policy in res:
1620         acl = ndr_unpack(security.descriptor,
1621                          str(policy["nTSecurityDescriptor"])).as_sddl()
1622         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1623         set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1624                     str(domainsid), use_ntvfs,
1625                     passdb=passdb)
1626
1627
1628 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1629                  domaindn, lp, use_ntvfs):
1630     """Set the ACL for the sysvol share and the subfolders
1631
1632     :param samdb: An LDB object on the SAM db
1633     :param netlogon: Physical path for the netlogon folder
1634     :param sysvol: Physical path for the sysvol folder
1635     :param uid: The UID of the "Administrator" user
1636     :param gid: The GID of the "Domain adminstrators" group
1637     :param domainsid: The SID of the domain
1638     :param dnsdomain: The DNS name of the domain
1639     :param domaindn: The DN of the domain (ie. DC=...)
1640     """
1641     s4_passdb = None
1642
1643     if not use_ntvfs:
1644         s3conf = s3param.get_context()
1645         s3conf.load(lp.configfile)
1646
1647         file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1648         try:
1649             try:
1650                 smbd.set_simple_acl(file.name, 0o755, gid)
1651             except OSError:
1652                 if not smbd.have_posix_acls():
1653                     # This clue is only strictly correct for RPM and
1654                     # Debian-like Linux systems, but hopefully other users
1655                     # will get enough clue from it.
1656                     raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires.  "
1657                                             "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1658
1659                 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires.  "
1660                                         "Try the mounting the filesystem with the 'acl' option.")
1661             try:
1662                 smbd.chown(file.name, uid, gid)
1663             except OSError:
1664                 raise ProvisioningError("Unable to chown a file on your filesystem.  "
1665                                         "You may not be running provision as root.")
1666         finally:
1667             file.close()
1668
1669         # This will ensure that the smbd code we are running when setting ACLs
1670         # is initialised with the smb.conf
1671         s3conf = s3param.get_context()
1672         s3conf.load(lp.configfile)
1673         # ensure we are using the right samba_dsdb passdb backend, no matter what
1674         s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1675         passdb.reload_static_pdb()
1676
1677         # ensure that we init the samba_dsdb backend, so the domain sid is
1678         # marked in secrets.tdb
1679         s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1680
1681         # now ensure everything matches correctly, to avoid wierd issues
1682         if passdb.get_global_sam_sid() != domainsid:
1683             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))
1684
1685         domain_info = s4_passdb.domain_info()
1686         if domain_info["dom_sid"] != domainsid:
1687             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))
1688
1689         if domain_info["dns_domain"].upper() != dnsdomain.upper():
1690             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()))
1691
1692     try:
1693         if use_ntvfs:
1694             os.chown(sysvol, -1, gid)
1695     except OSError:
1696         canchown = False
1697     else:
1698         canchown = True
1699
1700     # use admin sid dn as user dn, since admin should own most of the files,
1701     # the operation will be much faster
1702     userdn = '<SID={}-{}>'.format(domainsid, security.DOMAIN_RID_ADMINISTRATOR)
1703
1704     flags = (auth.AUTH_SESSION_INFO_DEFAULT_GROUPS |
1705              auth.AUTH_SESSION_INFO_AUTHENTICATED |
1706              auth.AUTH_SESSION_INFO_SIMPLE_PRIVILEGES)
1707
1708     session_info = auth.user_session(samdb, lp_ctx=lp, dn=userdn,
1709                                      session_info_flags=flags)
1710
1711     def _setntacl(path):
1712         """A helper to reuse args"""
1713         return setntacl(
1714             lp, path, SYSVOL_ACL, str(domainsid),
1715             use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb,
1716             service=SYSVOL_SERVICE, session_info=session_info)
1717
1718     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1719     _setntacl(sysvol)
1720     for root, dirs, files in os.walk(sysvol, topdown=False):
1721         for name in files:
1722             if use_ntvfs and canchown:
1723                 os.chown(os.path.join(root, name), -1, gid)
1724             _setntacl(os.path.join(root, name))
1725         for name in dirs:
1726             if use_ntvfs and canchown:
1727                 os.chown(os.path.join(root, name), -1, gid)
1728             _setntacl(os.path.join(root, name))
1729
1730     # Set acls on Policy folder and policies folders
1731     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1732
1733
1734 def acl_type(direct_db_access):
1735     if direct_db_access:
1736         return "DB"
1737     else:
1738         return "VFS"
1739
1740
1741 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1742     fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1743     fsacl_sddl = fsacl.as_sddl(domainsid)
1744     if fsacl_sddl != acl:
1745         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))
1746
1747     for root, dirs, files in os.walk(path, topdown=False):
1748         for name in files:
1749             fsacl = getntacl(lp, os.path.join(root, name),
1750                              direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1751             if fsacl is None:
1752                 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1753             fsacl_sddl = fsacl.as_sddl(domainsid)
1754             if fsacl_sddl != acl:
1755                 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))
1756
1757         for name in dirs:
1758             fsacl = getntacl(lp, os.path.join(root, name),
1759                              direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1760             if fsacl is None:
1761                 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1762             fsacl_sddl = fsacl.as_sddl(domainsid)
1763             if fsacl_sddl != acl:
1764                 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))
1765
1766
1767 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1768                    direct_db_access):
1769     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1770     folders beneath.
1771
1772     :param sysvol: Physical path for the sysvol folder
1773     :param dnsdomain: The DNS name of the domain
1774     :param domainsid: The SID of the domain
1775     :param domaindn: The DN of the domain (ie. DC=...)
1776     :param samdb: An LDB object on the SAM db
1777     :param lp: an LP object
1778     """
1779
1780     # Set ACL for GPO root folder
1781     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1782     fsacl = getntacl(lp, root_policy_path,
1783                      direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1784     if fsacl is None:
1785         raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1786     fsacl_sddl = fsacl.as_sddl(domainsid)
1787     if fsacl_sddl != POLICIES_ACL:
1788         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))
1789     res = samdb.search(base="CN=Policies,CN=System,%s" %(domaindn),
1790                        attrs=["cn", "nTSecurityDescriptor"],
1791                        expression="", scope=ldb.SCOPE_ONELEVEL)
1792
1793     for policy in res:
1794         acl = ndr_unpack(security.descriptor,
1795                          str(policy["nTSecurityDescriptor"])).as_sddl()
1796         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1797         check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1798                       domainsid, direct_db_access)
1799
1800
1801 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1802                    lp):
1803     """Set the ACL for the sysvol share and the subfolders
1804
1805     :param samdb: An LDB object on the SAM db
1806     :param netlogon: Physical path for the netlogon folder
1807     :param sysvol: Physical path for the sysvol folder
1808     :param uid: The UID of the "Administrator" user
1809     :param gid: The GID of the "Domain adminstrators" group
1810     :param domainsid: The SID of the domain
1811     :param dnsdomain: The DNS name of the domain
1812     :param domaindn: The DN of the domain (ie. DC=...)
1813     """
1814
1815     # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1816     s3conf = s3param.get_context()
1817     s3conf.load(lp.configfile)
1818     # ensure we are using the right samba_dsdb passdb backend, no matter what
1819     s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1820     # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1821     s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1822
1823     # now ensure everything matches correctly, to avoid wierd issues
1824     if passdb.get_global_sam_sid() != domainsid:
1825         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))
1826
1827     domain_info = s4_passdb.domain_info()
1828     if domain_info["dom_sid"] != domainsid:
1829         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))
1830
1831     if domain_info["dns_domain"].upper() != dnsdomain.upper():
1832         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()))
1833
1834     # Ensure we can read this directly, and via the smbd VFS
1835     for direct_db_access in [True, False]:
1836         # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1837         for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1838             fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1839             if fsacl is None:
1840                 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1841             fsacl_sddl = fsacl.as_sddl(domainsid)
1842             if fsacl_sddl != SYSVOL_ACL:
1843                 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))
1844
1845         # Check acls on Policy folder and policies folders
1846         check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1847                        direct_db_access)
1848
1849
1850 def interface_ips_v4(lp):
1851     """return only IPv4 IPs"""
1852     ips = samba.interface_ips(lp, False)
1853     ret = []
1854     for i in ips:
1855         if i.find(':') == -1:
1856             ret.append(i)
1857     return ret
1858
1859
1860 def interface_ips_v6(lp):
1861     """return only IPv6 IPs"""
1862     ips = samba.interface_ips(lp, False)
1863     ret = []
1864     for i in ips:
1865         if i.find(':') != -1:
1866             ret.append(i)
1867     return ret
1868
1869
1870 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1871                    schema=None,
1872                    targetdir=None, samdb_fill=FILL_FULL,
1873                    hostip=None, hostip6=None,
1874                    next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1875                    domainguid=None, policyguid=None, policyguid_dc=None,
1876                    invocationid=None, machinepass=None, ntdsguid=None,
1877                    dns_backend=None, dnspass=None,
1878                    serverrole=None, dom_for_fun_level=None,
1879                    am_rodc=False, lp=None, use_ntvfs=False,
1880                    skip_sysvolacl=False, backend_store=None):
1881     # create/adapt the group policy GUIDs
1882     # Default GUID for default policy are described at
1883     # "How Core Group Policy Works"
1884     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1885     if policyguid is None:
1886         policyguid = DEFAULT_POLICY_GUID
1887     policyguid = policyguid.upper()
1888     if policyguid_dc is None:
1889         policyguid_dc = DEFAULT_DC_POLICY_GUID
1890     policyguid_dc = policyguid_dc.upper()
1891
1892     if invocationid is None:
1893         invocationid = str(uuid.uuid4())
1894
1895     if krbtgtpass is None:
1896         krbtgtpass = samba.generate_random_machine_password(128, 255)
1897     if machinepass is None:
1898         machinepass = samba.generate_random_machine_password(128, 255)
1899     if dnspass is None:
1900         dnspass = samba.generate_random_password(128, 255)
1901
1902     samdb.transaction_start()
1903     try:
1904         samdb = fill_samdb(samdb, lp, names, logger=logger,
1905                            schema=schema,
1906                            policyguid=policyguid, policyguid_dc=policyguid_dc,
1907                            fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1908                            invocationid=invocationid, machinepass=machinepass,
1909                            dns_backend=dns_backend, dnspass=dnspass,
1910                            ntdsguid=ntdsguid, serverrole=serverrole,
1911                            dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1912                            next_rid=next_rid, dc_rid=dc_rid,
1913                            backend_store=backend_store)
1914
1915         # Set up group policies (domain policy and domain controller
1916         # policy)
1917         if serverrole == "active directory domain controller":
1918             create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1919                                policyguid_dc)
1920     except:
1921         samdb.transaction_cancel()
1922         raise
1923     else:
1924         samdb.transaction_commit()
1925
1926     if serverrole == "active directory domain controller":
1927         # Continue setting up sysvol for GPO. This appears to require being
1928         # outside a transaction.
1929         if not skip_sysvolacl:
1930             setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1931                          paths.root_gid, names.domainsid, names.dnsdomain,
1932                          names.domaindn, lp, use_ntvfs)
1933         else:
1934             logger.info("Setting acl on sysvol skipped")
1935
1936         secretsdb_self_join(secrets_ldb, domain=names.domain,
1937                             realm=names.realm, dnsdomain=names.dnsdomain,
1938                             netbiosname=names.netbiosname, domainsid=names.domainsid,
1939                             machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1940
1941         # Now set up the right msDS-SupportedEncryptionTypes into the DB
1942         # In future, this might be determined from some configuration
1943         kerberos_enctypes = str(ENC_ALL_TYPES)
1944
1945         try:
1946             msg = ldb.Message(ldb.Dn(samdb,
1947                                      samdb.searchone("distinguishedName",
1948                                                      expression="samAccountName=%s$" % names.netbiosname,
1949                                                      scope=ldb.SCOPE_SUBTREE).decode('utf8')))
1950             msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1951                 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1952                 name="msDS-SupportedEncryptionTypes")
1953             samdb.modify(msg)
1954         except ldb.LdbError as e:
1955             (enum, estr) = e.args
1956             if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1957                 # It might be that this attribute does not exist in this schema
1958                 raise
1959
1960         setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1961                      hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1962                      dnspass=dnspass, os_level=dom_for_fun_level,
1963                      targetdir=targetdir, fill_level=samdb_fill,
1964                      backend_store=backend_store)
1965
1966         domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1967                                      attribute="objectGUID")
1968         assert isinstance(domainguid, str)
1969
1970     lastProvisionUSNs = get_last_provision_usn(samdb)
1971     maxUSN = get_max_usn(samdb, str(names.rootdn))
1972     if lastProvisionUSNs is not None:
1973         update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1974     else:
1975         set_provision_usn(samdb, 0, maxUSN, invocationid)
1976
1977     logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1978     setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1979                       {'NTDSGUID': names.ntdsguid})
1980
1981     # fix any dangling GUIDs from the provision
1982     logger.info("Fixing provision GUIDs")
1983     chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1984                   quiet=True)
1985     samdb.transaction_start()
1986     try:
1987         # a small number of GUIDs are missing because of ordering issues in the
1988         # provision code
1989         for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1990             chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1991                                scope=ldb.SCOPE_BASE,
1992                                attrs=['defaultObjectCategory'])
1993         chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1994                            scope=ldb.SCOPE_ONELEVEL,
1995                            attrs=['ipsecOwnersReference',
1996                                   'ipsecFilterReference',
1997                                   'ipsecISAKMPReference',
1998                                   'ipsecNegotiationPolicyReference',
1999                                   'ipsecNFAReference'])
2000         if chk.check_database(DN=names.schemadn, scope=ldb.SCOPE_SUBTREE,
2001                               attrs=['attributeId', 'governsId']) != 0:
2002             raise ProvisioningError("Duplicate attributeId or governsId in schema. Must be fixed manually!!")
2003     except:
2004         samdb.transaction_cancel()
2005         raise
2006     else:
2007         samdb.transaction_commit()
2008
2009
2010 _ROLES_MAP = {
2011     "ROLE_STANDALONE": "standalone server",
2012     "ROLE_DOMAIN_MEMBER": "member server",
2013     "ROLE_DOMAIN_BDC": "active directory domain controller",
2014     "ROLE_DOMAIN_PDC": "active directory domain controller",
2015     "dc": "active directory domain controller",
2016     "member": "member server",
2017     "domain controller": "active directory domain controller",
2018     "active directory domain controller": "active directory domain controller",
2019     "member server": "member server",
2020     "standalone": "standalone server",
2021     "standalone server": "standalone server",
2022 }
2023
2024
2025 def sanitize_server_role(role):
2026     """Sanitize a server role name.
2027
2028     :param role: Server role
2029     :raise ValueError: If the role can not be interpreted
2030     :return: Sanitized server role (one of "member server",
2031         "active directory domain controller", "standalone server")
2032     """
2033     try:
2034         return _ROLES_MAP[role]
2035     except KeyError:
2036         raise ValueError(role)
2037
2038
2039 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
2040                             maxuid, maxgid):
2041     """Create AD entries for the fake ypserver.
2042
2043     This is needed for being able to manipulate posix attrs via ADUC.
2044     """
2045     samdb.transaction_start()
2046     try:
2047         logger.info("Setting up fake yp server settings")
2048         setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
2049             "DOMAINDN": domaindn,
2050             "NETBIOSNAME": netbiosname,
2051             "NISDOMAIN": nisdomain,
2052         })
2053     except:
2054         samdb.transaction_cancel()
2055         raise
2056     else:
2057         samdb.transaction_commit()
2058
2059
2060 def directory_create_or_exists(path, mode=0o755):
2061     if not os.path.exists(path):
2062         try:
2063             os.mkdir(path, mode)
2064         except OSError as e:
2065             if e.errno in [errno.EEXIST]:
2066                 pass
2067             else:
2068                 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
2069
2070
2071 def determine_host_ip(logger, lp, hostip=None):
2072     if hostip is None:
2073         logger.info("Looking up IPv4 addresses")
2074         hostips = interface_ips_v4(lp)
2075         if len(hostips) > 0:
2076             hostip = hostips[0]
2077             if len(hostips) > 1:
2078                 logger.warning("More than one IPv4 address found. Using %s",
2079                                hostip)
2080     if hostip == "127.0.0.1":
2081         hostip = None
2082     if hostip is None:
2083         logger.warning("No IPv4 address will be assigned")
2084
2085     return hostip
2086
2087
2088 def determine_host_ip6(logger, lp, hostip6=None):
2089     if hostip6 is None:
2090         logger.info("Looking up IPv6 addresses")
2091         hostips = interface_ips_v6(lp)
2092         if hostips:
2093             hostip6 = hostips[0]
2094         if len(hostips) > 1:
2095             logger.warning("More than one IPv6 address found. Using %s", hostip6)
2096     if hostip6 is None:
2097         logger.warning("No IPv6 address will be assigned")
2098
2099     return hostip6
2100
2101
2102 def provision(logger, session_info, smbconf=None,
2103               targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
2104               domaindn=None, schemadn=None, configdn=None, serverdn=None,
2105               domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
2106               next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
2107               krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
2108               dns_backend=None, dns_forwarder=None, dnspass=None,
2109               invocationid=None, machinepass=None, ntdsguid=None,
2110               root=None, nobody=None, users=None, backup=None, aci=None,
2111               serverrole=None, dom_for_fun_level=None, backend_type=None,
2112               sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
2113               useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
2114               use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
2115               ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False,
2116               ldap_backend_extra_port=None, base_schema=None,
2117               plaintext_secrets=False, backend_store=None):
2118     """Provision samba4
2119
2120     :note: caution, this wipes all existing data!
2121     """
2122
2123     try:
2124         serverrole = sanitize_server_role(serverrole)
2125     except ValueError:
2126         raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
2127
2128     if ldapadminpass is None:
2129         # Make a new, random password between Samba and it's LDAP server
2130         ldapadminpass = samba.generate_random_password(128, 255)
2131
2132     if backend_type is None:
2133         backend_type = "ldb"
2134     if backend_store is None:
2135         backend_store = get_default_backend_store()
2136
2137     if domainsid is None:
2138         domainsid = security.random_sid()
2139
2140     root_uid = findnss_uid([root or "root"])
2141     nobody_uid = findnss_uid([nobody or "nobody"])
2142     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
2143     root_gid = pwd.getpwuid(root_uid).pw_gid
2144
2145     try:
2146         bind_gid = findnss_gid(["bind", "named"])
2147     except KeyError:
2148         bind_gid = None
2149
2150     if targetdir is not None:
2151         smbconf = os.path.join(targetdir, "etc", "smb.conf")
2152     elif smbconf is None:
2153         smbconf = samba.param.default_path()
2154     if not os.path.exists(os.path.dirname(smbconf)):
2155         os.makedirs(os.path.dirname(smbconf))
2156
2157     server_services = []
2158     global_param = {}
2159     if use_rfc2307:
2160         global_param["idmap_ldb:use rfc2307"] = ["yes"]
2161
2162     if dns_backend != "SAMBA_INTERNAL":
2163         server_services.append("-dns")
2164     else:
2165         if dns_forwarder is not None:
2166             global_param["dns forwarder"] = [dns_forwarder]
2167
2168     if use_ntvfs:
2169         server_services.append("+smb")
2170         server_services.append("-s3fs")
2171         global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
2172
2173     if len(server_services) > 0:
2174         global_param["server services"] = server_services
2175
2176     # only install a new smb.conf if there isn't one there already
2177     if os.path.exists(smbconf):
2178         # if Samba Team members can't figure out the weird errors
2179         # loading an empty smb.conf gives, then we need to be smarter.
2180         # Pretend it just didn't exist --abartlet
2181         f = open(smbconf, 'r')
2182         try:
2183             data = f.read().lstrip()
2184         finally:
2185             f.close()
2186         if data is None or data == "":
2187             make_smbconf(smbconf, hostname, domain, realm,
2188                          targetdir, serverrole=serverrole,
2189                          eadb=useeadb, use_ntvfs=use_ntvfs,
2190                          lp=lp, global_param=global_param)
2191     else:
2192         make_smbconf(smbconf, hostname, domain, realm, targetdir,
2193                      serverrole=serverrole,
2194                      eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2195
2196     if lp is None:
2197         lp = samba.param.LoadParm()
2198     lp.load(smbconf)
2199     names = guess_names(lp=lp, hostname=hostname, domain=domain,
2200                         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2201                         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2202                         sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2203     paths = provision_paths_from_lp(lp, names.dnsdomain)
2204
2205     paths.bind_gid = bind_gid
2206     paths.root_uid = root_uid
2207     paths.root_gid = root_gid
2208
2209     hostip = determine_host_ip(logger, lp, hostip)
2210     hostip6 = determine_host_ip6(logger, lp, hostip6)
2211     names.hostip = hostip
2212     names.hostip6 = hostip6
2213     names.domainguid = domainguid
2214     names.domainsid = domainsid
2215     names.forestsid = domainsid
2216
2217     if serverrole is None:
2218         serverrole = lp.get("server role")
2219
2220     directory_create_or_exists(paths.private_dir, 0o700)
2221     directory_create_or_exists(paths.binddns_dir, 0o770)
2222     directory_create_or_exists(os.path.join(paths.private_dir, "tls"))
2223     directory_create_or_exists(paths.state_dir)
2224     if not plaintext_secrets:
2225         setup_encrypted_secrets_key(paths.encrypted_secrets_key_path)
2226
2227     if paths.sysvol and not os.path.exists(paths.sysvol):
2228         os.makedirs(paths.sysvol, 0o775)
2229
2230     ldapi_url = "ldapi://%s" % urllib_quote(paths.s4_ldapi_path, safe="")
2231
2232     schema = Schema(domainsid, invocationid=invocationid,
2233                     schemadn=names.schemadn, base_schema=base_schema)
2234
2235     if backend_type == "ldb":
2236         provision_backend = LDBBackend(backend_type, paths=paths,
2237                                        lp=lp,
2238                                        names=names, logger=logger)
2239     elif backend_type == "existing":
2240         # If support for this is ever added back, then the URI will need to be
2241         # specified again
2242         provision_backend = ExistingBackend(backend_type, paths=paths,
2243                                             lp=lp,
2244                                             names=names, logger=logger,
2245                                             ldap_backend_forced_uri=ldap_backend_forced_uri)
2246     elif backend_type == "fedora-ds":
2247         provision_backend = FDSBackend(backend_type, paths=paths,
2248                                        lp=lp,
2249                                        names=names, logger=logger, domainsid=domainsid,
2250                                        schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2251                                        slapd_path=slapd_path,
2252                                        root=root)
2253     elif backend_type == "openldap":
2254         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2255                                             lp=lp,
2256                                             names=names, logger=logger, domainsid=domainsid,
2257                                             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2258                                             slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2259                                             ldap_backend_extra_port=ldap_backend_extra_port,
2260                                             ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2261                                             ldap_backend_forced_uri=ldap_backend_forced_uri)
2262     else:
2263         raise ValueError("Unknown LDAP backend type selected")
2264
2265     provision_backend.init()
2266     provision_backend.start()
2267
2268     # only install a new shares config db if there is none
2269     if not os.path.exists(paths.shareconf):
2270         logger.info("Setting up share.ldb")
2271         share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2272         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2273
2274     logger.info("Setting up secrets.ldb")
2275     secrets_ldb = setup_secretsdb(paths,
2276                                   session_info=session_info,
2277                                   backend_credentials=provision_backend.credentials, lp=lp)
2278
2279     try:
2280         logger.info("Setting up the registry")
2281         setup_registry(paths.hklm, session_info, lp=lp)
2282
2283         logger.info("Setting up the privileges database")
2284         setup_privileges(paths.privilege, session_info, lp=lp)
2285
2286         logger.info("Setting up idmap db")
2287         idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2288
2289         setup_name_mappings(idmap, sid=str(domainsid),
2290                             root_uid=root_uid, nobody_uid=nobody_uid,
2291                             users_gid=users_gid, root_gid=root_gid)
2292
2293         logger.info("Setting up SAM db")
2294         samdb = setup_samdb(paths.samdb, session_info,
2295                             provision_backend, lp, names, logger=logger,
2296                             serverrole=serverrole,
2297                             schema=schema, fill=samdb_fill, am_rodc=am_rodc,
2298                             plaintext_secrets=plaintext_secrets,
2299                             backend_store=backend_store)
2300
2301         if serverrole == "active directory domain controller":
2302             if paths.netlogon is None:
2303                 raise MissingShareError("netlogon", paths.smbconf)
2304
2305             if paths.sysvol is None:
2306                 raise MissingShareError("sysvol", paths.smbconf)
2307
2308             if not os.path.isdir(paths.netlogon):
2309                 os.makedirs(paths.netlogon, 0o755)
2310
2311         if adminpass is None:
2312             adminpass = samba.generate_random_password(12, 32)
2313             adminpass_generated = True
2314         else:
2315             adminpass = unicode(adminpass, 'utf-8')
2316             adminpass_generated = False
2317
2318         if samdb_fill == FILL_FULL:
2319             provision_fill(samdb, secrets_ldb, logger, names, paths,
2320                            schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2321                            hostip=hostip, hostip6=hostip6,
2322                            next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2323                            krbtgtpass=krbtgtpass,
2324                            policyguid=policyguid, policyguid_dc=policyguid_dc,
2325                            invocationid=invocationid, machinepass=machinepass,
2326                            ntdsguid=ntdsguid, dns_backend=dns_backend,
2327                            dnspass=dnspass, serverrole=serverrole,
2328                            dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2329                            lp=lp, use_ntvfs=use_ntvfs,
2330                            skip_sysvolacl=skip_sysvolacl,
2331                            backend_store=backend_store)
2332
2333         if not is_heimdal_built():
2334             create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
2335             logger.info("The Kerberos KDC configuration for Samba AD is "
2336                         "located at %s", paths.kdcconf)
2337
2338         create_krb5_conf(paths.krb5conf,
2339                          dnsdomain=names.dnsdomain, hostname=names.hostname,
2340                          realm=names.realm)
2341         logger.info("A Kerberos configuration suitable for Samba AD has been "
2342                     "generated at %s", paths.krb5conf)
2343         logger.info("Merge the contents of this file with your system "
2344                     "krb5.conf or replace it with this one. Do not create a "
2345                     "symlink!")
2346
2347         if serverrole == "active directory domain controller":
2348             create_dns_update_list(lp, logger, paths)
2349
2350         backend_result = provision_backend.post_setup()
2351         provision_backend.shutdown()
2352
2353     except:
2354         secrets_ldb.transaction_cancel()
2355         raise
2356
2357     # Now commit the secrets.ldb to disk
2358     secrets_ldb.transaction_commit()
2359
2360     # the commit creates the dns.keytab in the private directory
2361     private_dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2362     bind_dns_keytab_path = os.path.join(paths.binddns_dir, paths.dns_keytab)
2363
2364     if os.path.isfile(private_dns_keytab_path):
2365         if os.path.isfile(bind_dns_keytab_path):
2366             try:
2367                 os.unlink(bind_dns_keytab_path)
2368             except OSError as e:
2369                 logger.error("Failed to remove %s: %s" %
2370                              (bind_dns_keytab_path, e.strerror))
2371
2372         # link the dns.keytab to the bind-dns directory
2373         try:
2374             os.link(private_dns_keytab_path, bind_dns_keytab_path)
2375         except OSError as e:
2376             logger.error("Failed to create link %s -> %s: %s" %
2377                          (private_dns_keytab_path, bind_dns_keytab_path, e.strerror))
2378
2379         # chown the dns.keytab in the bind-dns directory
2380         if paths.bind_gid is not None:
2381             try:
2382                 os.chmod(paths.binddns_dir, 0o770)
2383                 os.chown(paths.binddns_dir, -1, paths.bind_gid)
2384             except OSError:
2385                 if 'SAMBA_SELFTEST' not in os.environ:
2386                     logger.info("Failed to chown %s to bind gid %u",
2387                                 paths.binddns_dir, paths.bind_gid)
2388
2389             try:
2390                 os.chmod(bind_dns_keytab_path, 0o640)
2391                 os.chown(bind_dns_keytab_path, -1, paths.bind_gid)
2392             except OSError:
2393                 if 'SAMBA_SELFTEST' not in os.environ:
2394                     logger.info("Failed to chown %s to bind gid %u",
2395                                 bind_dns_keytab_path, paths.bind_gid)
2396
2397     result = ProvisionResult()
2398     result.server_role = serverrole
2399     result.domaindn = domaindn
2400     result.paths = paths
2401     result.names = names
2402     result.lp = lp
2403     result.samdb = samdb
2404     result.idmap = idmap
2405     result.domainsid = str(domainsid)
2406
2407     if samdb_fill == FILL_FULL:
2408         result.adminpass_generated = adminpass_generated
2409         result.adminpass = adminpass
2410     else:
2411         result.adminpass_generated = False
2412         result.adminpass = None
2413
2414     result.backend_result = backend_result
2415
2416     if use_rfc2307:
2417         provision_fake_ypserver(logger=logger, samdb=samdb,
2418                                 domaindn=names.domaindn, netbiosname=names.netbiosname,
2419                                 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2420
2421     return result
2422
2423
2424 def provision_become_dc(smbconf=None, targetdir=None,
2425                         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2426                         serverdn=None, domain=None, hostname=None, domainsid=None,
2427                         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2428                         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2429                         dns_backend=None, root=None, nobody=None, users=None,
2430                         backup=None, serverrole=None, ldap_backend=None,
2431                         ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2432
2433     logger = logging.getLogger("provision")
2434     samba.set_debug_level(debuglevel)
2435
2436     res = provision(logger, system_session(),
2437                     smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2438                     realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2439                     configdn=configdn, serverdn=serverdn, domain=domain,
2440                     hostname=hostname, hostip=None, domainsid=domainsid,
2441                     machinepass=machinepass,
2442                     serverrole="active directory domain controller",
2443                     sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2444                     use_ntvfs=use_ntvfs)
2445     res.lp.set("debuglevel", str(debuglevel))
2446     return res
2447
2448
2449 def create_krb5_conf(path, dnsdomain, hostname, realm):
2450     """Write out a file containing a valid krb5.conf file
2451
2452     :param path: Path of the new krb5.conf file.
2453     :param dnsdomain: DNS Domain name
2454     :param hostname: Local hostname
2455     :param realm: Realm name
2456     """
2457     setup_file(setup_path("krb5.conf"), path, {
2458             "DNSDOMAIN": dnsdomain,
2459             "HOSTNAME": hostname,
2460             "REALM": realm,
2461     })
2462
2463
2464 class ProvisioningError(Exception):
2465     """A generic provision error."""
2466
2467     def __init__(self, value):
2468         self.value = value
2469
2470     def __str__(self):
2471         return "ProvisioningError: " + self.value
2472
2473
2474 class InvalidNetbiosName(Exception):
2475     """A specified name was not a valid NetBIOS name."""
2476
2477     def __init__(self, name):
2478         super(InvalidNetbiosName, self).__init__(
2479             "The name '%r' is not a valid NetBIOS name" % name)
2480
2481
2482 class MissingShareError(ProvisioningError):
2483
2484     def __init__(self, name, smbconf):
2485         super(MissingShareError, self).__init__(
2486             "Existing smb.conf does not have a [%s] share, but you are "
2487             "configuring a DC. Please remove %s or add the share manually." %
2488             (name, smbconf))