summaryrefslogtreecommitdiffstats
path: root/ipaserver/plugins
diff options
context:
space:
mode:
authorFraser Tweedale <ftweedal@redhat.com>2016-10-10 14:31:49 +1000
committerDavid Kupka <dkupka@redhat.com>2016-11-10 10:21:47 +0100
commit66637f766dd0ddc50888013962be2294fd8d0e9a (patch)
tree51805147aa266accc0078be8bab63930f16e29b6 /ipaserver/plugins
parent9522970bfa28900abc90e959de483f59c79a3e5f (diff)
downloadfreeipa-66637f766dd0ddc50888013962be2294fd8d0e9a.tar.gz
freeipa-66637f766dd0ddc50888013962be2294fd8d0e9a.tar.xz
freeipa-66637f766dd0ddc50888013962be2294fd8d0e9a.zip
pkcs10: use python-cryptography for CSR processing
Update ``ipalib.pkcs10`` module to use python-cryptography for CSR processing instead of NSS. Part of: https://fedorahosted.org/freeipa/ticket/6398 Reviewed-By: Jan Cholasta <jcholast@redhat.com> Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
Diffstat (limited to 'ipaserver/plugins')
-rw-r--r--ipaserver/plugins/cert.py95
1 files changed, 43 insertions, 52 deletions
diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
index a66cc38e9..5e85942dd 100644
--- a/ipaserver/plugins/cert.py
+++ b/ipaserver/plugins/cert.py
@@ -20,14 +20,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
-import binascii
import collections
import datetime
import os
+import cryptography.x509
from nss import nss
from nss.error import NSPRError
-from pyasn1.error import PyAsn1Error
import six
from ipalib import Command, Str, Int, Flag
@@ -174,31 +173,9 @@ def validate_csr(ugettext, csr):
return
try:
pkcs10.load_certificate_request(csr)
- except (TypeError, binascii.Error) as e:
- raise errors.Base64DecodeError(reason=str(e))
- except Exception as e:
+ except (TypeError, ValueError) as e:
raise errors.CertificateOperationError(error=_('Failure decoding Certificate Signing Request: %s') % e)
-def normalize_csr(csr):
- """
- Strip any leading and trailing cruft around the BEGIN/END block
- """
- end_len = 37
- s = csr.find('-----BEGIN NEW CERTIFICATE REQUEST-----')
- if s == -1:
- s = csr.find('-----BEGIN CERTIFICATE REQUEST-----')
- e = csr.find('-----END NEW CERTIFICATE REQUEST-----')
- if e == -1:
- e = csr.find('-----END CERTIFICATE REQUEST-----')
- if e != -1:
- end_len = 33
-
- if s > -1 and e > -1:
- # We're normalizing here, not validating
- csr = csr[s:e+end_len]
-
- return csr
-
def normalize_serial_number(num):
"""
@@ -515,7 +492,6 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
'csr', validate_csr,
label=_('CSR'),
cli_name='csr_file',
- normalizer=normalize_csr,
noextrawhitespace=False,
),
)
@@ -607,17 +583,21 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
caacl_check(principal_type, principal, ca, profile_id)
try:
- subject = pkcs10.get_subject(csr)
- extensions = pkcs10.get_extensions(csr)
- subjectaltname = pkcs10.get_subjectaltname(csr) or ()
- except (NSPRError, PyAsn1Error, ValueError) as e:
+ csr_obj = pkcs10.load_certificate_request(csr)
+ except ValueError as e:
raise errors.CertificateOperationError(
error=_("Failure decoding Certificate Signing Request: %s") % e)
+ try:
+ ext_san = csr_obj.extensions.get_extension_for_oid(
+ cryptography.x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
+ except cryptography.x509.extensions.ExtensionNotFound:
+ ext_san = None
+
# self-service and host principals may bypass SAN permission check
if (bind_principal_string != principal_string
and bind_principal_type != HOST):
- if '2.5.29.17' in extensions:
+ if ext_san is not None:
self.check_access('request certificate with subjectaltname')
dn = None
@@ -650,10 +630,14 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
dn = principal_obj['dn']
# Ensure that the DN in the CSR matches the principal
- cn = subject.common_name #pylint: disable=E1101
- if not cn:
+ #
+ # We only look at the "most specific" CN value
+ cns = csr_obj.subject.get_attributes_for_oid(
+ cryptography.x509.oid.NameOID.COMMON_NAME)
+ if len(cns) == 0:
raise errors.ValidationError(name='csr',
error=_("No Common Name was found in subject of request."))
+ cn = cns[-1].value # "most specific" is end of list
if principal_type in (SERVICE, HOST):
if cn.lower() != principal.hostname.lower():
@@ -670,8 +654,11 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
)
# check email address
- mail = subject.email_address #pylint: disable=E1101
- if mail is not None and mail not in principal_obj.get('mail', []):
+ #
+ # fail if any email addr from DN does not appear in ldap entry
+ email_addrs = csr_obj.subject.get_attributes_for_oid(
+ cryptography.x509.oid.NameOID.EMAIL_ADDRESS)
+ if len(set(email_addrs) - set(principal_obj.get('mail', []))) > 0:
raise errors.ValidationError(
name='csr',
error=_(
@@ -685,9 +672,12 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
"to the 'userCertificate' attribute of entry '%s'.") % dn)
# Validate the subject alt name, if any
- for name_type, desc, name, _der_name in subjectaltname:
- if name_type == nss.certDNSName:
- name = unicode(name)
+ generalnames = []
+ if ext_san is not None:
+ generalnames = x509.process_othernames(ext_san.value)
+ for gn in generalnames:
+ if isinstance(gn, cryptography.x509.general_name.DNSName):
+ name = gn.value
alt_principal = None
alt_principal_obj = None
try:
@@ -703,8 +693,9 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
elif principal_type == USER:
raise errors.ValidationError(
name='csr',
- error=_("subject alt name type %s is forbidden "
- "for user principals") % desc
+ error=_(
+ "subject alt name type %s is forbidden "
+ "for user principals") % "DNSName"
)
except errors.NotFound:
# We don't want to issue any certificates referencing
@@ -721,17 +712,15 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
"with subject alt name '%s'.") % name)
if alt_principal is not None and not bypass_caacl:
caacl_check(principal_type, alt_principal, ca, profile_id)
- elif name_type in [
- (nss.certOtherName, x509.SAN_UPN),
- (nss.certOtherName, x509.SAN_KRB5PRINCIPALNAME),
- ]:
- if name != principal_string:
+ elif isinstance(gn, (x509.KRB5PrincipalName, x509.UPN)):
+ if gn.name != principal_string:
raise errors.ACIError(
- info=_("Principal '%s' in subject alt name does not "
- "match requested principal") % name)
- elif name_type == nss.certRFC822Name:
+ info=_(
+ "Principal '%s' in subject alt name does not "
+ "match requested principal") % gn.name)
+ elif isinstance(gn, cryptography.x509.general_name.RFC822Name):
if principal_type == USER:
- if name not in principal_obj.get('mail', []):
+ if gn.value not in principal_obj.get('mail', []):
raise errors.ValidationError(
name='csr',
error=_(
@@ -741,12 +730,14 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
else:
raise errors.ValidationError(
name='csr',
- error=_("subject alt name type %s is forbidden "
- "for non-user principals") % desc
+ error=_(
+ "subject alt name type %s is forbidden "
+ "for non-user principals") % "RFC822Name"
)
else:
raise errors.ACIError(
- info=_("Subject alt name type %s is forbidden") % desc)
+ info=_("Subject alt name type %s is forbidden")
+ % type(gn).__name__)
# Request the certificate
try: