provision_fill: move most db accesses into transactions
[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     # Set the domain functionality levels onto the database.
1263     # Various module (the password_hash module in particular) need
1264     # to know what level of AD we are emulating.
1265
1266     # These will be fixed into the database via the database
1267     # modifictions below, but we need them set from the start.
1268     samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1269     samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1270     samdb.set_opaque_integer("domainControllerFunctionality",
1271         domainControllerFunctionality)
1272
1273     samdb.set_domain_sid(str(names.domainsid))
1274     samdb.set_invocation_id(invocationid)
1275
1276     logger.info("Adding DomainDN: %s" % names.domaindn)
1277
1278     # impersonate domain admin
1279     admin_session_info = admin_session(lp, str(names.domainsid))
1280     samdb.set_session_info(admin_session_info)
1281     if names.domainguid is not None:
1282         domainguid_line = "objectGUID: %s\n-" % names.domainguid
1283     else:
1284         domainguid_line = ""
1285
1286     descr = b64encode(get_domain_descriptor(names.domainsid))
1287     setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1288             "DOMAINDN": names.domaindn,
1289             "DOMAINSID": str(names.domainsid),
1290             "DESCRIPTOR": descr,
1291             "DOMAINGUID": domainguid_line
1292             })
1293
1294     setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1295         "DOMAINDN": names.domaindn,
1296         "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1297         "NEXTRID": str(next_rid),
1298         "DEFAULTSITE": names.sitename,
1299         "CONFIGDN": names.configdn,
1300         "POLICYGUID": policyguid,
1301         "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1302         "SAMBA_VERSION_STRING": version
1303         })
1304
1305     # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1306     if fill == FILL_FULL:
1307         logger.info("Adding configuration container")
1308         descr = b64encode(get_config_descriptor(names.domainsid))
1309         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1310                 "CONFIGDN": names.configdn,
1311                 "DESCRIPTOR": descr,
1312                 })
1313
1314         # The LDIF here was created when the Schema object was constructed
1315         logger.info("Setting up sam.ldb schema")
1316         samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1317         samdb.modify_ldif(schema.schema_dn_modify)
1318         samdb.write_prefixes_from_schema()
1319         samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1320         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1321                        {"SCHEMADN": names.schemadn})
1322
1323     # Now register this container in the root of the forest
1324     msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1325     msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1326                 "subRefs")
1327
1328     samdb.invocation_id = invocationid
1329
1330     # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1331     if fill == FILL_FULL:
1332         logger.info("Setting up sam.ldb configuration data")
1333
1334         partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid))
1335         sites_descr = b64encode(get_config_sites_descriptor(names.domainsid))
1336         ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid))
1337         protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid))
1338         protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1339         protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid))
1340
1341         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1342                 "CONFIGDN": names.configdn,
1343                 "NETBIOSNAME": names.netbiosname,
1344                 "DEFAULTSITE": names.sitename,
1345                 "DNSDOMAIN": names.dnsdomain,
1346                 "DOMAIN": names.domain,
1347                 "SCHEMADN": names.schemadn,
1348                 "DOMAINDN": names.domaindn,
1349                 "SERVERDN": names.serverdn,
1350                 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1351                 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1352                 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1353                 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1354                 "SERVICES_DESCRIPTOR": protected1_descr,
1355                 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1356                 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1357                 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1358                 "PARTITIONS_DESCRIPTOR": partitions_descr,
1359                 "SITES_DESCRIPTOR": sites_descr,
1360                 })
1361
1362         logger.info("Setting up display specifiers")
1363         display_specifiers_ldif = read_ms_ldif(
1364             setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1365         display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1366                                                  {"CONFIGDN": names.configdn})
1367         check_all_substituted(display_specifiers_ldif)
1368         samdb.add_ldif(display_specifiers_ldif)
1369
1370         logger.info("Modifying display specifiers")
1371         setup_modify_ldif(samdb,
1372             setup_path("provision_configuration_modify.ldif"), {
1373             "CONFIGDN": names.configdn,
1374             "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1375             })
1376
1377     logger.info("Adding users container")
1378     users_desc = b64encode(get_domain_users_descriptor(names.domainsid))
1379     setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1380             "DOMAINDN": names.domaindn,
1381             "USERS_DESCRIPTOR": users_desc
1382             })
1383     logger.info("Modifying users container")
1384     setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1385             "DOMAINDN": names.domaindn})
1386     logger.info("Adding computers container")
1387     computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid))
1388     setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1389             "DOMAINDN": names.domaindn,
1390             "COMPUTERS_DESCRIPTOR": computers_desc
1391             })
1392     logger.info("Modifying computers container")
1393     setup_modify_ldif(samdb,
1394         setup_path("provision_computers_modify.ldif"), {
1395             "DOMAINDN": names.domaindn})
1396     logger.info("Setting up sam.ldb data")
1397     infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid))
1398     lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid))
1399     system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid))
1400     builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid))
1401     controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid))
1402     setup_add_ldif(samdb, setup_path("provision.ldif"), {
1403         "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1404         "DOMAINDN": names.domaindn,
1405         "NETBIOSNAME": names.netbiosname,
1406         "DEFAULTSITE": names.sitename,
1407         "CONFIGDN": names.configdn,
1408         "SERVERDN": names.serverdn,
1409         "RIDAVAILABLESTART": str(next_rid + 600),
1410         "POLICYGUID_DC": policyguid_dc,
1411         "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1412         "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1413         "SYSTEM_DESCRIPTOR": system_desc,
1414         "BUILTIN_DESCRIPTOR": builtin_desc,
1415         "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1416         })
1417
1418     # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1419     if fill == FILL_FULL:
1420         setup_modify_ldif(samdb,
1421                           setup_path("provision_configuration_references.ldif"), {
1422                 "CONFIGDN": names.configdn,
1423                 "SCHEMADN": names.schemadn})
1424
1425         logger.info("Setting up well known security principals")
1426         protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1427         setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1428             "CONFIGDN": names.configdn,
1429             "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1430             })
1431
1432     if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1433         setup_modify_ldif(samdb,
1434                           setup_path("provision_basedn_references.ldif"),
1435                           {"DOMAINDN": names.domaindn})
1436
1437         logger.info("Setting up sam.ldb users and groups")
1438         setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1439             "DOMAINDN": names.domaindn,
1440             "DOMAINSID": str(names.domainsid),
1441             "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1442             "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1443             })
1444
1445         logger.info("Setting up self join")
1446         setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1447             invocationid=invocationid,
1448             dns_backend=dns_backend,
1449             dnspass=dnspass,
1450             machinepass=machinepass,
1451             domainsid=names.domainsid,
1452             next_rid=next_rid,
1453             dc_rid=dc_rid,
1454             policyguid=policyguid,
1455             policyguid_dc=policyguid_dc,
1456             domainControllerFunctionality=domainControllerFunctionality,
1457             ntdsguid=ntdsguid)
1458
1459         ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1460         names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1461             attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1462         assert isinstance(names.ntdsguid, str)
1463
1464     return samdb
1465
1466
1467 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1468 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)"
1469 SYSVOL_SERVICE="sysvol"
1470
1471 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1472     setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1473     for root, dirs, files in os.walk(path, topdown=False):
1474         for name in files:
1475             setntacl(lp, os.path.join(root, name), acl, domsid,
1476                     use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1477         for name in dirs:
1478             setntacl(lp, os.path.join(root, name), acl, domsid,
1479                     use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1480
1481
1482 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1483     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1484     folders beneath.
1485
1486     :param sysvol: Physical path for the sysvol folder
1487     :param dnsdomain: The DNS name of the domain
1488     :param domainsid: The SID of the domain
1489     :param domaindn: The DN of the domain (ie. DC=...)
1490     :param samdb: An LDB object on the SAM db
1491     :param lp: an LP object
1492     """
1493
1494     # Set ACL for GPO root folder
1495     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1496     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1497             use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1498
1499     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1500                         attrs=["cn", "nTSecurityDescriptor"],
1501                         expression="", scope=ldb.SCOPE_ONELEVEL)
1502
1503     for policy in res:
1504         acl = ndr_unpack(security.descriptor,
1505                          str(policy["nTSecurityDescriptor"])).as_sddl()
1506         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1507         set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1508                     str(domainsid), use_ntvfs,
1509                     passdb=passdb)
1510
1511
1512 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1513         domaindn, lp, use_ntvfs):
1514     """Set the ACL for the sysvol share and the subfolders
1515
1516     :param samdb: An LDB object on the SAM db
1517     :param netlogon: Physical path for the netlogon folder
1518     :param sysvol: Physical path for the sysvol folder
1519     :param uid: The UID of the "Administrator" user
1520     :param gid: The GID of the "Domain adminstrators" group
1521     :param domainsid: The SID of the domain
1522     :param dnsdomain: The DNS name of the domain
1523     :param domaindn: The DN of the domain (ie. DC=...)
1524     """
1525     s4_passdb = None
1526
1527     if not use_ntvfs:
1528         s3conf = s3param.get_context()
1529         s3conf.load(lp.configfile)
1530
1531         file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1532         try:
1533             try:
1534                 smbd.set_simple_acl(file.name, 0755, gid)
1535             except OSError:
1536                 if not smbd.have_posix_acls():
1537                     # This clue is only strictly correct for RPM and
1538                     # Debian-like Linux systems, but hopefully other users
1539                     # will get enough clue from it.
1540                     raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires.  "
1541                                             "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1542
1543                 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires.  "
1544                                         "Try the mounting the filesystem with the 'acl' option.")
1545             try:
1546                 smbd.chown(file.name, uid, gid)
1547             except OSError:
1548                 raise ProvisioningError("Unable to chown a file on your filesystem.  "
1549                                         "You may not be running provision as root.")
1550         finally:
1551             file.close()
1552
1553         # This will ensure that the smbd code we are running when setting ACLs
1554         # is initialised with the smb.conf
1555         s3conf = s3param.get_context()
1556         s3conf.load(lp.configfile)
1557         # ensure we are using the right samba_dsdb passdb backend, no matter what
1558         s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1559         passdb.reload_static_pdb()
1560
1561         # ensure that we init the samba_dsdb backend, so the domain sid is
1562         # marked in secrets.tdb
1563         s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1564
1565         # now ensure everything matches correctly, to avoid wierd issues
1566         if passdb.get_global_sam_sid() != domainsid:
1567             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))
1568
1569         domain_info = s4_passdb.domain_info()
1570         if domain_info["dom_sid"] != domainsid:
1571             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))
1572
1573         if domain_info["dns_domain"].upper() != dnsdomain.upper():
1574             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()))
1575
1576
1577     try:
1578         if use_ntvfs:
1579             os.chown(sysvol, -1, gid)
1580     except OSError:
1581         canchown = False
1582     else:
1583         canchown = True
1584
1585     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1586     setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1587              skip_invalid_chown=True, passdb=s4_passdb,
1588              service=SYSVOL_SERVICE)
1589     for root, dirs, files in os.walk(sysvol, topdown=False):
1590         for name in files:
1591             if use_ntvfs and canchown:
1592                 os.chown(os.path.join(root, name), -1, gid)
1593             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1594                      use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1595                      passdb=s4_passdb, service=SYSVOL_SERVICE)
1596         for name in dirs:
1597             if use_ntvfs and canchown:
1598                 os.chown(os.path.join(root, name), -1, gid)
1599             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1600                      use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1601                      passdb=s4_passdb, service=SYSVOL_SERVICE)
1602
1603     # Set acls on Policy folder and policies folders
1604     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1605
1606 def acl_type(direct_db_access):
1607     if direct_db_access:
1608         return "DB"
1609     else:
1610         return "VFS"
1611
1612 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1613     fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1614     fsacl_sddl = fsacl.as_sddl(domainsid)
1615     if fsacl_sddl != acl:
1616         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))
1617
1618     for root, dirs, files in os.walk(path, topdown=False):
1619         for name in files:
1620             fsacl = getntacl(lp, os.path.join(root, name),
1621                              direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1622             if fsacl is None:
1623                 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1624             fsacl_sddl = fsacl.as_sddl(domainsid)
1625             if fsacl_sddl != acl:
1626                 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))
1627
1628         for name in dirs:
1629             fsacl = getntacl(lp, os.path.join(root, name),
1630                              direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1631             if fsacl is None:
1632                 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1633             fsacl_sddl = fsacl.as_sddl(domainsid)
1634             if fsacl_sddl != acl:
1635                 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))
1636
1637
1638 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1639         direct_db_access):
1640     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1641     folders beneath.
1642
1643     :param sysvol: Physical path for the sysvol folder
1644     :param dnsdomain: The DNS name of the domain
1645     :param domainsid: The SID of the domain
1646     :param domaindn: The DN of the domain (ie. DC=...)
1647     :param samdb: An LDB object on the SAM db
1648     :param lp: an LP object
1649     """
1650
1651     # Set ACL for GPO root folder
1652     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1653     fsacl = getntacl(lp, root_policy_path,
1654                      direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1655     if fsacl is None:
1656         raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1657     fsacl_sddl = fsacl.as_sddl(domainsid)
1658     if fsacl_sddl != POLICIES_ACL:
1659         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))
1660     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1661                         attrs=["cn", "nTSecurityDescriptor"],
1662                         expression="", scope=ldb.SCOPE_ONELEVEL)
1663
1664     for policy in res:
1665         acl = ndr_unpack(security.descriptor,
1666                          str(policy["nTSecurityDescriptor"])).as_sddl()
1667         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1668         check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1669                       domainsid, direct_db_access)
1670
1671
1672 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1673     lp):
1674     """Set the ACL for the sysvol share and the subfolders
1675
1676     :param samdb: An LDB object on the SAM db
1677     :param netlogon: Physical path for the netlogon folder
1678     :param sysvol: Physical path for the sysvol folder
1679     :param uid: The UID of the "Administrator" user
1680     :param gid: The GID of the "Domain adminstrators" group
1681     :param domainsid: The SID of the domain
1682     :param dnsdomain: The DNS name of the domain
1683     :param domaindn: The DN of the domain (ie. DC=...)
1684     """
1685
1686     # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1687     s3conf = s3param.get_context()
1688     s3conf.load(lp.configfile)
1689     # ensure we are using the right samba_dsdb passdb backend, no matter what
1690     s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1691     # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1692     s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1693
1694     # now ensure everything matches correctly, to avoid wierd issues
1695     if passdb.get_global_sam_sid() != domainsid:
1696         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))
1697
1698     domain_info = s4_passdb.domain_info()
1699     if domain_info["dom_sid"] != domainsid:
1700         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))
1701
1702     if domain_info["dns_domain"].upper() != dnsdomain.upper():
1703         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()))
1704
1705     # Ensure we can read this directly, and via the smbd VFS
1706     for direct_db_access in [True, False]:
1707         # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1708         for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1709             fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1710             if fsacl is None:
1711                 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1712             fsacl_sddl = fsacl.as_sddl(domainsid)
1713             if fsacl_sddl != SYSVOL_ACL:
1714                 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))
1715
1716         # Check acls on Policy folder and policies folders
1717         check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1718                 direct_db_access)
1719
1720
1721 def interface_ips_v4(lp):
1722     """return only IPv4 IPs"""
1723     ips = samba.interface_ips(lp, False)
1724     ret = []
1725     for i in ips:
1726         if i.find(':') == -1:
1727             ret.append(i)
1728     return ret
1729
1730
1731 def interface_ips_v6(lp):
1732     """return only IPv6 IPs"""
1733     ips = samba.interface_ips(lp, False)
1734     ret = []
1735     for i in ips:
1736         if i.find(':') != -1:
1737             ret.append(i)
1738     return ret
1739
1740
1741 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1742                    schema=None,
1743                    targetdir=None, samdb_fill=FILL_FULL,
1744                    hostip=None, hostip6=None,
1745                    next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1746                    domainguid=None, policyguid=None, policyguid_dc=None,
1747                    invocationid=None, machinepass=None, ntdsguid=None,
1748                    dns_backend=None, dnspass=None,
1749                    serverrole=None, dom_for_fun_level=None,
1750                    am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1751     # create/adapt the group policy GUIDs
1752     # Default GUID for default policy are described at
1753     # "How Core Group Policy Works"
1754     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1755     if policyguid is None:
1756         policyguid = DEFAULT_POLICY_GUID
1757     policyguid = policyguid.upper()
1758     if policyguid_dc is None:
1759         policyguid_dc = DEFAULT_DC_POLICY_GUID
1760     policyguid_dc = policyguid_dc.upper()
1761
1762     if invocationid is None:
1763         invocationid = str(uuid.uuid4())
1764
1765     if krbtgtpass is None:
1766         krbtgtpass = samba.generate_random_password(128, 255)
1767     if machinepass is None:
1768         machinepass  = samba.generate_random_password(128, 255)
1769     if dnspass is None:
1770         dnspass = samba.generate_random_password(128, 255)
1771
1772     samdb.transaction_start()
1773     try:
1774         samdb = fill_samdb(samdb, lp, names, logger=logger,
1775                        schema=schema,
1776                        policyguid=policyguid, policyguid_dc=policyguid_dc,
1777                        fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1778                        invocationid=invocationid, machinepass=machinepass,
1779                        dns_backend=dns_backend, dnspass=dnspass,
1780                        ntdsguid=ntdsguid, serverrole=serverrole,
1781                        dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1782                        next_rid=next_rid, dc_rid=dc_rid)
1783
1784     except:
1785         samdb.transaction_cancel()
1786         raise
1787     else:
1788         samdb.transaction_commit()
1789
1790     if serverrole == "active directory domain controller":
1791
1792         # Set up group policies (domain policy and domain controller
1793         # policy)
1794         create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1795                            policyguid_dc)
1796         if not skip_sysvolacl:
1797             setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1798                          paths.root_gid, names.domainsid, names.dnsdomain,
1799                          names.domaindn, lp, use_ntvfs)
1800         else:
1801             logger.info("Setting acl on sysvol skipped")
1802
1803         secretsdb_self_join(secrets_ldb, domain=names.domain,
1804                 realm=names.realm, dnsdomain=names.dnsdomain,
1805                 netbiosname=names.netbiosname, domainsid=names.domainsid,
1806                 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1807
1808         # Now set up the right msDS-SupportedEncryptionTypes into the DB
1809         # In future, this might be determined from some configuration
1810         kerberos_enctypes = str(ENC_ALL_TYPES)
1811
1812         try:
1813             msg = ldb.Message(ldb.Dn(samdb,
1814                                      samdb.searchone("distinguishedName",
1815                                                      expression="samAccountName=%s$" % names.netbiosname,
1816                                                      scope=ldb.SCOPE_SUBTREE)))
1817             msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1818                 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1819                 name="msDS-SupportedEncryptionTypes")
1820             samdb.modify(msg)
1821         except ldb.LdbError, (enum, estr):
1822             if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1823                 # It might be that this attribute does not exist in this schema
1824                 raise
1825
1826         setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1827                      hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1828                      dnspass=dnspass, os_level=dom_for_fun_level,
1829                      targetdir=targetdir, fill_level=samdb_fill)
1830
1831         domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1832                                      attribute="objectGUID")
1833         assert isinstance(domainguid, str)
1834
1835     lastProvisionUSNs = get_last_provision_usn(samdb)
1836     maxUSN = get_max_usn(samdb, str(names.rootdn))
1837     if lastProvisionUSNs is not None:
1838         update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1839     else:
1840         set_provision_usn(samdb, 0, maxUSN, invocationid)
1841
1842     logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1843     setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1844                       { 'NTDSGUID' : names.ntdsguid })
1845
1846     # fix any dangling GUIDs from the provision
1847     logger.info("Fixing provision GUIDs")
1848     chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1849             quiet=True)
1850     samdb.transaction_start()
1851     try:
1852         # a small number of GUIDs are missing because of ordering issues in the
1853         # provision code
1854         for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1855             chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1856                                scope=ldb.SCOPE_BASE,
1857                                attrs=['defaultObjectCategory'])
1858         chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1859                            scope=ldb.SCOPE_ONELEVEL,
1860                            attrs=['ipsecOwnersReference',
1861                                   'ipsecFilterReference',
1862                                   'ipsecISAKMPReference',
1863                                   'ipsecNegotiationPolicyReference',
1864                                   'ipsecNFAReference'])
1865     except:
1866         samdb.transaction_cancel()
1867         raise
1868     else:
1869         samdb.transaction_commit()
1870
1871
1872 _ROLES_MAP = {
1873     "ROLE_STANDALONE": "standalone server",
1874     "ROLE_DOMAIN_MEMBER": "member server",
1875     "ROLE_DOMAIN_BDC": "active directory domain controller",
1876     "ROLE_DOMAIN_PDC": "active directory domain controller",
1877     "dc": "active directory domain controller",
1878     "member": "member server",
1879     "domain controller": "active directory domain controller",
1880     "active directory domain controller": "active directory domain controller",
1881     "member server": "member server",
1882     "standalone": "standalone server",
1883     "standalone server": "standalone server",
1884     }
1885
1886
1887 def sanitize_server_role(role):
1888     """Sanitize a server role name.
1889
1890     :param role: Server role
1891     :raise ValueError: If the role can not be interpreted
1892     :return: Sanitized server role (one of "member server",
1893         "active directory domain controller", "standalone server")
1894     """
1895     try:
1896         return _ROLES_MAP[role]
1897     except KeyError:
1898         raise ValueError(role)
1899
1900
1901 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1902         maxuid, maxgid):
1903     """Create AD entries for the fake ypserver.
1904
1905     This is needed for being able to manipulate posix attrs via ADUC.
1906     """
1907     samdb.transaction_start()
1908     try:
1909         logger.info("Setting up fake yp server settings")
1910         setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1911         "DOMAINDN": domaindn,
1912         "NETBIOSNAME": netbiosname,
1913         "NISDOMAIN": nisdomain,
1914          })
1915     except:
1916         samdb.transaction_cancel()
1917         raise
1918     else:
1919         samdb.transaction_commit()
1920
1921
1922 def provision(logger, session_info, smbconf=None,
1923         targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1924         domaindn=None, schemadn=None, configdn=None, serverdn=None,
1925         domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1926         next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1927         krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1928         dns_backend=None, dns_forwarder=None, dnspass=None,
1929         invocationid=None, machinepass=None, ntdsguid=None,
1930         root=None, nobody=None, users=None, backup=None, aci=None,
1931         serverrole=None, dom_for_fun_level=None, backend_type=None,
1932         sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1933         useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1934         use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
1935         ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, ldap_backend_extra_port=None):
1936     """Provision samba4
1937
1938     :note: caution, this wipes all existing data!
1939     """
1940
1941     try:
1942         serverrole = sanitize_server_role(serverrole)
1943     except ValueError:
1944         raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1945
1946     if ldapadminpass is None:
1947         # Make a new, random password between Samba and it's LDAP server
1948         ldapadminpass = samba.generate_random_password(128, 255)
1949
1950     if backend_type is None:
1951         backend_type = "ldb"
1952
1953     if domainsid is None:
1954         domainsid = security.random_sid()
1955
1956     root_uid = findnss_uid([root or "root"])
1957     nobody_uid = findnss_uid([nobody or "nobody"])
1958     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1959     root_gid = pwd.getpwuid(root_uid).pw_gid
1960
1961     try:
1962         bind_gid = findnss_gid(["bind", "named"])
1963     except KeyError:
1964         bind_gid = None
1965
1966     if targetdir is not None:
1967         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1968     elif smbconf is None:
1969         smbconf = samba.param.default_path()
1970     if not os.path.exists(os.path.dirname(smbconf)):
1971         os.makedirs(os.path.dirname(smbconf))
1972
1973     server_services = []
1974     global_param = {}
1975     if use_rfc2307:
1976         global_param["idmap_ldb:use rfc2307"] = ["yes"]
1977
1978     if dns_backend != "SAMBA_INTERNAL":
1979         server_services.append("-dns")
1980     else:
1981         if dns_forwarder is not None:
1982             global_param["dns forwarder"] = [dns_forwarder]
1983
1984     if use_ntvfs:
1985         server_services.append("+smb")
1986         server_services.append("-s3fs")
1987         global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1988
1989     if len(server_services) > 0:
1990         global_param["server services"] = server_services
1991
1992     # only install a new smb.conf if there isn't one there already
1993     if os.path.exists(smbconf):
1994         # if Samba Team members can't figure out the weird errors
1995         # loading an empty smb.conf gives, then we need to be smarter.
1996         # Pretend it just didn't exist --abartlet
1997         f = open(smbconf, 'r')
1998         try:
1999             data = f.read().lstrip()
2000         finally:
2001             f.close()
2002         if data is None or data == "":
2003             make_smbconf(smbconf, hostname, domain, realm,
2004                          targetdir, serverrole=serverrole,
2005                          eadb=useeadb, use_ntvfs=use_ntvfs,
2006                          lp=lp, global_param=global_param)
2007     else:
2008         make_smbconf(smbconf, hostname, domain, realm, targetdir,
2009                      serverrole=serverrole,
2010                      eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2011
2012     if lp is None:
2013         lp = samba.param.LoadParm()
2014     lp.load(smbconf)
2015     names = guess_names(lp=lp, hostname=hostname, domain=domain,
2016         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2017         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2018         sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2019     paths = provision_paths_from_lp(lp, names.dnsdomain)
2020
2021     paths.bind_gid = bind_gid
2022     paths.root_uid = root_uid;
2023     paths.root_gid = root_gid
2024
2025     if hostip is None:
2026         logger.info("Looking up IPv4 addresses")
2027         hostips = interface_ips_v4(lp)
2028         if len(hostips) > 0:
2029             hostip = hostips[0]
2030             if len(hostips) > 1:
2031                 logger.warning("More than one IPv4 address found. Using %s",
2032                     hostip)
2033     if hostip == "127.0.0.1":
2034         hostip = None
2035     if hostip is None:
2036         logger.warning("No IPv4 address will be assigned")
2037
2038     if hostip6 is None:
2039         logger.info("Looking up IPv6 addresses")
2040         hostips = interface_ips_v6(lp)
2041         if hostips:
2042             hostip6 = hostips[0]
2043         if len(hostips) > 1:
2044             logger.warning("More than one IPv6 address found. Using %s", hostip6)
2045     if hostip6 is None:
2046         logger.warning("No IPv6 address will be assigned")
2047
2048     names.hostip = hostip
2049     names.hostip6 = hostip6
2050     names.domainguid = domainguid
2051     names.domainsid = domainsid
2052     names.forestsid = domainsid
2053
2054     if serverrole is None:
2055         serverrole = lp.get("server role")
2056
2057     if not os.path.exists(paths.private_dir):
2058         os.mkdir(paths.private_dir)
2059     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
2060         os.makedirs(os.path.join(paths.private_dir, "tls"), 0700)
2061     if not os.path.exists(paths.state_dir):
2062         os.mkdir(paths.state_dir)
2063
2064     if paths.sysvol and not os.path.exists(paths.sysvol):
2065         os.makedirs(paths.sysvol, 0775)
2066
2067     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2068
2069     schema = Schema(domainsid, invocationid=invocationid,
2070         schemadn=names.schemadn)
2071
2072     if backend_type == "ldb":
2073         provision_backend = LDBBackend(backend_type, paths=paths,
2074             lp=lp,
2075             names=names, logger=logger)
2076     elif backend_type == "existing":
2077         # If support for this is ever added back, then the URI will need to be
2078         # specified again
2079         provision_backend = ExistingBackend(backend_type, paths=paths,
2080             lp=lp,
2081             names=names, logger=logger,
2082             ldap_backend_forced_uri=ldap_backend_forced_uri)
2083     elif backend_type == "fedora-ds":
2084         provision_backend = FDSBackend(backend_type, paths=paths,
2085             lp=lp,
2086             names=names, logger=logger, domainsid=domainsid,
2087             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2088             slapd_path=slapd_path,
2089             root=root)
2090     elif backend_type == "openldap":
2091         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2092             lp=lp,
2093             names=names, logger=logger, domainsid=domainsid,
2094             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2095             slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2096             ldap_backend_extra_port=ldap_backend_extra_port,
2097             ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2098             ldap_backend_forced_uri=ldap_backend_forced_uri)
2099     else:
2100         raise ValueError("Unknown LDAP backend type selected")
2101
2102     provision_backend.init()
2103     provision_backend.start()
2104
2105     # only install a new shares config db if there is none
2106     if not os.path.exists(paths.shareconf):
2107         logger.info("Setting up share.ldb")
2108         share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2109         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2110
2111     logger.info("Setting up secrets.ldb")
2112     secrets_ldb = setup_secretsdb(paths,
2113         session_info=session_info,
2114         backend_credentials=provision_backend.credentials, lp=lp)
2115
2116     try:
2117         logger.info("Setting up the registry")
2118         setup_registry(paths.hklm, session_info, lp=lp)
2119
2120         logger.info("Setting up the privileges database")
2121         setup_privileges(paths.privilege, session_info, lp=lp)
2122
2123         logger.info("Setting up idmap db")
2124         idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2125
2126         setup_name_mappings(idmap, sid=str(domainsid),
2127                             root_uid=root_uid, nobody_uid=nobody_uid,
2128                             users_gid=users_gid, root_gid=root_gid)
2129
2130         logger.info("Setting up SAM db")
2131         samdb = setup_samdb(paths.samdb, session_info,
2132                             provision_backend, lp, names, logger=logger,
2133                             serverrole=serverrole,
2134                             schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2135
2136         if serverrole == "active directory domain controller":
2137             if paths.netlogon is None:
2138                 raise MissingShareError("netlogon", paths.smbconf)
2139
2140             if paths.sysvol is None:
2141                 raise MissingShareError("sysvol", paths.smbconf)
2142
2143             if not os.path.isdir(paths.netlogon):
2144                 os.makedirs(paths.netlogon, 0755)
2145
2146         if adminpass is None:
2147             adminpass = samba.generate_random_password(12, 32)
2148             adminpass_generated = True
2149         else:
2150             adminpass = unicode(adminpass, 'utf-8')
2151             adminpass_generated = False
2152
2153         if samdb_fill == FILL_FULL:
2154             provision_fill(samdb, secrets_ldb, logger, names, paths,
2155                     schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2156                     hostip=hostip, hostip6=hostip6,
2157                     next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2158                     krbtgtpass=krbtgtpass,
2159                     policyguid=policyguid, policyguid_dc=policyguid_dc,
2160                     invocationid=invocationid, machinepass=machinepass,
2161                     ntdsguid=ntdsguid, dns_backend=dns_backend,
2162                     dnspass=dnspass, serverrole=serverrole,
2163                     dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2164                     lp=lp, use_ntvfs=use_ntvfs,
2165                            skip_sysvolacl=skip_sysvolacl)
2166
2167         create_krb5_conf(paths.krb5conf,
2168                          dnsdomain=names.dnsdomain, hostname=names.hostname,
2169                          realm=names.realm)
2170         logger.info("A Kerberos configuration suitable for Samba 4 has been "
2171                     "generated at %s", paths.krb5conf)
2172
2173         if serverrole == "active directory domain controller":
2174             create_dns_update_list(lp, logger, paths)
2175
2176         backend_result = provision_backend.post_setup()
2177         provision_backend.shutdown()
2178
2179     except:
2180         secrets_ldb.transaction_cancel()
2181         raise
2182
2183     # Now commit the secrets.ldb to disk
2184     secrets_ldb.transaction_commit()
2185
2186     # the commit creates the dns.keytab, now chown it
2187     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2188     if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2189         try:
2190             os.chmod(dns_keytab_path, 0640)
2191             os.chown(dns_keytab_path, -1, paths.bind_gid)
2192         except OSError:
2193             if not os.environ.has_key('SAMBA_SELFTEST'):
2194                 logger.info("Failed to chown %s to bind gid %u",
2195                             dns_keytab_path, paths.bind_gid)
2196
2197     result = ProvisionResult()
2198     result.server_role = serverrole
2199     result.domaindn = domaindn
2200     result.paths = paths
2201     result.names = names
2202     result.lp = lp
2203     result.samdb = samdb
2204     result.idmap = idmap
2205     result.domainsid = str(domainsid)
2206
2207     if samdb_fill == FILL_FULL:
2208         result.adminpass_generated = adminpass_generated
2209         result.adminpass = adminpass
2210     else:
2211         result.adminpass_generated = False
2212         result.adminpass = None
2213
2214     result.backend_result = backend_result
2215
2216     if use_rfc2307:
2217         provision_fake_ypserver(logger=logger, samdb=samdb,
2218                 domaindn=names.domaindn, netbiosname=names.netbiosname,
2219                 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2220
2221     return result
2222
2223
2224 def provision_become_dc(smbconf=None, targetdir=None,
2225         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2226         serverdn=None, domain=None, hostname=None, domainsid=None,
2227         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2228         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2229         dns_backend=None, root=None, nobody=None, users=None,
2230         backup=None, serverrole=None, ldap_backend=None,
2231         ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2232
2233     logger = logging.getLogger("provision")
2234     samba.set_debug_level(debuglevel)
2235
2236     res = provision(logger, system_session(),
2237         smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2238         realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2239         configdn=configdn, serverdn=serverdn, domain=domain,
2240         hostname=hostname, hostip=None, domainsid=domainsid,
2241         machinepass=machinepass,
2242         serverrole="active directory domain controller",
2243         sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2244         use_ntvfs=use_ntvfs)
2245     res.lp.set("debuglevel", str(debuglevel))
2246     return res
2247
2248
2249 def create_krb5_conf(path, dnsdomain, hostname, realm):
2250     """Write out a file containing a valid krb5.conf file
2251
2252     :param path: Path of the new krb5.conf file.
2253     :param dnsdomain: DNS Domain name
2254     :param hostname: Local hostname
2255     :param realm: Realm name
2256     """
2257     setup_file(setup_path("krb5.conf"), path, {
2258             "DNSDOMAIN": dnsdomain,
2259             "HOSTNAME": hostname,
2260             "REALM": realm,
2261         })
2262
2263
2264 class ProvisioningError(Exception):
2265     """A generic provision error."""
2266
2267     def __init__(self, value):
2268         self.value = value
2269
2270     def __str__(self):
2271         return "ProvisioningError: " + self.value
2272
2273
2274 class InvalidNetbiosName(Exception):
2275     """A specified name was not a valid NetBIOS name."""
2276
2277     def __init__(self, name):
2278         super(InvalidNetbiosName, self).__init__(
2279             "The name '%r' is not a valid NetBIOS name" % name)
2280
2281
2282 class MissingShareError(ProvisioningError):
2283
2284     def __init__(self, name, smbconf):
2285         super(MissingShareError, self).__init__(
2286             "Existing smb.conf does not have a [%s] share, but you are "
2287             "configuring a DC. Please remove %s or add the share manually." %
2288             (name, smbconf))