s4:provision: add "+dns" to server services if the dns backend is SAMBA_INTERNAL
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision / __init__.py
blob295e272dd497b8e26685a0f36bbbd4ad54f0fb97
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
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.
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.
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/>.
26 """Functions for setting up a Samba configuration."""
28 __docformat__ = "restructuredText"
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 shutil
41 import string
43 import ldb
45 from samba.auth import system_session, admin_session
46 import samba
47 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
48 from samba import (
49 Ldb,
50 check_all_substituted,
51 setup_file,
52 substitute_var,
53 valid_netbios_name,
54 version,
56 from samba.dcerpc import security, misc
57 from samba.dcerpc.misc import (
58 SEC_CHAN_BDC,
59 SEC_CHAN_WKSTA,
61 from samba.dsdb import (
62 DS_DOMAIN_FUNCTION_2003,
63 DS_DOMAIN_FUNCTION_2008_R2,
64 ENC_ALL_TYPES,
66 from samba.idmap import IDmapDB
67 from samba.ms_display_specifiers import read_ms_ldif
68 from samba.ntacls import setntacl, dsacl2fsacl
69 from samba.ndr import ndr_pack, ndr_unpack
70 from samba.provision.backend import (
71 ExistingBackend,
72 FDSBackend,
73 LDBBackend,
74 OpenLDAPBackend,
76 from samba.provision.descriptor import (
77 get_config_descriptor,
78 get_domain_descriptor
80 from samba.provision.common import (
81 setup_path,
82 setup_add_ldif,
83 setup_modify_ldif,
84 setup_ldb
86 from samba.provision.sambadns import (
87 setup_ad_dns,
88 create_dns_update_list
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
98 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
99 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
100 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
101 DEFAULTSITE = "Default-First-Site-Name"
102 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
105 class ProvisionPaths(object):
107 def __init__(self):
108 self.shareconf = None
109 self.hklm = None
110 self.hkcu = None
111 self.hkcr = None
112 self.hku = None
113 self.hkpd = None
114 self.hkpt = None
115 self.samdb = None
116 self.idmapdb = None
117 self.secrets = None
118 self.keytab = None
119 self.dns_keytab = None
120 self.dns = None
121 self.winsdb = None
122 self.private_dir = None
125 class ProvisionNames(object):
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
141 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
142 """Get key provision parameters (realm, domain, ...) from a given provision
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
152 names = ProvisionNames()
153 names.adminpass = None
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("$","")
168 names.smbconf = smbconf
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"])
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)))
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"])
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,"")
200 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
201 attrs=[], base=configdn)
202 names.serverdn = server_res[0].dn
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]))
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])
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
246 def update_provision_usn(samdb, low, high, id, replace=False):
247 """Update the field provisionUSN in sam.ldb
249 This field is used to track range of USN modified by provision and
250 upgradeprovision.
251 This value is used afterward by next provision to figure out if
252 the field have been modified since last provision.
254 :param samdb: An LDB object connect to sam.ldb
255 :param low: The lowest USN modified by this upgrade
256 :param high: The highest USN modified by this upgrade
257 :param id: The invocation id of the samba's dc
258 :param replace: A boolean indicating if the range should replace any
259 existing one or appended (default)
262 tab = []
263 if not replace:
264 entry = samdb.search(base="@PROVISION",
265 scope=ldb.SCOPE_BASE,
266 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
267 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
268 if not re.search(';', e):
269 e = "%s;%s" % (e, id)
270 tab.append(str(e))
272 tab.append("%s-%s;%s" % (low, high, id))
273 delta = ldb.Message()
274 delta.dn = ldb.Dn(samdb, "@PROVISION")
275 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
276 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
277 entry = samdb.search(expression='provisionnerID=*',
278 base="@PROVISION", scope=ldb.SCOPE_BASE,
279 attrs=["provisionnerID"])
280 if len(entry) == 0 or len(entry[0]) == 0:
281 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
282 samdb.modify(delta)
285 def set_provision_usn(samdb, low, high, id):
286 """Set the field provisionUSN in sam.ldb
287 This field is used to track range of USN modified by provision and
288 upgradeprovision.
289 This value is used afterward by next provision to figure out if
290 the field have been modified since last provision.
292 :param samdb: An LDB object connect to sam.ldb
293 :param low: The lowest USN modified by this upgrade
294 :param high: The highest USN modified by this upgrade
295 :param id: The invocationId of the provision"""
297 tab = []
298 tab.append("%s-%s;%s" % (low, high, id))
300 delta = ldb.Message()
301 delta.dn = ldb.Dn(samdb, "@PROVISION")
302 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
303 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
304 samdb.add(delta)
307 def get_max_usn(samdb,basedn):
308 """ This function return the biggest USN present in the provision
310 :param samdb: A LDB object pointing to the sam.ldb
311 :param basedn: A string containing the base DN of the provision
312 (ie. DC=foo, DC=bar)
313 :return: The biggest USN in the provision"""
315 res = samdb.search(expression="objectClass=*",base=basedn,
316 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
317 controls=["search_options:1:2",
318 "server_sort:1:1:uSNChanged",
319 "paged_results:1:1"])
320 return res[0]["uSNChanged"]
323 def get_last_provision_usn(sam):
324 """Get USNs ranges modified by a provision or an upgradeprovision
326 :param sam: An LDB object pointing to the sam.ldb
327 :return: a dictionnary which keys are invocation id and values are an array
328 of integer representing the different ranges
330 try:
331 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
332 base="@PROVISION", scope=ldb.SCOPE_BASE,
333 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
334 except ldb.LdbError, (ecode, emsg):
335 if ecode == ldb.ERR_NO_SUCH_OBJECT:
336 return None
337 raise
338 if len(entry):
339 myids = []
340 range = {}
341 p = re.compile(r'-')
342 if entry[0].get("provisionnerID"):
343 for e in entry[0]["provisionnerID"]:
344 myids.append(str(e))
345 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
346 tab1 = str(r).split(';')
347 if len(tab1) == 2:
348 id = tab1[1]
349 else:
350 id = "default"
351 if (len(myids) > 0 and id not in myids):
352 continue
353 tab2 = p.split(tab1[0])
354 if range.get(id) == None:
355 range[id] = []
356 range[id].append(tab2[0])
357 range[id].append(tab2[1])
358 return range
359 else:
360 return None
363 class ProvisionResult(object):
365 def __init__(self):
366 self.paths = None
367 self.domaindn = None
368 self.lp = None
369 self.samdb = None
370 self.idmap = None
371 self.names = None
374 def check_install(lp, session_info, credentials):
375 """Check whether the current install seems ok.
377 :param lp: Loadparm context
378 :param session_info: Session information
379 :param credentials: Credentials
381 if lp.get("realm") == "":
382 raise Exception("Realm empty")
383 samdb = Ldb(lp.samdb_url(), session_info=session_info,
384 credentials=credentials, lp=lp)
385 if len(samdb.search("(cn=Administrator)")) != 1:
386 raise ProvisioningError("No administrator account found")
389 def findnss(nssfn, names):
390 """Find a user or group from a list of possibilities.
392 :param nssfn: NSS Function to try (should raise KeyError if not found)
393 :param names: Names to check.
394 :return: Value return by first names list.
396 for name in names:
397 try:
398 return nssfn(name)
399 except KeyError:
400 pass
401 raise KeyError("Unable to find user/group in %r" % names)
404 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
405 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
408 def provision_paths_from_lp(lp, dnsdomain):
409 """Set the default paths for provisioning.
411 :param lp: Loadparm context.
412 :param dnsdomain: DNS Domain name
414 paths = ProvisionPaths()
415 paths.private_dir = lp.get("private dir")
417 # This is stored without path prefix for the "privateKeytab" attribute in
418 # "secrets_dns.ldif".
419 paths.dns_keytab = "dns.keytab"
420 paths.keytab = "secrets.keytab"
422 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
423 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
424 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
425 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
426 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
427 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
428 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
429 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
430 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
431 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
432 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
433 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
434 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
435 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
436 paths.phpldapadminconfig = os.path.join(paths.private_dir,
437 "phpldapadmin-config.php")
438 paths.hklm = "hklm.ldb"
439 paths.hkcr = "hkcr.ldb"
440 paths.hkcu = "hkcu.ldb"
441 paths.hku = "hku.ldb"
442 paths.hkpd = "hkpd.ldb"
443 paths.hkpt = "hkpt.ldb"
444 paths.sysvol = lp.get("path", "sysvol")
445 paths.netlogon = lp.get("path", "netlogon")
446 paths.smbconf = lp.configfile
447 return paths
450 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
451 serverrole=None, rootdn=None, domaindn=None, configdn=None,
452 schemadn=None, serverdn=None, sitename=None):
453 """Guess configuration settings to use."""
455 if hostname is None:
456 hostname = socket.gethostname().split(".")[0]
458 netbiosname = lp.get("netbios name")
459 if netbiosname is None:
460 netbiosname = hostname
461 # remove forbidden chars
462 newnbname = ""
463 for x in netbiosname:
464 if x.isalnum() or x in VALID_NETBIOS_CHARS:
465 newnbname = "%s%c" % (newnbname, x)
466 # force the length to be <16
467 netbiosname = newnbname[0:15]
468 assert netbiosname is not None
469 netbiosname = netbiosname.upper()
470 if not valid_netbios_name(netbiosname):
471 raise InvalidNetbiosName(netbiosname)
473 if dnsdomain is None:
474 dnsdomain = lp.get("realm")
475 if dnsdomain is None or dnsdomain == "":
476 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
478 dnsdomain = dnsdomain.lower()
480 if serverrole is None:
481 serverrole = lp.get("server role")
482 if serverrole is None:
483 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
485 serverrole = serverrole.lower()
487 realm = dnsdomain.upper()
489 if lp.get("realm") == "":
490 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
492 if lp.get("realm").upper() != realm:
493 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))
495 if lp.get("server role").lower() != serverrole:
496 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))
498 if serverrole == "domain controller":
499 if domain is None:
500 # This will, for better or worse, default to 'WORKGROUP'
501 domain = lp.get("workgroup")
502 domain = domain.upper()
504 if lp.get("workgroup").upper() != domain:
505 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))
507 if domaindn is None:
508 domaindn = samba.dn_from_dns_name(dnsdomain)
510 if domain == netbiosname:
511 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
512 else:
513 domain = netbiosname
514 if domaindn is None:
515 domaindn = "DC=" + netbiosname
517 if not valid_netbios_name(domain):
518 raise InvalidNetbiosName(domain)
520 if hostname.upper() == realm:
521 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
522 if netbiosname.upper() == realm:
523 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
524 if domain == realm:
525 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
527 if rootdn is None:
528 rootdn = domaindn
530 if configdn is None:
531 configdn = "CN=Configuration," + rootdn
532 if schemadn is None:
533 schemadn = "CN=Schema," + configdn
535 if sitename is None:
536 sitename=DEFAULTSITE
538 names = ProvisionNames()
539 names.rootdn = rootdn
540 names.domaindn = domaindn
541 names.configdn = configdn
542 names.schemadn = schemadn
543 names.ldapmanagerdn = "CN=Manager," + rootdn
544 names.dnsdomain = dnsdomain
545 names.domain = domain
546 names.realm = realm
547 names.netbiosname = netbiosname
548 names.hostname = hostname
549 names.sitename = sitename
550 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
551 netbiosname, sitename, configdn)
553 return names
556 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
557 targetdir, sid_generator="internal", eadb=False, lp=None,
558 server_services=None):
559 """Create a new smb.conf file based on a couple of basic settings.
561 assert smbconf is not None
562 if hostname is None:
563 hostname = socket.gethostname().split(".")[0]
564 netbiosname = hostname.upper()
565 # remove forbidden chars
566 newnbname = ""
567 for x in netbiosname:
568 if x.isalnum() or x in VALID_NETBIOS_CHARS:
569 newnbname = "%s%c" % (newnbname, x)
570 #force the length to be <16
571 netbiosname = newnbname[0:15]
572 else:
573 netbiosname = hostname.upper()
575 if serverrole is None:
576 serverrole = "standalone"
578 assert serverrole in ("domain controller", "member server", "standalone")
579 if serverrole == "domain controller":
580 smbconfsuffix = "dc"
581 elif serverrole == "member server":
582 smbconfsuffix = "member"
583 elif serverrole == "standalone":
584 smbconfsuffix = "standalone"
586 if sid_generator is None:
587 sid_generator = "internal"
589 assert domain is not None
590 domain = domain.upper()
592 assert realm is not None
593 realm = realm.upper()
595 if lp is None:
596 lp = samba.param.LoadParm()
597 #Load non-existant file
598 if os.path.exists(smbconf):
599 lp.load(smbconf)
600 if eadb and not lp.get("posix:eadb"):
601 if targetdir is not None:
602 privdir = os.path.join(targetdir, "private")
603 else:
604 privdir = lp.get("private dir")
605 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
607 if server_services is not None:
608 server_services_line = "server services = " + " ".join(server_services)
609 else:
610 server_services_line = ""
612 if targetdir is not None:
613 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
614 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
615 statedir_line = "state directory = " + os.path.abspath(targetdir)
616 cachedir_line = "cache directory = " + os.path.abspath(targetdir)
618 lp.set("lock dir", os.path.abspath(targetdir))
619 lp.set("state directory", os.path.abspath(targetdir))
620 lp.set("cache directory", os.path.abspath(targetdir))
621 else:
622 privatedir_line = ""
623 lockdir_line = ""
624 statedir_line = ""
625 cachedir_line = ""
627 sysvol = os.path.join(lp.get("state directory"), "sysvol")
628 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
630 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
631 smbconf, {
632 "NETBIOS_NAME": netbiosname,
633 "DOMAIN": domain,
634 "REALM": realm,
635 "SERVERROLE": serverrole,
636 "NETLOGONPATH": netlogon,
637 "SYSVOLPATH": sysvol,
638 "PRIVATEDIR_LINE": privatedir_line,
639 "LOCKDIR_LINE": lockdir_line,
640 "STATEDIR_LINE": statedir_line,
641 "CACHEDIR_LINE": cachedir_line,
642 "SERVER_SERVICES_LINE": server_services_line
645 # reload the smb.conf
646 lp.load(smbconf)
648 # and dump it without any values that are the default
649 # this ensures that any smb.conf parameters that were set
650 # on the provision/join command line are set in the resulting smb.conf
651 f = open(smbconf, mode='w')
652 lp.dump(f, False)
653 f.close()
657 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
658 users_gid, wheel_gid):
659 """setup reasonable name mappings for sam names to unix names.
661 :param samdb: SamDB object.
662 :param idmap: IDmap db object.
663 :param sid: The domain sid.
664 :param domaindn: The domain DN.
665 :param root_uid: uid of the UNIX root user.
666 :param nobody_uid: uid of the UNIX nobody user.
667 :param users_gid: gid of the UNIX users group.
668 :param wheel_gid: gid of the UNIX wheel group.
670 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
671 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
673 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
674 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
677 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
678 provision_backend, names, schema, serverrole,
679 erase=False):
680 """Setup the partitions for the SAM database.
682 Alternatively, provision() may call this, and then populate the database.
684 :note: This will wipe the Sam Database!
686 :note: This function always removes the local SAM LDB file. The erase
687 parameter controls whether to erase the existing data, which
688 may not be stored locally but in LDAP.
691 assert session_info is not None
693 # We use options=["modules:"] to stop the modules loading - we
694 # just want to wipe and re-initialise the database, not start it up
696 try:
697 os.unlink(samdb_path)
698 except OSError:
699 pass
701 samdb = Ldb(url=samdb_path, session_info=session_info,
702 lp=lp, options=["modules:"])
704 ldap_backend_line = "# No LDAP backend"
705 if provision_backend.type is not "ldb":
706 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
708 samdb.transaction_start()
709 try:
710 logger.info("Setting up sam.ldb partitions and settings")
711 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
712 "LDAP_BACKEND_LINE": ldap_backend_line
716 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
717 "BACKEND_TYPE": provision_backend.type,
718 "SERVER_ROLE": serverrole
721 logger.info("Setting up sam.ldb rootDSE")
722 setup_samdb_rootdse(samdb, names)
723 except Exception:
724 samdb.transaction_cancel()
725 raise
726 else:
727 samdb.transaction_commit()
730 def secretsdb_self_join(secretsdb, domain,
731 netbiosname, machinepass, domainsid=None,
732 realm=None, dnsdomain=None,
733 keytab_path=None,
734 key_version_number=1,
735 secure_channel_type=SEC_CHAN_WKSTA):
736 """Add domain join-specific bits to a secrets database.
738 :param secretsdb: Ldb Handle to the secrets database
739 :param machinepass: Machine password
741 attrs = ["whenChanged",
742 "secret",
743 "priorSecret",
744 "priorChanged",
745 "krb5Keytab",
746 "privateKeytab"]
748 if realm is not None:
749 if dnsdomain is None:
750 dnsdomain = realm.lower()
751 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
752 else:
753 dnsname = None
754 shortname = netbiosname.lower()
756 # We don't need to set msg["flatname"] here, because rdn_name will handle
757 # it, and it causes problems for modifies anyway
758 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
759 msg["secureChannelType"] = [str(secure_channel_type)]
760 msg["objectClass"] = ["top", "primaryDomain"]
761 if dnsname is not None:
762 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
763 msg["realm"] = [realm]
764 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
765 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
766 msg["privateKeytab"] = ["secrets.keytab"]
768 msg["secret"] = [machinepass]
769 msg["samAccountName"] = ["%s$" % netbiosname]
770 msg["secureChannelType"] = [str(secure_channel_type)]
771 if domainsid is not None:
772 msg["objectSid"] = [ndr_pack(domainsid)]
774 # This complex expression tries to ensure that we don't have more
775 # than one record for this SID, realm or netbios domain at a time,
776 # but we don't delete the old record that we are about to modify,
777 # because that would delete the keytab and previous password.
778 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
779 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
780 scope=ldb.SCOPE_ONELEVEL)
782 for del_msg in res:
783 secretsdb.delete(del_msg.dn)
785 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
787 if len(res) == 1:
788 msg["priorSecret"] = [res[0]["secret"][0]]
789 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
791 try:
792 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
793 except KeyError:
794 pass
796 try:
797 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
798 except KeyError:
799 pass
801 for el in msg:
802 if el != 'dn':
803 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
804 secretsdb.modify(msg)
805 secretsdb.rename(res[0].dn, msg.dn)
806 else:
807 spn = [ 'HOST/%s' % shortname ]
808 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
809 # we are a domain controller then we add servicePrincipalName
810 # entries for the keytab code to update.
811 spn.extend([ 'HOST/%s' % dnsname ])
812 msg["servicePrincipalName"] = spn
814 secretsdb.add(msg)
817 def setup_secretsdb(paths, session_info, backend_credentials, lp):
818 """Setup the secrets database.
820 :note: This function does not handle exceptions and transaction on purpose,
821 it's up to the caller to do this job.
823 :param path: Path to the secrets database.
824 :param session_info: Session info.
825 :param credentials: Credentials
826 :param lp: Loadparm context
827 :return: LDB handle for the created secrets database
829 if os.path.exists(paths.secrets):
830 os.unlink(paths.secrets)
832 keytab_path = os.path.join(paths.private_dir, paths.keytab)
833 if os.path.exists(keytab_path):
834 os.unlink(keytab_path)
836 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
837 if os.path.exists(dns_keytab_path):
838 os.unlink(dns_keytab_path)
840 path = paths.secrets
842 secrets_ldb = Ldb(path, session_info=session_info,
843 lp=lp)
844 secrets_ldb.erase()
845 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
846 secrets_ldb = Ldb(path, session_info=session_info,
847 lp=lp)
848 secrets_ldb.transaction_start()
849 try:
850 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
852 if (backend_credentials is not None and
853 backend_credentials.authentication_requested()):
854 if backend_credentials.get_bind_dn() is not None:
855 setup_add_ldif(secrets_ldb,
856 setup_path("secrets_simple_ldap.ldif"), {
857 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
858 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
860 else:
861 setup_add_ldif(secrets_ldb,
862 setup_path("secrets_sasl_ldap.ldif"), {
863 "LDAPADMINUSER": backend_credentials.get_username(),
864 "LDAPADMINREALM": backend_credentials.get_realm(),
865 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
867 except Exception:
868 secrets_ldb.transaction_cancel()
869 raise
870 return secrets_ldb
874 def setup_privileges(path, session_info, lp):
875 """Setup the privileges database.
877 :param path: Path to the privileges database.
878 :param session_info: Session info.
879 :param credentials: Credentials
880 :param lp: Loadparm context
881 :return: LDB handle for the created secrets database
883 if os.path.exists(path):
884 os.unlink(path)
885 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
886 privilege_ldb.erase()
887 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
890 def setup_registry(path, session_info, lp):
891 """Setup the registry.
893 :param path: Path to the registry database
894 :param session_info: Session information
895 :param credentials: Credentials
896 :param lp: Loadparm context
898 reg = samba.registry.Registry()
899 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
900 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
901 provision_reg = setup_path("provision.reg")
902 assert os.path.exists(provision_reg)
903 reg.diff_apply(provision_reg)
906 def setup_idmapdb(path, session_info, lp):
907 """Setup the idmap database.
909 :param path: path to the idmap database
910 :param session_info: Session information
911 :param credentials: Credentials
912 :param lp: Loadparm context
914 if os.path.exists(path):
915 os.unlink(path)
917 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
918 idmap_ldb.erase()
919 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
920 return idmap_ldb
923 def setup_samdb_rootdse(samdb, names):
924 """Setup the SamDB rootdse.
926 :param samdb: Sam Database handle
928 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
929 "SCHEMADN": names.schemadn,
930 "DOMAINDN": names.domaindn,
931 "ROOTDN" : names.rootdn,
932 "CONFIGDN": names.configdn,
933 "SERVERDN": names.serverdn,
937 def setup_self_join(samdb, admin_session_info, names, fill, machinepass, dnspass,
938 domainsid, next_rid, invocationid,
939 policyguid, policyguid_dc, domainControllerFunctionality,
940 ntdsguid, dc_rid=None):
941 """Join a host to its own domain."""
942 assert isinstance(invocationid, str)
943 if ntdsguid is not None:
944 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
945 else:
946 ntdsguid_line = ""
948 if dc_rid is None:
949 dc_rid = next_rid
951 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
952 "CONFIGDN": names.configdn,
953 "SCHEMADN": names.schemadn,
954 "DOMAINDN": names.domaindn,
955 "SERVERDN": names.serverdn,
956 "INVOCATIONID": invocationid,
957 "NETBIOSNAME": names.netbiosname,
958 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
959 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
960 "DOMAINSID": str(domainsid),
961 "DCRID": str(dc_rid),
962 "SAMBA_VERSION_STRING": version,
963 "NTDSGUID": ntdsguid_line,
964 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
965 domainControllerFunctionality),
966 "RIDALLOCATIONSTART": str(next_rid + 100),
967 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
969 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
970 "POLICYGUID": policyguid,
971 "POLICYGUID_DC": policyguid_dc,
972 "DNSDOMAIN": names.dnsdomain,
973 "DOMAINDN": names.domaindn})
975 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
976 if fill == FILL_FULL:
977 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
978 "CONFIGDN": names.configdn,
979 "SCHEMADN": names.schemadn,
980 "DOMAINDN": names.domaindn,
981 "SERVERDN": names.serverdn,
982 "INVOCATIONID": invocationid,
983 "NETBIOSNAME": names.netbiosname,
984 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
985 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
986 "DOMAINSID": str(domainsid),
987 "DCRID": str(dc_rid),
988 "SAMBA_VERSION_STRING": version,
989 "NTDSGUID": ntdsguid_line,
990 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
991 domainControllerFunctionality)})
993 # Setup fSMORoleOwner entries to point at the newly created DC entry
994 setup_modify_ldif(samdb, setup_path("provision_self_join_modify_config.ldif"), {
995 "CONFIGDN": names.configdn,
996 "SCHEMADN": names.schemadn,
997 "DEFAULTSITE": names.sitename,
998 "NETBIOSNAME": names.netbiosname,
999 "SERVERDN": names.serverdn,
1002 system_session_info = system_session()
1003 samdb.set_session_info(system_session_info)
1004 # Setup fSMORoleOwner entries to point at the newly created DC entry
1006 # to modify a serverReference under cn=config when we are a subdomain, we must
1007 # be system due to ACLs
1008 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1009 "DOMAINDN": names.domaindn,
1010 "SERVERDN": names.serverdn,
1011 "NETBIOSNAME": names.netbiosname,
1014 samdb.set_session_info(admin_session_info)
1016 # This is Samba4 specific and should be replaced by the correct
1017 # DNS AD-style setup
1018 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1019 "DNSDOMAIN": names.dnsdomain,
1020 "DOMAINDN": names.domaindn,
1021 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1022 "HOSTNAME" : names.hostname,
1023 "DNSNAME" : '%s.%s' % (
1024 names.netbiosname.lower(), names.dnsdomain.lower())
1028 def getpolicypath(sysvolpath, dnsdomain, guid):
1029 """Return the physical path of policy given its guid.
1031 :param sysvolpath: Path to the sysvol folder
1032 :param dnsdomain: DNS name of the AD domain
1033 :param guid: The GUID of the policy
1034 :return: A string with the complete path to the policy folder
1037 if guid[0] != "{":
1038 guid = "{%s}" % guid
1039 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1040 return policy_path
1043 def create_gpo_struct(policy_path):
1044 if not os.path.exists(policy_path):
1045 os.makedirs(policy_path, 0775)
1046 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1047 "[General]\r\nVersion=0")
1048 p = os.path.join(policy_path, "MACHINE")
1049 if not os.path.exists(p):
1050 os.makedirs(p, 0775)
1051 p = os.path.join(policy_path, "USER")
1052 if not os.path.exists(p):
1053 os.makedirs(p, 0775)
1056 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1057 """Create the default GPO for a domain
1059 :param sysvolpath: Physical path for the sysvol folder
1060 :param dnsdomain: DNS domain name of the AD domain
1061 :param policyguid: GUID of the default domain policy
1062 :param policyguid_dc: GUID of the default domain controler policy
1064 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1065 create_gpo_struct(policy_path)
1067 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1068 create_gpo_struct(policy_path)
1071 def setup_samdb(path, session_info, provision_backend, lp, names,
1072 logger, fill, serverrole, schema, am_rodc=False):
1073 """Setup a complete SAM Database.
1075 :note: This will wipe the main SAM database file!
1078 # Also wipes the database
1079 setup_samdb_partitions(path, logger=logger, lp=lp,
1080 provision_backend=provision_backend, session_info=session_info,
1081 names=names, serverrole=serverrole, schema=schema)
1083 # Load the database, but don's load the global schema and don't connect
1084 # quite yet
1085 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1086 credentials=provision_backend.credentials, lp=lp,
1087 global_schema=False, am_rodc=am_rodc)
1089 logger.info("Pre-loading the Samba 4 and AD schema")
1091 # Load the schema from the one we computed earlier
1092 samdb.set_schema(schema)
1094 # Set the NTDS settings DN manually - in order to have it already around
1095 # before the provisioned tree exists and we connect
1096 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1098 # And now we can connect to the DB - the schema won't be loaded from the
1099 # DB
1100 samdb.connect(path)
1102 return samdb
1104 def fill_samdb(samdb, lp, names,
1105 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1106 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1107 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1108 next_rid=None, dc_rid=None):
1110 if next_rid is None:
1111 next_rid = 1000
1113 # Provision does not make much sense values larger than 1000000000
1114 # as the upper range of the rIDAvailablePool is 1073741823 and
1115 # we don't want to create a domain that cannot allocate rids.
1116 if next_rid < 1000 or next_rid > 1000000000:
1117 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1118 error += "the valid range is %u-%u. The default is %u." % (
1119 1000, 1000000000, 1000)
1120 raise ProvisioningError(error)
1122 # ATTENTION: Do NOT change these default values without discussion with the
1123 # team and/or release manager. They have a big impact on the whole program!
1124 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1126 if dom_for_fun_level is None:
1127 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1129 if dom_for_fun_level > domainControllerFunctionality:
1130 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!")
1132 domainFunctionality = dom_for_fun_level
1133 forestFunctionality = dom_for_fun_level
1135 # Set the NTDS settings DN manually - in order to have it already around
1136 # before the provisioned tree exists and we connect
1137 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1139 samdb.transaction_start()
1140 try:
1141 # Set the domain functionality levels onto the database.
1142 # Various module (the password_hash module in particular) need
1143 # to know what level of AD we are emulating.
1145 # These will be fixed into the database via the database
1146 # modifictions below, but we need them set from the start.
1147 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1148 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1149 samdb.set_opaque_integer("domainControllerFunctionality",
1150 domainControllerFunctionality)
1152 samdb.set_domain_sid(str(domainsid))
1153 samdb.set_invocation_id(invocationid)
1155 logger.info("Adding DomainDN: %s" % names.domaindn)
1157 # impersonate domain admin
1158 admin_session_info = admin_session(lp, str(domainsid))
1159 samdb.set_session_info(admin_session_info)
1160 if domainguid is not None:
1161 domainguid_line = "objectGUID: %s\n-" % domainguid
1162 else:
1163 domainguid_line = ""
1165 descr = b64encode(get_domain_descriptor(domainsid))
1166 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1167 "DOMAINDN": names.domaindn,
1168 "DOMAINSID": str(domainsid),
1169 "DESCRIPTOR": descr,
1170 "DOMAINGUID": domainguid_line
1173 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1174 "DOMAINDN": names.domaindn,
1175 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1176 "NEXTRID": str(next_rid),
1177 "DEFAULTSITE": names.sitename,
1178 "CONFIGDN": names.configdn,
1179 "POLICYGUID": policyguid,
1180 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1181 "SAMBA_VERSION_STRING": version
1184 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1185 if fill == FILL_FULL:
1186 logger.info("Adding configuration container")
1187 descr = b64encode(get_config_descriptor(domainsid))
1188 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1189 "CONFIGDN": names.configdn,
1190 "DESCRIPTOR": descr,
1193 # The LDIF here was created when the Schema object was constructed
1194 logger.info("Setting up sam.ldb schema")
1195 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1196 samdb.modify_ldif(schema.schema_dn_modify)
1197 samdb.write_prefixes_from_schema()
1198 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1199 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1200 {"SCHEMADN": names.schemadn})
1202 # Now register this container in the root of the forest
1203 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1204 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1205 "subRefs")
1207 except Exception:
1208 samdb.transaction_cancel()
1209 raise
1210 else:
1211 samdb.transaction_commit()
1213 samdb.transaction_start()
1214 try:
1215 samdb.invocation_id = invocationid
1217 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1218 if fill == FILL_FULL:
1219 logger.info("Setting up sam.ldb configuration data")
1220 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1221 "CONFIGDN": names.configdn,
1222 "NETBIOSNAME": names.netbiosname,
1223 "DEFAULTSITE": names.sitename,
1224 "DNSDOMAIN": names.dnsdomain,
1225 "DOMAIN": names.domain,
1226 "SCHEMADN": names.schemadn,
1227 "DOMAINDN": names.domaindn,
1228 "SERVERDN": names.serverdn,
1229 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1230 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1233 logger.info("Setting up display specifiers")
1234 display_specifiers_ldif = read_ms_ldif(
1235 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1236 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1237 {"CONFIGDN": names.configdn})
1238 check_all_substituted(display_specifiers_ldif)
1239 samdb.add_ldif(display_specifiers_ldif)
1241 logger.info("Adding users container")
1242 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1243 "DOMAINDN": names.domaindn})
1244 logger.info("Modifying users container")
1245 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1246 "DOMAINDN": names.domaindn})
1247 logger.info("Adding computers container")
1248 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1249 "DOMAINDN": names.domaindn})
1250 logger.info("Modifying computers container")
1251 setup_modify_ldif(samdb,
1252 setup_path("provision_computers_modify.ldif"), {
1253 "DOMAINDN": names.domaindn})
1254 logger.info("Setting up sam.ldb data")
1255 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1256 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1257 "DOMAINDN": names.domaindn,
1258 "NETBIOSNAME": names.netbiosname,
1259 "DEFAULTSITE": names.sitename,
1260 "CONFIGDN": names.configdn,
1261 "SERVERDN": names.serverdn,
1262 "RIDAVAILABLESTART": str(next_rid + 600),
1263 "POLICYGUID_DC": policyguid_dc
1266 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1267 if fill == FILL_FULL:
1268 setup_modify_ldif(samdb,
1269 setup_path("provision_configuration_references.ldif"), {
1270 "CONFIGDN": names.configdn,
1271 "SCHEMADN": names.schemadn})
1273 logger.info("Setting up well known security principals")
1274 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1275 "CONFIGDN": names.configdn,
1278 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1279 setup_modify_ldif(samdb,
1280 setup_path("provision_basedn_references.ldif"),
1281 {"DOMAINDN": names.domaindn})
1283 logger.info("Setting up sam.ldb users and groups")
1284 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1285 "DOMAINDN": names.domaindn,
1286 "DOMAINSID": str(domainsid),
1287 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1288 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1291 logger.info("Setting up self join")
1292 setup_self_join(samdb, admin_session_info, names=names, fill=fill, invocationid=invocationid,
1293 dnspass=dnspass,
1294 machinepass=machinepass,
1295 domainsid=domainsid,
1296 next_rid=next_rid,
1297 dc_rid=dc_rid,
1298 policyguid=policyguid,
1299 policyguid_dc=policyguid_dc,
1300 domainControllerFunctionality=domainControllerFunctionality,
1301 ntdsguid=ntdsguid)
1303 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1304 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1305 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1306 assert isinstance(names.ntdsguid, str)
1307 except Exception:
1308 samdb.transaction_cancel()
1309 raise
1310 else:
1311 samdb.transaction_commit()
1312 return samdb
1315 FILL_FULL = "FULL"
1316 FILL_SUBDOMAIN = "SUBDOMAIN"
1317 FILL_NT4SYNC = "NT4SYNC"
1318 FILL_DRS = "DRS"
1319 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1320 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)"
1323 def set_dir_acl(path, acl, lp, domsid):
1324 setntacl(lp, path, acl, domsid)
1325 for root, dirs, files in os.walk(path, topdown=False):
1326 for name in files:
1327 setntacl(lp, os.path.join(root, name), acl, domsid)
1328 for name in dirs:
1329 setntacl(lp, os.path.join(root, name), acl, domsid)
1332 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1333 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1334 folders beneath.
1336 :param sysvol: Physical path for the sysvol folder
1337 :param dnsdomain: The DNS name of the domain
1338 :param domainsid: The SID of the domain
1339 :param domaindn: The DN of the domain (ie. DC=...)
1340 :param samdb: An LDB object on the SAM db
1341 :param lp: an LP object
1344 # Set ACL for GPO root folder
1345 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1346 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1348 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1349 attrs=["cn", "nTSecurityDescriptor"],
1350 expression="", scope=ldb.SCOPE_ONELEVEL)
1352 for policy in res:
1353 acl = ndr_unpack(security.descriptor,
1354 str(policy["nTSecurityDescriptor"])).as_sddl()
1355 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1356 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1357 str(domainsid))
1360 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1361 lp):
1362 """Set the ACL for the sysvol share and the subfolders
1364 :param samdb: An LDB object on the SAM db
1365 :param netlogon: Physical path for the netlogon folder
1366 :param sysvol: Physical path for the sysvol folder
1367 :param gid: The GID of the "Domain adminstrators" group
1368 :param domainsid: The SID of the domain
1369 :param dnsdomain: The DNS name of the domain
1370 :param domaindn: The DN of the domain (ie. DC=...)
1373 try:
1374 os.chown(sysvol, -1, gid)
1375 except OSError:
1376 canchown = False
1377 else:
1378 canchown = True
1380 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1381 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1382 for root, dirs, files in os.walk(sysvol, topdown=False):
1383 for name in files:
1384 if canchown:
1385 os.chown(os.path.join(root, name), -1, gid)
1386 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1387 for name in dirs:
1388 if canchown:
1389 os.chown(os.path.join(root, name), -1, gid)
1390 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1392 # Set acls on Policy folder and policies folders
1393 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1396 def interface_ips_v4(lp):
1397 '''return only IPv4 IPs'''
1398 ips = samba.interface_ips(lp, False)
1399 ret = []
1400 for i in ips:
1401 if i.find(':') == -1:
1402 ret.append(i)
1403 return ret
1405 def interface_ips_v6(lp, linklocal=False):
1406 '''return only IPv6 IPs'''
1407 ips = samba.interface_ips(lp, False)
1408 ret = []
1409 for i in ips:
1410 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1411 ret.append(i)
1412 return ret
1415 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1416 domainsid, schema=None,
1417 targetdir=None, samdb_fill=FILL_FULL,
1418 hostip=None, hostip6=None,
1419 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1420 domainguid=None, policyguid=None, policyguid_dc=None,
1421 invocationid=None, machinepass=None, ntdsguid=None,
1422 dns_backend=None, dnspass=None,
1423 serverrole=None, dom_for_fun_level=None,
1424 am_rodc=False, lp=None):
1425 # create/adapt the group policy GUIDs
1426 # Default GUID for default policy are described at
1427 # "How Core Group Policy Works"
1428 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1429 if policyguid is None:
1430 policyguid = DEFAULT_POLICY_GUID
1431 policyguid = policyguid.upper()
1432 if policyguid_dc is None:
1433 policyguid_dc = DEFAULT_DC_POLICY_GUID
1434 policyguid_dc = policyguid_dc.upper()
1436 if invocationid is None:
1437 invocationid = str(uuid.uuid4())
1439 if adminpass is None:
1440 adminpass = samba.generate_random_password(12, 32)
1441 if krbtgtpass is None:
1442 krbtgtpass = samba.generate_random_password(128, 255)
1443 if machinepass is None:
1444 machinepass = samba.generate_random_password(128, 255)
1445 if dnspass is None:
1446 dnspass = samba.generate_random_password(128, 255)
1448 samdb = fill_samdb(samdb, lp, names, logger=logger,
1449 domainsid=domainsid, schema=schema, domainguid=domainguid,
1450 policyguid=policyguid, policyguid_dc=policyguid_dc,
1451 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1452 invocationid=invocationid, machinepass=machinepass,
1453 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1454 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1455 next_rid=next_rid, dc_rid=dc_rid)
1457 if serverrole == "domain controller":
1458 # Set up group policies (domain policy and domain controller
1459 # policy)
1460 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1461 policyguid_dc)
1462 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
1463 domainsid, names.dnsdomain, names.domaindn, lp)
1465 secretsdb_self_join(secrets_ldb, domain=names.domain,
1466 realm=names.realm, dnsdomain=names.dnsdomain,
1467 netbiosname=names.netbiosname, domainsid=domainsid,
1468 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1470 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1471 # In future, this might be determined from some configuration
1472 kerberos_enctypes = str(ENC_ALL_TYPES)
1474 try:
1475 msg = ldb.Message(ldb.Dn(samdb,
1476 samdb.searchone("distinguishedName",
1477 expression="samAccountName=%s$" % names.netbiosname,
1478 scope=ldb.SCOPE_SUBTREE)))
1479 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1480 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1481 name="msDS-SupportedEncryptionTypes")
1482 samdb.modify(msg)
1483 except ldb.LdbError, (enum, estr):
1484 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1485 # It might be that this attribute does not exist in this schema
1486 raise
1488 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1489 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1490 dnspass=dnspass, os_level=dom_for_fun_level,
1491 targetdir=targetdir, site=DEFAULTSITE)
1493 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1494 attribute="objectGUID")
1495 assert isinstance(domainguid, str)
1497 lastProvisionUSNs = get_last_provision_usn(samdb)
1498 maxUSN = get_max_usn(samdb, str(names.rootdn))
1499 if lastProvisionUSNs is not None:
1500 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1501 else:
1502 set_provision_usn(samdb, 0, maxUSN, invocationid)
1504 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1505 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1506 { 'NTDSGUID' : names.ntdsguid })
1508 # fix any dangling GUIDs from the provision
1509 logger.info("Fixing provision GUIDs")
1510 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True, quiet=True)
1511 samdb.transaction_start()
1512 # a small number of GUIDs are missing because of ordering issues in the
1513 # provision code
1514 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1515 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1516 scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1517 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1518 scope=ldb.SCOPE_ONELEVEL,
1519 attrs=['ipsecOwnersReference',
1520 'ipsecFilterReference',
1521 'ipsecISAKMPReference',
1522 'ipsecNegotiationPolicyReference',
1523 'ipsecNFAReference'])
1524 samdb.transaction_commit()
1526 def provision(logger, session_info, credentials, smbconf=None,
1527 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1528 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1529 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1530 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1531 domainguid=None, policyguid=None, policyguid_dc=None,
1532 dns_backend=None, dnspass=None,
1533 invocationid=None, machinepass=None, ntdsguid=None,
1534 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1535 serverrole=None, dom_for_fun_level=None,
1536 backend_type=None, sitename=None,
1537 ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1538 useeadb=False, am_rodc=False,
1539 lp=None):
1540 """Provision samba4
1542 :note: caution, this wipes all existing data!
1545 roles = {}
1546 roles["ROLE_STANDALONE"] = "standalone"
1547 roles["ROLE_DOMAIN_MEMBER"] = "member server"
1548 roles["ROLE_DOMAIN_BDC"] = "domain controller"
1549 roles["ROLE_DOMAIN_PDC"] = "domain controller"
1550 roles["dc"] = "domain controller"
1551 roles["member"] = "member server"
1552 roles["domain controller"] = "domain controller"
1553 roles["member server"] = "member server"
1554 roles["standalone"] = "standalone"
1556 try:
1557 serverrole = roles[serverrole]
1558 except KeyError:
1559 raise ProvisioningError('server role (%s) should be one of "domain controller", "member server", "standalone"' % serverrole)
1561 if ldapadminpass is None:
1562 # Make a new, random password between Samba and it's LDAP server
1563 ldapadminpass=samba.generate_random_password(128, 255)
1565 if backend_type is None:
1566 backend_type = "ldb"
1568 if domainsid is None:
1569 domainsid = security.random_sid()
1570 else:
1571 domainsid = security.dom_sid(domainsid)
1573 sid_generator = "internal"
1574 if backend_type == "fedora-ds":
1575 sid_generator = "backend"
1577 root_uid = findnss_uid([root or "root"])
1578 nobody_uid = findnss_uid([nobody or "nobody"])
1579 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1580 if wheel is None:
1581 wheel_gid = findnss_gid(["wheel", "adm"])
1582 else:
1583 wheel_gid = findnss_gid([wheel])
1584 try:
1585 bind_gid = findnss_gid(["bind", "named"])
1586 except KeyError:
1587 bind_gid = None
1589 if targetdir is not None:
1590 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1591 elif smbconf is None:
1592 smbconf = samba.param.default_path()
1593 if not os.path.exists(os.path.dirname(smbconf)):
1594 os.makedirs(os.path.dirname(smbconf))
1596 server_services = None
1597 if dns_backend == "SAMBA_INTERNAL":
1598 server_services = [ "+dns" ]
1600 # only install a new smb.conf if there isn't one there already
1601 if os.path.exists(smbconf):
1602 # if Samba Team members can't figure out the weird errors
1603 # loading an empty smb.conf gives, then we need to be smarter.
1604 # Pretend it just didn't exist --abartlet
1605 data = open(smbconf, 'r').read()
1606 data = data.lstrip()
1607 if data is None or data == "":
1608 make_smbconf(smbconf, hostname, domain, realm,
1609 serverrole, targetdir, sid_generator, useeadb,
1610 lp=lp, server_services=server_services)
1611 else:
1612 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1613 targetdir, sid_generator, useeadb, lp=lp,
1614 server_services=server_services)
1616 if lp is None:
1617 lp = samba.param.LoadParm()
1618 lp.load(smbconf)
1619 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1620 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1621 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1622 sitename=sitename, rootdn=rootdn)
1623 paths = provision_paths_from_lp(lp, names.dnsdomain)
1625 paths.bind_gid = bind_gid
1626 paths.wheel_gid = wheel_gid
1628 if hostip is None:
1629 logger.info("Looking up IPv4 addresses")
1630 hostips = interface_ips_v4(lp)
1631 if len(hostips) > 0:
1632 hostip = hostips[0]
1633 if len(hostips) > 1:
1634 logger.warning("More than one IPv4 address found. Using %s",
1635 hostip)
1636 if hostip == "127.0.0.1":
1637 hostip = None
1638 if hostip is None:
1639 logger.warning("No IPv4 address will be assigned")
1641 if hostip6 is None:
1642 logger.info("Looking up IPv6 addresses")
1643 hostips = interface_ips_v6(lp, linklocal=False)
1644 if hostips:
1645 hostip6 = hostips[0]
1646 if len(hostips) > 1:
1647 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1648 if hostip6 is None:
1649 logger.warning("No IPv6 address will be assigned")
1651 names.hostip = hostip
1652 names.hostip6 = hostip6
1654 if serverrole is None:
1655 serverrole = lp.get("server role")
1657 if not os.path.exists(paths.private_dir):
1658 os.mkdir(paths.private_dir)
1659 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1660 os.mkdir(os.path.join(paths.private_dir, "tls"))
1662 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1664 schema = Schema(domainsid, invocationid=invocationid,
1665 schemadn=names.schemadn)
1667 if backend_type == "ldb":
1668 provision_backend = LDBBackend(backend_type, paths=paths,
1669 lp=lp, credentials=credentials,
1670 names=names, logger=logger)
1671 elif backend_type == "existing":
1672 # If support for this is ever added back, then the URI will need to be specified again
1673 provision_backend = ExistingBackend(backend_type, paths=paths,
1674 lp=lp, credentials=credentials,
1675 names=names, logger=logger,
1676 ldap_backend_forced_uri=None)
1677 elif backend_type == "fedora-ds":
1678 provision_backend = FDSBackend(backend_type, paths=paths,
1679 lp=lp, credentials=credentials,
1680 names=names, logger=logger, domainsid=domainsid,
1681 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1682 slapd_path=slapd_path,
1683 root=root)
1684 elif backend_type == "openldap":
1685 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1686 lp=lp, credentials=credentials,
1687 names=names, logger=logger, domainsid=domainsid,
1688 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1689 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1690 else:
1691 raise ValueError("Unknown LDAP backend type selected")
1693 provision_backend.init()
1694 provision_backend.start()
1696 # only install a new shares config db if there is none
1697 if not os.path.exists(paths.shareconf):
1698 logger.info("Setting up share.ldb")
1699 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1700 lp=lp)
1701 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1703 logger.info("Setting up secrets.ldb")
1704 secrets_ldb = setup_secretsdb(paths,
1705 session_info=session_info,
1706 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1708 try:
1709 logger.info("Setting up the registry")
1710 setup_registry(paths.hklm, session_info,
1711 lp=lp)
1713 logger.info("Setting up the privileges database")
1714 setup_privileges(paths.privilege, session_info, lp=lp)
1716 logger.info("Setting up idmap db")
1717 idmap = setup_idmapdb(paths.idmapdb,
1718 session_info=session_info, lp=lp)
1720 setup_name_mappings(idmap, sid=str(domainsid),
1721 root_uid=root_uid, nobody_uid=nobody_uid,
1722 users_gid=users_gid, wheel_gid=wheel_gid)
1724 logger.info("Setting up SAM db")
1725 samdb = setup_samdb(paths.samdb, session_info,
1726 provision_backend, lp, names, logger=logger,
1727 serverrole=serverrole,
1728 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1730 if serverrole == "domain controller":
1731 if paths.netlogon is None:
1732 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1733 logger.info("Please either remove %s or see the template at %s" %
1734 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1735 assert paths.netlogon is not None
1737 if paths.sysvol is None:
1738 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1739 " are configuring a DC.")
1740 logger.info("Please either remove %s or see the template at %s" %
1741 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1742 assert paths.sysvol is not None
1744 if not os.path.isdir(paths.netlogon):
1745 os.makedirs(paths.netlogon, 0755)
1747 if samdb_fill == FILL_FULL:
1748 provision_fill(samdb, secrets_ldb, logger,
1749 names, paths, schema=schema, targetdir=targetdir,
1750 samdb_fill=samdb_fill, hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1751 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1752 krbtgtpass=krbtgtpass, domainguid=domainguid,
1753 policyguid=policyguid, policyguid_dc=policyguid_dc,
1754 invocationid=invocationid, machinepass=machinepass,
1755 ntdsguid=ntdsguid, dns_backend=dns_backend, dnspass=dnspass,
1756 serverrole=serverrole, dom_for_fun_level=dom_for_fun_level,
1757 am_rodc=am_rodc, lp=lp)
1759 create_krb5_conf(paths.krb5conf,
1760 dnsdomain=names.dnsdomain, hostname=names.hostname,
1761 realm=names.realm)
1762 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1763 "generated at %s", paths.krb5conf)
1765 if serverrole == "domain controller":
1766 create_dns_update_list(lp, logger, paths)
1768 provision_backend.post_setup()
1769 provision_backend.shutdown()
1771 create_phpldapadmin_config(paths.phpldapadminconfig,
1772 ldapi_url)
1773 except Exception:
1774 secrets_ldb.transaction_cancel()
1775 raise
1777 # Now commit the secrets.ldb to disk
1778 secrets_ldb.transaction_commit()
1780 # the commit creates the dns.keytab, now chown it
1781 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1782 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1783 try:
1784 os.chmod(dns_keytab_path, 0640)
1785 os.chown(dns_keytab_path, -1, paths.bind_gid)
1786 except OSError:
1787 if not os.environ.has_key('SAMBA_SELFTEST'):
1788 logger.info("Failed to chown %s to bind gid %u",
1789 dns_keytab_path, paths.bind_gid)
1791 logger.info("A phpLDAPadmin configuration file suitable for administering the Samba 4 LDAP server has been created in %s .",
1792 paths.phpldapadminconfig)
1794 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1795 logger.info("Server Role: %s" % serverrole)
1796 logger.info("Hostname: %s" % names.hostname)
1797 logger.info("NetBIOS Domain: %s" % names.domain)
1798 logger.info("DNS Domain: %s" % names.dnsdomain)
1799 logger.info("DOMAIN SID: %s" % str(domainsid))
1800 if samdb_fill == FILL_FULL:
1801 logger.info("Admin password: %s" % adminpass)
1802 if provision_backend.type is not "ldb":
1803 if provision_backend.credentials.get_bind_dn() is not None:
1804 logger.info("LDAP Backend Admin DN: %s" %
1805 provision_backend.credentials.get_bind_dn())
1806 else:
1807 logger.info("LDAP Admin User: %s" %
1808 provision_backend.credentials.get_username())
1810 logger.info("LDAP Admin Password: %s" %
1811 provision_backend.credentials.get_password())
1813 if provision_backend.slapd_command_escaped is not None:
1814 # now display slapd_command_file.txt to show how slapd must be
1815 # started next time
1816 logger.info("Use later the following commandline to start slapd, then Samba:")
1817 logger.info(provision_backend.slapd_command_escaped)
1818 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1819 provision_backend.ldapdir)
1821 result = ProvisionResult()
1822 result.domaindn = domaindn
1823 result.paths = paths
1824 result.names = names
1825 result.lp = lp
1826 result.samdb = samdb
1827 result.idmap = idmap
1828 return result
1831 def provision_become_dc(smbconf=None, targetdir=None,
1832 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1833 serverdn=None, domain=None, hostname=None, domainsid=None,
1834 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1835 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1836 dns_backend=None, root=None, nobody=None, users=None, wheel=None, backup=None,
1837 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1838 sitename=None, debuglevel=1):
1840 logger = logging.getLogger("provision")
1841 samba.set_debug_level(debuglevel)
1843 res = provision(logger, system_session(), None,
1844 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1845 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1846 configdn=configdn, serverdn=serverdn, domain=domain,
1847 hostname=hostname, hostip=None, domainsid=domainsid,
1848 machinepass=machinepass, serverrole="domain controller",
1849 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
1850 res.lp.set("debuglevel", str(debuglevel))
1851 return res
1854 def create_phpldapadmin_config(path, ldapi_uri):
1855 """Create a PHP LDAP admin configuration file.
1857 :param path: Path to write the configuration to.
1859 setup_file(setup_path("phpldapadmin-config.php"), path,
1860 {"S4_LDAPI_URI": ldapi_uri})
1863 def create_krb5_conf(path, dnsdomain, hostname, realm):
1864 """Write out a file containing zone statements suitable for inclusion in a
1865 named.conf file (including GSS-TSIG configuration).
1867 :param path: Path of the new named.conf file.
1868 :param dnsdomain: DNS Domain name
1869 :param hostname: Local hostname
1870 :param realm: Realm name
1872 setup_file(setup_path("krb5.conf"), path, {
1873 "DNSDOMAIN": dnsdomain,
1874 "HOSTNAME": hostname,
1875 "REALM": realm,
1879 class ProvisioningError(Exception):
1880 """A generic provision error."""
1882 def __init__(self, value):
1883 self.value = value
1885 def __str__(self):
1886 return "ProvisioningError: " + self.value
1889 class InvalidNetbiosName(Exception):
1890 """A specified name was not a valid NetBIOS name."""
1891 def __init__(self, name):
1892 super(InvalidNetbiosName, self).__init__(
1893 "The name '%r' is not a valid NetBIOS name" % name)