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