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