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