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