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 <rcritten@redhat.com>
This commit is contained in:
Jan Cholasta
2014-09-18 16:28:59 +02:00
committed by Martin Kosek
parent 2421b13a9b
commit 231f57cedb
6 changed files with 157 additions and 68 deletions

View File

@@ -424,6 +424,7 @@ mkdir -p %{buildroot}/%{_localstatedir}/lib/ipa/backup
mkdir -p %{buildroot}%{_sysconfdir}/ipa/ mkdir -p %{buildroot}%{_sysconfdir}/ipa/
/bin/touch %{buildroot}%{_sysconfdir}/ipa/default.conf /bin/touch %{buildroot}%{_sysconfdir}/ipa/default.conf
/bin/touch %{buildroot}%{_sysconfdir}/ipa/ca.crt /bin/touch %{buildroot}%{_sysconfdir}/ipa/ca.crt
mkdir -p %{buildroot}%{_sysconfdir}/ipa/nssdb
mkdir -p %{buildroot}/%{_localstatedir}/lib/ipa-client/sysrestore mkdir -p %{buildroot}/%{_localstatedir}/lib/ipa-client/sysrestore
mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d
install -pm 644 contrib/completion/ipa.bash_completion %{buildroot}%{_sysconfdir}/bash_completion.d/ipa 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 || : /bin/systemctl condrestart ntpd.service 2>&1 || :
fi fi
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 fi
%triggerin -n freeipa-client -- openssh-server %triggerin -n freeipa-client -- openssh-server
@@ -798,6 +810,11 @@ fi
%dir %attr(0755,root,root) %{_sysconfdir}/ipa/ %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/default.conf
%ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/ca.crt %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} %if ! %{ONLY_CLIENT}
%files tests -f tests-python.list %files tests -f tests-python.list

View File

@@ -46,7 +46,7 @@ try:
from ipaplatform import services from ipaplatform import services
from ipaplatform.paths import paths from ipaplatform.paths import paths
from ipapython import ipautil, sysrestore, version, certmonger, ipaldap 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 ipapython.config import IPAOptionParser
from ipalib import api, errors from ipalib import api, errors
from ipalib import x509, certstore from ipalib import x509, certstore
@@ -550,6 +550,15 @@ def uninstall(options, env):
cmonger.service_name, str(e)) cmonger.service_name, str(e))
# Remove our host cert and CA cert # 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'}) purge_ipa_certs({client_nss_nickname, 'IPA CA', 'External CA cert'})
try: try:
@@ -2524,19 +2533,20 @@ def install(options, env, fstore, statestore):
except ValueError: except ValueError:
pass pass
tmp_nss_dir = tempfile.mkdtemp()
try:
# Add CA certs to a temporary NSS database # Add CA certs to a temporary NSS database
try: try:
os.mkdir(paths.IPA_NSSDB_DIR)
pwd_file = ipautil.write_tmp_file(ipautil.ipa_generate_password()) pwd_file = ipautil.write_tmp_file(ipautil.ipa_generate_password())
run([paths.CERTUTIL, '-N', run([paths.CERTUTIL, '-N',
'-d', paths.IPA_NSSDB_DIR, '-d', tmp_nss_dir,
'-f', pwd_file.name]) '-f', pwd_file.name])
ca_certs = x509.load_certificate_list_from_file(CACERT) ca_certs = x509.load_certificate_list_from_file(CACERT)
ca_certs = [cert.der_data for cert in ca_certs] ca_certs = [cert.der_data for cert in ca_certs]
for i, cert in enumerate(ca_certs): for i, cert in enumerate(ca_certs):
run([paths.CERTUTIL, '-A', run([paths.CERTUTIL, '-A',
'-d', paths.IPA_NSSDB_DIR, '-d', tmp_nss_dir,
'-n', 'CA certificate %d' % (i + 1), '-n', 'CA certificate %d' % (i + 1),
'-t', 'C,,'], '-t', 'C,,'],
stdin=cert) stdin=cert)
@@ -2544,42 +2554,48 @@ def install(options, env, fstore, statestore):
root_logger.info("Failed to add CA to temporary NSS database.") root_logger.info("Failed to add CA to temporary NSS database.")
return CLIENT_INSTALL_ERROR return CLIENT_INSTALL_ERROR
# Now, let's try to connect to the server's XML-RPC interface # Now, let's try to connect to the server's RPC interface
connected = False connected = False
try: try:
api.Backend.rpcclient.connect(nss_dir=paths.IPA_NSSDB_DIR) api.Backend.rpcclient.connect(nss_dir=tmp_nss_dir)
connected = True connected = True
root_logger.debug('Try RPC connection') root_logger.debug("Try RPC connection")
api.Backend.rpcclient.forward('ping') api.Backend.rpcclient.forward('ping')
except errors.KerberosError, e: except errors.KerberosError, e:
if connected: if connected:
api.Backend.rpcclient.disconnect() api.Backend.rpcclient.disconnect()
root_logger.info('Cannot connect to the server due to ' + root_logger.info(
'Kerberos error: %s. Trying with delegate=True', str(e)) "Cannot connect to the server due to Kerberos error: %s. "
"Trying with delegate=True", e)
try: try:
api.Backend.rpcclient.connect(delegate=True, nss_dir=paths.IPA_NSSDB_DIR) api.Backend.rpcclient.connect(delegate=True,
root_logger.debug('Try RPC connection') nss_dir=tmp_nss_dir)
root_logger.debug("Try RPC connection")
api.Backend.rpcclient.forward('ping') 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 # The remote server is not capable of Kerberos S4U2Proxy
# This features is implemented in IPA server version 2.2 and higher # delegation. This features is implemented in IPA server
root_logger.warning("Target IPA server has a lower version than " + # version 2.2 and higher
"the enrolled client") root_logger.warning(
root_logger.warning("Some capabilities including the ipa " + "Target IPA server has a lower version than the enrolled "
"command capability may not be available") "client")
root_logger.warning(
"Some capabilities including the ipa command capability "
"may not be available")
except errors.PublicError, e2: except errors.PublicError, e2:
root_logger.warning( root_logger.warning(
'Second connect with delegate=True also failed: %s', str(e2)) "Second connect with delegate=True also failed: %s", e2)
root_logger.error( root_logger.error(
"Cannot connect to the IPA server XML-RPC interface: %s", "Cannot connect to the IPA server RPC interface: %s", e2)
str(e2))
return CLIENT_INSTALL_ERROR return CLIENT_INSTALL_ERROR
except errors.PublicError, e: except errors.PublicError, e:
root_logger.error( root_logger.error(
'Cannot connect to the server due to generic error: %s', str(e)) "Cannot connect to the server due to generic error: %s", e)
return CLIENT_INSTALL_ERROR return CLIENT_INSTALL_ERROR
finally:
shutil.rmtree(tmp_nss_dir)
# Use the RPC directly so older servers are supported # Use the RPC directly so older servers are supported
result = api.Backend.rpcclient.forward( result = api.Backend.rpcclient.forward(
@@ -2591,14 +2607,38 @@ def install(options, env, fstore, statestore):
if not remote_env['enable_ra']: if not remote_env['enable_ra']:
disable_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 # Get CA certificates from the certificate store
ca_certs = get_certs_from_ldap(cli_server[0], cli_basedn, cli_realm, ca_certs = get_certs_from_ldap(cli_server[0], cli_basedn, cli_realm,
remote_env['enable_ra']) 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) 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(): if not purge_ipa_certs():
root_logger.info( root_logger.info(
"Failed to remove old IPA certificates from the default NSS " "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) root_logger.error("Failed to open /etc/pki/nssdb/ipa.txt: %s", e)
return CLIENT_INSTALL_ERROR 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: 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, run([paths.CERTUTIL,
"-A", "-A",
"-d", paths.NSS_DB_DIR, "-d", paths.NSS_DB_DIR,
@@ -2624,12 +2662,13 @@ def install(options, env, fstore, statestore):
"-t", trust_flags], "-t", trust_flags],
stdin=cert) stdin=cert)
except CalledProcessError, e: 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() list_file.close()
return CLIENT_INSTALL_ERROR return CLIENT_INSTALL_ERROR
else: else:
root_logger.info('Added the CA to the default NSS database.')
list_file.write(nickname + '\n') list_file.write(nickname + '\n')
root_logger.info("Added CA certificates to the default NSS database.")
list_file.close() list_file.close()
@@ -2855,7 +2894,3 @@ finally:
os.remove(CCACHE_FILE) os.remove(CCACHE_FILE)
except Exception: except Exception:
pass pass
try:
shutil.rmtree(paths.IPA_NSSDB_DIR)
except Exception:
pass

View File

@@ -70,6 +70,15 @@ class CertUpdate(admintool.AdminTool):
def update_client(self, certs): def update_client(self, certs):
self.update_file(paths.IPA_CA_CRT, 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) self.update_db(paths.NSS_DB_DIR, certs)

View File

@@ -483,7 +483,7 @@ class SSLTransport(LanguageAwareTransport):
if self._connection and host == self._connection[0]: if self._connection and host == self._connection[0]:
return self._connection[1] 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) no_init = self.__nss_initialized(dbdir)
if sys.version_info < (2, 7): if sys.version_info < (2, 7):
conn = NSSHTTPS(host, 443, dbdir=dbdir, no_init=no_init) conn = NSSHTTPS(host, 443, dbdir=dbdir, no_init=no_init)

View File

@@ -63,7 +63,7 @@ class BasePathNamespace(object):
IPA_DNS_UPDATE_TXT = "/etc/ipa/.dns_update.txt" IPA_DNS_UPDATE_TXT = "/etc/ipa/.dns_update.txt"
IPA_CA_CRT = "/etc/ipa/ca.crt" IPA_CA_CRT = "/etc/ipa/ca.crt"
IPA_DEFAULT_CONF = "/etc/ipa/default.conf" 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_CONF = "/etc/krb5.conf"
KRB5_KEYTAB = "/etc/krb5.keytab" KRB5_KEYTAB = "/etc/krb5.keytab"
LDAP_CONF = "/etc/ldap.conf" LDAP_CONF = "/etc/ldap.conf"

View File

@@ -17,6 +17,34 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import os
from ipaplatform.paths import paths
from ipapython import ipautil
CA_NICKNAME_FMT = "%s IPA CA" CA_NICKNAME_FMT = "%s IPA CA"
def get_ca_nickname(realm, format=CA_NICKNAME_FMT): def get_ca_nickname(realm, format=CA_NICKNAME_FMT):
return format % realm 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)