From 231f57cedb4fea26d3317fe2b1f30d043c7d2524 Mon Sep 17 00:00:00 2001 From: Jan Cholasta Date: Thu, 18 Sep 2014 16:28:59 +0200 Subject: Introduce NSS database /etc/ipa/nssdb This is the new default NSS database for IPA. /etc/pki/nssdb is still maintained for backward compatibility. https://fedorahosted.org/freeipa/ticket/3259 Reviewed-By: Rob Crittenden --- freeipa.spec.in | 17 ++++ ipa-client/ipa-install/ipa-client-install | 159 ++++++++++++++++++------------ ipa-client/ipaclient/ipa_certupdate.py | 9 ++ ipalib/rpc.py | 2 +- ipaplatform/base/paths.py | 2 +- ipapython/certdb.py | 28 ++++++ 6 files changed, 153 insertions(+), 64 deletions(-) diff --git a/freeipa.spec.in b/freeipa.spec.in index e3ffc0bad..6e9747fde 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -424,6 +424,7 @@ mkdir -p %{buildroot}/%{_localstatedir}/lib/ipa/backup mkdir -p %{buildroot}%{_sysconfdir}/ipa/ /bin/touch %{buildroot}%{_sysconfdir}/ipa/default.conf /bin/touch %{buildroot}%{_sysconfdir}/ipa/ca.crt +mkdir -p %{buildroot}%{_sysconfdir}/ipa/nssdb mkdir -p %{buildroot}/%{_localstatedir}/lib/ipa-client/sysrestore mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d install -pm 644 contrib/completion/ipa.bash_completion %{buildroot}%{_sysconfdir}/bash_completion.d/ipa @@ -538,6 +539,17 @@ if [ $1 -gt 1 ] ; then /bin/systemctl condrestart ntpd.service 2>&1 || : fi fi + + if [ ! -f '/etc/ipa/nssdb/cert8.db' -a $restore -ge 2 ]; then + python2 -c 'from ipapython.certdb import create_ipa_nssdb; create_ipa_nssdb()' >/dev/null 2>&1 + tempfile=$(mktemp) + if certutil -L -d /etc/pki/nssdb -n 'IPA CA' -a >"$tempfile" 2>/var/log/ipaupgrade.log; then + certutil -A -d /etc/ipa/nssdb -n 'IPA CA' -t CT,C,C -a -i "$tempfile" >/var/log/ipaupgrade.log 2>&1 + elif certutil -L -d /etc/pki/nssdb -n 'External CA cert' -a >"$tempfile" 2>/var/log/ipaupgrade.log; then + certutil -A -d /etc/ipa/nssdb -n 'External CA cert' -t C,, -a -i "$tempfile" >/var/log/ipaupgrade.log 2>&1 + fi + rm -f "$tempfile" + fi fi %triggerin -n freeipa-client -- openssh-server @@ -798,6 +810,11 @@ fi %dir %attr(0755,root,root) %{_sysconfdir}/ipa/ %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/default.conf %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/ca.crt +%dir %attr(0755,root,root) %{_sysconfdir}/ipa/nssdb +%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/cert8.db +%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/key3.db +%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/secmod.db +%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/pwdfile.txt %if ! %{ONLY_CLIENT} %files tests -f tests-python.list diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install index 45e802207..ab40cd827 100755 --- a/ipa-client/ipa-install/ipa-client-install +++ b/ipa-client/ipa-install/ipa-client-install @@ -46,7 +46,7 @@ try: from ipaplatform import services from ipaplatform.paths import paths from ipapython import ipautil, sysrestore, version, certmonger, ipaldap - from ipapython import kernel_keyring + from ipapython import kernel_keyring, certdb from ipapython.config import IPAOptionParser from ipalib import api, errors from ipalib import x509, certstore @@ -550,6 +550,15 @@ def uninstall(options, env): cmonger.service_name, str(e)) # Remove our host cert and CA cert + for filename in (os.path.join(paths.IPA_NSSDB_DIR, 'cert8.db'), + os.path.join(paths.IPA_NSSDB_DIR, 'key3.db'), + os.path.join(paths.IPA_NSSDB_DIR, 'secmod.db'), + os.path.join(paths.IPA_NSSDB_DIR, 'pwdfile.txt')): + try: + os.remove(filename) + except OSError, e: + root_logger.error("Failed to remove %s: %s", filename, e) + purge_ipa_certs({client_nss_nickname, 'IPA CA', 'External CA cert'}) try: @@ -2524,62 +2533,69 @@ def install(options, env, fstore, statestore): except ValueError: pass - # Add CA certs to a temporary NSS database + tmp_nss_dir = tempfile.mkdtemp() try: - os.mkdir(paths.IPA_NSSDB_DIR) - pwd_file = ipautil.write_tmp_file(ipautil.ipa_generate_password()) - run([paths.CERTUTIL, '-N', - '-d', paths.IPA_NSSDB_DIR, - '-f', pwd_file.name]) - - ca_certs = x509.load_certificate_list_from_file(CACERT) - ca_certs = [cert.der_data for cert in ca_certs] - for i, cert in enumerate(ca_certs): - run([paths.CERTUTIL, '-A', - '-d', paths.IPA_NSSDB_DIR, - '-n', 'CA certificate %d' % (i + 1), - '-t', 'C,,'], - stdin=cert) - except CalledProcessError, e: - root_logger.info("Failed to add CA to temporary NSS database.") - return CLIENT_INSTALL_ERROR + # Add CA certs to a temporary NSS database + try: + pwd_file = ipautil.write_tmp_file(ipautil.ipa_generate_password()) + run([paths.CERTUTIL, '-N', + '-d', tmp_nss_dir, + '-f', pwd_file.name]) - # Now, let's try to connect to the server's XML-RPC interface - connected = False - try: - api.Backend.rpcclient.connect(nss_dir=paths.IPA_NSSDB_DIR) - connected = True - root_logger.debug('Try RPC connection') - api.Backend.rpcclient.forward('ping') - except errors.KerberosError, e: - if connected: - api.Backend.rpcclient.disconnect() - root_logger.info('Cannot connect to the server due to ' + - 'Kerberos error: %s. Trying with delegate=True', str(e)) + ca_certs = x509.load_certificate_list_from_file(CACERT) + ca_certs = [cert.der_data for cert in ca_certs] + for i, cert in enumerate(ca_certs): + run([paths.CERTUTIL, '-A', + '-d', tmp_nss_dir, + '-n', 'CA certificate %d' % (i + 1), + '-t', 'C,,'], + stdin=cert) + except CalledProcessError, e: + root_logger.info("Failed to add CA to temporary NSS database.") + return CLIENT_INSTALL_ERROR + + # Now, let's try to connect to the server's RPC interface + connected = False try: - api.Backend.rpcclient.connect(delegate=True, nss_dir=paths.IPA_NSSDB_DIR) - root_logger.debug('Try RPC connection') + api.Backend.rpcclient.connect(nss_dir=tmp_nss_dir) + connected = True + root_logger.debug("Try RPC connection") api.Backend.rpcclient.forward('ping') + except errors.KerberosError, e: + if connected: + api.Backend.rpcclient.disconnect() + root_logger.info( + "Cannot connect to the server due to Kerberos error: %s. " + "Trying with delegate=True", e) + try: + api.Backend.rpcclient.connect(delegate=True, + nss_dir=tmp_nss_dir) + root_logger.debug("Try RPC connection") + api.Backend.rpcclient.forward('ping') - root_logger.info('Connection with delegate=True successful') + root_logger.info("Connection with delegate=True successful") - # The remote server is not capable of Kerberos S4U2Proxy delegation - # This features is implemented in IPA server version 2.2 and higher - root_logger.warning("Target IPA server has a lower version than " + - "the enrolled client") - root_logger.warning("Some capabilities including the ipa " + - "command capability may not be available") - except errors.PublicError, e2: - root_logger.warning( - 'Second connect with delegate=True also failed: %s', str(e2)) + # The remote server is not capable of Kerberos S4U2Proxy + # delegation. This features is implemented in IPA server + # version 2.2 and higher + root_logger.warning( + "Target IPA server has a lower version than the enrolled " + "client") + root_logger.warning( + "Some capabilities including the ipa command capability " + "may not be available") + except errors.PublicError, e2: + root_logger.warning( + "Second connect with delegate=True also failed: %s", e2) + root_logger.error( + "Cannot connect to the IPA server RPC interface: %s", e2) + return CLIENT_INSTALL_ERROR + except errors.PublicError, e: root_logger.error( - "Cannot connect to the IPA server XML-RPC interface: %s", - str(e2)) + "Cannot connect to the server due to generic error: %s", e) return CLIENT_INSTALL_ERROR - except errors.PublicError, e: - root_logger.error( - 'Cannot connect to the server due to generic error: %s', str(e)) - return CLIENT_INSTALL_ERROR + finally: + shutil.rmtree(tmp_nss_dir) # Use the RPC directly so older servers are supported result = api.Backend.rpcclient.forward( @@ -2591,14 +2607,38 @@ def install(options, env, fstore, statestore): if not remote_env['enable_ra']: disable_ra() + # Create IPA NSS database + try: + certdb.create_ipa_nssdb() + except ipautil.CalledProcessError, e: + root_logger.error("Failed to create IPA NSS database: %s", e) + return CLIENT_INSTALL_ERROR + # Get CA certificates from the certificate store ca_certs = get_certs_from_ldap(cli_server[0], cli_basedn, cli_realm, remote_env['enable_ra']) + ca_certs_trust = [(c, n, certstore.key_policy_to_trust_flags(t, True, u)) + for (c, n, t, u) in ca_certs] - # Add the CA to the platform-dependant systemwide CA store + # Add the CA certificates to the IPA NSS database + root_logger.debug("Adding CA certificates to the IPA NSS database.") + for cert, nickname, trust_flags in ca_certs_trust: + try: + run([paths.CERTUTIL, + "-A", + "-d", paths.IPA_NSSDB_DIR, + "-n", nickname, + "-t", trust_flags], + stdin=cert) + except CalledProcessError, e: + root_logger.error( + "Failed to add %s to the IPA NSS database.", nickname) + return CLIENT_INSTALL_ERROR + + # Add the CA certificates to the platform-dependant systemwide CA store tasks.insert_ca_certs_into_systemwide_ca_store(ca_certs) - # Add the CA to the default NSS database and trust it + # Add the CA certificates to the default NSS database if not purge_ipa_certs(): root_logger.info( "Failed to remove old IPA certificates from the default NSS " @@ -2611,12 +2651,10 @@ def install(options, env, fstore, statestore): root_logger.error("Failed to open /etc/pki/nssdb/ipa.txt: %s", e) return CLIENT_INSTALL_ERROR - for cert, nickname, trusted, ext_key_usage in ca_certs: + root_logger.debug( + "Attempting to add CA certificates to the default NSS database.") + for cert, nickname, trust_flags in ca_certs_trust: try: - root_logger.debug("Attempting to add CA directly to the " - "default NSS database.") - trust_flags = certstore.key_policy_to_trust_flags( - trusted, True, ext_key_usage) run([paths.CERTUTIL, "-A", "-d", paths.NSS_DB_DIR, @@ -2624,12 +2662,13 @@ def install(options, env, fstore, statestore): "-t", trust_flags], stdin=cert) except CalledProcessError, e: - root_logger.info("Failed to add CA to the default NSS database.") + root_logger.error( + "Failed to add %s to the default NSS database.", nickname) list_file.close() return CLIENT_INSTALL_ERROR else: - root_logger.info('Added the CA to the default NSS database.') list_file.write(nickname + '\n') + root_logger.info("Added CA certificates to the default NSS database.") list_file.close() @@ -2855,7 +2894,3 @@ finally: os.remove(CCACHE_FILE) except Exception: pass - try: - shutil.rmtree(paths.IPA_NSSDB_DIR) - except Exception: - pass diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py index 8e7fe0470..57dbf200b 100644 --- a/ipa-client/ipaclient/ipa_certupdate.py +++ b/ipa-client/ipaclient/ipa_certupdate.py @@ -70,6 +70,15 @@ class CertUpdate(admintool.AdminTool): def update_client(self, certs): self.update_file(paths.IPA_CA_CRT, certs) + self.update_db(paths.IPA_NSSDB_DIR, certs) + + for nickname in ('IPA CA', 'External CA cert'): + try: + ipautil.run([paths.CERTUTIL, '-D', + '-d', paths.NSS_DB_DIR, + '-n', nickname]) + except ipautil.CalledProcessError, e: + pass self.update_db(paths.NSS_DB_DIR, certs) diff --git a/ipalib/rpc.py b/ipalib/rpc.py index e7e60f414..5934f0c26 100644 --- a/ipalib/rpc.py +++ b/ipalib/rpc.py @@ -483,7 +483,7 @@ class SSLTransport(LanguageAwareTransport): if self._connection and host == self._connection[0]: return self._connection[1] - dbdir = getattr(context, 'nss_dir', paths.NSS_DB_DIR) + dbdir = getattr(context, 'nss_dir', paths.IPA_NSSDB_DIR) no_init = self.__nss_initialized(dbdir) if sys.version_info < (2, 7): conn = NSSHTTPS(host, 443, dbdir=dbdir, no_init=no_init) diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py index 5093408e3..283aab682 100644 --- a/ipaplatform/base/paths.py +++ b/ipaplatform/base/paths.py @@ -63,7 +63,7 @@ class BasePathNamespace(object): IPA_DNS_UPDATE_TXT = "/etc/ipa/.dns_update.txt" IPA_CA_CRT = "/etc/ipa/ca.crt" IPA_DEFAULT_CONF = "/etc/ipa/default.conf" - IPA_NSSDB_DIR = "/etc/ipa/.nssdb" + IPA_NSSDB_DIR = "/etc/ipa/nssdb" KRB5_CONF = "/etc/krb5.conf" KRB5_KEYTAB = "/etc/krb5.keytab" LDAP_CONF = "/etc/ldap.conf" diff --git a/ipapython/certdb.py b/ipapython/certdb.py index a85831380..426c80996 100644 --- a/ipapython/certdb.py +++ b/ipapython/certdb.py @@ -17,6 +17,34 @@ # along with this program. If not, see . # +import os + +from ipaplatform.paths import paths +from ipapython import ipautil + CA_NICKNAME_FMT = "%s IPA CA" + + def get_ca_nickname(realm, format=CA_NICKNAME_FMT): return format % realm + + +def create_ipa_nssdb(): + pwdfile = os.path.join(paths.IPA_NSSDB_DIR, 'pwdfile.txt') + + ipautil.backup_file(pwdfile) + ipautil.backup_file(os.path.join(paths.IPA_NSSDB_DIR, 'cert8.db')) + ipautil.backup_file(os.path.join(paths.IPA_NSSDB_DIR, 'key3.db')) + ipautil.backup_file(os.path.join(paths.IPA_NSSDB_DIR, 'secmod.db')) + + with open(pwdfile, 'w') as f: + f.write(ipautil.ipa_generate_password(pwd_len=40)) + os.chmod(pwdfile, 0600) + + ipautil.run([paths.CERTUTIL, + "-N", + "-d", paths.IPA_NSSDB_DIR, + "-f", pwdfile]) + os.chmod(os.path.join(paths.IPA_NSSDB_DIR, 'cert8.db'), 0644) + os.chmod(os.path.join(paths.IPA_NSSDB_DIR, 'key3.db'), 0644) + os.chmod(os.path.join(paths.IPA_NSSDB_DIR, 'secmod.db'), 0644) -- cgit