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