s4:provision: set the correct nTSecurityDescriptor on CN=Infrastructure,... (bug...
[Samba/gbeck.git] / source4 / scripting / python / samba / provision / __init__.py
blob74288c1347d52eb8dd9afc338bfe5db755291a97
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,
87 from samba.provision.common import (
88 setup_path,
89 setup_add_ldif,
90 setup_modify_ldif,
92 from samba.provision.sambadns import (
93 setup_ad_dns,
94 create_dns_update_list
97 import samba.param
98 import samba.registry
99 from samba.schema import Schema
100 from samba.samdb import SamDB
101 from samba.dbchecker import dbcheck
104 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
105 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
106 DEFAULTSITE = "Default-First-Site-Name"
107 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
110 class ProvisionPaths(object):
112 def __init__(self):
113 self.shareconf = None
114 self.hklm = None
115 self.hkcu = None
116 self.hkcr = None
117 self.hku = None
118 self.hkpd = None
119 self.hkpt = None
120 self.samdb = None
121 self.idmapdb = None
122 self.secrets = None
123 self.keytab = None
124 self.dns_keytab = None
125 self.dns = None
126 self.winsdb = None
127 self.private_dir = None
128 self.state_dir = None
131 class ProvisionNames(object):
133 def __init__(self):
134 self.rootdn = None
135 self.domaindn = None
136 self.configdn = None
137 self.schemadn = None
138 self.ldapmanagerdn = None
139 self.dnsdomain = None
140 self.realm = None
141 self.netbiosname = None
142 self.domain = None
143 self.hostname = None
144 self.sitename = None
145 self.smbconf = None
148 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
149 lp):
150 """Get key provision parameters (realm, domain, ...) from a given provision
152 :param samdb: An LDB object connected to the sam.ldb file
153 :param secretsdb: An LDB object connected to the secrets.ldb file
154 :param idmapdb: An LDB object connected to the idmap.ldb file
155 :param paths: A list of path to provision object
156 :param smbconf: Path to the smb.conf file
157 :param lp: A LoadParm object
158 :return: A list of key provision parameters
160 names = ProvisionNames()
161 names.adminpass = None
163 # NT domain, kerberos realm, root dn, domain dn, domain dns name
164 names.domain = string.upper(lp.get("workgroup"))
165 names.realm = lp.get("realm")
166 names.dnsdomain = names.realm.lower()
167 basedn = samba.dn_from_dns_name(names.dnsdomain)
168 names.realm = string.upper(names.realm)
169 # netbiosname
170 # Get the netbiosname first (could be obtained from smb.conf in theory)
171 res = secretsdb.search(expression="(flatname=%s)" %
172 names.domain,base="CN=Primary Domains",
173 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
174 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
176 names.smbconf = smbconf
178 # That's a bit simplistic but it's ok as long as we have only 3
179 # partitions
180 current = samdb.search(expression="(objectClass=*)",
181 base="", scope=ldb.SCOPE_BASE,
182 attrs=["defaultNamingContext", "schemaNamingContext",
183 "configurationNamingContext","rootDomainNamingContext"])
185 names.configdn = current[0]["configurationNamingContext"]
186 configdn = str(names.configdn)
187 names.schemadn = current[0]["schemaNamingContext"]
188 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
189 current[0]["defaultNamingContext"][0]))):
190 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
191 "is not the same ..." % (paths.samdb,
192 str(current[0]["defaultNamingContext"][0]),
193 paths.smbconf, basedn)))
195 names.domaindn=current[0]["defaultNamingContext"]
196 names.rootdn=current[0]["rootDomainNamingContext"]
197 # default site name
198 res3 = samdb.search(expression="(objectClass=site)",
199 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
200 names.sitename = str(res3[0]["cn"])
202 # dns hostname and server dn
203 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
204 base="OU=Domain Controllers,%s" % basedn,
205 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
206 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
208 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
209 attrs=[], base=configdn)
210 names.serverdn = server_res[0].dn
212 # invocation id/objectguid
213 res5 = samdb.search(expression="(objectClass=*)",
214 base="CN=NTDS Settings,%s" % str(names.serverdn),
215 scope=ldb.SCOPE_BASE,
216 attrs=["invocationID", "objectGUID"])
217 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
218 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
220 # domain guid/sid
221 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
222 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
223 "objectSid","msDS-Behavior-Version" ])
224 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
225 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
226 if res6[0].get("msDS-Behavior-Version") is None or \
227 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
228 names.domainlevel = DS_DOMAIN_FUNCTION_2000
229 else:
230 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
232 # policy guid
233 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
234 base="CN=Policies,CN=System," + basedn,
235 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
236 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
237 # dc policy guid
238 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
239 " Policy)",
240 base="CN=Policies,CN=System," + basedn,
241 scope=ldb.SCOPE_ONELEVEL,
242 attrs=["cn","displayName"])
243 if len(res8) == 1:
244 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
245 else:
246 names.policyid_dc = None
248 res9 = idmapdb.search(expression="(cn=%s-%s)" %
249 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
250 attrs=["xidNumber", "type"])
251 if len(res9) != 1:
252 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
253 if res9[0]["type"][0] == "ID_TYPE_BOTH":
254 names.root_gid = res9[0]["xidNumber"][0]
255 else:
256 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
257 return names
260 def update_provision_usn(samdb, low, high, id, replace=False):
261 """Update the field provisionUSN in sam.ldb
263 This field is used to track range of USN modified by provision and
264 upgradeprovision.
265 This value is used afterward by next provision to figure out if
266 the field have been modified since last provision.
268 :param samdb: An LDB object connect to sam.ldb
269 :param low: The lowest USN modified by this upgrade
270 :param high: The highest USN modified by this upgrade
271 :param id: The invocation id of the samba's dc
272 :param replace: A boolean indicating if the range should replace any
273 existing one or appended (default)
276 tab = []
277 if not replace:
278 entry = samdb.search(base="@PROVISION",
279 scope=ldb.SCOPE_BASE,
280 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
281 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
282 if not re.search(';', e):
283 e = "%s;%s" % (e, id)
284 tab.append(str(e))
286 tab.append("%s-%s;%s" % (low, high, id))
287 delta = ldb.Message()
288 delta.dn = ldb.Dn(samdb, "@PROVISION")
289 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
290 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
291 entry = samdb.search(expression='provisionnerID=*',
292 base="@PROVISION", scope=ldb.SCOPE_BASE,
293 attrs=["provisionnerID"])
294 if len(entry) == 0 or len(entry[0]) == 0:
295 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
296 samdb.modify(delta)
299 def set_provision_usn(samdb, low, high, id):
300 """Set the field provisionUSN in sam.ldb
301 This field is used to track range of USN modified by provision and
302 upgradeprovision.
303 This value is used afterward by next provision to figure out if
304 the field have been modified since last provision.
306 :param samdb: An LDB object connect to sam.ldb
307 :param low: The lowest USN modified by this upgrade
308 :param high: The highest USN modified by this upgrade
309 :param id: The invocationId of the provision"""
311 tab = []
312 tab.append("%s-%s;%s" % (low, high, id))
314 delta = ldb.Message()
315 delta.dn = ldb.Dn(samdb, "@PROVISION")
316 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
317 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
318 samdb.add(delta)
321 def get_max_usn(samdb,basedn):
322 """ This function return the biggest USN present in the provision
324 :param samdb: A LDB object pointing to the sam.ldb
325 :param basedn: A string containing the base DN of the provision
326 (ie. DC=foo, DC=bar)
327 :return: The biggest USN in the provision"""
329 res = samdb.search(expression="objectClass=*",base=basedn,
330 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
331 controls=["search_options:1:2",
332 "server_sort:1:1:uSNChanged",
333 "paged_results:1:1"])
334 return res[0]["uSNChanged"]
337 def get_last_provision_usn(sam):
338 """Get USNs ranges modified by a provision or an upgradeprovision
340 :param sam: An LDB object pointing to the sam.ldb
341 :return: a dictionary which keys are invocation id and values are an array
342 of integer representing the different ranges
344 try:
345 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
346 base="@PROVISION", scope=ldb.SCOPE_BASE,
347 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
348 except ldb.LdbError, (ecode, emsg):
349 if ecode == ldb.ERR_NO_SUCH_OBJECT:
350 return None
351 raise
352 if len(entry) > 0:
353 myids = []
354 range = {}
355 p = re.compile(r'-')
356 if entry[0].get("provisionnerID"):
357 for e in entry[0]["provisionnerID"]:
358 myids.append(str(e))
359 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
360 tab1 = str(r).split(';')
361 if len(tab1) == 2:
362 id = tab1[1]
363 else:
364 id = "default"
365 if (len(myids) > 0 and id not in myids):
366 continue
367 tab2 = p.split(tab1[0])
368 if range.get(id) is None:
369 range[id] = []
370 range[id].append(tab2[0])
371 range[id].append(tab2[1])
372 return range
373 else:
374 return None
377 class ProvisionResult(object):
378 """Result of a provision.
380 :ivar server_role: The server role
381 :ivar paths: ProvisionPaths instance
382 :ivar domaindn: The domain dn, as string
385 def __init__(self):
386 self.server_role = None
387 self.paths = None
388 self.domaindn = None
389 self.lp = None
390 self.samdb = None
391 self.idmap = None
392 self.names = None
393 self.domainsid = None
394 self.adminpass_generated = None
395 self.adminpass = None
396 self.backend_result = None
398 def report_logger(self, logger):
399 """Report this provision result to a logger."""
400 logger.info(
401 "Once the above files are installed, your Samba4 server will "
402 "be ready to use")
403 if self.adminpass_generated:
404 logger.info("Admin password: %s", self.adminpass)
405 logger.info("Server Role: %s", self.server_role)
406 logger.info("Hostname: %s", self.names.hostname)
407 logger.info("NetBIOS Domain: %s", self.names.domain)
408 logger.info("DNS Domain: %s", self.names.dnsdomain)
409 logger.info("DOMAIN SID: %s", self.domainsid)
411 if self.backend_result:
412 self.backend_result.report_logger(logger)
415 def check_install(lp, session_info, credentials):
416 """Check whether the current install seems ok.
418 :param lp: Loadparm context
419 :param session_info: Session information
420 :param credentials: Credentials
422 if lp.get("realm") == "":
423 raise Exception("Realm empty")
424 samdb = Ldb(lp.samdb_url(), session_info=session_info,
425 credentials=credentials, lp=lp)
426 if len(samdb.search("(cn=Administrator)")) != 1:
427 raise ProvisioningError("No administrator account found")
430 def findnss(nssfn, names):
431 """Find a user or group from a list of possibilities.
433 :param nssfn: NSS Function to try (should raise KeyError if not found)
434 :param names: Names to check.
435 :return: Value return by first names list.
437 for name in names:
438 try:
439 return nssfn(name)
440 except KeyError:
441 pass
442 raise KeyError("Unable to find user/group in %r" % names)
445 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
446 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
449 def provision_paths_from_lp(lp, dnsdomain):
450 """Set the default paths for provisioning.
452 :param lp: Loadparm context.
453 :param dnsdomain: DNS Domain name
455 paths = ProvisionPaths()
456 paths.private_dir = lp.get("private dir")
457 paths.state_dir = lp.get("state directory")
459 # This is stored without path prefix for the "privateKeytab" attribute in
460 # "secrets_dns.ldif".
461 paths.dns_keytab = "dns.keytab"
462 paths.keytab = "secrets.keytab"
464 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
465 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
466 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
467 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
468 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
469 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
470 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
471 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
472 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
473 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
474 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
475 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
476 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
477 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
478 paths.hklm = "hklm.ldb"
479 paths.hkcr = "hkcr.ldb"
480 paths.hkcu = "hkcu.ldb"
481 paths.hku = "hku.ldb"
482 paths.hkpd = "hkpd.ldb"
483 paths.hkpt = "hkpt.ldb"
484 paths.sysvol = lp.get("path", "sysvol")
485 paths.netlogon = lp.get("path", "netlogon")
486 paths.smbconf = lp.configfile
487 return paths
490 def determine_netbios_name(hostname):
491 """Determine a netbios name from a hostname."""
492 # remove forbidden chars and force the length to be <16
493 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
494 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
497 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
498 serverrole=None, rootdn=None, domaindn=None, configdn=None,
499 schemadn=None, serverdn=None, sitename=None):
500 """Guess configuration settings to use."""
502 if hostname is None:
503 hostname = socket.gethostname().split(".")[0]
505 netbiosname = lp.get("netbios name")
506 if netbiosname is None:
507 netbiosname = determine_netbios_name(hostname)
508 netbiosname = netbiosname.upper()
509 if not valid_netbios_name(netbiosname):
510 raise InvalidNetbiosName(netbiosname)
512 if dnsdomain is None:
513 dnsdomain = lp.get("realm")
514 if dnsdomain is None or dnsdomain == "":
515 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
517 dnsdomain = dnsdomain.lower()
519 if serverrole is None:
520 serverrole = lp.get("server role")
521 if serverrole is None:
522 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
524 serverrole = serverrole.lower()
526 realm = dnsdomain.upper()
528 if lp.get("realm") == "":
529 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
531 if lp.get("realm").upper() != realm:
532 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))
534 if lp.get("server role").lower() != serverrole:
535 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))
537 if serverrole == "active directory domain controller":
538 if domain is None:
539 # This will, for better or worse, default to 'WORKGROUP'
540 domain = lp.get("workgroup")
541 domain = domain.upper()
543 if lp.get("workgroup").upper() != domain:
544 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))
546 if domaindn is None:
547 domaindn = samba.dn_from_dns_name(dnsdomain)
549 if domain == netbiosname:
550 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
551 else:
552 domain = netbiosname
553 if domaindn is None:
554 domaindn = "DC=" + netbiosname
556 if not valid_netbios_name(domain):
557 raise InvalidNetbiosName(domain)
559 if hostname.upper() == realm:
560 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
561 if netbiosname.upper() == realm:
562 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
563 if domain == realm:
564 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
566 if rootdn is None:
567 rootdn = domaindn
569 if configdn is None:
570 configdn = "CN=Configuration," + rootdn
571 if schemadn is None:
572 schemadn = "CN=Schema," + configdn
574 if sitename is None:
575 sitename = DEFAULTSITE
577 names = ProvisionNames()
578 names.rootdn = rootdn
579 names.domaindn = domaindn
580 names.configdn = configdn
581 names.schemadn = schemadn
582 names.ldapmanagerdn = "CN=Manager," + rootdn
583 names.dnsdomain = dnsdomain
584 names.domain = domain
585 names.realm = realm
586 names.netbiosname = netbiosname
587 names.hostname = hostname
588 names.sitename = sitename
589 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
590 netbiosname, sitename, configdn)
592 return names
595 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
596 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
597 global_param=None):
598 """Create a new smb.conf file based on a couple of basic settings.
600 assert smbconf is not None
602 if hostname is None:
603 hostname = socket.gethostname().split(".")[0]
605 netbiosname = determine_netbios_name(hostname)
607 if serverrole is None:
608 serverrole = "standalone server"
610 assert domain is not None
611 domain = domain.upper()
613 assert realm is not None
614 realm = realm.upper()
616 global_settings = {
617 "netbios name": netbiosname,
618 "workgroup": domain,
619 "realm": realm,
620 "server role": serverrole,
623 if lp is None:
624 lp = samba.param.LoadParm()
625 #Load non-existent file
626 if os.path.exists(smbconf):
627 lp.load(smbconf)
629 if global_param is not None:
630 for ent in global_param:
631 if global_param[ent] is not None:
632 global_settings[ent] = " ".join(global_param[ent])
634 if targetdir is not None:
635 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
636 global_settings["lock dir"] = os.path.abspath(targetdir)
637 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
638 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
640 lp.set("lock dir", os.path.abspath(targetdir))
641 lp.set("state directory", global_settings["state directory"])
642 lp.set("cache directory", global_settings["cache directory"])
644 if eadb:
645 if use_ntvfs and not lp.get("posix:eadb"):
646 if targetdir is not None:
647 privdir = os.path.join(targetdir, "private")
648 else:
649 privdir = lp.get("private dir")
650 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
651 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
652 if targetdir is not None:
653 statedir = os.path.join(targetdir, "state")
654 else:
655 statedir = lp.get("state directory")
656 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
658 shares = {}
659 if serverrole == "active directory domain controller":
660 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
661 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
662 "scripts")
663 else:
664 global_settings["passdb backend"] = "samba_dsdb"
666 f = open(smbconf, 'w')
667 try:
668 f.write("[globals]\n")
669 for key, val in global_settings.iteritems():
670 f.write("\t%s = %s\n" % (key, val))
671 f.write("\n")
673 for name, path in shares.iteritems():
674 f.write("[%s]\n" % name)
675 f.write("\tpath = %s\n" % path)
676 f.write("\tread only = no\n")
677 f.write("\n")
678 finally:
679 f.close()
680 # reload the smb.conf
681 lp.load(smbconf)
683 # and dump it without any values that are the default
684 # this ensures that any smb.conf parameters that were set
685 # on the provision/join command line are set in the resulting smb.conf
686 f = open(smbconf, mode='w')
687 try:
688 lp.dump(f, False)
689 finally:
690 f.close()
693 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
694 users_gid, root_gid):
695 """setup reasonable name mappings for sam names to unix names.
697 :param samdb: SamDB object.
698 :param idmap: IDmap db object.
699 :param sid: The domain sid.
700 :param domaindn: The domain DN.
701 :param root_uid: uid of the UNIX root user.
702 :param nobody_uid: uid of the UNIX nobody user.
703 :param users_gid: gid of the UNIX users group.
704 :param root_gid: gid of the UNIX root group.
706 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
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, logger, domainsid, domainguid, policyguid,
1147 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1148 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1149 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1151 if next_rid is None:
1152 next_rid = 1000
1154 # Provision does not make much sense values larger than 1000000000
1155 # as the upper range of the rIDAvailablePool is 1073741823 and
1156 # we don't want to create a domain that cannot allocate rids.
1157 if next_rid < 1000 or next_rid > 1000000000:
1158 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1159 error += "the valid range is %u-%u. The default is %u." % (
1160 1000, 1000000000, 1000)
1161 raise ProvisioningError(error)
1163 # ATTENTION: Do NOT change these default values without discussion with the
1164 # team and/or release manager. They have a big impact on the whole program!
1165 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1167 if dom_for_fun_level is None:
1168 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1170 if dom_for_fun_level > domainControllerFunctionality:
1171 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!")
1173 domainFunctionality = dom_for_fun_level
1174 forestFunctionality = dom_for_fun_level
1176 # Set the NTDS settings DN manually - in order to have it already around
1177 # before the provisioned tree exists and we connect
1178 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1180 samdb.transaction_start()
1181 try:
1182 # Set the domain functionality levels onto the database.
1183 # Various module (the password_hash module in particular) need
1184 # to know what level of AD we are emulating.
1186 # These will be fixed into the database via the database
1187 # modifictions below, but we need them set from the start.
1188 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1189 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1190 samdb.set_opaque_integer("domainControllerFunctionality",
1191 domainControllerFunctionality)
1193 samdb.set_domain_sid(str(domainsid))
1194 samdb.set_invocation_id(invocationid)
1196 logger.info("Adding DomainDN: %s" % names.domaindn)
1198 # impersonate domain admin
1199 admin_session_info = admin_session(lp, str(domainsid))
1200 samdb.set_session_info(admin_session_info)
1201 if domainguid is not None:
1202 domainguid_line = "objectGUID: %s\n-" % domainguid
1203 else:
1204 domainguid_line = ""
1206 descr = b64encode(get_domain_descriptor(domainsid))
1207 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1208 "DOMAINDN": names.domaindn,
1209 "DOMAINSID": str(domainsid),
1210 "DESCRIPTOR": descr,
1211 "DOMAINGUID": domainguid_line
1214 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1215 "DOMAINDN": names.domaindn,
1216 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1217 "NEXTRID": str(next_rid),
1218 "DEFAULTSITE": names.sitename,
1219 "CONFIGDN": names.configdn,
1220 "POLICYGUID": policyguid,
1221 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1222 "SAMBA_VERSION_STRING": version
1225 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1226 if fill == FILL_FULL:
1227 logger.info("Adding configuration container")
1228 descr = b64encode(get_config_descriptor(domainsid))
1229 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1230 "CONFIGDN": names.configdn,
1231 "DESCRIPTOR": descr,
1234 # The LDIF here was created when the Schema object was constructed
1235 logger.info("Setting up sam.ldb schema")
1236 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1237 samdb.modify_ldif(schema.schema_dn_modify)
1238 samdb.write_prefixes_from_schema()
1239 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1240 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1241 {"SCHEMADN": names.schemadn})
1243 # Now register this container in the root of the forest
1244 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1245 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1246 "subRefs")
1248 except:
1249 samdb.transaction_cancel()
1250 raise
1251 else:
1252 samdb.transaction_commit()
1254 samdb.transaction_start()
1255 try:
1256 samdb.invocation_id = invocationid
1258 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1259 if fill == FILL_FULL:
1260 logger.info("Setting up sam.ldb configuration data")
1261 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1262 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1263 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1264 "CONFIGDN": names.configdn,
1265 "NETBIOSNAME": names.netbiosname,
1266 "DEFAULTSITE": names.sitename,
1267 "DNSDOMAIN": names.dnsdomain,
1268 "DOMAIN": names.domain,
1269 "SCHEMADN": names.schemadn,
1270 "DOMAINDN": names.domaindn,
1271 "SERVERDN": names.serverdn,
1272 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1273 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1274 "PARTITIONS_DESCRIPTOR": partitions_descr,
1275 "SITES_DESCRIPTOR": sites_descr,
1278 logger.info("Setting up display specifiers")
1279 display_specifiers_ldif = read_ms_ldif(
1280 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1281 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1282 {"CONFIGDN": names.configdn})
1283 check_all_substituted(display_specifiers_ldif)
1284 samdb.add_ldif(display_specifiers_ldif)
1286 logger.info("Adding users container")
1287 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1288 "DOMAINDN": names.domaindn})
1289 logger.info("Modifying users container")
1290 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1291 "DOMAINDN": names.domaindn})
1292 logger.info("Adding computers container")
1293 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1294 "DOMAINDN": names.domaindn})
1295 logger.info("Modifying computers container")
1296 setup_modify_ldif(samdb,
1297 setup_path("provision_computers_modify.ldif"), {
1298 "DOMAINDN": names.domaindn})
1299 logger.info("Setting up sam.ldb data")
1300 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
1301 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1302 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1303 "DOMAINDN": names.domaindn,
1304 "NETBIOSNAME": names.netbiosname,
1305 "DEFAULTSITE": names.sitename,
1306 "CONFIGDN": names.configdn,
1307 "SERVERDN": names.serverdn,
1308 "RIDAVAILABLESTART": str(next_rid + 600),
1309 "POLICYGUID_DC": policyguid_dc,
1310 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1313 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1314 if fill == FILL_FULL:
1315 setup_modify_ldif(samdb,
1316 setup_path("provision_configuration_references.ldif"), {
1317 "CONFIGDN": names.configdn,
1318 "SCHEMADN": names.schemadn})
1320 logger.info("Setting up well known security principals")
1321 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1322 "CONFIGDN": names.configdn,
1325 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1326 setup_modify_ldif(samdb,
1327 setup_path("provision_basedn_references.ldif"),
1328 {"DOMAINDN": names.domaindn})
1330 logger.info("Setting up sam.ldb users and groups")
1331 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1332 "DOMAINDN": names.domaindn,
1333 "DOMAINSID": str(domainsid),
1334 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1335 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1338 logger.info("Setting up self join")
1339 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1340 invocationid=invocationid,
1341 dns_backend=dns_backend,
1342 dnspass=dnspass,
1343 machinepass=machinepass,
1344 domainsid=domainsid,
1345 next_rid=next_rid,
1346 dc_rid=dc_rid,
1347 policyguid=policyguid,
1348 policyguid_dc=policyguid_dc,
1349 domainControllerFunctionality=domainControllerFunctionality,
1350 ntdsguid=ntdsguid)
1352 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1353 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1354 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1355 assert isinstance(names.ntdsguid, str)
1356 except:
1357 samdb.transaction_cancel()
1358 raise
1359 else:
1360 samdb.transaction_commit()
1361 return samdb
1364 FILL_FULL = "FULL"
1365 FILL_SUBDOMAIN = "SUBDOMAIN"
1366 FILL_NT4SYNC = "NT4SYNC"
1367 FILL_DRS = "DRS"
1368 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1369 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)"
1372 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb):
1373 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1374 for root, dirs, files in os.walk(path, topdown=False):
1375 for name in files:
1376 setntacl(lp, os.path.join(root, name), acl, domsid,
1377 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1378 for name in dirs:
1379 setntacl(lp, os.path.join(root, name), acl, domsid,
1380 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1383 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1384 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1385 folders beneath.
1387 :param sysvol: Physical path for the sysvol folder
1388 :param dnsdomain: The DNS name of the domain
1389 :param domainsid: The SID of the domain
1390 :param domaindn: The DN of the domain (ie. DC=...)
1391 :param samdb: An LDB object on the SAM db
1392 :param lp: an LP object
1395 # Set ACL for GPO root folder
1396 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1397 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1398 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1400 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1401 attrs=["cn", "nTSecurityDescriptor"],
1402 expression="", scope=ldb.SCOPE_ONELEVEL)
1404 for policy in res:
1405 acl = ndr_unpack(security.descriptor,
1406 str(policy["nTSecurityDescriptor"])).as_sddl()
1407 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1408 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1409 str(domainsid), use_ntvfs,
1410 passdb=passdb)
1413 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1414 domaindn, lp, use_ntvfs):
1415 """Set the ACL for the sysvol share and the subfolders
1417 :param samdb: An LDB object on the SAM db
1418 :param netlogon: Physical path for the netlogon folder
1419 :param sysvol: Physical path for the sysvol folder
1420 :param uid: The UID of the "Administrator" user
1421 :param gid: The GID of the "Domain adminstrators" group
1422 :param domainsid: The SID of the domain
1423 :param dnsdomain: The DNS name of the domain
1424 :param domaindn: The DN of the domain (ie. DC=...)
1426 s4_passdb = None
1428 if not use_ntvfs:
1429 # This will ensure that the smbd code we are running when setting ACLs
1430 # is initialised with the smb.conf
1431 s3conf = s3param.get_context()
1432 s3conf.load(lp.configfile)
1433 # ensure we are using the right samba_dsdb passdb backend, no matter what
1434 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1435 passdb.reload_static_pdb()
1437 # ensure that we init the samba_dsdb backend, so the domain sid is
1438 # marked in secrets.tdb
1439 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1441 # now ensure everything matches correctly, to avoid wierd issues
1442 if passdb.get_global_sam_sid() != domainsid:
1443 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))
1445 domain_info = s4_passdb.domain_info()
1446 if domain_info["dom_sid"] != domainsid:
1447 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))
1449 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1450 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()))
1453 try:
1454 if use_ntvfs:
1455 os.chown(sysvol, -1, gid)
1456 except OSError:
1457 canchown = False
1458 else:
1459 canchown = True
1461 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1462 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1463 for root, dirs, files in os.walk(sysvol, topdown=False):
1464 for name in files:
1465 if use_ntvfs and canchown:
1466 os.chown(os.path.join(root, name), -1, gid)
1467 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1468 for name in dirs:
1469 if use_ntvfs and canchown:
1470 os.chown(os.path.join(root, name), -1, gid)
1471 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1473 # Set acls on Policy folder and policies folders
1474 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1476 def acl_type(direct_db_access):
1477 if direct_db_access:
1478 return "DB"
1479 else:
1480 return "VFS"
1482 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1483 fsacl = getntacl(lp, path, direct_db_access=direct_db_access)
1484 fsacl_sddl = fsacl.as_sddl(domainsid)
1485 if fsacl_sddl != acl:
1486 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))
1488 for root, dirs, files in os.walk(path, topdown=False):
1489 for name in files:
1490 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1491 if fsacl is None:
1492 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1493 fsacl_sddl = fsacl.as_sddl(domainsid)
1494 if fsacl_sddl != acl:
1495 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))
1497 for name in dirs:
1498 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1499 if fsacl is None:
1500 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1501 fsacl_sddl = fsacl.as_sddl(domainsid)
1502 if fsacl_sddl != acl:
1503 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))
1506 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1507 direct_db_access):
1508 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1509 folders beneath.
1511 :param sysvol: Physical path for the sysvol folder
1512 :param dnsdomain: The DNS name of the domain
1513 :param domainsid: The SID of the domain
1514 :param domaindn: The DN of the domain (ie. DC=...)
1515 :param samdb: An LDB object on the SAM db
1516 :param lp: an LP object
1519 # Set ACL for GPO root folder
1520 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1521 fsacl = getntacl(lp, root_policy_path, direct_db_access=direct_db_access)
1522 if fsacl is None:
1523 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1524 fsacl_sddl = fsacl.as_sddl(domainsid)
1525 if fsacl_sddl != POLICIES_ACL:
1526 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))
1527 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1528 attrs=["cn", "nTSecurityDescriptor"],
1529 expression="", scope=ldb.SCOPE_ONELEVEL)
1531 for policy in res:
1532 acl = ndr_unpack(security.descriptor,
1533 str(policy["nTSecurityDescriptor"])).as_sddl()
1534 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1535 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1536 domainsid, direct_db_access)
1539 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1540 lp):
1541 """Set the ACL for the sysvol share and the subfolders
1543 :param samdb: An LDB object on the SAM db
1544 :param netlogon: Physical path for the netlogon folder
1545 :param sysvol: Physical path for the sysvol folder
1546 :param uid: The UID of the "Administrator" user
1547 :param gid: The GID of the "Domain adminstrators" group
1548 :param domainsid: The SID of the domain
1549 :param dnsdomain: The DNS name of the domain
1550 :param domaindn: The DN of the domain (ie. DC=...)
1553 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1554 s3conf = s3param.get_context()
1555 s3conf.load(lp.configfile)
1556 # ensure we are using the right samba_dsdb passdb backend, no matter what
1557 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1558 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1559 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1561 # now ensure everything matches correctly, to avoid wierd issues
1562 if passdb.get_global_sam_sid() != domainsid:
1563 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))
1565 domain_info = s4_passdb.domain_info()
1566 if domain_info["dom_sid"] != domainsid:
1567 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))
1569 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1570 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()))
1572 # Ensure we can read this directly, and via the smbd VFS
1573 for direct_db_access in [True, False]:
1574 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1575 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1576 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access)
1577 if fsacl is None:
1578 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1579 fsacl_sddl = fsacl.as_sddl(domainsid)
1580 if fsacl_sddl != SYSVOL_ACL:
1581 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))
1583 # Check acls on Policy folder and policies folders
1584 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1585 direct_db_access)
1588 def interface_ips_v4(lp):
1589 """return only IPv4 IPs"""
1590 ips = samba.interface_ips(lp, False)
1591 ret = []
1592 for i in ips:
1593 if i.find(':') == -1:
1594 ret.append(i)
1595 return ret
1598 def interface_ips_v6(lp, linklocal=False):
1599 """return only IPv6 IPs"""
1600 ips = samba.interface_ips(lp, False)
1601 ret = []
1602 for i in ips:
1603 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1604 ret.append(i)
1605 return ret
1608 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1609 domainsid, schema=None,
1610 targetdir=None, samdb_fill=FILL_FULL,
1611 hostip=None, hostip6=None,
1612 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1613 domainguid=None, policyguid=None, policyguid_dc=None,
1614 invocationid=None, machinepass=None, ntdsguid=None,
1615 dns_backend=None, dnspass=None,
1616 serverrole=None, dom_for_fun_level=None,
1617 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1618 # create/adapt the group policy GUIDs
1619 # Default GUID for default policy are described at
1620 # "How Core Group Policy Works"
1621 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1622 if policyguid is None:
1623 policyguid = DEFAULT_POLICY_GUID
1624 policyguid = policyguid.upper()
1625 if policyguid_dc is None:
1626 policyguid_dc = DEFAULT_DC_POLICY_GUID
1627 policyguid_dc = policyguid_dc.upper()
1629 if invocationid is None:
1630 invocationid = str(uuid.uuid4())
1632 if krbtgtpass is None:
1633 krbtgtpass = samba.generate_random_password(128, 255)
1634 if machinepass is None:
1635 machinepass = samba.generate_random_password(128, 255)
1636 if dnspass is None:
1637 dnspass = samba.generate_random_password(128, 255)
1639 samdb = fill_samdb(samdb, lp, names, logger=logger,
1640 domainsid=domainsid, schema=schema, domainguid=domainguid,
1641 policyguid=policyguid, policyguid_dc=policyguid_dc,
1642 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1643 invocationid=invocationid, machinepass=machinepass,
1644 dns_backend=dns_backend, dnspass=dnspass,
1645 ntdsguid=ntdsguid, serverrole=serverrole,
1646 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1647 next_rid=next_rid, dc_rid=dc_rid)
1649 if serverrole == "active directory domain controller":
1651 # Set up group policies (domain policy and domain controller
1652 # policy)
1653 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1654 policyguid_dc)
1655 if not skip_sysvolacl:
1656 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1657 paths.root_gid, domainsid, names.dnsdomain,
1658 names.domaindn, lp, use_ntvfs)
1659 else:
1660 logger.info("Setting acl on sysvol skipped")
1662 secretsdb_self_join(secrets_ldb, domain=names.domain,
1663 realm=names.realm, dnsdomain=names.dnsdomain,
1664 netbiosname=names.netbiosname, domainsid=domainsid,
1665 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1667 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1668 # In future, this might be determined from some configuration
1669 kerberos_enctypes = str(ENC_ALL_TYPES)
1671 try:
1672 msg = ldb.Message(ldb.Dn(samdb,
1673 samdb.searchone("distinguishedName",
1674 expression="samAccountName=%s$" % names.netbiosname,
1675 scope=ldb.SCOPE_SUBTREE)))
1676 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1677 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1678 name="msDS-SupportedEncryptionTypes")
1679 samdb.modify(msg)
1680 except ldb.LdbError, (enum, estr):
1681 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1682 # It might be that this attribute does not exist in this schema
1683 raise
1685 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1686 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1687 dnspass=dnspass, os_level=dom_for_fun_level,
1688 targetdir=targetdir, site=DEFAULTSITE)
1690 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1691 attribute="objectGUID")
1692 assert isinstance(domainguid, str)
1694 lastProvisionUSNs = get_last_provision_usn(samdb)
1695 maxUSN = get_max_usn(samdb, str(names.rootdn))
1696 if lastProvisionUSNs is not None:
1697 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1698 else:
1699 set_provision_usn(samdb, 0, maxUSN, invocationid)
1701 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1702 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1703 { 'NTDSGUID' : names.ntdsguid })
1705 # fix any dangling GUIDs from the provision
1706 logger.info("Fixing provision GUIDs")
1707 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1708 quiet=True)
1709 samdb.transaction_start()
1710 try:
1711 # a small number of GUIDs are missing because of ordering issues in the
1712 # provision code
1713 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1714 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1715 scope=ldb.SCOPE_BASE,
1716 attrs=['defaultObjectCategory'])
1717 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1718 scope=ldb.SCOPE_ONELEVEL,
1719 attrs=['ipsecOwnersReference',
1720 'ipsecFilterReference',
1721 'ipsecISAKMPReference',
1722 'ipsecNegotiationPolicyReference',
1723 'ipsecNFAReference'])
1724 except:
1725 samdb.transaction_cancel()
1726 raise
1727 else:
1728 samdb.transaction_commit()
1731 _ROLES_MAP = {
1732 "ROLE_STANDALONE": "standalone server",
1733 "ROLE_DOMAIN_MEMBER": "member server",
1734 "ROLE_DOMAIN_BDC": "active directory domain controller",
1735 "ROLE_DOMAIN_PDC": "active directory domain controller",
1736 "dc": "active directory domain controller",
1737 "member": "member server",
1738 "domain controller": "active directory domain controller",
1739 "active directory domain controller": "active directory domain controller",
1740 "member server": "member server",
1741 "standalone": "standalone server",
1742 "standalone server": "standalone server",
1746 def sanitize_server_role(role):
1747 """Sanitize a server role name.
1749 :param role: Server role
1750 :raise ValueError: If the role can not be interpreted
1751 :return: Sanitized server role (one of "member server",
1752 "active directory domain controller", "standalone server")
1754 try:
1755 return _ROLES_MAP[role]
1756 except KeyError:
1757 raise ValueError(role)
1760 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1761 maxuid, maxgid):
1762 """Create AD entries for the fake ypserver.
1764 This is needed for being able to manipulate posix attrs via ADUC.
1766 samdb.transaction_start()
1767 try:
1768 logger.info("Setting up fake yp server settings")
1769 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1770 "DOMAINDN": domaindn,
1771 "NETBIOSNAME": netbiosname,
1772 "NISDOMAIN": nisdomain,
1774 except:
1775 samdb.transaction_cancel()
1776 raise
1777 else:
1778 samdb.transaction_commit()
1781 def provision(logger, session_info, credentials, smbconf=None,
1782 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1783 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1784 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1785 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1786 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1787 dns_backend=None, dns_forwarder=None, dnspass=None,
1788 invocationid=None, machinepass=None, ntdsguid=None,
1789 root=None, nobody=None, users=None, backup=None, aci=None,
1790 serverrole=None, dom_for_fun_level=None, backend_type=None,
1791 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1792 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1793 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1794 """Provision samba4
1796 :note: caution, this wipes all existing data!
1799 try:
1800 serverrole = sanitize_server_role(serverrole)
1801 except ValueError:
1802 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1804 if ldapadminpass is None:
1805 # Make a new, random password between Samba and it's LDAP server
1806 ldapadminpass = samba.generate_random_password(128, 255)
1808 if backend_type is None:
1809 backend_type = "ldb"
1811 if domainsid is None:
1812 domainsid = security.random_sid()
1813 else:
1814 domainsid = security.dom_sid(domainsid)
1816 root_uid = findnss_uid([root or "root"])
1817 nobody_uid = findnss_uid([nobody or "nobody"])
1818 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1819 root_gid = pwd.getpwuid(root_uid).pw_gid
1821 try:
1822 bind_gid = findnss_gid(["bind", "named"])
1823 except KeyError:
1824 bind_gid = None
1826 if targetdir is not None:
1827 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1828 elif smbconf is None:
1829 smbconf = samba.param.default_path()
1830 if not os.path.exists(os.path.dirname(smbconf)):
1831 os.makedirs(os.path.dirname(smbconf))
1833 server_services = []
1834 global_param = {}
1835 if use_rfc2307:
1836 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1838 if dns_backend != "SAMBA_INTERNAL":
1839 server_services.append("-dns")
1840 else:
1841 if dns_forwarder is not None:
1842 global_param["dns forwarder"] = [dns_forwarder]
1844 if use_ntvfs:
1845 server_services.append("+smb")
1846 server_services.append("-s3fs")
1847 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1849 if len(server_services) > 0:
1850 global_param["server services"] = server_services
1852 # only install a new smb.conf if there isn't one there already
1853 if os.path.exists(smbconf):
1854 # if Samba Team members can't figure out the weird errors
1855 # loading an empty smb.conf gives, then we need to be smarter.
1856 # Pretend it just didn't exist --abartlet
1857 f = open(smbconf, 'r')
1858 try:
1859 data = f.read().lstrip()
1860 finally:
1861 f.close()
1862 if data is None or data == "":
1863 make_smbconf(smbconf, hostname, domain, realm,
1864 targetdir, serverrole=serverrole,
1865 eadb=useeadb, use_ntvfs=use_ntvfs,
1866 lp=lp, global_param=global_param)
1867 else:
1868 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1869 serverrole=serverrole,
1870 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1872 if lp is None:
1873 lp = samba.param.LoadParm()
1874 lp.load(smbconf)
1875 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1876 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1877 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1878 sitename=sitename, rootdn=rootdn)
1879 paths = provision_paths_from_lp(lp, names.dnsdomain)
1881 paths.bind_gid = bind_gid
1882 paths.root_uid = root_uid;
1883 paths.root_gid = root_gid
1885 if hostip is None:
1886 logger.info("Looking up IPv4 addresses")
1887 hostips = interface_ips_v4(lp)
1888 if len(hostips) > 0:
1889 hostip = hostips[0]
1890 if len(hostips) > 1:
1891 logger.warning("More than one IPv4 address found. Using %s",
1892 hostip)
1893 if hostip == "127.0.0.1":
1894 hostip = None
1895 if hostip is None:
1896 logger.warning("No IPv4 address will be assigned")
1898 if hostip6 is None:
1899 logger.info("Looking up IPv6 addresses")
1900 hostips = interface_ips_v6(lp, linklocal=False)
1901 if hostips:
1902 hostip6 = hostips[0]
1903 if len(hostips) > 1:
1904 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1905 if hostip6 is None:
1906 logger.warning("No IPv6 address will be assigned")
1908 names.hostip = hostip
1909 names.hostip6 = hostip6
1911 if serverrole is None:
1912 serverrole = lp.get("server role")
1914 if not os.path.exists(paths.private_dir):
1915 os.mkdir(paths.private_dir)
1916 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1917 os.mkdir(os.path.join(paths.private_dir, "tls"))
1918 if not os.path.exists(paths.state_dir):
1919 os.mkdir(paths.state_dir)
1921 if paths.sysvol and not os.path.exists(paths.sysvol):
1922 os.makedirs(paths.sysvol, 0775)
1924 if not use_ntvfs and serverrole == "active directory domain controller":
1925 s3conf = s3param.get_context()
1926 s3conf.load(lp.configfile)
1928 if paths.sysvol is None:
1929 raise MissingShareError("sysvol", paths.smbconf)
1931 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1932 try:
1933 try:
1934 smbd.set_simple_acl(file.name, 0755, root_gid)
1935 except Exception:
1936 if not smbd.have_posix_acls():
1937 # This clue is only strictly correct for RPM and
1938 # Debian-like Linux systems, but hopefully other users
1939 # will get enough clue from it.
1940 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.")
1942 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1943 try:
1944 smbd.chown(file.name, root_uid, root_gid)
1945 except Exception:
1946 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1947 finally:
1948 file.close()
1950 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1952 schema = Schema(domainsid, invocationid=invocationid,
1953 schemadn=names.schemadn)
1955 if backend_type == "ldb":
1956 provision_backend = LDBBackend(backend_type, paths=paths,
1957 lp=lp, credentials=credentials,
1958 names=names, logger=logger)
1959 elif backend_type == "existing":
1960 # If support for this is ever added back, then the URI will need to be
1961 # specified again
1962 provision_backend = ExistingBackend(backend_type, paths=paths,
1963 lp=lp, credentials=credentials,
1964 names=names, logger=logger,
1965 ldap_backend_forced_uri=None)
1966 elif backend_type == "fedora-ds":
1967 provision_backend = FDSBackend(backend_type, paths=paths,
1968 lp=lp, credentials=credentials,
1969 names=names, logger=logger, domainsid=domainsid,
1970 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1971 slapd_path=slapd_path,
1972 root=root)
1973 elif backend_type == "openldap":
1974 provision_backend = OpenLDAPBackend(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, ol_mmr_urls=ol_mmr_urls)
1979 else:
1980 raise ValueError("Unknown LDAP backend type selected")
1982 provision_backend.init()
1983 provision_backend.start()
1985 # only install a new shares config db if there is none
1986 if not os.path.exists(paths.shareconf):
1987 logger.info("Setting up share.ldb")
1988 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1989 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1991 logger.info("Setting up secrets.ldb")
1992 secrets_ldb = setup_secretsdb(paths,
1993 session_info=session_info,
1994 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1996 try:
1997 logger.info("Setting up the registry")
1998 setup_registry(paths.hklm, session_info, lp=lp)
2000 logger.info("Setting up the privileges database")
2001 setup_privileges(paths.privilege, session_info, lp=lp)
2003 logger.info("Setting up idmap db")
2004 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2006 setup_name_mappings(idmap, sid=str(domainsid),
2007 root_uid=root_uid, nobody_uid=nobody_uid,
2008 users_gid=users_gid, root_gid=root_gid)
2010 logger.info("Setting up SAM db")
2011 samdb = setup_samdb(paths.samdb, session_info,
2012 provision_backend, lp, names, logger=logger,
2013 serverrole=serverrole,
2014 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2016 if serverrole == "active directory domain controller":
2017 if paths.netlogon is None:
2018 raise MissingShareError("netlogon", paths.smbconf)
2020 if paths.sysvol is None:
2021 raise MissingShareError("sysvol", paths.smbconf)
2023 if not os.path.isdir(paths.netlogon):
2024 os.makedirs(paths.netlogon, 0755)
2026 if adminpass is None:
2027 adminpass = samba.generate_random_password(12, 32)
2028 adminpass_generated = True
2029 else:
2030 adminpass_generated = False
2032 if samdb_fill == FILL_FULL:
2033 provision_fill(samdb, secrets_ldb, logger, names, paths,
2034 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2035 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2036 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2037 krbtgtpass=krbtgtpass, domainguid=domainguid,
2038 policyguid=policyguid, policyguid_dc=policyguid_dc,
2039 invocationid=invocationid, machinepass=machinepass,
2040 ntdsguid=ntdsguid, dns_backend=dns_backend,
2041 dnspass=dnspass, serverrole=serverrole,
2042 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2043 lp=lp, use_ntvfs=use_ntvfs,
2044 skip_sysvolacl=skip_sysvolacl)
2046 create_krb5_conf(paths.krb5conf,
2047 dnsdomain=names.dnsdomain, hostname=names.hostname,
2048 realm=names.realm)
2049 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2050 "generated at %s", paths.krb5conf)
2052 if serverrole == "active directory domain controller":
2053 create_dns_update_list(lp, logger, paths)
2055 backend_result = provision_backend.post_setup()
2056 provision_backend.shutdown()
2058 except:
2059 secrets_ldb.transaction_cancel()
2060 raise
2062 # Now commit the secrets.ldb to disk
2063 secrets_ldb.transaction_commit()
2065 # the commit creates the dns.keytab, now chown it
2066 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2067 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2068 try:
2069 os.chmod(dns_keytab_path, 0640)
2070 os.chown(dns_keytab_path, -1, paths.bind_gid)
2071 except OSError:
2072 if not os.environ.has_key('SAMBA_SELFTEST'):
2073 logger.info("Failed to chown %s to bind gid %u",
2074 dns_keytab_path, paths.bind_gid)
2076 result = ProvisionResult()
2077 result.server_role = serverrole
2078 result.domaindn = domaindn
2079 result.paths = paths
2080 result.names = names
2081 result.lp = lp
2082 result.samdb = samdb
2083 result.idmap = idmap
2084 result.domainsid = str(domainsid)
2086 if samdb_fill == FILL_FULL:
2087 result.adminpass_generated = adminpass_generated
2088 result.adminpass = adminpass
2089 else:
2090 result.adminpass_generated = False
2091 result.adminpass = None
2093 result.backend_result = backend_result
2095 if use_rfc2307:
2096 provision_fake_ypserver(logger=logger, samdb=samdb,
2097 domaindn=names.domaindn, netbiosname=names.netbiosname,
2098 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2100 return result
2103 def provision_become_dc(smbconf=None, targetdir=None,
2104 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2105 serverdn=None, domain=None, hostname=None, domainsid=None,
2106 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2107 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2108 dns_backend=None, root=None, nobody=None, users=None,
2109 backup=None, serverrole=None, ldap_backend=None,
2110 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2112 logger = logging.getLogger("provision")
2113 samba.set_debug_level(debuglevel)
2115 res = provision(logger, system_session(), None,
2116 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2117 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2118 configdn=configdn, serverdn=serverdn, domain=domain,
2119 hostname=hostname, hostip=None, domainsid=domainsid,
2120 machinepass=machinepass,
2121 serverrole="active directory domain controller",
2122 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2123 use_ntvfs=use_ntvfs)
2124 res.lp.set("debuglevel", str(debuglevel))
2125 return res
2128 def create_krb5_conf(path, dnsdomain, hostname, realm):
2129 """Write out a file containing zone statements suitable for inclusion in a
2130 named.conf file (including GSS-TSIG configuration).
2132 :param path: Path of the new named.conf file.
2133 :param dnsdomain: DNS Domain name
2134 :param hostname: Local hostname
2135 :param realm: Realm name
2137 setup_file(setup_path("krb5.conf"), path, {
2138 "DNSDOMAIN": dnsdomain,
2139 "HOSTNAME": hostname,
2140 "REALM": realm,
2144 class ProvisioningError(Exception):
2145 """A generic provision error."""
2147 def __init__(self, value):
2148 self.value = value
2150 def __str__(self):
2151 return "ProvisioningError: " + self.value
2154 class InvalidNetbiosName(Exception):
2155 """A specified name was not a valid NetBIOS name."""
2157 def __init__(self, name):
2158 super(InvalidNetbiosName, self).__init__(
2159 "The name '%r' is not a valid NetBIOS name" % name)
2162 class MissingShareError(ProvisioningError):
2164 def __init__(self, name, smbconf):
2165 super(MissingShareError, self).__init__(
2166 "Existing smb.conf does not have a [%s] share, but you are "
2167 "configuring a DC. Please remove %s or add the share manually." %
2168 (name, smbconf))