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