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