From e3acc3659c6349a0de837f9441c6324055d9a100 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Aug 26 2016 07:09:45 +0000 Subject: x509: use NSS enums and OIDs to identify SAN types GeneralName parsing currently relies heavily on strings from NSS. Make the code hopefully less brittle by identifying GeneralName types by NSS enums and, for otherName, the name-type OID also. Part of: https://fedorahosted.org/freeipa/ticket/6022 Reviewed-By: Jan Cholasta --- diff --git a/ipalib/x509.py b/ipalib/x509.py index 2dc6744..541609f 100644 --- a/ipalib/x509.py +++ b/ipalib/x509.py @@ -33,6 +33,7 @@ from __future__ import print_function +import collections import os import sys import base64 @@ -63,10 +64,8 @@ EKU_EMAIL_PROTECTION = '1.3.6.1.5.5.7.3.4' EKU_ANY = '2.5.29.37.0' EKU_PLACEHOLDER = '1.3.6.1.4.1.3319.6.10.16' -SAN_DNSNAME = 'DNS name' -SAN_RFC822NAME = 'RFC822 Name' -SAN_OTHERNAME_UPN = 'Other Name (OID.1.3.6.1.4.1.311.20.2.3)' -SAN_OTHERNAME_KRB5PRINCIPALNAME = 'Other Name (OID.1.3.6.1.5.2.2)' +SAN_UPN = '1.3.6.1.4.1.311.20.2.3' +SAN_KRB5PRINCIPALNAME = '1.3.6.1.5.2.2' _subject_base = None @@ -465,6 +464,10 @@ def _decode_krb5principalname(data): return name +GeneralNameInfo = collections.namedtuple( + 'GeneralNameInfo', ('type', 'desc', 'value')) + + def decode_generalnames(secitem): """ Decode a GeneralNames object (this the data for the Subject @@ -482,12 +485,25 @@ def decode_generalnames(secitem): asn1_names = decoder.decode(secitem.data, asn1Spec=_SubjectAltName())[0] names = [] for nss_name, asn1_name in zip(nss_names, asn1_names): - name_type = nss_name.type_string - if name_type == SAN_OTHERNAME_KRB5PRINCIPALNAME: + # NOTE: we use the NSS enum to identify the name type. + # (For otherName we also tuple it up with the type-id OID). + # The enum does not correspond exactly to the ASN.1 tags. + # If we ever want to switch to using the true tag numbers, + # the expression to get the tag is: + # + # asn1_name.getComponent().getTagSet()[0].asTuple()[2] + # + if nss_name.type_enum == nss.certOtherName: + oid = str(asn1_name['otherName']['type-id']) + nametype = (nss_name.type_enum, oid) + else: + nametype = nss_name.type_enum + + if nametype == (nss.certOtherName, SAN_KRB5PRINCIPALNAME): name = _decode_krb5principalname(asn1_name['otherName']['value']) else: name = nss_name.name - names.append((name_type, name)) + names.append(GeneralNameInfo(nametype, nss_name.type_string, name)) return names diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py index c259650..3e9eda5 100644 --- a/ipaserver/plugins/cert.py +++ b/ipaserver/plugins/cert.py @@ -559,8 +559,8 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): "to the 'userCertificate' attribute of entry '%s'.") % dn) # Validate the subject alt name, if any - for name_type, name in subjectaltname: - if name_type == x509.SAN_DNSNAME: + for name_type, desc, name in subjectaltname: + if name_type == nss.certDNSName: name = unicode(name) alt_principal_obj = None alt_principal_string = unicode(principal) @@ -574,7 +574,7 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): raise errors.ValidationError( name='csr', error=_("subject alt name type %s is forbidden " - "for user principals") % name_type + "for user principals") % desc ) except errors.NotFound: # We don't want to issue any certificates referencing @@ -591,13 +591,15 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): "with subject alt name '%s'.") % name) if alt_principal_string is not None and not bypass_caacl: caacl_check(principal_type, principal, ca, profile_id) - elif name_type in (x509.SAN_OTHERNAME_KRB5PRINCIPALNAME, - x509.SAN_OTHERNAME_UPN): + elif name_type in [ + (nss.certOtherName, x509.SAN_UPN), + (nss.certOtherName, x509.SAN_KRB5PRINCIPALNAME), + ]: if name != principal_string: raise errors.ACIError( info=_("Principal '%s' in subject alt name does not " "match requested principal") % name) - elif name_type == x509.SAN_RFC822NAME: + elif name_type == nss.certRFC822Name: if principal_type == USER: if name not in principal_obj.get('mail', []): raise errors.ValidationError( @@ -610,12 +612,11 @@ class cert_request(Create, BaseCertMethod, VirtualCommand): raise errors.ValidationError( name='csr', error=_("subject alt name type %s is forbidden " - "for non-user principals") % name_type + "for non-user principals") % desc ) else: raise errors.ACIError( - info=_("Subject alt name type %s is forbidden") % - name_type) + info=_("Subject alt name type %s is forbidden") % desc) # Request the certificate result = self.Backend.ra.request_certificate(