s4 provision: Ask for the dns forwarder IP address during interactive provision
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision / __init__.py
blob706a02396a710498a2d9e6f27a7979d150ee8800
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
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 string
41 import tempfile
43 import ldb
45 from samba.auth import system_session, admin_session
46 import samba
47 from samba.samba3 import smbd, passdb
48 from samba.samba3 import param as s3param
49 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
50 from samba import (
51 Ldb,
52 MAX_NETBIOS_NAME_LEN,
53 check_all_substituted,
54 is_valid_netbios_char,
55 setup_file,
56 substitute_var,
57 valid_netbios_name,
58 version,
60 from samba.dcerpc import security, misc
61 from samba.dcerpc.misc import (
62 SEC_CHAN_BDC,
63 SEC_CHAN_WKSTA,
65 from samba.dsdb import (
66 DS_DOMAIN_FUNCTION_2003,
67 DS_DOMAIN_FUNCTION_2008_R2,
68 ENC_ALL_TYPES,
70 from samba.idmap import IDmapDB
71 from samba.ms_display_specifiers import read_ms_ldif
72 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
73 from samba.ndr import ndr_pack, ndr_unpack
74 from samba.provision.backend import (
75 ExistingBackend,
76 FDSBackend,
77 LDBBackend,
78 OpenLDAPBackend,
80 from samba.provision.descriptor import (
81 get_config_descriptor,
82 get_domain_descriptor
84 from samba.provision.common import (
85 setup_path,
86 setup_add_ldif,
87 setup_modify_ldif,
89 from samba.provision.sambadns import (
90 setup_ad_dns,
91 create_dns_update_list
94 import samba.param
95 import samba.registry
96 from samba.schema import Schema
97 from samba.samdb import SamDB
98 from samba.dbchecker import dbcheck
101 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
102 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
103 DEFAULTSITE = "Default-First-Site-Name"
104 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
107 class ProvisionPaths(object):
109 def __init__(self):
110 self.shareconf = None
111 self.hklm = None
112 self.hkcu = None
113 self.hkcr = None
114 self.hku = None
115 self.hkpd = None
116 self.hkpt = None
117 self.samdb = None
118 self.idmapdb = None
119 self.secrets = None
120 self.keytab = None
121 self.dns_keytab = None
122 self.dns = None
123 self.winsdb = None
124 self.private_dir = None
125 self.state_dir = None
126 self.phpldapadminconfig = None
129 class ProvisionNames(object):
131 def __init__(self):
132 self.rootdn = None
133 self.domaindn = None
134 self.configdn = None
135 self.schemadn = None
136 self.ldapmanagerdn = None
137 self.dnsdomain = None
138 self.realm = None
139 self.netbiosname = None
140 self.domain = None
141 self.hostname = None
142 self.sitename = None
143 self.smbconf = None
145 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
146 """Get key provision parameters (realm, domain, ...) from a given provision
148 :param samdb: An LDB object connected to the sam.ldb file
149 :param secretsdb: An LDB object connected to the secrets.ldb file
150 :param idmapdb: An LDB object connected to the idmap.ldb file
151 :param paths: A list of path to provision object
152 :param smbconf: Path to the smb.conf file
153 :param lp: A LoadParm object
154 :return: A list of key provision parameters
156 names = ProvisionNames()
157 names.adminpass = None
159 # NT domain, kerberos realm, root dn, domain dn, domain dns name
160 names.domain = string.upper(lp.get("workgroup"))
161 names.realm = lp.get("realm")
162 names.dnsdomain = names.realm.lower()
163 basedn = samba.dn_from_dns_name(names.dnsdomain)
164 names.realm = string.upper(names.realm)
165 # netbiosname
166 # Get the netbiosname first (could be obtained from smb.conf in theory)
167 res = secretsdb.search(expression="(flatname=%s)" %
168 names.domain,base="CN=Primary Domains",
169 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
170 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
172 names.smbconf = smbconf
174 # That's a bit simplistic but it's ok as long as we have only 3
175 # partitions
176 current = samdb.search(expression="(objectClass=*)",
177 base="", scope=ldb.SCOPE_BASE,
178 attrs=["defaultNamingContext", "schemaNamingContext",
179 "configurationNamingContext","rootDomainNamingContext"])
181 names.configdn = current[0]["configurationNamingContext"]
182 configdn = str(names.configdn)
183 names.schemadn = current[0]["schemaNamingContext"]
184 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
185 current[0]["defaultNamingContext"][0]))):
186 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
187 "is not the same ..." % (paths.samdb,
188 str(current[0]["defaultNamingContext"][0]),
189 paths.smbconf, basedn)))
191 names.domaindn=current[0]["defaultNamingContext"]
192 names.rootdn=current[0]["rootDomainNamingContext"]
193 # default site name
194 res3 = samdb.search(expression="(objectClass=site)",
195 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
196 names.sitename = str(res3[0]["cn"])
198 # dns hostname and server dn
199 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
200 base="OU=Domain Controllers,%s" % basedn,
201 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
202 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
204 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
205 attrs=[], base=configdn)
206 names.serverdn = server_res[0].dn
208 # invocation id/objectguid
209 res5 = samdb.search(expression="(objectClass=*)",
210 base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
211 attrs=["invocationID", "objectGUID"])
212 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
213 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
215 # domain guid/sid
216 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
217 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
218 "objectSid","msDS-Behavior-Version" ])
219 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
220 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
221 if res6[0].get("msDS-Behavior-Version") is None or \
222 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
223 names.domainlevel = DS_DOMAIN_FUNCTION_2000
224 else:
225 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
227 # policy guid
228 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
229 base="CN=Policies,CN=System," + basedn,
230 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
231 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
232 # dc policy guid
233 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
234 " Policy)",
235 base="CN=Policies,CN=System," + basedn,
236 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
237 if len(res8) == 1:
238 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
239 else:
240 names.policyid_dc = None
241 res9 = idmapdb.search(expression="(cn=%s)" %
242 (security.SID_BUILTIN_ADMINISTRATORS),
243 attrs=["xidNumber"])
244 if len(res9) == 1:
245 names.wheel_gid = res9[0]["xidNumber"]
246 else:
247 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
248 return names
251 def update_provision_usn(samdb, low, high, id, replace=False):
252 """Update the field provisionUSN in sam.ldb
254 This field is used to track range of USN modified by provision and
255 upgradeprovision.
256 This value is used afterward by next provision to figure out if
257 the field have been modified since last provision.
259 :param samdb: An LDB object connect to sam.ldb
260 :param low: The lowest USN modified by this upgrade
261 :param high: The highest USN modified by this upgrade
262 :param id: The invocation id of the samba's dc
263 :param replace: A boolean indicating if the range should replace any
264 existing one or appended (default)
267 tab = []
268 if not replace:
269 entry = samdb.search(base="@PROVISION",
270 scope=ldb.SCOPE_BASE,
271 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
272 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
273 if not re.search(';', e):
274 e = "%s;%s" % (e, id)
275 tab.append(str(e))
277 tab.append("%s-%s;%s" % (low, high, id))
278 delta = ldb.Message()
279 delta.dn = ldb.Dn(samdb, "@PROVISION")
280 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
281 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
282 entry = samdb.search(expression='provisionnerID=*',
283 base="@PROVISION", scope=ldb.SCOPE_BASE,
284 attrs=["provisionnerID"])
285 if len(entry) == 0 or len(entry[0]) == 0:
286 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
287 samdb.modify(delta)
290 def set_provision_usn(samdb, low, high, id):
291 """Set the field provisionUSN in sam.ldb
292 This field is used to track range of USN modified by provision and
293 upgradeprovision.
294 This value is used afterward by next provision to figure out if
295 the field have been modified since last provision.
297 :param samdb: An LDB object connect to sam.ldb
298 :param low: The lowest USN modified by this upgrade
299 :param high: The highest USN modified by this upgrade
300 :param id: The invocationId of the provision"""
302 tab = []
303 tab.append("%s-%s;%s" % (low, high, id))
305 delta = ldb.Message()
306 delta.dn = ldb.Dn(samdb, "@PROVISION")
307 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
308 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
309 samdb.add(delta)
312 def get_max_usn(samdb,basedn):
313 """ This function return the biggest USN present in the provision
315 :param samdb: A LDB object pointing to the sam.ldb
316 :param basedn: A string containing the base DN of the provision
317 (ie. DC=foo, DC=bar)
318 :return: The biggest USN in the provision"""
320 res = samdb.search(expression="objectClass=*",base=basedn,
321 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
322 controls=["search_options:1:2",
323 "server_sort:1:1:uSNChanged",
324 "paged_results:1:1"])
325 return res[0]["uSNChanged"]
328 def get_last_provision_usn(sam):
329 """Get USNs ranges modified by a provision or an upgradeprovision
331 :param sam: An LDB object pointing to the sam.ldb
332 :return: a dictionnary which keys are invocation id and values are an array
333 of integer representing the different ranges
335 try:
336 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
337 base="@PROVISION", scope=ldb.SCOPE_BASE,
338 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
339 except ldb.LdbError, (ecode, emsg):
340 if ecode == ldb.ERR_NO_SUCH_OBJECT:
341 return None
342 raise
343 if len(entry):
344 myids = []
345 range = {}
346 p = re.compile(r'-')
347 if entry[0].get("provisionnerID"):
348 for e in entry[0]["provisionnerID"]:
349 myids.append(str(e))
350 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
351 tab1 = str(r).split(';')
352 if len(tab1) == 2:
353 id = tab1[1]
354 else:
355 id = "default"
356 if (len(myids) > 0 and id not in myids):
357 continue
358 tab2 = p.split(tab1[0])
359 if range.get(id) == None:
360 range[id] = []
361 range[id].append(tab2[0])
362 range[id].append(tab2[1])
363 return range
364 else:
365 return None
368 class ProvisionResult(object):
369 """Result of a provision.
371 :ivar server_role: The server role
372 :ivar paths: ProvisionPaths instance
373 :ivar domaindn: The domain dn, as string
376 def __init__(self):
377 self.server_role = None
378 self.paths = None
379 self.domaindn = None
380 self.lp = None
381 self.samdb = None
382 self.idmap = None
383 self.names = None
384 self.domainsid = None
385 self.adminpass_generated = None
386 self.adminpass = None
387 self.backend_result = None
389 def report_logger(self, logger):
390 """Report this provision result to a logger."""
391 logger.info(
392 "Once the above files are installed, your Samba4 server will "
393 "be ready to use")
394 if self.adminpass_generated:
395 logger.info("Admin password: %s", self.adminpass)
396 logger.info("Server Role: %s", self.server_role)
397 logger.info("Hostname: %s", self.names.hostname)
398 logger.info("NetBIOS Domain: %s", self.names.domain)
399 logger.info("DNS Domain: %s", self.names.dnsdomain)
400 logger.info("DOMAIN SID: %s", self.domainsid)
402 if self.paths.phpldapadminconfig is not None:
403 logger.info(
404 "A phpLDAPadmin configuration file suitable for administering "
405 "the Samba 4 LDAP server has been created in %s.",
406 self.paths.phpldapadminconfig)
408 if self.backend_result:
409 self.backend_result.report_logger(logger)
412 def check_install(lp, session_info, credentials):
413 """Check whether the current install seems ok.
415 :param lp: Loadparm context
416 :param session_info: Session information
417 :param credentials: Credentials
419 if lp.get("realm") == "":
420 raise Exception("Realm empty")
421 samdb = Ldb(lp.samdb_url(), session_info=session_info,
422 credentials=credentials, lp=lp)
423 if len(samdb.search("(cn=Administrator)")) != 1:
424 raise ProvisioningError("No administrator account found")
427 def findnss(nssfn, names):
428 """Find a user or group from a list of possibilities.
430 :param nssfn: NSS Function to try (should raise KeyError if not found)
431 :param names: Names to check.
432 :return: Value return by first names list.
434 for name in names:
435 try:
436 return nssfn(name)
437 except KeyError:
438 pass
439 raise KeyError("Unable to find user/group in %r" % names)
442 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
443 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
446 def provision_paths_from_lp(lp, dnsdomain):
447 """Set the default paths for provisioning.
449 :param lp: Loadparm context.
450 :param dnsdomain: DNS Domain name
452 paths = ProvisionPaths()
453 paths.private_dir = lp.get("private dir")
454 paths.state_dir = lp.get("state directory")
456 # This is stored without path prefix for the "privateKeytab" attribute in
457 # "secrets_dns.ldif".
458 paths.dns_keytab = "dns.keytab"
459 paths.keytab = "secrets.keytab"
461 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
462 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
463 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
464 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
465 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
466 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
467 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
468 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
469 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
470 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
471 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
472 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
473 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
474 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
475 paths.phpldapadminconfig = os.path.join(paths.private_dir,
476 "phpldapadmin-config.php")
477 paths.hklm = "hklm.ldb"
478 paths.hkcr = "hkcr.ldb"
479 paths.hkcu = "hkcu.ldb"
480 paths.hku = "hku.ldb"
481 paths.hkpd = "hkpd.ldb"
482 paths.hkpt = "hkpt.ldb"
483 paths.sysvol = lp.get("path", "sysvol")
484 paths.netlogon = lp.get("path", "netlogon")
485 paths.smbconf = lp.configfile
486 return paths
489 def determine_netbios_name(hostname):
490 """Determine a netbios name from a hostname."""
491 # remove forbidden chars and force the length to be <16
492 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
493 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
496 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
497 serverrole=None, rootdn=None, domaindn=None, configdn=None,
498 schemadn=None, serverdn=None, sitename=None):
499 """Guess configuration settings to use."""
501 if hostname is None:
502 hostname = socket.gethostname().split(".")[0]
504 netbiosname = lp.get("netbios name")
505 if netbiosname is None:
506 netbiosname = determine_netbios_name(hostname)
507 netbiosname = netbiosname.upper()
508 if not valid_netbios_name(netbiosname):
509 raise InvalidNetbiosName(netbiosname)
511 if dnsdomain is None:
512 dnsdomain = lp.get("realm")
513 if dnsdomain is None or dnsdomain == "":
514 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
516 dnsdomain = dnsdomain.lower()
518 if serverrole is None:
519 serverrole = lp.get("server role")
520 if serverrole is None:
521 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
523 serverrole = serverrole.lower()
525 realm = dnsdomain.upper()
527 if lp.get("realm") == "":
528 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
530 if lp.get("realm").upper() != realm:
531 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))
533 if lp.get("server role").lower() != serverrole:
534 raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), lp.configfile, serverrole))
536 if serverrole == "active directory domain controller":
537 if domain is None:
538 # This will, for better or worse, default to 'WORKGROUP'
539 domain = lp.get("workgroup")
540 domain = domain.upper()
542 if lp.get("workgroup").upper() != domain:
543 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))
545 if domaindn is None:
546 domaindn = samba.dn_from_dns_name(dnsdomain)
548 if domain == netbiosname:
549 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
550 else:
551 domain = netbiosname
552 if domaindn is None:
553 domaindn = "DC=" + netbiosname
555 if not valid_netbios_name(domain):
556 raise InvalidNetbiosName(domain)
558 if hostname.upper() == realm:
559 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
560 if netbiosname.upper() == realm:
561 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
562 if domain == realm:
563 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
565 if rootdn is None:
566 rootdn = domaindn
568 if configdn is None:
569 configdn = "CN=Configuration," + rootdn
570 if schemadn is None:
571 schemadn = "CN=Schema," + configdn
573 if sitename is None:
574 sitename = DEFAULTSITE
576 names = ProvisionNames()
577 names.rootdn = rootdn
578 names.domaindn = domaindn
579 names.configdn = configdn
580 names.schemadn = schemadn
581 names.ldapmanagerdn = "CN=Manager," + rootdn
582 names.dnsdomain = dnsdomain
583 names.domain = domain
584 names.realm = realm
585 names.netbiosname = netbiosname
586 names.hostname = hostname
587 names.sitename = sitename
588 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
589 netbiosname, sitename, configdn)
591 return names
594 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
595 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
596 global_param=None):
597 """Create a new smb.conf file based on a couple of basic settings.
599 assert smbconf is not None
601 if hostname is None:
602 hostname = socket.gethostname().split(".")[0]
604 netbiosname = determine_netbios_name(hostname)
606 if serverrole is None:
607 serverrole = "standalone server"
609 assert domain is not None
610 domain = domain.upper()
612 assert realm is not None
613 realm = realm.upper()
615 global_settings = {
616 "netbios name": netbiosname,
617 "workgroup": domain,
618 "realm": realm,
619 "server role": serverrole,
622 if lp is None:
623 lp = samba.param.LoadParm()
624 #Load non-existent file
625 if os.path.exists(smbconf):
626 lp.load(smbconf)
628 if global_param is not None:
629 for ent in global_param:
630 if global_param[ent] is not None:
631 global_settings[ent] = " ".join(global_param[ent])
633 if targetdir is not None:
634 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
635 global_settings["lock dir"] = os.path.abspath(targetdir)
636 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
637 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
639 lp.set("lock dir", os.path.abspath(targetdir))
640 lp.set("state directory", global_settings["state directory"])
641 lp.set("cache directory", global_settings["cache directory"])
643 if eadb:
644 if use_ntvfs and not lp.get("posix:eadb"):
645 if targetdir is not None:
646 privdir = os.path.join(targetdir, "private")
647 else:
648 privdir = lp.get("private dir")
649 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
650 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
651 if targetdir is not None:
652 statedir = os.path.join(targetdir, "state")
653 else:
654 statedir = lp.get("state directory")
655 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
657 shares = {}
658 if serverrole == "active directory domain controller":
659 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
660 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
661 "scripts")
662 else:
663 global_settings["passdb backend"] = "samba_dsdb"
665 f = open(smbconf, 'w')
666 try:
667 f.write("[globals]\n")
668 for key, val in global_settings.iteritems():
669 f.write("\t%s = %s\n" % (key, val))
670 f.write("\n")
672 for name, path in shares.iteritems():
673 f.write("[%s]\n" % name)
674 f.write("\tpath = %s\n" % path)
675 f.write("\tread only = no\n")
676 f.write("\n")
677 finally:
678 f.close()
679 # reload the smb.conf
680 lp.load(smbconf)
682 # and dump it without any values that are the default
683 # this ensures that any smb.conf parameters that were set
684 # on the provision/join command line are set in the resulting smb.conf
685 f = open(smbconf, mode='w')
686 try:
687 lp.dump(f, False)
688 finally:
689 f.close()
692 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
693 users_gid, wheel_gid):
694 """setup reasonable name mappings for sam names to unix names.
696 :param samdb: SamDB object.
697 :param idmap: IDmap db object.
698 :param sid: The domain sid.
699 :param domaindn: The domain DN.
700 :param root_uid: uid of the UNIX root user.
701 :param nobody_uid: uid of the UNIX nobody user.
702 :param users_gid: gid of the UNIX users group.
703 :param wheel_gid: gid of the UNIX wheel group.
705 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
706 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
708 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
709 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
712 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
713 provision_backend, names, schema, serverrole,
714 erase=False):
715 """Setup the partitions for the SAM database.
717 Alternatively, provision() may call this, and then populate the database.
719 :note: This will wipe the Sam Database!
721 :note: This function always removes the local SAM LDB file. The erase
722 parameter controls whether to erase the existing data, which
723 may not be stored locally but in LDAP.
726 assert session_info is not None
728 # We use options=["modules:"] to stop the modules loading - we
729 # just want to wipe and re-initialise the database, not start it up
731 try:
732 os.unlink(samdb_path)
733 except OSError:
734 pass
736 samdb = Ldb(url=samdb_path, session_info=session_info,
737 lp=lp, options=["modules:"])
739 ldap_backend_line = "# No LDAP backend"
740 if provision_backend.type != "ldb":
741 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
743 samdb.transaction_start()
744 try:
745 logger.info("Setting up sam.ldb partitions and settings")
746 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
747 "LDAP_BACKEND_LINE": ldap_backend_line
751 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
752 "BACKEND_TYPE": provision_backend.type,
753 "SERVER_ROLE": serverrole
756 logger.info("Setting up sam.ldb rootDSE")
757 setup_samdb_rootdse(samdb, names)
758 except:
759 samdb.transaction_cancel()
760 raise
761 else:
762 samdb.transaction_commit()
765 def secretsdb_self_join(secretsdb, domain,
766 netbiosname, machinepass, domainsid=None,
767 realm=None, dnsdomain=None,
768 keytab_path=None,
769 key_version_number=1,
770 secure_channel_type=SEC_CHAN_WKSTA):
771 """Add domain join-specific bits to a secrets database.
773 :param secretsdb: Ldb Handle to the secrets database
774 :param machinepass: Machine password
776 attrs = ["whenChanged",
777 "secret",
778 "priorSecret",
779 "priorChanged",
780 "krb5Keytab",
781 "privateKeytab"]
783 if realm is not None:
784 if dnsdomain is None:
785 dnsdomain = realm.lower()
786 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
787 else:
788 dnsname = None
789 shortname = netbiosname.lower()
791 # We don't need to set msg["flatname"] here, because rdn_name will handle
792 # it, and it causes problems for modifies anyway
793 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
794 msg["secureChannelType"] = [str(secure_channel_type)]
795 msg["objectClass"] = ["top", "primaryDomain"]
796 if dnsname is not None:
797 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
798 msg["realm"] = [realm]
799 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
800 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
801 msg["privateKeytab"] = ["secrets.keytab"]
803 msg["secret"] = [machinepass]
804 msg["samAccountName"] = ["%s$" % netbiosname]
805 msg["secureChannelType"] = [str(secure_channel_type)]
806 if domainsid is not None:
807 msg["objectSid"] = [ndr_pack(domainsid)]
809 # This complex expression tries to ensure that we don't have more
810 # than one record for this SID, realm or netbios domain at a time,
811 # but we don't delete the old record that we are about to modify,
812 # because that would delete the keytab and previous password.
813 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
814 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
815 scope=ldb.SCOPE_ONELEVEL)
817 for del_msg in res:
818 secretsdb.delete(del_msg.dn)
820 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
822 if len(res) == 1:
823 msg["priorSecret"] = [res[0]["secret"][0]]
824 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
826 try:
827 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
828 except KeyError:
829 pass
831 try:
832 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
833 except KeyError:
834 pass
836 for el in msg:
837 if el != 'dn':
838 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
839 secretsdb.modify(msg)
840 secretsdb.rename(res[0].dn, msg.dn)
841 else:
842 spn = [ 'HOST/%s' % shortname ]
843 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
844 # we are a domain controller then we add servicePrincipalName
845 # entries for the keytab code to update.
846 spn.extend([ 'HOST/%s' % dnsname ])
847 msg["servicePrincipalName"] = spn
849 secretsdb.add(msg)
852 def setup_secretsdb(paths, session_info, backend_credentials, lp):
853 """Setup the secrets database.
855 :note: This function does not handle exceptions and transaction on purpose,
856 it's up to the caller to do this job.
858 :param path: Path to the secrets database.
859 :param session_info: Session info.
860 :param credentials: Credentials
861 :param lp: Loadparm context
862 :return: LDB handle for the created secrets database
864 if os.path.exists(paths.secrets):
865 os.unlink(paths.secrets)
867 keytab_path = os.path.join(paths.private_dir, paths.keytab)
868 if os.path.exists(keytab_path):
869 os.unlink(keytab_path)
871 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
872 if os.path.exists(dns_keytab_path):
873 os.unlink(dns_keytab_path)
875 path = paths.secrets
877 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
878 secrets_ldb.erase()
879 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
880 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
881 secrets_ldb.transaction_start()
882 try:
883 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
885 if (backend_credentials is not None and
886 backend_credentials.authentication_requested()):
887 if backend_credentials.get_bind_dn() is not None:
888 setup_add_ldif(secrets_ldb,
889 setup_path("secrets_simple_ldap.ldif"), {
890 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
891 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
893 else:
894 setup_add_ldif(secrets_ldb,
895 setup_path("secrets_sasl_ldap.ldif"), {
896 "LDAPADMINUSER": backend_credentials.get_username(),
897 "LDAPADMINREALM": backend_credentials.get_realm(),
898 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
900 except:
901 secrets_ldb.transaction_cancel()
902 raise
903 return secrets_ldb
906 def setup_privileges(path, session_info, lp):
907 """Setup the privileges database.
909 :param path: Path to the privileges database.
910 :param session_info: Session info.
911 :param credentials: Credentials
912 :param lp: Loadparm context
913 :return: LDB handle for the created secrets database
915 if os.path.exists(path):
916 os.unlink(path)
917 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
918 privilege_ldb.erase()
919 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
922 def setup_registry(path, session_info, lp):
923 """Setup the registry.
925 :param path: Path to the registry database
926 :param session_info: Session information
927 :param credentials: Credentials
928 :param lp: Loadparm context
930 reg = samba.registry.Registry()
931 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
932 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
933 provision_reg = setup_path("provision.reg")
934 assert os.path.exists(provision_reg)
935 reg.diff_apply(provision_reg)
938 def setup_idmapdb(path, session_info, lp):
939 """Setup the idmap database.
941 :param path: path to the idmap database
942 :param session_info: Session information
943 :param credentials: Credentials
944 :param lp: Loadparm context
946 if os.path.exists(path):
947 os.unlink(path)
949 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
950 idmap_ldb.erase()
951 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
952 return idmap_ldb
955 def setup_samdb_rootdse(samdb, names):
956 """Setup the SamDB rootdse.
958 :param samdb: Sam Database handle
960 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
961 "SCHEMADN": names.schemadn,
962 "DOMAINDN": names.domaindn,
963 "ROOTDN" : names.rootdn,
964 "CONFIGDN": names.configdn,
965 "SERVERDN": names.serverdn,
969 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
970 dns_backend, dnspass, domainsid, next_rid, invocationid,
971 policyguid, policyguid_dc,
972 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
973 """Join a host to its own domain."""
974 assert isinstance(invocationid, str)
975 if ntdsguid is not None:
976 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
977 else:
978 ntdsguid_line = ""
980 if dc_rid is None:
981 dc_rid = next_rid
983 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
984 "CONFIGDN": names.configdn,
985 "SCHEMADN": names.schemadn,
986 "DOMAINDN": names.domaindn,
987 "SERVERDN": names.serverdn,
988 "INVOCATIONID": invocationid,
989 "NETBIOSNAME": names.netbiosname,
990 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
991 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
992 "DOMAINSID": str(domainsid),
993 "DCRID": str(dc_rid),
994 "SAMBA_VERSION_STRING": version,
995 "NTDSGUID": ntdsguid_line,
996 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
997 domainControllerFunctionality),
998 "RIDALLOCATIONSTART": str(next_rid + 100),
999 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1001 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1002 "POLICYGUID": policyguid,
1003 "POLICYGUID_DC": policyguid_dc,
1004 "DNSDOMAIN": names.dnsdomain,
1005 "DOMAINDN": names.domaindn})
1007 # If we are setting up a subdomain, then this has been replicated in, so we
1008 # don't need to add it
1009 if fill == FILL_FULL:
1010 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1011 "CONFIGDN": names.configdn,
1012 "SCHEMADN": names.schemadn,
1013 "DOMAINDN": names.domaindn,
1014 "SERVERDN": names.serverdn,
1015 "INVOCATIONID": invocationid,
1016 "NETBIOSNAME": names.netbiosname,
1017 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1018 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1019 "DOMAINSID": str(domainsid),
1020 "DCRID": str(dc_rid),
1021 "SAMBA_VERSION_STRING": version,
1022 "NTDSGUID": ntdsguid_line,
1023 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1024 domainControllerFunctionality)})
1026 # Setup fSMORoleOwner entries to point at the newly created DC entry
1027 setup_modify_ldif(samdb,
1028 setup_path("provision_self_join_modify_config.ldif"), {
1029 "CONFIGDN": names.configdn,
1030 "SCHEMADN": names.schemadn,
1031 "DEFAULTSITE": names.sitename,
1032 "NETBIOSNAME": names.netbiosname,
1033 "SERVERDN": names.serverdn,
1036 system_session_info = system_session()
1037 samdb.set_session_info(system_session_info)
1038 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1039 # modify a serverReference under cn=config when we are a subdomain, we must
1040 # be system due to ACLs
1041 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1042 "DOMAINDN": names.domaindn,
1043 "SERVERDN": names.serverdn,
1044 "NETBIOSNAME": names.netbiosname,
1047 samdb.set_session_info(admin_session_info)
1049 if dns_backend != "SAMBA_INTERNAL":
1050 # This is Samba4 specific and should be replaced by the correct
1051 # DNS AD-style setup
1052 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1053 "DNSDOMAIN": names.dnsdomain,
1054 "DOMAINDN": names.domaindn,
1055 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1056 "HOSTNAME" : names.hostname,
1057 "DNSNAME" : '%s.%s' % (
1058 names.netbiosname.lower(), names.dnsdomain.lower())
1062 def getpolicypath(sysvolpath, dnsdomain, guid):
1063 """Return the physical path of policy given its guid.
1065 :param sysvolpath: Path to the sysvol folder
1066 :param dnsdomain: DNS name of the AD domain
1067 :param guid: The GUID of the policy
1068 :return: A string with the complete path to the policy folder
1070 if guid[0] != "{":
1071 guid = "{%s}" % guid
1072 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1073 return policy_path
1076 def create_gpo_struct(policy_path):
1077 if not os.path.exists(policy_path):
1078 os.makedirs(policy_path, 0775)
1079 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1080 try:
1081 f.write("[General]\r\nVersion=0")
1082 finally:
1083 f.close()
1084 p = os.path.join(policy_path, "MACHINE")
1085 if not os.path.exists(p):
1086 os.makedirs(p, 0775)
1087 p = os.path.join(policy_path, "USER")
1088 if not os.path.exists(p):
1089 os.makedirs(p, 0775)
1092 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1093 """Create the default GPO for a domain
1095 :param sysvolpath: Physical path for the sysvol folder
1096 :param dnsdomain: DNS domain name of the AD domain
1097 :param policyguid: GUID of the default domain policy
1098 :param policyguid_dc: GUID of the default domain controler policy
1100 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1101 create_gpo_struct(policy_path)
1103 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1104 create_gpo_struct(policy_path)
1107 def setup_samdb(path, session_info, provision_backend, lp, names,
1108 logger, fill, serverrole, schema, am_rodc=False):
1109 """Setup a complete SAM Database.
1111 :note: This will wipe the main SAM database file!
1114 # Also wipes the database
1115 setup_samdb_partitions(path, logger=logger, lp=lp,
1116 provision_backend=provision_backend, session_info=session_info,
1117 names=names, serverrole=serverrole, schema=schema)
1119 # Load the database, but don's load the global schema and don't connect
1120 # quite yet
1121 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1122 credentials=provision_backend.credentials, lp=lp,
1123 global_schema=False, am_rodc=am_rodc)
1125 logger.info("Pre-loading the Samba 4 and AD schema")
1127 # Load the schema from the one we computed earlier
1128 samdb.set_schema(schema, write_indices_and_attributes=False)
1130 # Set the NTDS settings DN manually - in order to have it already around
1131 # before the provisioned tree exists and we connect
1132 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1134 # And now we can connect to the DB - the schema won't be loaded from the
1135 # DB
1136 samdb.connect(path)
1138 # But we have to give it one more kick to have it use the schema
1139 # during provision - it needs, now that it is connected, to write
1140 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1141 samdb.set_schema(schema, write_indices_and_attributes=True)
1143 return samdb
1146 def fill_samdb(samdb, lp, names,
1147 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1148 adminpass, krbtgtpass, machinepass, dns_backend, dnspass, invocationid, ntdsguid,
1149 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1150 next_rid=None, dc_rid=None):
1152 if next_rid is None:
1153 next_rid = 1000
1155 # Provision does not make much sense values larger than 1000000000
1156 # as the upper range of the rIDAvailablePool is 1073741823 and
1157 # we don't want to create a domain that cannot allocate rids.
1158 if next_rid < 1000 or next_rid > 1000000000:
1159 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1160 error += "the valid range is %u-%u. The default is %u." % (
1161 1000, 1000000000, 1000)
1162 raise ProvisioningError(error)
1164 # ATTENTION: Do NOT change these default values without discussion with the
1165 # team and/or release manager. They have a big impact on the whole program!
1166 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1168 if dom_for_fun_level is None:
1169 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1171 if dom_for_fun_level > domainControllerFunctionality:
1172 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!")
1174 domainFunctionality = dom_for_fun_level
1175 forestFunctionality = dom_for_fun_level
1177 # Set the NTDS settings DN manually - in order to have it already around
1178 # before the provisioned tree exists and we connect
1179 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1181 samdb.transaction_start()
1182 try:
1183 # Set the domain functionality levels onto the database.
1184 # Various module (the password_hash module in particular) need
1185 # to know what level of AD we are emulating.
1187 # These will be fixed into the database via the database
1188 # modifictions below, but we need them set from the start.
1189 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1190 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1191 samdb.set_opaque_integer("domainControllerFunctionality",
1192 domainControllerFunctionality)
1194 samdb.set_domain_sid(str(domainsid))
1195 samdb.set_invocation_id(invocationid)
1197 logger.info("Adding DomainDN: %s" % names.domaindn)
1199 # impersonate domain admin
1200 admin_session_info = admin_session(lp, str(domainsid))
1201 samdb.set_session_info(admin_session_info)
1202 if domainguid is not None:
1203 domainguid_line = "objectGUID: %s\n-" % domainguid
1204 else:
1205 domainguid_line = ""
1207 descr = b64encode(get_domain_descriptor(domainsid))
1208 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1209 "DOMAINDN": names.domaindn,
1210 "DOMAINSID": str(domainsid),
1211 "DESCRIPTOR": descr,
1212 "DOMAINGUID": domainguid_line
1215 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1216 "DOMAINDN": names.domaindn,
1217 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1218 "NEXTRID": str(next_rid),
1219 "DEFAULTSITE": names.sitename,
1220 "CONFIGDN": names.configdn,
1221 "POLICYGUID": policyguid,
1222 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1223 "SAMBA_VERSION_STRING": version
1226 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1227 if fill == FILL_FULL:
1228 logger.info("Adding configuration container")
1229 descr = b64encode(get_config_descriptor(domainsid))
1230 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1231 "CONFIGDN": names.configdn,
1232 "DESCRIPTOR": descr,
1235 # The LDIF here was created when the Schema object was constructed
1236 logger.info("Setting up sam.ldb schema")
1237 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1238 samdb.modify_ldif(schema.schema_dn_modify)
1239 samdb.write_prefixes_from_schema()
1240 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1241 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1242 {"SCHEMADN": names.schemadn})
1244 # Now register this container in the root of the forest
1245 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1246 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1247 "subRefs")
1249 except:
1250 samdb.transaction_cancel()
1251 raise
1252 else:
1253 samdb.transaction_commit()
1255 samdb.transaction_start()
1256 try:
1257 samdb.invocation_id = invocationid
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 logger.info("Setting up sam.ldb configuration data")
1262 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1263 "CONFIGDN": names.configdn,
1264 "NETBIOSNAME": names.netbiosname,
1265 "DEFAULTSITE": names.sitename,
1266 "DNSDOMAIN": names.dnsdomain,
1267 "DOMAIN": names.domain,
1268 "SCHEMADN": names.schemadn,
1269 "DOMAINDN": names.domaindn,
1270 "SERVERDN": names.serverdn,
1271 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1272 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1275 logger.info("Setting up display specifiers")
1276 display_specifiers_ldif = read_ms_ldif(
1277 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1278 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1279 {"CONFIGDN": names.configdn})
1280 check_all_substituted(display_specifiers_ldif)
1281 samdb.add_ldif(display_specifiers_ldif)
1283 logger.info("Adding users container")
1284 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1285 "DOMAINDN": names.domaindn})
1286 logger.info("Modifying users container")
1287 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1288 "DOMAINDN": names.domaindn})
1289 logger.info("Adding computers container")
1290 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1291 "DOMAINDN": names.domaindn})
1292 logger.info("Modifying computers container")
1293 setup_modify_ldif(samdb,
1294 setup_path("provision_computers_modify.ldif"), {
1295 "DOMAINDN": names.domaindn})
1296 logger.info("Setting up sam.ldb data")
1297 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1298 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1299 "DOMAINDN": names.domaindn,
1300 "NETBIOSNAME": names.netbiosname,
1301 "DEFAULTSITE": names.sitename,
1302 "CONFIGDN": names.configdn,
1303 "SERVERDN": names.serverdn,
1304 "RIDAVAILABLESTART": str(next_rid + 600),
1305 "POLICYGUID_DC": policyguid_dc
1308 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1309 if fill == FILL_FULL:
1310 setup_modify_ldif(samdb,
1311 setup_path("provision_configuration_references.ldif"), {
1312 "CONFIGDN": names.configdn,
1313 "SCHEMADN": names.schemadn})
1315 logger.info("Setting up well known security principals")
1316 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1317 "CONFIGDN": names.configdn,
1320 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1321 setup_modify_ldif(samdb,
1322 setup_path("provision_basedn_references.ldif"),
1323 {"DOMAINDN": names.domaindn})
1325 logger.info("Setting up sam.ldb users and groups")
1326 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1327 "DOMAINDN": names.domaindn,
1328 "DOMAINSID": str(domainsid),
1329 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1330 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1333 logger.info("Setting up self join")
1334 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1335 invocationid=invocationid,
1336 dns_backend=dns_backend,
1337 dnspass=dnspass,
1338 machinepass=machinepass,
1339 domainsid=domainsid,
1340 next_rid=next_rid,
1341 dc_rid=dc_rid,
1342 policyguid=policyguid,
1343 policyguid_dc=policyguid_dc,
1344 domainControllerFunctionality=domainControllerFunctionality,
1345 ntdsguid=ntdsguid)
1347 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1348 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1349 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1350 assert isinstance(names.ntdsguid, str)
1351 except:
1352 samdb.transaction_cancel()
1353 raise
1354 else:
1355 samdb.transaction_commit()
1356 return samdb
1359 FILL_FULL = "FULL"
1360 FILL_SUBDOMAIN = "SUBDOMAIN"
1361 FILL_NT4SYNC = "NT4SYNC"
1362 FILL_DRS = "DRS"
1363 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1364 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)"
1367 def set_dir_acl(path, acl, lp, domsid, use_ntvfs):
1368 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs)
1369 for root, dirs, files in os.walk(path, topdown=False):
1370 for name in files:
1371 setntacl(lp, os.path.join(root, name), acl, domsid, use_ntvfs=use_ntvfs)
1372 for name in dirs:
1373 setntacl(lp, os.path.join(root, name), acl, domsid, use_ntvfs=use_ntvfs)
1376 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs):
1377 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1378 folders beneath.
1380 :param sysvol: Physical path for the sysvol folder
1381 :param dnsdomain: The DNS name of the domain
1382 :param domainsid: The SID of the domain
1383 :param domaindn: The DN of the domain (ie. DC=...)
1384 :param samdb: An LDB object on the SAM db
1385 :param lp: an LP object
1388 # Set ACL for GPO root folder
1389 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1390 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid), use_ntvfs=use_ntvfs)
1392 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1393 attrs=["cn", "nTSecurityDescriptor"],
1394 expression="", scope=ldb.SCOPE_ONELEVEL)
1396 for policy in res:
1397 acl = ndr_unpack(security.descriptor,
1398 str(policy["nTSecurityDescriptor"])).as_sddl()
1399 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1400 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1401 str(domainsid), use_ntvfs)
1404 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain, domaindn,
1405 lp, use_ntvfs):
1406 """Set the ACL for the sysvol share and the subfolders
1408 :param samdb: An LDB object on the SAM db
1409 :param netlogon: Physical path for the netlogon folder
1410 :param sysvol: Physical path for the sysvol folder
1411 :param uid: The UID of the "Administrator" user
1412 :param gid: The GID of the "Domain adminstrators" group
1413 :param domainsid: The SID of the domain
1414 :param dnsdomain: The DNS name of the domain
1415 :param domaindn: The DN of the domain (ie. DC=...)
1418 if not use_ntvfs:
1419 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1420 s3conf = s3param.get_context()
1421 s3conf.load(lp.configfile)
1422 # ensure we are using the right samba_dsdb passdb backend, no matter what
1423 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1424 passdb.reload_static_pdb()
1426 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1427 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1429 # now ensure everything matches correctly, to avoid wierd issues
1430 if passdb.get_global_sam_sid() != domainsid:
1431 raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid))
1433 domain_info = s4_passdb.domain_info()
1434 if domain_info["dom_sid"] != domainsid:
1435 raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
1437 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1438 raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
1441 try:
1442 if use_ntvfs:
1443 os.chown(sysvol, -1, gid)
1444 except OSError:
1445 canchown = False
1446 else:
1447 canchown = True
1449 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1450 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs)
1451 for root, dirs, files in os.walk(sysvol, topdown=False):
1452 for name in files:
1453 if use_ntvfs and canchown:
1454 os.chown(os.path.join(root, name), -1, gid)
1455 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs)
1456 for name in dirs:
1457 if use_ntvfs and canchown:
1458 os.chown(os.path.join(root, name), -1, gid)
1459 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs)
1461 # Set acls on Policy folder and policies folders
1462 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs)
1464 def acl_type(direct_db_access):
1465 if direct_db_access:
1466 return "DB"
1467 else:
1468 return "VFS"
1470 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1471 fsacl = getntacl(lp, path, direct_db_access=direct_db_access)
1472 fsacl_sddl = fsacl.as_sddl(domainsid)
1473 if fsacl_sddl != acl:
1474 raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), path, fsacl_sddl, acl))
1476 for root, dirs, files in os.walk(path, topdown=False):
1477 for name in files:
1478 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1479 if fsacl is None:
1480 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1481 fsacl_sddl = fsacl.as_sddl(domainsid)
1482 if fsacl_sddl != acl:
1483 raise ProvisioningError('%s ACL on GPO file %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl))
1485 for name in files:
1486 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1487 if fsacl is None:
1488 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1489 fsacl_sddl = fsacl.as_sddl(domainsid)
1490 if fsacl_sddl != acl:
1491 raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl, acl))
1494 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, direct_db_access):
1495 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1496 folders beneath.
1498 :param sysvol: Physical path for the sysvol folder
1499 :param dnsdomain: The DNS name of the domain
1500 :param domainsid: The SID of the domain
1501 :param domaindn: The DN of the domain (ie. DC=...)
1502 :param samdb: An LDB object on the SAM db
1503 :param lp: an LP object
1506 # Set ACL for GPO root folder
1507 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1508 fsacl = getntacl(lp, root_policy_path, direct_db_access=direct_db_access)
1509 if fsacl is None:
1510 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1511 fsacl_sddl = fsacl.as_sddl(domainsid)
1512 if fsacl_sddl != POLICIES_ACL:
1513 raise ProvisioningError('%s ACL on policy root %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), policy_root, fsacl_sddl, acl))
1514 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1515 attrs=["cn", "nTSecurityDescriptor"],
1516 expression="", scope=ldb.SCOPE_ONELEVEL)
1518 for policy in res:
1519 acl = ndr_unpack(security.descriptor,
1520 str(policy["nTSecurityDescriptor"])).as_sddl()
1521 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1522 check_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1523 domainsid, direct_db_access)
1526 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1527 lp):
1528 """Set the ACL for the sysvol share and the subfolders
1530 :param samdb: An LDB object on the SAM db
1531 :param netlogon: Physical path for the netlogon folder
1532 :param sysvol: Physical path for the sysvol folder
1533 :param uid: The UID of the "Administrator" user
1534 :param gid: The GID of the "Domain adminstrators" group
1535 :param domainsid: The SID of the domain
1536 :param dnsdomain: The DNS name of the domain
1537 :param domaindn: The DN of the domain (ie. DC=...)
1540 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1541 s3conf = s3param.get_context()
1542 s3conf.load(lp.configfile)
1543 # ensure we are using the right samba_dsdb passdb backend, no matter what
1544 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1545 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1546 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1548 # now ensure everything matches correctly, to avoid wierd issues
1549 if passdb.get_global_sam_sid() != domainsid:
1550 raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid))
1552 domain_info = s4_passdb.domain_info()
1553 if domain_info["dom_sid"] != domainsid:
1554 raise ProvisioningError('SID as seen by pdb_samba_dsdb [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid))
1556 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1557 raise ProvisioningError('Realm as seen by pdb_samba_dsdb [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper()))
1559 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1560 for direct_db_access in [True, False]:
1561 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1562 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access)
1563 if fsacl is None:
1564 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1565 fsacl_sddl = fsacl.as_sddl(domainsid)
1566 if fsacl_sddl != SYSVOL_ACL:
1567 raise ProvisioningError('%s ACL on sysvol directory %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), dir_path, fsacl_sddl, SYSVOL_ACL))
1569 # Check acls on Policy folder and policies folders
1570 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, direct_db_access)
1573 def interface_ips_v4(lp):
1574 '''return only IPv4 IPs'''
1575 ips = samba.interface_ips(lp, False)
1576 ret = []
1577 for i in ips:
1578 if i.find(':') == -1:
1579 ret.append(i)
1580 return ret
1582 def interface_ips_v6(lp, linklocal=False):
1583 '''return only IPv6 IPs'''
1584 ips = samba.interface_ips(lp, False)
1585 ret = []
1586 for i in ips:
1587 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1588 ret.append(i)
1589 return ret
1592 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1593 domainsid, schema=None,
1594 targetdir=None, samdb_fill=FILL_FULL,
1595 hostip=None, hostip6=None,
1596 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1597 domainguid=None, policyguid=None, policyguid_dc=None,
1598 invocationid=None, machinepass=None, ntdsguid=None,
1599 dns_backend=None, dnspass=None,
1600 serverrole=None, dom_for_fun_level=None,
1601 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=True):
1602 # create/adapt the group policy GUIDs
1603 # Default GUID for default policy are described at
1604 # "How Core Group Policy Works"
1605 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1606 if policyguid is None:
1607 policyguid = DEFAULT_POLICY_GUID
1608 policyguid = policyguid.upper()
1609 if policyguid_dc is None:
1610 policyguid_dc = DEFAULT_DC_POLICY_GUID
1611 policyguid_dc = policyguid_dc.upper()
1613 if invocationid is None:
1614 invocationid = str(uuid.uuid4())
1616 if krbtgtpass is None:
1617 krbtgtpass = samba.generate_random_password(128, 255)
1618 if machinepass is None:
1619 machinepass = samba.generate_random_password(128, 255)
1620 if dnspass is None:
1621 dnspass = samba.generate_random_password(128, 255)
1623 samdb = fill_samdb(samdb, lp, names, logger=logger,
1624 domainsid=domainsid, schema=schema, domainguid=domainguid,
1625 policyguid=policyguid, policyguid_dc=policyguid_dc,
1626 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1627 invocationid=invocationid, machinepass=machinepass,
1628 dns_backend=dns_backend, dnspass=dnspass,
1629 ntdsguid=ntdsguid, serverrole=serverrole,
1630 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1631 next_rid=next_rid, dc_rid=dc_rid)
1633 if serverrole == "active directory domain controller":
1635 # Set up group policies (domain policy and domain controller
1636 # policy)
1637 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1638 policyguid_dc)
1639 if not skip_sysvolacl:
1640 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid, paths.wheel_gid,
1641 domainsid, names.dnsdomain, names.domaindn, lp, use_ntvfs)
1643 secretsdb_self_join(secrets_ldb, domain=names.domain,
1644 realm=names.realm, dnsdomain=names.dnsdomain,
1645 netbiosname=names.netbiosname, domainsid=domainsid,
1646 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1648 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1649 # In future, this might be determined from some configuration
1650 kerberos_enctypes = str(ENC_ALL_TYPES)
1652 try:
1653 msg = ldb.Message(ldb.Dn(samdb,
1654 samdb.searchone("distinguishedName",
1655 expression="samAccountName=%s$" % names.netbiosname,
1656 scope=ldb.SCOPE_SUBTREE)))
1657 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1658 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1659 name="msDS-SupportedEncryptionTypes")
1660 samdb.modify(msg)
1661 except ldb.LdbError, (enum, estr):
1662 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1663 # It might be that this attribute does not exist in this schema
1664 raise
1666 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1667 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1668 dnspass=dnspass, os_level=dom_for_fun_level,
1669 targetdir=targetdir, site=DEFAULTSITE)
1671 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1672 attribute="objectGUID")
1673 assert isinstance(domainguid, str)
1675 lastProvisionUSNs = get_last_provision_usn(samdb)
1676 maxUSN = get_max_usn(samdb, str(names.rootdn))
1677 if lastProvisionUSNs is not None:
1678 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1679 else:
1680 set_provision_usn(samdb, 0, maxUSN, invocationid)
1682 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1683 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1684 { 'NTDSGUID' : names.ntdsguid })
1686 # fix any dangling GUIDs from the provision
1687 logger.info("Fixing provision GUIDs")
1688 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1689 quiet=True)
1690 samdb.transaction_start()
1691 try:
1692 # a small number of GUIDs are missing because of ordering issues in the
1693 # provision code
1694 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1695 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1696 scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1697 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1698 scope=ldb.SCOPE_ONELEVEL,
1699 attrs=['ipsecOwnersReference',
1700 'ipsecFilterReference',
1701 'ipsecISAKMPReference',
1702 'ipsecNegotiationPolicyReference',
1703 'ipsecNFAReference'])
1704 except:
1705 samdb.transaction_cancel()
1706 raise
1707 else:
1708 samdb.transaction_commit()
1711 _ROLES_MAP = {
1712 "ROLE_STANDALONE": "standalone server",
1713 "ROLE_DOMAIN_MEMBER": "member server",
1714 "ROLE_DOMAIN_BDC": "active directory domain controller",
1715 "ROLE_DOMAIN_PDC": "active directory domain controller",
1716 "dc": "active directory domain controller",
1717 "member": "member server",
1718 "domain controller": "active directory domain controller",
1719 "active directory domain controller": "active directory domain controller",
1720 "member server": "member server",
1721 "standalone": "standalone server",
1722 "standalone server": "standalone server",
1726 def sanitize_server_role(role):
1727 """Sanitize a server role name.
1729 :param role: Server role
1730 :raise ValueError: If the role can not be interpreted
1731 :return: Sanitized server role (one of "member server",
1732 "active directory domain controller", "standalone server")
1734 try:
1735 return _ROLES_MAP[role]
1736 except KeyError:
1737 raise ValueError(role)
1739 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain, maxuid, maxgid):
1740 """Creates AD entries for the fake ypserver
1741 needed for being able to manipulate posix attrs via ADUC
1743 samdb.transaction_start()
1744 try:
1745 logger.info("Setting up fake yp server settings")
1746 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1747 "DOMAINDN": domaindn,
1748 "NETBIOSNAME": netbiosname,
1749 "NISDOMAIN": nisdomain,
1751 except Exception:
1752 samdb.transaction_cancel()
1753 raise
1754 else:
1755 samdb.transaction_commit()
1756 if maxuid != None:
1757 pass
1758 if maxgid != None:
1759 pass
1761 def provision(logger, session_info, credentials, smbconf=None,
1762 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1763 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1764 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1765 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1766 domainguid=None, policyguid=None, policyguid_dc=None,
1767 dns_backend=None, dns_forwarder=None, dnspass=None,
1768 invocationid=None, machinepass=None, ntdsguid=None,
1769 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1770 serverrole=None, dom_for_fun_level=None,
1771 backend_type=None, sitename=None,
1772 ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1773 useeadb=False, am_rodc=False,
1774 lp=None, use_ntvfs=False,
1775 use_rfc2307=False, maxuid=None, maxgid=None,
1776 skip_sysvolacl=True):
1777 """Provision samba4
1779 :note: caution, this wipes all existing data!
1782 try:
1783 serverrole = sanitize_server_role(serverrole)
1784 except ValueError:
1785 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1787 if ldapadminpass is None:
1788 # Make a new, random password between Samba and it's LDAP server
1789 ldapadminpass = samba.generate_random_password(128, 255)
1791 if backend_type is None:
1792 backend_type = "ldb"
1794 if domainsid is None:
1795 domainsid = security.random_sid()
1796 else:
1797 domainsid = security.dom_sid(domainsid)
1799 root_uid = findnss_uid([root or "root"])
1800 nobody_uid = findnss_uid([nobody or "nobody"])
1801 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1802 if wheel is None:
1803 wheel_gid = findnss_gid(["wheel", "adm"])
1804 else:
1805 wheel_gid = findnss_gid([wheel])
1806 try:
1807 bind_gid = findnss_gid(["bind", "named"])
1808 except KeyError:
1809 bind_gid = None
1811 if targetdir is not None:
1812 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1813 elif smbconf is None:
1814 smbconf = samba.param.default_path()
1815 if not os.path.exists(os.path.dirname(smbconf)):
1816 os.makedirs(os.path.dirname(smbconf))
1818 server_services = []
1819 global_param = {}
1820 if use_rfc2307:
1821 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1823 if dns_backend != "SAMBA_INTERNAL":
1824 server_services.append("-dns")
1825 else:
1826 if dns_forwarder is not None:
1827 global_param["dns forwarder"] = [dns_forwarder]
1829 if use_ntvfs:
1830 server_services.append("+smb")
1831 server_services.append("-s3fs")
1832 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1834 if len(server_services) > 0:
1835 global_param["server services"] = server_services
1837 # only install a new smb.conf if there isn't one there already
1838 if os.path.exists(smbconf):
1839 # if Samba Team members can't figure out the weird errors
1840 # loading an empty smb.conf gives, then we need to be smarter.
1841 # Pretend it just didn't exist --abartlet
1842 f = open(smbconf, 'r')
1843 try:
1844 data = f.read().lstrip()
1845 finally:
1846 f.close()
1847 if data is None or data == "":
1848 make_smbconf(smbconf, hostname, domain, realm,
1849 targetdir, serverrole=serverrole,
1850 eadb=useeadb, use_ntvfs=use_ntvfs,
1851 lp=lp, global_param=global_param)
1852 else:
1853 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1854 serverrole=serverrole,
1855 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1857 if lp is None:
1858 lp = samba.param.LoadParm()
1859 lp.load(smbconf)
1860 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1861 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1862 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1863 sitename=sitename, rootdn=rootdn)
1864 paths = provision_paths_from_lp(lp, names.dnsdomain)
1866 paths.bind_gid = bind_gid
1867 paths.root_uid = root_uid;
1868 paths.wheel_gid = wheel_gid
1870 if hostip is None:
1871 logger.info("Looking up IPv4 addresses")
1872 hostips = interface_ips_v4(lp)
1873 if len(hostips) > 0:
1874 hostip = hostips[0]
1875 if len(hostips) > 1:
1876 logger.warning("More than one IPv4 address found. Using %s",
1877 hostip)
1878 if hostip == "127.0.0.1":
1879 hostip = None
1880 if hostip is None:
1881 logger.warning("No IPv4 address will be assigned")
1883 if hostip6 is None:
1884 logger.info("Looking up IPv6 addresses")
1885 hostips = interface_ips_v6(lp, linklocal=False)
1886 if hostips:
1887 hostip6 = hostips[0]
1888 if len(hostips) > 1:
1889 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1890 if hostip6 is None:
1891 logger.warning("No IPv6 address will be assigned")
1893 names.hostip = hostip
1894 names.hostip6 = hostip6
1896 if serverrole is None:
1897 serverrole = lp.get("server role")
1899 if not os.path.exists(paths.private_dir):
1900 os.mkdir(paths.private_dir)
1901 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1902 os.mkdir(os.path.join(paths.private_dir, "tls"))
1903 if not os.path.exists(paths.state_dir):
1904 os.mkdir(paths.state_dir)
1906 if paths.sysvol and not os.path.exists(paths.sysvol):
1907 os.makedirs(paths.sysvol, 0775)
1909 if not use_ntvfs and serverrole == "active directory domain controller":
1910 s3conf = s3param.get_context()
1911 s3conf.load(lp.configfile)
1913 if paths.sysvol is None:
1914 raise MissingShareError("sysvol", paths.smbconf)
1916 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1917 try:
1918 try:
1919 smbd.set_simple_acl(file.name, 0755, wheel_gid)
1920 except Exception:
1921 if not smbd.have_posix_acls():
1922 # This clue is only strictly correct for RPM and
1923 # Debian-like Linux systems, but hopefully other users
1924 # will get enough clue from it.
1925 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1927 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1928 try:
1929 smbd.chown(file.name, root_uid, wheel_gid)
1930 except Exception:
1931 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root. ")
1932 finally:
1933 file.close()
1935 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1937 schema = Schema(domainsid, invocationid=invocationid,
1938 schemadn=names.schemadn)
1940 if backend_type == "ldb":
1941 provision_backend = LDBBackend(backend_type, paths=paths,
1942 lp=lp, credentials=credentials,
1943 names=names, logger=logger)
1944 elif backend_type == "existing":
1945 # If support for this is ever added back, then the URI will need to be specified again
1946 provision_backend = ExistingBackend(backend_type, paths=paths,
1947 lp=lp, credentials=credentials,
1948 names=names, logger=logger,
1949 ldap_backend_forced_uri=None)
1950 elif backend_type == "fedora-ds":
1951 provision_backend = FDSBackend(backend_type, paths=paths,
1952 lp=lp, credentials=credentials,
1953 names=names, logger=logger, domainsid=domainsid,
1954 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1955 slapd_path=slapd_path,
1956 root=root)
1957 elif backend_type == "openldap":
1958 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1959 lp=lp, credentials=credentials,
1960 names=names, logger=logger, domainsid=domainsid,
1961 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1962 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1963 else:
1964 raise ValueError("Unknown LDAP backend type selected")
1966 provision_backend.init()
1967 provision_backend.start()
1969 # only install a new shares config db if there is none
1970 if not os.path.exists(paths.shareconf):
1971 logger.info("Setting up share.ldb")
1972 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1973 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1975 logger.info("Setting up secrets.ldb")
1976 secrets_ldb = setup_secretsdb(paths,
1977 session_info=session_info,
1978 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1980 try:
1981 logger.info("Setting up the registry")
1982 setup_registry(paths.hklm, session_info, lp=lp)
1984 logger.info("Setting up the privileges database")
1985 setup_privileges(paths.privilege, session_info, lp=lp)
1987 logger.info("Setting up idmap db")
1988 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
1990 setup_name_mappings(idmap, sid=str(domainsid),
1991 root_uid=root_uid, nobody_uid=nobody_uid,
1992 users_gid=users_gid, wheel_gid=wheel_gid)
1994 logger.info("Setting up SAM db")
1995 samdb = setup_samdb(paths.samdb, session_info,
1996 provision_backend, lp, names, logger=logger,
1997 serverrole=serverrole,
1998 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2000 if serverrole == "active directory domain controller":
2001 if paths.netlogon is None:
2002 raise MissingShareError("netlogon", paths.smbconf)
2004 if paths.sysvol is None:
2005 raise MissingShareError("sysvol", paths.smbconf)
2007 if not os.path.isdir(paths.netlogon):
2008 os.makedirs(paths.netlogon, 0755)
2010 if adminpass is None:
2011 adminpass = samba.generate_random_password(12, 32)
2012 adminpass_generated = True
2013 else:
2014 adminpass_generated = False
2016 if samdb_fill == FILL_FULL:
2017 provision_fill(samdb, secrets_ldb, logger, names, paths,
2018 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2019 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2020 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2021 krbtgtpass=krbtgtpass, domainguid=domainguid,
2022 policyguid=policyguid, policyguid_dc=policyguid_dc,
2023 invocationid=invocationid, machinepass=machinepass,
2024 ntdsguid=ntdsguid, dns_backend=dns_backend,
2025 dnspass=dnspass, serverrole=serverrole,
2026 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2027 lp=lp, use_ntvfs=use_ntvfs,
2028 skip_sysvolacl=skip_sysvolacl)
2030 create_krb5_conf(paths.krb5conf,
2031 dnsdomain=names.dnsdomain, hostname=names.hostname,
2032 realm=names.realm)
2033 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2034 "generated at %s", paths.krb5conf)
2036 if serverrole == "active directory domain controller":
2037 create_dns_update_list(lp, logger, paths)
2039 backend_result = provision_backend.post_setup()
2040 provision_backend.shutdown()
2042 create_phpldapadmin_config(paths.phpldapadminconfig,
2043 ldapi_url)
2044 except:
2045 secrets_ldb.transaction_cancel()
2046 raise
2048 # Now commit the secrets.ldb to disk
2049 secrets_ldb.transaction_commit()
2051 # the commit creates the dns.keytab, now chown it
2052 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2053 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2054 try:
2055 os.chmod(dns_keytab_path, 0640)
2056 os.chown(dns_keytab_path, -1, paths.bind_gid)
2057 except OSError:
2058 if not os.environ.has_key('SAMBA_SELFTEST'):
2059 logger.info("Failed to chown %s to bind gid %u",
2060 dns_keytab_path, paths.bind_gid)
2062 result = ProvisionResult()
2063 result.server_role = serverrole
2064 result.domaindn = domaindn
2065 result.paths = paths
2066 result.names = names
2067 result.lp = lp
2068 result.samdb = samdb
2069 result.idmap = idmap
2070 result.domainsid = str(domainsid)
2072 if samdb_fill == FILL_FULL:
2073 result.adminpass_generated = adminpass_generated
2074 result.adminpass = adminpass
2075 else:
2076 result.adminpass_generated = False
2077 result.adminpass = None
2079 result.backend_result = backend_result
2081 if use_rfc2307:
2082 provision_fake_ypserver(logger=logger, samdb=samdb, domaindn=names.domaindn, netbiosname=names.netbiosname,
2083 nisdomain=(names.domain).lower(), maxuid=maxuid, maxgid=maxgid)
2085 return result
2088 def provision_become_dc(smbconf=None, targetdir=None,
2089 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2090 serverdn=None, domain=None, hostname=None, domainsid=None,
2091 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2092 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2093 dns_backend=None, root=None, nobody=None, users=None, wheel=None,
2094 backup=None, serverrole=None, ldap_backend=None,
2095 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2097 logger = logging.getLogger("provision")
2098 samba.set_debug_level(debuglevel)
2100 res = provision(logger, system_session(), None,
2101 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2102 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2103 configdn=configdn, serverdn=serverdn, domain=domain,
2104 hostname=hostname, hostip=None, domainsid=domainsid,
2105 machinepass=machinepass, serverrole="active directory domain controller",
2106 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass, use_ntvfs=use_ntvfs)
2107 res.lp.set("debuglevel", str(debuglevel))
2108 return res
2111 def create_phpldapadmin_config(path, ldapi_uri):
2112 """Create a PHP LDAP admin configuration file.
2114 :param path: Path to write the configuration to.
2116 setup_file(setup_path("phpldapadmin-config.php"), path,
2117 {"S4_LDAPI_URI": ldapi_uri})
2120 def create_krb5_conf(path, dnsdomain, hostname, realm):
2121 """Write out a file containing zone statements suitable for inclusion in a
2122 named.conf file (including GSS-TSIG configuration).
2124 :param path: Path of the new named.conf file.
2125 :param dnsdomain: DNS Domain name
2126 :param hostname: Local hostname
2127 :param realm: Realm name
2129 setup_file(setup_path("krb5.conf"), path, {
2130 "DNSDOMAIN": dnsdomain,
2131 "HOSTNAME": hostname,
2132 "REALM": realm,
2136 class ProvisioningError(Exception):
2137 """A generic provision error."""
2139 def __init__(self, value):
2140 self.value = value
2142 def __str__(self):
2143 return "ProvisioningError: " + self.value
2146 class InvalidNetbiosName(Exception):
2147 """A specified name was not a valid NetBIOS name."""
2149 def __init__(self, name):
2150 super(InvalidNetbiosName, self).__init__(
2151 "The name '%r' is not a valid NetBIOS name" % name)
2154 class MissingShareError(ProvisioningError):
2156 def __init__(self, name, smbconf):
2157 super(MissingShareError, self).__init__(
2158 "Existing smb.conf does not have a [%s] share, but you are "
2159 "configuring a DC. Please remove %s or add the share manually." %
2160 (name, smbconf))