provision/sambadns: remove redundant site parameter
[Samba.git] / python / samba / provision / __init__.py
blob477c5ddc4b91573c519f96dd1f9a5333ac80adf2
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.name_map = {}
170 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
171 lp):
172 """Get key provision parameters (realm, domain, ...) from a given provision
174 :param samdb: An LDB object connected to the sam.ldb file
175 :param secretsdb: An LDB object connected to the secrets.ldb file
176 :param idmapdb: An LDB object connected to the idmap.ldb file
177 :param paths: A list of path to provision object
178 :param smbconf: Path to the smb.conf file
179 :param lp: A LoadParm object
180 :return: A list of key provision parameters
182 names = ProvisionNames()
183 names.adminpass = None
185 # NT domain, kerberos realm, root dn, domain dn, domain dns name
186 names.domain = string.upper(lp.get("workgroup"))
187 names.realm = lp.get("realm")
188 names.dnsdomain = names.realm.lower()
189 basedn = samba.dn_from_dns_name(names.dnsdomain)
190 names.realm = string.upper(names.realm)
191 # netbiosname
192 # Get the netbiosname first (could be obtained from smb.conf in theory)
193 res = secretsdb.search(expression="(flatname=%s)" %
194 names.domain,base="CN=Primary Domains",
195 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
196 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
198 names.smbconf = smbconf
200 # That's a bit simplistic but it's ok as long as we have only 3
201 # partitions
202 current = samdb.search(expression="(objectClass=*)",
203 base="", scope=ldb.SCOPE_BASE,
204 attrs=["defaultNamingContext", "schemaNamingContext",
205 "configurationNamingContext","rootDomainNamingContext",
206 "namingContexts"])
208 names.configdn = current[0]["configurationNamingContext"][0]
209 names.schemadn = current[0]["schemaNamingContext"][0]
210 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
211 current[0]["defaultNamingContext"][0]))):
212 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
213 "is not the same ..." % (paths.samdb,
214 str(current[0]["defaultNamingContext"][0]),
215 paths.smbconf, basedn)))
217 names.domaindn=current[0]["defaultNamingContext"][0]
218 names.rootdn=current[0]["rootDomainNamingContext"][0]
219 names.ncs=current[0]["namingContexts"]
220 names.dnsforestdn = None
221 names.dnsdomaindn = None
223 for i in range(0, len(names.ncs)):
224 nc = names.ncs[i]
226 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
227 if nc == dnsforestdn:
228 names.dnsforestdn = dnsforestdn
229 continue
231 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
232 if nc == dnsdomaindn:
233 names.dnsdomaindn = dnsdomaindn
234 continue
236 # default site name
237 res3 = samdb.search(expression="(objectClass=site)",
238 base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
239 names.sitename = str(res3[0]["cn"])
241 # dns hostname and server dn
242 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
243 base="OU=Domain Controllers,%s" % basedn,
244 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
245 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
247 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
248 attrs=[], base=names.configdn)
249 names.serverdn = str(server_res[0].dn)
251 # invocation id/objectguid
252 res5 = samdb.search(expression="(objectClass=*)",
253 base="CN=NTDS Settings,%s" % str(names.serverdn),
254 scope=ldb.SCOPE_BASE,
255 attrs=["invocationID", "objectGUID"])
256 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
257 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
259 # domain guid/sid
260 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
261 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
262 "objectSid","msDS-Behavior-Version" ])
263 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
264 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
265 if res6[0].get("msDS-Behavior-Version") is None or \
266 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
267 names.domainlevel = DS_DOMAIN_FUNCTION_2000
268 else:
269 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
271 # policy guid
272 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
273 base="CN=Policies,CN=System," + basedn,
274 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
275 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
276 # dc policy guid
277 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
278 " Policy)",
279 base="CN=Policies,CN=System," + basedn,
280 scope=ldb.SCOPE_ONELEVEL,
281 attrs=["cn","displayName"])
282 if len(res8) == 1:
283 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
284 else:
285 names.policyid_dc = None
287 res9 = idmapdb.search(expression="(cn=%s-%s)" %
288 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
289 attrs=["xidNumber", "type"])
290 if len(res9) != 1:
291 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
292 if res9[0]["type"][0] == "ID_TYPE_BOTH":
293 names.root_gid = res9[0]["xidNumber"][0]
294 else:
295 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
297 res10 = samdb.search(expression="(samaccountname=dns)",
298 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
299 controls=["search_options:1:2"])
300 if (len(res10) > 0):
301 has_legacy_dns_account = True
302 else:
303 has_legacy_dns_account = False
305 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
306 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
307 controls=["search_options:1:2"])
308 if (len(res11) > 0):
309 has_dns_account = True
310 else:
311 has_dns_account = False
313 if names.dnsdomaindn is not None:
314 if has_dns_account:
315 names.dns_backend = 'BIND9_DLZ'
316 else:
317 names.dns_backend = 'SAMBA_INTERNAL'
318 elif has_dns_account or has_legacy_dns_account:
319 names.dns_backend = 'BIND9_FLATFILE'
320 else:
321 names.dns_backend = 'NONE'
323 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
324 names.name_map['DnsAdmins'] = str(dns_admins_sid)
326 return names
329 def update_provision_usn(samdb, low, high, id, replace=False):
330 """Update the field provisionUSN in sam.ldb
332 This field is used to track range of USN modified by provision and
333 upgradeprovision.
334 This value is used afterward by next provision to figure out if
335 the field have been modified since last provision.
337 :param samdb: An LDB object connect to sam.ldb
338 :param low: The lowest USN modified by this upgrade
339 :param high: The highest USN modified by this upgrade
340 :param id: The invocation id of the samba's dc
341 :param replace: A boolean indicating if the range should replace any
342 existing one or appended (default)
345 tab = []
346 if not replace:
347 entry = samdb.search(base="@PROVISION",
348 scope=ldb.SCOPE_BASE,
349 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
350 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
351 if not re.search(';', e):
352 e = "%s;%s" % (e, id)
353 tab.append(str(e))
355 tab.append("%s-%s;%s" % (low, high, id))
356 delta = ldb.Message()
357 delta.dn = ldb.Dn(samdb, "@PROVISION")
358 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
359 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
360 entry = samdb.search(expression='provisionnerID=*',
361 base="@PROVISION", scope=ldb.SCOPE_BASE,
362 attrs=["provisionnerID"])
363 if len(entry) == 0 or len(entry[0]) == 0:
364 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
365 samdb.modify(delta)
368 def set_provision_usn(samdb, low, high, id):
369 """Set the field provisionUSN in sam.ldb
370 This field is used to track range of USN modified by provision and
371 upgradeprovision.
372 This value is used afterward by next provision to figure out if
373 the field have been modified since last provision.
375 :param samdb: An LDB object connect to sam.ldb
376 :param low: The lowest USN modified by this upgrade
377 :param high: The highest USN modified by this upgrade
378 :param id: The invocationId of the provision"""
380 tab = []
381 tab.append("%s-%s;%s" % (low, high, id))
383 delta = ldb.Message()
384 delta.dn = ldb.Dn(samdb, "@PROVISION")
385 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
386 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
387 samdb.add(delta)
390 def get_max_usn(samdb,basedn):
391 """ This function return the biggest USN present in the provision
393 :param samdb: A LDB object pointing to the sam.ldb
394 :param basedn: A string containing the base DN of the provision
395 (ie. DC=foo, DC=bar)
396 :return: The biggest USN in the provision"""
398 res = samdb.search(expression="objectClass=*",base=basedn,
399 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
400 controls=["search_options:1:2",
401 "server_sort:1:1:uSNChanged",
402 "paged_results:1:1"])
403 return res[0]["uSNChanged"]
406 def get_last_provision_usn(sam):
407 """Get USNs ranges modified by a provision or an upgradeprovision
409 :param sam: An LDB object pointing to the sam.ldb
410 :return: a dictionary which keys are invocation id and values are an array
411 of integer representing the different ranges
413 try:
414 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
415 base="@PROVISION", scope=ldb.SCOPE_BASE,
416 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
417 except ldb.LdbError, (ecode, emsg):
418 if ecode == ldb.ERR_NO_SUCH_OBJECT:
419 return None
420 raise
421 if len(entry) > 0:
422 myids = []
423 range = {}
424 p = re.compile(r'-')
425 if entry[0].get("provisionnerID"):
426 for e in entry[0]["provisionnerID"]:
427 myids.append(str(e))
428 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
429 tab1 = str(r).split(';')
430 if len(tab1) == 2:
431 id = tab1[1]
432 else:
433 id = "default"
434 if (len(myids) > 0 and id not in myids):
435 continue
436 tab2 = p.split(tab1[0])
437 if range.get(id) is None:
438 range[id] = []
439 range[id].append(tab2[0])
440 range[id].append(tab2[1])
441 return range
442 else:
443 return None
446 class ProvisionResult(object):
447 """Result of a provision.
449 :ivar server_role: The server role
450 :ivar paths: ProvisionPaths instance
451 :ivar domaindn: The domain dn, as string
454 def __init__(self):
455 self.server_role = None
456 self.paths = None
457 self.domaindn = None
458 self.lp = None
459 self.samdb = None
460 self.idmap = None
461 self.names = None
462 self.domainsid = None
463 self.adminpass_generated = None
464 self.adminpass = None
465 self.backend_result = None
467 def report_logger(self, logger):
468 """Report this provision result to a logger."""
469 logger.info(
470 "Once the above files are installed, your Samba4 server will "
471 "be ready to use")
472 if self.adminpass_generated:
473 logger.info("Admin password: %s", self.adminpass)
474 logger.info("Server Role: %s", self.server_role)
475 logger.info("Hostname: %s", self.names.hostname)
476 logger.info("NetBIOS Domain: %s", self.names.domain)
477 logger.info("DNS Domain: %s", self.names.dnsdomain)
478 logger.info("DOMAIN SID: %s", self.domainsid)
480 if self.backend_result:
481 self.backend_result.report_logger(logger)
484 def check_install(lp, session_info, credentials):
485 """Check whether the current install seems ok.
487 :param lp: Loadparm context
488 :param session_info: Session information
489 :param credentials: Credentials
491 if lp.get("realm") == "":
492 raise Exception("Realm empty")
493 samdb = Ldb(lp.samdb_url(), session_info=session_info,
494 credentials=credentials, lp=lp)
495 if len(samdb.search("(cn=Administrator)")) != 1:
496 raise ProvisioningError("No administrator account found")
499 def findnss(nssfn, names):
500 """Find a user or group from a list of possibilities.
502 :param nssfn: NSS Function to try (should raise KeyError if not found)
503 :param names: Names to check.
504 :return: Value return by first names list.
506 for name in names:
507 try:
508 return nssfn(name)
509 except KeyError:
510 pass
511 raise KeyError("Unable to find user/group in %r" % names)
514 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
515 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
518 def provision_paths_from_lp(lp, dnsdomain):
519 """Set the default paths for provisioning.
521 :param lp: Loadparm context.
522 :param dnsdomain: DNS Domain name
524 paths = ProvisionPaths()
525 paths.private_dir = lp.get("private dir")
526 paths.state_dir = lp.get("state directory")
528 # This is stored without path prefix for the "privateKeytab" attribute in
529 # "secrets_dns.ldif".
530 paths.dns_keytab = "dns.keytab"
531 paths.keytab = "secrets.keytab"
533 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
534 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
535 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
536 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
537 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
538 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
539 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
540 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
541 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
542 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
543 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
544 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
545 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
546 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
547 paths.hklm = "hklm.ldb"
548 paths.hkcr = "hkcr.ldb"
549 paths.hkcu = "hkcu.ldb"
550 paths.hku = "hku.ldb"
551 paths.hkpd = "hkpd.ldb"
552 paths.hkpt = "hkpt.ldb"
553 paths.sysvol = lp.get("path", "sysvol")
554 paths.netlogon = lp.get("path", "netlogon")
555 paths.smbconf = lp.configfile
556 return paths
559 def determine_netbios_name(hostname):
560 """Determine a netbios name from a hostname."""
561 # remove forbidden chars and force the length to be <16
562 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
563 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
566 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
567 serverrole=None, rootdn=None, domaindn=None, configdn=None,
568 schemadn=None, serverdn=None, sitename=None,
569 domain_names_forced=False):
570 """Guess configuration settings to use."""
572 if hostname is None:
573 hostname = socket.gethostname().split(".")[0]
575 netbiosname = lp.get("netbios name")
576 if netbiosname is None:
577 netbiosname = determine_netbios_name(hostname)
578 netbiosname = netbiosname.upper()
579 if not valid_netbios_name(netbiosname):
580 raise InvalidNetbiosName(netbiosname)
582 if dnsdomain is None:
583 dnsdomain = lp.get("realm")
584 if dnsdomain is None or dnsdomain == "":
585 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
587 dnsdomain = dnsdomain.lower()
589 if serverrole is None:
590 serverrole = lp.get("server role")
591 if serverrole is None:
592 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
594 serverrole = serverrole.lower()
596 realm = dnsdomain.upper()
598 if lp.get("realm") == "":
599 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
601 if lp.get("realm").upper() != realm:
602 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))
604 if lp.get("server role").lower() != serverrole:
605 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))
607 if serverrole == "active directory domain controller":
608 if domain is None:
609 # This will, for better or worse, default to 'WORKGROUP'
610 domain = lp.get("workgroup")
611 domain = domain.upper()
613 if lp.get("workgroup").upper() != domain:
614 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))
616 if domaindn is None:
617 domaindn = samba.dn_from_dns_name(dnsdomain)
619 if domain == netbiosname:
620 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
621 else:
622 domain = netbiosname
623 if domaindn is None:
624 domaindn = "DC=" + netbiosname
626 if not valid_netbios_name(domain):
627 raise InvalidNetbiosName(domain)
629 if hostname.upper() == realm:
630 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
631 if netbiosname.upper() == realm:
632 raise ProvisioningError("guess_names: Realm '%s' must not be equal to NetBIOS hostname '%s'!" % (realm, netbiosname))
633 if domain == realm and not domain_names_forced:
634 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
636 if rootdn is None:
637 rootdn = domaindn
639 if configdn is None:
640 configdn = "CN=Configuration," + rootdn
641 if schemadn is None:
642 schemadn = "CN=Schema," + configdn
644 if sitename is None:
645 sitename = DEFAULTSITE
647 names = ProvisionNames()
648 names.rootdn = rootdn
649 names.domaindn = domaindn
650 names.configdn = configdn
651 names.schemadn = schemadn
652 names.ldapmanagerdn = "CN=Manager," + rootdn
653 names.dnsdomain = dnsdomain
654 names.domain = domain
655 names.realm = realm
656 names.netbiosname = netbiosname
657 names.hostname = hostname
658 names.sitename = sitename
659 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
660 netbiosname, sitename, configdn)
662 return names
665 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
666 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
667 global_param=None):
668 """Create a new smb.conf file based on a couple of basic settings.
670 assert smbconf is not None
672 if hostname is None:
673 hostname = socket.gethostname().split(".")[0]
675 netbiosname = determine_netbios_name(hostname)
677 if serverrole is None:
678 serverrole = "standalone server"
680 assert domain is not None
681 domain = domain.upper()
683 assert realm is not None
684 realm = realm.upper()
686 global_settings = {
687 "netbios name": netbiosname,
688 "workgroup": domain,
689 "realm": realm,
690 "server role": serverrole,
693 if lp is None:
694 lp = samba.param.LoadParm()
695 #Load non-existent file
696 if os.path.exists(smbconf):
697 lp.load(smbconf)
699 if global_param is not None:
700 for ent in global_param:
701 if global_param[ent] is not None:
702 global_settings[ent] = " ".join(global_param[ent])
704 if targetdir is not None:
705 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
706 global_settings["lock dir"] = os.path.abspath(targetdir)
707 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
708 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
710 lp.set("lock dir", os.path.abspath(targetdir))
711 lp.set("state directory", global_settings["state directory"])
712 lp.set("cache directory", global_settings["cache directory"])
714 if eadb:
715 if use_ntvfs and not lp.get("posix:eadb"):
716 if targetdir is not None:
717 privdir = os.path.join(targetdir, "private")
718 else:
719 privdir = lp.get("private dir")
720 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
721 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
722 if targetdir is not None:
723 statedir = os.path.join(targetdir, "state")
724 else:
725 statedir = lp.get("state directory")
726 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
728 shares = {}
729 if serverrole == "active directory domain controller":
730 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
731 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
732 "scripts")
733 else:
734 global_settings["passdb backend"] = "samba_dsdb"
736 f = open(smbconf, 'w')
737 try:
738 f.write("[globals]\n")
739 for key, val in global_settings.iteritems():
740 f.write("\t%s = %s\n" % (key, val))
741 f.write("\n")
743 for name, path in shares.iteritems():
744 f.write("[%s]\n" % name)
745 f.write("\tpath = %s\n" % path)
746 f.write("\tread only = no\n")
747 f.write("\n")
748 finally:
749 f.close()
750 # reload the smb.conf
751 lp.load(smbconf)
753 # and dump it without any values that are the default
754 # this ensures that any smb.conf parameters that were set
755 # on the provision/join command line are set in the resulting smb.conf
756 f = open(smbconf, mode='w')
757 try:
758 lp.dump(f, False)
759 finally:
760 f.close()
763 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
764 users_gid, root_gid):
765 """setup reasonable name mappings for sam names to unix names.
767 :param samdb: SamDB object.
768 :param idmap: IDmap db object.
769 :param sid: The domain sid.
770 :param domaindn: The domain DN.
771 :param root_uid: uid of the UNIX root user.
772 :param nobody_uid: uid of the UNIX nobody user.
773 :param users_gid: gid of the UNIX users group.
774 :param root_gid: gid of the UNIX root group.
776 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
778 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
779 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
782 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
783 provision_backend, names, schema, serverrole,
784 erase=False):
785 """Setup the partitions for the SAM database.
787 Alternatively, provision() may call this, and then populate the database.
789 :note: This will wipe the Sam Database!
791 :note: This function always removes the local SAM LDB file. The erase
792 parameter controls whether to erase the existing data, which
793 may not be stored locally but in LDAP.
796 assert session_info is not None
798 # We use options=["modules:"] to stop the modules loading - we
799 # just want to wipe and re-initialise the database, not start it up
801 try:
802 os.unlink(samdb_path)
803 except OSError:
804 pass
806 samdb = Ldb(url=samdb_path, session_info=session_info,
807 lp=lp, options=["modules:"])
809 ldap_backend_line = "# No LDAP backend"
810 if provision_backend.type != "ldb":
811 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
813 samdb.transaction_start()
814 try:
815 logger.info("Setting up sam.ldb partitions and settings")
816 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
817 "LDAP_BACKEND_LINE": ldap_backend_line
821 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
822 "BACKEND_TYPE": provision_backend.type,
823 "SERVER_ROLE": serverrole
826 logger.info("Setting up sam.ldb rootDSE")
827 setup_samdb_rootdse(samdb, names)
828 except:
829 samdb.transaction_cancel()
830 raise
831 else:
832 samdb.transaction_commit()
835 def secretsdb_self_join(secretsdb, domain,
836 netbiosname, machinepass, domainsid=None,
837 realm=None, dnsdomain=None,
838 keytab_path=None,
839 key_version_number=1,
840 secure_channel_type=SEC_CHAN_WKSTA):
841 """Add domain join-specific bits to a secrets database.
843 :param secretsdb: Ldb Handle to the secrets database
844 :param machinepass: Machine password
846 attrs = ["whenChanged",
847 "secret",
848 "priorSecret",
849 "priorChanged",
850 "krb5Keytab",
851 "privateKeytab"]
853 if realm is not None:
854 if dnsdomain is None:
855 dnsdomain = realm.lower()
856 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
857 else:
858 dnsname = None
859 shortname = netbiosname.lower()
861 # We don't need to set msg["flatname"] here, because rdn_name will handle
862 # it, and it causes problems for modifies anyway
863 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
864 msg["secureChannelType"] = [str(secure_channel_type)]
865 msg["objectClass"] = ["top", "primaryDomain"]
866 if dnsname is not None:
867 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
868 msg["realm"] = [realm]
869 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
870 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
871 msg["privateKeytab"] = ["secrets.keytab"]
873 msg["secret"] = [machinepass]
874 msg["samAccountName"] = ["%s$" % netbiosname]
875 msg["secureChannelType"] = [str(secure_channel_type)]
876 if domainsid is not None:
877 msg["objectSid"] = [ndr_pack(domainsid)]
879 # This complex expression tries to ensure that we don't have more
880 # than one record for this SID, realm or netbios domain at a time,
881 # but we don't delete the old record that we are about to modify,
882 # because that would delete the keytab and previous password.
883 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
884 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
885 scope=ldb.SCOPE_ONELEVEL)
887 for del_msg in res:
888 secretsdb.delete(del_msg.dn)
890 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
892 if len(res) == 1:
893 msg["priorSecret"] = [res[0]["secret"][0]]
894 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
896 try:
897 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
898 except KeyError:
899 pass
901 try:
902 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
903 except KeyError:
904 pass
906 for el in msg:
907 if el != 'dn':
908 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
909 secretsdb.modify(msg)
910 secretsdb.rename(res[0].dn, msg.dn)
911 else:
912 spn = [ 'HOST/%s' % shortname ]
913 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
914 # we are a domain controller then we add servicePrincipalName
915 # entries for the keytab code to update.
916 spn.extend([ 'HOST/%s' % dnsname ])
917 msg["servicePrincipalName"] = spn
919 secretsdb.add(msg)
922 def setup_secretsdb(paths, session_info, backend_credentials, lp):
923 """Setup the secrets database.
925 :note: This function does not handle exceptions and transaction on purpose,
926 it's up to the caller to do this job.
928 :param path: Path to the secrets database.
929 :param session_info: Session info.
930 :param credentials: Credentials
931 :param lp: Loadparm context
932 :return: LDB handle for the created secrets database
934 if os.path.exists(paths.secrets):
935 os.unlink(paths.secrets)
937 keytab_path = os.path.join(paths.private_dir, paths.keytab)
938 if os.path.exists(keytab_path):
939 os.unlink(keytab_path)
941 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
942 if os.path.exists(dns_keytab_path):
943 os.unlink(dns_keytab_path)
945 path = paths.secrets
947 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
948 secrets_ldb.erase()
949 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
950 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
951 secrets_ldb.transaction_start()
952 try:
953 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
955 if (backend_credentials is not None and
956 backend_credentials.authentication_requested()):
957 if backend_credentials.get_bind_dn() is not None:
958 setup_add_ldif(secrets_ldb,
959 setup_path("secrets_simple_ldap.ldif"), {
960 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
961 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
963 else:
964 setup_add_ldif(secrets_ldb,
965 setup_path("secrets_sasl_ldap.ldif"), {
966 "LDAPADMINUSER": backend_credentials.get_username(),
967 "LDAPADMINREALM": backend_credentials.get_realm(),
968 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
970 except:
971 secrets_ldb.transaction_cancel()
972 raise
973 return secrets_ldb
976 def setup_privileges(path, session_info, lp):
977 """Setup the privileges database.
979 :param path: Path to the privileges database.
980 :param session_info: Session info.
981 :param credentials: Credentials
982 :param lp: Loadparm context
983 :return: LDB handle for the created secrets database
985 if os.path.exists(path):
986 os.unlink(path)
987 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
988 privilege_ldb.erase()
989 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
992 def setup_registry(path, session_info, lp):
993 """Setup the registry.
995 :param path: Path to the registry database
996 :param session_info: Session information
997 :param credentials: Credentials
998 :param lp: Loadparm context
1000 reg = samba.registry.Registry()
1001 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1002 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1003 provision_reg = setup_path("provision.reg")
1004 assert os.path.exists(provision_reg)
1005 reg.diff_apply(provision_reg)
1008 def setup_idmapdb(path, session_info, lp):
1009 """Setup the idmap database.
1011 :param path: path to the idmap database
1012 :param session_info: Session information
1013 :param credentials: Credentials
1014 :param lp: Loadparm context
1016 if os.path.exists(path):
1017 os.unlink(path)
1019 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1020 idmap_ldb.erase()
1021 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1022 return idmap_ldb
1025 def setup_samdb_rootdse(samdb, names):
1026 """Setup the SamDB rootdse.
1028 :param samdb: Sam Database handle
1030 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1031 "SCHEMADN": names.schemadn,
1032 "DOMAINDN": names.domaindn,
1033 "ROOTDN" : names.rootdn,
1034 "CONFIGDN": names.configdn,
1035 "SERVERDN": names.serverdn,
1039 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1040 dns_backend, dnspass, domainsid, next_rid, invocationid,
1041 policyguid, policyguid_dc,
1042 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1043 """Join a host to its own domain."""
1044 assert isinstance(invocationid, str)
1045 if ntdsguid is not None:
1046 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1047 else:
1048 ntdsguid_line = ""
1050 if dc_rid is None:
1051 dc_rid = next_rid
1053 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1054 "CONFIGDN": names.configdn,
1055 "SCHEMADN": names.schemadn,
1056 "DOMAINDN": names.domaindn,
1057 "SERVERDN": names.serverdn,
1058 "INVOCATIONID": invocationid,
1059 "NETBIOSNAME": names.netbiosname,
1060 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1061 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1062 "DOMAINSID": str(domainsid),
1063 "DCRID": str(dc_rid),
1064 "SAMBA_VERSION_STRING": version,
1065 "NTDSGUID": ntdsguid_line,
1066 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1067 domainControllerFunctionality),
1068 "RIDALLOCATIONSTART": str(next_rid + 100),
1069 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1071 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1072 "POLICYGUID": policyguid,
1073 "POLICYGUID_DC": policyguid_dc,
1074 "DNSDOMAIN": names.dnsdomain,
1075 "DOMAINDN": names.domaindn})
1077 # If we are setting up a subdomain, then this has been replicated in, so we
1078 # don't need to add it
1079 if fill == FILL_FULL:
1080 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1081 "CONFIGDN": names.configdn,
1082 "SCHEMADN": names.schemadn,
1083 "DOMAINDN": names.domaindn,
1084 "SERVERDN": names.serverdn,
1085 "INVOCATIONID": invocationid,
1086 "NETBIOSNAME": names.netbiosname,
1087 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1088 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1089 "DOMAINSID": str(domainsid),
1090 "DCRID": str(dc_rid),
1091 "SAMBA_VERSION_STRING": version,
1092 "NTDSGUID": ntdsguid_line,
1093 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1094 domainControllerFunctionality)})
1096 # Setup fSMORoleOwner entries to point at the newly created DC entry
1097 setup_modify_ldif(samdb,
1098 setup_path("provision_self_join_modify_config.ldif"), {
1099 "CONFIGDN": names.configdn,
1100 "SCHEMADN": names.schemadn,
1101 "DEFAULTSITE": names.sitename,
1102 "NETBIOSNAME": names.netbiosname,
1103 "SERVERDN": names.serverdn,
1106 system_session_info = system_session()
1107 samdb.set_session_info(system_session_info)
1108 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1109 # modify a serverReference under cn=config when we are a subdomain, we must
1110 # be system due to ACLs
1111 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1112 "DOMAINDN": names.domaindn,
1113 "SERVERDN": names.serverdn,
1114 "NETBIOSNAME": names.netbiosname,
1117 samdb.set_session_info(admin_session_info)
1119 if dns_backend != "SAMBA_INTERNAL":
1120 # This is Samba4 specific and should be replaced by the correct
1121 # DNS AD-style setup
1122 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1123 "DNSDOMAIN": names.dnsdomain,
1124 "DOMAINDN": names.domaindn,
1125 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1126 "HOSTNAME" : names.hostname,
1127 "DNSNAME" : '%s.%s' % (
1128 names.netbiosname.lower(), names.dnsdomain.lower())
1132 def getpolicypath(sysvolpath, dnsdomain, guid):
1133 """Return the physical path of policy given its guid.
1135 :param sysvolpath: Path to the sysvol folder
1136 :param dnsdomain: DNS name of the AD domain
1137 :param guid: The GUID of the policy
1138 :return: A string with the complete path to the policy folder
1140 if guid[0] != "{":
1141 guid = "{%s}" % guid
1142 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1143 return policy_path
1146 def create_gpo_struct(policy_path):
1147 if not os.path.exists(policy_path):
1148 os.makedirs(policy_path, 0775)
1149 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1150 try:
1151 f.write("[General]\r\nVersion=0")
1152 finally:
1153 f.close()
1154 p = os.path.join(policy_path, "MACHINE")
1155 if not os.path.exists(p):
1156 os.makedirs(p, 0775)
1157 p = os.path.join(policy_path, "USER")
1158 if not os.path.exists(p):
1159 os.makedirs(p, 0775)
1162 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1163 """Create the default GPO for a domain
1165 :param sysvolpath: Physical path for the sysvol folder
1166 :param dnsdomain: DNS domain name of the AD domain
1167 :param policyguid: GUID of the default domain policy
1168 :param policyguid_dc: GUID of the default domain controler policy
1170 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1171 create_gpo_struct(policy_path)
1173 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1174 create_gpo_struct(policy_path)
1177 def setup_samdb(path, session_info, provision_backend, lp, names,
1178 logger, fill, serverrole, schema, am_rodc=False):
1179 """Setup a complete SAM Database.
1181 :note: This will wipe the main SAM database file!
1184 # Also wipes the database
1185 setup_samdb_partitions(path, logger=logger, lp=lp,
1186 provision_backend=provision_backend, session_info=session_info,
1187 names=names, serverrole=serverrole, schema=schema)
1189 # Load the database, but don's load the global schema and don't connect
1190 # quite yet
1191 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1192 credentials=provision_backend.credentials, lp=lp,
1193 global_schema=False, am_rodc=am_rodc)
1195 logger.info("Pre-loading the Samba 4 and AD schema")
1197 # Load the schema from the one we computed earlier
1198 samdb.set_schema(schema, write_indices_and_attributes=False)
1200 # Set the NTDS settings DN manually - in order to have it already around
1201 # before the provisioned tree exists and we connect
1202 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1204 # And now we can connect to the DB - the schema won't be loaded from the
1205 # DB
1206 try:
1207 samdb.connect(path)
1208 except ldb.LdbError, (num, string_error):
1209 if (num == ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS):
1210 raise ProvisioningError("Permission denied connecting to %s, are you running as root?" % path)
1211 else:
1212 raise
1214 # But we have to give it one more kick to have it use the schema
1215 # during provision - it needs, now that it is connected, to write
1216 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1217 samdb.set_schema(schema, write_indices_and_attributes=True)
1219 return samdb
1222 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1223 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1224 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1225 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1227 if next_rid is None:
1228 next_rid = 1000
1230 # Provision does not make much sense values larger than 1000000000
1231 # as the upper range of the rIDAvailablePool is 1073741823 and
1232 # we don't want to create a domain that cannot allocate rids.
1233 if next_rid < 1000 or next_rid > 1000000000:
1234 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1235 error += "the valid range is %u-%u. The default is %u." % (
1236 1000, 1000000000, 1000)
1237 raise ProvisioningError(error)
1239 # ATTENTION: Do NOT change these default values without discussion with the
1240 # team and/or release manager. They have a big impact on the whole program!
1241 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1243 if dom_for_fun_level is None:
1244 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1246 if dom_for_fun_level > domainControllerFunctionality:
1247 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!")
1249 domainFunctionality = dom_for_fun_level
1250 forestFunctionality = dom_for_fun_level
1252 # Set the NTDS settings DN manually - in order to have it already around
1253 # before the provisioned tree exists and we connect
1254 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1256 samdb.transaction_start()
1257 try:
1258 # Set the domain functionality levels onto the database.
1259 # Various module (the password_hash module in particular) need
1260 # to know what level of AD we are emulating.
1262 # These will be fixed into the database via the database
1263 # modifictions below, but we need them set from the start.
1264 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1265 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1266 samdb.set_opaque_integer("domainControllerFunctionality",
1267 domainControllerFunctionality)
1269 samdb.set_domain_sid(str(domainsid))
1270 samdb.set_invocation_id(invocationid)
1272 logger.info("Adding DomainDN: %s" % names.domaindn)
1274 # impersonate domain admin
1275 admin_session_info = admin_session(lp, str(domainsid))
1276 samdb.set_session_info(admin_session_info)
1277 if domainguid is not None:
1278 domainguid_line = "objectGUID: %s\n-" % domainguid
1279 else:
1280 domainguid_line = ""
1282 descr = b64encode(get_domain_descriptor(domainsid))
1283 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1284 "DOMAINDN": names.domaindn,
1285 "DOMAINSID": str(domainsid),
1286 "DESCRIPTOR": descr,
1287 "DOMAINGUID": domainguid_line
1290 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1291 "DOMAINDN": names.domaindn,
1292 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1293 "NEXTRID": str(next_rid),
1294 "DEFAULTSITE": names.sitename,
1295 "CONFIGDN": names.configdn,
1296 "POLICYGUID": policyguid,
1297 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1298 "SAMBA_VERSION_STRING": version
1301 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1302 if fill == FILL_FULL:
1303 logger.info("Adding configuration container")
1304 descr = b64encode(get_config_descriptor(domainsid))
1305 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1306 "CONFIGDN": names.configdn,
1307 "DESCRIPTOR": descr,
1310 # The LDIF here was created when the Schema object was constructed
1311 logger.info("Setting up sam.ldb schema")
1312 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1313 samdb.modify_ldif(schema.schema_dn_modify)
1314 samdb.write_prefixes_from_schema()
1315 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1316 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1317 {"SCHEMADN": names.schemadn})
1319 # Now register this container in the root of the forest
1320 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1321 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1322 "subRefs")
1324 except:
1325 samdb.transaction_cancel()
1326 raise
1327 else:
1328 samdb.transaction_commit()
1330 samdb.transaction_start()
1331 try:
1332 samdb.invocation_id = invocationid
1334 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1335 if fill == FILL_FULL:
1336 logger.info("Setting up sam.ldb configuration data")
1338 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1339 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1340 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(domainsid))
1341 protected1_descr = b64encode(get_config_delete_protected1_descriptor(domainsid))
1342 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(domainsid))
1343 protected2_descr = b64encode(get_config_delete_protected2_descriptor(domainsid))
1345 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1346 "CONFIGDN": names.configdn,
1347 "NETBIOSNAME": names.netbiosname,
1348 "DEFAULTSITE": names.sitename,
1349 "DNSDOMAIN": names.dnsdomain,
1350 "DOMAIN": names.domain,
1351 "SCHEMADN": names.schemadn,
1352 "DOMAINDN": names.domaindn,
1353 "SERVERDN": names.serverdn,
1354 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1355 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1356 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1357 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1358 "SERVICES_DESCRIPTOR": protected1_descr,
1359 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1360 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1361 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1362 "PARTITIONS_DESCRIPTOR": partitions_descr,
1363 "SITES_DESCRIPTOR": sites_descr,
1366 logger.info("Setting up display specifiers")
1367 display_specifiers_ldif = read_ms_ldif(
1368 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1369 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1370 {"CONFIGDN": names.configdn})
1371 check_all_substituted(display_specifiers_ldif)
1372 samdb.add_ldif(display_specifiers_ldif)
1374 logger.info("Modifying display specifiers")
1375 setup_modify_ldif(samdb,
1376 setup_path("provision_configuration_modify.ldif"), {
1377 "CONFIGDN": names.configdn,
1378 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1381 logger.info("Adding users container")
1382 users_desc = b64encode(get_domain_users_descriptor(domainsid))
1383 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1384 "DOMAINDN": names.domaindn,
1385 "USERS_DESCRIPTOR": users_desc
1387 logger.info("Modifying users container")
1388 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1389 "DOMAINDN": names.domaindn})
1390 logger.info("Adding computers container")
1391 computers_desc = b64encode(get_domain_computers_descriptor(domainsid))
1392 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1393 "DOMAINDN": names.domaindn,
1394 "COMPUTERS_DESCRIPTOR": computers_desc
1396 logger.info("Modifying computers container")
1397 setup_modify_ldif(samdb,
1398 setup_path("provision_computers_modify.ldif"), {
1399 "DOMAINDN": names.domaindn})
1400 logger.info("Setting up sam.ldb data")
1401 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
1402 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(domainsid))
1403 system_desc = b64encode(get_domain_delete_protected1_descriptor(domainsid))
1404 builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
1405 controllers_desc = b64encode(get_domain_controllers_descriptor(domainsid))
1406 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1407 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1408 "DOMAINDN": names.domaindn,
1409 "NETBIOSNAME": names.netbiosname,
1410 "DEFAULTSITE": names.sitename,
1411 "CONFIGDN": names.configdn,
1412 "SERVERDN": names.serverdn,
1413 "RIDAVAILABLESTART": str(next_rid + 600),
1414 "POLICYGUID_DC": policyguid_dc,
1415 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1416 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1417 "SYSTEM_DESCRIPTOR": system_desc,
1418 "BUILTIN_DESCRIPTOR": builtin_desc,
1419 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1422 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1423 if fill == FILL_FULL:
1424 setup_modify_ldif(samdb,
1425 setup_path("provision_configuration_references.ldif"), {
1426 "CONFIGDN": names.configdn,
1427 "SCHEMADN": names.schemadn})
1429 logger.info("Setting up well known security principals")
1430 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(domainsid))
1431 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1432 "CONFIGDN": names.configdn,
1433 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1436 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1437 setup_modify_ldif(samdb,
1438 setup_path("provision_basedn_references.ldif"),
1439 {"DOMAINDN": names.domaindn})
1441 logger.info("Setting up sam.ldb users and groups")
1442 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1443 "DOMAINDN": names.domaindn,
1444 "DOMAINSID": str(domainsid),
1445 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1446 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1449 logger.info("Setting up self join")
1450 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1451 invocationid=invocationid,
1452 dns_backend=dns_backend,
1453 dnspass=dnspass,
1454 machinepass=machinepass,
1455 domainsid=domainsid,
1456 next_rid=next_rid,
1457 dc_rid=dc_rid,
1458 policyguid=policyguid,
1459 policyguid_dc=policyguid_dc,
1460 domainControllerFunctionality=domainControllerFunctionality,
1461 ntdsguid=ntdsguid)
1463 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1464 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1465 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1466 assert isinstance(names.ntdsguid, str)
1467 except:
1468 samdb.transaction_cancel()
1469 raise
1470 else:
1471 samdb.transaction_commit()
1472 return samdb
1475 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1476 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)"
1477 SYSVOL_SERVICE="sysvol"
1479 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1480 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1481 for root, dirs, files in os.walk(path, topdown=False):
1482 for name in files:
1483 setntacl(lp, os.path.join(root, name), acl, domsid,
1484 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1485 for name in dirs:
1486 setntacl(lp, os.path.join(root, name), acl, domsid,
1487 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1490 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1491 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1492 folders beneath.
1494 :param sysvol: Physical path for the sysvol folder
1495 :param dnsdomain: The DNS name of the domain
1496 :param domainsid: The SID of the domain
1497 :param domaindn: The DN of the domain (ie. DC=...)
1498 :param samdb: An LDB object on the SAM db
1499 :param lp: an LP object
1502 # Set ACL for GPO root folder
1503 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1504 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1505 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1507 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1508 attrs=["cn", "nTSecurityDescriptor"],
1509 expression="", scope=ldb.SCOPE_ONELEVEL)
1511 for policy in res:
1512 acl = ndr_unpack(security.descriptor,
1513 str(policy["nTSecurityDescriptor"])).as_sddl()
1514 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1515 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1516 str(domainsid), use_ntvfs,
1517 passdb=passdb)
1520 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1521 domaindn, lp, use_ntvfs):
1522 """Set the ACL for the sysvol share and the subfolders
1524 :param samdb: An LDB object on the SAM db
1525 :param netlogon: Physical path for the netlogon folder
1526 :param sysvol: Physical path for the sysvol folder
1527 :param uid: The UID of the "Administrator" user
1528 :param gid: The GID of the "Domain adminstrators" group
1529 :param domainsid: The SID of the domain
1530 :param dnsdomain: The DNS name of the domain
1531 :param domaindn: The DN of the domain (ie. DC=...)
1533 s4_passdb = None
1535 if not use_ntvfs:
1536 s3conf = s3param.get_context()
1537 s3conf.load(lp.configfile)
1539 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
1540 try:
1541 try:
1542 smbd.set_simple_acl(file.name, 0755, gid)
1543 except OSError:
1544 if not smbd.have_posix_acls():
1545 # This clue is only strictly correct for RPM and
1546 # Debian-like Linux systems, but hopefully other users
1547 # will get enough clue from it.
1548 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. "
1549 "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1551 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
1552 "Try the mounting the filesystem with the 'acl' option.")
1553 try:
1554 smbd.chown(file.name, uid, gid)
1555 except OSError:
1556 raise ProvisioningError("Unable to chown a file on your filesystem. "
1557 "You may not be running provision as root.")
1558 finally:
1559 file.close()
1561 # This will ensure that the smbd code we are running when setting ACLs
1562 # is initialised with the smb.conf
1563 s3conf = s3param.get_context()
1564 s3conf.load(lp.configfile)
1565 # ensure we are using the right samba_dsdb passdb backend, no matter what
1566 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1567 passdb.reload_static_pdb()
1569 # ensure that we init the samba_dsdb backend, so the domain sid is
1570 # marked in secrets.tdb
1571 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1573 # now ensure everything matches correctly, to avoid wierd issues
1574 if passdb.get_global_sam_sid() != domainsid:
1575 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))
1577 domain_info = s4_passdb.domain_info()
1578 if domain_info["dom_sid"] != domainsid:
1579 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))
1581 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1582 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()))
1585 try:
1586 if use_ntvfs:
1587 os.chown(sysvol, -1, gid)
1588 except OSError:
1589 canchown = False
1590 else:
1591 canchown = True
1593 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1594 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1595 skip_invalid_chown=True, passdb=s4_passdb,
1596 service=SYSVOL_SERVICE)
1597 for root, dirs, files in os.walk(sysvol, topdown=False):
1598 for name in files:
1599 if use_ntvfs and canchown:
1600 os.chown(os.path.join(root, name), -1, gid)
1601 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1602 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1603 passdb=s4_passdb, service=SYSVOL_SERVICE)
1604 for name in dirs:
1605 if use_ntvfs and canchown:
1606 os.chown(os.path.join(root, name), -1, gid)
1607 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1608 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1609 passdb=s4_passdb, service=SYSVOL_SERVICE)
1611 # Set acls on Policy folder and policies folders
1612 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1614 def acl_type(direct_db_access):
1615 if direct_db_access:
1616 return "DB"
1617 else:
1618 return "VFS"
1620 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1621 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1622 fsacl_sddl = fsacl.as_sddl(domainsid)
1623 if fsacl_sddl != acl:
1624 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))
1626 for root, dirs, files in os.walk(path, topdown=False):
1627 for name in files:
1628 fsacl = getntacl(lp, os.path.join(root, name),
1629 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1630 if fsacl is None:
1631 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1632 fsacl_sddl = fsacl.as_sddl(domainsid)
1633 if fsacl_sddl != acl:
1634 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))
1636 for name in dirs:
1637 fsacl = getntacl(lp, os.path.join(root, name),
1638 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1639 if fsacl is None:
1640 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1641 fsacl_sddl = fsacl.as_sddl(domainsid)
1642 if fsacl_sddl != acl:
1643 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))
1646 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1647 direct_db_access):
1648 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1649 folders beneath.
1651 :param sysvol: Physical path for the sysvol folder
1652 :param dnsdomain: The DNS name of the domain
1653 :param domainsid: The SID of the domain
1654 :param domaindn: The DN of the domain (ie. DC=...)
1655 :param samdb: An LDB object on the SAM db
1656 :param lp: an LP object
1659 # Set ACL for GPO root folder
1660 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1661 fsacl = getntacl(lp, root_policy_path,
1662 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1663 if fsacl is None:
1664 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1665 fsacl_sddl = fsacl.as_sddl(domainsid)
1666 if fsacl_sddl != POLICIES_ACL:
1667 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))
1668 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1669 attrs=["cn", "nTSecurityDescriptor"],
1670 expression="", scope=ldb.SCOPE_ONELEVEL)
1672 for policy in res:
1673 acl = ndr_unpack(security.descriptor,
1674 str(policy["nTSecurityDescriptor"])).as_sddl()
1675 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1676 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1677 domainsid, direct_db_access)
1680 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1681 lp):
1682 """Set the ACL for the sysvol share and the subfolders
1684 :param samdb: An LDB object on the SAM db
1685 :param netlogon: Physical path for the netlogon folder
1686 :param sysvol: Physical path for the sysvol folder
1687 :param uid: The UID of the "Administrator" user
1688 :param gid: The GID of the "Domain adminstrators" group
1689 :param domainsid: The SID of the domain
1690 :param dnsdomain: The DNS name of the domain
1691 :param domaindn: The DN of the domain (ie. DC=...)
1694 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1695 s3conf = s3param.get_context()
1696 s3conf.load(lp.configfile)
1697 # ensure we are using the right samba_dsdb passdb backend, no matter what
1698 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1699 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1700 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1702 # now ensure everything matches correctly, to avoid wierd issues
1703 if passdb.get_global_sam_sid() != domainsid:
1704 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))
1706 domain_info = s4_passdb.domain_info()
1707 if domain_info["dom_sid"] != domainsid:
1708 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))
1710 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1711 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()))
1713 # Ensure we can read this directly, and via the smbd VFS
1714 for direct_db_access in [True, False]:
1715 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1716 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1717 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1718 if fsacl is None:
1719 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1720 fsacl_sddl = fsacl.as_sddl(domainsid)
1721 if fsacl_sddl != SYSVOL_ACL:
1722 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))
1724 # Check acls on Policy folder and policies folders
1725 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1726 direct_db_access)
1729 def interface_ips_v4(lp):
1730 """return only IPv4 IPs"""
1731 ips = samba.interface_ips(lp, False)
1732 ret = []
1733 for i in ips:
1734 if i.find(':') == -1:
1735 ret.append(i)
1736 return ret
1739 def interface_ips_v6(lp):
1740 """return only IPv6 IPs"""
1741 ips = samba.interface_ips(lp, False)
1742 ret = []
1743 for i in ips:
1744 if i.find(':') != -1:
1745 ret.append(i)
1746 return ret
1749 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1750 domainsid, schema=None,
1751 targetdir=None, samdb_fill=FILL_FULL,
1752 hostip=None, hostip6=None,
1753 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1754 domainguid=None, policyguid=None, policyguid_dc=None,
1755 invocationid=None, machinepass=None, ntdsguid=None,
1756 dns_backend=None, dnspass=None,
1757 serverrole=None, dom_for_fun_level=None,
1758 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1759 # create/adapt the group policy GUIDs
1760 # Default GUID for default policy are described at
1761 # "How Core Group Policy Works"
1762 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1763 if policyguid is None:
1764 policyguid = DEFAULT_POLICY_GUID
1765 policyguid = policyguid.upper()
1766 if policyguid_dc is None:
1767 policyguid_dc = DEFAULT_DC_POLICY_GUID
1768 policyguid_dc = policyguid_dc.upper()
1770 if invocationid is None:
1771 invocationid = str(uuid.uuid4())
1773 if krbtgtpass is None:
1774 krbtgtpass = samba.generate_random_password(128, 255)
1775 if machinepass is None:
1776 machinepass = samba.generate_random_password(128, 255)
1777 if dnspass is None:
1778 dnspass = samba.generate_random_password(128, 255)
1780 samdb = fill_samdb(samdb, lp, names, logger=logger,
1781 domainsid=domainsid, schema=schema, domainguid=domainguid,
1782 policyguid=policyguid, policyguid_dc=policyguid_dc,
1783 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1784 invocationid=invocationid, machinepass=machinepass,
1785 dns_backend=dns_backend, dnspass=dnspass,
1786 ntdsguid=ntdsguid, serverrole=serverrole,
1787 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1788 next_rid=next_rid, dc_rid=dc_rid)
1790 if serverrole == "active directory domain controller":
1792 # Set up group policies (domain policy and domain controller
1793 # policy)
1794 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1795 policyguid_dc)
1796 if not skip_sysvolacl:
1797 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1798 paths.root_gid, domainsid, names.dnsdomain,
1799 names.domaindn, lp, use_ntvfs)
1800 else:
1801 logger.info("Setting acl on sysvol skipped")
1803 secretsdb_self_join(secrets_ldb, domain=names.domain,
1804 realm=names.realm, dnsdomain=names.dnsdomain,
1805 netbiosname=names.netbiosname, domainsid=domainsid,
1806 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1808 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1809 # In future, this might be determined from some configuration
1810 kerberos_enctypes = str(ENC_ALL_TYPES)
1812 try:
1813 msg = ldb.Message(ldb.Dn(samdb,
1814 samdb.searchone("distinguishedName",
1815 expression="samAccountName=%s$" % names.netbiosname,
1816 scope=ldb.SCOPE_SUBTREE)))
1817 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1818 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1819 name="msDS-SupportedEncryptionTypes")
1820 samdb.modify(msg)
1821 except ldb.LdbError, (enum, estr):
1822 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1823 # It might be that this attribute does not exist in this schema
1824 raise
1826 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1827 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1828 dnspass=dnspass, os_level=dom_for_fun_level,
1829 targetdir=targetdir, fill_level=samdb_fill)
1831 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1832 attribute="objectGUID")
1833 assert isinstance(domainguid, str)
1835 lastProvisionUSNs = get_last_provision_usn(samdb)
1836 maxUSN = get_max_usn(samdb, str(names.rootdn))
1837 if lastProvisionUSNs is not None:
1838 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1839 else:
1840 set_provision_usn(samdb, 0, maxUSN, invocationid)
1842 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1843 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1844 { 'NTDSGUID' : names.ntdsguid })
1846 # fix any dangling GUIDs from the provision
1847 logger.info("Fixing provision GUIDs")
1848 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1849 quiet=True)
1850 samdb.transaction_start()
1851 try:
1852 # a small number of GUIDs are missing because of ordering issues in the
1853 # provision code
1854 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1855 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1856 scope=ldb.SCOPE_BASE,
1857 attrs=['defaultObjectCategory'])
1858 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1859 scope=ldb.SCOPE_ONELEVEL,
1860 attrs=['ipsecOwnersReference',
1861 'ipsecFilterReference',
1862 'ipsecISAKMPReference',
1863 'ipsecNegotiationPolicyReference',
1864 'ipsecNFAReference'])
1865 except:
1866 samdb.transaction_cancel()
1867 raise
1868 else:
1869 samdb.transaction_commit()
1872 _ROLES_MAP = {
1873 "ROLE_STANDALONE": "standalone server",
1874 "ROLE_DOMAIN_MEMBER": "member server",
1875 "ROLE_DOMAIN_BDC": "active directory domain controller",
1876 "ROLE_DOMAIN_PDC": "active directory domain controller",
1877 "dc": "active directory domain controller",
1878 "member": "member server",
1879 "domain controller": "active directory domain controller",
1880 "active directory domain controller": "active directory domain controller",
1881 "member server": "member server",
1882 "standalone": "standalone server",
1883 "standalone server": "standalone server",
1887 def sanitize_server_role(role):
1888 """Sanitize a server role name.
1890 :param role: Server role
1891 :raise ValueError: If the role can not be interpreted
1892 :return: Sanitized server role (one of "member server",
1893 "active directory domain controller", "standalone server")
1895 try:
1896 return _ROLES_MAP[role]
1897 except KeyError:
1898 raise ValueError(role)
1901 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1902 maxuid, maxgid):
1903 """Create AD entries for the fake ypserver.
1905 This is needed for being able to manipulate posix attrs via ADUC.
1907 samdb.transaction_start()
1908 try:
1909 logger.info("Setting up fake yp server settings")
1910 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1911 "DOMAINDN": domaindn,
1912 "NETBIOSNAME": netbiosname,
1913 "NISDOMAIN": nisdomain,
1915 except:
1916 samdb.transaction_cancel()
1917 raise
1918 else:
1919 samdb.transaction_commit()
1922 def provision(logger, session_info, smbconf=None,
1923 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1924 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1925 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1926 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1927 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1928 dns_backend=None, dns_forwarder=None, dnspass=None,
1929 invocationid=None, machinepass=None, ntdsguid=None,
1930 root=None, nobody=None, users=None, backup=None, aci=None,
1931 serverrole=None, dom_for_fun_level=None, backend_type=None,
1932 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1933 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1934 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
1935 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, ldap_backend_extra_port=None):
1936 """Provision samba4
1938 :note: caution, this wipes all existing data!
1941 try:
1942 serverrole = sanitize_server_role(serverrole)
1943 except ValueError:
1944 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1946 if ldapadminpass is None:
1947 # Make a new, random password between Samba and it's LDAP server
1948 ldapadminpass = samba.generate_random_password(128, 255)
1950 if backend_type is None:
1951 backend_type = "ldb"
1953 if domainsid is None:
1954 domainsid = security.random_sid()
1955 else:
1956 domainsid = security.dom_sid(domainsid)
1958 root_uid = findnss_uid([root or "root"])
1959 nobody_uid = findnss_uid([nobody or "nobody"])
1960 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1961 root_gid = pwd.getpwuid(root_uid).pw_gid
1963 try:
1964 bind_gid = findnss_gid(["bind", "named"])
1965 except KeyError:
1966 bind_gid = None
1968 if targetdir is not None:
1969 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1970 elif smbconf is None:
1971 smbconf = samba.param.default_path()
1972 if not os.path.exists(os.path.dirname(smbconf)):
1973 os.makedirs(os.path.dirname(smbconf))
1975 server_services = []
1976 global_param = {}
1977 if use_rfc2307:
1978 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1980 if dns_backend != "SAMBA_INTERNAL":
1981 server_services.append("-dns")
1982 else:
1983 if dns_forwarder is not None:
1984 global_param["dns forwarder"] = [dns_forwarder]
1986 if use_ntvfs:
1987 server_services.append("+smb")
1988 server_services.append("-s3fs")
1989 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1991 if len(server_services) > 0:
1992 global_param["server services"] = server_services
1994 # only install a new smb.conf if there isn't one there already
1995 if os.path.exists(smbconf):
1996 # if Samba Team members can't figure out the weird errors
1997 # loading an empty smb.conf gives, then we need to be smarter.
1998 # Pretend it just didn't exist --abartlet
1999 f = open(smbconf, 'r')
2000 try:
2001 data = f.read().lstrip()
2002 finally:
2003 f.close()
2004 if data is None or data == "":
2005 make_smbconf(smbconf, hostname, domain, realm,
2006 targetdir, serverrole=serverrole,
2007 eadb=useeadb, use_ntvfs=use_ntvfs,
2008 lp=lp, global_param=global_param)
2009 else:
2010 make_smbconf(smbconf, hostname, domain, realm, targetdir,
2011 serverrole=serverrole,
2012 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
2014 if lp is None:
2015 lp = samba.param.LoadParm()
2016 lp.load(smbconf)
2017 names = guess_names(lp=lp, hostname=hostname, domain=domain,
2018 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
2019 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
2020 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
2021 paths = provision_paths_from_lp(lp, names.dnsdomain)
2023 paths.bind_gid = bind_gid
2024 paths.root_uid = root_uid;
2025 paths.root_gid = root_gid
2027 if hostip is None:
2028 logger.info("Looking up IPv4 addresses")
2029 hostips = interface_ips_v4(lp)
2030 if len(hostips) > 0:
2031 hostip = hostips[0]
2032 if len(hostips) > 1:
2033 logger.warning("More than one IPv4 address found. Using %s",
2034 hostip)
2035 if hostip == "127.0.0.1":
2036 hostip = None
2037 if hostip is None:
2038 logger.warning("No IPv4 address will be assigned")
2040 if hostip6 is None:
2041 logger.info("Looking up IPv6 addresses")
2042 hostips = interface_ips_v6(lp)
2043 if hostips:
2044 hostip6 = hostips[0]
2045 if len(hostips) > 1:
2046 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2047 if hostip6 is None:
2048 logger.warning("No IPv6 address will be assigned")
2050 names.hostip = hostip
2051 names.hostip6 = hostip6
2053 if serverrole is None:
2054 serverrole = lp.get("server role")
2056 if not os.path.exists(paths.private_dir):
2057 os.mkdir(paths.private_dir)
2058 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
2059 os.makedirs(os.path.join(paths.private_dir, "tls"), 0700)
2060 if not os.path.exists(paths.state_dir):
2061 os.mkdir(paths.state_dir)
2063 if paths.sysvol and not os.path.exists(paths.sysvol):
2064 os.makedirs(paths.sysvol, 0775)
2066 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2068 schema = Schema(domainsid, invocationid=invocationid,
2069 schemadn=names.schemadn)
2071 if backend_type == "ldb":
2072 provision_backend = LDBBackend(backend_type, paths=paths,
2073 lp=lp,
2074 names=names, logger=logger)
2075 elif backend_type == "existing":
2076 # If support for this is ever added back, then the URI will need to be
2077 # specified again
2078 provision_backend = ExistingBackend(backend_type, paths=paths,
2079 lp=lp,
2080 names=names, logger=logger,
2081 ldap_backend_forced_uri=ldap_backend_forced_uri)
2082 elif backend_type == "fedora-ds":
2083 provision_backend = FDSBackend(backend_type, paths=paths,
2084 lp=lp,
2085 names=names, logger=logger, domainsid=domainsid,
2086 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2087 slapd_path=slapd_path,
2088 root=root)
2089 elif backend_type == "openldap":
2090 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2091 lp=lp,
2092 names=names, logger=logger, domainsid=domainsid,
2093 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2094 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2095 ldap_backend_extra_port=ldap_backend_extra_port,
2096 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2097 ldap_backend_forced_uri=ldap_backend_forced_uri)
2098 else:
2099 raise ValueError("Unknown LDAP backend type selected")
2101 provision_backend.init()
2102 provision_backend.start()
2104 # only install a new shares config db if there is none
2105 if not os.path.exists(paths.shareconf):
2106 logger.info("Setting up share.ldb")
2107 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2108 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2110 logger.info("Setting up secrets.ldb")
2111 secrets_ldb = setup_secretsdb(paths,
2112 session_info=session_info,
2113 backend_credentials=provision_backend.credentials, lp=lp)
2115 try:
2116 logger.info("Setting up the registry")
2117 setup_registry(paths.hklm, session_info, lp=lp)
2119 logger.info("Setting up the privileges database")
2120 setup_privileges(paths.privilege, session_info, lp=lp)
2122 logger.info("Setting up idmap db")
2123 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2125 setup_name_mappings(idmap, sid=str(domainsid),
2126 root_uid=root_uid, nobody_uid=nobody_uid,
2127 users_gid=users_gid, root_gid=root_gid)
2129 logger.info("Setting up SAM db")
2130 samdb = setup_samdb(paths.samdb, session_info,
2131 provision_backend, lp, names, logger=logger,
2132 serverrole=serverrole,
2133 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2135 if serverrole == "active directory domain controller":
2136 if paths.netlogon is None:
2137 raise MissingShareError("netlogon", paths.smbconf)
2139 if paths.sysvol is None:
2140 raise MissingShareError("sysvol", paths.smbconf)
2142 if not os.path.isdir(paths.netlogon):
2143 os.makedirs(paths.netlogon, 0755)
2145 if adminpass is None:
2146 adminpass = samba.generate_random_password(12, 32)
2147 adminpass_generated = True
2148 else:
2149 adminpass = unicode(adminpass, 'utf-8')
2150 adminpass_generated = False
2152 if samdb_fill == FILL_FULL:
2153 provision_fill(samdb, secrets_ldb, logger, names, paths,
2154 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2155 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2156 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2157 krbtgtpass=krbtgtpass, domainguid=domainguid,
2158 policyguid=policyguid, policyguid_dc=policyguid_dc,
2159 invocationid=invocationid, machinepass=machinepass,
2160 ntdsguid=ntdsguid, dns_backend=dns_backend,
2161 dnspass=dnspass, serverrole=serverrole,
2162 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2163 lp=lp, use_ntvfs=use_ntvfs,
2164 skip_sysvolacl=skip_sysvolacl)
2166 create_krb5_conf(paths.krb5conf,
2167 dnsdomain=names.dnsdomain, hostname=names.hostname,
2168 realm=names.realm)
2169 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2170 "generated at %s", paths.krb5conf)
2172 if serverrole == "active directory domain controller":
2173 create_dns_update_list(lp, logger, paths)
2175 backend_result = provision_backend.post_setup()
2176 provision_backend.shutdown()
2178 except:
2179 secrets_ldb.transaction_cancel()
2180 raise
2182 # Now commit the secrets.ldb to disk
2183 secrets_ldb.transaction_commit()
2185 # the commit creates the dns.keytab, now chown it
2186 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2187 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2188 try:
2189 os.chmod(dns_keytab_path, 0640)
2190 os.chown(dns_keytab_path, -1, paths.bind_gid)
2191 except OSError:
2192 if not os.environ.has_key('SAMBA_SELFTEST'):
2193 logger.info("Failed to chown %s to bind gid %u",
2194 dns_keytab_path, paths.bind_gid)
2196 result = ProvisionResult()
2197 result.server_role = serverrole
2198 result.domaindn = domaindn
2199 result.paths = paths
2200 result.names = names
2201 result.lp = lp
2202 result.samdb = samdb
2203 result.idmap = idmap
2204 result.domainsid = str(domainsid)
2206 if samdb_fill == FILL_FULL:
2207 result.adminpass_generated = adminpass_generated
2208 result.adminpass = adminpass
2209 else:
2210 result.adminpass_generated = False
2211 result.adminpass = None
2213 result.backend_result = backend_result
2215 if use_rfc2307:
2216 provision_fake_ypserver(logger=logger, samdb=samdb,
2217 domaindn=names.domaindn, netbiosname=names.netbiosname,
2218 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2220 return result
2223 def provision_become_dc(smbconf=None, targetdir=None,
2224 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2225 serverdn=None, domain=None, hostname=None, domainsid=None,
2226 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2227 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2228 dns_backend=None, root=None, nobody=None, users=None,
2229 backup=None, serverrole=None, ldap_backend=None,
2230 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2232 logger = logging.getLogger("provision")
2233 samba.set_debug_level(debuglevel)
2235 res = provision(logger, system_session(),
2236 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2237 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2238 configdn=configdn, serverdn=serverdn, domain=domain,
2239 hostname=hostname, hostip=None, domainsid=domainsid,
2240 machinepass=machinepass,
2241 serverrole="active directory domain controller",
2242 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2243 use_ntvfs=use_ntvfs)
2244 res.lp.set("debuglevel", str(debuglevel))
2245 return res
2248 def create_krb5_conf(path, dnsdomain, hostname, realm):
2249 """Write out a file containing a valid krb5.conf file
2251 :param path: Path of the new krb5.conf file.
2252 :param dnsdomain: DNS Domain name
2253 :param hostname: Local hostname
2254 :param realm: Realm name
2256 setup_file(setup_path("krb5.conf"), path, {
2257 "DNSDOMAIN": dnsdomain,
2258 "HOSTNAME": hostname,
2259 "REALM": realm,
2263 class ProvisioningError(Exception):
2264 """A generic provision error."""
2266 def __init__(self, value):
2267 self.value = value
2269 def __str__(self):
2270 return "ProvisioningError: " + self.value
2273 class InvalidNetbiosName(Exception):
2274 """A specified name was not a valid NetBIOS name."""
2276 def __init__(self, name):
2277 super(InvalidNetbiosName, self).__init__(
2278 "The name '%r' is not a valid NetBIOS name" % name)
2281 class MissingShareError(ProvisioningError):
2283 def __init__(self, name, smbconf):
2284 super(MissingShareError, self).__init__(
2285 "Existing smb.conf does not have a [%s] share, but you are "
2286 "configuring a DC. Please remove %s or add the share manually." %
2287 (name, smbconf))