VERSION: Disable git snapshots for the 4.2.3 release.
[Samba.git] / python / samba / provision / __init__.py
blob1603321bd8b526c1086037136c6d7bba4a0a2183
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.descriptor import (
80 get_empty_descriptor,
81 get_config_descriptor,
82 get_config_partitions_descriptor,
83 get_config_sites_descriptor,
84 get_config_ntds_quotas_descriptor,
85 get_config_delete_protected1_descriptor,
86 get_config_delete_protected1wd_descriptor,
87 get_config_delete_protected2_descriptor,
88 get_domain_descriptor,
89 get_domain_infrastructure_descriptor,
90 get_domain_builtin_descriptor,
91 get_domain_computers_descriptor,
92 get_domain_users_descriptor,
93 get_domain_controllers_descriptor,
94 get_domain_delete_protected1_descriptor,
95 get_domain_delete_protected2_descriptor,
96 get_dns_partition_descriptor,
97 get_dns_forest_microsoft_dns_descriptor,
98 get_dns_domain_microsoft_dns_descriptor,
100 from samba.provision.common import (
101 setup_path,
102 setup_add_ldif,
103 setup_modify_ldif,
104 FILL_FULL,
105 FILL_SUBDOMAIN,
106 FILL_NT4SYNC,
107 FILL_DRS
109 from samba.provision.sambadns import (
110 get_dnsadmins_sid,
111 setup_ad_dns,
112 create_dns_update_list
115 import samba.param
116 import samba.registry
117 from samba.schema import Schema
118 from samba.samdb import SamDB
119 from samba.dbchecker import dbcheck
122 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
123 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
124 DEFAULTSITE = "Default-First-Site-Name"
125 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
128 class ProvisionPaths(object):
130 def __init__(self):
131 self.shareconf = None
132 self.hklm = None
133 self.hkcu = None
134 self.hkcr = None
135 self.hku = None
136 self.hkpd = None
137 self.hkpt = None
138 self.samdb = None
139 self.idmapdb = None
140 self.secrets = None
141 self.keytab = None
142 self.dns_keytab = None
143 self.dns = None
144 self.winsdb = None
145 self.private_dir = None
146 self.state_dir = None
149 class ProvisionNames(object):
151 def __init__(self):
152 self.ncs = None
153 self.rootdn = None
154 self.domaindn = None
155 self.configdn = None
156 self.schemadn = None
157 self.dnsforestdn = None
158 self.dnsdomaindn = None
159 self.ldapmanagerdn = None
160 self.dnsdomain = None
161 self.realm = None
162 self.netbiosname = None
163 self.domain = None
164 self.hostname = None
165 self.sitename = None
166 self.smbconf = None
167 self.domainsid = None
168 self.forestsid = None
169 self.domainguid = None
170 self.name_map = {}
173 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
174 lp):
175 """Get key provision parameters (realm, domain, ...) from a given provision
177 :param samdb: An LDB object connected to the sam.ldb file
178 :param secretsdb: An LDB object connected to the secrets.ldb file
179 :param idmapdb: An LDB object connected to the idmap.ldb file
180 :param paths: A list of path to provision object
181 :param smbconf: Path to the smb.conf file
182 :param lp: A LoadParm object
183 :return: A list of key provision parameters
185 names = ProvisionNames()
186 names.adminpass = None
188 # NT domain, kerberos realm, root dn, domain dn, domain dns name
189 names.domain = string.upper(lp.get("workgroup"))
190 names.realm = lp.get("realm")
191 names.dnsdomain = names.realm.lower()
192 basedn = samba.dn_from_dns_name(names.dnsdomain)
193 names.realm = string.upper(names.realm)
194 # netbiosname
195 # Get the netbiosname first (could be obtained from smb.conf in theory)
196 res = secretsdb.search(expression="(flatname=%s)" %
197 names.domain,base="CN=Primary Domains",
198 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
199 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
201 names.smbconf = smbconf
203 # That's a bit simplistic but it's ok as long as we have only 3
204 # partitions
205 current = samdb.search(expression="(objectClass=*)",
206 base="", scope=ldb.SCOPE_BASE,
207 attrs=["defaultNamingContext", "schemaNamingContext",
208 "configurationNamingContext","rootDomainNamingContext",
209 "namingContexts"])
211 names.configdn = current[0]["configurationNamingContext"][0]
212 names.schemadn = current[0]["schemaNamingContext"][0]
213 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
214 current[0]["defaultNamingContext"][0]))):
215 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
216 "is not the same ..." % (paths.samdb,
217 str(current[0]["defaultNamingContext"][0]),
218 paths.smbconf, basedn)))
220 names.domaindn=current[0]["defaultNamingContext"][0]
221 names.rootdn=current[0]["rootDomainNamingContext"][0]
222 names.ncs=current[0]["namingContexts"]
223 names.dnsforestdn = None
224 names.dnsdomaindn = None
226 for i in range(0, len(names.ncs)):
227 nc = names.ncs[i]
229 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
230 if nc == dnsforestdn:
231 names.dnsforestdn = dnsforestdn
232 continue
234 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
235 if nc == dnsdomaindn:
236 names.dnsdomaindn = dnsdomaindn
237 continue
239 # default site name
240 res3 = samdb.search(expression="(objectClass=site)",
241 base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
242 names.sitename = str(res3[0]["cn"])
244 # dns hostname and server dn
245 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
246 base="OU=Domain Controllers,%s" % basedn,
247 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
248 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
250 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
251 attrs=[], base=names.configdn)
252 names.serverdn = str(server_res[0].dn)
254 # invocation id/objectguid
255 res5 = samdb.search(expression="(objectClass=*)",
256 base="CN=NTDS Settings,%s" % str(names.serverdn),
257 scope=ldb.SCOPE_BASE,
258 attrs=["invocationID", "objectGUID"])
259 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
260 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
262 # domain guid/sid
263 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
264 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
265 "objectSid","msDS-Behavior-Version" ])
266 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
267 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
268 names.forestsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
269 if res6[0].get("msDS-Behavior-Version") is None or \
270 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
271 names.domainlevel = DS_DOMAIN_FUNCTION_2000
272 else:
273 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
275 # policy guid
276 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
277 base="CN=Policies,CN=System," + basedn,
278 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
279 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
280 # dc policy guid
281 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
282 " Policy)",
283 base="CN=Policies,CN=System," + basedn,
284 scope=ldb.SCOPE_ONELEVEL,
285 attrs=["cn","displayName"])
286 if len(res8) == 1:
287 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
288 else:
289 names.policyid_dc = None
291 res9 = idmapdb.search(expression="(cn=%s-%s)" %
292 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
293 attrs=["xidNumber", "type"])
294 if len(res9) != 1:
295 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
296 if res9[0]["type"][0] == "ID_TYPE_BOTH":
297 names.root_gid = res9[0]["xidNumber"][0]
298 else:
299 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
301 res10 = samdb.search(expression="(samaccountname=dns)",
302 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
303 controls=["search_options:1:2"])
304 if (len(res10) > 0):
305 has_legacy_dns_account = True
306 else:
307 has_legacy_dns_account = False
309 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
310 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
311 controls=["search_options:1:2"])
312 if (len(res11) > 0):
313 has_dns_account = True
314 else:
315 has_dns_account = False
317 if names.dnsdomaindn is not None:
318 if has_dns_account:
319 names.dns_backend = 'BIND9_DLZ'
320 else:
321 names.dns_backend = 'SAMBA_INTERNAL'
322 elif has_dns_account or has_legacy_dns_account:
323 names.dns_backend = 'BIND9_FLATFILE'
324 else:
325 names.dns_backend = 'NONE'
327 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
328 names.name_map['DnsAdmins'] = str(dns_admins_sid)
330 return names
333 def update_provision_usn(samdb, low, high, id, replace=False):
334 """Update the field provisionUSN in sam.ldb
336 This field is used to track range of USN modified by provision and
337 upgradeprovision.
338 This value is used afterward by next provision to figure out if
339 the field have been modified since last provision.
341 :param samdb: An LDB object connect to sam.ldb
342 :param low: The lowest USN modified by this upgrade
343 :param high: The highest USN modified by this upgrade
344 :param id: The invocation id of the samba's dc
345 :param replace: A boolean indicating if the range should replace any
346 existing one or appended (default)
349 tab = []
350 if not replace:
351 entry = samdb.search(base="@PROVISION",
352 scope=ldb.SCOPE_BASE,
353 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
354 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
355 if not re.search(';', e):
356 e = "%s;%s" % (e, id)
357 tab.append(str(e))
359 tab.append("%s-%s;%s" % (low, high, id))
360 delta = ldb.Message()
361 delta.dn = ldb.Dn(samdb, "@PROVISION")
362 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
363 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
364 entry = samdb.search(expression='provisionnerID=*',
365 base="@PROVISION", scope=ldb.SCOPE_BASE,
366 attrs=["provisionnerID"])
367 if len(entry) == 0 or len(entry[0]) == 0:
368 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
369 samdb.modify(delta)
372 def set_provision_usn(samdb, low, high, id):
373 """Set the field provisionUSN in sam.ldb
374 This field is used to track range of USN modified by provision and
375 upgradeprovision.
376 This value is used afterward by next provision to figure out if
377 the field have been modified since last provision.
379 :param samdb: An LDB object connect to sam.ldb
380 :param low: The lowest USN modified by this upgrade
381 :param high: The highest USN modified by this upgrade
382 :param id: The invocationId of the provision"""
384 tab = []
385 tab.append("%s-%s;%s" % (low, high, id))
387 delta = ldb.Message()
388 delta.dn = ldb.Dn(samdb, "@PROVISION")
389 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
390 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
391 samdb.add(delta)
394 def get_max_usn(samdb,basedn):
395 """ This function return the biggest USN present in the provision
397 :param samdb: A LDB object pointing to the sam.ldb
398 :param basedn: A string containing the base DN of the provision
399 (ie. DC=foo, DC=bar)
400 :return: The biggest USN in the provision"""
402 res = samdb.search(expression="objectClass=*",base=basedn,
403 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
404 controls=["search_options:1:2",
405 "server_sort:1:1:uSNChanged",
406 "paged_results:1:1"])
407 return res[0]["uSNChanged"]
410 def get_last_provision_usn(sam):
411 """Get USNs ranges modified by a provision or an upgradeprovision
413 :param sam: An LDB object pointing to the sam.ldb
414 :return: a dictionary which keys are invocation id and values are an array
415 of integer representing the different ranges
417 try:
418 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
419 base="@PROVISION", scope=ldb.SCOPE_BASE,
420 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
421 except ldb.LdbError, (ecode, emsg):
422 if ecode == ldb.ERR_NO_SUCH_OBJECT:
423 return None
424 raise
425 if len(entry) > 0:
426 myids = []
427 range = {}
428 p = re.compile(r'-')
429 if entry[0].get("provisionnerID"):
430 for e in entry[0]["provisionnerID"]:
431 myids.append(str(e))
432 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
433 tab1 = str(r).split(';')
434 if len(tab1) == 2:
435 id = tab1[1]
436 else:
437 id = "default"
438 if (len(myids) > 0 and id not in myids):
439 continue
440 tab2 = p.split(tab1[0])
441 if range.get(id) is None:
442 range[id] = []
443 range[id].append(tab2[0])
444 range[id].append(tab2[1])
445 return range
446 else:
447 return None
450 class ProvisionResult(object):
451 """Result of a provision.
453 :ivar server_role: The server role
454 :ivar paths: ProvisionPaths instance
455 :ivar domaindn: The domain dn, as string
458 def __init__(self):
459 self.server_role = None
460 self.paths = None
461 self.domaindn = None
462 self.lp = None
463 self.samdb = None
464 self.idmap = None
465 self.names = None
466 self.domainsid = None
467 self.adminpass_generated = None
468 self.adminpass = None
469 self.backend_result = None
471 def report_logger(self, logger):
472 """Report this provision result to a logger."""
473 logger.info(
474 "Once the above files are installed, your Samba4 server will "
475 "be ready to use")
476 if self.adminpass_generated:
477 logger.info("Admin password: %s", self.adminpass)
478 logger.info("Server Role: %s", self.server_role)
479 logger.info("Hostname: %s", self.names.hostname)
480 logger.info("NetBIOS Domain: %s", self.names.domain)
481 logger.info("DNS Domain: %s", self.names.dnsdomain)
482 logger.info("DOMAIN SID: %s", self.domainsid)
484 if self.backend_result:
485 self.backend_result.report_logger(logger)
488 def check_install(lp, session_info, credentials):
489 """Check whether the current install seems ok.
491 :param lp: Loadparm context
492 :param session_info: Session information
493 :param credentials: Credentials
495 if lp.get("realm") == "":
496 raise Exception("Realm empty")
497 samdb = Ldb(lp.samdb_url(), session_info=session_info,
498 credentials=credentials, lp=lp)
499 if len(samdb.search("(cn=Administrator)")) != 1:
500 raise ProvisioningError("No administrator account found")
503 def findnss(nssfn, names):
504 """Find a user or group from a list of possibilities.
506 :param nssfn: NSS Function to try (should raise KeyError if not found)
507 :param names: Names to check.
508 :return: Value return by first names list.
510 for name in names:
511 try:
512 return nssfn(name)
513 except KeyError:
514 pass
515 raise KeyError("Unable to find user/group in %r" % names)
518 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
519 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
522 def provision_paths_from_lp(lp, dnsdomain):
523 """Set the default paths for provisioning.
525 :param lp: Loadparm context.
526 :param dnsdomain: DNS Domain name
528 paths = ProvisionPaths()
529 paths.private_dir = lp.get("private dir")
530 paths.state_dir = lp.get("state directory")
532 # This is stored without path prefix for the "privateKeytab" attribute in
533 # "secrets_dns.ldif".
534 paths.dns_keytab = "dns.keytab"
535 paths.keytab = "secrets.keytab"
537 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
538 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
539 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
540 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
541 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
542 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
543 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
544 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
545 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
546 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
547 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
548 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
549 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
550 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
551 paths.hklm = "hklm.ldb"
552 paths.hkcr = "hkcr.ldb"
553 paths.hkcu = "hkcu.ldb"
554 paths.hku = "hku.ldb"
555 paths.hkpd = "hkpd.ldb"
556 paths.hkpt = "hkpt.ldb"
557 paths.sysvol = lp.get("path", "sysvol")
558 paths.netlogon = lp.get("path", "netlogon")
559 paths.smbconf = lp.configfile
560 return paths
563 def determine_netbios_name(hostname):
564 """Determine a netbios name from a hostname."""
565 # remove forbidden chars and force the length to be <16
566 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
567 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
570 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
571 serverrole=None, rootdn=None, domaindn=None, configdn=None,
572 schemadn=None, serverdn=None, sitename=None,
573 domain_names_forced=False):
574 """Guess configuration settings to use."""
576 if hostname is None:
577 hostname = socket.gethostname().split(".")[0]
579 netbiosname = lp.get("netbios name")
580 if netbiosname is None:
581 netbiosname = determine_netbios_name(hostname)
582 netbiosname = netbiosname.upper()
583 if not valid_netbios_name(netbiosname):
584 raise InvalidNetbiosName(netbiosname)
586 if dnsdomain is None:
587 dnsdomain = lp.get("realm")
588 if dnsdomain is None or dnsdomain == "":
589 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
591 dnsdomain = dnsdomain.lower()
593 if serverrole is None:
594 serverrole = lp.get("server role")
595 if serverrole is None:
596 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
598 serverrole = serverrole.lower()
600 realm = dnsdomain.upper()
602 if lp.get("realm") == "":
603 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
605 if lp.get("realm").upper() != realm:
606 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(), lp.configfile, realm))
608 if lp.get("server role").lower() != serverrole:
609 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))
611 if serverrole == "active directory domain controller":
612 if domain is None:
613 # This will, for better or worse, default to 'WORKGROUP'
614 domain = lp.get("workgroup")
615 domain = domain.upper()
617 if lp.get("workgroup").upper() != domain:
618 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))
620 if domaindn is None:
621 domaindn = samba.dn_from_dns_name(dnsdomain)
623 if domain == netbiosname:
624 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
625 else:
626 domain = netbiosname
627 if domaindn is None:
628 domaindn = "DC=" + netbiosname
630 if not valid_netbios_name(domain):
631 raise InvalidNetbiosName(domain)
633 if hostname.upper() == realm:
634 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
635 if netbiosname.upper() == realm:
636 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
637 if domain == realm and not domain_names_forced:
638 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
640 if rootdn is None:
641 rootdn = domaindn
643 if configdn is None:
644 configdn = "CN=Configuration," + rootdn
645 if schemadn is None:
646 schemadn = "CN=Schema," + configdn
648 if sitename is None:
649 sitename = DEFAULTSITE
651 names = ProvisionNames()
652 names.rootdn = rootdn
653 names.domaindn = domaindn
654 names.configdn = configdn
655 names.schemadn = schemadn
656 names.ldapmanagerdn = "CN=Manager," + rootdn
657 names.dnsdomain = dnsdomain
658 names.domain = domain
659 names.realm = realm
660 names.netbiosname = netbiosname
661 names.hostname = hostname
662 names.sitename = sitename
663 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
664 netbiosname, sitename, configdn)
666 return names
669 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
670 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
671 global_param=None):
672 """Create a new smb.conf file based on a couple of basic settings.
674 assert smbconf is not None
676 if hostname is None:
677 hostname = socket.gethostname().split(".")[0]
679 netbiosname = determine_netbios_name(hostname)
681 if serverrole is None:
682 serverrole = "standalone server"
684 assert domain is not None
685 domain = domain.upper()
687 assert realm is not None
688 realm = realm.upper()
690 global_settings = {
691 "netbios name": netbiosname,
692 "workgroup": domain,
693 "realm": realm,
694 "server role": serverrole,
697 if lp is None:
698 lp = samba.param.LoadParm()
699 #Load non-existent file
700 if os.path.exists(smbconf):
701 lp.load(smbconf)
703 if global_param is not None:
704 for ent in global_param:
705 if global_param[ent] is not None:
706 global_settings[ent] = " ".join(global_param[ent])
708 if targetdir is not None:
709 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
710 global_settings["lock dir"] = os.path.abspath(targetdir)
711 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
712 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
714 lp.set("lock dir", os.path.abspath(targetdir))
715 lp.set("state directory", global_settings["state directory"])
716 lp.set("cache directory", global_settings["cache directory"])
718 if eadb:
719 if use_ntvfs and not lp.get("posix:eadb"):
720 if targetdir is not None:
721 privdir = os.path.join(targetdir, "private")
722 else:
723 privdir = lp.get("private dir")
724 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
725 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
726 if targetdir is not None:
727 statedir = os.path.join(targetdir, "state")
728 else:
729 statedir = lp.get("state directory")
730 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
732 shares = {}
733 if serverrole == "active directory domain controller":
734 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
735 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
736 "scripts")
737 else:
738 global_settings["passdb backend"] = "samba_dsdb"
740 f = open(smbconf, 'w')
741 try:
742 f.write("[globals]\n")
743 for key, val in global_settings.iteritems():
744 f.write("\t%s = %s\n" % (key, val))
745 f.write("\n")
747 for name, path in shares.iteritems():
748 f.write("[%s]\n" % name)
749 f.write("\tpath = %s\n" % path)
750 f.write("\tread only = no\n")
751 f.write("\n")
752 finally:
753 f.close()
754 # reload the smb.conf
755 lp.load(smbconf)
757 # and dump it without any values that are the default
758 # this ensures that any smb.conf parameters that were set
759 # on the provision/join command line are set in the resulting smb.conf
760 f = open(smbconf, mode='w')
761 try:
762 lp.dump(f, False)
763 finally:
764 f.close()
767 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
768 users_gid, root_gid):
769 """setup reasonable name mappings for sam names to unix names.
771 :param samdb: SamDB object.
772 :param idmap: IDmap db object.
773 :param sid: The domain sid.
774 :param domaindn: The domain DN.
775 :param root_uid: uid of the UNIX root user.
776 :param nobody_uid: uid of the UNIX nobody user.
777 :param users_gid: gid of the UNIX users group.
778 :param root_gid: gid of the UNIX root group.
780 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
782 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
783 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
786 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
787 provision_backend, names, schema, serverrole,
788 erase=False):
789 """Setup the partitions for the SAM database.
791 Alternatively, provision() may call this, and then populate the database.
793 :note: This will wipe the Sam Database!
795 :note: This function always removes the local SAM LDB file. The erase
796 parameter controls whether to erase the existing data, which
797 may not be stored locally but in LDAP.
800 assert session_info is not None
802 # We use options=["modules:"] to stop the modules loading - we
803 # just want to wipe and re-initialise the database, not start it up
805 try:
806 os.unlink(samdb_path)
807 except OSError:
808 pass
810 samdb = Ldb(url=samdb_path, session_info=session_info,
811 lp=lp, options=["modules:"])
813 ldap_backend_line = "# No LDAP backend"
814 if provision_backend.type != "ldb":
815 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
817 samdb.transaction_start()
818 try:
819 logger.info("Setting up sam.ldb partitions and settings")
820 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
821 "LDAP_BACKEND_LINE": ldap_backend_line
825 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
826 "BACKEND_TYPE": provision_backend.type,
827 "SERVER_ROLE": serverrole
830 logger.info("Setting up sam.ldb rootDSE")
831 setup_samdb_rootdse(samdb, names)
832 except:
833 samdb.transaction_cancel()
834 raise
835 else:
836 samdb.transaction_commit()
839 def secretsdb_self_join(secretsdb, domain,
840 netbiosname, machinepass, domainsid=None,
841 realm=None, dnsdomain=None,
842 keytab_path=None,
843 key_version_number=1,
844 secure_channel_type=SEC_CHAN_WKSTA):
845 """Add domain join-specific bits to a secrets database.
847 :param secretsdb: Ldb Handle to the secrets database
848 :param machinepass: Machine password
850 attrs = ["whenChanged",
851 "secret",
852 "priorSecret",
853 "priorChanged",
854 "krb5Keytab",
855 "privateKeytab"]
857 if realm is not None:
858 if dnsdomain is None:
859 dnsdomain = realm.lower()
860 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
861 else:
862 dnsname = None
863 shortname = netbiosname.lower()
865 # We don't need to set msg["flatname"] here, because rdn_name will handle
866 # it, and it causes problems for modifies anyway
867 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
868 msg["secureChannelType"] = [str(secure_channel_type)]
869 msg["objectClass"] = ["top", "primaryDomain"]
870 if dnsname is not None:
871 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
872 msg["realm"] = [realm]
873 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
874 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
875 msg["privateKeytab"] = ["secrets.keytab"]
877 msg["secret"] = [machinepass]
878 msg["samAccountName"] = ["%s$" % netbiosname]
879 msg["secureChannelType"] = [str(secure_channel_type)]
880 if domainsid is not None:
881 msg["objectSid"] = [ndr_pack(domainsid)]
883 # This complex expression tries to ensure that we don't have more
884 # than one record for this SID, realm or netbios domain at a time,
885 # but we don't delete the old record that we are about to modify,
886 # because that would delete the keytab and previous password.
887 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
888 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
889 scope=ldb.SCOPE_ONELEVEL)
891 for del_msg in res:
892 secretsdb.delete(del_msg.dn)
894 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
896 if len(res) == 1:
897 msg["priorSecret"] = [res[0]["secret"][0]]
898 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
900 try:
901 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
902 except KeyError:
903 pass
905 try:
906 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
907 except KeyError:
908 pass
910 for el in msg:
911 if el != 'dn':
912 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
913 secretsdb.modify(msg)
914 secretsdb.rename(res[0].dn, msg.dn)
915 else:
916 spn = [ 'HOST/%s' % shortname ]
917 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
918 # we are a domain controller then we add servicePrincipalName
919 # entries for the keytab code to update.
920 spn.extend([ 'HOST/%s' % dnsname ])
921 msg["servicePrincipalName"] = spn
923 secretsdb.add(msg)
926 def setup_secretsdb(paths, session_info, backend_credentials, lp):
927 """Setup the secrets database.
929 :note: This function does not handle exceptions and transaction on purpose,
930 it's up to the caller to do this job.
932 :param path: Path to the secrets database.
933 :param session_info: Session info.
934 :param credentials: Credentials
935 :param lp: Loadparm context
936 :return: LDB handle for the created secrets database
938 if os.path.exists(paths.secrets):
939 os.unlink(paths.secrets)
941 keytab_path = os.path.join(paths.private_dir, paths.keytab)
942 if os.path.exists(keytab_path):
943 os.unlink(keytab_path)
945 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
946 if os.path.exists(dns_keytab_path):
947 os.unlink(dns_keytab_path)
949 path = paths.secrets
951 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
952 secrets_ldb.erase()
953 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
954 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
955 secrets_ldb.transaction_start()
956 try:
957 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
959 if (backend_credentials is not None and
960 backend_credentials.authentication_requested()):
961 if backend_credentials.get_bind_dn() is not None:
962 setup_add_ldif(secrets_ldb,
963 setup_path("secrets_simple_ldap.ldif"), {
964 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
965 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
967 else:
968 setup_add_ldif(secrets_ldb,
969 setup_path("secrets_sasl_ldap.ldif"), {
970 "LDAPADMINUSER": backend_credentials.get_username(),
971 "LDAPADMINREALM": backend_credentials.get_realm(),
972 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
974 except:
975 secrets_ldb.transaction_cancel()
976 raise
977 return secrets_ldb
980 def setup_privileges(path, session_info, lp):
981 """Setup the privileges database.
983 :param path: Path to the privileges database.
984 :param session_info: Session info.
985 :param credentials: Credentials
986 :param lp: Loadparm context
987 :return: LDB handle for the created secrets database
989 if os.path.exists(path):
990 os.unlink(path)
991 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
992 privilege_ldb.erase()
993 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
996 def setup_registry(path, session_info, lp):
997 """Setup the registry.
999 :param path: Path to the registry database
1000 :param session_info: Session information
1001 :param credentials: Credentials
1002 :param lp: Loadparm context
1004 reg = samba.registry.Registry()
1005 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1006 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1007 provision_reg = setup_path("provision.reg")
1008 assert os.path.exists(provision_reg)
1009 reg.diff_apply(provision_reg)
1012 def setup_idmapdb(path, session_info, lp):
1013 """Setup the idmap database.
1015 :param path: path to the idmap database
1016 :param session_info: Session information
1017 :param credentials: Credentials
1018 :param lp: Loadparm context
1020 if os.path.exists(path):
1021 os.unlink(path)
1023 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1024 idmap_ldb.erase()
1025 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1026 return idmap_ldb
1029 def setup_samdb_rootdse(samdb, names):
1030 """Setup the SamDB rootdse.
1032 :param samdb: Sam Database handle
1034 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1035 "SCHEMADN": names.schemadn,
1036 "DOMAINDN": names.domaindn,
1037 "ROOTDN" : names.rootdn,
1038 "CONFIGDN": names.configdn,
1039 "SERVERDN": names.serverdn,
1043 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1044 dns_backend, dnspass, domainsid, next_rid, invocationid,
1045 policyguid, policyguid_dc,
1046 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1047 """Join a host to its own domain."""
1048 assert isinstance(invocationid, str)
1049 if ntdsguid is not None:
1050 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1051 else:
1052 ntdsguid_line = ""
1054 if dc_rid is None:
1055 dc_rid = next_rid
1057 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1058 "CONFIGDN": names.configdn,
1059 "SCHEMADN": names.schemadn,
1060 "DOMAINDN": names.domaindn,
1061 "SERVERDN": names.serverdn,
1062 "INVOCATIONID": invocationid,
1063 "NETBIOSNAME": names.netbiosname,
1064 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1065 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1066 "DOMAINSID": str(domainsid),
1067 "DCRID": str(dc_rid),
1068 "SAMBA_VERSION_STRING": version,
1069 "NTDSGUID": ntdsguid_line,
1070 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1071 domainControllerFunctionality),
1072 "RIDALLOCATIONSTART": str(next_rid + 100),
1073 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1075 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1076 "POLICYGUID": policyguid,
1077 "POLICYGUID_DC": policyguid_dc,
1078 "DNSDOMAIN": names.dnsdomain,
1079 "DOMAINDN": names.domaindn})
1081 # If we are setting up a subdomain, then this has been replicated in, so we
1082 # don't need to add it
1083 if fill == FILL_FULL:
1084 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1085 "CONFIGDN": names.configdn,
1086 "SCHEMADN": names.schemadn,
1087 "DOMAINDN": names.domaindn,
1088 "SERVERDN": names.serverdn,
1089 "INVOCATIONID": invocationid,
1090 "NETBIOSNAME": names.netbiosname,
1091 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1092 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1093 "DOMAINSID": str(domainsid),
1094 "DCRID": str(dc_rid),
1095 "SAMBA_VERSION_STRING": version,
1096 "NTDSGUID": ntdsguid_line,
1097 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1098 domainControllerFunctionality)})
1100 # Setup fSMORoleOwner entries to point at the newly created DC entry
1101 setup_modify_ldif(samdb,
1102 setup_path("provision_self_join_modify_config.ldif"), {
1103 "CONFIGDN": names.configdn,
1104 "SCHEMADN": names.schemadn,
1105 "DEFAULTSITE": names.sitename,
1106 "NETBIOSNAME": names.netbiosname,
1107 "SERVERDN": names.serverdn,
1110 system_session_info = system_session()
1111 samdb.set_session_info(system_session_info)
1112 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1113 # modify a serverReference under cn=config when we are a subdomain, we must
1114 # be system due to ACLs
1115 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1116 "DOMAINDN": names.domaindn,
1117 "SERVERDN": names.serverdn,
1118 "NETBIOSNAME": names.netbiosname,
1121 samdb.set_session_info(admin_session_info)
1123 if dns_backend != "SAMBA_INTERNAL":
1124 # This is Samba4 specific and should be replaced by the correct
1125 # DNS AD-style setup
1126 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1127 "DNSDOMAIN": names.dnsdomain,
1128 "DOMAINDN": names.domaindn,
1129 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1130 "HOSTNAME" : names.hostname,
1131 "DNSNAME" : '%s.%s' % (
1132 names.netbiosname.lower(), names.dnsdomain.lower())
1136 def getpolicypath(sysvolpath, dnsdomain, guid):
1137 """Return the physical path of policy given its guid.
1139 :param sysvolpath: Path to the sysvol folder
1140 :param dnsdomain: DNS name of the AD domain
1141 :param guid: The GUID of the policy
1142 :return: A string with the complete path to the policy folder
1144 if guid[0] != "{":
1145 guid = "{%s}" % guid
1146 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1147 return policy_path
1150 def create_gpo_struct(policy_path):
1151 if not os.path.exists(policy_path):
1152 os.makedirs(policy_path, 0775)
1153 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1154 try:
1155 f.write("[General]\r\nVersion=0")
1156 finally:
1157 f.close()
1158 p = os.path.join(policy_path, "MACHINE")
1159 if not os.path.exists(p):
1160 os.makedirs(p, 0775)
1161 p = os.path.join(policy_path, "USER")
1162 if not os.path.exists(p):
1163 os.makedirs(p, 0775)
1166 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1167 """Create the default GPO for a domain
1169 :param sysvolpath: Physical path for the sysvol folder
1170 :param dnsdomain: DNS domain name of the AD domain
1171 :param policyguid: GUID of the default domain policy
1172 :param policyguid_dc: GUID of the default domain controler policy
1174 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1175 create_gpo_struct(policy_path)
1177 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1178 create_gpo_struct(policy_path)
1181 def setup_samdb(path, session_info, provision_backend, lp, names,
1182 logger, fill, serverrole, schema, am_rodc=False):
1183 """Setup a complete SAM Database.
1185 :note: This will wipe the main SAM database file!
1188 # Also wipes the database
1189 setup_samdb_partitions(path, logger=logger, lp=lp,
1190 provision_backend=provision_backend, session_info=session_info,
1191 names=names, serverrole=serverrole, schema=schema)
1193 # Load the database, but don's load the global schema and don't connect
1194 # quite yet
1195 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1196 credentials=provision_backend.credentials, lp=lp,
1197 global_schema=False, am_rodc=am_rodc)
1199 logger.info("Pre-loading the Samba 4 and AD schema")
1201 # Load the schema from the one we computed earlier
1202 samdb.set_schema(schema, write_indices_and_attributes=False)
1204 # Set the NTDS settings DN manually - in order to have it already around
1205 # before the provisioned tree exists and we connect
1206 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1208 # And now we can connect to the DB - the schema won't be loaded from the
1209 # DB
1210 try:
1211 samdb.connect(path)
1212 except ldb.LdbError, (num, string_error):
1213 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1214 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1215 else:
1216 raise
1218 # But we have to give it one more kick to have it use the schema
1219 # during provision - it needs, now that it is connected, to write
1220 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1221 samdb.set_schema(schema, write_indices_and_attributes=True)
1223 return samdb
1226 def fill_samdb(samdb, lp, names, logger, policyguid,
1227 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1228 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1229 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1231 if next_rid is None:
1232 next_rid = 1000
1234 # Provision does not make much sense values larger than 1000000000
1235 # as the upper range of the rIDAvailablePool is 1073741823 and
1236 # we don't want to create a domain that cannot allocate rids.
1237 if next_rid < 1000 or next_rid > 1000000000:
1238 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1239 error += "the valid range is %u-%u. The default is %u." % (
1240 1000, 1000000000, 1000)
1241 raise ProvisioningError(error)
1243 # ATTENTION: Do NOT change these default values without discussion with the
1244 # team and/or release manager. They have a big impact on the whole program!
1245 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1247 if dom_for_fun_level is None:
1248 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1250 if dom_for_fun_level > domainControllerFunctionality:
1251 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!")
1253 domainFunctionality = dom_for_fun_level
1254 forestFunctionality = dom_for_fun_level
1256 # Set the NTDS settings DN manually - in order to have it already around
1257 # before the provisioned tree exists and we connect
1258 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1260 samdb.transaction_start()
1261 try:
1262 # Set the domain functionality levels onto the database.
1263 # Various module (the password_hash module in particular) need
1264 # to know what level of AD we are emulating.
1266 # These will be fixed into the database via the database
1267 # modifictions below, but we need them set from the start.
1268 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1269 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1270 samdb.set_opaque_integer("domainControllerFunctionality",
1271 domainControllerFunctionality)
1273 samdb.set_domain_sid(str(names.domainsid))
1274 samdb.set_invocation_id(invocationid)
1276 logger.info("Adding DomainDN: %s" % names.domaindn)
1278 # impersonate domain admin
1279 admin_session_info = admin_session(lp, str(names.domainsid))
1280 samdb.set_session_info(admin_session_info)
1281 if names.domainguid is not None:
1282 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1283 else:
1284 domainguid_line = ""
1286 descr = b64encode(get_domain_descriptor(names.domainsid))
1287 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1288 "DOMAINDN": names.domaindn,
1289 "DOMAINSID": str(names.domainsid),
1290 "DESCRIPTOR": descr,
1291 "DOMAINGUID": domainguid_line
1294 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1295 "DOMAINDN": names.domaindn,
1296 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1297 "NEXTRID": str(next_rid),
1298 "DEFAULTSITE": names.sitename,
1299 "CONFIGDN": names.configdn,
1300 "POLICYGUID": policyguid,
1301 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1302 "SAMBA_VERSION_STRING": version
1305 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1306 if fill == FILL_FULL:
1307 logger.info("Adding configuration container")
1308 descr = b64encode(get_config_descriptor(names.domainsid))
1309 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1310 "CONFIGDN": names.configdn,
1311 "DESCRIPTOR": descr,
1314 # The LDIF here was created when the Schema object was constructed
1315 logger.info("Setting up sam.ldb schema")
1316 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1317 samdb.modify_ldif(schema.schema_dn_modify)
1318 samdb.write_prefixes_from_schema()
1319 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1320 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1321 {"SCHEMADN": names.schemadn})
1323 # Now register this container in the root of the forest
1324 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1325 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1326 "subRefs")
1328 except:
1329 samdb.transaction_cancel()
1330 raise
1331 else:
1332 samdb.transaction_commit()
1334 samdb.transaction_start()
1335 try:
1336 samdb.invocation_id = invocationid
1338 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1339 if fill == FILL_FULL:
1340 logger.info("Setting up sam.ldb configuration data")
1342 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid))
1343 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid))
1344 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid))
1345 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid))
1346 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1347 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid))
1349 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1350 "CONFIGDN": names.configdn,
1351 "NETBIOSNAME": names.netbiosname,
1352 "DEFAULTSITE": names.sitename,
1353 "DNSDOMAIN": names.dnsdomain,
1354 "DOMAIN": names.domain,
1355 "SCHEMADN": names.schemadn,
1356 "DOMAINDN": names.domaindn,
1357 "SERVERDN": names.serverdn,
1358 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1359 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1360 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1361 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1362 "SERVICES_DESCRIPTOR": protected1_descr,
1363 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1364 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1365 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1366 "PARTITIONS_DESCRIPTOR": partitions_descr,
1367 "SITES_DESCRIPTOR": sites_descr,
1370 logger.info("Setting up display specifiers")
1371 display_specifiers_ldif = read_ms_ldif(
1372 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1373 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1374 {"CONFIGDN": names.configdn})
1375 check_all_substituted(display_specifiers_ldif)
1376 samdb.add_ldif(display_specifiers_ldif)
1378 logger.info("Modifying display specifiers")
1379 setup_modify_ldif(samdb,
1380 setup_path("provision_configuration_modify.ldif"), {
1381 "CONFIGDN": names.configdn,
1382 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1385 logger.info("Adding users container")
1386 users_desc = b64encode(get_domain_users_descriptor(names.domainsid))
1387 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1388 "DOMAINDN": names.domaindn,
1389 "USERS_DESCRIPTOR": users_desc
1391 logger.info("Modifying users container")
1392 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1393 "DOMAINDN": names.domaindn})
1394 logger.info("Adding computers container")
1395 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid))
1396 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1397 "DOMAINDN": names.domaindn,
1398 "COMPUTERS_DESCRIPTOR": computers_desc
1400 logger.info("Modifying computers container")
1401 setup_modify_ldif(samdb,
1402 setup_path("provision_computers_modify.ldif"), {
1403 "DOMAINDN": names.domaindn})
1404 logger.info("Setting up sam.ldb data")
1405 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid))
1406 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid))
1407 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid))
1408 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid))
1409 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid))
1410 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1411 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1412 "DOMAINDN": names.domaindn,
1413 "NETBIOSNAME": names.netbiosname,
1414 "DEFAULTSITE": names.sitename,
1415 "CONFIGDN": names.configdn,
1416 "SERVERDN": names.serverdn,
1417 "RIDAVAILABLESTART": str(next_rid + 600),
1418 "POLICYGUID_DC": policyguid_dc,
1419 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1420 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1421 "SYSTEM_DESCRIPTOR": system_desc,
1422 "BUILTIN_DESCRIPTOR": builtin_desc,
1423 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1426 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1427 if fill == FILL_FULL:
1428 setup_modify_ldif(samdb,
1429 setup_path("provision_configuration_references.ldif"), {
1430 "CONFIGDN": names.configdn,
1431 "SCHEMADN": names.schemadn})
1433 logger.info("Setting up well known security principals")
1434 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1435 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1436 "CONFIGDN": names.configdn,
1437 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1440 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1441 setup_modify_ldif(samdb,
1442 setup_path("provision_basedn_references.ldif"),
1443 {"DOMAINDN": names.domaindn})
1445 logger.info("Setting up sam.ldb users and groups")
1446 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1447 "DOMAINDN": names.domaindn,
1448 "DOMAINSID": str(names.domainsid),
1449 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1450 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1453 logger.info("Setting up self join")
1454 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1455 invocationid=invocationid,
1456 dns_backend=dns_backend,
1457 dnspass=dnspass,
1458 machinepass=machinepass,
1459 domainsid=names.domainsid,
1460 next_rid=next_rid,
1461 dc_rid=dc_rid,
1462 policyguid=policyguid,
1463 policyguid_dc=policyguid_dc,
1464 domainControllerFunctionality=domainControllerFunctionality,
1465 ntdsguid=ntdsguid)
1467 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1468 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1469 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1470 assert isinstance(names.ntdsguid, str)
1471 except:
1472 samdb.transaction_cancel()
1473 raise
1474 else:
1475 samdb.transaction_commit()
1476 return samdb
1479 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1480 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)"
1481 SYSVOL_SERVICE="sysvol"
1483 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1484 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1485 for root, dirs, files in os.walk(path, topdown=False):
1486 for name in files:
1487 setntacl(lp, os.path.join(root, name), acl, domsid,
1488 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1489 for name in dirs:
1490 setntacl(lp, os.path.join(root, name), acl, domsid,
1491 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1494 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1495 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1496 folders beneath.
1498 :param sysvol: Physical path for the sysvol folder
1499 :param dnsdomain: The DNS name of the domain
1500 :param domainsid: The SID of the domain
1501 :param domaindn: The DN of the domain (ie. DC=...)
1502 :param samdb: An LDB object on the SAM db
1503 :param lp: an LP object
1506 # Set ACL for GPO root folder
1507 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1508 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1509 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1511 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1512 attrs=["cn", "nTSecurityDescriptor"],
1513 expression="", scope=ldb.SCOPE_ONELEVEL)
1515 for policy in res:
1516 acl = ndr_unpack(security.descriptor,
1517 str(policy["nTSecurityDescriptor"])).as_sddl()
1518 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1519 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1520 str(domainsid), use_ntvfs,
1521 passdb=passdb)
1524 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1525 domaindn, lp, use_ntvfs):
1526 """Set the ACL for the sysvol share and the subfolders
1528 :param samdb: An LDB object on the SAM db
1529 :param netlogon: Physical path for the netlogon folder
1530 :param sysvol: Physical path for the sysvol folder
1531 :param uid: The UID of the "Administrator" user
1532 :param gid: The GID of the "Domain adminstrators" group
1533 :param domainsid: The SID of the domain
1534 :param dnsdomain: The DNS name of the domain
1535 :param domaindn: The DN of the domain (ie. DC=...)
1537 s4_passdb = None
1539 if not use_ntvfs:
1540 s3conf = s3param.get_context()
1541 s3conf.load(lp.configfile)
1543 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1544 try:
1545 try:
1546 smbd.set_simple_acl(file.name, 0755, gid)
1547 except OSError:
1548 if not smbd.have_posix_acls():
1549 # This clue is only strictly correct for RPM and
1550 # Debian-like Linux systems, but hopefully other users
1551 # will get enough clue from it.
1552 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1553 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1555 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1556 "Try the mounting the filesystem with the 'acl' option.")
1557 try:
1558 smbd.chown(file.name, uid, gid)
1559 except OSError:
1560 raise ProvisioningError("Unable to chown a file on your filesystem. "
1561 "You may not be running provision as root.")
1562 finally:
1563 file.close()
1565 # This will ensure that the smbd code we are running when setting ACLs
1566 # is initialised with the smb.conf
1567 s3conf = s3param.get_context()
1568 s3conf.load(lp.configfile)
1569 # ensure we are using the right samba_dsdb passdb backend, no matter what
1570 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1571 passdb.reload_static_pdb()
1573 # ensure that we init the samba_dsdb backend, so the domain sid is
1574 # marked in secrets.tdb
1575 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1577 # now ensure everything matches correctly, to avoid wierd issues
1578 if passdb.get_global_sam_sid() != domainsid:
1579 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))
1581 domain_info = s4_passdb.domain_info()
1582 if domain_info["dom_sid"] != domainsid:
1583 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))
1585 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1586 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()))
1589 try:
1590 if use_ntvfs:
1591 os.chown(sysvol, -1, gid)
1592 except OSError:
1593 canchown = False
1594 else:
1595 canchown = True
1597 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1598 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1599 skip_invalid_chown=True, passdb=s4_passdb,
1600 service=SYSVOL_SERVICE)
1601 for root, dirs, files in os.walk(sysvol, topdown=False):
1602 for name in files:
1603 if use_ntvfs and canchown:
1604 os.chown(os.path.join(root, name), -1, gid)
1605 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1606 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1607 passdb=s4_passdb, service=SYSVOL_SERVICE)
1608 for name in dirs:
1609 if use_ntvfs and canchown:
1610 os.chown(os.path.join(root, name), -1, gid)
1611 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1612 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1613 passdb=s4_passdb, service=SYSVOL_SERVICE)
1615 # Set acls on Policy folder and policies folders
1616 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1618 def acl_type(direct_db_access):
1619 if direct_db_access:
1620 return "DB"
1621 else:
1622 return "VFS"
1624 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1625 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1626 fsacl_sddl = fsacl.as_sddl(domainsid)
1627 if fsacl_sddl != acl:
1628 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))
1630 for root, dirs, files in os.walk(path, topdown=False):
1631 for name in files:
1632 fsacl = getntacl(lp, os.path.join(root, name),
1633 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1634 if fsacl is None:
1635 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1636 fsacl_sddl = fsacl.as_sddl(domainsid)
1637 if fsacl_sddl != acl:
1638 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))
1640 for name in dirs:
1641 fsacl = getntacl(lp, os.path.join(root, name),
1642 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1643 if fsacl is None:
1644 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1645 fsacl_sddl = fsacl.as_sddl(domainsid)
1646 if fsacl_sddl != acl:
1647 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))
1650 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1651 direct_db_access):
1652 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1653 folders beneath.
1655 :param sysvol: Physical path for the sysvol folder
1656 :param dnsdomain: The DNS name of the domain
1657 :param domainsid: The SID of the domain
1658 :param domaindn: The DN of the domain (ie. DC=...)
1659 :param samdb: An LDB object on the SAM db
1660 :param lp: an LP object
1663 # Set ACL for GPO root folder
1664 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1665 fsacl = getntacl(lp, root_policy_path,
1666 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1667 if fsacl is None:
1668 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1669 fsacl_sddl = fsacl.as_sddl(domainsid)
1670 if fsacl_sddl != POLICIES_ACL:
1671 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))
1672 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1673 attrs=["cn", "nTSecurityDescriptor"],
1674 expression="", scope=ldb.SCOPE_ONELEVEL)
1676 for policy in res:
1677 acl = ndr_unpack(security.descriptor,
1678 str(policy["nTSecurityDescriptor"])).as_sddl()
1679 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1680 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1681 domainsid, direct_db_access)
1684 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1685 lp):
1686 """Set the ACL for the sysvol share and the subfolders
1688 :param samdb: An LDB object on the SAM db
1689 :param netlogon: Physical path for the netlogon folder
1690 :param sysvol: Physical path for the sysvol folder
1691 :param uid: The UID of the "Administrator" user
1692 :param gid: The GID of the "Domain adminstrators" group
1693 :param domainsid: The SID of the domain
1694 :param dnsdomain: The DNS name of the domain
1695 :param domaindn: The DN of the domain (ie. DC=...)
1698 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1699 s3conf = s3param.get_context()
1700 s3conf.load(lp.configfile)
1701 # ensure we are using the right samba_dsdb passdb backend, no matter what
1702 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1703 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1704 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1706 # now ensure everything matches correctly, to avoid wierd issues
1707 if passdb.get_global_sam_sid() != domainsid:
1708 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))
1710 domain_info = s4_passdb.domain_info()
1711 if domain_info["dom_sid"] != domainsid:
1712 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))
1714 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1715 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()))
1717 # Ensure we can read this directly, and via the smbd VFS
1718 for direct_db_access in [True, False]:
1719 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1720 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1721 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1722 if fsacl is None:
1723 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1724 fsacl_sddl = fsacl.as_sddl(domainsid)
1725 if fsacl_sddl != SYSVOL_ACL:
1726 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))
1728 # Check acls on Policy folder and policies folders
1729 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1730 direct_db_access)
1733 def interface_ips_v4(lp):
1734 """return only IPv4 IPs"""
1735 ips = samba.interface_ips(lp, False)
1736 ret = []
1737 for i in ips:
1738 if i.find(':') == -1:
1739 ret.append(i)
1740 return ret
1743 def interface_ips_v6(lp):
1744 """return only IPv6 IPs"""
1745 ips = samba.interface_ips(lp, False)
1746 ret = []
1747 for i in ips:
1748 if i.find(':') != -1:
1749 ret.append(i)
1750 return ret
1753 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1754 schema=None,
1755 targetdir=None, samdb_fill=FILL_FULL,
1756 hostip=None, hostip6=None,
1757 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1758 domainguid=None, policyguid=None, policyguid_dc=None,
1759 invocationid=None, machinepass=None, ntdsguid=None,
1760 dns_backend=None, dnspass=None,
1761 serverrole=None, dom_for_fun_level=None,
1762 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1763 # create/adapt the group policy GUIDs
1764 # Default GUID for default policy are described at
1765 # "How Core Group Policy Works"
1766 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1767 if policyguid is None:
1768 policyguid = DEFAULT_POLICY_GUID
1769 policyguid = policyguid.upper()
1770 if policyguid_dc is None:
1771 policyguid_dc = DEFAULT_DC_POLICY_GUID
1772 policyguid_dc = policyguid_dc.upper()
1774 if invocationid is None:
1775 invocationid = str(uuid.uuid4())
1777 if krbtgtpass is None:
1778 krbtgtpass = samba.generate_random_password(128, 255)
1779 if machinepass is None:
1780 machinepass = samba.generate_random_password(128, 255)
1781 if dnspass is None:
1782 dnspass = samba.generate_random_password(128, 255)
1784 samdb = fill_samdb(samdb, lp, names, logger=logger,
1785 schema=schema,
1786 policyguid=policyguid, policyguid_dc=policyguid_dc,
1787 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1788 invocationid=invocationid, machinepass=machinepass,
1789 dns_backend=dns_backend, dnspass=dnspass,
1790 ntdsguid=ntdsguid, serverrole=serverrole,
1791 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1792 next_rid=next_rid, dc_rid=dc_rid)
1794 if serverrole == "active directory domain controller":
1796 # Set up group policies (domain policy and domain controller
1797 # policy)
1798 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1799 policyguid_dc)
1800 if not skip_sysvolacl:
1801 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1802 paths.root_gid, names.domainsid, names.dnsdomain,
1803 names.domaindn, lp, use_ntvfs)
1804 else:
1805 logger.info("Setting acl on sysvol skipped")
1807 secretsdb_self_join(secrets_ldb, domain=names.domain,
1808 realm=names.realm, dnsdomain=names.dnsdomain,
1809 netbiosname=names.netbiosname, domainsid=names.domainsid,
1810 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1812 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1813 # In future, this might be determined from some configuration
1814 kerberos_enctypes = str(ENC_ALL_TYPES)
1816 try:
1817 msg = ldb.Message(ldb.Dn(samdb,
1818 samdb.searchone("distinguishedName",
1819 expression="samAccountName=%s$" % names.netbiosname,
1820 scope=ldb.SCOPE_SUBTREE)))
1821 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1822 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1823 name="msDS-SupportedEncryptionTypes")
1824 samdb.modify(msg)
1825 except ldb.LdbError, (enum, estr):
1826 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1827 # It might be that this attribute does not exist in this schema
1828 raise
1830 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1831 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1832 dnspass=dnspass, os_level=dom_for_fun_level,
1833 targetdir=targetdir, fill_level=samdb_fill)
1835 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1836 attribute="objectGUID")
1837 assert isinstance(domainguid, str)
1839 lastProvisionUSNs = get_last_provision_usn(samdb)
1840 maxUSN = get_max_usn(samdb, str(names.rootdn))
1841 if lastProvisionUSNs is not None:
1842 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1843 else:
1844 set_provision_usn(samdb, 0, maxUSN, invocationid)
1846 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1847 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1848 { 'NTDSGUID' : names.ntdsguid })
1850 # fix any dangling GUIDs from the provision
1851 logger.info("Fixing provision GUIDs")
1852 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1853 quiet=True)
1854 samdb.transaction_start()
1855 try:
1856 # a small number of GUIDs are missing because of ordering issues in the
1857 # provision code
1858 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1859 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1860 scope=ldb.SCOPE_BASE,
1861 attrs=['defaultObjectCategory'])
1862 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1863 scope=ldb.SCOPE_ONELEVEL,
1864 attrs=['ipsecOwnersReference',
1865 'ipsecFilterReference',
1866 'ipsecISAKMPReference',
1867 'ipsecNegotiationPolicyReference',
1868 'ipsecNFAReference'])
1869 except:
1870 samdb.transaction_cancel()
1871 raise
1872 else:
1873 samdb.transaction_commit()
1876 _ROLES_MAP = {
1877 "ROLE_STANDALONE": "standalone server",
1878 "ROLE_DOMAIN_MEMBER": "member server",
1879 "ROLE_DOMAIN_BDC": "active directory domain controller",
1880 "ROLE_DOMAIN_PDC": "active directory domain controller",
1881 "dc": "active directory domain controller",
1882 "member": "member server",
1883 "domain controller": "active directory domain controller",
1884 "active directory domain controller": "active directory domain controller",
1885 "member server": "member server",
1886 "standalone": "standalone server",
1887 "standalone server": "standalone server",
1891 def sanitize_server_role(role):
1892 """Sanitize a server role name.
1894 :param role: Server role
1895 :raise ValueError: If the role can not be interpreted
1896 :return: Sanitized server role (one of "member server",
1897 "active directory domain controller", "standalone server")
1899 try:
1900 return _ROLES_MAP[role]
1901 except KeyError:
1902 raise ValueError(role)
1905 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1906 maxuid, maxgid):
1907 """Create AD entries for the fake ypserver.
1909 This is needed for being able to manipulate posix attrs via ADUC.
1911 samdb.transaction_start()
1912 try:
1913 logger.info("Setting up fake yp server settings")
1914 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1915 "DOMAINDN": domaindn,
1916 "NETBIOSNAME": netbiosname,
1917 "NISDOMAIN": nisdomain,
1919 except:
1920 samdb.transaction_cancel()
1921 raise
1922 else:
1923 samdb.transaction_commit()
1926 def provision(logger, session_info, smbconf=None,
1927 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1928 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1929 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1930 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1931 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1932 dns_backend=None, dns_forwarder=None, dnspass=None,
1933 invocationid=None, machinepass=None, ntdsguid=None,
1934 root=None, nobody=None, users=None, backup=None, aci=None,
1935 serverrole=None, dom_for_fun_level=None, backend_type=None,
1936 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1937 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1938 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
1939 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, ldap_backend_extra_port=None):
1940 """Provision samba4
1942 :note: caution, this wipes all existing data!
1945 try:
1946 serverrole = sanitize_server_role(serverrole)
1947 except ValueError:
1948 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1950 if ldapadminpass is None:
1951 # Make a new, random password between Samba and it's LDAP server
1952 ldapadminpass = samba.generate_random_password(128, 255)
1954 if backend_type is None:
1955 backend_type = "ldb"
1957 if domainsid is None:
1958 domainsid = security.random_sid()
1960 root_uid = findnss_uid([root or "root"])
1961 nobody_uid = findnss_uid([nobody or "nobody"])
1962 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1963 root_gid = pwd.getpwuid(root_uid).pw_gid
1965 try:
1966 bind_gid = findnss_gid(["bind", "named"])
1967 except KeyError:
1968 bind_gid = None
1970 if targetdir is not None:
1971 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1972 elif smbconf is None:
1973 smbconf = samba.param.default_path()
1974 if not os.path.exists(os.path.dirname(smbconf)):
1975 os.makedirs(os.path.dirname(smbconf))
1977 server_services = []
1978 global_param = {}
1979 if use_rfc2307:
1980 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1982 if dns_backend != "SAMBA_INTERNAL":
1983 server_services.append("-dns")
1984 else:
1985 if dns_forwarder is not None:
1986 global_param["dns forwarder"] = [dns_forwarder]
1988 if use_ntvfs:
1989 server_services.append("+smb")
1990 server_services.append("-s3fs")
1991 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1993 if len(server_services) > 0:
1994 global_param["server services"] = server_services
1996 # only install a new smb.conf if there isn't one there already
1997 if os.path.exists(smbconf):
1998 # if Samba Team members can't figure out the weird errors
1999 # loading an empty smb.conf gives, then we need to be smarter.
2000 # Pretend it just didn't exist --abartlet
2001 f = open(smbconf, 'r')
2002 try:
2003 data = f.read().lstrip()
2004 finally:
2005 f.close()
2006 if data is None or data == "":
2007 make_smbconf(smbconf, hostname, domain, realm,
2008 targetdir, serverrole=serverrole,
2009 eadb=useeadb, use_ntvfs=use_ntvfs,
2010 lp=lp, global_param=global_param)
2011 else:
2012 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2013 serverrole=serverrole,
2014 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2016 if lp is None:
2017 lp = samba.param.LoadParm()
2018 lp.load(smbconf)
2019 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2020 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2021 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2022 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2023 paths = provision_paths_from_lp(lp, names.dnsdomain)
2025 paths.bind_gid = bind_gid
2026 paths.root_uid = root_uid;
2027 paths.root_gid = root_gid
2029 if hostip is None:
2030 logger.info("Looking up IPv4 addresses")
2031 hostips = interface_ips_v4(lp)
2032 if len(hostips) > 0:
2033 hostip = hostips[0]
2034 if len(hostips) > 1:
2035 logger.warning("More than one IPv4 address found. Using %s",
2036 hostip)
2037 if hostip == "127.0.0.1":
2038 hostip = None
2039 if hostip is None:
2040 logger.warning("No IPv4 address will be assigned")
2042 if hostip6 is None:
2043 logger.info("Looking up IPv6 addresses")
2044 hostips = interface_ips_v6(lp)
2045 if hostips:
2046 hostip6 = hostips[0]
2047 if len(hostips) > 1:
2048 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2049 if hostip6 is None:
2050 logger.warning("No IPv6 address will be assigned")
2052 names.hostip = hostip
2053 names.hostip6 = hostip6
2054 names.domainguid = domainguid
2055 names.domainsid = domainsid
2056 names.forestsid = domainsid
2058 if serverrole is None:
2059 serverrole = lp.get("server role")
2061 if not os.path.exists(paths.private_dir):
2062 os.mkdir(paths.private_dir)
2063 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
2064 os.makedirs(os.path.join(paths.private_dir, "tls"), 0700)
2065 if not os.path.exists(paths.state_dir):
2066 os.mkdir(paths.state_dir)
2068 if paths.sysvol and not os.path.exists(paths.sysvol):
2069 os.makedirs(paths.sysvol, 0775)
2071 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2073 schema = Schema(domainsid, invocationid=invocationid,
2074 schemadn=names.schemadn)
2076 if backend_type == "ldb":
2077 provision_backend = LDBBackend(backend_type, paths=paths,
2078 lp=lp,
2079 names=names, logger=logger)
2080 elif backend_type == "existing":
2081 # If support for this is ever added back, then the URI will need to be
2082 # specified again
2083 provision_backend = ExistingBackend(backend_type, paths=paths,
2084 lp=lp,
2085 names=names, logger=logger,
2086 ldap_backend_forced_uri=ldap_backend_forced_uri)
2087 elif backend_type == "fedora-ds":
2088 provision_backend = FDSBackend(backend_type, paths=paths,
2089 lp=lp,
2090 names=names, logger=logger, domainsid=domainsid,
2091 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2092 slapd_path=slapd_path,
2093 root=root)
2094 elif backend_type == "openldap":
2095 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2096 lp=lp,
2097 names=names, logger=logger, domainsid=domainsid,
2098 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2099 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2100 ldap_backend_extra_port=ldap_backend_extra_port,
2101 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2102 ldap_backend_forced_uri=ldap_backend_forced_uri)
2103 else:
2104 raise ValueError("Unknown LDAP backend type selected")
2106 provision_backend.init()
2107 provision_backend.start()
2109 # only install a new shares config db if there is none
2110 if not os.path.exists(paths.shareconf):
2111 logger.info("Setting up share.ldb")
2112 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2113 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2115 logger.info("Setting up secrets.ldb")
2116 secrets_ldb = setup_secretsdb(paths,
2117 session_info=session_info,
2118 backend_credentials=provision_backend.credentials, lp=lp)
2120 try:
2121 logger.info("Setting up the registry")
2122 setup_registry(paths.hklm, session_info, lp=lp)
2124 logger.info("Setting up the privileges database")
2125 setup_privileges(paths.privilege, session_info, lp=lp)
2127 logger.info("Setting up idmap db")
2128 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2130 setup_name_mappings(idmap, sid=str(domainsid),
2131 root_uid=root_uid, nobody_uid=nobody_uid,
2132 users_gid=users_gid, root_gid=root_gid)
2134 logger.info("Setting up SAM db")
2135 samdb = setup_samdb(paths.samdb, session_info,
2136 provision_backend, lp, names, logger=logger,
2137 serverrole=serverrole,
2138 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2140 if serverrole == "active directory domain controller":
2141 if paths.netlogon is None:
2142 raise MissingShareError("netlogon", paths.smbconf)
2144 if paths.sysvol is None:
2145 raise MissingShareError("sysvol", paths.smbconf)
2147 if not os.path.isdir(paths.netlogon):
2148 os.makedirs(paths.netlogon, 0755)
2150 if adminpass is None:
2151 adminpass = samba.generate_random_password(12, 32)
2152 adminpass_generated = True
2153 else:
2154 adminpass = unicode(adminpass, 'utf-8')
2155 adminpass_generated = False
2157 if samdb_fill == FILL_FULL:
2158 provision_fill(samdb, secrets_ldb, logger, names, paths,
2159 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2160 hostip=hostip, hostip6=hostip6,
2161 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2162 krbtgtpass=krbtgtpass,
2163 policyguid=policyguid, policyguid_dc=policyguid_dc,
2164 invocationid=invocationid, machinepass=machinepass,
2165 ntdsguid=ntdsguid, dns_backend=dns_backend,
2166 dnspass=dnspass, serverrole=serverrole,
2167 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2168 lp=lp, use_ntvfs=use_ntvfs,
2169 skip_sysvolacl=skip_sysvolacl)
2171 create_krb5_conf(paths.krb5conf,
2172 dnsdomain=names.dnsdomain, hostname=names.hostname,
2173 realm=names.realm)
2174 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2175 "generated at %s", paths.krb5conf)
2177 if serverrole == "active directory domain controller":
2178 create_dns_update_list(lp, logger, paths)
2180 backend_result = provision_backend.post_setup()
2181 provision_backend.shutdown()
2183 except:
2184 secrets_ldb.transaction_cancel()
2185 raise
2187 # Now commit the secrets.ldb to disk
2188 secrets_ldb.transaction_commit()
2190 # the commit creates the dns.keytab, now chown it
2191 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2192 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2193 try:
2194 os.chmod(dns_keytab_path, 0640)
2195 os.chown(dns_keytab_path, -1, paths.bind_gid)
2196 except OSError:
2197 if not os.environ.has_key('SAMBA_SELFTEST'):
2198 logger.info("Failed to chown %s to bind gid %u",
2199 dns_keytab_path, paths.bind_gid)
2201 result = ProvisionResult()
2202 result.server_role = serverrole
2203 result.domaindn = domaindn
2204 result.paths = paths
2205 result.names = names
2206 result.lp = lp
2207 result.samdb = samdb
2208 result.idmap = idmap
2209 result.domainsid = str(domainsid)
2211 if samdb_fill == FILL_FULL:
2212 result.adminpass_generated = adminpass_generated
2213 result.adminpass = adminpass
2214 else:
2215 result.adminpass_generated = False
2216 result.adminpass = None
2218 result.backend_result = backend_result
2220 if use_rfc2307:
2221 provision_fake_ypserver(logger=logger, samdb=samdb,
2222 domaindn=names.domaindn, netbiosname=names.netbiosname,
2223 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2225 return result
2228 def provision_become_dc(smbconf=None, targetdir=None,
2229 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2230 serverdn=None, domain=None, hostname=None, domainsid=None,
2231 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2232 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2233 dns_backend=None, root=None, nobody=None, users=None,
2234 backup=None, serverrole=None, ldap_backend=None,
2235 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2237 logger = logging.getLogger("provision")
2238 samba.set_debug_level(debuglevel)
2240 res = provision(logger, system_session(),
2241 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2242 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2243 configdn=configdn, serverdn=serverdn, domain=domain,
2244 hostname=hostname, hostip=None, domainsid=domainsid,
2245 machinepass=machinepass,
2246 serverrole="active directory domain controller",
2247 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2248 use_ntvfs=use_ntvfs)
2249 res.lp.set("debuglevel", str(debuglevel))
2250 return res
2253 def create_krb5_conf(path, dnsdomain, hostname, realm):
2254 """Write out a file containing a valid krb5.conf file
2256 :param path: Path of the new krb5.conf file.
2257 :param dnsdomain: DNS Domain name
2258 :param hostname: Local hostname
2259 :param realm: Realm name
2261 setup_file(setup_path("krb5.conf"), path, {
2262 "DNSDOMAIN": dnsdomain,
2263 "HOSTNAME": hostname,
2264 "REALM": realm,
2268 class ProvisioningError(Exception):
2269 """A generic provision error."""
2271 def __init__(self, value):
2272 self.value = value
2274 def __str__(self):
2275 return "ProvisioningError: " + self.value
2278 class InvalidNetbiosName(Exception):
2279 """A specified name was not a valid NetBIOS name."""
2281 def __init__(self, name):
2282 super(InvalidNetbiosName, self).__init__(
2283 "The name '%r' is not a valid NetBIOS name" % name)
2286 class MissingShareError(ProvisioningError):
2288 def __init__(self, name, smbconf):
2289 super(MissingShareError, self).__init__(
2290 "Existing smb.conf does not have a [%s] share, but you are "
2291 "configuring a DC. Please remove %s or add the share manually." %
2292 (name, smbconf))