provision: setup names.dns_backend
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision / __init__.py
blob507582bad590f492cc332a0a6166015d33a2cc63
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 res10 = samdb.search(expression="(samaccountname=dns)",
295 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
296 controls=["search_options:1:2"])
297 if (len(res10) > 0):
298 has_legacy_dns_account = True
299 else:
300 has_legacy_dns_account = False
302 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
303 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
304 controls=["search_options:1:2"])
305 if (len(res11) > 0):
306 has_dns_account = True
307 else:
308 has_dns_account = False
310 if names.dnsdomaindn is not None:
311 if has_dns_account:
312 names.dns_backend = 'BIND9_DLZ'
313 else:
314 names.dns_backend = 'SAMBA_INTERNAL'
315 elif has_dns_account or has_legacy_dns_account:
316 names.dns_backend = 'BIND9_FLATFILE'
317 else:
318 names.dns_backend = 'NONE'
320 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
321 names.name_map['DnsAdmins'] = str(dns_admins_sid)
323 return names
326 def update_provision_usn(samdb, low, high, id, replace=False):
327 """Update the field provisionUSN in sam.ldb
329 This field is used to track range of USN modified by provision and
330 upgradeprovision.
331 This value is used afterward by next provision to figure out if
332 the field have been modified since last provision.
334 :param samdb: An LDB object connect to sam.ldb
335 :param low: The lowest USN modified by this upgrade
336 :param high: The highest USN modified by this upgrade
337 :param id: The invocation id of the samba's dc
338 :param replace: A boolean indicating if the range should replace any
339 existing one or appended (default)
342 tab = []
343 if not replace:
344 entry = samdb.search(base="@PROVISION",
345 scope=ldb.SCOPE_BASE,
346 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
347 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
348 if not re.search(';', e):
349 e = "%s;%s" % (e, id)
350 tab.append(str(e))
352 tab.append("%s-%s;%s" % (low, high, id))
353 delta = ldb.Message()
354 delta.dn = ldb.Dn(samdb, "@PROVISION")
355 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
356 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
357 entry = samdb.search(expression='provisionnerID=*',
358 base="@PROVISION", scope=ldb.SCOPE_BASE,
359 attrs=["provisionnerID"])
360 if len(entry) == 0 or len(entry[0]) == 0:
361 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
362 samdb.modify(delta)
365 def set_provision_usn(samdb, low, high, id):
366 """Set the field provisionUSN in sam.ldb
367 This field is used to track range of USN modified by provision and
368 upgradeprovision.
369 This value is used afterward by next provision to figure out if
370 the field have been modified since last provision.
372 :param samdb: An LDB object connect to sam.ldb
373 :param low: The lowest USN modified by this upgrade
374 :param high: The highest USN modified by this upgrade
375 :param id: The invocationId of the provision"""
377 tab = []
378 tab.append("%s-%s;%s" % (low, high, id))
380 delta = ldb.Message()
381 delta.dn = ldb.Dn(samdb, "@PROVISION")
382 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
383 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
384 samdb.add(delta)
387 def get_max_usn(samdb,basedn):
388 """ This function return the biggest USN present in the provision
390 :param samdb: A LDB object pointing to the sam.ldb
391 :param basedn: A string containing the base DN of the provision
392 (ie. DC=foo, DC=bar)
393 :return: The biggest USN in the provision"""
395 res = samdb.search(expression="objectClass=*",base=basedn,
396 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
397 controls=["search_options:1:2",
398 "server_sort:1:1:uSNChanged",
399 "paged_results:1:1"])
400 return res[0]["uSNChanged"]
403 def get_last_provision_usn(sam):
404 """Get USNs ranges modified by a provision or an upgradeprovision
406 :param sam: An LDB object pointing to the sam.ldb
407 :return: a dictionary which keys are invocation id and values are an array
408 of integer representing the different ranges
410 try:
411 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
412 base="@PROVISION", scope=ldb.SCOPE_BASE,
413 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
414 except ldb.LdbError, (ecode, emsg):
415 if ecode == ldb.ERR_NO_SUCH_OBJECT:
416 return None
417 raise
418 if len(entry) > 0:
419 myids = []
420 range = {}
421 p = re.compile(r'-')
422 if entry[0].get("provisionnerID"):
423 for e in entry[0]["provisionnerID"]:
424 myids.append(str(e))
425 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
426 tab1 = str(r).split(';')
427 if len(tab1) == 2:
428 id = tab1[1]
429 else:
430 id = "default"
431 if (len(myids) > 0 and id not in myids):
432 continue
433 tab2 = p.split(tab1[0])
434 if range.get(id) is None:
435 range[id] = []
436 range[id].append(tab2[0])
437 range[id].append(tab2[1])
438 return range
439 else:
440 return None
443 class ProvisionResult(object):
444 """Result of a provision.
446 :ivar server_role: The server role
447 :ivar paths: ProvisionPaths instance
448 :ivar domaindn: The domain dn, as string
451 def __init__(self):
452 self.server_role = None
453 self.paths = None
454 self.domaindn = None
455 self.lp = None
456 self.samdb = None
457 self.idmap = None
458 self.names = None
459 self.domainsid = None
460 self.adminpass_generated = None
461 self.adminpass = None
462 self.backend_result = None
464 def report_logger(self, logger):
465 """Report this provision result to a logger."""
466 logger.info(
467 "Once the above files are installed, your Samba4 server will "
468 "be ready to use")
469 if self.adminpass_generated:
470 logger.info("Admin password: %s", self.adminpass)
471 logger.info("Server Role: %s", self.server_role)
472 logger.info("Hostname: %s", self.names.hostname)
473 logger.info("NetBIOS Domain: %s", self.names.domain)
474 logger.info("DNS Domain: %s", self.names.dnsdomain)
475 logger.info("DOMAIN SID: %s", self.domainsid)
477 if self.backend_result:
478 self.backend_result.report_logger(logger)
481 def check_install(lp, session_info, credentials):
482 """Check whether the current install seems ok.
484 :param lp: Loadparm context
485 :param session_info: Session information
486 :param credentials: Credentials
488 if lp.get("realm") == "":
489 raise Exception("Realm empty")
490 samdb = Ldb(lp.samdb_url(), session_info=session_info,
491 credentials=credentials, lp=lp)
492 if len(samdb.search("(cn=Administrator)")) != 1:
493 raise ProvisioningError("No administrator account found")
496 def findnss(nssfn, names):
497 """Find a user or group from a list of possibilities.
499 :param nssfn: NSS Function to try (should raise KeyError if not found)
500 :param names: Names to check.
501 :return: Value return by first names list.
503 for name in names:
504 try:
505 return nssfn(name)
506 except KeyError:
507 pass
508 raise KeyError("Unable to find user/group in %r" % names)
511 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
512 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
515 def provision_paths_from_lp(lp, dnsdomain):
516 """Set the default paths for provisioning.
518 :param lp: Loadparm context.
519 :param dnsdomain: DNS Domain name
521 paths = ProvisionPaths()
522 paths.private_dir = lp.get("private dir")
523 paths.state_dir = lp.get("state directory")
525 # This is stored without path prefix for the "privateKeytab" attribute in
526 # "secrets_dns.ldif".
527 paths.dns_keytab = "dns.keytab"
528 paths.keytab = "secrets.keytab"
530 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
531 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
532 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
533 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
534 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
535 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
536 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
537 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
538 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
539 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
540 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
541 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
542 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
543 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
544 paths.hklm = "hklm.ldb"
545 paths.hkcr = "hkcr.ldb"
546 paths.hkcu = "hkcu.ldb"
547 paths.hku = "hku.ldb"
548 paths.hkpd = "hkpd.ldb"
549 paths.hkpt = "hkpt.ldb"
550 paths.sysvol = lp.get("path", "sysvol")
551 paths.netlogon = lp.get("path", "netlogon")
552 paths.smbconf = lp.configfile
553 return paths
556 def determine_netbios_name(hostname):
557 """Determine a netbios name from a hostname."""
558 # remove forbidden chars and force the length to be <16
559 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
560 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
563 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
564 serverrole=None, rootdn=None, domaindn=None, configdn=None,
565 schemadn=None, serverdn=None, sitename=None):
566 """Guess configuration settings to use."""
568 if hostname is None:
569 hostname = socket.gethostname().split(".")[0]
571 netbiosname = lp.get("netbios name")
572 if netbiosname is None:
573 netbiosname = determine_netbios_name(hostname)
574 netbiosname = netbiosname.upper()
575 if not valid_netbios_name(netbiosname):
576 raise InvalidNetbiosName(netbiosname)
578 if dnsdomain is None:
579 dnsdomain = lp.get("realm")
580 if dnsdomain is None or dnsdomain == "":
581 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
583 dnsdomain = dnsdomain.lower()
585 if serverrole is None:
586 serverrole = lp.get("server role")
587 if serverrole is None:
588 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
590 serverrole = serverrole.lower()
592 realm = dnsdomain.upper()
594 if lp.get("realm") == "":
595 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
597 if lp.get("realm").upper() != realm:
598 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))
600 if lp.get("server role").lower() != serverrole:
601 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))
603 if serverrole == "active directory domain controller":
604 if domain is None:
605 # This will, for better or worse, default to 'WORKGROUP'
606 domain = lp.get("workgroup")
607 domain = domain.upper()
609 if lp.get("workgroup").upper() != domain:
610 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))
612 if domaindn is None:
613 domaindn = samba.dn_from_dns_name(dnsdomain)
615 if domain == netbiosname:
616 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
617 else:
618 domain = netbiosname
619 if domaindn is None:
620 domaindn = "DC=" + netbiosname
622 if not valid_netbios_name(domain):
623 raise InvalidNetbiosName(domain)
625 if hostname.upper() == realm:
626 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
627 if netbiosname.upper() == realm:
628 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
629 if domain == realm:
630 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
632 if rootdn is None:
633 rootdn = domaindn
635 if configdn is None:
636 configdn = "CN=Configuration," + rootdn
637 if schemadn is None:
638 schemadn = "CN=Schema," + configdn
640 if sitename is None:
641 sitename = DEFAULTSITE
643 names = ProvisionNames()
644 names.rootdn = rootdn
645 names.domaindn = domaindn
646 names.configdn = configdn
647 names.schemadn = schemadn
648 names.ldapmanagerdn = "CN=Manager," + rootdn
649 names.dnsdomain = dnsdomain
650 names.domain = domain
651 names.realm = realm
652 names.netbiosname = netbiosname
653 names.hostname = hostname
654 names.sitename = sitename
655 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
656 netbiosname, sitename, configdn)
658 return names
661 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
662 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
663 global_param=None):
664 """Create a new smb.conf file based on a couple of basic settings.
666 assert smbconf is not None
668 if hostname is None:
669 hostname = socket.gethostname().split(".")[0]
671 netbiosname = determine_netbios_name(hostname)
673 if serverrole is None:
674 serverrole = "standalone server"
676 assert domain is not None
677 domain = domain.upper()
679 assert realm is not None
680 realm = realm.upper()
682 global_settings = {
683 "netbios name": netbiosname,
684 "workgroup": domain,
685 "realm": realm,
686 "server role": serverrole,
689 if lp is None:
690 lp = samba.param.LoadParm()
691 #Load non-existent file
692 if os.path.exists(smbconf):
693 lp.load(smbconf)
695 if global_param is not None:
696 for ent in global_param:
697 if global_param[ent] is not None:
698 global_settings[ent] = " ".join(global_param[ent])
700 if targetdir is not None:
701 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
702 global_settings["lock dir"] = os.path.abspath(targetdir)
703 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
704 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
706 lp.set("lock dir", os.path.abspath(targetdir))
707 lp.set("state directory", global_settings["state directory"])
708 lp.set("cache directory", global_settings["cache directory"])
710 if eadb:
711 if use_ntvfs and not lp.get("posix:eadb"):
712 if targetdir is not None:
713 privdir = os.path.join(targetdir, "private")
714 else:
715 privdir = lp.get("private dir")
716 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
717 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
718 if targetdir is not None:
719 statedir = os.path.join(targetdir, "state")
720 else:
721 statedir = lp.get("state directory")
722 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
724 shares = {}
725 if serverrole == "active directory domain controller":
726 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
727 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
728 "scripts")
729 else:
730 global_settings["passdb backend"] = "samba_dsdb"
732 f = open(smbconf, 'w')
733 try:
734 f.write("[globals]\n")
735 for key, val in global_settings.iteritems():
736 f.write("\t%s = %s\n" % (key, val))
737 f.write("\n")
739 for name, path in shares.iteritems():
740 f.write("[%s]\n" % name)
741 f.write("\tpath = %s\n" % path)
742 f.write("\tread only = no\n")
743 f.write("\n")
744 finally:
745 f.close()
746 # reload the smb.conf
747 lp.load(smbconf)
749 # and dump it without any values that are the default
750 # this ensures that any smb.conf parameters that were set
751 # on the provision/join command line are set in the resulting smb.conf
752 f = open(smbconf, mode='w')
753 try:
754 lp.dump(f, False)
755 finally:
756 f.close()
759 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
760 users_gid, root_gid):
761 """setup reasonable name mappings for sam names to unix names.
763 :param samdb: SamDB object.
764 :param idmap: IDmap db object.
765 :param sid: The domain sid.
766 :param domaindn: The domain DN.
767 :param root_uid: uid of the UNIX root user.
768 :param nobody_uid: uid of the UNIX nobody user.
769 :param users_gid: gid of the UNIX users group.
770 :param root_gid: gid of the UNIX root group.
772 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
774 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
775 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
778 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
779 provision_backend, names, schema, serverrole,
780 erase=False):
781 """Setup the partitions for the SAM database.
783 Alternatively, provision() may call this, and then populate the database.
785 :note: This will wipe the Sam Database!
787 :note: This function always removes the local SAM LDB file. The erase
788 parameter controls whether to erase the existing data, which
789 may not be stored locally but in LDAP.
792 assert session_info is not None
794 # We use options=["modules:"] to stop the modules loading - we
795 # just want to wipe and re-initialise the database, not start it up
797 try:
798 os.unlink(samdb_path)
799 except OSError:
800 pass
802 samdb = Ldb(url=samdb_path, session_info=session_info,
803 lp=lp, options=["modules:"])
805 ldap_backend_line = "# No LDAP backend"
806 if provision_backend.type != "ldb":
807 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
809 samdb.transaction_start()
810 try:
811 logger.info("Setting up sam.ldb partitions and settings")
812 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
813 "LDAP_BACKEND_LINE": ldap_backend_line
817 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
818 "BACKEND_TYPE": provision_backend.type,
819 "SERVER_ROLE": serverrole
822 logger.info("Setting up sam.ldb rootDSE")
823 setup_samdb_rootdse(samdb, names)
824 except:
825 samdb.transaction_cancel()
826 raise
827 else:
828 samdb.transaction_commit()
831 def secretsdb_self_join(secretsdb, domain,
832 netbiosname, machinepass, domainsid=None,
833 realm=None, dnsdomain=None,
834 keytab_path=None,
835 key_version_number=1,
836 secure_channel_type=SEC_CHAN_WKSTA):
837 """Add domain join-specific bits to a secrets database.
839 :param secretsdb: Ldb Handle to the secrets database
840 :param machinepass: Machine password
842 attrs = ["whenChanged",
843 "secret",
844 "priorSecret",
845 "priorChanged",
846 "krb5Keytab",
847 "privateKeytab"]
849 if realm is not None:
850 if dnsdomain is None:
851 dnsdomain = realm.lower()
852 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
853 else:
854 dnsname = None
855 shortname = netbiosname.lower()
857 # We don't need to set msg["flatname"] here, because rdn_name will handle
858 # it, and it causes problems for modifies anyway
859 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
860 msg["secureChannelType"] = [str(secure_channel_type)]
861 msg["objectClass"] = ["top", "primaryDomain"]
862 if dnsname is not None:
863 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
864 msg["realm"] = [realm]
865 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
866 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
867 msg["privateKeytab"] = ["secrets.keytab"]
869 msg["secret"] = [machinepass]
870 msg["samAccountName"] = ["%s$" % netbiosname]
871 msg["secureChannelType"] = [str(secure_channel_type)]
872 if domainsid is not None:
873 msg["objectSid"] = [ndr_pack(domainsid)]
875 # This complex expression tries to ensure that we don't have more
876 # than one record for this SID, realm or netbios domain at a time,
877 # but we don't delete the old record that we are about to modify,
878 # because that would delete the keytab and previous password.
879 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
880 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
881 scope=ldb.SCOPE_ONELEVEL)
883 for del_msg in res:
884 secretsdb.delete(del_msg.dn)
886 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
888 if len(res) == 1:
889 msg["priorSecret"] = [res[0]["secret"][0]]
890 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
892 try:
893 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
894 except KeyError:
895 pass
897 try:
898 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
899 except KeyError:
900 pass
902 for el in msg:
903 if el != 'dn':
904 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
905 secretsdb.modify(msg)
906 secretsdb.rename(res[0].dn, msg.dn)
907 else:
908 spn = [ 'HOST/%s' % shortname ]
909 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
910 # we are a domain controller then we add servicePrincipalName
911 # entries for the keytab code to update.
912 spn.extend([ 'HOST/%s' % dnsname ])
913 msg["servicePrincipalName"] = spn
915 secretsdb.add(msg)
918 def setup_secretsdb(paths, session_info, backend_credentials, lp):
919 """Setup the secrets database.
921 :note: This function does not handle exceptions and transaction on purpose,
922 it's up to the caller to do this job.
924 :param path: Path to the secrets database.
925 :param session_info: Session info.
926 :param credentials: Credentials
927 :param lp: Loadparm context
928 :return: LDB handle for the created secrets database
930 if os.path.exists(paths.secrets):
931 os.unlink(paths.secrets)
933 keytab_path = os.path.join(paths.private_dir, paths.keytab)
934 if os.path.exists(keytab_path):
935 os.unlink(keytab_path)
937 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
938 if os.path.exists(dns_keytab_path):
939 os.unlink(dns_keytab_path)
941 path = paths.secrets
943 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
944 secrets_ldb.erase()
945 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
946 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
947 secrets_ldb.transaction_start()
948 try:
949 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
951 if (backend_credentials is not None and
952 backend_credentials.authentication_requested()):
953 if backend_credentials.get_bind_dn() is not None:
954 setup_add_ldif(secrets_ldb,
955 setup_path("secrets_simple_ldap.ldif"), {
956 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
957 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
959 else:
960 setup_add_ldif(secrets_ldb,
961 setup_path("secrets_sasl_ldap.ldif"), {
962 "LDAPADMINUSER": backend_credentials.get_username(),
963 "LDAPADMINREALM": backend_credentials.get_realm(),
964 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
966 except:
967 secrets_ldb.transaction_cancel()
968 raise
969 return secrets_ldb
972 def setup_privileges(path, session_info, lp):
973 """Setup the privileges database.
975 :param path: Path to the privileges database.
976 :param session_info: Session info.
977 :param credentials: Credentials
978 :param lp: Loadparm context
979 :return: LDB handle for the created secrets database
981 if os.path.exists(path):
982 os.unlink(path)
983 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
984 privilege_ldb.erase()
985 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
988 def setup_registry(path, session_info, lp):
989 """Setup the registry.
991 :param path: Path to the registry database
992 :param session_info: Session information
993 :param credentials: Credentials
994 :param lp: Loadparm context
996 reg = samba.registry.Registry()
997 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
998 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
999 provision_reg = setup_path("provision.reg")
1000 assert os.path.exists(provision_reg)
1001 reg.diff_apply(provision_reg)
1004 def setup_idmapdb(path, session_info, lp):
1005 """Setup the idmap database.
1007 :param path: path to the idmap database
1008 :param session_info: Session information
1009 :param credentials: Credentials
1010 :param lp: Loadparm context
1012 if os.path.exists(path):
1013 os.unlink(path)
1015 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1016 idmap_ldb.erase()
1017 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1018 return idmap_ldb
1021 def setup_samdb_rootdse(samdb, names):
1022 """Setup the SamDB rootdse.
1024 :param samdb: Sam Database handle
1026 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1027 "SCHEMADN": names.schemadn,
1028 "DOMAINDN": names.domaindn,
1029 "ROOTDN" : names.rootdn,
1030 "CONFIGDN": names.configdn,
1031 "SERVERDN": names.serverdn,
1035 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1036 dns_backend, dnspass, domainsid, next_rid, invocationid,
1037 policyguid, policyguid_dc,
1038 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1039 """Join a host to its own domain."""
1040 assert isinstance(invocationid, str)
1041 if ntdsguid is not None:
1042 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1043 else:
1044 ntdsguid_line = ""
1046 if dc_rid is None:
1047 dc_rid = next_rid
1049 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1050 "CONFIGDN": names.configdn,
1051 "SCHEMADN": names.schemadn,
1052 "DOMAINDN": names.domaindn,
1053 "SERVERDN": names.serverdn,
1054 "INVOCATIONID": invocationid,
1055 "NETBIOSNAME": names.netbiosname,
1056 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1057 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1058 "DOMAINSID": str(domainsid),
1059 "DCRID": str(dc_rid),
1060 "SAMBA_VERSION_STRING": version,
1061 "NTDSGUID": ntdsguid_line,
1062 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1063 domainControllerFunctionality),
1064 "RIDALLOCATIONSTART": str(next_rid + 100),
1065 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1067 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1068 "POLICYGUID": policyguid,
1069 "POLICYGUID_DC": policyguid_dc,
1070 "DNSDOMAIN": names.dnsdomain,
1071 "DOMAINDN": names.domaindn})
1073 # If we are setting up a subdomain, then this has been replicated in, so we
1074 # don't need to add it
1075 if fill == FILL_FULL:
1076 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1077 "CONFIGDN": names.configdn,
1078 "SCHEMADN": names.schemadn,
1079 "DOMAINDN": names.domaindn,
1080 "SERVERDN": names.serverdn,
1081 "INVOCATIONID": invocationid,
1082 "NETBIOSNAME": names.netbiosname,
1083 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1084 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1085 "DOMAINSID": str(domainsid),
1086 "DCRID": str(dc_rid),
1087 "SAMBA_VERSION_STRING": version,
1088 "NTDSGUID": ntdsguid_line,
1089 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1090 domainControllerFunctionality)})
1092 # Setup fSMORoleOwner entries to point at the newly created DC entry
1093 setup_modify_ldif(samdb,
1094 setup_path("provision_self_join_modify_config.ldif"), {
1095 "CONFIGDN": names.configdn,
1096 "SCHEMADN": names.schemadn,
1097 "DEFAULTSITE": names.sitename,
1098 "NETBIOSNAME": names.netbiosname,
1099 "SERVERDN": names.serverdn,
1102 system_session_info = system_session()
1103 samdb.set_session_info(system_session_info)
1104 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1105 # modify a serverReference under cn=config when we are a subdomain, we must
1106 # be system due to ACLs
1107 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1108 "DOMAINDN": names.domaindn,
1109 "SERVERDN": names.serverdn,
1110 "NETBIOSNAME": names.netbiosname,
1113 samdb.set_session_info(admin_session_info)
1115 if dns_backend != "SAMBA_INTERNAL":
1116 # This is Samba4 specific and should be replaced by the correct
1117 # DNS AD-style setup
1118 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1119 "DNSDOMAIN": names.dnsdomain,
1120 "DOMAINDN": names.domaindn,
1121 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1122 "HOSTNAME" : names.hostname,
1123 "DNSNAME" : '%s.%s' % (
1124 names.netbiosname.lower(), names.dnsdomain.lower())
1128 def getpolicypath(sysvolpath, dnsdomain, guid):
1129 """Return the physical path of policy given its guid.
1131 :param sysvolpath: Path to the sysvol folder
1132 :param dnsdomain: DNS name of the AD domain
1133 :param guid: The GUID of the policy
1134 :return: A string with the complete path to the policy folder
1136 if guid[0] != "{":
1137 guid = "{%s}" % guid
1138 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1139 return policy_path
1142 def create_gpo_struct(policy_path):
1143 if not os.path.exists(policy_path):
1144 os.makedirs(policy_path, 0775)
1145 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1146 try:
1147 f.write("[General]\r\nVersion=0")
1148 finally:
1149 f.close()
1150 p = os.path.join(policy_path, "MACHINE")
1151 if not os.path.exists(p):
1152 os.makedirs(p, 0775)
1153 p = os.path.join(policy_path, "USER")
1154 if not os.path.exists(p):
1155 os.makedirs(p, 0775)
1158 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1159 """Create the default GPO for a domain
1161 :param sysvolpath: Physical path for the sysvol folder
1162 :param dnsdomain: DNS domain name of the AD domain
1163 :param policyguid: GUID of the default domain policy
1164 :param policyguid_dc: GUID of the default domain controler policy
1166 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1167 create_gpo_struct(policy_path)
1169 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1170 create_gpo_struct(policy_path)
1173 def setup_samdb(path, session_info, provision_backend, lp, names,
1174 logger, fill, serverrole, schema, am_rodc=False):
1175 """Setup a complete SAM Database.
1177 :note: This will wipe the main SAM database file!
1180 # Also wipes the database
1181 setup_samdb_partitions(path, logger=logger, lp=lp,
1182 provision_backend=provision_backend, session_info=session_info,
1183 names=names, serverrole=serverrole, schema=schema)
1185 # Load the database, but don's load the global schema and don't connect
1186 # quite yet
1187 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1188 credentials=provision_backend.credentials, lp=lp,
1189 global_schema=False, am_rodc=am_rodc)
1191 logger.info("Pre-loading the Samba 4 and AD schema")
1193 # Load the schema from the one we computed earlier
1194 samdb.set_schema(schema, write_indices_and_attributes=False)
1196 # Set the NTDS settings DN manually - in order to have it already around
1197 # before the provisioned tree exists and we connect
1198 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1200 # And now we can connect to the DB - the schema won't be loaded from the
1201 # DB
1202 samdb.connect(path)
1204 # But we have to give it one more kick to have it use the schema
1205 # during provision - it needs, now that it is connected, to write
1206 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1207 samdb.set_schema(schema, write_indices_and_attributes=True)
1209 return samdb
1212 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1213 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1214 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1215 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1217 if next_rid is None:
1218 next_rid = 1000
1220 # Provision does not make much sense values larger than 1000000000
1221 # as the upper range of the rIDAvailablePool is 1073741823 and
1222 # we don't want to create a domain that cannot allocate rids.
1223 if next_rid < 1000 or next_rid > 1000000000:
1224 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1225 error += "the valid range is %u-%u. The default is %u." % (
1226 1000, 1000000000, 1000)
1227 raise ProvisioningError(error)
1229 # ATTENTION: Do NOT change these default values without discussion with the
1230 # team and/or release manager. They have a big impact on the whole program!
1231 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1233 if dom_for_fun_level is None:
1234 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1236 if dom_for_fun_level > domainControllerFunctionality:
1237 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!")
1239 domainFunctionality = dom_for_fun_level
1240 forestFunctionality = dom_for_fun_level
1242 # Set the NTDS settings DN manually - in order to have it already around
1243 # before the provisioned tree exists and we connect
1244 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1246 samdb.transaction_start()
1247 try:
1248 # Set the domain functionality levels onto the database.
1249 # Various module (the password_hash module in particular) need
1250 # to know what level of AD we are emulating.
1252 # These will be fixed into the database via the database
1253 # modifictions below, but we need them set from the start.
1254 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1255 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1256 samdb.set_opaque_integer("domainControllerFunctionality",
1257 domainControllerFunctionality)
1259 samdb.set_domain_sid(str(domainsid))
1260 samdb.set_invocation_id(invocationid)
1262 logger.info("Adding DomainDN: %s" % names.domaindn)
1264 # impersonate domain admin
1265 admin_session_info = admin_session(lp, str(domainsid))
1266 samdb.set_session_info(admin_session_info)
1267 if domainguid is not None:
1268 domainguid_line = "objectGUID: %s\n-" % domainguid
1269 else:
1270 domainguid_line = ""
1272 descr = b64encode(get_domain_descriptor(domainsid))
1273 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1274 "DOMAINDN": names.domaindn,
1275 "DOMAINSID": str(domainsid),
1276 "DESCRIPTOR": descr,
1277 "DOMAINGUID": domainguid_line
1280 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1281 "DOMAINDN": names.domaindn,
1282 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1283 "NEXTRID": str(next_rid),
1284 "DEFAULTSITE": names.sitename,
1285 "CONFIGDN": names.configdn,
1286 "POLICYGUID": policyguid,
1287 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1288 "SAMBA_VERSION_STRING": version
1291 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1292 if fill == FILL_FULL:
1293 logger.info("Adding configuration container")
1294 descr = b64encode(get_config_descriptor(domainsid))
1295 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1296 "CONFIGDN": names.configdn,
1297 "DESCRIPTOR": descr,
1300 # The LDIF here was created when the Schema object was constructed
1301 logger.info("Setting up sam.ldb schema")
1302 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1303 samdb.modify_ldif(schema.schema_dn_modify)
1304 samdb.write_prefixes_from_schema()
1305 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1306 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1307 {"SCHEMADN": names.schemadn})
1309 # Now register this container in the root of the forest
1310 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1311 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1312 "subRefs")
1314 except:
1315 samdb.transaction_cancel()
1316 raise
1317 else:
1318 samdb.transaction_commit()
1320 samdb.transaction_start()
1321 try:
1322 samdb.invocation_id = invocationid
1324 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1325 if fill == FILL_FULL:
1326 logger.info("Setting up sam.ldb configuration data")
1328 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1329 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1330 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(domainsid))
1331 protected1_descr = b64encode(get_config_delete_protected1_descriptor(domainsid))
1332 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(domainsid))
1333 protected2_descr = b64encode(get_config_delete_protected2_descriptor(domainsid))
1335 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1336 "CONFIGDN": names.configdn,
1337 "NETBIOSNAME": names.netbiosname,
1338 "DEFAULTSITE": names.sitename,
1339 "DNSDOMAIN": names.dnsdomain,
1340 "DOMAIN": names.domain,
1341 "SCHEMADN": names.schemadn,
1342 "DOMAINDN": names.domaindn,
1343 "SERVERDN": names.serverdn,
1344 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1345 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1346 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1347 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1348 "SERVICES_DESCRIPTOR": protected1_descr,
1349 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1350 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1351 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1352 "PARTITIONS_DESCRIPTOR": partitions_descr,
1353 "SITES_DESCRIPTOR": sites_descr,
1356 logger.info("Setting up display specifiers")
1357 display_specifiers_ldif = read_ms_ldif(
1358 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1359 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1360 {"CONFIGDN": names.configdn})
1361 check_all_substituted(display_specifiers_ldif)
1362 samdb.add_ldif(display_specifiers_ldif)
1364 logger.info("Modifying display specifiers")
1365 setup_modify_ldif(samdb,
1366 setup_path("provision_configuration_modify.ldif"), {
1367 "CONFIGDN": names.configdn,
1368 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1371 logger.info("Adding users container")
1372 users_desc = b64encode(get_domain_users_descriptor(domainsid))
1373 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1374 "DOMAINDN": names.domaindn,
1375 "USERS_DESCRIPTOR": users_desc
1377 logger.info("Modifying users container")
1378 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1379 "DOMAINDN": names.domaindn})
1380 logger.info("Adding computers container")
1381 computers_desc = b64encode(get_domain_computers_descriptor(domainsid))
1382 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1383 "DOMAINDN": names.domaindn,
1384 "COMPUTERS_DESCRIPTOR": computers_desc
1386 logger.info("Modifying computers container")
1387 setup_modify_ldif(samdb,
1388 setup_path("provision_computers_modify.ldif"), {
1389 "DOMAINDN": names.domaindn})
1390 logger.info("Setting up sam.ldb data")
1391 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
1392 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(domainsid))
1393 system_desc = b64encode(get_domain_delete_protected1_descriptor(domainsid))
1394 builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
1395 controllers_desc = b64encode(get_domain_controllers_descriptor(domainsid))
1396 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1397 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1398 "DOMAINDN": names.domaindn,
1399 "NETBIOSNAME": names.netbiosname,
1400 "DEFAULTSITE": names.sitename,
1401 "CONFIGDN": names.configdn,
1402 "SERVERDN": names.serverdn,
1403 "RIDAVAILABLESTART": str(next_rid + 600),
1404 "POLICYGUID_DC": policyguid_dc,
1405 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1406 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1407 "SYSTEM_DESCRIPTOR": system_desc,
1408 "BUILTIN_DESCRIPTOR": builtin_desc,
1409 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1412 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1413 if fill == FILL_FULL:
1414 setup_modify_ldif(samdb,
1415 setup_path("provision_configuration_references.ldif"), {
1416 "CONFIGDN": names.configdn,
1417 "SCHEMADN": names.schemadn})
1419 logger.info("Setting up well known security principals")
1420 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(domainsid))
1421 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1422 "CONFIGDN": names.configdn,
1423 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1426 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1427 setup_modify_ldif(samdb,
1428 setup_path("provision_basedn_references.ldif"),
1429 {"DOMAINDN": names.domaindn})
1431 logger.info("Setting up sam.ldb users and groups")
1432 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1433 "DOMAINDN": names.domaindn,
1434 "DOMAINSID": str(domainsid),
1435 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1436 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1439 logger.info("Setting up self join")
1440 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1441 invocationid=invocationid,
1442 dns_backend=dns_backend,
1443 dnspass=dnspass,
1444 machinepass=machinepass,
1445 domainsid=domainsid,
1446 next_rid=next_rid,
1447 dc_rid=dc_rid,
1448 policyguid=policyguid,
1449 policyguid_dc=policyguid_dc,
1450 domainControllerFunctionality=domainControllerFunctionality,
1451 ntdsguid=ntdsguid)
1453 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1454 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1455 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1456 assert isinstance(names.ntdsguid, str)
1457 except:
1458 samdb.transaction_cancel()
1459 raise
1460 else:
1461 samdb.transaction_commit()
1462 return samdb
1465 FILL_FULL = "FULL"
1466 FILL_SUBDOMAIN = "SUBDOMAIN"
1467 FILL_NT4SYNC = "NT4SYNC"
1468 FILL_DRS = "DRS"
1469 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1470 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)"
1471 SYSVOL_SERVICE="sysvol"
1473 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1474 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1475 for root, dirs, files in os.walk(path, topdown=False):
1476 for name in files:
1477 setntacl(lp, os.path.join(root, name), acl, domsid,
1478 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1479 for name in dirs:
1480 setntacl(lp, os.path.join(root, name), acl, domsid,
1481 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1484 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1485 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1486 folders beneath.
1488 :param sysvol: Physical path for the sysvol folder
1489 :param dnsdomain: The DNS name of the domain
1490 :param domainsid: The SID of the domain
1491 :param domaindn: The DN of the domain (ie. DC=...)
1492 :param samdb: An LDB object on the SAM db
1493 :param lp: an LP object
1496 # Set ACL for GPO root folder
1497 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1498 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1499 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1501 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1502 attrs=["cn", "nTSecurityDescriptor"],
1503 expression="", scope=ldb.SCOPE_ONELEVEL)
1505 for policy in res:
1506 acl = ndr_unpack(security.descriptor,
1507 str(policy["nTSecurityDescriptor"])).as_sddl()
1508 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1509 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1510 str(domainsid), use_ntvfs,
1511 passdb=passdb)
1514 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1515 domaindn, lp, use_ntvfs):
1516 """Set the ACL for the sysvol share and the subfolders
1518 :param samdb: An LDB object on the SAM db
1519 :param netlogon: Physical path for the netlogon folder
1520 :param sysvol: Physical path for the sysvol folder
1521 :param uid: The UID of the "Administrator" user
1522 :param gid: The GID of the "Domain adminstrators" group
1523 :param domainsid: The SID of the domain
1524 :param dnsdomain: The DNS name of the domain
1525 :param domaindn: The DN of the domain (ie. DC=...)
1527 s4_passdb = None
1529 if not use_ntvfs:
1530 # This will ensure that the smbd code we are running when setting ACLs
1531 # is initialised with the smb.conf
1532 s3conf = s3param.get_context()
1533 s3conf.load(lp.configfile)
1534 # ensure we are using the right samba_dsdb passdb backend, no matter what
1535 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1536 passdb.reload_static_pdb()
1538 # ensure that we init the samba_dsdb backend, so the domain sid is
1539 # marked in secrets.tdb
1540 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1542 # now ensure everything matches correctly, to avoid wierd issues
1543 if passdb.get_global_sam_sid() != domainsid:
1544 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))
1546 domain_info = s4_passdb.domain_info()
1547 if domain_info["dom_sid"] != domainsid:
1548 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))
1550 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1551 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()))
1554 try:
1555 if use_ntvfs:
1556 os.chown(sysvol, -1, gid)
1557 except OSError:
1558 canchown = False
1559 else:
1560 canchown = True
1562 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1563 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1564 skip_invalid_chown=True, passdb=s4_passdb,
1565 service=SYSVOL_SERVICE)
1566 for root, dirs, files in os.walk(sysvol, topdown=False):
1567 for name in files:
1568 if use_ntvfs and canchown:
1569 os.chown(os.path.join(root, name), -1, gid)
1570 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1571 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1572 passdb=s4_passdb, service=SYSVOL_SERVICE)
1573 for name in dirs:
1574 if use_ntvfs and canchown:
1575 os.chown(os.path.join(root, name), -1, gid)
1576 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1577 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1578 passdb=s4_passdb, service=SYSVOL_SERVICE)
1580 # Set acls on Policy folder and policies folders
1581 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1583 def acl_type(direct_db_access):
1584 if direct_db_access:
1585 return "DB"
1586 else:
1587 return "VFS"
1589 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1590 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1591 fsacl_sddl = fsacl.as_sddl(domainsid)
1592 if fsacl_sddl != acl:
1593 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))
1595 for root, dirs, files in os.walk(path, topdown=False):
1596 for name in files:
1597 fsacl = getntacl(lp, os.path.join(root, name),
1598 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1599 if fsacl is None:
1600 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1601 fsacl_sddl = fsacl.as_sddl(domainsid)
1602 if fsacl_sddl != acl:
1603 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))
1605 for name in dirs:
1606 fsacl = getntacl(lp, os.path.join(root, name),
1607 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1608 if fsacl is None:
1609 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1610 fsacl_sddl = fsacl.as_sddl(domainsid)
1611 if fsacl_sddl != acl:
1612 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))
1615 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1616 direct_db_access):
1617 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1618 folders beneath.
1620 :param sysvol: Physical path for the sysvol folder
1621 :param dnsdomain: The DNS name of the domain
1622 :param domainsid: The SID of the domain
1623 :param domaindn: The DN of the domain (ie. DC=...)
1624 :param samdb: An LDB object on the SAM db
1625 :param lp: an LP object
1628 # Set ACL for GPO root folder
1629 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1630 fsacl = getntacl(lp, root_policy_path,
1631 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1632 if fsacl is None:
1633 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1634 fsacl_sddl = fsacl.as_sddl(domainsid)
1635 if fsacl_sddl != POLICIES_ACL:
1636 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))
1637 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1638 attrs=["cn", "nTSecurityDescriptor"],
1639 expression="", scope=ldb.SCOPE_ONELEVEL)
1641 for policy in res:
1642 acl = ndr_unpack(security.descriptor,
1643 str(policy["nTSecurityDescriptor"])).as_sddl()
1644 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1645 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1646 domainsid, direct_db_access)
1649 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1650 lp):
1651 """Set the ACL for the sysvol share and the subfolders
1653 :param samdb: An LDB object on the SAM db
1654 :param netlogon: Physical path for the netlogon folder
1655 :param sysvol: Physical path for the sysvol folder
1656 :param uid: The UID of the "Administrator" user
1657 :param gid: The GID of the "Domain adminstrators" group
1658 :param domainsid: The SID of the domain
1659 :param dnsdomain: The DNS name of the domain
1660 :param domaindn: The DN of the domain (ie. DC=...)
1663 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1664 s3conf = s3param.get_context()
1665 s3conf.load(lp.configfile)
1666 # ensure we are using the right samba_dsdb passdb backend, no matter what
1667 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1668 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1669 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1671 # now ensure everything matches correctly, to avoid wierd issues
1672 if passdb.get_global_sam_sid() != domainsid:
1673 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))
1675 domain_info = s4_passdb.domain_info()
1676 if domain_info["dom_sid"] != domainsid:
1677 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))
1679 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1680 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()))
1682 # Ensure we can read this directly, and via the smbd VFS
1683 for direct_db_access in [True, False]:
1684 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1685 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1686 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1687 if fsacl is None:
1688 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1689 fsacl_sddl = fsacl.as_sddl(domainsid)
1690 if fsacl_sddl != SYSVOL_ACL:
1691 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))
1693 # Check acls on Policy folder and policies folders
1694 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1695 direct_db_access)
1698 def interface_ips_v4(lp):
1699 """return only IPv4 IPs"""
1700 ips = samba.interface_ips(lp, False)
1701 ret = []
1702 for i in ips:
1703 if i.find(':') == -1:
1704 ret.append(i)
1705 return ret
1708 def interface_ips_v6(lp, linklocal=False):
1709 """return only IPv6 IPs"""
1710 ips = samba.interface_ips(lp, False)
1711 ret = []
1712 for i in ips:
1713 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1714 ret.append(i)
1715 return ret
1718 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1719 domainsid, schema=None,
1720 targetdir=None, samdb_fill=FILL_FULL,
1721 hostip=None, hostip6=None,
1722 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1723 domainguid=None, policyguid=None, policyguid_dc=None,
1724 invocationid=None, machinepass=None, ntdsguid=None,
1725 dns_backend=None, dnspass=None,
1726 serverrole=None, dom_for_fun_level=None,
1727 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1728 # create/adapt the group policy GUIDs
1729 # Default GUID for default policy are described at
1730 # "How Core Group Policy Works"
1731 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1732 if policyguid is None:
1733 policyguid = DEFAULT_POLICY_GUID
1734 policyguid = policyguid.upper()
1735 if policyguid_dc is None:
1736 policyguid_dc = DEFAULT_DC_POLICY_GUID
1737 policyguid_dc = policyguid_dc.upper()
1739 if invocationid is None:
1740 invocationid = str(uuid.uuid4())
1742 if krbtgtpass is None:
1743 krbtgtpass = samba.generate_random_password(128, 255)
1744 if machinepass is None:
1745 machinepass = samba.generate_random_password(128, 255)
1746 if dnspass is None:
1747 dnspass = samba.generate_random_password(128, 255)
1749 samdb = fill_samdb(samdb, lp, names, logger=logger,
1750 domainsid=domainsid, schema=schema, domainguid=domainguid,
1751 policyguid=policyguid, policyguid_dc=policyguid_dc,
1752 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1753 invocationid=invocationid, machinepass=machinepass,
1754 dns_backend=dns_backend, dnspass=dnspass,
1755 ntdsguid=ntdsguid, serverrole=serverrole,
1756 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1757 next_rid=next_rid, dc_rid=dc_rid)
1759 if serverrole == "active directory domain controller":
1761 # Set up group policies (domain policy and domain controller
1762 # policy)
1763 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1764 policyguid_dc)
1765 if not skip_sysvolacl:
1766 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1767 paths.root_gid, domainsid, names.dnsdomain,
1768 names.domaindn, lp, use_ntvfs)
1769 else:
1770 logger.info("Setting acl on sysvol skipped")
1772 secretsdb_self_join(secrets_ldb, domain=names.domain,
1773 realm=names.realm, dnsdomain=names.dnsdomain,
1774 netbiosname=names.netbiosname, domainsid=domainsid,
1775 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1777 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1778 # In future, this might be determined from some configuration
1779 kerberos_enctypes = str(ENC_ALL_TYPES)
1781 try:
1782 msg = ldb.Message(ldb.Dn(samdb,
1783 samdb.searchone("distinguishedName",
1784 expression="samAccountName=%s$" % names.netbiosname,
1785 scope=ldb.SCOPE_SUBTREE)))
1786 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1787 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1788 name="msDS-SupportedEncryptionTypes")
1789 samdb.modify(msg)
1790 except ldb.LdbError, (enum, estr):
1791 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1792 # It might be that this attribute does not exist in this schema
1793 raise
1795 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1796 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1797 dnspass=dnspass, os_level=dom_for_fun_level,
1798 targetdir=targetdir, site=DEFAULTSITE)
1800 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1801 attribute="objectGUID")
1802 assert isinstance(domainguid, str)
1804 lastProvisionUSNs = get_last_provision_usn(samdb)
1805 maxUSN = get_max_usn(samdb, str(names.rootdn))
1806 if lastProvisionUSNs is not None:
1807 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1808 else:
1809 set_provision_usn(samdb, 0, maxUSN, invocationid)
1811 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1812 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1813 { 'NTDSGUID' : names.ntdsguid })
1815 # fix any dangling GUIDs from the provision
1816 logger.info("Fixing provision GUIDs")
1817 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1818 quiet=True)
1819 samdb.transaction_start()
1820 try:
1821 # a small number of GUIDs are missing because of ordering issues in the
1822 # provision code
1823 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1824 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1825 scope=ldb.SCOPE_BASE,
1826 attrs=['defaultObjectCategory'])
1827 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1828 scope=ldb.SCOPE_ONELEVEL,
1829 attrs=['ipsecOwnersReference',
1830 'ipsecFilterReference',
1831 'ipsecISAKMPReference',
1832 'ipsecNegotiationPolicyReference',
1833 'ipsecNFAReference'])
1834 except:
1835 samdb.transaction_cancel()
1836 raise
1837 else:
1838 samdb.transaction_commit()
1841 _ROLES_MAP = {
1842 "ROLE_STANDALONE": "standalone server",
1843 "ROLE_DOMAIN_MEMBER": "member server",
1844 "ROLE_DOMAIN_BDC": "active directory domain controller",
1845 "ROLE_DOMAIN_PDC": "active directory domain controller",
1846 "dc": "active directory domain controller",
1847 "member": "member server",
1848 "domain controller": "active directory domain controller",
1849 "active directory domain controller": "active directory domain controller",
1850 "member server": "member server",
1851 "standalone": "standalone server",
1852 "standalone server": "standalone server",
1856 def sanitize_server_role(role):
1857 """Sanitize a server role name.
1859 :param role: Server role
1860 :raise ValueError: If the role can not be interpreted
1861 :return: Sanitized server role (one of "member server",
1862 "active directory domain controller", "standalone server")
1864 try:
1865 return _ROLES_MAP[role]
1866 except KeyError:
1867 raise ValueError(role)
1870 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1871 maxuid, maxgid):
1872 """Create AD entries for the fake ypserver.
1874 This is needed for being able to manipulate posix attrs via ADUC.
1876 samdb.transaction_start()
1877 try:
1878 logger.info("Setting up fake yp server settings")
1879 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1880 "DOMAINDN": domaindn,
1881 "NETBIOSNAME": netbiosname,
1882 "NISDOMAIN": nisdomain,
1884 except:
1885 samdb.transaction_cancel()
1886 raise
1887 else:
1888 samdb.transaction_commit()
1891 def provision(logger, session_info, credentials, smbconf=None,
1892 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1893 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1894 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1895 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1896 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1897 dns_backend=None, dns_forwarder=None, dnspass=None,
1898 invocationid=None, machinepass=None, ntdsguid=None,
1899 root=None, nobody=None, users=None, backup=None, aci=None,
1900 serverrole=None, dom_for_fun_level=None, backend_type=None,
1901 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1902 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1903 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1904 """Provision samba4
1906 :note: caution, this wipes all existing data!
1909 try:
1910 serverrole = sanitize_server_role(serverrole)
1911 except ValueError:
1912 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1914 if ldapadminpass is None:
1915 # Make a new, random password between Samba and it's LDAP server
1916 ldapadminpass = samba.generate_random_password(128, 255)
1918 if backend_type is None:
1919 backend_type = "ldb"
1921 if domainsid is None:
1922 domainsid = security.random_sid()
1923 else:
1924 domainsid = security.dom_sid(domainsid)
1926 root_uid = findnss_uid([root or "root"])
1927 nobody_uid = findnss_uid([nobody or "nobody"])
1928 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1929 root_gid = pwd.getpwuid(root_uid).pw_gid
1931 try:
1932 bind_gid = findnss_gid(["bind", "named"])
1933 except KeyError:
1934 bind_gid = None
1936 if targetdir is not None:
1937 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1938 elif smbconf is None:
1939 smbconf = samba.param.default_path()
1940 if not os.path.exists(os.path.dirname(smbconf)):
1941 os.makedirs(os.path.dirname(smbconf))
1943 server_services = []
1944 global_param = {}
1945 if use_rfc2307:
1946 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1948 if dns_backend != "SAMBA_INTERNAL":
1949 server_services.append("-dns")
1950 else:
1951 if dns_forwarder is not None:
1952 global_param["dns forwarder"] = [dns_forwarder]
1954 if use_ntvfs:
1955 server_services.append("+smb")
1956 server_services.append("-s3fs")
1957 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1959 if len(server_services) > 0:
1960 global_param["server services"] = server_services
1962 # only install a new smb.conf if there isn't one there already
1963 if os.path.exists(smbconf):
1964 # if Samba Team members can't figure out the weird errors
1965 # loading an empty smb.conf gives, then we need to be smarter.
1966 # Pretend it just didn't exist --abartlet
1967 f = open(smbconf, 'r')
1968 try:
1969 data = f.read().lstrip()
1970 finally:
1971 f.close()
1972 if data is None or data == "":
1973 make_smbconf(smbconf, hostname, domain, realm,
1974 targetdir, serverrole=serverrole,
1975 eadb=useeadb, use_ntvfs=use_ntvfs,
1976 lp=lp, global_param=global_param)
1977 else:
1978 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1979 serverrole=serverrole,
1980 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1982 if lp is None:
1983 lp = samba.param.LoadParm()
1984 lp.load(smbconf)
1985 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1986 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1987 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1988 sitename=sitename, rootdn=rootdn)
1989 paths = provision_paths_from_lp(lp, names.dnsdomain)
1991 paths.bind_gid = bind_gid
1992 paths.root_uid = root_uid;
1993 paths.root_gid = root_gid
1995 if hostip is None:
1996 logger.info("Looking up IPv4 addresses")
1997 hostips = interface_ips_v4(lp)
1998 if len(hostips) > 0:
1999 hostip = hostips[0]
2000 if len(hostips) > 1:
2001 logger.warning("More than one IPv4 address found. Using %s",
2002 hostip)
2003 if hostip == "127.0.0.1":
2004 hostip = None
2005 if hostip is None:
2006 logger.warning("No IPv4 address will be assigned")
2008 if hostip6 is None:
2009 logger.info("Looking up IPv6 addresses")
2010 hostips = interface_ips_v6(lp, linklocal=False)
2011 if hostips:
2012 hostip6 = hostips[0]
2013 if len(hostips) > 1:
2014 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2015 if hostip6 is None:
2016 logger.warning("No IPv6 address will be assigned")
2018 names.hostip = hostip
2019 names.hostip6 = hostip6
2021 if serverrole is None:
2022 serverrole = lp.get("server role")
2024 if not os.path.exists(paths.private_dir):
2025 os.mkdir(paths.private_dir)
2026 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
2027 os.mkdir(os.path.join(paths.private_dir, "tls"))
2028 if not os.path.exists(paths.state_dir):
2029 os.mkdir(paths.state_dir)
2031 if paths.sysvol and not os.path.exists(paths.sysvol):
2032 os.makedirs(paths.sysvol, 0775)
2034 if not use_ntvfs and serverrole == "active directory domain controller":
2035 s3conf = s3param.get_context()
2036 s3conf.load(lp.configfile)
2038 if paths.sysvol is None:
2039 raise MissingShareError("sysvol", paths.smbconf)
2041 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
2042 try:
2043 try:
2044 smbd.set_simple_acl(file.name, 0755, root_gid)
2045 except Exception:
2046 if not smbd.have_posix_acls():
2047 # This clue is only strictly correct for RPM and
2048 # Debian-like Linux systems, but hopefully other users
2049 # will get enough clue from it.
2050 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.")
2052 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
2053 try:
2054 smbd.chown(file.name, root_uid, root_gid)
2055 except Exception:
2056 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
2057 finally:
2058 file.close()
2060 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2062 schema = Schema(domainsid, invocationid=invocationid,
2063 schemadn=names.schemadn)
2065 if backend_type == "ldb":
2066 provision_backend = LDBBackend(backend_type, paths=paths,
2067 lp=lp, credentials=credentials,
2068 names=names, logger=logger)
2069 elif backend_type == "existing":
2070 # If support for this is ever added back, then the URI will need to be
2071 # specified again
2072 provision_backend = ExistingBackend(backend_type, paths=paths,
2073 lp=lp, credentials=credentials,
2074 names=names, logger=logger,
2075 ldap_backend_forced_uri=None)
2076 elif backend_type == "fedora-ds":
2077 provision_backend = FDSBackend(backend_type, paths=paths,
2078 lp=lp, credentials=credentials,
2079 names=names, logger=logger, domainsid=domainsid,
2080 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2081 slapd_path=slapd_path,
2082 root=root)
2083 elif backend_type == "openldap":
2084 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2085 lp=lp, credentials=credentials,
2086 names=names, logger=logger, domainsid=domainsid,
2087 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2088 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
2089 else:
2090 raise ValueError("Unknown LDAP backend type selected")
2092 provision_backend.init()
2093 provision_backend.start()
2095 # only install a new shares config db if there is none
2096 if not os.path.exists(paths.shareconf):
2097 logger.info("Setting up share.ldb")
2098 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2099 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2101 logger.info("Setting up secrets.ldb")
2102 secrets_ldb = setup_secretsdb(paths,
2103 session_info=session_info,
2104 backend_credentials=provision_backend.secrets_credentials, lp=lp)
2106 try:
2107 logger.info("Setting up the registry")
2108 setup_registry(paths.hklm, session_info, lp=lp)
2110 logger.info("Setting up the privileges database")
2111 setup_privileges(paths.privilege, session_info, lp=lp)
2113 logger.info("Setting up idmap db")
2114 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2116 setup_name_mappings(idmap, sid=str(domainsid),
2117 root_uid=root_uid, nobody_uid=nobody_uid,
2118 users_gid=users_gid, root_gid=root_gid)
2120 logger.info("Setting up SAM db")
2121 samdb = setup_samdb(paths.samdb, session_info,
2122 provision_backend, lp, names, logger=logger,
2123 serverrole=serverrole,
2124 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2126 if serverrole == "active directory domain controller":
2127 if paths.netlogon is None:
2128 raise MissingShareError("netlogon", paths.smbconf)
2130 if paths.sysvol is None:
2131 raise MissingShareError("sysvol", paths.smbconf)
2133 if not os.path.isdir(paths.netlogon):
2134 os.makedirs(paths.netlogon, 0755)
2136 if adminpass is None:
2137 adminpass = samba.generate_random_password(12, 32)
2138 adminpass_generated = True
2139 else:
2140 adminpass_generated = False
2142 if samdb_fill == FILL_FULL:
2143 provision_fill(samdb, secrets_ldb, logger, names, paths,
2144 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2145 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2146 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2147 krbtgtpass=krbtgtpass, domainguid=domainguid,
2148 policyguid=policyguid, policyguid_dc=policyguid_dc,
2149 invocationid=invocationid, machinepass=machinepass,
2150 ntdsguid=ntdsguid, dns_backend=dns_backend,
2151 dnspass=dnspass, serverrole=serverrole,
2152 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2153 lp=lp, use_ntvfs=use_ntvfs,
2154 skip_sysvolacl=skip_sysvolacl)
2156 create_krb5_conf(paths.krb5conf,
2157 dnsdomain=names.dnsdomain, hostname=names.hostname,
2158 realm=names.realm)
2159 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2160 "generated at %s", paths.krb5conf)
2162 if serverrole == "active directory domain controller":
2163 create_dns_update_list(lp, logger, paths)
2165 backend_result = provision_backend.post_setup()
2166 provision_backend.shutdown()
2168 except:
2169 secrets_ldb.transaction_cancel()
2170 raise
2172 # Now commit the secrets.ldb to disk
2173 secrets_ldb.transaction_commit()
2175 # the commit creates the dns.keytab, now chown it
2176 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2177 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2178 try:
2179 os.chmod(dns_keytab_path, 0640)
2180 os.chown(dns_keytab_path, -1, paths.bind_gid)
2181 except OSError:
2182 if not os.environ.has_key('SAMBA_SELFTEST'):
2183 logger.info("Failed to chown %s to bind gid %u",
2184 dns_keytab_path, paths.bind_gid)
2186 result = ProvisionResult()
2187 result.server_role = serverrole
2188 result.domaindn = domaindn
2189 result.paths = paths
2190 result.names = names
2191 result.lp = lp
2192 result.samdb = samdb
2193 result.idmap = idmap
2194 result.domainsid = str(domainsid)
2196 if samdb_fill == FILL_FULL:
2197 result.adminpass_generated = adminpass_generated
2198 result.adminpass = adminpass
2199 else:
2200 result.adminpass_generated = False
2201 result.adminpass = None
2203 result.backend_result = backend_result
2205 if use_rfc2307:
2206 provision_fake_ypserver(logger=logger, samdb=samdb,
2207 domaindn=names.domaindn, netbiosname=names.netbiosname,
2208 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2210 return result
2213 def provision_become_dc(smbconf=None, targetdir=None,
2214 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2215 serverdn=None, domain=None, hostname=None, domainsid=None,
2216 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2217 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2218 dns_backend=None, root=None, nobody=None, users=None,
2219 backup=None, serverrole=None, ldap_backend=None,
2220 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2222 logger = logging.getLogger("provision")
2223 samba.set_debug_level(debuglevel)
2225 res = provision(logger, system_session(), None,
2226 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2227 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2228 configdn=configdn, serverdn=serverdn, domain=domain,
2229 hostname=hostname, hostip=None, domainsid=domainsid,
2230 machinepass=machinepass,
2231 serverrole="active directory domain controller",
2232 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2233 use_ntvfs=use_ntvfs)
2234 res.lp.set("debuglevel", str(debuglevel))
2235 return res
2238 def create_krb5_conf(path, dnsdomain, hostname, realm):
2239 """Write out a file containing zone statements suitable for inclusion in a
2240 named.conf file (including GSS-TSIG configuration).
2242 :param path: Path of the new named.conf file.
2243 :param dnsdomain: DNS Domain name
2244 :param hostname: Local hostname
2245 :param realm: Realm name
2247 setup_file(setup_path("krb5.conf"), path, {
2248 "DNSDOMAIN": dnsdomain,
2249 "HOSTNAME": hostname,
2250 "REALM": realm,
2254 class ProvisioningError(Exception):
2255 """A generic provision error."""
2257 def __init__(self, value):
2258 self.value = value
2260 def __str__(self):
2261 return "ProvisioningError: " + self.value
2264 class InvalidNetbiosName(Exception):
2265 """A specified name was not a valid NetBIOS name."""
2267 def __init__(self, name):
2268 super(InvalidNetbiosName, self).__init__(
2269 "The name '%r' is not a valid NetBIOS name" % name)
2272 class MissingShareError(ProvisioningError):
2274 def __init__(self, name, smbconf):
2275 super(MissingShareError, self).__init__(
2276 "Existing smb.conf does not have a [%s] share, but you are "
2277 "configuring a DC. Please remove %s or add the share manually." %
2278 (name, smbconf))