provision: add get_config_ntds_quotas_descriptor()
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision / __init__.py
blob221b580c46e575b61e05dc091bbdfc09a1e25d79
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,
98 from samba.provision.common import (
99 setup_path,
100 setup_add_ldif,
101 setup_modify_ldif,
103 from samba.provision.sambadns import (
104 setup_ad_dns,
105 create_dns_update_list
108 import samba.param
109 import samba.registry
110 from samba.schema import Schema
111 from samba.samdb import SamDB
112 from samba.dbchecker import dbcheck
115 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
116 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
117 DEFAULTSITE = "Default-First-Site-Name"
118 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
121 class ProvisionPaths(object):
123 def __init__(self):
124 self.shareconf = None
125 self.hklm = None
126 self.hkcu = None
127 self.hkcr = None
128 self.hku = None
129 self.hkpd = None
130 self.hkpt = None
131 self.samdb = None
132 self.idmapdb = None
133 self.secrets = None
134 self.keytab = None
135 self.dns_keytab = None
136 self.dns = None
137 self.winsdb = None
138 self.private_dir = None
139 self.state_dir = None
142 class ProvisionNames(object):
144 def __init__(self):
145 self.ncs = None
146 self.rootdn = None
147 self.domaindn = None
148 self.configdn = None
149 self.schemadn = None
150 self.dnsforestdn = None
151 self.dnsdomaindn = None
152 self.ldapmanagerdn = None
153 self.dnsdomain = None
154 self.realm = None
155 self.netbiosname = None
156 self.domain = None
157 self.hostname = None
158 self.sitename = None
159 self.smbconf = None
162 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf,
163 lp):
164 """Get key provision parameters (realm, domain, ...) from a given provision
166 :param samdb: An LDB object connected to the sam.ldb file
167 :param secretsdb: An LDB object connected to the secrets.ldb file
168 :param idmapdb: An LDB object connected to the idmap.ldb file
169 :param paths: A list of path to provision object
170 :param smbconf: Path to the smb.conf file
171 :param lp: A LoadParm object
172 :return: A list of key provision parameters
174 names = ProvisionNames()
175 names.adminpass = None
177 # NT domain, kerberos realm, root dn, domain dn, domain dns name
178 names.domain = string.upper(lp.get("workgroup"))
179 names.realm = lp.get("realm")
180 names.dnsdomain = names.realm.lower()
181 basedn = samba.dn_from_dns_name(names.dnsdomain)
182 names.realm = string.upper(names.realm)
183 # netbiosname
184 # Get the netbiosname first (could be obtained from smb.conf in theory)
185 res = secretsdb.search(expression="(flatname=%s)" %
186 names.domain,base="CN=Primary Domains",
187 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
188 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
190 names.smbconf = smbconf
192 # That's a bit simplistic but it's ok as long as we have only 3
193 # partitions
194 current = samdb.search(expression="(objectClass=*)",
195 base="", scope=ldb.SCOPE_BASE,
196 attrs=["defaultNamingContext", "schemaNamingContext",
197 "configurationNamingContext","rootDomainNamingContext",
198 "namingContexts"])
200 names.configdn = current[0]["configurationNamingContext"]
201 configdn = str(names.configdn)
202 names.schemadn = current[0]["schemaNamingContext"]
203 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
204 current[0]["defaultNamingContext"][0]))):
205 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
206 "is not the same ..." % (paths.samdb,
207 str(current[0]["defaultNamingContext"][0]),
208 paths.smbconf, basedn)))
210 names.domaindn=current[0]["defaultNamingContext"]
211 names.rootdn=current[0]["rootDomainNamingContext"]
212 names.ncs=current[0]["namingContexts"]
213 names.dnsforestdn = None
214 names.dnsdomaindn = None
216 for i in range(0, len(names.ncs)):
217 nc = names.ncs[i]
219 dnsforestdn = "DC=ForestDnsZones,%s" % (str(names.rootdn))
220 if nc == dnsforestdn:
221 names.dnsforestdn = dnsforestdn
222 continue
224 dnsdomaindn = "DC=DomainDnsZones,%s" % (str(names.domaindn))
225 if nc == dnsdomaindn:
226 names.dnsdomaindn = dnsdomaindn
227 continue
229 # default site name
230 res3 = samdb.search(expression="(objectClass=site)",
231 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
232 names.sitename = str(res3[0]["cn"])
234 # dns hostname and server dn
235 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
236 base="OU=Domain Controllers,%s" % basedn,
237 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
238 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain, "")
240 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
241 attrs=[], base=configdn)
242 names.serverdn = server_res[0].dn
244 # invocation id/objectguid
245 res5 = samdb.search(expression="(objectClass=*)",
246 base="CN=NTDS Settings,%s" % str(names.serverdn),
247 scope=ldb.SCOPE_BASE,
248 attrs=["invocationID", "objectGUID"])
249 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
250 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
252 # domain guid/sid
253 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
254 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
255 "objectSid","msDS-Behavior-Version" ])
256 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
257 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
258 if res6[0].get("msDS-Behavior-Version") is None or \
259 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
260 names.domainlevel = DS_DOMAIN_FUNCTION_2000
261 else:
262 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
264 # policy guid
265 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
266 base="CN=Policies,CN=System," + basedn,
267 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
268 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
269 # dc policy guid
270 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
271 " Policy)",
272 base="CN=Policies,CN=System," + basedn,
273 scope=ldb.SCOPE_ONELEVEL,
274 attrs=["cn","displayName"])
275 if len(res8) == 1:
276 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
277 else:
278 names.policyid_dc = None
280 res9 = idmapdb.search(expression="(cn=%s-%s)" %
281 (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR),
282 attrs=["xidNumber", "type"])
283 if len(res9) != 1:
284 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid (%s-%s" % (str(names.domainsid), security.DOMAIN_RID_ADMINISTRATOR))
285 if res9[0]["type"][0] == "ID_TYPE_BOTH":
286 names.root_gid = res9[0]["xidNumber"][0]
287 else:
288 names.root_gid = pwd.getpwuid(int(res9[0]["xidNumber"][0])).pw_gid
289 return names
292 def update_provision_usn(samdb, low, high, id, replace=False):
293 """Update the field provisionUSN in sam.ldb
295 This field is used to track range of USN modified by provision and
296 upgradeprovision.
297 This value is used afterward by next provision to figure out if
298 the field have been modified since last provision.
300 :param samdb: An LDB object connect to sam.ldb
301 :param low: The lowest USN modified by this upgrade
302 :param high: The highest USN modified by this upgrade
303 :param id: The invocation id of the samba's dc
304 :param replace: A boolean indicating if the range should replace any
305 existing one or appended (default)
308 tab = []
309 if not replace:
310 entry = samdb.search(base="@PROVISION",
311 scope=ldb.SCOPE_BASE,
312 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
313 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
314 if not re.search(';', e):
315 e = "%s;%s" % (e, id)
316 tab.append(str(e))
318 tab.append("%s-%s;%s" % (low, high, id))
319 delta = ldb.Message()
320 delta.dn = ldb.Dn(samdb, "@PROVISION")
321 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
322 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
323 entry = samdb.search(expression='provisionnerID=*',
324 base="@PROVISION", scope=ldb.SCOPE_BASE,
325 attrs=["provisionnerID"])
326 if len(entry) == 0 or len(entry[0]) == 0:
327 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
328 samdb.modify(delta)
331 def set_provision_usn(samdb, low, high, id):
332 """Set the field provisionUSN in sam.ldb
333 This field is used to track range of USN modified by provision and
334 upgradeprovision.
335 This value is used afterward by next provision to figure out if
336 the field have been modified since last provision.
338 :param samdb: An LDB object connect to sam.ldb
339 :param low: The lowest USN modified by this upgrade
340 :param high: The highest USN modified by this upgrade
341 :param id: The invocationId of the provision"""
343 tab = []
344 tab.append("%s-%s;%s" % (low, high, id))
346 delta = ldb.Message()
347 delta.dn = ldb.Dn(samdb, "@PROVISION")
348 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
349 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
350 samdb.add(delta)
353 def get_max_usn(samdb,basedn):
354 """ This function return the biggest USN present in the provision
356 :param samdb: A LDB object pointing to the sam.ldb
357 :param basedn: A string containing the base DN of the provision
358 (ie. DC=foo, DC=bar)
359 :return: The biggest USN in the provision"""
361 res = samdb.search(expression="objectClass=*",base=basedn,
362 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
363 controls=["search_options:1:2",
364 "server_sort:1:1:uSNChanged",
365 "paged_results:1:1"])
366 return res[0]["uSNChanged"]
369 def get_last_provision_usn(sam):
370 """Get USNs ranges modified by a provision or an upgradeprovision
372 :param sam: An LDB object pointing to the sam.ldb
373 :return: a dictionary which keys are invocation id and values are an array
374 of integer representing the different ranges
376 try:
377 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
378 base="@PROVISION", scope=ldb.SCOPE_BASE,
379 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
380 except ldb.LdbError, (ecode, emsg):
381 if ecode == ldb.ERR_NO_SUCH_OBJECT:
382 return None
383 raise
384 if len(entry) > 0:
385 myids = []
386 range = {}
387 p = re.compile(r'-')
388 if entry[0].get("provisionnerID"):
389 for e in entry[0]["provisionnerID"]:
390 myids.append(str(e))
391 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
392 tab1 = str(r).split(';')
393 if len(tab1) == 2:
394 id = tab1[1]
395 else:
396 id = "default"
397 if (len(myids) > 0 and id not in myids):
398 continue
399 tab2 = p.split(tab1[0])
400 if range.get(id) is None:
401 range[id] = []
402 range[id].append(tab2[0])
403 range[id].append(tab2[1])
404 return range
405 else:
406 return None
409 class ProvisionResult(object):
410 """Result of a provision.
412 :ivar server_role: The server role
413 :ivar paths: ProvisionPaths instance
414 :ivar domaindn: The domain dn, as string
417 def __init__(self):
418 self.server_role = None
419 self.paths = None
420 self.domaindn = None
421 self.lp = None
422 self.samdb = None
423 self.idmap = None
424 self.names = None
425 self.domainsid = None
426 self.adminpass_generated = None
427 self.adminpass = None
428 self.backend_result = None
430 def report_logger(self, logger):
431 """Report this provision result to a logger."""
432 logger.info(
433 "Once the above files are installed, your Samba4 server will "
434 "be ready to use")
435 if self.adminpass_generated:
436 logger.info("Admin password: %s", self.adminpass)
437 logger.info("Server Role: %s", self.server_role)
438 logger.info("Hostname: %s", self.names.hostname)
439 logger.info("NetBIOS Domain: %s", self.names.domain)
440 logger.info("DNS Domain: %s", self.names.dnsdomain)
441 logger.info("DOMAIN SID: %s", self.domainsid)
443 if self.backend_result:
444 self.backend_result.report_logger(logger)
447 def check_install(lp, session_info, credentials):
448 """Check whether the current install seems ok.
450 :param lp: Loadparm context
451 :param session_info: Session information
452 :param credentials: Credentials
454 if lp.get("realm") == "":
455 raise Exception("Realm empty")
456 samdb = Ldb(lp.samdb_url(), session_info=session_info,
457 credentials=credentials, lp=lp)
458 if len(samdb.search("(cn=Administrator)")) != 1:
459 raise ProvisioningError("No administrator account found")
462 def findnss(nssfn, names):
463 """Find a user or group from a list of possibilities.
465 :param nssfn: NSS Function to try (should raise KeyError if not found)
466 :param names: Names to check.
467 :return: Value return by first names list.
469 for name in names:
470 try:
471 return nssfn(name)
472 except KeyError:
473 pass
474 raise KeyError("Unable to find user/group in %r" % names)
477 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
478 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
481 def provision_paths_from_lp(lp, dnsdomain):
482 """Set the default paths for provisioning.
484 :param lp: Loadparm context.
485 :param dnsdomain: DNS Domain name
487 paths = ProvisionPaths()
488 paths.private_dir = lp.get("private dir")
489 paths.state_dir = lp.get("state directory")
491 # This is stored without path prefix for the "privateKeytab" attribute in
492 # "secrets_dns.ldif".
493 paths.dns_keytab = "dns.keytab"
494 paths.keytab = "secrets.keytab"
496 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
497 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
498 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
499 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
500 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
501 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
502 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
503 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
504 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
505 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
506 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
507 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
508 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
509 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
510 paths.hklm = "hklm.ldb"
511 paths.hkcr = "hkcr.ldb"
512 paths.hkcu = "hkcu.ldb"
513 paths.hku = "hku.ldb"
514 paths.hkpd = "hkpd.ldb"
515 paths.hkpt = "hkpt.ldb"
516 paths.sysvol = lp.get("path", "sysvol")
517 paths.netlogon = lp.get("path", "netlogon")
518 paths.smbconf = lp.configfile
519 return paths
522 def determine_netbios_name(hostname):
523 """Determine a netbios name from a hostname."""
524 # remove forbidden chars and force the length to be <16
525 netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
526 return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
529 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
530 serverrole=None, rootdn=None, domaindn=None, configdn=None,
531 schemadn=None, serverdn=None, sitename=None):
532 """Guess configuration settings to use."""
534 if hostname is None:
535 hostname = socket.gethostname().split(".")[0]
537 netbiosname = lp.get("netbios name")
538 if netbiosname is None:
539 netbiosname = determine_netbios_name(hostname)
540 netbiosname = netbiosname.upper()
541 if not valid_netbios_name(netbiosname):
542 raise InvalidNetbiosName(netbiosname)
544 if dnsdomain is None:
545 dnsdomain = lp.get("realm")
546 if dnsdomain is None or dnsdomain == "":
547 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
549 dnsdomain = dnsdomain.lower()
551 if serverrole is None:
552 serverrole = lp.get("server role")
553 if serverrole is None:
554 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
556 serverrole = serverrole.lower()
558 realm = dnsdomain.upper()
560 if lp.get("realm") == "":
561 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
563 if lp.get("realm").upper() != realm:
564 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))
566 if lp.get("server role").lower() != serverrole:
567 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))
569 if serverrole == "active directory domain controller":
570 if domain is None:
571 # This will, for better or worse, default to 'WORKGROUP'
572 domain = lp.get("workgroup")
573 domain = domain.upper()
575 if lp.get("workgroup").upper() != domain:
576 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))
578 if domaindn is None:
579 domaindn = samba.dn_from_dns_name(dnsdomain)
581 if domain == netbiosname:
582 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
583 else:
584 domain = netbiosname
585 if domaindn is None:
586 domaindn = "DC=" + netbiosname
588 if not valid_netbios_name(domain):
589 raise InvalidNetbiosName(domain)
591 if hostname.upper() == realm:
592 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
593 if netbiosname.upper() == realm:
594 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
595 if domain == realm:
596 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
598 if rootdn is None:
599 rootdn = domaindn
601 if configdn is None:
602 configdn = "CN=Configuration," + rootdn
603 if schemadn is None:
604 schemadn = "CN=Schema," + configdn
606 if sitename is None:
607 sitename = DEFAULTSITE
609 names = ProvisionNames()
610 names.rootdn = rootdn
611 names.domaindn = domaindn
612 names.configdn = configdn
613 names.schemadn = schemadn
614 names.ldapmanagerdn = "CN=Manager," + rootdn
615 names.dnsdomain = dnsdomain
616 names.domain = domain
617 names.realm = realm
618 names.netbiosname = netbiosname
619 names.hostname = hostname
620 names.sitename = sitename
621 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
622 netbiosname, sitename, configdn)
624 return names
627 def make_smbconf(smbconf, hostname, domain, realm, targetdir,
628 serverrole=None, eadb=False, use_ntvfs=False, lp=None,
629 global_param=None):
630 """Create a new smb.conf file based on a couple of basic settings.
632 assert smbconf is not None
634 if hostname is None:
635 hostname = socket.gethostname().split(".")[0]
637 netbiosname = determine_netbios_name(hostname)
639 if serverrole is None:
640 serverrole = "standalone server"
642 assert domain is not None
643 domain = domain.upper()
645 assert realm is not None
646 realm = realm.upper()
648 global_settings = {
649 "netbios name": netbiosname,
650 "workgroup": domain,
651 "realm": realm,
652 "server role": serverrole,
655 if lp is None:
656 lp = samba.param.LoadParm()
657 #Load non-existent file
658 if os.path.exists(smbconf):
659 lp.load(smbconf)
661 if global_param is not None:
662 for ent in global_param:
663 if global_param[ent] is not None:
664 global_settings[ent] = " ".join(global_param[ent])
666 if targetdir is not None:
667 global_settings["private dir"] = os.path.abspath(os.path.join(targetdir, "private"))
668 global_settings["lock dir"] = os.path.abspath(targetdir)
669 global_settings["state directory"] = os.path.abspath(os.path.join(targetdir, "state"))
670 global_settings["cache directory"] = os.path.abspath(os.path.join(targetdir, "cache"))
672 lp.set("lock dir", os.path.abspath(targetdir))
673 lp.set("state directory", global_settings["state directory"])
674 lp.set("cache directory", global_settings["cache directory"])
676 if eadb:
677 if use_ntvfs and not lp.get("posix:eadb"):
678 if targetdir is not None:
679 privdir = os.path.join(targetdir, "private")
680 else:
681 privdir = lp.get("private dir")
682 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
683 elif not use_ntvfs and not lp.get("xattr_tdb:file"):
684 if targetdir is not None:
685 statedir = os.path.join(targetdir, "state")
686 else:
687 statedir = lp.get("state directory")
688 lp.set("xattr_tdb:file", os.path.abspath(os.path.join(statedir, "xattr.tdb")))
690 shares = {}
691 if serverrole == "active directory domain controller":
692 shares["sysvol"] = os.path.join(lp.get("state directory"), "sysvol")
693 shares["netlogon"] = os.path.join(shares["sysvol"], realm.lower(),
694 "scripts")
695 else:
696 global_settings["passdb backend"] = "samba_dsdb"
698 f = open(smbconf, 'w')
699 try:
700 f.write("[globals]\n")
701 for key, val in global_settings.iteritems():
702 f.write("\t%s = %s\n" % (key, val))
703 f.write("\n")
705 for name, path in shares.iteritems():
706 f.write("[%s]\n" % name)
707 f.write("\tpath = %s\n" % path)
708 f.write("\tread only = no\n")
709 f.write("\n")
710 finally:
711 f.close()
712 # reload the smb.conf
713 lp.load(smbconf)
715 # and dump it without any values that are the default
716 # this ensures that any smb.conf parameters that were set
717 # on the provision/join command line are set in the resulting smb.conf
718 f = open(smbconf, mode='w')
719 try:
720 lp.dump(f, False)
721 finally:
722 f.close()
725 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
726 users_gid, root_gid):
727 """setup reasonable name mappings for sam names to unix names.
729 :param samdb: SamDB object.
730 :param idmap: IDmap db object.
731 :param sid: The domain sid.
732 :param domaindn: The domain DN.
733 :param root_uid: uid of the UNIX root user.
734 :param nobody_uid: uid of the UNIX nobody user.
735 :param users_gid: gid of the UNIX users group.
736 :param root_gid: gid of the UNIX root group.
738 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
740 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
741 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
744 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
745 provision_backend, names, schema, serverrole,
746 erase=False):
747 """Setup the partitions for the SAM database.
749 Alternatively, provision() may call this, and then populate the database.
751 :note: This will wipe the Sam Database!
753 :note: This function always removes the local SAM LDB file. The erase
754 parameter controls whether to erase the existing data, which
755 may not be stored locally but in LDAP.
758 assert session_info is not None
760 # We use options=["modules:"] to stop the modules loading - we
761 # just want to wipe and re-initialise the database, not start it up
763 try:
764 os.unlink(samdb_path)
765 except OSError:
766 pass
768 samdb = Ldb(url=samdb_path, session_info=session_info,
769 lp=lp, options=["modules:"])
771 ldap_backend_line = "# No LDAP backend"
772 if provision_backend.type != "ldb":
773 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
775 samdb.transaction_start()
776 try:
777 logger.info("Setting up sam.ldb partitions and settings")
778 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
779 "LDAP_BACKEND_LINE": ldap_backend_line
783 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
784 "BACKEND_TYPE": provision_backend.type,
785 "SERVER_ROLE": serverrole
788 logger.info("Setting up sam.ldb rootDSE")
789 setup_samdb_rootdse(samdb, names)
790 except:
791 samdb.transaction_cancel()
792 raise
793 else:
794 samdb.transaction_commit()
797 def secretsdb_self_join(secretsdb, domain,
798 netbiosname, machinepass, domainsid=None,
799 realm=None, dnsdomain=None,
800 keytab_path=None,
801 key_version_number=1,
802 secure_channel_type=SEC_CHAN_WKSTA):
803 """Add domain join-specific bits to a secrets database.
805 :param secretsdb: Ldb Handle to the secrets database
806 :param machinepass: Machine password
808 attrs = ["whenChanged",
809 "secret",
810 "priorSecret",
811 "priorChanged",
812 "krb5Keytab",
813 "privateKeytab"]
815 if realm is not None:
816 if dnsdomain is None:
817 dnsdomain = realm.lower()
818 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
819 else:
820 dnsname = None
821 shortname = netbiosname.lower()
823 # We don't need to set msg["flatname"] here, because rdn_name will handle
824 # it, and it causes problems for modifies anyway
825 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
826 msg["secureChannelType"] = [str(secure_channel_type)]
827 msg["objectClass"] = ["top", "primaryDomain"]
828 if dnsname is not None:
829 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
830 msg["realm"] = [realm]
831 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
832 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
833 msg["privateKeytab"] = ["secrets.keytab"]
835 msg["secret"] = [machinepass]
836 msg["samAccountName"] = ["%s$" % netbiosname]
837 msg["secureChannelType"] = [str(secure_channel_type)]
838 if domainsid is not None:
839 msg["objectSid"] = [ndr_pack(domainsid)]
841 # This complex expression tries to ensure that we don't have more
842 # than one record for this SID, realm or netbios domain at a time,
843 # but we don't delete the old record that we are about to modify,
844 # because that would delete the keytab and previous password.
845 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
846 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(distinguishedName=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
847 scope=ldb.SCOPE_ONELEVEL)
849 for del_msg in res:
850 secretsdb.delete(del_msg.dn)
852 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
854 if len(res) == 1:
855 msg["priorSecret"] = [res[0]["secret"][0]]
856 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
858 try:
859 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
860 except KeyError:
861 pass
863 try:
864 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
865 except KeyError:
866 pass
868 for el in msg:
869 if el != 'dn':
870 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
871 secretsdb.modify(msg)
872 secretsdb.rename(res[0].dn, msg.dn)
873 else:
874 spn = [ 'HOST/%s' % shortname ]
875 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
876 # we are a domain controller then we add servicePrincipalName
877 # entries for the keytab code to update.
878 spn.extend([ 'HOST/%s' % dnsname ])
879 msg["servicePrincipalName"] = spn
881 secretsdb.add(msg)
884 def setup_secretsdb(paths, session_info, backend_credentials, lp):
885 """Setup the secrets database.
887 :note: This function does not handle exceptions and transaction on purpose,
888 it's up to the caller to do this job.
890 :param path: Path to the secrets database.
891 :param session_info: Session info.
892 :param credentials: Credentials
893 :param lp: Loadparm context
894 :return: LDB handle for the created secrets database
896 if os.path.exists(paths.secrets):
897 os.unlink(paths.secrets)
899 keytab_path = os.path.join(paths.private_dir, paths.keytab)
900 if os.path.exists(keytab_path):
901 os.unlink(keytab_path)
903 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
904 if os.path.exists(dns_keytab_path):
905 os.unlink(dns_keytab_path)
907 path = paths.secrets
909 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
910 secrets_ldb.erase()
911 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
912 secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
913 secrets_ldb.transaction_start()
914 try:
915 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
917 if (backend_credentials is not None and
918 backend_credentials.authentication_requested()):
919 if backend_credentials.get_bind_dn() is not None:
920 setup_add_ldif(secrets_ldb,
921 setup_path("secrets_simple_ldap.ldif"), {
922 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
923 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
925 else:
926 setup_add_ldif(secrets_ldb,
927 setup_path("secrets_sasl_ldap.ldif"), {
928 "LDAPADMINUSER": backend_credentials.get_username(),
929 "LDAPADMINREALM": backend_credentials.get_realm(),
930 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
932 except:
933 secrets_ldb.transaction_cancel()
934 raise
935 return secrets_ldb
938 def setup_privileges(path, session_info, lp):
939 """Setup the privileges database.
941 :param path: Path to the privileges database.
942 :param session_info: Session info.
943 :param credentials: Credentials
944 :param lp: Loadparm context
945 :return: LDB handle for the created secrets database
947 if os.path.exists(path):
948 os.unlink(path)
949 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
950 privilege_ldb.erase()
951 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
954 def setup_registry(path, session_info, lp):
955 """Setup the registry.
957 :param path: Path to the registry database
958 :param session_info: Session information
959 :param credentials: Credentials
960 :param lp: Loadparm context
962 reg = samba.registry.Registry()
963 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
964 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
965 provision_reg = setup_path("provision.reg")
966 assert os.path.exists(provision_reg)
967 reg.diff_apply(provision_reg)
970 def setup_idmapdb(path, session_info, lp):
971 """Setup the idmap database.
973 :param path: path to the idmap database
974 :param session_info: Session information
975 :param credentials: Credentials
976 :param lp: Loadparm context
978 if os.path.exists(path):
979 os.unlink(path)
981 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
982 idmap_ldb.erase()
983 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
984 return idmap_ldb
987 def setup_samdb_rootdse(samdb, names):
988 """Setup the SamDB rootdse.
990 :param samdb: Sam Database handle
992 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
993 "SCHEMADN": names.schemadn,
994 "DOMAINDN": names.domaindn,
995 "ROOTDN" : names.rootdn,
996 "CONFIGDN": names.configdn,
997 "SERVERDN": names.serverdn,
1001 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
1002 dns_backend, dnspass, domainsid, next_rid, invocationid,
1003 policyguid, policyguid_dc,
1004 domainControllerFunctionality, ntdsguid=None, dc_rid=None):
1005 """Join a host to its own domain."""
1006 assert isinstance(invocationid, str)
1007 if ntdsguid is not None:
1008 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1009 else:
1010 ntdsguid_line = ""
1012 if dc_rid is None:
1013 dc_rid = next_rid
1015 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1016 "CONFIGDN": names.configdn,
1017 "SCHEMADN": names.schemadn,
1018 "DOMAINDN": names.domaindn,
1019 "SERVERDN": names.serverdn,
1020 "INVOCATIONID": invocationid,
1021 "NETBIOSNAME": names.netbiosname,
1022 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1023 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1024 "DOMAINSID": str(domainsid),
1025 "DCRID": str(dc_rid),
1026 "SAMBA_VERSION_STRING": version,
1027 "NTDSGUID": ntdsguid_line,
1028 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1029 domainControllerFunctionality),
1030 "RIDALLOCATIONSTART": str(next_rid + 100),
1031 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1033 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1034 "POLICYGUID": policyguid,
1035 "POLICYGUID_DC": policyguid_dc,
1036 "DNSDOMAIN": names.dnsdomain,
1037 "DOMAINDN": names.domaindn})
1039 # If we are setting up a subdomain, then this has been replicated in, so we
1040 # don't need to add it
1041 if fill == FILL_FULL:
1042 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1043 "CONFIGDN": names.configdn,
1044 "SCHEMADN": names.schemadn,
1045 "DOMAINDN": names.domaindn,
1046 "SERVERDN": names.serverdn,
1047 "INVOCATIONID": invocationid,
1048 "NETBIOSNAME": names.netbiosname,
1049 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1050 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1051 "DOMAINSID": str(domainsid),
1052 "DCRID": str(dc_rid),
1053 "SAMBA_VERSION_STRING": version,
1054 "NTDSGUID": ntdsguid_line,
1055 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1056 domainControllerFunctionality)})
1058 # Setup fSMORoleOwner entries to point at the newly created DC entry
1059 setup_modify_ldif(samdb,
1060 setup_path("provision_self_join_modify_config.ldif"), {
1061 "CONFIGDN": names.configdn,
1062 "SCHEMADN": names.schemadn,
1063 "DEFAULTSITE": names.sitename,
1064 "NETBIOSNAME": names.netbiosname,
1065 "SERVERDN": names.serverdn,
1068 system_session_info = system_session()
1069 samdb.set_session_info(system_session_info)
1070 # Setup fSMORoleOwner entries to point at the newly created DC entry to
1071 # modify a serverReference under cn=config when we are a subdomain, we must
1072 # be system due to ACLs
1073 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1074 "DOMAINDN": names.domaindn,
1075 "SERVERDN": names.serverdn,
1076 "NETBIOSNAME": names.netbiosname,
1079 samdb.set_session_info(admin_session_info)
1081 if dns_backend != "SAMBA_INTERNAL":
1082 # This is Samba4 specific and should be replaced by the correct
1083 # DNS AD-style setup
1084 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1085 "DNSDOMAIN": names.dnsdomain,
1086 "DOMAINDN": names.domaindn,
1087 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1088 "HOSTNAME" : names.hostname,
1089 "DNSNAME" : '%s.%s' % (
1090 names.netbiosname.lower(), names.dnsdomain.lower())
1094 def getpolicypath(sysvolpath, dnsdomain, guid):
1095 """Return the physical path of policy given its guid.
1097 :param sysvolpath: Path to the sysvol folder
1098 :param dnsdomain: DNS name of the AD domain
1099 :param guid: The GUID of the policy
1100 :return: A string with the complete path to the policy folder
1102 if guid[0] != "{":
1103 guid = "{%s}" % guid
1104 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1105 return policy_path
1108 def create_gpo_struct(policy_path):
1109 if not os.path.exists(policy_path):
1110 os.makedirs(policy_path, 0775)
1111 f = open(os.path.join(policy_path, "GPT.INI"), 'w')
1112 try:
1113 f.write("[General]\r\nVersion=0")
1114 finally:
1115 f.close()
1116 p = os.path.join(policy_path, "MACHINE")
1117 if not os.path.exists(p):
1118 os.makedirs(p, 0775)
1119 p = os.path.join(policy_path, "USER")
1120 if not os.path.exists(p):
1121 os.makedirs(p, 0775)
1124 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1125 """Create the default GPO for a domain
1127 :param sysvolpath: Physical path for the sysvol folder
1128 :param dnsdomain: DNS domain name of the AD domain
1129 :param policyguid: GUID of the default domain policy
1130 :param policyguid_dc: GUID of the default domain controler policy
1132 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1133 create_gpo_struct(policy_path)
1135 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1136 create_gpo_struct(policy_path)
1139 def setup_samdb(path, session_info, provision_backend, lp, names,
1140 logger, fill, serverrole, schema, am_rodc=False):
1141 """Setup a complete SAM Database.
1143 :note: This will wipe the main SAM database file!
1146 # Also wipes the database
1147 setup_samdb_partitions(path, logger=logger, lp=lp,
1148 provision_backend=provision_backend, session_info=session_info,
1149 names=names, serverrole=serverrole, schema=schema)
1151 # Load the database, but don's load the global schema and don't connect
1152 # quite yet
1153 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1154 credentials=provision_backend.credentials, lp=lp,
1155 global_schema=False, am_rodc=am_rodc)
1157 logger.info("Pre-loading the Samba 4 and AD schema")
1159 # Load the schema from the one we computed earlier
1160 samdb.set_schema(schema, write_indices_and_attributes=False)
1162 # Set the NTDS settings DN manually - in order to have it already around
1163 # before the provisioned tree exists and we connect
1164 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1166 # And now we can connect to the DB - the schema won't be loaded from the
1167 # DB
1168 samdb.connect(path)
1170 # But we have to give it one more kick to have it use the schema
1171 # during provision - it needs, now that it is connected, to write
1172 # the schema @ATTRIBUTES and @INDEXLIST records to the database.
1173 samdb.set_schema(schema, write_indices_and_attributes=True)
1175 return samdb
1178 def fill_samdb(samdb, lp, names, logger, domainsid, domainguid, policyguid,
1179 policyguid_dc, fill, adminpass, krbtgtpass, machinepass, dns_backend,
1180 dnspass, invocationid, ntdsguid, serverrole, am_rodc=False,
1181 dom_for_fun_level=None, schema=None, next_rid=None, dc_rid=None):
1183 if next_rid is None:
1184 next_rid = 1000
1186 # Provision does not make much sense values larger than 1000000000
1187 # as the upper range of the rIDAvailablePool is 1073741823 and
1188 # we don't want to create a domain that cannot allocate rids.
1189 if next_rid < 1000 or next_rid > 1000000000:
1190 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1191 error += "the valid range is %u-%u. The default is %u." % (
1192 1000, 1000000000, 1000)
1193 raise ProvisioningError(error)
1195 # ATTENTION: Do NOT change these default values without discussion with the
1196 # team and/or release manager. They have a big impact on the whole program!
1197 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1199 if dom_for_fun_level is None:
1200 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1202 if dom_for_fun_level > domainControllerFunctionality:
1203 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!")
1205 domainFunctionality = dom_for_fun_level
1206 forestFunctionality = dom_for_fun_level
1208 # Set the NTDS settings DN manually - in order to have it already around
1209 # before the provisioned tree exists and we connect
1210 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1212 samdb.transaction_start()
1213 try:
1214 # Set the domain functionality levels onto the database.
1215 # Various module (the password_hash module in particular) need
1216 # to know what level of AD we are emulating.
1218 # These will be fixed into the database via the database
1219 # modifictions below, but we need them set from the start.
1220 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1221 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1222 samdb.set_opaque_integer("domainControllerFunctionality",
1223 domainControllerFunctionality)
1225 samdb.set_domain_sid(str(domainsid))
1226 samdb.set_invocation_id(invocationid)
1228 logger.info("Adding DomainDN: %s" % names.domaindn)
1230 # impersonate domain admin
1231 admin_session_info = admin_session(lp, str(domainsid))
1232 samdb.set_session_info(admin_session_info)
1233 if domainguid is not None:
1234 domainguid_line = "objectGUID: %s\n-" % domainguid
1235 else:
1236 domainguid_line = ""
1238 descr = b64encode(get_domain_descriptor(domainsid))
1239 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1240 "DOMAINDN": names.domaindn,
1241 "DOMAINSID": str(domainsid),
1242 "DESCRIPTOR": descr,
1243 "DOMAINGUID": domainguid_line
1246 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1247 "DOMAINDN": names.domaindn,
1248 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1249 "NEXTRID": str(next_rid),
1250 "DEFAULTSITE": names.sitename,
1251 "CONFIGDN": names.configdn,
1252 "POLICYGUID": policyguid,
1253 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1254 "SAMBA_VERSION_STRING": version
1257 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1258 if fill == FILL_FULL:
1259 logger.info("Adding configuration container")
1260 descr = b64encode(get_config_descriptor(domainsid))
1261 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1262 "CONFIGDN": names.configdn,
1263 "DESCRIPTOR": descr,
1266 # The LDIF here was created when the Schema object was constructed
1267 logger.info("Setting up sam.ldb schema")
1268 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1269 samdb.modify_ldif(schema.schema_dn_modify)
1270 samdb.write_prefixes_from_schema()
1271 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1272 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1273 {"SCHEMADN": names.schemadn})
1275 # Now register this container in the root of the forest
1276 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1277 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1278 "subRefs")
1280 except:
1281 samdb.transaction_cancel()
1282 raise
1283 else:
1284 samdb.transaction_commit()
1286 samdb.transaction_start()
1287 try:
1288 samdb.invocation_id = invocationid
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("Setting up sam.ldb configuration data")
1293 partitions_descr = b64encode(get_config_partitions_descriptor(domainsid))
1294 sites_descr = b64encode(get_config_sites_descriptor(domainsid))
1295 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1296 "CONFIGDN": names.configdn,
1297 "NETBIOSNAME": names.netbiosname,
1298 "DEFAULTSITE": names.sitename,
1299 "DNSDOMAIN": names.dnsdomain,
1300 "DOMAIN": names.domain,
1301 "SCHEMADN": names.schemadn,
1302 "DOMAINDN": names.domaindn,
1303 "SERVERDN": names.serverdn,
1304 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1305 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1306 "PARTITIONS_DESCRIPTOR": partitions_descr,
1307 "SITES_DESCRIPTOR": sites_descr,
1310 logger.info("Setting up display specifiers")
1311 display_specifiers_ldif = read_ms_ldif(
1312 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1313 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1314 {"CONFIGDN": names.configdn})
1315 check_all_substituted(display_specifiers_ldif)
1316 samdb.add_ldif(display_specifiers_ldif)
1318 logger.info("Adding users container")
1319 users_desc = b64encode(get_domain_users_descriptor(domainsid))
1320 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1321 "DOMAINDN": names.domaindn,
1322 "USERS_DESCRIPTOR": users_desc
1324 logger.info("Modifying users container")
1325 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1326 "DOMAINDN": names.domaindn})
1327 logger.info("Adding computers container")
1328 computers_desc = b64encode(get_domain_computers_descriptor(domainsid))
1329 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1330 "DOMAINDN": names.domaindn,
1331 "COMPUTERS_DESCRIPTOR": computers_desc
1333 logger.info("Modifying computers container")
1334 setup_modify_ldif(samdb,
1335 setup_path("provision_computers_modify.ldif"), {
1336 "DOMAINDN": names.domaindn})
1337 logger.info("Setting up sam.ldb data")
1338 infrastructure_desc = b64encode(get_domain_infrastructure_descriptor(domainsid))
1339 builtin_desc = b64encode(get_domain_builtin_descriptor(domainsid))
1340 controllers_desc = b64encode(get_domain_controllers_descriptor(domainsid))
1341 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1342 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1343 "DOMAINDN": names.domaindn,
1344 "NETBIOSNAME": names.netbiosname,
1345 "DEFAULTSITE": names.sitename,
1346 "CONFIGDN": names.configdn,
1347 "SERVERDN": names.serverdn,
1348 "RIDAVAILABLESTART": str(next_rid + 600),
1349 "POLICYGUID_DC": policyguid_dc,
1350 "INFRASTRUCTURE_DESCRIPTOR": infrastructure_desc,
1351 "BUILTIN_DESCRIPTOR": builtin_desc,
1352 "DOMAIN_CONTROLLERS_DESCRIPTOR": controllers_desc,
1355 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1356 if fill == FILL_FULL:
1357 setup_modify_ldif(samdb,
1358 setup_path("provision_configuration_references.ldif"), {
1359 "CONFIGDN": names.configdn,
1360 "SCHEMADN": names.schemadn})
1362 logger.info("Setting up well known security principals")
1363 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1364 "CONFIGDN": names.configdn,
1367 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1368 setup_modify_ldif(samdb,
1369 setup_path("provision_basedn_references.ldif"),
1370 {"DOMAINDN": names.domaindn})
1372 logger.info("Setting up sam.ldb users and groups")
1373 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1374 "DOMAINDN": names.domaindn,
1375 "DOMAINSID": str(domainsid),
1376 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1377 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1380 logger.info("Setting up self join")
1381 setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1382 invocationid=invocationid,
1383 dns_backend=dns_backend,
1384 dnspass=dnspass,
1385 machinepass=machinepass,
1386 domainsid=domainsid,
1387 next_rid=next_rid,
1388 dc_rid=dc_rid,
1389 policyguid=policyguid,
1390 policyguid_dc=policyguid_dc,
1391 domainControllerFunctionality=domainControllerFunctionality,
1392 ntdsguid=ntdsguid)
1394 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1395 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1396 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1397 assert isinstance(names.ntdsguid, str)
1398 except:
1399 samdb.transaction_cancel()
1400 raise
1401 else:
1402 samdb.transaction_commit()
1403 return samdb
1406 FILL_FULL = "FULL"
1407 FILL_SUBDOMAIN = "SUBDOMAIN"
1408 FILL_NT4SYNC = "NT4SYNC"
1409 FILL_DRS = "DRS"
1410 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1411 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)"
1412 SYSVOL_SERVICE="sysvol"
1414 def set_dir_acl(path, acl, lp, domsid, use_ntvfs, passdb, service=SYSVOL_SERVICE):
1415 setntacl(lp, path, acl, domsid, use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1416 for root, dirs, files in os.walk(path, topdown=False):
1417 for name in files:
1418 setntacl(lp, os.path.join(root, name), acl, domsid,
1419 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1420 for name in dirs:
1421 setntacl(lp, os.path.join(root, name), acl, domsid,
1422 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=service)
1425 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb):
1426 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1427 folders beneath.
1429 :param sysvol: Physical path for the sysvol folder
1430 :param dnsdomain: The DNS name of the domain
1431 :param domainsid: The SID of the domain
1432 :param domaindn: The DN of the domain (ie. DC=...)
1433 :param samdb: An LDB object on the SAM db
1434 :param lp: an LP object
1437 # Set ACL for GPO root folder
1438 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1439 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid),
1440 use_ntvfs=use_ntvfs, skip_invalid_chown=True, passdb=passdb, service=SYSVOL_SERVICE)
1442 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1443 attrs=["cn", "nTSecurityDescriptor"],
1444 expression="", scope=ldb.SCOPE_ONELEVEL)
1446 for policy in res:
1447 acl = ndr_unpack(security.descriptor,
1448 str(policy["nTSecurityDescriptor"])).as_sddl()
1449 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1450 set_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1451 str(domainsid), use_ntvfs,
1452 passdb=passdb)
1455 def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain,
1456 domaindn, lp, use_ntvfs):
1457 """Set the ACL for the sysvol share and the subfolders
1459 :param samdb: An LDB object on the SAM db
1460 :param netlogon: Physical path for the netlogon folder
1461 :param sysvol: Physical path for the sysvol folder
1462 :param uid: The UID of the "Administrator" user
1463 :param gid: The GID of the "Domain adminstrators" group
1464 :param domainsid: The SID of the domain
1465 :param dnsdomain: The DNS name of the domain
1466 :param domaindn: The DN of the domain (ie. DC=...)
1468 s4_passdb = None
1470 if not use_ntvfs:
1471 # This will ensure that the smbd code we are running when setting ACLs
1472 # is initialised with the smb.conf
1473 s3conf = s3param.get_context()
1474 s3conf.load(lp.configfile)
1475 # ensure we are using the right samba_dsdb passdb backend, no matter what
1476 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1477 passdb.reload_static_pdb()
1479 # ensure that we init the samba_dsdb backend, so the domain sid is
1480 # marked in secrets.tdb
1481 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1483 # now ensure everything matches correctly, to avoid wierd issues
1484 if passdb.get_global_sam_sid() != domainsid:
1485 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))
1487 domain_info = s4_passdb.domain_info()
1488 if domain_info["dom_sid"] != domainsid:
1489 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))
1491 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1492 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()))
1495 try:
1496 if use_ntvfs:
1497 os.chown(sysvol, -1, gid)
1498 except OSError:
1499 canchown = False
1500 else:
1501 canchown = True
1503 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1504 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid), use_ntvfs=use_ntvfs,
1505 skip_invalid_chown=True, passdb=s4_passdb,
1506 service=SYSVOL_SERVICE)
1507 for root, dirs, files in os.walk(sysvol, topdown=False):
1508 for name in files:
1509 if use_ntvfs and canchown:
1510 os.chown(os.path.join(root, name), -1, gid)
1511 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1512 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1513 passdb=s4_passdb, service=SYSVOL_SERVICE)
1514 for name in dirs:
1515 if use_ntvfs and canchown:
1516 os.chown(os.path.join(root, name), -1, gid)
1517 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid),
1518 use_ntvfs=use_ntvfs, skip_invalid_chown=True,
1519 passdb=s4_passdb, service=SYSVOL_SERVICE)
1521 # Set acls on Policy folder and policies folders
1522 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs, passdb=s4_passdb)
1524 def acl_type(direct_db_access):
1525 if direct_db_access:
1526 return "DB"
1527 else:
1528 return "VFS"
1530 def check_dir_acl(path, acl, lp, domainsid, direct_db_access):
1531 fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1532 fsacl_sddl = fsacl.as_sddl(domainsid)
1533 if fsacl_sddl != acl:
1534 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))
1536 for root, dirs, files in os.walk(path, topdown=False):
1537 for name in files:
1538 fsacl = getntacl(lp, os.path.join(root, name),
1539 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1540 if fsacl is None:
1541 raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1542 fsacl_sddl = fsacl.as_sddl(domainsid)
1543 if fsacl_sddl != acl:
1544 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))
1546 for name in dirs:
1547 fsacl = getntacl(lp, os.path.join(root, name),
1548 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1549 if fsacl is None:
1550 raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name)))
1551 fsacl_sddl = fsacl.as_sddl(domainsid)
1552 if fsacl_sddl != acl:
1553 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))
1556 def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1557 direct_db_access):
1558 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1559 folders beneath.
1561 :param sysvol: Physical path for the sysvol folder
1562 :param dnsdomain: The DNS name of the domain
1563 :param domainsid: The SID of the domain
1564 :param domaindn: The DN of the domain (ie. DC=...)
1565 :param samdb: An LDB object on the SAM db
1566 :param lp: an LP object
1569 # Set ACL for GPO root folder
1570 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1571 fsacl = getntacl(lp, root_policy_path,
1572 direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1573 if fsacl is None:
1574 raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path))
1575 fsacl_sddl = fsacl.as_sddl(domainsid)
1576 if fsacl_sddl != POLICIES_ACL:
1577 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))
1578 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1579 attrs=["cn", "nTSecurityDescriptor"],
1580 expression="", scope=ldb.SCOPE_ONELEVEL)
1582 for policy in res:
1583 acl = ndr_unpack(security.descriptor,
1584 str(policy["nTSecurityDescriptor"])).as_sddl()
1585 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1586 check_dir_acl(policy_path, dsacl2fsacl(acl, domainsid), lp,
1587 domainsid, direct_db_access)
1590 def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn,
1591 lp):
1592 """Set the ACL for the sysvol share and the subfolders
1594 :param samdb: An LDB object on the SAM db
1595 :param netlogon: Physical path for the netlogon folder
1596 :param sysvol: Physical path for the sysvol folder
1597 :param uid: The UID of the "Administrator" user
1598 :param gid: The GID of the "Domain adminstrators" group
1599 :param domainsid: The SID of the domain
1600 :param dnsdomain: The DNS name of the domain
1601 :param domaindn: The DN of the domain (ie. DC=...)
1604 # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf
1605 s3conf = s3param.get_context()
1606 s3conf.load(lp.configfile)
1607 # ensure we are using the right samba_dsdb passdb backend, no matter what
1608 s3conf.set("passdb backend", "samba_dsdb:%s" % samdb.url)
1609 # ensure that we init the samba_dsdb backend, so the domain sid is marked in secrets.tdb
1610 s4_passdb = passdb.PDB(s3conf.get("passdb backend"))
1612 # now ensure everything matches correctly, to avoid wierd issues
1613 if passdb.get_global_sam_sid() != domainsid:
1614 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))
1616 domain_info = s4_passdb.domain_info()
1617 if domain_info["dom_sid"] != domainsid:
1618 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))
1620 if domain_info["dns_domain"].upper() != dnsdomain.upper():
1621 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()))
1623 # Ensure we can read this directly, and via the smbd VFS
1624 for direct_db_access in [True, False]:
1625 # Check the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1626 for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]:
1627 fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE)
1628 if fsacl is None:
1629 raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path))
1630 fsacl_sddl = fsacl.as_sddl(domainsid)
1631 if fsacl_sddl != SYSVOL_ACL:
1632 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))
1634 # Check acls on Policy folder and policies folders
1635 check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp,
1636 direct_db_access)
1639 def interface_ips_v4(lp):
1640 """return only IPv4 IPs"""
1641 ips = samba.interface_ips(lp, False)
1642 ret = []
1643 for i in ips:
1644 if i.find(':') == -1:
1645 ret.append(i)
1646 return ret
1649 def interface_ips_v6(lp, linklocal=False):
1650 """return only IPv6 IPs"""
1651 ips = samba.interface_ips(lp, False)
1652 ret = []
1653 for i in ips:
1654 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1655 ret.append(i)
1656 return ret
1659 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1660 domainsid, schema=None,
1661 targetdir=None, samdb_fill=FILL_FULL,
1662 hostip=None, hostip6=None,
1663 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1664 domainguid=None, policyguid=None, policyguid_dc=None,
1665 invocationid=None, machinepass=None, ntdsguid=None,
1666 dns_backend=None, dnspass=None,
1667 serverrole=None, dom_for_fun_level=None,
1668 am_rodc=False, lp=None, use_ntvfs=False, skip_sysvolacl=False):
1669 # create/adapt the group policy GUIDs
1670 # Default GUID for default policy are described at
1671 # "How Core Group Policy Works"
1672 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1673 if policyguid is None:
1674 policyguid = DEFAULT_POLICY_GUID
1675 policyguid = policyguid.upper()
1676 if policyguid_dc is None:
1677 policyguid_dc = DEFAULT_DC_POLICY_GUID
1678 policyguid_dc = policyguid_dc.upper()
1680 if invocationid is None:
1681 invocationid = str(uuid.uuid4())
1683 if krbtgtpass is None:
1684 krbtgtpass = samba.generate_random_password(128, 255)
1685 if machinepass is None:
1686 machinepass = samba.generate_random_password(128, 255)
1687 if dnspass is None:
1688 dnspass = samba.generate_random_password(128, 255)
1690 samdb = fill_samdb(samdb, lp, names, logger=logger,
1691 domainsid=domainsid, schema=schema, domainguid=domainguid,
1692 policyguid=policyguid, policyguid_dc=policyguid_dc,
1693 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1694 invocationid=invocationid, machinepass=machinepass,
1695 dns_backend=dns_backend, dnspass=dnspass,
1696 ntdsguid=ntdsguid, serverrole=serverrole,
1697 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1698 next_rid=next_rid, dc_rid=dc_rid)
1700 if serverrole == "active directory domain controller":
1702 # Set up group policies (domain policy and domain controller
1703 # policy)
1704 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1705 policyguid_dc)
1706 if not skip_sysvolacl:
1707 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
1708 paths.root_gid, domainsid, names.dnsdomain,
1709 names.domaindn, lp, use_ntvfs)
1710 else:
1711 logger.info("Setting acl on sysvol skipped")
1713 secretsdb_self_join(secrets_ldb, domain=names.domain,
1714 realm=names.realm, dnsdomain=names.dnsdomain,
1715 netbiosname=names.netbiosname, domainsid=domainsid,
1716 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1718 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1719 # In future, this might be determined from some configuration
1720 kerberos_enctypes = str(ENC_ALL_TYPES)
1722 try:
1723 msg = ldb.Message(ldb.Dn(samdb,
1724 samdb.searchone("distinguishedName",
1725 expression="samAccountName=%s$" % names.netbiosname,
1726 scope=ldb.SCOPE_SUBTREE)))
1727 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1728 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1729 name="msDS-SupportedEncryptionTypes")
1730 samdb.modify(msg)
1731 except ldb.LdbError, (enum, estr):
1732 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1733 # It might be that this attribute does not exist in this schema
1734 raise
1736 setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1737 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1738 dnspass=dnspass, os_level=dom_for_fun_level,
1739 targetdir=targetdir, site=DEFAULTSITE)
1741 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1742 attribute="objectGUID")
1743 assert isinstance(domainguid, str)
1745 lastProvisionUSNs = get_last_provision_usn(samdb)
1746 maxUSN = get_max_usn(samdb, str(names.rootdn))
1747 if lastProvisionUSNs is not None:
1748 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1749 else:
1750 set_provision_usn(samdb, 0, maxUSN, invocationid)
1752 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1753 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1754 { 'NTDSGUID' : names.ntdsguid })
1756 # fix any dangling GUIDs from the provision
1757 logger.info("Fixing provision GUIDs")
1758 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1759 quiet=True)
1760 samdb.transaction_start()
1761 try:
1762 # a small number of GUIDs are missing because of ordering issues in the
1763 # provision code
1764 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1765 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1766 scope=ldb.SCOPE_BASE,
1767 attrs=['defaultObjectCategory'])
1768 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1769 scope=ldb.SCOPE_ONELEVEL,
1770 attrs=['ipsecOwnersReference',
1771 'ipsecFilterReference',
1772 'ipsecISAKMPReference',
1773 'ipsecNegotiationPolicyReference',
1774 'ipsecNFAReference'])
1775 except:
1776 samdb.transaction_cancel()
1777 raise
1778 else:
1779 samdb.transaction_commit()
1782 _ROLES_MAP = {
1783 "ROLE_STANDALONE": "standalone server",
1784 "ROLE_DOMAIN_MEMBER": "member server",
1785 "ROLE_DOMAIN_BDC": "active directory domain controller",
1786 "ROLE_DOMAIN_PDC": "active directory domain controller",
1787 "dc": "active directory domain controller",
1788 "member": "member server",
1789 "domain controller": "active directory domain controller",
1790 "active directory domain controller": "active directory domain controller",
1791 "member server": "member server",
1792 "standalone": "standalone server",
1793 "standalone server": "standalone server",
1797 def sanitize_server_role(role):
1798 """Sanitize a server role name.
1800 :param role: Server role
1801 :raise ValueError: If the role can not be interpreted
1802 :return: Sanitized server role (one of "member server",
1803 "active directory domain controller", "standalone server")
1805 try:
1806 return _ROLES_MAP[role]
1807 except KeyError:
1808 raise ValueError(role)
1811 def provision_fake_ypserver(logger, samdb, domaindn, netbiosname, nisdomain,
1812 maxuid, maxgid):
1813 """Create AD entries for the fake ypserver.
1815 This is needed for being able to manipulate posix attrs via ADUC.
1817 samdb.transaction_start()
1818 try:
1819 logger.info("Setting up fake yp server settings")
1820 setup_add_ldif(samdb, setup_path("ypServ30.ldif"), {
1821 "DOMAINDN": domaindn,
1822 "NETBIOSNAME": netbiosname,
1823 "NISDOMAIN": nisdomain,
1825 except:
1826 samdb.transaction_cancel()
1827 raise
1828 else:
1829 samdb.transaction_commit()
1832 def provision(logger, session_info, credentials, smbconf=None,
1833 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1834 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1835 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1836 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None,
1837 krbtgtpass=None, domainguid=None, policyguid=None, policyguid_dc=None,
1838 dns_backend=None, dns_forwarder=None, dnspass=None,
1839 invocationid=None, machinepass=None, ntdsguid=None,
1840 root=None, nobody=None, users=None, backup=None, aci=None,
1841 serverrole=None, dom_for_fun_level=None, backend_type=None,
1842 sitename=None, ol_mmr_urls=None, ol_olc=None, slapd_path="/bin/false",
1843 useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
1844 use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True):
1845 """Provision samba4
1847 :note: caution, this wipes all existing data!
1850 try:
1851 serverrole = sanitize_server_role(serverrole)
1852 except ValueError:
1853 raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
1855 if ldapadminpass is None:
1856 # Make a new, random password between Samba and it's LDAP server
1857 ldapadminpass = samba.generate_random_password(128, 255)
1859 if backend_type is None:
1860 backend_type = "ldb"
1862 if domainsid is None:
1863 domainsid = security.random_sid()
1864 else:
1865 domainsid = security.dom_sid(domainsid)
1867 root_uid = findnss_uid([root or "root"])
1868 nobody_uid = findnss_uid([nobody or "nobody"])
1869 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1870 root_gid = pwd.getpwuid(root_uid).pw_gid
1872 try:
1873 bind_gid = findnss_gid(["bind", "named"])
1874 except KeyError:
1875 bind_gid = None
1877 if targetdir is not None:
1878 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1879 elif smbconf is None:
1880 smbconf = samba.param.default_path()
1881 if not os.path.exists(os.path.dirname(smbconf)):
1882 os.makedirs(os.path.dirname(smbconf))
1884 server_services = []
1885 global_param = {}
1886 if use_rfc2307:
1887 global_param["idmap_ldb:use rfc2307"] = ["yes"]
1889 if dns_backend != "SAMBA_INTERNAL":
1890 server_services.append("-dns")
1891 else:
1892 if dns_forwarder is not None:
1893 global_param["dns forwarder"] = [dns_forwarder]
1895 if use_ntvfs:
1896 server_services.append("+smb")
1897 server_services.append("-s3fs")
1898 global_param["dcerpc endpoint servers"] = ["+winreg", "+srvsvc"]
1900 if len(server_services) > 0:
1901 global_param["server services"] = server_services
1903 # only install a new smb.conf if there isn't one there already
1904 if os.path.exists(smbconf):
1905 # if Samba Team members can't figure out the weird errors
1906 # loading an empty smb.conf gives, then we need to be smarter.
1907 # Pretend it just didn't exist --abartlet
1908 f = open(smbconf, 'r')
1909 try:
1910 data = f.read().lstrip()
1911 finally:
1912 f.close()
1913 if data is None or data == "":
1914 make_smbconf(smbconf, hostname, domain, realm,
1915 targetdir, serverrole=serverrole,
1916 eadb=useeadb, use_ntvfs=use_ntvfs,
1917 lp=lp, global_param=global_param)
1918 else:
1919 make_smbconf(smbconf, hostname, domain, realm, targetdir,
1920 serverrole=serverrole,
1921 eadb=useeadb, use_ntvfs=use_ntvfs, lp=lp, global_param=global_param)
1923 if lp is None:
1924 lp = samba.param.LoadParm()
1925 lp.load(smbconf)
1926 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1927 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1928 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1929 sitename=sitename, rootdn=rootdn)
1930 paths = provision_paths_from_lp(lp, names.dnsdomain)
1932 paths.bind_gid = bind_gid
1933 paths.root_uid = root_uid;
1934 paths.root_gid = root_gid
1936 if hostip is None:
1937 logger.info("Looking up IPv4 addresses")
1938 hostips = interface_ips_v4(lp)
1939 if len(hostips) > 0:
1940 hostip = hostips[0]
1941 if len(hostips) > 1:
1942 logger.warning("More than one IPv4 address found. Using %s",
1943 hostip)
1944 if hostip == "127.0.0.1":
1945 hostip = None
1946 if hostip is None:
1947 logger.warning("No IPv4 address will be assigned")
1949 if hostip6 is None:
1950 logger.info("Looking up IPv6 addresses")
1951 hostips = interface_ips_v6(lp, linklocal=False)
1952 if hostips:
1953 hostip6 = hostips[0]
1954 if len(hostips) > 1:
1955 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1956 if hostip6 is None:
1957 logger.warning("No IPv6 address will be assigned")
1959 names.hostip = hostip
1960 names.hostip6 = hostip6
1962 if serverrole is None:
1963 serverrole = lp.get("server role")
1965 if not os.path.exists(paths.private_dir):
1966 os.mkdir(paths.private_dir)
1967 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1968 os.mkdir(os.path.join(paths.private_dir, "tls"))
1969 if not os.path.exists(paths.state_dir):
1970 os.mkdir(paths.state_dir)
1972 if paths.sysvol and not os.path.exists(paths.sysvol):
1973 os.makedirs(paths.sysvol, 0775)
1975 if not use_ntvfs and serverrole == "active directory domain controller":
1976 s3conf = s3param.get_context()
1977 s3conf.load(lp.configfile)
1979 if paths.sysvol is None:
1980 raise MissingShareError("sysvol", paths.smbconf)
1982 file = tempfile.NamedTemporaryFile(dir=os.path.abspath(paths.sysvol))
1983 try:
1984 try:
1985 smbd.set_simple_acl(file.name, 0755, root_gid)
1986 except Exception:
1987 if not smbd.have_posix_acls():
1988 # This clue is only strictly correct for RPM and
1989 # Debian-like Linux systems, but hopefully other users
1990 # will get enough clue from it.
1991 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.")
1993 raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.")
1994 try:
1995 smbd.chown(file.name, root_uid, root_gid)
1996 except Exception:
1997 raise ProvisioningError("Unable to chown a file on your filesystem. You may not be running provision as root.")
1998 finally:
1999 file.close()
2001 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
2003 schema = Schema(domainsid, invocationid=invocationid,
2004 schemadn=names.schemadn)
2006 if backend_type == "ldb":
2007 provision_backend = LDBBackend(backend_type, paths=paths,
2008 lp=lp, credentials=credentials,
2009 names=names, logger=logger)
2010 elif backend_type == "existing":
2011 # If support for this is ever added back, then the URI will need to be
2012 # specified again
2013 provision_backend = ExistingBackend(backend_type, paths=paths,
2014 lp=lp, credentials=credentials,
2015 names=names, logger=logger,
2016 ldap_backend_forced_uri=None)
2017 elif backend_type == "fedora-ds":
2018 provision_backend = FDSBackend(backend_type, paths=paths,
2019 lp=lp, credentials=credentials,
2020 names=names, logger=logger, domainsid=domainsid,
2021 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2022 slapd_path=slapd_path,
2023 root=root)
2024 elif backend_type == "openldap":
2025 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
2026 lp=lp, credentials=credentials,
2027 names=names, logger=logger, domainsid=domainsid,
2028 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
2029 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
2030 else:
2031 raise ValueError("Unknown LDAP backend type selected")
2033 provision_backend.init()
2034 provision_backend.start()
2036 # only install a new shares config db if there is none
2037 if not os.path.exists(paths.shareconf):
2038 logger.info("Setting up share.ldb")
2039 share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
2040 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
2042 logger.info("Setting up secrets.ldb")
2043 secrets_ldb = setup_secretsdb(paths,
2044 session_info=session_info,
2045 backend_credentials=provision_backend.secrets_credentials, lp=lp)
2047 try:
2048 logger.info("Setting up the registry")
2049 setup_registry(paths.hklm, session_info, lp=lp)
2051 logger.info("Setting up the privileges database")
2052 setup_privileges(paths.privilege, session_info, lp=lp)
2054 logger.info("Setting up idmap db")
2055 idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
2057 setup_name_mappings(idmap, sid=str(domainsid),
2058 root_uid=root_uid, nobody_uid=nobody_uid,
2059 users_gid=users_gid, root_gid=root_gid)
2061 logger.info("Setting up SAM db")
2062 samdb = setup_samdb(paths.samdb, session_info,
2063 provision_backend, lp, names, logger=logger,
2064 serverrole=serverrole,
2065 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
2067 if serverrole == "active directory domain controller":
2068 if paths.netlogon is None:
2069 raise MissingShareError("netlogon", paths.smbconf)
2071 if paths.sysvol is None:
2072 raise MissingShareError("sysvol", paths.smbconf)
2074 if not os.path.isdir(paths.netlogon):
2075 os.makedirs(paths.netlogon, 0755)
2077 if adminpass is None:
2078 adminpass = samba.generate_random_password(12, 32)
2079 adminpass_generated = True
2080 else:
2081 adminpass_generated = False
2083 if samdb_fill == FILL_FULL:
2084 provision_fill(samdb, secrets_ldb, logger, names, paths,
2085 schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
2086 hostip=hostip, hostip6=hostip6, domainsid=domainsid,
2087 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
2088 krbtgtpass=krbtgtpass, domainguid=domainguid,
2089 policyguid=policyguid, policyguid_dc=policyguid_dc,
2090 invocationid=invocationid, machinepass=machinepass,
2091 ntdsguid=ntdsguid, dns_backend=dns_backend,
2092 dnspass=dnspass, serverrole=serverrole,
2093 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
2094 lp=lp, use_ntvfs=use_ntvfs,
2095 skip_sysvolacl=skip_sysvolacl)
2097 create_krb5_conf(paths.krb5conf,
2098 dnsdomain=names.dnsdomain, hostname=names.hostname,
2099 realm=names.realm)
2100 logger.info("A Kerberos configuration suitable for Samba 4 has been "
2101 "generated at %s", paths.krb5conf)
2103 if serverrole == "active directory domain controller":
2104 create_dns_update_list(lp, logger, paths)
2106 backend_result = provision_backend.post_setup()
2107 provision_backend.shutdown()
2109 except:
2110 secrets_ldb.transaction_cancel()
2111 raise
2113 # Now commit the secrets.ldb to disk
2114 secrets_ldb.transaction_commit()
2116 # the commit creates the dns.keytab, now chown it
2117 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
2118 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
2119 try:
2120 os.chmod(dns_keytab_path, 0640)
2121 os.chown(dns_keytab_path, -1, paths.bind_gid)
2122 except OSError:
2123 if not os.environ.has_key('SAMBA_SELFTEST'):
2124 logger.info("Failed to chown %s to bind gid %u",
2125 dns_keytab_path, paths.bind_gid)
2127 result = ProvisionResult()
2128 result.server_role = serverrole
2129 result.domaindn = domaindn
2130 result.paths = paths
2131 result.names = names
2132 result.lp = lp
2133 result.samdb = samdb
2134 result.idmap = idmap
2135 result.domainsid = str(domainsid)
2137 if samdb_fill == FILL_FULL:
2138 result.adminpass_generated = adminpass_generated
2139 result.adminpass = adminpass
2140 else:
2141 result.adminpass_generated = False
2142 result.adminpass = None
2144 result.backend_result = backend_result
2146 if use_rfc2307:
2147 provision_fake_ypserver(logger=logger, samdb=samdb,
2148 domaindn=names.domaindn, netbiosname=names.netbiosname,
2149 nisdomain=names.domain.lower(), maxuid=maxuid, maxgid=maxgid)
2151 return result
2154 def provision_become_dc(smbconf=None, targetdir=None,
2155 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
2156 serverdn=None, domain=None, hostname=None, domainsid=None,
2157 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
2158 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
2159 dns_backend=None, root=None, nobody=None, users=None,
2160 backup=None, serverrole=None, ldap_backend=None,
2161 ldap_backend_type=None, sitename=None, debuglevel=1, use_ntvfs=False):
2163 logger = logging.getLogger("provision")
2164 samba.set_debug_level(debuglevel)
2166 res = provision(logger, system_session(), None,
2167 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
2168 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
2169 configdn=configdn, serverdn=serverdn, domain=domain,
2170 hostname=hostname, hostip=None, domainsid=domainsid,
2171 machinepass=machinepass,
2172 serverrole="active directory domain controller",
2173 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass,
2174 use_ntvfs=use_ntvfs)
2175 res.lp.set("debuglevel", str(debuglevel))
2176 return res
2179 def create_krb5_conf(path, dnsdomain, hostname, realm):
2180 """Write out a file containing zone statements suitable for inclusion in a
2181 named.conf file (including GSS-TSIG configuration).
2183 :param path: Path of the new named.conf file.
2184 :param dnsdomain: DNS Domain name
2185 :param hostname: Local hostname
2186 :param realm: Realm name
2188 setup_file(setup_path("krb5.conf"), path, {
2189 "DNSDOMAIN": dnsdomain,
2190 "HOSTNAME": hostname,
2191 "REALM": realm,
2195 class ProvisioningError(Exception):
2196 """A generic provision error."""
2198 def __init__(self, value):
2199 self.value = value
2201 def __str__(self):
2202 return "ProvisioningError: " + self.value
2205 class InvalidNetbiosName(Exception):
2206 """A specified name was not a valid NetBIOS name."""
2208 def __init__(self, name):
2209 super(InvalidNetbiosName, self).__init__(
2210 "The name '%r' is not a valid NetBIOS name" % name)
2213 class MissingShareError(ProvisioningError):
2215 def __init__(self, name, smbconf):
2216 super(MissingShareError, self).__init__(
2217 "Existing smb.conf does not have a [%s] share, but you are "
2218 "configuring a DC. Please remove %s or add the share manually." %
2219 (name, smbconf))