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