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