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