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