shadow_copy2: improve debug in shadow_copy2_convert() in snapdirseverywhere mode
[Samba.git] / python / samba / provision / __init__.py
blob0a54af8ad20686f8150d5a32a3e6e7a3e8b7a5d7
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 """Guess configuration settings to use."""
567 if hostname is None:
568 hostname = socket.gethostname().split(".")[0]
570 netbiosname = lp.get("netbios name")
571 if netbiosname is None:
572 netbiosname = determine_netbios_name(hostname)
573 netbiosname = netbiosname.upper()
574 if not valid_netbios_name(netbiosname):
575 raise InvalidNetbiosName(netbiosname)
577 if dnsdomain is None:
578 dnsdomain = lp.get("realm")
579 if dnsdomain is None or dnsdomain == "":
580 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
582 dnsdomain = dnsdomain.lower()
584 if serverrole is None:
585 serverrole = lp.get("server role")
586 if serverrole is None:
587 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
589 serverrole = serverrole.lower()
591 realm = dnsdomain.upper()
593 if lp.get("realm") == "":
594 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
596 if lp.get("realm").upper() != realm:
597 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))
599 if lp.get("server role").lower() != serverrole:
600 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))
602 if serverrole == "active directory domain controller":
603 if domain is None:
604 # This will, for better or worse, default to 'WORKGROUP'
605 domain = lp.get("workgroup")
606 domain = domain.upper()
608 if lp.get("workgroup").upper() != domain:
609 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))
611 if domaindn is None:
612 domaindn = samba.dn_from_dns_name(dnsdomain)
614 if domain == netbiosname:
615 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
616 else:
617 domain = netbiosname
618 if domaindn is None:
619 domaindn = "DC=" + netbiosname
621 if not valid_netbios_name(domain):
622 raise InvalidNetbiosName(domain)
624 if hostname.upper() == realm:
625 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
626 if netbiosname.upper() == realm:
627 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
628 if domain == realm:
629 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
631 if rootdn is None:
632 rootdn = domaindn
634 if configdn is None:
635 configdn = "CN=Configuration," + rootdn
636 if schemadn is None:
637 schemadn = "CN=Schema," + configdn
639 if sitename is None:
640 sitename = DEFAULTSITE
642 names = ProvisionNames()
643 names.rootdn = rootdn
644 names.domaindn = domaindn
645 names.configdn = configdn
646 names.schemadn = schemadn
647 names.ldapmanagerdn = "CN=Manager," + rootdn
648 names.dnsdomain = dnsdomain
649 names.domain = domain
650 names.realm = realm
651 names.netbiosname = netbiosname
652 names.hostname = hostname
653 names.sitename = sitename
654 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
655 netbiosname, sitename, configdn)
657 return names
660 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
661 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
662 global_param=None):
663 """Create a new smb.conf file based on a couple of basic settings.
665 assert smbconf is not None
667 if hostname is None:
668 hostname = socket.gethostname().split(".")[0]
670 netbiosname = determine_netbios_name(hostname)
672 if serverrole is None:
673 serverrole = "standalone server"
675 assert domain is not None
676 domain = domain.upper()
678 assert realm is not None
679 realm = realm.upper()
681 global_settings = {
682 "netbios name": netbiosname,
683 "workgroup": domain,
684 "realm": realm,
685 "server role": serverrole,
688 if lp is None:
689 lp = samba.param.LoadParm()
690 #Load non-existent file
691 if os.path.exists(smbconf):
692 lp.load(smbconf)
694 if global_param is not None:
695 for ent in global_param:
696 if global_param[ent] is not None:
697 global_settings[ent] = " ".join(global_param[ent])
699 if targetdir is not None:
700 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
701 global_settings["lock dir"] = os.path.abspath(targetdir)
702 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
703 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
705 lp.set("lock dir", os.path.abspath(targetdir))
706 lp.set("state directory", global_settings["state directory"])
707 lp.set("cache directory", global_settings["cache directory"])
709 if eadb:
710 if use_ntvfs and not lp.get("posix:eadb"):
711 if targetdir is not None:
712 privdir = os.path.join(targetdir, "private")
713 else:
714 privdir = lp.get("private dir")
715 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
716 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
717 if targetdir is not None:
718 statedir = os.path.join(targetdir, "state")
719 else:
720 statedir = lp.get("state directory")
721 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
723 shares = {}
724 if serverrole == "active directory domain controller":
725 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
726 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
727 "scripts")
728 else:
729 global_settings["passdb backend"] = "samba_dsdb"
731 f = open(smbconf, 'w')
732 try:
733 f.write("[globals]\n")
734 for key, val in global_settings.iteritems():
735 f.write("\t%s = %s\n" % (key, val))
736 f.write("\n")
738 for name, path in shares.iteritems():
739 f.write("[%s]\n" % name)
740 f.write("\tpath = %s\n" % path)
741 f.write("\tread only = no\n")
742 f.write("\n")
743 finally:
744 f.close()
745 # reload the smb.conf
746 lp.load(smbconf)
748 # and dump it without any values that are the default
749 # this ensures that any smb.conf parameters that were set
750 # on the provision/join command line are set in the resulting smb.conf
751 f = open(smbconf, mode='w')
752 try:
753 lp.dump(f, False)
754 finally:
755 f.close()
758 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
759 users_gid, root_gid):
760 """setup reasonable name mappings for sam names to unix names.
762 :param samdb: SamDB object.
763 :param idmap: IDmap db object.
764 :param sid: The domain sid.
765 :param domaindn: The domain DN.
766 :param root_uid: uid of the UNIX root user.
767 :param nobody_uid: uid of the UNIX nobody user.
768 :param users_gid: gid of the UNIX users group.
769 :param root_gid: gid of the UNIX root group.
771 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
773 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
774 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
777 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
778 provision_backend, names, schema, serverrole,
779 erase=False):
780 """Setup the partitions for the SAM database.
782 Alternatively, provision() may call this, and then populate the database.
784 :note: This will wipe the Sam Database!
786 :note: This function always removes the local SAM LDB file. The erase
787 parameter controls whether to erase the existing data, which
788 may not be stored locally but in LDAP.
791 assert session_info is not None
793 # We use options=["modules:"] to stop the modules loading - we
794 # just want to wipe and re-initialise the database, not start it up
796 try:
797 os.unlink(samdb_path)
798 except OSError:
799 pass
801 samdb = Ldb(url=samdb_path, session_info=session_info,
802 lp=lp, options=["modules:"])
804 ldap_backend_line = "# No LDAP backend"
805 if provision_backend.type != "ldb":
806 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
808 samdb.transaction_start()
809 try:
810 logger.info("Setting up sam.ldb partitions and settings")
811 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
812 "LDAP_BACKEND_LINE": ldap_backend_line
816 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
817 "BACKEND_TYPE": provision_backend.type,
818 "SERVER_ROLE": serverrole
821 logger.info("Setting up sam.ldb rootDSE")
822 setup_samdb_rootdse(samdb, names)
823 except:
824 samdb.transaction_cancel()
825 raise
826 else:
827 samdb.transaction_commit()
830 def secretsdb_self_join(secretsdb, domain,
831 netbiosname, machinepass, domainsid=None,
832 realm=None, dnsdomain=None,
833 keytab_path=None,
834 key_version_number=1,
835 secure_channel_type=SEC_CHAN_WKSTA):
836 """Add domain join-specific bits to a secrets database.
838 :param secretsdb: Ldb Handle to the secrets database
839 :param machinepass: Machine password
841 attrs = ["whenChanged",
842 "secret",
843 "priorSecret",
844 "priorChanged",
845 "krb5Keytab",
846 "privateKeytab"]
848 if realm is not None:
849 if dnsdomain is None:
850 dnsdomain = realm.lower()
851 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
852 else:
853 dnsname = None
854 shortname = netbiosname.lower()
856 # We don't need to set msg["flatname"] here, because rdn_name will handle
857 # it, and it causes problems for modifies anyway
858 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
859 msg["secureChannelType"] = [str(secure_channel_type)]
860 msg["objectClass"] = ["top", "primaryDomain"]
861 if dnsname is not None:
862 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
863 msg["realm"] = [realm]
864 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
865 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
866 msg["privateKeytab"] = ["secrets.keytab"]
868 msg["secret"] = [machinepass]
869 msg["samAccountName"] = ["%s$" % netbiosname]
870 msg["secureChannelType"] = [str(secure_channel_type)]
871 if domainsid is not None:
872 msg["objectSid"] = [ndr_pack(domainsid)]
874 # This complex expression tries to ensure that we don't have more
875 # than one record for this SID, realm or netbios domain at a time,
876 # but we don't delete the old record that we are about to modify,
877 # because that would delete the keytab and previous password.
878 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
879 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
880 scope=ldb.SCOPE_ONELEVEL)
882 for del_msg in res:
883 secretsdb.delete(del_msg.dn)
885 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
887 if len(res) == 1:
888 msg["priorSecret"] = [res[0]["secret"][0]]
889 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
891 try:
892 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
893 except KeyError:
894 pass
896 try:
897 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
898 except KeyError:
899 pass
901 for el in msg:
902 if el != 'dn':
903 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
904 secretsdb.modify(msg)
905 secretsdb.rename(res[0].dn, msg.dn)
906 else:
907 spn = [ 'HOST/%s' % shortname ]
908 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
909 # we are a domain controller then we add servicePrincipalName
910 # entries for the keytab code to update.
911 spn.extend([ 'HOST/%s' % dnsname ])
912 msg["servicePrincipalName"] = spn
914 secretsdb.add(msg)
917 def setup_secretsdb(paths, session_info, backend_credentials, lp):
918 """Setup the secrets database.
920 :note: This function does not handle exceptions and transaction on purpose,
921 it's up to the caller to do this job.
923 :param path: Path to the secrets database.
924 :param session_info: Session info.
925 :param credentials: Credentials
926 :param lp: Loadparm context
927 :return: LDB handle for the created secrets database
929 if os.path.exists(paths.secrets):
930 os.unlink(paths.secrets)
932 keytab_path = os.path.join(paths.private_dir, paths.keytab)
933 if os.path.exists(keytab_path):
934 os.unlink(keytab_path)
936 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
937 if os.path.exists(dns_keytab_path):
938 os.unlink(dns_keytab_path)
940 path = paths.secrets
942 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
943 secrets_ldb.erase()
944 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
945 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
946 secrets_ldb.transaction_start()
947 try:
948 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
950 if (backend_credentials is not None and
951 backend_credentials.authentication_requested()):
952 if backend_credentials.get_bind_dn() is not None:
953 setup_add_ldif(secrets_ldb,
954 setup_path("secrets_simple_ldap.ldif"), {
955 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
956 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
958 else:
959 setup_add_ldif(secrets_ldb,
960 setup_path("secrets_sasl_ldap.ldif"), {
961 "LDAPADMINUSER": backend_credentials.get_username(),
962 "LDAPADMINREALM": backend_credentials.get_realm(),
963 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
965 except:
966 secrets_ldb.transaction_cancel()
967 raise
968 return secrets_ldb
971 def setup_privileges(path, session_info, lp):
972 """Setup the privileges database.
974 :param path: Path to the privileges database.
975 :param session_info: Session info.
976 :param credentials: Credentials
977 :param lp: Loadparm context
978 :return: LDB handle for the created secrets database
980 if os.path.exists(path):
981 os.unlink(path)
982 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
983 privilege_ldb.erase()
984 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
987 def setup_registry(path, session_info, lp):
988 """Setup the registry.
990 :param path: Path to the registry database
991 :param session_info: Session information
992 :param credentials: Credentials
993 :param lp: Loadparm context
995 reg = samba.registry.Registry()
996 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
997 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
998 provision_reg = setup_path("provision.reg")
999 assert os.path.exists(provision_reg)
1000 reg.diff_apply(provision_reg)
1003 def setup_idmapdb(path, session_info, lp):
1004 """Setup the idmap database.
1006 :param path: path to the idmap database
1007 :param session_info: Session information
1008 :param credentials: Credentials
1009 :param lp: Loadparm context
1011 if os.path.exists(path):
1012 os.unlink(path)
1014 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1015 idmap_ldb.erase()
1016 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1017 return idmap_ldb
1020 def setup_samdb_rootdse(samdb, names):
1021 """Setup the SamDB rootdse.
1023 :param samdb: Sam Database handle
1025 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1026 "SCHEMADN": names.schemadn,
1027 "DOMAINDN": names.domaindn,
1028 "ROOTDN" : names.rootdn,
1029 "CONFIGDN": names.configdn,
1030 "SERVERDN": names.serverdn,
1034 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1035 dns_backend, dnspass, domainsid, next_rid, invocationid,
1036 policyguid, policyguid_dc,
1037 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1038 """Join a host to its own domain."""
1039 assert isinstance(invocationid, str)
1040 if ntdsguid is not None:
1041 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1042 else:
1043 ntdsguid_line = ""
1045 if dc_rid is None:
1046 dc_rid = next_rid
1048 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1049 "CONFIGDN": names.configdn,
1050 "SCHEMADN": names.schemadn,
1051 "DOMAINDN": names.domaindn,
1052 "SERVERDN": names.serverdn,
1053 "INVOCATIONID": invocationid,
1054 "NETBIOSNAME": names.netbiosname,
1055 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1056 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1057 "DOMAINSID": str(domainsid),
1058 "DCRID": str(dc_rid),
1059 "SAMBA_VERSION_STRING": version,
1060 "NTDSGUID": ntdsguid_line,
1061 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1062 domainControllerFunctionality),
1063 "RIDALLOCATIONSTART": str(next_rid + 100),
1064 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1066 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1067 "POLICYGUID": policyguid,
1068 "POLICYGUID_DC": policyguid_dc,
1069 "DNSDOMAIN": names.dnsdomain,
1070 "DOMAINDN": names.domaindn})
1072 # If we are setting up a subdomain, then this has been replicated in, so we
1073 # don't need to add it
1074 if fill == FILL_FULL:
1075 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1076 "CONFIGDN": names.configdn,
1077 "SCHEMADN": names.schemadn,
1078 "DOMAINDN": names.domaindn,
1079 "SERVERDN": names.serverdn,
1080 "INVOCATIONID": invocationid,
1081 "NETBIOSNAME": names.netbiosname,
1082 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1083 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1084 "DOMAINSID": str(domainsid),
1085 "DCRID": str(dc_rid),
1086 "SAMBA_VERSION_STRING": version,
1087 "NTDSGUID": ntdsguid_line,
1088 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1089 domainControllerFunctionality)})
1091 # Setup fSMORoleOwner entries to point at the newly created DC entry
1092 setup_modify_ldif(samdb,
1093 setup_path("provision_self_join_modify_config.ldif"), {
1094 "CONFIGDN": names.configdn,
1095 "SCHEMADN": names.schemadn,
1096 "DEFAULTSITE": names.sitename,
1097 "NETBIOSNAME": names.netbiosname,
1098 "SERVERDN": names.serverdn,
1101 system_session_info = system_session()
1102 samdb.set_session_info(system_session_info)
1103 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1104 # modify a serverReference under cn=config when we are a subdomain, we must
1105 # be system due to ACLs
1106 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1107 "DOMAINDN": names.domaindn,
1108 "SERVERDN": names.serverdn,
1109 "NETBIOSNAME": names.netbiosname,
1112 samdb.set_session_info(admin_session_info)
1114 if dns_backend != "SAMBA_INTERNAL":
1115 # This is Samba4 specific and should be replaced by the correct
1116 # DNS AD-style setup
1117 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1118 "DNSDOMAIN": names.dnsdomain,
1119 "DOMAINDN": names.domaindn,
1120 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1121 "HOSTNAME" : names.hostname,
1122 "DNSNAME" : '%s.%s' % (
1123 names.netbiosname.lower(), names.dnsdomain.lower())
1127 def getpolicypath(sysvolpath, dnsdomain, guid):
1128 """Return the physical path of policy given its guid.
1130 :param sysvolpath: Path to the sysvol folder
1131 :param dnsdomain: DNS name of the AD domain
1132 :param guid: The GUID of the policy
1133 :return: A string with the complete path to the policy folder
1135 if guid[0] != "{":
1136 guid = "{%s}" % guid
1137 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1138 return policy_path
1141 def create_gpo_struct(policy_path):
1142 if not os.path.exists(policy_path):
1143 os.makedirs(policy_path, 0775)
1144 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1145 try:
1146 f.write("[General]\r\nVersion=0")
1147 finally:
1148 f.close()
1149 p = os.path.join(policy_path, "MACHINE")
1150 if not os.path.exists(p):
1151 os.makedirs(p, 0775)
1152 p = os.path.join(policy_path, "USER")
1153 if not os.path.exists(p):
1154 os.makedirs(p, 0775)
1157 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1158 """Create the default GPO for a domain
1160 :param sysvolpath: Physical path for the sysvol folder
1161 :param dnsdomain: DNS domain name of the AD domain
1162 :param policyguid: GUID of the default domain policy
1163 :param policyguid_dc: GUID of the default domain controler policy
1165 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1166 create_gpo_struct(policy_path)
1168 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1169 create_gpo_struct(policy_path)
1172 def setup_samdb(path, session_info, provision_backend, lp, names,
1173 logger, fill, serverrole, schema, am_rodc=False):
1174 """Setup a complete SAM Database.
1176 :note: This will wipe the main SAM database file!
1179 # Also wipes the database
1180 setup_samdb_partitions(path, logger=logger, lp=lp,
1181 provision_backend=provision_backend, session_info=session_info,
1182 names=names, serverrole=serverrole, schema=schema)
1184 # Load the database, but don's load the global schema and don't connect
1185 # quite yet
1186 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1187 credentials=provision_backend.credentials, lp=lp,
1188 global_schema=False, am_rodc=am_rodc)
1190 logger.info("Pre-loading the Samba 4 and AD schema")
1192 # Load the schema from the one we computed earlier
1193 samdb.set_schema(schema, write_indices_and_attributes=False)
1195 # Set the NTDS settings DN manually - in order to have it already around
1196 # before the provisioned tree exists and we connect
1197 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1199 # And now we can connect to the DB - the schema won't be loaded from the
1200 # DB
1201 samdb.connect(path)
1203 # But we have to give it one more kick to have it use the schema
1204 # during provision - it needs, now that it is connected, to write
1205 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1206 samdb.set_schema(schema, write_indices_and_attributes=True)
1208 return samdb
1211 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1212 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1213 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1214 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1216 if next_rid is None:
1217 next_rid = 1000
1219 # Provision does not make much sense values larger than 1000000000
1220 # as the upper range of the rIDAvailablePool is 1073741823 and
1221 # we don't want to create a domain that cannot allocate rids.
1222 if next_rid < 1000 or next_rid > 1000000000:
1223 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1224 error += "the valid range is %u-%u. The default is %u." % (
1225 1000, 1000000000, 1000)
1226 raise ProvisioningError(error)
1228 # ATTENTION: Do NOT change these default values without discussion with the
1229 # team and/or release manager. They have a big impact on the whole program!
1230 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1232 if dom_for_fun_level is None:
1233 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1235 if dom_for_fun_level > domainControllerFunctionality:
1236 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!")
1238 domainFunctionality = dom_for_fun_level
1239 forestFunctionality = dom_for_fun_level
1241 # Set the NTDS settings DN manually - in order to have it already around
1242 # before the provisioned tree exists and we connect
1243 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1245 samdb.transaction_start()
1246 try:
1247 # Set the domain functionality levels onto the database.
1248 # Various module (the password_hash module in particular) need
1249 # to know what level of AD we are emulating.
1251 # These will be fixed into the database via the database
1252 # modifictions below, but we need them set from the start.
1253 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1254 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1255 samdb.set_opaque_integer("domainControllerFunctionality",
1256 domainControllerFunctionality)
1258 samdb.set_domain_sid(str(domainsid))
1259 samdb.set_invocation_id(invocationid)
1261 logger.info("Adding DomainDN: %s" % names.domaindn)
1263 # impersonate domain admin
1264 admin_session_info = admin_session(lp, str(domainsid))
1265 samdb.set_session_info(admin_session_info)
1266 if domainguid is not None:
1267 domainguid_line = "objectGUID: %s\n-" % domainguid
1268 else:
1269 domainguid_line = ""
1271 descr = b64encode(get_domain_descriptor(domainsid))
1272 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1273 "DOMAINDN": names.domaindn,
1274 "DOMAINSID": str(domainsid),
1275 "DESCRIPTOR": descr,
1276 "DOMAINGUID": domainguid_line
1279 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1280 "DOMAINDN": names.domaindn,
1281 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1282 "NEXTRID": str(next_rid),
1283 "DEFAULTSITE": names.sitename,
1284 "CONFIGDN": names.configdn,
1285 "POLICYGUID": policyguid,
1286 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1287 "SAMBA_VERSION_STRING": version
1290 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1291 if fill == FILL_FULL:
1292 logger.info("Adding configuration container")
1293 descr = b64encode(get_config_descriptor(domainsid))
1294 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1295 "CONFIGDN": names.configdn,
1296 "DESCRIPTOR": descr,
1299 # The LDIF here was created when the Schema object was constructed
1300 logger.info("Setting up sam.ldb schema")
1301 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1302 samdb.modify_ldif(schema.schema_dn_modify)
1303 samdb.write_prefixes_from_schema()
1304 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1305 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1306 {"SCHEMADN": names.schemadn})
1308 # Now register this container in the root of the forest
1309 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1310 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1311 "subRefs")
1313 except:
1314 samdb.transaction_cancel()
1315 raise
1316 else:
1317 samdb.transaction_commit()
1319 samdb.transaction_start()
1320 try:
1321 samdb.invocation_id = invocationid
1323 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1324 if fill == FILL_FULL:
1325 logger.info("Setting up sam.ldb configuration data")
1327 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1328 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1329 ntdsquotas_descr = b64encode(get_config_ntds_quotas_descriptor(domainsid))
1330 protected1_descr = b64encode(get_config_delete_protected1_descriptor(domainsid))
1331 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(domainsid))
1332 protected2_descr = b64encode(get_config_delete_protected2_descriptor(domainsid))
1334 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1335 "CONFIGDN": names.configdn,
1336 "NETBIOSNAME": names.netbiosname,
1337 "DEFAULTSITE": names.sitename,
1338 "DNSDOMAIN": names.dnsdomain,
1339 "DOMAIN": names.domain,
1340 "SCHEMADN": names.schemadn,
1341 "DOMAINDN": names.domaindn,
1342 "SERVERDN": names.serverdn,
1343 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1344 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1345 "NTDSQUOTAS_DESCRIPTOR": ntdsquotas_descr,
1346 "LOSTANDFOUND_DESCRIPTOR": protected1wd_descr,
1347 "SERVICES_DESCRIPTOR": protected1_descr,
1348 "PHYSICALLOCATIONS_DESCRIPTOR": protected1wd_descr,
1349 "FORESTUPDATES_DESCRIPTOR": protected1wd_descr,
1350 "EXTENDEDRIGHTS_DESCRIPTOR": protected2_descr,
1351 "PARTITIONS_DESCRIPTOR": partitions_descr,
1352 "SITES_DESCRIPTOR": sites_descr,
1355 logger.info("Setting up display specifiers")
1356 display_specifiers_ldif = read_ms_ldif(
1357 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1358 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1359 {"CONFIGDN": names.configdn})
1360 check_all_substituted(display_specifiers_ldif)
1361 samdb.add_ldif(display_specifiers_ldif)
1363 logger.info("Modifying display specifiers")
1364 setup_modify_ldif(samdb,
1365 setup_path("provision_configuration_modify.ldif"), {
1366 "CONFIGDN": names.configdn,
1367 "DISPLAYSPECIFIERS_DESCRIPTOR": protected2_descr
1370 logger.info("Adding users container")
1371 users_desc = b64encode(get_domain_users_descriptor(domainsid))
1372 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1373 "DOMAINDN": names.domaindn,
1374 "USERS_DESCRIPTOR": users_desc
1376 logger.info("Modifying users container")
1377 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1378 "DOMAINDN": names.domaindn})
1379 logger.info("Adding computers container")
1380 computers_desc = b64encode(get_domain_computers_descriptor(domainsid))
1381 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1382 "DOMAINDN": names.domaindn,
1383 "COMPUTERS_DESCRIPTOR": computers_desc
1385 logger.info("Modifying computers container")
1386 setup_modify_ldif(samdb,
1387 setup_path("provision_computers_modify.ldif"), {
1388 "DOMAINDN": names.domaindn})
1389 logger.info("Setting up sam.ldb data")
1390 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
1391 lostandfound_desc = b64encode(get_domain_delete_protected2_descriptor(domainsid))
1392 system_desc = b64encode(get_domain_delete_protected1_descriptor(domainsid))
1393 builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
1394 controllers_desc = b64encode(get_domain_controllers_descriptor(domainsid))
1395 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1396 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1397 "DOMAINDN": names.domaindn,
1398 "NETBIOSNAME": names.netbiosname,
1399 "DEFAULTSITE": names.sitename,
1400 "CONFIGDN": names.configdn,
1401 "SERVERDN": names.serverdn,
1402 "RIDAVAILABLESTART": str(next_rid + 600),
1403 "POLICYGUID_DC": policyguid_dc,
1404 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1405 "LOSTANDFOUND_DESCRIPTOR": lostandfound_desc,
1406 "SYSTEM_DESCRIPTOR": system_desc,
1407 "BUILTIN_DESCRIPTOR": builtin_desc,
1408 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1411 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1412 if fill == FILL_FULL:
1413 setup_modify_ldif(samdb,
1414 setup_path("provision_configuration_references.ldif"), {
1415 "CONFIGDN": names.configdn,
1416 "SCHEMADN": names.schemadn})
1418 logger.info("Setting up well known security principals")
1419 protected1wd_descr = b64encode(get_config_delete_protected1wd_descriptor(domainsid))
1420 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1421 "CONFIGDN": names.configdn,
1422 "WELLKNOWNPRINCIPALS_DESCRIPTOR": protected1wd_descr,
1425 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1426 setup_modify_ldif(samdb,
1427 setup_path("provision_basedn_references.ldif"),
1428 {"DOMAINDN": names.domaindn})
1430 logger.info("Setting up sam.ldb users and groups")
1431 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1432 "DOMAINDN": names.domaindn,
1433 "DOMAINSID": str(domainsid),
1434 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1435 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1438 logger.info("Setting up self join")
1439 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1440 invocationid=invocationid,
1441 dns_backend=dns_backend,
1442 dnspass=dnspass,
1443 machinepass=machinepass,
1444 domainsid=domainsid,
1445 next_rid=next_rid,
1446 dc_rid=dc_rid,
1447 policyguid=policyguid,
1448 policyguid_dc=policyguid_dc,
1449 domainControllerFunctionality=domainControllerFunctionality,
1450 ntdsguid=ntdsguid)
1452 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1453 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1454 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1455 assert isinstance(names.ntdsguid, str)
1456 except:
1457 samdb.transaction_cancel()
1458 raise
1459 else:
1460 samdb.transaction_commit()
1461 return samdb
1464 FILL_FULL = "FULL"
1465 FILL_SUBDOMAIN = "SUBDOMAIN"
1466 FILL_NT4SYNC = "NT4SYNC"
1467 FILL_DRS = "DRS"
1468 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1469 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)"
1472 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb):
1473 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1474 for root, dirs, files in os.walk(path, topdown=False):
1475 for name in files:
1476 setntacl(lp, os.path.join(root, name), acl, domsid,
1477 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1478 for name in dirs:
1479 setntacl(lp, os.path.join(root, name), acl, domsid,
1480 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1483 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1484 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1485 folders beneath.
1487 :param sysvol: Physical path for the sysvol folder
1488 :param dnsdomain: The DNS name of the domain
1489 :param domainsid: The SID of the domain
1490 :param domaindn: The DN of the domain (ie. DC=...)
1491 :param samdb: An LDB object on the SAM db
1492 :param lp: an LP object
1495 # Set ACL for GPO root folder
1496 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1497 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1498 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb)
1500 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1501 attrs=["cn", "nTSecurityDescriptor"],
1502 expression="", scope=ldb.SCOPE_ONELEVEL)
1504 for policy in res:
1505 acl = ndr_unpack(security.descriptor,
1506 str(policy["nTSecurityDescriptor"])).as_sddl()
1507 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1508 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1509 str(domainsid), use_ntvfs,
1510 passdb=passdb)
1513 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1514 domaindn, lp, use_ntvfs):
1515 """Set the ACL for the sysvol share and the subfolders
1517 :param samdb: An LDB object on the SAM db
1518 :param netlogon: Physical path for the netlogon folder
1519 :param sysvol: Physical path for the sysvol folder
1520 :param uid: The UID of the "Administrator" user
1521 :param gid: The GID of the "Domain adminstrators" group
1522 :param domainsid: The SID of the domain
1523 :param dnsdomain: The DNS name of the domain
1524 :param domaindn: The DN of the domain (ie. DC=...)
1526 s4_passdb = None
1528 if not use_ntvfs:
1529 # This will ensure that the smbd code we are running when setting ACLs
1530 # is initialised with the smb.conf
1531 s3conf = s3param.get_context()
1532 s3conf.load(lp.configfile)
1533 # ensure we are using the right samba_dsdb passdb backend, no matter what
1534 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1535 passdb.reload_static_pdb()
1537 # ensure that we init the samba_dsdb backend, so the domain sid is
1538 # marked in secrets.tdb
1539 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1541 # now ensure everything matches correctly, to avoid wierd issues
1542 if passdb.get_global_sam_sid() != domainsid:
1543 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))
1545 domain_info = s4_passdb.domain_info()
1546 if domain_info["dom_sid"] != domainsid:
1547 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))
1549 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1550 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()))
1553 try:
1554 if use_ntvfs:
1555 os.chown(sysvol, -1, gid)
1556 except OSError:
1557 canchown = False
1558 else:
1559 canchown = True
1561 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1562 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1563 for root, dirs, files in os.walk(sysvol, topdown=False):
1564 for name in files:
1565 if use_ntvfs and canchown:
1566 os.chown(os.path.join(root, name), -1, gid)
1567 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1568 for name in dirs:
1569 if use_ntvfs and canchown:
1570 os.chown(os.path.join(root, name), -1, gid)
1571 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=s4_passdb)
1573 # Set acls on Policy folder and policies folders
1574 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1576 def acl_type(direct_db_access):
1577 if direct_db_access:
1578 return "DB"
1579 else:
1580 return "VFS"
1582 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1583 fsacl = getntacl(lp, path, direct_db_access=direct_db_access)
1584 fsacl_sddl = fsacl.as_sddl(domainsid)
1585 if fsacl_sddl != acl:
1586 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))
1588 for root, dirs, files in os.walk(path, topdown=False):
1589 for name in files:
1590 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1591 if fsacl is None:
1592 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1593 fsacl_sddl = fsacl.as_sddl(domainsid)
1594 if fsacl_sddl != acl:
1595 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))
1597 for name in dirs:
1598 fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access)
1599 if fsacl is None:
1600 raise ProvisioningError('%s ACL on GPO directory %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 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))
1606 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1607 direct_db_access):
1608 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1609 folders beneath.
1611 :param sysvol: Physical path for the sysvol folder
1612 :param dnsdomain: The DNS name of the domain
1613 :param domainsid: The SID of the domain
1614 :param domaindn: The DN of the domain (ie. DC=...)
1615 :param samdb: An LDB object on the SAM db
1616 :param lp: an LP object
1619 # Set ACL for GPO root folder
1620 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1621 fsacl = getntacl(lp, root_policy_path, direct_db_access=direct_db_access)
1622 if fsacl is None:
1623 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1624 fsacl_sddl = fsacl.as_sddl(domainsid)
1625 if fsacl_sddl != POLICIES_ACL:
1626 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))
1627 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1628 attrs=["cn", "nTSecurityDescriptor"],
1629 expression="", scope=ldb.SCOPE_ONELEVEL)
1631 for policy in res:
1632 acl = ndr_unpack(security.descriptor,
1633 str(policy["nTSecurityDescriptor"])).as_sddl()
1634 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1635 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1636 domainsid, direct_db_access)
1639 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1640 lp):
1641 """Set the ACL for the sysvol share and the subfolders
1643 :param samdb: An LDB object on the SAM db
1644 :param netlogon: Physical path for the netlogon folder
1645 :param sysvol: Physical path for the sysvol folder
1646 :param uid: The UID of the "Administrator" user
1647 :param gid: The GID of the "Domain adminstrators" group
1648 :param domainsid: The SID of the domain
1649 :param dnsdomain: The DNS name of the domain
1650 :param domaindn: The DN of the domain (ie. DC=...)
1653 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1654 s3conf = s3param.get_context()
1655 s3conf.load(lp.configfile)
1656 # ensure we are using the right samba_dsdb passdb backend, no matter what
1657 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1658 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1659 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1661 # now ensure everything matches correctly, to avoid wierd issues
1662 if passdb.get_global_sam_sid() != domainsid:
1663 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))
1665 domain_info = s4_passdb.domain_info()
1666 if domain_info["dom_sid"] != domainsid:
1667 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))
1669 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1670 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()))
1672 # Ensure we can read this directly, and via the smbd VFS
1673 for direct_db_access in [True, False]:
1674 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1675 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1676 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access)
1677 if fsacl is None:
1678 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1679 fsacl_sddl = fsacl.as_sddl(domainsid)
1680 if fsacl_sddl != SYSVOL_ACL:
1681 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))
1683 # Check acls on Policy folder and policies folders
1684 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1685 direct_db_access)
1688 def interface_ips_v4(lp):
1689 """return only IPv4 IPs"""
1690 ips = samba.interface_ips(lp, False)
1691 ret = []
1692 for i in ips:
1693 if i.find(':') == -1:
1694 ret.append(i)
1695 return ret
1698 def interface_ips_v6(lp):
1699 """return only IPv6 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 provision_fill(samdb, secrets_ldb, logger, names, paths,
1709 domainsid, schema=None,
1710 targetdir=None, samdb_fill=FILL_FULL,
1711 hostip=None, hostip6=None,
1712 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1713 domainguid=None, policyguid=None, policyguid_dc=None,
1714 invocationid=None, machinepass=None, ntdsguid=None,
1715 dns_backend=None, dnspass=None,
1716 serverrole=None, dom_for_fun_level=None,
1717 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1718 # create/adapt the group policy GUIDs
1719 # Default GUID for default policy are described at
1720 # "How Core Group Policy Works"
1721 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1722 if policyguid is None:
1723 policyguid = DEFAULT_POLICY_GUID
1724 policyguid = policyguid.upper()
1725 if policyguid_dc is None:
1726 policyguid_dc = DEFAULT_DC_POLICY_GUID
1727 policyguid_dc = policyguid_dc.upper()
1729 if invocationid is None:
1730 invocationid = str(uuid.uuid4())
1732 if krbtgtpass is None:
1733 krbtgtpass = samba.generate_random_password(128, 255)
1734 if machinepass is None:
1735 machinepass = samba.generate_random_password(128, 255)
1736 if dnspass is None:
1737 dnspass = samba.generate_random_password(128, 255)
1739 samdb = fill_samdb(samdb, lp, names, logger=logger,
1740 domainsid=domainsid, schema=schema, domainguid=domainguid,
1741 policyguid=policyguid, policyguid_dc=policyguid_dc,
1742 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1743 invocationid=invocationid, machinepass=machinepass,
1744 dns_backend=dns_backend, dnspass=dnspass,
1745 ntdsguid=ntdsguid, serverrole=serverrole,
1746 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1747 next_rid=next_rid, dc_rid=dc_rid)
1749 if serverrole == "active directory domain controller":
1751 # Set up group policies (domain policy and domain controller
1752 # policy)
1753 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1754 policyguid_dc)
1755 if not skip_sysvolacl:
1756 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1757 paths.root_gid, domainsid, names.dnsdomain,
1758 names.domaindn, lp, use_ntvfs)
1759 else:
1760 logger.info("Setting acl on sysvol skipped")
1762 secretsdb_self_join(secrets_ldb, domain=names.domain,
1763 realm=names.realm, dnsdomain=names.dnsdomain,
1764 netbiosname=names.netbiosname, domainsid=domainsid,
1765 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1767 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1768 # In future, this might be determined from some configuration
1769 kerberos_enctypes = str(ENC_ALL_TYPES)
1771 try:
1772 msg = ldb.Message(ldb.Dn(samdb,
1773 samdb.searchone("distinguishedName",
1774 expression="samAccountName=%s$" % names.netbiosname,
1775 scope=ldb.SCOPE_SUBTREE)))
1776 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1777 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1778 name="msDS-SupportedEncryptionTypes")
1779 samdb.modify(msg)
1780 except ldb.LdbError, (enum, estr):
1781 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1782 # It might be that this attribute does not exist in this schema
1783 raise
1785 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1786 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1787 dnspass=dnspass, os_level=dom_for_fun_level,
1788 targetdir=targetdir, site=DEFAULTSITE)
1790 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1791 attribute="objectGUID")
1792 assert isinstance(domainguid, str)
1794 lastProvisionUSNs = get_last_provision_usn(samdb)
1795 maxUSN = get_max_usn(samdb, str(names.rootdn))
1796 if lastProvisionUSNs is not None:
1797 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1798 else:
1799 set_provision_usn(samdb, 0, maxUSN, invocationid)
1801 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1802 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1803 { 'NTDSGUID' : names.ntdsguid })
1805 # fix any dangling GUIDs from the provision
1806 logger.info("Fixing provision GUIDs")
1807 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1808 quiet=True)
1809 samdb.transaction_start()
1810 try:
1811 # a small number of GUIDs are missing because of ordering issues in the
1812 # provision code
1813 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1814 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1815 scope=ldb.SCOPE_BASE,
1816 attrs=['defaultObjectCategory'])
1817 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1818 scope=ldb.SCOPE_ONELEVEL,
1819 attrs=['ipsecOwnersReference',
1820 'ipsecFilterReference',
1821 'ipsecISAKMPReference',
1822 'ipsecNegotiationPolicyReference',
1823 'ipsecNFAReference'])
1824 except:
1825 samdb.transaction_cancel()
1826 raise
1827 else:
1828 samdb.transaction_commit()
1831 _ROLES_MAP = {
1832 "ROLE_STANDALONE": "standalone server",
1833 "ROLE_DOMAIN_MEMBER": "member server",
1834 "ROLE_DOMAIN_BDC": "active directory domain controller",
1835 "ROLE_DOMAIN_PDC": "active directory domain controller",
1836 "dc": "active directory domain controller",
1837 "member": "member server",
1838 "domain controller": "active directory domain controller",
1839 "active directory domain controller": "active directory domain controller",
1840 "member server": "member server",
1841 "standalone": "standalone server",
1842 "standalone server": "standalone server",
1846 def sanitize_server_role(role):
1847 """Sanitize a server role name.
1849 :param role: Server role
1850 :raise ValueError: If the role can not be interpreted
1851 :return: Sanitized server role (one of "member server",
1852 "active directory domain controller", "standalone server")
1854 try:
1855 return _ROLES_MAP[role]
1856 except KeyError:
1857 raise ValueError(role)
1860 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1861 maxuid, maxgid):
1862 """Create AD entries for the fake ypserver.
1864 This is needed for being able to manipulate posix attrs via ADUC.
1866 samdb.transaction_start()
1867 try:
1868 logger.info("Setting up fake yp server settings")
1869 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1870 "DOMAINDN": domaindn,
1871 "NETBIOSNAME": netbiosname,
1872 "NISDOMAIN": nisdomain,
1874 except:
1875 samdb.transaction_cancel()
1876 raise
1877 else:
1878 samdb.transaction_commit()
1881 def provision(logger, session_info, credentials, smbconf=None,
1882 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1883 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1884 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1885 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1886 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1887 dns_backend=None, dns_forwarder=None, dnspass=None,
1888 invocationid=None, machinepass=None, ntdsguid=None,
1889 root=None, nobody=None, users=None, backup=None, aci=None,
1890 serverrole=None, dom_for_fun_level=None, backend_type=None,
1891 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1892 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1893 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1894 """Provision samba4
1896 :note: caution, this wipes all existing data!
1899 try:
1900 serverrole = sanitize_server_role(serverrole)
1901 except ValueError:
1902 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1904 if ldapadminpass is None:
1905 # Make a new, random password between Samba and it's LDAP server
1906 ldapadminpass = samba.generate_random_password(128, 255)
1908 if backend_type is None:
1909 backend_type = "ldb"
1911 if domainsid is None:
1912 domainsid = security.random_sid()
1913 else:
1914 domainsid = security.dom_sid(domainsid)
1916 root_uid = findnss_uid([root or "root"])
1917 nobody_uid = findnss_uid([nobody or "nobody"])
1918 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1919 root_gid = pwd.getpwuid(root_uid).pw_gid
1921 try:
1922 bind_gid = findnss_gid(["bind", "named"])
1923 except KeyError:
1924 bind_gid = None
1926 if targetdir is not None:
1927 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1928 elif smbconf is None:
1929 smbconf = samba.param.default_path()
1930 if not os.path.exists(os.path.dirname(smbconf)):
1931 os.makedirs(os.path.dirname(smbconf))
1933 server_services = []
1934 global_param = {}
1935 if use_rfc2307:
1936 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1938 if dns_backend != "SAMBA_INTERNAL":
1939 server_services.append("-dns")
1940 else:
1941 if dns_forwarder is not None:
1942 global_param["dns forwarder"] = [dns_forwarder]
1944 if use_ntvfs:
1945 server_services.append("+smb")
1946 server_services.append("-s3fs")
1947 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1949 if len(server_services) > 0:
1950 global_param["server services"] = server_services
1952 # only install a new smb.conf if there isn't one there already
1953 if os.path.exists(smbconf):
1954 # if Samba Team members can't figure out the weird errors
1955 # loading an empty smb.conf gives, then we need to be smarter.
1956 # Pretend it just didn't exist --abartlet
1957 f = open(smbconf, 'r')
1958 try:
1959 data = f.read().lstrip()
1960 finally:
1961 f.close()
1962 if data is None or data == "":
1963 make_smbconf(smbconf, hostname, domain, realm,
1964 targetdir, serverrole=serverrole,
1965 eadb=useeadb, use_ntvfs=use_ntvfs,
1966 lp=lp, global_param=global_param)
1967 else:
1968 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1969 serverrole=serverrole,
1970 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1972 if lp is None:
1973 lp = samba.param.LoadParm()
1974 lp.load(smbconf)
1975 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1976 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1977 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1978 sitename=sitename, rootdn=rootdn)
1979 paths = provision_paths_from_lp(lp, names.dnsdomain)
1981 paths.bind_gid = bind_gid
1982 paths.root_uid = root_uid;
1983 paths.root_gid = root_gid
1985 if hostip is None:
1986 logger.info("Looking up IPv4 addresses")
1987 hostips = interface_ips_v4(lp)
1988 if len(hostips) > 0:
1989 hostip = hostips[0]
1990 if len(hostips) > 1:
1991 logger.warning("More than one IPv4 address found. Using %s",
1992 hostip)
1993 if hostip == "127.0.0.1":
1994 hostip = None
1995 if hostip is None:
1996 logger.warning("No IPv4 address will be assigned")
1998 if hostip6 is None:
1999 logger.info("Looking up IPv6 addresses")
2000 hostips = interface_ips_v6(lp)
2001 if hostips:
2002 hostip6 = hostips[0]
2003 if len(hostips) > 1:
2004 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2005 if hostip6 is None:
2006 logger.warning("No IPv6 address will be assigned")
2008 names.hostip = hostip
2009 names.hostip6 = hostip6
2011 if serverrole is None:
2012 serverrole = lp.get("server role")
2014 if not os.path.exists(paths.private_dir):
2015 os.mkdir(paths.private_dir)
2016 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
2017 os.makedirs(os.path.join(paths.private_dir, "tls"), 0700)
2018 if not os.path.exists(paths.state_dir):
2019 os.mkdir(paths.state_dir)
2021 if paths.sysvol and not os.path.exists(paths.sysvol):
2022 os.makedirs(paths.sysvol, 0775)
2024 if not use_ntvfs and serverrole == "active directory domain controller":
2025 s3conf = s3param.get_context()
2026 s3conf.load(lp.configfile)
2028 if paths.sysvol is None:
2029 raise MissingShareError("sysvol", paths.smbconf)
2031 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
2032 try:
2033 try:
2034 smbd.set_simple_acl(file.name, 0755, root_gid)
2035 except Exception:
2036 if not smbd.have_posix_acls():
2037 # This clue is only strictly correct for RPM and
2038 # Debian-like Linux systems, but hopefully other users
2039 # will get enough clue from it.
2040 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.")
2042 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
2043 try:
2044 smbd.chown(file.name, root_uid, root_gid)
2045 except Exception:
2046 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
2047 finally:
2048 file.close()
2050 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2052 schema = Schema(domainsid, invocationid=invocationid,
2053 schemadn=names.schemadn)
2055 if backend_type == "ldb":
2056 provision_backend = LDBBackend(backend_type, paths=paths,
2057 lp=lp, credentials=credentials,
2058 names=names, logger=logger)
2059 elif backend_type == "existing":
2060 # If support for this is ever added back, then the URI will need to be
2061 # specified again
2062 provision_backend = ExistingBackend(backend_type, paths=paths,
2063 lp=lp, credentials=credentials,
2064 names=names, logger=logger,
2065 ldap_backend_forced_uri=None)
2066 elif backend_type == "fedora-ds":
2067 provision_backend = FDSBackend(backend_type, paths=paths,
2068 lp=lp, credentials=credentials,
2069 names=names, logger=logger, domainsid=domainsid,
2070 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2071 slapd_path=slapd_path,
2072 root=root)
2073 elif backend_type == "openldap":
2074 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2075 lp=lp, credentials=credentials,
2076 names=names, logger=logger, domainsid=domainsid,
2077 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2078 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
2079 else:
2080 raise ValueError("Unknown LDAP backend type selected")
2082 provision_backend.init()
2083 provision_backend.start()
2085 # only install a new shares config db if there is none
2086 if not os.path.exists(paths.shareconf):
2087 logger.info("Setting up share.ldb")
2088 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2089 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2091 logger.info("Setting up secrets.ldb")
2092 secrets_ldb = setup_secretsdb(paths,
2093 session_info=session_info,
2094 backend_credentials=provision_backend.secrets_credentials, lp=lp)
2096 try:
2097 logger.info("Setting up the registry")
2098 setup_registry(paths.hklm, session_info, lp=lp)
2100 logger.info("Setting up the privileges database")
2101 setup_privileges(paths.privilege, session_info, lp=lp)
2103 logger.info("Setting up idmap db")
2104 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2106 setup_name_mappings(idmap, sid=str(domainsid),
2107 root_uid=root_uid, nobody_uid=nobody_uid,
2108 users_gid=users_gid, root_gid=root_gid)
2110 logger.info("Setting up SAM db")
2111 samdb = setup_samdb(paths.samdb, session_info,
2112 provision_backend, lp, names, logger=logger,
2113 serverrole=serverrole,
2114 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2116 if serverrole == "active directory domain controller":
2117 if paths.netlogon is None:
2118 raise MissingShareError("netlogon", paths.smbconf)
2120 if paths.sysvol is None:
2121 raise MissingShareError("sysvol", paths.smbconf)
2123 if not os.path.isdir(paths.netlogon):
2124 os.makedirs(paths.netlogon, 0755)
2126 if adminpass is None:
2127 adminpass = samba.generate_random_password(12, 32)
2128 adminpass_generated = True
2129 else:
2130 adminpass = unicode(adminpass, 'utf-8')
2131 adminpass_generated = False
2133 if samdb_fill == FILL_FULL:
2134 provision_fill(samdb, secrets_ldb, logger, names, paths,
2135 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2136 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2137 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2138 krbtgtpass=krbtgtpass, domainguid=domainguid,
2139 policyguid=policyguid, policyguid_dc=policyguid_dc,
2140 invocationid=invocationid, machinepass=machinepass,
2141 ntdsguid=ntdsguid, dns_backend=dns_backend,
2142 dnspass=dnspass, serverrole=serverrole,
2143 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2144 lp=lp, use_ntvfs=use_ntvfs,
2145 skip_sysvolacl=skip_sysvolacl)
2147 create_krb5_conf(paths.krb5conf,
2148 dnsdomain=names.dnsdomain, hostname=names.hostname,
2149 realm=names.realm)
2150 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2151 "generated at %s", paths.krb5conf)
2153 if serverrole == "active directory domain controller":
2154 create_dns_update_list(lp, logger, paths)
2156 backend_result = provision_backend.post_setup()
2157 provision_backend.shutdown()
2159 except:
2160 secrets_ldb.transaction_cancel()
2161 raise
2163 # Now commit the secrets.ldb to disk
2164 secrets_ldb.transaction_commit()
2166 # the commit creates the dns.keytab, now chown it
2167 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2168 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2169 try:
2170 os.chmod(dns_keytab_path, 0640)
2171 os.chown(dns_keytab_path, -1, paths.bind_gid)
2172 except OSError:
2173 if not os.environ.has_key('SAMBA_SELFTEST'):
2174 logger.info("Failed to chown %s to bind gid %u",
2175 dns_keytab_path, paths.bind_gid)
2177 result = ProvisionResult()
2178 result.server_role = serverrole
2179 result.domaindn = domaindn
2180 result.paths = paths
2181 result.names = names
2182 result.lp = lp
2183 result.samdb = samdb
2184 result.idmap = idmap
2185 result.domainsid = str(domainsid)
2187 if samdb_fill == FILL_FULL:
2188 result.adminpass_generated = adminpass_generated
2189 result.adminpass = adminpass
2190 else:
2191 result.adminpass_generated = False
2192 result.adminpass = None
2194 result.backend_result = backend_result
2196 if use_rfc2307:
2197 provision_fake_ypserver(logger=logger, samdb=samdb,
2198 domaindn=names.domaindn, netbiosname=names.netbiosname,
2199 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2201 return result
2204 def provision_become_dc(smbconf=None, targetdir=None,
2205 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2206 serverdn=None, domain=None, hostname=None, domainsid=None,
2207 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2208 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2209 dns_backend=None, root=None, nobody=None, users=None,
2210 backup=None, serverrole=None, ldap_backend=None,
2211 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2213 logger = logging.getLogger("provision")
2214 samba.set_debug_level(debuglevel)
2216 res = provision(logger, system_session(), None,
2217 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2218 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2219 configdn=configdn, serverdn=serverdn, domain=domain,
2220 hostname=hostname, hostip=None, domainsid=domainsid,
2221 machinepass=machinepass,
2222 serverrole="active directory domain controller",
2223 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2224 use_ntvfs=use_ntvfs)
2225 res.lp.set("debuglevel", str(debuglevel))
2226 return res
2229 def create_krb5_conf(path, dnsdomain, hostname, realm):
2230 """Write out a file containing zone statements suitable for inclusion in a
2231 named.conf file (including GSS-TSIG configuration).
2233 :param path: Path of the new named.conf file.
2234 :param dnsdomain: DNS Domain name
2235 :param hostname: Local hostname
2236 :param realm: Realm name
2238 setup_file(setup_path("krb5.conf"), path, {
2239 "DNSDOMAIN": dnsdomain,
2240 "HOSTNAME": hostname,
2241 "REALM": realm,
2245 class ProvisioningError(Exception):
2246 """A generic provision error."""
2248 def __init__(self, value):
2249 self.value = value
2251 def __str__(self):
2252 return "ProvisioningError: " + self.value
2255 class InvalidNetbiosName(Exception):
2256 """A specified name was not a valid NetBIOS name."""
2258 def __init__(self, name):
2259 super(InvalidNetbiosName, self).__init__(
2260 "The name '%r' is not a valid NetBIOS name" % name)
2263 class MissingShareError(ProvisioningError):
2265 def __init__(self, name, smbconf):
2266 super(MissingShareError, self).__init__(
2267 "Existing smb.conf does not have a [%s] share, but you are "
2268 "configuring a DC. Please remove %s or add the share manually." %
2269 (name, smbconf))