Dne 29.9.2014 v 18:09 Rob Crittenden napsal(a):
Jan Cholasta wrote:
Dne 29.9.2014 v 15:05 Rob Crittenden napsal(a):
Jan Cholasta wrote:
Dne 26.9.2014 v 17:13 Rob Crittenden napsal(a):
Jan Cholasta wrote:
Dne 23.9.2014 v 20:39 Rob Crittenden napsal(a):
Jan Cholasta wrote:
Hi,
the attached patches fix various bugs and shortcomings in the CA
management and renewal code. Related tickets:
<https://fedorahosted.org/freeipa/ticket/4416>,
<https://fedorahosted.org/freeipa/ticket/4460>.
(Patch 319 was originally posted at
<http://www.redhat.com/archives/freeipa-devel/2014-September/msg00132.html>.)
Only two of the patches includes what ticket(s) they address. Most
have
the tersest of commit messages. If got more and more difficult to see
why the changes were needed at all, as you'll see.
Sorry, fixed (hopefully).
Note that most of these patches fix stuff I didn't have time to fix
before I posted the original CA management patches, hence the missing
tickets.
Well, the policy is that every commit should have a ticket. So I guess
re-open the old tickets or open new ones. This will help someone in the
future know the "why" of a patch. I've certainly been guilty
OK, I will reopen the related tickets.
Here is a new set of reviews as trying to intermingle was making my
eyes
cross:
319:
I guess I still don't understand why you can't pull the certs out of
LDAP when creating this database. When this code runs, we know that the
client is configured, so we have access to authentication. Why can't
create_ipa_nssdb pull the certs directly? It seems more robust to me,
and the code is already written in ipa-client-install to do this.
Well, I don't understand why do you want them to be updated so much, as
nothing will break if they are not. Also try to imagine what would
happen if, say, 10k clients were updated at the same moment...
What's the point of a database missing certificates?
It won't be missing any certificates if /etc/pki/nssdb was not missing
any certificates before the update.
As I said, the update will not break anything. It will not fix anything
either, but I don't think this kind of fixing should be done during
client RPM upgrade. It is not consistent with anything else we do during
client RPM upgrade, it does not scale well and it just does not feel
right to me in overall.
Ok, I'll concede the point that there is no difference post-update, but
it doesn't do what ipa-certupdate does. You can potentially end up with
a completely diffferent set of certificates. So why the difference?
If you end up with a *completely* different set of certificates, it's
because the admins screwed up, so it's theirs responsibility to fix it,
not ours IMHO.
Post install of new client bits:
# certutil -L -d /etc/ipa/nssdb/
IPA CA CT,C,C
After running ipa-certupdate:
# certutil -L -d /etc/ipa/nssdb/
CN=Primary CA,O=example.com,C=US ,,
CN=Secondary CA,O=example.com,C=US ,,
GREYOAK.COM IPA CA CT,C,C
Quite the difference.
Not much of a difference when you realize that "IPA CA" and "GREYOAK.COM
IPA CA" are the same certificate and the rest are there just for
completeness.
I'll ACK for now since this doesn't materially change anything and
shouldn't break any installs but it begs the question of why it is
acceptable now but not acceptable to make ipa-certupdate do the same.
I have changed the NSS database used by the RPC code from /etc/pki/nssdb
to /etc/ipa/nssdb. What the RPM update does is a necessary configuration
update to keep RPC working. ipa-certupdate on the other hand fetches new
policy from the server, which is quite a different thing IMO.
So ACK for 319, 324-331, 340.
Thanks!
The LDAP update happens in renew_ca_cert. Are there any relevant errors
in /var/log/messages? Is there caRenewalMaster in ipaConfigString of the
master entry of the master where you run ipa-cacert-manage renew?
Sep 25 16:06:44 grindle renew_ca_cert: Traceback (most recent call last):
File "/usr/lib64/ipa/certmonger/renew_ca_cert", line 214, in <module>
main()
File "/usr/lib64/ipa/certmonger/renew_ca_cert", line 79, in main
ca = cainstance.CAInstance(host_name=api.env.host, ldapi=False)
File
"/usr/lib/python2.7/site-packages/ipaserver/install/cainstance.py", line
357, in __init__
self.ra_agent_pwd = self.ra_agent_db + "/pwdfile.txt"
TypeError: unsupported operand type(s) for +: 'NoneType' and 'str'
Sep 25 16:06:44 grindle certmonger: Certificate named "caSigningCert
cert-pki-ca" in token "NSS Certificate DB" in database
"/etc/pki/pki-tomcat/alias" issued by CA and saved.
One of the KRA patches broke this. I assumed you'd be testing on top of
ipa-4-1, so I didn't fix it, sorry. See the attached patch 342 for a
fix. Martin, can you please review it?
rob
The patches needed a rebase, attached.
--
Jan Cholasta
>From 4ce41384736f7732588b236f6667b8b1e8e751ce Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Thu, 18 Sep 2014 16:28:59 +0200
Subject: [PATCH 01/10] 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
---
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 9171c82..d16a865 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -423,6 +423,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
@@ -537,6 +538,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
@@ -795,6 +807,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 45e8022..ab40cd8 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 8e7fe04..57dbf20 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 e7e60f4..5934f0c 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 6f2a29e..1493918 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 a858313..426c809 100644
--- a/ipapython/certdb.py
+++ b/ipapython/certdb.py
@@ -17,6 +17,34 @@
# 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"
+
+
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)
--
1.9.3
>From 873cd4d73da5dd91cbe13c1b6526b66982e088d8 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Thu, 18 Sep 2014 11:19:46 +0200
Subject: [PATCH 02/10] Move NSSDatabase from ipaserver.certs to
ipapython.certdb
https://fedorahosted.org/freeipa/ticket/4416
---
ipapython/certdb.py | 451 +++++++++++++++++++++++++++++++++++++++++++++
ipaserver/install/certs.py | 448 +-------------------------------------------
2 files changed, 452 insertions(+), 447 deletions(-)
diff --git a/ipapython/certdb.py b/ipapython/certdb.py
index 426c809..e190a70 100644
--- a/ipapython/certdb.py
+++ b/ipapython/certdb.py
@@ -18,8 +18,14 @@
#
import os
+import re
+import tempfile
+import shutil
+from nss import nss
+from nss.error import NSPRError
from ipaplatform.paths import paths
+from ipapython.ipa_log_manager import root_logger
from ipapython import ipautil
CA_NICKNAME_FMT = "%s IPA CA"
@@ -48,3 +54,448 @@ def create_ipa_nssdb():
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)
+
+
+def find_cert_from_txt(cert, start=0):
+ """
+ Given a cert blob (str) which may or may not contian leading and
+ trailing text, pull out just the certificate part. This will return
+ the FIRST cert in a stream of data.
+
+ Returns a tuple (certificate, last position in cert)
+ """
+ s = cert.find('-----BEGIN CERTIFICATE-----', start)
+ e = cert.find('-----END CERTIFICATE-----', s)
+ if e > 0:
+ e = e + 25
+
+ if s < 0 or e < 0:
+ raise RuntimeError("Unable to find certificate")
+
+ cert = cert[s:e]
+ return (cert, e)
+
+
+class NSSDatabase(object):
+ """A general-purpose wrapper around a NSS cert database
+
+ For permanent NSS databases, pass the cert DB directory to __init__
+
+ For temporary databases, do not pass nssdir, and call close() when done
+ to remove the DB. Alternatively, a NSSDatabase can be used as a
+ context manager that calls close() automatically.
+ """
+ # Traditionally, we used CertDB for our NSS DB operations, but that class
+ # got too tied to IPA server details, killing reusability.
+ # BaseCertDB is a class that knows nothing about IPA.
+ # Generic NSS DB code should be moved here.
+ def __init__(self, nssdir=None):
+ if nssdir is None:
+ self.secdir = tempfile.mkdtemp()
+ self._is_temporary = True
+ else:
+ self.secdir = nssdir
+ self._is_temporary = False
+
+ def close(self):
+ if self._is_temporary:
+ shutil.rmtree(self.secdir)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, tb):
+ self.close()
+
+ def run_certutil(self, args, stdin=None):
+ new_args = [paths.CERTUTIL, "-d", self.secdir]
+ new_args = new_args + args
+ return ipautil.run(new_args, stdin)
+
+ def create_db(self, password_filename):
+ """Create cert DB
+
+ :param password_filename: Name of file containing the database password
+ """
+ self.run_certutil(["-N", "-f", password_filename])
+
+ def list_certs(self):
+ """Return nicknames and cert flags for all certs in the database
+
+ :return: List of (name, trust_flags) tuples
+ """
+ certs, stderr, returncode = self.run_certutil(["-L"])
+ certs = certs.splitlines()
+
+ # FIXME, this relies on NSS never changing the formatting of certutil
+ certlist = []
+ for cert in certs:
+ match = re.match(r'^(.+?)\s+(\w*,\w*,\w*)\s*$', cert)
+ if match:
+ certlist.append(match.groups())
+
+ return tuple(certlist)
+
+ def find_server_certs(self):
+ """Return nicknames and cert flags for server certs in the database
+
+ Server certs have an "u" character in the trust flags.
+
+ :return: List of (name, trust_flags) tuples
+ """
+ server_certs = []
+ for name, flags in self.list_certs():
+ if 'u' in flags:
+ server_certs.append((name, flags))
+
+ return server_certs
+
+ def get_trust_chain(self, nickname):
+ """Return names of certs in a given cert's trust chain
+
+ :param nickname: Name of the cert
+ :return: List of certificate names
+ """
+ root_nicknames = []
+ chain, stderr, returncode = self.run_certutil([
+ "-O", "-n", nickname])
+ chain = chain.splitlines()
+
+ for c in chain:
+ m = re.match('\s*"(.*)" \[.*', c)
+ if m:
+ root_nicknames.append(m.groups()[0])
+
+ return root_nicknames
+
+ def import_pkcs12(self, pkcs12_filename, db_password_filename,
+ pkcs12_passwd=None):
+ args = [paths.PK12UTIL, "-d", self.secdir,
+ "-i", pkcs12_filename,
+ "-k", db_password_filename, '-v']
+ if pkcs12_passwd is not None:
+ pkcs12_passwd = pkcs12_passwd + '\n'
+ args = args + ["-w", paths.DEV_STDIN]
+ try:
+ ipautil.run(args, stdin=pkcs12_passwd)
+ except ipautil.CalledProcessError, e:
+ if e.returncode == 17:
+ raise RuntimeError("incorrect password for pkcs#12 file %s" %
+ pkcs12_filename)
+ elif e.returncode == 10:
+ raise RuntimeError("Failed to open %s" % pkcs12_filename)
+ else:
+ raise RuntimeError("unknown error import pkcs#12 file %s" %
+ pkcs12_filename)
+
+ def import_files(self, files, db_password_filename, import_keys=False,
+ key_password=None, key_nickname=None):
+ """
+ Import certificates and a single private key from multiple files
+
+ The files may be in PEM and DER certificate, PKCS#7 certificate chain,
+ PKCS#8 and raw private key and PKCS#12 formats.
+
+ :param files: Names of files to import
+ :param db_password_filename: Name of file containing the database
+ password
+ :param import_keys: Whether to import private keys
+ :param key_password: Password to decrypt private keys
+ :param key_nickname: Nickname of the private key to import from PKCS#12
+ files
+ """
+ key_file = None
+ extracted_key = None
+ extracted_certs = ''
+
+ for filename in files:
+ try:
+ with open(filename, 'rb') as f:
+ data = f.read()
+ except IOError as e:
+ raise RuntimeError(
+ "Failed to open %s: %s" % (filename, e.strerror))
+
+ # Try to parse the file as PEM file
+ matches = list(re.finditer(
+ r'-----BEGIN (.+?)-----(.*?)-----END \1-----', data, re.DOTALL))
+ if matches:
+ loaded = False
+ for match in matches:
+ body = match.group()
+ label = match.group(1)
+ line = len(data[:match.start() + 1].splitlines())
+
+ if label in ('CERTIFICATE', 'X509 CERTIFICATE',
+ 'X.509 CERTIFICATE'):
+ try:
+ x509.load_certificate(match.group(2))
+ except NSPRError as e:
+ if label != 'CERTIFICATE':
+ root_logger.warning(
+ "Skipping certificate in %s at line %s: %s",
+ filename, line, e)
+ continue
+ else:
+ extracted_certs += body + '\n'
+ loaded = True
+ continue
+
+ if label in ('PKCS7', 'PKCS #7 SIGNED DATA', 'CERTIFICATE'):
+ args = [
+ paths.OPENSSL, 'pkcs7',
+ '-print_certs',
+ ]
+ try:
+ stdout, stderr, rc = ipautil.run(args, stdin=body)
+ except ipautil.CalledProcessError as e:
+ if label == 'CERTIFICATE':
+ root_logger.warning(
+ "Skipping certificate in %s at line %s: %s",
+ filename, line, e)
+ else:
+ root_logger.warning(
+ "Skipping PKCS#7 in %s at line %s: %s",
+ filename, line, e)
+ continue
+ else:
+ extracted_certs += stdout + '\n'
+ loaded = True
+ continue
+
+ if label in ('PRIVATE KEY', 'ENCRYPTED PRIVATE KEY',
+ 'RSA PRIVATE KEY', 'DSA PRIVATE KEY',
+ 'EC PRIVATE KEY'):
+ if not import_keys:
+ continue
+
+ if key_file:
+ raise RuntimeError(
+ "Can't load private key from both %s and %s" %
+ (key_file, filename))
+
+ args = [
+ paths.OPENSSL, 'pkcs8',
+ '-topk8',
+ '-passout', 'file:' + db_password_filename,
+ ]
+ if ((label != 'PRIVATE KEY' and key_password) or
+ label == 'ENCRYPTED PRIVATE KEY'):
+ key_pwdfile = ipautil.write_tmp_file(key_password)
+ args += [
+ '-passin', 'file:' + key_pwdfile.name,
+ ]
+ try:
+ stdout, stderr, rc = ipautil.run(args, stdin=body)
+ except ipautil.CalledProcessError as e:
+ root_logger.warning(
+ "Skipping private key in %s at line %s: %s",
+ filename, line, e)
+ continue
+ else:
+ extracted_key = stdout
+ key_file = filename
+ loaded = True
+ continue
+ if loaded:
+ continue
+ raise RuntimeError("Failed to load %s" % filename)
+
+ # Try to load the file as DER certificate
+ try:
+ x509.load_certificate(data, x509.DER)
+ except NSPRError:
+ pass
+ else:
+ data = x509.make_pem(base64.b64encode(data))
+ extracted_certs += data + '\n'
+ continue
+
+ # Try to import the file as PKCS#12 file
+ if import_keys:
+ try:
+ self.import_pkcs12(
+ filename, db_password_filename, key_password)
+ except RuntimeError:
+ pass
+ else:
+ if key_file:
+ raise RuntimeError(
+ "Can't load private key from both %s and %s" %
+ (key_file, filename))
+ key_file = filename
+
+ server_certs = self.find_server_certs()
+ if key_nickname:
+ for nickname, trust_flags in server_certs:
+ if nickname == key_nickname:
+ break
+ else:
+ raise RuntimeError(
+ "Server certificate \"%s\" not found in %s" %
+ (key_nickname, filename))
+ else:
+ if len(server_certs) > 1:
+ raise RuntimeError(
+ "%s server certificates found in %s, "
+ "expecting only one" %
+ (len(server_certs), filename))
+
+ continue
+
+ raise RuntimeError("Failed to load %s" % filename)
+
+ if import_keys and not key_file:
+ raise RuntimeError(
+ "No server certificates found in %s" % (', '.join(files)))
+
+ nss_certs = x509.load_certificate_list(extracted_certs)
+ nss_cert = None
+ for nss_cert in nss_certs:
+ nickname = str(nss_cert.subject)
+ self.add_cert(nss_cert.der_data, nickname, ',,')
+ del nss_certs, nss_cert
+
+ if extracted_key:
+ in_file = ipautil.write_tmp_file(extracted_certs + extracted_key)
+ out_file = tempfile.NamedTemporaryFile()
+ out_password = ipautil.ipa_generate_password()
+ out_pwdfile = ipautil.write_tmp_file(out_password)
+ args = [
+ paths.OPENSSL, 'pkcs12',
+ '-export',
+ '-in', in_file.name,
+ '-out', out_file.name,
+ '-passin', 'file:' + db_password_filename,
+ '-passout', 'file:' + out_pwdfile.name,
+ ]
+ try:
+ ipautil.run(args)
+ except ipautil.CalledProcessError as e:
+ raise RuntimeError(
+ "No matching certificate found for private key from %s" %
+ key_file)
+
+ self.import_pkcs12(out_file.name, db_password_filename,
+ out_password)
+
+ def trust_root_cert(self, root_nickname, trust_flags=None):
+ if root_nickname[:7] == "Builtin":
+ root_logger.debug(
+ "No need to add trust for built-in root CAs, skipping %s" %
+ root_nickname)
+ else:
+ if trust_flags is None:
+ trust_flags = 'C,,'
+ try:
+ self.run_certutil(["-M", "-n", root_nickname,
+ "-t", trust_flags])
+ except ipautil.CalledProcessError, e:
+ raise RuntimeError(
+ "Setting trust on %s failed" % root_nickname)
+
+ def get_cert(self, nickname, pem=False):
+ args = ['-L', '-n', nickname]
+ if pem:
+ args.append('-a')
+ else:
+ args.append('-r')
+ try:
+ cert, err, returncode = self.run_certutil(args)
+ except ipautil.CalledProcessError:
+ raise RuntimeError("Failed to get %s" % nickname)
+ return cert
+
+ def export_pem_cert(self, nickname, location):
+ """Export the given cert to PEM file in the given location"""
+ cert = self.get_cert(nickname)
+ with open(location, "w+") as fd:
+ fd.write(cert)
+ os.chmod(location, 0444)
+
+ def import_pem_cert(self, nickname, flags, location):
+ """Import a cert form the given PEM file.
+
+ The file must contain exactly one certificate.
+ """
+ try:
+ with open(location) as fd:
+ certs = fd.read()
+ except IOError as e:
+ raise RuntimeError(
+ "Failed to open %s: %s" % (location, e.strerror)
+ )
+
+ cert, st = find_cert_from_txt(certs)
+ self.add_cert(cert, nickname, flags, pem=True)
+
+ try:
+ find_cert_from_txt(certs, st)
+ except RuntimeError:
+ pass
+ else:
+ raise ValueError('%s contains more than one certificate' %
+ location)
+
+ def add_cert(self, cert, nick, flags, pem=False):
+ args = ["-A", "-n", nick, "-t", flags]
+ if pem:
+ args.append("-a")
+ self.run_certutil(args, stdin=cert)
+
+ def delete_cert(self, nick):
+ self.run_certutil(["-D", "-n", nick])
+
+ def verify_server_cert_validity(self, nickname, hostname):
+ """Verify a certificate is valid for a SSL server with given hostname
+
+ Raises a ValueError if the certificate is invalid.
+ """
+ certdb = cert = None
+ if nss.nss_is_initialized():
+ nss.nss_shutdown()
+ nss.nss_init(self.secdir)
+ try:
+ certdb = nss.get_default_certdb()
+ cert = nss.find_cert_from_nickname(nickname)
+ intended_usage = nss.certificateUsageSSLServer
+ try:
+ approved_usage = cert.verify_now(certdb, True, intended_usage)
+ except NSPRError, e:
+ if e.errno != -8102:
+ raise ValueError(e.strerror)
+ approved_usage = 0
+ if not approved_usage & intended_usage:
+ raise ValueError('invalid for a SSL server')
+ if not cert.verify_hostname(hostname):
+ raise ValueError('invalid for server %s' % hostname)
+ finally:
+ del certdb, cert
+ nss.nss_shutdown()
+
+ return None
+
+ def verify_ca_cert_validity(self, nickname):
+ certdb = cert = None
+ if nss.nss_is_initialized():
+ nss.nss_shutdown()
+ nss.nss_init(self.secdir)
+ try:
+ certdb = nss.get_default_certdb()
+ cert = nss.find_cert_from_nickname(nickname)
+ if not cert.subject:
+ raise ValueError("has empty subject")
+ if not cert.is_ca_cert():
+ raise ValueError("not a CA certificate")
+ intended_usage = nss.certificateUsageSSLCA
+ try:
+ approved_usage = cert.verify_now(certdb, True, intended_usage)
+ except NSPRError, e:
+ if e.errno != -8102: # SEC_ERROR_INADEQUATE_KEY_USAGE
+ raise ValueError(e.strerror)
+ approved_usage = 0
+ if approved_usage & intended_usage != intended_usage:
+ raise ValueError('invalid for a CA')
+ finally:
+ del certdb, cert
+ nss.nss_shutdown()
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index 55feb65..5399a0f 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -19,7 +19,6 @@
import os
import stat
-import re
import sys
import tempfile
import shutil
@@ -28,15 +27,12 @@ import pwd
import base64
from hashlib import sha1
-from nss import nss
-from nss.error import NSPRError
-
from ipapython.ipa_log_manager import root_logger
from ipapython import dogtag
from ipapython import sysrestore
from ipapython import ipautil
from ipapython import certmonger
-from ipapython.certdb import get_ca_nickname
+from ipapython.certdb import get_ca_nickname, find_cert_from_txt, NSSDatabase
from ipapython.dn import DN
from ipalib import pkcs10, x509, api
from ipalib.errors import CertificateOperationError
@@ -48,23 +44,6 @@ from ipaplatform.paths import paths
# where apache can reach
NSS_DIR = paths.HTTPD_ALIAS_DIR
-def find_cert_from_txt(cert, start=0):
- """
- Given a cert blob (str) which may or may not contian leading and
- trailing text, pull out just the certificate part. This will return
- the FIRST cert in a stream of data.
-
- Returns a tuple (certificate, last position in cert)
- """
- s = cert.find('-----BEGIN CERTIFICATE-----', start)
- e = cert.find('-----END CERTIFICATE-----', s)
- if e > 0: e = e + 25
-
- if s < 0 or e < 0:
- raise RuntimeError("Unable to find certificate")
-
- cert = cert[s:e]
- return (cert, e)
def get_cert_nickname(cert):
"""
@@ -83,431 +62,6 @@ def get_cert_nickname(cert):
return (str(dn[0]), dn)
-class NSSDatabase(object):
- """A general-purpose wrapper around a NSS cert database
-
- For permanent NSS databases, pass the cert DB directory to __init__
-
- For temporary databases, do not pass nssdir, and call close() when done
- to remove the DB. Alternatively, a NSSDatabase can be used as a
- context manager that calls close() automatically.
- """
- # Traditionally, we used CertDB for our NSS DB operations, but that class
- # got too tied to IPA server details, killing reusability.
- # BaseCertDB is a class that knows nothing about IPA.
- # Generic NSS DB code should be moved here.
- def __init__(self, nssdir=None):
- if nssdir is None:
- self.secdir = tempfile.mkdtemp()
- self._is_temporary = True
- else:
- self.secdir = nssdir
- self._is_temporary = False
-
- def close(self):
- if self._is_temporary:
- shutil.rmtree(self.secdir)
-
- def __enter__(self):
- return self
-
- def __exit__(self, type, value, tb):
- self.close()
-
- def run_certutil(self, args, stdin=None):
- new_args = [paths.CERTUTIL, "-d", self.secdir]
- new_args = new_args + args
- return ipautil.run(new_args, stdin)
-
- def create_db(self, password_filename):
- """Create cert DB
-
- :param password_filename: Name of file containing the database password
- """
- self.run_certutil(["-N", "-f", password_filename])
-
- def list_certs(self):
- """Return nicknames and cert flags for all certs in the database
-
- :return: List of (name, trust_flags) tuples
- """
- certs, stderr, returncode = self.run_certutil(["-L"])
- certs = certs.splitlines()
-
- # FIXME, this relies on NSS never changing the formatting of certutil
- certlist = []
- for cert in certs:
- match = re.match(r'^(.+?)\s+(\w*,\w*,\w*)\s*$', cert)
- if match:
- certlist.append(match.groups())
-
- return tuple(certlist)
-
- def find_server_certs(self):
- """Return nicknames and cert flags for server certs in the database
-
- Server certs have an "u" character in the trust flags.
-
- :return: List of (name, trust_flags) tuples
- """
- server_certs = []
- for name, flags in self.list_certs():
- if 'u' in flags:
- server_certs.append((name, flags))
-
- return server_certs
-
- def get_trust_chain(self, nickname):
- """Return names of certs in a given cert's trust chain
-
- :param nickname: Name of the cert
- :return: List of certificate names
- """
- root_nicknames = []
- chain, stderr, returncode = self.run_certutil([
- "-O", "-n", nickname])
- chain = chain.splitlines()
-
- for c in chain:
- m = re.match('\s*"(.*)" \[.*', c)
- if m:
- root_nicknames.append(m.groups()[0])
-
- return root_nicknames
-
- def import_pkcs12(self, pkcs12_filename, db_password_filename,
- pkcs12_passwd=None):
- args = [paths.PK12UTIL, "-d", self.secdir,
- "-i", pkcs12_filename,
- "-k", db_password_filename, '-v']
- if pkcs12_passwd is not None:
- pkcs12_passwd = pkcs12_passwd + '\n'
- args = args + ["-w", paths.DEV_STDIN]
- try:
- ipautil.run(args, stdin=pkcs12_passwd)
- except ipautil.CalledProcessError, e:
- if e.returncode == 17:
- raise RuntimeError("incorrect password for pkcs#12 file %s" %
- pkcs12_filename)
- elif e.returncode == 10:
- raise RuntimeError("Failed to open %s" % pkcs12_filename)
- else:
- raise RuntimeError("unknown error import pkcs#12 file %s" %
- pkcs12_filename)
-
- def import_files(self, files, db_password_filename, import_keys=False,
- key_password=None, key_nickname=None):
- """
- Import certificates and a single private key from multiple files
-
- The files may be in PEM and DER certificate, PKCS#7 certificate chain,
- PKCS#8 and raw private key and PKCS#12 formats.
-
- :param files: Names of files to import
- :param db_password_filename: Name of file containing the database
- password
- :param import_keys: Whether to import private keys
- :param key_password: Password to decrypt private keys
- :param key_nickname: Nickname of the private key to import from PKCS#12
- files
- """
- key_file = None
- extracted_key = None
- extracted_certs = ''
-
- for filename in files:
- try:
- with open(filename, 'rb') as f:
- data = f.read()
- except IOError as e:
- raise RuntimeError(
- "Failed to open %s: %s" % (filename, e.strerror))
-
- # Try to parse the file as PEM file
- matches = list(re.finditer(
- r'-----BEGIN (.+?)-----(.*?)-----END \1-----', data, re.DOTALL))
- if matches:
- loaded = False
- for match in matches:
- body = match.group()
- label = match.group(1)
- line = len(data[:match.start() + 1].splitlines())
-
- if label in ('CERTIFICATE', 'X509 CERTIFICATE',
- 'X.509 CERTIFICATE'):
- try:
- x509.load_certificate(match.group(2))
- except NSPRError as e:
- if label != 'CERTIFICATE':
- root_logger.warning(
- "Skipping certificate in %s at line %s: %s",
- filename, line, e)
- continue
- else:
- extracted_certs += body + '\n'
- loaded = True
- continue
-
- if label in ('PKCS7', 'PKCS #7 SIGNED DATA', 'CERTIFICATE'):
- args = [
- paths.OPENSSL, 'pkcs7',
- '-print_certs',
- ]
- try:
- stdout, stderr, rc = ipautil.run(args, stdin=body)
- except ipautil.CalledProcessError as e:
- if label == 'CERTIFICATE':
- root_logger.warning(
- "Skipping certificate in %s at line %s: %s",
- filename, line, e)
- else:
- root_logger.warning(
- "Skipping PKCS#7 in %s at line %s: %s",
- filename, line, e)
- continue
- else:
- extracted_certs += stdout + '\n'
- loaded = True
- continue
-
- if label in ('PRIVATE KEY', 'ENCRYPTED PRIVATE KEY',
- 'RSA PRIVATE KEY', 'DSA PRIVATE KEY',
- 'EC PRIVATE KEY'):
- if not import_keys:
- continue
-
- if key_file:
- raise RuntimeError(
- "Can't load private key from both %s and %s" %
- (key_file, filename))
-
- args = [
- paths.OPENSSL, 'pkcs8',
- '-topk8',
- '-passout', 'file:' + db_password_filename,
- ]
- if ((label != 'PRIVATE KEY' and key_password) or
- label == 'ENCRYPTED PRIVATE KEY'):
- key_pwdfile = ipautil.write_tmp_file(key_password)
- args += [
- '-passin', 'file:' + key_pwdfile.name,
- ]
- try:
- stdout, stderr, rc = ipautil.run(args, stdin=body)
- except ipautil.CalledProcessError as e:
- root_logger.warning(
- "Skipping private key in %s at line %s: %s",
- filename, line, e)
- continue
- else:
- extracted_key = stdout
- key_file = filename
- loaded = True
- continue
- if loaded:
- continue
- raise RuntimeError("Failed to load %s" % filename)
-
- # Try to load the file as DER certificate
- try:
- x509.load_certificate(data, x509.DER)
- except NSPRError:
- pass
- else:
- data = x509.make_pem(base64.b64encode(data))
- extracted_certs += data + '\n'
- continue
-
- # Try to import the file as PKCS#12 file
- if import_keys:
- try:
- self.import_pkcs12(
- filename, db_password_filename, key_password)
- except RuntimeError:
- pass
- else:
- if key_file:
- raise RuntimeError(
- "Can't load private key from both %s and %s" %
- (key_file, filename))
- key_file = filename
-
- server_certs = self.find_server_certs()
- if key_nickname:
- for nickname, trust_flags in server_certs:
- if nickname == key_nickname:
- break
- else:
- raise RuntimeError(
- "Server certificate \"%s\" not found in %s" %
- (key_nickname, filename))
- else:
- if len(server_certs) > 1:
- raise RuntimeError(
- "%s server certificates found in %s, "
- "expecting only one" %
- (len(server_certs), filename))
-
- continue
-
- raise RuntimeError("Failed to load %s" % filename)
-
- if import_keys and not key_file:
- raise RuntimeError(
- "No server certificates found in %s" % (', '.join(files)))
-
- nss_certs = x509.load_certificate_list(extracted_certs)
- nss_cert = None
- for nss_cert in nss_certs:
- nickname = str(nss_cert.subject)
- self.add_cert(nss_cert.der_data, nickname, ',,')
- del nss_certs, nss_cert
-
- if extracted_key:
- in_file = ipautil.write_tmp_file(extracted_certs + extracted_key)
- out_file = tempfile.NamedTemporaryFile()
- out_password = ipautil.ipa_generate_password()
- out_pwdfile = ipautil.write_tmp_file(out_password)
- args = [
- paths.OPENSSL, 'pkcs12',
- '-export',
- '-in', in_file.name,
- '-out', out_file.name,
- '-passin', 'file:' + db_password_filename,
- '-passout', 'file:' + out_pwdfile.name,
- ]
- try:
- ipautil.run(args)
- except ipautil.CalledProcessError as e:
- raise RuntimeError(
- "No matching certificate found for private key from %s" %
- key_file)
-
- self.import_pkcs12(out_file.name, db_password_filename,
- out_password)
-
- def trust_root_cert(self, root_nickname, trust_flags=None):
- if root_nickname[:7] == "Builtin":
- root_logger.debug(
- "No need to add trust for built-in root CAs, skipping %s" %
- root_nickname)
- else:
- if trust_flags is None:
- trust_flags = 'C,,'
- try:
- self.run_certutil(["-M", "-n", root_nickname,
- "-t", trust_flags])
- except ipautil.CalledProcessError, e:
- raise RuntimeError(
- "Setting trust on %s failed" % root_nickname)
-
- def get_cert(self, nickname, pem=False):
- args = ['-L', '-n', nickname]
- if pem:
- args.append('-a')
- else:
- args.append('-r')
- try:
- cert, err, returncode = self.run_certutil(args)
- except ipautil.CalledProcessError:
- raise RuntimeError("Failed to get %s" % nickname)
- return cert
-
- def export_pem_cert(self, nickname, location):
- """Export the given cert to PEM file in the given location"""
- cert = self.get_cert(nickname)
- with open(location, "w+") as fd:
- fd.write(cert)
- os.chmod(location, 0444)
-
- def import_pem_cert(self, nickname, flags, location):
- """Import a cert form the given PEM file.
-
- The file must contain exactly one certificate.
- """
- try:
- with open(location) as fd:
- certs = fd.read()
- except IOError as e:
- raise RuntimeError(
- "Failed to open %s: %s" % (location, e.strerror)
- )
-
- cert, st = find_cert_from_txt(certs)
- self.add_cert(cert, nickname, flags, pem=True)
-
- try:
- find_cert_from_txt(certs, st)
- except RuntimeError:
- pass
- else:
- raise ValueError('%s contains more than one certificate' %
- location)
-
- def add_cert(self, cert, nick, flags, pem=False):
- args = ["-A", "-n", nick, "-t", flags]
- if pem:
- args.append("-a")
- self.run_certutil(args, stdin=cert)
-
- def delete_cert(self, nick):
- self.run_certutil(["-D", "-n", nick])
-
- def verify_server_cert_validity(self, nickname, hostname):
- """Verify a certificate is valid for a SSL server with given hostname
-
- Raises a ValueError if the certificate is invalid.
- """
- certdb = cert = None
- if nss.nss_is_initialized():
- nss.nss_shutdown()
- nss.nss_init(self.secdir)
- try:
- certdb = nss.get_default_certdb()
- cert = nss.find_cert_from_nickname(nickname)
- intended_usage = nss.certificateUsageSSLServer
- try:
- approved_usage = cert.verify_now(certdb, True, intended_usage)
- except NSPRError, e:
- if e.errno != -8102:
- raise ValueError(e.strerror)
- approved_usage = 0
- if not approved_usage & intended_usage:
- raise ValueError('invalid for a SSL server')
- if not cert.verify_hostname(hostname):
- raise ValueError('invalid for server %s' % hostname)
- finally:
- del certdb, cert
- nss.nss_shutdown()
-
- return None
-
- def verify_ca_cert_validity(self, nickname):
- certdb = cert = None
- if nss.nss_is_initialized():
- nss.nss_shutdown()
- nss.nss_init(self.secdir)
- try:
- certdb = nss.get_default_certdb()
- cert = nss.find_cert_from_nickname(nickname)
- if not cert.subject:
- raise ValueError("has empty subject")
- if not cert.is_ca_cert():
- raise ValueError("not a CA certificate")
- intended_usage = nss.certificateUsageSSLCA
- try:
- approved_usage = cert.verify_now(certdb, True, intended_usage)
- except NSPRError, e:
- if e.errno != -8102: # SEC_ERROR_INADEQUATE_KEY_USAGE
- raise ValueError(e.strerror)
- approved_usage = 0
- if approved_usage & intended_usage != intended_usage:
- raise ValueError('invalid for a CA')
- finally:
- del certdb, cert
- nss.nss_shutdown()
-
-
class CertDB(object):
"""An IPA-server-specific wrapper around NSS
--
1.9.3
>From ff2a7a7fc4ae623b26c39dc62bfb6c0f86d2c02d Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Thu, 18 Sep 2014 11:42:14 +0200
Subject: [PATCH 03/10] Add NSSDatabase.has_nickname for checking nickname
presence in a NSS DB
https://fedorahosted.org/freeipa/ticket/4416
---
ipapython/certdb.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/ipapython/certdb.py b/ipapython/certdb.py
index e190a70..792cd75 100644
--- a/ipapython/certdb.py
+++ b/ipapython/certdb.py
@@ -406,6 +406,15 @@ class NSSDatabase(object):
raise RuntimeError("Failed to get %s" % nickname)
return cert
+ def has_nickname(self, nickname):
+ try:
+ self.get_cert(nickname)
+ except RuntimeError:
+ # This might be error other than "nickname not found". Beware.
+ return False
+ else:
+ return True
+
def export_pem_cert(self, nickname, location):
"""Export the given cert to PEM file in the given location"""
cert = self.get_cert(nickname)
--
1.9.3
>From ac0084a99a1edc3dc4e1d0d65066938b3d36f5bc Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Thu, 18 Sep 2014 12:00:15 +0200
Subject: [PATCH 04/10] Use NSSDatabase instead of direct certutil calls in
client code
https://fedorahosted.org/freeipa/ticket/4416
---
ipa-client/ipa-install/ipa-client-install | 50 ++++++++-----------------------
ipa-client/ipaclient/ipa_certupdate.py | 14 ++++-----
ipapython/certdb.py | 20 ++++++-------
3 files changed, 26 insertions(+), 58 deletions(-)
diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index ab40cd8..22085ec 100755
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -226,14 +226,6 @@ def logging_setup(options):
def log_service_error(name, action, error):
root_logger.error("%s failed to %s: %s", name, action, str(error))
-def nickname_exists(nickname):
- (sout, serr, returncode) = run([paths.CERTUTIL, "-L", "-d", paths.NSS_DB_DIR, "-n", nickname], raiseonerr=False)
-
- if returncode == 0:
- return True
- else:
- return False
-
def purge_ipa_certs(additional=[]):
filename = paths.NSSDB_IPA_TXT
if file_exists(filename):
@@ -258,12 +250,11 @@ def purge_ipa_certs(additional=[]):
if nickname:
nicknames.add(nickname)
+ sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
for nickname in nicknames:
- while nickname_exists(nickname):
+ while sys_db.has_nickname(nickname):
try:
- run([paths.CERTUTIL, "-D",
- "-d", paths.NSS_DB_DIR,
- "-n", nickname])
+ sys_db.delete_cert(nickname)
except Exception, e:
root_logger.error(
"Failed to remove %s from /etc/pki/nssdb: %s", nickname, e)
@@ -2533,23 +2524,16 @@ def install(options, env, fstore, statestore):
except ValueError:
pass
- tmp_nss_dir = tempfile.mkdtemp()
- try:
+ with certdb.NSSDatabase() as tmp_db:
# 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])
+ tmp_db.create_db(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', tmp_nss_dir,
- '-n', 'CA certificate %d' % (i + 1),
- '-t', 'C,,'],
- stdin=cert)
+ tmp_db.add_cert(cert, 'CA certificate %d' % (i + 1), 'C,,')
except CalledProcessError, e:
root_logger.info("Failed to add CA to temporary NSS database.")
return CLIENT_INSTALL_ERROR
@@ -2557,7 +2541,7 @@ def install(options, env, fstore, statestore):
# Now, let's try to connect to the server's RPC interface
connected = False
try:
- api.Backend.rpcclient.connect(nss_dir=tmp_nss_dir)
+ api.Backend.rpcclient.connect(nss_dir=tmp_db.secdir)
connected = True
root_logger.debug("Try RPC connection")
api.Backend.rpcclient.forward('ping')
@@ -2569,7 +2553,7 @@ def install(options, env, fstore, statestore):
"Trying with delegate=True", e)
try:
api.Backend.rpcclient.connect(delegate=True,
- nss_dir=tmp_nss_dir)
+ nss_dir=tmp_db.secdir)
root_logger.debug("Try RPC connection")
api.Backend.rpcclient.forward('ping')
@@ -2594,8 +2578,6 @@ def install(options, env, fstore, statestore):
root_logger.error(
"Cannot connect to the server due to generic error: %s", 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(
@@ -2622,14 +2604,10 @@ def install(options, env, fstore, statestore):
# Add the CA certificates to the IPA NSS database
root_logger.debug("Adding CA certificates to the IPA NSS database.")
+ ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR)
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)
+ ipa_db.add_cert(cert, nickname, trust_flags)
except CalledProcessError, e:
root_logger.error(
"Failed to add %s to the IPA NSS database.", nickname)
@@ -2653,14 +2631,10 @@ def install(options, env, fstore, statestore):
root_logger.debug(
"Attempting to add CA certificates to the default NSS database.")
+ sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
for cert, nickname, trust_flags in ca_certs_trust:
try:
- run([paths.CERTUTIL,
- "-A",
- "-d", paths.NSS_DB_DIR,
- "-n", nickname,
- "-t", trust_flags],
- stdin=cert)
+ sys_db.add_cert(cert, nickname, trust_flags)
except CalledProcessError, e:
root_logger.error(
"Failed to add %s to the default NSS database.", nickname)
diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index 57dbf20..f7b0e29 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -22,7 +22,7 @@ import tempfile
import shutil
from ipapython import (admintool, ipautil, ipaldap, sysrestore, dogtag,
- certmonger)
+ certmonger, certdb)
from ipaplatform import services
from ipaplatform.paths import paths
from ipaplatform.tasks import tasks
@@ -72,11 +72,10 @@ class CertUpdate(admintool.AdminTool):
self.update_file(paths.IPA_CA_CRT, certs)
self.update_db(paths.IPA_NSSDB_DIR, certs)
+ sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
for nickname in ('IPA CA', 'External CA cert'):
try:
- ipautil.run([paths.CERTUTIL, '-D',
- '-d', paths.NSS_DB_DIR,
- '-n', nickname])
+ sys_db.delete_cert(nickname)
except ipautil.CalledProcessError, e:
pass
@@ -165,15 +164,12 @@ class CertUpdate(admintool.AdminTool):
self.log.error("failed to update %s: %s", filename, e)
def update_db(self, path, certs):
+ db = certdb.NSSDatabase(path)
for cert, nickname, trusted, eku in certs:
trust_flags = certstore.key_policy_to_trust_flags(
trusted, True, eku)
try:
- ipautil.run([paths.CERTUTIL, '-A',
- '-d', path,
- '-n', nickname,
- '-t', trust_flags],
- stdin=cert)
+ db.add_cert(cert, nickname, trust_flags)
except ipautil.CalledProcessError, e:
self.log.error(
"failed to update %s in %s: %s", nickname, path, e)
diff --git a/ipapython/certdb.py b/ipapython/certdb.py
index 792cd75..09c87c7 100644
--- a/ipapython/certdb.py
+++ b/ipapython/certdb.py
@@ -36,24 +36,22 @@ def get_ca_nickname(realm, format=CA_NICKNAME_FMT):
def create_ipa_nssdb():
- pwdfile = os.path.join(paths.IPA_NSSDB_DIR, 'pwdfile.txt')
+ db = NSSDatabase(paths.IPA_NSSDB_DIR)
+ pwdfile = os.path.join(db.secdir, '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'))
+ ipautil.backup_file(os.path.join(db.secdir, 'cert8.db'))
+ ipautil.backup_file(os.path.join(db.secdir, 'key3.db'))
+ ipautil.backup_file(os.path.join(db.secdir, '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)
+ db.create_db(pwdfile)
+ os.chmod(os.path.join(db.secdir, 'cert8.db'), 0644)
+ os.chmod(os.path.join(db.secdir, 'key3.db'), 0644)
+ os.chmod(os.path.join(db.secdir, 'secmod.db'), 0644)
def find_cert_from_txt(cert, start=0):
--
1.9.3
>From d546ad0bc0b8c7836d15db9a35a60ed6751840b7 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Mon, 22 Sep 2014 11:13:15 +0200
Subject: [PATCH 05/10] Use /etc/ipa/nssdb to get nicknames of IPA certs
installed in /etc/pki/nssdb
Previously a list of nicknames was kept in /etc/pki/nssdb/ipa.txt. The file
is removed now.
https://fedorahosted.org/freeipa/ticket/3259
---
ipa-client/ipa-install/ipa-client-install | 78 +++++++++----------------------
ipa-client/ipaclient/ipa_certupdate.py | 59 +++++++++--------------
ipaplatform/base/paths.py | 1 -
3 files changed, 42 insertions(+), 96 deletions(-)
diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index 22085ec..2e59df9 100755
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -226,41 +226,6 @@ def logging_setup(options):
def log_service_error(name, action, error):
root_logger.error("%s failed to %s: %s", name, action, str(error))
-def purge_ipa_certs(additional=[]):
- filename = paths.NSSDB_IPA_TXT
- if file_exists(filename):
- try:
- with open(filename, 'r') as f:
- lines = f.readlines()
- except IOError, e:
- root_logger.error("Failed to open %s: %s", filename, e)
- return False
- finally:
- try:
- os.unlink(filename)
- except OSError, e:
- root_logger.error("Failed to remove %s: %s", filename, e)
- return False
- else:
- lines = []
-
- nicknames = set(additional)
- for line in lines:
- nickname = line.strip()
- if nickname:
- nicknames.add(nickname)
-
- sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
- for nickname in nicknames:
- while sys_db.has_nickname(nickname):
- try:
- sys_db.delete_cert(nickname)
- except Exception, e:
- root_logger.error(
- "Failed to remove %s from /etc/pki/nssdb: %s", nickname, e)
-
- return True
-
def cert_summary(msg, certs, indent=' '):
if msg:
s = '%s\n' % msg
@@ -541,16 +506,32 @@ 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')):
+ ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR)
+ try:
+ ipa_certs = ipa_db.list_certs()
+ except CalledProcessError, e:
+ root_logger.error(
+ "Failed to list certificates in %s: %s", ipa_db.secdir, e)
+ ipa_certs = []
+
+ for filename in (os.path.join(ipa_db.secdir, 'cert8.db'),
+ os.path.join(ipa_db.secdir, 'key3.db'),
+ os.path.join(ipa_db.secdir, 'secmod.db'),
+ os.path.join(ipa_db.secdir, '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'})
+ sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
+ for nickname, trust_flags in ipa_certs:
+ while sys_db.has_nickname(nickname):
+ try:
+ sys_db.delete_cert(nickname)
+ except Exception, e:
+ root_logger.error("Failed to remove %s from %s: %s",
+ nickname, sys_db.secdir, e)
+ break
try:
cmonger.stop()
@@ -2617,18 +2598,6 @@ def install(options, env, fstore, statestore):
tasks.insert_ca_certs_into_systemwide_ca_store(ca_certs)
# 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 "
- "database.")
- return CLIENT_INSTALL_ERROR
-
- try:
- list_file = open(paths.NSSDB_IPA_TXT, 'w')
- except IOError, e:
- root_logger.error("Failed to open /etc/pki/nssdb/ipa.txt: %s", e)
- return CLIENT_INSTALL_ERROR
-
root_logger.debug(
"Attempting to add CA certificates to the default NSS database.")
sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
@@ -2638,14 +2607,9 @@ def install(options, env, fstore, statestore):
except CalledProcessError, e:
root_logger.error(
"Failed to add %s to the default NSS database.", nickname)
- list_file.close()
return CLIENT_INSTALL_ERROR
- else:
- list_file.write(nickname + '\n')
root_logger.info("Added CA certificates to the default NSS database.")
- list_file.close()
-
if not options.on_master:
client_dns(cli_server[0], hostname, options.dns_updates)
diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index f7b0e29..8259755 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -70,49 +70,32 @@ 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)
+ ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR)
sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
- for nickname in ('IPA CA', 'External CA cert'):
- try:
- sys_db.delete_cert(nickname)
- except ipautil.CalledProcessError, e:
- pass
-
- self.update_db(paths.NSS_DB_DIR, certs)
- new_nicknames = set(c[1] for c in certs)
- old_nicknames = set()
- if ipautil.file_exists(paths.NSSDB_IPA_TXT):
- try:
- list_file = open(paths.NSSDB_IPA_TXT, 'r')
- except IOError, e:
- self.log.error("failed to open %s: %s", paths.NSSDB_IPA_TXT, e)
- else:
+ # Remove IPA certs from /etc/pki/nssdb
+ for nickname, trust_flags in ipa_db.list_certs():
+ while sys_db.has_nickname(nickname):
try:
- lines = list_file.readlines()
- except IOError, e:
- self.log.error(
- "failed to read %s: %s", paths.NSSDB_IPA_TXT, e)
- else:
- for line in lines:
- nickname = line.strip()
- if nickname:
- old_nicknames.add(nickname)
- list_file.close()
- if new_nicknames != old_nicknames:
- try:
- list_file = open(paths.NSSDB_IPA_TXT, 'w')
- except IOError, e:
- self.log.error("failed to open %s: %s", paths.NSSDB_IPA_TXT, e)
- else:
+ sys_db.delete_cert(nickname)
+ except ipautil.CalledProcessError, e:
+ self.log.error("Failed to remove %s from %s: %s",
+ nickname, sys_db.secdir, e)
+ break
+
+ # Remove old IPA certs from /etc/ipa/nssdb
+ for nickname in ('IPA CA', 'External CA cert'):
+ while ipa_db.has_nickname(nickname):
try:
- for nickname in new_nicknames:
- list_file.write(nickname + '\n')
- except IOError, e:
- self.log.error(
- "failed to write %s: %s", paths.NSSDB_IPA_TXT, e)
- list_file.close()
+ ipa_db.delete_cert(nickname)
+ except ipautil.CalledProcessError, e:
+ self.log.error("Failed to remove %s from %s: %s",
+ nickname, ipa_db.secdir, e)
+ break
+
+ self.update_db(ipa_db.secdir, certs)
+ self.update_db(sys_db.secdir, certs)
tasks.remove_ca_certs_from_systemwide_ca_store()
tasks.insert_ca_certs_into_systemwide_ca_store(certs)
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 1493918..5f73d85 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -85,7 +85,6 @@ class BasePathNamespace(object):
NSSDB_CERT8_DB = "/etc/pki/nssdb/cert8.db"
NSSDB_KEY3_DB = "/etc/pki/nssdb/key3.db"
NSSDB_SECMOD_DB = "/etc/pki/nssdb/secmod.db"
- NSSDB_IPA_TXT = "/etc/pki/nssdb/ipa.txt"
PKI_TOMCAT = "/etc/pki/pki-tomcat"
PKI_TOMCAT_ALIAS_DIR = "/etc/pki/pki-tomcat/alias/"
PKI_TOMCAT_PASSWORD_CONF = "/etc/pki/pki-tomcat/password.conf"
--
1.9.3
>From 1e575f872f4b6d5f3ceba524323c8f92f2739fe2 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Thu, 18 Sep 2014 10:52:56 +0200
Subject: [PATCH 06/10] Check if IPA client is configured in ipa-certupdate
https://fedorahosted.org/freeipa/ticket/4460
---
ipa-client/ipaclient/ipa_certupdate.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index 8259755..c25dcae 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -41,6 +41,12 @@ class CertUpdate(admintool.AdminTool):
super(CertUpdate, self).validate_options(needs_root=True)
def run(self):
+ fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
+ if (not fstore.has_files() and
+ not os.path.exists(paths.IPA_DEFAULT_CONF)):
+ raise admintool.ScriptError(
+ "IPA client is not configured on this system.")
+
api.bootstrap(context='cli_installer')
api.finalize()
--
1.9.3
>From 0ed24b6d72561af1cfa88c961a892da798c64692 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Thu, 18 Sep 2014 11:15:49 +0200
Subject: [PATCH 07/10] Get server hostname from jsonrpc_uri in ipa-certupdate
https://fedorahosted.org/freeipa/ticket/3259
---
ipa-client/ipaclient/ipa_certupdate.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index c25dcae..fd6c80d 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -20,6 +20,7 @@
import os
import tempfile
import shutil
+import urlparse
from ipapython import (admintool, ipautil, ipaldap, sysrestore, dogtag,
certmonger, certdb)
@@ -50,10 +51,7 @@ class CertUpdate(admintool.AdminTool):
api.bootstrap(context='cli_installer')
api.finalize()
- try:
- server = api.env.server
- except AttributeError:
- server = api.env.host
+ server = urlparse.urlsplit(api.env.jsonrpc_uri).hostname
ldap = ipaldap.IPAdmin(server)
tmpdir = tempfile.mkdtemp(prefix="tmp-")
--
1.9.3
>From 9e7d783924d242bf9d88a663188b4d1a85599660 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Wed, 17 Sep 2014 15:04:11 +0200
Subject: [PATCH 08/10] Remove ipa-ca.crt from systemwide CA store on client
uninstall and cert update
The file was used by previous versions of IPA to provide the IPA CA certificate
to p11-kit and has since been obsoleted by ipa.p11-kit, a file which contains
all the CA certificates and associated trust policy from the LDAP certificate
store.
Since p11-kit is hooked into /etc/httpd/alias, ipa-ca.crt must be removed to
prevent certificate import failures in installer code.
Also add ipa.p11-kit to the files owned by the freeipa-python package.
https://fedorahosted.org/freeipa/ticket/3259
---
freeipa.spec.in | 1 +
ipaplatform/base/paths.py | 1 +
ipaplatform/fedora/tasks.py | 38 ++++++++++++++++++++++++++++----------
3 files changed, 30 insertions(+), 10 deletions(-)
diff --git a/freeipa.spec.in b/freeipa.spec.in
index d16a865..99cd6df 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -812,6 +812,7 @@ fi
%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/key3.db
%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/secmod.db
%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/pwdfile.txt
+%ghost %config(noreplace) %{_sysconfdir}/pki/ca-trust/source/ipa.p11-kit
%if ! %{ONLY_CLIENT}
%files tests -f tests-python.list
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 5f73d85..baaa109 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -80,6 +80,7 @@ class BasePathNamespace(object):
PAM_LDAP_CONF = "/etc/pam_ldap.conf"
PASSWD = "/etc/passwd"
ETC_PKI_CA_DIR = "/etc/pki-ca"
+ SYSTEMWIDE_CA_STORE = "/etc/pki/ca-trust/source/anchors/"
IPA_P11_KIT = "/etc/pki/ca-trust/source/ipa.p11-kit"
NSS_DB_DIR = "/etc/pki/nssdb"
NSSDB_CERT8_DB = "/etc/pki/nssdb/cert8.db"
diff --git a/ipaplatform/fedora/tasks.py b/ipaplatform/fedora/tasks.py
index 9f4a76b..351f523 100644
--- a/ipaplatform/fedora/tasks.py
+++ b/ipaplatform/fedora/tasks.py
@@ -158,6 +158,16 @@ class FedoraTaskNamespace(BaseTaskNamespace):
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:
@@ -250,25 +260,33 @@ class FedoraTaskNamespace(BaseTaskNamespace):
return False
def remove_ca_certs_from_systemwide_ca_store(self):
- new_cacert_path = paths.IPA_P11_KIT
+ ipa_ca_crt = os.path.join(paths.SYSTEMWIDE_CA_STORE, 'ipa-ca.crt')
+ update = False
# Remove CA cert from systemwide store
- if os.path.exists(new_cacert_path):
+ 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)
- ipautil.run([paths.UPDATE_CA_TRUST])
except OSError, e:
- root_logger.error('Could not remove: %s, %s'
- % (new_cacert_path, str(e)))
- return False
+ 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' % str(e))
+ root_logger.error(
+ "Could not update systemwide CA trust database: %s", e)
return False
else:
- root_logger.info('Systemwide CA database updated.')
+ root_logger.info("Systemwide CA database updated.")
+ return True
- return True
+ return False
def backup_and_replace_hostname(self, fstore, statestore, hostname):
old_hostname = socket.gethostname()
--
1.9.3
>From d91f8f06fb32be86c36891ab1bc6f0c222813d50 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Tue, 16 Sep 2014 10:34:57 +0200
Subject: [PATCH 09/10] Fix certmonger.wait_for_request
https://fedorahosted.org/freeipa/ticket/4558
---
ipapython/certmonger.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipapython/certmonger.py b/ipapython/certmonger.py
index 85b0e9a..bcfafda 100644
--- a/ipapython/certmonger.py
+++ b/ipapython/certmonger.py
@@ -474,7 +474,7 @@ def check_state(dirs):
def wait_for_request(request_id, timeout=120):
for i in range(0, timeout, 5):
- state = get_request_value(request_id, 'state').strip()
+ state = get_request_value(request_id, 'status')
root_logger.debug("certmonger request is in state %r", state)
if state in ('CA_REJECTED', 'CA_UNREACHABLE', 'CA_UNCONFIGURED',
'NEED_GUIDANCE', 'NEED_CA', 'MONITORING'):
--
1.9.3
>From f94de3dfd2a63b5d314244e9545294a13a17301d Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Wed, 24 Sep 2014 19:22:59 +0200
Subject: [PATCH 10/10] Fix certmonger search for the CA cert in ipa-certupdate
and ipa-cacert-manage
The search criteria did not include the CA agent name.
https://fedorahosted.org/freeipa/ticket/3259
---
ipa-client/ipaclient/ipa_certupdate.py | 1 +
ipaserver/install/ipa_cacert_manage.py | 7 +++++--
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index fd6c80d..ff16b9b 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -120,6 +120,7 @@ class CertUpdate(admintool.AdminTool):
criteria = {
'cert-database': dogtag_constants.ALIAS_DIR,
'cert-nickname': nickname,
+ 'ca-name': 'dogtag-ipa-ca-renew-agent',
}
request_id = certmonger.get_request_id(criteria)
if request_id is not None:
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
index 1acc623..a521e39 100644
--- a/ipaserver/install/ipa_cacert_manage.py
+++ b/ipaserver/install/ipa_cacert_manage.py
@@ -149,8 +149,11 @@ class CACertManage(admintool.AdminTool):
raise admintool.ScriptError("CA is not configured on this system")
nss_dir = ca.dogtag_constants.ALIAS_DIR
- criteria = {'cert-database': nss_dir,
- 'cert-nickname': self.cert_nickname}
+ criteria = {
+ 'cert-database': nss_dir,
+ 'cert-nickname': self.cert_nickname,
+ 'ca-name': 'dogtag-ipa-ca-renew-agent',
+ }
self.request_id = certmonger.get_request_id(criteria)
if self.request_id is None:
raise admintool.ScriptError(
--
1.9.3
>From 25a8f874aad6a3d4398ccbdf48865a6312bf31dd Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Tue, 30 Sep 2014 09:47:23 +0200
Subject: [PATCH] Do not crash in CAInstance.__init__ when default argument
values are used
https://fedorahosted.org/freeipa/ticket/3872
---
ipaserver/install/cainstance.py | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 26c6037..3a296f5 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -352,9 +352,15 @@ class CAInstance(DogtagInstance):
self.cert_chain_file = None
self.create_ra_agent_db = True
- self.canickname = get_ca_nickname(realm)
+ if realm is not None:
+ self.canickname = get_ca_nickname(realm)
+ else:
+ self.canickname = None
self.ra_agent_db = ra_db
- self.ra_agent_pwd = self.ra_agent_db + "/pwdfile.txt"
+ if self.ra_agent_db is not None:
+ self.ra_agent_pwd = self.ra_agent_db + "/pwdfile.txt"
+ else:
+ self.ra_agent_pwd = None
self.ra_cert = None
self.requestId = None
self.tracking_reqs = (('Server-Cert cert-pki-ca', None),
--
1.9.3
_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel