session wrapper UPDATE add crl cert ext fetch wrap
diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c
index 2a86e68..bf868bd 100644
--- a/src/session_mbedtls.c
+++ b/src/session_mbedtls.c
@@ -1565,3 +1565,194 @@
 
     return pk;
 }
+
+int
+nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *uri_count)
+{
+    int ret = 0;
+    mbedtls_x509_crt *cert;
+    unsigned char *p, *end_v3_ext, *end_ext, *end_ext_octet, *end_crl_dist_points;
+    size_t len;
+    mbedtls_x509_buf ext_oid = {0};
+    int is_critical = 0;
+    mbedtls_x509_sequence general_names = {0};
+    mbedtls_x509_sequence *iter = NULL;
+    mbedtls_x509_subject_alternative_name san = {0};
+    void *tmp;
+
+    NC_CHECK_ARG_RET(NULL, cert_store, uris, uri_count, 1);
+
+    *uris = NULL;
+    *uri_count = 0;
+
+    /* iterate over all the CAs */
+    cert = cert_store;
+    while (cert) {
+        if (!cert->v3_ext.len) {
+            /* no extensions, skip this cert */
+            cert = cert->next;
+            continue;
+        }
+
+        /* go over all the extensions and try to find the CRL distribution points */
+        p = cert->v3_ext.p;
+        end_v3_ext = p + cert->v3_ext.len;
+
+        /*
+         * Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
+         */
+        ret = mbedtls_asn1_get_tag(&p, end_v3_ext, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+        if (ret) {
+            ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret));
+            goto cleanup;
+        }
+
+        while (p < end_v3_ext) {
+            /*
+             * Extension  ::=  SEQUENCE  {
+             *      extnID      OBJECT IDENTIFIER,
+             *      critical    BOOLEAN DEFAULT FALSE,
+             *      extnValue   OCTET STRING  }
+             */
+            ret = mbedtls_asn1_get_tag(&p, end_v3_ext, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+            if (ret) {
+                ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret));
+                goto cleanup;
+            }
+
+            end_ext = p + len;
+
+            /* parse extnID */
+            ret = mbedtls_asn1_get_tag(&p, end_ext, &ext_oid.len, MBEDTLS_ASN1_OID);
+            if (ret) {
+                ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret));
+                goto cleanup;
+            }
+            ext_oid.tag = MBEDTLS_ASN1_OID;
+            ext_oid.p = p;
+
+            if (memcmp(ext_oid.p, MBEDTLS_OID_CRL_DISTRIBUTION_POINTS, ext_oid.len)) {
+                /* not the extension we are looking for */
+                p = end_ext;
+                continue;
+            }
+
+            p += ext_oid.len;
+
+            /* parse optional critical */
+            ret = mbedtls_asn1_get_bool(&p, end_ext, &is_critical);
+            if (ret && (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG)) {
+                ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret));
+                goto cleanup;
+            }
+
+            /* parse extnValue */
+            ret = mbedtls_asn1_get_tag(&p, end_ext, &len, MBEDTLS_ASN1_OCTET_STRING);
+            if (ret) {
+                ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret));
+                goto cleanup;
+            }
+
+            end_ext_octet = p + len;
+
+            /*
+             * parse extnValue, that is CRLDistributionPoints
+             *
+             * CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
+             */
+            ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+            if (ret) {
+                ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret));
+                goto cleanup;
+            }
+            if (p + len != end_ext_octet) {
+                /* length mismatch */
+                ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret));
+                goto cleanup;
+            } else if (!len) {
+                /* empty sequence, but size is 1..max */
+                ERR(NULL, "Failed to parse CRL distribution points extension (empty sequence).");
+                goto cleanup;
+            }
+
+            end_crl_dist_points = p + len;
+
+            while (p < end_crl_dist_points) {
+                /*
+                 * DistributionPoint ::= SEQUENCE {
+                 *      distributionPoint       [0]     DistributionPointName OPTIONAL,
+                 *      reasons                 [1]     ReasonFlags OPTIONAL,
+                 *      cRLIssuer               [2]     GeneralNames OPTIONAL }
+                 */
+                ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+                if (ret) {
+                    ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret));
+                    goto cleanup;
+                }
+                if (!len) {
+                    /* empty sequence */
+                    continue;
+                }
+
+                /* parse distributionPoint */
+                ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0);
+                if (!ret) {
+                    /*
+                     * DistributionPointName ::= CHOICE {
+                     *      fullName                [0]     GeneralNames,
+                     *      nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
+                     */
+                    ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0);
+                    if (ret) {
+                        if ((ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) && (*p == (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 1))) {
+                            /* it's nameRelativeToCRLIssuer, but we don't support it */
+                            ERR(NULL, "Failed to parse CRL distribution points extension (nameRelativeToCRLIssuer not supported).");
+                            goto cleanup;
+                        } else {
+                            ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret));
+                            goto cleanup;
+                        }
+                    }
+
+                    /* parse GeneralNames, but thankfully there is an api for this */
+                    ret = mbedtls_x509_get_subject_alt_name_ext(&p, p + len, &general_names);
+                    if (ret) {
+                        ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret));
+                        goto cleanup;
+                    }
+
+                    /* iterate over all the GeneralNames */
+                    iter = &general_names;
+                    while (iter) {
+                        ret = mbedtls_x509_parse_subject_alt_name(&iter->buf, &san);
+                        if (ret && (ret != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE)) {
+                            ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret));
+                            goto cleanup;
+                        }
+
+                        if (san.type == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER) {
+                            /* found an URI */
+                            tmp = realloc(*uris, (*uri_count + 1) * sizeof **uris);
+                            NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup);
+                            *uris = tmp;
+
+                            *uris[*uri_count] = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len);
+                            NC_CHECK_ERRMEM_GOTO(!*uris[*uri_count], ret = 1, cleanup);
+                            ++(*uri_count);
+                        }
+                        iter = iter->next;
+                    }
+
+                } else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) {
+                    /* failed to parse it, but not because it's optional */
+                    ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret));
+                    goto cleanup;
+                }
+            }
+        }
+        cert = cert->next;
+    }
+
+cleanup:
+    return ret;
+}