nfs4acls: Introduce a helper variable
[Samba.git] / python / samba / provision / __init__.py
blob953bd0f3bd311beb69c9c80d9e2d646865f2215e
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 if len(res4) == 0:
249 raise ProvisioningError("Unable to find DC called CN=%s under OU=Domain Controllers,%s" % (names.netbiosname, basedn))
251 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
253 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
254 attrs=[], base=names.configdn)
255 names.serverdn = str(server_res[0].dn)
257 # invocation id/objectguid
258 res5 = samdb.search(expression="(objectClass=*)",
259 base="CN=NTDS Settings,%s" % str(names.serverdn),
260 scope=ldb.SCOPE_BASE,
261 attrs=["invocationID", "objectGUID"])
262 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
263 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
265 # domain guid/sid
266 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
267 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
268 "objectSid","msDS-Behavior-Version" ])
269 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
270 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
271 names.forestsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
272 if res6[0].get("msDS-Behavior-Version") is None or \
273 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
274 names.domainlevel = DS_DOMAIN_FUNCTION_2000
275 else:
276 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
278 # policy guid
279 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
280 base="CN=Policies,CN=System," + basedn,
281 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
282 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
283 # dc policy guid
284 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
285 " Policy)",
286 base="CN=Policies,CN=System," + basedn,
287 scope=ldb.SCOPE_ONELEVEL,
288 attrs=["cn","displayName"])
289 if len(res8) == 1:
290 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
291 else:
292 names.policyid_dc = None
294 res9 = idmapdb.search(expression="(cn=%s-%s)" %
295 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
296 attrs=["xidNumber", "type"])
297 if len(res9) != 1:
298 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
299 if res9[0]["type"][0] == "ID_TYPE_BOTH":
300 names.root_gid = res9[0]["xidNumber"][0]
301 else:
302 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
304 res10 = samdb.search(expression="(samaccountname=dns)",
305 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
306 controls=["search_options:1:2"])
307 if (len(res10) > 0):
308 has_legacy_dns_account = True
309 else:
310 has_legacy_dns_account = False
312 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
313 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
314 controls=["search_options:1:2"])
315 if (len(res11) > 0):
316 has_dns_account = True
317 else:
318 has_dns_account = False
320 if names.dnsdomaindn is not None:
321 if has_dns_account:
322 names.dns_backend = 'BIND9_DLZ'
323 else:
324 names.dns_backend = 'SAMBA_INTERNAL'
325 elif has_dns_account or has_legacy_dns_account:
326 names.dns_backend = 'BIND9_FLATFILE'
327 else:
328 names.dns_backend = 'NONE'
330 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
331 names.name_map['DnsAdmins'] = str(dns_admins_sid)
333 return names
336 def update_provision_usn(samdb, low, high, id, replace=False):
337 """Update the field provisionUSN in sam.ldb
339 This field is used to track range of USN modified by provision and
340 upgradeprovision.
341 This value is used afterward by next provision to figure out if
342 the field have been modified since last provision.
344 :param samdb: An LDB object connect to sam.ldb
345 :param low: The lowest USN modified by this upgrade
346 :param high: The highest USN modified by this upgrade
347 :param id: The invocation id of the samba's dc
348 :param replace: A boolean indicating if the range should replace any
349 existing one or appended (default)
352 tab = []
353 if not replace:
354 entry = samdb.search(base="@PROVISION",
355 scope=ldb.SCOPE_BASE,
356 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
357 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
358 if not re.search(';', e):
359 e = "%s;%s" % (e, id)
360 tab.append(str(e))
362 tab.append("%s-%s;%s" % (low, high, id))
363 delta = ldb.Message()
364 delta.dn = ldb.Dn(samdb, "@PROVISION")
365 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
366 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
367 entry = samdb.search(expression='provisionnerID=*',
368 base="@PROVISION", scope=ldb.SCOPE_BASE,
369 attrs=["provisionnerID"])
370 if len(entry) == 0 or len(entry[0]) == 0:
371 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
372 samdb.modify(delta)
375 def set_provision_usn(samdb, low, high, id):
376 """Set the field provisionUSN in sam.ldb
377 This field is used to track range of USN modified by provision and
378 upgradeprovision.
379 This value is used afterward by next provision to figure out if
380 the field have been modified since last provision.
382 :param samdb: An LDB object connect to sam.ldb
383 :param low: The lowest USN modified by this upgrade
384 :param high: The highest USN modified by this upgrade
385 :param id: The invocationId of the provision"""
387 tab = []
388 tab.append("%s-%s;%s" % (low, high, id))
390 delta = ldb.Message()
391 delta.dn = ldb.Dn(samdb, "@PROVISION")
392 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
393 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
394 samdb.add(delta)
397 def get_max_usn(samdb,basedn):
398 """ This function return the biggest USN present in the provision
400 :param samdb: A LDB object pointing to the sam.ldb
401 :param basedn: A string containing the base DN of the provision
402 (ie. DC=foo, DC=bar)
403 :return: The biggest USN in the provision"""
405 res = samdb.search(expression="objectClass=*",base=basedn,
406 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
407 controls=["search_options:1:2",
408 "server_sort:1:1:uSNChanged",
409 "paged_results:1:1"])
410 return res[0]["uSNChanged"]
413 def get_last_provision_usn(sam):
414 """Get USNs ranges modified by a provision or an upgradeprovision
416 :param sam: An LDB object pointing to the sam.ldb
417 :return: a dictionary which keys are invocation id and values are an array
418 of integer representing the different ranges
420 try:
421 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
422 base="@PROVISION", scope=ldb.SCOPE_BASE,
423 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
424 except ldb.LdbError, (ecode, emsg):
425 if ecode == ldb.ERR_NO_SUCH_OBJECT:
426 return None
427 raise
428 if len(entry) > 0:
429 myids = []
430 range = {}
431 p = re.compile(r'-')
432 if entry[0].get("provisionnerID"):
433 for e in entry[0]["provisionnerID"]:
434 myids.append(str(e))
435 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
436 tab1 = str(r).split(';')
437 if len(tab1) == 2:
438 id = tab1[1]
439 else:
440 id = "default"
441 if (len(myids) > 0 and id not in myids):
442 continue
443 tab2 = p.split(tab1[0])
444 if range.get(id) is None:
445 range[id] = []
446 range[id].append(tab2[0])
447 range[id].append(tab2[1])
448 return range
449 else:
450 return None
453 class ProvisionResult(object):
454 """Result of a provision.
456 :ivar server_role: The server role
457 :ivar paths: ProvisionPaths instance
458 :ivar domaindn: The domain dn, as string
461 def __init__(self):
462 self.server_role = None
463 self.paths = None
464 self.domaindn = None
465 self.lp = None
466 self.samdb = None
467 self.idmap = None
468 self.names = None
469 self.domainsid = None
470 self.adminpass_generated = None
471 self.adminpass = None
472 self.backend_result = None
474 def report_logger(self, logger):
475 """Report this provision result to a logger."""
476 logger.info(
477 "Once the above files are installed, your Samba4 server will "
478 "be ready to use")
479 if self.adminpass_generated:
480 logger.info("Admin password: %s", self.adminpass)
481 logger.info("Server Role: %s", self.server_role)
482 logger.info("Hostname: %s", self.names.hostname)
483 logger.info("NetBIOS Domain: %s", self.names.domain)
484 logger.info("DNS Domain: %s", self.names.dnsdomain)
485 logger.info("DOMAIN SID: %s", self.domainsid)
487 if self.backend_result:
488 self.backend_result.report_logger(logger)
491 def check_install(lp, session_info, credentials):
492 """Check whether the current install seems ok.
494 :param lp: Loadparm context
495 :param session_info: Session information
496 :param credentials: Credentials
498 if lp.get("realm") == "":
499 raise Exception("Realm empty")
500 samdb = Ldb(lp.samdb_url(), session_info=session_info,
501 credentials=credentials, lp=lp)
502 if len(samdb.search("(cn=Administrator)")) != 1:
503 raise ProvisioningError("No administrator account found")
506 def findnss(nssfn, names):
507 """Find a user or group from a list of possibilities.
509 :param nssfn: NSS Function to try (should raise KeyError if not found)
510 :param names: Names to check.
511 :return: Value return by first names list.
513 for name in names:
514 try:
515 return nssfn(name)
516 except KeyError:
517 pass
518 raise KeyError("Unable to find user/group in %r" % names)
521 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
522 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
525 def provision_paths_from_lp(lp, dnsdomain):
526 """Set the default paths for provisioning.
528 :param lp: Loadparm context.
529 :param dnsdomain: DNS Domain name
531 paths = ProvisionPaths()
532 paths.private_dir = lp.get("private dir")
533 paths.state_dir = lp.get("state directory")
535 # This is stored without path prefix for the "privateKeytab" attribute in
536 # "secrets_dns.ldif".
537 paths.dns_keytab = "dns.keytab"
538 paths.keytab = "secrets.keytab"
540 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
541 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
542 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
543 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
544 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
545 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
546 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
547 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
548 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
549 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
550 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
551 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
552 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
553 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
554 paths.hklm = "hklm.ldb"
555 paths.hkcr = "hkcr.ldb"
556 paths.hkcu = "hkcu.ldb"
557 paths.hku = "hku.ldb"
558 paths.hkpd = "hkpd.ldb"
559 paths.hkpt = "hkpt.ldb"
560 paths.sysvol = lp.get("path", "sysvol")
561 paths.netlogon = lp.get("path", "netlogon")
562 paths.smbconf = lp.configfile
563 return paths
566 def determine_netbios_name(hostname):
567 """Determine a netbios name from a hostname."""
568 # remove forbidden chars and force the length to be <16
569 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
570 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
573 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
574 serverrole=None, rootdn=None, domaindn=None, configdn=None,
575 schemadn=None, serverdn=None, sitename=None,
576 domain_names_forced=False):
577 """Guess configuration settings to use."""
579 if hostname is None:
580 hostname = socket.gethostname().split(".")[0]
582 netbiosname = lp.get("netbios name")
583 if netbiosname is None:
584 netbiosname = determine_netbios_name(hostname)
585 netbiosname = netbiosname.upper()
586 if not valid_netbios_name(netbiosname):
587 raise InvalidNetbiosName(netbiosname)
589 if dnsdomain is None:
590 dnsdomain = lp.get("realm")
591 if dnsdomain is None or dnsdomain == "":
592 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
594 dnsdomain = dnsdomain.lower()
596 if serverrole is None:
597 serverrole = lp.get("server role")
598 if serverrole is None:
599 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
601 serverrole = serverrole.lower()
603 realm = dnsdomain.upper()
605 if lp.get("realm") == "":
606 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
608 if lp.get("realm").upper() != realm:
609 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))
611 if lp.get("server role").lower() != serverrole:
612 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))
614 if serverrole == "active directory domain controller":
615 if domain is None:
616 # This will, for better or worse, default to 'WORKGROUP'
617 domain = lp.get("workgroup")
618 domain = domain.upper()
620 if lp.get("workgroup").upper() != domain:
621 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))
623 if domaindn is None:
624 domaindn = samba.dn_from_dns_name(dnsdomain)
626 if domain == netbiosname:
627 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
628 else:
629 domain = netbiosname
630 if domaindn is None:
631 domaindn = "DC=" + netbiosname
633 if not valid_netbios_name(domain):
634 raise InvalidNetbiosName(domain)
636 if hostname.upper() == realm:
637 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
638 if netbiosname.upper() == realm:
639 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
640 if domain == realm and not domain_names_forced:
641 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
643 if rootdn is None:
644 rootdn = domaindn
646 if configdn is None:
647 configdn = "CN=Configuration," + rootdn
648 if schemadn is None:
649 schemadn = "CN=Schema," + configdn
651 if sitename is None:
652 sitename = DEFAULTSITE
654 names = ProvisionNames()
655 names.rootdn = rootdn
656 names.domaindn = domaindn
657 names.configdn = configdn
658 names.schemadn = schemadn
659 names.ldapmanagerdn = "CN=Manager," + rootdn
660 names.dnsdomain = dnsdomain
661 names.domain = domain
662 names.realm = realm
663 names.netbiosname = netbiosname
664 names.hostname = hostname
665 names.sitename = sitename
666 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
667 netbiosname, sitename, configdn)
669 return names
672 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
673 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
674 global_param=None):
675 """Create a new smb.conf file based on a couple of basic settings.
677 assert smbconf is not None
679 if hostname is None:
680 hostname = socket.gethostname().split(".")[0]
682 netbiosname = determine_netbios_name(hostname)
684 if serverrole is None:
685 serverrole = "standalone server"
687 assert domain is not None
688 domain = domain.upper()
690 assert realm is not None
691 realm = realm.upper()
693 global_settings = {
694 "netbios name": netbiosname,
695 "workgroup": domain,
696 "realm": realm,
697 "server role": serverrole,
700 if lp is None:
701 lp = samba.param.LoadParm()
702 #Load non-existent file
703 if os.path.exists(smbconf):
704 lp.load(smbconf)
706 if global_param is not None:
707 for ent in global_param:
708 if global_param[ent] is not None:
709 global_settings[ent] = " ".join(global_param[ent])
711 if targetdir is not None:
712 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
713 global_settings["lock dir"] = os.path.abspath(targetdir)
714 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
715 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
717 lp.set("lock dir", os.path.abspath(targetdir))
718 lp.set("state directory", global_settings["state directory"])
719 lp.set("cache directory", global_settings["cache directory"])
721 if eadb:
722 if use_ntvfs and not lp.get("posix:eadb"):
723 if targetdir is not None:
724 privdir = os.path.join(targetdir, "private")
725 else:
726 privdir = lp.get("private dir")
727 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
728 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
729 if targetdir is not None:
730 statedir = os.path.join(targetdir, "state")
731 else:
732 statedir = lp.get("state directory")
733 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
735 shares = {}
736 if serverrole == "active directory domain controller":
737 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
738 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
739 "scripts")
740 else:
741 global_settings["passdb backend"] = "samba_dsdb"
743 f = open(smbconf, 'w')
744 try:
745 f.write("[globals]\n")
746 for key, val in global_settings.iteritems():
747 f.write("\t%s = %s\n" % (key, val))
748 f.write("\n")
750 for name, path in shares.iteritems():
751 f.write("[%s]\n" % name)
752 f.write("\tpath = %s\n" % path)
753 f.write("\tread only = no\n")
754 f.write("\n")
755 finally:
756 f.close()
757 # reload the smb.conf
758 lp.load(smbconf)
760 # and dump it without any values that are the default
761 # this ensures that any smb.conf parameters that were set
762 # on the provision/join command line are set in the resulting smb.conf
763 f = open(smbconf, mode='w')
764 try:
765 lp.dump(f, False)
766 finally:
767 f.close()
770 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
771 users_gid, root_gid):
772 """setup reasonable name mappings for sam names to unix names.
774 :param samdb: SamDB object.
775 :param idmap: IDmap db object.
776 :param sid: The domain sid.
777 :param domaindn: The domain DN.
778 :param root_uid: uid of the UNIX root user.
779 :param nobody_uid: uid of the UNIX nobody user.
780 :param users_gid: gid of the UNIX users group.
781 :param root_gid: gid of the UNIX root group.
783 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
785 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
786 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
789 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
790 provision_backend, names, schema, serverrole,
791 erase=False):
792 """Setup the partitions for the SAM database.
794 Alternatively, provision() may call this, and then populate the database.
796 :note: This will wipe the Sam Database!
798 :note: This function always removes the local SAM LDB file. The erase
799 parameter controls whether to erase the existing data, which
800 may not be stored locally but in LDAP.
803 assert session_info is not None
805 # We use options=["modules:"] to stop the modules loading - we
806 # just want to wipe and re-initialise the database, not start it up
808 try:
809 os.unlink(samdb_path)
810 except OSError:
811 pass
813 samdb = Ldb(url=samdb_path, session_info=session_info,
814 lp=lp, options=["modules:"])
816 ldap_backend_line = "# No LDAP backend"
817 if provision_backend.type != "ldb":
818 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
820 samdb.transaction_start()
821 try:
822 logger.info("Setting up sam.ldb partitions and settings")
823 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
824 "LDAP_BACKEND_LINE": ldap_backend_line
828 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
829 "BACKEND_TYPE": provision_backend.type,
830 "SERVER_ROLE": serverrole
833 logger.info("Setting up sam.ldb rootDSE")
834 setup_samdb_rootdse(samdb, names)
835 except:
836 samdb.transaction_cancel()
837 raise
838 else:
839 samdb.transaction_commit()
842 def secretsdb_self_join(secretsdb, domain,
843 netbiosname, machinepass, domainsid=None,
844 realm=None, dnsdomain=None,
845 keytab_path=None,
846 key_version_number=1,
847 secure_channel_type=SEC_CHAN_WKSTA):
848 """Add domain join-specific bits to a secrets database.
850 :param secretsdb: Ldb Handle to the secrets database
851 :param machinepass: Machine password
853 attrs = ["whenChanged",
854 "secret",
855 "priorSecret",
856 "priorChanged",
857 "krb5Keytab",
858 "privateKeytab"]
860 if realm is not None:
861 if dnsdomain is None:
862 dnsdomain = realm.lower()
863 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
864 else:
865 dnsname = None
866 shortname = netbiosname.lower()
868 # We don't need to set msg["flatname"] here, because rdn_name will handle
869 # it, and it causes problems for modifies anyway
870 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
871 msg["secureChannelType"] = [str(secure_channel_type)]
872 msg["objectClass"] = ["top", "primaryDomain"]
873 if dnsname is not None:
874 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
875 msg["realm"] = [realm]
876 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
877 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
878 msg["privateKeytab"] = ["secrets.keytab"]
880 msg["secret"] = [machinepass]
881 msg["samAccountName"] = ["%s$" % netbiosname]
882 msg["secureChannelType"] = [str(secure_channel_type)]
883 if domainsid is not None:
884 msg["objectSid"] = [ndr_pack(domainsid)]
886 # This complex expression tries to ensure that we don't have more
887 # than one record for this SID, realm or netbios domain at a time,
888 # but we don't delete the old record that we are about to modify,
889 # because that would delete the keytab and previous password.
890 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
891 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
892 scope=ldb.SCOPE_ONELEVEL)
894 for del_msg in res:
895 secretsdb.delete(del_msg.dn)
897 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
899 if len(res) == 1:
900 msg["priorSecret"] = [res[0]["secret"][0]]
901 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
903 try:
904 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
905 except KeyError:
906 pass
908 try:
909 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
910 except KeyError:
911 pass
913 for el in msg:
914 if el != 'dn':
915 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
916 secretsdb.modify(msg)
917 secretsdb.rename(res[0].dn, msg.dn)
918 else:
919 spn = [ 'HOST/%s' % shortname ]
920 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
921 # we are a domain controller then we add servicePrincipalName
922 # entries for the keytab code to update.
923 spn.extend([ 'HOST/%s' % dnsname ])
924 msg["servicePrincipalName"] = spn
926 secretsdb.add(msg)
929 def setup_secretsdb(paths, session_info, backend_credentials, lp):
930 """Setup the secrets database.
932 :note: This function does not handle exceptions and transaction on purpose,
933 it's up to the caller to do this job.
935 :param path: Path to the secrets database.
936 :param session_info: Session info.
937 :param credentials: Credentials
938 :param lp: Loadparm context
939 :return: LDB handle for the created secrets database
941 if os.path.exists(paths.secrets):
942 os.unlink(paths.secrets)
944 keytab_path = os.path.join(paths.private_dir, paths.keytab)
945 if os.path.exists(keytab_path):
946 os.unlink(keytab_path)
948 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
949 if os.path.exists(dns_keytab_path):
950 os.unlink(dns_keytab_path)
952 path = paths.secrets
954 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
955 secrets_ldb.erase()
956 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
957 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
958 secrets_ldb.transaction_start()
959 try:
960 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
962 if (backend_credentials is not None and
963 backend_credentials.authentication_requested()):
964 if backend_credentials.get_bind_dn() is not None:
965 setup_add_ldif(secrets_ldb,
966 setup_path("secrets_simple_ldap.ldif"), {
967 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
968 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
970 else:
971 setup_add_ldif(secrets_ldb,
972 setup_path("secrets_sasl_ldap.ldif"), {
973 "LDAPADMINUSER": backend_credentials.get_username(),
974 "LDAPADMINREALM": backend_credentials.get_realm(),
975 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
977 except:
978 secrets_ldb.transaction_cancel()
979 raise
980 return secrets_ldb
983 def setup_privileges(path, session_info, lp):
984 """Setup the privileges database.
986 :param path: Path to the privileges database.
987 :param session_info: Session info.
988 :param credentials: Credentials
989 :param lp: Loadparm context
990 :return: LDB handle for the created secrets database
992 if os.path.exists(path):
993 os.unlink(path)
994 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
995 privilege_ldb.erase()
996 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
999 def setup_registry(path, session_info, lp):
1000 """Setup the registry.
1002 :param path: Path to the registry database
1003 :param session_info: Session information
1004 :param credentials: Credentials
1005 :param lp: Loadparm context
1007 reg = samba.registry.Registry()
1008 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1009 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1010 provision_reg = setup_path("provision.reg")
1011 assert os.path.exists(provision_reg)
1012 reg.diff_apply(provision_reg)
1015 def setup_idmapdb(path, session_info, lp):
1016 """Setup the idmap database.
1018 :param path: path to the idmap database
1019 :param session_info: Session information
1020 :param credentials: Credentials
1021 :param lp: Loadparm context
1023 if os.path.exists(path):
1024 os.unlink(path)
1026 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1027 idmap_ldb.erase()
1028 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1029 return idmap_ldb
1032 def setup_samdb_rootdse(samdb, names):
1033 """Setup the SamDB rootdse.
1035 :param samdb: Sam Database handle
1037 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1038 "SCHEMADN": names.schemadn,
1039 "DOMAINDN": names.domaindn,
1040 "ROOTDN" : names.rootdn,
1041 "CONFIGDN": names.configdn,
1042 "SERVERDN": names.serverdn,
1046 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1047 dns_backend, dnspass, domainsid, next_rid, invocationid,
1048 policyguid, policyguid_dc,
1049 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1050 """Join a host to its own domain."""
1051 assert isinstance(invocationid, str)
1052 if ntdsguid is not None:
1053 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1054 else:
1055 ntdsguid_line = ""
1057 if dc_rid is None:
1058 dc_rid = next_rid
1060 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1061 "CONFIGDN": names.configdn,
1062 "SCHEMADN": names.schemadn,
1063 "DOMAINDN": names.domaindn,
1064 "SERVERDN": names.serverdn,
1065 "INVOCATIONID": invocationid,
1066 "NETBIOSNAME": names.netbiosname,
1067 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1068 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1069 "DOMAINSID": str(domainsid),
1070 "DCRID": str(dc_rid),
1071 "SAMBA_VERSION_STRING": version,
1072 "NTDSGUID": ntdsguid_line,
1073 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1074 domainControllerFunctionality),
1075 "RIDALLOCATIONSTART": str(next_rid + 100),
1076 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1078 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1079 "POLICYGUID": policyguid,
1080 "POLICYGUID_DC": policyguid_dc,
1081 "DNSDOMAIN": names.dnsdomain,
1082 "DOMAINDN": names.domaindn})
1084 # If we are setting up a subdomain, then this has been replicated in, so we
1085 # don't need to add it
1086 if fill == FILL_FULL:
1087 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1088 "CONFIGDN": names.configdn,
1089 "SCHEMADN": names.schemadn,
1090 "DOMAINDN": names.domaindn,
1091 "SERVERDN": names.serverdn,
1092 "INVOCATIONID": invocationid,
1093 "NETBIOSNAME": names.netbiosname,
1094 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1095 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1096 "DOMAINSID": str(domainsid),
1097 "DCRID": str(dc_rid),
1098 "SAMBA_VERSION_STRING": version,
1099 "NTDSGUID": ntdsguid_line,
1100 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1101 domainControllerFunctionality)})
1103 # Setup fSMORoleOwner entries to point at the newly created DC entry
1104 setup_modify_ldif(samdb,
1105 setup_path("provision_self_join_modify_config.ldif"), {
1106 "CONFIGDN": names.configdn,
1107 "SCHEMADN": names.schemadn,
1108 "DEFAULTSITE": names.sitename,
1109 "NETBIOSNAME": names.netbiosname,
1110 "SERVERDN": names.serverdn,
1113 system_session_info = system_session()
1114 samdb.set_session_info(system_session_info)
1115 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1116 # modify a serverReference under cn=config when we are a subdomain, we must
1117 # be system due to ACLs
1118 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1119 "DOMAINDN": names.domaindn,
1120 "SERVERDN": names.serverdn,
1121 "NETBIOSNAME": names.netbiosname,
1124 samdb.set_session_info(admin_session_info)
1126 if dns_backend != "SAMBA_INTERNAL":
1127 # This is Samba4 specific and should be replaced by the correct
1128 # DNS AD-style setup
1129 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1130 "DNSDOMAIN": names.dnsdomain,
1131 "DOMAINDN": names.domaindn,
1132 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1133 "HOSTNAME" : names.hostname,
1134 "DNSNAME" : '%s.%s' % (
1135 names.netbiosname.lower(), names.dnsdomain.lower())
1139 def getpolicypath(sysvolpath, dnsdomain, guid):
1140 """Return the physical path of policy given its guid.
1142 :param sysvolpath: Path to the sysvol folder
1143 :param dnsdomain: DNS name of the AD domain
1144 :param guid: The GUID of the policy
1145 :return: A string with the complete path to the policy folder
1147 if guid[0] != "{":
1148 guid = "{%s}" % guid
1149 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1150 return policy_path
1153 def create_gpo_struct(policy_path):
1154 if not os.path.exists(policy_path):
1155 os.makedirs(policy_path, 0775)
1156 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1157 try:
1158 f.write("[General]\r\nVersion=0")
1159 finally:
1160 f.close()
1161 p = os.path.join(policy_path, "MACHINE")
1162 if not os.path.exists(p):
1163 os.makedirs(p, 0775)
1164 p = os.path.join(policy_path, "USER")
1165 if not os.path.exists(p):
1166 os.makedirs(p, 0775)
1169 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1170 """Create the default GPO for a domain
1172 :param sysvolpath: Physical path for the sysvol folder
1173 :param dnsdomain: DNS domain name of the AD domain
1174 :param policyguid: GUID of the default domain policy
1175 :param policyguid_dc: GUID of the default domain controler policy
1177 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1178 create_gpo_struct(policy_path)
1180 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1181 create_gpo_struct(policy_path)
1184 def setup_samdb(path, session_info, provision_backend, lp, names,
1185 logger, fill, serverrole, schema, am_rodc=False):
1186 """Setup a complete SAM Database.
1188 :note: This will wipe the main SAM database file!
1191 # Also wipes the database
1192 setup_samdb_partitions(path, logger=logger, lp=lp,
1193 provision_backend=provision_backend, session_info=session_info,
1194 names=names, serverrole=serverrole, schema=schema)
1196 # Load the database, but don's load the global schema and don't connect
1197 # quite yet
1198 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1199 credentials=provision_backend.credentials, lp=lp,
1200 global_schema=False, am_rodc=am_rodc)
1202 logger.info("Pre-loading the Samba 4 and AD schema")
1204 # Load the schema from the one we computed earlier
1205 samdb.set_schema(schema, write_indices_and_attributes=False)
1207 # Set the NTDS settings DN manually - in order to have it already around
1208 # before the provisioned tree exists and we connect
1209 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1211 # And now we can connect to the DB - the schema won't be loaded from the
1212 # DB
1213 try:
1214 samdb.connect(path)
1215 except ldb.LdbError, (num, string_error):
1216 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1217 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1218 else:
1219 raise
1221 # But we have to give it one more kick to have it use the schema
1222 # during provision - it needs, now that it is connected, to write
1223 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1224 samdb.set_schema(schema, write_indices_and_attributes=True)
1226 return samdb
1229 def fill_samdb(samdb, lp, names, logger, policyguid,
1230 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1231 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1232 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1234 if next_rid is None:
1235 next_rid = 1000
1237 # Provision does not make much sense values larger than 1000000000
1238 # as the upper range of the rIDAvailablePool is 1073741823 and
1239 # we don't want to create a domain that cannot allocate rids.
1240 if next_rid < 1000 or next_rid > 1000000000:
1241 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1242 error += "the valid range is %u-%u. The default is %u." % (
1243 1000, 1000000000, 1000)
1244 raise ProvisioningError(error)
1246 # ATTENTION: Do NOT change these default values without discussion with the
1247 # team and/or release manager. They have a big impact on the whole program!
1248 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1250 if dom_for_fun_level is None:
1251 dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
1253 if dom_for_fun_level > domainControllerFunctionality:
1254 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!")
1256 domainFunctionality = dom_for_fun_level
1257 forestFunctionality = dom_for_fun_level
1259 # Set the NTDS settings DN manually - in order to have it already around
1260 # before the provisioned tree exists and we connect
1261 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1263 samdb.transaction_start()
1264 try:
1265 # Set the domain functionality levels onto the database.
1266 # Various module (the password_hash module in particular) need
1267 # to know what level of AD we are emulating.
1269 # These will be fixed into the database via the database
1270 # modifictions below, but we need them set from the start.
1271 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1272 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1273 samdb.set_opaque_integer("domainControllerFunctionality",
1274 domainControllerFunctionality)
1276 samdb.set_domain_sid(str(names.domainsid))
1277 samdb.set_invocation_id(invocationid)
1279 logger.info("Adding DomainDN: %s" % names.domaindn)
1281 # impersonate domain admin
1282 admin_session_info = admin_session(lp, str(names.domainsid))
1283 samdb.set_session_info(admin_session_info)
1284 if names.domainguid is not None:
1285 domainguid_line = "objectGUID: %s\n-" % names.domainguid
1286 else:
1287 domainguid_line = ""
1289 descr = b64encode(get_domain_descriptor(names.domainsid))
1290 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1291 "DOMAINDN": names.domaindn,
1292 "DOMAINSID": str(names.domainsid),
1293 "DESCRIPTOR": descr,
1294 "DOMAINGUID": domainguid_line
1297 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1298 "DOMAINDN": names.domaindn,
1299 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1300 "NEXTRID": str(next_rid),
1301 "DEFAULTSITE": names.sitename,
1302 "CONFIGDN": names.configdn,
1303 "POLICYGUID": policyguid,
1304 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1305 "SAMBA_VERSION_STRING": version
1308 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1309 if fill == FILL_FULL:
1310 logger.info("Adding configuration container")
1311 descr = b64encode(get_config_descriptor(names.domainsid))
1312 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1313 "CONFIGDN": names.configdn,
1314 "DESCRIPTOR": descr,
1317 # The LDIF here was created when the Schema object was constructed
1318 logger.info("Setting up sam.ldb schema")
1319 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1320 samdb.modify_ldif(schema.schema_dn_modify)
1321 samdb.write_prefixes_from_schema()
1322 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1323 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1324 {"SCHEMADN": names.schemadn})
1326 # Now register this container in the root of the forest
1327 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1328 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1329 "subRefs")
1331 except:
1332 samdb.transaction_cancel()
1333 raise
1334 else:
1335 samdb.transaction_commit()
1337 samdb.transaction_start()
1338 try:
1339 samdb.invocation_id = invocationid
1341 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1342 if fill == FILL_FULL:
1343 logger.info("Setting up sam.ldb configuration data")
1345 partitions_descr = b64encode(get_config_partitions_descriptor(names.domainsid))
1346 sites_descr = b64encode(get_config_sites_descriptor(names.domainsid))
1347 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(names.domainsid))
1348 protected1_descr = b64encode(get_config_delete_protected1_descriptor(names.domainsid))
1349 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1350 protected2_descr = b64encode(get_config_delete_protected2_descriptor(names.domainsid))
1352 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1353 "CONFIGDN": names.configdn,
1354 "NETBIOSNAME": names.netbiosname,
1355 "DEFAULTSITE": names.sitename,
1356 "DNSDOMAIN": names.dnsdomain,
1357 "DOMAIN": names.domain,
1358 "SCHEMADN": names.schemadn,
1359 "DOMAINDN": names.domaindn,
1360 "SERVERDN": names.serverdn,
1361 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1362 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1363 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1364 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1365 "SERVICES_DESCRIPTOR": protected1_descr,
1366 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1367 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1368 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1369 "PARTITIONS_DESCRIPTOR": partitions_descr,
1370 "SITES_DESCRIPTOR": sites_descr,
1373 logger.info("Setting up display specifiers")
1374 display_specifiers_ldif = read_ms_ldif(
1375 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1376 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1377 {"CONFIGDN": names.configdn})
1378 check_all_substituted(display_specifiers_ldif)
1379 samdb.add_ldif(display_specifiers_ldif)
1381 logger.info("Modifying display specifiers")
1382 setup_modify_ldif(samdb,
1383 setup_path("provision_configuration_modify.ldif"), {
1384 "CONFIGDN": names.configdn,
1385 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1388 logger.info("Adding users container")
1389 users_desc = b64encode(get_domain_users_descriptor(names.domainsid))
1390 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1391 "DOMAINDN": names.domaindn,
1392 "USERS_DESCRIPTOR": users_desc
1394 logger.info("Modifying users container")
1395 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1396 "DOMAINDN": names.domaindn})
1397 logger.info("Adding computers container")
1398 computers_desc = b64encode(get_domain_computers_descriptor(names.domainsid))
1399 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1400 "DOMAINDN": names.domaindn,
1401 "COMPUTERS_DESCRIPTOR": computers_desc
1403 logger.info("Modifying computers container")
1404 setup_modify_ldif(samdb,
1405 setup_path("provision_computers_modify.ldif"), {
1406 "DOMAINDN": names.domaindn})
1407 logger.info("Setting up sam.ldb data")
1408 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(names.domainsid))
1409 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(names.domainsid))
1410 system_desc = b64encode(get_domain_delete_protected1_descriptor(names.domainsid))
1411 builtin_desc = b64encode(get_domain_builtin_descriptor(names.domainsid))
1412 controllers_desc = b64encode(get_domain_controllers_descriptor(names.domainsid))
1413 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1414 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1415 "DOMAINDN": names.domaindn,
1416 "NETBIOSNAME": names.netbiosname,
1417 "DEFAULTSITE": names.sitename,
1418 "CONFIGDN": names.configdn,
1419 "SERVERDN": names.serverdn,
1420 "RIDAVAILABLESTART": str(next_rid + 600),
1421 "POLICYGUID_DC": policyguid_dc,
1422 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1423 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1424 "SYSTEM_DESCRIPTOR": system_desc,
1425 "BUILTIN_DESCRIPTOR": builtin_desc,
1426 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1429 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1430 if fill == FILL_FULL:
1431 setup_modify_ldif(samdb,
1432 setup_path("provision_configuration_references.ldif"), {
1433 "CONFIGDN": names.configdn,
1434 "SCHEMADN": names.schemadn})
1436 logger.info("Setting up well known security principals")
1437 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(names.domainsid))
1438 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1439 "CONFIGDN": names.configdn,
1440 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1443 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1444 setup_modify_ldif(samdb,
1445 setup_path("provision_basedn_references.ldif"),
1446 {"DOMAINDN": names.domaindn})
1448 logger.info("Setting up sam.ldb users and groups")
1449 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1450 "DOMAINDN": names.domaindn,
1451 "DOMAINSID": str(names.domainsid),
1452 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1453 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1456 logger.info("Setting up self join")
1457 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1458 invocationid=invocationid,
1459 dns_backend=dns_backend,
1460 dnspass=dnspass,
1461 machinepass=machinepass,
1462 domainsid=names.domainsid,
1463 next_rid=next_rid,
1464 dc_rid=dc_rid,
1465 policyguid=policyguid,
1466 policyguid_dc=policyguid_dc,
1467 domainControllerFunctionality=domainControllerFunctionality,
1468 ntdsguid=ntdsguid)
1470 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1471 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1472 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1473 assert isinstance(names.ntdsguid, str)
1474 except:
1475 samdb.transaction_cancel()
1476 raise
1477 else:
1478 samdb.transaction_commit()
1479 return samdb
1482 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1483 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)"
1484 SYSVOL_SERVICE="sysvol"
1486 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1487 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1488 for root, dirs, files in os.walk(path, topdown=False):
1489 for name in files:
1490 setntacl(lp, os.path.join(root, name), acl, domsid,
1491 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1492 for name in dirs:
1493 setntacl(lp, os.path.join(root, name), acl, domsid,
1494 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1497 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1498 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1499 folders beneath.
1501 :param sysvol: Physical path for the sysvol folder
1502 :param dnsdomain: The DNS name of the domain
1503 :param domainsid: The SID of the domain
1504 :param domaindn: The DN of the domain (ie. DC=...)
1505 :param samdb: An LDB object on the SAM db
1506 :param lp: an LP object
1509 # Set ACL for GPO root folder
1510 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1511 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1512 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1514 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1515 attrs=["cn", "nTSecurityDescriptor"],
1516 expression="", scope=ldb.SCOPE_ONELEVEL)
1518 for policy in res:
1519 acl = ndr_unpack(security.descriptor,
1520 str(policy["nTSecurityDescriptor"])).as_sddl()
1521 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1522 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1523 str(domainsid), use_ntvfs,
1524 passdb=passdb)
1527 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1528 domaindn, lp, use_ntvfs):
1529 """Set the ACL for the sysvol share and the subfolders
1531 :param samdb: An LDB object on the SAM db
1532 :param netlogon: Physical path for the netlogon folder
1533 :param sysvol: Physical path for the sysvol folder
1534 :param uid: The UID of the "Administrator" user
1535 :param gid: The GID of the "Domain adminstrators" group
1536 :param domainsid: The SID of the domain
1537 :param dnsdomain: The DNS name of the domain
1538 :param domaindn: The DN of the domain (ie. DC=...)
1540 s4_passdb = None
1542 if not use_ntvfs:
1543 s3conf = s3param.get_context()
1544 s3conf.load(lp.configfile)
1546 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1547 try:
1548 try:
1549 smbd.set_simple_acl(file.name, 0755, gid)
1550 except OSError:
1551 if not smbd.have_posix_acls():
1552 # This clue is only strictly correct for RPM and
1553 # Debian-like Linux systems, but hopefully other users
1554 # will get enough clue from it.
1555 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1556 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1558 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1559 "Try the mounting the filesystem with the 'acl' option.")
1560 try:
1561 smbd.chown(file.name, uid, gid)
1562 except OSError:
1563 raise ProvisioningError("Unable to chown a file on your filesystem. "
1564 "You may not be running provision as root.")
1565 finally:
1566 file.close()
1568 # This will ensure that the smbd code we are running when setting ACLs
1569 # is initialised with the smb.conf
1570 s3conf = s3param.get_context()
1571 s3conf.load(lp.configfile)
1572 # ensure we are using the right samba_dsdb passdb backend, no matter what
1573 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1574 passdb.reload_static_pdb()
1576 # ensure that we init the samba_dsdb backend, so the domain sid is
1577 # marked in secrets.tdb
1578 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1580 # now ensure everything matches correctly, to avoid wierd issues
1581 if passdb.get_global_sam_sid() != domainsid:
1582 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))
1584 domain_info = s4_passdb.domain_info()
1585 if domain_info["dom_sid"] != domainsid:
1586 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))
1588 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1589 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()))
1592 try:
1593 if use_ntvfs:
1594 os.chown(sysvol, -1, gid)
1595 except OSError:
1596 canchown = False
1597 else:
1598 canchown = True
1600 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1601 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1602 skip_invalid_chown=True, passdb=s4_passdb,
1603 service=SYSVOL_SERVICE)
1604 for root, dirs, files in os.walk(sysvol, topdown=False):
1605 for name in files:
1606 if use_ntvfs and canchown:
1607 os.chown(os.path.join(root, name), -1, gid)
1608 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1609 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1610 passdb=s4_passdb, service=SYSVOL_SERVICE)
1611 for name in dirs:
1612 if use_ntvfs and canchown:
1613 os.chown(os.path.join(root, name), -1, gid)
1614 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1615 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1616 passdb=s4_passdb, service=SYSVOL_SERVICE)
1618 # Set acls on Policy folder and policies folders
1619 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1621 def acl_type(direct_db_access):
1622 if direct_db_access:
1623 return "DB"
1624 else:
1625 return "VFS"
1627 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1628 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1629 fsacl_sddl = fsacl.as_sddl(domainsid)
1630 if fsacl_sddl != acl:
1631 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))
1633 for root, dirs, files in os.walk(path, topdown=False):
1634 for name in files:
1635 fsacl = getntacl(lp, os.path.join(root, name),
1636 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1637 if fsacl is None:
1638 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1639 fsacl_sddl = fsacl.as_sddl(domainsid)
1640 if fsacl_sddl != acl:
1641 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))
1643 for name in dirs:
1644 fsacl = getntacl(lp, os.path.join(root, name),
1645 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1646 if fsacl is None:
1647 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1648 fsacl_sddl = fsacl.as_sddl(domainsid)
1649 if fsacl_sddl != acl:
1650 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))
1653 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1654 direct_db_access):
1655 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1656 folders beneath.
1658 :param sysvol: Physical path for the sysvol folder
1659 :param dnsdomain: The DNS name of the domain
1660 :param domainsid: The SID of the domain
1661 :param domaindn: The DN of the domain (ie. DC=...)
1662 :param samdb: An LDB object on the SAM db
1663 :param lp: an LP object
1666 # Set ACL for GPO root folder
1667 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1668 fsacl = getntacl(lp, root_policy_path,
1669 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1670 if fsacl is None:
1671 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1672 fsacl_sddl = fsacl.as_sddl(domainsid)
1673 if fsacl_sddl != POLICIES_ACL:
1674 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))
1675 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1676 attrs=["cn", "nTSecurityDescriptor"],
1677 expression="", scope=ldb.SCOPE_ONELEVEL)
1679 for policy in res:
1680 acl = ndr_unpack(security.descriptor,
1681 str(policy["nTSecurityDescriptor"])).as_sddl()
1682 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1683 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1684 domainsid, direct_db_access)
1687 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1688 lp):
1689 """Set the ACL for the sysvol share and the subfolders
1691 :param samdb: An LDB object on the SAM db
1692 :param netlogon: Physical path for the netlogon folder
1693 :param sysvol: Physical path for the sysvol folder
1694 :param uid: The UID of the "Administrator" user
1695 :param gid: The GID of the "Domain adminstrators" group
1696 :param domainsid: The SID of the domain
1697 :param dnsdomain: The DNS name of the domain
1698 :param domaindn: The DN of the domain (ie. DC=...)
1701 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1702 s3conf = s3param.get_context()
1703 s3conf.load(lp.configfile)
1704 # ensure we are using the right samba_dsdb passdb backend, no matter what
1705 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1706 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1707 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1709 # now ensure everything matches correctly, to avoid wierd issues
1710 if passdb.get_global_sam_sid() != domainsid:
1711 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))
1713 domain_info = s4_passdb.domain_info()
1714 if domain_info["dom_sid"] != domainsid:
1715 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))
1717 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1718 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()))
1720 # Ensure we can read this directly, and via the smbd VFS
1721 for direct_db_access in [True, False]:
1722 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1723 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1724 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1725 if fsacl is None:
1726 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1727 fsacl_sddl = fsacl.as_sddl(domainsid)
1728 if fsacl_sddl != SYSVOL_ACL:
1729 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))
1731 # Check acls on Policy folder and policies folders
1732 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1733 direct_db_access)
1736 def interface_ips_v4(lp):
1737 """return only IPv4 IPs"""
1738 ips = samba.interface_ips(lp, False)
1739 ret = []
1740 for i in ips:
1741 if i.find(':') == -1:
1742 ret.append(i)
1743 return ret
1746 def interface_ips_v6(lp):
1747 """return only IPv6 IPs"""
1748 ips = samba.interface_ips(lp, False)
1749 ret = []
1750 for i in ips:
1751 if i.find(':') != -1:
1752 ret.append(i)
1753 return ret
1756 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1757 schema=None,
1758 targetdir=None, samdb_fill=FILL_FULL,
1759 hostip=None, hostip6=None,
1760 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1761 domainguid=None, policyguid=None, policyguid_dc=None,
1762 invocationid=None, machinepass=None, ntdsguid=None,
1763 dns_backend=None, dnspass=None,
1764 serverrole=None, dom_for_fun_level=None,
1765 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1766 # create/adapt the group policy GUIDs
1767 # Default GUID for default policy are described at
1768 # "How Core Group Policy Works"
1769 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1770 if policyguid is None:
1771 policyguid = DEFAULT_POLICY_GUID
1772 policyguid = policyguid.upper()
1773 if policyguid_dc is None:
1774 policyguid_dc = DEFAULT_DC_POLICY_GUID
1775 policyguid_dc = policyguid_dc.upper()
1777 if invocationid is None:
1778 invocationid = str(uuid.uuid4())
1780 if krbtgtpass is None:
1781 krbtgtpass = samba.generate_random_password(128, 255)
1782 if machinepass is None:
1783 machinepass = samba.generate_random_password(128, 255)
1784 if dnspass is None:
1785 dnspass = samba.generate_random_password(128, 255)
1787 samdb = fill_samdb(samdb, lp, names, logger=logger,
1788 schema=schema,
1789 policyguid=policyguid, policyguid_dc=policyguid_dc,
1790 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1791 invocationid=invocationid, machinepass=machinepass,
1792 dns_backend=dns_backend, dnspass=dnspass,
1793 ntdsguid=ntdsguid, serverrole=serverrole,
1794 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1795 next_rid=next_rid, dc_rid=dc_rid)
1797 if serverrole == "active directory domain controller":
1799 # Set up group policies (domain policy and domain controller
1800 # policy)
1801 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1802 policyguid_dc)
1803 if not skip_sysvolacl:
1804 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1805 paths.root_gid, names.domainsid, names.dnsdomain,
1806 names.domaindn, lp, use_ntvfs)
1807 else:
1808 logger.info("Setting acl on sysvol skipped")
1810 secretsdb_self_join(secrets_ldb, domain=names.domain,
1811 realm=names.realm, dnsdomain=names.dnsdomain,
1812 netbiosname=names.netbiosname, domainsid=names.domainsid,
1813 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1815 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1816 # In future, this might be determined from some configuration
1817 kerberos_enctypes = str(ENC_ALL_TYPES)
1819 try:
1820 msg = ldb.Message(ldb.Dn(samdb,
1821 samdb.searchone("distinguishedName",
1822 expression="samAccountName=%s$" % names.netbiosname,
1823 scope=ldb.SCOPE_SUBTREE)))
1824 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1825 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1826 name="msDS-SupportedEncryptionTypes")
1827 samdb.modify(msg)
1828 except ldb.LdbError, (enum, estr):
1829 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1830 # It might be that this attribute does not exist in this schema
1831 raise
1833 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1834 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1835 dnspass=dnspass, os_level=dom_for_fun_level,
1836 targetdir=targetdir, fill_level=samdb_fill)
1838 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1839 attribute="objectGUID")
1840 assert isinstance(domainguid, str)
1842 lastProvisionUSNs = get_last_provision_usn(samdb)
1843 maxUSN = get_max_usn(samdb, str(names.rootdn))
1844 if lastProvisionUSNs is not None:
1845 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1846 else:
1847 set_provision_usn(samdb, 0, maxUSN, invocationid)
1849 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1850 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1851 { 'NTDSGUID' : names.ntdsguid })
1853 # fix any dangling GUIDs from the provision
1854 logger.info("Fixing provision GUIDs")
1855 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1856 quiet=True)
1857 samdb.transaction_start()
1858 try:
1859 # a small number of GUIDs are missing because of ordering issues in the
1860 # provision code
1861 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1862 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1863 scope=ldb.SCOPE_BASE,
1864 attrs=['defaultObjectCategory'])
1865 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1866 scope=ldb.SCOPE_ONELEVEL,
1867 attrs=['ipsecOwnersReference',
1868 'ipsecFilterReference',
1869 'ipsecISAKMPReference',
1870 'ipsecNegotiationPolicyReference',
1871 'ipsecNFAReference'])
1872 except:
1873 samdb.transaction_cancel()
1874 raise
1875 else:
1876 samdb.transaction_commit()
1879 _ROLES_MAP = {
1880 "ROLE_STANDALONE": "standalone server",
1881 "ROLE_DOMAIN_MEMBER": "member server",
1882 "ROLE_DOMAIN_BDC": "active directory domain controller",
1883 "ROLE_DOMAIN_PDC": "active directory domain controller",
1884 "dc": "active directory domain controller",
1885 "member": "member server",
1886 "domain controller": "active directory domain controller",
1887 "active directory domain controller": "active directory domain controller",
1888 "member server": "member server",
1889 "standalone": "standalone server",
1890 "standalone server": "standalone server",
1894 def sanitize_server_role(role):
1895 """Sanitize a server role name.
1897 :param role: Server role
1898 :raise ValueError: If the role can not be interpreted
1899 :return: Sanitized server role (one of "member server",
1900 "active directory domain controller", "standalone server")
1902 try:
1903 return _ROLES_MAP[role]
1904 except KeyError:
1905 raise ValueError(role)
1908 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1909 maxuid, maxgid):
1910 """Create AD entries for the fake ypserver.
1912 This is needed for being able to manipulate posix attrs via ADUC.
1914 samdb.transaction_start()
1915 try:
1916 logger.info("Setting up fake yp server settings")
1917 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1918 "DOMAINDN": domaindn,
1919 "NETBIOSNAME": netbiosname,
1920 "NISDOMAIN": nisdomain,
1922 except:
1923 samdb.transaction_cancel()
1924 raise
1925 else:
1926 samdb.transaction_commit()
1929 def provision(logger, session_info, smbconf=None,
1930 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1931 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1932 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1933 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1934 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1935 dns_backend=None, dns_forwarder=None, dnspass=None,
1936 invocationid=None, machinepass=None, ntdsguid=None,
1937 root=None, nobody=None, users=None, backup=None, aci=None,
1938 serverrole=None, dom_for_fun_level=None, backend_type=None,
1939 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1940 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1941 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
1942 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, ldap_backend_extra_port=None):
1943 """Provision samba4
1945 :note: caution, this wipes all existing data!
1948 try:
1949 serverrole = sanitize_server_role(serverrole)
1950 except ValueError:
1951 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1953 if ldapadminpass is None:
1954 # Make a new, random password between Samba and it's LDAP server
1955 ldapadminpass = samba.generate_random_password(128, 255)
1957 if backend_type is None:
1958 backend_type = "ldb"
1960 if domainsid is None:
1961 domainsid = security.random_sid()
1963 root_uid = findnss_uid([root or "root"])
1964 nobody_uid = findnss_uid([nobody or "nobody"])
1965 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1966 root_gid = pwd.getpwuid(root_uid).pw_gid
1968 try:
1969 bind_gid = findnss_gid(["bind", "named"])
1970 except KeyError:
1971 bind_gid = None
1973 if targetdir is not None:
1974 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1975 elif smbconf is None:
1976 smbconf = samba.param.default_path()
1977 if not os.path.exists(os.path.dirname(smbconf)):
1978 os.makedirs(os.path.dirname(smbconf))
1980 server_services = []
1981 global_param = {}
1982 if use_rfc2307:
1983 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1985 if dns_backend != "SAMBA_INTERNAL":
1986 server_services.append("-dns")
1987 else:
1988 if dns_forwarder is not None:
1989 global_param["dns forwarder"] = [dns_forwarder]
1991 if use_ntvfs:
1992 server_services.append("+smb")
1993 server_services.append("-s3fs")
1994 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1996 if len(server_services) > 0:
1997 global_param["server services"] = server_services
1999 # only install a new smb.conf if there isn't one there already
2000 if os.path.exists(smbconf):
2001 # if Samba Team members can't figure out the weird errors
2002 # loading an empty smb.conf gives, then we need to be smarter.
2003 # Pretend it just didn't exist --abartlet
2004 f = open(smbconf, 'r')
2005 try:
2006 data = f.read().lstrip()
2007 finally:
2008 f.close()
2009 if data is None or data == "":
2010 make_smbconf(smbconf, hostname, domain, realm,
2011 targetdir, serverrole=serverrole,
2012 eadb=useeadb, use_ntvfs=use_ntvfs,
2013 lp=lp, global_param=global_param)
2014 else:
2015 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2016 serverrole=serverrole,
2017 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2019 if lp is None:
2020 lp = samba.param.LoadParm()
2021 lp.load(smbconf)
2022 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2023 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2024 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2025 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2026 paths = provision_paths_from_lp(lp, names.dnsdomain)
2028 paths.bind_gid = bind_gid
2029 paths.root_uid = root_uid;
2030 paths.root_gid = root_gid
2032 if hostip is None:
2033 logger.info("Looking up IPv4 addresses")
2034 hostips = interface_ips_v4(lp)
2035 if len(hostips) > 0:
2036 hostip = hostips[0]
2037 if len(hostips) > 1:
2038 logger.warning("More than one IPv4 address found. Using %s",
2039 hostip)
2040 if hostip == "127.0.0.1":
2041 hostip = None
2042 if hostip is None:
2043 logger.warning("No IPv4 address will be assigned")
2045 if hostip6 is None:
2046 logger.info("Looking up IPv6 addresses")
2047 hostips = interface_ips_v6(lp)
2048 if hostips:
2049 hostip6 = hostips[0]
2050 if len(hostips) > 1:
2051 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2052 if hostip6 is None:
2053 logger.warning("No IPv6 address will be assigned")
2055 names.hostip = hostip
2056 names.hostip6 = hostip6
2057 names.domainguid = domainguid
2058 names.domainsid = domainsid
2059 names.forestsid = domainsid
2061 if serverrole is None:
2062 serverrole = lp.get("server role")
2064 if not os.path.exists(paths.private_dir):
2065 os.mkdir(paths.private_dir)
2066 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
2067 os.makedirs(os.path.join(paths.private_dir, "tls"), 0700)
2068 if not os.path.exists(paths.state_dir):
2069 os.mkdir(paths.state_dir)
2071 if paths.sysvol and not os.path.exists(paths.sysvol):
2072 os.makedirs(paths.sysvol, 0775)
2074 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2076 schema = Schema(domainsid, invocationid=invocationid,
2077 schemadn=names.schemadn)
2079 if backend_type == "ldb":
2080 provision_backend = LDBBackend(backend_type, paths=paths,
2081 lp=lp,
2082 names=names, logger=logger)
2083 elif backend_type == "existing":
2084 # If support for this is ever added back, then the URI will need to be
2085 # specified again
2086 provision_backend = ExistingBackend(backend_type, paths=paths,
2087 lp=lp,
2088 names=names, logger=logger,
2089 ldap_backend_forced_uri=ldap_backend_forced_uri)
2090 elif backend_type == "fedora-ds":
2091 provision_backend = FDSBackend(backend_type, paths=paths,
2092 lp=lp,
2093 names=names, logger=logger, domainsid=domainsid,
2094 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2095 slapd_path=slapd_path,
2096 root=root)
2097 elif backend_type == "openldap":
2098 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2099 lp=lp,
2100 names=names, logger=logger, domainsid=domainsid,
2101 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2102 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2103 ldap_backend_extra_port=ldap_backend_extra_port,
2104 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2105 ldap_backend_forced_uri=ldap_backend_forced_uri)
2106 else:
2107 raise ValueError("Unknown LDAP backend type selected")
2109 provision_backend.init()
2110 provision_backend.start()
2112 # only install a new shares config db if there is none
2113 if not os.path.exists(paths.shareconf):
2114 logger.info("Setting up share.ldb")
2115 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2116 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2118 logger.info("Setting up secrets.ldb")
2119 secrets_ldb = setup_secretsdb(paths,
2120 session_info=session_info,
2121 backend_credentials=provision_backend.credentials, lp=lp)
2123 try:
2124 logger.info("Setting up the registry")
2125 setup_registry(paths.hklm, session_info, lp=lp)
2127 logger.info("Setting up the privileges database")
2128 setup_privileges(paths.privilege, session_info, lp=lp)
2130 logger.info("Setting up idmap db")
2131 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2133 setup_name_mappings(idmap, sid=str(domainsid),
2134 root_uid=root_uid, nobody_uid=nobody_uid,
2135 users_gid=users_gid, root_gid=root_gid)
2137 logger.info("Setting up SAM db")
2138 samdb = setup_samdb(paths.samdb, session_info,
2139 provision_backend, lp, names, logger=logger,
2140 serverrole=serverrole,
2141 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2143 if serverrole == "active directory domain controller":
2144 if paths.netlogon is None:
2145 raise MissingShareError("netlogon", paths.smbconf)
2147 if paths.sysvol is None:
2148 raise MissingShareError("sysvol", paths.smbconf)
2150 if not os.path.isdir(paths.netlogon):
2151 os.makedirs(paths.netlogon, 0755)
2153 if adminpass is None:
2154 adminpass = samba.generate_random_password(12, 32)
2155 adminpass_generated = True
2156 else:
2157 adminpass = unicode(adminpass, 'utf-8')
2158 adminpass_generated = False
2160 if samdb_fill == FILL_FULL:
2161 provision_fill(samdb, secrets_ldb, logger, names, paths,
2162 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2163 hostip=hostip, hostip6=hostip6,
2164 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2165 krbtgtpass=krbtgtpass,
2166 policyguid=policyguid, policyguid_dc=policyguid_dc,
2167 invocationid=invocationid, machinepass=machinepass,
2168 ntdsguid=ntdsguid, dns_backend=dns_backend,
2169 dnspass=dnspass, serverrole=serverrole,
2170 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2171 lp=lp, use_ntvfs=use_ntvfs,
2172 skip_sysvolacl=skip_sysvolacl)
2174 create_krb5_conf(paths.krb5conf,
2175 dnsdomain=names.dnsdomain, hostname=names.hostname,
2176 realm=names.realm)
2177 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2178 "generated at %s", paths.krb5conf)
2180 if serverrole == "active directory domain controller":
2181 create_dns_update_list(lp, logger, paths)
2183 backend_result = provision_backend.post_setup()
2184 provision_backend.shutdown()
2186 except:
2187 secrets_ldb.transaction_cancel()
2188 raise
2190 # Now commit the secrets.ldb to disk
2191 secrets_ldb.transaction_commit()
2193 # the commit creates the dns.keytab, now chown it
2194 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2195 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2196 try:
2197 os.chmod(dns_keytab_path, 0640)
2198 os.chown(dns_keytab_path, -1, paths.bind_gid)
2199 except OSError:
2200 if not os.environ.has_key('SAMBA_SELFTEST'):
2201 logger.info("Failed to chown %s to bind gid %u",
2202 dns_keytab_path, paths.bind_gid)
2204 result = ProvisionResult()
2205 result.server_role = serverrole
2206 result.domaindn = domaindn
2207 result.paths = paths
2208 result.names = names
2209 result.lp = lp
2210 result.samdb = samdb
2211 result.idmap = idmap
2212 result.domainsid = str(domainsid)
2214 if samdb_fill == FILL_FULL:
2215 result.adminpass_generated = adminpass_generated
2216 result.adminpass = adminpass
2217 else:
2218 result.adminpass_generated = False
2219 result.adminpass = None
2221 result.backend_result = backend_result
2223 if use_rfc2307:
2224 provision_fake_ypserver(logger=logger, samdb=samdb,
2225 domaindn=names.domaindn, netbiosname=names.netbiosname,
2226 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2228 return result
2231 def provision_become_dc(smbconf=None, targetdir=None,
2232 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2233 serverdn=None, domain=None, hostname=None, domainsid=None,
2234 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2235 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2236 dns_backend=None, root=None, nobody=None, users=None,
2237 backup=None, serverrole=None, ldap_backend=None,
2238 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2240 logger = logging.getLogger("provision")
2241 samba.set_debug_level(debuglevel)
2243 res = provision(logger, system_session(),
2244 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2245 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2246 configdn=configdn, serverdn=serverdn, domain=domain,
2247 hostname=hostname, hostip=None, domainsid=domainsid,
2248 machinepass=machinepass,
2249 serverrole="active directory domain controller",
2250 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2251 use_ntvfs=use_ntvfs)
2252 res.lp.set("debuglevel", str(debuglevel))
2253 return res
2256 def create_krb5_conf(path, dnsdomain, hostname, realm):
2257 """Write out a file containing a valid krb5.conf file
2259 :param path: Path of the new krb5.conf file.
2260 :param dnsdomain: DNS Domain name
2261 :param hostname: Local hostname
2262 :param realm: Realm name
2264 setup_file(setup_path("krb5.conf"), path, {
2265 "DNSDOMAIN": dnsdomain,
2266 "HOSTNAME": hostname,
2267 "REALM": realm,
2271 class ProvisioningError(Exception):
2272 """A generic provision error."""
2274 def __init__(self, value):
2275 self.value = value
2277 def __str__(self):
2278 return "ProvisioningError: " + self.value
2281 class InvalidNetbiosName(Exception):
2282 """A specified name was not a valid NetBIOS name."""
2284 def __init__(self, name):
2285 super(InvalidNetbiosName, self).__init__(
2286 "The name '%r' is not a valid NetBIOS name" % name)
2289 class MissingShareError(ProvisioningError):
2291 def __init__(self, name, smbconf):
2292 super(MissingShareError, self).__init__(
2293 "Existing smb.conf does not have a [%s] share, but you are "
2294 "configuring a DC. Please remove %s or add the share manually." %
2295 (name, smbconf))