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