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