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