provision: introduce names.name_map = {}
[Samba.git] / source4 / scripting / python / samba / provision / __init__.py
blobd3b0744a4c839dd8ef6016ec874b16b1034fb842
1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba4 server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__ = "restructuredText"
29 from base64 import b64encode
30 import os
31 import re
32 import pwd
33 import grp
34 import logging
35 import time
36 import uuid
37 import socket
38 import urllib
39 import string
40 import tempfile
42 import ldb
44 from samba.auth import system_session, admin_session
45 import samba
46 from samba.samba3 import smbd, passdb
47 from samba.samba3 import param as s3param
48 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
49 from samba import (
50 Ldb,
51 MAX_NETBIOS_NAME_LEN,
52 check_all_substituted,
53 is_valid_netbios_char,
54 setup_file,
55 substitute_var,
56 valid_netbios_name,
57 version,
59 from samba.dcerpc import security, misc
60 from samba.dcerpc.misc import (
61 SEC_CHAN_BDC,
62 SEC_CHAN_WKSTA,
64 from samba.dsdb import (
65 DS_DOMAIN_FUNCTION_2003,
66 DS_DOMAIN_FUNCTION_2008_R2,
67 ENC_ALL_TYPES,
69 from samba.idmap import IDmapDB
70 from samba.ms_display_specifiers import read_ms_ldif
71 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
72 from samba.ndr import ndr_pack, ndr_unpack
73 from samba.provision.backend import (
74 ExistingBackend,
75 FDSBackend,
76 LDBBackend,
77 OpenLDAPBackend,
79 from samba.provision.descriptor import (
80 get_empty_descriptor,
81 get_config_descriptor,
82 get_config_partitions_descriptor,
83 get_config_sites_descriptor,
84 get_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,
105 from samba.provision.sambadns import (
106 setup_ad_dns,
107 create_dns_update_list
110 import samba.param
111 import samba.registry
112 from samba.schema import Schema
113 from samba.samdb import SamDB
114 from samba.dbchecker import dbcheck
117 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
118 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
119 DEFAULTSITE = "Default-First-Site-Name"
120 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
123 class ProvisionPaths(object):
125 def __init__(self):
126 self.shareconf = None
127 self.hklm = None
128 self.hkcu = None
129 self.hkcr = None
130 self.hku = None
131 self.hkpd = None
132 self.hkpt = None
133 self.samdb = None
134 self.idmapdb = None
135 self.secrets = None
136 self.keytab = None
137 self.dns_keytab = None
138 self.dns = None
139 self.winsdb = None
140 self.private_dir = None
141 self.state_dir = None
144 class ProvisionNames(object):
146 def __init__(self):
147 self.ncs = None
148 self.rootdn = None
149 self.domaindn = None
150 self.configdn = None
151 self.schemadn = None
152 self.dnsforestdn = None
153 self.dnsdomaindn = None
154 self.ldapmanagerdn = None
155 self.dnsdomain = None
156 self.realm = None
157 self.netbiosname = None
158 self.domain = None
159 self.hostname = None
160 self.sitename = None
161 self.smbconf = None
162 self.name_map = {}
165 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
166 lp):
167 """Get key provision parameters (realm, domain, ...) from a given provision
169 :param samdb: An LDB object connected to the sam.ldb file
170 :param secretsdb: An LDB object connected to the secrets.ldb file
171 :param idmapdb: An LDB object connected to the idmap.ldb file
172 :param paths: A list of path to provision object
173 :param smbconf: Path to the smb.conf file
174 :param lp: A LoadParm object
175 :return: A list of key provision parameters
177 names = ProvisionNames()
178 names.adminpass = None
180 # NT domain, kerberos realm, root dn, domain dn, domain dns name
181 names.domain = string.upper(lp.get("workgroup"))
182 names.realm = lp.get("realm")
183 names.dnsdomain = names.realm.lower()
184 basedn = samba.dn_from_dns_name(names.dnsdomain)
185 names.realm = string.upper(names.realm)
186 # netbiosname
187 # Get the netbiosname first (could be obtained from smb.conf in theory)
188 res = secretsdb.search(expression="(flatname=%s)" %
189 names.domain,base="CN=Primary Domains",
190 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
191 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
193 names.smbconf = smbconf
195 # That's a bit simplistic but it's ok as long as we have only 3
196 # partitions
197 current = samdb.search(expression="(objectClass=*)",
198 base="", scope=ldb.SCOPE_BASE,
199 attrs=["defaultNamingContext", "schemaNamingContext",
200 "configurationNamingContext","rootDomainNamingContext",
201 "namingContexts"])
203 names.configdn = current[0]["configurationNamingContext"]
204 configdn = str(names.configdn)
205 names.schemadn = current[0]["schemaNamingContext"]
206 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
207 current[0]["defaultNamingContext"][0]))):
208 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
209 "is not the same ..." % (paths.samdb,
210 str(current[0]["defaultNamingContext"][0]),
211 paths.smbconf, basedn)))
213 names.domaindn=current[0]["defaultNamingContext"]
214 names.rootdn=current[0]["rootDomainNamingContext"]
215 names.ncs=current[0]["namingContexts"]
216 names.dnsforestdn = None
217 names.dnsdomaindn = None
219 for i in range(0, len(names.ncs)):
220 nc = names.ncs[i]
222 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
223 if nc == dnsforestdn:
224 names.dnsforestdn = dnsforestdn
225 continue
227 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
228 if nc == dnsdomaindn:
229 names.dnsdomaindn = dnsdomaindn
230 continue
232 # default site name
233 res3 = samdb.search(expression="(objectClass=site)",
234 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
235 names.sitename = str(res3[0]["cn"])
237 # dns hostname and server dn
238 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
239 base="OU=Domain Controllers,%s" % basedn,
240 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
241 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
243 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
244 attrs=[], base=configdn)
245 names.serverdn = server_res[0].dn
247 # invocation id/objectguid
248 res5 = samdb.search(expression="(objectClass=*)",
249 base="CN=NTDS Settings,%s" % str(names.serverdn),
250 scope=ldb.SCOPE_BASE,
251 attrs=["invocationID", "objectGUID"])
252 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
253 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
255 # domain guid/sid
256 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
257 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
258 "objectSid","msDS-Behavior-Version" ])
259 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
260 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
261 if res6[0].get("msDS-Behavior-Version") is None or \
262 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
263 names.domainlevel = DS_DOMAIN_FUNCTION_2000
264 else:
265 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
267 # policy guid
268 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
269 base="CN=Policies,CN=System," + basedn,
270 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
271 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
272 # dc policy guid
273 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
274 " Policy)",
275 base="CN=Policies,CN=System," + basedn,
276 scope=ldb.SCOPE_ONELEVEL,
277 attrs=["cn","displayName"])
278 if len(res8) == 1:
279 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
280 else:
281 names.policyid_dc = None
283 res9 = idmapdb.search(expression="(cn=%s-%s)" %
284 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
285 attrs=["xidNumber", "type"])
286 if len(res9) != 1:
287 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
288 if res9[0]["type"][0] == "ID_TYPE_BOTH":
289 names.root_gid = res9[0]["xidNumber"][0]
290 else:
291 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
292 return names
295 def update_provision_usn(samdb, low, high, id, replace=False):
296 """Update the field provisionUSN in sam.ldb
298 This field is used to track range of USN modified by provision and
299 upgradeprovision.
300 This value is used afterward by next provision to figure out if
301 the field have been modified since last provision.
303 :param samdb: An LDB object connect to sam.ldb
304 :param low: The lowest USN modified by this upgrade
305 :param high: The highest USN modified by this upgrade
306 :param id: The invocation id of the samba's dc
307 :param replace: A boolean indicating if the range should replace any
308 existing one or appended (default)
311 tab = []
312 if not replace:
313 entry = samdb.search(base="@PROVISION",
314 scope=ldb.SCOPE_BASE,
315 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
316 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
317 if not re.search(';', e):
318 e = "%s;%s" % (e, id)
319 tab.append(str(e))
321 tab.append("%s-%s;%s" % (low, high, id))
322 delta = ldb.Message()
323 delta.dn = ldb.Dn(samdb, "@PROVISION")
324 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
325 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
326 entry = samdb.search(expression='provisionnerID=*',
327 base="@PROVISION", scope=ldb.SCOPE_BASE,
328 attrs=["provisionnerID"])
329 if len(entry) == 0 or len(entry[0]) == 0:
330 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
331 samdb.modify(delta)
334 def set_provision_usn(samdb, low, high, id):
335 """Set the field provisionUSN in sam.ldb
336 This field is used to track range of USN modified by provision and
337 upgradeprovision.
338 This value is used afterward by next provision to figure out if
339 the field have been modified since last provision.
341 :param samdb: An LDB object connect to sam.ldb
342 :param low: The lowest USN modified by this upgrade
343 :param high: The highest USN modified by this upgrade
344 :param id: The invocationId of the provision"""
346 tab = []
347 tab.append("%s-%s;%s" % (low, high, id))
349 delta = ldb.Message()
350 delta.dn = ldb.Dn(samdb, "@PROVISION")
351 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
352 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
353 samdb.add(delta)
356 def get_max_usn(samdb,basedn):
357 """ This function return the biggest USN present in the provision
359 :param samdb: A LDB object pointing to the sam.ldb
360 :param basedn: A string containing the base DN of the provision
361 (ie. DC=foo, DC=bar)
362 :return: The biggest USN in the provision"""
364 res = samdb.search(expression="objectClass=*",base=basedn,
365 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
366 controls=["search_options:1:2",
367 "server_sort:1:1:uSNChanged",
368 "paged_results:1:1"])
369 return res[0]["uSNChanged"]
372 def get_last_provision_usn(sam):
373 """Get USNs ranges modified by a provision or an upgradeprovision
375 :param sam: An LDB object pointing to the sam.ldb
376 :return: a dictionary which keys are invocation id and values are an array
377 of integer representing the different ranges
379 try:
380 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
381 base="@PROVISION", scope=ldb.SCOPE_BASE,
382 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
383 except ldb.LdbError, (ecode, emsg):
384 if ecode == ldb.ERR_NO_SUCH_OBJECT:
385 return None
386 raise
387 if len(entry) > 0:
388 myids = []
389 range = {}
390 p = re.compile(r'-')
391 if entry[0].get("provisionnerID"):
392 for e in entry[0]["provisionnerID"]:
393 myids.append(str(e))
394 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
395 tab1 = str(r).split(';')
396 if len(tab1) == 2:
397 id = tab1[1]
398 else:
399 id = "default"
400 if (len(myids) > 0 and id not in myids):
401 continue
402 tab2 = p.split(tab1[0])
403 if range.get(id) is None:
404 range[id] = []
405 range[id].append(tab2[0])
406 range[id].append(tab2[1])
407 return range
408 else:
409 return None
412 class ProvisionResult(object):
413 """Result of a provision.
415 :ivar server_role: The server role
416 :ivar paths: ProvisionPaths instance
417 :ivar domaindn: The domain dn, as string
420 def __init__(self):
421 self.server_role = None
422 self.paths = None
423 self.domaindn = None
424 self.lp = None
425 self.samdb = None
426 self.idmap = None
427 self.names = None
428 self.domainsid = None
429 self.adminpass_generated = None
430 self.adminpass = None
431 self.backend_result = None
433 def report_logger(self, logger):
434 """Report this provision result to a logger."""
435 logger.info(
436 "Once the above files are installed, your Samba4 server will "
437 "be ready to use")
438 if self.adminpass_generated:
439 logger.info("Admin password: %s", self.adminpass)
440 logger.info("Server Role: %s", self.server_role)
441 logger.info("Hostname: %s", self.names.hostname)
442 logger.info("NetBIOS Domain: %s", self.names.domain)
443 logger.info("DNS Domain: %s", self.names.dnsdomain)
444 logger.info("DOMAIN SID: %s", self.domainsid)
446 if self.backend_result:
447 self.backend_result.report_logger(logger)
450 def check_install(lp, session_info, credentials):
451 """Check whether the current install seems ok.
453 :param lp: Loadparm context
454 :param session_info: Session information
455 :param credentials: Credentials
457 if lp.get("realm") == "":
458 raise Exception("Realm empty")
459 samdb = Ldb(lp.samdb_url(), session_info=session_info,
460 credentials=credentials, lp=lp)
461 if len(samdb.search("(cn=Administrator)")) != 1:
462 raise ProvisioningError("No administrator account found")
465 def findnss(nssfn, names):
466 """Find a user or group from a list of possibilities.
468 :param nssfn: NSS Function to try (should raise KeyError if not found)
469 :param names: Names to check.
470 :return: Value return by first names list.
472 for name in names:
473 try:
474 return nssfn(name)
475 except KeyError:
476 pass
477 raise KeyError("Unable to find user/group in %r" % names)
480 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
481 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
484 def provision_paths_from_lp(lp, dnsdomain):
485 """Set the default paths for provisioning.
487 :param lp: Loadparm context.
488 :param dnsdomain: DNS Domain name
490 paths = ProvisionPaths()
491 paths.private_dir = lp.get("private dir")
492 paths.state_dir = lp.get("state directory")
494 # This is stored without path prefix for the "privateKeytab" attribute in
495 # "secrets_dns.ldif".
496 paths.dns_keytab = "dns.keytab"
497 paths.keytab = "secrets.keytab"
499 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
500 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
501 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
502 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
503 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
504 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
505 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
506 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
507 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
508 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
509 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
510 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
511 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
512 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
513 paths.hklm = "hklm.ldb"
514 paths.hkcr = "hkcr.ldb"
515 paths.hkcu = "hkcu.ldb"
516 paths.hku = "hku.ldb"
517 paths.hkpd = "hkpd.ldb"
518 paths.hkpt = "hkpt.ldb"
519 paths.sysvol = lp.get("path", "sysvol")
520 paths.netlogon = lp.get("path", "netlogon")
521 paths.smbconf = lp.configfile
522 return paths
525 def determine_netbios_name(hostname):
526 """Determine a netbios name from a hostname."""
527 # remove forbidden chars and force the length to be <16
528 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
529 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
532 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
533 serverrole=None, rootdn=None, domaindn=None, configdn=None,
534 schemadn=None, serverdn=None, sitename=None):
535 """Guess configuration settings to use."""
537 if hostname is None:
538 hostname = socket.gethostname().split(".")[0]
540 netbiosname = lp.get("netbios name")
541 if netbiosname is None:
542 netbiosname = determine_netbios_name(hostname)
543 netbiosname = netbiosname.upper()
544 if not valid_netbios_name(netbiosname):
545 raise InvalidNetbiosName(netbiosname)
547 if dnsdomain is None:
548 dnsdomain = lp.get("realm")
549 if dnsdomain is None or dnsdomain == "":
550 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
552 dnsdomain = dnsdomain.lower()
554 if serverrole is None:
555 serverrole = lp.get("server role")
556 if serverrole is None:
557 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
559 serverrole = serverrole.lower()
561 realm = dnsdomain.upper()
563 if lp.get("realm") == "":
564 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
566 if lp.get("realm").upper() != realm:
567 raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
569 if lp.get("server role").lower() != serverrole:
570 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))
572 if serverrole == "active directory domain controller":
573 if domain is None:
574 # This will, for better or worse, default to 'WORKGROUP'
575 domain = lp.get("workgroup")
576 domain = domain.upper()
578 if lp.get("workgroup").upper() != domain:
579 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))
581 if domaindn is None:
582 domaindn = samba.dn_from_dns_name(dnsdomain)
584 if domain == netbiosname:
585 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
586 else:
587 domain = netbiosname
588 if domaindn is None:
589 domaindn = "DC=" + netbiosname
591 if not valid_netbios_name(domain):
592 raise InvalidNetbiosName(domain)
594 if hostname.upper() == realm:
595 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
596 if netbiosname.upper() == realm:
597 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
598 if domain == realm:
599 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
601 if rootdn is None:
602 rootdn = domaindn
604 if configdn is None:
605 configdn = "CN=Configuration," + rootdn
606 if schemadn is None:
607 schemadn = "CN=Schema," + configdn
609 if sitename is None:
610 sitename = DEFAULTSITE
612 names = ProvisionNames()
613 names.rootdn = rootdn
614 names.domaindn = domaindn
615 names.configdn = configdn
616 names.schemadn = schemadn
617 names.ldapmanagerdn = "CN=Manager," + rootdn
618 names.dnsdomain = dnsdomain
619 names.domain = domain
620 names.realm = realm
621 names.netbiosname = netbiosname
622 names.hostname = hostname
623 names.sitename = sitename
624 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
625 netbiosname, sitename, configdn)
627 return names
630 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
631 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
632 global_param=None):
633 """Create a new smb.conf file based on a couple of basic settings.
635 assert smbconf is not None
637 if hostname is None:
638 hostname = socket.gethostname().split(".")[0]
640 netbiosname = determine_netbios_name(hostname)
642 if serverrole is None:
643 serverrole = "standalone server"
645 assert domain is not None
646 domain = domain.upper()
648 assert realm is not None
649 realm = realm.upper()
651 global_settings = {
652 "netbios name": netbiosname,
653 "workgroup": domain,
654 "realm": realm,
655 "server role": serverrole,
658 if lp is None:
659 lp = samba.param.LoadParm()
660 #Load non-existent file
661 if os.path.exists(smbconf):
662 lp.load(smbconf)
664 if global_param is not None:
665 for ent in global_param:
666 if global_param[ent] is not None:
667 global_settings[ent] = " ".join(global_param[ent])
669 if targetdir is not None:
670 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
671 global_settings["lock dir"] = os.path.abspath(targetdir)
672 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
673 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
675 lp.set("lock dir", os.path.abspath(targetdir))
676 lp.set("state directory", global_settings["state directory"])
677 lp.set("cache directory", global_settings["cache directory"])
679 if eadb:
680 if use_ntvfs and not lp.get("posix:eadb"):
681 if targetdir is not None:
682 privdir = os.path.join(targetdir, "private")
683 else:
684 privdir = lp.get("private dir")
685 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
686 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
687 if targetdir is not None:
688 statedir = os.path.join(targetdir, "state")
689 else:
690 statedir = lp.get("state directory")
691 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
693 shares = {}
694 if serverrole == "active directory domain controller":
695 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
696 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
697 "scripts")
698 else:
699 global_settings["passdb backend"] = "samba_dsdb"
701 f = open(smbconf, 'w')
702 try:
703 f.write("[globals]\n")
704 for key, val in global_settings.iteritems():
705 f.write("\t%s = %s\n" % (key, val))
706 f.write("\n")
708 for name, path in shares.iteritems():
709 f.write("[%s]\n" % name)
710 f.write("\tpath = %s\n" % path)
711 f.write("\tread only = no\n")
712 f.write("\n")
713 finally:
714 f.close()
715 # reload the smb.conf
716 lp.load(smbconf)
718 # and dump it without any values that are the default
719 # this ensures that any smb.conf parameters that were set
720 # on the provision/join command line are set in the resulting smb.conf
721 f = open(smbconf, mode='w')
722 try:
723 lp.dump(f, False)
724 finally:
725 f.close()
728 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
729 users_gid, root_gid):
730 """setup reasonable name mappings for sam names to unix names.
732 :param samdb: SamDB object.
733 :param idmap: IDmap db object.
734 :param sid: The domain sid.
735 :param domaindn: The domain DN.
736 :param root_uid: uid of the UNIX root user.
737 :param nobody_uid: uid of the UNIX nobody user.
738 :param users_gid: gid of the UNIX users group.
739 :param root_gid: gid of the UNIX root group.
741 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
743 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
744 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
747 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
748 provision_backend, names, schema, serverrole,
749 erase=False):
750 """Setup the partitions for the SAM database.
752 Alternatively, provision() may call this, and then populate the database.
754 :note: This will wipe the Sam Database!
756 :note: This function always removes the local SAM LDB file. The erase
757 parameter controls whether to erase the existing data, which
758 may not be stored locally but in LDAP.
761 assert session_info is not None
763 # We use options=["modules:"] to stop the modules loading - we
764 # just want to wipe and re-initialise the database, not start it up
766 try:
767 os.unlink(samdb_path)
768 except OSError:
769 pass
771 samdb = Ldb(url=samdb_path, session_info=session_info,
772 lp=lp, options=["modules:"])
774 ldap_backend_line = "# No LDAP backend"
775 if provision_backend.type != "ldb":
776 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
778 samdb.transaction_start()
779 try:
780 logger.info("Setting up sam.ldb partitions and settings")
781 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
782 "LDAP_BACKEND_LINE": ldap_backend_line
786 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
787 "BACKEND_TYPE": provision_backend.type,
788 "SERVER_ROLE": serverrole
791 logger.info("Setting up sam.ldb rootDSE")
792 setup_samdb_rootdse(samdb, names)
793 except:
794 samdb.transaction_cancel()
795 raise
796 else:
797 samdb.transaction_commit()
800 def secretsdb_self_join(secretsdb, domain,
801 netbiosname, machinepass, domainsid=None,
802 realm=None, dnsdomain=None,
803 keytab_path=None,
804 key_version_number=1,
805 secure_channel_type=SEC_CHAN_WKSTA):
806 """Add domain join-specific bits to a secrets database.
808 :param secretsdb: Ldb Handle to the secrets database
809 :param machinepass: Machine password
811 attrs = ["whenChanged",
812 "secret",
813 "priorSecret",
814 "priorChanged",
815 "krb5Keytab",
816 "privateKeytab"]
818 if realm is not None:
819 if dnsdomain is None:
820 dnsdomain = realm.lower()
821 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
822 else:
823 dnsname = None
824 shortname = netbiosname.lower()
826 # We don't need to set msg["flatname"] here, because rdn_name will handle
827 # it, and it causes problems for modifies anyway
828 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
829 msg["secureChannelType"] = [str(secure_channel_type)]
830 msg["objectClass"] = ["top", "primaryDomain"]
831 if dnsname is not None:
832 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
833 msg["realm"] = [realm]
834 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
835 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
836 msg["privateKeytab"] = ["secrets.keytab"]
838 msg["secret"] = [machinepass]
839 msg["samAccountName"] = ["%s$" % netbiosname]
840 msg["secureChannelType"] = [str(secure_channel_type)]
841 if domainsid is not None:
842 msg["objectSid"] = [ndr_pack(domainsid)]
844 # This complex expression tries to ensure that we don't have more
845 # than one record for this SID, realm or netbios domain at a time,
846 # but we don't delete the old record that we are about to modify,
847 # because that would delete the keytab and previous password.
848 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
849 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
850 scope=ldb.SCOPE_ONELEVEL)
852 for del_msg in res:
853 secretsdb.delete(del_msg.dn)
855 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
857 if len(res) == 1:
858 msg["priorSecret"] = [res[0]["secret"][0]]
859 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
861 try:
862 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
863 except KeyError:
864 pass
866 try:
867 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
868 except KeyError:
869 pass
871 for el in msg:
872 if el != 'dn':
873 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
874 secretsdb.modify(msg)
875 secretsdb.rename(res[0].dn, msg.dn)
876 else:
877 spn = [ 'HOST/%s' % shortname ]
878 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
879 # we are a domain controller then we add servicePrincipalName
880 # entries for the keytab code to update.
881 spn.extend([ 'HOST/%s' % dnsname ])
882 msg["servicePrincipalName"] = spn
884 secretsdb.add(msg)
887 def setup_secretsdb(paths, session_info, backend_credentials, lp):
888 """Setup the secrets database.
890 :note: This function does not handle exceptions and transaction on purpose,
891 it's up to the caller to do this job.
893 :param path: Path to the secrets database.
894 :param session_info: Session info.
895 :param credentials: Credentials
896 :param lp: Loadparm context
897 :return: LDB handle for the created secrets database
899 if os.path.exists(paths.secrets):
900 os.unlink(paths.secrets)
902 keytab_path = os.path.join(paths.private_dir, paths.keytab)
903 if os.path.exists(keytab_path):
904 os.unlink(keytab_path)
906 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
907 if os.path.exists(dns_keytab_path):
908 os.unlink(dns_keytab_path)
910 path = paths.secrets
912 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
913 secrets_ldb.erase()
914 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
915 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
916 secrets_ldb.transaction_start()
917 try:
918 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
920 if (backend_credentials is not None and
921 backend_credentials.authentication_requested()):
922 if backend_credentials.get_bind_dn() is not None:
923 setup_add_ldif(secrets_ldb,
924 setup_path("secrets_simple_ldap.ldif"), {
925 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
926 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
928 else:
929 setup_add_ldif(secrets_ldb,
930 setup_path("secrets_sasl_ldap.ldif"), {
931 "LDAPADMINUSER": backend_credentials.get_username(),
932 "LDAPADMINREALM": backend_credentials.get_realm(),
933 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
935 except:
936 secrets_ldb.transaction_cancel()
937 raise
938 return secrets_ldb
941 def setup_privileges(path, session_info, lp):
942 """Setup the privileges database.
944 :param path: Path to the privileges database.
945 :param session_info: Session info.
946 :param credentials: Credentials
947 :param lp: Loadparm context
948 :return: LDB handle for the created secrets database
950 if os.path.exists(path):
951 os.unlink(path)
952 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
953 privilege_ldb.erase()
954 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
957 def setup_registry(path, session_info, lp):
958 """Setup the registry.
960 :param path: Path to the registry database
961 :param session_info: Session information
962 :param credentials: Credentials
963 :param lp: Loadparm context
965 reg = samba.registry.Registry()
966 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
967 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
968 provision_reg = setup_path("provision.reg")
969 assert os.path.exists(provision_reg)
970 reg.diff_apply(provision_reg)
973 def setup_idmapdb(path, session_info, lp):
974 """Setup the idmap database.
976 :param path: path to the idmap database
977 :param session_info: Session information
978 :param credentials: Credentials
979 :param lp: Loadparm context
981 if os.path.exists(path):
982 os.unlink(path)
984 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
985 idmap_ldb.erase()
986 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
987 return idmap_ldb
990 def setup_samdb_rootdse(samdb, names):
991 """Setup the SamDB rootdse.
993 :param samdb: Sam Database handle
995 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
996 "SCHEMADN": names.schemadn,
997 "DOMAINDN": names.domaindn,
998 "ROOTDN" : names.rootdn,
999 "CONFIGDN": names.configdn,
1000 "SERVERDN": names.serverdn,
1004 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1005 dns_backend, dnspass, domainsid, next_rid, invocationid,
1006 policyguid, policyguid_dc,
1007 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1008 """Join a host to its own domain."""
1009 assert isinstance(invocationid, str)
1010 if ntdsguid is not None:
1011 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1012 else:
1013 ntdsguid_line = ""
1015 if dc_rid is None:
1016 dc_rid = next_rid
1018 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1019 "CONFIGDN": names.configdn,
1020 "SCHEMADN": names.schemadn,
1021 "DOMAINDN": names.domaindn,
1022 "SERVERDN": names.serverdn,
1023 "INVOCATIONID": invocationid,
1024 "NETBIOSNAME": names.netbiosname,
1025 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1026 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1027 "DOMAINSID": str(domainsid),
1028 "DCRID": str(dc_rid),
1029 "SAMBA_VERSION_STRING": version,
1030 "NTDSGUID": ntdsguid_line,
1031 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1032 domainControllerFunctionality),
1033 "RIDALLOCATIONSTART": str(next_rid + 100),
1034 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1036 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1037 "POLICYGUID": policyguid,
1038 "POLICYGUID_DC": policyguid_dc,
1039 "DNSDOMAIN": names.dnsdomain,
1040 "DOMAINDN": names.domaindn})
1042 # If we are setting up a subdomain, then this has been replicated in, so we
1043 # don't need to add it
1044 if fill == FILL_FULL:
1045 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1046 "CONFIGDN": names.configdn,
1047 "SCHEMADN": names.schemadn,
1048 "DOMAINDN": names.domaindn,
1049 "SERVERDN": names.serverdn,
1050 "INVOCATIONID": invocationid,
1051 "NETBIOSNAME": names.netbiosname,
1052 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1053 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1054 "DOMAINSID": str(domainsid),
1055 "DCRID": str(dc_rid),
1056 "SAMBA_VERSION_STRING": version,
1057 "NTDSGUID": ntdsguid_line,
1058 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1059 domainControllerFunctionality)})
1061 # Setup fSMORoleOwner entries to point at the newly created DC entry
1062 setup_modify_ldif(samdb,
1063 setup_path("provision_self_join_modify_config.ldif"), {
1064 "CONFIGDN": names.configdn,
1065 "SCHEMADN": names.schemadn,
1066 "DEFAULTSITE": names.sitename,
1067 "NETBIOSNAME": names.netbiosname,
1068 "SERVERDN": names.serverdn,
1071 system_session_info = system_session()
1072 samdb.set_session_info(system_session_info)
1073 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1074 # modify a serverReference under cn=config when we are a subdomain, we must
1075 # be system due to ACLs
1076 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1077 "DOMAINDN": names.domaindn,
1078 "SERVERDN": names.serverdn,
1079 "NETBIOSNAME": names.netbiosname,
1082 samdb.set_session_info(admin_session_info)
1084 if dns_backend != "SAMBA_INTERNAL":
1085 # This is Samba4 specific and should be replaced by the correct
1086 # DNS AD-style setup
1087 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1088 "DNSDOMAIN": names.dnsdomain,
1089 "DOMAINDN": names.domaindn,
1090 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1091 "HOSTNAME" : names.hostname,
1092 "DNSNAME" : '%s.%s' % (
1093 names.netbiosname.lower(), names.dnsdomain.lower())
1097 def getpolicypath(sysvolpath, dnsdomain, guid):
1098 """Return the physical path of policy given its guid.
1100 :param sysvolpath: Path to the sysvol folder
1101 :param dnsdomain: DNS name of the AD domain
1102 :param guid: The GUID of the policy
1103 :return: A string with the complete path to the policy folder
1105 if guid[0] != "{":
1106 guid = "{%s}" % guid
1107 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1108 return policy_path
1111 def create_gpo_struct(policy_path):
1112 if not os.path.exists(policy_path):
1113 os.makedirs(policy_path, 0775)
1114 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1115 try:
1116 f.write("[General]\r\nVersion=0")
1117 finally:
1118 f.close()
1119 p = os.path.join(policy_path, "MACHINE")
1120 if not os.path.exists(p):
1121 os.makedirs(p, 0775)
1122 p = os.path.join(policy_path, "USER")
1123 if not os.path.exists(p):
1124 os.makedirs(p, 0775)
1127 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1128 """Create the default GPO for a domain
1130 :param sysvolpath: Physical path for the sysvol folder
1131 :param dnsdomain: DNS domain name of the AD domain
1132 :param policyguid: GUID of the default domain policy
1133 :param policyguid_dc: GUID of the default domain controler policy
1135 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1136 create_gpo_struct(policy_path)
1138 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1139 create_gpo_struct(policy_path)
1142 def setup_samdb(path, session_info, provision_backend, lp, names,
1143 logger, fill, serverrole, schema, am_rodc=False):
1144 """Setup a complete SAM Database.
1146 :note: This will wipe the main SAM database file!
1149 # Also wipes the database
1150 setup_samdb_partitions(path, logger=logger, lp=lp,
1151 provision_backend=provision_backend, session_info=session_info,
1152 names=names, serverrole=serverrole, schema=schema)
1154 # Load the database, but don's load the global schema and don't connect
1155 # quite yet
1156 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1157 credentials=provision_backend.credentials, lp=lp,
1158 global_schema=False, am_rodc=am_rodc)
1160 logger.info("Pre-loading the Samba 4 and AD schema")
1162 # Load the schema from the one we computed earlier
1163 samdb.set_schema(schema, write_indices_and_attributes=False)
1165 # Set the NTDS settings DN manually - in order to have it already around
1166 # before the provisioned tree exists and we connect
1167 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1169 # And now we can connect to the DB - the schema won't be loaded from the
1170 # DB
1171 samdb.connect(path)
1173 # But we have to give it one more kick to have it use the schema
1174 # during provision - it needs, now that it is connected, to write
1175 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1176 samdb.set_schema(schema, write_indices_and_attributes=True)
1178 return samdb
1181 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1182 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1183 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1184 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1186 if next_rid is None:
1187 next_rid = 1000
1189 # Provision does not make much sense values larger than 1000000000
1190 # as the upper range of the rIDAvailablePool is 1073741823 and
1191 # we don't want to create a domain that cannot allocate rids.
1192 if next_rid < 1000 or next_rid > 1000000000:
1193 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1194 error += "the valid range is %u-%u. The default is %u." % (
1195 1000, 1000000000, 1000)
1196 raise ProvisioningError(error)
1198 # ATTENTION: Do NOT change these default values without discussion with the
1199 # team and/or release manager. They have a big impact on the whole program!
1200 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1202 if dom_for_fun_level is None:
1203 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1205 if dom_for_fun_level > domainControllerFunctionality:
1206 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!")
1208 domainFunctionality = dom_for_fun_level
1209 forestFunctionality = dom_for_fun_level
1211 # Set the NTDS settings DN manually - in order to have it already around
1212 # before the provisioned tree exists and we connect
1213 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1215 samdb.transaction_start()
1216 try:
1217 # Set the domain functionality levels onto the database.
1218 # Various module (the password_hash module in particular) need
1219 # to know what level of AD we are emulating.
1221 # These will be fixed into the database via the database
1222 # modifictions below, but we need them set from the start.
1223 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1224 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1225 samdb.set_opaque_integer("domainControllerFunctionality",
1226 domainControllerFunctionality)
1228 samdb.set_domain_sid(str(domainsid))
1229 samdb.set_invocation_id(invocationid)
1231 logger.info("Adding DomainDN: %s" % names.domaindn)
1233 # impersonate domain admin
1234 admin_session_info = admin_session(lp, str(domainsid))
1235 samdb.set_session_info(admin_session_info)
1236 if domainguid is not None:
1237 domainguid_line = "objectGUID: %s\n-" % domainguid
1238 else:
1239 domainguid_line = ""
1241 descr = b64encode(get_domain_descriptor(domainsid))
1242 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1243 "DOMAINDN": names.domaindn,
1244 "DOMAINSID": str(domainsid),
1245 "DESCRIPTOR": descr,
1246 "DOMAINGUID": domainguid_line
1249 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1250 "DOMAINDN": names.domaindn,
1251 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1252 "NEXTRID": str(next_rid),
1253 "DEFAULTSITE": names.sitename,
1254 "CONFIGDN": names.configdn,
1255 "POLICYGUID": policyguid,
1256 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1257 "SAMBA_VERSION_STRING": version
1260 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1261 if fill == FILL_FULL:
1262 logger.info("Adding configuration container")
1263 descr = b64encode(get_config_descriptor(domainsid))
1264 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1265 "CONFIGDN": names.configdn,
1266 "DESCRIPTOR": descr,
1269 # The LDIF here was created when the Schema object was constructed
1270 logger.info("Setting up sam.ldb schema")
1271 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1272 samdb.modify_ldif(schema.schema_dn_modify)
1273 samdb.write_prefixes_from_schema()
1274 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1275 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1276 {"SCHEMADN": names.schemadn})
1278 # Now register this container in the root of the forest
1279 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1280 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1281 "subRefs")
1283 except:
1284 samdb.transaction_cancel()
1285 raise
1286 else:
1287 samdb.transaction_commit()
1289 samdb.transaction_start()
1290 try:
1291 samdb.invocation_id = invocationid
1293 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1294 if fill == FILL_FULL:
1295 logger.info("Setting up sam.ldb configuration data")
1296 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1297 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1298 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1299 "CONFIGDN": names.configdn,
1300 "NETBIOSNAME": names.netbiosname,
1301 "DEFAULTSITE": names.sitename,
1302 "DNSDOMAIN": names.dnsdomain,
1303 "DOMAIN": names.domain,
1304 "SCHEMADN": names.schemadn,
1305 "DOMAINDN": names.domaindn,
1306 "SERVERDN": names.serverdn,
1307 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1308 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1309 "PARTITIONS_DESCRIPTOR": partitions_descr,
1310 "SITES_DESCRIPTOR": sites_descr,
1313 logger.info("Setting up display specifiers")
1314 display_specifiers_ldif = read_ms_ldif(
1315 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1316 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1317 {"CONFIGDN": names.configdn})
1318 check_all_substituted(display_specifiers_ldif)
1319 samdb.add_ldif(display_specifiers_ldif)
1321 logger.info("Adding users container")
1322 users_desc = b64encode(get_domain_users_descriptor(domainsid))
1323 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1324 "DOMAINDN": names.domaindn,
1325 "USERS_DESCRIPTOR": users_desc
1327 logger.info("Modifying users container")
1328 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1329 "DOMAINDN": names.domaindn})
1330 logger.info("Adding computers container")
1331 computers_desc = b64encode(get_domain_computers_descriptor(domainsid))
1332 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1333 "DOMAINDN": names.domaindn,
1334 "COMPUTERS_DESCRIPTOR": computers_desc
1336 logger.info("Modifying computers container")
1337 setup_modify_ldif(samdb,
1338 setup_path("provision_computers_modify.ldif"), {
1339 "DOMAINDN": names.domaindn})
1340 logger.info("Setting up sam.ldb data")
1341 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
1342 builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
1343 controllers_desc = b64encode(get_domain_controllers_descriptor(domainsid))
1344 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1345 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1346 "DOMAINDN": names.domaindn,
1347 "NETBIOSNAME": names.netbiosname,
1348 "DEFAULTSITE": names.sitename,
1349 "CONFIGDN": names.configdn,
1350 "SERVERDN": names.serverdn,
1351 "RIDAVAILABLESTART": str(next_rid + 600),
1352 "POLICYGUID_DC": policyguid_dc,
1353 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1354 "BUILTIN_DESCRIPTOR": builtin_desc,
1355 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1358 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1359 if fill == FILL_FULL:
1360 setup_modify_ldif(samdb,
1361 setup_path("provision_configuration_references.ldif"), {
1362 "CONFIGDN": names.configdn,
1363 "SCHEMADN": names.schemadn})
1365 logger.info("Setting up well known security principals")
1366 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1367 "CONFIGDN": names.configdn,
1370 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1371 setup_modify_ldif(samdb,
1372 setup_path("provision_basedn_references.ldif"),
1373 {"DOMAINDN": names.domaindn})
1375 logger.info("Setting up sam.ldb users and groups")
1376 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1377 "DOMAINDN": names.domaindn,
1378 "DOMAINSID": str(domainsid),
1379 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1380 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1383 logger.info("Setting up self join")
1384 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1385 invocationid=invocationid,
1386 dns_backend=dns_backend,
1387 dnspass=dnspass,
1388 machinepass=machinepass,
1389 domainsid=domainsid,
1390 next_rid=next_rid,
1391 dc_rid=dc_rid,
1392 policyguid=policyguid,
1393 policyguid_dc=policyguid_dc,
1394 domainControllerFunctionality=domainControllerFunctionality,
1395 ntdsguid=ntdsguid)
1397 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1398 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1399 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1400 assert isinstance(names.ntdsguid, str)
1401 except:
1402 samdb.transaction_cancel()
1403 raise
1404 else:
1405 samdb.transaction_commit()
1406 return samdb
1409 FILL_FULL = "FULL"
1410 FILL_SUBDOMAIN = "SUBDOMAIN"
1411 FILL_NT4SYNC = "NT4SYNC"
1412 FILL_DRS = "DRS"
1413 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1414 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)"
1415 SYSVOL_SERVICE="sysvol"
1417 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1418 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1419 for root, dirs, files in os.walk(path, topdown=False):
1420 for name in files:
1421 setntacl(lp, os.path.join(root, name), acl, domsid,
1422 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1423 for name in dirs:
1424 setntacl(lp, os.path.join(root, name), acl, domsid,
1425 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1428 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1429 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1430 folders beneath.
1432 :param sysvol: Physical path for the sysvol folder
1433 :param dnsdomain: The DNS name of the domain
1434 :param domainsid: The SID of the domain
1435 :param domaindn: The DN of the domain (ie. DC=...)
1436 :param samdb: An LDB object on the SAM db
1437 :param lp: an LP object
1440 # Set ACL for GPO root folder
1441 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1442 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1443 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1445 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1446 attrs=["cn", "nTSecurityDescriptor"],
1447 expression="", scope=ldb.SCOPE_ONELEVEL)
1449 for policy in res:
1450 acl = ndr_unpack(security.descriptor,
1451 str(policy["nTSecurityDescriptor"])).as_sddl()
1452 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1453 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1454 str(domainsid), use_ntvfs,
1455 passdb=passdb)
1458 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1459 domaindn, lp, use_ntvfs):
1460 """Set the ACL for the sysvol share and the subfolders
1462 :param samdb: An LDB object on the SAM db
1463 :param netlogon: Physical path for the netlogon folder
1464 :param sysvol: Physical path for the sysvol folder
1465 :param uid: The UID of the "Administrator" user
1466 :param gid: The GID of the "Domain adminstrators" group
1467 :param domainsid: The SID of the domain
1468 :param dnsdomain: The DNS name of the domain
1469 :param domaindn: The DN of the domain (ie. DC=...)
1471 s4_passdb = None
1473 if not use_ntvfs:
1474 # This will ensure that the smbd code we are running when setting ACLs
1475 # is initialised with the smb.conf
1476 s3conf = s3param.get_context()
1477 s3conf.load(lp.configfile)
1478 # ensure we are using the right samba_dsdb passdb backend, no matter what
1479 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1480 passdb.reload_static_pdb()
1482 # ensure that we init the samba_dsdb backend, so the domain sid is
1483 # marked in secrets.tdb
1484 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1486 # now ensure everything matches correctly, to avoid wierd issues
1487 if passdb.get_global_sam_sid() != domainsid:
1488 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))
1490 domain_info = s4_passdb.domain_info()
1491 if domain_info["dom_sid"] != domainsid:
1492 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))
1494 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1495 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()))
1498 try:
1499 if use_ntvfs:
1500 os.chown(sysvol, -1, gid)
1501 except OSError:
1502 canchown = False
1503 else:
1504 canchown = True
1506 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1507 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1508 skip_invalid_chown=True, passdb=s4_passdb,
1509 service=SYSVOL_SERVICE)
1510 for root, dirs, files in os.walk(sysvol, topdown=False):
1511 for name in files:
1512 if use_ntvfs and canchown:
1513 os.chown(os.path.join(root, name), -1, gid)
1514 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1515 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1516 passdb=s4_passdb, service=SYSVOL_SERVICE)
1517 for name in dirs:
1518 if use_ntvfs and canchown:
1519 os.chown(os.path.join(root, name), -1, gid)
1520 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1521 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1522 passdb=s4_passdb, service=SYSVOL_SERVICE)
1524 # Set acls on Policy folder and policies folders
1525 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1527 def acl_type(direct_db_access):
1528 if direct_db_access:
1529 return "DB"
1530 else:
1531 return "VFS"
1533 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1534 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1535 fsacl_sddl = fsacl.as_sddl(domainsid)
1536 if fsacl_sddl != acl:
1537 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))
1539 for root, dirs, files in os.walk(path, topdown=False):
1540 for name in files:
1541 fsacl = getntacl(lp, os.path.join(root, name),
1542 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1543 if fsacl is None:
1544 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1545 fsacl_sddl = fsacl.as_sddl(domainsid)
1546 if fsacl_sddl != acl:
1547 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))
1549 for name in dirs:
1550 fsacl = getntacl(lp, os.path.join(root, name),
1551 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1552 if fsacl is None:
1553 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1554 fsacl_sddl = fsacl.as_sddl(domainsid)
1555 if fsacl_sddl != acl:
1556 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))
1559 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1560 direct_db_access):
1561 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1562 folders beneath.
1564 :param sysvol: Physical path for the sysvol folder
1565 :param dnsdomain: The DNS name of the domain
1566 :param domainsid: The SID of the domain
1567 :param domaindn: The DN of the domain (ie. DC=...)
1568 :param samdb: An LDB object on the SAM db
1569 :param lp: an LP object
1572 # Set ACL for GPO root folder
1573 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1574 fsacl = getntacl(lp, root_policy_path,
1575 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1576 if fsacl is None:
1577 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1578 fsacl_sddl = fsacl.as_sddl(domainsid)
1579 if fsacl_sddl != POLICIES_ACL:
1580 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))
1581 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1582 attrs=["cn", "nTSecurityDescriptor"],
1583 expression="", scope=ldb.SCOPE_ONELEVEL)
1585 for policy in res:
1586 acl = ndr_unpack(security.descriptor,
1587 str(policy["nTSecurityDescriptor"])).as_sddl()
1588 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1589 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1590 domainsid, direct_db_access)
1593 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1594 lp):
1595 """Set the ACL for the sysvol share and the subfolders
1597 :param samdb: An LDB object on the SAM db
1598 :param netlogon: Physical path for the netlogon folder
1599 :param sysvol: Physical path for the sysvol folder
1600 :param uid: The UID of the "Administrator" user
1601 :param gid: The GID of the "Domain adminstrators" group
1602 :param domainsid: The SID of the domain
1603 :param dnsdomain: The DNS name of the domain
1604 :param domaindn: The DN of the domain (ie. DC=...)
1607 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1608 s3conf = s3param.get_context()
1609 s3conf.load(lp.configfile)
1610 # ensure we are using the right samba_dsdb passdb backend, no matter what
1611 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1612 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1613 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1615 # now ensure everything matches correctly, to avoid wierd issues
1616 if passdb.get_global_sam_sid() != domainsid:
1617 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))
1619 domain_info = s4_passdb.domain_info()
1620 if domain_info["dom_sid"] != domainsid:
1621 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))
1623 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1624 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()))
1626 # Ensure we can read this directly, and via the smbd VFS
1627 for direct_db_access in [True, False]:
1628 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1629 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1630 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1631 if fsacl is None:
1632 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1633 fsacl_sddl = fsacl.as_sddl(domainsid)
1634 if fsacl_sddl != SYSVOL_ACL:
1635 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))
1637 # Check acls on Policy folder and policies folders
1638 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1639 direct_db_access)
1642 def interface_ips_v4(lp):
1643 """return only IPv4 IPs"""
1644 ips = samba.interface_ips(lp, False)
1645 ret = []
1646 for i in ips:
1647 if i.find(':') == -1:
1648 ret.append(i)
1649 return ret
1652 def interface_ips_v6(lp, linklocal=False):
1653 """return only IPv6 IPs"""
1654 ips = samba.interface_ips(lp, False)
1655 ret = []
1656 for i in ips:
1657 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1658 ret.append(i)
1659 return ret
1662 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1663 domainsid, schema=None,
1664 targetdir=None, samdb_fill=FILL_FULL,
1665 hostip=None, hostip6=None,
1666 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1667 domainguid=None, policyguid=None, policyguid_dc=None,
1668 invocationid=None, machinepass=None, ntdsguid=None,
1669 dns_backend=None, dnspass=None,
1670 serverrole=None, dom_for_fun_level=None,
1671 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1672 # create/adapt the group policy GUIDs
1673 # Default GUID for default policy are described at
1674 # "How Core Group Policy Works"
1675 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1676 if policyguid is None:
1677 policyguid = DEFAULT_POLICY_GUID
1678 policyguid = policyguid.upper()
1679 if policyguid_dc is None:
1680 policyguid_dc = DEFAULT_DC_POLICY_GUID
1681 policyguid_dc = policyguid_dc.upper()
1683 if invocationid is None:
1684 invocationid = str(uuid.uuid4())
1686 if krbtgtpass is None:
1687 krbtgtpass = samba.generate_random_password(128, 255)
1688 if machinepass is None:
1689 machinepass = samba.generate_random_password(128, 255)
1690 if dnspass is None:
1691 dnspass = samba.generate_random_password(128, 255)
1693 samdb = fill_samdb(samdb, lp, names, logger=logger,
1694 domainsid=domainsid, schema=schema, domainguid=domainguid,
1695 policyguid=policyguid, policyguid_dc=policyguid_dc,
1696 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1697 invocationid=invocationid, machinepass=machinepass,
1698 dns_backend=dns_backend, dnspass=dnspass,
1699 ntdsguid=ntdsguid, serverrole=serverrole,
1700 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1701 next_rid=next_rid, dc_rid=dc_rid)
1703 if serverrole == "active directory domain controller":
1705 # Set up group policies (domain policy and domain controller
1706 # policy)
1707 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1708 policyguid_dc)
1709 if not skip_sysvolacl:
1710 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1711 paths.root_gid, domainsid, names.dnsdomain,
1712 names.domaindn, lp, use_ntvfs)
1713 else:
1714 logger.info("Setting acl on sysvol skipped")
1716 secretsdb_self_join(secrets_ldb, domain=names.domain,
1717 realm=names.realm, dnsdomain=names.dnsdomain,
1718 netbiosname=names.netbiosname, domainsid=domainsid,
1719 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1721 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1722 # In future, this might be determined from some configuration
1723 kerberos_enctypes = str(ENC_ALL_TYPES)
1725 try:
1726 msg = ldb.Message(ldb.Dn(samdb,
1727 samdb.searchone("distinguishedName",
1728 expression="samAccountName=%s$" % names.netbiosname,
1729 scope=ldb.SCOPE_SUBTREE)))
1730 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1731 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1732 name="msDS-SupportedEncryptionTypes")
1733 samdb.modify(msg)
1734 except ldb.LdbError, (enum, estr):
1735 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1736 # It might be that this attribute does not exist in this schema
1737 raise
1739 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1740 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1741 dnspass=dnspass, os_level=dom_for_fun_level,
1742 targetdir=targetdir, site=DEFAULTSITE)
1744 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1745 attribute="objectGUID")
1746 assert isinstance(domainguid, str)
1748 lastProvisionUSNs = get_last_provision_usn(samdb)
1749 maxUSN = get_max_usn(samdb, str(names.rootdn))
1750 if lastProvisionUSNs is not None:
1751 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1752 else:
1753 set_provision_usn(samdb, 0, maxUSN, invocationid)
1755 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1756 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1757 { 'NTDSGUID' : names.ntdsguid })
1759 # fix any dangling GUIDs from the provision
1760 logger.info("Fixing provision GUIDs")
1761 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1762 quiet=True)
1763 samdb.transaction_start()
1764 try:
1765 # a small number of GUIDs are missing because of ordering issues in the
1766 # provision code
1767 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1768 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1769 scope=ldb.SCOPE_BASE,
1770 attrs=['defaultObjectCategory'])
1771 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1772 scope=ldb.SCOPE_ONELEVEL,
1773 attrs=['ipsecOwnersReference',
1774 'ipsecFilterReference',
1775 'ipsecISAKMPReference',
1776 'ipsecNegotiationPolicyReference',
1777 'ipsecNFAReference'])
1778 except:
1779 samdb.transaction_cancel()
1780 raise
1781 else:
1782 samdb.transaction_commit()
1785 _ROLES_MAP = {
1786 "ROLE_STANDALONE": "standalone server",
1787 "ROLE_DOMAIN_MEMBER": "member server",
1788 "ROLE_DOMAIN_BDC": "active directory domain controller",
1789 "ROLE_DOMAIN_PDC": "active directory domain controller",
1790 "dc": "active directory domain controller",
1791 "member": "member server",
1792 "domain controller": "active directory domain controller",
1793 "active directory domain controller": "active directory domain controller",
1794 "member server": "member server",
1795 "standalone": "standalone server",
1796 "standalone server": "standalone server",
1800 def sanitize_server_role(role):
1801 """Sanitize a server role name.
1803 :param role: Server role
1804 :raise ValueError: If the role can not be interpreted
1805 :return: Sanitized server role (one of "member server",
1806 "active directory domain controller", "standalone server")
1808 try:
1809 return _ROLES_MAP[role]
1810 except KeyError:
1811 raise ValueError(role)
1814 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1815 maxuid, maxgid):
1816 """Create AD entries for the fake ypserver.
1818 This is needed for being able to manipulate posix attrs via ADUC.
1820 samdb.transaction_start()
1821 try:
1822 logger.info("Setting up fake yp server settings")
1823 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1824 "DOMAINDN": domaindn,
1825 "NETBIOSNAME": netbiosname,
1826 "NISDOMAIN": nisdomain,
1828 except:
1829 samdb.transaction_cancel()
1830 raise
1831 else:
1832 samdb.transaction_commit()
1835 def provision(logger, session_info, credentials, smbconf=None,
1836 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1837 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1838 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1839 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1840 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1841 dns_backend=None, dns_forwarder=None, dnspass=None,
1842 invocationid=None, machinepass=None, ntdsguid=None,
1843 root=None, nobody=None, users=None, backup=None, aci=None,
1844 serverrole=None, dom_for_fun_level=None, backend_type=None,
1845 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1846 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1847 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1848 """Provision samba4
1850 :note: caution, this wipes all existing data!
1853 try:
1854 serverrole = sanitize_server_role(serverrole)
1855 except ValueError:
1856 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1858 if ldapadminpass is None:
1859 # Make a new, random password between Samba and it's LDAP server
1860 ldapadminpass = samba.generate_random_password(128, 255)
1862 if backend_type is None:
1863 backend_type = "ldb"
1865 if domainsid is None:
1866 domainsid = security.random_sid()
1867 else:
1868 domainsid = security.dom_sid(domainsid)
1870 root_uid = findnss_uid([root or "root"])
1871 nobody_uid = findnss_uid([nobody or "nobody"])
1872 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1873 root_gid = pwd.getpwuid(root_uid).pw_gid
1875 try:
1876 bind_gid = findnss_gid(["bind", "named"])
1877 except KeyError:
1878 bind_gid = None
1880 if targetdir is not None:
1881 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1882 elif smbconf is None:
1883 smbconf = samba.param.default_path()
1884 if not os.path.exists(os.path.dirname(smbconf)):
1885 os.makedirs(os.path.dirname(smbconf))
1887 server_services = []
1888 global_param = {}
1889 if use_rfc2307:
1890 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1892 if dns_backend != "SAMBA_INTERNAL":
1893 server_services.append("-dns")
1894 else:
1895 if dns_forwarder is not None:
1896 global_param["dns forwarder"] = [dns_forwarder]
1898 if use_ntvfs:
1899 server_services.append("+smb")
1900 server_services.append("-s3fs")
1901 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1903 if len(server_services) > 0:
1904 global_param["server services"] = server_services
1906 # only install a new smb.conf if there isn't one there already
1907 if os.path.exists(smbconf):
1908 # if Samba Team members can't figure out the weird errors
1909 # loading an empty smb.conf gives, then we need to be smarter.
1910 # Pretend it just didn't exist --abartlet
1911 f = open(smbconf, 'r')
1912 try:
1913 data = f.read().lstrip()
1914 finally:
1915 f.close()
1916 if data is None or data == "":
1917 make_smbconf(smbconf, hostname, domain, realm,
1918 targetdir, serverrole=serverrole,
1919 eadb=useeadb, use_ntvfs=use_ntvfs,
1920 lp=lp, global_param=global_param)
1921 else:
1922 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1923 serverrole=serverrole,
1924 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1926 if lp is None:
1927 lp = samba.param.LoadParm()
1928 lp.load(smbconf)
1929 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1930 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1931 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1932 sitename=sitename, rootdn=rootdn)
1933 paths = provision_paths_from_lp(lp, names.dnsdomain)
1935 paths.bind_gid = bind_gid
1936 paths.root_uid = root_uid;
1937 paths.root_gid = root_gid
1939 if hostip is None:
1940 logger.info("Looking up IPv4 addresses")
1941 hostips = interface_ips_v4(lp)
1942 if len(hostips) > 0:
1943 hostip = hostips[0]
1944 if len(hostips) > 1:
1945 logger.warning("More than one IPv4 address found. Using %s",
1946 hostip)
1947 if hostip == "127.0.0.1":
1948 hostip = None
1949 if hostip is None:
1950 logger.warning("No IPv4 address will be assigned")
1952 if hostip6 is None:
1953 logger.info("Looking up IPv6 addresses")
1954 hostips = interface_ips_v6(lp, linklocal=False)
1955 if hostips:
1956 hostip6 = hostips[0]
1957 if len(hostips) > 1:
1958 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1959 if hostip6 is None:
1960 logger.warning("No IPv6 address will be assigned")
1962 names.hostip = hostip
1963 names.hostip6 = hostip6
1965 if serverrole is None:
1966 serverrole = lp.get("server role")
1968 if not os.path.exists(paths.private_dir):
1969 os.mkdir(paths.private_dir)
1970 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1971 os.mkdir(os.path.join(paths.private_dir, "tls"))
1972 if not os.path.exists(paths.state_dir):
1973 os.mkdir(paths.state_dir)
1975 if paths.sysvol and not os.path.exists(paths.sysvol):
1976 os.makedirs(paths.sysvol, 0775)
1978 if not use_ntvfs and serverrole == "active directory domain controller":
1979 s3conf = s3param.get_context()
1980 s3conf.load(lp.configfile)
1982 if paths.sysvol is None:
1983 raise MissingShareError("sysvol", paths.smbconf)
1985 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1986 try:
1987 try:
1988 smbd.set_simple_acl(file.name, 0755, root_gid)
1989 except Exception:
1990 if not smbd.have_posix_acls():
1991 # This clue is only strictly correct for RPM and
1992 # Debian-like Linux systems, but hopefully other users
1993 # will get enough clue from it.
1994 raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires. Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
1996 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1997 try:
1998 smbd.chown(file.name, root_uid, root_gid)
1999 except Exception:
2000 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
2001 finally:
2002 file.close()
2004 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2006 schema = Schema(domainsid, invocationid=invocationid,
2007 schemadn=names.schemadn)
2009 if backend_type == "ldb":
2010 provision_backend = LDBBackend(backend_type, paths=paths,
2011 lp=lp, credentials=credentials,
2012 names=names, logger=logger)
2013 elif backend_type == "existing":
2014 # If support for this is ever added back, then the URI will need to be
2015 # specified again
2016 provision_backend = ExistingBackend(backend_type, paths=paths,
2017 lp=lp, credentials=credentials,
2018 names=names, logger=logger,
2019 ldap_backend_forced_uri=None)
2020 elif backend_type == "fedora-ds":
2021 provision_backend = FDSBackend(backend_type, paths=paths,
2022 lp=lp, credentials=credentials,
2023 names=names, logger=logger, domainsid=domainsid,
2024 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2025 slapd_path=slapd_path,
2026 root=root)
2027 elif backend_type == "openldap":
2028 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2029 lp=lp, credentials=credentials,
2030 names=names, logger=logger, domainsid=domainsid,
2031 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2032 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
2033 else:
2034 raise ValueError("Unknown LDAP backend type selected")
2036 provision_backend.init()
2037 provision_backend.start()
2039 # only install a new shares config db if there is none
2040 if not os.path.exists(paths.shareconf):
2041 logger.info("Setting up share.ldb")
2042 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2043 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2045 logger.info("Setting up secrets.ldb")
2046 secrets_ldb = setup_secretsdb(paths,
2047 session_info=session_info,
2048 backend_credentials=provision_backend.secrets_credentials, lp=lp)
2050 try:
2051 logger.info("Setting up the registry")
2052 setup_registry(paths.hklm, session_info, lp=lp)
2054 logger.info("Setting up the privileges database")
2055 setup_privileges(paths.privilege, session_info, lp=lp)
2057 logger.info("Setting up idmap db")
2058 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2060 setup_name_mappings(idmap, sid=str(domainsid),
2061 root_uid=root_uid, nobody_uid=nobody_uid,
2062 users_gid=users_gid, root_gid=root_gid)
2064 logger.info("Setting up SAM db")
2065 samdb = setup_samdb(paths.samdb, session_info,
2066 provision_backend, lp, names, logger=logger,
2067 serverrole=serverrole,
2068 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2070 if serverrole == "active directory domain controller":
2071 if paths.netlogon is None:
2072 raise MissingShareError("netlogon", paths.smbconf)
2074 if paths.sysvol is None:
2075 raise MissingShareError("sysvol", paths.smbconf)
2077 if not os.path.isdir(paths.netlogon):
2078 os.makedirs(paths.netlogon, 0755)
2080 if adminpass is None:
2081 adminpass = samba.generate_random_password(12, 32)
2082 adminpass_generated = True
2083 else:
2084 adminpass_generated = False
2086 if samdb_fill == FILL_FULL:
2087 provision_fill(samdb, secrets_ldb, logger, names, paths,
2088 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2089 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2090 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2091 krbtgtpass=krbtgtpass, domainguid=domainguid,
2092 policyguid=policyguid, policyguid_dc=policyguid_dc,
2093 invocationid=invocationid, machinepass=machinepass,
2094 ntdsguid=ntdsguid, dns_backend=dns_backend,
2095 dnspass=dnspass, serverrole=serverrole,
2096 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2097 lp=lp, use_ntvfs=use_ntvfs,
2098 skip_sysvolacl=skip_sysvolacl)
2100 create_krb5_conf(paths.krb5conf,
2101 dnsdomain=names.dnsdomain, hostname=names.hostname,
2102 realm=names.realm)
2103 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2104 "generated at %s", paths.krb5conf)
2106 if serverrole == "active directory domain controller":
2107 create_dns_update_list(lp, logger, paths)
2109 backend_result = provision_backend.post_setup()
2110 provision_backend.shutdown()
2112 except:
2113 secrets_ldb.transaction_cancel()
2114 raise
2116 # Now commit the secrets.ldb to disk
2117 secrets_ldb.transaction_commit()
2119 # the commit creates the dns.keytab, now chown it
2120 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2121 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2122 try:
2123 os.chmod(dns_keytab_path, 0640)
2124 os.chown(dns_keytab_path, -1, paths.bind_gid)
2125 except OSError:
2126 if not os.environ.has_key('SAMBA_SELFTEST'):
2127 logger.info("Failed to chown %s to bind gid %u",
2128 dns_keytab_path, paths.bind_gid)
2130 result = ProvisionResult()
2131 result.server_role = serverrole
2132 result.domaindn = domaindn
2133 result.paths = paths
2134 result.names = names
2135 result.lp = lp
2136 result.samdb = samdb
2137 result.idmap = idmap
2138 result.domainsid = str(domainsid)
2140 if samdb_fill == FILL_FULL:
2141 result.adminpass_generated = adminpass_generated
2142 result.adminpass = adminpass
2143 else:
2144 result.adminpass_generated = False
2145 result.adminpass = None
2147 result.backend_result = backend_result
2149 if use_rfc2307:
2150 provision_fake_ypserver(logger=logger, samdb=samdb,
2151 domaindn=names.domaindn, netbiosname=names.netbiosname,
2152 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2154 return result
2157 def provision_become_dc(smbconf=None, targetdir=None,
2158 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2159 serverdn=None, domain=None, hostname=None, domainsid=None,
2160 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2161 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2162 dns_backend=None, root=None, nobody=None, users=None,
2163 backup=None, serverrole=None, ldap_backend=None,
2164 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2166 logger = logging.getLogger("provision")
2167 samba.set_debug_level(debuglevel)
2169 res = provision(logger, system_session(), None,
2170 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2171 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2172 configdn=configdn, serverdn=serverdn, domain=domain,
2173 hostname=hostname, hostip=None, domainsid=domainsid,
2174 machinepass=machinepass,
2175 serverrole="active directory domain controller",
2176 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2177 use_ntvfs=use_ntvfs)
2178 res.lp.set("debuglevel", str(debuglevel))
2179 return res
2182 def create_krb5_conf(path, dnsdomain, hostname, realm):
2183 """Write out a file containing zone statements suitable for inclusion in a
2184 named.conf file (including GSS-TSIG configuration).
2186 :param path: Path of the new named.conf file.
2187 :param dnsdomain: DNS Domain name
2188 :param hostname: Local hostname
2189 :param realm: Realm name
2191 setup_file(setup_path("krb5.conf"), path, {
2192 "DNSDOMAIN": dnsdomain,
2193 "HOSTNAME": hostname,
2194 "REALM": realm,
2198 class ProvisioningError(Exception):
2199 """A generic provision error."""
2201 def __init__(self, value):
2202 self.value = value
2204 def __str__(self):
2205 return "ProvisioningError: " + self.value
2208 class InvalidNetbiosName(Exception):
2209 """A specified name was not a valid NetBIOS name."""
2211 def __init__(self, name):
2212 super(InvalidNetbiosName, self).__init__(
2213 "The name '%r' is not a valid NetBIOS name" % name)
2216 class MissingShareError(ProvisioningError):
2218 def __init__(self, name, smbconf):
2219 super(MissingShareError, self).__init__(
2220 "Existing smb.conf does not have a [%s] share, but you are "
2221 "configuring a DC. Please remove %s or add the share manually." %
2222 (name, smbconf))