summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Viktorin <pviktori@redhat.com>2013-06-11 20:25:56 -0400
committerPetr Viktorin <pviktori@redhat.com>2013-08-28 20:15:08 +0200
commitc706859df2ae6a000d33874e4bb6bf79e9e9da52 (patch)
tree856469d66979033d8479c8c4ca4ca1b7586bde20
parentc392146101422808b8781c85f0f2720db230da28 (diff)
downloadfreeipa.git-c706859df2ae6a000d33874e4bb6bf79e9e9da52.tar.gz
freeipa.git-c706859df2ae6a000d33874e4bb6bf79e9e9da52.tar.xz
freeipa.git-c706859df2ae6a000d33874e4bb6bf79e9e9da52.zip
Add CA-less install tests
Differences from the test plan at http://www.freeipa.org/index.php?title=V3/CA-less_install&oldid=6669 are: - The following tests are included in all applicable positive install tests, rather than being standalone test cases: - Verify CA certificate stored in LDAP - Verify CA PEM file created by IPA server install - Verify that IPA server install does not configure certmonger - Verify CA PEM file created by IPA replica install - Verify that IPA replica install does not configure certmonger - Verify CA PEM file created by IPA client install - PKI setup is done only once for each test class - Master installation is done once for the IPA command tests, and once for the certinstall tests - Certificates are compared after base64 decoding to avoid failures from formatting mismatches - Minor changes necessary for automation (e.g. adding --unattended and --password options, correcting error messages) - Web UI tests are not included here https://fedorahosted.org/freeipa/ticket/3830
-rw-r--r--ipatests/setup.py.in1
-rw-r--r--ipatests/test_integration/scripts/caless-create-pki116
-rw-r--r--ipatests/test_integration/test_caless.py1364
3 files changed, 1481 insertions, 0 deletions
diff --git a/ipatests/setup.py.in b/ipatests/setup.py.in
index 3ea2729e..afbe9abc 100644
--- a/ipatests/setup.py.in
+++ b/ipatests/setup.py.in
@@ -79,6 +79,7 @@ def setup_package():
scripts=['ipa-run-tests', 'ipa-test-config', 'ipa-test-task'],
package_data = {
'ipatests.test_install': ['*.update'],
+ 'ipatests.test_integration': ['scripts/*'],
'ipatests.test_pkcs10': ['*.csr']}
)
finally:
diff --git a/ipatests/test_integration/scripts/caless-create-pki b/ipatests/test_integration/scripts/caless-create-pki
new file mode 100644
index 00000000..a0b6f13c
--- /dev/null
+++ b/ipatests/test_integration/scripts/caless-create-pki
@@ -0,0 +1,116 @@
+#!/bin/bash -e
+
+profile_ca=(-t CT,C,C -v 120)
+profile_server=(-t ,, -v 12)
+
+crl_path=${crl_path-$(readlink -f $dbdir)}
+
+gen_cert() {
+ local profile="$1" nick="$2" subject="$3" ca options pwfile noise csr crt
+ shift 3
+
+ echo "gen_cert(profile=$profile nick=$nick subject=$subject)"
+
+ ca="$(dirname $nick)"
+ if [ "$ca" = "." ]; then
+ ca="$nick"
+ fi
+
+ eval "options=(\"\${profile_$profile[@]}\")"
+ if [ "$ca" = "$nick" ]; then
+ options=("${options[@]}" -x -m 1)
+ else
+ options=("${options[@]}" -c "$ca")
+ fi
+
+ pwfile="$(mktemp)"
+ echo "$dbpassword" >"$pwfile"
+
+ noise="$(mktemp)"
+ head -c 20 /dev/urandom >"$noise"
+
+ if [ ! -d "$dbdir" ]; then
+ mkdir "$dbdir"
+ certutil -N -d "$dbdir" -f "$pwfile"
+ fi
+
+ csr="$(mktemp)"
+ crt="$(mktemp)"
+ certutil -R -d "$dbdir" -s "$subject" -f "$pwfile" -z "$noise" -o "$csr" -4 >/dev/null <<EOF
+1
+7
+file://$crl_path/$ca.crl
+-1
+-1
+-1
+n
+n
+EOF
+ certutil -C -d "$dbdir" -f "$pwfile" -m "$RANDOM" -i "$csr" -o "$crt" "${options[@]}" "$@"
+ certutil -A -d "$dbdir" -n "$nick" -f "$pwfile" -i "$crt" "${options[@]}"
+
+ rm -f "$pwfile" "$noise" "$csr" "$crt"
+}
+
+revoke_cert() {
+ local nick="$1" ca pwfile serial
+ shift 1
+
+ echo "revoke_cert(nick=$nick)"
+
+ ca="$(dirname $nick)"
+ if [ "$ca" = "." ]; then
+ ca="$nick"
+ fi
+
+ pwfile="$(mktemp)"
+ echo "$dbpassword" >"$pwfile"
+
+ if ! crlutil -L -d "$dbdir" -n "$ca" &>/dev/null; then
+ crlutil -G -d "$dbdir" -n "$ca" -c /dev/null -f "$pwfile"
+ fi
+
+ sleep 1
+
+ mkdir -p "$(dirname $dbdir/$ca.crl)"
+ serial=$(certutil -L -d "$dbdir" -n "$nick" | awk '/^\s+Serial Number: / { print $3 }')
+ crlutil -M -d "$dbdir" -n "$ca" -c /dev/stdin -f "$pwfile" -o "$dbdir/$ca.crl" <<EOF
+addcert $serial $(date -u +%Y%m%d%H%M%SZ)
+EOF
+
+ rm -f "$pwfile"
+}
+
+gen_server_certs() {
+ local nick="$1" hostname="$2" org="$3"
+ shift 3
+
+ echo "gen_server_certs(nick=$nick hostname=$hostname org=$org)"
+
+ gen_cert server "$nick" "CN=$hostname,O=$org" "$@"
+ gen_cert server "$nick-badname" "CN=not-$hostname,O=$org" "$@"
+ gen_cert server "$nick-altname" "CN=alt-$hostname,O=$org" -8 "$hostname" "$@"
+ gen_cert server "$nick-expired" "CN=$hostname,OU=Expired,O=$org" -w -24 "$@"
+ gen_cert server "$nick-badusage" "CN=$hostname,OU=Bad Usage,O=$org" --keyUsage dataEncipherment,keyAgreement "$@"
+ gen_cert server "$nick-revoked" "CN=$hostname,OU=Revoked,O=$org" "$@"
+ revoke_cert "$nick-revoked"
+}
+
+gen_subtree() {
+ local nick="$1" org="$2"
+ shift 2
+
+ echo "gen_subtree(nick=$nick org=$org)"
+
+ gen_cert ca "$nick" "CN=CA,O=$org" "$@"
+ gen_cert server "$nick/wildcard" "CN=*.$domain,O=$org"
+ gen_server_certs "$nick/server" "$server1" "$org"
+ gen_server_certs "$nick/replica" "$server2" "$org"
+ gen_server_certs "$nick/client" "$client" "$org"
+}
+
+gen_cert server server-selfsign "CN=$server1,O=Self-signed"
+gen_cert server replica-selfsign "CN=$server2,O=Self-signed"
+gen_subtree ca1 'Example Organization'
+gen_subtree ca1/subca 'Subsidiary Example Organization'
+gen_subtree ca2 'Other Example Organization'
diff --git a/ipatests/test_integration/test_caless.py b/ipatests/test_integration/test_caless.py
new file mode 100644
index 00000000..643c9c48
--- /dev/null
+++ b/ipatests/test_integration/test_caless.py
@@ -0,0 +1,1364 @@
+# Authors:
+# Petr Viktorin <pviktori@redhat.com>
+#
+# Copyright (C) 2013 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import tempfile
+import shutil
+import base64
+import glob
+import contextlib
+
+from ipalib import x509
+from ipapython import ipautil
+from ipapython.dn import DN
+from ipatests.test_integration.base import IntegrationTest
+from ipatests.test_integration import tasks
+from ipatests.order_plugin import ordered
+
+_DEFAULT = object()
+
+
+def get_install_stdin(cert_passwords=()):
+ lines = [
+ 'yes', # Existing BIND configuration detected, overwrite? [no]
+ '', # Server host name (has default)
+ '', # Confirm domain name (has default)
+ ]
+ lines.extend(cert_passwords) # Enter foo.p12 unlock password
+ lines += [
+ '', # Do you want to configure the reverse zone? [yes]
+ '', # Please specify the reverse zone name [47.34.10.in-addr.arpa.]
+ 'yes', # Continue with these values?
+ ]
+ return '\n'.join(lines + [''])
+
+
+def get_replica_prepare_stdin(cert_passwords=()):
+ lines = list(cert_passwords) # Enter foo.p12 unlock password
+ return '\n'.join(lines + [''])
+
+
+def assert_error(result, stderr_text, returncode=None):
+ "Assert that `result` command failed and its stderr contains `stderr_text`"
+ assert stderr_text in result.stderr_text, result.stderr_text
+ if returncode:
+ assert result.returncode == returncode
+ else:
+ assert result.returncode > 0
+
+
+class CALessBase(IntegrationTest):
+ @classmethod
+ def install(cls):
+ super(CALessBase, cls).install()
+ cls.cert_dir = tempfile.mkdtemp(prefix="ipatest-")
+ cls.pem_filename = os.path.join(cls.cert_dir, 'root.pem')
+ scriptfile = os.path.join(os.path.dirname(__file__),
+ 'scripts',
+ 'caless-create-pki')
+ cls.cert_password = cls.master.config.admin_password
+
+ cls.crl_path = os.path.join(cls.master.config.test_dir, 'crl')
+
+ if cls.replicas:
+ replica_hostname = cls.replicas[0].hostname
+ else:
+ replica_hostname = 'unused-replica.test'
+ if cls.clients:
+ client_hostname = cls.clients[0].hostname
+ else:
+ client_hostname = 'unused-client.test'
+ env = {
+ 'domain': cls.master.domain.name,
+ 'server1': cls.master.hostname,
+ 'server2': replica_hostname,
+ 'client': client_hostname,
+ 'dbdir': 'nssdb',
+ 'dbpassword': cls.cert_password,
+ 'crl_path': cls.crl_path,
+ }
+ ipautil.run(['bash', '-ex', scriptfile], cwd=cls.cert_dir, env=env)
+
+ for host in cls.get_all_hosts():
+ tasks.apply_common_fixes(host)
+
+ # Copy CRLs over
+ base = os.path.join(cls.cert_dir, 'nssdb')
+ host.mkdir_recursive(cls.crl_path)
+ for source in glob.glob(os.path.join(base, '*.crl')):
+ dest = os.path.join(cls.crl_path, os.path.basename(source))
+ host.put_file(source, dest)
+
+ @classmethod
+ def uninstall(cls):
+ # Remove the NSS database
+ shutil.rmtree(cls.cert_dir)
+
+ # Remove CA cert in /etc/pki/nssdb, in case of failed (un)install
+ for host in cls.get_all_hosts():
+ cls.master.run_command(['certutil', '-d', '/etc/pki/nssdb', '-D',
+ '-n', 'External CA cert'],
+ raiseonerr=False)
+
+ super(CALessBase, cls).uninstall()
+
+ @classmethod
+ def install_server(cls, host=None,
+ http_pkcs12='server.p12', dirsrv_pkcs12='server.p12',
+ http_pkcs12_exists=True, dirsrv_pkcs12_exists=True,
+ http_pin=_DEFAULT, dirsrv_pin=_DEFAULT,
+ root_ca_file='root.pem', unattended=True,
+ stdin_text=None):
+ """Install a CA-less server
+
+ Return value is the remote ipa-server-install command
+ """
+ if host is None:
+ host = cls.master
+ if http_pin is _DEFAULT:
+ http_pin = cls.cert_password
+ if dirsrv_pin is _DEFAULT:
+ dirsrv_pin = cls.cert_password
+
+ files_to_copy = ['root.pem']
+ if http_pkcs12_exists:
+ files_to_copy.append(http_pkcs12)
+ if dirsrv_pkcs12_exists:
+ files_to_copy.append(dirsrv_pkcs12)
+ for filename in set(files_to_copy):
+ cls.copy_cert(host, filename)
+
+ cls.collect_log(host, '/var/log/ipaserver-install.log')
+ cls.collect_log(host, '/var/log/ipaclient-install.log')
+ inst = host.domain.realm.replace('.', '-')
+ cls.collect_log(host, '/var/log/dirsrv/slapd-%s/errors' % inst)
+ cls.collect_log(host, '/var/log/dirsrv/slapd-%s/access' % inst)
+
+ args = [
+ 'ipa-server-install',
+ '--http_pkcs12', http_pkcs12,
+ '--dirsrv_pkcs12', dirsrv_pkcs12,
+ '--root-ca-file', root_ca_file,
+ '--ip-address', host.ip,
+ '-r', host.domain.name,
+ '-p', host.config.dirman_password,
+ '-a', host.config.admin_password,
+ '--setup-dns',
+ '--forwarder', host.config.dns_forwarder,
+ ]
+
+ if http_pin:
+ args.extend(['--http_pin', http_pin])
+ if dirsrv_pin:
+ args.extend(['--dirsrv_pin', dirsrv_pin])
+ if unattended:
+ args.extend(['-U'])
+
+ return host.run_command(args, raiseonerr=False, stdin_text=stdin_text)
+
+ @classmethod
+ def copy_cert(cls, host, filename):
+ host.put_file(os.path.join(cls.cert_dir, filename),
+ os.path.join(host.config.test_dir, filename))
+
+ @classmethod
+ def uninstall_server(self, host=None):
+ if host is None:
+ host = self.master
+ host.run_command(['ipa-server-install', '--uninstall', '-U'])
+
+ def prepare_replica(self, _replica_number=0, replica=None, master=None,
+ http_pkcs12='replica.p12', dirsrv_pkcs12='replica.p12',
+ http_pkcs12_exists=True, dirsrv_pkcs12_exists=True,
+ http_pin=_DEFAULT, dirsrv_pin=_DEFAULT,
+ root_ca_file='root.pem', unattended=True,
+ stdin_text=None):
+ """Prepare a CA-less replica
+
+ Puts the bundle file into test_dir on the replica if successful,
+ otherwise ensures it is missing.
+
+ Return value is the remote ipa-replica-prepare command
+ """
+ if replica is None:
+ replica = self.replicas[_replica_number]
+ if master is None:
+ master = self.master
+ if http_pin is _DEFAULT:
+ http_pin = self.cert_password
+ if dirsrv_pin is _DEFAULT:
+ dirsrv_pin = self.cert_password
+
+ files_to_copy = ['root.pem']
+ if http_pkcs12_exists:
+ files_to_copy.append(http_pkcs12)
+ if dirsrv_pkcs12_exists:
+ files_to_copy.append(dirsrv_pkcs12)
+ for filename in set(files_to_copy):
+ master.put_file(os.path.join(self.cert_dir, filename),
+ os.path.join(master.config.test_dir, filename))
+
+ self.collect_log(replica, '/var/log/ipareplica-install.log')
+ self.collect_log(replica, '/var/log/ipaclient-install.log')
+ inst = replica.domain.realm.replace('.', '-')
+ self.collect_log(replica, '/var/log/dirsrv/slapd-%s/errors' % inst)
+ self.collect_log(replica, '/var/log/dirsrv/slapd-%s/access' % inst)
+
+ args = [
+ 'ipa-replica-prepare',
+ '--ip-address', replica.ip,
+ '-p', replica.config.dirman_password,
+ ]
+
+ if http_pkcs12:
+ args.extend(['--http_pkcs12', http_pkcs12])
+ if dirsrv_pkcs12:
+ args.extend(['--dirsrv_pkcs12', dirsrv_pkcs12])
+ if http_pin:
+ args.extend(['--http_pin', http_pin])
+ if dirsrv_pin:
+ args.extend(['--dirsrv_pin', dirsrv_pin])
+
+ args.extend([replica.hostname])
+
+ result = master.run_command(args, raiseonerr=False,
+ stdin_text=stdin_text)
+
+ if result.returncode == 0:
+ replica_bundle = master.get_file_contents(
+ '/var/lib/ipa/replica-info-%s.gpg' % replica.hostname)
+ replica.put_file_contents(self.get_replica_filename(replica),
+ replica_bundle)
+ else:
+ replica.run_command(['rm', self.get_replica_filename(replica)],
+ raiseonerr=False)
+
+ return result
+
+ def get_replica_filename(self, replica):
+ return os.path.join(replica.config.test_dir,
+ 'replica-info.gpg')
+
+ def install_replica(self, _replica_number=0, replica=None,
+ unattended=True):
+ """Install a CA-less replica
+
+ The bundle file is expected to be in the test_dir
+
+ Return value is the remote ipa-replica-install command
+ """
+ if replica is None:
+ replica = self.replicas[_replica_number]
+
+ args = ['ipa-replica-install', '-U',
+ '-p', replica.config.dirman_password,
+ '-w', replica.config.admin_password,
+ '--ip-address', replica.ip,
+ self.get_replica_filename(replica)]
+ if unattended:
+ args.append('-U')
+ return replica.run_command(args)
+
+ @classmethod
+ def export_pkcs12(cls, nickname, filename='server.p12'):
+ """Export a cert as PKCS#12 to the given file"""
+ ipautil.run(['pk12util',
+ '-o', filename,
+ '-n', nickname,
+ '-d', 'nssdb',
+ '-K', cls.cert_password,
+ '-W', cls.cert_password], cwd=cls.cert_dir)
+
+ @classmethod
+ def get_pem(cls, nickname):
+ pem_cert, _stderr, _returncode = ipautil.run(
+ ['certutil', '-L', '-d', 'nssdb', '-n', nickname, '-a'],
+ cwd=cls.cert_dir)
+ return pem_cert
+
+ def verify_installation(self):
+ """Verify CA cert PEM file and LDAP entry created by install
+
+ Called from every positive server install test
+ """
+ with open(self.pem_filename) as f:
+ expected_cacrt = f.read()
+ self.log.debug('Expected /etc/ipa/ca.crt contents:\n%s',
+ expected_cacrt)
+ expected_binary_cacrt = base64.b64decode(x509.strip_header(
+ expected_cacrt))
+ self.log.debug('Expected binary CA cert:\n%r',
+ expected_binary_cacrt)
+ for host in [self.master] + self.replicas:
+ # Check the LDAP entry
+ ldap = host.ldap_connect()
+ entry = ldap.get_entry(DN(('cn', 'CACert'), ('cn', 'ipa'),
+ ('cn', 'etc'), host.domain.basedn))
+ cert_from_ldap = entry.single_value('cACertificate')
+ self.log.debug('CA cert from LDAP on %s:\n%r',
+ host, cert_from_ldap)
+ assert cert_from_ldap == expected_binary_cacrt
+
+ # Verify certmonger was not started
+ result = host.run_command(['getcert', 'list'], raiseonerr=False)
+ assert result > 0
+ assert ('Please verify that the certmonger service has been '
+ 'started.' in result.stdout_text), result.stdout_text
+
+ for host in self.get_all_hosts():
+ # Check the cert PEM file
+ remote_cacrt = host.get_file_contents('/etc/ipa/ca.crt')
+ self.log.debug('%s:/etc/ipa/ca.crt contents:\n%s',
+ host, remote_cacrt)
+ binary_cacrt = base64.b64decode(x509.strip_header(remote_cacrt))
+ self.log.debug('%s: Decoded /etc/ipa/ca.crt:\n%r',
+ host, binary_cacrt)
+ assert expected_binary_cacrt == binary_cacrt
+
+
+class TestServerInstall(CALessBase):
+ num_replicas = 0
+
+ def tearDown(self):
+ self.uninstall_server()
+
+ # Remove CA cert in /etc/pki/nssdb, in case of failed (un)install
+ for host in self.get_all_hosts():
+ self.master.run_command(['certutil', '-d', '/etc/pki/nssdb', '-D',
+ '-n', 'External CA cert'],
+ raiseonerr=False)
+
+ def test_nonexistent_ca_pem_file(self):
+ "IPA server install with non-existent CA PEM file "
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca2'))
+
+ result = self.install_server(root_ca_file='does_not_exist')
+ assert_error(result,
+ 'Failed to open does_not_exist: No such file '
+ 'or directory')
+
+ def test_unknown_ca(self):
+ "IPA server install with CA PEM file with unknown CA certificate"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca2'))
+
+ result = self.install_server()
+ assert_error(result,
+ 'server.p12 is not signed by root.pem, or the full '
+ 'certificate chain is not present in the PKCS#12 '
+ 'file')
+
+ def test_ca_server_cert(self):
+ "IPA server install with CA PEM file with server certificate"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1/server'))
+
+ result = self.install_server()
+ assert_error(result,
+ 'trust chain of the server certificate in server.p12 '
+ 'contains 1 certificates, expected 2')
+
+ def test_ca_2_certs(self):
+ "IPA server install with CA PEM file with 2 certificates"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+ f.write(self.get_pem('ca2'))
+
+ result = self.install_server()
+ assert_error(result, 'root.pem contains more than one certificate')
+
+ def test_nonexistent_http_pkcs12_file(self):
+ "IPA server install with non-existent HTTP PKCS#12 file"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='does_not_exist',
+ http_pkcs12_exists=False)
+ assert_error(result, 'Failed to open does_not_exist')
+
+ def test_nonexistent_ds_pkcs12_file(self):
+ "IPA server install with non-existent DS PKCS#12 file"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(dirsrv_pkcs12='does_not_exist',
+ dirsrv_pkcs12_exists=False)
+ assert_error(result, 'Failed to open does_not_exist')
+
+ def test_missing_http_password(self):
+ "IPA server install with missing HTTP PKCS#12 password (unattended)"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pin=False)
+ assert_error(result,
+ 'ipa-server-install: error: You must specify --http_pin '
+ 'with --http_pkcs12')
+
+ def test_missing_ds_password(self):
+ "IPA server install with missing DS PKCS#12 password (unattended)"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(dirsrv_pin=False)
+ assert_error(result,
+ 'ipa-server-install: error: You must specify '
+ '--dirsrv_pin with --dirsrv_pkcs12')
+
+ def test_incorect_http_pin(self):
+ "IPA server install with incorrect HTTP PKCS#12 password"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pin='bad<pin>')
+ assert_error(result, 'incorrect password for pkcs#12 file server.p12')
+
+ def test_incorect_ds_pin(self):
+ "IPA server install with incorrect DS PKCS#12 password"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(dirsrv_pin='bad<pin>')
+ assert_error(result, 'incorrect password for pkcs#12 file server.p12')
+
+ def test_invalid_http_cn(self):
+ "IPA server install with HTTP certificate with invalid CN"
+
+ self.export_pkcs12('ca1/server-badname', filename='http.p12')
+ self.export_pkcs12('ca1/server', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'The server certificate in http.p12 is not valid: '
+ 'invalid for server %s' % self.master.hostname)
+
+ def test_invalid_ds_cn(self):
+ "IPA server install with DS certificate with invalid CN"
+
+ self.export_pkcs12('ca1/server', filename='http.p12')
+ self.export_pkcs12('ca1/server-badname', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'The server certificate in dirsrv.p12 is not valid: '
+ 'invalid for server %s' % self.master.hostname)
+
+ def test_expired_http(self):
+ "IPA server install with expired HTTP certificate"
+
+ self.export_pkcs12('ca1/server-expired', filename='http.p12')
+ self.export_pkcs12('ca1/server', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'The server certificate in http.p12 is not valid: '
+ "(SEC_ERROR_EXPIRED_CERTIFICATE) Peer's Certificate has "
+ 'expired.')
+
+ def test_expired_ds(self):
+ "IPA server install with expired DS certificate"
+
+ self.export_pkcs12('ca1/server', filename='http.p12')
+ self.export_pkcs12('ca1/server-expired', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'The server certificate in dirsrv.p12 is not valid: '
+ "(SEC_ERROR_EXPIRED_CERTIFICATE) Peer's Certificate has "
+ 'expired.')
+
+ def test_http_bad_usage(self):
+ "IPA server install with HTTP certificate with invalid key usage"
+
+ self.export_pkcs12('ca1/server-badusage', filename='http.p12')
+ self.export_pkcs12('ca1/server', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'The server certificate in http.p12 is not valid: '
+ 'invalid for a SSL server')
+
+ def test_ds_bad_usage(self):
+ "IPA server install with DS certificate with invalid key usage"
+
+ self.export_pkcs12('ca1/server', filename='http.p12')
+ self.export_pkcs12('ca1/server-badusage', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'The server certificate in dirsrv.p12 is not valid: '
+ 'invalid for a SSL server')
+
+ def test_revoked_http(self):
+ "IPA server install with revoked HTTP certificate"
+
+ self.export_pkcs12('ca1/server-revoked', filename='http.p12')
+ self.export_pkcs12('ca1/server', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert result.returncode > 0
+
+ def test_revoked_ds(self):
+ "IPA server install with revoked DS certificate"
+
+ self.export_pkcs12('ca1/server', filename='http.p12')
+ self.export_pkcs12('ca1/server-revoked', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert result.returncode > 0
+
+ def test_http_intermediate_ca(self):
+ "IPA server install with HTTP certificate issued by intermediate CA"
+
+ self.export_pkcs12('ca1/subca/server', filename='http.p12')
+ self.export_pkcs12('ca1/server', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'http.p12 is not signed by root.pem, or the full '
+ 'certificate chain is not present in the PKCS#12 file')
+
+ def test_ds_intermediate_ca(self):
+ "IPA server install with DS certificate issued by intermediate CA"
+
+ self.export_pkcs12('ca1/server', filename='http.p12')
+ self.export_pkcs12('ca1/subca/server', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'dirsrv.p12 is not signed by root.pem, or the full '
+ 'certificate chain is not present in the PKCS#12 file')
+
+ def test_ca_self_signed(self):
+ "IPA server install with self-signed certificate"
+
+ self.export_pkcs12('server-selfsign')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('server-selfsign'))
+
+ result = self.install_server()
+ assert result.returncode > 0
+
+ def test_valid_certs(self):
+ "IPA server install with valid certificates"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server()
+ assert result.returncode == 0
+ self.verify_installation()
+
+ def test_wildcard_http(self):
+ "IPA server install with wildcard HTTP certificate"
+
+ self.export_pkcs12('ca1/wildcard', filename='http.p12')
+ self.export_pkcs12('ca1/server', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert result.returncode == 0
+ self.verify_installation()
+
+ def test_wildcard_ds(self):
+ "IPA server install with wildcard DS certificate"
+
+ self.export_pkcs12('ca1/server', filename='http.p12')
+ self.export_pkcs12('ca1/wildcard', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert result.returncode == 0
+ self.verify_installation()
+
+ def test_http_san(self):
+ "IPA server install with HTTP certificate with SAN"
+
+ self.export_pkcs12('ca1/server-altname', filename='http.p12')
+ self.export_pkcs12('ca1/server', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert result.returncode == 0
+ self.verify_installation()
+
+ def test_ds_san(self):
+ "IPA server install with DS certificate with SAN"
+
+ self.export_pkcs12('ca1/server', filename='http.p12')
+ self.export_pkcs12('ca1/server-altname', filename='dirsrv.p12')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert result.returncode == 0
+ self.verify_installation()
+
+ def test_interactive_missing_http_pkcs_password(self):
+ "IPA server install with prompt for HTTP PKCS#12 password"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ stdin_text = get_install_stdin(cert_passwords=[self.cert_password])
+
+ result = self.install_server(http_pin=False, unattended=False,
+ stdin_text=stdin_text)
+ assert result.returncode == 0
+ self.verify_installation()
+ assert ('Enter server.p12 unlock password:'
+ in result.stdout_text), result.stdout_text
+
+ def test_interactive_missing_ds_pkcs_password(self):
+ "IPA server install with prompt for DS PKCS#12 password"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ stdin_text = get_install_stdin(cert_passwords=[self.cert_password])
+
+ result = self.install_server(dirsrv_pin=False, unattended=False,
+ stdin_text=stdin_text)
+ assert result.returncode == 0
+ self.verify_installation()
+ assert ('Enter server.p12 unlock password:'
+ in result.stdout_text), result.stdout_text
+
+
+class TestReplicaInstall(CALessBase):
+ num_replicas = 1
+
+ def setUp(self):
+ # Install the master for every test
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server()
+ assert result.returncode == 0
+
+ def tearDown(self):
+ # Uninstall both master and replica
+ replica = self.replicas[0]
+ tasks.kinit_admin(self.master)
+ self.uninstall_server(replica)
+ self.master.run_command(['ipa-replica-manage', 'del', replica.hostname,
+ '--force'], raiseonerr=False)
+ self.master.run_command(['ipa', 'host-del', replica.hostname],
+ raiseonerr=False)
+
+ replica.run_command(['certutil', '-d', '/etc/pki/nssdb', '-D',
+ '-n', 'External CA cert'], raiseonerr=False)
+
+ self.uninstall_server()
+ self.master.run_command(['certutil', '-d', '/etc/pki/nssdb', '-D',
+ '-n', 'External CA cert'], raiseonerr=False)
+
+ def test_no_certs(self):
+ "IPA replica install without certificates"
+
+ result = self.master.run_command(['ipa-replica-prepare',
+ self.replicas[0].hostname],
+ raiseonerr=False)
+ assert result.returncode > 0
+ assert ('Cannot issue certificates: a CA is not installed. Use the '
+ '--http_pkcs12, --dirsrv_pkcs12 options to provide custom '
+ 'certificates.' in result.stderr_text), result.stderr_text
+
+ def test_nonexistent_http_pkcs12_file(self):
+ "IPA replica install with non-existent HTTP PKCS#12 file"
+
+ self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='does_not_exist',
+ dirsrv_pkcs12='dirsrv.p12',
+ http_pkcs12_exists=False)
+ assert_error(result, 'Failed to open does_not_exist')
+
+ def test_nonexistent_ds_pkcs12_file(self):
+ "IPA replica install with non-existent DS PKCS#12 file"
+
+ self.export_pkcs12('ca1/replica', filename='http.p12')
+
+ result = self.prepare_replica(dirsrv_pkcs12='does_not_exist',
+ http_pkcs12='http.p12',
+ dirsrv_pkcs12_exists=False)
+ assert_error(result, 'Failed to open does_not_exist')
+
+ def test_incorect_http_pin(self):
+ "IPA replica install with incorrect HTTP PKCS#12 password"
+
+ self.export_pkcs12('ca1/replica', filename='replica.p12')
+
+ result = self.prepare_replica(http_pin='bad<pin>')
+ assert result.returncode > 0
+ assert_error(result, 'incorrect password for pkcs#12 file replica.p12')
+
+ def test_incorect_ds_pin(self):
+ "IPA replica install with incorrect DS PKCS#12 password"
+
+ self.export_pkcs12('ca1/replica', filename='replica.p12')
+
+ result = self.prepare_replica(dirsrv_pin='bad<pin>')
+ assert_error(result, 'incorrect password for pkcs#12 file replica.p12')
+
+ def test_http_unknown_ca(self):
+ "IPA replica install with HTTP certificate issued by unknown CA"
+
+ self.export_pkcs12('ca2/replica', filename='http.p12')
+ self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'http.p12 is not signed by /etc/ipa/ca.crt, or the full '
+ 'certificate chain is not present in the PKCS#12 file')
+
+ def test_ds_unknown_ca(self):
+ "IPA replica install with DS certificate issued by unknown CA"
+
+ self.export_pkcs12('ca1/replica', filename='http.p12')
+ self.export_pkcs12('ca2/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'dirsrv.p12 is not signed by /etc/ipa/ca.crt, or the '
+ 'full certificate chain is not present in the PKCS#12 '
+ 'file')
+
+ def test_invalid_http_cn(self):
+ "IPA replica install with HTTP certificate with invalid CN"
+
+ self.export_pkcs12('ca1/replica-badname', filename='http.p12')
+ self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'The server certificate in http.p12 is not valid: '
+ 'invalid for server %s' % self.replicas[0].hostname)
+
+ def test_invalid_ds_cn(self):
+ "IPA replica install with DS certificate with invalid CN"
+
+ self.export_pkcs12('ca1/replica', filename='http.p12')
+ self.export_pkcs12('ca1/replica-badname', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'The server certificate in dirsrv.p12 is not valid: '
+ 'invalid for server %s' % self.replicas[0].hostname)
+
+ def test_expired_http(self):
+ "IPA replica install with expired HTTP certificate"
+
+ self.export_pkcs12('ca1/replica-expired', filename='http.p12')
+ self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'The server certificate in http.p12 is not valid: '
+ "(SEC_ERROR_EXPIRED_CERTIFICATE) Peer's Certificate has "
+ 'expired.')
+
+ def test_expired_ds(self):
+ "IPA replica install with expired DS certificate"
+
+ self.export_pkcs12('ca1/replica-expired', filename='http.p12')
+ self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'The server certificate in http.p12 is not valid: '
+ "(SEC_ERROR_EXPIRED_CERTIFICATE) Peer's Certificate has "
+ 'expired.')
+
+ def test_http_bad_usage(self):
+ "IPA replica install with HTTP certificate with invalid key usage"
+
+ self.export_pkcs12('ca1/replica-badusage', filename='http.p12')
+ self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'The server certificate in http.p12 is not valid: '
+ 'invalid for a SSL server')
+
+ def test_ds_bad_usage(self):
+ "IPA replica install with DS certificate with invalid key usage"
+
+ self.export_pkcs12('ca1/replica', filename='http.p12')
+ self.export_pkcs12('ca1/replica-badusage', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'The server certificate in dirsrv.p12 is not valid: '
+ 'invalid for a SSL server')
+
+ def test_revoked_http(self):
+ "IPA replica install with revoked HTTP certificate"
+
+ self.export_pkcs12('ca1/replica-revoked', filename='http.p12')
+ self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert result.returncode > 0
+
+ def test_revoked_ds(self):
+ "IPA replica install with revoked DS certificate"
+
+ self.export_pkcs12('ca1/replica', filename='http.p12')
+ self.export_pkcs12('ca1/replica-revoked', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert result.returncode > 0
+
+ def test_http_intermediate_ca(self):
+ "IPA replica install with HTTP certificate issued by intermediate CA"
+
+ self.export_pkcs12('ca1/subca/replica', filename='http.p12')
+ self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'http.p12 is not signed by /etc/ipa/ca.crt, or the full '
+ 'certificate chain is not present in the PKCS#12 file')
+
+ def test_ds_intermediate_ca(self):
+ "IPA replica install with DS certificate issued by intermediate CA"
+
+ self.export_pkcs12('ca1/replica', filename='http.p12')
+ self.export_pkcs12('ca1/subca/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert_error(result,
+ 'dirsrv.p12 is not signed by /etc/ipa/ca.crt, or the '
+ 'full certificate chain is not present in the PKCS#12 '
+ 'file')
+
+ def test_valid_certs(self):
+ "IPA replica install with valid certificates"
+
+ self.export_pkcs12('ca1/replica', filename='server.p12')
+
+ result = self.prepare_replica(http_pkcs12='server.p12',
+ dirsrv_pkcs12='server.p12')
+ assert result.returncode == 0
+
+ result = self.install_replica()
+ assert result.returncode == 0
+
+ self.verify_installation()
+
+ def test_wildcard_http(self):
+ "IPA replica install with wildcard HTTP certificate"
+
+ self.export_pkcs12('ca1/wildcard', filename='http.p12')
+ self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert result.returncode == 0
+
+ result = self.install_replica()
+ assert result.returncode == 0
+
+ self.verify_installation()
+
+ def test_wildcard_ds(self):
+ "IPA replica install with wildcard DS certificate"
+
+ self.export_pkcs12('ca1/wildcard', filename='http.p12')
+ self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert result.returncode == 0
+
+ result = self.install_replica()
+ assert result.returncode == 0
+
+ self.verify_installation()
+
+ def test_http_san(self):
+ "IPA replica install with HTTP certificate with SAN"
+
+ self.export_pkcs12('ca1/replica-altname', filename='http.p12')
+ self.export_pkcs12('ca1/replica', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert result.returncode == 0
+
+ result = self.install_replica()
+ assert result.returncode == 0
+
+ self.verify_installation()
+
+ def test_ds_san(self):
+ "IPA replica install with DS certificate with SAN"
+
+ self.export_pkcs12('ca1/replica', filename='http.p12')
+ self.export_pkcs12('ca1/replica-altname', filename='dirsrv.p12')
+
+ result = self.prepare_replica(http_pkcs12='http.p12',
+ dirsrv_pkcs12='dirsrv.p12')
+ assert result.returncode == 0
+
+ result = self.install_replica()
+ assert result.returncode == 0
+
+ self.verify_installation()
+
+ def test_interactive_missing_http_pkcs_password(self):
+ "IPA replica install with missing HTTP PKCS#12 password"
+
+ self.export_pkcs12('ca1/replica', filename='replica.p12')
+
+ stdin_text = get_replica_prepare_stdin(
+ cert_passwords=[self.cert_password])
+
+ result = self.prepare_replica(http_pin=False, unattended=False,
+ stdin_text=stdin_text)
+ assert result.returncode == 0
+
+ result = self.install_replica()
+ assert result.returncode == 0
+
+ self.verify_installation()
+
+ def test_interactive_missing_ds_pkcs_password(self):
+ "IPA replica install with missing DS PKCS#12 password"
+
+ self.export_pkcs12('ca1/replica', filename='replica.p12')
+
+ stdin_text = get_replica_prepare_stdin(
+ cert_passwords=[self.cert_password])
+
+ result = self.prepare_replica(dirsrv_pin=False, unattended=False,
+ stdin_text=stdin_text)
+ assert result.returncode == 0
+
+ result = self.install_replica()
+ assert result.returncode == 0
+
+ self.verify_installation()
+
+
+class TestClientInstall(CALessBase):
+ num_clients = 1
+
+ def test_client_install(self):
+ "IPA client install"
+
+ self.export_pkcs12('ca1/server')
+ with open(self.pem_filename, 'w') as f:
+ f.write(self.get_pem('ca1'))
+
+ result = self.install_server()
+ assert result.returncode == 0
+
+ self.clients[0].run_command(['ipa-client-install',
+ '--domain', self.master.domain.name,
+ '--server', self.master.hostname,
+ '-p', self.master.config.admin_name,
+ '-w', self.master.config.admin_password,
+ '-U'])
+
+ self.verify_installation()
+
+
+@ordered
+class TestIPACommands(CALessBase):
+ @classmethod
+ def install(cls):
+ super(TestIPACommands, cls).install()
+
+ cls.export_pkcs12('ca1/server')
+ with open(cls.pem_filename, 'w') as f:
+ f.write(cls.get_pem('ca1'))
+
+ result = cls.install_server()
+ assert result.returncode == 0
+
+ tasks.kinit_admin(cls.master)
+
+ cls.client_pem = ''.join(cls.get_pem('ca1/client').splitlines()[1:-1])
+ cls.log.debug('Client PEM:\n%r' % cls.client_pem)
+ cls.test_hostname = 'testhost.%s' % cls.master.domain.name
+ cls.test_service = 'test/%s' % cls.test_hostname
+
+ def check_ipa_command_not_available(self, command):
+ "Verify that the given IPA subcommand is not available"
+
+ result = self.master.run_command(['ipa', command], raiseonerr=False)
+ assert_error(result, "ipa: ERROR: unknown command '%s'" % command)
+
+ def test_cert_commands_unavailable(self):
+ for cmd in (
+ 'cert-status',
+ 'cert-show',
+ 'cert-find',
+ 'cert-revoke',
+ 'cert-remove-hold',
+ 'cert-status'):
+ func = lambda: self.check_ipa_command_not_available(cmd)
+ func.description = 'Verify that %s command is not available' % cmd
+ yield (func, )
+
+ def test_cert_help_unavailable(self):
+ "Verify that cert plugin help is not available"
+ result = self.master.run_command(['ipa', 'help', 'cert'],
+ raiseonerr=False)
+ assert_error(result,
+ "ipa: ERROR: no command nor help topic 'cert'",
+ returncode=1)
+
+ @contextlib.contextmanager
+ def host(self):
+ "Context manager that adds and removes a host entry with a certificate"
+ self.master.run_command(['ipa', 'host-add', self.test_hostname,
+ '--force',
+ '--certificate', self.client_pem])
+ try:
+ yield
+ finally:
+ self.master.run_command(['ipa', 'host-del', self.test_hostname],
+ raiseonerr=False)
+
+ @contextlib.contextmanager
+ def service(self):
+ "Context manager that adds and removes host & service entries"
+ with self.host():
+ self.master.run_command(['ipa', 'service-add', self.test_service,
+ '--force',
+ '--certificate', self.client_pem])
+ yield
+
+ def test_service_mod_doesnt_revoke(self):
+ "Verify that service-mod does not attempt to revoke certificate"
+ with self.service():
+ self.master.run_command(['ipa', 'service-mod', self.test_service,
+ '--certificate='])
+
+ def test_service_disable_doesnt_revoke(self):
+ "Verify that service-disable does not attempt to revoke certificate"
+ with self.service():
+ self.master.run_command(['ipa', 'service-disable',
+ self.test_service])
+
+ def test_service_del_doesnt_revoke(self):
+ "Verify that service-del does not attempt to revoke certificate"
+ with self.service():
+ self.master.run_command(['ipa', 'service-del', self.test_service])
+
+ def test_host_mod_doesnt_revoke(self):
+ "Verify that host-mod does not attempt to revoke host's certificate"
+ with self.host():
+ self.master.run_command(['ipa', 'host-mod', self.test_hostname,
+ '--certificate='])
+
+ def test_host_disable_doesnt_revoke(self):
+ "Verify that host-disable does not attempt to revoke host certificate"
+ with self.host():
+ self.master.run_command(['ipa', 'host-disable',
+ self.test_hostname])
+
+ def test_host_del_doesnt_revoke(self):
+ "Verify that host-del does not attempt to revoke host's certificate"
+ with self.host():
+ self.master.run_command(['ipa', 'host-del', self.test_hostname])
+
+
+class TestCertinstall(CALessBase):
+ @classmethod
+ def install(cls):
+ super(TestCertinstall, cls).install()
+
+ cls.export_pkcs12('ca1/server')
+ with open(cls.pem_filename, 'w') as f:
+ f.write(cls.get_pem('ca1'))
+
+ result = cls.install_server()
+ assert result.returncode == 0
+
+ tasks.kinit_admin(cls.master)
+
+ def certinstall(self, mode, cert_nick=None, cert_exists=True,
+ filename='server.p12', pin=_DEFAULT, stdin_text=None):
+ if cert_nick:
+ self.export_pkcs12(cert_nick)
+ if pin is _DEFAULT:
+ pin = self.cert_password
+ if cert_exists:
+ self.copy_cert(self.master, filename)
+ args = ['ipa-server-certinstall',
+ '-%s' % mode, filename]
+ if pin is not None:
+ option = {'w': '--http_pin', 'd': '--dirsrv_pin'}[mode]
+ args += [option, pin]
+ if mode == 'd':
+ if stdin_text:
+ stdin_text = '%s\n%s' % (self.master.config.dirman_password,
+ stdin_text)
+ else:
+ stdin_text = self.master.config.dirman_password + '\n'
+ return self.master.run_command(args,
+ raiseonerr=False,
+ stdin_text=stdin_text)
+
+ def test_nonexistent_http_pkcs12_file(self):
+ "Install new HTTP certificate from non-existent PKCS#12 file"
+
+ result = self.certinstall('w', filename='does_not_exist', pin='none',
+ cert_exists=False)
+ assert_error(result, 'Failed to open does_not_exist')
+
+ def test_nonexistent_ds_pkcs12_file(self):
+ "Install new DS certificate from non-existent PKCS#12 file"
+
+ result = self.certinstall('d', filename='does_not_exist', pin='none',
+ cert_exists=False)
+ assert_error(result, 'Failed to open does_not_exist')
+
+ def test_incorect_http_pin(self):
+ "Install new HTTP certificate with incorrect PKCS#12 password"
+
+ result = self.certinstall('w', 'ca1/server', pin='bad<pin>')
+ assert_error(result,
+ 'incorrect password for pkcs#12 file server.p12')
+
+ def test_incorect_dirsrv_pin(self):
+ "Install new DS certificate with incorrect PKCS#12 password"
+
+ result = self.certinstall('d', 'ca1/server', pin='bad<pin>')
+ assert_error(result,
+ 'incorrect password for pkcs#12 file server.p12')
+
+ def test_invalid_http_cn(self):
+ "Install new HTTP certificate with invalid CN "
+
+ result = self.certinstall('w', 'ca1/server-badname')
+ assert_error(result,
+ 'The server certificate in server.p12 is not valid: '
+ 'invalid for server %s' % self.master.hostname)
+
+ def test_invalid_ds_cn(self):
+ "Install new DS certificate with invalid CN "
+
+ result = self.certinstall('d', 'ca1/server-badname')
+ assert_error(result,
+ 'The server certificate in server.p12 is not valid: '
+ 'invalid for server %s' % self.master.hostname)
+
+ def test_expired_http(self):
+ "Install new expired HTTP certificate"
+
+ result = self.certinstall('w', 'ca1/server-expired')
+ assert_error(result,
+ 'The server certificate in server.p12 is not valid: '
+ "(SEC_ERROR_EXPIRED_CERTIFICATE) Peer's Certificate has "
+ 'expired.')
+
+ def test_expired_ds(self):
+ "Install new expired DS certificate"
+
+ result = self.certinstall('d', 'ca1/server-expired')
+ assert_error(result,
+ 'The server certificate in server.p12 is not valid: '
+ "(SEC_ERROR_EXPIRED_CERTIFICATE) Peer's Certificate has "
+ 'expired.')
+
+ def test_http_bad_usage(self):
+ "Install new HTTP certificate with invalid key usage"
+
+ result = self.certinstall('w', 'ca1/server-badusage')
+ assert_error(result,
+ 'The server certificate in server.p12 is not valid: '
+ 'invalid for a SSL server')
+
+ def test_ds_bad_usage(self):
+ "Install new DS certificate with invalid key usage"
+
+ result = self.certinstall('d', 'ca1/server-badusage')
+ assert_error(result,
+ 'The server certificate in server.p12 is not valid: '
+ 'invalid for a SSL server')
+
+ def test_revoked_http(self):
+ "Install new revoked HTTP certificate"
+
+ result = self.certinstall('w', 'ca1/server-revoked')
+ assert result.returncode > 0
+
+ def test_revoked_ds(self):
+ "Install new revoked DS certificate"
+
+ result = self.certinstall('d', 'ca1/server-revoked')
+ assert result.returncode > 0
+
+ def test_http_intermediate_ca(self):
+ "Install new HTTP certificate issued by intermediate CA"
+
+ result = self.certinstall('w', 'ca1/subca/server')
+ assert_error(result,
+ 'server.p12 is not signed by /etc/ipa/ca.crt, or the '
+ 'full certificate chain is not present in the PKCS#12 '
+ 'file')
+
+ def test_ds_intermediate_ca(self):
+ "Install new DS certificate issued by intermediate CA"
+
+ result = self.certinstall('d', 'ca1/subca/server')
+ assert_error(result,
+ 'server.p12 is not signed by /etc/ipa/ca.crt, or the '
+ 'full certificate chain is not present in the PKCS#12 '
+ 'file')
+
+ def test_self_signed(self):
+ "Install new self-signed certificate"
+
+ result = self.certinstall('w', 'server-selfsign')
+ assert_error(result,
+ 'server.p12 is not signed by /etc/ipa/ca.crt, or the '
+ 'full certificate chain is not present in the PKCS#12 '
+ 'file')
+
+ def test_valid_http(self):
+ "Install new valid HTTP certificate"
+
+ result = self.certinstall('w', 'ca1/server')
+ assert result.returncode == 0
+
+ def test_valid_ds(self):
+ "Install new valid DS certificate"
+
+ result = self.certinstall('d', 'ca1/server')
+ assert result.returncode == 0
+
+ def test_wildcard_http(self):
+ "Install new wildcard HTTP certificate"
+
+ result = self.certinstall('w', 'ca1/wildcard')
+ assert result.returncode == 0
+
+ def test_wildcard_ds(self):
+ "Install new wildcard DS certificate"
+
+ result = self.certinstall('d', 'ca1/wildcard')
+ assert result.returncode == 0
+
+ def test_http_san(self):
+ "Install new HTTP certificate with SAN"
+
+ result = self.certinstall('w', 'ca1/server-altname')
+ assert result.returncode == 0
+
+ def test_ds_san(self):
+ "Install new DS certificate with SAN"
+
+ result = self.certinstall('d', 'ca1/server-altname')
+ assert result.returncode == 0
+
+ def test_interactive_missing_http_pkcs_password(self):
+ "Install new HTTP certificate with missing PKCS#12 password"
+
+ result = self.certinstall('w', 'ca1/server',
+ pin=None,
+ stdin_text=self.cert_password + '\n')
+ assert result.returncode == 0
+
+ def test_interactive_missing_ds_pkcs_password(self):
+ "Install new DS certificate with missing PKCS#12 password"
+
+ result = self.certinstall('d', 'ca1/server',
+ pin=None,
+ stdin_text=self.cert_password + '\n')
+ assert result.returncode == 0