diff options
-rw-r--r-- | ipatests/test_xmlrpc/data/smime-mod.cfg.tmpl | 108 | ||||
-rw-r--r-- | ipatests/test_xmlrpc/test_caacl_profile_enforcement.py | 236 |
2 files changed, 344 insertions, 0 deletions
diff --git a/ipatests/test_xmlrpc/data/smime-mod.cfg.tmpl b/ipatests/test_xmlrpc/data/smime-mod.cfg.tmpl new file mode 100644 index 000000000..da94c25e5 --- /dev/null +++ b/ipatests/test_xmlrpc/data/smime-mod.cfg.tmpl @@ -0,0 +1,108 @@ +auth.instance_id=raCertAuth +classId=caEnrollImpl +desc=Certificate for S-MIME extension +enable=true +enableBy=ipara +input.i1.class_id=certReqInputImpl +input.i2.class_id=submitterInfoInputImpl +input.list=i1,i2 +name=SMIME certificate profile +output.list=o1 +output.o1.class_id=certOutputImpl +policyset.list=serverCertSet +policyset.serverCertSet.1.constraint.class_id=subjectNameConstraintImpl +policyset.serverCertSet.1.constraint.name=Subject Name Constraint +policyset.serverCertSet.1.constraint.params.accept=true +policyset.serverCertSet.1.constraint.params.pattern=CN=refuse sign +policyset.serverCertSet.1.default.class_id=subjectNameDefaultImpl +policyset.serverCertSet.1.default.name=Subject Name Default +policyset.serverCertSet.1.default.params.name=CN=$request.req_subject_name.cn$, O={iparealm} +policyset.serverCertSet.2.constraint.class_id=validityConstraintImpl +policyset.serverCertSet.2.constraint.name=Validity Constraint +policyset.serverCertSet.2.constraint.params.notAfterCheck=false +policyset.serverCertSet.2.constraint.params.notBeforeCheck=false +policyset.serverCertSet.2.constraint.params.range=740 +policyset.serverCertSet.2.default.class_id=validityDefaultImpl +policyset.serverCertSet.2.default.name=Validity Default +policyset.serverCertSet.2.default.params.range=731 +policyset.serverCertSet.2.default.params.startTime=0 +policyset.serverCertSet.3.constraint.class_id=keyConstraintImpl +policyset.serverCertSet.3.constraint.name=Key Constraint +policyset.serverCertSet.3.constraint.params.keyParameters=1024,2048,3072,4096 +policyset.serverCertSet.3.constraint.params.keyType=RSA +policyset.serverCertSet.3.default.class_id=userKeyDefaultImpl +policyset.serverCertSet.3.default.name=Key Default +policyset.serverCertSet.4.constraint.class_id=noConstraintImpl +policyset.serverCertSet.4.constraint.name=No Constraint +policyset.serverCertSet.4.default.class_id=authorityKeyIdentifierExtDefaultImpl +policyset.serverCertSet.4.default.name=Authority Key Identifier Default +policyset.serverCertSet.5.constraint.class_id=noConstraintImpl +policyset.serverCertSet.5.constraint.name=No Constraint +policyset.serverCertSet.5.default.class_id=authInfoAccessExtDefaultImpl +policyset.serverCertSet.5.default.name=AIA Extension Default +policyset.serverCertSet.5.default.params.authInfoAccessADEnable_0=true +policyset.serverCertSet.5.default.params.authInfoAccessADLocationType_0=URIName +policyset.serverCertSet.5.default.params.authInfoAccessADLocation_0=http://ipa-ca.{ipadomain}/ca/ocsp +policyset.serverCertSet.5.default.params.authInfoAccessADMethod_0=1.3.6.1.5.5.7.48.1 +policyset.serverCertSet.5.default.params.authInfoAccessCritical=false +policyset.serverCertSet.5.default.params.authInfoAccessNumADs=1 +policyset.serverCertSet.6.constraint.class_id=keyUsageExtConstraintImpl +policyset.serverCertSet.6.constraint.name=Key Usage Extension Constraint +policyset.serverCertSet.6.constraint.params.keyUsageCritical=true +policyset.serverCertSet.6.constraint.params.keyUsageCrlSign=false +policyset.serverCertSet.6.constraint.params.keyUsageDataEncipherment=true +policyset.serverCertSet.6.constraint.params.keyUsageDecipherOnly=false +policyset.serverCertSet.6.constraint.params.keyUsageDigitalSignature=true +policyset.serverCertSet.6.constraint.params.keyUsageEncipherOnly=false +policyset.serverCertSet.6.constraint.params.keyUsageKeyAgreement=false +policyset.serverCertSet.6.constraint.params.keyUsageKeyCertSign=false +policyset.serverCertSet.6.constraint.params.keyUsageKeyEncipherment=true +policyset.serverCertSet.6.constraint.params.keyUsageNonRepudiation=true +policyset.serverCertSet.6.default.class_id=keyUsageExtDefaultImpl +policyset.serverCertSet.6.default.name=Key Usage Default +policyset.serverCertSet.6.default.params.keyUsageCritical=true +policyset.serverCertSet.6.default.params.keyUsageCrlSign=false +policyset.serverCertSet.6.default.params.keyUsageDataEncipherment=true +policyset.serverCertSet.6.default.params.keyUsageDecipherOnly=false +policyset.serverCertSet.6.default.params.keyUsageDigitalSignature=true +policyset.serverCertSet.6.default.params.keyUsageEncipherOnly=false +policyset.serverCertSet.6.default.params.keyUsageKeyAgreement=false +policyset.serverCertSet.6.default.params.keyUsageKeyCertSign=false +policyset.serverCertSet.6.default.params.keyUsageKeyEncipherment=true +policyset.serverCertSet.6.default.params.keyUsageNonRepudiation=true +policyset.serverCertSet.7.constraint.class_id=noConstraintImpl +policyset.serverCertSet.7.constraint.name=No Constraint +policyset.serverCertSet.7.default.class_id=extendedKeyUsageExtDefaultImpl +policyset.serverCertSet.7.default.name=Extended Key Usage Extension Default +policyset.serverCertSet.7.default.params.exKeyUsageCritical=false +policyset.serverCertSet.7.default.params.exKeyUsageOIDs=1.3.6.1.5.5.7.3.4 +policyset.serverCertSet.8.constraint.class_id=signingAlgConstraintImpl +policyset.serverCertSet.8.constraint.name=No Constraint +policyset.serverCertSet.8.constraint.params.signingAlgsAllowed=SHA1withRSA,SHA256withRSA,SHA512withRSA,MD5withRSA,MD2withRSA,SHA1withDSA,SHA1withEC,SHA256withEC,SHA384withEC,SHA512withEC +policyset.serverCertSet.8.default.class_id=signingAlgDefaultImpl +policyset.serverCertSet.8.default.name=Signing Alg +policyset.serverCertSet.8.default.params.signingAlg=- +policyset.serverCertSet.9.constraint.class_id=noConstraintImpl +policyset.serverCertSet.9.constraint.name=No Constraint +policyset.serverCertSet.9.default.class_id=crlDistributionPointsExtDefaultImpl +policyset.serverCertSet.9.default.name=CRL Distribution Points Extension Default +policyset.serverCertSet.9.default.params.crlDistPointsCritical=false +policyset.serverCertSet.9.default.params.crlDistPointsEnable_0=true +policyset.serverCertSet.9.default.params.crlDistPointsIssuerName_0=CN=Certificate Authority,o=ipaca +policyset.serverCertSet.9.default.params.crlDistPointsIssuerType_0=DirectoryName +policyset.serverCertSet.9.default.params.crlDistPointsNum=1 +policyset.serverCertSet.9.default.params.crlDistPointsPointName_0=http://ipa-ca.{ipadomain}/ipa/crl/MasterCRL.bin +policyset.serverCertSet.9.default.params.crlDistPointsPointType_0=URIName +policyset.serverCertSet.9.default.params.crlDistPointsReasons_0= +policyset.serverCertSet.10.constraint.class_id=noConstraintImpl +policyset.serverCertSet.10.constraint.name=No Constraint +policyset.serverCertSet.10.default.class_id=subjectKeyIdentifierExtDefaultImpl +policyset.serverCertSet.10.default.name=Subject Key Identifier Extension Default +policyset.serverCertSet.10.default.params.critical=false +policyset.serverCertSet.11.constraint.class_id=noConstraintImpl +policyset.serverCertSet.11.constraint.name=No Constraint +policyset.serverCertSet.11.default.class_id=userExtensionDefaultImpl +policyset.serverCertSet.11.default.name=User Supplied Extension Default +policyset.serverCertSet.11.default.params.userExtOID=2.5.29.17 +policyset.serverCertSet.list=1,2,3,4,5,6,7,8,9,10,11 +visible=false diff --git a/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py b/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py new file mode 100644 index 000000000..e9472b078 --- /dev/null +++ b/ipatests/test_xmlrpc/test_caacl_profile_enforcement.py @@ -0,0 +1,236 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2015 FreeIPA Contributors see COPYING for license +# + +import os +import pytest +import tempfile + +from ipalib import api, errors +from ipatests.util import ( + prepare_config, unlock_principal_password, change_principal) +from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test +from ipatests.test_xmlrpc.test_certprofile_plugin import CertprofileTracker +from ipatests.test_xmlrpc.test_caacl_plugin import CAACLTracker + +from ipapython.ipautil import run + +BASE_DIR = os.path.dirname(__file__) + +SMIME_PROFILE_TEMPLATE = os.path.join(BASE_DIR, 'data/smime.cfg.tmpl') +SMIME_MOD_CONSTR_PROFILE_TEMPLATE = os.path.join(BASE_DIR, 'data/smime-mod.cfg.tmpl') +CERT_OPENSSL_CONFIG_TEMPLATE = os.path.join(BASE_DIR, 'data/usercert.conf.tmpl') +CERT_RSA_PRIVATE_KEY_PATH = os.path.join(BASE_DIR, 'data/usercert-priv-key.pem') + + +CERT_SUBJECT_BASE = ( + api.Command.config_show() + ['result']['ipacertificatesubjectbase'][0] +) + +SMIME_USER_INIT_PW = u'Change123' +SMIME_USER_PW = u'Secret123' + + +def generate_user_csr(username, domain=None): + csr_values = dict( + ipacertbase=CERT_SUBJECT_BASE, + ipadomain=domain if domain else api.env.domain, + username=username) + + with tempfile.NamedTemporaryFile(mode='w') as csr_file: + run(['openssl', 'req', '-new', '-key', CERT_RSA_PRIVATE_KEY_PATH, + '-out', csr_file.name, + '-config', prepare_config( + CERT_OPENSSL_CONFIG_TEMPLATE, csr_values)]) + + with open(csr_file.name, 'r') as f: + csr = unicode(f.read()) + + return csr + + +@pytest.fixture(scope='class') +def smime_profile(request): + profile_path = prepare_config( + SMIME_PROFILE_TEMPLATE, + dict(ipadomain=api.env.domain, iparealm=api.env.realm)) + + tracker = CertprofileTracker(u'smime', store=True, + desc=u"S/MIME certificate profile", + profile=profile_path) + + return tracker.make_fixture(request) + + +@pytest.fixture(scope='class') +def smime_acl(request): + tracker = CAACLTracker(u'smime_acl') + + return tracker.make_fixture(request) + + +# TODO: rewrite these into Tracker instances +# UserTracker has problems while setting passwords. +# Until fixed, will use this fixture. +@pytest.fixture(scope='class') +def smime_user(request): + username = u'alice' + api.Command.user_add(uid=username, givenname=u'Alice', sn=u'SMIME', + userpassword=SMIME_USER_INIT_PW) + + unlock_principal_password(username, SMIME_USER_INIT_PW, SMIME_USER_PW) + + def fin(): + api.Command.user_del(username) + request.addfinalizer(fin) + + return username + + +@pytest.fixture(scope='class') +def smime_group(request): + api.Command.group_add(u'smime_users') + + def fin(): + api.Command.group_del(u'smime_users') + request.addfinalizer(fin) + + return u'smime_users' + + +class TestCertSignMIME(XMLRPC_test): + + def test_cert_import(self, smime_profile): + smime_profile.ensure_exists() + + def test_create_acl(self, smime_acl): + smime_acl.ensure_exists() + + def test_add_profile_to_acl(self, smime_acl, smime_profile): + smime_acl.add_profile(certprofile=smime_profile.name) + + # rewrite to trackers, prepare elsewhere + def test_add_user_to_group(self, smime_group, smime_user): + api.Command.group_add_member(smime_group, user=smime_user) + + def test_add_group_to_acl(self, smime_group, smime_acl): + smime_acl.add_user(group=smime_group) + + def test_sign_smime_csr(self, smime_profile, smime_user): + csr = generate_user_csr(smime_user) + with change_principal(smime_user, SMIME_USER_PW): + api.Command.cert_request(csr, principal=smime_user, + profile_id=smime_profile.name) + + +class TestSignWithDisabledACL(XMLRPC_test): + + def test_import_profile_and_acl(self, smime_profile, smime_acl): + smime_profile.ensure_exists() + smime_acl.ensure_missing() + smime_acl.ensure_exists() + + def test_add_profile_to_acl(self, smime_acl, smime_profile): + smime_acl.add_profile(certprofile=smime_profile.name) + + # rewrite to trackers, prepare elsewhere + def test_add_user_to_group(self, smime_group, smime_user): + api.Command.group_add_member(smime_group, user=smime_user) + + def test_add_group_to_acl(self, smime_group, smime_acl): + smime_acl.add_user(group=smime_group) + + def test_disable_acl(self, smime_acl): + smime_acl.disable() + + def test_signing_with_disabled_acl(self, smime_acl, smime_profile, + smime_user): + csr = generate_user_csr(smime_user) + with change_principal(smime_user, SMIME_USER_PW): + with pytest.raises(errors.ACIError): + api.Command.cert_request( + csr, profile_id=smime_profile.name, + principal=smime_user) + + def test_admin_overrides_disabled_acl(self, smime_acl, smime_profile, + smime_user): + csr = generate_user_csr(smime_user) + api.Command.cert_request( + csr, profile_id=smime_profile.name, + principal=smime_user) + + +class TestSignWithoutGroupMembership(XMLRPC_test): + + def test_import_profile_and_acl(self, smime_profile, smime_acl): + smime_profile.ensure_exists() + smime_acl.ensure_missing() + smime_acl.ensure_exists() + + def test_add_profile_to_acl(self, smime_acl, smime_profile): + smime_acl.add_profile(certprofile=smime_profile.name) + + def test_add_group_to_acl(self, smime_group, smime_acl, smime_user): + # smime user should not be a member of this group + # + # adding smime_user fixture to ensure it exists + smime_acl.add_user(group=smime_group) + + def test_signing_with_non_member_principal(self, smime_acl, smime_profile, + smime_user): + + csr = generate_user_csr(smime_user) + with change_principal(smime_user, SMIME_USER_PW): + with pytest.raises(errors.ACIError): + api.Command.cert_request( + csr, + profile_id=smime_profile.name, + principal=smime_user) + + def test_admin_overrides_group_membership(self, smime_acl, smime_profile, + smime_user): + csr = generate_user_csr(smime_user) + api.Command.cert_request( + csr, profile_id=smime_profile.name, + principal=smime_user) + + +class TestSignWithChangedProfile(XMLRPC_test): + """ Test to verify that the updated profile is used. + + The profile change requires different CN in CSR + than the one configured. This leads to rejection + based on not meeting the profile constraints. + """ + + def test_prepare_env(self, smime_profile, smime_acl): + smime_profile.ensure_exists() + smime_acl.ensure_exists() + + smime_acl.add_profile(certprofile=smime_profile.name) + + def test_prepare_user_and_group(self, smime_group, smime_user, smime_acl): + api.Command.group_add_member(smime_group, user=smime_user) + smime_acl.add_user(group=smime_group) + + def test_modify_smime_profile(self, smime_profile): + updated_profile_path = prepare_config(SMIME_MOD_CONSTR_PROFILE_TEMPLATE, + dict( + ipadomain=api.env.domain, + iparealm=api.env.realm)) + + with open(updated_profile_path) as f: + updated_profile = unicode(f.read()) + + updates = {u'file': updated_profile} + update_smime_profile = smime_profile.make_update_command(updates) + update_smime_profile() + + def test_sign_smime_csr(self, smime_profile, smime_user): + csr = generate_user_csr(smime_user) + with change_principal(smime_user, SMIME_USER_PW): + with pytest.raises(errors.CertificateOperationError): + api.Command.cert_request(csr, principal=smime_user, + profile_id=smime_profile.name) |