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