diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am index b50a487..c7fbe21 100644 --- a/doc/manpages/Makefile.am +++ b/doc/manpages/Makefile.am @@ -428,8 +428,11 @@ APIMANS += gnutls_x509_crt_get_subject_key_id.3 APIMANS += gnutls_x509_crt_get_authority_key_id.3 APIMANS += gnutls_x509_crt_get_pk_algorithm.3 APIMANS += gnutls_x509_crt_get_subject_alt_name.3 +APIMANS += gnutls_x509_crt_get_issuer_alt_name.3 APIMANS += gnutls_x509_crt_get_subject_alt_name2.3 +APIMANS += gnutls_x509_crt_get_issuer_alt_name2.3 APIMANS += gnutls_x509_crt_get_subject_alt_othername_oid.3 +APIMANS += gnutls_x509_crt_get_issuer_alt_othername_oid.3 APIMANS += gnutls_x509_crt_get_basic_constraints.3 APIMANS += gnutls_x509_crt_get_ca_status.3 APIMANS += gnutls_x509_crt_get_key_usage.3 diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h index 2a87cba..e61ef25 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -198,6 +198,21 @@ extern "C" void *ret, size_t * ret_size); + int gnutls_x509_crt_get_issuer_alt_name (gnutls_x509_crt_t cert, + unsigned int seq, void *ret, + size_t * ret_size, + unsigned int *critical); + int gnutls_x509_crt_get_issuer_alt_name2 (gnutls_x509_crt_t cert, + unsigned int seq, void *ret, + size_t * ret_size, + unsigned int *ret_type, + unsigned int *critical); + + int gnutls_x509_crt_get_issuer_alt_othername_oid (gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, + size_t * ret_size); + int gnutls_x509_crt_get_ca_status (gnutls_x509_crt_t cert, unsigned int *critical); int gnutls_x509_crt_get_basic_constraints (gnutls_x509_crt_t cert, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 73dc6aa..887fef5 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -562,6 +562,9 @@ GNUTLS_2_8 gnutls_x509_crq_set_key_purpose_oid; gnutls_x509_crq_set_key_usage; gnutls_x509_crq_set_subject_alt_name; + gnutls_x509_crt_get_issuer_alt_name2; + gnutls_x509_crt_get_issuer_alt_name; + gnutls_x509_crt_get_issuer_alt_othername_oid; gnutls_x509_crt_get_verify_algorithm; gnutls_x509_crt_set_crq_extensions; gnutls_x509_crt_verify_hash; diff --git a/lib/x509/output.c b/lib/x509/output.c index e170a9b..c8fdbe1 100644 --- a/lib/x509/output.c +++ b/lib/x509/output.c @@ -687,6 +687,148 @@ print_san (gnutls_string * str, const char *prefix, int type, } static void +print_ian (gnutls_string * str, const char *prefix, int type, + cert_type_t cert) +{ + unsigned int ian_idx; + char str_ip[64]; + char *p; + + for (ian_idx = 0;; ian_idx++) + { + char *buffer = NULL; + size_t size = 0; + int err; + + if (type == TYPE_CRT) + err = + gnutls_x509_crt_get_issuer_alt_name (cert.crt, ian_idx, buffer, + &size, NULL); + else + return; + + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + addf (str, "error: get_issuer_alt_name: %s\n", + gnutls_strerror (err)); + return; + } + + buffer = gnutls_malloc (size); + if (!buffer) + { + addf (str, "error: malloc: %s\n", + gnutls_strerror (GNUTLS_E_MEMORY_ERROR)); + return; + } + + if (type == TYPE_CRT) + err = + gnutls_x509_crt_get_issuer_alt_name (cert.crt, ian_idx, buffer, + &size, NULL); + + if (err < 0) + { + gnutls_free (buffer); + addf (str, "error: get_issuer_alt_name2: %s\n", + gnutls_strerror (err)); + return; + } + + switch (err) + { + case GNUTLS_SAN_DNSNAME: + addf (str, "%s\t\t\tDNSname: %.*s\n", prefix, (int) size, buffer); + break; + + case GNUTLS_SAN_RFC822NAME: + addf (str, "%s\t\t\tRFC822name: %.*s\n", prefix, (int) size, buffer); + break; + + case GNUTLS_SAN_URI: + addf (str, "%s\t\t\tURI: %.*s\n", prefix, (int) size, buffer); + break; + + case GNUTLS_SAN_IPADDRESS: + p = ip_to_string (buffer, size, str_ip, sizeof (str_ip)); + if (p == NULL) + p = ERROR_STR; + addf (str, "%s\t\t\tIPAddress: %s\n", prefix, p); + break; + + case GNUTLS_SAN_DN: + addf (str, "%s\t\t\tdirectoryName: %.*s\n", prefix, + (int) size, buffer); + break; + + case GNUTLS_SAN_OTHERNAME: + { + char *oid = NULL; + size_t oidsize; + + oidsize = 0; + if (type == TYPE_CRT) + err = gnutls_x509_crt_get_issuer_alt_othername_oid + (cert.crt, ian_idx, oid, &oidsize); + + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_free (buffer); + addf (str, "error: get_issuer_alt_othername_oid: %s\n", + gnutls_strerror (err)); + return; + } + + oid = gnutls_malloc (oidsize); + if (!oid) + { + gnutls_free (buffer); + addf (str, "error: malloc: %s\n", + gnutls_strerror (GNUTLS_E_MEMORY_ERROR)); + return; + } + + if (type == TYPE_CRT) + err = gnutls_x509_crt_get_issuer_alt_othername_oid + (cert.crt, ian_idx, oid, &oidsize); + if (err < 0) + { + gnutls_free (buffer); + gnutls_free (oid); + addf (str, "error: get_issuer_alt_othername_oid2: %s\n", + gnutls_strerror (err)); + return; + } + + if (err == GNUTLS_SAN_OTHERNAME_XMPP) + addf (str, _("%s\t\t\tXMPP Address: %.*s\n"), prefix, + (int) size, buffer); + else + { + addf (str, _("%s\t\t\totherName OID: %.*s\n"), prefix, + (int) oidsize, oid); + addf (str, _("%s\t\t\totherName DER: "), prefix); + hexprint (str, buffer, size); + addf (str, _("\n%s\t\t\totherName ASCII: "), prefix); + asciiprint (str, buffer, size); + addf (str, "\n"); + } + gnutls_free (oid); + } + break; + + default: + addf (str, "error: unknown Issuer AltName\n"); + break; + } + + gnutls_free (buffer); + } +} + +static void print_extensions (gnutls_string * str, const char *prefix, int type, cert_type_t cert) { @@ -698,6 +840,7 @@ print_extensions (gnutls_string * str, const char *prefix, int type, size_t sizeof_oid = sizeof (oid); int critical; size_t san_idx = 0; + size_t ian_idx = 0; size_t proxy_idx = 0; size_t basic_idx = 0; size_t keyusage_idx = 0; @@ -828,6 +971,21 @@ print_extensions (gnutls_string * str, const char *prefix, int type, san_idx++; } + else if (strcmp (oid, "2.5.29.18") == 0) + { + if (ian_idx) + { + addf (str, "error: more than one Issuer AltName extension\n"); + continue; + } + + addf (str, _("%s\t\tIssuer Alternative Name (%s):\n"), prefix, + critical ? _("critical") : _("not critical")); + + print_ian (str, prefix, type, cert); + + ian_idx++; + } else if (strcmp (oid, "2.5.29.31") == 0) { if (crldist_idx) diff --git a/lib/x509/x509.c b/lib/x509/x509.c index 048ff89..103253d 100644 --- a/lib/x509/x509.c +++ b/lib/x509/x509.c @@ -1079,10 +1079,10 @@ _gnutls_parse_general_name (ASN1_TYPE src, const char *src_name, } static int -get_subject_alt_name (gnutls_x509_crt_t cert, - unsigned int seq, void *ret, - size_t * ret_size, unsigned int *ret_type, - unsigned int *critical, int othername_oid) +get_alt_name(gnutls_x509_crt_t cert, const char *extension_id, + unsigned int seq, void *ret, + size_t * ret_size, unsigned int *ret_type, + unsigned int *critical, int othername_oid) { int result; gnutls_datum_t dnsname; @@ -1101,7 +1101,7 @@ get_subject_alt_name (gnutls_x509_crt_t cert, *ret_size = 0; if ((result = - _gnutls_x509_crt_get_extension (cert, "2.5.29.17", 0, &dnsname, + _gnutls_x509_crt_get_extension (cert, extension_id, 0, &dnsname, critical)) < 0) { return result; @@ -1113,8 +1113,20 @@ get_subject_alt_name (gnutls_x509_crt_t cert, return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; } - result = asn1_create_element - (_gnutls_get_pkix (), "PKIX1.SubjectAltName", &c2); + if (strcmp("2.5.29.17", extension_id) == 0) + { + result = asn1_create_element(_gnutls_get_pkix (), "PKIX1.SubjectAltName", &c2); + } + else if (strcmp("2.5.29.18", extension_id) == 0) + { + result = asn1_create_element(_gnutls_get_pkix (), "PKIX1.IssuerAltName", &c2); + } + else + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + if (result != ASN1_SUCCESS) { gnutls_assert (); @@ -1188,7 +1200,49 @@ gnutls_x509_crt_get_subject_alt_name (gnutls_x509_crt_t cert, size_t * ret_size, unsigned int *critical) { - return get_subject_alt_name (cert, seq, ret, ret_size, NULL, critical, 0); + return get_alt_name (cert, "2.5.29.17", seq, ret, ret_size, NULL, critical, 0); +} + +/** + * gnutls_x509_crt_get_issuer_alt_name - Get certificate's issuer alternative name, if any + * @cert: should contain a #gnutls_x509_crt_t structure + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ret: is the place where the alternative name will be copied to + * @ret_size: holds the size of ret. + * @critical: will be non zero if the extension is marked as critical (may be null) + * + * This function will return the issuer alternative names, contained in the + * given certificate. + * + * This is specified in X509v3 Certificate Extensions. GNUTLS will + * return the Isssuer Alternative name (2.5.29.18), or a negative error code. + * + * When the SAN type is otherName, it will extract the data in the + * otherName's value field, and %GNUTLS_SAN_OTHERNAME is returned. + * You may use gnutls_x509_crt_get_subject_alt_othername_oid() to get + * the corresponding OID and the "virtual" SAN types (e.g., + * %GNUTLS_SAN_OTHERNAME_XMPP). + * + * If an otherName OID is known, the data will be decoded. Otherwise + * the returned data will be DER encoded, and you will have to decode + * it yourself. Currently, only the RFC 3920 id-on-xmppAddr Issuer AltName + * is recognized. + * + * Returns: the alternative issuer name type on success, one of the + * enumerated #gnutls_x509_subject_alt_name_t. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ret_size is not large enough + * to hold the value. In that case @ret_size will be updated with + * the required size. If the certificate does not have an + * Alternative name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + **/ +int +gnutls_x509_crt_get_issuer_alt_name (gnutls_x509_crt_t cert, + unsigned int seq, void *ret, + size_t * ret_size, + unsigned int *critical) +{ + return get_alt_name (cert, "2.5.29.18", seq, ret, ret_size, NULL, critical, 0); } /** @@ -1222,8 +1276,41 @@ gnutls_x509_crt_get_subject_alt_name2 (gnutls_x509_crt_t cert, unsigned int *ret_type, unsigned int *critical) { - return get_subject_alt_name (cert, seq, ret, ret_size, ret_type, critical, - 0); + return get_alt_name (cert, "2.5.29.17", seq, ret, ret_size, ret_type, critical, 0); +} + +/** + * gnutls_x509_crt_get_issuer_alt_name2 - Get certificate issuer's alternative name, if any + * @cert: should contain a #gnutls_x509_crt_t structure + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ret: is the place where the alternative name will be copied to + * @ret_size: holds the size of ret. + * @ret_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t). + * @critical: will be non zero if the extension is marked as critical (may be null) + * + * This function will return the alternative names, contained in the + * given certificate. It is the same as + * gnutls_x509_crt_get_issuer_alt_name() except for the fact that it + * will return the type of the alternative name in @ret_type even if + * the function fails for some reason (i.e. the buffer provided is + * not enough). + * + * Returns: the alternative issuer name type on success, one of the + * enumerated #gnutls_x509_subject_alt_name_t. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ret_size is not large enough + * to hold the value. In that case @ret_size will be updated with + * the required size. If the certificate does not have an + * Alternative name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + **/ +int +gnutls_x509_crt_get_issuer_alt_name2 (gnutls_x509_crt_t cert, + unsigned int seq, void *ret, + size_t * ret_size, + unsigned int *ret_type, + unsigned int *critical) +{ + return get_alt_name (cert, "2.5.29.18", seq, ret, ret_size, ret_type, critical, 0); } /** @@ -1257,7 +1344,41 @@ gnutls_x509_crt_get_subject_alt_othername_oid (gnutls_x509_crt_t cert, unsigned int seq, void *ret, size_t * ret_size) { - return get_subject_alt_name (cert, seq, ret, ret_size, NULL, NULL, 1); + return get_alt_name (cert, "2.5.29.17", seq, ret, ret_size, NULL, NULL, 1); +} + +/** + * gnutls_x509_crt_get_issuer_alt_othername_oid - Get Issuer AltName otherName OID + * @cert: should contain a #gnutls_x509_crt_t structure + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ret: is the place where the otherName OID will be copied to + * @ret_size: holds the size of ret. + * + * This function will extract the type OID of an otherName Subject + * Alternative Name, contained in the given certificate, and return + * the type as an enumerated element. + * + * This function is only useful if + * gnutls_x509_crt_get_issuer_alt_name() returned + * %GNUTLS_SAN_OTHERNAME. + * + * Returns: the alternative issuer name type on success, one of the + * enumerated gnutls_x509_subject_alt_name_t. For supported OIDs, it + * will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types, + * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for + * unknown OIDs. It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if + * @ret_size is not large enough to hold the value. In that case + * @ret_size will be updated with the required size. If the + * certificate does not have an Alternative name with the specified + * sequence number and with the otherName type then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + **/ +int +gnutls_x509_crt_get_issuer_alt_othername_oid (gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, size_t * ret_size) +{ + return get_alt_name (cert, "2.5.29.18", seq, ret, ret_size, NULL, NULL, 1); } /** diff --git a/tests/Makefile.am b/tests/Makefile.am index 5538fb7..4779c64 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -58,7 +58,7 @@ ctests = simple gc set_pkcs12_cred certder mpi \ finished hostname-check cve-2008-4989 pkcs12_s2k chainverify \ crq_key_id x509sign-verify cve-2009-1415 cve-2009-1416 \ crq_apis init_roundtrip pkcs12_s2k_pem dn2 mini-eagain \ - nul-in-x509-names + nul-in-x509-names x509_altname if ENABLE_OPENSSL ctests += openssl