s4-openldap: Restored openldap-related options to the provision script
[Samba.git] / python / samba / provision / __init__.py
blob631fff6abfdad67cc4215dcefeacdadc05ab4a9f
1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba4 server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__ = "restructuredText"
29 from base64 import b64encode
30 import os
31 import re
32 import pwd
33 import grp
34 import logging
35 import time
36 import uuid
37 import socket
38 import urllib
39 import string
40 import tempfile
42 import ldb
44 from samba.auth import system_session, admin_session
45 import samba
46 from samba.samba3 import smbd, passdb
47 from samba.samba3 import param as s3param
48 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
49 from samba import (
50 Ldb,
51 MAX_NETBIOS_NAME_LEN,
52 check_all_substituted,
53 is_valid_netbios_char,
54 setup_file,
55 substitute_var,
56 valid_netbios_name,
57 version,
59 from samba.dcerpc import security, misc
60 from samba.dcerpc.misc import (
61 SEC_CHAN_BDC,
62 SEC_CHAN_WKSTA,
64 from samba.dsdb import (
65 DS_DOMAIN_FUNCTION_2003,
66 DS_DOMAIN_FUNCTION_2008_R2,
67 ENC_ALL_TYPES,
69 from samba.idmap import IDmapDB
70 from samba.ms_display_specifiers import read_ms_ldif
71 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
72 from samba.ndr import ndr_pack, ndr_unpack
73 from samba.provision.backend import (
74 ExistingBackend,
75 FDSBackend,
76 LDBBackend,
77 OpenLDAPBackend,
79 from samba.descriptor import (
80 get_empty_descriptor,
81 get_config_descriptor,
82 get_config_partitions_descriptor,
83 get_config_sites_descriptor,
84 get_config_ntds_quotas_descriptor,
85 get_config_delete_protected1_descriptor,
86 get_config_delete_protected1wd_descriptor,
87 get_config_delete_protected2_descriptor,
88 get_domain_descriptor,
89 get_domain_infrastructure_descriptor,
90 get_domain_builtin_descriptor,
91 get_domain_computers_descriptor,
92 get_domain_users_descriptor,
93 get_domain_controllers_descriptor,
94 get_domain_delete_protected1_descriptor,
95 get_domain_delete_protected2_descriptor,
96 get_dns_partition_descriptor,
97 get_dns_forest_microsoft_dns_descriptor,
98 get_dns_domain_microsoft_dns_descriptor,
100 from samba.provision.common import (
101 setup_path,
102 setup_add_ldif,
103 setup_modify_ldif,
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"][0]
205 names.schemadn = current[0]["schemaNamingContext"][0]
206 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
207 current[0]["defaultNamingContext"][0]))):
208 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
209 "is not the same ..." % (paths.samdb,
210 str(current[0]["defaultNamingContext"][0]),
211 paths.smbconf, basedn)))
213 names.domaindn=current[0]["defaultNamingContext"][0]
214 names.rootdn=current[0]["rootDomainNamingContext"][0]
215 names.ncs=current[0]["namingContexts"]
216 names.dnsforestdn = None
217 names.dnsdomaindn = None
219 for i in range(0, len(names.ncs)):
220 nc = names.ncs[i]
222 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
223 if nc == dnsforestdn:
224 names.dnsforestdn = dnsforestdn
225 continue
227 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
228 if nc == dnsdomaindn:
229 names.dnsdomaindn = dnsdomaindn
230 continue
232 # default site name
233 res3 = samdb.search(expression="(objectClass=site)",
234 base="CN=Sites," + names.configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
235 names.sitename = str(res3[0]["cn"])
237 # dns hostname and server dn
238 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
239 base="OU=Domain Controllers,%s" % basedn,
240 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
241 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
243 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
244 attrs=[], base=names.configdn)
245 names.serverdn = str(server_res[0].dn)
247 # invocation id/objectguid
248 res5 = samdb.search(expression="(objectClass=*)",
249 base="CN=NTDS Settings,%s" % str(names.serverdn),
250 scope=ldb.SCOPE_BASE,
251 attrs=["invocationID", "objectGUID"])
252 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
253 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
255 # domain guid/sid
256 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
257 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
258 "objectSid","msDS-Behavior-Version" ])
259 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
260 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
261 if res6[0].get("msDS-Behavior-Version") is None or \
262 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
263 names.domainlevel = DS_DOMAIN_FUNCTION_2000
264 else:
265 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
267 # policy guid
268 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
269 base="CN=Policies,CN=System," + basedn,
270 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
271 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
272 # dc policy guid
273 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
274 " Policy)",
275 base="CN=Policies,CN=System," + basedn,
276 scope=ldb.SCOPE_ONELEVEL,
277 attrs=["cn","displayName"])
278 if len(res8) == 1:
279 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
280 else:
281 names.policyid_dc = None
283 res9 = idmapdb.search(expression="(cn=%s-%s)" %
284 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
285 attrs=["xidNumber", "type"])
286 if len(res9) != 1:
287 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
288 if res9[0]["type"][0] == "ID_TYPE_BOTH":
289 names.root_gid = res9[0]["xidNumber"][0]
290 else:
291 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
293 res10 = samdb.search(expression="(samaccountname=dns)",
294 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
295 controls=["search_options:1:2"])
296 if (len(res10) > 0):
297 has_legacy_dns_account = True
298 else:
299 has_legacy_dns_account = False
301 res11 = samdb.search(expression="(samaccountname=dns-%s)" % names.netbiosname,
302 scope=ldb.SCOPE_SUBTREE, attrs=["dn"],
303 controls=["search_options:1:2"])
304 if (len(res11) > 0):
305 has_dns_account = True
306 else:
307 has_dns_account = False
309 if names.dnsdomaindn is not None:
310 if has_dns_account:
311 names.dns_backend = 'BIND9_DLZ'
312 else:
313 names.dns_backend = 'SAMBA_INTERNAL'
314 elif has_dns_account or has_legacy_dns_account:
315 names.dns_backend = 'BIND9_FLATFILE'
316 else:
317 names.dns_backend = 'NONE'
319 dns_admins_sid = get_dnsadmins_sid(samdb, names.domaindn)
320 names.name_map['DnsAdmins'] = str(dns_admins_sid)
322 return names
325 def update_provision_usn(samdb, low, high, id, replace=False):
326 """Update the field provisionUSN in sam.ldb
328 This field is used to track range of USN modified by provision and
329 upgradeprovision.
330 This value is used afterward by next provision to figure out if
331 the field have been modified since last provision.
333 :param samdb: An LDB object connect to sam.ldb
334 :param low: The lowest USN modified by this upgrade
335 :param high: The highest USN modified by this upgrade
336 :param id: The invocation id of the samba's dc
337 :param replace: A boolean indicating if the range should replace any
338 existing one or appended (default)
341 tab = []
342 if not replace:
343 entry = samdb.search(base="@PROVISION",
344 scope=ldb.SCOPE_BASE,
345 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
346 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
347 if not re.search(';', e):
348 e = "%s;%s" % (e, id)
349 tab.append(str(e))
351 tab.append("%s-%s;%s" % (low, high, id))
352 delta = ldb.Message()
353 delta.dn = ldb.Dn(samdb, "@PROVISION")
354 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
355 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
356 entry = samdb.search(expression='provisionnerID=*',
357 base="@PROVISION", scope=ldb.SCOPE_BASE,
358 attrs=["provisionnerID"])
359 if len(entry) == 0 or len(entry[0]) == 0:
360 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
361 samdb.modify(delta)
364 def set_provision_usn(samdb, low, high, id):
365 """Set the field provisionUSN in sam.ldb
366 This field is used to track range of USN modified by provision and
367 upgradeprovision.
368 This value is used afterward by next provision to figure out if
369 the field have been modified since last provision.
371 :param samdb: An LDB object connect to sam.ldb
372 :param low: The lowest USN modified by this upgrade
373 :param high: The highest USN modified by this upgrade
374 :param id: The invocationId of the provision"""
376 tab = []
377 tab.append("%s-%s;%s" % (low, high, id))
379 delta = ldb.Message()
380 delta.dn = ldb.Dn(samdb, "@PROVISION")
381 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
382 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
383 samdb.add(delta)
386 def get_max_usn(samdb,basedn):
387 """ This function return the biggest USN present in the provision
389 :param samdb: A LDB object pointing to the sam.ldb
390 :param basedn: A string containing the base DN of the provision
391 (ie. DC=foo, DC=bar)
392 :return: The biggest USN in the provision"""
394 res = samdb.search(expression="objectClass=*",base=basedn,
395 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
396 controls=["search_options:1:2",
397 "server_sort:1:1:uSNChanged",
398 "paged_results:1:1"])
399 return res[0]["uSNChanged"]
402 def get_last_provision_usn(sam):
403 """Get USNs ranges modified by a provision or an upgradeprovision
405 :param sam: An LDB object pointing to the sam.ldb
406 :return: a dictionary which keys are invocation id and values are an array
407 of integer representing the different ranges
409 try:
410 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
411 base="@PROVISION", scope=ldb.SCOPE_BASE,
412 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
413 except ldb.LdbError, (ecode, emsg):
414 if ecode == ldb.ERR_NO_SUCH_OBJECT:
415 return None
416 raise
417 if len(entry) > 0:
418 myids = []
419 range = {}
420 p = re.compile(r'-')
421 if entry[0].get("provisionnerID"):
422 for e in entry[0]["provisionnerID"]:
423 myids.append(str(e))
424 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
425 tab1 = str(r).split(';')
426 if len(tab1) == 2:
427 id = tab1[1]
428 else:
429 id = "default"
430 if (len(myids) > 0 and id not in myids):
431 continue
432 tab2 = p.split(tab1[0])
433 if range.get(id) is None:
434 range[id] = []
435 range[id].append(tab2[0])
436 range[id].append(tab2[1])
437 return range
438 else:
439 return None
442 class ProvisionResult(object):
443 """Result of a provision.
445 :ivar server_role: The server role
446 :ivar paths: ProvisionPaths instance
447 :ivar domaindn: The domain dn, as string
450 def __init__(self):
451 self.server_role = None
452 self.paths = None
453 self.domaindn = None
454 self.lp = None
455 self.samdb = None
456 self.idmap = None
457 self.names = None
458 self.domainsid = None
459 self.adminpass_generated = None
460 self.adminpass = None
461 self.backend_result = None
463 def report_logger(self, logger):
464 """Report this provision result to a logger."""
465 logger.info(
466 "Once the above files are installed, your Samba4 server will "
467 "be ready to use")
468 if self.adminpass_generated:
469 logger.info("Admin password: %s", self.adminpass)
470 logger.info("Server Role: %s", self.server_role)
471 logger.info("Hostname: %s", self.names.hostname)
472 logger.info("NetBIOS Domain: %s", self.names.domain)
473 logger.info("DNS Domain: %s", self.names.dnsdomain)
474 logger.info("DOMAIN SID: %s", self.domainsid)
476 if self.backend_result:
477 self.backend_result.report_logger(logger)
480 def check_install(lp, session_info, credentials):
481 """Check whether the current install seems ok.
483 :param lp: Loadparm context
484 :param session_info: Session information
485 :param credentials: Credentials
487 if lp.get("realm") == "":
488 raise Exception("Realm empty")
489 samdb = Ldb(lp.samdb_url(), session_info=session_info,
490 credentials=credentials, lp=lp)
491 if len(samdb.search("(cn=Administrator)")) != 1:
492 raise ProvisioningError("No administrator account found")
495 def findnss(nssfn, names):
496 """Find a user or group from a list of possibilities.
498 :param nssfn: NSS Function to try (should raise KeyError if not found)
499 :param names: Names to check.
500 :return: Value return by first names list.
502 for name in names:
503 try:
504 return nssfn(name)
505 except KeyError:
506 pass
507 raise KeyError("Unable to find user/group in %r" % names)
510 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
511 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
514 def provision_paths_from_lp(lp, dnsdomain):
515 """Set the default paths for provisioning.
517 :param lp: Loadparm context.
518 :param dnsdomain: DNS Domain name
520 paths = ProvisionPaths()
521 paths.private_dir = lp.get("private dir")
522 paths.state_dir = lp.get("state directory")
524 # This is stored without path prefix for the "privateKeytab" attribute in
525 # "secrets_dns.ldif".
526 paths.dns_keytab = "dns.keytab"
527 paths.keytab = "secrets.keytab"
529 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
530 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
531 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
532 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
533 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
534 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
535 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
536 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
537 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
538 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
539 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
540 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
541 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
542 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
543 paths.hklm = "hklm.ldb"
544 paths.hkcr = "hkcr.ldb"
545 paths.hkcu = "hkcu.ldb"
546 paths.hku = "hku.ldb"
547 paths.hkpd = "hkpd.ldb"
548 paths.hkpt = "hkpt.ldb"
549 paths.sysvol = lp.get("path", "sysvol")
550 paths.netlogon = lp.get("path", "netlogon")
551 paths.smbconf = lp.configfile
552 return paths
555 def determine_netbios_name(hostname):
556 """Determine a netbios name from a hostname."""
557 # remove forbidden chars and force the length to be <16
558 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
559 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
562 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
563 serverrole=None, rootdn=None, domaindn=None, configdn=None,
564 schemadn=None, serverdn=None, sitename=None,
565 domain_names_forced=False):
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 and not domain_names_forced:
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):
1709 """return only IPv6 IPs"""
1710 ips = samba.interface_ips(lp, False)
1711 ret = []
1712 for i in ips:
1713 if 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=None,
1902 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1903 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
1904 ldap_backend_forced_uri=None, nosync=False, ldap_dryrun_mode=False, ldap_backend_extra_port=None):
1905 """Provision samba4
1907 :note: caution, this wipes all existing data!
1910 try:
1911 serverrole = sanitize_server_role(serverrole)
1912 except ValueError:
1913 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1915 if ldapadminpass is None:
1916 # Make a new, random password between Samba and it's LDAP server
1917 ldapadminpass = samba.generate_random_password(128, 255)
1919 if backend_type is None:
1920 backend_type = "ldb"
1922 if domainsid is None:
1923 domainsid = security.random_sid()
1924 else:
1925 domainsid = security.dom_sid(domainsid)
1927 root_uid = findnss_uid([root or "root"])
1928 nobody_uid = findnss_uid([nobody or "nobody"])
1929 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1930 root_gid = pwd.getpwuid(root_uid).pw_gid
1932 try:
1933 bind_gid = findnss_gid(["bind", "named"])
1934 except KeyError:
1935 bind_gid = None
1937 if targetdir is not None:
1938 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1939 elif smbconf is None:
1940 smbconf = samba.param.default_path()
1941 if not os.path.exists(os.path.dirname(smbconf)):
1942 os.makedirs(os.path.dirname(smbconf))
1944 server_services = []
1945 global_param = {}
1946 if use_rfc2307:
1947 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1949 if dns_backend != "SAMBA_INTERNAL":
1950 server_services.append("-dns")
1951 else:
1952 if dns_forwarder is not None:
1953 global_param["dns forwarder"] = [dns_forwarder]
1955 if use_ntvfs:
1956 server_services.append("+smb")
1957 server_services.append("-s3fs")
1958 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1960 if len(server_services) > 0:
1961 global_param["server services"] = server_services
1963 # only install a new smb.conf if there isn't one there already
1964 if os.path.exists(smbconf):
1965 # if Samba Team members can't figure out the weird errors
1966 # loading an empty smb.conf gives, then we need to be smarter.
1967 # Pretend it just didn't exist --abartlet
1968 f = open(smbconf, 'r')
1969 try:
1970 data = f.read().lstrip()
1971 finally:
1972 f.close()
1973 if data is None or data == "":
1974 make_smbconf(smbconf, hostname, domain, realm,
1975 targetdir, serverrole=serverrole,
1976 eadb=useeadb, use_ntvfs=use_ntvfs,
1977 lp=lp, global_param=global_param)
1978 else:
1979 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1980 serverrole=serverrole,
1981 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1983 if lp is None:
1984 lp = samba.param.LoadParm()
1985 lp.load(smbconf)
1986 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1987 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1988 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1989 sitename=sitename, rootdn=rootdn, domain_names_forced=(samdb_fill == FILL_DRS))
1990 paths = provision_paths_from_lp(lp, names.dnsdomain)
1992 paths.bind_gid = bind_gid
1993 paths.root_uid = root_uid;
1994 paths.root_gid = root_gid
1996 if hostip is None:
1997 logger.info("Looking up IPv4 addresses")
1998 hostips = interface_ips_v4(lp)
1999 if len(hostips) > 0:
2000 hostip = hostips[0]
2001 if len(hostips) > 1:
2002 logger.warning("More than one IPv4 address found. Using %s",
2003 hostip)
2004 if hostip == "127.0.0.1":
2005 hostip = None
2006 if hostip is None:
2007 logger.warning("No IPv4 address will be assigned")
2009 if hostip6 is None:
2010 logger.info("Looking up IPv6 addresses")
2011 hostips = interface_ips_v6(lp)
2012 if hostips:
2013 hostip6 = hostips[0]
2014 if len(hostips) > 1:
2015 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2016 if hostip6 is None:
2017 logger.warning("No IPv6 address will be assigned")
2019 names.hostip = hostip
2020 names.hostip6 = hostip6
2022 if serverrole is None:
2023 serverrole = lp.get("server role")
2025 if not os.path.exists(paths.private_dir):
2026 os.mkdir(paths.private_dir)
2027 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
2028 os.mkdir(os.path.join(paths.private_dir, "tls"))
2029 if not os.path.exists(paths.state_dir):
2030 os.mkdir(paths.state_dir)
2032 if paths.sysvol and not os.path.exists(paths.sysvol):
2033 os.makedirs(paths.sysvol, 0775)
2035 if not use_ntvfs and serverrole == "active directory domain controller":
2036 s3conf = s3param.get_context()
2037 s3conf.load(lp.configfile)
2039 if paths.sysvol is None:
2040 raise MissingShareError("sysvol", paths.smbconf)
2042 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
2043 try:
2044 try:
2045 smbd.set_simple_acl(file.name, 0755, root_gid)
2046 except Exception:
2047 if not smbd.have_posix_acls():
2048 # This clue is only strictly correct for RPM and
2049 # Debian-like Linux systems, but hopefully other users
2050 # will get enough clue from it.
2051 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.")
2053 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
2054 try:
2055 smbd.chown(file.name, root_uid, root_gid)
2056 except Exception:
2057 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
2058 finally:
2059 file.close()
2061 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2063 schema = Schema(domainsid, invocationid=invocationid,
2064 schemadn=names.schemadn)
2066 if backend_type == "ldb":
2067 provision_backend = LDBBackend(backend_type, paths=paths,
2068 lp=lp, credentials=credentials,
2069 names=names, logger=logger)
2070 elif backend_type == "existing":
2071 # If support for this is ever added back, then the URI will need to be
2072 # specified again
2073 provision_backend = ExistingBackend(backend_type, paths=paths,
2074 lp=lp, credentials=credentials,
2075 names=names, logger=logger,
2076 ldap_backend_forced_uri=ldap_backend_forced_uri)
2077 elif backend_type == "fedora-ds":
2078 provision_backend = FDSBackend(backend_type, paths=paths,
2079 lp=lp, credentials=credentials,
2080 names=names, logger=logger, domainsid=domainsid,
2081 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2082 slapd_path=slapd_path,
2083 root=root)
2084 elif backend_type == "openldap":
2085 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2086 lp=lp, credentials=credentials,
2087 names=names, logger=logger, domainsid=domainsid,
2088 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2089 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls,
2090 ldap_backend_extra_port=ldap_backend_extra_port,
2091 ldap_dryrun_mode=ldap_dryrun_mode, nosync=nosync,
2092 ldap_backend_forced_uri=ldap_backend_forced_uri)
2093 else:
2094 raise ValueError("Unknown LDAP backend type selected")
2096 provision_backend.init()
2097 provision_backend.start()
2099 # only install a new shares config db if there is none
2100 if not os.path.exists(paths.shareconf):
2101 logger.info("Setting up share.ldb")
2102 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2103 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2105 logger.info("Setting up secrets.ldb")
2106 secrets_ldb = setup_secretsdb(paths,
2107 session_info=session_info,
2108 backend_credentials=provision_backend.secrets_credentials, lp=lp)
2110 try:
2111 logger.info("Setting up the registry")
2112 setup_registry(paths.hklm, session_info, lp=lp)
2114 logger.info("Setting up the privileges database")
2115 setup_privileges(paths.privilege, session_info, lp=lp)
2117 logger.info("Setting up idmap db")
2118 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2120 setup_name_mappings(idmap, sid=str(domainsid),
2121 root_uid=root_uid, nobody_uid=nobody_uid,
2122 users_gid=users_gid, root_gid=root_gid)
2124 logger.info("Setting up SAM db")
2125 samdb = setup_samdb(paths.samdb, session_info,
2126 provision_backend, lp, names, logger=logger,
2127 serverrole=serverrole,
2128 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2130 if serverrole == "active directory domain controller":
2131 if paths.netlogon is None:
2132 raise MissingShareError("netlogon", paths.smbconf)
2134 if paths.sysvol is None:
2135 raise MissingShareError("sysvol", paths.smbconf)
2137 if not os.path.isdir(paths.netlogon):
2138 os.makedirs(paths.netlogon, 0755)
2140 if adminpass is None:
2141 adminpass = samba.generate_random_password(12, 32)
2142 adminpass_generated = True
2143 else:
2144 adminpass = unicode(adminpass, 'utf-8')
2145 adminpass_generated = False
2147 if samdb_fill == FILL_FULL:
2148 provision_fill(samdb, secrets_ldb, logger, names, paths,
2149 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2150 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2151 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2152 krbtgtpass=krbtgtpass, domainguid=domainguid,
2153 policyguid=policyguid, policyguid_dc=policyguid_dc,
2154 invocationid=invocationid, machinepass=machinepass,
2155 ntdsguid=ntdsguid, dns_backend=dns_backend,
2156 dnspass=dnspass, serverrole=serverrole,
2157 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2158 lp=lp, use_ntvfs=use_ntvfs,
2159 skip_sysvolacl=skip_sysvolacl)
2161 create_krb5_conf(paths.krb5conf,
2162 dnsdomain=names.dnsdomain, hostname=names.hostname,
2163 realm=names.realm)
2164 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2165 "generated at %s", paths.krb5conf)
2167 if serverrole == "active directory domain controller":
2168 create_dns_update_list(lp, logger, paths)
2170 backend_result = provision_backend.post_setup()
2171 provision_backend.shutdown()
2173 except:
2174 secrets_ldb.transaction_cancel()
2175 raise
2177 # Now commit the secrets.ldb to disk
2178 secrets_ldb.transaction_commit()
2180 # the commit creates the dns.keytab, now chown it
2181 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2182 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2183 try:
2184 os.chmod(dns_keytab_path, 0640)
2185 os.chown(dns_keytab_path, -1, paths.bind_gid)
2186 except OSError:
2187 if not os.environ.has_key('SAMBA_SELFTEST'):
2188 logger.info("Failed to chown %s to bind gid %u",
2189 dns_keytab_path, paths.bind_gid)
2191 result = ProvisionResult()
2192 result.server_role = serverrole
2193 result.domaindn = domaindn
2194 result.paths = paths
2195 result.names = names
2196 result.lp = lp
2197 result.samdb = samdb
2198 result.idmap = idmap
2199 result.domainsid = str(domainsid)
2201 if samdb_fill == FILL_FULL:
2202 result.adminpass_generated = adminpass_generated
2203 result.adminpass = adminpass
2204 else:
2205 result.adminpass_generated = False
2206 result.adminpass = None
2208 result.backend_result = backend_result
2210 if use_rfc2307:
2211 provision_fake_ypserver(logger=logger, samdb=samdb,
2212 domaindn=names.domaindn, netbiosname=names.netbiosname,
2213 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2215 return result
2218 def provision_become_dc(smbconf=None, targetdir=None,
2219 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2220 serverdn=None, domain=None, hostname=None, domainsid=None,
2221 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2222 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2223 dns_backend=None, root=None, nobody=None, users=None,
2224 backup=None, serverrole=None, ldap_backend=None,
2225 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2227 logger = logging.getLogger("provision")
2228 samba.set_debug_level(debuglevel)
2230 res = provision(logger, system_session(), None,
2231 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2232 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2233 configdn=configdn, serverdn=serverdn, domain=domain,
2234 hostname=hostname, hostip=None, domainsid=domainsid,
2235 machinepass=machinepass,
2236 serverrole="active directory domain controller",
2237 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2238 use_ntvfs=use_ntvfs)
2239 res.lp.set("debuglevel", str(debuglevel))
2240 return res
2243 def create_krb5_conf(path, dnsdomain, hostname, realm):
2244 """Write out a file containing zone statements suitable for inclusion in a
2245 named.conf file (including GSS-TSIG configuration).
2247 :param path: Path of the new named.conf file.
2248 :param dnsdomain: DNS Domain name
2249 :param hostname: Local hostname
2250 :param realm: Realm name
2252 setup_file(setup_path("krb5.conf"), path, {
2253 "DNSDOMAIN": dnsdomain,
2254 "HOSTNAME": hostname,
2255 "REALM": realm,
2259 class ProvisioningError(Exception):
2260 """A generic provision error."""
2262 def __init__(self, value):
2263 self.value = value
2265 def __str__(self):
2266 return "ProvisioningError: " + self.value
2269 class InvalidNetbiosName(Exception):
2270 """A specified name was not a valid NetBIOS name."""
2272 def __init__(self, name):
2273 super(InvalidNetbiosName, self).__init__(
2274 "The name '%r' is not a valid NetBIOS name" % name)
2277 class MissingShareError(ProvisioningError):
2279 def __init__(self, name, smbconf):
2280 super(MissingShareError, self).__init__(
2281 "Existing smb.conf does not have a [%s] share, but you are "
2282 "configuring a DC. Please remove %s or add the share manually." %
2283 (name, smbconf))