provision: add get_dns_{forest,domain}_microsoft_dns_descriptor()
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision / __init__.py
blob390a092989602e41bf3aecbb7db6a06c3158ae98
1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning a Samba4 server
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
5 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
6 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Functions for setting up a Samba configuration."""
27 __docformat__ = "restructuredText"
29 from base64 import b64encode
30 import os
31 import re
32 import pwd
33 import grp
34 import logging
35 import time
36 import uuid
37 import socket
38 import urllib
39 import string
40 import tempfile
42 import ldb
44 from samba.auth import system_session, admin_session
45 import samba
46 from samba.samba3 import smbd, passdb
47 from samba.samba3 import param as s3param
48 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
49 from samba import (
50 Ldb,
51 MAX_NETBIOS_NAME_LEN,
52 check_all_substituted,
53 is_valid_netbios_char,
54 setup_file,
55 substitute_var,
56 valid_netbios_name,
57 version,
59 from samba.dcerpc import security, misc
60 from samba.dcerpc.misc import (
61 SEC_CHAN_BDC,
62 SEC_CHAN_WKSTA,
64 from samba.dsdb import (
65 DS_DOMAIN_FUNCTION_2003,
66 DS_DOMAIN_FUNCTION_2008_R2,
67 ENC_ALL_TYPES,
69 from samba.idmap import IDmapDB
70 from samba.ms_display_specifiers import read_ms_ldif
71 from samba.ntacls import setntacl, getntacl, dsacl2fsacl
72 from samba.ndr import ndr_pack, ndr_unpack
73 from samba.provision.backend import (
74 ExistingBackend,
75 FDSBackend,
76 LDBBackend,
77 OpenLDAPBackend,
79 from samba.provision.descriptor import (
80 get_empty_descriptor,
81 get_config_descriptor,
82 get_config_partitions_descriptor,
83 get_config_sites_descriptor,
84 get_config_ntds_quotas_descriptor,
85 get_config_delete_protected1_descriptor,
86 get_config_delete_protected1wd_descriptor,
87 get_config_delete_protected2_descriptor,
88 get_domain_descriptor,
89 get_domain_infrastructure_descriptor,
90 get_domain_builtin_descriptor,
91 get_domain_computers_descriptor,
92 get_domain_users_descriptor,
93 get_domain_controllers_descriptor,
94 get_domain_delete_protected1_descriptor,
95 get_domain_delete_protected2_descriptor,
96 get_dns_partition_descriptor,
97 get_dns_forest_microsoft_dns_descriptor,
98 get_dns_domain_microsoft_dns_descriptor,
100 from samba.provision.common import (
101 setup_path,
102 setup_add_ldif,
103 setup_modify_ldif,
105 from samba.provision.sambadns import (
106 setup_ad_dns,
107 create_dns_update_list
110 import samba.param
111 import samba.registry
112 from samba.schema import Schema
113 from samba.samdb import SamDB
114 from samba.dbchecker import dbcheck
117 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
118 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
119 DEFAULTSITE = "Default-First-Site-Name"
120 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
123 class ProvisionPaths(object):
125 def __init__(self):
126 self.shareconf = None
127 self.hklm = None
128 self.hkcu = None
129 self.hkcr = None
130 self.hku = None
131 self.hkpd = None
132 self.hkpt = None
133 self.samdb = None
134 self.idmapdb = None
135 self.secrets = None
136 self.keytab = None
137 self.dns_keytab = None
138 self.dns = None
139 self.winsdb = None
140 self.private_dir = None
141 self.state_dir = None
144 class ProvisionNames(object):
146 def __init__(self):
147 self.ncs = None
148 self.rootdn = None
149 self.domaindn = None
150 self.configdn = None
151 self.schemadn = None
152 self.dnsforestdn = None
153 self.dnsdomaindn = None
154 self.ldapmanagerdn = None
155 self.dnsdomain = None
156 self.realm = None
157 self.netbiosname = None
158 self.domain = None
159 self.hostname = None
160 self.sitename = None
161 self.smbconf = None
164 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
165 lp):
166 """Get key provision parameters (realm, domain, ...) from a given provision
168 :param samdb: An LDB object connected to the sam.ldb file
169 :param secretsdb: An LDB object connected to the secrets.ldb file
170 :param idmapdb: An LDB object connected to the idmap.ldb file
171 :param paths: A list of path to provision object
172 :param smbconf: Path to the smb.conf file
173 :param lp: A LoadParm object
174 :return: A list of key provision parameters
176 names = ProvisionNames()
177 names.adminpass = None
179 # NT domain, kerberos realm, root dn, domain dn, domain dns name
180 names.domain = string.upper(lp.get("workgroup"))
181 names.realm = lp.get("realm")
182 names.dnsdomain = names.realm.lower()
183 basedn = samba.dn_from_dns_name(names.dnsdomain)
184 names.realm = string.upper(names.realm)
185 # netbiosname
186 # Get the netbiosname first (could be obtained from smb.conf in theory)
187 res = secretsdb.search(expression="(flatname=%s)" %
188 names.domain,base="CN=Primary Domains",
189 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
190 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
192 names.smbconf = smbconf
194 # That's a bit simplistic but it's ok as long as we have only 3
195 # partitions
196 current = samdb.search(expression="(objectClass=*)",
197 base="", scope=ldb.SCOPE_BASE,
198 attrs=["defaultNamingContext", "schemaNamingContext",
199 "configurationNamingContext","rootDomainNamingContext",
200 "namingContexts"])
202 names.configdn = current[0]["configurationNamingContext"]
203 configdn = str(names.configdn)
204 names.schemadn = current[0]["schemaNamingContext"]
205 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
206 current[0]["defaultNamingContext"][0]))):
207 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
208 "is not the same ..." % (paths.samdb,
209 str(current[0]["defaultNamingContext"][0]),
210 paths.smbconf, basedn)))
212 names.domaindn=current[0]["defaultNamingContext"]
213 names.rootdn=current[0]["rootDomainNamingContext"]
214 names.ncs=current[0]["namingContexts"]
215 names.dnsforestdn = None
216 names.dnsdomaindn = None
218 for i in range(0, len(names.ncs)):
219 nc = names.ncs[i]
221 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
222 if nc == dnsforestdn:
223 names.dnsforestdn = dnsforestdn
224 continue
226 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
227 if nc == dnsdomaindn:
228 names.dnsdomaindn = dnsdomaindn
229 continue
231 # default site name
232 res3 = samdb.search(expression="(objectClass=site)",
233 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
234 names.sitename = str(res3[0]["cn"])
236 # dns hostname and server dn
237 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
238 base="OU=Domain Controllers,%s" % basedn,
239 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
240 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
242 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
243 attrs=[], base=configdn)
244 names.serverdn = server_res[0].dn
246 # invocation id/objectguid
247 res5 = samdb.search(expression="(objectClass=*)",
248 base="CN=NTDS Settings,%s" % str(names.serverdn),
249 scope=ldb.SCOPE_BASE,
250 attrs=["invocationID", "objectGUID"])
251 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
252 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
254 # domain guid/sid
255 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
256 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
257 "objectSid","msDS-Behavior-Version" ])
258 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
259 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
260 if res6[0].get("msDS-Behavior-Version") is None or \
261 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
262 names.domainlevel = DS_DOMAIN_FUNCTION_2000
263 else:
264 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
266 # policy guid
267 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
268 base="CN=Policies,CN=System," + basedn,
269 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
270 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
271 # dc policy guid
272 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
273 " Policy)",
274 base="CN=Policies,CN=System," + basedn,
275 scope=ldb.SCOPE_ONELEVEL,
276 attrs=["cn","displayName"])
277 if len(res8) == 1:
278 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
279 else:
280 names.policyid_dc = None
282 res9 = idmapdb.search(expression="(cn=%s-%s)" %
283 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
284 attrs=["xidNumber", "type"])
285 if len(res9) != 1:
286 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
287 if res9[0]["type"][0] == "ID_TYPE_BOTH":
288 names.root_gid = res9[0]["xidNumber"][0]
289 else:
290 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
291 return names
294 def update_provision_usn(samdb, low, high, id, replace=False):
295 """Update the field provisionUSN in sam.ldb
297 This field is used to track range of USN modified by provision and
298 upgradeprovision.
299 This value is used afterward by next provision to figure out if
300 the field have been modified since last provision.
302 :param samdb: An LDB object connect to sam.ldb
303 :param low: The lowest USN modified by this upgrade
304 :param high: The highest USN modified by this upgrade
305 :param id: The invocation id of the samba's dc
306 :param replace: A boolean indicating if the range should replace any
307 existing one or appended (default)
310 tab = []
311 if not replace:
312 entry = samdb.search(base="@PROVISION",
313 scope=ldb.SCOPE_BASE,
314 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
315 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
316 if not re.search(';', e):
317 e = "%s;%s" % (e, id)
318 tab.append(str(e))
320 tab.append("%s-%s;%s" % (low, high, id))
321 delta = ldb.Message()
322 delta.dn = ldb.Dn(samdb, "@PROVISION")
323 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
324 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
325 entry = samdb.search(expression='provisionnerID=*',
326 base="@PROVISION", scope=ldb.SCOPE_BASE,
327 attrs=["provisionnerID"])
328 if len(entry) == 0 or len(entry[0]) == 0:
329 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
330 samdb.modify(delta)
333 def set_provision_usn(samdb, low, high, id):
334 """Set the field provisionUSN in sam.ldb
335 This field is used to track range of USN modified by provision and
336 upgradeprovision.
337 This value is used afterward by next provision to figure out if
338 the field have been modified since last provision.
340 :param samdb: An LDB object connect to sam.ldb
341 :param low: The lowest USN modified by this upgrade
342 :param high: The highest USN modified by this upgrade
343 :param id: The invocationId of the provision"""
345 tab = []
346 tab.append("%s-%s;%s" % (low, high, id))
348 delta = ldb.Message()
349 delta.dn = ldb.Dn(samdb, "@PROVISION")
350 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
351 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
352 samdb.add(delta)
355 def get_max_usn(samdb,basedn):
356 """ This function return the biggest USN present in the provision
358 :param samdb: A LDB object pointing to the sam.ldb
359 :param basedn: A string containing the base DN of the provision
360 (ie. DC=foo, DC=bar)
361 :return: The biggest USN in the provision"""
363 res = samdb.search(expression="objectClass=*",base=basedn,
364 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
365 controls=["search_options:1:2",
366 "server_sort:1:1:uSNChanged",
367 "paged_results:1:1"])
368 return res[0]["uSNChanged"]
371 def get_last_provision_usn(sam):
372 """Get USNs ranges modified by a provision or an upgradeprovision
374 :param sam: An LDB object pointing to the sam.ldb
375 :return: a dictionary which keys are invocation id and values are an array
376 of integer representing the different ranges
378 try:
379 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
380 base="@PROVISION", scope=ldb.SCOPE_BASE,
381 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
382 except ldb.LdbError, (ecode, emsg):
383 if ecode == ldb.ERR_NO_SUCH_OBJECT:
384 return None
385 raise
386 if len(entry) > 0:
387 myids = []
388 range = {}
389 p = re.compile(r'-')
390 if entry[0].get("provisionnerID"):
391 for e in entry[0]["provisionnerID"]:
392 myids.append(str(e))
393 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
394 tab1 = str(r).split(';')
395 if len(tab1) == 2:
396 id = tab1[1]
397 else:
398 id = "default"
399 if (len(myids) > 0 and id not in myids):
400 continue
401 tab2 = p.split(tab1[0])
402 if range.get(id) is None:
403 range[id] = []
404 range[id].append(tab2[0])
405 range[id].append(tab2[1])
406 return range
407 else:
408 return None
411 class ProvisionResult(object):
412 """Result of a provision.
414 :ivar server_role: The server role
415 :ivar paths: ProvisionPaths instance
416 :ivar domaindn: The domain dn, as string
419 def __init__(self):
420 self.server_role = None
421 self.paths = None
422 self.domaindn = None
423 self.lp = None
424 self.samdb = None
425 self.idmap = None
426 self.names = None
427 self.domainsid = None
428 self.adminpass_generated = None
429 self.adminpass = None
430 self.backend_result = None
432 def report_logger(self, logger):
433 """Report this provision result to a logger."""
434 logger.info(
435 "Once the above files are installed, your Samba4 server will "
436 "be ready to use")
437 if self.adminpass_generated:
438 logger.info("Admin password: %s", self.adminpass)
439 logger.info("Server Role: %s", self.server_role)
440 logger.info("Hostname: %s", self.names.hostname)
441 logger.info("NetBIOS Domain: %s", self.names.domain)
442 logger.info("DNS Domain: %s", self.names.dnsdomain)
443 logger.info("DOMAIN SID: %s", self.domainsid)
445 if self.backend_result:
446 self.backend_result.report_logger(logger)
449 def check_install(lp, session_info, credentials):
450 """Check whether the current install seems ok.
452 :param lp: Loadparm context
453 :param session_info: Session information
454 :param credentials: Credentials
456 if lp.get("realm") == "":
457 raise Exception("Realm empty")
458 samdb = Ldb(lp.samdb_url(), session_info=session_info,
459 credentials=credentials, lp=lp)
460 if len(samdb.search("(cn=Administrator)")) != 1:
461 raise ProvisioningError("No administrator account found")
464 def findnss(nssfn, names):
465 """Find a user or group from a list of possibilities.
467 :param nssfn: NSS Function to try (should raise KeyError if not found)
468 :param names: Names to check.
469 :return: Value return by first names list.
471 for name in names:
472 try:
473 return nssfn(name)
474 except KeyError:
475 pass
476 raise KeyError("Unable to find user/group in %r" % names)
479 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
480 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
483 def provision_paths_from_lp(lp, dnsdomain):
484 """Set the default paths for provisioning.
486 :param lp: Loadparm context.
487 :param dnsdomain: DNS Domain name
489 paths = ProvisionPaths()
490 paths.private_dir = lp.get("private dir")
491 paths.state_dir = lp.get("state directory")
493 # This is stored without path prefix for the "privateKeytab" attribute in
494 # "secrets_dns.ldif".
495 paths.dns_keytab = "dns.keytab"
496 paths.keytab = "secrets.keytab"
498 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
499 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
500 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
501 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
502 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
503 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
504 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
505 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
506 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
507 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
508 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
509 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
510 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
511 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
512 paths.hklm = "hklm.ldb"
513 paths.hkcr = "hkcr.ldb"
514 paths.hkcu = "hkcu.ldb"
515 paths.hku = "hku.ldb"
516 paths.hkpd = "hkpd.ldb"
517 paths.hkpt = "hkpt.ldb"
518 paths.sysvol = lp.get("path", "sysvol")
519 paths.netlogon = lp.get("path", "netlogon")
520 paths.smbconf = lp.configfile
521 return paths
524 def determine_netbios_name(hostname):
525 """Determine a netbios name from a hostname."""
526 # remove forbidden chars and force the length to be <16
527 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
528 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
531 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
532 serverrole=None, rootdn=None, domaindn=None, configdn=None,
533 schemadn=None, serverdn=None, sitename=None):
534 """Guess configuration settings to use."""
536 if hostname is None:
537 hostname = socket.gethostname().split(".")[0]
539 netbiosname = lp.get("netbios name")
540 if netbiosname is None:
541 netbiosname = determine_netbios_name(hostname)
542 netbiosname = netbiosname.upper()
543 if not valid_netbios_name(netbiosname):
544 raise InvalidNetbiosName(netbiosname)
546 if dnsdomain is None:
547 dnsdomain = lp.get("realm")
548 if dnsdomain is None or dnsdomain == "":
549 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
551 dnsdomain = dnsdomain.lower()
553 if serverrole is None:
554 serverrole = lp.get("server role")
555 if serverrole is None:
556 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
558 serverrole = serverrole.lower()
560 realm = dnsdomain.upper()
562 if lp.get("realm") == "":
563 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
565 if lp.get("realm").upper() != realm:
566 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))
568 if lp.get("server role").lower() != serverrole:
569 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))
571 if serverrole == "active directory domain controller":
572 if domain is None:
573 # This will, for better or worse, default to 'WORKGROUP'
574 domain = lp.get("workgroup")
575 domain = domain.upper()
577 if lp.get("workgroup").upper() != domain:
578 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))
580 if domaindn is None:
581 domaindn = samba.dn_from_dns_name(dnsdomain)
583 if domain == netbiosname:
584 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
585 else:
586 domain = netbiosname
587 if domaindn is None:
588 domaindn = "DC=" + netbiosname
590 if not valid_netbios_name(domain):
591 raise InvalidNetbiosName(domain)
593 if hostname.upper() == realm:
594 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
595 if netbiosname.upper() == realm:
596 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
597 if domain == realm:
598 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
600 if rootdn is None:
601 rootdn = domaindn
603 if configdn is None:
604 configdn = "CN=Configuration," + rootdn
605 if schemadn is None:
606 schemadn = "CN=Schema," + configdn
608 if sitename is None:
609 sitename = DEFAULTSITE
611 names = ProvisionNames()
612 names.rootdn = rootdn
613 names.domaindn = domaindn
614 names.configdn = configdn
615 names.schemadn = schemadn
616 names.ldapmanagerdn = "CN=Manager," + rootdn
617 names.dnsdomain = dnsdomain
618 names.domain = domain
619 names.realm = realm
620 names.netbiosname = netbiosname
621 names.hostname = hostname
622 names.sitename = sitename
623 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
624 netbiosname, sitename, configdn)
626 return names
629 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
630 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
631 global_param=None):
632 """Create a new smb.conf file based on a couple of basic settings.
634 assert smbconf is not None
636 if hostname is None:
637 hostname = socket.gethostname().split(".")[0]
639 netbiosname = determine_netbios_name(hostname)
641 if serverrole is None:
642 serverrole = "standalone server"
644 assert domain is not None
645 domain = domain.upper()
647 assert realm is not None
648 realm = realm.upper()
650 global_settings = {
651 "netbios name": netbiosname,
652 "workgroup": domain,
653 "realm": realm,
654 "server role": serverrole,
657 if lp is None:
658 lp = samba.param.LoadParm()
659 #Load non-existent file
660 if os.path.exists(smbconf):
661 lp.load(smbconf)
663 if global_param is not None:
664 for ent in global_param:
665 if global_param[ent] is not None:
666 global_settings[ent] = " ".join(global_param[ent])
668 if targetdir is not None:
669 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
670 global_settings["lock dir"] = os.path.abspath(targetdir)
671 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
672 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
674 lp.set("lock dir", os.path.abspath(targetdir))
675 lp.set("state directory", global_settings["state directory"])
676 lp.set("cache directory", global_settings["cache directory"])
678 if eadb:
679 if use_ntvfs and not lp.get("posix:eadb"):
680 if targetdir is not None:
681 privdir = os.path.join(targetdir, "private")
682 else:
683 privdir = lp.get("private dir")
684 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
685 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
686 if targetdir is not None:
687 statedir = os.path.join(targetdir, "state")
688 else:
689 statedir = lp.get("state directory")
690 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
692 shares = {}
693 if serverrole == "active directory domain controller":
694 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
695 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
696 "scripts")
697 else:
698 global_settings["passdb backend"] = "samba_dsdb"
700 f = open(smbconf, 'w')
701 try:
702 f.write("[globals]\n")
703 for key, val in global_settings.iteritems():
704 f.write("\t%s = %s\n" % (key, val))
705 f.write("\n")
707 for name, path in shares.iteritems():
708 f.write("[%s]\n" % name)
709 f.write("\tpath = %s\n" % path)
710 f.write("\tread only = no\n")
711 f.write("\n")
712 finally:
713 f.close()
714 # reload the smb.conf
715 lp.load(smbconf)
717 # and dump it without any values that are the default
718 # this ensures that any smb.conf parameters that were set
719 # on the provision/join command line are set in the resulting smb.conf
720 f = open(smbconf, mode='w')
721 try:
722 lp.dump(f, False)
723 finally:
724 f.close()
727 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
728 users_gid, root_gid):
729 """setup reasonable name mappings for sam names to unix names.
731 :param samdb: SamDB object.
732 :param idmap: IDmap db object.
733 :param sid: The domain sid.
734 :param domaindn: The domain DN.
735 :param root_uid: uid of the UNIX root user.
736 :param nobody_uid: uid of the UNIX nobody user.
737 :param users_gid: gid of the UNIX users group.
738 :param root_gid: gid of the UNIX root group.
740 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
742 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
743 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
746 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
747 provision_backend, names, schema, serverrole,
748 erase=False):
749 """Setup the partitions for the SAM database.
751 Alternatively, provision() may call this, and then populate the database.
753 :note: This will wipe the Sam Database!
755 :note: This function always removes the local SAM LDB file. The erase
756 parameter controls whether to erase the existing data, which
757 may not be stored locally but in LDAP.
760 assert session_info is not None
762 # We use options=["modules:"] to stop the modules loading - we
763 # just want to wipe and re-initialise the database, not start it up
765 try:
766 os.unlink(samdb_path)
767 except OSError:
768 pass
770 samdb = Ldb(url=samdb_path, session_info=session_info,
771 lp=lp, options=["modules:"])
773 ldap_backend_line = "# No LDAP backend"
774 if provision_backend.type != "ldb":
775 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
777 samdb.transaction_start()
778 try:
779 logger.info("Setting up sam.ldb partitions and settings")
780 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
781 "LDAP_BACKEND_LINE": ldap_backend_line
785 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
786 "BACKEND_TYPE": provision_backend.type,
787 "SERVER_ROLE": serverrole
790 logger.info("Setting up sam.ldb rootDSE")
791 setup_samdb_rootdse(samdb, names)
792 except:
793 samdb.transaction_cancel()
794 raise
795 else:
796 samdb.transaction_commit()
799 def secretsdb_self_join(secretsdb, domain,
800 netbiosname, machinepass, domainsid=None,
801 realm=None, dnsdomain=None,
802 keytab_path=None,
803 key_version_number=1,
804 secure_channel_type=SEC_CHAN_WKSTA):
805 """Add domain join-specific bits to a secrets database.
807 :param secretsdb: Ldb Handle to the secrets database
808 :param machinepass: Machine password
810 attrs = ["whenChanged",
811 "secret",
812 "priorSecret",
813 "priorChanged",
814 "krb5Keytab",
815 "privateKeytab"]
817 if realm is not None:
818 if dnsdomain is None:
819 dnsdomain = realm.lower()
820 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
821 else:
822 dnsname = None
823 shortname = netbiosname.lower()
825 # We don't need to set msg["flatname"] here, because rdn_name will handle
826 # it, and it causes problems for modifies anyway
827 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
828 msg["secureChannelType"] = [str(secure_channel_type)]
829 msg["objectClass"] = ["top", "primaryDomain"]
830 if dnsname is not None:
831 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
832 msg["realm"] = [realm]
833 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
834 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
835 msg["privateKeytab"] = ["secrets.keytab"]
837 msg["secret"] = [machinepass]
838 msg["samAccountName"] = ["%s$" % netbiosname]
839 msg["secureChannelType"] = [str(secure_channel_type)]
840 if domainsid is not None:
841 msg["objectSid"] = [ndr_pack(domainsid)]
843 # This complex expression tries to ensure that we don't have more
844 # than one record for this SID, realm or netbios domain at a time,
845 # but we don't delete the old record that we are about to modify,
846 # because that would delete the keytab and previous password.
847 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
848 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
849 scope=ldb.SCOPE_ONELEVEL)
851 for del_msg in res:
852 secretsdb.delete(del_msg.dn)
854 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
856 if len(res) == 1:
857 msg["priorSecret"] = [res[0]["secret"][0]]
858 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
860 try:
861 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
862 except KeyError:
863 pass
865 try:
866 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
867 except KeyError:
868 pass
870 for el in msg:
871 if el != 'dn':
872 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
873 secretsdb.modify(msg)
874 secretsdb.rename(res[0].dn, msg.dn)
875 else:
876 spn = [ 'HOST/%s' % shortname ]
877 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
878 # we are a domain controller then we add servicePrincipalName
879 # entries for the keytab code to update.
880 spn.extend([ 'HOST/%s' % dnsname ])
881 msg["servicePrincipalName"] = spn
883 secretsdb.add(msg)
886 def setup_secretsdb(paths, session_info, backend_credentials, lp):
887 """Setup the secrets database.
889 :note: This function does not handle exceptions and transaction on purpose,
890 it's up to the caller to do this job.
892 :param path: Path to the secrets database.
893 :param session_info: Session info.
894 :param credentials: Credentials
895 :param lp: Loadparm context
896 :return: LDB handle for the created secrets database
898 if os.path.exists(paths.secrets):
899 os.unlink(paths.secrets)
901 keytab_path = os.path.join(paths.private_dir, paths.keytab)
902 if os.path.exists(keytab_path):
903 os.unlink(keytab_path)
905 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
906 if os.path.exists(dns_keytab_path):
907 os.unlink(dns_keytab_path)
909 path = paths.secrets
911 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
912 secrets_ldb.erase()
913 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
914 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
915 secrets_ldb.transaction_start()
916 try:
917 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
919 if (backend_credentials is not None and
920 backend_credentials.authentication_requested()):
921 if backend_credentials.get_bind_dn() is not None:
922 setup_add_ldif(secrets_ldb,
923 setup_path("secrets_simple_ldap.ldif"), {
924 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
925 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
927 else:
928 setup_add_ldif(secrets_ldb,
929 setup_path("secrets_sasl_ldap.ldif"), {
930 "LDAPADMINUSER": backend_credentials.get_username(),
931 "LDAPADMINREALM": backend_credentials.get_realm(),
932 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
934 except:
935 secrets_ldb.transaction_cancel()
936 raise
937 return secrets_ldb
940 def setup_privileges(path, session_info, lp):
941 """Setup the privileges database.
943 :param path: Path to the privileges database.
944 :param session_info: Session info.
945 :param credentials: Credentials
946 :param lp: Loadparm context
947 :return: LDB handle for the created secrets database
949 if os.path.exists(path):
950 os.unlink(path)
951 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
952 privilege_ldb.erase()
953 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
956 def setup_registry(path, session_info, lp):
957 """Setup the registry.
959 :param path: Path to the registry database
960 :param session_info: Session information
961 :param credentials: Credentials
962 :param lp: Loadparm context
964 reg = samba.registry.Registry()
965 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
966 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
967 provision_reg = setup_path("provision.reg")
968 assert os.path.exists(provision_reg)
969 reg.diff_apply(provision_reg)
972 def setup_idmapdb(path, session_info, lp):
973 """Setup the idmap database.
975 :param path: path to the idmap database
976 :param session_info: Session information
977 :param credentials: Credentials
978 :param lp: Loadparm context
980 if os.path.exists(path):
981 os.unlink(path)
983 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
984 idmap_ldb.erase()
985 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
986 return idmap_ldb
989 def setup_samdb_rootdse(samdb, names):
990 """Setup the SamDB rootdse.
992 :param samdb: Sam Database handle
994 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
995 "SCHEMADN": names.schemadn,
996 "DOMAINDN": names.domaindn,
997 "ROOTDN" : names.rootdn,
998 "CONFIGDN": names.configdn,
999 "SERVERDN": names.serverdn,
1003 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1004 dns_backend, dnspass, domainsid, next_rid, invocationid,
1005 policyguid, policyguid_dc,
1006 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1007 """Join a host to its own domain."""
1008 assert isinstance(invocationid, str)
1009 if ntdsguid is not None:
1010 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1011 else:
1012 ntdsguid_line = ""
1014 if dc_rid is None:
1015 dc_rid = next_rid
1017 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1018 "CONFIGDN": names.configdn,
1019 "SCHEMADN": names.schemadn,
1020 "DOMAINDN": names.domaindn,
1021 "SERVERDN": names.serverdn,
1022 "INVOCATIONID": invocationid,
1023 "NETBIOSNAME": names.netbiosname,
1024 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1025 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1026 "DOMAINSID": str(domainsid),
1027 "DCRID": str(dc_rid),
1028 "SAMBA_VERSION_STRING": version,
1029 "NTDSGUID": ntdsguid_line,
1030 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1031 domainControllerFunctionality),
1032 "RIDALLOCATIONSTART": str(next_rid + 100),
1033 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1035 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1036 "POLICYGUID": policyguid,
1037 "POLICYGUID_DC": policyguid_dc,
1038 "DNSDOMAIN": names.dnsdomain,
1039 "DOMAINDN": names.domaindn})
1041 # If we are setting up a subdomain, then this has been replicated in, so we
1042 # don't need to add it
1043 if fill == FILL_FULL:
1044 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1045 "CONFIGDN": names.configdn,
1046 "SCHEMADN": names.schemadn,
1047 "DOMAINDN": names.domaindn,
1048 "SERVERDN": names.serverdn,
1049 "INVOCATIONID": invocationid,
1050 "NETBIOSNAME": names.netbiosname,
1051 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1052 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1053 "DOMAINSID": str(domainsid),
1054 "DCRID": str(dc_rid),
1055 "SAMBA_VERSION_STRING": version,
1056 "NTDSGUID": ntdsguid_line,
1057 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1058 domainControllerFunctionality)})
1060 # Setup fSMORoleOwner entries to point at the newly created DC entry
1061 setup_modify_ldif(samdb,
1062 setup_path("provision_self_join_modify_config.ldif"), {
1063 "CONFIGDN": names.configdn,
1064 "SCHEMADN": names.schemadn,
1065 "DEFAULTSITE": names.sitename,
1066 "NETBIOSNAME": names.netbiosname,
1067 "SERVERDN": names.serverdn,
1070 system_session_info = system_session()
1071 samdb.set_session_info(system_session_info)
1072 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1073 # modify a serverReference under cn=config when we are a subdomain, we must
1074 # be system due to ACLs
1075 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1076 "DOMAINDN": names.domaindn,
1077 "SERVERDN": names.serverdn,
1078 "NETBIOSNAME": names.netbiosname,
1081 samdb.set_session_info(admin_session_info)
1083 if dns_backend != "SAMBA_INTERNAL":
1084 # This is Samba4 specific and should be replaced by the correct
1085 # DNS AD-style setup
1086 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1087 "DNSDOMAIN": names.dnsdomain,
1088 "DOMAINDN": names.domaindn,
1089 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1090 "HOSTNAME" : names.hostname,
1091 "DNSNAME" : '%s.%s' % (
1092 names.netbiosname.lower(), names.dnsdomain.lower())
1096 def getpolicypath(sysvolpath, dnsdomain, guid):
1097 """Return the physical path of policy given its guid.
1099 :param sysvolpath: Path to the sysvol folder
1100 :param dnsdomain: DNS name of the AD domain
1101 :param guid: The GUID of the policy
1102 :return: A string with the complete path to the policy folder
1104 if guid[0] != "{":
1105 guid = "{%s}" % guid
1106 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1107 return policy_path
1110 def create_gpo_struct(policy_path):
1111 if not os.path.exists(policy_path):
1112 os.makedirs(policy_path, 0775)
1113 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1114 try:
1115 f.write("[General]\r\nVersion=0")
1116 finally:
1117 f.close()
1118 p = os.path.join(policy_path, "MACHINE")
1119 if not os.path.exists(p):
1120 os.makedirs(p, 0775)
1121 p = os.path.join(policy_path, "USER")
1122 if not os.path.exists(p):
1123 os.makedirs(p, 0775)
1126 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1127 """Create the default GPO for a domain
1129 :param sysvolpath: Physical path for the sysvol folder
1130 :param dnsdomain: DNS domain name of the AD domain
1131 :param policyguid: GUID of the default domain policy
1132 :param policyguid_dc: GUID of the default domain controler policy
1134 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1135 create_gpo_struct(policy_path)
1137 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1138 create_gpo_struct(policy_path)
1141 def setup_samdb(path, session_info, provision_backend, lp, names,
1142 logger, fill, serverrole, schema, am_rodc=False):
1143 """Setup a complete SAM Database.
1145 :note: This will wipe the main SAM database file!
1148 # Also wipes the database
1149 setup_samdb_partitions(path, logger=logger, lp=lp,
1150 provision_backend=provision_backend, session_info=session_info,
1151 names=names, serverrole=serverrole, schema=schema)
1153 # Load the database, but don's load the global schema and don't connect
1154 # quite yet
1155 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1156 credentials=provision_backend.credentials, lp=lp,
1157 global_schema=False, am_rodc=am_rodc)
1159 logger.info("Pre-loading the Samba 4 and AD schema")
1161 # Load the schema from the one we computed earlier
1162 samdb.set_schema(schema, write_indices_and_attributes=False)
1164 # Set the NTDS settings DN manually - in order to have it already around
1165 # before the provisioned tree exists and we connect
1166 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1168 # And now we can connect to the DB - the schema won't be loaded from the
1169 # DB
1170 samdb.connect(path)
1172 # But we have to give it one more kick to have it use the schema
1173 # during provision - it needs, now that it is connected, to write
1174 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1175 samdb.set_schema(schema, write_indices_and_attributes=True)
1177 return samdb
1180 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1181 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1182 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1183 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1185 if next_rid is None:
1186 next_rid = 1000
1188 # Provision does not make much sense values larger than 1000000000
1189 # as the upper range of the rIDAvailablePool is 1073741823 and
1190 # we don't want to create a domain that cannot allocate rids.
1191 if next_rid < 1000 or next_rid > 1000000000:
1192 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1193 error += "the valid range is %u-%u. The default is %u." % (
1194 1000, 1000000000, 1000)
1195 raise ProvisioningError(error)
1197 # ATTENTION: Do NOT change these default values without discussion with the
1198 # team and/or release manager. They have a big impact on the whole program!
1199 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1201 if dom_for_fun_level is None:
1202 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1204 if dom_for_fun_level > domainControllerFunctionality:
1205 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!")
1207 domainFunctionality = dom_for_fun_level
1208 forestFunctionality = dom_for_fun_level
1210 # Set the NTDS settings DN manually - in order to have it already around
1211 # before the provisioned tree exists and we connect
1212 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1214 samdb.transaction_start()
1215 try:
1216 # Set the domain functionality levels onto the database.
1217 # Various module (the password_hash module in particular) need
1218 # to know what level of AD we are emulating.
1220 # These will be fixed into the database via the database
1221 # modifictions below, but we need them set from the start.
1222 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1223 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1224 samdb.set_opaque_integer("domainControllerFunctionality",
1225 domainControllerFunctionality)
1227 samdb.set_domain_sid(str(domainsid))
1228 samdb.set_invocation_id(invocationid)
1230 logger.info("Adding DomainDN: %s" % names.domaindn)
1232 # impersonate domain admin
1233 admin_session_info = admin_session(lp, str(domainsid))
1234 samdb.set_session_info(admin_session_info)
1235 if domainguid is not None:
1236 domainguid_line = "objectGUID: %s\n-" % domainguid
1237 else:
1238 domainguid_line = ""
1240 descr = b64encode(get_domain_descriptor(domainsid))
1241 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1242 "DOMAINDN": names.domaindn,
1243 "DOMAINSID": str(domainsid),
1244 "DESCRIPTOR": descr,
1245 "DOMAINGUID": domainguid_line
1248 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1249 "DOMAINDN": names.domaindn,
1250 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1251 "NEXTRID": str(next_rid),
1252 "DEFAULTSITE": names.sitename,
1253 "CONFIGDN": names.configdn,
1254 "POLICYGUID": policyguid,
1255 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1256 "SAMBA_VERSION_STRING": version
1259 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1260 if fill == FILL_FULL:
1261 logger.info("Adding configuration container")
1262 descr = b64encode(get_config_descriptor(domainsid))
1263 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1264 "CONFIGDN": names.configdn,
1265 "DESCRIPTOR": descr,
1268 # The LDIF here was created when the Schema object was constructed
1269 logger.info("Setting up sam.ldb schema")
1270 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1271 samdb.modify_ldif(schema.schema_dn_modify)
1272 samdb.write_prefixes_from_schema()
1273 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1274 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1275 {"SCHEMADN": names.schemadn})
1277 # Now register this container in the root of the forest
1278 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1279 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1280 "subRefs")
1282 except:
1283 samdb.transaction_cancel()
1284 raise
1285 else:
1286 samdb.transaction_commit()
1288 samdb.transaction_start()
1289 try:
1290 samdb.invocation_id = invocationid
1292 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1293 if fill == FILL_FULL:
1294 logger.info("Setting up sam.ldb configuration data")
1295 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1296 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1297 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1298 "CONFIGDN": names.configdn,
1299 "NETBIOSNAME": names.netbiosname,
1300 "DEFAULTSITE": names.sitename,
1301 "DNSDOMAIN": names.dnsdomain,
1302 "DOMAIN": names.domain,
1303 "SCHEMADN": names.schemadn,
1304 "DOMAINDN": names.domaindn,
1305 "SERVERDN": names.serverdn,
1306 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1307 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1308 "PARTITIONS_DESCRIPTOR": partitions_descr,
1309 "SITES_DESCRIPTOR": sites_descr,
1312 logger.info("Setting up display specifiers")
1313 display_specifiers_ldif = read_ms_ldif(
1314 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1315 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1316 {"CONFIGDN": names.configdn})
1317 check_all_substituted(display_specifiers_ldif)
1318 samdb.add_ldif(display_specifiers_ldif)
1320 logger.info("Adding users container")
1321 users_desc = b64encode(get_domain_users_descriptor(domainsid))
1322 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1323 "DOMAINDN": names.domaindn,
1324 "USERS_DESCRIPTOR": users_desc
1326 logger.info("Modifying users container")
1327 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1328 "DOMAINDN": names.domaindn})
1329 logger.info("Adding computers container")
1330 computers_desc = b64encode(get_domain_computers_descriptor(domainsid))
1331 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1332 "DOMAINDN": names.domaindn,
1333 "COMPUTERS_DESCRIPTOR": computers_desc
1335 logger.info("Modifying computers container")
1336 setup_modify_ldif(samdb,
1337 setup_path("provision_computers_modify.ldif"), {
1338 "DOMAINDN": names.domaindn})
1339 logger.info("Setting up sam.ldb data")
1340 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
1341 builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
1342 controllers_desc = b64encode(get_domain_controllers_descriptor(domainsid))
1343 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1344 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1345 "DOMAINDN": names.domaindn,
1346 "NETBIOSNAME": names.netbiosname,
1347 "DEFAULTSITE": names.sitename,
1348 "CONFIGDN": names.configdn,
1349 "SERVERDN": names.serverdn,
1350 "RIDAVAILABLESTART": str(next_rid + 600),
1351 "POLICYGUID_DC": policyguid_dc,
1352 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1353 "BUILTIN_DESCRIPTOR": builtin_desc,
1354 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1357 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1358 if fill == FILL_FULL:
1359 setup_modify_ldif(samdb,
1360 setup_path("provision_configuration_references.ldif"), {
1361 "CONFIGDN": names.configdn,
1362 "SCHEMADN": names.schemadn})
1364 logger.info("Setting up well known security principals")
1365 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1366 "CONFIGDN": names.configdn,
1369 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1370 setup_modify_ldif(samdb,
1371 setup_path("provision_basedn_references.ldif"),
1372 {"DOMAINDN": names.domaindn})
1374 logger.info("Setting up sam.ldb users and groups")
1375 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1376 "DOMAINDN": names.domaindn,
1377 "DOMAINSID": str(domainsid),
1378 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1379 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1382 logger.info("Setting up self join")
1383 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1384 invocationid=invocationid,
1385 dns_backend=dns_backend,
1386 dnspass=dnspass,
1387 machinepass=machinepass,
1388 domainsid=domainsid,
1389 next_rid=next_rid,
1390 dc_rid=dc_rid,
1391 policyguid=policyguid,
1392 policyguid_dc=policyguid_dc,
1393 domainControllerFunctionality=domainControllerFunctionality,
1394 ntdsguid=ntdsguid)
1396 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1397 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1398 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1399 assert isinstance(names.ntdsguid, str)
1400 except:
1401 samdb.transaction_cancel()
1402 raise
1403 else:
1404 samdb.transaction_commit()
1405 return samdb
1408 FILL_FULL = "FULL"
1409 FILL_SUBDOMAIN = "SUBDOMAIN"
1410 FILL_NT4SYNC = "NT4SYNC"
1411 FILL_DRS = "DRS"
1412 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1413 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)"
1414 SYSVOL_SERVICE="sysvol"
1416 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1417 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1418 for root, dirs, files in os.walk(path, topdown=False):
1419 for name in files:
1420 setntacl(lp, os.path.join(root, name), acl, domsid,
1421 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1422 for name in dirs:
1423 setntacl(lp, os.path.join(root, name), acl, domsid,
1424 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1427 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1428 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1429 folders beneath.
1431 :param sysvol: Physical path for the sysvol folder
1432 :param dnsdomain: The DNS name of the domain
1433 :param domainsid: The SID of the domain
1434 :param domaindn: The DN of the domain (ie. DC=...)
1435 :param samdb: An LDB object on the SAM db
1436 :param lp: an LP object
1439 # Set ACL for GPO root folder
1440 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1441 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1442 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1444 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1445 attrs=["cn", "nTSecurityDescriptor"],
1446 expression="", scope=ldb.SCOPE_ONELEVEL)
1448 for policy in res:
1449 acl = ndr_unpack(security.descriptor,
1450 str(policy["nTSecurityDescriptor"])).as_sddl()
1451 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1452 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1453 str(domainsid), use_ntvfs,
1454 passdb=passdb)
1457 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1458 domaindn, lp, use_ntvfs):
1459 """Set the ACL for the sysvol share and the subfolders
1461 :param samdb: An LDB object on the SAM db
1462 :param netlogon: Physical path for the netlogon folder
1463 :param sysvol: Physical path for the sysvol folder
1464 :param uid: The UID of the "Administrator" user
1465 :param gid: The GID of the "Domain adminstrators" group
1466 :param domainsid: The SID of the domain
1467 :param dnsdomain: The DNS name of the domain
1468 :param domaindn: The DN of the domain (ie. DC=...)
1470 s4_passdb = None
1472 if not use_ntvfs:
1473 # This will ensure that the smbd code we are running when setting ACLs
1474 # is initialised with the smb.conf
1475 s3conf = s3param.get_context()
1476 s3conf.load(lp.configfile)
1477 # ensure we are using the right samba_dsdb passdb backend, no matter what
1478 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1479 passdb.reload_static_pdb()
1481 # ensure that we init the samba_dsdb backend, so the domain sid is
1482 # marked in secrets.tdb
1483 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1485 # now ensure everything matches correctly, to avoid wierd issues
1486 if passdb.get_global_sam_sid() != domainsid:
1487 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))
1489 domain_info = s4_passdb.domain_info()
1490 if domain_info["dom_sid"] != domainsid:
1491 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))
1493 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1494 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()))
1497 try:
1498 if use_ntvfs:
1499 os.chown(sysvol, -1, gid)
1500 except OSError:
1501 canchown = False
1502 else:
1503 canchown = True
1505 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1506 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1507 skip_invalid_chown=True, passdb=s4_passdb,
1508 service=SYSVOL_SERVICE)
1509 for root, dirs, files in os.walk(sysvol, topdown=False):
1510 for name in files:
1511 if use_ntvfs and canchown:
1512 os.chown(os.path.join(root, name), -1, gid)
1513 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1514 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1515 passdb=s4_passdb, service=SYSVOL_SERVICE)
1516 for name in dirs:
1517 if use_ntvfs and canchown:
1518 os.chown(os.path.join(root, name), -1, gid)
1519 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1520 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1521 passdb=s4_passdb, service=SYSVOL_SERVICE)
1523 # Set acls on Policy folder and policies folders
1524 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1526 def acl_type(direct_db_access):
1527 if direct_db_access:
1528 return "DB"
1529 else:
1530 return "VFS"
1532 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1533 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1534 fsacl_sddl = fsacl.as_sddl(domainsid)
1535 if fsacl_sddl != acl:
1536 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))
1538 for root, dirs, files in os.walk(path, topdown=False):
1539 for name in files:
1540 fsacl = getntacl(lp, os.path.join(root, name),
1541 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1542 if fsacl is None:
1543 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1544 fsacl_sddl = fsacl.as_sddl(domainsid)
1545 if fsacl_sddl != acl:
1546 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))
1548 for name in dirs:
1549 fsacl = getntacl(lp, os.path.join(root, name),
1550 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1551 if fsacl is None:
1552 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1553 fsacl_sddl = fsacl.as_sddl(domainsid)
1554 if fsacl_sddl != acl:
1555 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))
1558 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1559 direct_db_access):
1560 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1561 folders beneath.
1563 :param sysvol: Physical path for the sysvol folder
1564 :param dnsdomain: The DNS name of the domain
1565 :param domainsid: The SID of the domain
1566 :param domaindn: The DN of the domain (ie. DC=...)
1567 :param samdb: An LDB object on the SAM db
1568 :param lp: an LP object
1571 # Set ACL for GPO root folder
1572 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1573 fsacl = getntacl(lp, root_policy_path,
1574 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1575 if fsacl is None:
1576 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1577 fsacl_sddl = fsacl.as_sddl(domainsid)
1578 if fsacl_sddl != POLICIES_ACL:
1579 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))
1580 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1581 attrs=["cn", "nTSecurityDescriptor"],
1582 expression="", scope=ldb.SCOPE_ONELEVEL)
1584 for policy in res:
1585 acl = ndr_unpack(security.descriptor,
1586 str(policy["nTSecurityDescriptor"])).as_sddl()
1587 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1588 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1589 domainsid, direct_db_access)
1592 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1593 lp):
1594 """Set the ACL for the sysvol share and the subfolders
1596 :param samdb: An LDB object on the SAM db
1597 :param netlogon: Physical path for the netlogon folder
1598 :param sysvol: Physical path for the sysvol folder
1599 :param uid: The UID of the "Administrator" user
1600 :param gid: The GID of the "Domain adminstrators" group
1601 :param domainsid: The SID of the domain
1602 :param dnsdomain: The DNS name of the domain
1603 :param domaindn: The DN of the domain (ie. DC=...)
1606 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1607 s3conf = s3param.get_context()
1608 s3conf.load(lp.configfile)
1609 # ensure we are using the right samba_dsdb passdb backend, no matter what
1610 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1611 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1612 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1614 # now ensure everything matches correctly, to avoid wierd issues
1615 if passdb.get_global_sam_sid() != domainsid:
1616 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))
1618 domain_info = s4_passdb.domain_info()
1619 if domain_info["dom_sid"] != domainsid:
1620 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))
1622 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1623 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()))
1625 # Ensure we can read this directly, and via the smbd VFS
1626 for direct_db_access in [True, False]:
1627 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1628 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1629 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1630 if fsacl is None:
1631 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1632 fsacl_sddl = fsacl.as_sddl(domainsid)
1633 if fsacl_sddl != SYSVOL_ACL:
1634 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))
1636 # Check acls on Policy folder and policies folders
1637 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1638 direct_db_access)
1641 def interface_ips_v4(lp):
1642 """return only IPv4 IPs"""
1643 ips = samba.interface_ips(lp, False)
1644 ret = []
1645 for i in ips:
1646 if i.find(':') == -1:
1647 ret.append(i)
1648 return ret
1651 def interface_ips_v6(lp, linklocal=False):
1652 """return only IPv6 IPs"""
1653 ips = samba.interface_ips(lp, False)
1654 ret = []
1655 for i in ips:
1656 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1657 ret.append(i)
1658 return ret
1661 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1662 domainsid, schema=None,
1663 targetdir=None, samdb_fill=FILL_FULL,
1664 hostip=None, hostip6=None,
1665 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1666 domainguid=None, policyguid=None, policyguid_dc=None,
1667 invocationid=None, machinepass=None, ntdsguid=None,
1668 dns_backend=None, dnspass=None,
1669 serverrole=None, dom_for_fun_level=None,
1670 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1671 # create/adapt the group policy GUIDs
1672 # Default GUID for default policy are described at
1673 # "How Core Group Policy Works"
1674 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1675 if policyguid is None:
1676 policyguid = DEFAULT_POLICY_GUID
1677 policyguid = policyguid.upper()
1678 if policyguid_dc is None:
1679 policyguid_dc = DEFAULT_DC_POLICY_GUID
1680 policyguid_dc = policyguid_dc.upper()
1682 if invocationid is None:
1683 invocationid = str(uuid.uuid4())
1685 if krbtgtpass is None:
1686 krbtgtpass = samba.generate_random_password(128, 255)
1687 if machinepass is None:
1688 machinepass = samba.generate_random_password(128, 255)
1689 if dnspass is None:
1690 dnspass = samba.generate_random_password(128, 255)
1692 samdb = fill_samdb(samdb, lp, names, logger=logger,
1693 domainsid=domainsid, schema=schema, domainguid=domainguid,
1694 policyguid=policyguid, policyguid_dc=policyguid_dc,
1695 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1696 invocationid=invocationid, machinepass=machinepass,
1697 dns_backend=dns_backend, dnspass=dnspass,
1698 ntdsguid=ntdsguid, serverrole=serverrole,
1699 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1700 next_rid=next_rid, dc_rid=dc_rid)
1702 if serverrole == "active directory domain controller":
1704 # Set up group policies (domain policy and domain controller
1705 # policy)
1706 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1707 policyguid_dc)
1708 if not skip_sysvolacl:
1709 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1710 paths.root_gid, domainsid, names.dnsdomain,
1711 names.domaindn, lp, use_ntvfs)
1712 else:
1713 logger.info("Setting acl on sysvol skipped")
1715 secretsdb_self_join(secrets_ldb, domain=names.domain,
1716 realm=names.realm, dnsdomain=names.dnsdomain,
1717 netbiosname=names.netbiosname, domainsid=domainsid,
1718 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1720 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1721 # In future, this might be determined from some configuration
1722 kerberos_enctypes = str(ENC_ALL_TYPES)
1724 try:
1725 msg = ldb.Message(ldb.Dn(samdb,
1726 samdb.searchone("distinguishedName",
1727 expression="samAccountName=%s$" % names.netbiosname,
1728 scope=ldb.SCOPE_SUBTREE)))
1729 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1730 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1731 name="msDS-SupportedEncryptionTypes")
1732 samdb.modify(msg)
1733 except ldb.LdbError, (enum, estr):
1734 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1735 # It might be that this attribute does not exist in this schema
1736 raise
1738 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1739 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1740 dnspass=dnspass, os_level=dom_for_fun_level,
1741 targetdir=targetdir, site=DEFAULTSITE)
1743 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1744 attribute="objectGUID")
1745 assert isinstance(domainguid, str)
1747 lastProvisionUSNs = get_last_provision_usn(samdb)
1748 maxUSN = get_max_usn(samdb, str(names.rootdn))
1749 if lastProvisionUSNs is not None:
1750 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1751 else:
1752 set_provision_usn(samdb, 0, maxUSN, invocationid)
1754 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1755 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1756 { 'NTDSGUID' : names.ntdsguid })
1758 # fix any dangling GUIDs from the provision
1759 logger.info("Fixing provision GUIDs")
1760 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1761 quiet=True)
1762 samdb.transaction_start()
1763 try:
1764 # a small number of GUIDs are missing because of ordering issues in the
1765 # provision code
1766 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1767 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1768 scope=ldb.SCOPE_BASE,
1769 attrs=['defaultObjectCategory'])
1770 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1771 scope=ldb.SCOPE_ONELEVEL,
1772 attrs=['ipsecOwnersReference',
1773 'ipsecFilterReference',
1774 'ipsecISAKMPReference',
1775 'ipsecNegotiationPolicyReference',
1776 'ipsecNFAReference'])
1777 except:
1778 samdb.transaction_cancel()
1779 raise
1780 else:
1781 samdb.transaction_commit()
1784 _ROLES_MAP = {
1785 "ROLE_STANDALONE": "standalone server",
1786 "ROLE_DOMAIN_MEMBER": "member server",
1787 "ROLE_DOMAIN_BDC": "active directory domain controller",
1788 "ROLE_DOMAIN_PDC": "active directory domain controller",
1789 "dc": "active directory domain controller",
1790 "member": "member server",
1791 "domain controller": "active directory domain controller",
1792 "active directory domain controller": "active directory domain controller",
1793 "member server": "member server",
1794 "standalone": "standalone server",
1795 "standalone server": "standalone server",
1799 def sanitize_server_role(role):
1800 """Sanitize a server role name.
1802 :param role: Server role
1803 :raise ValueError: If the role can not be interpreted
1804 :return: Sanitized server role (one of "member server",
1805 "active directory domain controller", "standalone server")
1807 try:
1808 return _ROLES_MAP[role]
1809 except KeyError:
1810 raise ValueError(role)
1813 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1814 maxuid, maxgid):
1815 """Create AD entries for the fake ypserver.
1817 This is needed for being able to manipulate posix attrs via ADUC.
1819 samdb.transaction_start()
1820 try:
1821 logger.info("Setting up fake yp server settings")
1822 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1823 "DOMAINDN": domaindn,
1824 "NETBIOSNAME": netbiosname,
1825 "NISDOMAIN": nisdomain,
1827 except:
1828 samdb.transaction_cancel()
1829 raise
1830 else:
1831 samdb.transaction_commit()
1834 def provision(logger, session_info, credentials, smbconf=None,
1835 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1836 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1837 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1838 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1839 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1840 dns_backend=None, dns_forwarder=None, dnspass=None,
1841 invocationid=None, machinepass=None, ntdsguid=None,
1842 root=None, nobody=None, users=None, backup=None, aci=None,
1843 serverrole=None, dom_for_fun_level=None, backend_type=None,
1844 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1845 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1846 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1847 """Provision samba4
1849 :note: caution, this wipes all existing data!
1852 try:
1853 serverrole = sanitize_server_role(serverrole)
1854 except ValueError:
1855 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1857 if ldapadminpass is None:
1858 # Make a new, random password between Samba and it's LDAP server
1859 ldapadminpass = samba.generate_random_password(128, 255)
1861 if backend_type is None:
1862 backend_type = "ldb"
1864 if domainsid is None:
1865 domainsid = security.random_sid()
1866 else:
1867 domainsid = security.dom_sid(domainsid)
1869 root_uid = findnss_uid([root or "root"])
1870 nobody_uid = findnss_uid([nobody or "nobody"])
1871 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1872 root_gid = pwd.getpwuid(root_uid).pw_gid
1874 try:
1875 bind_gid = findnss_gid(["bind", "named"])
1876 except KeyError:
1877 bind_gid = None
1879 if targetdir is not None:
1880 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1881 elif smbconf is None:
1882 smbconf = samba.param.default_path()
1883 if not os.path.exists(os.path.dirname(smbconf)):
1884 os.makedirs(os.path.dirname(smbconf))
1886 server_services = []
1887 global_param = {}
1888 if use_rfc2307:
1889 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1891 if dns_backend != "SAMBA_INTERNAL":
1892 server_services.append("-dns")
1893 else:
1894 if dns_forwarder is not None:
1895 global_param["dns forwarder"] = [dns_forwarder]
1897 if use_ntvfs:
1898 server_services.append("+smb")
1899 server_services.append("-s3fs")
1900 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1902 if len(server_services) > 0:
1903 global_param["server services"] = server_services
1905 # only install a new smb.conf if there isn't one there already
1906 if os.path.exists(smbconf):
1907 # if Samba Team members can't figure out the weird errors
1908 # loading an empty smb.conf gives, then we need to be smarter.
1909 # Pretend it just didn't exist --abartlet
1910 f = open(smbconf, 'r')
1911 try:
1912 data = f.read().lstrip()
1913 finally:
1914 f.close()
1915 if data is None or data == "":
1916 make_smbconf(smbconf, hostname, domain, realm,
1917 targetdir, serverrole=serverrole,
1918 eadb=useeadb, use_ntvfs=use_ntvfs,
1919 lp=lp, global_param=global_param)
1920 else:
1921 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1922 serverrole=serverrole,
1923 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1925 if lp is None:
1926 lp = samba.param.LoadParm()
1927 lp.load(smbconf)
1928 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1929 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1930 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1931 sitename=sitename, rootdn=rootdn)
1932 paths = provision_paths_from_lp(lp, names.dnsdomain)
1934 paths.bind_gid = bind_gid
1935 paths.root_uid = root_uid;
1936 paths.root_gid = root_gid
1938 if hostip is None:
1939 logger.info("Looking up IPv4 addresses")
1940 hostips = interface_ips_v4(lp)
1941 if len(hostips) > 0:
1942 hostip = hostips[0]
1943 if len(hostips) > 1:
1944 logger.warning("More than one IPv4 address found. Using %s",
1945 hostip)
1946 if hostip == "127.0.0.1":
1947 hostip = None
1948 if hostip is None:
1949 logger.warning("No IPv4 address will be assigned")
1951 if hostip6 is None:
1952 logger.info("Looking up IPv6 addresses")
1953 hostips = interface_ips_v6(lp, linklocal=False)
1954 if hostips:
1955 hostip6 = hostips[0]
1956 if len(hostips) > 1:
1957 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1958 if hostip6 is None:
1959 logger.warning("No IPv6 address will be assigned")
1961 names.hostip = hostip
1962 names.hostip6 = hostip6
1964 if serverrole is None:
1965 serverrole = lp.get("server role")
1967 if not os.path.exists(paths.private_dir):
1968 os.mkdir(paths.private_dir)
1969 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1970 os.mkdir(os.path.join(paths.private_dir, "tls"))
1971 if not os.path.exists(paths.state_dir):
1972 os.mkdir(paths.state_dir)
1974 if paths.sysvol and not os.path.exists(paths.sysvol):
1975 os.makedirs(paths.sysvol, 0775)
1977 if not use_ntvfs and serverrole == "active directory domain controller":
1978 s3conf = s3param.get_context()
1979 s3conf.load(lp.configfile)
1981 if paths.sysvol is None:
1982 raise MissingShareError("sysvol", paths.smbconf)
1984 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1985 try:
1986 try:
1987 smbd.set_simple_acl(file.name, 0755, root_gid)
1988 except Exception:
1989 if not smbd.have_posix_acls():
1990 # This clue is only strictly correct for RPM and
1991 # Debian-like Linux systems, but hopefully other users
1992 # will get enough clue from it.
1993 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.")
1995 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1996 try:
1997 smbd.chown(file.name, root_uid, root_gid)
1998 except Exception:
1999 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
2000 finally:
2001 file.close()
2003 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2005 schema = Schema(domainsid, invocationid=invocationid,
2006 schemadn=names.schemadn)
2008 if backend_type == "ldb":
2009 provision_backend = LDBBackend(backend_type, paths=paths,
2010 lp=lp, credentials=credentials,
2011 names=names, logger=logger)
2012 elif backend_type == "existing":
2013 # If support for this is ever added back, then the URI will need to be
2014 # specified again
2015 provision_backend = ExistingBackend(backend_type, paths=paths,
2016 lp=lp, credentials=credentials,
2017 names=names, logger=logger,
2018 ldap_backend_forced_uri=None)
2019 elif backend_type == "fedora-ds":
2020 provision_backend = FDSBackend(backend_type, paths=paths,
2021 lp=lp, credentials=credentials,
2022 names=names, logger=logger, domainsid=domainsid,
2023 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2024 slapd_path=slapd_path,
2025 root=root)
2026 elif backend_type == "openldap":
2027 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2028 lp=lp, credentials=credentials,
2029 names=names, logger=logger, domainsid=domainsid,
2030 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2031 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
2032 else:
2033 raise ValueError("Unknown LDAP backend type selected")
2035 provision_backend.init()
2036 provision_backend.start()
2038 # only install a new shares config db if there is none
2039 if not os.path.exists(paths.shareconf):
2040 logger.info("Setting up share.ldb")
2041 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2042 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2044 logger.info("Setting up secrets.ldb")
2045 secrets_ldb = setup_secretsdb(paths,
2046 session_info=session_info,
2047 backend_credentials=provision_backend.secrets_credentials, lp=lp)
2049 try:
2050 logger.info("Setting up the registry")
2051 setup_registry(paths.hklm, session_info, lp=lp)
2053 logger.info("Setting up the privileges database")
2054 setup_privileges(paths.privilege, session_info, lp=lp)
2056 logger.info("Setting up idmap db")
2057 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2059 setup_name_mappings(idmap, sid=str(domainsid),
2060 root_uid=root_uid, nobody_uid=nobody_uid,
2061 users_gid=users_gid, root_gid=root_gid)
2063 logger.info("Setting up SAM db")
2064 samdb = setup_samdb(paths.samdb, session_info,
2065 provision_backend, lp, names, logger=logger,
2066 serverrole=serverrole,
2067 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2069 if serverrole == "active directory domain controller":
2070 if paths.netlogon is None:
2071 raise MissingShareError("netlogon", paths.smbconf)
2073 if paths.sysvol is None:
2074 raise MissingShareError("sysvol", paths.smbconf)
2076 if not os.path.isdir(paths.netlogon):
2077 os.makedirs(paths.netlogon, 0755)
2079 if adminpass is None:
2080 adminpass = samba.generate_random_password(12, 32)
2081 adminpass_generated = True
2082 else:
2083 adminpass_generated = False
2085 if samdb_fill == FILL_FULL:
2086 provision_fill(samdb, secrets_ldb, logger, names, paths,
2087 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2088 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2089 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2090 krbtgtpass=krbtgtpass, domainguid=domainguid,
2091 policyguid=policyguid, policyguid_dc=policyguid_dc,
2092 invocationid=invocationid, machinepass=machinepass,
2093 ntdsguid=ntdsguid, dns_backend=dns_backend,
2094 dnspass=dnspass, serverrole=serverrole,
2095 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2096 lp=lp, use_ntvfs=use_ntvfs,
2097 skip_sysvolacl=skip_sysvolacl)
2099 create_krb5_conf(paths.krb5conf,
2100 dnsdomain=names.dnsdomain, hostname=names.hostname,
2101 realm=names.realm)
2102 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2103 "generated at %s", paths.krb5conf)
2105 if serverrole == "active directory domain controller":
2106 create_dns_update_list(lp, logger, paths)
2108 backend_result = provision_backend.post_setup()
2109 provision_backend.shutdown()
2111 except:
2112 secrets_ldb.transaction_cancel()
2113 raise
2115 # Now commit the secrets.ldb to disk
2116 secrets_ldb.transaction_commit()
2118 # the commit creates the dns.keytab, now chown it
2119 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2120 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2121 try:
2122 os.chmod(dns_keytab_path, 0640)
2123 os.chown(dns_keytab_path, -1, paths.bind_gid)
2124 except OSError:
2125 if not os.environ.has_key('SAMBA_SELFTEST'):
2126 logger.info("Failed to chown %s to bind gid %u",
2127 dns_keytab_path, paths.bind_gid)
2129 result = ProvisionResult()
2130 result.server_role = serverrole
2131 result.domaindn = domaindn
2132 result.paths = paths
2133 result.names = names
2134 result.lp = lp
2135 result.samdb = samdb
2136 result.idmap = idmap
2137 result.domainsid = str(domainsid)
2139 if samdb_fill == FILL_FULL:
2140 result.adminpass_generated = adminpass_generated
2141 result.adminpass = adminpass
2142 else:
2143 result.adminpass_generated = False
2144 result.adminpass = None
2146 result.backend_result = backend_result
2148 if use_rfc2307:
2149 provision_fake_ypserver(logger=logger, samdb=samdb,
2150 domaindn=names.domaindn, netbiosname=names.netbiosname,
2151 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2153 return result
2156 def provision_become_dc(smbconf=None, targetdir=None,
2157 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2158 serverdn=None, domain=None, hostname=None, domainsid=None,
2159 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2160 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2161 dns_backend=None, root=None, nobody=None, users=None,
2162 backup=None, serverrole=None, ldap_backend=None,
2163 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2165 logger = logging.getLogger("provision")
2166 samba.set_debug_level(debuglevel)
2168 res = provision(logger, system_session(), None,
2169 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2170 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2171 configdn=configdn, serverdn=serverdn, domain=domain,
2172 hostname=hostname, hostip=None, domainsid=domainsid,
2173 machinepass=machinepass,
2174 serverrole="active directory domain controller",
2175 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2176 use_ntvfs=use_ntvfs)
2177 res.lp.set("debuglevel", str(debuglevel))
2178 return res
2181 def create_krb5_conf(path, dnsdomain, hostname, realm):
2182 """Write out a file containing zone statements suitable for inclusion in a
2183 named.conf file (including GSS-TSIG configuration).
2185 :param path: Path of the new named.conf file.
2186 :param dnsdomain: DNS Domain name
2187 :param hostname: Local hostname
2188 :param realm: Realm name
2190 setup_file(setup_path("krb5.conf"), path, {
2191 "DNSDOMAIN": dnsdomain,
2192 "HOSTNAME": hostname,
2193 "REALM": realm,
2197 class ProvisioningError(Exception):
2198 """A generic provision error."""
2200 def __init__(self, value):
2201 self.value = value
2203 def __str__(self):
2204 return "ProvisioningError: " + self.value
2207 class InvalidNetbiosName(Exception):
2208 """A specified name was not a valid NetBIOS name."""
2210 def __init__(self, name):
2211 super(InvalidNetbiosName, self).__init__(
2212 "The name '%r' is not a valid NetBIOS name" % name)
2215 class MissingShareError(ProvisioningError):
2217 def __init__(self, name, smbconf):
2218 super(MissingShareError, self).__init__(
2219 "Existing smb.conf does not have a [%s] share, but you are "
2220 "configuring a DC. Please remove %s or add the share manually." %
2221 (name, smbconf))