provision: import/export get_dns_partition_descriptor()
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision / __init__.py
blob169b2d912a1922bd45f464303d723b391597339a
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_domain_descriptor,
85 get_domain_infrastructure_descriptor,
86 get_domain_builtin_descriptor,
87 get_domain_computers_descriptor,
88 get_domain_users_descriptor,
89 get_domain_controllers_descriptor,
90 get_dns_partition_descriptor,
92 from samba.provision.common import (
93 setup_path,
94 setup_add_ldif,
95 setup_modify_ldif,
97 from samba.provision.sambadns import (
98 setup_ad_dns,
99 create_dns_update_list
102 import samba.param
103 import samba.registry
104 from samba.schema import Schema
105 from samba.samdb import SamDB
106 from samba.dbchecker import dbcheck
109 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
110 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
111 DEFAULTSITE = "Default-First-Site-Name"
112 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
115 class ProvisionPaths(object):
117 def __init__(self):
118 self.shareconf = None
119 self.hklm = None
120 self.hkcu = None
121 self.hkcr = None
122 self.hku = None
123 self.hkpd = None
124 self.hkpt = None
125 self.samdb = None
126 self.idmapdb = None
127 self.secrets = None
128 self.keytab = None
129 self.dns_keytab = None
130 self.dns = None
131 self.winsdb = None
132 self.private_dir = None
133 self.state_dir = None
136 class ProvisionNames(object):
138 def __init__(self):
139 self.ncs = None
140 self.rootdn = None
141 self.domaindn = None
142 self.configdn = None
143 self.schemadn = None
144 self.dnsforestdn = None
145 self.dnsdomaindn = None
146 self.ldapmanagerdn = None
147 self.dnsdomain = None
148 self.realm = None
149 self.netbiosname = None
150 self.domain = None
151 self.hostname = None
152 self.sitename = None
153 self.smbconf = None
156 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
157 lp):
158 """Get key provision parameters (realm, domain, ...) from a given provision
160 :param samdb: An LDB object connected to the sam.ldb file
161 :param secretsdb: An LDB object connected to the secrets.ldb file
162 :param idmapdb: An LDB object connected to the idmap.ldb file
163 :param paths: A list of path to provision object
164 :param smbconf: Path to the smb.conf file
165 :param lp: A LoadParm object
166 :return: A list of key provision parameters
168 names = ProvisionNames()
169 names.adminpass = None
171 # NT domain, kerberos realm, root dn, domain dn, domain dns name
172 names.domain = string.upper(lp.get("workgroup"))
173 names.realm = lp.get("realm")
174 names.dnsdomain = names.realm.lower()
175 basedn = samba.dn_from_dns_name(names.dnsdomain)
176 names.realm = string.upper(names.realm)
177 # netbiosname
178 # Get the netbiosname first (could be obtained from smb.conf in theory)
179 res = secretsdb.search(expression="(flatname=%s)" %
180 names.domain,base="CN=Primary Domains",
181 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
182 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
184 names.smbconf = smbconf
186 # That's a bit simplistic but it's ok as long as we have only 3
187 # partitions
188 current = samdb.search(expression="(objectClass=*)",
189 base="", scope=ldb.SCOPE_BASE,
190 attrs=["defaultNamingContext", "schemaNamingContext",
191 "configurationNamingContext","rootDomainNamingContext",
192 "namingContexts"])
194 names.configdn = current[0]["configurationNamingContext"]
195 configdn = str(names.configdn)
196 names.schemadn = current[0]["schemaNamingContext"]
197 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
198 current[0]["defaultNamingContext"][0]))):
199 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
200 "is not the same ..." % (paths.samdb,
201 str(current[0]["defaultNamingContext"][0]),
202 paths.smbconf, basedn)))
204 names.domaindn=current[0]["defaultNamingContext"]
205 names.rootdn=current[0]["rootDomainNamingContext"]
206 names.ncs=current[0]["namingContexts"]
207 names.dnsforestdn = None
208 names.dnsdomaindn = None
210 for i in range(0, len(names.ncs)):
211 nc = names.ncs[i]
213 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
214 if nc == dnsforestdn:
215 names.dnsforestdn = dnsforestdn
216 continue
218 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
219 if nc == dnsdomaindn:
220 names.dnsdomaindn = dnsdomaindn
221 continue
223 # default site name
224 res3 = samdb.search(expression="(objectClass=site)",
225 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
226 names.sitename = str(res3[0]["cn"])
228 # dns hostname and server dn
229 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
230 base="OU=Domain Controllers,%s" % basedn,
231 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
232 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
234 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
235 attrs=[], base=configdn)
236 names.serverdn = server_res[0].dn
238 # invocation id/objectguid
239 res5 = samdb.search(expression="(objectClass=*)",
240 base="CN=NTDS Settings,%s" % str(names.serverdn),
241 scope=ldb.SCOPE_BASE,
242 attrs=["invocationID", "objectGUID"])
243 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
244 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
246 # domain guid/sid
247 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
248 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
249 "objectSid","msDS-Behavior-Version" ])
250 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
251 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
252 if res6[0].get("msDS-Behavior-Version") is None or \
253 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
254 names.domainlevel = DS_DOMAIN_FUNCTION_2000
255 else:
256 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
258 # policy guid
259 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
260 base="CN=Policies,CN=System," + basedn,
261 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
262 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
263 # dc policy guid
264 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
265 " Policy)",
266 base="CN=Policies,CN=System," + basedn,
267 scope=ldb.SCOPE_ONELEVEL,
268 attrs=["cn","displayName"])
269 if len(res8) == 1:
270 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
271 else:
272 names.policyid_dc = None
274 res9 = idmapdb.search(expression="(cn=%s-%s)" %
275 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
276 attrs=["xidNumber", "type"])
277 if len(res9) != 1:
278 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
279 if res9[0]["type"][0] == "ID_TYPE_BOTH":
280 names.root_gid = res9[0]["xidNumber"][0]
281 else:
282 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
283 return names
286 def update_provision_usn(samdb, low, high, id, replace=False):
287 """Update the field provisionUSN in sam.ldb
289 This field is used to track range of USN modified by provision and
290 upgradeprovision.
291 This value is used afterward by next provision to figure out if
292 the field have been modified since last provision.
294 :param samdb: An LDB object connect to sam.ldb
295 :param low: The lowest USN modified by this upgrade
296 :param high: The highest USN modified by this upgrade
297 :param id: The invocation id of the samba's dc
298 :param replace: A boolean indicating if the range should replace any
299 existing one or appended (default)
302 tab = []
303 if not replace:
304 entry = samdb.search(base="@PROVISION",
305 scope=ldb.SCOPE_BASE,
306 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
307 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
308 if not re.search(';', e):
309 e = "%s;%s" % (e, id)
310 tab.append(str(e))
312 tab.append("%s-%s;%s" % (low, high, id))
313 delta = ldb.Message()
314 delta.dn = ldb.Dn(samdb, "@PROVISION")
315 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
316 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
317 entry = samdb.search(expression='provisionnerID=*',
318 base="@PROVISION", scope=ldb.SCOPE_BASE,
319 attrs=["provisionnerID"])
320 if len(entry) == 0 or len(entry[0]) == 0:
321 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
322 samdb.modify(delta)
325 def set_provision_usn(samdb, low, high, id):
326 """Set the field provisionUSN in sam.ldb
327 This field is used to track range of USN modified by provision and
328 upgradeprovision.
329 This value is used afterward by next provision to figure out if
330 the field have been modified since last provision.
332 :param samdb: An LDB object connect to sam.ldb
333 :param low: The lowest USN modified by this upgrade
334 :param high: The highest USN modified by this upgrade
335 :param id: The invocationId of the provision"""
337 tab = []
338 tab.append("%s-%s;%s" % (low, high, id))
340 delta = ldb.Message()
341 delta.dn = ldb.Dn(samdb, "@PROVISION")
342 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
343 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
344 samdb.add(delta)
347 def get_max_usn(samdb,basedn):
348 """ This function return the biggest USN present in the provision
350 :param samdb: A LDB object pointing to the sam.ldb
351 :param basedn: A string containing the base DN of the provision
352 (ie. DC=foo, DC=bar)
353 :return: The biggest USN in the provision"""
355 res = samdb.search(expression="objectClass=*",base=basedn,
356 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
357 controls=["search_options:1:2",
358 "server_sort:1:1:uSNChanged",
359 "paged_results:1:1"])
360 return res[0]["uSNChanged"]
363 def get_last_provision_usn(sam):
364 """Get USNs ranges modified by a provision or an upgradeprovision
366 :param sam: An LDB object pointing to the sam.ldb
367 :return: a dictionary which keys are invocation id and values are an array
368 of integer representing the different ranges
370 try:
371 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
372 base="@PROVISION", scope=ldb.SCOPE_BASE,
373 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
374 except ldb.LdbError, (ecode, emsg):
375 if ecode == ldb.ERR_NO_SUCH_OBJECT:
376 return None
377 raise
378 if len(entry) > 0:
379 myids = []
380 range = {}
381 p = re.compile(r'-')
382 if entry[0].get("provisionnerID"):
383 for e in entry[0]["provisionnerID"]:
384 myids.append(str(e))
385 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
386 tab1 = str(r).split(';')
387 if len(tab1) == 2:
388 id = tab1[1]
389 else:
390 id = "default"
391 if (len(myids) > 0 and id not in myids):
392 continue
393 tab2 = p.split(tab1[0])
394 if range.get(id) is None:
395 range[id] = []
396 range[id].append(tab2[0])
397 range[id].append(tab2[1])
398 return range
399 else:
400 return None
403 class ProvisionResult(object):
404 """Result of a provision.
406 :ivar server_role: The server role
407 :ivar paths: ProvisionPaths instance
408 :ivar domaindn: The domain dn, as string
411 def __init__(self):
412 self.server_role = None
413 self.paths = None
414 self.domaindn = None
415 self.lp = None
416 self.samdb = None
417 self.idmap = None
418 self.names = None
419 self.domainsid = None
420 self.adminpass_generated = None
421 self.adminpass = None
422 self.backend_result = None
424 def report_logger(self, logger):
425 """Report this provision result to a logger."""
426 logger.info(
427 "Once the above files are installed, your Samba4 server will "
428 "be ready to use")
429 if self.adminpass_generated:
430 logger.info("Admin password: %s", self.adminpass)
431 logger.info("Server Role: %s", self.server_role)
432 logger.info("Hostname: %s", self.names.hostname)
433 logger.info("NetBIOS Domain: %s", self.names.domain)
434 logger.info("DNS Domain: %s", self.names.dnsdomain)
435 logger.info("DOMAIN SID: %s", self.domainsid)
437 if self.backend_result:
438 self.backend_result.report_logger(logger)
441 def check_install(lp, session_info, credentials):
442 """Check whether the current install seems ok.
444 :param lp: Loadparm context
445 :param session_info: Session information
446 :param credentials: Credentials
448 if lp.get("realm") == "":
449 raise Exception("Realm empty")
450 samdb = Ldb(lp.samdb_url(), session_info=session_info,
451 credentials=credentials, lp=lp)
452 if len(samdb.search("(cn=Administrator)")) != 1:
453 raise ProvisioningError("No administrator account found")
456 def findnss(nssfn, names):
457 """Find a user or group from a list of possibilities.
459 :param nssfn: NSS Function to try (should raise KeyError if not found)
460 :param names: Names to check.
461 :return: Value return by first names list.
463 for name in names:
464 try:
465 return nssfn(name)
466 except KeyError:
467 pass
468 raise KeyError("Unable to find user/group in %r" % names)
471 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
472 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
475 def provision_paths_from_lp(lp, dnsdomain):
476 """Set the default paths for provisioning.
478 :param lp: Loadparm context.
479 :param dnsdomain: DNS Domain name
481 paths = ProvisionPaths()
482 paths.private_dir = lp.get("private dir")
483 paths.state_dir = lp.get("state directory")
485 # This is stored without path prefix for the "privateKeytab" attribute in
486 # "secrets_dns.ldif".
487 paths.dns_keytab = "dns.keytab"
488 paths.keytab = "secrets.keytab"
490 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
491 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
492 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
493 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
494 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
495 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
496 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
497 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
498 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
499 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
500 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
501 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
502 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
503 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
504 paths.hklm = "hklm.ldb"
505 paths.hkcr = "hkcr.ldb"
506 paths.hkcu = "hkcu.ldb"
507 paths.hku = "hku.ldb"
508 paths.hkpd = "hkpd.ldb"
509 paths.hkpt = "hkpt.ldb"
510 paths.sysvol = lp.get("path", "sysvol")
511 paths.netlogon = lp.get("path", "netlogon")
512 paths.smbconf = lp.configfile
513 return paths
516 def determine_netbios_name(hostname):
517 """Determine a netbios name from a hostname."""
518 # remove forbidden chars and force the length to be <16
519 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
520 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
523 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
524 serverrole=None, rootdn=None, domaindn=None, configdn=None,
525 schemadn=None, serverdn=None, sitename=None):
526 """Guess configuration settings to use."""
528 if hostname is None:
529 hostname = socket.gethostname().split(".")[0]
531 netbiosname = lp.get("netbios name")
532 if netbiosname is None:
533 netbiosname = determine_netbios_name(hostname)
534 netbiosname = netbiosname.upper()
535 if not valid_netbios_name(netbiosname):
536 raise InvalidNetbiosName(netbiosname)
538 if dnsdomain is None:
539 dnsdomain = lp.get("realm")
540 if dnsdomain is None or dnsdomain == "":
541 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
543 dnsdomain = dnsdomain.lower()
545 if serverrole is None:
546 serverrole = lp.get("server role")
547 if serverrole is None:
548 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
550 serverrole = serverrole.lower()
552 realm = dnsdomain.upper()
554 if lp.get("realm") == "":
555 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
557 if lp.get("realm").upper() != realm:
558 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))
560 if lp.get("server role").lower() != serverrole:
561 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))
563 if serverrole == "active directory domain controller":
564 if domain is None:
565 # This will, for better or worse, default to 'WORKGROUP'
566 domain = lp.get("workgroup")
567 domain = domain.upper()
569 if lp.get("workgroup").upper() != domain:
570 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))
572 if domaindn is None:
573 domaindn = samba.dn_from_dns_name(dnsdomain)
575 if domain == netbiosname:
576 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
577 else:
578 domain = netbiosname
579 if domaindn is None:
580 domaindn = "DC=" + netbiosname
582 if not valid_netbios_name(domain):
583 raise InvalidNetbiosName(domain)
585 if hostname.upper() == realm:
586 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
587 if netbiosname.upper() == realm:
588 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
589 if domain == realm:
590 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
592 if rootdn is None:
593 rootdn = domaindn
595 if configdn is None:
596 configdn = "CN=Configuration," + rootdn
597 if schemadn is None:
598 schemadn = "CN=Schema," + configdn
600 if sitename is None:
601 sitename = DEFAULTSITE
603 names = ProvisionNames()
604 names.rootdn = rootdn
605 names.domaindn = domaindn
606 names.configdn = configdn
607 names.schemadn = schemadn
608 names.ldapmanagerdn = "CN=Manager," + rootdn
609 names.dnsdomain = dnsdomain
610 names.domain = domain
611 names.realm = realm
612 names.netbiosname = netbiosname
613 names.hostname = hostname
614 names.sitename = sitename
615 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
616 netbiosname, sitename, configdn)
618 return names
621 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
622 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
623 global_param=None):
624 """Create a new smb.conf file based on a couple of basic settings.
626 assert smbconf is not None
628 if hostname is None:
629 hostname = socket.gethostname().split(".")[0]
631 netbiosname = determine_netbios_name(hostname)
633 if serverrole is None:
634 serverrole = "standalone server"
636 assert domain is not None
637 domain = domain.upper()
639 assert realm is not None
640 realm = realm.upper()
642 global_settings = {
643 "netbios name": netbiosname,
644 "workgroup": domain,
645 "realm": realm,
646 "server role": serverrole,
649 if lp is None:
650 lp = samba.param.LoadParm()
651 #Load non-existent file
652 if os.path.exists(smbconf):
653 lp.load(smbconf)
655 if global_param is not None:
656 for ent in global_param:
657 if global_param[ent] is not None:
658 global_settings[ent] = " ".join(global_param[ent])
660 if targetdir is not None:
661 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
662 global_settings["lock dir"] = os.path.abspath(targetdir)
663 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
664 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
666 lp.set("lock dir", os.path.abspath(targetdir))
667 lp.set("state directory", global_settings["state directory"])
668 lp.set("cache directory", global_settings["cache directory"])
670 if eadb:
671 if use_ntvfs and not lp.get("posix:eadb"):
672 if targetdir is not None:
673 privdir = os.path.join(targetdir, "private")
674 else:
675 privdir = lp.get("private dir")
676 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
677 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
678 if targetdir is not None:
679 statedir = os.path.join(targetdir, "state")
680 else:
681 statedir = lp.get("state directory")
682 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
684 shares = {}
685 if serverrole == "active directory domain controller":
686 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
687 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
688 "scripts")
689 else:
690 global_settings["passdb backend"] = "samba_dsdb"
692 f = open(smbconf, 'w')
693 try:
694 f.write("[globals]\n")
695 for key, val in global_settings.iteritems():
696 f.write("\t%s = %s\n" % (key, val))
697 f.write("\n")
699 for name, path in shares.iteritems():
700 f.write("[%s]\n" % name)
701 f.write("\tpath = %s\n" % path)
702 f.write("\tread only = no\n")
703 f.write("\n")
704 finally:
705 f.close()
706 # reload the smb.conf
707 lp.load(smbconf)
709 # and dump it without any values that are the default
710 # this ensures that any smb.conf parameters that were set
711 # on the provision/join command line are set in the resulting smb.conf
712 f = open(smbconf, mode='w')
713 try:
714 lp.dump(f, False)
715 finally:
716 f.close()
719 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
720 users_gid, root_gid):
721 """setup reasonable name mappings for sam names to unix names.
723 :param samdb: SamDB object.
724 :param idmap: IDmap db object.
725 :param sid: The domain sid.
726 :param domaindn: The domain DN.
727 :param root_uid: uid of the UNIX root user.
728 :param nobody_uid: uid of the UNIX nobody user.
729 :param users_gid: gid of the UNIX users group.
730 :param root_gid: gid of the UNIX root group.
732 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
734 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
735 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
738 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
739 provision_backend, names, schema, serverrole,
740 erase=False):
741 """Setup the partitions for the SAM database.
743 Alternatively, provision() may call this, and then populate the database.
745 :note: This will wipe the Sam Database!
747 :note: This function always removes the local SAM LDB file. The erase
748 parameter controls whether to erase the existing data, which
749 may not be stored locally but in LDAP.
752 assert session_info is not None
754 # We use options=["modules:"] to stop the modules loading - we
755 # just want to wipe and re-initialise the database, not start it up
757 try:
758 os.unlink(samdb_path)
759 except OSError:
760 pass
762 samdb = Ldb(url=samdb_path, session_info=session_info,
763 lp=lp, options=["modules:"])
765 ldap_backend_line = "# No LDAP backend"
766 if provision_backend.type != "ldb":
767 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
769 samdb.transaction_start()
770 try:
771 logger.info("Setting up sam.ldb partitions and settings")
772 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
773 "LDAP_BACKEND_LINE": ldap_backend_line
777 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
778 "BACKEND_TYPE": provision_backend.type,
779 "SERVER_ROLE": serverrole
782 logger.info("Setting up sam.ldb rootDSE")
783 setup_samdb_rootdse(samdb, names)
784 except:
785 samdb.transaction_cancel()
786 raise
787 else:
788 samdb.transaction_commit()
791 def secretsdb_self_join(secretsdb, domain,
792 netbiosname, machinepass, domainsid=None,
793 realm=None, dnsdomain=None,
794 keytab_path=None,
795 key_version_number=1,
796 secure_channel_type=SEC_CHAN_WKSTA):
797 """Add domain join-specific bits to a secrets database.
799 :param secretsdb: Ldb Handle to the secrets database
800 :param machinepass: Machine password
802 attrs = ["whenChanged",
803 "secret",
804 "priorSecret",
805 "priorChanged",
806 "krb5Keytab",
807 "privateKeytab"]
809 if realm is not None:
810 if dnsdomain is None:
811 dnsdomain = realm.lower()
812 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
813 else:
814 dnsname = None
815 shortname = netbiosname.lower()
817 # We don't need to set msg["flatname"] here, because rdn_name will handle
818 # it, and it causes problems for modifies anyway
819 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
820 msg["secureChannelType"] = [str(secure_channel_type)]
821 msg["objectClass"] = ["top", "primaryDomain"]
822 if dnsname is not None:
823 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
824 msg["realm"] = [realm]
825 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
826 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
827 msg["privateKeytab"] = ["secrets.keytab"]
829 msg["secret"] = [machinepass]
830 msg["samAccountName"] = ["%s$" % netbiosname]
831 msg["secureChannelType"] = [str(secure_channel_type)]
832 if domainsid is not None:
833 msg["objectSid"] = [ndr_pack(domainsid)]
835 # This complex expression tries to ensure that we don't have more
836 # than one record for this SID, realm or netbios domain at a time,
837 # but we don't delete the old record that we are about to modify,
838 # because that would delete the keytab and previous password.
839 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
840 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
841 scope=ldb.SCOPE_ONELEVEL)
843 for del_msg in res:
844 secretsdb.delete(del_msg.dn)
846 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
848 if len(res) == 1:
849 msg["priorSecret"] = [res[0]["secret"][0]]
850 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
852 try:
853 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
854 except KeyError:
855 pass
857 try:
858 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
859 except KeyError:
860 pass
862 for el in msg:
863 if el != 'dn':
864 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
865 secretsdb.modify(msg)
866 secretsdb.rename(res[0].dn, msg.dn)
867 else:
868 spn = [ 'HOST/%s' % shortname ]
869 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
870 # we are a domain controller then we add servicePrincipalName
871 # entries for the keytab code to update.
872 spn.extend([ 'HOST/%s' % dnsname ])
873 msg["servicePrincipalName"] = spn
875 secretsdb.add(msg)
878 def setup_secretsdb(paths, session_info, backend_credentials, lp):
879 """Setup the secrets database.
881 :note: This function does not handle exceptions and transaction on purpose,
882 it's up to the caller to do this job.
884 :param path: Path to the secrets database.
885 :param session_info: Session info.
886 :param credentials: Credentials
887 :param lp: Loadparm context
888 :return: LDB handle for the created secrets database
890 if os.path.exists(paths.secrets):
891 os.unlink(paths.secrets)
893 keytab_path = os.path.join(paths.private_dir, paths.keytab)
894 if os.path.exists(keytab_path):
895 os.unlink(keytab_path)
897 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
898 if os.path.exists(dns_keytab_path):
899 os.unlink(dns_keytab_path)
901 path = paths.secrets
903 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
904 secrets_ldb.erase()
905 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
906 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
907 secrets_ldb.transaction_start()
908 try:
909 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
911 if (backend_credentials is not None and
912 backend_credentials.authentication_requested()):
913 if backend_credentials.get_bind_dn() is not None:
914 setup_add_ldif(secrets_ldb,
915 setup_path("secrets_simple_ldap.ldif"), {
916 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
917 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
919 else:
920 setup_add_ldif(secrets_ldb,
921 setup_path("secrets_sasl_ldap.ldif"), {
922 "LDAPADMINUSER": backend_credentials.get_username(),
923 "LDAPADMINREALM": backend_credentials.get_realm(),
924 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
926 except:
927 secrets_ldb.transaction_cancel()
928 raise
929 return secrets_ldb
932 def setup_privileges(path, session_info, lp):
933 """Setup the privileges database.
935 :param path: Path to the privileges database.
936 :param session_info: Session info.
937 :param credentials: Credentials
938 :param lp: Loadparm context
939 :return: LDB handle for the created secrets database
941 if os.path.exists(path):
942 os.unlink(path)
943 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
944 privilege_ldb.erase()
945 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
948 def setup_registry(path, session_info, lp):
949 """Setup the registry.
951 :param path: Path to the registry database
952 :param session_info: Session information
953 :param credentials: Credentials
954 :param lp: Loadparm context
956 reg = samba.registry.Registry()
957 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
958 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
959 provision_reg = setup_path("provision.reg")
960 assert os.path.exists(provision_reg)
961 reg.diff_apply(provision_reg)
964 def setup_idmapdb(path, session_info, lp):
965 """Setup the idmap database.
967 :param path: path to the idmap database
968 :param session_info: Session information
969 :param credentials: Credentials
970 :param lp: Loadparm context
972 if os.path.exists(path):
973 os.unlink(path)
975 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
976 idmap_ldb.erase()
977 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
978 return idmap_ldb
981 def setup_samdb_rootdse(samdb, names):
982 """Setup the SamDB rootdse.
984 :param samdb: Sam Database handle
986 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
987 "SCHEMADN": names.schemadn,
988 "DOMAINDN": names.domaindn,
989 "ROOTDN" : names.rootdn,
990 "CONFIGDN": names.configdn,
991 "SERVERDN": names.serverdn,
995 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
996 dns_backend, dnspass, domainsid, next_rid, invocationid,
997 policyguid, policyguid_dc,
998 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
999 """Join a host to its own domain."""
1000 assert isinstance(invocationid, str)
1001 if ntdsguid is not None:
1002 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1003 else:
1004 ntdsguid_line = ""
1006 if dc_rid is None:
1007 dc_rid = next_rid
1009 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1010 "CONFIGDN": names.configdn,
1011 "SCHEMADN": names.schemadn,
1012 "DOMAINDN": names.domaindn,
1013 "SERVERDN": names.serverdn,
1014 "INVOCATIONID": invocationid,
1015 "NETBIOSNAME": names.netbiosname,
1016 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1017 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1018 "DOMAINSID": str(domainsid),
1019 "DCRID": str(dc_rid),
1020 "SAMBA_VERSION_STRING": version,
1021 "NTDSGUID": ntdsguid_line,
1022 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1023 domainControllerFunctionality),
1024 "RIDALLOCATIONSTART": str(next_rid + 100),
1025 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1027 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1028 "POLICYGUID": policyguid,
1029 "POLICYGUID_DC": policyguid_dc,
1030 "DNSDOMAIN": names.dnsdomain,
1031 "DOMAINDN": names.domaindn})
1033 # If we are setting up a subdomain, then this has been replicated in, so we
1034 # don't need to add it
1035 if fill == FILL_FULL:
1036 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1037 "CONFIGDN": names.configdn,
1038 "SCHEMADN": names.schemadn,
1039 "DOMAINDN": names.domaindn,
1040 "SERVERDN": names.serverdn,
1041 "INVOCATIONID": invocationid,
1042 "NETBIOSNAME": names.netbiosname,
1043 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1044 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1045 "DOMAINSID": str(domainsid),
1046 "DCRID": str(dc_rid),
1047 "SAMBA_VERSION_STRING": version,
1048 "NTDSGUID": ntdsguid_line,
1049 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1050 domainControllerFunctionality)})
1052 # Setup fSMORoleOwner entries to point at the newly created DC entry
1053 setup_modify_ldif(samdb,
1054 setup_path("provision_self_join_modify_config.ldif"), {
1055 "CONFIGDN": names.configdn,
1056 "SCHEMADN": names.schemadn,
1057 "DEFAULTSITE": names.sitename,
1058 "NETBIOSNAME": names.netbiosname,
1059 "SERVERDN": names.serverdn,
1062 system_session_info = system_session()
1063 samdb.set_session_info(system_session_info)
1064 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1065 # modify a serverReference under cn=config when we are a subdomain, we must
1066 # be system due to ACLs
1067 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1068 "DOMAINDN": names.domaindn,
1069 "SERVERDN": names.serverdn,
1070 "NETBIOSNAME": names.netbiosname,
1073 samdb.set_session_info(admin_session_info)
1075 if dns_backend != "SAMBA_INTERNAL":
1076 # This is Samba4 specific and should be replaced by the correct
1077 # DNS AD-style setup
1078 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1079 "DNSDOMAIN": names.dnsdomain,
1080 "DOMAINDN": names.domaindn,
1081 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1082 "HOSTNAME" : names.hostname,
1083 "DNSNAME" : '%s.%s' % (
1084 names.netbiosname.lower(), names.dnsdomain.lower())
1088 def getpolicypath(sysvolpath, dnsdomain, guid):
1089 """Return the physical path of policy given its guid.
1091 :param sysvolpath: Path to the sysvol folder
1092 :param dnsdomain: DNS name of the AD domain
1093 :param guid: The GUID of the policy
1094 :return: A string with the complete path to the policy folder
1096 if guid[0] != "{":
1097 guid = "{%s}" % guid
1098 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1099 return policy_path
1102 def create_gpo_struct(policy_path):
1103 if not os.path.exists(policy_path):
1104 os.makedirs(policy_path, 0775)
1105 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1106 try:
1107 f.write("[General]\r\nVersion=0")
1108 finally:
1109 f.close()
1110 p = os.path.join(policy_path, "MACHINE")
1111 if not os.path.exists(p):
1112 os.makedirs(p, 0775)
1113 p = os.path.join(policy_path, "USER")
1114 if not os.path.exists(p):
1115 os.makedirs(p, 0775)
1118 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1119 """Create the default GPO for a domain
1121 :param sysvolpath: Physical path for the sysvol folder
1122 :param dnsdomain: DNS domain name of the AD domain
1123 :param policyguid: GUID of the default domain policy
1124 :param policyguid_dc: GUID of the default domain controler policy
1126 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1127 create_gpo_struct(policy_path)
1129 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1130 create_gpo_struct(policy_path)
1133 def setup_samdb(path, session_info, provision_backend, lp, names,
1134 logger, fill, serverrole, schema, am_rodc=False):
1135 """Setup a complete SAM Database.
1137 :note: This will wipe the main SAM database file!
1140 # Also wipes the database
1141 setup_samdb_partitions(path, logger=logger, lp=lp,
1142 provision_backend=provision_backend, session_info=session_info,
1143 names=names, serverrole=serverrole, schema=schema)
1145 # Load the database, but don's load the global schema and don't connect
1146 # quite yet
1147 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1148 credentials=provision_backend.credentials, lp=lp,
1149 global_schema=False, am_rodc=am_rodc)
1151 logger.info("Pre-loading the Samba 4 and AD schema")
1153 # Load the schema from the one we computed earlier
1154 samdb.set_schema(schema, write_indices_and_attributes=False)
1156 # Set the NTDS settings DN manually - in order to have it already around
1157 # before the provisioned tree exists and we connect
1158 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1160 # And now we can connect to the DB - the schema won't be loaded from the
1161 # DB
1162 samdb.connect(path)
1164 # But we have to give it one more kick to have it use the schema
1165 # during provision - it needs, now that it is connected, to write
1166 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1167 samdb.set_schema(schema, write_indices_and_attributes=True)
1169 return samdb
1172 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1173 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1174 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1175 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1177 if next_rid is None:
1178 next_rid = 1000
1180 # Provision does not make much sense values larger than 1000000000
1181 # as the upper range of the rIDAvailablePool is 1073741823 and
1182 # we don't want to create a domain that cannot allocate rids.
1183 if next_rid < 1000 or next_rid > 1000000000:
1184 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1185 error += "the valid range is %u-%u. The default is %u." % (
1186 1000, 1000000000, 1000)
1187 raise ProvisioningError(error)
1189 # ATTENTION: Do NOT change these default values without discussion with the
1190 # team and/or release manager. They have a big impact on the whole program!
1191 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1193 if dom_for_fun_level is None:
1194 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1196 if dom_for_fun_level > domainControllerFunctionality:
1197 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!")
1199 domainFunctionality = dom_for_fun_level
1200 forestFunctionality = dom_for_fun_level
1202 # Set the NTDS settings DN manually - in order to have it already around
1203 # before the provisioned tree exists and we connect
1204 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1206 samdb.transaction_start()
1207 try:
1208 # Set the domain functionality levels onto the database.
1209 # Various module (the password_hash module in particular) need
1210 # to know what level of AD we are emulating.
1212 # These will be fixed into the database via the database
1213 # modifictions below, but we need them set from the start.
1214 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1215 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1216 samdb.set_opaque_integer("domainControllerFunctionality",
1217 domainControllerFunctionality)
1219 samdb.set_domain_sid(str(domainsid))
1220 samdb.set_invocation_id(invocationid)
1222 logger.info("Adding DomainDN: %s" % names.domaindn)
1224 # impersonate domain admin
1225 admin_session_info = admin_session(lp, str(domainsid))
1226 samdb.set_session_info(admin_session_info)
1227 if domainguid is not None:
1228 domainguid_line = "objectGUID: %s\n-" % domainguid
1229 else:
1230 domainguid_line = ""
1232 descr = b64encode(get_domain_descriptor(domainsid))
1233 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1234 "DOMAINDN": names.domaindn,
1235 "DOMAINSID": str(domainsid),
1236 "DESCRIPTOR": descr,
1237 "DOMAINGUID": domainguid_line
1240 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1241 "DOMAINDN": names.domaindn,
1242 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1243 "NEXTRID": str(next_rid),
1244 "DEFAULTSITE": names.sitename,
1245 "CONFIGDN": names.configdn,
1246 "POLICYGUID": policyguid,
1247 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1248 "SAMBA_VERSION_STRING": version
1251 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1252 if fill == FILL_FULL:
1253 logger.info("Adding configuration container")
1254 descr = b64encode(get_config_descriptor(domainsid))
1255 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1256 "CONFIGDN": names.configdn,
1257 "DESCRIPTOR": descr,
1260 # The LDIF here was created when the Schema object was constructed
1261 logger.info("Setting up sam.ldb schema")
1262 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1263 samdb.modify_ldif(schema.schema_dn_modify)
1264 samdb.write_prefixes_from_schema()
1265 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1266 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1267 {"SCHEMADN": names.schemadn})
1269 # Now register this container in the root of the forest
1270 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1271 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1272 "subRefs")
1274 except:
1275 samdb.transaction_cancel()
1276 raise
1277 else:
1278 samdb.transaction_commit()
1280 samdb.transaction_start()
1281 try:
1282 samdb.invocation_id = invocationid
1284 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1285 if fill == FILL_FULL:
1286 logger.info("Setting up sam.ldb configuration data")
1287 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1288 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1289 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1290 "CONFIGDN": names.configdn,
1291 "NETBIOSNAME": names.netbiosname,
1292 "DEFAULTSITE": names.sitename,
1293 "DNSDOMAIN": names.dnsdomain,
1294 "DOMAIN": names.domain,
1295 "SCHEMADN": names.schemadn,
1296 "DOMAINDN": names.domaindn,
1297 "SERVERDN": names.serverdn,
1298 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1299 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1300 "PARTITIONS_DESCRIPTOR": partitions_descr,
1301 "SITES_DESCRIPTOR": sites_descr,
1304 logger.info("Setting up display specifiers")
1305 display_specifiers_ldif = read_ms_ldif(
1306 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1307 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1308 {"CONFIGDN": names.configdn})
1309 check_all_substituted(display_specifiers_ldif)
1310 samdb.add_ldif(display_specifiers_ldif)
1312 logger.info("Adding users container")
1313 users_desc = b64encode(get_domain_users_descriptor(domainsid))
1314 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1315 "DOMAINDN": names.domaindn,
1316 "USERS_DESCRIPTOR": users_desc
1318 logger.info("Modifying users container")
1319 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1320 "DOMAINDN": names.domaindn})
1321 logger.info("Adding computers container")
1322 computers_desc = b64encode(get_domain_computers_descriptor(domainsid))
1323 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1324 "DOMAINDN": names.domaindn,
1325 "COMPUTERS_DESCRIPTOR": computers_desc
1327 logger.info("Modifying computers container")
1328 setup_modify_ldif(samdb,
1329 setup_path("provision_computers_modify.ldif"), {
1330 "DOMAINDN": names.domaindn})
1331 logger.info("Setting up sam.ldb data")
1332 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
1333 builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
1334 controllers_desc = b64encode(get_domain_controllers_descriptor(domainsid))
1335 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1336 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1337 "DOMAINDN": names.domaindn,
1338 "NETBIOSNAME": names.netbiosname,
1339 "DEFAULTSITE": names.sitename,
1340 "CONFIGDN": names.configdn,
1341 "SERVERDN": names.serverdn,
1342 "RIDAVAILABLESTART": str(next_rid + 600),
1343 "POLICYGUID_DC": policyguid_dc,
1344 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1345 "BUILTIN_DESCRIPTOR": builtin_desc,
1346 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1349 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1350 if fill == FILL_FULL:
1351 setup_modify_ldif(samdb,
1352 setup_path("provision_configuration_references.ldif"), {
1353 "CONFIGDN": names.configdn,
1354 "SCHEMADN": names.schemadn})
1356 logger.info("Setting up well known security principals")
1357 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1358 "CONFIGDN": names.configdn,
1361 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1362 setup_modify_ldif(samdb,
1363 setup_path("provision_basedn_references.ldif"),
1364 {"DOMAINDN": names.domaindn})
1366 logger.info("Setting up sam.ldb users and groups")
1367 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1368 "DOMAINDN": names.domaindn,
1369 "DOMAINSID": str(domainsid),
1370 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1371 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1374 logger.info("Setting up self join")
1375 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1376 invocationid=invocationid,
1377 dns_backend=dns_backend,
1378 dnspass=dnspass,
1379 machinepass=machinepass,
1380 domainsid=domainsid,
1381 next_rid=next_rid,
1382 dc_rid=dc_rid,
1383 policyguid=policyguid,
1384 policyguid_dc=policyguid_dc,
1385 domainControllerFunctionality=domainControllerFunctionality,
1386 ntdsguid=ntdsguid)
1388 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1389 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1390 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1391 assert isinstance(names.ntdsguid, str)
1392 except:
1393 samdb.transaction_cancel()
1394 raise
1395 else:
1396 samdb.transaction_commit()
1397 return samdb
1400 FILL_FULL = "FULL"
1401 FILL_SUBDOMAIN = "SUBDOMAIN"
1402 FILL_NT4SYNC = "NT4SYNC"
1403 FILL_DRS = "DRS"
1404 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1405 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)"
1406 SYSVOL_SERVICE="sysvol"
1408 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1409 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1410 for root, dirs, files in os.walk(path, topdown=False):
1411 for name in files:
1412 setntacl(lp, os.path.join(root, name), acl, domsid,
1413 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1414 for name in dirs:
1415 setntacl(lp, os.path.join(root, name), acl, domsid,
1416 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1419 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1420 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1421 folders beneath.
1423 :param sysvol: Physical path for the sysvol folder
1424 :param dnsdomain: The DNS name of the domain
1425 :param domainsid: The SID of the domain
1426 :param domaindn: The DN of the domain (ie. DC=...)
1427 :param samdb: An LDB object on the SAM db
1428 :param lp: an LP object
1431 # Set ACL for GPO root folder
1432 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1433 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1434 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1436 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1437 attrs=["cn", "nTSecurityDescriptor"],
1438 expression="", scope=ldb.SCOPE_ONELEVEL)
1440 for policy in res:
1441 acl = ndr_unpack(security.descriptor,
1442 str(policy["nTSecurityDescriptor"])).as_sddl()
1443 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1444 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1445 str(domainsid), use_ntvfs,
1446 passdb=passdb)
1449 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1450 domaindn, lp, use_ntvfs):
1451 """Set the ACL for the sysvol share and the subfolders
1453 :param samdb: An LDB object on the SAM db
1454 :param netlogon: Physical path for the netlogon folder
1455 :param sysvol: Physical path for the sysvol folder
1456 :param uid: The UID of the "Administrator" user
1457 :param gid: The GID of the "Domain adminstrators" group
1458 :param domainsid: The SID of the domain
1459 :param dnsdomain: The DNS name of the domain
1460 :param domaindn: The DN of the domain (ie. DC=...)
1462 s4_passdb = None
1464 if not use_ntvfs:
1465 # This will ensure that the smbd code we are running when setting ACLs
1466 # is initialised with the smb.conf
1467 s3conf = s3param.get_context()
1468 s3conf.load(lp.configfile)
1469 # ensure we are using the right samba_dsdb passdb backend, no matter what
1470 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1471 passdb.reload_static_pdb()
1473 # ensure that we init the samba_dsdb backend, so the domain sid is
1474 # marked in secrets.tdb
1475 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1477 # now ensure everything matches correctly, to avoid wierd issues
1478 if passdb.get_global_sam_sid() != domainsid:
1479 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))
1481 domain_info = s4_passdb.domain_info()
1482 if domain_info["dom_sid"] != domainsid:
1483 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))
1485 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1486 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()))
1489 try:
1490 if use_ntvfs:
1491 os.chown(sysvol, -1, gid)
1492 except OSError:
1493 canchown = False
1494 else:
1495 canchown = True
1497 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1498 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1499 skip_invalid_chown=True, passdb=s4_passdb,
1500 service=SYSVOL_SERVICE)
1501 for root, dirs, files in os.walk(sysvol, topdown=False):
1502 for name in files:
1503 if use_ntvfs and canchown:
1504 os.chown(os.path.join(root, name), -1, gid)
1505 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1506 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1507 passdb=s4_passdb, service=SYSVOL_SERVICE)
1508 for name in dirs:
1509 if use_ntvfs and canchown:
1510 os.chown(os.path.join(root, name), -1, gid)
1511 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1512 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1513 passdb=s4_passdb, service=SYSVOL_SERVICE)
1515 # Set acls on Policy folder and policies folders
1516 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1518 def acl_type(direct_db_access):
1519 if direct_db_access:
1520 return "DB"
1521 else:
1522 return "VFS"
1524 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1525 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1526 fsacl_sddl = fsacl.as_sddl(domainsid)
1527 if fsacl_sddl != acl:
1528 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))
1530 for root, dirs, files in os.walk(path, topdown=False):
1531 for name in files:
1532 fsacl = getntacl(lp, os.path.join(root, name),
1533 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1534 if fsacl is None:
1535 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1536 fsacl_sddl = fsacl.as_sddl(domainsid)
1537 if fsacl_sddl != acl:
1538 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))
1540 for name in dirs:
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 directory %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 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))
1550 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1551 direct_db_access):
1552 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1553 folders beneath.
1555 :param sysvol: Physical path for the sysvol folder
1556 :param dnsdomain: The DNS name of the domain
1557 :param domainsid: The SID of the domain
1558 :param domaindn: The DN of the domain (ie. DC=...)
1559 :param samdb: An LDB object on the SAM db
1560 :param lp: an LP object
1563 # Set ACL for GPO root folder
1564 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1565 fsacl = getntacl(lp, root_policy_path,
1566 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1567 if fsacl is None:
1568 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1569 fsacl_sddl = fsacl.as_sddl(domainsid)
1570 if fsacl_sddl != POLICIES_ACL:
1571 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))
1572 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1573 attrs=["cn", "nTSecurityDescriptor"],
1574 expression="", scope=ldb.SCOPE_ONELEVEL)
1576 for policy in res:
1577 acl = ndr_unpack(security.descriptor,
1578 str(policy["nTSecurityDescriptor"])).as_sddl()
1579 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1580 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1581 domainsid, direct_db_access)
1584 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1585 lp):
1586 """Set the ACL for the sysvol share and the subfolders
1588 :param samdb: An LDB object on the SAM db
1589 :param netlogon: Physical path for the netlogon folder
1590 :param sysvol: Physical path for the sysvol folder
1591 :param uid: The UID of the "Administrator" user
1592 :param gid: The GID of the "Domain adminstrators" group
1593 :param domainsid: The SID of the domain
1594 :param dnsdomain: The DNS name of the domain
1595 :param domaindn: The DN of the domain (ie. DC=...)
1598 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1599 s3conf = s3param.get_context()
1600 s3conf.load(lp.configfile)
1601 # ensure we are using the right samba_dsdb passdb backend, no matter what
1602 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1603 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1604 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1606 # now ensure everything matches correctly, to avoid wierd issues
1607 if passdb.get_global_sam_sid() != domainsid:
1608 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))
1610 domain_info = s4_passdb.domain_info()
1611 if domain_info["dom_sid"] != domainsid:
1612 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))
1614 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1615 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()))
1617 # Ensure we can read this directly, and via the smbd VFS
1618 for direct_db_access in [True, False]:
1619 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1620 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1621 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1622 if fsacl is None:
1623 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1624 fsacl_sddl = fsacl.as_sddl(domainsid)
1625 if fsacl_sddl != SYSVOL_ACL:
1626 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))
1628 # Check acls on Policy folder and policies folders
1629 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1630 direct_db_access)
1633 def interface_ips_v4(lp):
1634 """return only IPv4 IPs"""
1635 ips = samba.interface_ips(lp, False)
1636 ret = []
1637 for i in ips:
1638 if i.find(':') == -1:
1639 ret.append(i)
1640 return ret
1643 def interface_ips_v6(lp, linklocal=False):
1644 """return only IPv6 IPs"""
1645 ips = samba.interface_ips(lp, False)
1646 ret = []
1647 for i in ips:
1648 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1649 ret.append(i)
1650 return ret
1653 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1654 domainsid, schema=None,
1655 targetdir=None, samdb_fill=FILL_FULL,
1656 hostip=None, hostip6=None,
1657 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1658 domainguid=None, policyguid=None, policyguid_dc=None,
1659 invocationid=None, machinepass=None, ntdsguid=None,
1660 dns_backend=None, dnspass=None,
1661 serverrole=None, dom_for_fun_level=None,
1662 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1663 # create/adapt the group policy GUIDs
1664 # Default GUID for default policy are described at
1665 # "How Core Group Policy Works"
1666 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1667 if policyguid is None:
1668 policyguid = DEFAULT_POLICY_GUID
1669 policyguid = policyguid.upper()
1670 if policyguid_dc is None:
1671 policyguid_dc = DEFAULT_DC_POLICY_GUID
1672 policyguid_dc = policyguid_dc.upper()
1674 if invocationid is None:
1675 invocationid = str(uuid.uuid4())
1677 if krbtgtpass is None:
1678 krbtgtpass = samba.generate_random_password(128, 255)
1679 if machinepass is None:
1680 machinepass = samba.generate_random_password(128, 255)
1681 if dnspass is None:
1682 dnspass = samba.generate_random_password(128, 255)
1684 samdb = fill_samdb(samdb, lp, names, logger=logger,
1685 domainsid=domainsid, schema=schema, domainguid=domainguid,
1686 policyguid=policyguid, policyguid_dc=policyguid_dc,
1687 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1688 invocationid=invocationid, machinepass=machinepass,
1689 dns_backend=dns_backend, dnspass=dnspass,
1690 ntdsguid=ntdsguid, serverrole=serverrole,
1691 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1692 next_rid=next_rid, dc_rid=dc_rid)
1694 if serverrole == "active directory domain controller":
1696 # Set up group policies (domain policy and domain controller
1697 # policy)
1698 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1699 policyguid_dc)
1700 if not skip_sysvolacl:
1701 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1702 paths.root_gid, domainsid, names.dnsdomain,
1703 names.domaindn, lp, use_ntvfs)
1704 else:
1705 logger.info("Setting acl on sysvol skipped")
1707 secretsdb_self_join(secrets_ldb, domain=names.domain,
1708 realm=names.realm, dnsdomain=names.dnsdomain,
1709 netbiosname=names.netbiosname, domainsid=domainsid,
1710 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1712 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1713 # In future, this might be determined from some configuration
1714 kerberos_enctypes = str(ENC_ALL_TYPES)
1716 try:
1717 msg = ldb.Message(ldb.Dn(samdb,
1718 samdb.searchone("distinguishedName",
1719 expression="samAccountName=%s$" % names.netbiosname,
1720 scope=ldb.SCOPE_SUBTREE)))
1721 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1722 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1723 name="msDS-SupportedEncryptionTypes")
1724 samdb.modify(msg)
1725 except ldb.LdbError, (enum, estr):
1726 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1727 # It might be that this attribute does not exist in this schema
1728 raise
1730 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1731 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1732 dnspass=dnspass, os_level=dom_for_fun_level,
1733 targetdir=targetdir, site=DEFAULTSITE)
1735 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1736 attribute="objectGUID")
1737 assert isinstance(domainguid, str)
1739 lastProvisionUSNs = get_last_provision_usn(samdb)
1740 maxUSN = get_max_usn(samdb, str(names.rootdn))
1741 if lastProvisionUSNs is not None:
1742 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1743 else:
1744 set_provision_usn(samdb, 0, maxUSN, invocationid)
1746 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1747 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1748 { 'NTDSGUID' : names.ntdsguid })
1750 # fix any dangling GUIDs from the provision
1751 logger.info("Fixing provision GUIDs")
1752 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1753 quiet=True)
1754 samdb.transaction_start()
1755 try:
1756 # a small number of GUIDs are missing because of ordering issues in the
1757 # provision code
1758 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1759 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1760 scope=ldb.SCOPE_BASE,
1761 attrs=['defaultObjectCategory'])
1762 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1763 scope=ldb.SCOPE_ONELEVEL,
1764 attrs=['ipsecOwnersReference',
1765 'ipsecFilterReference',
1766 'ipsecISAKMPReference',
1767 'ipsecNegotiationPolicyReference',
1768 'ipsecNFAReference'])
1769 except:
1770 samdb.transaction_cancel()
1771 raise
1772 else:
1773 samdb.transaction_commit()
1776 _ROLES_MAP = {
1777 "ROLE_STANDALONE": "standalone server",
1778 "ROLE_DOMAIN_MEMBER": "member server",
1779 "ROLE_DOMAIN_BDC": "active directory domain controller",
1780 "ROLE_DOMAIN_PDC": "active directory domain controller",
1781 "dc": "active directory domain controller",
1782 "member": "member server",
1783 "domain controller": "active directory domain controller",
1784 "active directory domain controller": "active directory domain controller",
1785 "member server": "member server",
1786 "standalone": "standalone server",
1787 "standalone server": "standalone server",
1791 def sanitize_server_role(role):
1792 """Sanitize a server role name.
1794 :param role: Server role
1795 :raise ValueError: If the role can not be interpreted
1796 :return: Sanitized server role (one of "member server",
1797 "active directory domain controller", "standalone server")
1799 try:
1800 return _ROLES_MAP[role]
1801 except KeyError:
1802 raise ValueError(role)
1805 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1806 maxuid, maxgid):
1807 """Create AD entries for the fake ypserver.
1809 This is needed for being able to manipulate posix attrs via ADUC.
1811 samdb.transaction_start()
1812 try:
1813 logger.info("Setting up fake yp server settings")
1814 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1815 "DOMAINDN": domaindn,
1816 "NETBIOSNAME": netbiosname,
1817 "NISDOMAIN": nisdomain,
1819 except:
1820 samdb.transaction_cancel()
1821 raise
1822 else:
1823 samdb.transaction_commit()
1826 def provision(logger, session_info, credentials, smbconf=None,
1827 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1828 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1829 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1830 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1831 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1832 dns_backend=None, dns_forwarder=None, dnspass=None,
1833 invocationid=None, machinepass=None, ntdsguid=None,
1834 root=None, nobody=None, users=None, backup=None, aci=None,
1835 serverrole=None, dom_for_fun_level=None, backend_type=None,
1836 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1837 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1838 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1839 """Provision samba4
1841 :note: caution, this wipes all existing data!
1844 try:
1845 serverrole = sanitize_server_role(serverrole)
1846 except ValueError:
1847 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1849 if ldapadminpass is None:
1850 # Make a new, random password between Samba and it's LDAP server
1851 ldapadminpass = samba.generate_random_password(128, 255)
1853 if backend_type is None:
1854 backend_type = "ldb"
1856 if domainsid is None:
1857 domainsid = security.random_sid()
1858 else:
1859 domainsid = security.dom_sid(domainsid)
1861 root_uid = findnss_uid([root or "root"])
1862 nobody_uid = findnss_uid([nobody or "nobody"])
1863 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1864 root_gid = pwd.getpwuid(root_uid).pw_gid
1866 try:
1867 bind_gid = findnss_gid(["bind", "named"])
1868 except KeyError:
1869 bind_gid = None
1871 if targetdir is not None:
1872 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1873 elif smbconf is None:
1874 smbconf = samba.param.default_path()
1875 if not os.path.exists(os.path.dirname(smbconf)):
1876 os.makedirs(os.path.dirname(smbconf))
1878 server_services = []
1879 global_param = {}
1880 if use_rfc2307:
1881 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1883 if dns_backend != "SAMBA_INTERNAL":
1884 server_services.append("-dns")
1885 else:
1886 if dns_forwarder is not None:
1887 global_param["dns forwarder"] = [dns_forwarder]
1889 if use_ntvfs:
1890 server_services.append("+smb")
1891 server_services.append("-s3fs")
1892 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1894 if len(server_services) > 0:
1895 global_param["server services"] = server_services
1897 # only install a new smb.conf if there isn't one there already
1898 if os.path.exists(smbconf):
1899 # if Samba Team members can't figure out the weird errors
1900 # loading an empty smb.conf gives, then we need to be smarter.
1901 # Pretend it just didn't exist --abartlet
1902 f = open(smbconf, 'r')
1903 try:
1904 data = f.read().lstrip()
1905 finally:
1906 f.close()
1907 if data is None or data == "":
1908 make_smbconf(smbconf, hostname, domain, realm,
1909 targetdir, serverrole=serverrole,
1910 eadb=useeadb, use_ntvfs=use_ntvfs,
1911 lp=lp, global_param=global_param)
1912 else:
1913 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1914 serverrole=serverrole,
1915 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1917 if lp is None:
1918 lp = samba.param.LoadParm()
1919 lp.load(smbconf)
1920 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1921 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1922 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1923 sitename=sitename, rootdn=rootdn)
1924 paths = provision_paths_from_lp(lp, names.dnsdomain)
1926 paths.bind_gid = bind_gid
1927 paths.root_uid = root_uid;
1928 paths.root_gid = root_gid
1930 if hostip is None:
1931 logger.info("Looking up IPv4 addresses")
1932 hostips = interface_ips_v4(lp)
1933 if len(hostips) > 0:
1934 hostip = hostips[0]
1935 if len(hostips) > 1:
1936 logger.warning("More than one IPv4 address found. Using %s",
1937 hostip)
1938 if hostip == "127.0.0.1":
1939 hostip = None
1940 if hostip is None:
1941 logger.warning("No IPv4 address will be assigned")
1943 if hostip6 is None:
1944 logger.info("Looking up IPv6 addresses")
1945 hostips = interface_ips_v6(lp, linklocal=False)
1946 if hostips:
1947 hostip6 = hostips[0]
1948 if len(hostips) > 1:
1949 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1950 if hostip6 is None:
1951 logger.warning("No IPv6 address will be assigned")
1953 names.hostip = hostip
1954 names.hostip6 = hostip6
1956 if serverrole is None:
1957 serverrole = lp.get("server role")
1959 if not os.path.exists(paths.private_dir):
1960 os.mkdir(paths.private_dir)
1961 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1962 os.mkdir(os.path.join(paths.private_dir, "tls"))
1963 if not os.path.exists(paths.state_dir):
1964 os.mkdir(paths.state_dir)
1966 if paths.sysvol and not os.path.exists(paths.sysvol):
1967 os.makedirs(paths.sysvol, 0775)
1969 if not use_ntvfs and serverrole == "active directory domain controller":
1970 s3conf = s3param.get_context()
1971 s3conf.load(lp.configfile)
1973 if paths.sysvol is None:
1974 raise MissingShareError("sysvol", paths.smbconf)
1976 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1977 try:
1978 try:
1979 smbd.set_simple_acl(file.name, 0755, root_gid)
1980 except Exception:
1981 if not smbd.have_posix_acls():
1982 # This clue is only strictly correct for RPM and
1983 # Debian-like Linux systems, but hopefully other users
1984 # will get enough clue from it.
1985 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.")
1987 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1988 try:
1989 smbd.chown(file.name, root_uid, root_gid)
1990 except Exception:
1991 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1992 finally:
1993 file.close()
1995 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1997 schema = Schema(domainsid, invocationid=invocationid,
1998 schemadn=names.schemadn)
2000 if backend_type == "ldb":
2001 provision_backend = LDBBackend(backend_type, paths=paths,
2002 lp=lp, credentials=credentials,
2003 names=names, logger=logger)
2004 elif backend_type == "existing":
2005 # If support for this is ever added back, then the URI will need to be
2006 # specified again
2007 provision_backend = ExistingBackend(backend_type, paths=paths,
2008 lp=lp, credentials=credentials,
2009 names=names, logger=logger,
2010 ldap_backend_forced_uri=None)
2011 elif backend_type == "fedora-ds":
2012 provision_backend = FDSBackend(backend_type, paths=paths,
2013 lp=lp, credentials=credentials,
2014 names=names, logger=logger, domainsid=domainsid,
2015 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2016 slapd_path=slapd_path,
2017 root=root)
2018 elif backend_type == "openldap":
2019 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2020 lp=lp, credentials=credentials,
2021 names=names, logger=logger, domainsid=domainsid,
2022 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2023 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
2024 else:
2025 raise ValueError("Unknown LDAP backend type selected")
2027 provision_backend.init()
2028 provision_backend.start()
2030 # only install a new shares config db if there is none
2031 if not os.path.exists(paths.shareconf):
2032 logger.info("Setting up share.ldb")
2033 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2034 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2036 logger.info("Setting up secrets.ldb")
2037 secrets_ldb = setup_secretsdb(paths,
2038 session_info=session_info,
2039 backend_credentials=provision_backend.secrets_credentials, lp=lp)
2041 try:
2042 logger.info("Setting up the registry")
2043 setup_registry(paths.hklm, session_info, lp=lp)
2045 logger.info("Setting up the privileges database")
2046 setup_privileges(paths.privilege, session_info, lp=lp)
2048 logger.info("Setting up idmap db")
2049 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2051 setup_name_mappings(idmap, sid=str(domainsid),
2052 root_uid=root_uid, nobody_uid=nobody_uid,
2053 users_gid=users_gid, root_gid=root_gid)
2055 logger.info("Setting up SAM db")
2056 samdb = setup_samdb(paths.samdb, session_info,
2057 provision_backend, lp, names, logger=logger,
2058 serverrole=serverrole,
2059 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2061 if serverrole == "active directory domain controller":
2062 if paths.netlogon is None:
2063 raise MissingShareError("netlogon", paths.smbconf)
2065 if paths.sysvol is None:
2066 raise MissingShareError("sysvol", paths.smbconf)
2068 if not os.path.isdir(paths.netlogon):
2069 os.makedirs(paths.netlogon, 0755)
2071 if adminpass is None:
2072 adminpass = samba.generate_random_password(12, 32)
2073 adminpass_generated = True
2074 else:
2075 adminpass_generated = False
2077 if samdb_fill == FILL_FULL:
2078 provision_fill(samdb, secrets_ldb, logger, names, paths,
2079 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2080 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2081 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2082 krbtgtpass=krbtgtpass, domainguid=domainguid,
2083 policyguid=policyguid, policyguid_dc=policyguid_dc,
2084 invocationid=invocationid, machinepass=machinepass,
2085 ntdsguid=ntdsguid, dns_backend=dns_backend,
2086 dnspass=dnspass, serverrole=serverrole,
2087 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2088 lp=lp, use_ntvfs=use_ntvfs,
2089 skip_sysvolacl=skip_sysvolacl)
2091 create_krb5_conf(paths.krb5conf,
2092 dnsdomain=names.dnsdomain, hostname=names.hostname,
2093 realm=names.realm)
2094 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2095 "generated at %s", paths.krb5conf)
2097 if serverrole == "active directory domain controller":
2098 create_dns_update_list(lp, logger, paths)
2100 backend_result = provision_backend.post_setup()
2101 provision_backend.shutdown()
2103 except:
2104 secrets_ldb.transaction_cancel()
2105 raise
2107 # Now commit the secrets.ldb to disk
2108 secrets_ldb.transaction_commit()
2110 # the commit creates the dns.keytab, now chown it
2111 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2112 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2113 try:
2114 os.chmod(dns_keytab_path, 0640)
2115 os.chown(dns_keytab_path, -1, paths.bind_gid)
2116 except OSError:
2117 if not os.environ.has_key('SAMBA_SELFTEST'):
2118 logger.info("Failed to chown %s to bind gid %u",
2119 dns_keytab_path, paths.bind_gid)
2121 result = ProvisionResult()
2122 result.server_role = serverrole
2123 result.domaindn = domaindn
2124 result.paths = paths
2125 result.names = names
2126 result.lp = lp
2127 result.samdb = samdb
2128 result.idmap = idmap
2129 result.domainsid = str(domainsid)
2131 if samdb_fill == FILL_FULL:
2132 result.adminpass_generated = adminpass_generated
2133 result.adminpass = adminpass
2134 else:
2135 result.adminpass_generated = False
2136 result.adminpass = None
2138 result.backend_result = backend_result
2140 if use_rfc2307:
2141 provision_fake_ypserver(logger=logger, samdb=samdb,
2142 domaindn=names.domaindn, netbiosname=names.netbiosname,
2143 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2145 return result
2148 def provision_become_dc(smbconf=None, targetdir=None,
2149 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2150 serverdn=None, domain=None, hostname=None, domainsid=None,
2151 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2152 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2153 dns_backend=None, root=None, nobody=None, users=None,
2154 backup=None, serverrole=None, ldap_backend=None,
2155 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2157 logger = logging.getLogger("provision")
2158 samba.set_debug_level(debuglevel)
2160 res = provision(logger, system_session(), None,
2161 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2162 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2163 configdn=configdn, serverdn=serverdn, domain=domain,
2164 hostname=hostname, hostip=None, domainsid=domainsid,
2165 machinepass=machinepass,
2166 serverrole="active directory domain controller",
2167 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2168 use_ntvfs=use_ntvfs)
2169 res.lp.set("debuglevel", str(debuglevel))
2170 return res
2173 def create_krb5_conf(path, dnsdomain, hostname, realm):
2174 """Write out a file containing zone statements suitable for inclusion in a
2175 named.conf file (including GSS-TSIG configuration).
2177 :param path: Path of the new named.conf file.
2178 :param dnsdomain: DNS Domain name
2179 :param hostname: Local hostname
2180 :param realm: Realm name
2182 setup_file(setup_path("krb5.conf"), path, {
2183 "DNSDOMAIN": dnsdomain,
2184 "HOSTNAME": hostname,
2185 "REALM": realm,
2189 class ProvisioningError(Exception):
2190 """A generic provision error."""
2192 def __init__(self, value):
2193 self.value = value
2195 def __str__(self):
2196 return "ProvisioningError: " + self.value
2199 class InvalidNetbiosName(Exception):
2200 """A specified name was not a valid NetBIOS name."""
2202 def __init__(self, name):
2203 super(InvalidNetbiosName, self).__init__(
2204 "The name '%r' is not a valid NetBIOS name" % name)
2207 class MissingShareError(ProvisioningError):
2209 def __init__(self, name, smbconf):
2210 super(MissingShareError, self).__init__(
2211 "Existing smb.conf does not have a [%s] share, but you are "
2212 "configuring a DC. Please remove %s or add the share manually." %
2213 (name, smbconf))