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