scripting: Fill the ProvisionNames hash with strings, not ldb.MessageElement or Dn
[Samba/gbeck.git] / python / samba / provision / __init__.py
blob252cfd9021f737d8e9217c70d96440796a2261ff
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)"
1470 SYSVOL_SERVICE="sysvol"
1472 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1473 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
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, service=service)
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, service=service)
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, service=SYSVOL_SERVICE)
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,
1563 skip_invalid_chown=True, passdb=s4_passdb,
1564 service=SYSVOL_SERVICE)
1565 for root, dirs, files in os.walk(sysvol, topdown=False):
1566 for name in files:
1567 if use_ntvfs and canchown:
1568 os.chown(os.path.join(root, name), -1, gid)
1569 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1570 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1571 passdb=s4_passdb, service=SYSVOL_SERVICE)
1572 for name in dirs:
1573 if use_ntvfs and canchown:
1574 os.chown(os.path.join(root, name), -1, gid)
1575 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1576 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1577 passdb=s4_passdb, service=SYSVOL_SERVICE)
1579 # Set acls on Policy folder and policies folders
1580 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1582 def acl_type(direct_db_access):
1583 if direct_db_access:
1584 return "DB"
1585 else:
1586 return "VFS"
1588 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1589 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1590 fsacl_sddl = fsacl.as_sddl(domainsid)
1591 if fsacl_sddl != acl:
1592 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))
1594 for root, dirs, files in os.walk(path, topdown=False):
1595 for name in files:
1596 fsacl = getntacl(lp, os.path.join(root, name),
1597 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1598 if fsacl is None:
1599 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1600 fsacl_sddl = fsacl.as_sddl(domainsid)
1601 if fsacl_sddl != acl:
1602 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))
1604 for name in dirs:
1605 fsacl = getntacl(lp, os.path.join(root, name),
1606 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1607 if fsacl is None:
1608 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1609 fsacl_sddl = fsacl.as_sddl(domainsid)
1610 if fsacl_sddl != acl:
1611 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))
1614 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1615 direct_db_access):
1616 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1617 folders beneath.
1619 :param sysvol: Physical path for the sysvol folder
1620 :param dnsdomain: The DNS name of the domain
1621 :param domainsid: The SID of the domain
1622 :param domaindn: The DN of the domain (ie. DC=...)
1623 :param samdb: An LDB object on the SAM db
1624 :param lp: an LP object
1627 # Set ACL for GPO root folder
1628 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1629 fsacl = getntacl(lp, root_policy_path,
1630 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1631 if fsacl is None:
1632 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1633 fsacl_sddl = fsacl.as_sddl(domainsid)
1634 if fsacl_sddl != POLICIES_ACL:
1635 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))
1636 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1637 attrs=["cn", "nTSecurityDescriptor"],
1638 expression="", scope=ldb.SCOPE_ONELEVEL)
1640 for policy in res:
1641 acl = ndr_unpack(security.descriptor,
1642 str(policy["nTSecurityDescriptor"])).as_sddl()
1643 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1644 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1645 domainsid, direct_db_access)
1648 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1649 lp):
1650 """Set the ACL for the sysvol share and the subfolders
1652 :param samdb: An LDB object on the SAM db
1653 :param netlogon: Physical path for the netlogon folder
1654 :param sysvol: Physical path for the sysvol folder
1655 :param uid: The UID of the "Administrator" user
1656 :param gid: The GID of the "Domain adminstrators" group
1657 :param domainsid: The SID of the domain
1658 :param dnsdomain: The DNS name of the domain
1659 :param domaindn: The DN of the domain (ie. DC=...)
1662 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1663 s3conf = s3param.get_context()
1664 s3conf.load(lp.configfile)
1665 # ensure we are using the right samba_dsdb passdb backend, no matter what
1666 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1667 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1668 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1670 # now ensure everything matches correctly, to avoid wierd issues
1671 if passdb.get_global_sam_sid() != domainsid:
1672 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))
1674 domain_info = s4_passdb.domain_info()
1675 if domain_info["dom_sid"] != domainsid:
1676 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))
1678 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1679 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()))
1681 # Ensure we can read this directly, and via the smbd VFS
1682 for direct_db_access in [True, False]:
1683 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1684 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1685 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1686 if fsacl is None:
1687 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1688 fsacl_sddl = fsacl.as_sddl(domainsid)
1689 if fsacl_sddl != SYSVOL_ACL:
1690 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))
1692 # Check acls on Policy folder and policies folders
1693 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1694 direct_db_access)
1697 def interface_ips_v4(lp):
1698 """return only IPv4 IPs"""
1699 ips = samba.interface_ips(lp, False)
1700 ret = []
1701 for i in ips:
1702 if i.find(':') == -1:
1703 ret.append(i)
1704 return ret
1707 def interface_ips_v6(lp, linklocal=False):
1708 """return only IPv6 IPs"""
1709 ips = samba.interface_ips(lp, False)
1710 ret = []
1711 for i in ips:
1712 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1713 ret.append(i)
1714 return ret
1717 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1718 domainsid, schema=None,
1719 targetdir=None, samdb_fill=FILL_FULL,
1720 hostip=None, hostip6=None,
1721 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1722 domainguid=None, policyguid=None, policyguid_dc=None,
1723 invocationid=None, machinepass=None, ntdsguid=None,
1724 dns_backend=None, dnspass=None,
1725 serverrole=None, dom_for_fun_level=None,
1726 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1727 # create/adapt the group policy GUIDs
1728 # Default GUID for default policy are described at
1729 # "How Core Group Policy Works"
1730 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1731 if policyguid is None:
1732 policyguid = DEFAULT_POLICY_GUID
1733 policyguid = policyguid.upper()
1734 if policyguid_dc is None:
1735 policyguid_dc = DEFAULT_DC_POLICY_GUID
1736 policyguid_dc = policyguid_dc.upper()
1738 if invocationid is None:
1739 invocationid = str(uuid.uuid4())
1741 if krbtgtpass is None:
1742 krbtgtpass = samba.generate_random_password(128, 255)
1743 if machinepass is None:
1744 machinepass = samba.generate_random_password(128, 255)
1745 if dnspass is None:
1746 dnspass = samba.generate_random_password(128, 255)
1748 samdb = fill_samdb(samdb, lp, names, logger=logger,
1749 domainsid=domainsid, schema=schema, domainguid=domainguid,
1750 policyguid=policyguid, policyguid_dc=policyguid_dc,
1751 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1752 invocationid=invocationid, machinepass=machinepass,
1753 dns_backend=dns_backend, dnspass=dnspass,
1754 ntdsguid=ntdsguid, serverrole=serverrole,
1755 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1756 next_rid=next_rid, dc_rid=dc_rid)
1758 if serverrole == "active directory domain controller":
1760 # Set up group policies (domain policy and domain controller
1761 # policy)
1762 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1763 policyguid_dc)
1764 if not skip_sysvolacl:
1765 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1766 paths.root_gid, domainsid, names.dnsdomain,
1767 names.domaindn, lp, use_ntvfs)
1768 else:
1769 logger.info("Setting acl on sysvol skipped")
1771 secretsdb_self_join(secrets_ldb, domain=names.domain,
1772 realm=names.realm, dnsdomain=names.dnsdomain,
1773 netbiosname=names.netbiosname, domainsid=domainsid,
1774 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1776 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1777 # In future, this might be determined from some configuration
1778 kerberos_enctypes = str(ENC_ALL_TYPES)
1780 try:
1781 msg = ldb.Message(ldb.Dn(samdb,
1782 samdb.searchone("distinguishedName",
1783 expression="samAccountName=%s$" % names.netbiosname,
1784 scope=ldb.SCOPE_SUBTREE)))
1785 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1786 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1787 name="msDS-SupportedEncryptionTypes")
1788 samdb.modify(msg)
1789 except ldb.LdbError, (enum, estr):
1790 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1791 # It might be that this attribute does not exist in this schema
1792 raise
1794 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1795 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1796 dnspass=dnspass, os_level=dom_for_fun_level,
1797 targetdir=targetdir, site=DEFAULTSITE)
1799 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1800 attribute="objectGUID")
1801 assert isinstance(domainguid, str)
1803 lastProvisionUSNs = get_last_provision_usn(samdb)
1804 maxUSN = get_max_usn(samdb, str(names.rootdn))
1805 if lastProvisionUSNs is not None:
1806 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1807 else:
1808 set_provision_usn(samdb, 0, maxUSN, invocationid)
1810 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1811 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1812 { 'NTDSGUID' : names.ntdsguid })
1814 # fix any dangling GUIDs from the provision
1815 logger.info("Fixing provision GUIDs")
1816 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1817 quiet=True)
1818 samdb.transaction_start()
1819 try:
1820 # a small number of GUIDs are missing because of ordering issues in the
1821 # provision code
1822 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1823 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1824 scope=ldb.SCOPE_BASE,
1825 attrs=['defaultObjectCategory'])
1826 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1827 scope=ldb.SCOPE_ONELEVEL,
1828 attrs=['ipsecOwnersReference',
1829 'ipsecFilterReference',
1830 'ipsecISAKMPReference',
1831 'ipsecNegotiationPolicyReference',
1832 'ipsecNFAReference'])
1833 except:
1834 samdb.transaction_cancel()
1835 raise
1836 else:
1837 samdb.transaction_commit()
1840 _ROLES_MAP = {
1841 "ROLE_STANDALONE": "standalone server",
1842 "ROLE_DOMAIN_MEMBER": "member server",
1843 "ROLE_DOMAIN_BDC": "active directory domain controller",
1844 "ROLE_DOMAIN_PDC": "active directory domain controller",
1845 "dc": "active directory domain controller",
1846 "member": "member server",
1847 "domain controller": "active directory domain controller",
1848 "active directory domain controller": "active directory domain controller",
1849 "member server": "member server",
1850 "standalone": "standalone server",
1851 "standalone server": "standalone server",
1855 def sanitize_server_role(role):
1856 """Sanitize a server role name.
1858 :param role: Server role
1859 :raise ValueError: If the role can not be interpreted
1860 :return: Sanitized server role (one of "member server",
1861 "active directory domain controller", "standalone server")
1863 try:
1864 return _ROLES_MAP[role]
1865 except KeyError:
1866 raise ValueError(role)
1869 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1870 maxuid, maxgid):
1871 """Create AD entries for the fake ypserver.
1873 This is needed for being able to manipulate posix attrs via ADUC.
1875 samdb.transaction_start()
1876 try:
1877 logger.info("Setting up fake yp server settings")
1878 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1879 "DOMAINDN": domaindn,
1880 "NETBIOSNAME": netbiosname,
1881 "NISDOMAIN": nisdomain,
1883 except:
1884 samdb.transaction_cancel()
1885 raise
1886 else:
1887 samdb.transaction_commit()
1890 def provision(logger, session_info, credentials, smbconf=None,
1891 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1892 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1893 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1894 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1895 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1896 dns_backend=None, dns_forwarder=None, dnspass=None,
1897 invocationid=None, machinepass=None, ntdsguid=None,
1898 root=None, nobody=None, users=None, backup=None, aci=None,
1899 serverrole=None, dom_for_fun_level=None, backend_type=None,
1900 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1901 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1902 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1903 """Provision samba4
1905 :note: caution, this wipes all existing data!
1908 try:
1909 serverrole = sanitize_server_role(serverrole)
1910 except ValueError:
1911 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1913 if ldapadminpass is None:
1914 # Make a new, random password between Samba and it's LDAP server
1915 ldapadminpass = samba.generate_random_password(128, 255)
1917 if backend_type is None:
1918 backend_type = "ldb"
1920 if domainsid is None:
1921 domainsid = security.random_sid()
1922 else:
1923 domainsid = security.dom_sid(domainsid)
1925 root_uid = findnss_uid([root or "root"])
1926 nobody_uid = findnss_uid([nobody or "nobody"])
1927 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1928 root_gid = pwd.getpwuid(root_uid).pw_gid
1930 try:
1931 bind_gid = findnss_gid(["bind", "named"])
1932 except KeyError:
1933 bind_gid = None
1935 if targetdir is not None:
1936 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1937 elif smbconf is None:
1938 smbconf = samba.param.default_path()
1939 if not os.path.exists(os.path.dirname(smbconf)):
1940 os.makedirs(os.path.dirname(smbconf))
1942 server_services = []
1943 global_param = {}
1944 if use_rfc2307:
1945 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1947 if dns_backend != "SAMBA_INTERNAL":
1948 server_services.append("-dns")
1949 else:
1950 if dns_forwarder is not None:
1951 global_param["dns forwarder"] = [dns_forwarder]
1953 if use_ntvfs:
1954 server_services.append("+smb")
1955 server_services.append("-s3fs")
1956 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1958 if len(server_services) > 0:
1959 global_param["server services"] = server_services
1961 # only install a new smb.conf if there isn't one there already
1962 if os.path.exists(smbconf):
1963 # if Samba Team members can't figure out the weird errors
1964 # loading an empty smb.conf gives, then we need to be smarter.
1965 # Pretend it just didn't exist --abartlet
1966 f = open(smbconf, 'r')
1967 try:
1968 data = f.read().lstrip()
1969 finally:
1970 f.close()
1971 if data is None or data == "":
1972 make_smbconf(smbconf, hostname, domain, realm,
1973 targetdir, serverrole=serverrole,
1974 eadb=useeadb, use_ntvfs=use_ntvfs,
1975 lp=lp, global_param=global_param)
1976 else:
1977 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1978 serverrole=serverrole,
1979 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1981 if lp is None:
1982 lp = samba.param.LoadParm()
1983 lp.load(smbconf)
1984 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1985 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1986 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1987 sitename=sitename, rootdn=rootdn)
1988 paths = provision_paths_from_lp(lp, names.dnsdomain)
1990 paths.bind_gid = bind_gid
1991 paths.root_uid = root_uid;
1992 paths.root_gid = root_gid
1994 if hostip is None:
1995 logger.info("Looking up IPv4 addresses")
1996 hostips = interface_ips_v4(lp)
1997 if len(hostips) > 0:
1998 hostip = hostips[0]
1999 if len(hostips) > 1:
2000 logger.warning("More than one IPv4 address found. Using %s",
2001 hostip)
2002 if hostip == "127.0.0.1":
2003 hostip = None
2004 if hostip is None:
2005 logger.warning("No IPv4 address will be assigned")
2007 if hostip6 is None:
2008 logger.info("Looking up IPv6 addresses")
2009 hostips = interface_ips_v6(lp, linklocal=False)
2010 if hostips:
2011 hostip6 = hostips[0]
2012 if len(hostips) > 1:
2013 logger.warning("More than one IPv6 address found. Using %s", hostip6)
2014 if hostip6 is None:
2015 logger.warning("No IPv6 address will be assigned")
2017 names.hostip = hostip
2018 names.hostip6 = hostip6
2020 if serverrole is None:
2021 serverrole = lp.get("server role")
2023 if not os.path.exists(paths.private_dir):
2024 os.mkdir(paths.private_dir)
2025 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
2026 os.mkdir(os.path.join(paths.private_dir, "tls"))
2027 if not os.path.exists(paths.state_dir):
2028 os.mkdir(paths.state_dir)
2030 if paths.sysvol and not os.path.exists(paths.sysvol):
2031 os.makedirs(paths.sysvol, 0775)
2033 if not use_ntvfs and serverrole == "active directory domain controller":
2034 s3conf = s3param.get_context()
2035 s3conf.load(lp.configfile)
2037 if paths.sysvol is None:
2038 raise MissingShareError("sysvol", paths.smbconf)
2040 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
2041 try:
2042 try:
2043 smbd.set_simple_acl(file.name, 0755, root_gid)
2044 except Exception:
2045 if not smbd.have_posix_acls():
2046 # This clue is only strictly correct for RPM and
2047 # Debian-like Linux systems, but hopefully other users
2048 # will get enough clue from it.
2049 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.")
2051 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
2052 try:
2053 smbd.chown(file.name, root_uid, root_gid)
2054 except Exception:
2055 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
2056 finally:
2057 file.close()
2059 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2061 schema = Schema(domainsid, invocationid=invocationid,
2062 schemadn=names.schemadn)
2064 if backend_type == "ldb":
2065 provision_backend = LDBBackend(backend_type, paths=paths,
2066 lp=lp, credentials=credentials,
2067 names=names, logger=logger)
2068 elif backend_type == "existing":
2069 # If support for this is ever added back, then the URI will need to be
2070 # specified again
2071 provision_backend = ExistingBackend(backend_type, paths=paths,
2072 lp=lp, credentials=credentials,
2073 names=names, logger=logger,
2074 ldap_backend_forced_uri=None)
2075 elif backend_type == "fedora-ds":
2076 provision_backend = FDSBackend(backend_type, paths=paths,
2077 lp=lp, credentials=credentials,
2078 names=names, logger=logger, domainsid=domainsid,
2079 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2080 slapd_path=slapd_path,
2081 root=root)
2082 elif backend_type == "openldap":
2083 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2084 lp=lp, credentials=credentials,
2085 names=names, logger=logger, domainsid=domainsid,
2086 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2087 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
2088 else:
2089 raise ValueError("Unknown LDAP backend type selected")
2091 provision_backend.init()
2092 provision_backend.start()
2094 # only install a new shares config db if there is none
2095 if not os.path.exists(paths.shareconf):
2096 logger.info("Setting up share.ldb")
2097 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2098 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2100 logger.info("Setting up secrets.ldb")
2101 secrets_ldb = setup_secretsdb(paths,
2102 session_info=session_info,
2103 backend_credentials=provision_backend.secrets_credentials, lp=lp)
2105 try:
2106 logger.info("Setting up the registry")
2107 setup_registry(paths.hklm, session_info, lp=lp)
2109 logger.info("Setting up the privileges database")
2110 setup_privileges(paths.privilege, session_info, lp=lp)
2112 logger.info("Setting up idmap db")
2113 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2115 setup_name_mappings(idmap, sid=str(domainsid),
2116 root_uid=root_uid, nobody_uid=nobody_uid,
2117 users_gid=users_gid, root_gid=root_gid)
2119 logger.info("Setting up SAM db")
2120 samdb = setup_samdb(paths.samdb, session_info,
2121 provision_backend, lp, names, logger=logger,
2122 serverrole=serverrole,
2123 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2125 if serverrole == "active directory domain controller":
2126 if paths.netlogon is None:
2127 raise MissingShareError("netlogon", paths.smbconf)
2129 if paths.sysvol is None:
2130 raise MissingShareError("sysvol", paths.smbconf)
2132 if not os.path.isdir(paths.netlogon):
2133 os.makedirs(paths.netlogon, 0755)
2135 if adminpass is None:
2136 adminpass = samba.generate_random_password(12, 32)
2137 adminpass_generated = True
2138 else:
2139 adminpass = unicode(adminpass, 'utf-8')
2140 adminpass_generated = False
2142 if samdb_fill == FILL_FULL:
2143 provision_fill(samdb, secrets_ldb, logger, names, paths,
2144 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2145 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2146 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2147 krbtgtpass=krbtgtpass, domainguid=domainguid,
2148 policyguid=policyguid, policyguid_dc=policyguid_dc,
2149 invocationid=invocationid, machinepass=machinepass,
2150 ntdsguid=ntdsguid, dns_backend=dns_backend,
2151 dnspass=dnspass, serverrole=serverrole,
2152 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2153 lp=lp, use_ntvfs=use_ntvfs,
2154 skip_sysvolacl=skip_sysvolacl)
2156 create_krb5_conf(paths.krb5conf,
2157 dnsdomain=names.dnsdomain, hostname=names.hostname,
2158 realm=names.realm)
2159 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2160 "generated at %s", paths.krb5conf)
2162 if serverrole == "active directory domain controller":
2163 create_dns_update_list(lp, logger, paths)
2165 backend_result = provision_backend.post_setup()
2166 provision_backend.shutdown()
2168 except:
2169 secrets_ldb.transaction_cancel()
2170 raise
2172 # Now commit the secrets.ldb to disk
2173 secrets_ldb.transaction_commit()
2175 # the commit creates the dns.keytab, now chown it
2176 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2177 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2178 try:
2179 os.chmod(dns_keytab_path, 0640)
2180 os.chown(dns_keytab_path, -1, paths.bind_gid)
2181 except OSError:
2182 if not os.environ.has_key('SAMBA_SELFTEST'):
2183 logger.info("Failed to chown %s to bind gid %u",
2184 dns_keytab_path, paths.bind_gid)
2186 result = ProvisionResult()
2187 result.server_role = serverrole
2188 result.domaindn = domaindn
2189 result.paths = paths
2190 result.names = names
2191 result.lp = lp
2192 result.samdb = samdb
2193 result.idmap = idmap
2194 result.domainsid = str(domainsid)
2196 if samdb_fill == FILL_FULL:
2197 result.adminpass_generated = adminpass_generated
2198 result.adminpass = adminpass
2199 else:
2200 result.adminpass_generated = False
2201 result.adminpass = None
2203 result.backend_result = backend_result
2205 if use_rfc2307:
2206 provision_fake_ypserver(logger=logger, samdb=samdb,
2207 domaindn=names.domaindn, netbiosname=names.netbiosname,
2208 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2210 return result
2213 def provision_become_dc(smbconf=None, targetdir=None,
2214 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2215 serverdn=None, domain=None, hostname=None, domainsid=None,
2216 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2217 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2218 dns_backend=None, root=None, nobody=None, users=None,
2219 backup=None, serverrole=None, ldap_backend=None,
2220 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2222 logger = logging.getLogger("provision")
2223 samba.set_debug_level(debuglevel)
2225 res = provision(logger, system_session(), None,
2226 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2227 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2228 configdn=configdn, serverdn=serverdn, domain=domain,
2229 hostname=hostname, hostip=None, domainsid=domainsid,
2230 machinepass=machinepass,
2231 serverrole="active directory domain controller",
2232 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2233 use_ntvfs=use_ntvfs)
2234 res.lp.set("debuglevel", str(debuglevel))
2235 return res
2238 def create_krb5_conf(path, dnsdomain, hostname, realm):
2239 """Write out a file containing zone statements suitable for inclusion in a
2240 named.conf file (including GSS-TSIG configuration).
2242 :param path: Path of the new named.conf file.
2243 :param dnsdomain: DNS Domain name
2244 :param hostname: Local hostname
2245 :param realm: Realm name
2247 setup_file(setup_path("krb5.conf"), path, {
2248 "DNSDOMAIN": dnsdomain,
2249 "HOSTNAME": hostname,
2250 "REALM": realm,
2254 class ProvisioningError(Exception):
2255 """A generic provision error."""
2257 def __init__(self, value):
2258 self.value = value
2260 def __str__(self):
2261 return "ProvisioningError: " + self.value
2264 class InvalidNetbiosName(Exception):
2265 """A specified name was not a valid NetBIOS name."""
2267 def __init__(self, name):
2268 super(InvalidNetbiosName, self).__init__(
2269 "The name '%r' is not a valid NetBIOS name" % name)
2272 class MissingShareError(ProvisioningError):
2274 def __init__(self, name, smbconf):
2275 super(MissingShareError, self).__init__(
2276 "Existing smb.conf does not have a [%s] share, but you are "
2277 "configuring a DC. Please remove %s or add the share manually." %
2278 (name, smbconf))