]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Implement some postponed requirements
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 15 Jan 2019 15:42:17 +0000 (09:42 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 15 Jan 2019 15:42:17 +0000 (09:42 -0600)
At this point I'm filling in blanks and TODOs rather than focus on a
particular feature. Mainly, this commit adds

- Validate certificate extensions
- Improve the main loop so it stops on the TAL's first successful URI

23 files changed:
src/Makefile.am
src/asn1/signed_data.c
src/common.c
src/common.h
src/main.c
src/object/certificate.c
src/object/certificate.h
src/object/tal.c
src/object/tal.h
src/random.c [new file with mode: 0644]
src/random.h [new file with mode: 0644]
src/resource.c
src/resource.h
src/resource/asn.c
src/resource/asn.h
src/resource/ip4.c
src/resource/ip4.h
src/resource/ip6.c
src/resource/ip6.h
src/sorted_array.c
src/sorted_array.h
src/state.c
src/state.h

index 323151f2a1b1865774c4330363e98e0ec73a434f..5b4c71049d4d2791d0c01ae9e23dcd77ff893667 100644 (file)
@@ -12,6 +12,7 @@ rpki_validator_SOURCES += debug.h debug.c
 rpki_validator_SOURCES += file.h file.c
 rpki_validator_SOURCES += line_file.h line_file.c
 rpki_validator_SOURCES += log.h log.c
+rpki_validator_SOURCES += random.h random.c
 rpki_validator_SOURCES += resource.h resource.c
 rpki_validator_SOURCES += sorted_array.h sorted_array.c
 rpki_validator_SOURCES += state.h state.c
index d1a8497efbbc6ea46444bd7597883ee5a718121f..25650b29c19802c9ba40e488f32362548a800673 100644 (file)
@@ -33,9 +33,25 @@ is_digest_algorithm(AlgorithmIdentifier_t *id, char *what)
        return 0;
 }
 
+static int
+get_sid(struct SignerInfo *sinfo, OCTET_STRING_t **result)
+{
+       switch (sinfo->sid.present) {
+       case SignerIdentifier_PR_subjectKeyIdentifier:
+               *result = &sinfo->sid.choice.subjectKeyIdentifier;
+               return 0;
+       case SignerIdentifier_PR_issuerAndSerialNumber:
+               return pr_err("Signer Info's sid is an IssuerAndSerialNumber, not a SubjectKeyIdentifier.");
+       case SignerIdentifier_PR_NOTHING:
+               break;
+       }
+
+       return pr_err("Signer Info's sid is not a SubjectKeyIdentifier.");
+}
+
 static int
 handle_sdata_certificate(ANY_t *any, STACK_OF(X509_CRL) *crls,
-    struct resources *res)
+    struct resources *res, OCTET_STRING_t *sid)
 {
        const unsigned char *tmp;
        X509 *cert;
@@ -71,7 +87,7 @@ handle_sdata_certificate(ANY_t *any, STACK_OF(X509_CRL) *crls,
                        goto end2;
        }
 
-       error = certificate_traverse_ee(cert);
+       error = certificate_traverse_ee(cert, sid);
 
 end2:
        X509_free(cert);
@@ -225,6 +241,7 @@ validate(struct SignedData *sdata, STACK_OF(X509_CRL) *crls,
     struct resources *res)
 {
        struct SignerInfo *sinfo;
+       OCTET_STRING_t *sid = NULL;
        int error;
 
        /* rfc6488#section-2.1 */
@@ -259,23 +276,13 @@ validate(struct SignedData *sdata, STACK_OF(X509_CRL) *crls,
        if (error)
                return error;
 
-       /* section-2.1.3 */
+       /* rfc6488#section-2.1.3 */
        /* Specific sub-validations will be performed later by calling code. */
 
-       /* rfc6488#section-2.1.4 */
-       /* rfc6488#section-3.1.c 1/2 */
-       if (sdata->certificates == NULL)
-               return pr_err("The SignedData does not contain certificates.");
-
-       if (sdata->certificates->list.count != 1) {
-               return pr_err("The SignedData contains %d certificates, one expected.",
-                   sdata->certificates->list.count);
-       }
-
-       error = handle_sdata_certificate(sdata->certificates->list.array[0],
-           crls, res);
-       if (error)
-               return error;
+       /*
+        * We will validate the certificate later, because we need the sid
+        * first.
+        */
 
        /* rfc6488#section-2.1.5 */
        /* rfc6488#section-3.1.d */
@@ -294,9 +301,25 @@ validate(struct SignedData *sdata, STACK_OF(X509_CRL) *crls,
 
        /* rfc6488#section-2.1.6.2 */
        /* rfc6488#section-3.1.c 2/2 */
-       /*
-        * TODO need the "EE certificate carried in the CMS certificates field."
-        */
+       /* (Most of this requirement is in handle_ski_ee().) */
+       error = get_sid(sinfo, &sid);
+       if (error)
+               return error;
+
+       /* rfc6488#section-2.1.4 */
+       /* rfc6488#section-3.1.c 1/2 */
+       if (sdata->certificates == NULL)
+               return pr_err("The SignedData does not contain certificates.");
+
+       if (sdata->certificates->list.count != 1) {
+               return pr_err("The SignedData contains %d certificates, one expected.",
+                   sdata->certificates->list.count);
+       }
+
+       error = handle_sdata_certificate(sdata->certificates->list.array[0],
+           crls, res, sid);
+       if (error)
+               return error;
 
        /* rfc6488#section-2.1.6.3 */
        /* rfc6488#section-3.1.j 2/2 */
index f2ac0812d10ccb53afc7d4681875f6832a2bc9e4..cbef2d0e51876a23792c964676f813e9e76f4632 100644 (file)
@@ -30,6 +30,10 @@ file_has_extension(char const *filename, size_t filename_len, char const *ext)
  *
  * You need to free the result once you're done.
  * This function does not assume that @guri is null-terminated.
+ *
+ * By contract, if @guri is not RSYNC, this will return ENOTRSYNC.
+ * This often should not be treated as an error; please handle gracefully.
+ * TODO open call hirarchy.
  */
 int
 uri_g2l(char const *guri, size_t guri_len, char **result)
@@ -42,8 +46,10 @@ uri_g2l(char const *guri, size_t guri_len, char **result)
 
        prefix_len = strlen(PREFIX);
 
-       if (guri_len < prefix_len || strncmp(PREFIX, guri, prefix_len) != 0)
-               return pr_err("Global URI does not begin with '%s'.", PREFIX);
+       if (guri_len < prefix_len || strncmp(PREFIX, guri, prefix_len) != 0) {
+               pr_err("Global URI does not begin with '%s'.", PREFIX);
+               return ENOTRSYNC; /* Not really an error, so not negative */
+       }
 
        guri += prefix_len;
        guri_len -= prefix_len;
index f18345ba733e1b91bd81abd99ff9c5e2c995c5c9..4962d5c22342300be01fe7a707d3ad4828eae6c1 100644 (file)
@@ -8,6 +8,13 @@
 #define ENOTSUPPORTED 3172
 /* "I haven't implemented this yet." */
 #define ENOTIMPLEMENTED 3173
+/*
+ * "URI was not RSYNC; ignore it."
+ * Not really an error. The RFCs usually declare URI lists; usually only one of
+ * them is required to be RSYNC and the others should be skipped (until we
+ * start supporting them.)
+ */
+#define ENOTRSYNC 3174
 
 extern char const *repository;
 extern size_t repository_len;
index c0457c1d9f82906be6411b5f5ad74b71fc67c88b..72e95f955670743e4309a7714ae083197f61f8f8 100644 (file)
@@ -43,7 +43,7 @@ handle_tal_certificate(char *uri)
        error = certificate_validate_rfc6487(cert, true);
        if (error)
                goto revert;
-       error = certificate_traverse_ca(cert, NULL);
+       error = certificate_traverse_ta(cert, NULL);
 
 revert:
        X509_free(cert);
@@ -59,6 +59,22 @@ end:
 static int
 handle_tal_uri(struct tal *tal, char const *guri)
 {
+       /*
+        * Because of the way the foreach iterates, this function must return
+        *
+        * - 0 on soft errors.
+        * - `> 0` on URI handled successfully.
+        * - `< 0` on hard errors.
+        *
+        * A "soft error" is "the connection to the preferred URI fails, or the
+        * retrieved CA certificate public key does not match the TAL public
+        * key." (RFC 7730)
+        *
+        * A "hard error" is any other error.
+        *
+        * TODO this will probably need an update after the merge.
+        */
+
        struct validation *state;
        char *luri;
        int error;
@@ -66,26 +82,42 @@ handle_tal_uri(struct tal *tal, char const *guri)
        /* TODO this probably needs the state... */
        error = download_files(guri);
        if (error)
-               return error;
+               return 0;
 
        error = validation_prepare(&state, tal);
        if (error)
-               return error;
+               return -abs(error);
 
        pr_debug_add("TAL URI %s {", guri);
 
        if (!is_certificate(guri)) {
                pr_err("TAL file does not point to a certificate. (Expected .cer, got '%s')",
                    guri);
-               error = -ENOTSUPPORTED;
+               error = -EINVAL;
                goto end;
        }
 
        error = uri_g2l(guri, strlen(guri), &luri);
-       if (error)
-               return error;
+       if (error) {
+               error = -abs(error);
+               goto end;
+       }
 
        error = handle_tal_certificate(luri);
+       if (error) {
+               switch (validation_pubkey_state(state)) {
+               case PKS_INVALID:
+                       error = 0;
+                       break;
+               case PKS_VALID:
+               case PKS_UNTESTED:
+                       error = -abs(error);
+                       break;
+               }
+       } else {
+               error = 1;
+       }
+
        free(luri);
 
 end:
@@ -100,6 +132,7 @@ main(int argc, char **argv)
        struct tal *tal;
        int error;
        bool is_rsync_active = true;
+       bool shuffle_uris = false;
 
        print_stack_trace_on_segfault();
 
@@ -107,6 +140,8 @@ main(int argc, char **argv)
                return pr_err("Repository path as first argument and TAL file as second argument, please.");
        if (argc >= 4)
                is_rsync_active = false;
+       if (argc >= 5)
+               shuffle_uris = true; /* TODO lol fix this */
 
        error = hash_init();
        if (error)
@@ -125,7 +160,10 @@ main(int argc, char **argv)
 
        error = tal_load(argv[2], &tal);
        if (!error) {
+               if (shuffle_uris)
+                       tal_shuffle_uris(tal);
                error = foreach_uri(tal, handle_tal_uri);
+               error = (error >= 0) ? 0 : error;
                tal_destroy(tal);
        }
 
index 9016b0d0b14d5a66a8d5a025159f2c5f3fc2d825..da744fe7de069195fbe5675c371a7ab46c2d54f7 100644 (file)
  */
 typedef AUTHORITY_INFO_ACCESS SIGNATURE_INFO_ACCESS;
 
+struct extension_metadata {
+       char *name;
+       int nid;
+       bool critical;
+};
+
+static const struct extension_metadata BC = {
+       "Basic Constraints",
+       NID_basic_constraints,
+       true,
+};
+static const struct extension_metadata SKI = {
+       "Subject Key Identifier",
+       NID_subject_key_identifier,
+       false,
+};
+static const struct extension_metadata AKI = {
+       "Authority Key Identifier",
+       NID_authority_key_identifier,
+       false,
+};
+static const struct extension_metadata KU = {
+       "Key Usage",
+       NID_key_usage,
+       true,
+};
+static const struct extension_metadata CDP = {
+       "CRL Distribution Points",
+       NID_crl_distribution_points,
+       false,
+};
+static const struct extension_metadata AIA = {
+       "Authority Information Access",
+       NID_info_access,
+       false,
+};
+static const struct extension_metadata SIA = {
+       "Subject Information Access",
+       NID_sinfo_access ,
+       false,
+};
+static const struct extension_metadata CP = {
+       "Certificate Policies",
+       NID_certificate_policies,
+       true,
+};
+static const struct extension_metadata IR = {
+       "IP Resources",
+       NID_sbgp_ipAddrBlock,
+       true,
+};
+static const struct extension_metadata AR = {
+       "AS Resources",
+       NID_sbgp_autonomousSysNum,
+       true,
+};
+
+struct extension_handler {
+       struct extension_metadata const *meta;
+       bool mandatory;
+       int (*cb)(X509_EXTENSION *, void *);
+       void *arg;
+
+       void (*free)(void *);
+
+       /* For internal use */
+       bool found;
+};
+
+struct sia_arguments {
+       X509 *cert;
+       STACK_OF(X509_CRL) *crls;
+};
+
 bool is_certificate(char const *file_name)
 {
        return file_has_extension(file_name, strlen(file_name), ".cer");
@@ -132,31 +206,37 @@ validate_spki(const unsigned char *cert_spk, int cert_spk_len)
        error = asn1_decode(_tal_spki, _tal_spki_len,
            &asn_DEF_SubjectPublicKeyInfo, (void **) &tal_spki);
        if (error)
-               return error;
+               goto fail1;
 
        /* Algorithm Identifier */
        error = oid2arcs(&tal_spki->algorithm.algorithm, &tal_alg_arcs);
        if (error)
-               goto fail;
+               goto fail2;
 
        if (!ARCS_EQUAL_OIDS(&tal_alg_arcs, oid_rsa)) {
                error = pr_err("TAL's public key format is not RSA PKCS#1 v1.5 with SHA-256.");
-               goto fail;
+               goto fail3;
        }
 
        /* SPK */
        if (tal_spki->subjectPublicKey.size != cert_spk_len)
-               goto not_equal;
+               goto fail4;
        if (memcmp(tal_spki->subjectPublicKey.buf, cert_spk, cert_spk_len) != 0)
-               goto not_equal;
+               goto fail4;
 
+       free_arcs(&tal_alg_arcs);
        ASN_STRUCT_FREE(asn_DEF_SubjectPublicKeyInfo, tal_spki);
+       validation_pubkey_valid(state);
        return 0;
 
-not_equal:
+fail4:
        error = pr_err("TAL's public key is different than the root certificate's public key.");
-fail:
+fail3:
+       free_arcs(&tal_alg_arcs);
+fail2:
        ASN_STRUCT_FREE(asn_DEF_SubjectPublicKeyInfo, tal_spki);
+fail1:
+       validation_pubkey_invalid(state);
        return error;
 }
 
@@ -214,13 +294,6 @@ validate_public_key(X509 *cert, bool is_root)
        return 0;
 }
 
-static int
-validate_extensions(X509 *cert)
-{
-       /* TODO (field) */
-       return 0;
-}
-
 int
 certificate_validate_rfc6487(X509 *cert, bool is_root)
 {
@@ -279,7 +352,8 @@ certificate_validate_rfc6487(X509 *cert, bool is_root)
        if (error)
                return error;
 
-       return validate_extensions(cert);
+       /* We'll validate extensions later. */
+       return 0;
 }
 
 int
@@ -383,6 +457,10 @@ abort:
 /*
  * "GENERAL_NAME, global to local"
  * Result has to be freed.
+ *
+ * If this function returns ENOTRSYNC, it means that @name was not an RSYNC URI.
+ * This often should not be treated as an error; please handle gracefully.
+ * TODO open call hierarchy.
  */
 static int
 gn_g2l(GENERAL_NAME *name, char **luri)
@@ -404,15 +482,11 @@ gn_g2l(GENERAL_NAME *name, char **luri)
         * Does it imply that the GeneralName CHOICE is constrained to type
         * "uniformResourceIdentifier"? I guess so, though I don't see anything
         * stopping a few of the other types from also being capable of storing
-        * URIs. Then again, DER is all about unique serialized representation.
+        * URIs.
         *
         * Also, nobody seems to be using the other types, and handling them
         * would be a titanic pain in the ass. So this is what I'm committing
         * to.
-        *
-        * I know that this is the logical conclusion; it's just that I know
-        * that at some point in the future I'm going find myself bewilderingly
-        * staring at this if again.
         */
        if (type != GEN_URI) {
                pr_err("Unknown GENERAL_NAME type: %d", type);
@@ -557,6 +631,24 @@ certificate_get_resources(X509 *cert, struct resources *resources)
        return 0;
 }
 
+static int
+cannot_decode(struct extension_metadata const *meta)
+{
+       return pr_err("Extension '%s' seems to be malformed. Cannot decode.",
+           meta->name);
+}
+
+static bool
+is_rsync(ASN1_IA5STRING *uri)
+{
+       static char const *const PREFIX = "rsync://";
+       size_t prefix_len = strlen(PREFIX);
+
+       return (uri->length >= prefix_len)
+           ? (strncmp((char *) uri->data, PREFIX, strlen(PREFIX)) == 0)
+           : false;
+}
+
 static int
 handle_rpkiManifest(ACCESS_DESCRIPTION *ad, STACK_OF(X509_CRL) *crls)
 {
@@ -606,28 +698,299 @@ handle_signedObject(ACCESS_DESCRIPTION *ad)
        return error;
 }
 
-int
-certificate_traverse_ca(X509 *cert, STACK_OF(X509_CRL) *crls)
+static int
+handle_bc(X509_EXTENSION *ext, void *arg)
+{
+       BASIC_CONSTRAINTS *bc;
+       int error;
+
+       bc = X509V3_EXT_d2i(ext);
+       if (bc == NULL)
+               return cannot_decode(&BC);
+
+       /*
+        * 'The issuer determines whether the "cA" boolean is set.'
+        * ................................. Uh-huh. So nothing then.
+        * Well, libcrypto should do the RFC 5280 thing with it anyway.
+        */
+
+       error = (bc->pathlen == NULL)
+           ? 0
+           : pr_err("%s extension contains a Path Length Constraint.", BC.name);
+
+       BASIC_CONSTRAINTS_free(bc);
+       return error;
+}
+
+static int
+handle_ski(X509_EXTENSION *ext, void *arg)
+{
+       X509_PUBKEY *pubkey;
+       const unsigned char *spk;
+       int spk_len;
+       int ok;
+
+       /*
+        * "Applications are not required to verify that key identifiers match
+        * when performing certification path validation."
+        * (rfc5280#section-4.2.1.2)
+        * I think "match" refers to the "parent's SKI must match the
+        * children's AKI" requirement, not "The SKI must match the SHA-1 of the
+        * SPK" requirement.
+        * So I guess we're only supposed to check the SHA-1.
+        */
+
+       pubkey = X509_get_X509_PUBKEY((X509 *) arg);
+       if (pubkey == NULL) {
+               crypto_err("X509_get_X509_PUBKEY() returned NULL");
+               return -EINVAL;
+       }
+
+       ok = X509_PUBKEY_get0_param(NULL, &spk, &spk_len, NULL, pubkey);
+       if (!ok) {
+               crypto_err("X509_PUBKEY_get0_param() returned %d", ok);
+               return -EINVAL;
+       }
+
+       /* Get the SHA-1 of spk */
+       /* TODO (certext) ... */
+
+       /* Decode ext */
+
+       /* Compare the SHA and the decoded ext */
+
+       /* Free the decoded ext */
+
+       return 0;
+}
+
+static int
+handle_ski_ee(X509_EXTENSION *ext, void *arg)
+{
+       ASN1_OCTET_STRING *ski;
+       OCTET_STRING_t *sid = arg;
+       int error = 0;
+
+       ski = X509V3_EXT_d2i(ext);
+       if (ski == NULL)
+               return cannot_decode(&SKI);
+
+       /* rfc6488#section-2.1.6.2 */
+       /* rfc6488#section-3.1.c 2/2 */
+       if (ski->length != sid->size
+           || memcmp(ski->data, sid->buf, sid->size) != 0) {
+               error = pr_err("The EE certificate's subjectKeyIdentifier does not equal the Signed Object's sid.");
+       }
+
+       ASN1_OCTET_STRING_free(ski);
+       return error;
+}
+
+static int
+handle_aki_ta(X509_EXTENSION *ext, void *arg)
+{
+       return 0; /* TODO (certext) implement. */
+}
+
+static int
+handle_aki(X509_EXTENSION *ext, void *arg)
+{
+       AUTHORITY_KEYID *aki;
+       int error = 0;
+
+       aki = X509V3_EXT_d2i(ext);
+       if (aki == NULL)
+               return cannot_decode(&AKI);
+
+       if (aki->issuer != NULL) {
+               error = pr_err("%s extension contains an authorityCertIssuer.",
+                   AKI.name);
+               goto end;
+       }
+       if (aki->serial != NULL) {
+               error = pr_err("%s extension contains an authorityCertSerialNumber.",
+                   AKI.name);
+               goto end;
+       }
+
+       /* TODO (certext) stuff */
+
+end:
+       AUTHORITY_KEYID_free(aki);
+       return error;
+}
+
+static int
+handle_ku(X509_EXTENSION *ext, unsigned char byte1)
 {
+       /*
+        * About the key usage string: At time of writing, it's 9 bits long.
+        * But zeroized rightmost bits can be omitted.
+        * This implementation assumes that the ninth bit should always be zero.
+        */
+
+       ASN1_BIT_STRING *ku;
+       unsigned char data[2];
+       int error = 0;
+
+       ku = X509V3_EXT_d2i(ext);
+       if (ku == NULL)
+               return cannot_decode(&KU);
+
+       if (ku->length == 0) {
+               error = pr_err("%s bit string has no enabled bits.", KU.name);
+               goto end;
+       }
+
+       memset(data, 0, sizeof(data));
+       memcpy(data, ku->data, ku->length);
+
+       if (ku->data[0] != byte1) {
+               error = pr_err("Illegal key usage flag string: %u%u%u%u%u%u%u%u%u",
+                   !!(ku->data[0] & 0x80), !!(ku->data[0] & 0x40),
+                   !!(ku->data[0] & 0x20), !!(ku->data[0] & 0x10),
+                   !!(ku->data[0] & 0x08), !!(ku->data[0] & 0x04),
+                   !!(ku->data[0] & 0x02), !!(ku->data[0] & 0x01),
+                   !!(ku->data[1] & 0x80));
+               goto end;
+       }
+
+end:
+       ASN1_BIT_STRING_free(ku);
+       return error;
+}
+
+static int
+handle_ku_ca(X509_EXTENSION *ext, void *arg)
+{
+       return handle_ku(ext, 0x06);
+}
+
+static int
+handle_ku_ee(X509_EXTENSION *ext, void *arg)
+{
+       return handle_ku(ext, 0x80);
+}
+
+static int
+handle_cdp(X509_EXTENSION *ext, void *arg)
+{
+       STACK_OF(DIST_POINT) *crldp = X509V3_EXT_d2i(ext);
+       DIST_POINT *dp;
+       GENERAL_NAMES *names;
+       GENERAL_NAME *name;
+       int i;
+       int error = 0;
+       char *error_msg;
+
+       crldp = X509V3_EXT_d2i(ext);
+       if (crldp == NULL)
+               return cannot_decode(&CDP);
+
+       if (sk_DIST_POINT_num(crldp) != 1) {
+               error = pr_err("The %s extension has %u distribution points. (1 expected)",
+                   CDP.name, sk_DIST_POINT_num(crldp));
+               goto end;
+       }
+
+       dp = sk_DIST_POINT_value(crldp, 0);
+
+       if (dp->CRLissuer != NULL) {
+               error_msg = "has a CRLIssuer field";
+               goto dist_point_error;
+       }
+       if (dp->reasons != NULL) {
+               error_msg = "has a Reasons field";
+               goto dist_point_error;
+       }
+
+       if (dp->distpoint == NULL) {
+               error_msg = "lacks a distributionPoint field";
+               goto dist_point_error;
+       }
+
+       /* Bleargh. There's no enum. 0 is fullname, 1 is relativename. */
+       switch (dp->distpoint->type) {
+       case 0:
+               break;
+       case 1:
+               error_msg = "has a relative name";
+               goto dist_point_error;
+       default:
+               error_msg = "has an unknown type of name";
+               goto dist_point_error;
+       }
+
+       names = dp->distpoint->name.fullname;
+       for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
+               name = sk_GENERAL_NAME_value(names, i);
+               if (name->type == GEN_URI && is_rsync(name->d.uniformResourceIdentifier)) {
+                       /*
+                        * TODO (certext) check the URI matches what we rsync'd.
+                        * Also indent properly.
+                        */
+                       error = 0;
+                       goto end;
+               }
+       }
+
+       error_msg = "lacks an RSYNC URI";
+
+dist_point_error:
+       error = pr_err("The %s extension's distribution point %s.", CDP.name,
+           error_msg);
+
+end:
+       sk_DIST_POINT_pop_free(crldp, DIST_POINT_free);
+       return error;
+}
+
+static int
+handle_aia(X509_EXTENSION *ext, void *arg)
+{
+       AUTHORITY_INFO_ACCESS *aia;
+       ACCESS_DESCRIPTION *ad;
+       int i;
+
+       aia = X509V3_EXT_d2i(ext);
+       if (aia == NULL)
+               return cannot_decode(&AIA);
+
+       for (i = 0; i < sk_ACCESS_DESCRIPTION_num(aia); i++) {
+               ad = sk_ACCESS_DESCRIPTION_value(aia, i);
+               if (OBJ_obj2nid(ad->method) == NID_ad_ca_issuers) {
+                       /*
+                        * TODO (certext) check the URI matches what we rsync'd.
+                        */
+               }
+       }
+
+       AUTHORITY_INFO_ACCESS_free(aia);
+       return 0;
+}
+
+static int
+handle_sia_ca(X509_EXTENSION *ext, void *arg)
+{
+       struct sia_arguments *args = arg;
        struct validation *state;
        SIGNATURE_INFO_ACCESS *sia;
        ACCESS_DESCRIPTION *ad;
+       bool rsync_found = false;
        bool manifest_found = false;
        int i;
        int error;
 
-       sia = X509_get_ext_d2i(cert, NID_sinfo_access, NULL, NULL);
-       if (sia == NULL) {
-               pr_err("Certificate lacks a Subject Information Access extension.");
-               return -ESRCH;
-       }
+       sia = X509V3_EXT_d2i(ext);
+       if (sia == NULL)
+               return cannot_decode(&SIA);
 
        state = state_retrieve();
        if (state == NULL) {
                error = -EINVAL;
                goto end2;
        }
-       error = validation_push_cert(state, cert);
+       error = validation_push_cert(state, args->cert, false);
        if (error)
                goto end2;
 
@@ -636,16 +999,26 @@ certificate_traverse_ca(X509 *cert, STACK_OF(X509_CRL) *crls)
                ad = sk_ACCESS_DESCRIPTION_value(sia, i);
                if (OBJ_obj2nid(ad->method) == NID_caRepository) {
                        error = handle_caRepository(ad);
+                       if (error == ENOTRSYNC)
+                               continue;
                        if (error)
                                goto end1;
+                       rsync_found = true;
+                       break;
                }
        }
 
+       if (!rsync_found) {
+               pr_err("SIA extension lacks an RSYNC URI caRepository.");
+               error = -ESRCH;
+               goto end1;
+       }
+
        /* validate */
        for (i = 0; i < sk_ACCESS_DESCRIPTION_num(sia); i++) {
                ad = sk_ACCESS_DESCRIPTION_value(sia, i);
                if (OBJ_obj2nid(ad->method) == NID_rpkiManifest) {
-                       error = handle_rpkiManifest(ad, crls);
+                       error = handle_rpkiManifest(ad, args->crls);
                        if (error)
                                goto end1;
                        manifest_found = true;
@@ -654,7 +1027,7 @@ certificate_traverse_ca(X509 *cert, STACK_OF(X509_CRL) *crls)
 
        /* rfc6481#section-2 */
        if (!manifest_found) {
-               pr_err("Repository publication point seems to have no manifest.");
+               pr_err("SIA extension lacks an rpkiManifest access description.");
                error = -ESRCH;
        }
 
@@ -665,19 +1038,17 @@ end2:
        return error;
 }
 
-int
-certificate_traverse_ee(X509 *cert)
+static int
+handle_sia_ee(X509_EXTENSION *ext, void *arg)
 {
        SIGNATURE_INFO_ACCESS *sia;
        ACCESS_DESCRIPTION *ad;
        int i;
-       int error;
+       int error = 0;
 
-       sia = X509_get_ext_d2i(cert, NID_sinfo_access, NULL, NULL);
-       if (sia == NULL) {
-               pr_err("Certificate lacks a Subject Information Access extension.");
-               return -ESRCH;
-       }
+       sia = X509V3_EXT_d2i(ext);
+       if (sia == NULL)
+               return cannot_decode(&SIA);
 
        for (i = 0; i < sk_ACCESS_DESCRIPTION_num(sia); i++) {
                ad = sk_ACCESS_DESCRIPTION_value(sia, i);
@@ -697,3 +1068,153 @@ end:
        AUTHORITY_INFO_ACCESS_free(sia);
        return error;
 }
+
+static int
+handle_cp(X509_EXTENSION *ext, void *arg)
+{
+       return 0; /* TODO (certext) Implement */
+}
+
+static int
+handle_ir(X509_EXTENSION *ext, void *arg)
+{
+       return 0; /* Handled in certificate_get_resources(). */
+}
+
+static int
+handle_ar(X509_EXTENSION *ext, void *arg)
+{
+       return 0; /* Handled in certificate_get_resources(). */
+}
+
+static int
+handle_extension(struct extension_handler *handlers, X509_EXTENSION *ext)
+{
+       struct extension_handler *handler;
+       int nid;
+
+       nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext));
+
+       for (handler = handlers; handler->meta != NULL; handler++) {
+               if (handler->meta->nid == nid) {
+                       if (handler->found)
+                               goto dupe;
+                       handler->found = true;
+
+                       if (handler->meta->critical) {
+                               if (!X509_EXTENSION_get_critical(ext))
+                                       goto not_critical;
+                       } else {
+                               if (X509_EXTENSION_get_critical(ext))
+                                       goto critical;
+                       }
+
+                       return handler->cb(ext, handler->arg);
+               }
+       }
+
+       if (!X509_EXTENSION_get_critical(ext))
+               return 0; /* Unknown and not critical; ignore it. */
+
+       return pr_err("Certificate has unknown extension. (Extension NID: %d)",
+           nid);
+dupe:
+       return pr_err("Certificate has more than one '%s' extension.",
+           handler->meta->name);
+not_critical:
+       return pr_err("Extension '%s' is supposed to be marked critical.",
+           handler->meta->name);
+critical:
+       return pr_err("Extension '%s' is not supposed to be marked critical.",
+           handler->meta->name);
+}
+
+static int
+handle_cert_extensions(struct extension_handler *handlers, X509 *cert)
+{
+       struct extension_handler *handler;
+       int e;
+       int error;
+
+       for (e = 0; e < X509_get_ext_count(cert); e++) {
+               error = handle_extension(handlers, X509_get_ext(cert, e));
+               if (error)
+                       return error;
+       }
+
+       for (handler = handlers; handler->meta != NULL; handler++) {
+               if (handler->mandatory && !handler->found)
+                       return pr_err("Certificate is missing the '%s' extension.",
+                           handler->meta->name);
+       }
+
+       return 0;
+}
+
+int
+certificate_traverse_ta(X509 *cert, STACK_OF(X509_CRL) *crls)
+{
+       struct sia_arguments sia_args;
+       struct extension_handler handlers[] = {
+          /* ext   reqd   handler        arg       */
+           { &BC,  true,  handle_bc,               },
+           { &SKI, true,  handle_ski,    &cert     },
+           { &AKI, false, handle_aki_ta,           },
+           { &KU,  true,  handle_ku_ca,            },
+           { &SIA, true,  handle_sia_ca, &sia_args },
+           { &CP,  true,  handle_cp,               },
+           { &IR,  false, handle_ir,               },
+           { &AR,  false, handle_ar,               },
+           { NULL },
+       };
+
+       sia_args.cert = cert;
+       sia_args.crls = crls;
+
+       return handle_cert_extensions(handlers, cert);
+}
+
+int
+certificate_traverse_ca(X509 *cert, STACK_OF(X509_CRL) *crls)
+{
+       struct sia_arguments sia_args;
+       struct extension_handler handlers[] = {
+          /* ext   reqd   handler        arg       */
+           { &BC,  true,  handle_bc,            },
+           { &SKI, true,  handle_ski,    &cert     },
+           { &AKI, true,  handle_aki,              },
+           { &KU,  true,  handle_ku_ca,            },
+           { &CDP, true,  handle_cdp,              },
+           { &AIA, true,  handle_aia,              },
+           { &SIA, true,  handle_sia_ca, &sia_args },
+           { &CP,  true,  handle_cp,               },
+           { &IR,  false, handle_ir,               },
+           { &AR,  false, handle_ar,               },
+           { NULL },
+       };
+
+       sia_args.cert = cert;
+       sia_args.crls = crls;
+
+       return handle_cert_extensions(handlers, cert);
+}
+
+int
+certificate_traverse_ee(X509 *cert, OCTET_STRING_t *sid)
+{
+       struct extension_handler handlers[] = {
+          /* ext   reqd   handler        arg */
+           { &SKI, true,  handle_ski_ee, sid },
+           { &AKI, true,  handle_aki,        },
+           { &KU,  true,  handle_ku_ee,      },
+           { &CDP, true,  handle_cdp,        },
+           { &AIA, true,  handle_aia,        },
+           { &SIA, true,  handle_sia_ee,     },
+           { &CP,  true,  handle_cp,         },
+           { &IR,  false, handle_ir,         },
+           { &AR,  false, handle_ar,         },
+           { NULL },
+       };
+
+       return handle_cert_extensions(handlers, cert);
+}
index 53066ed6a4509cd013a79430365cf6ad5509ca14..cae7e107c2ff8663a1dcc294fa29d37bbcb70f1e 100644 (file)
@@ -23,8 +23,14 @@ int certificate_validate_rfc6487(X509 *, bool);
  * Returns the IP and AS resources declared in the respective extensions.
  */
 int certificate_get_resources(X509 *, struct resources *);
+
+/**
+ * Handles the SIA extension, Trust Anchor style.
+ * (ie. Recursively walks through the certificate's children.)
+ */
+int certificate_traverse_ta(X509 *, STACK_OF(X509_CRL) *);
 /**
- * Handles the SIA extension, CA style.
+ * Handles the SIA extension, (intermediate) CA style.
  * (ie. Recursively walks through the certificate's children.)
  */
 int certificate_traverse_ca(X509 *, STACK_OF(X509_CRL) *);
@@ -33,6 +39,6 @@ int certificate_traverse_ca(X509 *, STACK_OF(X509_CRL) *);
  * (Doesn't actually "traverse" anything. The name is just for the sake of
  * mirroring.)
  */
-int certificate_traverse_ee(X509 *);
+int certificate_traverse_ee(X509 *, OCTET_STRING_t *);
 
 #endif /* SRC_OBJECT_CERTIFICATE_H_ */
index ffc49631b659220ac0243ac5c60da3a6d57c9823..5a8f1aa148fd388977e06967babf9e7bbab386ec 100644 (file)
+#define _GNU_SOURCE
+
 #include "tal.h"
 
 #include <errno.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/queue.h>
 #include <sys/stat.h>
 #include <openssl/evp.h>
 
 #include "line_file.h"
 #include "log.h"
+#include "random.h"
 #include "crypto/base64.h"
 
-struct uri {
-       char *string;
-       SLIST_ENTRY(uri) next;
+struct uris {
+       char **array; /* This is an array of string pointers. */
+       unsigned int count;
+       unsigned int size;
 };
 
-SLIST_HEAD(uri_list, uri);
-
 struct tal {
-       struct uri_list uris;
+       struct uris uris;
        unsigned char *spki; /* Decoded; not base64. */
        size_t spki_len;
 };
 
-static void
-uri_destroy(struct uri *uri)
+static int
+uris_init(struct uris *uris)
 {
-       free(uri->string);
-       free(uri);
+       uris->count = 0;
+       uris->size = 4; /* Most TALs only define one. */
+       uris->array = malloc(uris->size * sizeof(char *));
+       return (uris->array != NULL) ? 0 : -ENOMEM;
 }
 
 static void
-uris_destroy(struct uri_list *uris)
+uris_destroy(struct uris *uris)
 {
-       struct uri *uri;
-
-       while (!SLIST_EMPTY(uris)) {
-               uri = SLIST_FIRST(uris);
-               SLIST_REMOVE_HEAD(uris, next);
-               uri_destroy(uri);
-       }
+       unsigned int i;
+       for (i = 0; i < uris->count; i++)
+               free(uris->array[i]);
+       free(uris->array);
 }
 
 static int
-read_uri(struct line_file *lfile, struct uri **result)
+uris_add(struct uris *uris, char *uri)
 {
-       struct uri *uri;
-       int err;
-
-       uri = malloc(sizeof(struct uri));
-       if (uri == NULL)
-               return pr_enomem();
-
-       err = lfile_read(lfile, &uri->string);
-       if (err) {
-               free(uri);
-               return err;
+       char **tmp;
+
+       if (uris->count + 1 >= uris->size) {
+               uris->size *= 2;
+               tmp = realloc(uris->array, uris->size * sizeof(char *));
+               if (tmp == NULL)
+                       return pr_enomem();
+               uris->array = tmp;
        }
 
-       *result = uri;
+       uris->array[uris->count++] = uri;
        return 0;
 }
 
 static int
-read_uris(struct line_file *lfile, struct uri_list *uris)
+read_uris(struct line_file *lfile, struct uris *uris)
 {
-       struct uri *previous, *uri;
-       int err;
+       char *uri;
+       int error;
 
-       err = read_uri(lfile, &uri);
-       if (err)
-               return err;
+       error = lfile_read(lfile, &uri);
+       if (error)
+               return error;
 
-       if (strcmp(uri->string, "") == 0) {
-               uri_destroy(uri);
-               return pr_err("TAL file %s contains no URIs",
-                   lfile_name(lfile));
+       if (strcmp(uri, "") == 0) {
+               free(uri);
+               return pr_err("TAL file contains no URIs");
        }
 
-       SLIST_INIT(uris);
-       SLIST_INSERT_HEAD(uris, uri, next);
+       error = uris_add(uris, uri);
+       if (error)
+               return error;
 
        do {
-               previous = uri;
-
-               err = read_uri(lfile, &uri);
-               if (err)
-                       return err;
+               error = lfile_read(lfile, &uri);
+               if (error)
+                       return error;
 
-               if (strcmp(uri->string, "") == 0) {
-                       uri_destroy(uri);
+               if (strcmp(uri, "") == 0) {
+                       free(uri);
                        return 0; /* Happy path */
                }
 
-               SLIST_INSERT_AFTER(previous, uri, next);
+               error = uris_add(uris, uri);
+               if (error)
+                       return error;
        } while (true);
 }
 
@@ -146,36 +143,42 @@ tal_load(const char *file_name, struct tal **result)
 {
        struct line_file *lfile;
        struct tal *tal;
-       int err;
+       int error;
 
-       err = lfile_open(file_name, &lfile);
-       if (err)
-               return err;
+       error = lfile_open(file_name, &lfile);
+       if (error)
+               goto fail4;
 
        tal = malloc(sizeof(struct tal));
        if (tal == NULL) {
-               lfile_close(lfile);
-               return -ENOMEM;
+               error = -ENOMEM;
+               goto fail3;
        }
 
-       err = read_uris(lfile, &tal->uris);
-       if (err) {
-               free(tal);
-               lfile_close(lfile);
-               return err;
-       }
+       error = uris_init(&tal->uris);
+       if (error)
+               goto fail2;
 
-       err = read_spki(lfile, tal);
-       if (err) {
-               uris_destroy(&tal->uris);
-               free(tal);
-               lfile_close(lfile);
-               return err;
-       }
+       error = read_uris(lfile, &tal->uris);
+       if (error)
+               goto fail1;
+
+       error = read_spki(lfile, tal);
+       if (error)
+               goto fail1;
 
        lfile_close(lfile);
        *result = tal;
        return 0;
+
+fail1:
+       uris_destroy(&tal->uris);
+fail2:
+       free(tal);
+fail3:
+       lfile_close(lfile);
+fail4:
+       return error;
 }
 
 void tal_destroy(struct tal *tal)
@@ -191,11 +194,11 @@ void tal_destroy(struct tal *tal)
 int
 foreach_uri(struct tal *tal, foreach_uri_cb cb)
 {
-       struct uri *cursor;
+       unsigned int i;
        int error;
 
-       SLIST_FOREACH(cursor, &tal->uris, next) {
-               error = cb(tal, cursor->string);
+       for (i = 0; i < tal->uris.count; i++) {
+               error = cb(tal, tal->uris.array[i]);
                if (error)
                        return error;
        }
@@ -203,6 +206,25 @@ foreach_uri(struct tal *tal, foreach_uri_cb cb)
        return 0;
 }
 
+void
+tal_shuffle_uris(struct tal *tal)
+{
+       char **array = tal->uris.array;
+       unsigned int count = tal->uris.count;
+       char *tmp;
+       long random_index;
+       unsigned int i;
+
+       random_init();
+
+       for (i = 0; i < count; i++) {
+               tmp = array[i];
+               random_index = random_at_most(count - 1 - i) + i;
+               array[i] = array[random_index];
+               array[random_index] = tmp;
+       }
+}
+
 void
 tal_get_spki(struct tal *tal, unsigned char const **buffer, size_t *len)
 {
index d20a9025ce9dd38acd806b233e457bba85095f1b..6be3099eb7ff1252409d93fed3482b6d155e77c3 100644 (file)
@@ -12,6 +12,7 @@ void tal_destroy(struct tal *);
 
 typedef int (*foreach_uri_cb)(struct tal *, char const *);
 int foreach_uri(struct tal *, foreach_uri_cb);
+void tal_shuffle_uris(struct tal *);
 
 void tal_get_spki(struct tal *, unsigned char const **, size_t *);
 
diff --git a/src/random.c b/src/random.c
new file mode 100644 (file)
index 0000000..291ee37
--- /dev/null
@@ -0,0 +1,38 @@
+#include "random.h"
+#include <stdlib.h>
+#include <time.h>
+
+void
+random_init(void)
+{
+       /*
+        * time() has second precision, which is fine.
+        * I don't think that anyone will legitimately need to run this program
+        * more than once a second.
+        */
+       srandom(time(NULL));
+}
+
+/**
+ * Assumes 0 <= max <= RAND_MAX
+ * Returns in the closed interval [0, max]
+ *
+ * Source: https://stackoverflow.com/questions/2509679
+ */
+long random_at_most(long max)
+{
+       /* max <= RAND_MAX < ULONG_MAX, so this is okay. */
+       unsigned long num_bins = (unsigned long) max + 1;
+       unsigned long num_rand = (unsigned long) RAND_MAX + 1;
+       unsigned long bin_size = num_rand / num_bins;
+       unsigned long defect = num_rand % num_bins;
+       long x;
+
+       do {
+               x = random();
+       /* This is carefully written not to overflow */
+       } while (num_rand - defect <= (unsigned long) x);
+
+       /* Truncated division is intentional */
+       return x / bin_size;
+}
diff --git a/src/random.h b/src/random.h
new file mode 100644 (file)
index 0000000..1e837ce
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef SRC_RANDOM_H_
+#define SRC_RANDOM_H_
+
+void random_init(void);
+long random_at_most(long max);
+
+#endif /* SRC_RANDOM_H_ */
index 6b84a5dda872b13f832bf0463c27b7a9c75a2199..0e343016710ea794ea4672c70f8e1785af28dabb 100644 (file)
@@ -139,7 +139,7 @@ inherit_aors(struct resources *resources, int family)
        struct resources *parent;
 
        parent = get_parent_resources();
-       if (!parent)
+       if (parent == NULL)
                return pr_err("Certificate inherits IP resources, but parent does not define any resources.");
 
        switch (family) {
@@ -400,7 +400,7 @@ inherit_asiors(struct resources *resources)
        struct resources *parent;
 
        parent = get_parent_resources();
-       if (!parent)
+       if (parent == NULL)
                return pr_err("Certificate inherits ASN resources, but parent does not define any resources.");
 
        if (resources->asns != NULL)
@@ -498,6 +498,14 @@ resources_add_asn(struct resources *resources, struct ASIdentifiers *ids)
        return pr_err("Unknown ASIdentifierChoice: %d", ids->asnum->present);
 }
 
+bool
+resources_empty(struct resources *res)
+{
+       return rasn_empty(res->asns)
+           && res4_empty(res->ip4s)
+           && res6_empty(res->ip6s);
+}
+
 bool
 resources_contains_asn(struct resources *res, ASId_t asn)
 {
index f70c9549fddff1b331b136537ef373e10050dcee..d488860673ebf62db5d09d8561ce82f29ccf3bff 100644 (file)
@@ -16,6 +16,7 @@ void resources_destroy(struct resources *);
 int resources_add_ip(struct resources *, struct IPAddressFamily *);
 int resources_add_asn(struct resources *, struct ASIdentifiers *);
 
+bool resources_empty(struct resources *);
 bool resources_contains_asn(struct resources *, ASId_t);
 bool resources_contains_ipv4(struct resources *, struct ipv4_prefix *);
 bool resources_contains_ipv6(struct resources *, struct ipv6_prefix *);
index 1dd436a80618d57264b1c86a3cd685ff20def572..11581ab925b5514382f992bec7b13b1ddfa4b649 100644 (file)
@@ -59,6 +59,12 @@ rasn_add(struct resources_asn *asns, ASId_t min, ASId_t max)
        return sarray_add((struct sorted_array *) asns, &n);
 }
 
+bool
+rasn_empty(struct resources_asn *asns)
+{
+       return sarray_empty((struct sorted_array *) asns);
+}
+
 bool
 rasn_contains(struct resources_asn *asns, ASId_t min, ASId_t max)
 {
index 0d812bf22e0ff2f3ab2bf8b7db8a0cbbf9d472d9..855372a35b7950432273684f5177119879442671 100644 (file)
@@ -11,6 +11,7 @@ void rasn_get(struct resources_asn *);
 void rasn_put(struct resources_asn *);
 
 int rasn_add(struct resources_asn *, ASId_t, ASId_t);
+bool rasn_empty(struct resources_asn *);
 bool rasn_contains(struct resources_asn *, ASId_t, ASId_t);
 
 #endif /* SRC_RESOURCE_ASN_H_ */
index 4612b6351c3a3e6bcc54d6363d08a8e9bf6d6c3b..140f0961eb55c4374947888df5891aed5a393317 100644 (file)
@@ -77,6 +77,11 @@ res4_add_range(struct resources_ipv4 *ips, struct ipv4_range *range)
        return sarray_add((struct sorted_array *) ips, &n);
 }
 
+bool res4_empty(struct resources_ipv4 *ips)
+{
+       return sarray_empty((struct sorted_array *) ips);
+}
+
 bool
 res4_contains_prefix(struct resources_ipv4 *ips, struct ipv4_prefix *prefix)
 {
index 8abf0b27cc79cf920ed2a5c9108b61d2ab6234a6..bf4db9b45bf77286a5c4b3f34699e7b763df5440 100644 (file)
@@ -12,6 +12,7 @@ void res4_put(struct resources_ipv4 *);
 
 int res4_add_prefix(struct resources_ipv4 *, struct ipv4_prefix *);
 int res4_add_range(struct resources_ipv4 *, struct ipv4_range *);
+bool res4_empty(struct resources_ipv4 *);
 bool res4_contains_prefix(struct resources_ipv4 *, struct ipv4_prefix *);
 bool res4_contains_range(struct resources_ipv4 *, struct ipv4_range *);
 
index 42346554f3d1dfff73c231fab2ca39852119e779..dccf661c2b2072af302afe8b818ab77806926a33 100644 (file)
@@ -93,6 +93,12 @@ res6_add_range(struct resources_ipv6 *ips, struct ipv6_range *range)
        return sarray_add((struct sorted_array *) ips, range);
 }
 
+bool
+res6_empty(struct resources_ipv6 *ips)
+{
+       return sarray_empty((struct sorted_array *) ips);
+}
+
 bool
 res6_contains_prefix(struct resources_ipv6 *ips, struct ipv6_prefix *prefix)
 {
index 74aa40ce6164342456e276339e3600f8afff78c5..6e78c805cb89be9645bb5b8fa5d6323b527dfae5 100644 (file)
@@ -12,6 +12,7 @@ void res6_put(struct resources_ipv6 *);
 
 int res6_add_prefix(struct resources_ipv6 *ps, struct ipv6_prefix *);
 int res6_add_range(struct resources_ipv6 *, struct ipv6_range *);
+bool res6_empty(struct resources_ipv6 *ips);
 bool res6_contains_prefix(struct resources_ipv6 *, struct ipv6_prefix *);
 bool res6_contains_range(struct resources_ipv6 *, struct ipv6_range *);
 
index 6c5355934f8e7a4579783303e07d50f23f0ef99f..8ee83c4a5a1e7d2057eeb21e964027d78577f6e0 100644 (file)
@@ -126,6 +126,12 @@ sarray_add(struct sorted_array *sarray, void *element)
        return 0;
 }
 
+bool
+sarray_empty(struct sorted_array *sarray)
+{
+       return (sarray == NULL) || (sarray->count == 0);
+}
+
 bool
 sarray_contains(struct sorted_array *sarray, void *elem)
 {
index 92281f028ad19dfcac24d1a844bffc8dac2fb07f..4198b5f6f40682485c55c47129450c067e1effc6 100644 (file)
@@ -39,6 +39,7 @@ void sarray_put(struct sorted_array *);
 #define EINTERSECTION  7900
 
 int sarray_add(struct sorted_array *, void *);
+bool sarray_empty(struct sorted_array *);
 bool sarray_contains(struct sorted_array *, void *);
 
 char const *sarray_err2str(int);
index 3dff9e60aa124a325975e1e7c6ae4b9f6f244055..e2205f97859528b6f3c9721fe9108b63a03341b1 100644 (file)
@@ -33,6 +33,9 @@ struct validation {
         * seemingly not intended to be used outside of its library.)
         */
        struct restack *rsrcs;
+
+       /* Did the TAL's public key match the root certificate's public key? */
+       enum pubkey_state pubkey_state;
 };
 
 /*
@@ -107,6 +110,8 @@ validation_prepare(struct validation **out, struct tal *tal)
                goto abort3;
        }
 
+       result->pubkey_state = PKS_UNTESTED;
+
        *out = result;
        return 0;
 
@@ -160,8 +165,23 @@ validation_resources(struct validation *state)
        return state->rsrcs;
 }
 
+void validation_pubkey_valid(struct validation *state)
+{
+       state->pubkey_state = PKS_VALID;
+}
+
+void validation_pubkey_invalid(struct validation *state)
+{
+       state->pubkey_state = PKS_INVALID;
+}
+
+enum pubkey_state validation_pubkey_state(struct validation *state)
+{
+       return state->pubkey_state;
+}
+
 int
-validation_push_cert(struct validation *state, X509 *cert)
+validation_push_cert(struct validation *state, X509 *cert, bool is_ta)
 {
        struct resources *resources;
        int ok;
@@ -175,6 +195,16 @@ validation_push_cert(struct validation *state, X509 *cert)
        if (error)
                goto fail;
 
+       /*
+        * rfc7730#section-2.2
+        * "The INR extension(s) of this trust anchor MUST contain a non-empty
+        * set of number resources."
+        * The "It MUST NOT use the "inherit" form of the INR extension(s)"
+        * part is already handled in certificate_get_resources().
+        */
+       if (is_ta && resources_empty(resources))
+               return pr_err("Trust Anchor certificate does not define any number resources.");
+
        ok = sk_X509_push(state->trusted, cert);
        if (ok <= 0) {
                error = crypto_err(
index 9b5ac89f76b31e820f3c7407b866eaf7a5562ca5..0172ead3b19aff349743b02e92e9c58f421b2c90 100644 (file)
@@ -15,7 +15,17 @@ X509_STORE *validation_store(struct validation *);
 STACK_OF(X509) *validation_certs(struct validation *);
 struct restack *validation_resources(struct validation *);
 
-int validation_push_cert(struct validation *, X509 *);
+enum pubkey_state {
+       PKS_VALID,
+       PKS_INVALID,
+       PKS_UNTESTED,
+};
+
+void validation_pubkey_valid(struct validation *);
+void validation_pubkey_invalid(struct validation *);
+enum pubkey_state validation_pubkey_state(struct validation *);
+
+int validation_push_cert(struct validation *, X509 *, bool);
 int validation_pop_cert(struct validation *);
 
 struct resources *validation_peek_resource(struct validation *);