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