provision: setup names.name_map['DnsAdmins']
[Samba/gbeck.git] / source4 / scripting / python / samba / provision / __init__.py
blob4aff6f68a025ee0529622a053562bc9b43ec744c
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 get_dnsadmins_sid,
107 setup_ad_dns,
108 create_dns_update_list
111 import samba.param
112 import samba.registry
113 from samba.schema import Schema
114 from samba.samdb import SamDB
115 from samba.dbchecker import dbcheck
118 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
119 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
120 DEFAULTSITE = "Default-First-Site-Name"
121 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
124 class ProvisionPaths(object):
126 def __init__(self):
127 self.shareconf = None
128 self.hklm = None
129 self.hkcu = None
130 self.hkcr = None
131 self.hku = None
132 self.hkpd = None
133 self.hkpt = None
134 self.samdb = None
135 self.idmapdb = None
136 self.secrets = None
137 self.keytab = None
138 self.dns_keytab = None
139 self.dns = None
140 self.winsdb = None
141 self.private_dir = None
142 self.state_dir = None
145 class ProvisionNames(object):
147 def __init__(self):
148 self.ncs = None
149 self.rootdn = None
150 self.domaindn = None
151 self.configdn = None
152 self.schemadn = None
153 self.dnsforestdn = None
154 self.dnsdomaindn = None
155 self.ldapmanagerdn = None
156 self.dnsdomain = None
157 self.realm = None
158 self.netbiosname = None
159 self.domain = None
160 self.hostname = None
161 self.sitename = None
162 self.smbconf = None
163 self.name_map = {}
166 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
167 lp):
168 """Get key provision parameters (realm, domain, ...) from a given provision
170 :param samdb: An LDB object connected to the sam.ldb file
171 :param secretsdb: An LDB object connected to the secrets.ldb file
172 :param idmapdb: An LDB object connected to the idmap.ldb file
173 :param paths: A list of path to provision object
174 :param smbconf: Path to the smb.conf file
175 :param lp: A LoadParm object
176 :return: A list of key provision parameters
178 names = ProvisionNames()
179 names.adminpass = None
181 # NT domain, kerberos realm, root dn, domain dn, domain dns name
182 names.domain = string.upper(lp.get("workgroup"))
183 names.realm = lp.get("realm")
184 names.dnsdomain = names.realm.lower()
185 basedn = samba.dn_from_dns_name(names.dnsdomain)
186 names.realm = string.upper(names.realm)
187 # netbiosname
188 # Get the netbiosname first (could be obtained from smb.conf in theory)
189 res = secretsdb.search(expression="(flatname=%s)" %
190 names.domain,base="CN=Primary Domains",
191 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
192 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
194 names.smbconf = smbconf
196 # That's a bit simplistic but it's ok as long as we have only 3
197 # partitions
198 current = samdb.search(expression="(objectClass=*)",
199 base="", scope=ldb.SCOPE_BASE,
200 attrs=["defaultNamingContext", "schemaNamingContext",
201 "configurationNamingContext","rootDomainNamingContext",
202 "namingContexts"])
204 names.configdn = current[0]["configurationNamingContext"]
205 configdn = str(names.configdn)
206 names.schemadn = current[0]["schemaNamingContext"]
207 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
208 current[0]["defaultNamingContext"][0]))):
209 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
210 "is not the same ..." % (paths.samdb,
211 str(current[0]["defaultNamingContext"][0]),
212 paths.smbconf, basedn)))
214 names.domaindn=current[0]["defaultNamingContext"]
215 names.rootdn=current[0]["rootDomainNamingContext"]
216 names.ncs=current[0]["namingContexts"]
217 names.dnsforestdn = None
218 names.dnsdomaindn = None
220 for i in range(0, len(names.ncs)):
221 nc = names.ncs[i]
223 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
224 if nc == dnsforestdn:
225 names.dnsforestdn = dnsforestdn
226 continue
228 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
229 if nc == dnsdomaindn:
230 names.dnsdomaindn = dnsdomaindn
231 continue
233 # default site name
234 res3 = samdb.search(expression="(objectClass=site)",
235 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
236 names.sitename = str(res3[0]["cn"])
238 # dns hostname and server dn
239 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
240 base="OU=Domain Controllers,%s" % basedn,
241 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
242 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
244 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
245 attrs=[], base=configdn)
246 names.serverdn = server_res[0].dn
248 # invocation id/objectguid
249 res5 = samdb.search(expression="(objectClass=*)",
250 base="CN=NTDS Settings,%s" % str(names.serverdn),
251 scope=ldb.SCOPE_BASE,
252 attrs=["invocationID", "objectGUID"])
253 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
254 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
256 # domain guid/sid
257 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
258 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
259 "objectSid","msDS-Behavior-Version" ])
260 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
261 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
262 if res6[0].get("msDS-Behavior-Version") is None or \
263 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
264 names.domainlevel = DS_DOMAIN_FUNCTION_2000
265 else:
266 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
268 # policy guid
269 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
270 base="CN=Policies,CN=System," + basedn,
271 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
272 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
273 # dc policy guid
274 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
275 " Policy)",
276 base="CN=Policies,CN=System," + basedn,
277 scope=ldb.SCOPE_ONELEVEL,
278 attrs=["cn","displayName"])
279 if len(res8) == 1:
280 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
281 else:
282 names.policyid_dc = None
284 res9 = idmapdb.search(expression="(cn=%s-%s)" %
285 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
286 attrs=["xidNumber", "type"])
287 if len(res9) != 1:
288 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
289 if res9[0]["type"][0] == "ID_TYPE_BOTH":
290 names.root_gid = res9[0]["xidNumber"][0]
291 else:
292 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
294 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
295 names.name_map['DnsAdmins'] = str(dns_admins_sid)
297 return names
300 def update_provision_usn(samdb, low, high, id, replace=False):
301 """Update the field provisionUSN in sam.ldb
303 This field is used to track range of USN modified by provision and
304 upgradeprovision.
305 This value is used afterward by next provision to figure out if
306 the field have been modified since last provision.
308 :param samdb: An LDB object connect to sam.ldb
309 :param low: The lowest USN modified by this upgrade
310 :param high: The highest USN modified by this upgrade
311 :param id: The invocation id of the samba's dc
312 :param replace: A boolean indicating if the range should replace any
313 existing one or appended (default)
316 tab = []
317 if not replace:
318 entry = samdb.search(base="@PROVISION",
319 scope=ldb.SCOPE_BASE,
320 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
321 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
322 if not re.search(';', e):
323 e = "%s;%s" % (e, id)
324 tab.append(str(e))
326 tab.append("%s-%s;%s" % (low, high, id))
327 delta = ldb.Message()
328 delta.dn = ldb.Dn(samdb, "@PROVISION")
329 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
330 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
331 entry = samdb.search(expression='provisionnerID=*',
332 base="@PROVISION", scope=ldb.SCOPE_BASE,
333 attrs=["provisionnerID"])
334 if len(entry) == 0 or len(entry[0]) == 0:
335 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
336 samdb.modify(delta)
339 def set_provision_usn(samdb, low, high, id):
340 """Set the field provisionUSN in sam.ldb
341 This field is used to track range of USN modified by provision and
342 upgradeprovision.
343 This value is used afterward by next provision to figure out if
344 the field have been modified since last provision.
346 :param samdb: An LDB object connect to sam.ldb
347 :param low: The lowest USN modified by this upgrade
348 :param high: The highest USN modified by this upgrade
349 :param id: The invocationId of the provision"""
351 tab = []
352 tab.append("%s-%s;%s" % (low, high, id))
354 delta = ldb.Message()
355 delta.dn = ldb.Dn(samdb, "@PROVISION")
356 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
357 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
358 samdb.add(delta)
361 def get_max_usn(samdb,basedn):
362 """ This function return the biggest USN present in the provision
364 :param samdb: A LDB object pointing to the sam.ldb
365 :param basedn: A string containing the base DN of the provision
366 (ie. DC=foo, DC=bar)
367 :return: The biggest USN in the provision"""
369 res = samdb.search(expression="objectClass=*",base=basedn,
370 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
371 controls=["search_options:1:2",
372 "server_sort:1:1:uSNChanged",
373 "paged_results:1:1"])
374 return res[0]["uSNChanged"]
377 def get_last_provision_usn(sam):
378 """Get USNs ranges modified by a provision or an upgradeprovision
380 :param sam: An LDB object pointing to the sam.ldb
381 :return: a dictionary which keys are invocation id and values are an array
382 of integer representing the different ranges
384 try:
385 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
386 base="@PROVISION", scope=ldb.SCOPE_BASE,
387 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
388 except ldb.LdbError, (ecode, emsg):
389 if ecode == ldb.ERR_NO_SUCH_OBJECT:
390 return None
391 raise
392 if len(entry) > 0:
393 myids = []
394 range = {}
395 p = re.compile(r'-')
396 if entry[0].get("provisionnerID"):
397 for e in entry[0]["provisionnerID"]:
398 myids.append(str(e))
399 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
400 tab1 = str(r).split(';')
401 if len(tab1) == 2:
402 id = tab1[1]
403 else:
404 id = "default"
405 if (len(myids) > 0 and id not in myids):
406 continue
407 tab2 = p.split(tab1[0])
408 if range.get(id) is None:
409 range[id] = []
410 range[id].append(tab2[0])
411 range[id].append(tab2[1])
412 return range
413 else:
414 return None
417 class ProvisionResult(object):
418 """Result of a provision.
420 :ivar server_role: The server role
421 :ivar paths: ProvisionPaths instance
422 :ivar domaindn: The domain dn, as string
425 def __init__(self):
426 self.server_role = None
427 self.paths = None
428 self.domaindn = None
429 self.lp = None
430 self.samdb = None
431 self.idmap = None
432 self.names = None
433 self.domainsid = None
434 self.adminpass_generated = None
435 self.adminpass = None
436 self.backend_result = None
438 def report_logger(self, logger):
439 """Report this provision result to a logger."""
440 logger.info(
441 "Once the above files are installed, your Samba4 server will "
442 "be ready to use")
443 if self.adminpass_generated:
444 logger.info("Admin password: %s", self.adminpass)
445 logger.info("Server Role: %s", self.server_role)
446 logger.info("Hostname: %s", self.names.hostname)
447 logger.info("NetBIOS Domain: %s", self.names.domain)
448 logger.info("DNS Domain: %s", self.names.dnsdomain)
449 logger.info("DOMAIN SID: %s", self.domainsid)
451 if self.backend_result:
452 self.backend_result.report_logger(logger)
455 def check_install(lp, session_info, credentials):
456 """Check whether the current install seems ok.
458 :param lp: Loadparm context
459 :param session_info: Session information
460 :param credentials: Credentials
462 if lp.get("realm") == "":
463 raise Exception("Realm empty")
464 samdb = Ldb(lp.samdb_url(), session_info=session_info,
465 credentials=credentials, lp=lp)
466 if len(samdb.search("(cn=Administrator)")) != 1:
467 raise ProvisioningError("No administrator account found")
470 def findnss(nssfn, names):
471 """Find a user or group from a list of possibilities.
473 :param nssfn: NSS Function to try (should raise KeyError if not found)
474 :param names: Names to check.
475 :return: Value return by first names list.
477 for name in names:
478 try:
479 return nssfn(name)
480 except KeyError:
481 pass
482 raise KeyError("Unable to find user/group in %r" % names)
485 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
486 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
489 def provision_paths_from_lp(lp, dnsdomain):
490 """Set the default paths for provisioning.
492 :param lp: Loadparm context.
493 :param dnsdomain: DNS Domain name
495 paths = ProvisionPaths()
496 paths.private_dir = lp.get("private dir")
497 paths.state_dir = lp.get("state directory")
499 # This is stored without path prefix for the "privateKeytab" attribute in
500 # "secrets_dns.ldif".
501 paths.dns_keytab = "dns.keytab"
502 paths.keytab = "secrets.keytab"
504 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
505 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
506 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
507 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
508 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
509 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
510 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
511 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
512 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
513 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
514 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
515 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
516 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
517 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
518 paths.hklm = "hklm.ldb"
519 paths.hkcr = "hkcr.ldb"
520 paths.hkcu = "hkcu.ldb"
521 paths.hku = "hku.ldb"
522 paths.hkpd = "hkpd.ldb"
523 paths.hkpt = "hkpt.ldb"
524 paths.sysvol = lp.get("path", "sysvol")
525 paths.netlogon = lp.get("path", "netlogon")
526 paths.smbconf = lp.configfile
527 return paths
530 def determine_netbios_name(hostname):
531 """Determine a netbios name from a hostname."""
532 # remove forbidden chars and force the length to be <16
533 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
534 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
537 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
538 serverrole=None, rootdn=None, domaindn=None, configdn=None,
539 schemadn=None, serverdn=None, sitename=None):
540 """Guess configuration settings to use."""
542 if hostname is None:
543 hostname = socket.gethostname().split(".")[0]
545 netbiosname = lp.get("netbios name")
546 if netbiosname is None:
547 netbiosname = determine_netbios_name(hostname)
548 netbiosname = netbiosname.upper()
549 if not valid_netbios_name(netbiosname):
550 raise InvalidNetbiosName(netbiosname)
552 if dnsdomain is None:
553 dnsdomain = lp.get("realm")
554 if dnsdomain is None or dnsdomain == "":
555 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
557 dnsdomain = dnsdomain.lower()
559 if serverrole is None:
560 serverrole = lp.get("server role")
561 if serverrole is None:
562 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
564 serverrole = serverrole.lower()
566 realm = dnsdomain.upper()
568 if lp.get("realm") == "":
569 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
571 if lp.get("realm").upper() != realm:
572 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))
574 if lp.get("server role").lower() != serverrole:
575 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))
577 if serverrole == "active directory domain controller":
578 if domain is None:
579 # This will, for better or worse, default to 'WORKGROUP'
580 domain = lp.get("workgroup")
581 domain = domain.upper()
583 if lp.get("workgroup").upper() != domain:
584 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))
586 if domaindn is None:
587 domaindn = samba.dn_from_dns_name(dnsdomain)
589 if domain == netbiosname:
590 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
591 else:
592 domain = netbiosname
593 if domaindn is None:
594 domaindn = "DC=" + netbiosname
596 if not valid_netbios_name(domain):
597 raise InvalidNetbiosName(domain)
599 if hostname.upper() == realm:
600 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
601 if netbiosname.upper() == realm:
602 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
603 if domain == realm:
604 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
606 if rootdn is None:
607 rootdn = domaindn
609 if configdn is None:
610 configdn = "CN=Configuration," + rootdn
611 if schemadn is None:
612 schemadn = "CN=Schema," + configdn
614 if sitename is None:
615 sitename = DEFAULTSITE
617 names = ProvisionNames()
618 names.rootdn = rootdn
619 names.domaindn = domaindn
620 names.configdn = configdn
621 names.schemadn = schemadn
622 names.ldapmanagerdn = "CN=Manager," + rootdn
623 names.dnsdomain = dnsdomain
624 names.domain = domain
625 names.realm = realm
626 names.netbiosname = netbiosname
627 names.hostname = hostname
628 names.sitename = sitename
629 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
630 netbiosname, sitename, configdn)
632 return names
635 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
636 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
637 global_param=None):
638 """Create a new smb.conf file based on a couple of basic settings.
640 assert smbconf is not None
642 if hostname is None:
643 hostname = socket.gethostname().split(".")[0]
645 netbiosname = determine_netbios_name(hostname)
647 if serverrole is None:
648 serverrole = "standalone server"
650 assert domain is not None
651 domain = domain.upper()
653 assert realm is not None
654 realm = realm.upper()
656 global_settings = {
657 "netbios name": netbiosname,
658 "workgroup": domain,
659 "realm": realm,
660 "server role": serverrole,
663 if lp is None:
664 lp = samba.param.LoadParm()
665 #Load non-existent file
666 if os.path.exists(smbconf):
667 lp.load(smbconf)
669 if global_param is not None:
670 for ent in global_param:
671 if global_param[ent] is not None:
672 global_settings[ent] = " ".join(global_param[ent])
674 if targetdir is not None:
675 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
676 global_settings["lock dir"] = os.path.abspath(targetdir)
677 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
678 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
680 lp.set("lock dir", os.path.abspath(targetdir))
681 lp.set("state directory", global_settings["state directory"])
682 lp.set("cache directory", global_settings["cache directory"])
684 if eadb:
685 if use_ntvfs and not lp.get("posix:eadb"):
686 if targetdir is not None:
687 privdir = os.path.join(targetdir, "private")
688 else:
689 privdir = lp.get("private dir")
690 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
691 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
692 if targetdir is not None:
693 statedir = os.path.join(targetdir, "state")
694 else:
695 statedir = lp.get("state directory")
696 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
698 shares = {}
699 if serverrole == "active directory domain controller":
700 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
701 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
702 "scripts")
703 else:
704 global_settings["passdb backend"] = "samba_dsdb"
706 f = open(smbconf, 'w')
707 try:
708 f.write("[globals]\n")
709 for key, val in global_settings.iteritems():
710 f.write("\t%s = %s\n" % (key, val))
711 f.write("\n")
713 for name, path in shares.iteritems():
714 f.write("[%s]\n" % name)
715 f.write("\tpath = %s\n" % path)
716 f.write("\tread only = no\n")
717 f.write("\n")
718 finally:
719 f.close()
720 # reload the smb.conf
721 lp.load(smbconf)
723 # and dump it without any values that are the default
724 # this ensures that any smb.conf parameters that were set
725 # on the provision/join command line are set in the resulting smb.conf
726 f = open(smbconf, mode='w')
727 try:
728 lp.dump(f, False)
729 finally:
730 f.close()
733 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
734 users_gid, root_gid):
735 """setup reasonable name mappings for sam names to unix names.
737 :param samdb: SamDB object.
738 :param idmap: IDmap db object.
739 :param sid: The domain sid.
740 :param domaindn: The domain DN.
741 :param root_uid: uid of the UNIX root user.
742 :param nobody_uid: uid of the UNIX nobody user.
743 :param users_gid: gid of the UNIX users group.
744 :param root_gid: gid of the UNIX root group.
746 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
748 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
749 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
752 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
753 provision_backend, names, schema, serverrole,
754 erase=False):
755 """Setup the partitions for the SAM database.
757 Alternatively, provision() may call this, and then populate the database.
759 :note: This will wipe the Sam Database!
761 :note: This function always removes the local SAM LDB file. The erase
762 parameter controls whether to erase the existing data, which
763 may not be stored locally but in LDAP.
766 assert session_info is not None
768 # We use options=["modules:"] to stop the modules loading - we
769 # just want to wipe and re-initialise the database, not start it up
771 try:
772 os.unlink(samdb_path)
773 except OSError:
774 pass
776 samdb = Ldb(url=samdb_path, session_info=session_info,
777 lp=lp, options=["modules:"])
779 ldap_backend_line = "# No LDAP backend"
780 if provision_backend.type != "ldb":
781 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
783 samdb.transaction_start()
784 try:
785 logger.info("Setting up sam.ldb partitions and settings")
786 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
787 "LDAP_BACKEND_LINE": ldap_backend_line
791 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
792 "BACKEND_TYPE": provision_backend.type,
793 "SERVER_ROLE": serverrole
796 logger.info("Setting up sam.ldb rootDSE")
797 setup_samdb_rootdse(samdb, names)
798 except:
799 samdb.transaction_cancel()
800 raise
801 else:
802 samdb.transaction_commit()
805 def secretsdb_self_join(secretsdb, domain,
806 netbiosname, machinepass, domainsid=None,
807 realm=None, dnsdomain=None,
808 keytab_path=None,
809 key_version_number=1,
810 secure_channel_type=SEC_CHAN_WKSTA):
811 """Add domain join-specific bits to a secrets database.
813 :param secretsdb: Ldb Handle to the secrets database
814 :param machinepass: Machine password
816 attrs = ["whenChanged",
817 "secret",
818 "priorSecret",
819 "priorChanged",
820 "krb5Keytab",
821 "privateKeytab"]
823 if realm is not None:
824 if dnsdomain is None:
825 dnsdomain = realm.lower()
826 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
827 else:
828 dnsname = None
829 shortname = netbiosname.lower()
831 # We don't need to set msg["flatname"] here, because rdn_name will handle
832 # it, and it causes problems for modifies anyway
833 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
834 msg["secureChannelType"] = [str(secure_channel_type)]
835 msg["objectClass"] = ["top", "primaryDomain"]
836 if dnsname is not None:
837 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
838 msg["realm"] = [realm]
839 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
840 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
841 msg["privateKeytab"] = ["secrets.keytab"]
843 msg["secret"] = [machinepass]
844 msg["samAccountName"] = ["%s$" % netbiosname]
845 msg["secureChannelType"] = [str(secure_channel_type)]
846 if domainsid is not None:
847 msg["objectSid"] = [ndr_pack(domainsid)]
849 # This complex expression tries to ensure that we don't have more
850 # than one record for this SID, realm or netbios domain at a time,
851 # but we don't delete the old record that we are about to modify,
852 # because that would delete the keytab and previous password.
853 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
854 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
855 scope=ldb.SCOPE_ONELEVEL)
857 for del_msg in res:
858 secretsdb.delete(del_msg.dn)
860 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
862 if len(res) == 1:
863 msg["priorSecret"] = [res[0]["secret"][0]]
864 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
866 try:
867 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
868 except KeyError:
869 pass
871 try:
872 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
873 except KeyError:
874 pass
876 for el in msg:
877 if el != 'dn':
878 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
879 secretsdb.modify(msg)
880 secretsdb.rename(res[0].dn, msg.dn)
881 else:
882 spn = [ 'HOST/%s' % shortname ]
883 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
884 # we are a domain controller then we add servicePrincipalName
885 # entries for the keytab code to update.
886 spn.extend([ 'HOST/%s' % dnsname ])
887 msg["servicePrincipalName"] = spn
889 secretsdb.add(msg)
892 def setup_secretsdb(paths, session_info, backend_credentials, lp):
893 """Setup the secrets database.
895 :note: This function does not handle exceptions and transaction on purpose,
896 it's up to the caller to do this job.
898 :param path: Path to the secrets database.
899 :param session_info: Session info.
900 :param credentials: Credentials
901 :param lp: Loadparm context
902 :return: LDB handle for the created secrets database
904 if os.path.exists(paths.secrets):
905 os.unlink(paths.secrets)
907 keytab_path = os.path.join(paths.private_dir, paths.keytab)
908 if os.path.exists(keytab_path):
909 os.unlink(keytab_path)
911 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
912 if os.path.exists(dns_keytab_path):
913 os.unlink(dns_keytab_path)
915 path = paths.secrets
917 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
918 secrets_ldb.erase()
919 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
920 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
921 secrets_ldb.transaction_start()
922 try:
923 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
925 if (backend_credentials is not None and
926 backend_credentials.authentication_requested()):
927 if backend_credentials.get_bind_dn() is not None:
928 setup_add_ldif(secrets_ldb,
929 setup_path("secrets_simple_ldap.ldif"), {
930 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
931 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
933 else:
934 setup_add_ldif(secrets_ldb,
935 setup_path("secrets_sasl_ldap.ldif"), {
936 "LDAPADMINUSER": backend_credentials.get_username(),
937 "LDAPADMINREALM": backend_credentials.get_realm(),
938 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
940 except:
941 secrets_ldb.transaction_cancel()
942 raise
943 return secrets_ldb
946 def setup_privileges(path, session_info, lp):
947 """Setup the privileges database.
949 :param path: Path to the privileges database.
950 :param session_info: Session info.
951 :param credentials: Credentials
952 :param lp: Loadparm context
953 :return: LDB handle for the created secrets database
955 if os.path.exists(path):
956 os.unlink(path)
957 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
958 privilege_ldb.erase()
959 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
962 def setup_registry(path, session_info, lp):
963 """Setup the registry.
965 :param path: Path to the registry database
966 :param session_info: Session information
967 :param credentials: Credentials
968 :param lp: Loadparm context
970 reg = samba.registry.Registry()
971 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
972 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
973 provision_reg = setup_path("provision.reg")
974 assert os.path.exists(provision_reg)
975 reg.diff_apply(provision_reg)
978 def setup_idmapdb(path, session_info, lp):
979 """Setup the idmap database.
981 :param path: path to the idmap database
982 :param session_info: Session information
983 :param credentials: Credentials
984 :param lp: Loadparm context
986 if os.path.exists(path):
987 os.unlink(path)
989 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
990 idmap_ldb.erase()
991 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
992 return idmap_ldb
995 def setup_samdb_rootdse(samdb, names):
996 """Setup the SamDB rootdse.
998 :param samdb: Sam Database handle
1000 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1001 "SCHEMADN": names.schemadn,
1002 "DOMAINDN": names.domaindn,
1003 "ROOTDN" : names.rootdn,
1004 "CONFIGDN": names.configdn,
1005 "SERVERDN": names.serverdn,
1009 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1010 dns_backend, dnspass, domainsid, next_rid, invocationid,
1011 policyguid, policyguid_dc,
1012 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1013 """Join a host to its own domain."""
1014 assert isinstance(invocationid, str)
1015 if ntdsguid is not None:
1016 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1017 else:
1018 ntdsguid_line = ""
1020 if dc_rid is None:
1021 dc_rid = next_rid
1023 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1024 "CONFIGDN": names.configdn,
1025 "SCHEMADN": names.schemadn,
1026 "DOMAINDN": names.domaindn,
1027 "SERVERDN": names.serverdn,
1028 "INVOCATIONID": invocationid,
1029 "NETBIOSNAME": names.netbiosname,
1030 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1031 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1032 "DOMAINSID": str(domainsid),
1033 "DCRID": str(dc_rid),
1034 "SAMBA_VERSION_STRING": version,
1035 "NTDSGUID": ntdsguid_line,
1036 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1037 domainControllerFunctionality),
1038 "RIDALLOCATIONSTART": str(next_rid + 100),
1039 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1041 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1042 "POLICYGUID": policyguid,
1043 "POLICYGUID_DC": policyguid_dc,
1044 "DNSDOMAIN": names.dnsdomain,
1045 "DOMAINDN": names.domaindn})
1047 # If we are setting up a subdomain, then this has been replicated in, so we
1048 # don't need to add it
1049 if fill == FILL_FULL:
1050 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1051 "CONFIGDN": names.configdn,
1052 "SCHEMADN": names.schemadn,
1053 "DOMAINDN": names.domaindn,
1054 "SERVERDN": names.serverdn,
1055 "INVOCATIONID": invocationid,
1056 "NETBIOSNAME": names.netbiosname,
1057 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1058 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1059 "DOMAINSID": str(domainsid),
1060 "DCRID": str(dc_rid),
1061 "SAMBA_VERSION_STRING": version,
1062 "NTDSGUID": ntdsguid_line,
1063 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1064 domainControllerFunctionality)})
1066 # Setup fSMORoleOwner entries to point at the newly created DC entry
1067 setup_modify_ldif(samdb,
1068 setup_path("provision_self_join_modify_config.ldif"), {
1069 "CONFIGDN": names.configdn,
1070 "SCHEMADN": names.schemadn,
1071 "DEFAULTSITE": names.sitename,
1072 "NETBIOSNAME": names.netbiosname,
1073 "SERVERDN": names.serverdn,
1076 system_session_info = system_session()
1077 samdb.set_session_info(system_session_info)
1078 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1079 # modify a serverReference under cn=config when we are a subdomain, we must
1080 # be system due to ACLs
1081 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1082 "DOMAINDN": names.domaindn,
1083 "SERVERDN": names.serverdn,
1084 "NETBIOSNAME": names.netbiosname,
1087 samdb.set_session_info(admin_session_info)
1089 if dns_backend != "SAMBA_INTERNAL":
1090 # This is Samba4 specific and should be replaced by the correct
1091 # DNS AD-style setup
1092 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1093 "DNSDOMAIN": names.dnsdomain,
1094 "DOMAINDN": names.domaindn,
1095 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1096 "HOSTNAME" : names.hostname,
1097 "DNSNAME" : '%s.%s' % (
1098 names.netbiosname.lower(), names.dnsdomain.lower())
1102 def getpolicypath(sysvolpath, dnsdomain, guid):
1103 """Return the physical path of policy given its guid.
1105 :param sysvolpath: Path to the sysvol folder
1106 :param dnsdomain: DNS name of the AD domain
1107 :param guid: The GUID of the policy
1108 :return: A string with the complete path to the policy folder
1110 if guid[0] != "{":
1111 guid = "{%s}" % guid
1112 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1113 return policy_path
1116 def create_gpo_struct(policy_path):
1117 if not os.path.exists(policy_path):
1118 os.makedirs(policy_path, 0775)
1119 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1120 try:
1121 f.write("[General]\r\nVersion=0")
1122 finally:
1123 f.close()
1124 p = os.path.join(policy_path, "MACHINE")
1125 if not os.path.exists(p):
1126 os.makedirs(p, 0775)
1127 p = os.path.join(policy_path, "USER")
1128 if not os.path.exists(p):
1129 os.makedirs(p, 0775)
1132 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1133 """Create the default GPO for a domain
1135 :param sysvolpath: Physical path for the sysvol folder
1136 :param dnsdomain: DNS domain name of the AD domain
1137 :param policyguid: GUID of the default domain policy
1138 :param policyguid_dc: GUID of the default domain controler policy
1140 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1141 create_gpo_struct(policy_path)
1143 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1144 create_gpo_struct(policy_path)
1147 def setup_samdb(path, session_info, provision_backend, lp, names,
1148 logger, fill, serverrole, schema, am_rodc=False):
1149 """Setup a complete SAM Database.
1151 :note: This will wipe the main SAM database file!
1154 # Also wipes the database
1155 setup_samdb_partitions(path, logger=logger, lp=lp,
1156 provision_backend=provision_backend, session_info=session_info,
1157 names=names, serverrole=serverrole, schema=schema)
1159 # Load the database, but don's load the global schema and don't connect
1160 # quite yet
1161 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1162 credentials=provision_backend.credentials, lp=lp,
1163 global_schema=False, am_rodc=am_rodc)
1165 logger.info("Pre-loading the Samba 4 and AD schema")
1167 # Load the schema from the one we computed earlier
1168 samdb.set_schema(schema, write_indices_and_attributes=False)
1170 # Set the NTDS settings DN manually - in order to have it already around
1171 # before the provisioned tree exists and we connect
1172 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1174 # And now we can connect to the DB - the schema won't be loaded from the
1175 # DB
1176 samdb.connect(path)
1178 # But we have to give it one more kick to have it use the schema
1179 # during provision - it needs, now that it is connected, to write
1180 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1181 samdb.set_schema(schema, write_indices_and_attributes=True)
1183 return samdb
1186 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1187 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1188 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1189 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1191 if next_rid is None:
1192 next_rid = 1000
1194 # Provision does not make much sense values larger than 1000000000
1195 # as the upper range of the rIDAvailablePool is 1073741823 and
1196 # we don't want to create a domain that cannot allocate rids.
1197 if next_rid < 1000 or next_rid > 1000000000:
1198 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1199 error += "the valid range is %u-%u. The default is %u." % (
1200 1000, 1000000000, 1000)
1201 raise ProvisioningError(error)
1203 # ATTENTION: Do NOT change these default values without discussion with the
1204 # team and/or release manager. They have a big impact on the whole program!
1205 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1207 if dom_for_fun_level is None:
1208 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1210 if dom_for_fun_level > domainControllerFunctionality:
1211 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!")
1213 domainFunctionality = dom_for_fun_level
1214 forestFunctionality = dom_for_fun_level
1216 # Set the NTDS settings DN manually - in order to have it already around
1217 # before the provisioned tree exists and we connect
1218 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1220 samdb.transaction_start()
1221 try:
1222 # Set the domain functionality levels onto the database.
1223 # Various module (the password_hash module in particular) need
1224 # to know what level of AD we are emulating.
1226 # These will be fixed into the database via the database
1227 # modifictions below, but we need them set from the start.
1228 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1229 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1230 samdb.set_opaque_integer("domainControllerFunctionality",
1231 domainControllerFunctionality)
1233 samdb.set_domain_sid(str(domainsid))
1234 samdb.set_invocation_id(invocationid)
1236 logger.info("Adding DomainDN: %s" % names.domaindn)
1238 # impersonate domain admin
1239 admin_session_info = admin_session(lp, str(domainsid))
1240 samdb.set_session_info(admin_session_info)
1241 if domainguid is not None:
1242 domainguid_line = "objectGUID: %s\n-" % domainguid
1243 else:
1244 domainguid_line = ""
1246 descr = b64encode(get_domain_descriptor(domainsid))
1247 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1248 "DOMAINDN": names.domaindn,
1249 "DOMAINSID": str(domainsid),
1250 "DESCRIPTOR": descr,
1251 "DOMAINGUID": domainguid_line
1254 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1255 "DOMAINDN": names.domaindn,
1256 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1257 "NEXTRID": str(next_rid),
1258 "DEFAULTSITE": names.sitename,
1259 "CONFIGDN": names.configdn,
1260 "POLICYGUID": policyguid,
1261 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1262 "SAMBA_VERSION_STRING": version
1265 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1266 if fill == FILL_FULL:
1267 logger.info("Adding configuration container")
1268 descr = b64encode(get_config_descriptor(domainsid))
1269 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1270 "CONFIGDN": names.configdn,
1271 "DESCRIPTOR": descr,
1274 # The LDIF here was created when the Schema object was constructed
1275 logger.info("Setting up sam.ldb schema")
1276 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1277 samdb.modify_ldif(schema.schema_dn_modify)
1278 samdb.write_prefixes_from_schema()
1279 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1280 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1281 {"SCHEMADN": names.schemadn})
1283 # Now register this container in the root of the forest
1284 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1285 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1286 "subRefs")
1288 except:
1289 samdb.transaction_cancel()
1290 raise
1291 else:
1292 samdb.transaction_commit()
1294 samdb.transaction_start()
1295 try:
1296 samdb.invocation_id = invocationid
1298 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1299 if fill == FILL_FULL:
1300 logger.info("Setting up sam.ldb configuration data")
1301 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1302 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1303 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1304 "CONFIGDN": names.configdn,
1305 "NETBIOSNAME": names.netbiosname,
1306 "DEFAULTSITE": names.sitename,
1307 "DNSDOMAIN": names.dnsdomain,
1308 "DOMAIN": names.domain,
1309 "SCHEMADN": names.schemadn,
1310 "DOMAINDN": names.domaindn,
1311 "SERVERDN": names.serverdn,
1312 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1313 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1314 "PARTITIONS_DESCRIPTOR": partitions_descr,
1315 "SITES_DESCRIPTOR": sites_descr,
1318 logger.info("Setting up display specifiers")
1319 display_specifiers_ldif = read_ms_ldif(
1320 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1321 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1322 {"CONFIGDN": names.configdn})
1323 check_all_substituted(display_specifiers_ldif)
1324 samdb.add_ldif(display_specifiers_ldif)
1326 logger.info("Adding users container")
1327 users_desc = b64encode(get_domain_users_descriptor(domainsid))
1328 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1329 "DOMAINDN": names.domaindn,
1330 "USERS_DESCRIPTOR": users_desc
1332 logger.info("Modifying users container")
1333 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1334 "DOMAINDN": names.domaindn})
1335 logger.info("Adding computers container")
1336 computers_desc = b64encode(get_domain_computers_descriptor(domainsid))
1337 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1338 "DOMAINDN": names.domaindn,
1339 "COMPUTERS_DESCRIPTOR": computers_desc
1341 logger.info("Modifying computers container")
1342 setup_modify_ldif(samdb,
1343 setup_path("provision_computers_modify.ldif"), {
1344 "DOMAINDN": names.domaindn})
1345 logger.info("Setting up sam.ldb data")
1346 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
1347 builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
1348 controllers_desc = b64encode(get_domain_controllers_descriptor(domainsid))
1349 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1350 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1351 "DOMAINDN": names.domaindn,
1352 "NETBIOSNAME": names.netbiosname,
1353 "DEFAULTSITE": names.sitename,
1354 "CONFIGDN": names.configdn,
1355 "SERVERDN": names.serverdn,
1356 "RIDAVAILABLESTART": str(next_rid + 600),
1357 "POLICYGUID_DC": policyguid_dc,
1358 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1359 "BUILTIN_DESCRIPTOR": builtin_desc,
1360 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1363 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1364 if fill == FILL_FULL:
1365 setup_modify_ldif(samdb,
1366 setup_path("provision_configuration_references.ldif"), {
1367 "CONFIGDN": names.configdn,
1368 "SCHEMADN": names.schemadn})
1370 logger.info("Setting up well known security principals")
1371 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1372 "CONFIGDN": names.configdn,
1375 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1376 setup_modify_ldif(samdb,
1377 setup_path("provision_basedn_references.ldif"),
1378 {"DOMAINDN": names.domaindn})
1380 logger.info("Setting up sam.ldb users and groups")
1381 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1382 "DOMAINDN": names.domaindn,
1383 "DOMAINSID": str(domainsid),
1384 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1385 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1388 logger.info("Setting up self join")
1389 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1390 invocationid=invocationid,
1391 dns_backend=dns_backend,
1392 dnspass=dnspass,
1393 machinepass=machinepass,
1394 domainsid=domainsid,
1395 next_rid=next_rid,
1396 dc_rid=dc_rid,
1397 policyguid=policyguid,
1398 policyguid_dc=policyguid_dc,
1399 domainControllerFunctionality=domainControllerFunctionality,
1400 ntdsguid=ntdsguid)
1402 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1403 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1404 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1405 assert isinstance(names.ntdsguid, str)
1406 except:
1407 samdb.transaction_cancel()
1408 raise
1409 else:
1410 samdb.transaction_commit()
1411 return samdb
1414 FILL_FULL = "FULL"
1415 FILL_SUBDOMAIN = "SUBDOMAIN"
1416 FILL_NT4SYNC = "NT4SYNC"
1417 FILL_DRS = "DRS"
1418 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1419 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)"
1420 SYSVOL_SERVICE="sysvol"
1422 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1423 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1424 for root, dirs, files in os.walk(path, topdown=False):
1425 for name in files:
1426 setntacl(lp, os.path.join(root, name), acl, domsid,
1427 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1428 for name in dirs:
1429 setntacl(lp, os.path.join(root, name), acl, domsid,
1430 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1433 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1434 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1435 folders beneath.
1437 :param sysvol: Physical path for the sysvol folder
1438 :param dnsdomain: The DNS name of the domain
1439 :param domainsid: The SID of the domain
1440 :param domaindn: The DN of the domain (ie. DC=...)
1441 :param samdb: An LDB object on the SAM db
1442 :param lp: an LP object
1445 # Set ACL for GPO root folder
1446 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1447 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1448 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1450 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1451 attrs=["cn", "nTSecurityDescriptor"],
1452 expression="", scope=ldb.SCOPE_ONELEVEL)
1454 for policy in res:
1455 acl = ndr_unpack(security.descriptor,
1456 str(policy["nTSecurityDescriptor"])).as_sddl()
1457 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1458 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1459 str(domainsid), use_ntvfs,
1460 passdb=passdb)
1463 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1464 domaindn, lp, use_ntvfs):
1465 """Set the ACL for the sysvol share and the subfolders
1467 :param samdb: An LDB object on the SAM db
1468 :param netlogon: Physical path for the netlogon folder
1469 :param sysvol: Physical path for the sysvol folder
1470 :param uid: The UID of the "Administrator" user
1471 :param gid: The GID of the "Domain adminstrators" group
1472 :param domainsid: The SID of the domain
1473 :param dnsdomain: The DNS name of the domain
1474 :param domaindn: The DN of the domain (ie. DC=...)
1476 s4_passdb = None
1478 if not use_ntvfs:
1479 # This will ensure that the smbd code we are running when setting ACLs
1480 # is initialised with the smb.conf
1481 s3conf = s3param.get_context()
1482 s3conf.load(lp.configfile)
1483 # ensure we are using the right samba_dsdb passdb backend, no matter what
1484 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1485 passdb.reload_static_pdb()
1487 # ensure that we init the samba_dsdb backend, so the domain sid is
1488 # marked in secrets.tdb
1489 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1491 # now ensure everything matches correctly, to avoid wierd issues
1492 if passdb.get_global_sam_sid() != domainsid:
1493 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))
1495 domain_info = s4_passdb.domain_info()
1496 if domain_info["dom_sid"] != domainsid:
1497 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))
1499 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1500 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()))
1503 try:
1504 if use_ntvfs:
1505 os.chown(sysvol, -1, gid)
1506 except OSError:
1507 canchown = False
1508 else:
1509 canchown = True
1511 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1512 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1513 skip_invalid_chown=True, passdb=s4_passdb,
1514 service=SYSVOL_SERVICE)
1515 for root, dirs, files in os.walk(sysvol, topdown=False):
1516 for name in files:
1517 if use_ntvfs and canchown:
1518 os.chown(os.path.join(root, name), -1, gid)
1519 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1520 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1521 passdb=s4_passdb, service=SYSVOL_SERVICE)
1522 for name in dirs:
1523 if use_ntvfs and canchown:
1524 os.chown(os.path.join(root, name), -1, gid)
1525 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1526 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1527 passdb=s4_passdb, service=SYSVOL_SERVICE)
1529 # Set acls on Policy folder and policies folders
1530 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1532 def acl_type(direct_db_access):
1533 if direct_db_access:
1534 return "DB"
1535 else:
1536 return "VFS"
1538 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1539 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1540 fsacl_sddl = fsacl.as_sddl(domainsid)
1541 if fsacl_sddl != acl:
1542 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))
1544 for root, dirs, files in os.walk(path, topdown=False):
1545 for name in files:
1546 fsacl = getntacl(lp, os.path.join(root, name),
1547 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1548 if fsacl is None:
1549 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1550 fsacl_sddl = fsacl.as_sddl(domainsid)
1551 if fsacl_sddl != acl:
1552 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))
1554 for name in dirs:
1555 fsacl = getntacl(lp, os.path.join(root, name),
1556 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1557 if fsacl is None:
1558 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1559 fsacl_sddl = fsacl.as_sddl(domainsid)
1560 if fsacl_sddl != acl:
1561 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))
1564 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1565 direct_db_access):
1566 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1567 folders beneath.
1569 :param sysvol: Physical path for the sysvol folder
1570 :param dnsdomain: The DNS name of the domain
1571 :param domainsid: The SID of the domain
1572 :param domaindn: The DN of the domain (ie. DC=...)
1573 :param samdb: An LDB object on the SAM db
1574 :param lp: an LP object
1577 # Set ACL for GPO root folder
1578 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1579 fsacl = getntacl(lp, root_policy_path,
1580 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1581 if fsacl is None:
1582 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1583 fsacl_sddl = fsacl.as_sddl(domainsid)
1584 if fsacl_sddl != POLICIES_ACL:
1585 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))
1586 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1587 attrs=["cn", "nTSecurityDescriptor"],
1588 expression="", scope=ldb.SCOPE_ONELEVEL)
1590 for policy in res:
1591 acl = ndr_unpack(security.descriptor,
1592 str(policy["nTSecurityDescriptor"])).as_sddl()
1593 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1594 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1595 domainsid, direct_db_access)
1598 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1599 lp):
1600 """Set the ACL for the sysvol share and the subfolders
1602 :param samdb: An LDB object on the SAM db
1603 :param netlogon: Physical path for the netlogon folder
1604 :param sysvol: Physical path for the sysvol folder
1605 :param uid: The UID of the "Administrator" user
1606 :param gid: The GID of the "Domain adminstrators" group
1607 :param domainsid: The SID of the domain
1608 :param dnsdomain: The DNS name of the domain
1609 :param domaindn: The DN of the domain (ie. DC=...)
1612 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1613 s3conf = s3param.get_context()
1614 s3conf.load(lp.configfile)
1615 # ensure we are using the right samba_dsdb passdb backend, no matter what
1616 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1617 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1618 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1620 # now ensure everything matches correctly, to avoid wierd issues
1621 if passdb.get_global_sam_sid() != domainsid:
1622 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))
1624 domain_info = s4_passdb.domain_info()
1625 if domain_info["dom_sid"] != domainsid:
1626 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))
1628 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1629 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()))
1631 # Ensure we can read this directly, and via the smbd VFS
1632 for direct_db_access in [True, False]:
1633 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1634 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1635 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1636 if fsacl is None:
1637 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1638 fsacl_sddl = fsacl.as_sddl(domainsid)
1639 if fsacl_sddl != SYSVOL_ACL:
1640 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))
1642 # Check acls on Policy folder and policies folders
1643 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1644 direct_db_access)
1647 def interface_ips_v4(lp):
1648 """return only IPv4 IPs"""
1649 ips = samba.interface_ips(lp, False)
1650 ret = []
1651 for i in ips:
1652 if i.find(':') == -1:
1653 ret.append(i)
1654 return ret
1657 def interface_ips_v6(lp, linklocal=False):
1658 """return only IPv6 IPs"""
1659 ips = samba.interface_ips(lp, False)
1660 ret = []
1661 for i in ips:
1662 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1663 ret.append(i)
1664 return ret
1667 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1668 domainsid, schema=None,
1669 targetdir=None, samdb_fill=FILL_FULL,
1670 hostip=None, hostip6=None,
1671 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1672 domainguid=None, policyguid=None, policyguid_dc=None,
1673 invocationid=None, machinepass=None, ntdsguid=None,
1674 dns_backend=None, dnspass=None,
1675 serverrole=None, dom_for_fun_level=None,
1676 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1677 # create/adapt the group policy GUIDs
1678 # Default GUID for default policy are described at
1679 # "How Core Group Policy Works"
1680 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1681 if policyguid is None:
1682 policyguid = DEFAULT_POLICY_GUID
1683 policyguid = policyguid.upper()
1684 if policyguid_dc is None:
1685 policyguid_dc = DEFAULT_DC_POLICY_GUID
1686 policyguid_dc = policyguid_dc.upper()
1688 if invocationid is None:
1689 invocationid = str(uuid.uuid4())
1691 if krbtgtpass is None:
1692 krbtgtpass = samba.generate_random_password(128, 255)
1693 if machinepass is None:
1694 machinepass = samba.generate_random_password(128, 255)
1695 if dnspass is None:
1696 dnspass = samba.generate_random_password(128, 255)
1698 samdb = fill_samdb(samdb, lp, names, logger=logger,
1699 domainsid=domainsid, schema=schema, domainguid=domainguid,
1700 policyguid=policyguid, policyguid_dc=policyguid_dc,
1701 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1702 invocationid=invocationid, machinepass=machinepass,
1703 dns_backend=dns_backend, dnspass=dnspass,
1704 ntdsguid=ntdsguid, serverrole=serverrole,
1705 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1706 next_rid=next_rid, dc_rid=dc_rid)
1708 if serverrole == "active directory domain controller":
1710 # Set up group policies (domain policy and domain controller
1711 # policy)
1712 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1713 policyguid_dc)
1714 if not skip_sysvolacl:
1715 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1716 paths.root_gid, domainsid, names.dnsdomain,
1717 names.domaindn, lp, use_ntvfs)
1718 else:
1719 logger.info("Setting acl on sysvol skipped")
1721 secretsdb_self_join(secrets_ldb, domain=names.domain,
1722 realm=names.realm, dnsdomain=names.dnsdomain,
1723 netbiosname=names.netbiosname, domainsid=domainsid,
1724 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1726 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1727 # In future, this might be determined from some configuration
1728 kerberos_enctypes = str(ENC_ALL_TYPES)
1730 try:
1731 msg = ldb.Message(ldb.Dn(samdb,
1732 samdb.searchone("distinguishedName",
1733 expression="samAccountName=%s$" % names.netbiosname,
1734 scope=ldb.SCOPE_SUBTREE)))
1735 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1736 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1737 name="msDS-SupportedEncryptionTypes")
1738 samdb.modify(msg)
1739 except ldb.LdbError, (enum, estr):
1740 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1741 # It might be that this attribute does not exist in this schema
1742 raise
1744 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1745 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1746 dnspass=dnspass, os_level=dom_for_fun_level,
1747 targetdir=targetdir, site=DEFAULTSITE)
1749 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1750 attribute="objectGUID")
1751 assert isinstance(domainguid, str)
1753 lastProvisionUSNs = get_last_provision_usn(samdb)
1754 maxUSN = get_max_usn(samdb, str(names.rootdn))
1755 if lastProvisionUSNs is not None:
1756 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1757 else:
1758 set_provision_usn(samdb, 0, maxUSN, invocationid)
1760 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1761 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1762 { 'NTDSGUID' : names.ntdsguid })
1764 # fix any dangling GUIDs from the provision
1765 logger.info("Fixing provision GUIDs")
1766 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1767 quiet=True)
1768 samdb.transaction_start()
1769 try:
1770 # a small number of GUIDs are missing because of ordering issues in the
1771 # provision code
1772 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1773 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1774 scope=ldb.SCOPE_BASE,
1775 attrs=['defaultObjectCategory'])
1776 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1777 scope=ldb.SCOPE_ONELEVEL,
1778 attrs=['ipsecOwnersReference',
1779 'ipsecFilterReference',
1780 'ipsecISAKMPReference',
1781 'ipsecNegotiationPolicyReference',
1782 'ipsecNFAReference'])
1783 except:
1784 samdb.transaction_cancel()
1785 raise
1786 else:
1787 samdb.transaction_commit()
1790 _ROLES_MAP = {
1791 "ROLE_STANDALONE": "standalone server",
1792 "ROLE_DOMAIN_MEMBER": "member server",
1793 "ROLE_DOMAIN_BDC": "active directory domain controller",
1794 "ROLE_DOMAIN_PDC": "active directory domain controller",
1795 "dc": "active directory domain controller",
1796 "member": "member server",
1797 "domain controller": "active directory domain controller",
1798 "active directory domain controller": "active directory domain controller",
1799 "member server": "member server",
1800 "standalone": "standalone server",
1801 "standalone server": "standalone server",
1805 def sanitize_server_role(role):
1806 """Sanitize a server role name.
1808 :param role: Server role
1809 :raise ValueError: If the role can not be interpreted
1810 :return: Sanitized server role (one of "member server",
1811 "active directory domain controller", "standalone server")
1813 try:
1814 return _ROLES_MAP[role]
1815 except KeyError:
1816 raise ValueError(role)
1819 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1820 maxuid, maxgid):
1821 """Create AD entries for the fake ypserver.
1823 This is needed for being able to manipulate posix attrs via ADUC.
1825 samdb.transaction_start()
1826 try:
1827 logger.info("Setting up fake yp server settings")
1828 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1829 "DOMAINDN": domaindn,
1830 "NETBIOSNAME": netbiosname,
1831 "NISDOMAIN": nisdomain,
1833 except:
1834 samdb.transaction_cancel()
1835 raise
1836 else:
1837 samdb.transaction_commit()
1840 def provision(logger, session_info, credentials, smbconf=None,
1841 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1842 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1843 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1844 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1845 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1846 dns_backend=None, dns_forwarder=None, dnspass=None,
1847 invocationid=None, machinepass=None, ntdsguid=None,
1848 root=None, nobody=None, users=None, backup=None, aci=None,
1849 serverrole=None, dom_for_fun_level=None, backend_type=None,
1850 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1851 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1852 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1853 """Provision samba4
1855 :note: caution, this wipes all existing data!
1858 try:
1859 serverrole = sanitize_server_role(serverrole)
1860 except ValueError:
1861 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1863 if ldapadminpass is None:
1864 # Make a new, random password between Samba and it's LDAP server
1865 ldapadminpass = samba.generate_random_password(128, 255)
1867 if backend_type is None:
1868 backend_type = "ldb"
1870 if domainsid is None:
1871 domainsid = security.random_sid()
1872 else:
1873 domainsid = security.dom_sid(domainsid)
1875 root_uid = findnss_uid([root or "root"])
1876 nobody_uid = findnss_uid([nobody or "nobody"])
1877 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1878 root_gid = pwd.getpwuid(root_uid).pw_gid
1880 try:
1881 bind_gid = findnss_gid(["bind", "named"])
1882 except KeyError:
1883 bind_gid = None
1885 if targetdir is not None:
1886 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1887 elif smbconf is None:
1888 smbconf = samba.param.default_path()
1889 if not os.path.exists(os.path.dirname(smbconf)):
1890 os.makedirs(os.path.dirname(smbconf))
1892 server_services = []
1893 global_param = {}
1894 if use_rfc2307:
1895 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1897 if dns_backend != "SAMBA_INTERNAL":
1898 server_services.append("-dns")
1899 else:
1900 if dns_forwarder is not None:
1901 global_param["dns forwarder"] = [dns_forwarder]
1903 if use_ntvfs:
1904 server_services.append("+smb")
1905 server_services.append("-s3fs")
1906 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1908 if len(server_services) > 0:
1909 global_param["server services"] = server_services
1911 # only install a new smb.conf if there isn't one there already
1912 if os.path.exists(smbconf):
1913 # if Samba Team members can't figure out the weird errors
1914 # loading an empty smb.conf gives, then we need to be smarter.
1915 # Pretend it just didn't exist --abartlet
1916 f = open(smbconf, 'r')
1917 try:
1918 data = f.read().lstrip()
1919 finally:
1920 f.close()
1921 if data is None or data == "":
1922 make_smbconf(smbconf, hostname, domain, realm,
1923 targetdir, serverrole=serverrole,
1924 eadb=useeadb, use_ntvfs=use_ntvfs,
1925 lp=lp, global_param=global_param)
1926 else:
1927 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1928 serverrole=serverrole,
1929 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1931 if lp is None:
1932 lp = samba.param.LoadParm()
1933 lp.load(smbconf)
1934 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1935 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1936 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1937 sitename=sitename, rootdn=rootdn)
1938 paths = provision_paths_from_lp(lp, names.dnsdomain)
1940 paths.bind_gid = bind_gid
1941 paths.root_uid = root_uid;
1942 paths.root_gid = root_gid
1944 if hostip is None:
1945 logger.info("Looking up IPv4 addresses")
1946 hostips = interface_ips_v4(lp)
1947 if len(hostips) > 0:
1948 hostip = hostips[0]
1949 if len(hostips) > 1:
1950 logger.warning("More than one IPv4 address found. Using %s",
1951 hostip)
1952 if hostip == "127.0.0.1":
1953 hostip = None
1954 if hostip is None:
1955 logger.warning("No IPv4 address will be assigned")
1957 if hostip6 is None:
1958 logger.info("Looking up IPv6 addresses")
1959 hostips = interface_ips_v6(lp, linklocal=False)
1960 if hostips:
1961 hostip6 = hostips[0]
1962 if len(hostips) > 1:
1963 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1964 if hostip6 is None:
1965 logger.warning("No IPv6 address will be assigned")
1967 names.hostip = hostip
1968 names.hostip6 = hostip6
1970 if serverrole is None:
1971 serverrole = lp.get("server role")
1973 if not os.path.exists(paths.private_dir):
1974 os.mkdir(paths.private_dir)
1975 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1976 os.mkdir(os.path.join(paths.private_dir, "tls"))
1977 if not os.path.exists(paths.state_dir):
1978 os.mkdir(paths.state_dir)
1980 if paths.sysvol and not os.path.exists(paths.sysvol):
1981 os.makedirs(paths.sysvol, 0775)
1983 if not use_ntvfs and serverrole == "active directory domain controller":
1984 s3conf = s3param.get_context()
1985 s3conf.load(lp.configfile)
1987 if paths.sysvol is None:
1988 raise MissingShareError("sysvol", paths.smbconf)
1990 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1991 try:
1992 try:
1993 smbd.set_simple_acl(file.name, 0755, root_gid)
1994 except Exception:
1995 if not smbd.have_posix_acls():
1996 # This clue is only strictly correct for RPM and
1997 # Debian-like Linux systems, but hopefully other users
1998 # will get enough clue from it.
1999 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.")
2001 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
2002 try:
2003 smbd.chown(file.name, root_uid, root_gid)
2004 except Exception:
2005 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
2006 finally:
2007 file.close()
2009 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2011 schema = Schema(domainsid, invocationid=invocationid,
2012 schemadn=names.schemadn)
2014 if backend_type == "ldb":
2015 provision_backend = LDBBackend(backend_type, paths=paths,
2016 lp=lp, credentials=credentials,
2017 names=names, logger=logger)
2018 elif backend_type == "existing":
2019 # If support for this is ever added back, then the URI will need to be
2020 # specified again
2021 provision_backend = ExistingBackend(backend_type, paths=paths,
2022 lp=lp, credentials=credentials,
2023 names=names, logger=logger,
2024 ldap_backend_forced_uri=None)
2025 elif backend_type == "fedora-ds":
2026 provision_backend = FDSBackend(backend_type, paths=paths,
2027 lp=lp, credentials=credentials,
2028 names=names, logger=logger, domainsid=domainsid,
2029 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2030 slapd_path=slapd_path,
2031 root=root)
2032 elif backend_type == "openldap":
2033 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2034 lp=lp, credentials=credentials,
2035 names=names, logger=logger, domainsid=domainsid,
2036 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2037 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
2038 else:
2039 raise ValueError("Unknown LDAP backend type selected")
2041 provision_backend.init()
2042 provision_backend.start()
2044 # only install a new shares config db if there is none
2045 if not os.path.exists(paths.shareconf):
2046 logger.info("Setting up share.ldb")
2047 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2048 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2050 logger.info("Setting up secrets.ldb")
2051 secrets_ldb = setup_secretsdb(paths,
2052 session_info=session_info,
2053 backend_credentials=provision_backend.secrets_credentials, lp=lp)
2055 try:
2056 logger.info("Setting up the registry")
2057 setup_registry(paths.hklm, session_info, lp=lp)
2059 logger.info("Setting up the privileges database")
2060 setup_privileges(paths.privilege, session_info, lp=lp)
2062 logger.info("Setting up idmap db")
2063 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2065 setup_name_mappings(idmap, sid=str(domainsid),
2066 root_uid=root_uid, nobody_uid=nobody_uid,
2067 users_gid=users_gid, root_gid=root_gid)
2069 logger.info("Setting up SAM db")
2070 samdb = setup_samdb(paths.samdb, session_info,
2071 provision_backend, lp, names, logger=logger,
2072 serverrole=serverrole,
2073 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2075 if serverrole == "active directory domain controller":
2076 if paths.netlogon is None:
2077 raise MissingShareError("netlogon", paths.smbconf)
2079 if paths.sysvol is None:
2080 raise MissingShareError("sysvol", paths.smbconf)
2082 if not os.path.isdir(paths.netlogon):
2083 os.makedirs(paths.netlogon, 0755)
2085 if adminpass is None:
2086 adminpass = samba.generate_random_password(12, 32)
2087 adminpass_generated = True
2088 else:
2089 adminpass_generated = False
2091 if samdb_fill == FILL_FULL:
2092 provision_fill(samdb, secrets_ldb, logger, names, paths,
2093 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2094 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2095 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2096 krbtgtpass=krbtgtpass, domainguid=domainguid,
2097 policyguid=policyguid, policyguid_dc=policyguid_dc,
2098 invocationid=invocationid, machinepass=machinepass,
2099 ntdsguid=ntdsguid, dns_backend=dns_backend,
2100 dnspass=dnspass, serverrole=serverrole,
2101 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2102 lp=lp, use_ntvfs=use_ntvfs,
2103 skip_sysvolacl=skip_sysvolacl)
2105 create_krb5_conf(paths.krb5conf,
2106 dnsdomain=names.dnsdomain, hostname=names.hostname,
2107 realm=names.realm)
2108 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2109 "generated at %s", paths.krb5conf)
2111 if serverrole == "active directory domain controller":
2112 create_dns_update_list(lp, logger, paths)
2114 backend_result = provision_backend.post_setup()
2115 provision_backend.shutdown()
2117 except:
2118 secrets_ldb.transaction_cancel()
2119 raise
2121 # Now commit the secrets.ldb to disk
2122 secrets_ldb.transaction_commit()
2124 # the commit creates the dns.keytab, now chown it
2125 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2126 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2127 try:
2128 os.chmod(dns_keytab_path, 0640)
2129 os.chown(dns_keytab_path, -1, paths.bind_gid)
2130 except OSError:
2131 if not os.environ.has_key('SAMBA_SELFTEST'):
2132 logger.info("Failed to chown %s to bind gid %u",
2133 dns_keytab_path, paths.bind_gid)
2135 result = ProvisionResult()
2136 result.server_role = serverrole
2137 result.domaindn = domaindn
2138 result.paths = paths
2139 result.names = names
2140 result.lp = lp
2141 result.samdb = samdb
2142 result.idmap = idmap
2143 result.domainsid = str(domainsid)
2145 if samdb_fill == FILL_FULL:
2146 result.adminpass_generated = adminpass_generated
2147 result.adminpass = adminpass
2148 else:
2149 result.adminpass_generated = False
2150 result.adminpass = None
2152 result.backend_result = backend_result
2154 if use_rfc2307:
2155 provision_fake_ypserver(logger=logger, samdb=samdb,
2156 domaindn=names.domaindn, netbiosname=names.netbiosname,
2157 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2159 return result
2162 def provision_become_dc(smbconf=None, targetdir=None,
2163 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2164 serverdn=None, domain=None, hostname=None, domainsid=None,
2165 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2166 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2167 dns_backend=None, root=None, nobody=None, users=None,
2168 backup=None, serverrole=None, ldap_backend=None,
2169 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2171 logger = logging.getLogger("provision")
2172 samba.set_debug_level(debuglevel)
2174 res = provision(logger, system_session(), None,
2175 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2176 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2177 configdn=configdn, serverdn=serverdn, domain=domain,
2178 hostname=hostname, hostip=None, domainsid=domainsid,
2179 machinepass=machinepass,
2180 serverrole="active directory domain controller",
2181 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2182 use_ntvfs=use_ntvfs)
2183 res.lp.set("debuglevel", str(debuglevel))
2184 return res
2187 def create_krb5_conf(path, dnsdomain, hostname, realm):
2188 """Write out a file containing zone statements suitable for inclusion in a
2189 named.conf file (including GSS-TSIG configuration).
2191 :param path: Path of the new named.conf file.
2192 :param dnsdomain: DNS Domain name
2193 :param hostname: Local hostname
2194 :param realm: Realm name
2196 setup_file(setup_path("krb5.conf"), path, {
2197 "DNSDOMAIN": dnsdomain,
2198 "HOSTNAME": hostname,
2199 "REALM": realm,
2203 class ProvisioningError(Exception):
2204 """A generic provision error."""
2206 def __init__(self, value):
2207 self.value = value
2209 def __str__(self):
2210 return "ProvisioningError: " + self.value
2213 class InvalidNetbiosName(Exception):
2214 """A specified name was not a valid NetBIOS name."""
2216 def __init__(self, name):
2217 super(InvalidNetbiosName, self).__init__(
2218 "The name '%r' is not a valid NetBIOS name" % name)
2221 class MissingShareError(ProvisioningError):
2223 def __init__(self, name, smbconf):
2224 super(MissingShareError, self).__init__(
2225 "Existing smb.conf does not have a [%s] share, but you are "
2226 "configuring a DC. Please remove %s or add the share manually." %
2227 (name, smbconf))