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