# Authors: # Petr Viktorin # # 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 . 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.transport.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.transport.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 is not None: args.extend(['--http_pin', http_pin]) if dirsrv_pin is not None: 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.transport.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.transport.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 is not None: args.extend(['--http_pin', http_pin]) if dirsrv_pin is not None: 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', password=None): """Export a cert as PKCS#12 to the given file""" if password is None: password = cls.cert_password ipautil.run(['pk12util', '-o', filename, '-n', nickname, '-d', 'nssdb', '-K', cls.cert_password, '-W', 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=None) 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=None) 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') 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') 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=None, 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=None, 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_no_http_password(self): "IPA server install with empty HTTP password" self.export_pkcs12('ca1/server', filename='http.p12', password='') 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', http_pin='') assert result.returncode == 0 self.verify_installation() def test_no_ds_password(self): "IPA server install with empty DS password" self.export_pkcs12('ca1/server', filename='http.p12') self.export_pkcs12('ca1/server', filename='dirsrv.p12', password='') 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', dirsrv_pin='') assert result.returncode == 0 self.verify_installation() 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') 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') 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=None, 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=None, unattended=False, stdin_text=stdin_text) assert result.returncode == 0 result = self.install_replica() assert result.returncode == 0 self.verify_installation() def test_no_http_password(self): "IPA replica install with empty HTTP password" self.export_pkcs12('ca1/replica', filename='http.p12', password='') self.export_pkcs12('ca1/replica', filename='dirsrv.p12') result = self.prepare_replica(http_pkcs12='http.p12', dirsrv_pkcs12='dirsrv.p12', http_pin='') assert result.returncode == 0 result = self.install_replica() assert result.returncode == 0 self.verify_installation() def test_no_ds_password(self): "IPA replica install with empty DS password" self.export_pkcs12('ca1/replica', filename='http.p12') self.export_pkcs12('ca1/replica', filename='dirsrv.p12', password='') result = self.prepare_replica(http_pkcs12='http.p12', dirsrv_pkcs12='dirsrv.p12', dirsrv_pin='') assert result.returncode == 0 result = self.install_replica() assert result.returncode == 0 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 func.test_argument = 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, p12_pin=None, args=None): if cert_nick: self.export_pkcs12(cert_nick, password=p12_pin) if pin is _DEFAULT: pin = self.cert_password if cert_exists: self.copy_cert(self.master, filename) if not args: args = ['ipa-server-certinstall', '-%s' % mode, filename] if pin is not None: args += ['--pin', pin] if mode == 'd': args += ['--dirman-password', self.master.config.dirman_password] 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') 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') 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 def test_no_http_password(self): "Install new HTTP certificate with no PKCS#12 password" result = self.certinstall('w', 'ca1/server', pin='', p12_pin='') assert result.returncode == 0 def test_no_ds_password(self): "Install new DS certificate with no PKCS#12 password" result = self.certinstall('d', 'ca1/server', pin='', p12_pin='') assert result.returncode == 0 def test_http_old_options(self): "Install new valid DS certificate using pre-v3.3 CLI options" # http://www.freeipa.org/page/V3/ipa-server-certinstall_CLI_cleanup args = ['ipa-server-certinstall', '-w', 'server.p12', '--http_pin', self.cert_password] result = self.certinstall('w', 'ca1/server', args=args) assert result.returncode == 0 def test_ds_old_options(self): "Install new valid DS certificate using pre-v3.3 CLI options" # http://www.freeipa.org/page/V3/ipa-server-certinstall_CLI_cleanup args = ['ipa-server-certinstall', '-d', 'server.p12', '--dirsrv_pin', self.cert_password] stdin_text = self.master.config.dirman_password + '\n' result = self.certinstall('d', 'ca1/server', args=args, stdin_text=stdin_text) assert result.returncode == 0