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