s4:provision: set the correct nTSecurityDescriptor on CN=Computers,... (bug #9481)
[Samba/bb.git] / source4 / scripting / python / samba / provision / __init__.py
blob52dacdec32c42dfcb662d52ee602c505574733d7
1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba4 server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__ = "restructuredText"
29 from base64 import b64encode
30 import os
31 import re
32 import pwd
33 import grp
34 import logging
35 import time
36 import uuid
37 import socket
38 import urllib
39 import string
40 import tempfile
42 import ldb
44 from samba.auth import system_session, admin_session
45 import samba
46 from samba.samba3 import smbd, passdb
47 from samba.samba3 import param as s3param
48 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
49 from samba import (
50 Ldb,
51 MAX_NETBIOS_NAME_LEN,
52 check_all_substituted,
53 is_valid_netbios_char,
54 setup_file,
55 substitute_var,
56 valid_netbios_name,
57 version,
59 from samba.dcerpc import security, misc
60 from samba.dcerpc.misc import (
61 SEC_CHAN_BDC,
62 SEC_CHAN_WKSTA,
64 from samba.dsdb import (
65 DS_DOMAIN_FUNCTION_2003,
66 DS_DOMAIN_FUNCTION_2008_R2,
67 ENC_ALL_TYPES,
69 from samba.idmap import IDmapDB
70 from samba.ms_display_specifiers import read_ms_ldif
71 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
72 from samba.ndr import ndr_pack, ndr_unpack
73 from samba.provision.backend import (
74 ExistingBackend,
75 FDSBackend,
76 LDBBackend,
77 OpenLDAPBackend,
79 from samba.provision.descriptor import (
80 get_empty_descriptor,
81 get_config_descriptor,
82 get_config_partitions_descriptor,
83 get_config_sites_descriptor,
84 get_domain_descriptor,
85 get_domain_infrastructure_descriptor,
86 get_domain_builtin_descriptor,
87 get_domain_computers_descriptor,
89 from samba.provision.common import (
90 setup_path,
91 setup_add_ldif,
92 setup_modify_ldif,
94 from samba.provision.sambadns import (
95 setup_ad_dns,
96 create_dns_update_list
99 import samba.param
100 import samba.registry
101 from samba.schema import Schema
102 from samba.samdb import SamDB
103 from samba.dbchecker import dbcheck
106 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
107 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
108 DEFAULTSITE = "Default-First-Site-Name"
109 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
112 class ProvisionPaths(object):
114 def __init__(self):
115 self.shareconf = None
116 self.hklm = None
117 self.hkcu = None
118 self.hkcr = None
119 self.hku = None
120 self.hkpd = None
121 self.hkpt = None
122 self.samdb = None
123 self.idmapdb = None
124 self.secrets = None
125 self.keytab = None
126 self.dns_keytab = None
127 self.dns = None
128 self.winsdb = None
129 self.private_dir = None
130 self.state_dir = None
133 class ProvisionNames(object):
135 def __init__(self):
136 self.rootdn = None
137 self.domaindn = None
138 self.configdn = None
139 self.schemadn = None
140 self.ldapmanagerdn = None
141 self.dnsdomain = None
142 self.realm = None
143 self.netbiosname = None
144 self.domain = None
145 self.hostname = None
146 self.sitename = None
147 self.smbconf = None
150 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
151 lp):
152 """Get key provision parameters (realm, domain, ...) from a given provision
154 :param samdb: An LDB object connected to the sam.ldb file
155 :param secretsdb: An LDB object connected to the secrets.ldb file
156 :param idmapdb: An LDB object connected to the idmap.ldb file
157 :param paths: A list of path to provision object
158 :param smbconf: Path to the smb.conf file
159 :param lp: A LoadParm object
160 :return: A list of key provision parameters
162 names = ProvisionNames()
163 names.adminpass = None
165 # NT domain, kerberos realm, root dn, domain dn, domain dns name
166 names.domain = string.upper(lp.get("workgroup"))
167 names.realm = lp.get("realm")
168 names.dnsdomain = names.realm.lower()
169 basedn = samba.dn_from_dns_name(names.dnsdomain)
170 names.realm = string.upper(names.realm)
171 # netbiosname
172 # Get the netbiosname first (could be obtained from smb.conf in theory)
173 res = secretsdb.search(expression="(flatname=%s)" %
174 names.domain,base="CN=Primary Domains",
175 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
176 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
178 names.smbconf = smbconf
180 # That's a bit simplistic but it's ok as long as we have only 3
181 # partitions
182 current = samdb.search(expression="(objectClass=*)",
183 base="", scope=ldb.SCOPE_BASE,
184 attrs=["defaultNamingContext", "schemaNamingContext",
185 "configurationNamingContext","rootDomainNamingContext"])
187 names.configdn = current[0]["configurationNamingContext"]
188 configdn = str(names.configdn)
189 names.schemadn = current[0]["schemaNamingContext"]
190 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
191 current[0]["defaultNamingContext"][0]))):
192 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
193 "is not the same ..." % (paths.samdb,
194 str(current[0]["defaultNamingContext"][0]),
195 paths.smbconf, basedn)))
197 names.domaindn=current[0]["defaultNamingContext"]
198 names.rootdn=current[0]["rootDomainNamingContext"]
199 # default site name
200 res3 = samdb.search(expression="(objectClass=site)",
201 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
202 names.sitename = str(res3[0]["cn"])
204 # dns hostname and server dn
205 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
206 base="OU=Domain Controllers,%s" % basedn,
207 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
208 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
210 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
211 attrs=[], base=configdn)
212 names.serverdn = server_res[0].dn
214 # invocation id/objectguid
215 res5 = samdb.search(expression="(objectClass=*)",
216 base="CN=NTDS Settings,%s" % str(names.serverdn),
217 scope=ldb.SCOPE_BASE,
218 attrs=["invocationID", "objectGUID"])
219 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
220 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
222 # domain guid/sid
223 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
224 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
225 "objectSid","msDS-Behavior-Version" ])
226 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
227 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
228 if res6[0].get("msDS-Behavior-Version") is None or \
229 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
230 names.domainlevel = DS_DOMAIN_FUNCTION_2000
231 else:
232 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
234 # policy guid
235 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
236 base="CN=Policies,CN=System," + basedn,
237 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
238 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
239 # dc policy guid
240 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
241 " Policy)",
242 base="CN=Policies,CN=System," + basedn,
243 scope=ldb.SCOPE_ONELEVEL,
244 attrs=["cn","displayName"])
245 if len(res8) == 1:
246 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
247 else:
248 names.policyid_dc = None
250 res9 = idmapdb.search(expression="(cn=%s-%s)" %
251 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
252 attrs=["xidNumber", "type"])
253 if len(res9) != 1:
254 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
255 if res9[0]["type"][0] == "ID_TYPE_BOTH":
256 names.root_gid = res9[0]["xidNumber"][0]
257 else:
258 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
259 return names
262 def update_provision_usn(samdb, low, high, id, replace=False):
263 """Update the field provisionUSN in sam.ldb
265 This field is used to track range of USN modified by provision and
266 upgradeprovision.
267 This value is used afterward by next provision to figure out if
268 the field have been modified since last provision.
270 :param samdb: An LDB object connect to sam.ldb
271 :param low: The lowest USN modified by this upgrade
272 :param high: The highest USN modified by this upgrade
273 :param id: The invocation id of the samba's dc
274 :param replace: A boolean indicating if the range should replace any
275 existing one or appended (default)
278 tab = []
279 if not replace:
280 entry = samdb.search(base="@PROVISION",
281 scope=ldb.SCOPE_BASE,
282 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
283 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
284 if not re.search(';', e):
285 e = "%s;%s" % (e, id)
286 tab.append(str(e))
288 tab.append("%s-%s;%s" % (low, high, id))
289 delta = ldb.Message()
290 delta.dn = ldb.Dn(samdb, "@PROVISION")
291 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
292 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
293 entry = samdb.search(expression='provisionnerID=*',
294 base="@PROVISION", scope=ldb.SCOPE_BASE,
295 attrs=["provisionnerID"])
296 if len(entry) == 0 or len(entry[0]) == 0:
297 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
298 samdb.modify(delta)
301 def set_provision_usn(samdb, low, high, id):
302 """Set the field provisionUSN in sam.ldb
303 This field is used to track range of USN modified by provision and
304 upgradeprovision.
305 This value is used afterward by next provision to figure out if
306 the field have been modified since last provision.
308 :param samdb: An LDB object connect to sam.ldb
309 :param low: The lowest USN modified by this upgrade
310 :param high: The highest USN modified by this upgrade
311 :param id: The invocationId of the provision"""
313 tab = []
314 tab.append("%s-%s;%s" % (low, high, id))
316 delta = ldb.Message()
317 delta.dn = ldb.Dn(samdb, "@PROVISION")
318 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
319 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
320 samdb.add(delta)
323 def get_max_usn(samdb,basedn):
324 """ This function return the biggest USN present in the provision
326 :param samdb: A LDB object pointing to the sam.ldb
327 :param basedn: A string containing the base DN of the provision
328 (ie. DC=foo, DC=bar)
329 :return: The biggest USN in the provision"""
331 res = samdb.search(expression="objectClass=*",base=basedn,
332 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
333 controls=["search_options:1:2",
334 "server_sort:1:1:uSNChanged",
335 "paged_results:1:1"])
336 return res[0]["uSNChanged"]
339 def get_last_provision_usn(sam):
340 """Get USNs ranges modified by a provision or an upgradeprovision
342 :param sam: An LDB object pointing to the sam.ldb
343 :return: a dictionary which keys are invocation id and values are an array
344 of integer representing the different ranges
346 try:
347 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
348 base="@PROVISION", scope=ldb.SCOPE_BASE,
349 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
350 except ldb.LdbError, (ecode, emsg):
351 if ecode == ldb.ERR_NO_SUCH_OBJECT:
352 return None
353 raise
354 if len(entry) > 0:
355 myids = []
356 range = {}
357 p = re.compile(r'-')
358 if entry[0].get("provisionnerID"):
359 for e in entry[0]["provisionnerID"]:
360 myids.append(str(e))
361 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
362 tab1 = str(r).split(';')
363 if len(tab1) == 2:
364 id = tab1[1]
365 else:
366 id = "default"
367 if (len(myids) > 0 and id not in myids):
368 continue
369 tab2 = p.split(tab1[0])
370 if range.get(id) is None:
371 range[id] = []
372 range[id].append(tab2[0])
373 range[id].append(tab2[1])
374 return range
375 else:
376 return None
379 class ProvisionResult(object):
380 """Result of a provision.
382 :ivar server_role: The server role
383 :ivar paths: ProvisionPaths instance
384 :ivar domaindn: The domain dn, as string
387 def __init__(self):
388 self.server_role = None
389 self.paths = None
390 self.domaindn = None
391 self.lp = None
392 self.samdb = None
393 self.idmap = None
394 self.names = None
395 self.domainsid = None
396 self.adminpass_generated = None
397 self.adminpass = None
398 self.backend_result = None
400 def report_logger(self, logger):
401 """Report this provision result to a logger."""
402 logger.info(
403 "Once the above files are installed, your Samba4 server will "
404 "be ready to use")
405 if self.adminpass_generated:
406 logger.info("Admin password: %s", self.adminpass)
407 logger.info("Server Role: %s", self.server_role)
408 logger.info("Hostname: %s", self.names.hostname)
409 logger.info("NetBIOS Domain: %s", self.names.domain)
410 logger.info("DNS Domain: %s", self.names.dnsdomain)
411 logger.info("DOMAIN SID: %s", self.domainsid)
413 if self.backend_result:
414 self.backend_result.report_logger(logger)
417 def check_install(lp, session_info, credentials):
418 """Check whether the current install seems ok.
420 :param lp: Loadparm context
421 :param session_info: Session information
422 :param credentials: Credentials
424 if lp.get("realm") == "":
425 raise Exception("Realm empty")
426 samdb = Ldb(lp.samdb_url(), session_info=session_info,
427 credentials=credentials, lp=lp)
428 if len(samdb.search("(cn=Administrator)")) != 1:
429 raise ProvisioningError("No administrator account found")
432 def findnss(nssfn, names):
433 """Find a user or group from a list of possibilities.
435 :param nssfn: NSS Function to try (should raise KeyError if not found)
436 :param names: Names to check.
437 :return: Value return by first names list.
439 for name in names:
440 try:
441 return nssfn(name)
442 except KeyError:
443 pass
444 raise KeyError("Unable to find user/group in %r" % names)
447 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
448 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
451 def provision_paths_from_lp(lp, dnsdomain):
452 """Set the default paths for provisioning.
454 :param lp: Loadparm context.
455 :param dnsdomain: DNS Domain name
457 paths = ProvisionPaths()
458 paths.private_dir = lp.get("private dir")
459 paths.state_dir = lp.get("state directory")
461 # This is stored without path prefix for the "privateKeytab" attribute in
462 # "secrets_dns.ldif".
463 paths.dns_keytab = "dns.keytab"
464 paths.keytab = "secrets.keytab"
466 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
467 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
468 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
469 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
470 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
471 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
472 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
473 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
474 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
475 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
476 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
477 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
478 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
479 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
480 paths.hklm = "hklm.ldb"
481 paths.hkcr = "hkcr.ldb"
482 paths.hkcu = "hkcu.ldb"
483 paths.hku = "hku.ldb"
484 paths.hkpd = "hkpd.ldb"
485 paths.hkpt = "hkpt.ldb"
486 paths.sysvol = lp.get("path", "sysvol")
487 paths.netlogon = lp.get("path", "netlogon")
488 paths.smbconf = lp.configfile
489 return paths
492 def determine_netbios_name(hostname):
493 """Determine a netbios name from a hostname."""
494 # remove forbidden chars and force the length to be <16
495 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
496 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
499 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
500 serverrole=None, rootdn=None, domaindn=None, configdn=None,
501 schemadn=None, serverdn=None, sitename=None):
502 """Guess configuration settings to use."""
504 if hostname is None:
505 hostname = socket.gethostname().split(".")[0]
507 netbiosname = lp.get("netbios name")
508 if netbiosname is None:
509 netbiosname = determine_netbios_name(hostname)
510 netbiosname = netbiosname.upper()
511 if not valid_netbios_name(netbiosname):
512 raise InvalidNetbiosName(netbiosname)
514 if dnsdomain is None:
515 dnsdomain = lp.get("realm")
516 if dnsdomain is None or dnsdomain == "":
517 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
519 dnsdomain = dnsdomain.lower()
521 if serverrole is None:
522 serverrole = lp.get("server role")
523 if serverrole is None:
524 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
526 serverrole = serverrole.lower()
528 realm = dnsdomain.upper()
530 if lp.get("realm") == "":
531 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
533 if lp.get("realm").upper() != realm:
534 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))
536 if lp.get("server role").lower() != serverrole:
537 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))
539 if serverrole == "active directory domain controller":
540 if domain is None:
541 # This will, for better or worse, default to 'WORKGROUP'
542 domain = lp.get("workgroup")
543 domain = domain.upper()
545 if lp.get("workgroup").upper() != domain:
546 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))
548 if domaindn is None:
549 domaindn = samba.dn_from_dns_name(dnsdomain)
551 if domain == netbiosname:
552 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
553 else:
554 domain = netbiosname
555 if domaindn is None:
556 domaindn = "DC=" + netbiosname
558 if not valid_netbios_name(domain):
559 raise InvalidNetbiosName(domain)
561 if hostname.upper() == realm:
562 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
563 if netbiosname.upper() == realm:
564 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
565 if domain == realm:
566 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
568 if rootdn is None:
569 rootdn = domaindn
571 if configdn is None:
572 configdn = "CN=Configuration," + rootdn
573 if schemadn is None:
574 schemadn = "CN=Schema," + configdn
576 if sitename is None:
577 sitename = DEFAULTSITE
579 names = ProvisionNames()
580 names.rootdn = rootdn
581 names.domaindn = domaindn
582 names.configdn = configdn
583 names.schemadn = schemadn
584 names.ldapmanagerdn = "CN=Manager," + rootdn
585 names.dnsdomain = dnsdomain
586 names.domain = domain
587 names.realm = realm
588 names.netbiosname = netbiosname
589 names.hostname = hostname
590 names.sitename = sitename
591 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
592 netbiosname, sitename, configdn)
594 return names
597 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
598 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
599 global_param=None):
600 """Create a new smb.conf file based on a couple of basic settings.
602 assert smbconf is not None
604 if hostname is None:
605 hostname = socket.gethostname().split(".")[0]
607 netbiosname = determine_netbios_name(hostname)
609 if serverrole is None:
610 serverrole = "standalone server"
612 assert domain is not None
613 domain = domain.upper()
615 assert realm is not None
616 realm = realm.upper()
618 global_settings = {
619 "netbios name": netbiosname,
620 "workgroup": domain,
621 "realm": realm,
622 "server role": serverrole,
625 if lp is None:
626 lp = samba.param.LoadParm()
627 #Load non-existent file
628 if os.path.exists(smbconf):
629 lp.load(smbconf)
631 if global_param is not None:
632 for ent in global_param:
633 if global_param[ent] is not None:
634 global_settings[ent] = " ".join(global_param[ent])
636 if targetdir is not None:
637 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
638 global_settings["lock dir"] = os.path.abspath(targetdir)
639 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
640 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
642 lp.set("lock dir", os.path.abspath(targetdir))
643 lp.set("state directory", global_settings["state directory"])
644 lp.set("cache directory", global_settings["cache directory"])
646 if eadb:
647 if use_ntvfs and not lp.get("posix:eadb"):
648 if targetdir is not None:
649 privdir = os.path.join(targetdir, "private")
650 else:
651 privdir = lp.get("private dir")
652 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
653 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
654 if targetdir is not None:
655 statedir = os.path.join(targetdir, "state")
656 else:
657 statedir = lp.get("state directory")
658 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
660 shares = {}
661 if serverrole == "active directory domain controller":
662 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
663 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
664 "scripts")
665 else:
666 global_settings["passdb backend"] = "samba_dsdb"
668 f = open(smbconf, 'w')
669 try:
670 f.write("[globals]\n")
671 for key, val in global_settings.iteritems():
672 f.write("\t%s = %s\n" % (key, val))
673 f.write("\n")
675 for name, path in shares.iteritems():
676 f.write("[%s]\n" % name)
677 f.write("\tpath = %s\n" % path)
678 f.write("\tread only = no\n")
679 f.write("\n")
680 finally:
681 f.close()
682 # reload the smb.conf
683 lp.load(smbconf)
685 # and dump it without any values that are the default
686 # this ensures that any smb.conf parameters that were set
687 # on the provision/join command line are set in the resulting smb.conf
688 f = open(smbconf, mode='w')
689 try:
690 lp.dump(f, False)
691 finally:
692 f.close()
695 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
696 users_gid, root_gid):
697 """setup reasonable name mappings for sam names to unix names.
699 :param samdb: SamDB object.
700 :param idmap: IDmap db object.
701 :param sid: The domain sid.
702 :param domaindn: The domain DN.
703 :param root_uid: uid of the UNIX root user.
704 :param nobody_uid: uid of the UNIX nobody user.
705 :param users_gid: gid of the UNIX users group.
706 :param root_gid: gid of the UNIX root group.
708 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
710 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
711 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
714 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
715 provision_backend, names, schema, serverrole,
716 erase=False):
717 """Setup the partitions for the SAM database.
719 Alternatively, provision() may call this, and then populate the database.
721 :note: This will wipe the Sam Database!
723 :note: This function always removes the local SAM LDB file. The erase
724 parameter controls whether to erase the existing data, which
725 may not be stored locally but in LDAP.
728 assert session_info is not None
730 # We use options=["modules:"] to stop the modules loading - we
731 # just want to wipe and re-initialise the database, not start it up
733 try:
734 os.unlink(samdb_path)
735 except OSError:
736 pass
738 samdb = Ldb(url=samdb_path, session_info=session_info,
739 lp=lp, options=["modules:"])
741 ldap_backend_line = "# No LDAP backend"
742 if provision_backend.type != "ldb":
743 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
745 samdb.transaction_start()
746 try:
747 logger.info("Setting up sam.ldb partitions and settings")
748 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
749 "LDAP_BACKEND_LINE": ldap_backend_line
753 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
754 "BACKEND_TYPE": provision_backend.type,
755 "SERVER_ROLE": serverrole
758 logger.info("Setting up sam.ldb rootDSE")
759 setup_samdb_rootdse(samdb, names)
760 except:
761 samdb.transaction_cancel()
762 raise
763 else:
764 samdb.transaction_commit()
767 def secretsdb_self_join(secretsdb, domain,
768 netbiosname, machinepass, domainsid=None,
769 realm=None, dnsdomain=None,
770 keytab_path=None,
771 key_version_number=1,
772 secure_channel_type=SEC_CHAN_WKSTA):
773 """Add domain join-specific bits to a secrets database.
775 :param secretsdb: Ldb Handle to the secrets database
776 :param machinepass: Machine password
778 attrs = ["whenChanged",
779 "secret",
780 "priorSecret",
781 "priorChanged",
782 "krb5Keytab",
783 "privateKeytab"]
785 if realm is not None:
786 if dnsdomain is None:
787 dnsdomain = realm.lower()
788 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
789 else:
790 dnsname = None
791 shortname = netbiosname.lower()
793 # We don't need to set msg["flatname"] here, because rdn_name will handle
794 # it, and it causes problems for modifies anyway
795 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
796 msg["secureChannelType"] = [str(secure_channel_type)]
797 msg["objectClass"] = ["top", "primaryDomain"]
798 if dnsname is not None:
799 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
800 msg["realm"] = [realm]
801 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
802 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
803 msg["privateKeytab"] = ["secrets.keytab"]
805 msg["secret"] = [machinepass]
806 msg["samAccountName"] = ["%s$" % netbiosname]
807 msg["secureChannelType"] = [str(secure_channel_type)]
808 if domainsid is not None:
809 msg["objectSid"] = [ndr_pack(domainsid)]
811 # This complex expression tries to ensure that we don't have more
812 # than one record for this SID, realm or netbios domain at a time,
813 # but we don't delete the old record that we are about to modify,
814 # because that would delete the keytab and previous password.
815 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
816 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
817 scope=ldb.SCOPE_ONELEVEL)
819 for del_msg in res:
820 secretsdb.delete(del_msg.dn)
822 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
824 if len(res) == 1:
825 msg["priorSecret"] = [res[0]["secret"][0]]
826 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
828 try:
829 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
830 except KeyError:
831 pass
833 try:
834 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
835 except KeyError:
836 pass
838 for el in msg:
839 if el != 'dn':
840 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
841 secretsdb.modify(msg)
842 secretsdb.rename(res[0].dn, msg.dn)
843 else:
844 spn = [ 'HOST/%s' % shortname ]
845 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
846 # we are a domain controller then we add servicePrincipalName
847 # entries for the keytab code to update.
848 spn.extend([ 'HOST/%s' % dnsname ])
849 msg["servicePrincipalName"] = spn
851 secretsdb.add(msg)
854 def setup_secretsdb(paths, session_info, backend_credentials, lp):
855 """Setup the secrets database.
857 :note: This function does not handle exceptions and transaction on purpose,
858 it's up to the caller to do this job.
860 :param path: Path to the secrets database.
861 :param session_info: Session info.
862 :param credentials: Credentials
863 :param lp: Loadparm context
864 :return: LDB handle for the created secrets database
866 if os.path.exists(paths.secrets):
867 os.unlink(paths.secrets)
869 keytab_path = os.path.join(paths.private_dir, paths.keytab)
870 if os.path.exists(keytab_path):
871 os.unlink(keytab_path)
873 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
874 if os.path.exists(dns_keytab_path):
875 os.unlink(dns_keytab_path)
877 path = paths.secrets
879 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
880 secrets_ldb.erase()
881 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
882 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
883 secrets_ldb.transaction_start()
884 try:
885 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
887 if (backend_credentials is not None and
888 backend_credentials.authentication_requested()):
889 if backend_credentials.get_bind_dn() is not None:
890 setup_add_ldif(secrets_ldb,
891 setup_path("secrets_simple_ldap.ldif"), {
892 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
893 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
895 else:
896 setup_add_ldif(secrets_ldb,
897 setup_path("secrets_sasl_ldap.ldif"), {
898 "LDAPADMINUSER": backend_credentials.get_username(),
899 "LDAPADMINREALM": backend_credentials.get_realm(),
900 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
902 except:
903 secrets_ldb.transaction_cancel()
904 raise
905 return secrets_ldb
908 def setup_privileges(path, session_info, lp):
909 """Setup the privileges database.
911 :param path: Path to the privileges database.
912 :param session_info: Session info.
913 :param credentials: Credentials
914 :param lp: Loadparm context
915 :return: LDB handle for the created secrets database
917 if os.path.exists(path):
918 os.unlink(path)
919 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
920 privilege_ldb.erase()
921 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
924 def setup_registry(path, session_info, lp):
925 """Setup the registry.
927 :param path: Path to the registry database
928 :param session_info: Session information
929 :param credentials: Credentials
930 :param lp: Loadparm context
932 reg = samba.registry.Registry()
933 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
934 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
935 provision_reg = setup_path("provision.reg")
936 assert os.path.exists(provision_reg)
937 reg.diff_apply(provision_reg)
940 def setup_idmapdb(path, session_info, lp):
941 """Setup the idmap database.
943 :param path: path to the idmap database
944 :param session_info: Session information
945 :param credentials: Credentials
946 :param lp: Loadparm context
948 if os.path.exists(path):
949 os.unlink(path)
951 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
952 idmap_ldb.erase()
953 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
954 return idmap_ldb
957 def setup_samdb_rootdse(samdb, names):
958 """Setup the SamDB rootdse.
960 :param samdb: Sam Database handle
962 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
963 "SCHEMADN": names.schemadn,
964 "DOMAINDN": names.domaindn,
965 "ROOTDN" : names.rootdn,
966 "CONFIGDN": names.configdn,
967 "SERVERDN": names.serverdn,
971 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
972 dns_backend, dnspass, domainsid, next_rid, invocationid,
973 policyguid, policyguid_dc,
974 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
975 """Join a host to its own domain."""
976 assert isinstance(invocationid, str)
977 if ntdsguid is not None:
978 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
979 else:
980 ntdsguid_line = ""
982 if dc_rid is None:
983 dc_rid = next_rid
985 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
986 "CONFIGDN": names.configdn,
987 "SCHEMADN": names.schemadn,
988 "DOMAINDN": names.domaindn,
989 "SERVERDN": names.serverdn,
990 "INVOCATIONID": invocationid,
991 "NETBIOSNAME": names.netbiosname,
992 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
993 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
994 "DOMAINSID": str(domainsid),
995 "DCRID": str(dc_rid),
996 "SAMBA_VERSION_STRING": version,
997 "NTDSGUID": ntdsguid_line,
998 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
999 domainControllerFunctionality),
1000 "RIDALLOCATIONSTART": str(next_rid + 100),
1001 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1003 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1004 "POLICYGUID": policyguid,
1005 "POLICYGUID_DC": policyguid_dc,
1006 "DNSDOMAIN": names.dnsdomain,
1007 "DOMAINDN": names.domaindn})
1009 # If we are setting up a subdomain, then this has been replicated in, so we
1010 # don't need to add it
1011 if fill == FILL_FULL:
1012 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1013 "CONFIGDN": names.configdn,
1014 "SCHEMADN": names.schemadn,
1015 "DOMAINDN": names.domaindn,
1016 "SERVERDN": names.serverdn,
1017 "INVOCATIONID": invocationid,
1018 "NETBIOSNAME": names.netbiosname,
1019 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1020 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1021 "DOMAINSID": str(domainsid),
1022 "DCRID": str(dc_rid),
1023 "SAMBA_VERSION_STRING": version,
1024 "NTDSGUID": ntdsguid_line,
1025 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1026 domainControllerFunctionality)})
1028 # Setup fSMORoleOwner entries to point at the newly created DC entry
1029 setup_modify_ldif(samdb,
1030 setup_path("provision_self_join_modify_config.ldif"), {
1031 "CONFIGDN": names.configdn,
1032 "SCHEMADN": names.schemadn,
1033 "DEFAULTSITE": names.sitename,
1034 "NETBIOSNAME": names.netbiosname,
1035 "SERVERDN": names.serverdn,
1038 system_session_info = system_session()
1039 samdb.set_session_info(system_session_info)
1040 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1041 # modify a serverReference under cn=config when we are a subdomain, we must
1042 # be system due to ACLs
1043 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1044 "DOMAINDN": names.domaindn,
1045 "SERVERDN": names.serverdn,
1046 "NETBIOSNAME": names.netbiosname,
1049 samdb.set_session_info(admin_session_info)
1051 if dns_backend != "SAMBA_INTERNAL":
1052 # This is Samba4 specific and should be replaced by the correct
1053 # DNS AD-style setup
1054 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1055 "DNSDOMAIN": names.dnsdomain,
1056 "DOMAINDN": names.domaindn,
1057 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1058 "HOSTNAME" : names.hostname,
1059 "DNSNAME" : '%s.%s' % (
1060 names.netbiosname.lower(), names.dnsdomain.lower())
1064 def getpolicypath(sysvolpath, dnsdomain, guid):
1065 """Return the physical path of policy given its guid.
1067 :param sysvolpath: Path to the sysvol folder
1068 :param dnsdomain: DNS name of the AD domain
1069 :param guid: The GUID of the policy
1070 :return: A string with the complete path to the policy folder
1072 if guid[0] != "{":
1073 guid = "{%s}" % guid
1074 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1075 return policy_path
1078 def create_gpo_struct(policy_path):
1079 if not os.path.exists(policy_path):
1080 os.makedirs(policy_path, 0775)
1081 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1082 try:
1083 f.write("[General]\r\nVersion=0")
1084 finally:
1085 f.close()
1086 p = os.path.join(policy_path, "MACHINE")
1087 if not os.path.exists(p):
1088 os.makedirs(p, 0775)
1089 p = os.path.join(policy_path, "USER")
1090 if not os.path.exists(p):
1091 os.makedirs(p, 0775)
1094 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1095 """Create the default GPO for a domain
1097 :param sysvolpath: Physical path for the sysvol folder
1098 :param dnsdomain: DNS domain name of the AD domain
1099 :param policyguid: GUID of the default domain policy
1100 :param policyguid_dc: GUID of the default domain controler policy
1102 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1103 create_gpo_struct(policy_path)
1105 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1106 create_gpo_struct(policy_path)
1109 def setup_samdb(path, session_info, provision_backend, lp, names,
1110 logger, fill, serverrole, schema, am_rodc=False):
1111 """Setup a complete SAM Database.
1113 :note: This will wipe the main SAM database file!
1116 # Also wipes the database
1117 setup_samdb_partitions(path, logger=logger, lp=lp,
1118 provision_backend=provision_backend, session_info=session_info,
1119 names=names, serverrole=serverrole, schema=schema)
1121 # Load the database, but don's load the global schema and don't connect
1122 # quite yet
1123 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1124 credentials=provision_backend.credentials, lp=lp,
1125 global_schema=False, am_rodc=am_rodc)
1127 logger.info("Pre-loading the Samba 4 and AD schema")
1129 # Load the schema from the one we computed earlier
1130 samdb.set_schema(schema, write_indices_and_attributes=False)
1132 # Set the NTDS settings DN manually - in order to have it already around
1133 # before the provisioned tree exists and we connect
1134 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1136 # And now we can connect to the DB - the schema won't be loaded from the
1137 # DB
1138 samdb.connect(path)
1140 # But we have to give it one more kick to have it use the schema
1141 # during provision - it needs, now that it is connected, to write
1142 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1143 samdb.set_schema(schema, write_indices_and_attributes=True)
1145 return samdb
1148 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1149 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1150 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1151 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1153 if next_rid is None:
1154 next_rid = 1000
1156 # Provision does not make much sense values larger than 1000000000
1157 # as the upper range of the rIDAvailablePool is 1073741823 and
1158 # we don't want to create a domain that cannot allocate rids.
1159 if next_rid < 1000 or next_rid > 1000000000:
1160 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1161 error += "the valid range is %u-%u. The default is %u." % (
1162 1000, 1000000000, 1000)
1163 raise ProvisioningError(error)
1165 # ATTENTION: Do NOT change these default values without discussion with the
1166 # team and/or release manager. They have a big impact on the whole program!
1167 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1169 if dom_for_fun_level is None:
1170 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1172 if dom_for_fun_level > domainControllerFunctionality:
1173 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!")
1175 domainFunctionality = dom_for_fun_level
1176 forestFunctionality = dom_for_fun_level
1178 # Set the NTDS settings DN manually - in order to have it already around
1179 # before the provisioned tree exists and we connect
1180 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1182 samdb.transaction_start()
1183 try:
1184 # Set the domain functionality levels onto the database.
1185 # Various module (the password_hash module in particular) need
1186 # to know what level of AD we are emulating.
1188 # These will be fixed into the database via the database
1189 # modifictions below, but we need them set from the start.
1190 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1191 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1192 samdb.set_opaque_integer("domainControllerFunctionality",
1193 domainControllerFunctionality)
1195 samdb.set_domain_sid(str(domainsid))
1196 samdb.set_invocation_id(invocationid)
1198 logger.info("Adding DomainDN: %s" % names.domaindn)
1200 # impersonate domain admin
1201 admin_session_info = admin_session(lp, str(domainsid))
1202 samdb.set_session_info(admin_session_info)
1203 if domainguid is not None:
1204 domainguid_line = "objectGUID: %s\n-" % domainguid
1205 else:
1206 domainguid_line = ""
1208 descr = b64encode(get_domain_descriptor(domainsid))
1209 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1210 "DOMAINDN": names.domaindn,
1211 "DOMAINSID": str(domainsid),
1212 "DESCRIPTOR": descr,
1213 "DOMAINGUID": domainguid_line
1216 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1217 "DOMAINDN": names.domaindn,
1218 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1219 "NEXTRID": str(next_rid),
1220 "DEFAULTSITE": names.sitename,
1221 "CONFIGDN": names.configdn,
1222 "POLICYGUID": policyguid,
1223 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1224 "SAMBA_VERSION_STRING": version
1227 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1228 if fill == FILL_FULL:
1229 logger.info("Adding configuration container")
1230 descr = b64encode(get_config_descriptor(domainsid))
1231 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1232 "CONFIGDN": names.configdn,
1233 "DESCRIPTOR": descr,
1236 # The LDIF here was created when the Schema object was constructed
1237 logger.info("Setting up sam.ldb schema")
1238 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1239 samdb.modify_ldif(schema.schema_dn_modify)
1240 samdb.write_prefixes_from_schema()
1241 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1242 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1243 {"SCHEMADN": names.schemadn})
1245 # Now register this container in the root of the forest
1246 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1247 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1248 "subRefs")
1250 except:
1251 samdb.transaction_cancel()
1252 raise
1253 else:
1254 samdb.transaction_commit()
1256 samdb.transaction_start()
1257 try:
1258 samdb.invocation_id = invocationid
1260 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1261 if fill == FILL_FULL:
1262 logger.info("Setting up sam.ldb configuration data")
1263 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1264 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1265 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1266 "CONFIGDN": names.configdn,
1267 "NETBIOSNAME": names.netbiosname,
1268 "DEFAULTSITE": names.sitename,
1269 "DNSDOMAIN": names.dnsdomain,
1270 "DOMAIN": names.domain,
1271 "SCHEMADN": names.schemadn,
1272 "DOMAINDN": names.domaindn,
1273 "SERVERDN": names.serverdn,
1274 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1275 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1276 "PARTITIONS_DESCRIPTOR": partitions_descr,
1277 "SITES_DESCRIPTOR": sites_descr,
1280 logger.info("Setting up display specifiers")
1281 display_specifiers_ldif = read_ms_ldif(
1282 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1283 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1284 {"CONFIGDN": names.configdn})
1285 check_all_substituted(display_specifiers_ldif)
1286 samdb.add_ldif(display_specifiers_ldif)
1288 logger.info("Adding users container")
1289 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1290 "DOMAINDN": names.domaindn})
1291 logger.info("Modifying users container")
1292 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1293 "DOMAINDN": names.domaindn})
1294 logger.info("Adding computers container")
1295 computers_desc = b64encode(get_domain_computers_descriptor(domainsid))
1296 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1297 "DOMAINDN": names.domaindn,
1298 "COMPUTERS_DESCRIPTOR": computers_desc
1300 logger.info("Modifying computers container")
1301 setup_modify_ldif(samdb,
1302 setup_path("provision_computers_modify.ldif"), {
1303 "DOMAINDN": names.domaindn})
1304 logger.info("Setting up sam.ldb data")
1305 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
1306 builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
1307 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1308 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1309 "DOMAINDN": names.domaindn,
1310 "NETBIOSNAME": names.netbiosname,
1311 "DEFAULTSITE": names.sitename,
1312 "CONFIGDN": names.configdn,
1313 "SERVERDN": names.serverdn,
1314 "RIDAVAILABLESTART": str(next_rid + 600),
1315 "POLICYGUID_DC": policyguid_dc,
1316 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1317 "BUILTIN_DESCRIPTOR": builtin_desc,
1320 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1321 if fill == FILL_FULL:
1322 setup_modify_ldif(samdb,
1323 setup_path("provision_configuration_references.ldif"), {
1324 "CONFIGDN": names.configdn,
1325 "SCHEMADN": names.schemadn})
1327 logger.info("Setting up well known security principals")
1328 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1329 "CONFIGDN": names.configdn,
1332 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1333 setup_modify_ldif(samdb,
1334 setup_path("provision_basedn_references.ldif"),
1335 {"DOMAINDN": names.domaindn})
1337 logger.info("Setting up sam.ldb users and groups")
1338 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1339 "DOMAINDN": names.domaindn,
1340 "DOMAINSID": str(domainsid),
1341 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1342 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1345 logger.info("Setting up self join")
1346 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1347 invocationid=invocationid,
1348 dns_backend=dns_backend,
1349 dnspass=dnspass,
1350 machinepass=machinepass,
1351 domainsid=domainsid,
1352 next_rid=next_rid,
1353 dc_rid=dc_rid,
1354 policyguid=policyguid,
1355 policyguid_dc=policyguid_dc,
1356 domainControllerFunctionality=domainControllerFunctionality,
1357 ntdsguid=ntdsguid)
1359 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1360 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1361 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1362 assert isinstance(names.ntdsguid, str)
1363 except:
1364 samdb.transaction_cancel()
1365 raise
1366 else:
1367 samdb.transaction_commit()
1368 return samdb
1371 FILL_FULL = "FULL"
1372 FILL_SUBDOMAIN = "SUBDOMAIN"
1373 FILL_NT4SYNC = "NT4SYNC"
1374 FILL_DRS = "DRS"
1375 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1376 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)"
1379 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb):
1380 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1381 for root, dirs, files in os.walk(path, topdown=False):
1382 for name in files:
1383 setntacl(lp, os.path.join(root, name), acl, domsid,
1384 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1385 for name in dirs:
1386 setntacl(lp, os.path.join(root, name), acl, domsid,
1387 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1390 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1391 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1392 folders beneath.
1394 :param sysvol: Physical path for the sysvol folder
1395 :param dnsdomain: The DNS name of the domain
1396 :param domainsid: The SID of the domain
1397 :param domaindn: The DN of the domain (ie. DC=...)
1398 :param samdb: An LDB object on the SAM db
1399 :param lp: an LP object
1402 # Set ACL for GPO root folder
1403 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1404 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1405 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1407 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1408 attrs=["cn", "nTSecurityDescriptor"],
1409 expression="", scope=ldb.SCOPE_ONELEVEL)
1411 for policy in res:
1412 acl = ndr_unpack(security.descriptor,
1413 str(policy["nTSecurityDescriptor"])).as_sddl()
1414 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1415 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1416 str(domainsid), use_ntvfs,
1417 passdb=passdb)
1420 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1421 domaindn, lp, use_ntvfs):
1422 """Set the ACL for the sysvol share and the subfolders
1424 :param samdb: An LDB object on the SAM db
1425 :param netlogon: Physical path for the netlogon folder
1426 :param sysvol: Physical path for the sysvol folder
1427 :param uid: The UID of the "Administrator" user
1428 :param gid: The GID of the "Domain adminstrators" group
1429 :param domainsid: The SID of the domain
1430 :param dnsdomain: The DNS name of the domain
1431 :param domaindn: The DN of the domain (ie. DC=...)
1433 s4_passdb = None
1435 if not use_ntvfs:
1436 # This will ensure that the smbd code we are running when setting ACLs
1437 # is initialised with the smb.conf
1438 s3conf = s3param.get_context()
1439 s3conf.load(lp.configfile)
1440 # ensure we are using the right samba_dsdb passdb backend, no matter what
1441 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1442 passdb.reload_static_pdb()
1444 # ensure that we init the samba_dsdb backend, so the domain sid is
1445 # marked in secrets.tdb
1446 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1448 # now ensure everything matches correctly, to avoid wierd issues
1449 if passdb.get_global_sam_sid() != domainsid:
1450 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))
1452 domain_info = s4_passdb.domain_info()
1453 if domain_info["dom_sid"] != domainsid:
1454 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))
1456 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1457 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()))
1460 try:
1461 if use_ntvfs:
1462 os.chown(sysvol, -1, gid)
1463 except OSError:
1464 canchown = False
1465 else:
1466 canchown = True
1468 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1469 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1470 for root, dirs, files in os.walk(sysvol, topdown=False):
1471 for name in files:
1472 if use_ntvfs and canchown:
1473 os.chown(os.path.join(root, name), -1, gid)
1474 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1475 for name in dirs:
1476 if use_ntvfs and canchown:
1477 os.chown(os.path.join(root, name), -1, gid)
1478 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1480 # Set acls on Policy folder and policies folders
1481 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1483 def acl_type(direct_db_access):
1484 if direct_db_access:
1485 return "DB"
1486 else:
1487 return "VFS"
1489 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1490 fsacl = getntacl(lp, path, direct_db_access=direct_db_access)
1491 fsacl_sddl = fsacl.as_sddl(domainsid)
1492 if fsacl_sddl != acl:
1493 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))
1495 for root, dirs, files in os.walk(path, topdown=False):
1496 for name in files:
1497 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1498 if fsacl is None:
1499 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1500 fsacl_sddl = fsacl.as_sddl(domainsid)
1501 if fsacl_sddl != acl:
1502 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))
1504 for name in dirs:
1505 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1506 if fsacl is None:
1507 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1508 fsacl_sddl = fsacl.as_sddl(domainsid)
1509 if fsacl_sddl != acl:
1510 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))
1513 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1514 direct_db_access):
1515 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1516 folders beneath.
1518 :param sysvol: Physical path for the sysvol folder
1519 :param dnsdomain: The DNS name of the domain
1520 :param domainsid: The SID of the domain
1521 :param domaindn: The DN of the domain (ie. DC=...)
1522 :param samdb: An LDB object on the SAM db
1523 :param lp: an LP object
1526 # Set ACL for GPO root folder
1527 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1528 fsacl = getntacl(lp, root_policy_path, direct_db_access=direct_db_access)
1529 if fsacl is None:
1530 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1531 fsacl_sddl = fsacl.as_sddl(domainsid)
1532 if fsacl_sddl != POLICIES_ACL:
1533 raise ProvisioningError('%s ACL on policy root %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), root_policy_path, fsacl_sddl, fsacl))
1534 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1535 attrs=["cn", "nTSecurityDescriptor"],
1536 expression="", scope=ldb.SCOPE_ONELEVEL)
1538 for policy in res:
1539 acl = ndr_unpack(security.descriptor,
1540 str(policy["nTSecurityDescriptor"])).as_sddl()
1541 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1542 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1543 domainsid, direct_db_access)
1546 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1547 lp):
1548 """Set the ACL for the sysvol share and the subfolders
1550 :param samdb: An LDB object on the SAM db
1551 :param netlogon: Physical path for the netlogon folder
1552 :param sysvol: Physical path for the sysvol folder
1553 :param uid: The UID of the "Administrator" user
1554 :param gid: The GID of the "Domain adminstrators" group
1555 :param domainsid: The SID of the domain
1556 :param dnsdomain: The DNS name of the domain
1557 :param domaindn: The DN of the domain (ie. DC=...)
1560 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1561 s3conf = s3param.get_context()
1562 s3conf.load(lp.configfile)
1563 # ensure we are using the right samba_dsdb passdb backend, no matter what
1564 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1565 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1566 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1568 # now ensure everything matches correctly, to avoid wierd issues
1569 if passdb.get_global_sam_sid() != domainsid:
1570 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))
1572 domain_info = s4_passdb.domain_info()
1573 if domain_info["dom_sid"] != domainsid:
1574 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))
1576 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1577 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()))
1579 # Ensure we can read this directly, and via the smbd VFS
1580 for direct_db_access in [True, False]:
1581 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1582 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1583 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access)
1584 if fsacl is None:
1585 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1586 fsacl_sddl = fsacl.as_sddl(domainsid)
1587 if fsacl_sddl != SYSVOL_ACL:
1588 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))
1590 # Check acls on Policy folder and policies folders
1591 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1592 direct_db_access)
1595 def interface_ips_v4(lp):
1596 """return only IPv4 IPs"""
1597 ips = samba.interface_ips(lp, False)
1598 ret = []
1599 for i in ips:
1600 if i.find(':') == -1:
1601 ret.append(i)
1602 return ret
1605 def interface_ips_v6(lp, linklocal=False):
1606 """return only IPv6 IPs"""
1607 ips = samba.interface_ips(lp, False)
1608 ret = []
1609 for i in ips:
1610 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1611 ret.append(i)
1612 return ret
1615 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1616 domainsid, schema=None,
1617 targetdir=None, samdb_fill=FILL_FULL,
1618 hostip=None, hostip6=None,
1619 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1620 domainguid=None, policyguid=None, policyguid_dc=None,
1621 invocationid=None, machinepass=None, ntdsguid=None,
1622 dns_backend=None, dnspass=None,
1623 serverrole=None, dom_for_fun_level=None,
1624 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1625 # create/adapt the group policy GUIDs
1626 # Default GUID for default policy are described at
1627 # "How Core Group Policy Works"
1628 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1629 if policyguid is None:
1630 policyguid = DEFAULT_POLICY_GUID
1631 policyguid = policyguid.upper()
1632 if policyguid_dc is None:
1633 policyguid_dc = DEFAULT_DC_POLICY_GUID
1634 policyguid_dc = policyguid_dc.upper()
1636 if invocationid is None:
1637 invocationid = str(uuid.uuid4())
1639 if krbtgtpass is None:
1640 krbtgtpass = samba.generate_random_password(128, 255)
1641 if machinepass is None:
1642 machinepass = samba.generate_random_password(128, 255)
1643 if dnspass is None:
1644 dnspass = samba.generate_random_password(128, 255)
1646 samdb = fill_samdb(samdb, lp, names, logger=logger,
1647 domainsid=domainsid, schema=schema, domainguid=domainguid,
1648 policyguid=policyguid, policyguid_dc=policyguid_dc,
1649 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1650 invocationid=invocationid, machinepass=machinepass,
1651 dns_backend=dns_backend, dnspass=dnspass,
1652 ntdsguid=ntdsguid, serverrole=serverrole,
1653 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1654 next_rid=next_rid, dc_rid=dc_rid)
1656 if serverrole == "active directory domain controller":
1658 # Set up group policies (domain policy and domain controller
1659 # policy)
1660 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1661 policyguid_dc)
1662 if not skip_sysvolacl:
1663 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1664 paths.root_gid, domainsid, names.dnsdomain,
1665 names.domaindn, lp, use_ntvfs)
1666 else:
1667 logger.info("Setting acl on sysvol skipped")
1669 secretsdb_self_join(secrets_ldb, domain=names.domain,
1670 realm=names.realm, dnsdomain=names.dnsdomain,
1671 netbiosname=names.netbiosname, domainsid=domainsid,
1672 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1674 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1675 # In future, this might be determined from some configuration
1676 kerberos_enctypes = str(ENC_ALL_TYPES)
1678 try:
1679 msg = ldb.Message(ldb.Dn(samdb,
1680 samdb.searchone("distinguishedName",
1681 expression="samAccountName=%s$" % names.netbiosname,
1682 scope=ldb.SCOPE_SUBTREE)))
1683 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1684 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1685 name="msDS-SupportedEncryptionTypes")
1686 samdb.modify(msg)
1687 except ldb.LdbError, (enum, estr):
1688 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1689 # It might be that this attribute does not exist in this schema
1690 raise
1692 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1693 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1694 dnspass=dnspass, os_level=dom_for_fun_level,
1695 targetdir=targetdir, site=DEFAULTSITE)
1697 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1698 attribute="objectGUID")
1699 assert isinstance(domainguid, str)
1701 lastProvisionUSNs = get_last_provision_usn(samdb)
1702 maxUSN = get_max_usn(samdb, str(names.rootdn))
1703 if lastProvisionUSNs is not None:
1704 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1705 else:
1706 set_provision_usn(samdb, 0, maxUSN, invocationid)
1708 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1709 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1710 { 'NTDSGUID' : names.ntdsguid })
1712 # fix any dangling GUIDs from the provision
1713 logger.info("Fixing provision GUIDs")
1714 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1715 quiet=True)
1716 samdb.transaction_start()
1717 try:
1718 # a small number of GUIDs are missing because of ordering issues in the
1719 # provision code
1720 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1721 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1722 scope=ldb.SCOPE_BASE,
1723 attrs=['defaultObjectCategory'])
1724 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1725 scope=ldb.SCOPE_ONELEVEL,
1726 attrs=['ipsecOwnersReference',
1727 'ipsecFilterReference',
1728 'ipsecISAKMPReference',
1729 'ipsecNegotiationPolicyReference',
1730 'ipsecNFAReference'])
1731 except:
1732 samdb.transaction_cancel()
1733 raise
1734 else:
1735 samdb.transaction_commit()
1738 _ROLES_MAP = {
1739 "ROLE_STANDALONE": "standalone server",
1740 "ROLE_DOMAIN_MEMBER": "member server",
1741 "ROLE_DOMAIN_BDC": "active directory domain controller",
1742 "ROLE_DOMAIN_PDC": "active directory domain controller",
1743 "dc": "active directory domain controller",
1744 "member": "member server",
1745 "domain controller": "active directory domain controller",
1746 "active directory domain controller": "active directory domain controller",
1747 "member server": "member server",
1748 "standalone": "standalone server",
1749 "standalone server": "standalone server",
1753 def sanitize_server_role(role):
1754 """Sanitize a server role name.
1756 :param role: Server role
1757 :raise ValueError: If the role can not be interpreted
1758 :return: Sanitized server role (one of "member server",
1759 "active directory domain controller", "standalone server")
1761 try:
1762 return _ROLES_MAP[role]
1763 except KeyError:
1764 raise ValueError(role)
1767 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1768 maxuid, maxgid):
1769 """Create AD entries for the fake ypserver.
1771 This is needed for being able to manipulate posix attrs via ADUC.
1773 samdb.transaction_start()
1774 try:
1775 logger.info("Setting up fake yp server settings")
1776 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1777 "DOMAINDN": domaindn,
1778 "NETBIOSNAME": netbiosname,
1779 "NISDOMAIN": nisdomain,
1781 except:
1782 samdb.transaction_cancel()
1783 raise
1784 else:
1785 samdb.transaction_commit()
1788 def provision(logger, session_info, credentials, smbconf=None,
1789 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1790 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1791 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1792 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1793 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1794 dns_backend=None, dns_forwarder=None, dnspass=None,
1795 invocationid=None, machinepass=None, ntdsguid=None,
1796 root=None, nobody=None, users=None, backup=None, aci=None,
1797 serverrole=None, dom_for_fun_level=None, backend_type=None,
1798 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1799 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1800 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1801 """Provision samba4
1803 :note: caution, this wipes all existing data!
1806 try:
1807 serverrole = sanitize_server_role(serverrole)
1808 except ValueError:
1809 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1811 if ldapadminpass is None:
1812 # Make a new, random password between Samba and it's LDAP server
1813 ldapadminpass = samba.generate_random_password(128, 255)
1815 if backend_type is None:
1816 backend_type = "ldb"
1818 if domainsid is None:
1819 domainsid = security.random_sid()
1820 else:
1821 domainsid = security.dom_sid(domainsid)
1823 root_uid = findnss_uid([root or "root"])
1824 nobody_uid = findnss_uid([nobody or "nobody"])
1825 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1826 root_gid = pwd.getpwuid(root_uid).pw_gid
1828 try:
1829 bind_gid = findnss_gid(["bind", "named"])
1830 except KeyError:
1831 bind_gid = None
1833 if targetdir is not None:
1834 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1835 elif smbconf is None:
1836 smbconf = samba.param.default_path()
1837 if not os.path.exists(os.path.dirname(smbconf)):
1838 os.makedirs(os.path.dirname(smbconf))
1840 server_services = []
1841 global_param = {}
1842 if use_rfc2307:
1843 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1845 if dns_backend != "SAMBA_INTERNAL":
1846 server_services.append("-dns")
1847 else:
1848 if dns_forwarder is not None:
1849 global_param["dns forwarder"] = [dns_forwarder]
1851 if use_ntvfs:
1852 server_services.append("+smb")
1853 server_services.append("-s3fs")
1854 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1856 if len(server_services) > 0:
1857 global_param["server services"] = server_services
1859 # only install a new smb.conf if there isn't one there already
1860 if os.path.exists(smbconf):
1861 # if Samba Team members can't figure out the weird errors
1862 # loading an empty smb.conf gives, then we need to be smarter.
1863 # Pretend it just didn't exist --abartlet
1864 f = open(smbconf, 'r')
1865 try:
1866 data = f.read().lstrip()
1867 finally:
1868 f.close()
1869 if data is None or data == "":
1870 make_smbconf(smbconf, hostname, domain, realm,
1871 targetdir, serverrole=serverrole,
1872 eadb=useeadb, use_ntvfs=use_ntvfs,
1873 lp=lp, global_param=global_param)
1874 else:
1875 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1876 serverrole=serverrole,
1877 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1879 if lp is None:
1880 lp = samba.param.LoadParm()
1881 lp.load(smbconf)
1882 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1883 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1884 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1885 sitename=sitename, rootdn=rootdn)
1886 paths = provision_paths_from_lp(lp, names.dnsdomain)
1888 paths.bind_gid = bind_gid
1889 paths.root_uid = root_uid;
1890 paths.root_gid = root_gid
1892 if hostip is None:
1893 logger.info("Looking up IPv4 addresses")
1894 hostips = interface_ips_v4(lp)
1895 if len(hostips) > 0:
1896 hostip = hostips[0]
1897 if len(hostips) > 1:
1898 logger.warning("More than one IPv4 address found. Using %s",
1899 hostip)
1900 if hostip == "127.0.0.1":
1901 hostip = None
1902 if hostip is None:
1903 logger.warning("No IPv4 address will be assigned")
1905 if hostip6 is None:
1906 logger.info("Looking up IPv6 addresses")
1907 hostips = interface_ips_v6(lp, linklocal=False)
1908 if hostips:
1909 hostip6 = hostips[0]
1910 if len(hostips) > 1:
1911 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1912 if hostip6 is None:
1913 logger.warning("No IPv6 address will be assigned")
1915 names.hostip = hostip
1916 names.hostip6 = hostip6
1918 if serverrole is None:
1919 serverrole = lp.get("server role")
1921 if not os.path.exists(paths.private_dir):
1922 os.mkdir(paths.private_dir)
1923 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1924 os.mkdir(os.path.join(paths.private_dir, "tls"))
1925 if not os.path.exists(paths.state_dir):
1926 os.mkdir(paths.state_dir)
1928 if paths.sysvol and not os.path.exists(paths.sysvol):
1929 os.makedirs(paths.sysvol, 0775)
1931 if not use_ntvfs and serverrole == "active directory domain controller":
1932 s3conf = s3param.get_context()
1933 s3conf.load(lp.configfile)
1935 if paths.sysvol is None:
1936 raise MissingShareError("sysvol", paths.smbconf)
1938 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1939 try:
1940 try:
1941 smbd.set_simple_acl(file.name, 0755, root_gid)
1942 except Exception:
1943 if not smbd.have_posix_acls():
1944 # This clue is only strictly correct for RPM and
1945 # Debian-like Linux systems, but hopefully other users
1946 # will get enough clue from it.
1947 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.")
1949 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1950 try:
1951 smbd.chown(file.name, root_uid, root_gid)
1952 except Exception:
1953 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1954 finally:
1955 file.close()
1957 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1959 schema = Schema(domainsid, invocationid=invocationid,
1960 schemadn=names.schemadn)
1962 if backend_type == "ldb":
1963 provision_backend = LDBBackend(backend_type, paths=paths,
1964 lp=lp, credentials=credentials,
1965 names=names, logger=logger)
1966 elif backend_type == "existing":
1967 # If support for this is ever added back, then the URI will need to be
1968 # specified again
1969 provision_backend = ExistingBackend(backend_type, paths=paths,
1970 lp=lp, credentials=credentials,
1971 names=names, logger=logger,
1972 ldap_backend_forced_uri=None)
1973 elif backend_type == "fedora-ds":
1974 provision_backend = FDSBackend(backend_type, paths=paths,
1975 lp=lp, credentials=credentials,
1976 names=names, logger=logger, domainsid=domainsid,
1977 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1978 slapd_path=slapd_path,
1979 root=root)
1980 elif backend_type == "openldap":
1981 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1982 lp=lp, credentials=credentials,
1983 names=names, logger=logger, domainsid=domainsid,
1984 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1985 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1986 else:
1987 raise ValueError("Unknown LDAP backend type selected")
1989 provision_backend.init()
1990 provision_backend.start()
1992 # only install a new shares config db if there is none
1993 if not os.path.exists(paths.shareconf):
1994 logger.info("Setting up share.ldb")
1995 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1996 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1998 logger.info("Setting up secrets.ldb")
1999 secrets_ldb = setup_secretsdb(paths,
2000 session_info=session_info,
2001 backend_credentials=provision_backend.secrets_credentials, lp=lp)
2003 try:
2004 logger.info("Setting up the registry")
2005 setup_registry(paths.hklm, session_info, lp=lp)
2007 logger.info("Setting up the privileges database")
2008 setup_privileges(paths.privilege, session_info, lp=lp)
2010 logger.info("Setting up idmap db")
2011 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2013 setup_name_mappings(idmap, sid=str(domainsid),
2014 root_uid=root_uid, nobody_uid=nobody_uid,
2015 users_gid=users_gid, root_gid=root_gid)
2017 logger.info("Setting up SAM db")
2018 samdb = setup_samdb(paths.samdb, session_info,
2019 provision_backend, lp, names, logger=logger,
2020 serverrole=serverrole,
2021 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2023 if serverrole == "active directory domain controller":
2024 if paths.netlogon is None:
2025 raise MissingShareError("netlogon", paths.smbconf)
2027 if paths.sysvol is None:
2028 raise MissingShareError("sysvol", paths.smbconf)
2030 if not os.path.isdir(paths.netlogon):
2031 os.makedirs(paths.netlogon, 0755)
2033 if adminpass is None:
2034 adminpass = samba.generate_random_password(12, 32)
2035 adminpass_generated = True
2036 else:
2037 adminpass_generated = False
2039 if samdb_fill == FILL_FULL:
2040 provision_fill(samdb, secrets_ldb, logger, names, paths,
2041 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2042 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2043 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2044 krbtgtpass=krbtgtpass, domainguid=domainguid,
2045 policyguid=policyguid, policyguid_dc=policyguid_dc,
2046 invocationid=invocationid, machinepass=machinepass,
2047 ntdsguid=ntdsguid, dns_backend=dns_backend,
2048 dnspass=dnspass, serverrole=serverrole,
2049 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2050 lp=lp, use_ntvfs=use_ntvfs,
2051 skip_sysvolacl=skip_sysvolacl)
2053 create_krb5_conf(paths.krb5conf,
2054 dnsdomain=names.dnsdomain, hostname=names.hostname,
2055 realm=names.realm)
2056 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2057 "generated at %s", paths.krb5conf)
2059 if serverrole == "active directory domain controller":
2060 create_dns_update_list(lp, logger, paths)
2062 backend_result = provision_backend.post_setup()
2063 provision_backend.shutdown()
2065 except:
2066 secrets_ldb.transaction_cancel()
2067 raise
2069 # Now commit the secrets.ldb to disk
2070 secrets_ldb.transaction_commit()
2072 # the commit creates the dns.keytab, now chown it
2073 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2074 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2075 try:
2076 os.chmod(dns_keytab_path, 0640)
2077 os.chown(dns_keytab_path, -1, paths.bind_gid)
2078 except OSError:
2079 if not os.environ.has_key('SAMBA_SELFTEST'):
2080 logger.info("Failed to chown %s to bind gid %u",
2081 dns_keytab_path, paths.bind_gid)
2083 result = ProvisionResult()
2084 result.server_role = serverrole
2085 result.domaindn = domaindn
2086 result.paths = paths
2087 result.names = names
2088 result.lp = lp
2089 result.samdb = samdb
2090 result.idmap = idmap
2091 result.domainsid = str(domainsid)
2093 if samdb_fill == FILL_FULL:
2094 result.adminpass_generated = adminpass_generated
2095 result.adminpass = adminpass
2096 else:
2097 result.adminpass_generated = False
2098 result.adminpass = None
2100 result.backend_result = backend_result
2102 if use_rfc2307:
2103 provision_fake_ypserver(logger=logger, samdb=samdb,
2104 domaindn=names.domaindn, netbiosname=names.netbiosname,
2105 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2107 return result
2110 def provision_become_dc(smbconf=None, targetdir=None,
2111 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2112 serverdn=None, domain=None, hostname=None, domainsid=None,
2113 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2114 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2115 dns_backend=None, root=None, nobody=None, users=None,
2116 backup=None, serverrole=None, ldap_backend=None,
2117 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2119 logger = logging.getLogger("provision")
2120 samba.set_debug_level(debuglevel)
2122 res = provision(logger, system_session(), None,
2123 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2124 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2125 configdn=configdn, serverdn=serverdn, domain=domain,
2126 hostname=hostname, hostip=None, domainsid=domainsid,
2127 machinepass=machinepass,
2128 serverrole="active directory domain controller",
2129 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2130 use_ntvfs=use_ntvfs)
2131 res.lp.set("debuglevel", str(debuglevel))
2132 return res
2135 def create_krb5_conf(path, dnsdomain, hostname, realm):
2136 """Write out a file containing zone statements suitable for inclusion in a
2137 named.conf file (including GSS-TSIG configuration).
2139 :param path: Path of the new named.conf file.
2140 :param dnsdomain: DNS Domain name
2141 :param hostname: Local hostname
2142 :param realm: Realm name
2144 setup_file(setup_path("krb5.conf"), path, {
2145 "DNSDOMAIN": dnsdomain,
2146 "HOSTNAME": hostname,
2147 "REALM": realm,
2151 class ProvisioningError(Exception):
2152 """A generic provision error."""
2154 def __init__(self, value):
2155 self.value = value
2157 def __str__(self):
2158 return "ProvisioningError: " + self.value
2161 class InvalidNetbiosName(Exception):
2162 """A specified name was not a valid NetBIOS name."""
2164 def __init__(self, name):
2165 super(InvalidNetbiosName, self).__init__(
2166 "The name '%r' is not a valid NetBIOS name" % name)
2169 class MissingShareError(ProvisioningError):
2171 def __init__(self, name, smbconf):
2172 super(MissingShareError, self).__init__(
2173 "Existing smb.conf does not have a [%s] share, but you are "
2174 "configuring a DC. Please remove %s or add the share manually." %
2175 (name, smbconf))