On Tue, Jul 19, 2016 at 08:50:34AM +0200, Jan Cholasta wrote:
> Hi,
> 
> On 14.7.2016 13:44, Fraser Tweedale wrote:
> > Hi all,
> > 
> > The attached patch includes SANs in cert-show output.  If you have
> > certs with esoteric altnames (especially any that are more than just
> > ASN.1 string types), please test with those certs.
> > 
> > https://fedorahosted.org/freeipa/ticket/6022
> 
> I think it would be better to have a separate attribute for each supported
> SAN type rather than cramming everything into subject_alt_name. That way if
> you care only about a single specific type you won't have to go through all
> the values and parse them. Also it would allow you to use param types
> appropriate to the SAN types (DNSNameParam for DNS names, Principal for
> principal names, etc.)
> 
> Nitpick: please don't mix moving existing stuff and adding new stuff in a
> single patch.
> 
Updated patches attached.

Patches 0092..0094 are refactors and bugfixes.
Patch 0090-2 is the main feature (depends on 0092..0094).

Thanks,
Fraser
From 0f85eba4efdd9281725c54268e8d213c412edebf Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Thu, 21 Jul 2016 16:27:49 +1000
Subject: [PATCH 92/94] Move GeneralName parsing code to ipalib.x509

GeneralName parsing code is primarily relevant to X.509.  An
upcoming change will add SAN parsing to the cert-show command, so
first move the GeneralName parsing code from ipalib.pkcs10 to
ipalib.x509.

Part of: https://fedorahosted.org/freeipa/ticket/6022
---
 ipalib/pkcs10.py          |  93 ++-----------------------------------
 ipalib/x509.py            | 114 +++++++++++++++++++++++++++++++++++++++++++++-
 ipaserver/plugins/cert.py |   8 ++--
 3 files changed, 120 insertions(+), 95 deletions(-)

diff --git a/ipalib/pkcs10.py b/ipalib/pkcs10.py
index 
e340c1a2005ad781542a32a0a76753e80364dacf..158ebb3a25be2bd292f3883545fe8afe49b7be8c
 100644
--- a/ipalib/pkcs10.py
+++ b/ipalib/pkcs10.py
@@ -22,9 +22,10 @@ from __future__ import print_function
 import sys
 import base64
 import nss.nss as nss
-from pyasn1.type import univ, char, namedtype, tag
+from pyasn1.type import univ, namedtype, tag
 from pyasn1.codec.der import decoder
 import six
+from ipalib import x509
 
 if six.PY3:
     unicode = str
@@ -32,11 +33,6 @@ if six.PY3:
 PEM = 0
 DER = 1
 
-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)'
-
 def get_subject(csr, datatype=PEM):
     """
     Given a CSR return the subject value.
@@ -72,78 +68,6 @@ def get_extensions(csr, datatype=PEM):
     return tuple(get_prefixed_oid_str(ext)[4:]
                  for ext in request.extensions)
 
-class _PrincipalName(univ.Sequence):
-    componentType = namedtype.NamedTypes(
-        namedtype.NamedType('name-type', univ.Integer().subtype(
-            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
-        ),
-        namedtype.NamedType('name-string', 
univ.SequenceOf(char.GeneralString()).subtype(
-            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
-        ),
-    )
-
-class _KRB5PrincipalName(univ.Sequence):
-    componentType = namedtype.NamedTypes(
-        namedtype.NamedType('realm', char.GeneralString().subtype(
-            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
-        ),
-        namedtype.NamedType('principalName', _PrincipalName().subtype(
-            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
-        ),
-    )
-
-def _decode_krb5principalname(data):
-    principal = decoder.decode(data, asn1Spec=_KRB5PrincipalName())[0]
-    realm = (str(principal['realm']).replace('\\', '\\\\')
-                                    .replace('@', '\\@'))
-    name = principal['principalName']['name-string']
-    name = '/'.join(str(n).replace('\\', '\\\\')
-                          .replace('/', '\\/')
-                          .replace('@', '\\@') for n in name)
-    name = '%s@%s' % (name, realm)
-    return name
-
-class _AnotherName(univ.Sequence):
-    componentType = namedtype.NamedTypes(
-        namedtype.NamedType('type-id', univ.ObjectIdentifier()),
-        namedtype.NamedType('value', univ.Any().subtype(
-            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
-        ),
-    )
-
-class _GeneralName(univ.Choice):
-    componentType = namedtype.NamedTypes(
-        namedtype.NamedType('otherName', _AnotherName().subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
-        ),
-        namedtype.NamedType('rfc822Name', char.IA5String().subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
-        ),
-        namedtype.NamedType('dNSName', char.IA5String().subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))
-        ),
-        namedtype.NamedType('x400Address', univ.Sequence().subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))
-        ),
-        namedtype.NamedType('directoryName', univ.Choice().subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
-        ),
-        namedtype.NamedType('ediPartyName', univ.Sequence().subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 5))
-        ),
-        namedtype.NamedType('uniformResourceIdentifier', 
char.IA5String().subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 6))
-        ),
-        namedtype.NamedType('iPAddress', univ.OctetString().subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 7))
-        ),
-        namedtype.NamedType('registeredID', univ.ObjectIdentifier().subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 8))
-        ),
-    )
-
-class _SubjectAltName(univ.SequenceOf):
-    componentType = _GeneralName()
 
 def get_subjectaltname(csr, datatype=PEM):
     """
@@ -159,19 +83,8 @@ def get_subjectaltname(csr, datatype=PEM):
         return None
     del request
 
-    nss_names = nss.x509_alt_name(extension.value, nss.AsObject)
-    asn1_names = decoder.decode(extension.value.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:
-            name = _decode_krb5principalname(asn1_name['otherName']['value'])
-        else:
-            name = nss_name.name
-        names.append((name_type, name))
+    return x509.decode_generalnames(extension.value)
 
-    return tuple(names)
 
 # Unfortunately, NSS can only parse the extension request attribute, so
 # we have to parse friendly name ourselves (see RFC 2986)
diff --git a/ipalib/x509.py b/ipalib/x509.py
index 
82194922d151a1b0f2df03df3578ad45b43b71c9..15168de08240a84794efef409d022eaa983291c9
 100644
--- a/ipalib/x509.py
+++ b/ipalib/x509.py
@@ -40,7 +40,7 @@ import re
 
 import nss.nss as nss
 from nss.error import NSPRError
-from pyasn1.type import univ, namedtype, tag
+from pyasn1.type import univ, char, namedtype, tag
 from pyasn1.codec.der import decoder, encoder
 import six
 
@@ -63,6 +63,11 @@ 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)'
+
 _subject_base = None
 
 def subject_base():
@@ -374,6 +379,113 @@ def encode_ext_key_usage(ext_key_usage):
     eku = encoder.encode(eku)
     return _encode_extension('2.5.29.37', EKU_ANY not in ext_key_usage, eku)
 
+
+class _AnotherName(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('type-id', univ.ObjectIdentifier()),
+        namedtype.NamedType('value', univ.Any().subtype(
+            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
+        ),
+    )
+
+
+class _GeneralName(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('otherName', _AnotherName().subtype(
+            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
+        ),
+        namedtype.NamedType('rfc822Name', char.IA5String().subtype(
+            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
+        ),
+        namedtype.NamedType('dNSName', char.IA5String().subtype(
+            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))
+        ),
+        namedtype.NamedType('x400Address', univ.Sequence().subtype(
+            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))
+        ),
+        namedtype.NamedType('directoryName', univ.Choice().subtype(
+            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
+        ),
+        namedtype.NamedType('ediPartyName', univ.Sequence().subtype(
+            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 5))
+        ),
+        namedtype.NamedType('uniformResourceIdentifier', 
char.IA5String().subtype(
+            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 6))
+        ),
+        namedtype.NamedType('iPAddress', univ.OctetString().subtype(
+            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 7))
+        ),
+        namedtype.NamedType('registeredID', univ.ObjectIdentifier().subtype(
+            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 8))
+        ),
+    )
+
+
+class _SubjectAltName(univ.SequenceOf):
+    componentType = _GeneralName()
+
+
+class _PrincipalName(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('name-type', univ.Integer().subtype(
+            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
+        ),
+        namedtype.NamedType('name-string', 
univ.SequenceOf(char.GeneralString()).subtype(
+            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
+        ),
+    )
+
+
+class _KRB5PrincipalName(univ.Sequence):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('realm', char.GeneralString().subtype(
+            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
+        ),
+        namedtype.NamedType('principalName', _PrincipalName().subtype(
+            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
+        ),
+    )
+
+
+def _decode_krb5principalname(data):
+    principal = decoder.decode(data, asn1Spec=_KRB5PrincipalName())[0]
+    realm = (str(principal['realm']).replace('\\', '\\\\')
+                                    .replace('@', '\\@'))
+    name = principal['principalName']['name-string']
+    name = '/'.join(str(n).replace('\\', '\\\\')
+                          .replace('/', '\\/')
+                          .replace('@', '\\@') for n in name)
+    name = '%s@%s' % (name, realm)
+    return name
+
+
+def decode_generalnames(secitem):
+    """
+    Decode a GeneralNames object (this the data for the Subject
+    Alt Name and Issuer Alt Name extensions, among others).
+
+    ``secitem``
+      The input is the DER-encoded extension data, without the
+      OCTET STRING header, as an nss SecItem object.
+
+    Return a list of tuples of name types (as string, suitable for
+    presentation) and names (as string, suitable for presentation).
+
+    """
+    nss_names = nss.x509_alt_name(secitem, repr_kind=nss.AsObject)
+    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:
+            name = _decode_krb5principalname(asn1_name['otherName']['value'])
+        else:
+            name = nss_name.name
+        names.append((name_type, name))
+
+    return names
+
+
 if __name__ == '__main__':
     # this can be run with:
     # python ipalib/x509.py < /etc/ipa/ca.crt
diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
index 
06041d3083565e8d093b610473d6083111d406d2..85be2cf4daeb282b2c2ba866017c8e5745abda6d
 100644
--- a/ipaserver/plugins/cert.py
+++ b/ipaserver/plugins/cert.py
@@ -535,7 +535,7 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
 
         # Validate the subject alt name, if any
         for name_type, name in subjectaltname:
-            if name_type == pkcs10.SAN_DNSNAME:
+            if name_type == x509.SAN_DNSNAME:
                 name = unicode(name)
                 alt_principal_obj = None
                 alt_principal_string = unicode(principal)
@@ -566,13 +566,13 @@ 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 (pkcs10.SAN_OTHERNAME_KRB5PRINCIPALNAME,
-                               pkcs10.SAN_OTHERNAME_UPN):
+            elif name_type in (x509.SAN_OTHERNAME_KRB5PRINCIPALNAME,
+                               x509.SAN_OTHERNAME_UPN):
                 if name != principal_string:
                     raise errors.ACIError(
                         info=_("Principal '%s' in subject alt name does not "
                                "match requested principal") % name)
-            elif name_type == pkcs10.SAN_RFC822NAME:
+            elif name_type == x509.SAN_RFC822NAME:
                 if principal_type == USER:
                     if name not in principal_obj.get('mail', []):
                         raise errors.ValidationError(
-- 
2.5.5

From e9af4c5cd0bbc488f0a65c576286b0dc4452110b Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Fri, 22 Jul 2016 12:05:13 +1000
Subject: [PATCH 93/94] x509: fix SAN directoryName parsing

The subjectAltName extension parsing code in ipalib.x509 fails on
directoryName values because the Choice structure is not endowed
with an inner type.  Implement the Name structure, whose inner type
is a CHOICE { SEQUENCE OF RelativeDistinguishedName }, to resolve.

Note that the structure still does not get fully parsed; only enough
to recognise the SequenceOf tag and not fail.

Part of: https://fedorahosted.org/freeipa/ticket/6022
---
 ipalib/x509.py | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/ipalib/x509.py b/ipalib/x509.py
index 
15168de08240a84794efef409d022eaa983291c9..2dc67441c92686826dd24f00a5ad30566cd032da
 100644
--- a/ipalib/x509.py
+++ b/ipalib/x509.py
@@ -196,6 +196,12 @@ def is_self_signed(certificate, datatype=PEM, dbdir=None):
     del nsscert
     return self_signed
 
+class _Name(univ.Choice):
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('rdnSequence',
+            univ.SequenceOf()),
+    )
+
 class _TBSCertificate(univ.Sequence):
     componentType = namedtype.NamedTypes(
         namedtype.NamedType(
@@ -204,9 +210,9 @@ class _TBSCertificate(univ.Sequence):
                 tag.tagClassContext, tag.tagFormatSimple, 0))),
         namedtype.NamedType('serialNumber', univ.Integer()),
         namedtype.NamedType('signature', univ.Sequence()),
-        namedtype.NamedType('issuer', univ.Sequence()),
+        namedtype.NamedType('issuer', _Name()),
         namedtype.NamedType('validity', univ.Sequence()),
-        namedtype.NamedType('subject', univ.Sequence()),
+        namedtype.NamedType('subject', _Name()),
         namedtype.NamedType('subjectPublicKeyInfo', univ.Sequence()),
         namedtype.OptionalNamedType(
             'issuerUniquedID',
@@ -403,7 +409,7 @@ class _GeneralName(univ.Choice):
         namedtype.NamedType('x400Address', univ.Sequence().subtype(
             implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))
         ),
-        namedtype.NamedType('directoryName', univ.Choice().subtype(
+        namedtype.NamedType('directoryName', _Name().subtype(
             implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
         ),
         namedtype.NamedType('ediPartyName', univ.Sequence().subtype(
-- 
2.5.5

From 26b24435264f76fdfa5ec02cc4aac52b40511c13 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Fri, 22 Jul 2016 12:11:59 +1000
Subject: [PATCH 94/94] 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
---
 ipalib/x509.py            | 30 +++++++++++++++++++++++-------
 ipaserver/plugins/cert.py | 19 ++++++++++---------
 2 files changed, 33 insertions(+), 16 deletions(-)

diff --git a/ipalib/x509.py b/ipalib/x509.py
index 
2dc67441c92686826dd24f00a5ad30566cd032da..541609fbc1a53a73eafcff2327e53a292c2d9a3c
 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 
85be2cf4daeb282b2c2ba866017c8e5745abda6d..207f6964645254ebc417cab80634a68911ae0a08
 100644
--- a/ipaserver/plugins/cert.py
+++ b/ipaserver/plugins/cert.py
@@ -534,8 +534,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)
@@ -549,7 +549,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
@@ -566,13 +566,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(
@@ -585,12 +587,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(
-- 
2.5.5

From 9991e76716312f5cd15bfb71e69ec95466872c9f Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Thu, 14 Jul 2016 21:36:33 +1000
Subject: [PATCH] cert-show: show subject alternative names

Enhance the cert-show command to return subject alternative name
values.

Fixes: https://fedorahosted.org/freeipa/ticket/6022
---
 ipaserver/plugins/cert.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 79 insertions(+), 1 deletion(-)

diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
index 
207f6964645254ebc417cab80634a68911ae0a08..606d6cdbc28d30892ab60ad4aeb41ecbbd646589
 100644
--- a/ipaserver/plugins/cert.py
+++ b/ipaserver/plugins/cert.py
@@ -38,7 +38,7 @@ from ipalib import ngettext
 from ipalib.constants import IPA_CA_CN
 from ipalib.crud import Create, PKQuery, Retrieve, Search
 from ipalib.frontend import Method, Object
-from ipalib.parameters import Bytes, DateTime, DNParam, Principal
+from ipalib.parameters import Bytes, DateTime, DNParam, DNSNameParam, Principal
 from ipalib.plugable import Registry
 from .virtual import VirtualCommand
 from .baseldap import pkey_to_value
@@ -293,6 +293,61 @@ class BaseCertObject(Object):
             label=_('Serial number (hex)'),
             flags={'no_create', 'no_update', 'no_search'},
         ),
+        Str(
+            'san_rfc822name*',
+            label=_('Subject Alternative Name (Email)'),
+            flags={'no_create', 'no_update', 'no_search'},
+        ),
+        DNSNameParam(
+            'san_dnsname*',
+            label=_('Subject Alternative Name (DNS)'),
+            flags={'no_create', 'no_update', 'no_search'},
+        ),
+        Str(
+            'san_x400address*',
+            label=_('Subject Alternative Name (X.400 address)'),
+            flags={'no_create', 'no_update', 'no_search'},
+        ),
+        Str(
+            'san_directoryname*',
+            label=_('Subject Alternative Name (Directory name)'),
+            flags={'no_create', 'no_update', 'no_search'},
+        ),
+        Str(
+            'san_edipartyname*',
+            label=_('Subject Alternative Name (EDI Party name)'),
+            flags={'no_create', 'no_update', 'no_search'},
+        ),
+        Str(
+            'san_uri*',
+            label=_('Subject Alternative Name (URI)'),
+            flags={'no_create', 'no_update', 'no_search'},
+        ),
+        Str(
+            'san_ipaddress*',
+            label=_('Subject Alternative Name (IP Address)'),
+            flags={'no_create', 'no_update', 'no_search'},
+        ),
+        Str(
+            'san_oid*',
+            label=_('Subject Alternative Name (OID)'),
+            flags={'no_create', 'no_update', 'no_search'},
+        ),
+        Principal(
+            'san_other_upn*',
+            label=_('Subject Alternative Name (UPN)'),
+            flags={'no_create', 'no_update', 'no_search'},
+        ),
+        Principal(
+            'san_other_kpn*',
+            label=_('Subject Alternative Name (Kerberos Principal)'),
+            flags={'no_create', 'no_update', 'no_search'},
+        ),
+        Str(
+            'san_other*',
+            label=_('Subject Alternative Name (Other Name)'),
+            flags={'no_create', 'no_update', 'no_search'},
+        ),
     )
 
     def _parse(self, obj):
@@ -308,6 +363,29 @@ class BaseCertObject(Object):
         obj['serial_number'] = cert.serial_number
         obj['serial_number_hex'] = u'0x%X' % cert.serial_number
 
+        attr_name_map = {
+            nss.certRFC822Name: 'san_rfc822name',
+            nss.certDNSName: 'san_dnsname',
+            nss.certX400Address: 'san_x400address',
+            nss.certDirectoryName: 'san_directoryname',
+            nss.certEDIPartyName: 'san_edipartyname',
+            nss.certURI: 'san_uri',
+            nss.certIPAddress: 'san_ipaddress',
+            nss.certRegisterID: 'san_oid',
+            (nss.certOtherName, x509.SAN_UPN): 'san_other_upn',
+            (nss.certOtherName, x509.SAN_KRB5PRINCIPALNAME): 'san_other_kpn',
+        }
+
+        try:
+            ext_san = cert.get_extension(nss.SEC_OID_X509_SUBJECT_ALT_NAME)
+            general_names = x509.decode_generalnames(ext_san.value)
+        except KeyError:
+            general_names = []
+
+        for name_type, desc, name in general_names:
+            attr_name = attr_name_map.get(name_type, 'san_other')
+            obj.setdefault(attr_name, []).append(unicode(name))
+
 
 class BaseCertMethod(Method):
     def get_options(self):
-- 
2.5.5

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to