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