c7fda92f5e1800d94742d8fd383bf46a0e5d12d3
[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         "passdb backend": "samba4",
617         "netbios name": netbiosname,
618         "workgroup": domain,
619         "realm": realm,
620         "server role": serverrole,
621         }
622
623     if lp is None:
624         lp = samba.param.LoadParm()
625     #Load non-existent file
626     if os.path.exists(smbconf):
627         lp.load(smbconf)
628
629     if global_param is not None:
630         for ent in global_param:
631             if global_param[ent] is not None:
632                 global_settings[ent] = " ".join(global_param[ent])
633
634     if targetdir is not None:
635         global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
636         global_settings["lock dir"] = os.path.abspath(targetdir)
637         global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
638         global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
639
640         lp.set("lock dir", os.path.abspath(targetdir))
641         lp.set("state directory",  global_settings["state directory"])
642         lp.set("cache directory", global_settings["cache directory"])
643
644     if eadb:
645         if use_ntvfs and not lp.get("posix:eadb"):
646             if targetdir is not None:
647                 privdir = os.path.join(targetdir, "private")
648             else:
649                 privdir = lp.get("private dir")
650             lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
651         elif not use_ntvfs and not lp.get("xattr_tdb:file"):
652             if targetdir is not None:
653                 statedir = os.path.join(targetdir, "state")
654             else:
655                 statedir = lp.get("state directory")
656             lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
657
658     shares = {}
659     if serverrole == "active directory domain controller":
660         shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
661         shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
662             "scripts")
663
664     f = open(smbconf, 'w')
665     try:
666         f.write("[globals]\n")
667         for key, val in global_settings.iteritems():
668             f.write("\t%s = %s\n" % (key, val))
669         f.write("\n")
670
671         for name, path in shares.iteritems():
672             f.write("[%s]\n" % name)
673             f.write("\tpath = %s\n" % path)
674             f.write("\tread only = no\n")
675             f.write("\n")
676     finally:
677         f.close()
678     # reload the smb.conf
679     lp.load(smbconf)
680
681     # and dump it without any values that are the default
682     # this ensures that any smb.conf parameters that were set
683     # on the provision/join command line are set in the resulting smb.conf
684     f = open(smbconf, mode='w')
685     try:
686         lp.dump(f, False)
687     finally:
688         f.close()
689
690
691 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
692                         users_gid, wheel_gid):
693     """setup reasonable name mappings for sam names to unix names.
694
695     :param samdb: SamDB object.
696     :param idmap: IDmap db object.
697     :param sid: The domain sid.
698     :param domaindn: The domain DN.
699     :param root_uid: uid of the UNIX root user.
700     :param nobody_uid: uid of the UNIX nobody user.
701     :param users_gid: gid of the UNIX users group.
702     :param wheel_gid: gid of the UNIX wheel group.
703     """
704     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
705     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
706
707     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
708     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
709
710
711 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
712                            provision_backend, names, schema, serverrole,
713                            erase=False):
714     """Setup the partitions for the SAM database.
715
716     Alternatively, provision() may call this, and then populate the database.
717
718     :note: This will wipe the Sam Database!
719
720     :note: This function always removes the local SAM LDB file. The erase
721         parameter controls whether to erase the existing data, which
722         may not be stored locally but in LDAP.
723
724     """
725     assert session_info is not None
726
727     # We use options=["modules:"] to stop the modules loading - we
728     # just want to wipe and re-initialise the database, not start it up
729
730     try:
731         os.unlink(samdb_path)
732     except OSError:
733         pass
734
735     samdb = Ldb(url=samdb_path, session_info=session_info,
736                 lp=lp, options=["modules:"])
737
738     ldap_backend_line = "# No LDAP backend"
739     if provision_backend.type != "ldb":
740         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
741
742     samdb.transaction_start()
743     try:
744         logger.info("Setting up sam.ldb partitions and settings")
745         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
746                 "LDAP_BACKEND_LINE": ldap_backend_line
747         })
748
749
750         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
751                 "BACKEND_TYPE": provision_backend.type,
752                 "SERVER_ROLE": serverrole
753                 })
754
755         logger.info("Setting up sam.ldb rootDSE")
756         setup_samdb_rootdse(samdb, names)
757     except:
758         samdb.transaction_cancel()
759         raise
760     else:
761         samdb.transaction_commit()
762
763
764 def secretsdb_self_join(secretsdb, domain,
765                         netbiosname, machinepass, domainsid=None,
766                         realm=None, dnsdomain=None,
767                         keytab_path=None,
768                         key_version_number=1,
769                         secure_channel_type=SEC_CHAN_WKSTA):
770     """Add domain join-specific bits to a secrets database.
771
772     :param secretsdb: Ldb Handle to the secrets database
773     :param machinepass: Machine password
774     """
775     attrs = ["whenChanged",
776            "secret",
777            "priorSecret",
778            "priorChanged",
779            "krb5Keytab",
780            "privateKeytab"]
781
782     if realm is not None:
783         if dnsdomain is None:
784             dnsdomain = realm.lower()
785         dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
786     else:
787         dnsname = None
788     shortname = netbiosname.lower()
789
790     # We don't need to set msg["flatname"] here, because rdn_name will handle
791     # it, and it causes problems for modifies anyway
792     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
793     msg["secureChannelType"] = [str(secure_channel_type)]
794     msg["objectClass"] = ["top", "primaryDomain"]
795     if dnsname is not None:
796         msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
797         msg["realm"] = [realm]
798         msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
799         msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
800         msg["privateKeytab"] = ["secrets.keytab"]
801
802     msg["secret"] = [machinepass]
803     msg["samAccountName"] = ["%s$" % netbiosname]
804     msg["secureChannelType"] = [str(secure_channel_type)]
805     if domainsid is not None:
806         msg["objectSid"] = [ndr_pack(domainsid)]
807
808     # This complex expression tries to ensure that we don't have more
809     # than one record for this SID, realm or netbios domain at a time,
810     # but we don't delete the old record that we are about to modify,
811     # because that would delete the keytab and previous password.
812     res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
813         expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
814         scope=ldb.SCOPE_ONELEVEL)
815
816     for del_msg in res:
817         secretsdb.delete(del_msg.dn)
818
819     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
820
821     if len(res) == 1:
822         msg["priorSecret"] = [res[0]["secret"][0]]
823         msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
824
825         try:
826             msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
827         except KeyError:
828             pass
829
830         try:
831             msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
832         except KeyError:
833             pass
834
835         for el in msg:
836             if el != 'dn':
837                 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
838         secretsdb.modify(msg)
839         secretsdb.rename(res[0].dn, msg.dn)
840     else:
841         spn = [ 'HOST/%s' % shortname ]
842         if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
843             # we are a domain controller then we add servicePrincipalName
844             # entries for the keytab code to update.
845             spn.extend([ 'HOST/%s' % dnsname ])
846         msg["servicePrincipalName"] = spn
847
848         secretsdb.add(msg)
849
850
851 def setup_secretsdb(paths, session_info, backend_credentials, lp):
852     """Setup the secrets database.
853
854    :note: This function does not handle exceptions and transaction on purpose,
855        it's up to the caller to do this job.
856
857     :param path: Path to the secrets database.
858     :param session_info: Session info.
859     :param credentials: Credentials
860     :param lp: Loadparm context
861     :return: LDB handle for the created secrets database
862     """
863     if os.path.exists(paths.secrets):
864         os.unlink(paths.secrets)
865
866     keytab_path = os.path.join(paths.private_dir, paths.keytab)
867     if os.path.exists(keytab_path):
868         os.unlink(keytab_path)
869
870     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
871     if os.path.exists(dns_keytab_path):
872         os.unlink(dns_keytab_path)
873
874     path = paths.secrets
875
876     secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
877     secrets_ldb.erase()
878     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
879     secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
880     secrets_ldb.transaction_start()
881     try:
882         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
883
884         if (backend_credentials is not None and
885             backend_credentials.authentication_requested()):
886             if backend_credentials.get_bind_dn() is not None:
887                 setup_add_ldif(secrets_ldb,
888                     setup_path("secrets_simple_ldap.ldif"), {
889                         "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
890                         "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
891                         })
892             else:
893                 setup_add_ldif(secrets_ldb,
894                     setup_path("secrets_sasl_ldap.ldif"), {
895                         "LDAPADMINUSER": backend_credentials.get_username(),
896                         "LDAPADMINREALM": backend_credentials.get_realm(),
897                         "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
898                         })
899     except:
900         secrets_ldb.transaction_cancel()
901         raise
902     return secrets_ldb
903
904
905 def setup_privileges(path, session_info, lp):
906     """Setup the privileges database.
907
908     :param path: Path to the privileges database.
909     :param session_info: Session info.
910     :param credentials: Credentials
911     :param lp: Loadparm context
912     :return: LDB handle for the created secrets database
913     """
914     if os.path.exists(path):
915         os.unlink(path)
916     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
917     privilege_ldb.erase()
918     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
919
920
921 def setup_registry(path, session_info, lp):
922     """Setup the registry.
923
924     :param path: Path to the registry database
925     :param session_info: Session information
926     :param credentials: Credentials
927     :param lp: Loadparm context
928     """
929     reg = samba.registry.Registry()
930     hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
931     reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
932     provision_reg = setup_path("provision.reg")
933     assert os.path.exists(provision_reg)
934     reg.diff_apply(provision_reg)
935
936
937 def setup_idmapdb(path, session_info, lp):
938     """Setup the idmap database.
939
940     :param path: path to the idmap database
941     :param session_info: Session information
942     :param credentials: Credentials
943     :param lp: Loadparm context
944     """
945     if os.path.exists(path):
946         os.unlink(path)
947
948     idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
949     idmap_ldb.erase()
950     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
951     return idmap_ldb
952
953
954 def setup_samdb_rootdse(samdb, names):
955     """Setup the SamDB rootdse.
956
957     :param samdb: Sam Database handle
958     """
959     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
960         "SCHEMADN": names.schemadn,
961         "DOMAINDN": names.domaindn,
962         "ROOTDN"  : names.rootdn,
963         "CONFIGDN": names.configdn,
964         "SERVERDN": names.serverdn,
965         })
966
967
968 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
969         dnspass, domainsid, next_rid, invocationid, policyguid, policyguid_dc,
970         domainControllerFunctionality, ntdsguid=None, dc_rid=None):
971     """Join a host to its own domain."""
972     assert isinstance(invocationid, str)
973     if ntdsguid is not None:
974         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
975     else:
976         ntdsguid_line = ""
977
978     if dc_rid is None:
979         dc_rid = next_rid
980
981     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
982               "CONFIGDN": names.configdn,
983               "SCHEMADN": names.schemadn,
984               "DOMAINDN": names.domaindn,
985               "SERVERDN": names.serverdn,
986               "INVOCATIONID": invocationid,
987               "NETBIOSNAME": names.netbiosname,
988               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
989               "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
990               "DOMAINSID": str(domainsid),
991               "DCRID": str(dc_rid),
992               "SAMBA_VERSION_STRING": version,
993               "NTDSGUID": ntdsguid_line,
994               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
995                   domainControllerFunctionality),
996               "RIDALLOCATIONSTART": str(next_rid + 100),
997               "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
998
999     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1000               "POLICYGUID": policyguid,
1001               "POLICYGUID_DC": policyguid_dc,
1002               "DNSDOMAIN": names.dnsdomain,
1003               "DOMAINDN": names.domaindn})
1004
1005     # If we are setting up a subdomain, then this has been replicated in, so we
1006     # don't need to add it
1007     if fill == FILL_FULL:
1008         setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1009                 "CONFIGDN": names.configdn,
1010                 "SCHEMADN": names.schemadn,
1011                 "DOMAINDN": names.domaindn,
1012                 "SERVERDN": names.serverdn,
1013                 "INVOCATIONID": invocationid,
1014                 "NETBIOSNAME": names.netbiosname,
1015                 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1016                 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1017                 "DOMAINSID": str(domainsid),
1018                 "DCRID": str(dc_rid),
1019                 "SAMBA_VERSION_STRING": version,
1020                 "NTDSGUID": ntdsguid_line,
1021                 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1022                     domainControllerFunctionality)})
1023
1024     # Setup fSMORoleOwner entries to point at the newly created DC entry
1025         setup_modify_ldif(samdb,
1026             setup_path("provision_self_join_modify_config.ldif"), {
1027                 "CONFIGDN": names.configdn,
1028                 "SCHEMADN": names.schemadn,
1029                 "DEFAULTSITE": names.sitename,
1030                 "NETBIOSNAME": names.netbiosname,
1031                 "SERVERDN": names.serverdn,
1032                 })
1033
1034     system_session_info = system_session()
1035     samdb.set_session_info(system_session_info)
1036     # Setup fSMORoleOwner entries to point at the newly created DC entry to
1037     # modify a serverReference under cn=config when we are a subdomain, we must
1038     # be system due to ACLs
1039     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1040               "DOMAINDN": names.domaindn,
1041               "SERVERDN": names.serverdn,
1042               "NETBIOSNAME": names.netbiosname,
1043               })
1044
1045     samdb.set_session_info(admin_session_info)
1046
1047     # This is Samba4 specific and should be replaced by the correct
1048     # DNS AD-style setup
1049     setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1050               "DNSDOMAIN": names.dnsdomain,
1051               "DOMAINDN": names.domaindn,
1052               "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1053               "HOSTNAME" : names.hostname,
1054               "DNSNAME" : '%s.%s' % (
1055                   names.netbiosname.lower(), names.dnsdomain.lower())
1056               })
1057
1058
1059 def getpolicypath(sysvolpath, dnsdomain, guid):
1060     """Return the physical path of policy given its guid.
1061
1062     :param sysvolpath: Path to the sysvol folder
1063     :param dnsdomain: DNS name of the AD domain
1064     :param guid: The GUID of the policy
1065     :return: A string with the complete path to the policy folder
1066     """
1067     if guid[0] != "{":
1068         guid = "{%s}" % guid
1069     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1070     return policy_path
1071
1072
1073 def create_gpo_struct(policy_path):
1074     if not os.path.exists(policy_path):
1075         os.makedirs(policy_path, 0775)
1076     f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1077     try:
1078         f.write("[General]\r\nVersion=0")
1079     finally:
1080         f.close()
1081     p = os.path.join(policy_path, "MACHINE")
1082     if not os.path.exists(p):
1083         os.makedirs(p, 0775)
1084     p = os.path.join(policy_path, "USER")
1085     if not os.path.exists(p):
1086         os.makedirs(p, 0775)
1087
1088
1089 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1090     """Create the default GPO for a domain
1091
1092     :param sysvolpath: Physical path for the sysvol folder
1093     :param dnsdomain: DNS domain name of the AD domain
1094     :param policyguid: GUID of the default domain policy
1095     :param policyguid_dc: GUID of the default domain controler policy
1096     """
1097     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1098     create_gpo_struct(policy_path)
1099
1100     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1101     create_gpo_struct(policy_path)
1102
1103
1104 def setup_samdb(path, session_info, provision_backend, lp, names,
1105         logger, fill, serverrole, schema, am_rodc=False):
1106     """Setup a complete SAM Database.
1107
1108     :note: This will wipe the main SAM database file!
1109     """
1110
1111     # Also wipes the database
1112     setup_samdb_partitions(path, logger=logger, lp=lp,
1113         provision_backend=provision_backend, session_info=session_info,
1114         names=names, serverrole=serverrole, schema=schema)
1115
1116     # Load the database, but don's load the global schema and don't connect
1117     # quite yet
1118     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1119                   credentials=provision_backend.credentials, lp=lp,
1120                   global_schema=False, am_rodc=am_rodc)
1121
1122     logger.info("Pre-loading the Samba 4 and AD schema")
1123
1124     # Load the schema from the one we computed earlier
1125     samdb.set_schema(schema, write_indices_and_attributes=False)
1126
1127     # Set the NTDS settings DN manually - in order to have it already around
1128     # before the provisioned tree exists and we connect
1129     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1130
1131     # And now we can connect to the DB - the schema won't be loaded from the
1132     # DB
1133     samdb.connect(path)
1134
1135     # But we have to give it one more kick to have it use the schema
1136     # during provision - it needs, now that it is connected, to write
1137     # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1138     samdb.set_schema(schema, write_indices_and_attributes=True)
1139
1140     return samdb
1141
1142
1143 def fill_samdb(samdb, lp, names,
1144         logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1145         adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1146         serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1147         next_rid=None, dc_rid=None):
1148
1149     if next_rid is None:
1150         next_rid = 1000
1151
1152     # Provision does not make much sense values larger than 1000000000
1153     # as the upper range of the rIDAvailablePool is 1073741823 and
1154     # we don't want to create a domain that cannot allocate rids.
1155     if next_rid < 1000 or next_rid > 1000000000:
1156         error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1157         error += "the valid range is %u-%u. The default is %u." % (
1158             1000, 1000000000, 1000)
1159         raise ProvisioningError(error)
1160
1161     # ATTENTION: Do NOT change these default values without discussion with the
1162     # team and/or release manager. They have a big impact on the whole program!
1163     domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1164
1165     if dom_for_fun_level is None:
1166         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1167
1168     if dom_for_fun_level > domainControllerFunctionality:
1169         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!")
1170
1171     domainFunctionality = dom_for_fun_level
1172     forestFunctionality = dom_for_fun_level
1173
1174     # Set the NTDS settings DN manually - in order to have it already around
1175     # before the provisioned tree exists and we connect
1176     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1177
1178     samdb.transaction_start()
1179     try:
1180         # Set the domain functionality levels onto the database.
1181         # Various module (the password_hash module in particular) need
1182         # to know what level of AD we are emulating.
1183
1184         # These will be fixed into the database via the database
1185         # modifictions below, but we need them set from the start.
1186         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1187         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1188         samdb.set_opaque_integer("domainControllerFunctionality",
1189             domainControllerFunctionality)
1190
1191         samdb.set_domain_sid(str(domainsid))
1192         samdb.set_invocation_id(invocationid)
1193
1194         logger.info("Adding DomainDN: %s" % names.domaindn)
1195
1196         # impersonate domain admin
1197         admin_session_info = admin_session(lp, str(domainsid))
1198         samdb.set_session_info(admin_session_info)
1199         if domainguid is not None:
1200             domainguid_line = "objectGUID: %s\n-" % domainguid
1201         else:
1202             domainguid_line = ""
1203
1204         descr = b64encode(get_domain_descriptor(domainsid))
1205         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1206                 "DOMAINDN": names.domaindn,
1207                 "DOMAINSID": str(domainsid),
1208                 "DESCRIPTOR": descr,
1209                 "DOMAINGUID": domainguid_line
1210                 })
1211
1212         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1213             "DOMAINDN": names.domaindn,
1214             "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1215             "NEXTRID": str(next_rid),
1216             "DEFAULTSITE": names.sitename,
1217             "CONFIGDN": names.configdn,
1218             "POLICYGUID": policyguid,
1219             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1220             "SAMBA_VERSION_STRING": version
1221             })
1222
1223         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1224         if fill == FILL_FULL:
1225             logger.info("Adding configuration container")
1226             descr = b64encode(get_config_descriptor(domainsid))
1227             setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1228                     "CONFIGDN": names.configdn,
1229                     "DESCRIPTOR": descr,
1230                     })
1231
1232             # The LDIF here was created when the Schema object was constructed
1233             logger.info("Setting up sam.ldb schema")
1234             samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1235             samdb.modify_ldif(schema.schema_dn_modify)
1236             samdb.write_prefixes_from_schema()
1237             samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1238             setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1239                            {"SCHEMADN": names.schemadn})
1240
1241         # Now register this container in the root of the forest
1242         msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1243         msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1244                     "subRefs")
1245
1246     except:
1247         samdb.transaction_cancel()
1248         raise
1249     else:
1250         samdb.transaction_commit()
1251
1252     samdb.transaction_start()
1253     try:
1254         samdb.invocation_id = invocationid
1255
1256         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1257         if fill == FILL_FULL:
1258             logger.info("Setting up sam.ldb configuration data")
1259             setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1260                     "CONFIGDN": names.configdn,
1261                     "NETBIOSNAME": names.netbiosname,
1262                     "DEFAULTSITE": names.sitename,
1263                     "DNSDOMAIN": names.dnsdomain,
1264                     "DOMAIN": names.domain,
1265                     "SCHEMADN": names.schemadn,
1266                     "DOMAINDN": names.domaindn,
1267                     "SERVERDN": names.serverdn,
1268                     "FOREST_FUNCTIONALITY": str(forestFunctionality),
1269                     "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1270                     })
1271
1272             logger.info("Setting up display specifiers")
1273             display_specifiers_ldif = read_ms_ldif(
1274                 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1275             display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1276                                                      {"CONFIGDN": names.configdn})
1277             check_all_substituted(display_specifiers_ldif)
1278             samdb.add_ldif(display_specifiers_ldif)
1279
1280         logger.info("Adding users container")
1281         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1282                 "DOMAINDN": names.domaindn})
1283         logger.info("Modifying users container")
1284         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1285                 "DOMAINDN": names.domaindn})
1286         logger.info("Adding computers container")
1287         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1288                 "DOMAINDN": names.domaindn})
1289         logger.info("Modifying computers container")
1290         setup_modify_ldif(samdb,
1291             setup_path("provision_computers_modify.ldif"), {
1292                 "DOMAINDN": names.domaindn})
1293         logger.info("Setting up sam.ldb data")
1294         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1295             "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1296             "DOMAINDN": names.domaindn,
1297             "NETBIOSNAME": names.netbiosname,
1298             "DEFAULTSITE": names.sitename,
1299             "CONFIGDN": names.configdn,
1300             "SERVERDN": names.serverdn,
1301             "RIDAVAILABLESTART": str(next_rid + 600),
1302             "POLICYGUID_DC": policyguid_dc
1303             })
1304
1305         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1306         if fill == FILL_FULL:
1307             setup_modify_ldif(samdb,
1308                               setup_path("provision_configuration_references.ldif"), {
1309                     "CONFIGDN": names.configdn,
1310                     "SCHEMADN": names.schemadn})
1311
1312             logger.info("Setting up well known security principals")
1313             setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1314                 "CONFIGDN": names.configdn,
1315                 })
1316
1317         if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1318             setup_modify_ldif(samdb,
1319                               setup_path("provision_basedn_references.ldif"),
1320                               {"DOMAINDN": names.domaindn})
1321
1322             logger.info("Setting up sam.ldb users and groups")
1323             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1324                 "DOMAINDN": names.domaindn,
1325                 "DOMAINSID": str(domainsid),
1326                 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1327                 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1328                 })
1329
1330             logger.info("Setting up self join")
1331             setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1332                 invocationid=invocationid,
1333                 dnspass=dnspass,
1334                 machinepass=machinepass,
1335                 domainsid=domainsid,
1336                 next_rid=next_rid,
1337                 dc_rid=dc_rid,
1338                 policyguid=policyguid,
1339                 policyguid_dc=policyguid_dc,
1340                 domainControllerFunctionality=domainControllerFunctionality,
1341                 ntdsguid=ntdsguid)
1342
1343             ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1344             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1345                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1346             assert isinstance(names.ntdsguid, str)
1347     except:
1348         samdb.transaction_cancel()
1349         raise
1350     else:
1351         samdb.transaction_commit()
1352         return samdb
1353
1354
1355 FILL_FULL = "FULL"
1356 FILL_SUBDOMAIN = "SUBDOMAIN"
1357 FILL_NT4SYNC = "NT4SYNC"
1358 FILL_DRS = "DRS"
1359 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1360 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)"
1361
1362
1363 def set_dir_acl(path, acl, lp, domsid, use_ntvfs):
1364     setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs)
1365     for root, dirs, files in os.walk(path, topdown=False):
1366         for name in files:
1367             setntacl(lp, os.path.join(root, name), acl, domsid, use_ntvfs=use_ntvfs)
1368         for name in dirs:
1369             setntacl(lp, os.path.join(root, name), acl, domsid, use_ntvfs=use_ntvfs)
1370
1371
1372 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs):
1373     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1374     folders beneath.
1375
1376     :param sysvol: Physical path for the sysvol folder
1377     :param dnsdomain: The DNS name of the domain
1378     :param domainsid: The SID of the domain
1379     :param domaindn: The DN of the domain (ie. DC=...)
1380     :param samdb: An LDB object on the SAM db
1381     :param lp: an LP object
1382     """
1383
1384     # Set ACL for GPO root folder
1385     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1386     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid), use_ntvfs=use_ntvfs)
1387
1388     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1389                         attrs=["cn", "nTSecurityDescriptor"],
1390                         expression="", scope=ldb.SCOPE_ONELEVEL)
1391
1392     for policy in res:
1393         acl = ndr_unpack(security.descriptor,
1394                          str(policy["nTSecurityDescriptor"])).as_sddl()
1395         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1396         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1397                     str(domainsid), use_ntvfs)
1398
1399
1400 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain, domaindn,
1401     lp, use_ntvfs):
1402     """Set the ACL for the sysvol share and the subfolders
1403
1404     :param samdb: An LDB object on the SAM db
1405     :param netlogon: Physical path for the netlogon folder
1406     :param sysvol: Physical path for the sysvol folder
1407     :param uid: The UID of the "Administrator" user
1408     :param gid: The GID of the "Domain adminstrators" group
1409     :param domainsid: The SID of the domain
1410     :param dnsdomain: The DNS name of the domain
1411     :param domaindn: The DN of the domain (ie. DC=...)
1412     """
1413
1414     if not use_ntvfs:
1415         # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1416         s3conf = s3param.get_context()
1417         s3conf.load(lp.configfile)
1418         # ensure we are using the right samba4 passdb backend, no matter what
1419         s3conf.set("passdb backend", "samba4:%s" % samdb.url)
1420         passdb.reload_static_pdb()
1421
1422         # ensure that we init the samba4 backend, so the domain sid is marked in secrets.tdb
1423         s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1424
1425         # now ensure everything matches correctly, to avoid wierd issues
1426         if passdb.get_global_sam_sid() != domainsid:
1427             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))
1428
1429         domain_info = s4_passdb.domain_info()
1430         if domain_info["dom_sid"] != domainsid:
1431             raise ProvisioningError('SID as seen by pdb_samba4 [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
1432
1433         if domain_info["dns_domain"].upper() != dnsdomain.upper():
1434             raise ProvisioningError('Realm as seen by pdb_samba4 [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
1435
1436
1437     try:
1438         if use_ntvfs:
1439             os.chown(sysvol, -1, gid)
1440     except OSError:
1441         canchown = False
1442     else:
1443         canchown = True
1444
1445     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1446     setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs)
1447     for root, dirs, files in os.walk(sysvol, topdown=False):
1448         for name in files:
1449             if use_ntvfs and canchown:
1450                 os.chown(os.path.join(root, name), -1, gid)
1451             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs)
1452         for name in dirs:
1453             if use_ntvfs and canchown:
1454                 os.chown(os.path.join(root, name), -1, gid)
1455             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs)
1456
1457     # Set acls on Policy folder and policies folders
1458     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs)
1459
1460 def acl_type(direct_db_access):
1461     if direct_db_access:
1462         return "DB"
1463     else:
1464         return "VFS"
1465
1466 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1467     fsacl = getntacl(lp, path, direct_db_access=direct_db_access)
1468     fsacl_sddl = fsacl.as_sddl(domainsid)
1469     if fsacl_sddl != acl:
1470         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))
1471         
1472     for root, dirs, files in os.walk(path, topdown=False):
1473         for name in files:
1474             fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1475             if fsacl is None:
1476                 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1477             fsacl_sddl = fsacl.as_sddl(domainsid)
1478             if fsacl_sddl != acl:
1479                 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, acl))
1480
1481         for name in files:
1482             fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1483             if fsacl is None:
1484                 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1485             fsacl_sddl = fsacl.as_sddl(domainsid)
1486             if fsacl_sddl != acl:
1487                 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, acl))
1488
1489
1490 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, direct_db_access):
1491     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1492     folders beneath.
1493
1494     :param sysvol: Physical path for the sysvol folder
1495     :param dnsdomain: The DNS name of the domain
1496     :param domainsid: The SID of the domain
1497     :param domaindn: The DN of the domain (ie. DC=...)
1498     :param samdb: An LDB object on the SAM db
1499     :param lp: an LP object
1500     """
1501
1502     # Set ACL for GPO root folder
1503     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1504     fsacl = getntacl(lp, root_policy_path, direct_db_access=direct_db_access)
1505     if fsacl is None:
1506         raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1507     fsacl_sddl = fsacl.as_sddl(domainsid)
1508     if fsacl_sddl != POLICIES_ACL:
1509         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))
1510     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1511                         attrs=["cn", "nTSecurityDescriptor"],
1512                         expression="", scope=ldb.SCOPE_ONELEVEL)
1513
1514     for policy in res:
1515         acl = ndr_unpack(security.descriptor,
1516                          str(policy["nTSecurityDescriptor"])).as_sddl()
1517         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1518         check_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1519                       domainsid, direct_db_access)
1520
1521
1522 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1523     lp):
1524     """Set the ACL for the sysvol share and the subfolders
1525
1526     :param samdb: An LDB object on the SAM db
1527     :param netlogon: Physical path for the netlogon folder
1528     :param sysvol: Physical path for the sysvol folder
1529     :param uid: The UID of the "Administrator" user
1530     :param gid: The GID of the "Domain adminstrators" group
1531     :param domainsid: The SID of the domain
1532     :param dnsdomain: The DNS name of the domain
1533     :param domaindn: The DN of the domain (ie. DC=...)
1534     """
1535
1536     # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1537     s3conf = s3param.get_context()
1538     s3conf.load(lp.configfile)
1539     # ensure we are using the right samba4 passdb backend, no matter what
1540     s3conf.set("passdb backend", "samba4:%s" % samdb.url)
1541     # ensure that we init the samba4 backend, so the domain sid is marked in secrets.tdb
1542     s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1543
1544     # now ensure everything matches correctly, to avoid wierd issues
1545     if passdb.get_global_sam_sid() != domainsid:
1546         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))
1547
1548     domain_info = s4_passdb.domain_info()
1549     if domain_info["dom_sid"] != domainsid:
1550         raise ProvisioningError('SID as seen by pdb_samba4 [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
1551
1552     if domain_info["dns_domain"].upper() != dnsdomain.upper():
1553         raise ProvisioningError('Realm as seen by pdb_samba4 [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
1554
1555     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1556     for direct_db_access in [True, False]:
1557         for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1558             fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access)
1559             if fsacl is None:
1560                 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1561             fsacl_sddl = fsacl.as_sddl(domainsid)
1562             if fsacl_sddl != SYSVOL_ACL:
1563                 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))
1564
1565         # Check acls on Policy folder and policies folders
1566         check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, direct_db_access)
1567
1568
1569 def interface_ips_v4(lp):
1570     '''return only IPv4 IPs'''
1571     ips = samba.interface_ips(lp, False)
1572     ret = []
1573     for i in ips:
1574         if i.find(':') == -1:
1575             ret.append(i)
1576     return ret
1577
1578 def interface_ips_v6(lp, linklocal=False):
1579     '''return only IPv6 IPs'''
1580     ips = samba.interface_ips(lp, False)
1581     ret = []
1582     for i in ips:
1583         if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1584             ret.append(i)
1585     return ret
1586
1587
1588 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1589                    domainsid, schema=None,
1590                    targetdir=None, samdb_fill=FILL_FULL,
1591                    hostip=None, hostip6=None,
1592                    next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1593                    domainguid=None, policyguid=None, policyguid_dc=None,
1594                    invocationid=None, machinepass=None, ntdsguid=None,
1595                    dns_backend=None, dnspass=None,
1596                    serverrole=None, dom_for_fun_level=None,
1597                    am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=True):
1598     # create/adapt the group policy GUIDs
1599     # Default GUID for default policy are described at
1600     # "How Core Group Policy Works"
1601     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1602     if policyguid is None:
1603         policyguid = DEFAULT_POLICY_GUID
1604     policyguid = policyguid.upper()
1605     if policyguid_dc is None:
1606         policyguid_dc = DEFAULT_DC_POLICY_GUID
1607     policyguid_dc = policyguid_dc.upper()
1608
1609     if invocationid is None:
1610         invocationid = str(uuid.uuid4())
1611
1612     if krbtgtpass is None:
1613         krbtgtpass = samba.generate_random_password(128, 255)
1614     if machinepass is None:
1615         machinepass  = samba.generate_random_password(128, 255)
1616     if dnspass is None:
1617         dnspass = samba.generate_random_password(128, 255)
1618
1619     samdb = fill_samdb(samdb, lp, names, logger=logger,
1620                        domainsid=domainsid, schema=schema, domainguid=domainguid,
1621                        policyguid=policyguid, policyguid_dc=policyguid_dc,
1622                        fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1623                        invocationid=invocationid, machinepass=machinepass,
1624                        dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1625                        dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1626                        next_rid=next_rid, dc_rid=dc_rid)
1627
1628     if serverrole == "active directory domain controller":
1629
1630         # Set up group policies (domain policy and domain controller
1631         # policy)
1632         create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1633                            policyguid_dc)
1634         if not skip_sysvolacl:
1635             setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid, paths.wheel_gid,
1636                          domainsid, names.dnsdomain, names.domaindn, lp, use_ntvfs)
1637
1638         secretsdb_self_join(secrets_ldb, domain=names.domain,
1639                             realm=names.realm, dnsdomain=names.dnsdomain,
1640                             netbiosname=names.netbiosname, domainsid=domainsid,
1641                             machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1642
1643         # Now set up the right msDS-SupportedEncryptionTypes into the DB
1644         # In future, this might be determined from some configuration
1645         kerberos_enctypes = str(ENC_ALL_TYPES)
1646
1647         try:
1648             msg = ldb.Message(ldb.Dn(samdb,
1649                                      samdb.searchone("distinguishedName",
1650                                                      expression="samAccountName=%s$" % names.netbiosname,
1651                                                      scope=ldb.SCOPE_SUBTREE)))
1652             msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1653                 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1654                 name="msDS-SupportedEncryptionTypes")
1655             samdb.modify(msg)
1656         except ldb.LdbError, (enum, estr):
1657             if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1658                 # It might be that this attribute does not exist in this schema
1659                 raise
1660
1661         setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1662                      hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1663                      dnspass=dnspass, os_level=dom_for_fun_level,
1664                      targetdir=targetdir, site=DEFAULTSITE)
1665
1666         domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1667                                      attribute="objectGUID")
1668         assert isinstance(domainguid, str)
1669
1670     lastProvisionUSNs = get_last_provision_usn(samdb)
1671     maxUSN = get_max_usn(samdb, str(names.rootdn))
1672     if lastProvisionUSNs is not None:
1673         update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1674     else:
1675         set_provision_usn(samdb, 0, maxUSN, invocationid)
1676
1677     logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1678     setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1679                       { 'NTDSGUID' : names.ntdsguid })
1680
1681     # fix any dangling GUIDs from the provision
1682     logger.info("Fixing provision GUIDs")
1683     chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1684             quiet=True)
1685     samdb.transaction_start()
1686     try:
1687         # a small number of GUIDs are missing because of ordering issues in the
1688         # provision code
1689         for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1690             chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1691                                scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1692         chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1693                            scope=ldb.SCOPE_ONELEVEL,
1694                            attrs=['ipsecOwnersReference',
1695                                   'ipsecFilterReference',
1696                                   'ipsecISAKMPReference',
1697                                   'ipsecNegotiationPolicyReference',
1698                                   'ipsecNFAReference'])
1699     except:
1700         samdb.transaction_cancel()
1701         raise
1702     else:
1703         samdb.transaction_commit()
1704
1705
1706 _ROLES_MAP = {
1707     "ROLE_STANDALONE": "standalone server",
1708     "ROLE_DOMAIN_MEMBER": "member server",
1709     "ROLE_DOMAIN_BDC": "active directory domain controller",
1710     "ROLE_DOMAIN_PDC": "active directory domain controller",
1711     "dc": "active directory domain controller",
1712     "member": "member server",
1713     "domain controller": "active directory domain controller",
1714     "active directory domain controller": "active directory domain controller",
1715     "member server": "member server",
1716     "standalone": "standalone server",
1717     "standalone server": "standalone server",
1718     }
1719
1720
1721 def sanitize_server_role(role):
1722     """Sanitize a server role name.
1723
1724     :param role: Server role
1725     :raise ValueError: If the role can not be interpreted
1726     :return: Sanitized server role (one of "member server",
1727         "active directory domain controller", "standalone server")
1728     """
1729     try:
1730         return  _ROLES_MAP[role]
1731     except KeyError:
1732         raise ValueError(role)
1733
1734 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain, maxuid, maxgid):
1735     """Creates AD entries for the fake ypserver
1736     needed for being able to manipulate posix attrs via ADUC
1737     """
1738     samdb.transaction_start()
1739     try:
1740         logger.info("Setting up fake yp server settings")
1741         setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1742         "DOMAINDN": domaindn,
1743         "NETBIOSNAME": netbiosname,
1744         "NISDOMAIN": nisdomain,
1745          })
1746     except Exception:
1747         samdb.transaction_cancel()
1748         raise
1749     else:
1750         samdb.transaction_commit()
1751     if maxuid != None:
1752         pass
1753     if maxgid != None:
1754         pass
1755
1756 def provision(logger, session_info, credentials, smbconf=None,
1757         targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1758         domaindn=None, schemadn=None, configdn=None, serverdn=None,
1759         domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1760         next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1761         domainguid=None, policyguid=None, policyguid_dc=None,
1762         dns_backend=None, dnspass=None,
1763         invocationid=None, machinepass=None, ntdsguid=None,
1764         root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1765         serverrole=None, dom_for_fun_level=None, 
1766         backend_type=None, sitename=None,
1767         ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1768         useeadb=False, am_rodc=False,
1769         lp=None, use_ntvfs=False,
1770         use_rfc2307=False, maxuid=None, maxgid=None,
1771               skip_sysvolacl=True):
1772     """Provision samba4
1773
1774     :note: caution, this wipes all existing data!
1775     """
1776
1777     try:
1778         serverrole = sanitize_server_role(serverrole)
1779     except ValueError:
1780         raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1781
1782     if ldapadminpass is None:
1783         # Make a new, random password between Samba and it's LDAP server
1784         ldapadminpass = samba.generate_random_password(128, 255)
1785
1786     if backend_type is None:
1787         backend_type = "ldb"
1788
1789     if domainsid is None:
1790         domainsid = security.random_sid()
1791     else:
1792         domainsid = security.dom_sid(domainsid)
1793
1794     root_uid = findnss_uid([root or "root"])
1795     nobody_uid = findnss_uid([nobody or "nobody"])
1796     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1797     if wheel is None:
1798         wheel_gid = findnss_gid(["wheel", "adm"])
1799     else:
1800         wheel_gid = findnss_gid([wheel])
1801     try:
1802         bind_gid = findnss_gid(["bind", "named"])
1803     except KeyError:
1804         bind_gid = None
1805
1806     if targetdir is not None:
1807         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1808     elif smbconf is None:
1809         smbconf = samba.param.default_path()
1810     if not os.path.exists(os.path.dirname(smbconf)):
1811         os.makedirs(os.path.dirname(smbconf))
1812
1813     server_services = []
1814     global_param = {}
1815     if use_rfc2307:
1816         global_param["idmap_ldb:use rfc2307"] = ["yes"]
1817
1818     if dns_backend == "SAMBA_INTERNAL":
1819         server_services.append("+dns")
1820
1821     if use_ntvfs:
1822         server_services.append("+smb")
1823         server_services.append("-s3fs")
1824         global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1825
1826     if len(server_services) > 0:
1827         global_param["server services"] = server_services
1828
1829     # only install a new smb.conf if there isn't one there already
1830     if os.path.exists(smbconf):
1831         # if Samba Team members can't figure out the weird errors
1832         # loading an empty smb.conf gives, then we need to be smarter.
1833         # Pretend it just didn't exist --abartlet
1834         f = open(smbconf, 'r')
1835         try:
1836             data = f.read().lstrip()
1837         finally:
1838             f.close()
1839         if data is None or data == "":
1840             make_smbconf(smbconf, hostname, domain, realm,
1841                          targetdir, serverrole=serverrole,
1842                          eadb=useeadb, use_ntvfs=use_ntvfs,
1843                          lp=lp, global_param=global_param)
1844     else:
1845         make_smbconf(smbconf, hostname, domain, realm, targetdir,
1846                      serverrole=serverrole,
1847                      eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1848
1849     if lp is None:
1850         lp = samba.param.LoadParm()
1851     lp.load(smbconf)
1852     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1853         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1854         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1855         sitename=sitename, rootdn=rootdn)
1856     paths = provision_paths_from_lp(lp, names.dnsdomain)
1857
1858     paths.bind_gid = bind_gid
1859     paths.root_uid = root_uid;
1860     paths.wheel_gid = wheel_gid
1861
1862     if hostip is None:
1863         logger.info("Looking up IPv4 addresses")
1864         hostips = interface_ips_v4(lp)
1865         if len(hostips) > 0:
1866             hostip = hostips[0]
1867             if len(hostips) > 1:
1868                 logger.warning("More than one IPv4 address found. Using %s",
1869                     hostip)
1870     if hostip == "127.0.0.1":
1871         hostip = None
1872     if hostip is None:
1873         logger.warning("No IPv4 address will be assigned")
1874
1875     if hostip6 is None:
1876         logger.info("Looking up IPv6 addresses")
1877         hostips = interface_ips_v6(lp, linklocal=False)
1878         if hostips:
1879             hostip6 = hostips[0]
1880         if len(hostips) > 1:
1881             logger.warning("More than one IPv6 address found. Using %s", hostip6)
1882     if hostip6 is None:
1883         logger.warning("No IPv6 address will be assigned")
1884
1885     names.hostip = hostip
1886     names.hostip6 = hostip6
1887
1888     if serverrole is None:
1889         serverrole = lp.get("server role")
1890
1891     if not os.path.exists(paths.private_dir):
1892         os.mkdir(paths.private_dir)
1893     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1894         os.mkdir(os.path.join(paths.private_dir, "tls"))
1895     if not os.path.exists(paths.state_dir):
1896         os.mkdir(paths.state_dir)
1897
1898     if paths.sysvol and not os.path.exists(paths.sysvol):
1899         os.makedirs(paths.sysvol, 0775)
1900
1901     if not use_ntvfs and serverrole == "active directory domain controller":
1902         s3conf = s3param.get_context()
1903         s3conf.load(lp.configfile)
1904
1905         if paths.sysvol is None:
1906             raise MissingShareError("sysvol", paths.smbconf)
1907
1908         if not smbd.have_posix_acls():
1909             # This clue is only strictly correct for RPM and
1910             # Debian-like Linux systems, but hopefully other users
1911             # will get enough clue from it.
1912             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.")
1913             
1914         file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1915         try:
1916             try:
1917                 smbd.set_simple_acl(file.name, 0755, wheel_gid)
1918             except Exception:
1919                 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires.  Try the mounting the filesystem with the 'acl' option.")
1920             try:
1921                 smbd.chown(file.name, root_uid, wheel_gid)
1922             except Exception:
1923                 raise ProvisioningError("Unable to chown a file on your filesystem.  You may not be running provision as root.  ")
1924         finally:
1925             file.close()
1926
1927     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1928
1929     schema = Schema(domainsid, invocationid=invocationid,
1930         schemadn=names.schemadn)
1931
1932     if backend_type == "ldb":
1933         provision_backend = LDBBackend(backend_type, paths=paths,
1934             lp=lp, credentials=credentials,
1935             names=names, logger=logger)
1936     elif backend_type == "existing":
1937         # If support for this is ever added back, then the URI will need to be specified again
1938         provision_backend = ExistingBackend(backend_type, paths=paths,
1939             lp=lp, credentials=credentials,
1940             names=names, logger=logger,
1941             ldap_backend_forced_uri=None)
1942     elif backend_type == "fedora-ds":
1943         provision_backend = FDSBackend(backend_type, paths=paths,
1944             lp=lp, credentials=credentials,
1945             names=names, logger=logger, domainsid=domainsid,
1946             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1947             slapd_path=slapd_path,
1948             root=root)
1949     elif backend_type == "openldap":
1950         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1951             lp=lp, credentials=credentials,
1952             names=names, logger=logger, domainsid=domainsid,
1953             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1954             slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1955     else:
1956         raise ValueError("Unknown LDAP backend type selected")
1957
1958     provision_backend.init()
1959     provision_backend.start()
1960
1961     # only install a new shares config db if there is none
1962     if not os.path.exists(paths.shareconf):
1963         logger.info("Setting up share.ldb")
1964         share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1965         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1966
1967     logger.info("Setting up secrets.ldb")
1968     secrets_ldb = setup_secretsdb(paths,
1969         session_info=session_info,
1970         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1971
1972     try:
1973         logger.info("Setting up the registry")
1974         setup_registry(paths.hklm, session_info, lp=lp)
1975
1976         logger.info("Setting up the privileges database")
1977         setup_privileges(paths.privilege, session_info, lp=lp)
1978
1979         logger.info("Setting up idmap db")
1980         idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
1981
1982         setup_name_mappings(idmap, sid=str(domainsid),
1983                             root_uid=root_uid, nobody_uid=nobody_uid,
1984                             users_gid=users_gid, wheel_gid=wheel_gid)
1985
1986         logger.info("Setting up SAM db")
1987         samdb = setup_samdb(paths.samdb, session_info,
1988                             provision_backend, lp, names, logger=logger,
1989                             serverrole=serverrole,
1990                             schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1991
1992         if serverrole == "active directory domain controller":
1993             if paths.netlogon is None:
1994                 raise MissingShareError("netlogon", paths.smbconf)
1995
1996             if paths.sysvol is None:
1997                 raise MissingShareError("sysvol", paths.smbconf)
1998
1999             if not os.path.isdir(paths.netlogon):
2000                 os.makedirs(paths.netlogon, 0755)
2001
2002         if adminpass is None:
2003             adminpass = samba.generate_random_password(12, 32)
2004             adminpass_generated = True
2005         else:
2006             adminpass_generated = False
2007
2008         if samdb_fill == FILL_FULL:
2009             provision_fill(samdb, secrets_ldb, logger, names, paths,
2010                     schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2011                     hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2012                     next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2013                     krbtgtpass=krbtgtpass, domainguid=domainguid,
2014                     policyguid=policyguid, policyguid_dc=policyguid_dc,
2015                     invocationid=invocationid, machinepass=machinepass,
2016                     ntdsguid=ntdsguid, dns_backend=dns_backend,
2017                     dnspass=dnspass, serverrole=serverrole,
2018                     dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2019                     lp=lp, use_ntvfs=use_ntvfs,
2020                            skip_sysvolacl=skip_sysvolacl)
2021
2022         create_krb5_conf(paths.krb5conf,
2023                          dnsdomain=names.dnsdomain, hostname=names.hostname,
2024                          realm=names.realm)
2025         logger.info("A Kerberos configuration suitable for Samba 4 has been "
2026                     "generated at %s", paths.krb5conf)
2027
2028         if serverrole == "active directory domain controller":
2029             create_dns_update_list(lp, logger, paths)
2030
2031         backend_result = provision_backend.post_setup()
2032         provision_backend.shutdown()
2033
2034         create_phpldapadmin_config(paths.phpldapadminconfig,
2035                                    ldapi_url)
2036     except:
2037         secrets_ldb.transaction_cancel()
2038         raise
2039
2040     # Now commit the secrets.ldb to disk
2041     secrets_ldb.transaction_commit()
2042
2043     # the commit creates the dns.keytab, now chown it
2044     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2045     if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2046         try:
2047             os.chmod(dns_keytab_path, 0640)
2048             os.chown(dns_keytab_path, -1, paths.bind_gid)
2049         except OSError:
2050             if not os.environ.has_key('SAMBA_SELFTEST'):
2051                 logger.info("Failed to chown %s to bind gid %u",
2052                             dns_keytab_path, paths.bind_gid)
2053
2054     result = ProvisionResult()
2055     result.server_role = serverrole
2056     result.domaindn = domaindn
2057     result.paths = paths
2058     result.names = names
2059     result.lp = lp
2060     result.samdb = samdb
2061     result.idmap = idmap
2062     result.domainsid = str(domainsid)
2063
2064     if samdb_fill == FILL_FULL:
2065         result.adminpass_generated = adminpass_generated
2066         result.adminpass = adminpass
2067     else:
2068         result.adminpass_generated = False
2069         result.adminpass = None
2070
2071     result.backend_result = backend_result
2072
2073     if use_rfc2307:
2074         provision_fake_ypserver(logger=logger, samdb=samdb, domaindn=names.domaindn, netbiosname=names.netbiosname,
2075                                  nisdomain=(names.domain).lower(), maxuid=maxuid, maxgid=maxgid)
2076
2077     return result
2078
2079
2080 def provision_become_dc(smbconf=None, targetdir=None,
2081         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2082         serverdn=None, domain=None, hostname=None, domainsid=None,
2083         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2084         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2085         dns_backend=None, root=None, nobody=None, users=None, wheel=None,
2086         backup=None, serverrole=None, ldap_backend=None,
2087         ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2088
2089     logger = logging.getLogger("provision")
2090     samba.set_debug_level(debuglevel)
2091
2092     res = provision(logger, system_session(), None,
2093         smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2094         realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2095         configdn=configdn, serverdn=serverdn, domain=domain,
2096         hostname=hostname, hostip=None, domainsid=domainsid,
2097         machinepass=machinepass, serverrole="active directory domain controller",
2098         sitename=sitename, dns_backend=dns_backend, dnspass=dnspass, use_ntvfs=use_ntvfs)
2099     res.lp.set("debuglevel", str(debuglevel))
2100     return res
2101
2102
2103 def create_phpldapadmin_config(path, ldapi_uri):
2104     """Create a PHP LDAP admin configuration file.
2105
2106     :param path: Path to write the configuration to.
2107     """
2108     setup_file(setup_path("phpldapadmin-config.php"), path,
2109             {"S4_LDAPI_URI": ldapi_uri})
2110
2111
2112 def create_krb5_conf(path, dnsdomain, hostname, realm):
2113     """Write out a file containing zone statements suitable for inclusion in a
2114     named.conf file (including GSS-TSIG configuration).
2115
2116     :param path: Path of the new named.conf file.
2117     :param dnsdomain: DNS Domain name
2118     :param hostname: Local hostname
2119     :param realm: Realm name
2120     """
2121     setup_file(setup_path("krb5.conf"), path, {
2122             "DNSDOMAIN": dnsdomain,
2123             "HOSTNAME": hostname,
2124             "REALM": realm,
2125         })
2126
2127
2128 class ProvisioningError(Exception):
2129     """A generic provision error."""
2130
2131     def __init__(self, value):
2132         self.value = value
2133
2134     def __str__(self):
2135         return "ProvisioningError: " + self.value
2136
2137
2138 class InvalidNetbiosName(Exception):
2139     """A specified name was not a valid NetBIOS name."""
2140
2141     def __init__(self, name):
2142         super(InvalidNetbiosName, self).__init__(
2143             "The name '%r' is not a valid NetBIOS name" % name)
2144
2145
2146 class MissingShareError(ProvisioningError):
2147
2148     def __init__(self, name, smbconf):
2149         super(MissingShareError, self).__init__(
2150             "Existing smb.conf does not have a [%s] share, but you are "
2151             "configuring a DC. Please remove %s or add the share manually." %
2152             (name, smbconf))