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