]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
More certificate validation
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Thu, 29 Nov 2018 21:52:13 +0000 (15:52 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Thu, 29 Nov 2018 21:52:37 +0000 (15:52 -0600)
Includes an implementation of RFC 3779.

There's a lot of diff pollution due to another log.c refactor.
I can't seem to nail the right implementation of that thing.

37 files changed:
src/Makefile.am
src/address.c [new file with mode: 0644]
src/address.h [new file with mode: 0644]
src/asn1/content_info.c
src/asn1/content_info.h
src/asn1/decode.c
src/asn1/decode.h
src/asn1/oid.c
src/asn1/oid.h
src/asn1/signed_data.c
src/asn1/signed_data.h
src/common.c
src/common.h
src/file.c
src/file.h
src/log.c
src/log.h
src/main.c
src/object/certificate.c
src/object/certificate.h
src/object/crl.c
src/object/crl.h
src/object/manifest.c
src/object/manifest.h
src/object/roa.c
src/object/roa.h
src/object/signed_object.c
src/object/signed_object.h
src/resource.c [new file with mode: 0644]
src/resource.h [new file with mode: 0644]
src/sorted_array.c [new file with mode: 0644]
src/sorted_array.h [new file with mode: 0644]
src/state.c
src/state.h
test/Makefile.am
test/address_test.c [new file with mode: 0644]
test/tal_test.c

index 92a6c2fac21aaccea00f2fa487b7cacaf0cc56ed..0dd1f32ae3d3c8d2c6563f5f284fbdfe646754fd 100644 (file)
@@ -6,11 +6,14 @@ bin_PROGRAMS = rpki_validator
 
 rpki_validator_SOURCES  = main.c
 
+rpki_validator_SOURCES += address.h address.c
 rpki_validator_SOURCES += common.h common.c
 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 += resource.h resource.c
+rpki_validator_SOURCES += sorted_array.h sorted_array.c
 rpki_validator_SOURCES += state.h state.c
 
 rpki_validator_SOURCES += asn1/content_info.h asn1/content_info.c
diff --git a/src/address.c b/src/address.c
new file mode 100644 (file)
index 0000000..1c26cbd
--- /dev/null
@@ -0,0 +1,51 @@
+#include "address.h"
+
+#include <string.h>
+
+bool
+prefix4_contains(const struct ipv4_prefix *a, const struct ipv4_prefix *b)
+{
+       uint32_t maskbits;
+       uint32_t a_bits;
+       uint32_t b_bits;
+
+       if (a->len > b->len)
+               return false;
+
+       maskbits = ((uint64_t) 0xffffffffU) << (32 - a->len);
+       a_bits = ntohl(a->addr.s_addr) & maskbits;
+       b_bits = ntohl(b->addr.s_addr) & maskbits;
+
+       return a_bits == b_bits;
+}
+
+bool
+prefix6_contains(const struct ipv6_prefix *a, const struct ipv6_prefix *b)
+{
+       struct in6_addr a2;
+       struct in6_addr b2;
+       unsigned int quadrant;
+       uint32_t mask;
+       unsigned int i;
+
+       if (a->len > b->len)
+               return false;
+
+       memcpy(&a2, &a->addr, sizeof(a2));
+       memcpy(&b2, &b->addr, sizeof(b2));
+
+       /* Zeroize the suffixes of a2 and b2 */
+       quadrant = a->len >> 5; /* ">> 5" is the same as "/ 32" */
+       if (quadrant > 3)
+               quadrant = 3;
+       mask = ((uint64_t) 0xffffffffU) << (32 - (a->len & 0x1f));
+       a2.s6_addr32[quadrant] = htonl(ntohl(a2.s6_addr32[quadrant]) & mask);
+       b2.s6_addr32[quadrant] = htonl(ntohl(b2.s6_addr32[quadrant]) & mask);
+       for (i = quadrant + 1; i < 4; i++) {
+               a2.s6_addr32[i] = 0;
+               b2.s6_addr32[i] = 0;
+       }
+
+       /* Finally compare */
+       return memcmp(&a2, &b2, sizeof(a2)) == 0;
+}
diff --git a/src/address.h b/src/address.h
new file mode 100644 (file)
index 0000000..abf44d7
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef SRC_ADDRESS_H_
+#define SRC_ADDRESS_H_
+
+#include <stdbool.h>
+#include <netinet/in.h>
+
+struct ipv4_prefix {
+       struct in_addr addr;
+       int len;
+};
+
+struct ipv6_prefix {
+       struct in6_addr addr;
+       int len;
+};
+
+bool prefix4_contains(const struct ipv4_prefix *, const struct ipv4_prefix *);
+bool prefix6_contains(const struct ipv6_prefix *, const struct ipv6_prefix *);
+
+#endif /* SRC_ADDRESS_H_ */
index d363d789741cd15d21d2dc939d87448cc3c2725a..147d1653818ba25853d7ce12cf9ab7c29bb3cdd1 100644 (file)
@@ -28,13 +28,12 @@ validate(struct ContentInfo *info)
 }
 
 static int
-decode(struct validation *state, struct file_contents *fc,
-    struct ContentInfo **result)
+decode(struct file_contents *fc, struct ContentInfo **result)
 {
        struct ContentInfo *cinfo;
        int error;
 
-       error = asn1_decode_fc(state, fc, &asn_DEF_ContentInfo, (void **) &cinfo);
+       error = asn1_decode_fc(fc, &asn_DEF_ContentInfo, (void **) &cinfo);
        if (error)
                return error;
 
@@ -49,17 +48,16 @@ decode(struct validation *state, struct file_contents *fc,
 }
 
 int
-content_info_load(struct validation *state, const char *file_name,
-    struct ContentInfo **result)
+content_info_load(const char *file_name, struct ContentInfo **result)
 {
        struct file_contents fc;
        int error;
 
-       error = file_load(state, file_name, &fc);
+       error = file_load(file_name, &fc);
        if (error)
                return error;
 
-       error = decode(state, &fc, result);
+       error = decode(&fc, result);
 
        file_free(&fc);
        return error;
index b8f91d9dc070288c0aa79885b08a47d4f6f3efb4..74c104ef6664139b3770c415da7e1afb54581f7a 100644 (file)
@@ -4,9 +4,8 @@
 /* Some wrappers for libcmscodec's ContentInfo. */
 
 #include <libcmscodec/ContentInfo.h>
-#include "state.h"
 
-int content_info_load(struct validation *, const char *, struct ContentInfo **);
+int content_info_load(const char *, struct ContentInfo **);
 void content_info_free(struct ContentInfo *);
 
 #endif /* SRC_CONTENT_INFO_H_ */
index 4abd49baacee7d9d95ac49a01c49e73209a05991..9aa4cb3917cdc998c6b2ebd032271fe928baf924 100644 (file)
@@ -5,8 +5,7 @@
 #include "log.h"
 
 static int
-validate(struct validation *state, asn_TYPE_descriptor_t const *descriptor,
-    void *result)
+validate(asn_TYPE_descriptor_t const *descriptor, void *result)
 {
        char error_msg[256];
        size_t error_msg_size;
@@ -17,7 +16,7 @@ validate(struct validation *state, asn_TYPE_descriptor_t const *descriptor,
        error = asn_check_constraints(descriptor, result, error_msg,
            &error_msg_size);
        if (error == -1) {
-               pr_err(state, "Error validating ASN.1 object: %s", error_msg);
+               pr_err("Error validating ASN.1 object: %s", error_msg);
                return -EINVAL;
        }
 
@@ -25,7 +24,7 @@ validate(struct validation *state, asn_TYPE_descriptor_t const *descriptor,
 }
 
 int
-asn1_decode(struct validation *state, const void *buffer, size_t buffer_size,
+asn1_decode(const void *buffer, size_t buffer_size,
     asn_TYPE_descriptor_t const *descriptor, void **result)
 {
        asn_dec_rval_t rval;
@@ -36,13 +35,13 @@ asn1_decode(struct validation *state, const void *buffer, size_t buffer_size,
        rval = ber_decode(0, descriptor, result, buffer, buffer_size);
        if (rval.code != RC_OK) {
                /* TODO if rval.code == RC_WMORE (1), more work is needed */
-               pr_err(state, "Error decoding ASN.1 object: %d", rval.code);
+               pr_err("Error decoding ASN.1 object: %d", rval.code);
                /* Must free partial object according to API contracts. */
                ASN_STRUCT_FREE(*descriptor, *result);
                return -EINVAL;
        }
 
-       error = validate(state, descriptor, *result);
+       error = validate(descriptor, *result);
        if (error) {
                ASN_STRUCT_FREE(*descriptor, *result);
                return error;
@@ -52,24 +51,22 @@ asn1_decode(struct validation *state, const void *buffer, size_t buffer_size,
 }
 
 int
-asn1_decode_any(struct validation *state, ANY_t *any,
-    asn_TYPE_descriptor_t const *descriptor, void **result)
+asn1_decode_any(ANY_t *any, asn_TYPE_descriptor_t const *descriptor,
+    void **result)
 {
-       return asn1_decode(state, any->buf, any->size, descriptor, result);
+       return asn1_decode(any->buf, any->size, descriptor, result);
 }
 
 int
-asn1_decode_octet_string(struct validation *state, OCTET_STRING_t *string,
+asn1_decode_octet_string(OCTET_STRING_t *string,
     asn_TYPE_descriptor_t const *descriptor, void **result)
 {
-       return asn1_decode(state, string->buf, string->size, descriptor,
-           result);
+       return asn1_decode(string->buf, string->size, descriptor, result);
 }
 
 int
-asn1_decode_fc(struct validation *state, struct file_contents *fc,
+asn1_decode_fc(struct file_contents *fc,
     asn_TYPE_descriptor_t const *descriptor, void **result)
 {
-       return asn1_decode(state, fc->buffer, fc->buffer_size, descriptor,
-           result);
+       return asn1_decode(fc->buffer, fc->buffer_size, descriptor, result);
 }
index 7c68ed5aae9eeaf5e93dcab2deac3d28f95b33e0..e20f878d86bf52b7f67549d871a4504ab203d7a5 100644 (file)
@@ -4,15 +4,12 @@
 #include <libcmscodec/ANY.h>
 #include <libcmscodec/constr_TYPE.h>
 #include "file.h"
-#include "state.h"
 
-int asn1_decode(struct validation *, const void *, size_t,
-    asn_TYPE_descriptor_t const *, void **);
-int asn1_decode_any(struct validation *, ANY_t *, asn_TYPE_descriptor_t const *,
+int asn1_decode(const void *, size_t, asn_TYPE_descriptor_t const *, void **);
+int asn1_decode_any(ANY_t *, asn_TYPE_descriptor_t const *, void **);
+int asn1_decode_octet_string(OCTET_STRING_t *, asn_TYPE_descriptor_t const *,
+    void **);
+int asn1_decode_fc(struct file_contents *, asn_TYPE_descriptor_t const *,
     void **);
-int asn1_decode_octet_string(struct validation *, OCTET_STRING_t *,
-    asn_TYPE_descriptor_t const *, void **);
-int asn1_decode_fc(struct validation *, struct file_contents *,
-    asn_TYPE_descriptor_t const *, void **);
 
 #endif /* SRC_ASN1_DECODE_H_ */
index 031ce113b2a5ba7109fe854b58a072692c46e0bd..6089ba934497b6e26984dcec4f86ffeb0186cfce 100644 (file)
@@ -51,12 +51,12 @@ oid2arcs(OBJECT_IDENTIFIER_t *oid, struct oid_arcs *result)
 
 /* Callers must free @result. */
 int
-any2arcs(struct validation *state, ANY_t *any, struct oid_arcs *result)
+any2arcs(ANY_t *any, struct oid_arcs *result)
 {
        OBJECT_IDENTIFIER_t *oid;
        int error;
 
-       error = asn1_decode_any(state, any, &asn_DEF_OBJECT_IDENTIFIER,
+       error = asn1_decode_any(any, &asn_DEF_OBJECT_IDENTIFIER,
            (void **) &oid);
        if (error)
                return error;
index d31b3b4c405de3f42c167410d941eac0b38df746..8dbc630bcae968e39f19750f814378af5cddedd2 100644 (file)
@@ -4,7 +4,6 @@
 #include <stdbool.h>
 #include <libcmscodec/AlgorithmIdentifier.h>
 #include "common.h"
-#include "state.h"
 
 /* These objects are expected to live on the stack. */
 struct oid_arcs {
@@ -39,7 +38,7 @@ typedef asn_oid_arc_t OID[];
 #define OID_SHA512                   { 2, 16, 840, 1, 101, 3, 4, 2, 3 }
 
 int oid2arcs(OBJECT_IDENTIFIER_t *, struct oid_arcs *);
-int any2arcs(struct validation *, ANY_t *, struct oid_arcs *);
+int any2arcs(ANY_t *, struct oid_arcs *);
 
 bool arcs_equal(struct oid_arcs const *, struct oid_arcs const *);
 /* Use ARCS_EQUAL_OID() instead. */
index 731084a0fa94f06c95467867a5c6897d4280471e..fdc3f9b4ff34c6553dbf404e4ff4a654bb6558f6 100644 (file)
@@ -41,8 +41,8 @@ is_digest_algorithm(AlgorithmIdentifier_t *aid, bool *result)
 }
 
 static int
-validate_content_type_attribute(struct validation *state,
-    CMSAttributeValue_t *value, EncapsulatedContentInfo_t *eci)
+validate_content_type_attribute(CMSAttributeValue_t *value,
+    EncapsulatedContentInfo_t *eci)
 {
        struct oid_arcs attrValue_arcs;
        struct oid_arcs EncapContentInfo_arcs;
@@ -50,7 +50,7 @@ validate_content_type_attribute(struct validation *state,
 
        /* rfc6488#section-3.1.h */
 
-       error = any2arcs(state, value, &attrValue_arcs);
+       error = any2arcs(value, &attrValue_arcs);
        if (error)
                return error;
 
@@ -61,8 +61,7 @@ validate_content_type_attribute(struct validation *state,
        }
 
        if (!arcs_equal(&attrValue_arcs, &EncapContentInfo_arcs)) {
-               pr_err(state,
-                   "The eContentType in the EncapsulatedContentInfo does not match the attrValues in the content-type attribute.");
+               pr_err("The eContentType in the EncapsulatedContentInfo does not match the attrValues in the content-type attribute.");
                error = -EINVAL;
        }
 
@@ -72,14 +71,13 @@ validate_content_type_attribute(struct validation *state,
 }
 
 static int
-validate_message_digest_attribute(struct validation *state, CMSAttributeValue_t *value)
+validate_message_digest_attribute(CMSAttributeValue_t *value)
 {
        return 0; /* TODO need the content being signed */
 }
 
 static int
-validate_signed_attrs(struct validation *state, struct SignerInfo *sinfo,
-    EncapsulatedContentInfo_t *eci)
+validate_signed_attrs(struct SignerInfo *sinfo, EncapsulatedContentInfo_t *eci)
 {
        struct CMSAttribute *attr;
        struct CMSAttribute__attrValues *attrs;
@@ -92,28 +90,26 @@ validate_signed_attrs(struct validation *state, struct SignerInfo *sinfo,
        int error;
 
        if (sinfo->signedAttrs == NULL) {
-               pr_err(state, "The SignerInfo's signedAttrs field is NULL.");
+               pr_err("The SignerInfo's signedAttrs field is NULL.");
                return -EINVAL;
        }
 
        for (i = 0; i < sinfo->signedAttrs->list.count; i++) {
                attr = sinfo->signedAttrs->list.array[i];
                if (attr == NULL) {
-                       pr_err(state, "SignedAttrs array element %u is NULL.",
-                           i);
+                       pr_err("SignedAttrs array element %u is NULL.", i);
                        continue;
                }
                attrs = &attr->attrValues;
 
                if (attrs->list.count != 1) {
-                       pr_err(state, 0,
+                       pr_err(0,
                            "signedAttrs's attribute set size (%d) is different than 1.",
                            attr->attrValues.list.count);
                        return -EINVAL;
                }
                if (attrs->list.array == NULL || attrs->list.array[0] == NULL) {
-                       pr_err(state,
-                           "Programming error: Array size is 1 but array itself is NULL.");
+                       pr_err("Programming error: Array size is 1 but array itself is NULL.");
                        return -EINVAL;
                }
 
@@ -123,25 +119,25 @@ validate_signed_attrs(struct validation *state, struct SignerInfo *sinfo,
 
                if (ARCS_EQUAL_OIDS(&attrType, oid_cta)) {
                        if (content_type_found) {
-                               pr_err(state, "Multiple ContentTypes found.");
+                               pr_err("Multiple ContentTypes found.");
                                goto illegal_attrType;
                        }
-                       error = validate_content_type_attribute(state,
+                       error = validate_content_type_attribute(
                            attr->attrValues.list.array[0], eci);
                        content_type_found = true;
 
                } else if (ARCS_EQUAL_OIDS(&attrType, oid_mda)) {
                        if (message_digest_found) {
-                               pr_err(state, "Multiple MessageDigests found.");
+                               pr_err("Multiple MessageDigests found.");
                                goto illegal_attrType;
                        }
-                       error = validate_message_digest_attribute(state,
+                       error = validate_message_digest_attribute(
                            attr->attrValues.list.array[0]);
                        message_digest_found = true;
 
                } else if (ARCS_EQUAL_OIDS(&attrType, oid_sta)) {
                        if (signing_time_found) {
-                               pr_err(state, "Multiple SigningTimes found.");
+                               pr_err("Multiple SigningTimes found.");
                                goto illegal_attrType;
                        }
                        error = 0; /* No validations needed for now. */
@@ -149,7 +145,7 @@ validate_signed_attrs(struct validation *state, struct SignerInfo *sinfo,
 
                } else if (ARCS_EQUAL_OIDS(&attrType, oid_bst)) {
                        if (binary_signing_time_found) {
-                               pr_err(state, "Multiple BinarySigningTimes found.");
+                               pr_err("Multiple BinarySigningTimes found.");
                                goto illegal_attrType;
                        }
                        error = 0; /* No validations needed for now. */
@@ -157,7 +153,7 @@ validate_signed_attrs(struct validation *state, struct SignerInfo *sinfo,
 
                } else {
                        /* rfc6488#section-3.1.g */
-                       pr_err(state, "Illegal attrType OID in SignerInfo.");
+                       pr_err("Illegal attrType OID in SignerInfo.");
                        goto illegal_attrType;
                }
 
@@ -169,11 +165,11 @@ validate_signed_attrs(struct validation *state, struct SignerInfo *sinfo,
 
        /* rfc6488#section-3.1.f */
        if (!content_type_found) {
-               pr_err(state, "SignerInfo lacks a ContentType attribute.");
+               pr_err("SignerInfo lacks a ContentType attribute.");
                return -EINVAL;
        }
        if (!message_digest_found) {
-               pr_err(state, "SignerInfo lacks a MessageDigest attribute.");
+               pr_err("SignerInfo lacks a MessageDigest attribute.");
                return -EINVAL;
        }
 
@@ -185,7 +181,7 @@ illegal_attrType:
 }
 
 static int
-validate(struct validation *state, struct SignedData *sdata)
+validate(struct SignedData *sdata)
 {
        struct SignerInfo *sinfo;
        bool is_digest;
@@ -193,8 +189,7 @@ validate(struct validation *state, struct SignedData *sdata)
 
        /* rfc6488#section-2.1 */
        if (sdata->signerInfos.list.count != 1) {
-               pr_err(state,
-                   "The SignedData's SignerInfo set is supposed to have only one element. (%d given.)",
+               pr_err("The SignedData's SignerInfo set is supposed to have only one element. (%d given.)",
                    sdata->signerInfos.list.count);
                return -EINVAL;
        }
@@ -202,8 +197,7 @@ validate(struct validation *state, struct SignedData *sdata)
        /* rfc6488#section-2.1.1 */
        /* rfc6488#section-3.1.b */
        if (sdata->version != 3) {
-               pr_err(state,
-                   "The SignedData version is only allowed to be 3. (Was %ld.)",
+               pr_err("The SignedData version is only allowed to be 3. (Was %ld.)",
                    sdata->version);
                return -EINVAL;
        }
@@ -211,8 +205,7 @@ validate(struct validation *state, struct SignedData *sdata)
        /* rfc6488#section-2.1.2 */
        /* rfc6488#section-3.1.j 1/2 */
        if (sdata->digestAlgorithms.list.count != 1) {
-               pr_err(state,
-                   "The SignedData's digestAlgorithms set is supposed to have only one element. (%d given.)",
+               pr_err("The SignedData's digestAlgorithms set is supposed to have only one element. (%d given.)",
                    sdata->digestAlgorithms.list.count);
                return -EINVAL;
        }
@@ -228,8 +221,7 @@ validate(struct validation *state, struct SignedData *sdata)
        if (error)
                return error;
        if (!is_digest) {
-               pr_err(state,
-                   "The SignedData's digestAlgorithm OID is not listed in RFC 5754.");
+               pr_err("The SignedData's digestAlgorithm OID is not listed in RFC 5754.");
                return -EINVAL;
        }
 
@@ -239,13 +231,12 @@ validate(struct validation *state, struct SignedData *sdata)
        /* rfc6488#section-2.1.4 */
        /* rfc6488#section-3.1.c TODO missing half of the requirement. */
        if (sdata->certificates == NULL) {
-               pr_err(state, "The SignedData does not contain certificates.");
+               pr_err("The SignedData does not contain certificates.");
                return -EINVAL;
        }
 
        if (sdata->certificates->list.count != 1) {
-               pr_err(state,
-                   "The SignedData contains %d certificates, one expected.",
+               pr_err("The SignedData contains %d certificates, one expected.",
                    sdata->certificates->list.count);
                return -EINVAL;
        }
@@ -253,7 +244,7 @@ validate(struct validation *state, struct SignedData *sdata)
        /* rfc6488#section-2.1.5 */
        /* rfc6488#section-3.1.d */
        if (sdata->crls != NULL && sdata->crls->list.count > 0) {
-               pr_err(state, "The SignedData contains at least one crls.");
+               pr_err("The SignedData contains at least one crls.");
                return -EINVAL;
        }
 
@@ -261,12 +252,11 @@ validate(struct validation *state, struct SignedData *sdata)
        /* rfc6488#section-3.1.e */
        sinfo = sdata->signerInfos.list.array[0];
        if (sinfo == NULL) {
-               pr_err(state, "The SignerInfo object is NULL.");
+               pr_err("The SignerInfo object is NULL.");
                return -EINVAL;
        }
        if (sinfo->version != 3) {
-               pr_err(state,
-                   "The SignerInfo version is only allowed to be 3. (Was %ld.)",
+               pr_err("The SignerInfo version is only allowed to be 3. (Was %ld.)",
                    sinfo->version);
                return -EINVAL;
        }
@@ -283,13 +273,12 @@ validate(struct validation *state, struct SignedData *sdata)
        if (error)
                return error;
        if (!is_digest) {
-               pr_err(state,
-                   "The SignerInfo digestAlgorithm OID is not listed in RFC 5754.");
+               pr_err("The SignerInfo digestAlgorithm OID is not listed in RFC 5754.");
                return -EINVAL;
        }
 
        /* rfc6488#section-2.1.6.4 */
-       error = validate_signed_attrs(state, sinfo, &sdata->encapContentInfo);
+       error = validate_signed_attrs(sinfo, &sdata->encapContentInfo);
        if (error)
                return error;
 
@@ -317,7 +306,7 @@ validate(struct validation *state, struct SignedData *sdata)
        /* rfc6488#section-2.1.6.7 */
        /* rfc6488#section-3.1.i */
        if (sinfo->unsignedAttrs != NULL && sinfo->unsignedAttrs->list.count > 0) {
-               pr_err(state, "SignerInfo has at least one unsignedAttr.");
+               pr_err("SignerInfo has at least one unsignedAttr.");
                return -EINVAL;
        }
 
@@ -329,19 +318,17 @@ validate(struct validation *state, struct SignedData *sdata)
 }
 
 int
-signed_data_decode(struct validation *state, ANY_t *coded,
-    struct SignedData **result)
+signed_data_decode(ANY_t *coded, struct SignedData **result)
 {
        struct SignedData *sdata;
        int error;
 
        /* rfc6488#section-3.1.l TODO this is BER, not guaranteed to be DER. */
-       error = asn1_decode_any(state, coded, &asn_DEF_SignedData,
-           (void **) &sdata);
+       error = asn1_decode_any(coded, &asn_DEF_SignedData, (void **) &sdata);
        if (error)
                return error;
 
-       error = validate(state, sdata);
+       error = validate(sdata);
        if (error) {
                signed_data_free(sdata);
                return error;
@@ -359,8 +346,7 @@ signed_data_free(struct SignedData *sdata)
 
 /* Caller must free *@result. */
 int
-get_content_type_attr(struct validation *state, struct SignedData *sdata,
-    OBJECT_IDENTIFIER_t **result)
+get_content_type_attr(struct SignedData *sdata, OBJECT_IDENTIFIER_t **result)
 {
        struct SignedAttributes *signedAttrs;
        struct CMSAttribute *attr;
@@ -394,8 +380,7 @@ get_content_type_attr(struct validation *state, struct SignedData *sdata,
                                return -EINVAL;
                        if (attr->attrValues.list.array[0] == NULL)
                                return -EINVAL;
-                       return asn1_decode_any(state,
-                           attr->attrValues.list.array[0],
+                       return asn1_decode_any(attr->attrValues.list.array[0],
                            &asn_DEF_OBJECT_IDENTIFIER,
                            (void **) result);
                }
index 6d21361ef28d88d33efb1b648e7deac832b89098..f35d2c6b55cf4ee22afe41ab192b196252633084 100644 (file)
@@ -4,12 +4,10 @@
 /* Some wrappers for libcmscodec's SignedData. */
 
 #include <libcmscodec/SignedData.h>
-#include "state.h"
 
-int signed_data_decode(struct validation *, ANY_t *, struct SignedData **);
+int signed_data_decode(ANY_t *, struct SignedData **);
 void signed_data_free(struct SignedData *);
 
-int get_content_type_attr(struct validation *, struct SignedData *,
-    OBJECT_IDENTIFIER_t **);
+int get_content_type_attr(struct SignedData *, OBJECT_IDENTIFIER_t **);
 
 #endif /* SRC_SIGNED_DATA_H_ */
index 9dbd2e6272a167b3f397d62df092ca9899d49eb8..48f857d31bab4cfde0d36edcb0992073a6d0b008 100644 (file)
@@ -27,7 +27,7 @@ file_has_extension(char const *file_name, char const *extension)
  * You need to free the result once you're done.
  */
 int
-uri_g2l(struct validation *state, char const *guri, char **result)
+uri_g2l(char const *guri, char **result)
 {
        char const *const PREFIX = "rsync://";
        char *luri;
@@ -38,13 +38,8 @@ uri_g2l(struct validation *state, char const *guri, char **result)
        prefix_len = strlen(PREFIX);
 
        if (strncmp(PREFIX, guri, prefix_len) != 0) {
-               if (state == NULL) {
-                       warnx("Global URI %s does not begin with '%s'.", guri,
-                           PREFIX);
-               } else {
-                       pr_err(state, "Global URI %s does not begin with '%s'.",
-                           guri, PREFIX);
-               }
+               pr_err("Global URI %s does not begin with '%s'.", guri,
+                   PREFIX);
                return -EINVAL;
        }
 
@@ -69,14 +64,14 @@ uri_g2l(struct validation *state, char const *guri, char **result)
 }
 
 int
-gn2uri(struct validation *state, GENERAL_NAME *gn, char const **uri)
+gn2uri(GENERAL_NAME *gn, char const **uri)
 {
        ASN1_STRING *asn_string;
        int type;
 
        asn_string = GENERAL_NAME_get0_value(gn, &type);
        if (type != GEN_URI) {
-               pr_debug(state, "Unknown GENERAL_NAME type: %d", type);
+               pr_err("Unknown GENERAL_NAME type: %d", type);
                return -ENOTSUPPORTED;
        }
 
index 409eef955dc48dc018522a2a613ae9ffe9e2e0e7..80ac26157cc4f5c5e72e66b8c2fa15cb067e9439 100644 (file)
@@ -3,7 +3,6 @@
 
 #include <stdbool.h>
 #include <openssl/x509v3.h>
-#include "state.h"
 
 /* "I think that this is not supposed to be implemented." */
 #define ENOTSUPPORTED 3172
@@ -17,7 +16,7 @@ extern int NID_rpkiNotify;
 #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
 
 bool file_has_extension(char const *, char const *);
-int uri_g2l(struct validation *, char const *, char **);
-int gn2uri(struct validation *, GENERAL_NAME *, char const **);
+int uri_g2l(char const *, char **);
+int gn2uri(GENERAL_NAME *, char const **);
 
 #endif /* SRC_RTR_COMMON_H_ */
index 310b010be2c91541a28715312b99cda7cd09e241..e896c37fb09c0f210f01a1dbffe4db6eb12cf791 100644 (file)
@@ -21,8 +21,7 @@ get_file_size(FILE *file, long int *size)
 }
 
 int
-file_load(struct validation *state, const char *file_name,
-    struct file_contents *fc)
+file_load(const char *file_name, struct file_contents *fc)
 {
        FILE *file;
        long int file_size;
@@ -30,15 +29,13 @@ file_load(struct validation *state, const char *file_name,
        int error;
 
        file = fopen(file_name, "rb");
-       if (file == NULL) {
-               return pr_errno(state, errno, "Could not open file '%s'",
-                   file_name);
-       }
+       if (file == NULL)
+               return pr_errno(errno, "Could not open file '%s'", file_name);
 
        /* TODO if @file is a directory, this returns a very large integer. */
        error = get_file_size(file, &file_size);
        if (error) {
-               pr_errno(state, error, "Could not compute the file size of %s",
+               pr_errno(error, "Could not compute the file size of %s",
                    file_name);
                fclose(file);
                return error;
@@ -47,7 +44,7 @@ file_load(struct validation *state, const char *file_name,
        fc->buffer_size = file_size;
        fc->buffer = malloc(fc->buffer_size);
        if (fc->buffer == NULL) {
-               pr_err(state, "Out of memory.");
+               pr_err("Out of memory.");
                fclose(file);
                return -ENOMEM;
        }
@@ -61,7 +58,7 @@ file_load(struct validation *state, const char *file_name,
                         * code. It literally doesn't say how to obtain the
                         * error code.
                         */
-                       pr_errno(state, error,
+                       pr_errno(error,
                            "File reading error. Error message (apparently)",
                            file_name);
                        free(fc->buffer);
@@ -73,9 +70,9 @@ file_load(struct validation *state, const char *file_name,
                 * As far as I can tell from the man page, feof() cannot return
                 * less bytes that requested like read() does.
                 */
-               pr_err(state, "Likely programming error: fread() < file size");
-               pr_err(state, "fr:%zu bs:%zu EOF:%d", fread_result,
-                   fc->buffer_size, feof(file));
+               pr_err("Likely programming error: fread() < file size");
+               pr_err("fr:%zu bs:%zu EOF:%d", fread_result, fc->buffer_size,
+                   feof(file));
                free(fc->buffer);
                fclose(file);
                return -EINVAL;
index 3e4123904ba650301cf3ae98e4393666ad8c7f4e..063c85db56bdb00f285216155a6da347c0b39638 100644 (file)
@@ -2,7 +2,6 @@
 #define SRC_FILE_H_
 
 #include <stddef.h>
-#include "state.h"
 
 /*
  * The entire contents of the file, loaded into a buffer.
@@ -14,7 +13,7 @@ struct file_contents {
        size_t buffer_size;
 };
 
-int file_load(struct validation *, const char *, struct file_contents *);
+int file_load(const char *, struct file_contents *);
 void file_free(struct file_contents *);
 
 #endif /* SRC_FILE_H_ */
index 7919d8d6b738107ff69d82f7c3a7380ababaa00a..c491ee5135a721fc6b1b4fa737a9978619c6926c 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -10,7 +10,7 @@
 static unsigned int indent;
 
 static void
-pr_indent(BIO *stream)
+pr_indent(void)
 {
        unsigned int __indent = indent;
        unsigned int i;
@@ -19,7 +19,7 @@ pr_indent(BIO *stream)
 //             __indent = INDENT_MAX;
 
        for (i = 0; i < __indent; i++)
-               BIO_printf(stream, "  ");
+               printf("  ");
 }
 
 static void
@@ -38,63 +38,60 @@ pr_rm_indent(void)
 }
 
 static void
-print_debug_prefix(BIO *bio)
+print_debug_prefix(void)
 {
-       BIO_printf(bio, "DBG: ");
-       pr_indent(bio);
+       printf("DBG: ");
+       pr_indent();
 }
 
 #endif
 
 void
-pr_debug(struct validation *state, const char *format, ...)
+pr_debug(const char *format, ...)
 {
 #ifdef DEBUG
-       BIO *bio = validation_stdout(state);
        va_list args;
 
-       print_debug_prefix(bio);
+       print_debug_prefix();
 
        va_start(args, format);
-       BIO_vprintf(bio, format, args);
+       vprintf(format, args);
        va_end(args);
-       BIO_printf(bio, "\n");
+       printf("\n");
 #endif
 }
 
 void
-pr_debug_add(struct validation *state, const char *format, ...)
+pr_debug_add(const char *format, ...)
 {
 #ifdef DEBUG
-       BIO *bio = validation_stdout(state);
        va_list args;
 
-       print_debug_prefix(bio);
+       print_debug_prefix();
 
        va_start(args, format);
-       BIO_vprintf(bio, format, args);
+       vprintf(format, args);
        va_end(args);
-       BIO_printf(bio, "\n");
+       printf("\n");
 
        pr_add_indent();
 #endif
 }
 
 void
-pr_debug_rm(struct validation *state, const char *format, ...)
+pr_debug_rm(const char *format, ...)
 {
 #ifdef DEBUG
-       BIO *bio = validation_stdout(state);
        va_list args;
 
        pr_rm_indent();
 
-       print_debug_prefix(bio);
+       print_debug_prefix();
 
        va_start(args, format);
-       BIO_vprintf(bio, format, args);
+       vprintf(format, args);
        va_end(args);
-       BIO_printf(bio, "\n");
+       printf("\n");
 #endif
 }
 
@@ -102,15 +99,14 @@ pr_debug_rm(struct validation *state, const char *format, ...)
  * Always appends a newline at the end.
  */
 void
-pr_err(struct validation *state, const char *format, ...)
+pr_err(const char *format, ...)
 {
-       BIO *bio = validation_stderr(state);
        va_list args;
 
        va_start(args, format);
-       BIO_vprintf(bio, format, args);
+       vfprintf(stderr, format, args);
        va_end(args);
-       BIO_printf(bio, "\n");
+       fprintf(stderr, "\n");
 }
 
 /**
@@ -128,23 +124,22 @@ pr_err(struct validation *state, const char *format, ...)
  * Always appends a newline at the end.
  */
 int
-pr_errno(struct validation *state, int error, const char *format, ...)
+pr_errno(int error, const char *format, ...)
 {
-       BIO *bio = validation_stderr(state);
        va_list args;
 
        va_start(args, format);
-       BIO_vprintf(bio, format, args);
+       vfprintf(stderr, format, args);
        va_end(args);
 
        if (error) {
-               BIO_printf(bio, ": %s", strerror(error));
+               fprintf(stderr, ": %s", strerror(error));
        } else {
                /* We should assume that there WAS an error; go generic. */
                error = -EINVAL;
        }
 
-       BIO_printf(bio, "\n");
+       fprintf(stderr, "\n");
 
        return error;
 }
@@ -174,9 +169,9 @@ crypto_err(struct validation *state, const char *format, ...)
        va_start(args, format);
        BIO_vprintf(bio, format, args);
        va_end(args);
+       BIO_printf(bio, ": ");
 
        if (error) {
-               BIO_printf(bio, ": ");
                /*
                 * Reminder: This clears the error queue.
                 * BTW: The string format is pretty ugly. Maybe override this.
@@ -184,6 +179,7 @@ crypto_err(struct validation *state, const char *format, ...)
                ERR_print_errors(bio);
        } else {
                /* We should assume that there WAS an error; go generic. */
+               BIO_printf(bio, "(There are no error messages in the stack.)");
                error = -EINVAL;
        }
 
index 6697054659ec3c3bc4e3ae0d8dad8d6e042c8a99..be2792758848df37691426497fcfab909083ab38 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -3,15 +3,14 @@
 
 #include "state.h"
 
-void pr_debug(struct validation *, const char *, ...);
-void pr_debug_add(struct validation *, const char *, ...);
-void pr_debug_rm(struct validation *, const char *, ...);
+void pr_debug(const char *, ...);
+void pr_debug_add(const char *, ...);
+void pr_debug_rm(const char *, ...);
 
-void pr_err(struct validation *, const char *, ...);
-int pr_errno(struct validation *, int, const char *, ...);
+void pr_err(const char *, ...);
+int pr_errno(int, const char *, ...);
 int crypto_err(struct validation *, const char *, ...);
 
-#define PR_DEBUG(msg) \
-    printf("%s:%d (%s()): " msg "\n", __FILE__, __LINE__, __func__)
+#define PR_DEBUG printf("%s:%d (%s())\n", __FILE__, __LINE__, __func__)
 
 #endif /* SRC_LOG_H_ */
index 467b742b3020d450e1c913278bbcfea330eeb4e9..7e8812b31bb01eed3274412de9348c66245b0b70 100644 (file)
@@ -39,7 +39,7 @@ handle_tal_uri(char const *uri)
        char *cert_file;
        int error;
 
-       error = uri_g2l(NULL, uri, &cert_file);
+       error = uri_g2l(uri, &cert_file);
        if (error)
                return error;
 
@@ -47,20 +47,19 @@ handle_tal_uri(char const *uri)
        if (error)
                goto end1;
 
-       pr_debug_add(state, "TAL URI %s {", uri);
+       pr_debug_add("TAL URI %s {", uri);
 
        if (!is_certificate(uri)) {
-               pr_err(state,
-                   "TAL file does not point to a certificate. (Expected .cer, got '%s')",
+               pr_err("TAL file does not point to a certificate. (Expected .cer, got '%s')",
                    uri);
                error = -ENOTSUPPORTED;
                goto end2;
        }
 
-       error = certificate_handle_extensions(state, validation_peek(state));
+       error = certificate_traverse(state, validation_peek_cert(state));
 
 end2:
-       pr_debug_rm(state, "}");
+       pr_debug_rm("}");
        validation_destroy(state);
 end1:
        free(cert_file);
index 48be031669c326884483cb7c14e5621adf69d9a2..6b8f0f3a34071dcd06081956060565e25fa9951a 100644 (file)
@@ -2,8 +2,9 @@
 
 #include <libcmscodec/SubjectInfoAccessSyntax.h>
 #include <openssl/err.h>
-#include <openssl/x509.h>
 #include <openssl/x509v3.h>
+#include <libcmscodec/ASIdentifiers.h>
+#include <libcmscodec/IPAddrBlocks.h>
 
 #include "common.h"
 #include "log.h"
@@ -22,124 +23,240 @@ bool is_certificate(char const *file_name)
        return file_has_extension(file_name, "cer");
 }
 
-X509 *
-certificate_load(struct validation *state, const char *file)
+int
+certificate_load(struct validation *state, const char *file, X509 **result)
 {
        X509 *cert = NULL;
        BIO *bio;
+       int error;
 
        bio = BIO_new(BIO_s_file());
-       if (bio == NULL) {
-               crypto_err(state, "BIO_new(BIO_s_file()) returned NULL");
-               goto end;
-       }
+       if (bio == NULL)
+               return crypto_err(state, "BIO_new(BIO_s_file()) returned NULL");
        if (BIO_read_filename(bio, file) <= 0) {
-               crypto_err(state, "Error reading certificate '%s'", file);
+               error = crypto_err(state, "Error reading certificate '%s'", file);
                goto end;
        }
 
        cert = d2i_X509_bio(bio, NULL);
-       if (cert == NULL)
-               crypto_err(state, "Error parsing certificate '%s'", file);
+       if (cert == NULL) {
+               error = crypto_err(state, "Error parsing certificate '%s'", file);
+               goto end;
+       }
+
+       *result = cert;
+       error = 0;
 
 end:
        BIO_free(bio);
-       return cert;
+       return error;
 }
 
 int
-certificate_handle_extensions(struct validation *state, X509 *cert)
+certificate_validate(struct validation *state, X509 *cert,
+    STACK_OF(X509_CRL) *crls)
+{
+       /*
+        * TODO
+        * The only difference between -CAfile and -trusted, as it seems, is
+        * that -CAfile consults the default file location, while -trusted does
+        * not. As far as I can tell, this means that we absolutely need to use
+        * -trusted.
+        * So, just in case, enable -no-CAfile and -no-CApath.
+        */
+
+       X509_STORE_CTX *ctx;
+       int ok;
+       int error;
+
+       ctx = X509_STORE_CTX_new();
+       if (ctx == NULL) {
+               crypto_err(state, "X509_STORE_CTX_new() returned NULL");
+               return -EINVAL;
+       }
+
+       /* Returns 0 or 1 , all callers test ! only. */
+       ok = X509_STORE_CTX_init(ctx, validation_store(state), cert, NULL);
+       if (!ok) {
+               crypto_err(state, "X509_STORE_CTX_init() returned %d", ok);
+               goto abort;
+       }
+
+       X509_STORE_CTX_trusted_stack(ctx, validation_certs(state));
+       X509_STORE_CTX_set0_crls(ctx, crls);
+
+       /*
+        * HERE'S THE MEAT OF LIBCRYPTO'S VALIDATION.
+        *
+        * Can return negative codes, all callers do <= 0.
+        *
+        * Debugging BTW: If you're looking for ctx->verify,
+        * it might be internal_verify() from x509_vfy.c.
+        */
+       ok = X509_verify_cert(ctx);
+       if (ok <= 0) {
+               /*
+                * ARRRRGGGGGGGGGGGGG
+                * Do not use crypto_err() here; for some reason the proper
+                * error code is stored in the context.
+                */
+               error = X509_STORE_CTX_get_error(ctx);
+               if (error) {
+                       pr_err("Certificate validation failed: %s",
+                           X509_verify_cert_error_string(error));
+               } else {
+                       /*
+                        * ...But don't trust X509_STORE_CTX_get_error() either.
+                        * That said, there's not much to do about !error,
+                        * so hope for the best.
+                        */
+                       crypto_err(state, "Certificate validation failed: %d",
+                           ok);
+               }
+
+               goto abort;
+       }
+
+       X509_STORE_CTX_free(ctx);
+       return 0;
+
+abort:
+       X509_STORE_CTX_free(ctx);
+       return -EINVAL;
+}
+
+/*
+ * "GENERAL_NAME, global to local"
+ * Result has to be freed.
+ */
+static int
+gn_g2l(GENERAL_NAME *name, char **luri)
 {
-       SIGNATURE_INFO_ACCESS *sia;
-       ACCESS_DESCRIPTION *ad;
        char const *uri;
-       char *luri;
-       int nid;
+       int error;
+
+       error = gn2uri(name, &uri);
+       if (error)
+               return error;
+
+       return uri_g2l(uri, luri);
+}
+
+static int
+handle_ip_extension(struct validation *state, X509_EXTENSION *ext,
+    struct resources *resources)
+{
+       ASN1_OCTET_STRING *string;
+       struct IPAddrBlocks *blocks;
        int i;
        int error;
 
-       sia = X509_get_ext_d2i(cert, NID_sinfo_access, &error, NULL);
-       if (sia == NULL) {
-               switch (error) {
-               case -1:
-                       pr_err(state, "Certificate lacks an SIA extension.");
-                       return -ESRCH;
-               case -2:
-                       pr_err(state, "Certificate has more than one SIA extension.");
-                       return -EINVAL;
-               default:
-                       pr_err(state,
-                           "X509_get_ext_d2i() returned unknown error code %d.",
-                           error);
-                       return -EINVAL;
-               }
+       string = X509_EXTENSION_get_data(ext);
+       error = asn1_decode(string->data, string->length, &asn_DEF_IPAddrBlocks,
+           (void **) &blocks);
+       if (error)
+               return error;
+
+       /*
+        * TODO There MUST be only one IPAddressFamily SEQUENCE per AFI.
+        * Each SEQUENCE MUST be ordered by ascending addressFamily values.
+        */
+       for (i = 0; i < blocks->list.count; i++) {
+               error = resources_add_ip(resources, blocks->list.array[i],
+                   validation_peek_resource(state));
+               if (error)
+                       break;
        }
 
-       pr_debug_add(state, "SIA {");
-       error = 0;
+       ASN_STRUCT_FREE(asn_DEF_IPAddrBlocks, blocks);
+       return error;
+}
 
-       for (i = 0; i < sk_ACCESS_DESCRIPTION_num(sia); i++) {
-               ad = sk_ACCESS_DESCRIPTION_value(sia, i);
-               nid = OBJ_obj2nid(ad->method);
+static int
+handle_asn_extension(struct validation *state, X509_EXTENSION *ext,
+    struct resources *resources)
+{
+       ASN1_OCTET_STRING *string;
+       struct ASIdentifiers *ids;
+       int error;
 
-               if (nid == NID_rpkiManifest) {
-                       error = gn2uri(state, ad->location, &uri);
-                       if (error)
-                               goto end;
-                       error = uri_g2l(state, uri, &luri);
-                       if (error)
-                               goto end;
-                       error = handle_manifest(state, luri);
-                       free(luri);
-                       if (error)
-                               goto end;
+       string = X509_EXTENSION_get_data(ext);
+       error = asn1_decode(string->data, string->length,
+           &asn_DEF_ASIdentifiers, (void **) &ids);
+       if (error)
+               return error;
 
-               } else if (nid == NID_rpkiNotify) {
-                       /* TODO Another fucking RFC... */
-                       pr_debug(state, "Unimplemented thingy: rpkiNotify");
+       error = resources_add_asn(resources, ids,
+           validation_peek_resource(state));
 
-               } else if (nid == NID_caRepository) {
-                       error = gn2uri(state, ad->location, &uri);
-                       if (error)
-                               goto end;
-                       /* TODO no idea what to do with this. */
-                       pr_debug(state, "CA Repository URI: %s", uri);
+       ASN_STRUCT_FREE(asn_DEF_ASIdentifiers, ids);
+       return error;
+}
 
-               } else {
-                       pr_debug(state, "Unknown NID: %d", nid);
-                       goto end;
+int
+certificate_get_resources(struct validation *state, X509 *cert,
+    struct resources *resources)
+{
+       X509_EXTENSION *ext;
+       int i;
+       int error = 0;
+
+       /* Reference: X509_get_ext_d2i */
+       /* TODO ensure that each extension can only be found once. */
+
+       for (i = 0; i < X509_get_ext_count(cert); i++) {
+               ext = X509_get_ext(cert, i);
+
+               switch (OBJ_obj2nid(X509_EXTENSION_get_object(ext))) {
+               case NID_sbgp_ipAddrBlock:
+                       pr_debug_add("IP {");
+                       error = handle_ip_extension(state, ext, resources);
+                       pr_debug_rm("}");
+                       break;
+               case NID_sbgp_autonomousSysNum:
+                       pr_debug_add("ASN {");
+                       error = handle_asn_extension(state, ext, resources);
+                       pr_debug_rm("}");
+                       break;
                }
+
+               if (error)
+                       return error;
        }
 
-end:
-       AUTHORITY_INFO_ACCESS_free(sia);
-       pr_debug_rm(state, "}");
        return error;
 }
 
-int
-certificate_handle(struct validation *state, char const *file)
+int certificate_traverse(struct validation *state, X509 *cert)
 {
-       X509 *certificate;
+       SIGNATURE_INFO_ACCESS *sia;
+       ACCESS_DESCRIPTION *ad;
+       char *uri;
+       int i;
        int error;
 
-       pr_debug_add(state, "Certificate {");
-
-       certificate = certificate_load(state, file);
-       if (certificate == NULL) {
-               /* TODO get the right one through the ERR_* functions. */
-               error = -EINVAL;
-               goto end;
+       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;
        }
 
-       error = validation_push(state, certificate);
-       if (error)
-               goto end;
+       error = 0;
+       for (i = 0; i < sk_ACCESS_DESCRIPTION_num(sia); i++) {
+               ad = sk_ACCESS_DESCRIPTION_value(sia, i);
 
-       error = certificate_handle_extensions(state, certificate);
-       validation_pop(state);
+               if (OBJ_obj2nid(ad->method) == NID_rpkiManifest) {
+                       error = gn_g2l(ad->location, &uri);
+                       if (error)
+                               goto end;
+                       error = handle_manifest(state, uri);
+                       free(uri);
+                       if (error)
+                               goto end;
+               }
+       }
 
 end:
-       pr_debug_rm(state, "}");
+       AUTHORITY_INFO_ACCESS_free(sia);
        return error;
 }
index fc75c83cf48b23498932a6dc91fb900b8bcea587..f8bc3fbc00a0d1e56a8284d817d46151e91151b1 100644 (file)
@@ -2,11 +2,22 @@
 #define SRC_OBJECT_CERTIFICATE_H_
 
 #include <stdbool.h>
+#include <openssl/x509.h>
+#include "resource.h"
 #include "state.h"
 
 bool is_certificate(char const *);
-X509 *certificate_load(struct validation *, const char *);
-int certificate_handle(struct validation *, char const *);
-int certificate_handle_extensions(struct validation *, X509 *);
+int certificate_load(struct validation *, const char *, X509 **);
+
+/*
+ * Note: You actually need all three of these functions for a full validation;
+ * certificate_validate() checks the certificate's relationship with its
+ * parents, certificate_get_resources() covers the IP and ASN extensions, and
+ * you will need certificate_traverse() to walk through the children.
+ */
+
+int certificate_validate(struct validation *, X509 *, STACK_OF(X509_CRL) *);
+int certificate_get_resources(struct validation *, X509 *, struct resources *);
+int certificate_traverse(struct validation *, X509 *);
 
 #endif /* SRC_OBJECT_CERTIFICATE_H_ */
index a86c30c178182d1a38279af781cf7fd557ebb377..81d84de4ee678144c606eff050e9e1d1833043b8 100644 (file)
@@ -2,7 +2,6 @@
 
 #include <libcmscodec/SubjectInfoAccessSyntax.h>
 #include <openssl/err.h>
-#include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
 #include "common.h"
 #include "manifest.h"
 #include "asn1/decode.h"
 
-bool is_crl(char const *file_name)
-{
-       return file_has_extension(file_name, "crl");
-}
-
-static X509_CRL *
-load_crl(struct validation *state, const char *file)
+static int
+__crl_load(struct validation *state, const char *file, X509_CRL **result)
 {
        X509_CRL *crl = NULL;
        BIO *bio;
+       int error;
 
        bio = BIO_new(BIO_s_file());
-       if (bio == NULL) {
-               crypto_err(state, "BIO_new(BIO_s_file()) returned NULL");
-               goto end;
-       }
+       if (bio == NULL)
+               return crypto_err(state, "BIO_new(BIO_s_file()) returned NULL");
        if (BIO_read_filename(bio, file) <= 0) {
-               crypto_err(state, "Error reading CRL '%s'", file);
+               error = crypto_err(state, "Error reading CRL '%s'", file);
                goto end;
        }
 
        crl = d2i_X509_CRL_bio(bio, NULL);
-       if (crl == NULL)
-               crypto_err(state, "Error parsing CRL '%s'", file);
-
-end:
-       BIO_free(bio);
-       return crl;
-}
-
-static int
-handle_authority_key_identifier(struct validation *state, X509_EXTENSION *ext)
-{
-       /* TODO */
-       pr_debug(state, "Unimplemented still: Authority Key Identifier");
-       /* AUTHORITY_KEYID *aki = X509V3_EXT_d2i(ext); */
-       /* AUTHORITY_KEYID_free(aki); */
-       return 0;
-}
-
-static int
-handle_revoked(struct validation *state, X509_REVOKED *revoked)
-{
-       const ASN1_INTEGER *serialNumber;
-       const ASN1_TIME *revocationDate;
-
-       serialNumber = X509_REVOKED_get0_serialNumber(revoked);
-       revocationDate = X509_REVOKED_get0_revocationDate(revoked);
-
-       if (serialNumber == NULL) {
-               pr_err(state, "Revoked entry's serial number is NULL.");
-               return -EINVAL;
-       }
-       if (revocationDate == NULL) {
-               pr_err(state, "Revoked entry's revocation date is NULL.");
-               return -EINVAL;
-       }
-       if (X509_REVOKED_get0_extensions(revoked) != NULL) {
-               pr_err(state, "Revoked entry's extension list is not NULL.");
-               return -EINVAL;
-       }
-
-       pr_debug(state, "Revoked:%ld", ASN1_INTEGER_get(serialNumber));
-//     ASN1_TIME_print(bio_err, revocationDate);
-//     printf("\n");
-       return 0;
-}
-
-static int
-handle_revoked_list(struct validation *state, X509_CRL *crl)
-{
-       STACK_OF(X509_REVOKED) *list;
-       unsigned int i;
-       int error;
-
-       list = X509_CRL_get_REVOKED(crl);
-       for (i = 0; i < sk_X509_REVOKED_num(list); i++) {
-               error = handle_revoked(state, sk_X509_REVOKED_value(list, i));
-               if (error)
-                       return error;
+       if (crl == NULL) {
+               error = crypto_err(state, "Error parsing CRL '%s'", file);
+               goto end;
        }
 
-       return 0;
-}
+       *result = crl;
+       error = 0;
 
-static int
-handle_crl_number(struct validation *state, X509_EXTENSION *ext)
-{
-       pr_debug(state, "Unimplemented still: CRL Number"); /* TODO */
-       return 0;
-}
-
-static int
-handle_extensions(struct validation *state, X509_CRL *crl)
-{
-       const STACK_OF(X509_EXTENSION) *exts;
-       X509_EXTENSION *ext;
-       unsigned int i;
-       int nid;
-       int error;
-
-       exts = X509_CRL_get0_extensions(crl);
-       for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) {
-               ext = sk_X509_EXTENSION_value(exts, i);
-               nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext));
-
-               switch (nid) {
-               case NID_authority_key_identifier:
-                       error = handle_authority_key_identifier(state, ext);
-                       break;
-               case NID_crl_number:
-                       error = handle_crl_number(state, ext);
-                       break;
-               default:
-                       pr_err(state, "CRL has illegal extension: NID %d", nid);
-                       return -EINVAL;
-               }
-
-               if (error)
-                       return error;
-       }
-
-       return 0;
+end:
+       BIO_free(bio);
+       return error;
 }
 
 int
-handle_crl(struct validation *state, char const *file)
+crl_load(struct validation *state, char const *file, X509_CRL **result)
 {
-       X509_CRL *crl;
        int error;
 
-       pr_debug_add(state, "CRL {");
-
-       crl = load_crl(state, file);
-       if (!crl) {
-               /* TODO get the right one through the ERR_* functions. */
-               error = -EINVAL;
-               goto abort2;
-       }
-
-       error = handle_revoked_list(state, crl);
-       if (error)
-               goto abort3;
-
-       error = handle_extensions(state, crl);
+       pr_debug_add("CRL {");
+       error = __crl_load(state, file, result);
+       pr_debug_rm("}");
 
-abort3:
-       X509_CRL_free(crl);
-abort2:
-       pr_debug_rm(state, "}");
        return error;
 }
index b1a99bf3cd09ac2ee49efbb8e9be1cce51afe51c..ed80a37a7ad0c02e5ccec695c1110ef16829c9be 100644 (file)
@@ -2,9 +2,9 @@
 #define SRC_OBJECT_CRL_H_
 
 #include <stdbool.h>
+#include <openssl/x509.h>
 #include "state.h"
 
-bool is_crl(char const *);
-int handle_crl(struct validation *, char const *);
+int crl_load(struct validation *, char const *, X509_CRL **);
 
 #endif /* SRC_OBJECT_CRL_H_ */
index 87b90748f2f7730a86f57955c50356665e55f577..f614f4264b0b59475a2983d57e442e2bc280e662 100644 (file)
@@ -5,18 +5,17 @@
 
 #include "common.h"
 #include "log.h"
+#include "resource.h"
 #include "asn1/oid.h"
 #include "object/certificate.h"
 #include "object/crl.h"
 #include "object/roa.h"
 #include "object/signed_object.h"
 
-/* TODO not being called right now. */
-bool
-is_manifest(char const *file_name)
-{
-       return file_has_extension(file_name, "mft");
-}
+struct manifest {
+       struct Manifest *obj;
+       char const *file_path;
+};
 
 static int
 validate_dates(GeneralizedTime_t *this, GeneralizedTime_t *next)
@@ -155,51 +154,162 @@ succeed:
        return 0;
 }
 
+typedef int (*foreach_cb)(struct validation *, char *, void *);
+
+struct foreach_args {
+       STACK_OF(X509_CRL) *crls;
+       struct resources *resources;
+};
+
 static int
-handle_file(struct validation *state, char const *mft, IA5String_t *string)
+foreach_file(struct validation *state, struct manifest *mft, char *extension,
+    foreach_cb cb, void *arg)
 {
-       char *luri;
+       char *uri;
+       char *luri; /* "Local URI". As in "URI that we can easily reference." */
+       int i;
        int error;
 
-       /* TODO Treating string->buf as a C string is probably not correct. */
-//     pr_debug_add(state, "File %s {", string->buf);
+       for (i = 0; i < mft->obj->fileList.list.count; i++) {
+               /* TODO This cast is probably not correct. */
+               uri = (char *) mft->obj->fileList.list.array[i]->file.buf;
+
+               if (file_has_extension(uri, extension)) {
+                       error = get_relative_file(mft->file_path, uri, &luri);
+                       if (error)
+                               return error;
+                       error = cb(state, luri, arg);
+                       free(luri);
+                       if (error)
+                               return error;
+               }
+       }
+
+       return 0;
+}
 
-       error = get_relative_file(mft, (char const *) string->buf, &luri);
+static int
+pile_crls(struct validation *state, char *file, void *crls)
+{
+       X509_CRL *crl;
+       int error;
+       int idx;
+
+       error = crl_load(state, file, &crl);
        if (error)
-               goto end;
+               return error;
+
+       idx = sk_X509_CRL_push(crls, crl);
+       if (idx <= 0) {
+               error = crypto_err(state, "Could not add CRL to a CRL stack");
+               X509_CRL_free(crl);
+               return error;
+       }
+
+       return 0;
+}
+
+static int
+pile_addr_ranges(struct validation *state, char *file, void *__args)
+{
+       struct foreach_args *args = __args;
+       struct resources *resources;
+       X509 *cert;
+       int error = 0;
+
+       pr_debug_add("Certificate {");
+
+       /*
+        * Errors on some of these functions should not interrupt the tree
+        * traversal, so ignore them.
+        * (Error messages should have been printed in stderr.)
+        */
 
-       pr_debug_add(state, "File %s {", luri);
+       if (certificate_load(state, file, &cert))
+               goto end; /* Fine */
 
-       if (is_certificate(luri))
-               error = certificate_handle(state, luri);
-       else if (is_crl(luri))
-               error = handle_crl(state, luri);
-       else if (is_roa(luri))
-               error = handle_roa(state, luri);
-       else
-               pr_debug(state, "Unhandled file type.");
+       if (certificate_validate(state, cert, args->crls))
+               goto revert; /* Fine */
 
-       free(luri);
+       resources = resources_create();
+       if (resources == NULL) {
+               error = -ENOMEM; /* Not fine */
+               goto revert;
+       }
+
+       if (certificate_get_resources(state, cert, resources))
+               goto revert2; /* Fine */
+
+       if (validation_push_cert(state, cert, resources)) {
+               /*
+                * Validation_push_cert() only fails on OPENSSL_sk_push().
+                * The latter really only fails on memory allocation fault.
+                * That's grounds to interrupt tree traversal.
+                */
+               error = -EINVAL; /* Not fine */
+               goto revert2;
+       }
+       certificate_traverse(state, cert); /* Error code is useless. */
+       validation_pop_cert(state); /* Error code is useless. */
+
+       error = resources_join(args->resources, resources); /* Not fine */
+
+revert2:
+       resources_destroy(resources);
+revert:
+       X509_free(cert);
 end:
-       pr_debug_rm(state, "}");
+       pr_debug_rm("}");
        return error;
 }
 
 static int
-__handle_manifest(struct validation *state, char const *mft,
-    struct Manifest *manifest)
+print_roa(struct validation *state, char *file, void *arg)
 {
-       int i;
+       /* TODO */
+       handle_roa(file);
+       return 0;
+}
+
+static int
+__handle_manifest(struct validation *state, struct manifest *mft)
+{
+       struct foreach_args args;
        int error;
 
-       for (i = 0; i < manifest->fileList.list.count; i++) {
-               error = handle_file(state, mft,
-                   &manifest->fileList.list.array[i]->file);
-               if (error)
-                       return error;
+       /* Init */
+       args.crls = sk_X509_CRL_new_null();
+       if (args.crls == NULL) {
+               pr_err("Out of memory.");
+               return -ENOMEM;
        }
 
-       return 0;
+       args.resources = resources_create();
+       if (args.resources == NULL) {
+               sk_X509_CRL_free(args.crls);
+               return -ENOMEM;
+       }
+
+       /* Get CRLs as a stack. There will usually only be one. */
+       error = foreach_file(state, mft, "crl", pile_crls, args.crls);
+       if (error)
+               goto end;
+
+       /*
+        * Use CRL stack to validate certificates.
+        * Pile up valid address ranges from the valid certificates.
+        */
+       error = foreach_file(state, mft, "cer", pile_addr_ranges, &args);
+       if (error)
+               goto end;
+
+       /* Use valid address ranges to print ROAs that match them. */
+       error = foreach_file(state, mft, "roa", print_roa, &args);
+
+end:
+       resources_destroy(args.resources);
+       sk_X509_CRL_pop_free(args.crls, X509_CRL_free);
+       return error;
 }
 
 int
@@ -207,18 +317,20 @@ handle_manifest(struct validation *state, char const *file_path)
 {
        static OID oid = OID_MANIFEST;
        struct oid_arcs arcs = OID2ARCS(oid);
-       struct Manifest *manifest;
+       struct manifest mft;
        int error;
 
-       error = signed_object_decode(state, file_path, &asn_DEF_Manifest, &arcs,
-           (void **) &manifest);
+       mft.file_path = file_path;
+
+       error = signed_object_decode(file_path, &asn_DEF_Manifest, &arcs,
+           (void **) &mft.obj);
        if (error)
                return error;
 
-       error = validate_manifest(manifest);
+       error = validate_manifest(mft.obj);
        if (!error)
-               error = __handle_manifest(state, file_path, manifest);
+               error = __handle_manifest(state, &mft);
 
-       ASN_STRUCT_FREE(asn_DEF_Manifest, manifest);
+       ASN_STRUCT_FREE(asn_DEF_Manifest, mft.obj);
        return error;
 }
index e957c4c2e55ec420677596b6938fe32032581483..c49f28af1937ddabe3b7837cbc83aeda512e930e 100644 (file)
@@ -4,7 +4,6 @@
 #include <stdbool.h>
 #include "state.h"
 
-bool is_manifest(char const *);
 int handle_manifest(struct validation *, char const *);
 
 #endif /* SRC_OBJECT_MANIFEST_H_ */
index 9c6e5b13d445bcd66c1d92d8adb28b5e925ece26..f918d40146b8d3d43c2907f706260c47c25e4ff2 100644 (file)
@@ -9,17 +9,12 @@
 #include "asn1/oid.h"
 #include "object/signed_object.h"
 
-bool is_roa(char const *file_name)
-{
-       return file_has_extension(file_name, "roa");
-}
-
 static int
-validate_roa(struct validation *state, struct RouteOriginAttestation *roa)
+validate_roa(struct RouteOriginAttestation *roa)
 {
        /* rfc6482#section-3.1 */
        if (roa->version != 0) {
-               pr_err(state, "ROA's version (%ld) is nonzero.", roa->version);
+               pr_err("ROA's version (%ld) is nonzero.", roa->version);
                return -EINVAL;
        }
 
@@ -31,8 +26,7 @@ validate_roa(struct validation *state, struct RouteOriginAttestation *roa)
 }
 
 static int
-print_addr(struct validation *state, long asn, uint8_t family,
-    struct ROAIPAddress *roa_addr)
+print_addr(long asn, uint8_t family, struct ROAIPAddress *roa_addr)
 {
        union {
                struct in6_addr ip6;
@@ -53,7 +47,7 @@ print_addr(struct validation *state, long asn, uint8_t family,
                family = AF_INET6;
                break;
        default:
-               pr_err(state, "Unknown family value: %u", family);
+               pr_err("Unknown family value: %u", family);
                return -EINVAL;
        }
 
@@ -69,7 +63,7 @@ print_addr(struct validation *state, long asn, uint8_t family,
        memcpy(&addr, roa_addr->address.buf, roa_addr->address.size);
        str2 = inet_ntop(family, &addr, str.ip6, sizeof(str));
        if (str2 == NULL)
-               return pr_errno(state, errno, "Cannot parse IP address");
+               return pr_errno(errno, "Cannot parse IP address");
 
        prefix_len = 8 * roa_addr->address.size - roa_addr->address.bits_unused;
 
@@ -85,7 +79,7 @@ print_addr(struct validation *state, long asn, uint8_t family,
 }
 
 static int
-__handle_roa(struct validation *state, struct RouteOriginAttestation *roa)
+__handle_roa(struct RouteOriginAttestation *roa)
 {
        struct ROAIPAddressFamily *block;
        int b;
@@ -108,7 +102,7 @@ __handle_roa(struct validation *state, struct RouteOriginAttestation *roa)
                if (block->addresses.list.array == NULL)
                        return -EINVAL;
                for (a = 0; a < block->addresses.list.count; a++) {
-                       error = print_addr(state, roa->asID,
+                       error = print_addr(roa->asID,
                            block->addressFamily.buf[1],
                            block->addresses.list.array[a]);
                        if (error)
@@ -119,22 +113,26 @@ __handle_roa(struct validation *state, struct RouteOriginAttestation *roa)
        return 0;
 }
 
-int handle_roa(struct validation *state, char const *file)
+int handle_roa(char const *file)
 {
        static OID oid = OID_ROA;
        struct oid_arcs arcs = OID2ARCS(oid);
        struct RouteOriginAttestation *roa;
        int error;
 
-       error = signed_object_decode(state, file,
-           &asn_DEF_RouteOriginAttestation, &arcs, (void **) &roa);
+       pr_debug_add("ROA {");
+
+       error = signed_object_decode(file, &asn_DEF_RouteOriginAttestation,
+           &arcs, (void **) &roa);
        if (error)
-               return error;
+               goto end;
 
-       error = validate_roa(state, roa);
+       error = validate_roa(roa);
        if (!error)
-               error = __handle_roa(state, roa);
+               error = __handle_roa(roa);
 
        ASN_STRUCT_FREE(asn_DEF_RouteOriginAttestation, roa);
+end:
+       pr_debug_rm("}");
        return error;
 }
index a2efda4741331c1047b4997abf47fb4913cd21a0..40b5cd0f43ed8a96e81a7943065af4a3097aa202 100644 (file)
@@ -2,9 +2,7 @@
 #define SRC_OBJECT_ROA_H_
 
 #include <stdbool.h>
-#include "state.h"
 
-bool is_roa(char const *);
-int handle_roa(struct validation *, char const *);
+int handle_roa(char const *);
 
 #endif /* SRC_OBJECT_ROA_H_ */
index d162e76742b58cae5230eadf74be6ce27f2acbab..90e930a615f1bc8dcdef074a7ca9c0657b5698d6 100644 (file)
@@ -7,8 +7,7 @@
 #include "asn1/signed_data.h"
 
 static int
-validate_eContentType(struct validation *state,
-    struct SignedData *sdata,
+validate_eContentType(struct SignedData *sdata,
     asn_TYPE_descriptor_t const *descriptor,
     struct oid_arcs const *oid)
 {
@@ -22,8 +21,7 @@ validate_eContentType(struct validation *state,
        equals = arcs_equal(&arcs, oid);
        free_arcs(&arcs);
        if (!equals) {
-               pr_err(state,
-                   "SignedObject's encapContentInfo lacks the OID of a %s.",
+               pr_err("SignedObject's encapContentInfo lacks the OID of a %s.",
                    descriptor->name);
                return -EINVAL;
        }
@@ -32,8 +30,7 @@ validate_eContentType(struct validation *state,
 }
 
 static int
-validate_content_type(struct validation *state,
-    struct SignedData *sdata,
+validate_content_type(struct SignedData *sdata,
     asn_TYPE_descriptor_t const *descriptor,
     struct oid_arcs const *oid)
 {
@@ -42,7 +39,7 @@ validate_content_type(struct validation *state,
        bool equals;
        int error;
 
-       error = get_content_type_attr(state, sdata, &ctype);
+       error = get_content_type_attr(sdata, &ctype);
        if (error)
                return error;
        error = oid2arcs(ctype, &arcs);
@@ -52,8 +49,7 @@ validate_content_type(struct validation *state,
        equals = arcs_equal(&arcs, oid);
        free_arcs(&arcs);
        if (!equals) {
-               pr_err(state,
-                   "SignedObject's content type attribute lacks the OID of a %s.",
+               pr_err("SignedObject's content type attribute lacks the OID of a %s.",
                    descriptor->name);
                return -EINVAL;
        }
@@ -62,8 +58,7 @@ validate_content_type(struct validation *state,
 }
 
 int
-signed_object_decode(struct validation *state,
-    char const *file,
+signed_object_decode(char const *file,
     asn_TYPE_descriptor_t const *descriptor,
     struct oid_arcs const *oid,
     void **result)
@@ -72,29 +67,29 @@ signed_object_decode(struct validation *state,
        struct SignedData *sdata;
        int error;
 
-       error = content_info_load(state, file, &cinfo);
+       error = content_info_load(file, &cinfo);
        if (error)
                goto end1;
 
-       error = signed_data_decode(state, &cinfo->content, &sdata);
+       error = signed_data_decode(&cinfo->content, &sdata);
        if (error)
                goto end2;
 
        /* rfc6482#section-2 */
        /* rfc6486#section-4.1 */
        /* rfc6486#section-4.4.1 */
-       error = validate_eContentType(state, sdata, descriptor, oid);
+       error = validate_eContentType(sdata, descriptor, oid);
        if (error)
                goto end3;
 
        /* rfc6482#section-2 */
        /* rfc6486#section-4.3 */
-       error = validate_content_type(state, sdata, descriptor, oid);
+       error = validate_content_type(sdata, descriptor, oid);
        if (error)
                goto end3;
 
-       error = asn1_decode_octet_string(state,
-           sdata->encapContentInfo.eContent, descriptor, result);
+       error = asn1_decode_octet_string(sdata->encapContentInfo.eContent,
+           descriptor, result);
 
 end3:  signed_data_free(sdata);
 end2:  content_info_free(cinfo);
index 7bc49088dfbd27d4f4a3eb0c865b2e243af4f28a..9311d1f62337cd3280fe5afdb0833b46bfdd8d04 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "asn1/oid.h"
 
-int signed_object_decode(struct validation *, char const *,
-    asn_TYPE_descriptor_t const *, struct oid_arcs const *, void **);
+int signed_object_decode(char const *, asn_TYPE_descriptor_t const *,
+    struct oid_arcs const *, void **);
 
 #endif /* SRC_OBJECT_SIGNED_OBJECT_H_ */
diff --git a/src/resource.c b/src/resource.c
new file mode 100644 (file)
index 0000000..cf1dec1
--- /dev/null
@@ -0,0 +1,606 @@
+#include "resource.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+
+#include "address.h"
+#include "log.h"
+#include "sorted_array.h"
+
+struct resource4 {
+       struct ipv4_prefix prefix;
+};
+
+struct resource6 {
+       struct ipv6_prefix prefix;
+};
+
+struct resource_asn {
+       ASId_t min;
+       ASId_t max;
+};
+
+/* The resources we extracted from one certificate. */
+struct resources {
+       struct sorted_array *ip4s;
+       struct sorted_array *ip6s;
+       struct sorted_array *asns;
+
+       /*
+        * Used by restack. Points to the resources of the parent certificate.
+        */
+       SLIST_ENTRY(resources) next;
+};
+
+/*
+ * "Resource stack". It's a chain of resources, to complement a chain of
+ * certificates.
+ */
+SLIST_HEAD(restack, resources);
+
+static enum sarray_comparison
+ip4_cmp(void *arg1, void *arg2)
+{
+       struct ipv4_prefix *p1 = &((struct resource4 *) arg1)->prefix;
+       struct ipv4_prefix *p2 = &((struct resource4 *) arg2)->prefix;
+       uint32_t a1;
+       uint32_t a2;
+
+       if (p1->addr.s_addr == p2->addr.s_addr && p1->len == p2->len)
+               return SACMP_EQUAL;
+       if (prefix4_contains(p1, p2))
+               return SACMP_CHILD;
+       if (prefix4_contains(p2, p1))
+               return SACMP_PARENT;
+
+       a1 = ntohl(p1->addr.s_addr);
+       a2 = ntohl(p2->addr.s_addr);
+       if (a1 < a2)
+               return SACMP_RIGHT;
+       if (a2 < a1)
+               return SACMP_LEFT;
+
+       /* TODO Actually an error. Do something about it? */
+       return SACMP_INTERSECTION;
+}
+
+static enum sarray_comparison
+ip6_cmp(void *arg1, void *arg2)
+{
+       struct ipv6_prefix *p1 = &((struct resource6 *) arg1)->prefix;
+       struct ipv6_prefix *p2 = &((struct resource6 *) arg2)->prefix;
+       struct in6_addr *a1 = &p1->addr;
+       struct in6_addr *a2 = &p2->addr;
+       uint32_t q1;
+       uint32_t q2;
+       unsigned int q;
+
+       if (memcmp(a1, a2, sizeof(*a1)) == 0 && p1->len == p2->len)
+               return SACMP_EQUAL;
+       if (prefix6_contains(p1, p2))
+               return SACMP_CHILD;
+       if (prefix6_contains(p2, p1))
+               return SACMP_PARENT;
+
+       for (q = 0; q < 4; q++) {
+               q1 = ntohl(p1->addr.s6_addr32[q]);
+               q2 = ntohl(p2->addr.s6_addr32[q]);
+               if (q1 < q2)
+                       return SACMP_RIGHT;
+               if (q2 < q1)
+                       return SACMP_LEFT;
+       }
+
+       /* TODO Actually an error. Do something about it? */
+       return SACMP_INTERSECTION;
+}
+
+static enum sarray_comparison
+asn_cmp(void *arg1, void *arg2)
+{
+       struct resource_asn *asn1 = arg1;
+       struct resource_asn *asn2 = arg2;
+
+       if (asn1->min == asn2->min && asn1->max == asn2->max)
+               return SACMP_EQUAL;
+       if (asn1->min <= asn2->min && asn2->max <= asn1->max)
+               return SACMP_CHILD;
+       if (asn2->min <= asn1->min && asn1->max <= asn2->max)
+               return SACMP_PARENT;
+       if (asn1->max < asn2->min)
+               return SACMP_RIGHT;
+       if (asn2->max < asn1->min)
+               return SACMP_LEFT;
+
+       return SACMP_INTERSECTION;
+}
+
+SARRAY_API(r4array, resource4, ip4_cmp)
+SARRAY_API(r6array, resource6, ip6_cmp)
+SARRAY_API(asnarray, resource_asn, asn_cmp)
+
+struct resources *
+resources_create(void)
+{
+       struct resources *result;
+
+       result = malloc(sizeof(struct resources));
+       if (result == NULL) {
+               pr_err("Out of memory.");
+               return NULL;
+       }
+
+       result->ip4s = NULL;
+       result->ip6s = NULL;
+       result->asns = NULL;
+
+       return result;
+}
+
+void
+resources_destroy(struct resources *resources)
+{
+       if (resources->ip4s != NULL)
+               r4array_put(resources->ip4s);
+       if (resources->ip6s != NULL)
+               r6array_put(resources->ip6s);
+       if (resources->asns != NULL)
+               asnarray_put(resources->asns);
+       free(resources);
+}
+
+static int
+get_addr_family(OCTET_STRING_t *octets)
+{
+       if (octets->size != 2) {
+               pr_err("Address family has %d octets. (2 expected.)",
+                   octets->size);
+               return -1;
+       }
+
+       if (octets->buf[0] != 0)
+               goto unknown;
+       switch (octets->buf[1]) {
+       case 1:
+               return AF_INET;
+       case 2:
+               return AF_INET6;
+       }
+
+unknown:
+       pr_err("Address family has unknown value 0x%02x%02x.", octets->buf[0],
+           octets->buf[1]);
+       return -1;
+}
+
+static void
+pr_debug_prefix(int family, void *addr, int length)
+{
+#ifdef DEBUG
+       char buffer[INET6_ADDRSTRLEN];
+       char const *string;
+
+       string = inet_ntop(family, addr, buffer, sizeof(buffer));
+       if (string != NULL)
+               pr_debug("Prefix: %s/%u", string, length);
+       else {
+               pr_debug("Prefix: (Cannot convert to string. Errcode %d)",
+                   errno);
+       }
+#endif
+}
+
+static int
+inherit_aors(struct resources *resources, int family, struct resources *parent)
+{
+       switch (family) {
+       case AF_INET:
+               if (resources->ip4s != NULL) {
+                       pr_err("Oh noes4"); /* TODO */
+                       return -EINVAL;
+               }
+               if (parent->ip4s == NULL) {
+                       pr_err("Certificate inherits IPv4 resources from parent, but parent lacks IPv4 resources.");
+                       return -EINVAL;
+               }
+               resources->ip4s = parent->ip4s;
+               r4array_get(resources->ip4s);
+               return 0;
+
+       case AF_INET6:
+               if (resources->ip6s != NULL) {
+                       pr_err("Oh noes6"); /* TODO */
+                       return -EINVAL;
+               }
+               if (parent->ip6s == NULL) {
+                       pr_err("Certificate inherits IPv6 resources from parent, but parent lacks IPv6 resources.");
+                       return -EINVAL;
+               }
+               resources->ip6s = parent->ip6s;
+               r6array_get(resources->ip6s);
+               return 0;
+       }
+
+       pr_err("Programming error: Unknown IP family: %d", family);
+       return -EINVAL;
+}
+
+static int
+decode_prefix4(BIT_STRING_t *str, struct ipv4_prefix *result)
+{
+       /* TODO validate bits unused and stuff */
+       if (str->size > 4) {
+               pr_err("IPv4 address has too many octets. (%u)", str->size);
+               return -EINVAL;
+       }
+
+       memset(&result->addr, 0, sizeof(result->addr));
+       memcpy(&result->addr, str->buf, str->size);
+       result->len = 8 * str->size - str->bits_unused;
+       return 0;
+}
+
+static int
+decode_prefix6(BIT_STRING_t *str, struct ipv6_prefix *result)
+{
+       if (str->size > 16) {
+               pr_err("IPv6 address has too many octets. (%u)", str->size);
+               return -EINVAL;
+       }
+
+       memset(&result->addr, 0, sizeof(result->addr));
+       memcpy(&result->addr, str->buf, str->size);
+       result->len = 8 * str->size - str->bits_unused;
+       return 0;
+}
+
+static int
+add_prefix4(struct resources *resources, IPAddress2_t *addr,
+    struct resources *parent)
+{
+       struct resource4 r4;
+       int error;
+
+       error = decode_prefix4(addr, &r4.prefix);
+       if (error)
+               return error;
+
+       if (parent && !r4array_contains(parent->ip4s, &r4)) {
+               pr_err("Parent certificate doesn't own child's IPv4 resource.");
+               return -EINVAL;
+       }
+
+       if (resources->ip4s == NULL) {
+               resources->ip4s = r4array_create();
+               if (resources->ip4s == NULL) {
+                       pr_err("Out of memory.");
+                       return -ENOMEM;
+               }
+       }
+
+       error = r4array_add(resources->ip4s, &r4);
+       if (error)
+               return error; /* TODO error message */
+
+       pr_debug_prefix(AF_INET, &r4.prefix.addr, r4.prefix.len);
+       return 0;
+}
+
+static int
+add_prefix6(struct resources *resources, IPAddress2_t *addr,
+    struct resources *parent)
+{
+       struct resource6 r6;
+       int error;
+
+       error = decode_prefix6(addr, &r6.prefix);
+       if (error)
+               return error;
+
+       if (parent && !r6array_contains(parent->ip6s, &r6)) {
+               pr_err("Parent certificate doesn't own child's IPv6 resource.");
+               return -EINVAL;
+       }
+
+       if (resources->ip6s == NULL) {
+               resources->ip6s = r6array_create();
+               if (resources->ip6s == NULL) {
+                       pr_err("Out of memory.");
+                       return -ENOMEM;
+               }
+       }
+
+       error = r6array_add(resources->ip6s, &r6);
+       if (error)
+               return error; /* TODO error message */
+
+       pr_debug_prefix(AF_INET6, &r6.prefix.addr, r6.prefix.len);
+       return 0;
+}
+
+static int
+add_prefix(struct resources *resources, int family, IPAddress2_t *addr,
+    struct resources *parent)
+{
+       switch (family) {
+       case AF_INET:
+               return add_prefix4(resources, addr, parent);
+       case AF_INET6:
+               return add_prefix6(resources, addr, parent);
+       }
+
+       pr_err("Unknown address family: %d", family);
+       return 0;
+}
+
+static int
+add_aors(struct resources *resources, int family,
+    struct IPAddressChoice__addressesOrRanges *aors, struct resources *parent)
+{
+       struct IPAddressOrRange *aor;
+       int i;
+       int error = 0;
+
+       /*
+        * TODO The addressPrefix and addressRange elements MUST be sorted
+        * using the binary representation of (...)
+        * TODO Any pair of IPAddressOrRange choices in
+        * an extension MUST NOT overlap each other.
+        */
+
+       for (i = 0; i < aors->list.count; i++) {
+               aor = aors->list.array[i];
+               switch (aor->present) {
+               case IPAddressOrRange_PR_addressPrefix:
+                       error = add_prefix(resources, family,
+                           &aor->choice.addressPrefix, parent);
+                       if (error)
+                               return error;
+                       break;
+               case IPAddressOrRange_PR_addressRange:
+                       /*
+                        * We're definitely not supposed to support this.
+                        *
+                        * rfc3779#section-2.2.3.7 says "This specification
+                        * requires that any range of addresses that can be
+                        * encoded as a prefix MUST be encoded using an
+                        * IPAddress element (...), and any range that cannot be
+                        * encoded as a prefix MUST be encoded using an
+                        * IPAddressRange (...).
+                        *
+                        * rfc6482#section-3.3 says "Note that the syntax here
+                        * is more restrictive than that used in the IP address
+                        * delegation extension defined in RFC 3779. That
+                        * extension can represent arbitrary address ranges,
+                        * whereas ROAs need to represent only prefixes."
+                        */
+                       pr_err("IPAddressOrRange is a range. This is unsupported.");
+                       return -EINVAL;
+               case IPAddressOrRange_PR_NOTHING:
+                       /* rfc3779#section-2.2.3.7 */
+                       pr_err("Unknown IPAddressOrRange type: %d",
+                           aor->present);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+int
+resources_add_ip(struct resources *resources, struct IPAddressFamily *obj,
+    struct resources *parent)
+{
+       int family;
+
+       family = get_addr_family(&obj->addressFamily);
+       if (family == -1)
+               return -EINVAL;
+
+       switch (obj->ipAddressChoice.present) {
+       case IPAddressChoice_PR_NOTHING:
+               break;
+       case IPAddressChoice_PR_inherit:
+               return inherit_aors(resources, family, parent);
+       case IPAddressChoice_PR_addressesOrRanges:
+               return add_aors(resources, family,
+                   &obj->ipAddressChoice.choice.addressesOrRanges, parent);
+       }
+
+       /* rfc3779#section-2.2.3.4 */
+       pr_err("Unknown ipAddressChoice type: %d",
+           obj->ipAddressChoice.present);
+       return -EINVAL;
+}
+
+static int
+inherit_asiors(struct resources *resources, struct resources *parent)
+{
+       if (resources->asns != NULL) {
+               pr_err("Oh noesa"); /* TODO */
+               return -EINVAL;
+       }
+       if (parent->asns == NULL) {
+               pr_err("Certificate inherits ASN resources from parent, but parent lacks ASN resources.");
+               return -EINVAL;
+       }
+       resources->asns = parent->asns;
+       asnarray_get(resources->asns);
+       return 0;
+}
+
+static int
+add_asn(struct resources *resources, ASId_t min, ASId_t max,
+    struct resources *parent)
+{
+       struct resource_asn ra;
+       int error;
+
+       if (resources->asns == NULL) {
+               resources->asns = asnarray_create();
+               if (resources->asns == NULL) {
+                       pr_err("Out of memory.");
+                       return -ENOMEM;
+               }
+       }
+
+       ra.min = min;
+       ra.max = max;
+       error = asnarray_add(resources->asns, &ra);
+       if (error)
+               return error; /* TODO error msg */
+
+       if (min == max)
+               pr_debug("ASN: %ld", min);
+       else
+               pr_debug("ASN: %ld-%ld", min, max);
+       return 0;
+}
+
+static int
+add_asior(struct resources *resources, struct ASIdOrRange *obj,
+    struct resources *parent)
+{
+       switch (obj->present) {
+       case ASIdOrRange_PR_NOTHING:
+               break;
+       case ASIdOrRange_PR_id:
+               return add_asn(resources, obj->choice.id, obj->choice.id,
+                   parent);
+       case ASIdOrRange_PR_range:
+               return add_asn(resources, obj->choice.range.min,
+                   obj->choice.range.max, parent);
+       }
+
+       pr_err("Unknown ASIdOrRange type: %d", obj->present);
+       return -EINVAL;
+}
+
+int
+resources_add_asn(struct resources *resources, struct ASIdentifiers *ids,
+    struct resources *parent)
+{
+       struct ASIdentifierChoice__asIdsOrRanges *iors;
+       int i;
+       int error;
+
+       if (ids->asnum == NULL) {
+               pr_err("ASN extension lacks 'asnum' element.");
+               return -EINVAL;
+       }
+       if (ids->rdi != NULL) {
+               pr_err("ASN extension has 'rdi' element. (Prohibited by RFC6487)");
+               return -EINVAL;
+       }
+
+       switch (ids->asnum->present) {
+       case ASIdentifierChoice_PR_inherit:
+               return inherit_asiors(resources, parent);
+       case ASIdentifierChoice_PR_asIdsOrRanges:
+               /*
+                * TODO
+                * Any pair of items in the asIdsOrRanges SEQUENCE MUST NOT
+                * overlap. Any contiguous series of AS identifiers MUST be
+                * combined into a single range whenever possible. The AS
+                * identifiers in the asIdsOrRanges element MUST be sorted by
+                * increasing numeric value.
+                */
+               iors = &ids->asnum->choice.asIdsOrRanges;
+               for (i = 0; i < iors->list.count; i++) {
+                       error = add_asior(resources, iors->list.array[i],
+                           parent);
+                       if (error)
+                               return error;
+               }
+               return 0;
+
+       case ASIdentifierChoice_PR_NOTHING:
+               break;
+       }
+
+       pr_err("Unknown ASIdentifierChoice: %d", ids->asnum->present);
+       return -EINVAL;
+}
+
+int
+resources_join(struct resources *r1, struct resources *r2)
+{
+       int error;
+
+       if (r1->ip4s != NULL) {
+               error = r4array_join(r1->ip4s, r2->ip4s);
+               if (error)
+                       return error;
+       }
+       if (r1->ip6s != NULL) {
+               error = r6array_join(r1->ip6s, r2->ip6s);
+               if (error)
+                       return error;
+       }
+       if (r1->asns != NULL) {
+               error = asnarray_join(r1->asns, r2->asns);
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
+
+struct restack *
+restack_create(void)
+{
+       struct restack *result;
+
+       result = malloc(sizeof(struct restack));
+       if (result == NULL) {
+               pr_err("Out of memory.");
+               return NULL;
+       }
+
+       SLIST_INIT(result);
+       return result;
+}
+
+void
+restack_destroy(struct restack *stack)
+{
+       struct resources *resources;
+       unsigned int r = 0;
+
+       while (!SLIST_EMPTY(stack)) {
+               resources = SLIST_FIRST(stack);
+               SLIST_REMOVE_HEAD(stack, next);
+               resources_destroy(resources);
+               r++;
+       }
+
+       free(stack);
+       pr_debug("Deleted %u resources from the stack.", r);
+}
+
+void
+restack_push(struct restack *stack, struct resources *new)
+{
+       SLIST_INSERT_HEAD(stack, new, next);
+}
+
+struct resources *
+restack_pop(struct restack *stack)
+{
+       struct resources *res;
+
+       res = SLIST_FIRST(stack);
+       if (res != NULL) {
+               SLIST_REMOVE_HEAD(stack, next);
+               SLIST_NEXT(res, next) = NULL;
+       }
+
+       return res;
+}
+
+struct resources *
+restack_peek(struct restack *stack)
+{
+       return SLIST_FIRST(stack);
+}
diff --git a/src/resource.h b/src/resource.h
new file mode 100644 (file)
index 0000000..5a9ae04
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef SRC_RESOURCE_H_
+#define SRC_RESOURCE_H_
+
+#include <libcmscodec/ASIdentifiers.h>
+#include <libcmscodec/ASIdOrRange.h>
+#include <libcmscodec/IPAddressFamily.h>
+#include <openssl/safestack.h>
+#include <sys/queue.h>
+
+struct resources;
+struct restack;
+
+struct resources *resources_create(void);
+void resources_destroy(struct resources *);
+
+int resources_add_ip(struct resources *, struct IPAddressFamily *,
+    struct resources *);
+int resources_add_asn(struct resources *, struct ASIdentifiers *,
+    struct resources *);
+
+int resources_join(struct resources *, struct resources *);
+
+struct restack *restack_create(void);
+void restack_destroy(struct restack *);
+
+void restack_push(struct restack *, struct resources *);
+struct resources *restack_pop(struct restack *);
+struct resources *restack_peek(struct restack *);
+
+#endif /* SRC_RESOURCE_H_ */
diff --git a/src/sorted_array.c b/src/sorted_array.c
new file mode 100644 (file)
index 0000000..199dccc
--- /dev/null
@@ -0,0 +1,209 @@
+#include "sorted_array.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include "log.h"
+
+struct sorted_array {
+       void *array;
+       /* Actual number of elements in @array */
+       unsigned int count;
+       /* Total allocated slots in @array */
+       unsigned int len;
+       /* Size of each array element */
+       size_t size;
+       /* Comparison function for element insertion */
+       sarray_cmp cmp;
+
+       unsigned int refcount;
+};
+
+struct sorted_array *
+sarray_create(size_t elem_size, sarray_cmp cmp)
+{
+       struct sorted_array *result;
+
+       result = malloc(sizeof(struct sorted_array));
+       if (result == NULL)
+               return NULL;
+
+       result->array = calloc(8, elem_size);
+       if (result->array == NULL) {
+               free(result);
+               return NULL;
+       }
+       result->count = 0;
+       result->len = 8;
+       result->size = elem_size;
+       result->cmp = cmp;
+       result->refcount = 1;
+
+       return result;
+}
+
+void
+sarray_get(struct sorted_array *sarray)
+{
+       sarray->refcount++;
+}
+
+void
+sarray_put(struct sorted_array *sarray)
+{
+       sarray->refcount--;
+       if (sarray->refcount == 0) {
+               free(sarray->array);
+               free(sarray);
+       }
+}
+
+void
+sarray_destroy(struct sorted_array *sarray)
+{
+       free(sarray->array);
+       free(sarray);
+}
+
+/* Does not check boundaries. */
+static void *
+get_nth_element(struct sorted_array *sarray, unsigned int index)
+{
+       return ((char *)sarray->array) + index * sarray->size;
+}
+
+static int
+compare(struct sorted_array *sarray, void *new)
+{
+       enum sarray_comparison cmp;
+
+       if (sarray->count == 0)
+               return 0;
+
+       cmp = sarray->cmp(get_nth_element(sarray, sarray->count - 1), new);
+       switch (cmp) {
+       case SACMP_EQUAL:
+               return -EEQUAL;
+       case SACMP_CHILD:
+               return -ECHILD;
+       case SACMP_PARENT:
+               return -EPARENT;
+       case SACMP_LEFT:
+               return -ELEFT;
+       case SACMP_RIGHT:
+               return 0;
+       case SACMP_INTERSECTION:
+               return -EINTERSECTION;
+       }
+
+       pr_err("Programming error: Unknown comparison value: %d", cmp);
+       return -EINVAL;
+}
+
+int
+sarray_add(struct sorted_array *sarray, void *element)
+{
+       int error;
+       void *tmp;
+
+       error = compare(sarray, element);
+       if (error)
+               return error;
+
+       if (sarray->count >= sarray->len) {
+               tmp = realloc(sarray->array, 2 * sarray->len * sarray->size);
+               if (tmp == NULL)
+                       return -ENOMEM;
+               sarray->array = tmp;
+               sarray->len *= 2;
+       }
+
+       memcpy(get_nth_element(sarray, sarray->count), element, sarray->size);
+       sarray->count++;
+       return 0;
+}
+
+/* https://stackoverflow.com/questions/364985 */
+static int
+pow2roundup(unsigned int x)
+{
+       --x;
+       x |= x >> 1;
+       x |= x >> 2;
+       x |= x >> 4;
+       x |= x >> 8;
+       x |= x >> 16;
+       return x + 1;
+}
+
+/*
+ * Appends the elements from @addend to @sarray's tail.
+ */
+int
+sarray_join(struct sorted_array *sarray, struct sorted_array *addend)
+{
+       int error;
+       unsigned int new_count;
+       size_t new_len;
+       void *tmp;
+
+       if (addend == NULL || addend->count == 0)
+               return 0;
+
+       error = compare(sarray, addend->array);
+       if (error)
+               return error;
+
+       new_count = sarray->count + addend->count;
+       if (new_count > sarray->len) {
+               new_len = pow2roundup(new_count);
+               tmp = realloc(sarray->array, new_len * sarray->size);
+               if (tmp == NULL)
+                       return -ENOMEM;
+               sarray->array = tmp;
+               sarray->len = new_len;
+       }
+
+       memcpy(get_nth_element(sarray, sarray->count), addend->array,
+           new_count * sarray->size);
+       sarray->count += addend->count;
+       return 0;
+}
+
+bool
+sarray_contains(struct sorted_array *sarray, void *elem)
+{
+       unsigned int left, mid, right;
+       enum sarray_comparison cmp;
+
+       if (sarray == NULL || sarray->count == 0)
+               return false;
+
+       left = 0;
+       right = sarray->count - 1;
+
+       while (left <= right) {
+               mid = left + (right - left) / 2;
+               cmp = sarray->cmp(get_nth_element(sarray, mid), elem);
+               switch (cmp) {
+               case SACMP_LEFT:
+                       right = mid - 1;
+                       continue;
+               case SACMP_RIGHT:
+                       left = mid + 1;
+                       continue;
+               case SACMP_EQUAL:
+               case SACMP_CHILD:
+                       return true;
+               case SACMP_PARENT:
+                       return false;
+               case SACMP_INTERSECTION:
+                       /* Fall through; it's not supposed to happen here. */
+                       break;
+               }
+
+               pr_err("Programming error: Unknown comparison value: %d", cmp);
+               return false;
+       }
+
+       return false;
+}
diff --git a/src/sorted_array.h b/src/sorted_array.h
new file mode 100644 (file)
index 0000000..41f633d
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef SRC_SORTED_ARRAY_H_
+#define SRC_SORTED_ARRAY_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+
+/*
+ * This implementation is not a generic sorted array; It's intended to store RFC
+ * 3779 resources, which requires the elements to be sorted.
+ * So you can only add elements to the tail of the array. The implementation
+ * will validate this and prevent collisions too.
+ */
+
+struct sorted_array;
+
+enum sarray_comparison {
+       SACMP_EQUAL,
+       SACMP_CHILD,
+       SACMP_PARENT,
+       SACMP_LEFT,
+       SACMP_RIGHT,
+       SACMP_INTERSECTION,
+};
+
+typedef enum sarray_comparison (*sarray_cmp)(void *, void *);
+
+struct sorted_array *sarray_create(size_t, sarray_cmp);
+void sarray_get(struct sorted_array *);
+void sarray_put(struct sorted_array *);
+
+#define EEQUAL         7894
+#define ECHILD2                7895
+#define EPARENT                7896
+#define ELEFT          7897
+#define EINTERSECTION  7898
+
+int sarray_add(struct sorted_array *, void *);
+int sarray_join(struct sorted_array *, struct sorted_array *);
+
+bool sarray_contains(struct sorted_array *, void *);
+
+
+#define SARRAY_API(name, type, cmp)                                    \
+static struct sorted_array *                                           \
+name##_create(void)                                                    \
+{                                                                      \
+       return sarray_create(sizeof(struct type), cmp);                 \
+}                                                                      \
+static void                                                            \
+name##_get(struct sorted_array *sarray)                                        \
+{                                                                      \
+       sarray_get(sarray);                                             \
+}                                                                      \
+static void                                                            \
+name##_put(struct sorted_array *sarray)                                        \
+{                                                                      \
+       sarray_put(sarray);                                             \
+}                                                                      \
+static int                                                             \
+name##_add(struct sorted_array *sarray, struct type *element)          \
+{                                                                      \
+       return sarray_add(sarray, element);                             \
+}                                                                      \
+static int                                                             \
+name##_join(struct sorted_array *sarray, struct sorted_array *addend)  \
+{                                                                      \
+       return sarray_join(sarray, addend);                             \
+}                                                                      \
+static bool                                                            \
+name##_contains(struct sorted_array *sarray, struct type *element)     \
+{                                                                      \
+       return sarray_contains(sarray, element);                        \
+}
+
+
+#endif /* SRC_SORTED_ARRAY_H_ */
index 3485db566a43afac499a10bfb73522b0835ea9e7..02e7f0e04d3c0cfa388587727aacfc6bf1eb110e 100644 (file)
@@ -2,8 +2,8 @@
 
 #include <errno.h>
 #include <openssl/err.h>
-#include <openssl/x509.h>
 #include <openssl/x509v3.h>
+#include <sys/queue.h>
 #include "common.h"
 #include "log.h"
 #include "object/certificate.h"
@@ -32,6 +32,19 @@ struct validation {
 
        /** Certificates we've already validated. */
        STACK_OF(X509) *trusted;
+       /**
+        * The resources owned by the certificates from @trusted.
+        *
+        * (One for each certificate; these two stacks should practically always
+        * have the same size. The reason why I don't combine them is because
+        * libcrypto's validation function wants the stack of X509 and I'm not
+        * creating it over and over again.)
+        *
+        * (This is a SLIST and not a STACK_OF because the OpenSSL stack
+        * implementation is different than the LibreSSL one, and the latter is
+        * seemingly not intended to be used outside of its library.)
+        */
+       struct restack *rsrcs;
 };
 
 /*
@@ -76,9 +89,9 @@ init_trusted(struct validation *result, char *root)
        int ok;
        int error;
 
-       cert = certificate_load(result, root);
-       if (cert == NULL)
-               return -EINVAL;
+       error = certificate_load(result, root, &cert);
+       if (error)
+               return error;
 
        result->trusted = sk_X509_new_null();
        if (result->trusted == NULL) {
@@ -106,6 +119,7 @@ int
 validation_create(struct validation **out, char *root)
 {
        struct validation *result;
+       struct resources *resources;
        int error = -ENOMEM;
 
        result = malloc(sizeof(struct validation));
@@ -135,9 +149,29 @@ validation_create(struct validation **out, char *root)
        if (error)
                goto abort4;
 
+       result->rsrcs = restack_create();
+       if (!result->rsrcs)
+               goto abort5;
+
+       resources = resources_create();
+       if (resources == NULL)
+               goto abort6;
+
+       error = certificate_get_resources(result, validation_peek_cert(result),
+           resources);
+       if (error)
+               goto abort7;
+
+       restack_push(result->rsrcs, resources);
        *out = result;
        return 0;
 
+abort7:
+       resources_destroy(resources);
+abort6:
+       restack_destroy(result->rsrcs);
+abort5:
+       sk_X509_pop_free(result->trusted, X509_free);
 abort4:
        X509_STORE_free(result->store);
 abort3:
@@ -160,10 +194,11 @@ validation_destroy(struct validation *state)
         */
        cert_num = sk_X509_num(state->trusted);
        if (cert_num != 1) {
-               pr_err(state, "Error: validation state has %d certificates. (1 expected)",
+               pr_err("Error: validation state has %d certificates. (1 expected)",
                    cert_num);
        }
 
+       restack_destroy(state->rsrcs);
        sk_X509_pop_free(state->trusted, X509_free);
        X509_STORE_free(state->store);
        BIO_free_all(state->err);
@@ -171,85 +206,77 @@ validation_destroy(struct validation *state)
        free(state);
 }
 
-/**
- * "Swallows" @cert; do not delete it.
- */
-int
-validation_push(struct validation *state, X509 *cert)
+BIO *
+validation_stdout(struct validation *state)
 {
-       /*
-        * TODO
-        * The only difference between -CAfile and -trusted, as it seems, is
-        * that -CAfile consults the default file location, while -trusted does
-        * not. As far as I can tell, this means that we absolutely need to use
-        * -trusted.
-        * So, just in case, enable -no-CAfile and -no-CApath.
-        */
+       return state->out;
+}
 
-       X509_STORE_CTX *ctx;
-       int ok;
+BIO *
+validation_stderr(struct validation *state)
+{
+       return state->err;
+}
 
-       ctx = X509_STORE_CTX_new();
-       if (ctx == NULL) {
-               crypto_err(state, "X509_STORE_CTX_new() returned NULL");
-               goto end1;
-       }
+X509_STORE *
+validation_store(struct validation *state)
+{
+       return state->store;
+}
 
-       /* Returns 0 or 1 , all callers test ! only. */
-       ok = X509_STORE_CTX_init(ctx, state->store, cert, NULL);
-       if (!ok) {
-               crypto_err(state, "X509_STORE_CTX_init() returned %d", ok);
-               goto end2;
-       }
+STACK_OF(X509) *
+validation_certs(struct validation *state)
+{
+       return state->trusted;
+}
 
-       X509_STORE_CTX_trusted_stack(ctx, state->trusted);
+struct restack *
+validation_resources(struct validation *state)
+{
+       return state->rsrcs;
+}
 
-       /* Can return negative codes, all callers do <= 0. */
-       ok = X509_verify_cert(ctx);
-       if (ok <= 0) {
-               crypto_err(state, "Certificate validation failed: %d", ok);
-               goto end2;
-       }
+int
+validation_push_cert(struct validation *state, X509 *cert,
+    struct resources *resources)
+{
+       int ok;
 
-       /* Returns number of stack elements or 0 */
        ok = sk_X509_push(state->trusted, cert);
        if (ok <= 0) {
                crypto_err(state,
                    "Could not add certificate to trusted stack: %d", ok);
-               goto end2;
+               return -ENOMEM; /* Presumably */
        }
 
-       X509_STORE_CTX_free(ctx);
-       return 0;
+       restack_push(state->rsrcs, resources);
 
-end2:
-       X509_STORE_CTX_free(ctx);
-end1:
-       X509_free(cert);
-       return -EINVAL;
+       return 0;
 }
 
-void
-validation_pop(struct validation *state)
+int
+validation_pop_cert(struct validation *state)
 {
-       X509 *cert = sk_X509_pop(state->trusted);
-       X509_free(cert);
+       if (sk_X509_pop(state->trusted) == NULL) {
+               return crypto_err(state,
+                   "Programming error: Attempted to pop empty cert stack");
+       }
+       if (restack_pop(state->rsrcs) == NULL) {
+               pr_err("Programming error: Attempted to pop empty resource stack");
+               return -EINVAL;
+       }
+
+       return 0;
 }
 
 X509 *
-validation_peek(struct validation *state)
+validation_peek_cert(struct validation *state)
 {
        return sk_X509_value(state->trusted, sk_X509_num(state->trusted) - 1);
 }
 
-BIO *
-validation_stdout(struct validation *state)
-{
-       return state->out;
-}
-
-BIO *
-validation_stderr(struct validation *state)
+struct resources *
+validation_peek_resource(struct validation *state)
 {
-       return state->err;
+       return restack_peek(state->rsrcs);
 }
index 52218b5a272db1bddb91a4fc33c7ff1e1b4605ee..52a4c3d9a563aab6de821994564a53f63b798734 100644 (file)
@@ -2,17 +2,24 @@
 #define SRC_STATE_H_
 
 #include <openssl/bio.h>
+#include <openssl/x509.h>
+#include "resource.h"
 
 struct validation;
 
 int validation_create(struct validation **, char *);
 void validation_destroy(struct validation *);
 
-int validation_push(struct validation *, X509 *);
-void validation_pop(struct validation *);
-X509 *validation_peek(struct validation *);
-
 BIO *validation_stdout(struct validation *);
 BIO *validation_stderr(struct validation *);
+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 *, struct resources *);
+int validation_pop_cert(struct validation *);
+X509 *validation_peek_cert(struct validation *);
+
+struct resources *validation_peek_resource(struct validation *);
 
 #endif /* SRC_STATE_H_ */
index adbd3c723a364ef089d365f9f9665ec49b43a27f..5134cfbc604f2be440fec7c7d67aa5dc88951be8 100644 (file)
@@ -1,9 +1,12 @@
 AM_CFLAGS = -pedantic -Wall -std=gnu11 -I../src ${CHECK_CFLAGS}
 MY_LDADD = ${CHECK_LIBS}
 
-check_PROGRAMS = line_file.test tal.test
+check_PROGRAMS = address.test line_file.test tal.test
 TESTS = ${check_PROGRAMS}
 
+address_test_SOURCES = address_test.c ../src/address.c ../src/address.h
+address_test_LDADD = ${MY_LDADD}
+
 line_file_test_SOURCES = line_file_test.c ../src/line_file.c ../src/line_file.h
 line_file_test_LDADD = ${MY_LDADD}
 
diff --git a/test/address_test.c b/test/address_test.c
new file mode 100644 (file)
index 0000000..30e448d
--- /dev/null
@@ -0,0 +1,264 @@
+#include "address.h"
+
+#include <check.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static bool
+p4test(uint32_t a1, int l1, uint32_t a2, int l2)
+{
+       struct ipv4_prefix a, b;
+
+       a.addr.s_addr = htonl(a1);
+       a.len = l1;
+       b.addr.s_addr = htonl(a2);
+       b.len = l2;
+
+       return prefix4_contains(&a, &b);
+}
+
+START_TEST(test_prefix4_contains)
+{
+       unsigned int i;
+
+       /* Prefix-only tests */
+
+       ck_assert_int_eq(false, p4test(0x12345678u, 32, 0x12345677u, 32));
+       ck_assert_int_eq(true,  p4test(0x12345678u, 32, 0x12345678u, 32));
+       ck_assert_int_eq(false, p4test(0x12345678u, 32, 0x12345679u, 32));
+
+       ck_assert_int_eq(false, p4test(0x01020304u, 30, 0x01020303u, 32));
+       ck_assert_int_eq(true,  p4test(0x01020304u, 30, 0x01020304u, 32));
+       ck_assert_int_eq(true,  p4test(0x01020304u, 30, 0x01020305u, 32));
+       ck_assert_int_eq(true,  p4test(0x01020304u, 30, 0x01020306u, 32));
+       ck_assert_int_eq(true,  p4test(0x01020304u, 30, 0x01020307u, 32));
+       ck_assert_int_eq(false, p4test(0x01020304u, 30, 0x01020308u, 32));
+
+       ck_assert_int_eq(true,  p4test(0x00000000u,  0, 0x00000000u, 32));
+       ck_assert_int_eq(true,  p4test(0x00000000u,  0, 0x12345678u, 32));
+       ck_assert_int_eq(true,  p4test(0x00000000u,  0, 0xFFFFFFFFu, 32));
+
+       /* Length-only tests */
+
+       for (i = 0; i < 33; i++)
+               ck_assert_int_eq(true, p4test(0, i, 0, 32));
+       for (i = 0; i < 32; i++)
+               ck_assert_int_eq(false, p4test(0, 32, 0, i));
+       for (i = 0; i < 33; i++)
+               ck_assert_int_eq(true, p4test(0, 0, 0, i));
+       for (i = 1; i < 33; i++)
+               ck_assert_int_eq(false, p4test(0, i, 0, 0));
+}
+END_TEST
+
+static void
+p6init(struct ipv6_prefix *p, uint32_t q1, uint32_t q2, uint32_t q3,
+    uint32_t q4, int len)
+{
+       p->addr.s6_addr32[0] = htonl(q1);
+       p->addr.s6_addr32[1] = htonl(q2);
+       p->addr.s6_addr32[2] = htonl(q3);
+       p->addr.s6_addr32[3] = htonl(q4);
+       p->len = len;
+}
+
+START_TEST(test_prefix6_contains)
+{
+       struct ipv6_prefix a, b;
+
+       p6init(&a, 0, 0, 0, 0, 128);
+       p6init(&b, 0, 0, 0, 0, 128);
+
+       /* Length-only tests */
+
+       for (a.len = 0; a.len < 129; a.len++)
+               ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       a.len = 128;
+       for (b.len = 0; b.len < 128; b.len++)
+               ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       a.len = 0;
+       for (b.len = 0; b.len < 129; b.len++)
+               ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       b.len = 0;
+       for (a.len = 1; a.len < 129; a.len++)
+               ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       /* Full quadrants */
+
+       /* pl = 0 */
+       p6init(&a, 0, 0, 0, 0, 0);
+       p6init(&b, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 128);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+       /* Others were already tested above. */
+
+       /* pl = 32 */
+       p6init(&a, 0x13131313u, 0, 0, 0, 32);
+       p6init(&b, 0x13131313u, 0, 0, 0, 32);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x13131313u, 0xffffffffu, 0xffffffffu, 0xffffffffu, 128);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x13151313u, 0xffffffffu, 0xffffffffu, 0xffffffffu, 128);
+       ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       /* pl = 64 */
+       p6init(&a, 0x13131313u, 0x13131313u, 0, 0, 64);
+       p6init(&b, 0x13131313u, 0x13131313u, 0, 0, 64);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x13131313u, 0x13131313u, 0xffffffffu, 0xffffffffu, 128);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x13151313u, 0x13131313u, 0, 0, 128);
+       ck_assert_int_eq(false, prefix6_contains(&a, &b));
+       p6init(&b, 0x13131313u, 0x13151313u, 0xffffffffu, 0xffffffffu, 128);
+       ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       /* pl = 96 */
+       p6init(&a, 0x13131313u, 0x13131313u, 0x13131313u, 0, 96);
+       p6init(&b, 0x13131313u, 0x13131313u, 0x13131313u, 0, 96);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x13131313u, 0x13131313u, 0x13131313u, 0xffffffffu, 128);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x13151313u, 0x13131313u, 0x13131313u, 0, 128);
+       ck_assert_int_eq(false, prefix6_contains(&a, &b));
+       p6init(&b, 0x13131313u, 0x13151313u, 0x13131313u, 0x12345678u, 128);
+       ck_assert_int_eq(false, prefix6_contains(&a, &b));
+       p6init(&b, 0x13131313u, 0x13131313u, 0x13151313u, 0xffffffffu, 128);
+       ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       /* Try different prefixes in the same quadrant*/
+
+       p6init(&a, 0x20010000u, 0, 0, 0, 16);
+       p6init(&b, 0x20000000u, 0, 0, 0, 0);
+       for (b.len = 0; b.len < 129; b.len++)
+               ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       p6init(&a, 0, 0x20010000u, 0, 0, 48);
+       p6init(&b, 0, 0x20000000u, 0, 0, 0);
+       for (b.len = 0; b.len < 129; b.len++)
+               ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       p6init(&a, 0, 0, 0x20010000u, 0, 80);
+       p6init(&b, 0, 0, 0x20000000u, 0, 0);
+       for (b.len = 0; b.len < 129; b.len++)
+               ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       p6init(&a, 0, 0, 0, 0x20010000u, 112);
+       p6init(&b, 0, 0, 0, 0x20000000u, 0);
+       for (b.len = 0; b.len < 129; b.len++)
+               ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       /* Try different prefixes in different quadrants */
+
+       /* q2 */
+       p6init(&a, 1, 0x20010000u, 0, 0, 48);
+       p6init(&b, 0, 0x20010000u, 0, 0, 0);
+       for (b.len = 0; b.len < 129; b.len++)
+               ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       /* q3 */
+       p6init(&a, 1, 0, 0x20010000u, 0, 80);
+       p6init(&b, 0, 0, 0x20000000u, 0, 0);
+       for (b.len = 0; b.len < 129; b.len++)
+               ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       p6init(&a, 0, 1, 0x20010000u, 0, 80);
+       for (b.len = 0; b.len < 129; b.len++)
+               ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       /* q4 */
+       p6init(&a, 1, 0, 0, 0x20010000u, 112);
+       p6init(&b, 0, 0, 0, 0x20000000u, 0);
+       for (b.len = 0; b.len < 129; b.len++)
+               ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       p6init(&a, 0, 1, 0, 0x20010000u, 112);
+       for (b.len = 0; b.len < 129; b.len++)
+               ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       p6init(&a, 0, 0, 1, 0x20010000u, 112);
+       for (b.len = 0; b.len < 129; b.len++)
+               ck_assert_int_eq(false, prefix6_contains(&a, &b));
+
+       /* Try actually containing prefixes */
+
+       /* q1 */
+       p6init(&a, 0x20010000u, 0, 0, 0, 16);
+       p6init(&b, 0x20010000u, 0, 0, 0, 16);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x2001ffffu, 0, 0, 0, 32);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x2001ffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 128);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       /* q2 */
+       p6init(&a, 0x20010000u, 0x20010000u, 0, 0, 48);
+       p6init(&b, 0x20010000u, 0x20010000u, 0, 0, 48);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x20010000u, 0x2001ffffu, 0, 0, 64);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x20010000u, 0x2001ffffu, 0xffffffffu, 0xffffffffu, 128);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       /* q3 */
+       p6init(&a, 0x20010000u, 0x20010000u, 0x20010000u, 0, 80);
+       p6init(&b, 0x20010000u, 0x20010000u, 0x20010000u, 0, 80);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x20010000u, 0x20010000u, 0x2001ffffu, 0, 96);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x20010000u, 0x20010000u, 0x2001ffffu, 0xffffffff, 128);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       /* q4 */
+       p6init(&a, 0x20010000u, 0x20010000u, 0x20010000u, 0x20010000, 112);
+       p6init(&b, 0x20010000u, 0x20010000u, 0x20010000u, 0x20010000, 112);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+
+       p6init(&b, 0x20010000u, 0x20010000u, 0x20010000u, 0x2001ffff, 128);
+       ck_assert_int_eq(true, prefix6_contains(&a, &b));
+}
+END_TEST
+
+Suite *lfile_read_suite(void)
+{
+       Suite *suite;
+       TCase *core;
+
+       core = tcase_create("Core");
+       tcase_add_test(core, test_prefix6_contains);
+       tcase_add_test(core, test_prefix4_contains);
+
+       suite = suite_create("address");
+       suite_add_tcase(suite, core);
+       return suite;
+}
+
+int main(void)
+{
+       Suite *suite;
+       SRunner *runner;
+       int tests_failed;
+
+       suite = lfile_read_suite();
+
+       runner = srunner_create(suite);
+       srunner_run_all(runner, CK_NORMAL);
+       tests_failed = srunner_ntests_failed(runner);
+       srunner_free(runner);
+
+       return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
index 4bc0f35ac140f616d4861d72d00127b322007461..19da17821484bf6b5c75fb257aac5b8d40f8b26f 100644 (file)
@@ -1,4 +1,4 @@
-#include "tal.c"
+#include "object/tal.c"
 
 #include <check.h>
 #include <errno.h>
@@ -8,8 +8,11 @@ START_TEST(tal_load_normal)
 {
        struct tal *tal;
        struct uri *uri;
+       /*
        unsigned int i;
+       */
        /* Got this by feeding the subjectPublicKeyInfo to `base64 -d`. */
+       /*
        unsigned char decoded[] = {
            0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48,
            0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
@@ -39,6 +42,7 @@ START_TEST(tal_load_normal)
            0xD4, 0x32, 0xB7, 0x11, 0x38, 0x71, 0xCF, 0xF3, 0xA4, 0x0F, 0x64,
            0x83, 0x63, 0x0D, 0x02, 0x03, 0x01, 0x00, 0x01
        };
+       */
 
        ck_assert_int_eq(tal_load("tal/lacnic.tal", &tal), 0);
 
@@ -51,9 +55,11 @@ START_TEST(tal_load_normal)
        uri = SLIST_NEXT(uri, next);
        ck_assert(uri == NULL);
 
+       /*
        ck_assert_uint_eq(ARRAY_LEN(decoded), tal->spki_size);
        for (i = 0; i < ARRAY_LEN(decoded); i++)
                ck_assert_uint_eq(tal->spki[i], decoded[i]);
+       */
 
        tal_destroy(tal);
 }