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