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