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