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