summaryrefslogtreecommitdiffstats
path: root/ipaserver/plugins/cert.py
diff options
context:
space:
mode:
authorFraser Tweedale <ftweedal@redhat.com>2016-07-14 21:36:33 +1000
committerJan Cholasta <jcholast@redhat.com>2016-08-26 09:09:45 +0200
commit48aaf2bbf5df6dcedaa466753c8fafb478cb31b2 (patch)
treec39d3aecceebe3180588e49ef2718cedb3584c0e /ipaserver/plugins/cert.py
parenta381d888cd6effc480c373f19f6a0ecbf00c4182 (diff)
downloadfreeipa-48aaf2bbf5df6dcedaa466753c8fafb478cb31b2.tar.gz
freeipa-48aaf2bbf5df6dcedaa466753c8fafb478cb31b2.tar.xz
freeipa-48aaf2bbf5df6dcedaa466753c8fafb478cb31b2.zip
cert-show: show subject alternative names
Enhance the cert-show command to return subject alternative name values. Fixes: https://fedorahosted.org/freeipa/ticket/6022 Reviewed-By: Jan Cholasta <jcholast@redhat.com>
Diffstat (limited to 'ipaserver/plugins/cert.py')
-rw-r--r--ipaserver/plugins/cert.py129
1 files changed, 126 insertions, 3 deletions
diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
index 9ee0b38c0..1184f8059 100644
--- a/ipaserver/plugins/cert.py
+++ b/ipaserver/plugins/cert.py
@@ -39,7 +39,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
@@ -50,6 +50,7 @@ from ipalib.request import context
from ipalib import output
from ipapython import kerberos
from ipapython.dn import DN
+from ipapython.ipa_log_manager import root_logger
from ipaserver.plugins.service import normalize_principal, validate_realm
if six.PY3:
@@ -274,6 +275,61 @@ class BaseCertObject(Object):
label=_('Subject'),
flags={'no_create', 'no_update', 'no_search'},
),
+ Str(
+ 'san_rfc822name*',
+ label=_('Subject email address'),
+ flags={'no_create', 'no_update', 'no_search'},
+ ),
+ DNSNameParam(
+ 'san_dnsname*',
+ label=_('Subject DNS name'),
+ flags={'no_create', 'no_update', 'no_search'},
+ ),
+ Str(
+ 'san_x400address*',
+ label=_('Subject X.400 address'),
+ flags={'no_create', 'no_update', 'no_search'},
+ ),
+ DNParam(
+ 'san_directoryname*',
+ label=_('Subject directory name'),
+ flags={'no_create', 'no_update', 'no_search'},
+ ),
+ Str(
+ 'san_edipartyname*',
+ label=_('Subject EDI Party name'),
+ flags={'no_create', 'no_update', 'no_search'},
+ ),
+ Str(
+ 'san_uri*',
+ label=_('Subject URI'),
+ flags={'no_create', 'no_update', 'no_search'},
+ ),
+ Str(
+ 'san_ipaddress*',
+ label=_('Subject IP Address'),
+ flags={'no_create', 'no_update', 'no_search'},
+ ),
+ Str(
+ 'san_oid*',
+ label=_('Subject OID'),
+ flags={'no_create', 'no_update', 'no_search'},
+ ),
+ Principal(
+ 'san_other_upn*',
+ label=_('Subject UPN'),
+ flags={'no_create', 'no_update', 'no_search'},
+ ),
+ Principal(
+ 'san_other_kpn*',
+ label=_('Subject Kerberos principal name'),
+ flags={'no_create', 'no_update', 'no_search'},
+ ),
+ Str(
+ 'san_other*',
+ label=_('Subject Other Name'),
+ flags={'no_create', 'no_update', 'no_search'},
+ ),
DNParam(
'issuer',
label=_('Issuer'),
@@ -315,6 +371,19 @@ class BaseCertObject(Object):
)
def _parse(self, obj, full=True):
+ """Extract certificate-specific data into a result object.
+
+ ``obj``
+ Result object containing certificate, into which extracted
+ data will be inserted.
+ ``full``
+ Whether to include all fields, or only the ones we guess
+ people want to see most of the time. Also add
+ recognised otherNames to the generic ``san_other``
+ attribute when ``True`` in addition to the specialised
+ attribute.
+
+ """
cert = obj.get('certificate')
if cert is not None:
cert = x509.load_certificate(cert)
@@ -329,11 +398,65 @@ class BaseCertObject(Object):
obj['sha1_fingerprint'] = unicode(
nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])
+ 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, der_name in general_names:
+ try:
+ self._add_san_attribute(
+ obj, full, name_type, name, der_name)
+ except Exception as e:
+ # Invalid GeneralName (i.e. not a valid X.509 cert);
+ # don't fail but log something about it
+ root_logger.warning(
+ "Encountered bad GeneralName; skipping", exc_info=True)
+
serial_number = obj.get('serial_number')
if serial_number is not None:
obj['serial_number_hex'] = u'0x%X' % serial_number
+ def _add_san_attribute(
+ self, obj, full, name_type, name, der_name):
+ name_type_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',
+ }
+ default_attrs = {
+ 'san_rfc822name', 'san_dnsname', 'san_other_upn', 'san_other_kpn',
+ }
+
+ attr_name = name_type_map.get(name_type, 'san_other')
+
+ if full or attr_name in default_attrs:
+ if attr_name != 'san_other':
+ name_formatted = name
+ else:
+ # display as "OID : b64(DER)"
+ name_formatted = u'{}:{}'.format(
+ name_type[1], base64.b64encode(der_name))
+ attr_value = self.params[attr_name].type(name_formatted)
+ obj.setdefault(attr_name, []).append(attr_value)
+
+ if full and attr_name.startswith('san_other_'):
+ # also include known otherName in generic otherName attribute
+ name_formatted = u'{}:{}'.format(
+ name_type[1], base64.b64encode(der_name))
+ attr_value = self.params['san_other'].type(name_formatted)
+ obj.setdefault('san_other', []).append(attr_value)
+
+
class BaseCertMethod(Method):
def get_options(self):
yield Str('cacn?',
@@ -622,7 +745,7 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
result = self.Backend.ra.request_certificate(
csr, profile_id, ca_id, request_type=request_type)
if not raw:
- self.obj._parse(result)
+ self.obj._parse(result, all)
result['request_id'] = int(result['request_id'])
# Success? Then add it to the principal's entry
@@ -800,7 +923,7 @@ class cert_show(Retrieve, CertMethod, VirtualCommand):
if not raw:
result['certificate'] = result['certificate'].replace('\r\n', '')
- self.obj._parse(result)
+ self.obj._parse(result, all)
result['revoked'] = ('revocation_reason' in result)
self.obj._fill_owners(result)