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