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