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