summaryrefslogtreecommitdiffstats
path: root/ipaplatform/redhat
diff options
context:
space:
mode:
authorJan Cholasta <jcholast@redhat.com>2014-10-03 11:14:56 +0200
committerMartin Kosek <mkosek@redhat.com>2014-10-09 15:37:24 +0200
commit308d2dd406d5ec935b209c52803ce7de4136fe0b (patch)
tree3d8b88422e1351de453f2f2a6b255316d302248b /ipaplatform/redhat
parent57c510dcc7a08d908fd55856a735b8dca6684571 (diff)
downloadfreeipa-308d2dd406d5ec935b209c52803ce7de4136fe0b.tar.gz
freeipa-308d2dd406d5ec935b209c52803ce7de4136fe0b.tar.xz
freeipa-308d2dd406d5ec935b209c52803ce7de4136fe0b.zip
Split off generic Red Hat-like platform code from Fedora platform code
https://fedorahosted.org/freeipa/ticket/4562 Reviewed-By: Martin Kosek <mkosek@redhat.com>
Diffstat (limited to 'ipaplatform/redhat')
-rw-r--r--ipaplatform/redhat/__init__.py22
-rw-r--r--ipaplatform/redhat/authconfig.py86
-rw-r--r--ipaplatform/redhat/paths.py33
-rw-r--r--ipaplatform/redhat/services.py237
-rw-r--r--ipaplatform/redhat/tasks.py394
5 files changed, 772 insertions, 0 deletions
diff --git a/ipaplatform/redhat/__init__.py b/ipaplatform/redhat/__init__.py
new file mode 100644
index 000000000..b6925a7a7
--- /dev/null
+++ b/ipaplatform/redhat/__init__.py
@@ -0,0 +1,22 @@
+# Authors:
+# Tomas Babej <tbabej@redhat.com>
+#
+# Copyright (C) 2014 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+'''
+This module contains Red Hat OS family specific platform files.
+'''
diff --git a/ipaplatform/redhat/authconfig.py b/ipaplatform/redhat/authconfig.py
new file mode 100644
index 000000000..901eb5163
--- /dev/null
+++ b/ipaplatform/redhat/authconfig.py
@@ -0,0 +1,86 @@
+# Authors: Simo Sorce <ssorce@redhat.com>
+# Alexander Bokovoy <abokovoy@redhat.com>
+# Tomas Babej <tbabej@redhat.com>
+#
+# Copyright (C) 2007-2014 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from ipapython import ipautil
+
+
+class RedHatAuthConfig(object):
+ """
+ AuthConfig class implements system-independent interface to configure
+ system authentication resources. In Red Hat systems this is done with
+ authconfig(8) utility.
+
+ AuthConfig class is nothing more than a tool to gather configuration
+ options and execute their processing. These options then converted by
+ an actual implementation to series of a system calls to appropriate
+ utilities performing real configuration.
+
+ If you need to re-use existing AuthConfig instance for multiple runs,
+ make sure to call 'AuthConfig.reset()' between the runs.
+ """
+
+ def __init__(self):
+ self.parameters = {}
+
+ def enable(self, option):
+ self.parameters[option] = True
+ return self
+
+ def disable(self, option):
+ self.parameters[option] = False
+ return self
+
+ def add_option(self, option):
+ self.parameters[option] = None
+ return self
+
+ def add_parameter(self, option, value):
+ self.parameters[option] = [value]
+ return self
+
+ def reset(self):
+ self.parameters = {}
+ return self
+
+ def build_args(self):
+ args = []
+
+ for (option, value) in self.parameters.items():
+ if type(value) is bool:
+ if value:
+ args.append("--enable%s" % (option))
+ else:
+ args.append("--disable%s" % (option))
+ elif type(value) in (tuple, list):
+ args.append("--%s" % (option))
+ args.append("%s" % (value[0]))
+ elif value is None:
+ args.append("--%s" % (option))
+ else:
+ args.append("--%s%s" % (option, value))
+
+ return args
+
+ def execute(self, update=True):
+ if update:
+ self.add_option("update")
+
+ args = self.build_args()
+ ipautil.run(["/usr/sbin/authconfig"] + args)
diff --git a/ipaplatform/redhat/paths.py b/ipaplatform/redhat/paths.py
new file mode 100644
index 000000000..6d7e76dc5
--- /dev/null
+++ b/ipaplatform/redhat/paths.py
@@ -0,0 +1,33 @@
+# Authors:
+# Tomas Babej <tbabej@redhat.com>
+#
+# Copyright (C) 2014 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+'''
+This Red Hat OS family base platform module exports default filesystem paths as
+common in Red Hat OS family-based systems.
+'''
+
+# Fallback to default path definitions
+from ipaplatform.base.paths import BasePathNamespace
+
+
+class RedHatPathNamespace(BasePathNamespace):
+ pass
+
+
+paths = RedHatPathNamespace()
diff --git a/ipaplatform/redhat/services.py b/ipaplatform/redhat/services.py
new file mode 100644
index 000000000..76e123ebe
--- /dev/null
+++ b/ipaplatform/redhat/services.py
@@ -0,0 +1,237 @@
+# Author: Alexander Bokovoy <abokovoy@redhat.com>
+# Tomas Babej <tbabej@redhat.com>
+#
+# Copyright (C) 2011-2014 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+"""
+Contains Red Hat OS family-specific service class implementations.
+"""
+
+import os
+import time
+
+from ipaplatform.tasks import tasks
+from ipaplatform.base import services as base_services
+
+from ipapython import ipautil, dogtag
+from ipapython.ipa_log_manager import root_logger
+from ipalib import api
+from ipaplatform.paths import paths
+
+# Mappings from service names as FreeIPA code references to these services
+# to their actual systemd service names
+
+# For beginning just remap names to add .service
+# As more services will migrate to systemd, unit names will deviate and
+# mapping will be kept in this dictionary
+redhat_system_units = dict((x, "%s.service" % x)
+ for x in base_services.wellknownservices)
+
+redhat_system_units['rpcgssd'] = 'nfs-secure.service'
+redhat_system_units['rpcidmapd'] = 'nfs-idmap.service'
+
+# Rewrite dirsrv and pki-tomcatd services as they support instances via separate
+# service generator. To make this working, one needs to have both foo@.servic
+# and foo.target -- the latter is used when request should be coming for
+# all instances (like stop). systemd, unfortunately, does not allow one
+# to request action for all service instances at once if only foo@.service
+# unit is available. To add more, if any of those services need to be
+# started/stopped automagically, one needs to manually create symlinks in
+# /etc/systemd/system/foo.target.wants/ (look into systemd.py's enable()
+# code).
+
+redhat_system_units['dirsrv'] = 'dirsrv@.service'
+# Our directory server instance for PKI is dirsrv@PKI-IPA.service
+redhat_system_units['pkids'] = 'dirsrv@PKI-IPA.service'
+# Old style PKI instance
+redhat_system_units['pki-cad'] = 'pki-cad@pki-ca.service'
+redhat_system_units['pki_cad'] = redhat_system_units['pki-cad']
+# Our PKI instance is pki-tomcatd@pki-tomcat.service
+redhat_system_units['pki-tomcatd'] = 'pki-tomcatd@pki-tomcat.service'
+redhat_system_units['pki_tomcatd'] = redhat_system_units['pki-tomcatd']
+redhat_system_units['ipa-otpd'] = 'ipa-otpd.socket'
+
+
+# Service classes that implement Red Hat OS family-specific behaviour
+
+class RedHatService(base_services.SystemdService):
+ system_units = redhat_system_units
+
+ def __init__(self, service_name):
+ systemd_name = service_name
+ if service_name in self.system_units:
+ systemd_name = self.system_units[service_name]
+ else:
+ if '.' not in service_name:
+ # if service_name does not have a dot, it is not foo.service
+ # and not a foo.target. Thus, not correct service name for
+ # systemd, default to foo.service style then
+ systemd_name = "%s.service" % (service_name)
+ super(RedHatService, self).__init__(service_name, systemd_name)
+
+
+class RedHatDirectoryService(RedHatService):
+
+ def tune_nofile_platform(self, num=8192, fstore=None):
+ """
+ Increase the number of files descriptors available to directory server
+ from the default 1024 to 8192. This will allow to support a greater
+ number of clients out of the box.
+
+ This is a part of the implementation that is systemd-specific.
+
+ Returns False if the setting of the nofile limit needs to be skipped.
+ """
+
+ if os.path.exists(paths.SYSCONFIG_DIRSRV_SYSTEMD):
+ # We need to enable LimitNOFILE=8192 in the dirsrv@.service
+ # Since 389-ds-base-1.2.10-0.8.a7 the configuration of the
+ # service parameters is performed via
+ # /etc/sysconfig/dirsrv.systemd file which is imported by systemd
+ # into dirsrv@.service unit
+
+ replacevars = {'LimitNOFILE': str(num)}
+ ipautil.inifile_replace_variables(paths.SYSCONFIG_DIRSRV_SYSTEMD,
+ 'service',
+ replacevars=replacevars)
+ tasks.restore_context(paths.SYSCONFIG_DIRSRV_SYSTEMD)
+ ipautil.run(["/bin/systemctl", "--system", "daemon-reload"],
+ raiseonerr=False)
+
+ return True
+
+ def restart(self, instance_name="", capture_output=True, wait=True):
+ # We need to explicitly enable instances to install proper symlinks as
+ # dirsrv.target.wants/ dependencies. Standard systemd service class does it
+ # on enable() method call. Unfortunately, ipa-server-install does not do
+ # explicit dirsrv.enable() because the service startup is handled by ipactl.
+ #
+ # If we wouldn't do this, our instances will not be started as systemd would
+ # not have any clue about instances (PKI-IPA and the domain we serve)
+ # at all. Thus, hook into dirsrv.restart().
+
+ if instance_name:
+ elements = self.systemd_name.split("@")
+
+ srv_etc = os.path.join(paths.ETC_SYSTEMD_SYSTEM_DIR,
+ self.systemd_name)
+ srv_tgt = os.path.join(paths.ETC_SYSTEMD_SYSTEM_DIR,
+ self.SYSTEMD_SRV_TARGET % (elements[0]))
+ srv_lnk = os.path.join(srv_tgt,
+ self.service_instance(instance_name))
+
+ if not os.path.exists(srv_etc):
+ self.enable(instance_name)
+ elif not os.path.samefile(srv_etc, srv_lnk):
+ os.unlink(srv_lnk)
+ os.symlink(srv_etc, srv_lnk)
+
+ super(RedHatDirectoryService, self).restart(instance_name,
+ capture_output=capture_output, wait=wait)
+
+
+class RedHatIPAService(RedHatService):
+ # Enforce restart of IPA services when we do enable it
+ # This gets around the fact that after ipa-server-install systemd thinks
+ # ipa.service is not yet started but all services were actually started
+ # already.
+ def enable(self, instance_name=""):
+ super(RedHatIPAService, self).enable(instance_name)
+ self.restart(instance_name)
+
+
+class RedHatSSHService(RedHatService):
+ def get_config_dir(self, instance_name=""):
+ return '/etc/ssh'
+
+
+class RedHatCAService(RedHatService):
+ def wait_until_running(self):
+ # We must not wait for the httpd proxy if httpd is not set up yet.
+ # Unfortunately, knownservices.httpd.is_installed() can return
+ # false positives, so check for existence of our configuration file.
+ # TODO: Use a cleaner solution
+ use_proxy = True
+ if not (os.path.exists('/etc/httpd/conf.d/ipa.conf') and
+ os.path.exists(paths.HTTPD_IPA_PKI_PROXY_CONF)):
+ root_logger.debug(
+ 'The httpd proxy is not installed, wait on local port')
+ use_proxy = False
+ root_logger.debug('Waiting until the CA is running')
+ timeout = float(api.env.startup_timeout)
+ op_timeout = time.time() + timeout
+ while time.time() < op_timeout:
+ try:
+ status = dogtag.ca_status(use_proxy=use_proxy)
+ except Exception:
+ status = 'check interrupted'
+ root_logger.debug('The CA status is: %s' % status)
+ if status == 'running':
+ break
+ root_logger.debug('Waiting for CA to start...')
+ time.sleep(1)
+ else:
+ raise RuntimeError('CA did not start in %ss' % timeout)
+
+ def start(self, instance_name="", capture_output=True, wait=True):
+ super(RedHatCAService, self).start(
+ instance_name, capture_output=capture_output, wait=wait)
+ if wait:
+ self.wait_until_running()
+
+ def restart(self, instance_name="", capture_output=True, wait=True):
+ super(RedHatCAService, self).restart(
+ instance_name, capture_output=capture_output, wait=wait)
+ if wait:
+ self.wait_until_running()
+
+
+# Function that constructs proper Red Hat OS family-specific server classes for
+# services of specified name
+
+def redhat_service_class_factory(name):
+ if name == 'dirsrv':
+ return RedHatDirectoryService(name)
+ if name == 'ipa':
+ return RedHatIPAService(name)
+ if name == 'sshd':
+ return RedHatSSHService(name)
+ if name in ('pki-cad', 'pki_cad', 'pki-tomcatd', 'pki_tomcatd'):
+ return RedHatCAService(name)
+ return RedHatService(name)
+
+
+# Magicdict containing RedHatService instances.
+
+class RedHatServices(base_services.KnownServices):
+ def service_class_factory(self, name):
+ return redhat_service_class_factory(name)
+
+ def __init__(self):
+ services = dict()
+ for s in base_services.wellknownservices:
+ services[s] = self.service_class_factory(s)
+ # Call base class constructor. This will lock services to read-only
+ super(RedHatServices, self).__init__(services)
+
+
+# Objects below are expected to be exported by platform module
+
+from ipaplatform.base.services import timedate_services
+service = redhat_service_class_factory
+knownservices = RedHatServices()
diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py
new file mode 100644
index 000000000..30033b274
--- /dev/null
+++ b/ipaplatform/redhat/tasks.py
@@ -0,0 +1,394 @@
+# Authors: Simo Sorce <ssorce@redhat.com>
+# Alexander Bokovoy <abokovoy@redhat.com>
+# Martin Kosek <mkosek@redhat.com>
+# Tomas Babej <tbabej@redhat.com>
+#
+# Copyright (C) 2007-2014 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+'''
+This module contains default Red Hat OS family-specific implementations of
+system tasks.
+'''
+
+import os
+import stat
+import socket
+import sys
+import urllib
+import base64
+
+from subprocess import CalledProcessError
+from nss.error import NSPRError
+from pyasn1.error import PyAsn1Error
+
+from ipapython.ipa_log_manager import root_logger, log_mgr
+from ipapython import ipautil
+import ipapython.errors
+
+from ipalib import x509 # FIXME: do not import from ipalib
+
+from ipaplatform.paths import paths
+from ipaplatform.redhat.authconfig import RedHatAuthConfig
+from ipaplatform.base.tasks import BaseTaskNamespace
+
+
+log = log_mgr.get_logger(__name__)
+
+
+def selinux_enabled():
+ """
+ Check if SELinux is enabled.
+ """
+ if os.path.exists(paths.SELINUXENABLED):
+ try:
+ ipautil.run([paths.SELINUXENABLED])
+ return True
+ except ipautil.CalledProcessError:
+ # selinuxenabled returns 1 if not enabled
+ return False
+ else:
+ # No selinuxenabled, no SELinux
+ return False
+
+
+class RedHatTaskNamespace(BaseTaskNamespace):
+
+ def restore_context(self, filepath, restorecon=paths.SBIN_RESTORECON):
+ """
+ restore security context on the file path
+ SELinux equivalent is /path/to/restorecon <filepath>
+ restorecon's return values are not reliable so we have to
+ ignore them (BZ #739604).
+
+ ipautil.run() will do the logging.
+ """
+
+ if not selinux_enabled():
+ return
+
+ if (os.path.exists(restorecon)):
+ ipautil.run([restorecon, filepath], raiseonerr=False)
+
+ def check_selinux_status(self, restorecon=paths.RESTORECON):
+ """
+ We don't have a specific package requirement for policycoreutils
+ which provides restorecon. This is because we don't require
+ SELinux on client installs. However if SELinux is enabled then
+ this package is required.
+
+ This function returns nothing but may raise a Runtime exception
+ if SELinux is enabled but restorecon is not available.
+ """
+ if not selinux_enabled():
+ return
+
+ if not os.path.exists(restorecon):
+ raise RuntimeError('SELinux is enabled but %s does not exist.\n'
+ 'Install the policycoreutils package and start '
+ 'the installation again.' % restorecon)
+
+ def restore_pre_ipa_client_configuration(self, fstore, statestore,
+ was_sssd_installed,
+ was_sssd_configured):
+
+ auth_config = RedHatAuthConfig()
+ if statestore.has_state('authconfig'):
+ # disable only those configurations that we enabled during install
+ for conf in ('ldap', 'krb5', 'sssd', 'sssdauth', 'mkhomedir'):
+ cnf = statestore.restore_state('authconfig', conf)
+ # Do not disable sssd, as this can cause issues with its later
+ # uses. Remove it from statestore however, so that it becomes
+ # empty at the end of uninstall process.
+ if cnf and conf != 'sssd':
+ auth_config.disable(conf)
+ else:
+ # There was no authconfig status store
+ # It means the code was upgraded after original install
+ # Fall back to old logic
+ auth_config.disable("ldap")
+ auth_config.disable("krb5")
+ if not(was_sssd_installed and was_sssd_configured):
+ # Only disable sssdauth. Disabling sssd would cause issues
+ # with its later uses.
+ auth_config.disable("sssdauth")
+ auth_config.disable("mkhomedir")
+
+ auth_config.execute()
+
+ def set_nisdomain(self, nisdomain):
+ # Let authconfig setup the permanent configuration
+ auth_config = RedHatAuthConfig()
+ auth_config.add_parameter("nisdomain", nisdomain)
+ auth_config.execute()
+
+ def modify_nsswitch_pam_stack(self, sssd, mkhomedir, statestore):
+ auth_config = RedHatAuthConfig()
+
+ if sssd:
+ statestore.backup_state('authconfig', 'sssd', True)
+ statestore.backup_state('authconfig', 'sssdauth', True)
+ auth_config.enable("sssd")
+ auth_config.enable("sssdauth")
+ else:
+ statestore.backup_state('authconfig', 'ldap', True)
+ auth_config.enable("ldap")
+ auth_config.enable("forcelegacy")
+
+ if mkhomedir:
+ statestore.backup_state('authconfig', 'mkhomedir', True)
+ auth_config.enable("mkhomedir")
+
+ auth_config.execute()
+
+ def modify_pam_to_use_krb5(self, statestore):
+ auth_config = RedHatAuthConfig()
+ statestore.backup_state('authconfig', 'krb5', True)
+ auth_config.enable("krb5")
+ auth_config.add_option("nostart")
+ auth_config.execute()
+
+ def insert_ca_certs_into_systemwide_ca_store(self, ca_certs):
+ new_cacert_path = os.path.join(paths.SYSTEMWIDE_CA_STORE, 'ipa-ca.crt')
+
+ if os.path.exists(new_cacert_path):
+ try:
+ os.remove(new_cacert_path)
+ except OSError, e:
+ root_logger.error(
+ "Could not remove %s: %s", new_cacert_path, e)
+ return False
+
+ new_cacert_path = paths.IPA_P11_KIT
+
+ try:
+ f = open(new_cacert_path, 'w')
+ except IOError, e:
+ root_logger.info("Failed to open %s: %s" % (new_cacert_path, e))
+ return False
+
+ f.write("# This file was created by IPA. Do not edit.\n"
+ "\n")
+
+ has_eku = set()
+ for cert, nickname, trusted, ext_key_usage in ca_certs:
+ try:
+ subject = x509.get_der_subject(cert, x509.DER)
+ issuer = x509.get_der_issuer(cert, x509.DER)
+ serial_number = x509.get_der_serial_number(cert, x509.DER)
+ public_key_info = x509.get_der_public_key_info(cert, x509.DER)
+ except (NSPRError, PyAsn1Error), e:
+ root_logger.warning(
+ "Failed to decode certificate \"%s\": %s", nickname, e)
+ continue
+
+ label = urllib.quote(nickname)
+ subject = urllib.quote(subject)
+ issuer = urllib.quote(issuer)
+ serial_number = urllib.quote(serial_number)
+ public_key_info = urllib.quote(public_key_info)
+
+ cert = base64.b64encode(cert)
+ cert = x509.make_pem(cert)
+
+ obj = ("[p11-kit-object-v1]\n"
+ "class: certificate\n"
+ "certificate-type: x-509\n"
+ "certificate-category: authority\n"
+ "label: \"%(label)s\"\n"
+ "subject: \"%(subject)s\"\n"
+ "issuer: \"%(issuer)s\"\n"
+ "serial-number: \"%(serial_number)s\"\n"
+ "x-public-key-info: \"%(public_key_info)s\"\n" %
+ dict(label=label,
+ subject=subject,
+ issuer=issuer,
+ serial_number=serial_number,
+ public_key_info=public_key_info))
+ if trusted is True:
+ obj += "trusted: true\n"
+ elif trusted is False:
+ obj += "x-distrusted: true\n"
+ obj += "%s\n\n" % cert
+ f.write(obj)
+
+ if ext_key_usage is not None and public_key_info not in has_eku:
+ if not ext_key_usage:
+ ext_key_usage = {x509.EKU_PLACEHOLDER}
+ try:
+ ext_key_usage = x509.encode_ext_key_usage(ext_key_usage)
+ except PyAsn1Error, e:
+ root_logger.warning(
+ "Failed to encode extended key usage for \"%s\": %s",
+ nickname, e)
+ continue
+ value = urllib.quote(ext_key_usage)
+ obj = ("[p11-kit-object-v1]\n"
+ "class: x-certificate-extension\n"
+ "label: \"ExtendedKeyUsage for %(label)s\"\n"
+ "x-public-key-info: \"%(public_key_info)s\"\n"
+ "object-id: 2.5.29.37\n"
+ "value: \"%(value)s\"\n\n" %
+ dict(label=label,
+ public_key_info=public_key_info,
+ value=value))
+ f.write(obj)
+ has_eku.add(public_key_info)
+
+ f.close()
+
+ # Add the CA to the systemwide CA trust database
+ try:
+ ipautil.run([paths.UPDATE_CA_TRUST])
+ except CalledProcessError, e:
+ root_logger.info("Failed to add CA to the systemwide "
+ "CA trust database: %s" % str(e))
+ else:
+ root_logger.info('Added the CA to the systemwide CA trust '
+ 'database.')
+ return True
+
+ return False
+
+ def remove_ca_certs_from_systemwide_ca_store(self):
+ ipa_ca_crt = os.path.join(paths.SYSTEMWIDE_CA_STORE, 'ipa-ca.crt')
+ update = False
+
+ # Remove CA cert from systemwide store
+ for new_cacert_path in (paths.IPA_P11_KIT, ipa_ca_crt):
+ if not os.path.exists(new_cacert_path):
+ continue
+ try:
+ os.remove(new_cacert_path)
+ except OSError, e:
+ root_logger.error(
+ "Could not remove %s: %s", new_cacert_path, e)
+ else:
+ update = True
+
+ if update:
+ try:
+ ipautil.run([paths.UPDATE_CA_TRUST])
+ except CalledProcessError, e:
+ root_logger.error(
+ "Could not update systemwide CA trust database: %s", e)
+ return False
+ else:
+ root_logger.info("Systemwide CA database updated.")
+ return True
+
+ return False
+
+ def backup_and_replace_hostname(self, fstore, statestore, hostname):
+ old_hostname = socket.gethostname()
+ try:
+ ipautil.run([paths.BIN_HOSTNAME, hostname])
+ except ipautil.CalledProcessError, e:
+ print >>sys.stderr, ("Failed to set this machine hostname to "
+ "%s (%s)." % (hostname, str(e)))
+
+ filepath = paths.ETC_HOSTNAME
+ if os.path.exists(filepath):
+ # read old hostname
+ with open(filepath, 'r') as f:
+ for line in f.readlines():
+ line = line.strip()
+ if not line or line.startswith('#'):
+ # skip comment or empty line
+ continue
+ old_hostname = line
+ break
+ fstore.backup_file(filepath)
+
+ with open(filepath, 'w') as f:
+ f.write("%s\n" % hostname)
+ os.chmod(filepath,
+ stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
+ os.chown(filepath, 0, 0)
+ self.restore_context(filepath)
+
+ # store old hostname
+ statestore.backup_state('network', 'hostname', old_hostname)
+
+ def restore_network_configuration(self, fstore, statestore):
+ old_filepath = paths.SYSCONFIG_NETWORK
+ old_hostname = statestore.get_state('network', 'hostname')
+ hostname_was_configured = False
+
+ if fstore.has_file(old_filepath):
+ # This is Fedora >=18 instance that was upgraded from previous
+ # Fedora version which held network configuration
+ # in /etc/sysconfig/network
+ old_filepath_restore = paths.SYSCONFIG_NETWORK_IPABKP
+ fstore.restore_file(old_filepath, old_filepath_restore)
+ print "Deprecated configuration file '%s' was restored to '%s'" \
+ % (old_filepath, old_filepath_restore)
+ hostname_was_configured = True
+
+ filepath = paths.ETC_HOSTNAME
+ if fstore.has_file(filepath):
+ fstore.restore_file(filepath)
+ hostname_was_configured = True
+
+ if not hostname_was_configured and old_hostname:
+ # hostname was not configured before but was set by IPA. Delete
+ # /etc/hostname to restore previous configuration
+ try:
+ os.remove(filepath)
+ except OSError:
+ pass
+
+ def set_selinux_booleans(self, required_settings, backup_func=None):
+ def get_setsebool_args(changes):
+ args = [paths.SETSEBOOL, "-P"]
+ args.extend(["%s=%s" % update for update in changes.iteritems()])
+
+ return args
+
+ if not selinux_enabled():
+ return False
+
+ updated_vars = {}
+ failed_vars = {}
+ for setting, state in required_settings.iteritems():
+ try:
+ (stdout, stderr, rc) = ipautil.run([paths.GETSEBOOL, setting])
+ original_state = stdout.split()[2]
+ if backup_func is not None:
+ backup_func(setting, original_state)
+
+ if original_state != state:
+ updated_vars[setting] = state
+ except ipautil.CalledProcessError, e:
+ log.error("Cannot get SELinux boolean '%s': %s", setting, e)
+ failed_vars[setting] = state
+
+ if updated_vars:
+ args = get_setsebool_args(updated_vars)
+ try:
+ ipautil.run(args)
+ except ipautil.CalledProcessError:
+ failed_vars.update(updated_vars)
+
+ if failed_vars:
+ raise ipapython.errors.SetseboolError(
+ failed=failed_vars,
+ command=' '.join(get_setsebool_args(failed_vars)))
+
+ return True
+
+
+tasks = RedHatTaskNamespace()