2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
43 from credentials import Credentials, DONT_USE_KERBEROS
44 from auth import system_session, admin_session
45 from samba import version, Ldb, substitute_var, valid_netbios_name
46 from samba import check_all_substituted
47 from samba import DS_DOMAIN_FUNCTION_2000, DS_DC_FUNCTION_2008_R2
48 from samba.samdb import SamDB
49 from samba.idmap import IDmapDB
50 from samba.dcerpc import security
52 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
53 from ms_schema import read_ms_schema
54 from ms_display_specifiers import read_ms_ldif
55 from signal import SIGTERM
57 __docformat__ = "restructuredText"
60 class ProvisioningError(ValueError):
65 """Find the setup directory used by provision."""
66 dirname = os.path.dirname(__file__)
67 if "/site-packages/" in dirname:
68 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
69 for suffix in ["share/setup", "share/samba/setup", "setup"]:
70 ret = os.path.join(prefix, suffix)
71 if os.path.isdir(ret):
74 ret = os.path.join(dirname, "../../../setup")
75 if os.path.isdir(ret):
77 raise Exception("Unable to find setup directory.")
80 DEFAULTSITE = "Default-First-Site-Name"
82 class InvalidNetbiosName(Exception):
83 """A specified name was not a valid NetBIOS name."""
84 def __init__(self, name):
85 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
88 class ProvisionPaths(object):
101 self.dns_keytab = None
104 self.private_dir = None
106 self.slapdconf = None
107 self.modulesconf = None
108 self.memberofconf = None
109 self.fedoradsinf = None
110 self.fedoradspartitions = None
111 self.fedoradssasl = None
113 self.olmmrserveridsconf = None
114 self.olmmrsyncreplconf = None
117 self.olcseedldif = None
120 class ProvisionNames(object):
127 self.ldapmanagerdn = None
128 self.dnsdomain = None
130 self.netbiosname = None
137 class ProvisionResult(object):
144 class Schema(object):
145 def __init__(self, setup_path, schemadn=None,
146 serverdn=None, sambadn=None, ldap_backend_type=None):
147 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
149 :param samdb: Load a schema into a SamDB.
150 :param setup_path: Setup path function.
151 :param schemadn: DN of the schema
152 :param serverdn: DN of the server
154 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
158 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
159 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
160 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
161 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
162 check_all_substituted(self.schema_data)
164 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
165 {"SCHEMADN": schemadn,
166 "SERVERDN": serverdn,
168 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
169 {"SCHEMADN": schemadn
172 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
173 prefixmap = b64encode(prefixmap)
175 # We don't actually add this ldif, just parse it
176 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
177 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
180 # Return a hash with the forward attribute as a key and the back as the value
181 def get_linked_attributes(schemadn,schemaldb):
182 attrs = ["linkID", "lDAPDisplayName"]
183 res = schemaldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
185 for i in range (0, len(res)):
186 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
187 target = schemaldb.searchone(basedn=schemadn,
188 expression=expression,
189 attribute="lDAPDisplayName",
191 if target is not None:
192 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
196 def get_dnsyntax_attributes(schemadn,schemaldb):
197 attrs = ["linkID", "lDAPDisplayName"]
198 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
200 for i in range (0, len(res)):
201 attributes.append(str(res[i]["lDAPDisplayName"]))
206 def check_install(lp, session_info, credentials):
207 """Check whether the current install seems ok.
209 :param lp: Loadparm context
210 :param session_info: Session information
211 :param credentials: Credentials
213 if lp.get("realm") == "":
214 raise Exception("Realm empty")
215 ldb = Ldb(lp.get("sam database"), session_info=session_info,
216 credentials=credentials, lp=lp)
217 if len(ldb.search("(cn=Administrator)")) != 1:
218 raise ProvisioningError("No administrator account found")
221 def findnss(nssfn, names):
222 """Find a user or group from a list of possibilities.
224 :param nssfn: NSS Function to try (should raise KeyError if not found)
225 :param names: Names to check.
226 :return: Value return by first names list.
233 raise KeyError("Unable to find user/group %r" % names)
236 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
237 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
240 def read_and_sub_file(file, subst_vars):
241 """Read a file and sub in variables found in it
243 :param file: File to be read (typically from setup directory)
244 param subst_vars: Optional variables to subsitute in the file.
246 data = open(file, 'r').read()
247 if subst_vars is not None:
248 data = substitute_var(data, subst_vars)
249 check_all_substituted(data)
253 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
254 """Setup a ldb in the private dir.
256 :param ldb: LDB file to import data into
257 :param ldif_path: Path of the LDIF file to load
258 :param subst_vars: Optional variables to subsitute in LDIF.
260 assert isinstance(ldif_path, str)
262 data = read_and_sub_file(ldif_path, subst_vars)
266 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
267 """Modify a ldb in the private dir.
269 :param ldb: LDB object.
270 :param ldif_path: LDIF file path.
271 :param subst_vars: Optional dictionary with substitution variables.
273 data = read_and_sub_file(ldif_path, subst_vars)
275 ldb.modify_ldif(data)
278 def setup_ldb(ldb, ldif_path, subst_vars):
279 """Import a LDIF a file into a LDB handle, optionally substituting variables.
281 :note: Either all LDIF data will be added or none (using transactions).
283 :param ldb: LDB file to import into.
284 :param ldif_path: Path to the LDIF file.
285 :param subst_vars: Dictionary with substitution variables.
287 assert ldb is not None
288 ldb.transaction_start()
290 setup_add_ldif(ldb, ldif_path, subst_vars)
292 ldb.transaction_cancel()
294 ldb.transaction_commit()
297 def setup_file(template, fname, subst_vars):
298 """Setup a file in the private dir.
300 :param template: Path of the template file.
301 :param fname: Path of the file to create.
302 :param subst_vars: Substitution variables.
306 if os.path.exists(f):
309 data = read_and_sub_file(template, subst_vars)
310 open(f, 'w').write(data)
313 def provision_paths_from_lp(lp, dnsdomain):
314 """Set the default paths for provisioning.
316 :param lp: Loadparm context.
317 :param dnsdomain: DNS Domain name
319 paths = ProvisionPaths()
320 paths.private_dir = lp.get("private dir")
321 paths.keytab = "secrets.keytab"
322 paths.dns_keytab = "dns.keytab"
324 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
325 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
326 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
327 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
328 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
329 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
330 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
331 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
332 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
333 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
334 paths.phpldapadminconfig = os.path.join(paths.private_dir,
335 "phpldapadmin-config.php")
336 paths.ldapdir = os.path.join(paths.private_dir,
338 paths.slapdconf = os.path.join(paths.ldapdir,
340 paths.slapdpid = os.path.join(paths.ldapdir,
342 paths.modulesconf = os.path.join(paths.ldapdir,
344 paths.memberofconf = os.path.join(paths.ldapdir,
346 paths.fedoradsinf = os.path.join(paths.ldapdir,
348 paths.fedoradspartitions = os.path.join(paths.ldapdir,
349 "fedorads-partitions.ldif")
350 paths.fedoradssasl = os.path.join(paths.ldapdir,
351 "fedorads-sasl.ldif")
352 paths.fedoradssamba = os.path.join(paths.ldapdir,
353 "fedorads-samba.ldif")
354 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
355 "mmr_serverids.conf")
356 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
358 paths.olcdir = os.path.join(paths.ldapdir,
360 paths.olcseedldif = os.path.join(paths.ldapdir,
362 paths.hklm = "hklm.ldb"
363 paths.hkcr = "hkcr.ldb"
364 paths.hkcu = "hkcu.ldb"
365 paths.hku = "hku.ldb"
366 paths.hkpd = "hkpd.ldb"
367 paths.hkpt = "hkpt.ldb"
369 paths.sysvol = lp.get("path", "sysvol")
371 paths.netlogon = lp.get("path", "netlogon")
373 paths.smbconf = lp.configfile
378 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
379 serverrole=None, rootdn=None, domaindn=None, configdn=None,
380 schemadn=None, serverdn=None, sitename=None, sambadn=None):
381 """Guess configuration settings to use."""
384 hostname = socket.gethostname().split(".")[0].lower()
386 netbiosname = hostname.upper()
387 if not valid_netbios_name(netbiosname):
388 raise InvalidNetbiosName(netbiosname)
390 hostname = hostname.lower()
392 if dnsdomain is None:
393 dnsdomain = lp.get("realm")
395 if serverrole is None:
396 serverrole = lp.get("server role")
398 assert dnsdomain is not None
399 realm = dnsdomain.upper()
401 if lp.get("realm").upper() != realm:
402 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
403 (lp.get("realm"), lp.configfile, realm))
405 dnsdomain = dnsdomain.lower()
407 if serverrole == "domain controller":
409 domain = lp.get("workgroup")
411 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
412 if lp.get("workgroup").upper() != domain.upper():
413 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
414 lp.get("workgroup"), domain)
418 domaindn = "CN=" + netbiosname
420 assert domain is not None
421 domain = domain.upper()
422 if not valid_netbios_name(domain):
423 raise InvalidNetbiosName(domain)
425 if netbiosname.upper() == realm.upper():
426 raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
428 if hostname.upper() == realm.upper():
429 raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
431 if domain.upper() == realm.upper():
432 raise Exception("realm %s must not be equal to domain name %s", realm, domain)
438 configdn = "CN=Configuration," + rootdn
440 schemadn = "CN=Schema," + configdn
447 names = ProvisionNames()
448 names.rootdn = rootdn
449 names.domaindn = domaindn
450 names.configdn = configdn
451 names.schemadn = schemadn
452 names.sambadn = sambadn
453 names.ldapmanagerdn = "CN=Manager," + rootdn
454 names.dnsdomain = dnsdomain
455 names.domain = domain
457 names.netbiosname = netbiosname
458 names.hostname = hostname
459 names.sitename = sitename
460 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
465 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
467 """Create a new smb.conf file based on a couple of basic settings.
469 assert smbconf is not None
471 hostname = socket.gethostname().split(".")[0].lower()
473 if serverrole is None:
474 serverrole = "standalone"
476 assert serverrole in ("domain controller", "member server", "standalone")
477 if serverrole == "domain controller":
479 elif serverrole == "member server":
480 smbconfsuffix = "member"
481 elif serverrole == "standalone":
482 smbconfsuffix = "standalone"
484 assert domain is not None
485 assert realm is not None
487 default_lp = param.LoadParm()
488 #Load non-existant file
489 if os.path.exists(smbconf):
490 default_lp.load(smbconf)
492 if targetdir is not None:
493 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
494 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
496 default_lp.set("lock dir", os.path.abspath(targetdir))
501 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
502 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
504 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
506 "HOSTNAME": hostname,
509 "SERVERROLE": serverrole,
510 "NETLOGONPATH": netlogon,
511 "SYSVOLPATH": sysvol,
512 "PRIVATEDIR_LINE": privatedir_line,
513 "LOCKDIR_LINE": lockdir_line
517 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
518 users_gid, wheel_gid):
519 """setup reasonable name mappings for sam names to unix names.
521 :param samdb: SamDB object.
522 :param idmap: IDmap db object.
523 :param sid: The domain sid.
524 :param domaindn: The domain DN.
525 :param root_uid: uid of the UNIX root user.
526 :param nobody_uid: uid of the UNIX nobody user.
527 :param users_gid: gid of the UNIX users group.
528 :param wheel_gid: gid of the UNIX wheel group."""
530 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
531 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
533 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
534 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
536 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
538 serverrole, ldap_backend=None,
540 """Setup the partitions for the SAM database.
542 Alternatively, provision() may call this, and then populate the database.
544 :note: This will wipe the Sam Database!
546 :note: This function always removes the local SAM LDB file. The erase
547 parameter controls whether to erase the existing data, which
548 may not be stored locally but in LDAP.
550 assert session_info is not None
552 # We use options=["modules:"] to stop the modules loading - we
553 # just want to wipe and re-initialise the database, not start it up
556 samdb = Ldb(url=samdb_path, session_info=session_info,
557 credentials=credentials, lp=lp, options=["modules:"])
559 samdb.erase_except_schema_controlled()
561 os.unlink(samdb_path)
562 samdb = Ldb(url=samdb_path, session_info=session_info,
563 credentials=credentials, lp=lp, options=["modules:"])
565 samdb.erase_except_schema_controlled()
568 #Add modules to the list to activate them by default
569 #beware often order is important
571 # Some Known ordering constraints:
572 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
573 # - objectclass must be before password_hash, because password_hash checks
574 # that the objectclass is of type person (filled in by objectclass
575 # module when expanding the objectclass list)
576 # - partition must be last
577 # - each partition has its own module list then
578 modules_list = ["resolve_oids",
598 "extended_dn_out_ldb"]
599 modules_list2 = ["show_deleted",
602 domaindn_ldb = "users.ldb"
603 configdn_ldb = "configuration.ldb"
604 schemadn_ldb = "schema.ldb"
605 if ldap_backend is not None:
606 domaindn_ldb = ldap_backend.ldapi_uri
607 configdn_ldb = ldap_backend.ldapi_uri
608 schemadn_ldb = ldap_backend.ldapi_uri
610 if ldap_backend.ldap_backend_type == "fedora-ds":
611 backend_modules = ["nsuniqueid", "paged_searches"]
612 # We can handle linked attributes here, as we don't have directory-side subtree operations
613 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
614 elif ldap_backend.ldap_backend_type == "openldap":
615 backend_modules = ["entryuuid", "paged_searches"]
616 # OpenLDAP handles subtree renames, so we don't want to do any of these things
617 tdb_modules_list = ["extended_dn_out_dereference"]
619 elif serverrole == "domain controller":
620 tdb_modules_list.insert(0, "repl_meta_data")
623 backend_modules = ["objectguid"]
625 if tdb_modules_list is None:
626 tdb_modules_list_as_string = ""
628 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
630 samdb.transaction_start()
632 message("Setting up sam.ldb partitions and settings")
633 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
634 "SCHEMADN": names.schemadn,
635 "SCHEMADN_LDB": schemadn_ldb,
636 "SCHEMADN_MOD2": ",objectguid",
637 "CONFIGDN": names.configdn,
638 "CONFIGDN_LDB": configdn_ldb,
639 "DOMAINDN": names.domaindn,
640 "DOMAINDN_LDB": domaindn_ldb,
641 "SCHEMADN_MOD": "schema_fsmo,instancetype",
642 "CONFIGDN_MOD": "naming_fsmo,instancetype",
643 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
644 "MODULES_LIST": ",".join(modules_list),
645 "TDB_MODULES_LIST": tdb_modules_list_as_string,
646 "MODULES_LIST2": ",".join(modules_list2),
647 "BACKEND_MOD": ",".join(backend_modules),
650 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
652 message("Setting up sam.ldb rootDSE")
653 setup_samdb_rootdse(samdb, setup_path, names)
656 samdb.transaction_cancel()
659 samdb.transaction_commit()
663 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
664 netbiosname, domainsid, keytab_path, samdb_url,
665 dns_keytab_path, dnspass, machinepass):
666 """Add DC-specific bits to a secrets database.
668 :param secretsdb: Ldb Handle to the secrets database
669 :param setup_path: Setup path function
670 :param machinepass: Machine password
672 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
674 "DNSDOMAIN": dnsdomain,
675 "DNS_KEYTAB": dns_keytab_path,
676 "DNSPASS_B64": b64encode(dnspass),
679 setup_ldb(secretsdb, setup_path("secrets_self_join.ldif"), {
680 "MACHINEPASS_B64": b64encode(machinepass),
683 "DNSDOMAIN": dnsdomain,
684 "DOMAINSID": str(domainsid),
685 "SECRETS_KEYTAB": keytab_path,
686 "NETBIOSNAME": netbiosname,
687 "SALT_PRINCIPAL": "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper()),
688 "KEY_VERSION_NUMBER": "1"
692 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
693 """Setup the secrets database.
695 :param path: Path to the secrets database.
696 :param setup_path: Get the path to a setup file.
697 :param session_info: Session info.
698 :param credentials: Credentials
699 :param lp: Loadparm context
700 :return: LDB handle for the created secrets database
702 if os.path.exists(path):
704 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
707 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
708 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
710 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
712 if credentials is not None and credentials.authentication_requested():
713 if credentials.get_bind_dn() is not None:
714 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
715 "LDAPMANAGERDN": credentials.get_bind_dn(),
716 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
719 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
720 "LDAPADMINUSER": credentials.get_username(),
721 "LDAPADMINREALM": credentials.get_realm(),
722 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
727 def setup_registry(path, setup_path, session_info, lp):
728 """Setup the registry.
730 :param path: Path to the registry database
731 :param setup_path: Function that returns the path to a setup.
732 :param session_info: Session information
733 :param credentials: Credentials
734 :param lp: Loadparm context
736 reg = registry.Registry()
737 hive = registry.open_ldb(path, session_info=session_info,
739 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
740 provision_reg = setup_path("provision.reg")
741 assert os.path.exists(provision_reg)
742 reg.diff_apply(provision_reg)
745 def setup_idmapdb(path, setup_path, session_info, lp):
746 """Setup the idmap database.
748 :param path: path to the idmap database
749 :param setup_path: Function that returns a path to a setup file
750 :param session_info: Session information
751 :param credentials: Credentials
752 :param lp: Loadparm context
754 if os.path.exists(path):
757 idmap_ldb = IDmapDB(path, session_info=session_info,
761 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
765 def setup_samdb_rootdse(samdb, setup_path, names):
766 """Setup the SamDB rootdse.
768 :param samdb: Sam Database handle
769 :param setup_path: Obtain setup path
771 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
772 "SCHEMADN": names.schemadn,
773 "NETBIOSNAME": names.netbiosname,
774 "DNSDOMAIN": names.dnsdomain,
775 "REALM": names.realm,
776 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
777 "DOMAINDN": names.domaindn,
778 "ROOTDN": names.rootdn,
779 "CONFIGDN": names.configdn,
780 "SERVERDN": names.serverdn,
784 def setup_self_join(samdb, names,
785 machinepass, dnspass,
786 domainsid, invocationid, setup_path,
787 policyguid, policyguid_dc, domainControllerFunctionality):
788 """Join a host to its own domain."""
789 assert isinstance(invocationid, str)
790 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
791 "CONFIGDN": names.configdn,
792 "SCHEMADN": names.schemadn,
793 "DOMAINDN": names.domaindn,
794 "SERVERDN": names.serverdn,
795 "INVOCATIONID": invocationid,
796 "NETBIOSNAME": names.netbiosname,
797 "DEFAULTSITE": names.sitename,
798 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
799 "MACHINEPASS_B64": b64encode(machinepass),
800 "DNSPASS_B64": b64encode(dnspass),
801 "REALM": names.realm,
802 "DOMAIN": names.domain,
803 "DNSDOMAIN": names.dnsdomain,
804 "SAMBA_VERSION_STRING": version,
805 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
807 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
808 "POLICYGUID": policyguid,
809 "POLICYGUID_DC": policyguid_dc,
810 "DNSDOMAIN": names.dnsdomain,
811 "DOMAINSID": str(domainsid),
812 "DOMAINDN": names.domaindn})
814 # add the NTDSGUID based SPNs
815 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
816 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
817 expression="", scope=SCOPE_BASE)
818 assert isinstance(names.ntdsguid, str)
820 # Setup fSMORoleOwner entries to point at the newly created DC entry
821 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
822 "DOMAIN": names.domain,
823 "DNSDOMAIN": names.dnsdomain,
824 "DOMAINDN": names.domaindn,
825 "CONFIGDN": names.configdn,
826 "SCHEMADN": names.schemadn,
827 "DEFAULTSITE": names.sitename,
828 "SERVERDN": names.serverdn,
829 "NETBIOSNAME": names.netbiosname,
830 "NTDSGUID": names.ntdsguid
834 def setup_samdb(path, setup_path, session_info, credentials, lp,
836 domainsid, domainguid, policyguid, policyguid_dc,
837 fill, adminpass, krbtgtpass,
838 machinepass, invocationid, dnspass,
839 serverrole, schema=None, ldap_backend=None):
840 """Setup a complete SAM Database.
842 :note: This will wipe the main SAM database file!
845 domainFunctionality = DS_DOMAIN_FUNCTION_2000
846 forestFunctionality = DS_DOMAIN_FUNCTION_2000
847 domainControllerFunctionality = DS_DC_FUNCTION_2008_R2
849 # Also wipes the database
850 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
851 credentials=credentials, session_info=session_info,
853 ldap_backend=ldap_backend, serverrole=serverrole)
856 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
857 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
859 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
860 samdb = Ldb(session_info=session_info,
861 credentials=credentials, lp=lp)
863 message("Pre-loading the Samba 4 and AD schema")
865 # Load the schema from the one we computed earlier
866 samdb.set_schema_from_ldb(schema.ldb)
868 # And now we can connect to the DB - the schema won't be loaded from the DB
872 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
877 samdb.transaction_start()
879 message("Erasing data from partitions")
880 # Load the schema (again). This time it will force a reindex,
881 # and will therefore make the erase_partitions() below
882 # computationally sane
883 samdb.set_schema_from_ldb(schema.ldb)
884 samdb.erase_partitions()
886 # Set the domain functionality levels onto the database.
887 # Various module (the password_hash module in particular) need
888 # to know what level of AD we are emulating.
890 # These will be fixed into the database via the database
891 # modifictions below, but we need them set from the start.
892 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
893 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
894 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
896 samdb.set_domain_sid(str(domainsid))
897 if serverrole == "domain controller":
898 samdb.set_invocation_id(invocationid)
900 message("Adding DomainDN: %s" % names.domaindn)
901 if serverrole == "domain controller":
902 domain_oc = "domainDNS"
904 domain_oc = "samba4LocalDomain"
906 #impersonate domain admin
907 admin_session_info = admin_session(lp, str(domainsid))
908 samdb.set_session_info(admin_session_info)
910 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
911 "DOMAINDN": names.domaindn,
912 "DOMAIN_OC": domain_oc
915 message("Modifying DomainDN: " + names.domaindn + "")
916 if domainguid is not None:
917 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
921 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
922 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
923 "DOMAINSID": str(domainsid),
924 "SCHEMADN": names.schemadn,
925 "NETBIOSNAME": names.netbiosname,
926 "DEFAULTSITE": names.sitename,
927 "CONFIGDN": names.configdn,
928 "SERVERDN": names.serverdn,
929 "POLICYGUID": policyguid,
930 "DOMAINDN": names.domaindn,
931 "DOMAINGUID_MOD": domainguid_mod,
932 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
933 "SAMBA_VERSION_STRING": version
936 message("Adding configuration container")
937 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
938 "CONFIGDN": names.configdn,
940 message("Modifying configuration container")
941 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
942 "CONFIGDN": names.configdn,
943 "SCHEMADN": names.schemadn,
946 # The LDIF here was created when the Schema object was constructed
947 message("Setting up sam.ldb schema")
948 samdb.add_ldif(schema.schema_dn_add)
949 samdb.modify_ldif(schema.schema_dn_modify)
950 samdb.write_prefixes_from_schema()
951 samdb.add_ldif(schema.schema_data)
952 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
953 {"SCHEMADN": names.schemadn})
955 message("Setting up sam.ldb configuration data")
956 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
957 "CONFIGDN": names.configdn,
958 "NETBIOSNAME": names.netbiosname,
959 "DEFAULTSITE": names.sitename,
960 "DNSDOMAIN": names.dnsdomain,
961 "DOMAIN": names.domain,
962 "SCHEMADN": names.schemadn,
963 "DOMAINDN": names.domaindn,
964 "SERVERDN": names.serverdn,
965 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
968 message("Setting up display specifiers")
969 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
970 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
971 check_all_substituted(display_specifiers_ldif)
972 samdb.add_ldif(display_specifiers_ldif)
974 message("Adding users container")
975 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
976 "DOMAINDN": names.domaindn})
977 message("Modifying users container")
978 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
979 "DOMAINDN": names.domaindn})
980 message("Adding computers container")
981 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
982 "DOMAINDN": names.domaindn})
983 message("Modifying computers container")
984 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
985 "DOMAINDN": names.domaindn})
986 message("Setting up sam.ldb data")
987 setup_add_ldif(samdb, setup_path("provision.ldif"), {
988 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
989 "DOMAINDN": names.domaindn,
990 "NETBIOSNAME": names.netbiosname,
991 "DEFAULTSITE": names.sitename,
992 "CONFIGDN": names.configdn,
993 "SERVERDN": names.serverdn,
994 "POLICYGUID_DC": policyguid_dc
997 if fill == FILL_FULL:
998 message("Setting up sam.ldb users and groups")
999 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1000 "DOMAINDN": names.domaindn,
1001 "DOMAINSID": str(domainsid),
1002 "CONFIGDN": names.configdn,
1003 "ADMINPASS_B64": b64encode(adminpass),
1004 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1007 if serverrole == "domain controller":
1008 message("Setting up self join")
1009 setup_self_join(samdb, names=names, invocationid=invocationid,
1011 machinepass=machinepass,
1012 domainsid=domainsid, policyguid=policyguid,
1013 policyguid_dc=policyguid_dc,
1014 setup_path=setup_path,
1015 domainControllerFunctionality=domainControllerFunctionality)
1017 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1018 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1019 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1020 assert isinstance(names.ntdsguid, str)
1023 samdb.transaction_cancel()
1026 samdb.transaction_commit()
1031 FILL_NT4SYNC = "NT4SYNC"
1035 def provision(setup_dir, message, session_info,
1036 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1038 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1040 domain=None, hostname=None, hostip=None, hostip6=None,
1041 domainsid=None, adminpass=None, ldapadminpass=None,
1042 krbtgtpass=None, domainguid=None,
1043 policyguid=None, policyguid_dc=None, invocationid=None,
1045 dnspass=None, root=None, nobody=None, users=None,
1046 wheel=None, backup=None, aci=None, serverrole=None,
1047 ldap_backend_extra_port=None, ldap_backend_type=None,
1049 ol_mmr_urls=None, ol_olc=None,
1050 setup_ds_path=None, slapd_path=None, nosync=False,
1051 ldap_dryrun_mode=False):
1054 :note: caution, this wipes all existing data!
1057 def setup_path(file):
1058 return os.path.join(setup_dir, file)
1060 if domainsid is None:
1061 domainsid = security.random_sid()
1063 domainsid = security.dom_sid(domainsid)
1066 # create/adapt the group policy GUIDs
1067 if policyguid is None:
1068 policyguid = str(uuid.uuid4())
1069 policyguid = policyguid.upper()
1070 if policyguid_dc is None:
1071 policyguid_dc = str(uuid.uuid4())
1072 policyguid_dc = policyguid_dc.upper()
1074 if adminpass is None:
1075 adminpass = glue.generate_random_str(12)
1076 if krbtgtpass is None:
1077 krbtgtpass = glue.generate_random_str(12)
1078 if machinepass is None:
1079 machinepass = glue.generate_random_str(12)
1081 dnspass = glue.generate_random_str(12)
1082 if ldapadminpass is None:
1083 #Make a new, random password between Samba and it's LDAP server
1084 ldapadminpass=glue.generate_random_str(12)
1087 root_uid = findnss_uid([root or "root"])
1088 nobody_uid = findnss_uid([nobody or "nobody"])
1089 users_gid = findnss_gid([users or "users"])
1091 wheel_gid = findnss_gid(["wheel", "adm"])
1093 wheel_gid = findnss_gid([wheel])
1095 if targetdir is not None:
1096 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1097 os.makedirs(os.path.join(targetdir, "etc"))
1098 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1099 elif smbconf is None:
1100 smbconf = param.default_path()
1102 # only install a new smb.conf if there isn't one there already
1103 if not os.path.exists(smbconf):
1104 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1107 lp = param.LoadParm()
1110 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1111 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1112 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1115 paths = provision_paths_from_lp(lp, names.dnsdomain)
1119 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1120 except socket.gaierror, (socket.EAI_NODATA, msg):
1125 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1126 except socket.gaierror, (socket.EAI_NODATA, msg):
1129 if serverrole is None:
1130 serverrole = lp.get("server role")
1132 assert serverrole in ("domain controller", "member server", "standalone")
1133 if invocationid is None and serverrole == "domain controller":
1134 invocationid = str(uuid.uuid4())
1136 if not os.path.exists(paths.private_dir):
1137 os.mkdir(paths.private_dir)
1139 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1141 schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
1142 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1144 secrets_credentials = credentials
1145 provision_backend = None
1146 if ldap_backend_type:
1147 # We only support an LDAP backend over ldapi://
1149 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1150 lp=lp, credentials=credentials,
1152 message=message, hostname=hostname,
1153 root=root, schema=schema,
1154 ldap_backend_type=ldap_backend_type,
1155 ldapadminpass=ldapadminpass,
1156 ldap_backend_extra_port=ldap_backend_extra_port,
1157 ol_mmr_urls=ol_mmr_urls,
1158 slapd_path=slapd_path,
1159 setup_ds_path=setup_ds_path,
1160 ldap_dryrun_mode=ldap_dryrun_mode)
1162 # Now use the backend credentials to access the databases
1163 credentials = provision_backend.credentials
1164 secrets_credentials = provision_backend.adminCredentials
1165 ldapi_url = provision_backend.ldapi_uri
1167 # only install a new shares config db if there is none
1168 if not os.path.exists(paths.shareconf):
1169 message("Setting up share.ldb")
1170 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1171 credentials=credentials, lp=lp)
1172 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1175 message("Setting up secrets.ldb")
1176 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1177 session_info=session_info,
1178 credentials=secrets_credentials, lp=lp)
1180 message("Setting up the registry")
1181 setup_registry(paths.hklm, setup_path, session_info,
1184 message("Setting up idmap db")
1185 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1188 message("Setting up SAM db")
1189 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1190 credentials=credentials, lp=lp, names=names,
1192 domainsid=domainsid,
1193 schema=schema, domainguid=domainguid,
1194 policyguid=policyguid, policyguid_dc=policyguid_dc,
1196 adminpass=adminpass, krbtgtpass=krbtgtpass,
1197 invocationid=invocationid,
1198 machinepass=machinepass, dnspass=dnspass,
1199 serverrole=serverrole, ldap_backend=provision_backend)
1201 if serverrole == "domain controller":
1202 if paths.netlogon is None:
1203 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1204 message("Please either remove %s or see the template at %s" %
1205 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1206 assert(paths.netlogon is not None)
1208 if paths.sysvol is None:
1209 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1210 message("Please either remove %s or see the template at %s" %
1211 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1212 assert(paths.sysvol is not None)
1214 # Set up group policies (domain policy and domain controller policy)
1216 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1217 "{" + policyguid + "}")
1218 os.makedirs(policy_path, 0755)
1219 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1220 "[General]\r\nVersion=65543")
1221 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1222 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1224 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1225 "{" + policyguid_dc + "}")
1226 os.makedirs(policy_path_dc, 0755)
1227 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1228 "[General]\r\nVersion=2")
1229 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1230 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1232 if not os.path.isdir(paths.netlogon):
1233 os.makedirs(paths.netlogon, 0755)
1235 if samdb_fill == FILL_FULL:
1236 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1237 root_uid=root_uid, nobody_uid=nobody_uid,
1238 users_gid=users_gid, wheel_gid=wheel_gid)
1240 message("Setting up sam.ldb rootDSE marking as synchronized")
1241 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1243 # Only make a zone file on the first DC, it should be replicated with DNS replication
1244 if serverrole == "domain controller":
1245 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1246 credentials=credentials, lp=lp)
1247 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain,
1249 netbiosname=names.netbiosname,
1250 domainsid=domainsid,
1251 keytab_path=paths.keytab, samdb_url=paths.samdb,
1252 dns_keytab_path=paths.dns_keytab,
1253 dnspass=dnspass, machinepass=machinepass,
1254 dnsdomain=names.dnsdomain)
1256 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1257 assert isinstance(domainguid, str)
1259 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1260 domaindn=names.domaindn, hostip=hostip,
1261 hostip6=hostip6, hostname=names.hostname,
1262 dnspass=dnspass, realm=names.realm,
1263 domainguid=domainguid, ntdsguid=names.ntdsguid)
1265 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1266 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1268 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1269 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1270 keytab_name=paths.dns_keytab)
1271 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1272 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1274 create_krb5_conf(paths.krb5conf, setup_path,
1275 dnsdomain=names.dnsdomain, hostname=names.hostname,
1277 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1280 if provision_backend is not None:
1281 if ldap_backend_type == "fedora-ds":
1282 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1284 # delete default SASL mappings
1285 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1287 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1288 for i in range (0, len(res)):
1289 dn = str(res[i]["dn"])
1292 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1295 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1297 m.dn = ldb.Dn(1, names.domaindn)
1300 m.dn = ldb.Dn(1, names.configdn)
1303 m.dn = ldb.Dn(1, names.schemadn)
1306 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1307 if provision_backend.slapd.poll() is None:
1309 if hasattr(provision_backend.slapd, "terminate"):
1310 provision_backend.slapd.terminate()
1312 # Older python versions don't have .terminate()
1314 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1316 #and now wait for it to die
1317 provision_backend.slapd.communicate()
1319 # now display slapd_command_file.txt to show how slapd must be started next time
1320 message("Use later the following commandline to start slapd, then Samba:")
1321 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1322 message(slapd_command)
1323 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1325 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1326 "SLAPD_COMMAND" : slapd_command})
1329 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1332 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1334 message("Once the above files are installed, your Samba4 server will be ready to use")
1335 message("Server Role: %s" % serverrole)
1336 message("Hostname: %s" % names.hostname)
1337 message("NetBIOS Domain: %s" % names.domain)
1338 message("DNS Domain: %s" % names.dnsdomain)
1339 message("DOMAIN SID: %s" % str(domainsid))
1340 if samdb_fill == FILL_FULL:
1341 message("Admin password: %s" % adminpass)
1342 if provision_backend:
1343 if provision_backend.credentials.get_bind_dn() is not None:
1344 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1346 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1348 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1350 result = ProvisionResult()
1351 result.domaindn = domaindn
1352 result.paths = paths
1354 result.samdb = samdb
1359 def provision_become_dc(setup_dir=None,
1360 smbconf=None, targetdir=None, realm=None,
1361 rootdn=None, domaindn=None, schemadn=None,
1362 configdn=None, serverdn=None,
1363 domain=None, hostname=None, domainsid=None,
1364 adminpass=None, krbtgtpass=None, domainguid=None,
1365 policyguid=None, policyguid_dc=None, invocationid=None,
1367 dnspass=None, root=None, nobody=None, users=None,
1368 wheel=None, backup=None, serverrole=None,
1369 ldap_backend=None, ldap_backend_type=None,
1370 sitename=None, debuglevel=1):
1373 """print a message if quiet is not set."""
1376 glue.set_debug_level(debuglevel)
1378 return provision(setup_dir, message, system_session(), None,
1379 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1380 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1381 configdn=configdn, serverdn=serverdn, domain=domain,
1382 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1383 machinepass=machinepass, serverrole="domain controller",
1387 def setup_db_config(setup_path, dbdir):
1388 """Setup a Berkeley database.
1390 :param setup_path: Setup path function.
1391 :param dbdir: Database directory."""
1392 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1393 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1394 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1395 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1397 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1398 {"LDAPDBDIR": dbdir})
1400 class ProvisionBackend(object):
1401 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1402 names=None, message=None,
1403 hostname=None, root=None,
1404 schema=None, ldapadminpass=None,
1405 ldap_backend_type=None, ldap_backend_extra_port=None,
1407 setup_ds_path=None, slapd_path=None,
1408 nosync=False, ldap_dryrun_mode=False):
1409 """Provision an LDAP backend for samba4
1411 This works for OpenLDAP and Fedora DS
1414 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1416 if not os.path.isdir(paths.ldapdir):
1417 os.makedirs(paths.ldapdir, 0700)
1419 if ldap_backend_type == "existing":
1420 #Check to see that this 'existing' LDAP backend in fact exists
1421 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1422 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1423 expression="(objectClass=OpenLDAProotDSE)")
1425 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1426 # This caused them to be set into the long-term database later in the script.
1427 self.credentials = credentials
1428 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1431 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1432 # if another instance of slapd is already running
1434 ldapi_db = Ldb(self.ldapi_uri)
1435 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1436 expression="(objectClass=OpenLDAProotDSE)");
1438 f = open(paths.slapdpid, "r")
1441 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1445 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1450 # Try to print helpful messages when the user has not specified the path to slapd
1451 if slapd_path is None:
1452 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1453 if not os.path.exists(slapd_path):
1454 message (slapd_path)
1455 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1457 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1459 os.unlink(schemadb_path)
1464 # Put the LDIF of the schema into a database so we can search on
1465 # it to generate schema-dependent configurations in Fedora DS and
1467 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1468 schema.ldb.connect(schemadb_path)
1469 schema.ldb.transaction_start()
1471 # These bits of LDIF are supplied when the Schema object is created
1472 schema.ldb.add_ldif(schema.schema_dn_add)
1473 schema.ldb.modify_ldif(schema.schema_dn_modify)
1474 schema.ldb.add_ldif(schema.schema_data)
1475 schema.ldb.transaction_commit()
1477 self.credentials = Credentials()
1478 self.credentials.guess(lp)
1479 #Kerberos to an ldapi:// backend makes no sense
1480 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1482 self.adminCredentials = Credentials()
1483 self.adminCredentials.guess(lp)
1484 #Kerberos to an ldapi:// backend makes no sense
1485 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1487 self.ldap_backend_type = ldap_backend_type
1489 if ldap_backend_type == "fedora-ds":
1490 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1491 names=names, message=message,
1493 ldapadminpass=ldapadminpass, root=root,
1495 ldap_backend_extra_port=ldap_backend_extra_port,
1496 setup_ds_path=setup_ds_path,
1497 slapd_path=slapd_path,
1499 ldap_dryrun_mode=ldap_dryrun_mode)
1501 elif ldap_backend_type == "openldap":
1502 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1503 names=names, message=message,
1505 ldapadminpass=ldapadminpass, root=root,
1507 ldap_backend_extra_port=ldap_backend_extra_port,
1508 ol_mmr_urls=ol_mmr_urls,
1509 slapd_path=slapd_path,
1511 ldap_dryrun_mode=ldap_dryrun_mode)
1513 raise ProvisioningError("Unknown LDAP backend type selected")
1515 self.credentials.set_password(ldapadminpass)
1516 self.adminCredentials.set_username("samba-admin")
1517 self.adminCredentials.set_password(ldapadminpass)
1519 # Now start the slapd, so we can provision onto it. We keep the
1520 # subprocess context around, to kill this off at the successful
1522 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1524 while self.slapd.poll() is None:
1525 # Wait until the socket appears
1527 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1528 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1529 expression="(objectClass=OpenLDAProotDSE)")
1530 # If we have got here, then we must have a valid connection to the LDAP server!
1536 raise ProvisioningError("slapd died before we could make a connection to it")
1539 def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1541 hostname=None, ldapadminpass=None, root=None,
1543 ldap_backend_extra_port=None,
1545 slapd_path=None, nosync=False,
1546 ldap_dryrun_mode=False):
1548 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1551 nosync_config = "dbnosync"
1553 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1554 refint_attributes = ""
1555 memberof_config = "# Generated from Samba4 schema\n"
1556 for att in lnkattr.keys():
1557 if lnkattr[att] is not None:
1558 refint_attributes = refint_attributes + " " + att
1560 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1561 { "MEMBER_ATTR" : att ,
1562 "MEMBEROF_ATTR" : lnkattr[att] })
1564 refint_config = read_and_sub_file(setup_path("refint.conf"),
1565 { "LINK_ATTRS" : refint_attributes})
1567 attrs = ["linkID", "lDAPDisplayName"]
1568 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1570 for i in range (0, len(res)):
1571 index_attr = res[i]["lDAPDisplayName"][0]
1572 if index_attr == "objectGUID":
1573 index_attr = "entryUUID"
1575 index_config += "index " + index_attr + " eq\n"
1577 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1579 mmr_replicator_acl = ""
1580 mmr_serverids_config = ""
1581 mmr_syncrepl_schema_config = ""
1582 mmr_syncrepl_config_config = ""
1583 mmr_syncrepl_user_config = ""
1586 if ol_mmr_urls is not None:
1587 # For now, make these equal
1588 mmr_pass = ldapadminpass
1590 url_list=filter(None,ol_mmr_urls.split(' '))
1591 if (len(url_list) == 1):
1592 url_list=filter(None,ol_mmr_urls.split(','))
1595 mmr_on_config = "MirrorMode On"
1596 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1598 for url in url_list:
1600 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1601 { "SERVERID" : str(serverid),
1602 "LDAPSERVER" : url })
1605 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1607 "MMRDN": names.schemadn,
1609 "MMR_PASSWORD": mmr_pass})
1612 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1614 "MMRDN": names.configdn,
1616 "MMR_PASSWORD": mmr_pass})
1619 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1621 "MMRDN": names.domaindn,
1623 "MMR_PASSWORD": mmr_pass })
1624 # OpenLDAP cn=config initialisation
1625 olc_syncrepl_config = ""
1627 # if mmr = yes, generate cn=config-replication directives
1628 # and olc_seed.lif for the other mmr-servers
1629 if ol_mmr_urls is not None:
1631 olc_serverids_config = ""
1632 olc_syncrepl_seed_config = ""
1633 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1635 for url in url_list:
1637 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1638 { "SERVERID" : str(serverid),
1639 "LDAPSERVER" : url })
1642 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1645 "MMR_PASSWORD": mmr_pass})
1647 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1649 "LDAPSERVER" : url})
1651 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1652 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1653 "OLC_PW": ldapadminpass,
1654 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1657 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1658 {"DNSDOMAIN": names.dnsdomain,
1659 "LDAPDIR": paths.ldapdir,
1660 "DOMAINDN": names.domaindn,
1661 "CONFIGDN": names.configdn,
1662 "SCHEMADN": names.schemadn,
1663 "MEMBEROF_CONFIG": memberof_config,
1664 "MIRRORMODE": mmr_on_config,
1665 "REPLICATOR_ACL": mmr_replicator_acl,
1666 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1667 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1668 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1669 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1670 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1671 "OLC_MMR_CONFIG": olc_mmr_config,
1672 "REFINT_CONFIG": refint_config,
1673 "INDEX_CONFIG": index_config,
1674 "NOSYNC": nosync_config})
1676 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1677 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1678 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1680 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1681 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1683 setup_file(setup_path("cn=samba.ldif"),
1684 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1685 { "UUID": str(uuid.uuid4()),
1686 "LDAPTIME": timestring(int(time.time()))} )
1687 setup_file(setup_path("cn=samba-admin.ldif"),
1688 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1689 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1690 "UUID": str(uuid.uuid4()),
1691 "LDAPTIME": timestring(int(time.time()))} )
1693 if ol_mmr_urls is not None:
1694 setup_file(setup_path("cn=replicator.ldif"),
1695 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1696 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1697 "UUID": str(uuid.uuid4()),
1698 "LDAPTIME": timestring(int(time.time()))} )
1701 mapping = "schema-map-openldap-2.3"
1702 backend_schema = "backend-schema.schema"
1704 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1705 assert backend_schema_data is not None
1706 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1708 # now we generate the needed strings to start slapd automatically,
1709 # first ldapi_uri...
1710 if ldap_backend_extra_port is not None:
1711 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1712 # specified there as part of it's clue as to it's own name,
1713 # and not to replicate to itself
1714 if ol_mmr_urls is None:
1715 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1717 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1719 server_port_string = ""
1721 # Prepare the 'result' information - the commands to return in particular
1722 result.slapd_provision_command = [slapd_path]
1724 result.slapd_provision_command.append("-F" + paths.olcdir)
1726 result.slapd_provision_command.append("-h")
1728 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1729 result.slapd_command = list(result.slapd_provision_command)
1731 result.slapd_provision_command.append(result.ldapi_uri)
1732 result.slapd_provision_command.append("-d0")
1734 uris = result.ldapi_uri
1735 if server_port_string is not "":
1736 uris = uris + " " + server_port_string
1738 result.slapd_command.append(uris)
1740 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1741 result.credentials.set_username("samba-admin")
1743 # If we were just looking for crashes up to this point, it's a
1744 # good time to exit before we realise we don't have OpenLDAP on
1746 if ldap_dryrun_mode:
1749 # Finally, convert the configuration into cn=config style!
1750 if not os.path.isdir(paths.olcdir):
1751 os.makedirs(paths.olcdir, 0770)
1753 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1755 # We can't do this, as OpenLDAP is strange. It gives an error
1756 # output to the above, but does the conversion sucessfully...
1759 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1761 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1762 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1764 # Don't confuse the admin by leaving the slapd.conf around
1765 os.remove(paths.slapdconf)
1768 def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1770 hostname=None, ldapadminpass=None, root=None,
1772 ldap_backend_extra_port=None,
1776 ldap_dryrun_mode=False):
1778 if ldap_backend_extra_port is not None:
1779 serverport = "ServerPort=%d" % ldap_backend_extra_port
1783 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1785 "HOSTNAME": hostname,
1786 "DNSDOMAIN": names.dnsdomain,
1787 "LDAPDIR": paths.ldapdir,
1788 "DOMAINDN": names.domaindn,
1789 "LDAPMANAGERDN": names.ldapmanagerdn,
1790 "LDAPMANAGERPASS": ldapadminpass,
1791 "SERVERPORT": serverport})
1793 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1794 {"CONFIGDN": names.configdn,
1795 "SCHEMADN": names.schemadn,
1796 "SAMBADN": names.sambadn,
1799 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1800 {"SAMBADN": names.sambadn,
1803 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1804 {"SAMBADN": names.sambadn,
1805 "LDAPADMINPASS": ldapadminpass
1808 mapping = "schema-map-fedora-ds-1.0"
1809 backend_schema = "99_ad.ldif"
1811 # Build a schema file in Fedora DS format
1812 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1813 assert backend_schema_data is not None
1814 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1816 result.credentials.set_bind_dn(names.ldapmanagerdn)
1818 # Destory the target directory, or else setup-ds.pl will complain
1819 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1820 shutil.rmtree(fedora_ds_dir, True)
1822 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1823 #In the 'provision' command line, stay in the foreground so we can easily kill it
1824 result.slapd_provision_command.append("-d0")
1826 #the command for the final run is the normal script
1827 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1829 # If we were just looking for crashes up to this point, it's a
1830 # good time to exit before we realise we don't have Fedora DS on
1831 if ldap_dryrun_mode:
1834 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1835 if setup_ds_path is None:
1836 raise ProvisioningError("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
1837 if not os.path.exists(setup_ds_path):
1838 message (setup_ds_path)
1839 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1841 # Run the Fedora DS setup utility
1842 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1844 raise ProvisioningError("setup-ds failed")
1847 retcode = subprocess.call([
1848 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
1849 close_fds=True, shell=False)
1851 raise("ldib2db failed")
1853 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1854 """Create a PHP LDAP admin configuration file.
1856 :param path: Path to write the configuration to.
1857 :param setup_path: Function to generate setup paths.
1859 setup_file(setup_path("phpldapadmin-config.php"), path,
1860 {"S4_LDAPI_URI": ldapi_uri})
1863 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1864 hostip, hostip6, hostname, dnspass, realm, domainguid,
1866 """Write out a DNS zone file, from the info in the current database.
1868 :param path: Path of the new zone file.
1869 :param setup_path: Setup path function.
1870 :param dnsdomain: DNS Domain name
1871 :param domaindn: DN of the Domain
1872 :param hostip: Local IPv4 IP
1873 :param hostip6: Local IPv6 IP
1874 :param hostname: Local hostname
1875 :param dnspass: Password for DNS
1876 :param realm: Realm name
1877 :param domainguid: GUID of the domain.
1878 :param ntdsguid: GUID of the hosts nTDSDSA record.
1880 assert isinstance(domainguid, str)
1882 if hostip6 is not None:
1883 hostip6_base_line = " IN AAAA " + hostip6
1884 hostip6_host_line = hostname + " IN AAAA " + hostip6
1886 hostip6_base_line = ""
1887 hostip6_host_line = ""
1889 if hostip is not None:
1890 hostip_base_line = " IN A " + hostip
1891 hostip_host_line = hostname + " IN A " + hostip
1893 hostip_base_line = ""
1894 hostip_host_line = ""
1896 setup_file(setup_path("provision.zone"), path, {
1897 "DNSPASS_B64": b64encode(dnspass),
1898 "HOSTNAME": hostname,
1899 "DNSDOMAIN": dnsdomain,
1901 "HOSTIP_BASE_LINE": hostip_base_line,
1902 "HOSTIP_HOST_LINE": hostip_host_line,
1903 "DOMAINGUID": domainguid,
1904 "DATESTRING": time.strftime("%Y%m%d%H"),
1905 "DEFAULTSITE": DEFAULTSITE,
1906 "NTDSGUID": ntdsguid,
1907 "HOSTIP6_BASE_LINE": hostip6_base_line,
1908 "HOSTIP6_HOST_LINE": hostip6_host_line,
1912 def create_named_conf(path, setup_path, realm, dnsdomain,
1914 """Write out a file containing zone statements suitable for inclusion in a
1915 named.conf file (including GSS-TSIG configuration).
1917 :param path: Path of the new named.conf file.
1918 :param setup_path: Setup path function.
1919 :param realm: Realm name
1920 :param dnsdomain: DNS Domain name
1921 :param private_dir: Path to private directory
1922 :param keytab_name: File name of DNS keytab file
1925 setup_file(setup_path("named.conf"), path, {
1926 "DNSDOMAIN": dnsdomain,
1928 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1929 "PRIVATE_DIR": private_dir
1932 def create_named_txt(path, setup_path, realm, dnsdomain,
1933 private_dir, keytab_name):
1934 """Write out a file containing zone statements suitable for inclusion in a
1935 named.conf file (including GSS-TSIG configuration).
1937 :param path: Path of the new named.conf file.
1938 :param setup_path: Setup path function.
1939 :param realm: Realm name
1940 :param dnsdomain: DNS Domain name
1941 :param private_dir: Path to private directory
1942 :param keytab_name: File name of DNS keytab file
1945 setup_file(setup_path("named.txt"), path, {
1946 "DNSDOMAIN": dnsdomain,
1948 "DNS_KEYTAB": keytab_name,
1949 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1950 "PRIVATE_DIR": private_dir
1953 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1954 """Write out a file containing zone statements suitable for inclusion in a
1955 named.conf file (including GSS-TSIG configuration).
1957 :param path: Path of the new named.conf file.
1958 :param setup_path: Setup path function.
1959 :param dnsdomain: DNS Domain name
1960 :param hostname: Local hostname
1961 :param realm: Realm name
1964 setup_file(setup_path("krb5.conf"), path, {
1965 "DNSDOMAIN": dnsdomain,
1966 "HOSTNAME": hostname,