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