[Upstream] x509: optimize subject alternative name access 94/117894/3
authorSaurav Babu <saurav.babu@samsung.com>
Tue, 7 Mar 2017 12:34:33 +0000 (18:04 +0530)
committerSaurav Babu <saurav.babu@samsung.com>
Mon, 3 Apr 2017 12:16:22 +0000 (17:46 +0530)
That reads SAN and IAN early on import, significantly reducing
the running time of functions which iterate over the alternative
names of a certificate, e.g., gnutls_x509_crt_check_hostname().

https://gitlab.com/gnutls/gnutls/issues/165
gnutls_x509_crt_check_hostname2() is slow for certificates with many
subject alternative names

gnutls_x509_crt_check_hostname2() can be computationally expensive for
with certificates that contain a larger amount of subject alternative
names. E.g. the first certificate for polytimer.rocks has 944 entries.

gnutls_x509_crt_check_hostname2() repetitively calls
gnutls_x509_crt_get_subject_alt_name() until it finds an alternative
name that matches the specified hostname. In case of the certificate to
polytimer.rocks, this match occurs on the 648th try. It takes around 30
secs to find this match in case of Tizen Phone.

This patch is partially added in Tizen as base code in Tizen is
different from that of Upstream

Change-Id: Iaba3e4f251b3bc2860c125e9ece06f24acae5c08
Signed-off-by: Saurav Babu <saurav.babu@samsung.com>
lib/x509/x509.c
lib/x509/x509_int.h

index 0c34b58..9a258bb 100644 (file)
@@ -64,6 +64,22 @@ int gnutls_x509_crt_init(gnutls_x509_crt_t * cert)
                return _gnutls_asn2err(result);
        }
 
+       result = gnutls_subject_alt_names_init(&tmp->san);
+       if (result < 0) {
+               gnutls_assert();
+               asn1_delete_structure(&tmp->cert);
+               gnutls_free(tmp);
+               return result;
+       }
+       result = gnutls_subject_alt_names_init(&tmp->ian);
+       if (result < 0) {
+               gnutls_assert();
+               asn1_delete_structure(&tmp->cert);
+               gnutls_free(tmp);
+               gnutls_subject_alt_names_deinit(tmp->san);
+               return result;
+       }
+
        /* If you add anything here, be sure to check if it has to be added
           to gnutls_x509_crt_import as well. */
 
@@ -141,9 +157,45 @@ void gnutls_x509_crt_deinit(gnutls_x509_crt_t cert)
                asn1_delete_structure(&cert->cert);
        gnutls_free(cert->raw_dn.data);
        gnutls_free(cert->raw_issuer_dn.data);
+       gnutls_subject_alt_names_deinit(cert->san);
+       gnutls_subject_alt_names_deinit(cert->ian);
        gnutls_free(cert);
 }
 
+static int cache_alt_names(gnutls_x509_crt_t cert)
+{
+       gnutls_datum_t tmpder = {NULL, 0};
+       int ret;
+
+       /* pre-parse subject alt name */
+       ret = _gnutls_x509_crt_get_extension(cert, "2.5.29.17", 0, &tmpder, NULL);
+       if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+               gnutls_free(tmpder.data);
+               return gnutls_assert_val(ret);
+       }
+
+       if (ret >= 0) {
+               ret = gnutls_x509_ext_import_subject_alt_names(&tmpder, cert->san, 0);
+               gnutls_free(tmpder.data);
+               tmpder.data = NULL;
+               if (ret < 0)
+                       return gnutls_assert_val(ret);
+       }
+
+       ret = _gnutls_x509_crt_get_extension(cert, "2.5.29.18", 0, &tmpder, NULL);
+       if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+               return gnutls_assert_val(ret);
+
+       if (ret >= 0) {
+               ret = gnutls_x509_ext_import_subject_alt_names(&tmpder, cert->ian, 0);
+               gnutls_free(tmpder.data);
+               if (ret < 0)
+                       return gnutls_assert_val(ret);
+       }
+
+       return 0;
+}
+
 /**
  * gnutls_x509_crt_import:
  * @cert: The structure to store the parsed certificate.
@@ -244,6 +296,12 @@ gnutls_x509_crt_import(gnutls_x509_crt_t cert,
 
        cert->expanded = 1;
 
+       result = cache_alt_names(cert);
+       if (result < 0) {
+               gnutls_assert();
+               goto cleanup;
+       }
+
        /* Since we do not want to disable any extension
         */
        cert->use_extensions = 1;
@@ -1251,50 +1309,26 @@ cleanup:
 }
 
 static int
-get_alt_name(gnutls_x509_crt_t cert, const char *extension_id,
+get_alt_name(gnutls_subject_alt_names_t san,
             unsigned int seq, uint8_t *alt,
             size_t * alt_size, unsigned int *alt_type,
             unsigned int *critical, int othername_oid)
 {
        int ret;
-       gnutls_datum_t dnsname = {NULL, 0};
        gnutls_datum_t ooid = {NULL, 0};
-       gnutls_datum_t res;
-       gnutls_subject_alt_names_t sans = NULL;
+       gnutls_datum_t oname;
+       gnutls_datum_t virt = {NULL, 0};
        unsigned int type;
 
-       if (cert == NULL) {
+       if (san == NULL) {
                gnutls_assert();
-               return GNUTLS_E_INVALID_REQUEST;
+               return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
        }
 
        if (alt == NULL)
                *alt_size = 0;
 
-       if ((ret =
-            _gnutls_x509_crt_get_extension(cert, extension_id, 0,
-                                           &dnsname, critical)) < 0) {
-               return ret;
-       }
-
-       if (dnsname.size == 0 || dnsname.data == NULL) {
-               gnutls_assert();
-               return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
-       }
-
-       ret = gnutls_subject_alt_names_init(&sans);
-       if (ret < 0) {
-               gnutls_assert();
-               goto cleanup;
-       }
-
-       ret = gnutls_x509_ext_import_subject_alt_names(&dnsname, sans, 0);
-       if (ret < 0) {
-               gnutls_assert();
-               goto cleanup;
-       }
-
-       ret = gnutls_subject_alt_names_get(sans, seq, &type, &res, &ooid);
+       ret = gnutls_subject_alt_names_get(san, seq, &type, &oname, &ooid);
        if (ret < 0) {
                gnutls_assert();
                goto cleanup;
@@ -1311,9 +1345,9 @@ get_alt_name(gnutls_x509_crt_t cert, const char *extension_id,
                ret = _gnutls_copy_string(&ooid, alt, alt_size);
        } else {
                if (is_type_printable(type)) {
-                       ret = _gnutls_copy_string(&res, alt, alt_size);
+                       ret = _gnutls_copy_string(&oname, alt, alt_size);
                } else {
-                       ret = _gnutls_copy_data(&res, alt, alt_size);
+                       ret = _gnutls_copy_data(&oname, alt, alt_size);
                }
        }
 
@@ -1324,9 +1358,7 @@ get_alt_name(gnutls_x509_crt_t cert, const char *extension_id,
 
        ret = type;
 cleanup:
-       gnutls_free(dnsname.data);
-       if (sans != NULL)
-               gnutls_subject_alt_names_deinit(sans);
+       gnutls_free(virt.data);
 
        return ret;
 }
@@ -1367,7 +1399,7 @@ gnutls_x509_crt_get_subject_alt_name(gnutls_x509_crt_t cert,
                                     size_t * san_size,
                                     unsigned int *critical)
 {
-       return get_alt_name(cert, "2.5.29.17", seq, san, san_size, NULL,
+       return get_alt_name(cert->san, seq, san, san_size, NULL,
                            critical, 0);
 }
 
@@ -1410,7 +1442,7 @@ gnutls_x509_crt_get_issuer_alt_name(gnutls_x509_crt_t cert,
                                    size_t * ian_size,
                                    unsigned int *critical)
 {
-       return get_alt_name(cert, "2.5.29.18", seq, ian, ian_size, NULL,
+       return get_alt_name(cert->ian, seq, ian, ian_size, NULL,
                            critical, 0);
 }
 
@@ -1445,7 +1477,7 @@ gnutls_x509_crt_get_subject_alt_name2(gnutls_x509_crt_t cert,
                                      unsigned int *san_type,
                                      unsigned int *critical)
 {
-       return get_alt_name(cert, "2.5.29.17", seq, san, san_size,
+       return get_alt_name(cert->san, seq, san, san_size,
                            san_type, critical, 0);
 }
 
@@ -1483,7 +1515,7 @@ gnutls_x509_crt_get_issuer_alt_name2(gnutls_x509_crt_t cert,
                                     unsigned int *ian_type,
                                     unsigned int *critical)
 {
-       return get_alt_name(cert, "2.5.29.18", seq, ian, ian_size,
+       return get_alt_name(cert->ian, seq, ian, ian_size,
                            ian_type, critical, 0);
 }
 
@@ -1522,7 +1554,7 @@ gnutls_x509_crt_get_subject_alt_othername_oid(gnutls_x509_crt_t cert,
                                              unsigned int seq,
                                              void *oid, size_t * oid_size)
 {
-       return get_alt_name(cert, "2.5.29.17", seq, oid, oid_size, NULL,
+       return get_alt_name(cert->san, seq, oid, oid_size, NULL,
                            NULL, 1);
 }
 
@@ -1563,7 +1595,7 @@ 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,
+       return get_alt_name(cert->ian, seq, ret, ret_size, NULL,
                            NULL, 1);
 }
 
index de8ec7e..ed229ef 100644 (file)
@@ -67,6 +67,9 @@ typedef struct gnutls_x509_crt_int {
         * get_raw_*_dn(). */
        gnutls_datum_t raw_dn;
        gnutls_datum_t raw_issuer_dn;
+       /* this cached value allows fast access to alt names */
+       gnutls_subject_alt_names_t san;
+       gnutls_subject_alt_names_t ian;
 
        struct pin_info_st pin;
 } gnutls_x509_crt_int;