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