s4:provision: set the correct nTSecurityDescriptor on CN=Users,... (bug #9481)
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision / __init__.py
blobc5a8b397ab7d82aa3cab1fa5a3f16cc7115ac805
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,
88 get_domain_users_descriptor,
90 from samba.provision.common import (
91 setup_path,
92 setup_add_ldif,
93 setup_modify_ldif,
95 from samba.provision.sambadns import (
96 setup_ad_dns,
97 create_dns_update_list
100 import samba.param
101 import samba.registry
102 from samba.schema import Schema
103 from samba.samdb import SamDB
104 from samba.dbchecker import dbcheck
107 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
108 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
109 DEFAULTSITE = "Default-First-Site-Name"
110 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
113 class ProvisionPaths(object):
115 def __init__(self):
116 self.shareconf = None
117 self.hklm = None
118 self.hkcu = None
119 self.hkcr = None
120 self.hku = None
121 self.hkpd = None
122 self.hkpt = None
123 self.samdb = None
124 self.idmapdb = None
125 self.secrets = None
126 self.keytab = None
127 self.dns_keytab = None
128 self.dns = None
129 self.winsdb = None
130 self.private_dir = None
131 self.state_dir = None
134 class ProvisionNames(object):
136 def __init__(self):
137 self.rootdn = None
138 self.domaindn = None
139 self.configdn = None
140 self.schemadn = None
141 self.ldapmanagerdn = None
142 self.dnsdomain = None
143 self.realm = None
144 self.netbiosname = None
145 self.domain = None
146 self.hostname = None
147 self.sitename = None
148 self.smbconf = None
151 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
152 lp):
153 """Get key provision parameters (realm, domain, ...) from a given provision
155 :param samdb: An LDB object connected to the sam.ldb file
156 :param secretsdb: An LDB object connected to the secrets.ldb file
157 :param idmapdb: An LDB object connected to the idmap.ldb file
158 :param paths: A list of path to provision object
159 :param smbconf: Path to the smb.conf file
160 :param lp: A LoadParm object
161 :return: A list of key provision parameters
163 names = ProvisionNames()
164 names.adminpass = None
166 # NT domain, kerberos realm, root dn, domain dn, domain dns name
167 names.domain = string.upper(lp.get("workgroup"))
168 names.realm = lp.get("realm")
169 names.dnsdomain = names.realm.lower()
170 basedn = samba.dn_from_dns_name(names.dnsdomain)
171 names.realm = string.upper(names.realm)
172 # netbiosname
173 # Get the netbiosname first (could be obtained from smb.conf in theory)
174 res = secretsdb.search(expression="(flatname=%s)" %
175 names.domain,base="CN=Primary Domains",
176 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
177 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
179 names.smbconf = smbconf
181 # That's a bit simplistic but it's ok as long as we have only 3
182 # partitions
183 current = samdb.search(expression="(objectClass=*)",
184 base="", scope=ldb.SCOPE_BASE,
185 attrs=["defaultNamingContext", "schemaNamingContext",
186 "configurationNamingContext","rootDomainNamingContext"])
188 names.configdn = current[0]["configurationNamingContext"]
189 configdn = str(names.configdn)
190 names.schemadn = current[0]["schemaNamingContext"]
191 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
192 current[0]["defaultNamingContext"][0]))):
193 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
194 "is not the same ..." % (paths.samdb,
195 str(current[0]["defaultNamingContext"][0]),
196 paths.smbconf, basedn)))
198 names.domaindn=current[0]["defaultNamingContext"]
199 names.rootdn=current[0]["rootDomainNamingContext"]
200 # default site name
201 res3 = samdb.search(expression="(objectClass=site)",
202 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
203 names.sitename = str(res3[0]["cn"])
205 # dns hostname and server dn
206 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
207 base="OU=Domain Controllers,%s" % basedn,
208 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
209 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
211 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
212 attrs=[], base=configdn)
213 names.serverdn = server_res[0].dn
215 # invocation id/objectguid
216 res5 = samdb.search(expression="(objectClass=*)",
217 base="CN=NTDS Settings,%s" % str(names.serverdn),
218 scope=ldb.SCOPE_BASE,
219 attrs=["invocationID", "objectGUID"])
220 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
221 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
223 # domain guid/sid
224 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
225 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
226 "objectSid","msDS-Behavior-Version" ])
227 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
228 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
229 if res6[0].get("msDS-Behavior-Version") is None or \
230 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
231 names.domainlevel = DS_DOMAIN_FUNCTION_2000
232 else:
233 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
235 # policy guid
236 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
237 base="CN=Policies,CN=System," + basedn,
238 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
239 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
240 # dc policy guid
241 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
242 " Policy)",
243 base="CN=Policies,CN=System," + basedn,
244 scope=ldb.SCOPE_ONELEVEL,
245 attrs=["cn","displayName"])
246 if len(res8) == 1:
247 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
248 else:
249 names.policyid_dc = None
251 res9 = idmapdb.search(expression="(cn=%s-%s)" %
252 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
253 attrs=["xidNumber", "type"])
254 if len(res9) != 1:
255 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
256 if res9[0]["type"][0] == "ID_TYPE_BOTH":
257 names.root_gid = res9[0]["xidNumber"][0]
258 else:
259 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
260 return names
263 def update_provision_usn(samdb, low, high, id, replace=False):
264 """Update the field provisionUSN in sam.ldb
266 This field is used to track range of USN modified by provision and
267 upgradeprovision.
268 This value is used afterward by next provision to figure out if
269 the field have been modified since last provision.
271 :param samdb: An LDB object connect to sam.ldb
272 :param low: The lowest USN modified by this upgrade
273 :param high: The highest USN modified by this upgrade
274 :param id: The invocation id of the samba's dc
275 :param replace: A boolean indicating if the range should replace any
276 existing one or appended (default)
279 tab = []
280 if not replace:
281 entry = samdb.search(base="@PROVISION",
282 scope=ldb.SCOPE_BASE,
283 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
284 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
285 if not re.search(';', e):
286 e = "%s;%s" % (e, id)
287 tab.append(str(e))
289 tab.append("%s-%s;%s" % (low, high, id))
290 delta = ldb.Message()
291 delta.dn = ldb.Dn(samdb, "@PROVISION")
292 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
293 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
294 entry = samdb.search(expression='provisionnerID=*',
295 base="@PROVISION", scope=ldb.SCOPE_BASE,
296 attrs=["provisionnerID"])
297 if len(entry) == 0 or len(entry[0]) == 0:
298 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
299 samdb.modify(delta)
302 def set_provision_usn(samdb, low, high, id):
303 """Set the field provisionUSN in sam.ldb
304 This field is used to track range of USN modified by provision and
305 upgradeprovision.
306 This value is used afterward by next provision to figure out if
307 the field have been modified since last provision.
309 :param samdb: An LDB object connect to sam.ldb
310 :param low: The lowest USN modified by this upgrade
311 :param high: The highest USN modified by this upgrade
312 :param id: The invocationId of the provision"""
314 tab = []
315 tab.append("%s-%s;%s" % (low, high, id))
317 delta = ldb.Message()
318 delta.dn = ldb.Dn(samdb, "@PROVISION")
319 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
320 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
321 samdb.add(delta)
324 def get_max_usn(samdb,basedn):
325 """ This function return the biggest USN present in the provision
327 :param samdb: A LDB object pointing to the sam.ldb
328 :param basedn: A string containing the base DN of the provision
329 (ie. DC=foo, DC=bar)
330 :return: The biggest USN in the provision"""
332 res = samdb.search(expression="objectClass=*",base=basedn,
333 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
334 controls=["search_options:1:2",
335 "server_sort:1:1:uSNChanged",
336 "paged_results:1:1"])
337 return res[0]["uSNChanged"]
340 def get_last_provision_usn(sam):
341 """Get USNs ranges modified by a provision or an upgradeprovision
343 :param sam: An LDB object pointing to the sam.ldb
344 :return: a dictionary which keys are invocation id and values are an array
345 of integer representing the different ranges
347 try:
348 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
349 base="@PROVISION", scope=ldb.SCOPE_BASE,
350 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
351 except ldb.LdbError, (ecode, emsg):
352 if ecode == ldb.ERR_NO_SUCH_OBJECT:
353 return None
354 raise
355 if len(entry) > 0:
356 myids = []
357 range = {}
358 p = re.compile(r'-')
359 if entry[0].get("provisionnerID"):
360 for e in entry[0]["provisionnerID"]:
361 myids.append(str(e))
362 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
363 tab1 = str(r).split(';')
364 if len(tab1) == 2:
365 id = tab1[1]
366 else:
367 id = "default"
368 if (len(myids) > 0 and id not in myids):
369 continue
370 tab2 = p.split(tab1[0])
371 if range.get(id) is None:
372 range[id] = []
373 range[id].append(tab2[0])
374 range[id].append(tab2[1])
375 return range
376 else:
377 return None
380 class ProvisionResult(object):
381 """Result of a provision.
383 :ivar server_role: The server role
384 :ivar paths: ProvisionPaths instance
385 :ivar domaindn: The domain dn, as string
388 def __init__(self):
389 self.server_role = None
390 self.paths = None
391 self.domaindn = None
392 self.lp = None
393 self.samdb = None
394 self.idmap = None
395 self.names = None
396 self.domainsid = None
397 self.adminpass_generated = None
398 self.adminpass = None
399 self.backend_result = None
401 def report_logger(self, logger):
402 """Report this provision result to a logger."""
403 logger.info(
404 "Once the above files are installed, your Samba4 server will "
405 "be ready to use")
406 if self.adminpass_generated:
407 logger.info("Admin password: %s", self.adminpass)
408 logger.info("Server Role: %s", self.server_role)
409 logger.info("Hostname: %s", self.names.hostname)
410 logger.info("NetBIOS Domain: %s", self.names.domain)
411 logger.info("DNS Domain: %s", self.names.dnsdomain)
412 logger.info("DOMAIN SID: %s", self.domainsid)
414 if self.backend_result:
415 self.backend_result.report_logger(logger)
418 def check_install(lp, session_info, credentials):
419 """Check whether the current install seems ok.
421 :param lp: Loadparm context
422 :param session_info: Session information
423 :param credentials: Credentials
425 if lp.get("realm") == "":
426 raise Exception("Realm empty")
427 samdb = Ldb(lp.samdb_url(), session_info=session_info,
428 credentials=credentials, lp=lp)
429 if len(samdb.search("(cn=Administrator)")) != 1:
430 raise ProvisioningError("No administrator account found")
433 def findnss(nssfn, names):
434 """Find a user or group from a list of possibilities.
436 :param nssfn: NSS Function to try (should raise KeyError if not found)
437 :param names: Names to check.
438 :return: Value return by first names list.
440 for name in names:
441 try:
442 return nssfn(name)
443 except KeyError:
444 pass
445 raise KeyError("Unable to find user/group in %r" % names)
448 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
449 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
452 def provision_paths_from_lp(lp, dnsdomain):
453 """Set the default paths for provisioning.
455 :param lp: Loadparm context.
456 :param dnsdomain: DNS Domain name
458 paths = ProvisionPaths()
459 paths.private_dir = lp.get("private dir")
460 paths.state_dir = lp.get("state directory")
462 # This is stored without path prefix for the "privateKeytab" attribute in
463 # "secrets_dns.ldif".
464 paths.dns_keytab = "dns.keytab"
465 paths.keytab = "secrets.keytab"
467 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
468 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
469 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
470 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
471 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
472 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
473 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
474 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
475 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
476 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
477 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
478 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
479 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
480 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
481 paths.hklm = "hklm.ldb"
482 paths.hkcr = "hkcr.ldb"
483 paths.hkcu = "hkcu.ldb"
484 paths.hku = "hku.ldb"
485 paths.hkpd = "hkpd.ldb"
486 paths.hkpt = "hkpt.ldb"
487 paths.sysvol = lp.get("path", "sysvol")
488 paths.netlogon = lp.get("path", "netlogon")
489 paths.smbconf = lp.configfile
490 return paths
493 def determine_netbios_name(hostname):
494 """Determine a netbios name from a hostname."""
495 # remove forbidden chars and force the length to be <16
496 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
497 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
500 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
501 serverrole=None, rootdn=None, domaindn=None, configdn=None,
502 schemadn=None, serverdn=None, sitename=None):
503 """Guess configuration settings to use."""
505 if hostname is None:
506 hostname = socket.gethostname().split(".")[0]
508 netbiosname = lp.get("netbios name")
509 if netbiosname is None:
510 netbiosname = determine_netbios_name(hostname)
511 netbiosname = netbiosname.upper()
512 if not valid_netbios_name(netbiosname):
513 raise InvalidNetbiosName(netbiosname)
515 if dnsdomain is None:
516 dnsdomain = lp.get("realm")
517 if dnsdomain is None or dnsdomain == "":
518 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
520 dnsdomain = dnsdomain.lower()
522 if serverrole is None:
523 serverrole = lp.get("server role")
524 if serverrole is None:
525 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
527 serverrole = serverrole.lower()
529 realm = dnsdomain.upper()
531 if lp.get("realm") == "":
532 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
534 if lp.get("realm").upper() != realm:
535 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))
537 if lp.get("server role").lower() != serverrole:
538 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))
540 if serverrole == "active directory domain controller":
541 if domain is None:
542 # This will, for better or worse, default to 'WORKGROUP'
543 domain = lp.get("workgroup")
544 domain = domain.upper()
546 if lp.get("workgroup").upper() != domain:
547 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))
549 if domaindn is None:
550 domaindn = samba.dn_from_dns_name(dnsdomain)
552 if domain == netbiosname:
553 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
554 else:
555 domain = netbiosname
556 if domaindn is None:
557 domaindn = "DC=" + netbiosname
559 if not valid_netbios_name(domain):
560 raise InvalidNetbiosName(domain)
562 if hostname.upper() == realm:
563 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
564 if netbiosname.upper() == realm:
565 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
566 if domain == realm:
567 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
569 if rootdn is None:
570 rootdn = domaindn
572 if configdn is None:
573 configdn = "CN=Configuration," + rootdn
574 if schemadn is None:
575 schemadn = "CN=Schema," + configdn
577 if sitename is None:
578 sitename = DEFAULTSITE
580 names = ProvisionNames()
581 names.rootdn = rootdn
582 names.domaindn = domaindn
583 names.configdn = configdn
584 names.schemadn = schemadn
585 names.ldapmanagerdn = "CN=Manager," + rootdn
586 names.dnsdomain = dnsdomain
587 names.domain = domain
588 names.realm = realm
589 names.netbiosname = netbiosname
590 names.hostname = hostname
591 names.sitename = sitename
592 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
593 netbiosname, sitename, configdn)
595 return names
598 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
599 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
600 global_param=None):
601 """Create a new smb.conf file based on a couple of basic settings.
603 assert smbconf is not None
605 if hostname is None:
606 hostname = socket.gethostname().split(".")[0]
608 netbiosname = determine_netbios_name(hostname)
610 if serverrole is None:
611 serverrole = "standalone server"
613 assert domain is not None
614 domain = domain.upper()
616 assert realm is not None
617 realm = realm.upper()
619 global_settings = {
620 "netbios name": netbiosname,
621 "workgroup": domain,
622 "realm": realm,
623 "server role": serverrole,
626 if lp is None:
627 lp = samba.param.LoadParm()
628 #Load non-existent file
629 if os.path.exists(smbconf):
630 lp.load(smbconf)
632 if global_param is not None:
633 for ent in global_param:
634 if global_param[ent] is not None:
635 global_settings[ent] = " ".join(global_param[ent])
637 if targetdir is not None:
638 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
639 global_settings["lock dir"] = os.path.abspath(targetdir)
640 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
641 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
643 lp.set("lock dir", os.path.abspath(targetdir))
644 lp.set("state directory", global_settings["state directory"])
645 lp.set("cache directory", global_settings["cache directory"])
647 if eadb:
648 if use_ntvfs and not lp.get("posix:eadb"):
649 if targetdir is not None:
650 privdir = os.path.join(targetdir, "private")
651 else:
652 privdir = lp.get("private dir")
653 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
654 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
655 if targetdir is not None:
656 statedir = os.path.join(targetdir, "state")
657 else:
658 statedir = lp.get("state directory")
659 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
661 shares = {}
662 if serverrole == "active directory domain controller":
663 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
664 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
665 "scripts")
666 else:
667 global_settings["passdb backend"] = "samba_dsdb"
669 f = open(smbconf, 'w')
670 try:
671 f.write("[globals]\n")
672 for key, val in global_settings.iteritems():
673 f.write("\t%s = %s\n" % (key, val))
674 f.write("\n")
676 for name, path in shares.iteritems():
677 f.write("[%s]\n" % name)
678 f.write("\tpath = %s\n" % path)
679 f.write("\tread only = no\n")
680 f.write("\n")
681 finally:
682 f.close()
683 # reload the smb.conf
684 lp.load(smbconf)
686 # and dump it without any values that are the default
687 # this ensures that any smb.conf parameters that were set
688 # on the provision/join command line are set in the resulting smb.conf
689 f = open(smbconf, mode='w')
690 try:
691 lp.dump(f, False)
692 finally:
693 f.close()
696 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
697 users_gid, root_gid):
698 """setup reasonable name mappings for sam names to unix names.
700 :param samdb: SamDB object.
701 :param idmap: IDmap db object.
702 :param sid: The domain sid.
703 :param domaindn: The domain DN.
704 :param root_uid: uid of the UNIX root user.
705 :param nobody_uid: uid of the UNIX nobody user.
706 :param users_gid: gid of the UNIX users group.
707 :param root_gid: gid of the UNIX root group.
709 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
711 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
712 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
715 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
716 provision_backend, names, schema, serverrole,
717 erase=False):
718 """Setup the partitions for the SAM database.
720 Alternatively, provision() may call this, and then populate the database.
722 :note: This will wipe the Sam Database!
724 :note: This function always removes the local SAM LDB file. The erase
725 parameter controls whether to erase the existing data, which
726 may not be stored locally but in LDAP.
729 assert session_info is not None
731 # We use options=["modules:"] to stop the modules loading - we
732 # just want to wipe and re-initialise the database, not start it up
734 try:
735 os.unlink(samdb_path)
736 except OSError:
737 pass
739 samdb = Ldb(url=samdb_path, session_info=session_info,
740 lp=lp, options=["modules:"])
742 ldap_backend_line = "# No LDAP backend"
743 if provision_backend.type != "ldb":
744 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
746 samdb.transaction_start()
747 try:
748 logger.info("Setting up sam.ldb partitions and settings")
749 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
750 "LDAP_BACKEND_LINE": ldap_backend_line
754 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
755 "BACKEND_TYPE": provision_backend.type,
756 "SERVER_ROLE": serverrole
759 logger.info("Setting up sam.ldb rootDSE")
760 setup_samdb_rootdse(samdb, names)
761 except:
762 samdb.transaction_cancel()
763 raise
764 else:
765 samdb.transaction_commit()
768 def secretsdb_self_join(secretsdb, domain,
769 netbiosname, machinepass, domainsid=None,
770 realm=None, dnsdomain=None,
771 keytab_path=None,
772 key_version_number=1,
773 secure_channel_type=SEC_CHAN_WKSTA):
774 """Add domain join-specific bits to a secrets database.
776 :param secretsdb: Ldb Handle to the secrets database
777 :param machinepass: Machine password
779 attrs = ["whenChanged",
780 "secret",
781 "priorSecret",
782 "priorChanged",
783 "krb5Keytab",
784 "privateKeytab"]
786 if realm is not None:
787 if dnsdomain is None:
788 dnsdomain = realm.lower()
789 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
790 else:
791 dnsname = None
792 shortname = netbiosname.lower()
794 # We don't need to set msg["flatname"] here, because rdn_name will handle
795 # it, and it causes problems for modifies anyway
796 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
797 msg["secureChannelType"] = [str(secure_channel_type)]
798 msg["objectClass"] = ["top", "primaryDomain"]
799 if dnsname is not None:
800 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
801 msg["realm"] = [realm]
802 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
803 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
804 msg["privateKeytab"] = ["secrets.keytab"]
806 msg["secret"] = [machinepass]
807 msg["samAccountName"] = ["%s$" % netbiosname]
808 msg["secureChannelType"] = [str(secure_channel_type)]
809 if domainsid is not None:
810 msg["objectSid"] = [ndr_pack(domainsid)]
812 # This complex expression tries to ensure that we don't have more
813 # than one record for this SID, realm or netbios domain at a time,
814 # but we don't delete the old record that we are about to modify,
815 # because that would delete the keytab and previous password.
816 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
817 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
818 scope=ldb.SCOPE_ONELEVEL)
820 for del_msg in res:
821 secretsdb.delete(del_msg.dn)
823 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
825 if len(res) == 1:
826 msg["priorSecret"] = [res[0]["secret"][0]]
827 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
829 try:
830 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
831 except KeyError:
832 pass
834 try:
835 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
836 except KeyError:
837 pass
839 for el in msg:
840 if el != 'dn':
841 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
842 secretsdb.modify(msg)
843 secretsdb.rename(res[0].dn, msg.dn)
844 else:
845 spn = [ 'HOST/%s' % shortname ]
846 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
847 # we are a domain controller then we add servicePrincipalName
848 # entries for the keytab code to update.
849 spn.extend([ 'HOST/%s' % dnsname ])
850 msg["servicePrincipalName"] = spn
852 secretsdb.add(msg)
855 def setup_secretsdb(paths, session_info, backend_credentials, lp):
856 """Setup the secrets database.
858 :note: This function does not handle exceptions and transaction on purpose,
859 it's up to the caller to do this job.
861 :param path: Path to the secrets database.
862 :param session_info: Session info.
863 :param credentials: Credentials
864 :param lp: Loadparm context
865 :return: LDB handle for the created secrets database
867 if os.path.exists(paths.secrets):
868 os.unlink(paths.secrets)
870 keytab_path = os.path.join(paths.private_dir, paths.keytab)
871 if os.path.exists(keytab_path):
872 os.unlink(keytab_path)
874 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
875 if os.path.exists(dns_keytab_path):
876 os.unlink(dns_keytab_path)
878 path = paths.secrets
880 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
881 secrets_ldb.erase()
882 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
883 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
884 secrets_ldb.transaction_start()
885 try:
886 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
888 if (backend_credentials is not None and
889 backend_credentials.authentication_requested()):
890 if backend_credentials.get_bind_dn() is not None:
891 setup_add_ldif(secrets_ldb,
892 setup_path("secrets_simple_ldap.ldif"), {
893 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
894 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
896 else:
897 setup_add_ldif(secrets_ldb,
898 setup_path("secrets_sasl_ldap.ldif"), {
899 "LDAPADMINUSER": backend_credentials.get_username(),
900 "LDAPADMINREALM": backend_credentials.get_realm(),
901 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
903 except:
904 secrets_ldb.transaction_cancel()
905 raise
906 return secrets_ldb
909 def setup_privileges(path, session_info, lp):
910 """Setup the privileges database.
912 :param path: Path to the privileges database.
913 :param session_info: Session info.
914 :param credentials: Credentials
915 :param lp: Loadparm context
916 :return: LDB handle for the created secrets database
918 if os.path.exists(path):
919 os.unlink(path)
920 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
921 privilege_ldb.erase()
922 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
925 def setup_registry(path, session_info, lp):
926 """Setup the registry.
928 :param path: Path to the registry database
929 :param session_info: Session information
930 :param credentials: Credentials
931 :param lp: Loadparm context
933 reg = samba.registry.Registry()
934 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
935 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
936 provision_reg = setup_path("provision.reg")
937 assert os.path.exists(provision_reg)
938 reg.diff_apply(provision_reg)
941 def setup_idmapdb(path, session_info, lp):
942 """Setup the idmap database.
944 :param path: path to the idmap database
945 :param session_info: Session information
946 :param credentials: Credentials
947 :param lp: Loadparm context
949 if os.path.exists(path):
950 os.unlink(path)
952 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
953 idmap_ldb.erase()
954 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
955 return idmap_ldb
958 def setup_samdb_rootdse(samdb, names):
959 """Setup the SamDB rootdse.
961 :param samdb: Sam Database handle
963 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
964 "SCHEMADN": names.schemadn,
965 "DOMAINDN": names.domaindn,
966 "ROOTDN" : names.rootdn,
967 "CONFIGDN": names.configdn,
968 "SERVERDN": names.serverdn,
972 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
973 dns_backend, dnspass, domainsid, next_rid, invocationid,
974 policyguid, policyguid_dc,
975 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
976 """Join a host to its own domain."""
977 assert isinstance(invocationid, str)
978 if ntdsguid is not None:
979 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
980 else:
981 ntdsguid_line = ""
983 if dc_rid is None:
984 dc_rid = next_rid
986 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
987 "CONFIGDN": names.configdn,
988 "SCHEMADN": names.schemadn,
989 "DOMAINDN": names.domaindn,
990 "SERVERDN": names.serverdn,
991 "INVOCATIONID": invocationid,
992 "NETBIOSNAME": names.netbiosname,
993 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
994 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
995 "DOMAINSID": str(domainsid),
996 "DCRID": str(dc_rid),
997 "SAMBA_VERSION_STRING": version,
998 "NTDSGUID": ntdsguid_line,
999 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1000 domainControllerFunctionality),
1001 "RIDALLOCATIONSTART": str(next_rid + 100),
1002 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1004 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1005 "POLICYGUID": policyguid,
1006 "POLICYGUID_DC": policyguid_dc,
1007 "DNSDOMAIN": names.dnsdomain,
1008 "DOMAINDN": names.domaindn})
1010 # If we are setting up a subdomain, then this has been replicated in, so we
1011 # don't need to add it
1012 if fill == FILL_FULL:
1013 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1014 "CONFIGDN": names.configdn,
1015 "SCHEMADN": names.schemadn,
1016 "DOMAINDN": names.domaindn,
1017 "SERVERDN": names.serverdn,
1018 "INVOCATIONID": invocationid,
1019 "NETBIOSNAME": names.netbiosname,
1020 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1021 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1022 "DOMAINSID": str(domainsid),
1023 "DCRID": str(dc_rid),
1024 "SAMBA_VERSION_STRING": version,
1025 "NTDSGUID": ntdsguid_line,
1026 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1027 domainControllerFunctionality)})
1029 # Setup fSMORoleOwner entries to point at the newly created DC entry
1030 setup_modify_ldif(samdb,
1031 setup_path("provision_self_join_modify_config.ldif"), {
1032 "CONFIGDN": names.configdn,
1033 "SCHEMADN": names.schemadn,
1034 "DEFAULTSITE": names.sitename,
1035 "NETBIOSNAME": names.netbiosname,
1036 "SERVERDN": names.serverdn,
1039 system_session_info = system_session()
1040 samdb.set_session_info(system_session_info)
1041 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1042 # modify a serverReference under cn=config when we are a subdomain, we must
1043 # be system due to ACLs
1044 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1045 "DOMAINDN": names.domaindn,
1046 "SERVERDN": names.serverdn,
1047 "NETBIOSNAME": names.netbiosname,
1050 samdb.set_session_info(admin_session_info)
1052 if dns_backend != "SAMBA_INTERNAL":
1053 # This is Samba4 specific and should be replaced by the correct
1054 # DNS AD-style setup
1055 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1056 "DNSDOMAIN": names.dnsdomain,
1057 "DOMAINDN": names.domaindn,
1058 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1059 "HOSTNAME" : names.hostname,
1060 "DNSNAME" : '%s.%s' % (
1061 names.netbiosname.lower(), names.dnsdomain.lower())
1065 def getpolicypath(sysvolpath, dnsdomain, guid):
1066 """Return the physical path of policy given its guid.
1068 :param sysvolpath: Path to the sysvol folder
1069 :param dnsdomain: DNS name of the AD domain
1070 :param guid: The GUID of the policy
1071 :return: A string with the complete path to the policy folder
1073 if guid[0] != "{":
1074 guid = "{%s}" % guid
1075 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1076 return policy_path
1079 def create_gpo_struct(policy_path):
1080 if not os.path.exists(policy_path):
1081 os.makedirs(policy_path, 0775)
1082 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1083 try:
1084 f.write("[General]\r\nVersion=0")
1085 finally:
1086 f.close()
1087 p = os.path.join(policy_path, "MACHINE")
1088 if not os.path.exists(p):
1089 os.makedirs(p, 0775)
1090 p = os.path.join(policy_path, "USER")
1091 if not os.path.exists(p):
1092 os.makedirs(p, 0775)
1095 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1096 """Create the default GPO for a domain
1098 :param sysvolpath: Physical path for the sysvol folder
1099 :param dnsdomain: DNS domain name of the AD domain
1100 :param policyguid: GUID of the default domain policy
1101 :param policyguid_dc: GUID of the default domain controler policy
1103 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1104 create_gpo_struct(policy_path)
1106 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1107 create_gpo_struct(policy_path)
1110 def setup_samdb(path, session_info, provision_backend, lp, names,
1111 logger, fill, serverrole, schema, am_rodc=False):
1112 """Setup a complete SAM Database.
1114 :note: This will wipe the main SAM database file!
1117 # Also wipes the database
1118 setup_samdb_partitions(path, logger=logger, lp=lp,
1119 provision_backend=provision_backend, session_info=session_info,
1120 names=names, serverrole=serverrole, schema=schema)
1122 # Load the database, but don's load the global schema and don't connect
1123 # quite yet
1124 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1125 credentials=provision_backend.credentials, lp=lp,
1126 global_schema=False, am_rodc=am_rodc)
1128 logger.info("Pre-loading the Samba 4 and AD schema")
1130 # Load the schema from the one we computed earlier
1131 samdb.set_schema(schema, write_indices_and_attributes=False)
1133 # Set the NTDS settings DN manually - in order to have it already around
1134 # before the provisioned tree exists and we connect
1135 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1137 # And now we can connect to the DB - the schema won't be loaded from the
1138 # DB
1139 samdb.connect(path)
1141 # But we have to give it one more kick to have it use the schema
1142 # during provision - it needs, now that it is connected, to write
1143 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1144 samdb.set_schema(schema, write_indices_and_attributes=True)
1146 return samdb
1149 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1150 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1151 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1152 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1154 if next_rid is None:
1155 next_rid = 1000
1157 # Provision does not make much sense values larger than 1000000000
1158 # as the upper range of the rIDAvailablePool is 1073741823 and
1159 # we don't want to create a domain that cannot allocate rids.
1160 if next_rid < 1000 or next_rid > 1000000000:
1161 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1162 error += "the valid range is %u-%u. The default is %u." % (
1163 1000, 1000000000, 1000)
1164 raise ProvisioningError(error)
1166 # ATTENTION: Do NOT change these default values without discussion with the
1167 # team and/or release manager. They have a big impact on the whole program!
1168 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1170 if dom_for_fun_level is None:
1171 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1173 if dom_for_fun_level > domainControllerFunctionality:
1174 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!")
1176 domainFunctionality = dom_for_fun_level
1177 forestFunctionality = dom_for_fun_level
1179 # Set the NTDS settings DN manually - in order to have it already around
1180 # before the provisioned tree exists and we connect
1181 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1183 samdb.transaction_start()
1184 try:
1185 # Set the domain functionality levels onto the database.
1186 # Various module (the password_hash module in particular) need
1187 # to know what level of AD we are emulating.
1189 # These will be fixed into the database via the database
1190 # modifictions below, but we need them set from the start.
1191 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1192 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1193 samdb.set_opaque_integer("domainControllerFunctionality",
1194 domainControllerFunctionality)
1196 samdb.set_domain_sid(str(domainsid))
1197 samdb.set_invocation_id(invocationid)
1199 logger.info("Adding DomainDN: %s" % names.domaindn)
1201 # impersonate domain admin
1202 admin_session_info = admin_session(lp, str(domainsid))
1203 samdb.set_session_info(admin_session_info)
1204 if domainguid is not None:
1205 domainguid_line = "objectGUID: %s\n-" % domainguid
1206 else:
1207 domainguid_line = ""
1209 descr = b64encode(get_domain_descriptor(domainsid))
1210 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1211 "DOMAINDN": names.domaindn,
1212 "DOMAINSID": str(domainsid),
1213 "DESCRIPTOR": descr,
1214 "DOMAINGUID": domainguid_line
1217 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1218 "DOMAINDN": names.domaindn,
1219 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1220 "NEXTRID": str(next_rid),
1221 "DEFAULTSITE": names.sitename,
1222 "CONFIGDN": names.configdn,
1223 "POLICYGUID": policyguid,
1224 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1225 "SAMBA_VERSION_STRING": version
1228 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1229 if fill == FILL_FULL:
1230 logger.info("Adding configuration container")
1231 descr = b64encode(get_config_descriptor(domainsid))
1232 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1233 "CONFIGDN": names.configdn,
1234 "DESCRIPTOR": descr,
1237 # The LDIF here was created when the Schema object was constructed
1238 logger.info("Setting up sam.ldb schema")
1239 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1240 samdb.modify_ldif(schema.schema_dn_modify)
1241 samdb.write_prefixes_from_schema()
1242 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1243 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1244 {"SCHEMADN": names.schemadn})
1246 # Now register this container in the root of the forest
1247 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1248 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1249 "subRefs")
1251 except:
1252 samdb.transaction_cancel()
1253 raise
1254 else:
1255 samdb.transaction_commit()
1257 samdb.transaction_start()
1258 try:
1259 samdb.invocation_id = invocationid
1261 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1262 if fill == FILL_FULL:
1263 logger.info("Setting up sam.ldb configuration data")
1264 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1265 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1266 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1267 "CONFIGDN": names.configdn,
1268 "NETBIOSNAME": names.netbiosname,
1269 "DEFAULTSITE": names.sitename,
1270 "DNSDOMAIN": names.dnsdomain,
1271 "DOMAIN": names.domain,
1272 "SCHEMADN": names.schemadn,
1273 "DOMAINDN": names.domaindn,
1274 "SERVERDN": names.serverdn,
1275 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1276 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1277 "PARTITIONS_DESCRIPTOR": partitions_descr,
1278 "SITES_DESCRIPTOR": sites_descr,
1281 logger.info("Setting up display specifiers")
1282 display_specifiers_ldif = read_ms_ldif(
1283 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1284 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1285 {"CONFIGDN": names.configdn})
1286 check_all_substituted(display_specifiers_ldif)
1287 samdb.add_ldif(display_specifiers_ldif)
1289 logger.info("Adding users container")
1290 users_desc = b64encode(get_domain_users_descriptor(domainsid))
1291 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1292 "DOMAINDN": names.domaindn,
1293 "USERS_DESCRIPTOR": users_desc
1295 logger.info("Modifying users container")
1296 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1297 "DOMAINDN": names.domaindn})
1298 logger.info("Adding computers container")
1299 computers_desc = b64encode(get_domain_computers_descriptor(domainsid))
1300 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1301 "DOMAINDN": names.domaindn,
1302 "COMPUTERS_DESCRIPTOR": computers_desc
1304 logger.info("Modifying computers container")
1305 setup_modify_ldif(samdb,
1306 setup_path("provision_computers_modify.ldif"), {
1307 "DOMAINDN": names.domaindn})
1308 logger.info("Setting up sam.ldb data")
1309 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
1310 builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
1311 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1312 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1313 "DOMAINDN": names.domaindn,
1314 "NETBIOSNAME": names.netbiosname,
1315 "DEFAULTSITE": names.sitename,
1316 "CONFIGDN": names.configdn,
1317 "SERVERDN": names.serverdn,
1318 "RIDAVAILABLESTART": str(next_rid + 600),
1319 "POLICYGUID_DC": policyguid_dc,
1320 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1321 "BUILTIN_DESCRIPTOR": builtin_desc,
1324 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1325 if fill == FILL_FULL:
1326 setup_modify_ldif(samdb,
1327 setup_path("provision_configuration_references.ldif"), {
1328 "CONFIGDN": names.configdn,
1329 "SCHEMADN": names.schemadn})
1331 logger.info("Setting up well known security principals")
1332 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1333 "CONFIGDN": names.configdn,
1336 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1337 setup_modify_ldif(samdb,
1338 setup_path("provision_basedn_references.ldif"),
1339 {"DOMAINDN": names.domaindn})
1341 logger.info("Setting up sam.ldb users and groups")
1342 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1343 "DOMAINDN": names.domaindn,
1344 "DOMAINSID": str(domainsid),
1345 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1346 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1349 logger.info("Setting up self join")
1350 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1351 invocationid=invocationid,
1352 dns_backend=dns_backend,
1353 dnspass=dnspass,
1354 machinepass=machinepass,
1355 domainsid=domainsid,
1356 next_rid=next_rid,
1357 dc_rid=dc_rid,
1358 policyguid=policyguid,
1359 policyguid_dc=policyguid_dc,
1360 domainControllerFunctionality=domainControllerFunctionality,
1361 ntdsguid=ntdsguid)
1363 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1364 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1365 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1366 assert isinstance(names.ntdsguid, str)
1367 except:
1368 samdb.transaction_cancel()
1369 raise
1370 else:
1371 samdb.transaction_commit()
1372 return samdb
1375 FILL_FULL = "FULL"
1376 FILL_SUBDOMAIN = "SUBDOMAIN"
1377 FILL_NT4SYNC = "NT4SYNC"
1378 FILL_DRS = "DRS"
1379 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1380 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)"
1383 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb):
1384 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1385 for root, dirs, files in os.walk(path, topdown=False):
1386 for name in files:
1387 setntacl(lp, os.path.join(root, name), acl, domsid,
1388 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1389 for name in dirs:
1390 setntacl(lp, os.path.join(root, name), acl, domsid,
1391 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1394 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1395 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1396 folders beneath.
1398 :param sysvol: Physical path for the sysvol folder
1399 :param dnsdomain: The DNS name of the domain
1400 :param domainsid: The SID of the domain
1401 :param domaindn: The DN of the domain (ie. DC=...)
1402 :param samdb: An LDB object on the SAM db
1403 :param lp: an LP object
1406 # Set ACL for GPO root folder
1407 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1408 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1409 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1411 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1412 attrs=["cn", "nTSecurityDescriptor"],
1413 expression="", scope=ldb.SCOPE_ONELEVEL)
1415 for policy in res:
1416 acl = ndr_unpack(security.descriptor,
1417 str(policy["nTSecurityDescriptor"])).as_sddl()
1418 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1419 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1420 str(domainsid), use_ntvfs,
1421 passdb=passdb)
1424 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1425 domaindn, lp, use_ntvfs):
1426 """Set the ACL for the sysvol share and the subfolders
1428 :param samdb: An LDB object on the SAM db
1429 :param netlogon: Physical path for the netlogon folder
1430 :param sysvol: Physical path for the sysvol folder
1431 :param uid: The UID of the "Administrator" user
1432 :param gid: The GID of the "Domain adminstrators" group
1433 :param domainsid: The SID of the domain
1434 :param dnsdomain: The DNS name of the domain
1435 :param domaindn: The DN of the domain (ie. DC=...)
1437 s4_passdb = None
1439 if not use_ntvfs:
1440 # This will ensure that the smbd code we are running when setting ACLs
1441 # is initialised with the smb.conf
1442 s3conf = s3param.get_context()
1443 s3conf.load(lp.configfile)
1444 # ensure we are using the right samba_dsdb passdb backend, no matter what
1445 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1446 passdb.reload_static_pdb()
1448 # ensure that we init the samba_dsdb backend, so the domain sid is
1449 # marked in secrets.tdb
1450 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1452 # now ensure everything matches correctly, to avoid wierd issues
1453 if passdb.get_global_sam_sid() != domainsid:
1454 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))
1456 domain_info = s4_passdb.domain_info()
1457 if domain_info["dom_sid"] != domainsid:
1458 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))
1460 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1461 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()))
1464 try:
1465 if use_ntvfs:
1466 os.chown(sysvol, -1, gid)
1467 except OSError:
1468 canchown = False
1469 else:
1470 canchown = True
1472 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1473 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1474 for root, dirs, files in os.walk(sysvol, topdown=False):
1475 for name in files:
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)
1479 for name in dirs:
1480 if use_ntvfs and canchown:
1481 os.chown(os.path.join(root, name), -1, gid)
1482 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1484 # Set acls on Policy folder and policies folders
1485 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1487 def acl_type(direct_db_access):
1488 if direct_db_access:
1489 return "DB"
1490 else:
1491 return "VFS"
1493 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1494 fsacl = getntacl(lp, path, direct_db_access=direct_db_access)
1495 fsacl_sddl = fsacl.as_sddl(domainsid)
1496 if fsacl_sddl != acl:
1497 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))
1499 for root, dirs, files in os.walk(path, topdown=False):
1500 for name in files:
1501 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1502 if fsacl is None:
1503 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1504 fsacl_sddl = fsacl.as_sddl(domainsid)
1505 if fsacl_sddl != acl:
1506 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))
1508 for name in dirs:
1509 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1510 if fsacl is None:
1511 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1512 fsacl_sddl = fsacl.as_sddl(domainsid)
1513 if fsacl_sddl != acl:
1514 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))
1517 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1518 direct_db_access):
1519 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1520 folders beneath.
1522 :param sysvol: Physical path for the sysvol folder
1523 :param dnsdomain: The DNS name of the domain
1524 :param domainsid: The SID of the domain
1525 :param domaindn: The DN of the domain (ie. DC=...)
1526 :param samdb: An LDB object on the SAM db
1527 :param lp: an LP object
1530 # Set ACL for GPO root folder
1531 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1532 fsacl = getntacl(lp, root_policy_path, direct_db_access=direct_db_access)
1533 if fsacl is None:
1534 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1535 fsacl_sddl = fsacl.as_sddl(domainsid)
1536 if fsacl_sddl != POLICIES_ACL:
1537 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))
1538 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1539 attrs=["cn", "nTSecurityDescriptor"],
1540 expression="", scope=ldb.SCOPE_ONELEVEL)
1542 for policy in res:
1543 acl = ndr_unpack(security.descriptor,
1544 str(policy["nTSecurityDescriptor"])).as_sddl()
1545 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1546 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1547 domainsid, direct_db_access)
1550 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1551 lp):
1552 """Set the ACL for the sysvol share and the subfolders
1554 :param samdb: An LDB object on the SAM db
1555 :param netlogon: Physical path for the netlogon folder
1556 :param sysvol: Physical path for the sysvol folder
1557 :param uid: The UID of the "Administrator" user
1558 :param gid: The GID of the "Domain adminstrators" group
1559 :param domainsid: The SID of the domain
1560 :param dnsdomain: The DNS name of the domain
1561 :param domaindn: The DN of the domain (ie. DC=...)
1564 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1565 s3conf = s3param.get_context()
1566 s3conf.load(lp.configfile)
1567 # ensure we are using the right samba_dsdb passdb backend, no matter what
1568 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1569 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1570 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1572 # now ensure everything matches correctly, to avoid wierd issues
1573 if passdb.get_global_sam_sid() != domainsid:
1574 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))
1576 domain_info = s4_passdb.domain_info()
1577 if domain_info["dom_sid"] != domainsid:
1578 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))
1580 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1581 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()))
1583 # Ensure we can read this directly, and via the smbd VFS
1584 for direct_db_access in [True, False]:
1585 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1586 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1587 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access)
1588 if fsacl is None:
1589 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1590 fsacl_sddl = fsacl.as_sddl(domainsid)
1591 if fsacl_sddl != SYSVOL_ACL:
1592 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))
1594 # Check acls on Policy folder and policies folders
1595 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1596 direct_db_access)
1599 def interface_ips_v4(lp):
1600 """return only IPv4 IPs"""
1601 ips = samba.interface_ips(lp, False)
1602 ret = []
1603 for i in ips:
1604 if i.find(':') == -1:
1605 ret.append(i)
1606 return ret
1609 def interface_ips_v6(lp, linklocal=False):
1610 """return only IPv6 IPs"""
1611 ips = samba.interface_ips(lp, False)
1612 ret = []
1613 for i in ips:
1614 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1615 ret.append(i)
1616 return ret
1619 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1620 domainsid, schema=None,
1621 targetdir=None, samdb_fill=FILL_FULL,
1622 hostip=None, hostip6=None,
1623 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1624 domainguid=None, policyguid=None, policyguid_dc=None,
1625 invocationid=None, machinepass=None, ntdsguid=None,
1626 dns_backend=None, dnspass=None,
1627 serverrole=None, dom_for_fun_level=None,
1628 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1629 # create/adapt the group policy GUIDs
1630 # Default GUID for default policy are described at
1631 # "How Core Group Policy Works"
1632 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1633 if policyguid is None:
1634 policyguid = DEFAULT_POLICY_GUID
1635 policyguid = policyguid.upper()
1636 if policyguid_dc is None:
1637 policyguid_dc = DEFAULT_DC_POLICY_GUID
1638 policyguid_dc = policyguid_dc.upper()
1640 if invocationid is None:
1641 invocationid = str(uuid.uuid4())
1643 if krbtgtpass is None:
1644 krbtgtpass = samba.generate_random_password(128, 255)
1645 if machinepass is None:
1646 machinepass = samba.generate_random_password(128, 255)
1647 if dnspass is None:
1648 dnspass = samba.generate_random_password(128, 255)
1650 samdb = fill_samdb(samdb, lp, names, logger=logger,
1651 domainsid=domainsid, schema=schema, domainguid=domainguid,
1652 policyguid=policyguid, policyguid_dc=policyguid_dc,
1653 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1654 invocationid=invocationid, machinepass=machinepass,
1655 dns_backend=dns_backend, dnspass=dnspass,
1656 ntdsguid=ntdsguid, serverrole=serverrole,
1657 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1658 next_rid=next_rid, dc_rid=dc_rid)
1660 if serverrole == "active directory domain controller":
1662 # Set up group policies (domain policy and domain controller
1663 # policy)
1664 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1665 policyguid_dc)
1666 if not skip_sysvolacl:
1667 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1668 paths.root_gid, domainsid, names.dnsdomain,
1669 names.domaindn, lp, use_ntvfs)
1670 else:
1671 logger.info("Setting acl on sysvol skipped")
1673 secretsdb_self_join(secrets_ldb, domain=names.domain,
1674 realm=names.realm, dnsdomain=names.dnsdomain,
1675 netbiosname=names.netbiosname, domainsid=domainsid,
1676 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1678 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1679 # In future, this might be determined from some configuration
1680 kerberos_enctypes = str(ENC_ALL_TYPES)
1682 try:
1683 msg = ldb.Message(ldb.Dn(samdb,
1684 samdb.searchone("distinguishedName",
1685 expression="samAccountName=%s$" % names.netbiosname,
1686 scope=ldb.SCOPE_SUBTREE)))
1687 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1688 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1689 name="msDS-SupportedEncryptionTypes")
1690 samdb.modify(msg)
1691 except ldb.LdbError, (enum, estr):
1692 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1693 # It might be that this attribute does not exist in this schema
1694 raise
1696 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1697 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1698 dnspass=dnspass, os_level=dom_for_fun_level,
1699 targetdir=targetdir, site=DEFAULTSITE)
1701 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1702 attribute="objectGUID")
1703 assert isinstance(domainguid, str)
1705 lastProvisionUSNs = get_last_provision_usn(samdb)
1706 maxUSN = get_max_usn(samdb, str(names.rootdn))
1707 if lastProvisionUSNs is not None:
1708 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1709 else:
1710 set_provision_usn(samdb, 0, maxUSN, invocationid)
1712 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1713 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1714 { 'NTDSGUID' : names.ntdsguid })
1716 # fix any dangling GUIDs from the provision
1717 logger.info("Fixing provision GUIDs")
1718 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1719 quiet=True)
1720 samdb.transaction_start()
1721 try:
1722 # a small number of GUIDs are missing because of ordering issues in the
1723 # provision code
1724 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1725 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1726 scope=ldb.SCOPE_BASE,
1727 attrs=['defaultObjectCategory'])
1728 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1729 scope=ldb.SCOPE_ONELEVEL,
1730 attrs=['ipsecOwnersReference',
1731 'ipsecFilterReference',
1732 'ipsecISAKMPReference',
1733 'ipsecNegotiationPolicyReference',
1734 'ipsecNFAReference'])
1735 except:
1736 samdb.transaction_cancel()
1737 raise
1738 else:
1739 samdb.transaction_commit()
1742 _ROLES_MAP = {
1743 "ROLE_STANDALONE": "standalone server",
1744 "ROLE_DOMAIN_MEMBER": "member server",
1745 "ROLE_DOMAIN_BDC": "active directory domain controller",
1746 "ROLE_DOMAIN_PDC": "active directory domain controller",
1747 "dc": "active directory domain controller",
1748 "member": "member server",
1749 "domain controller": "active directory domain controller",
1750 "active directory domain controller": "active directory domain controller",
1751 "member server": "member server",
1752 "standalone": "standalone server",
1753 "standalone server": "standalone server",
1757 def sanitize_server_role(role):
1758 """Sanitize a server role name.
1760 :param role: Server role
1761 :raise ValueError: If the role can not be interpreted
1762 :return: Sanitized server role (one of "member server",
1763 "active directory domain controller", "standalone server")
1765 try:
1766 return _ROLES_MAP[role]
1767 except KeyError:
1768 raise ValueError(role)
1771 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1772 maxuid, maxgid):
1773 """Create AD entries for the fake ypserver.
1775 This is needed for being able to manipulate posix attrs via ADUC.
1777 samdb.transaction_start()
1778 try:
1779 logger.info("Setting up fake yp server settings")
1780 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1781 "DOMAINDN": domaindn,
1782 "NETBIOSNAME": netbiosname,
1783 "NISDOMAIN": nisdomain,
1785 except:
1786 samdb.transaction_cancel()
1787 raise
1788 else:
1789 samdb.transaction_commit()
1792 def provision(logger, session_info, credentials, smbconf=None,
1793 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1794 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1795 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1796 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1797 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1798 dns_backend=None, dns_forwarder=None, dnspass=None,
1799 invocationid=None, machinepass=None, ntdsguid=None,
1800 root=None, nobody=None, users=None, backup=None, aci=None,
1801 serverrole=None, dom_for_fun_level=None, backend_type=None,
1802 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1803 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1804 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1805 """Provision samba4
1807 :note: caution, this wipes all existing data!
1810 try:
1811 serverrole = sanitize_server_role(serverrole)
1812 except ValueError:
1813 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1815 if ldapadminpass is None:
1816 # Make a new, random password between Samba and it's LDAP server
1817 ldapadminpass = samba.generate_random_password(128, 255)
1819 if backend_type is None:
1820 backend_type = "ldb"
1822 if domainsid is None:
1823 domainsid = security.random_sid()
1824 else:
1825 domainsid = security.dom_sid(domainsid)
1827 root_uid = findnss_uid([root or "root"])
1828 nobody_uid = findnss_uid([nobody or "nobody"])
1829 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1830 root_gid = pwd.getpwuid(root_uid).pw_gid
1832 try:
1833 bind_gid = findnss_gid(["bind", "named"])
1834 except KeyError:
1835 bind_gid = None
1837 if targetdir is not None:
1838 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1839 elif smbconf is None:
1840 smbconf = samba.param.default_path()
1841 if not os.path.exists(os.path.dirname(smbconf)):
1842 os.makedirs(os.path.dirname(smbconf))
1844 server_services = []
1845 global_param = {}
1846 if use_rfc2307:
1847 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1849 if dns_backend != "SAMBA_INTERNAL":
1850 server_services.append("-dns")
1851 else:
1852 if dns_forwarder is not None:
1853 global_param["dns forwarder"] = [dns_forwarder]
1855 if use_ntvfs:
1856 server_services.append("+smb")
1857 server_services.append("-s3fs")
1858 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1860 if len(server_services) > 0:
1861 global_param["server services"] = server_services
1863 # only install a new smb.conf if there isn't one there already
1864 if os.path.exists(smbconf):
1865 # if Samba Team members can't figure out the weird errors
1866 # loading an empty smb.conf gives, then we need to be smarter.
1867 # Pretend it just didn't exist --abartlet
1868 f = open(smbconf, 'r')
1869 try:
1870 data = f.read().lstrip()
1871 finally:
1872 f.close()
1873 if data is None or data == "":
1874 make_smbconf(smbconf, hostname, domain, realm,
1875 targetdir, serverrole=serverrole,
1876 eadb=useeadb, use_ntvfs=use_ntvfs,
1877 lp=lp, global_param=global_param)
1878 else:
1879 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1880 serverrole=serverrole,
1881 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1883 if lp is None:
1884 lp = samba.param.LoadParm()
1885 lp.load(smbconf)
1886 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1887 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1888 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1889 sitename=sitename, rootdn=rootdn)
1890 paths = provision_paths_from_lp(lp, names.dnsdomain)
1892 paths.bind_gid = bind_gid
1893 paths.root_uid = root_uid;
1894 paths.root_gid = root_gid
1896 if hostip is None:
1897 logger.info("Looking up IPv4 addresses")
1898 hostips = interface_ips_v4(lp)
1899 if len(hostips) > 0:
1900 hostip = hostips[0]
1901 if len(hostips) > 1:
1902 logger.warning("More than one IPv4 address found. Using %s",
1903 hostip)
1904 if hostip == "127.0.0.1":
1905 hostip = None
1906 if hostip is None:
1907 logger.warning("No IPv4 address will be assigned")
1909 if hostip6 is None:
1910 logger.info("Looking up IPv6 addresses")
1911 hostips = interface_ips_v6(lp, linklocal=False)
1912 if hostips:
1913 hostip6 = hostips[0]
1914 if len(hostips) > 1:
1915 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1916 if hostip6 is None:
1917 logger.warning("No IPv6 address will be assigned")
1919 names.hostip = hostip
1920 names.hostip6 = hostip6
1922 if serverrole is None:
1923 serverrole = lp.get("server role")
1925 if not os.path.exists(paths.private_dir):
1926 os.mkdir(paths.private_dir)
1927 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1928 os.mkdir(os.path.join(paths.private_dir, "tls"))
1929 if not os.path.exists(paths.state_dir):
1930 os.mkdir(paths.state_dir)
1932 if paths.sysvol and not os.path.exists(paths.sysvol):
1933 os.makedirs(paths.sysvol, 0775)
1935 if not use_ntvfs and serverrole == "active directory domain controller":
1936 s3conf = s3param.get_context()
1937 s3conf.load(lp.configfile)
1939 if paths.sysvol is None:
1940 raise MissingShareError("sysvol", paths.smbconf)
1942 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1943 try:
1944 try:
1945 smbd.set_simple_acl(file.name, 0755, root_gid)
1946 except Exception:
1947 if not smbd.have_posix_acls():
1948 # This clue is only strictly correct for RPM and
1949 # Debian-like Linux systems, but hopefully other users
1950 # will get enough clue from it.
1951 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.")
1953 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1954 try:
1955 smbd.chown(file.name, root_uid, root_gid)
1956 except Exception:
1957 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1958 finally:
1959 file.close()
1961 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1963 schema = Schema(domainsid, invocationid=invocationid,
1964 schemadn=names.schemadn)
1966 if backend_type == "ldb":
1967 provision_backend = LDBBackend(backend_type, paths=paths,
1968 lp=lp, credentials=credentials,
1969 names=names, logger=logger)
1970 elif backend_type == "existing":
1971 # If support for this is ever added back, then the URI will need to be
1972 # specified again
1973 provision_backend = ExistingBackend(backend_type, paths=paths,
1974 lp=lp, credentials=credentials,
1975 names=names, logger=logger,
1976 ldap_backend_forced_uri=None)
1977 elif backend_type == "fedora-ds":
1978 provision_backend = FDSBackend(backend_type, paths=paths,
1979 lp=lp, credentials=credentials,
1980 names=names, logger=logger, domainsid=domainsid,
1981 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1982 slapd_path=slapd_path,
1983 root=root)
1984 elif backend_type == "openldap":
1985 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1986 lp=lp, credentials=credentials,
1987 names=names, logger=logger, domainsid=domainsid,
1988 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1989 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1990 else:
1991 raise ValueError("Unknown LDAP backend type selected")
1993 provision_backend.init()
1994 provision_backend.start()
1996 # only install a new shares config db if there is none
1997 if not os.path.exists(paths.shareconf):
1998 logger.info("Setting up share.ldb")
1999 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2000 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2002 logger.info("Setting up secrets.ldb")
2003 secrets_ldb = setup_secretsdb(paths,
2004 session_info=session_info,
2005 backend_credentials=provision_backend.secrets_credentials, lp=lp)
2007 try:
2008 logger.info("Setting up the registry")
2009 setup_registry(paths.hklm, session_info, lp=lp)
2011 logger.info("Setting up the privileges database")
2012 setup_privileges(paths.privilege, session_info, lp=lp)
2014 logger.info("Setting up idmap db")
2015 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2017 setup_name_mappings(idmap, sid=str(domainsid),
2018 root_uid=root_uid, nobody_uid=nobody_uid,
2019 users_gid=users_gid, root_gid=root_gid)
2021 logger.info("Setting up SAM db")
2022 samdb = setup_samdb(paths.samdb, session_info,
2023 provision_backend, lp, names, logger=logger,
2024 serverrole=serverrole,
2025 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2027 if serverrole == "active directory domain controller":
2028 if paths.netlogon is None:
2029 raise MissingShareError("netlogon", paths.smbconf)
2031 if paths.sysvol is None:
2032 raise MissingShareError("sysvol", paths.smbconf)
2034 if not os.path.isdir(paths.netlogon):
2035 os.makedirs(paths.netlogon, 0755)
2037 if adminpass is None:
2038 adminpass = samba.generate_random_password(12, 32)
2039 adminpass_generated = True
2040 else:
2041 adminpass_generated = False
2043 if samdb_fill == FILL_FULL:
2044 provision_fill(samdb, secrets_ldb, logger, names, paths,
2045 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2046 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2047 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2048 krbtgtpass=krbtgtpass, domainguid=domainguid,
2049 policyguid=policyguid, policyguid_dc=policyguid_dc,
2050 invocationid=invocationid, machinepass=machinepass,
2051 ntdsguid=ntdsguid, dns_backend=dns_backend,
2052 dnspass=dnspass, serverrole=serverrole,
2053 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2054 lp=lp, use_ntvfs=use_ntvfs,
2055 skip_sysvolacl=skip_sysvolacl)
2057 create_krb5_conf(paths.krb5conf,
2058 dnsdomain=names.dnsdomain, hostname=names.hostname,
2059 realm=names.realm)
2060 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2061 "generated at %s", paths.krb5conf)
2063 if serverrole == "active directory domain controller":
2064 create_dns_update_list(lp, logger, paths)
2066 backend_result = provision_backend.post_setup()
2067 provision_backend.shutdown()
2069 except:
2070 secrets_ldb.transaction_cancel()
2071 raise
2073 # Now commit the secrets.ldb to disk
2074 secrets_ldb.transaction_commit()
2076 # the commit creates the dns.keytab, now chown it
2077 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2078 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2079 try:
2080 os.chmod(dns_keytab_path, 0640)
2081 os.chown(dns_keytab_path, -1, paths.bind_gid)
2082 except OSError:
2083 if not os.environ.has_key('SAMBA_SELFTEST'):
2084 logger.info("Failed to chown %s to bind gid %u",
2085 dns_keytab_path, paths.bind_gid)
2087 result = ProvisionResult()
2088 result.server_role = serverrole
2089 result.domaindn = domaindn
2090 result.paths = paths
2091 result.names = names
2092 result.lp = lp
2093 result.samdb = samdb
2094 result.idmap = idmap
2095 result.domainsid = str(domainsid)
2097 if samdb_fill == FILL_FULL:
2098 result.adminpass_generated = adminpass_generated
2099 result.adminpass = adminpass
2100 else:
2101 result.adminpass_generated = False
2102 result.adminpass = None
2104 result.backend_result = backend_result
2106 if use_rfc2307:
2107 provision_fake_ypserver(logger=logger, samdb=samdb,
2108 domaindn=names.domaindn, netbiosname=names.netbiosname,
2109 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2111 return result
2114 def provision_become_dc(smbconf=None, targetdir=None,
2115 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2116 serverdn=None, domain=None, hostname=None, domainsid=None,
2117 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2118 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2119 dns_backend=None, root=None, nobody=None, users=None,
2120 backup=None, serverrole=None, ldap_backend=None,
2121 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2123 logger = logging.getLogger("provision")
2124 samba.set_debug_level(debuglevel)
2126 res = provision(logger, system_session(), None,
2127 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2128 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2129 configdn=configdn, serverdn=serverdn, domain=domain,
2130 hostname=hostname, hostip=None, domainsid=domainsid,
2131 machinepass=machinepass,
2132 serverrole="active directory domain controller",
2133 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2134 use_ntvfs=use_ntvfs)
2135 res.lp.set("debuglevel", str(debuglevel))
2136 return res
2139 def create_krb5_conf(path, dnsdomain, hostname, realm):
2140 """Write out a file containing zone statements suitable for inclusion in a
2141 named.conf file (including GSS-TSIG configuration).
2143 :param path: Path of the new named.conf file.
2144 :param dnsdomain: DNS Domain name
2145 :param hostname: Local hostname
2146 :param realm: Realm name
2148 setup_file(setup_path("krb5.conf"), path, {
2149 "DNSDOMAIN": dnsdomain,
2150 "HOSTNAME": hostname,
2151 "REALM": realm,
2155 class ProvisioningError(Exception):
2156 """A generic provision error."""
2158 def __init__(self, value):
2159 self.value = value
2161 def __str__(self):
2162 return "ProvisioningError: " + self.value
2165 class InvalidNetbiosName(Exception):
2166 """A specified name was not a valid NetBIOS name."""
2168 def __init__(self, name):
2169 super(InvalidNetbiosName, self).__init__(
2170 "The name '%r' is not a valid NetBIOS name" % name)
2173 class MissingShareError(ProvisioningError):
2175 def __init__(self, name, smbconf):
2176 super(MissingShareError, self).__init__(
2177 "Existing smb.conf does not have a [%s] share, but you are "
2178 "configuring a DC. Please remove %s or add the share manually." %
2179 (name, smbconf))