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