From: Alberto Leiva Popper Date: Thu, 29 Nov 2018 21:52:13 +0000 (-0600) Subject: More certificate validation X-Git-Tag: v0.0.2~121 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=60c1716ab28d2eaa124bb6b87d43597f54ea4aac;p=thirdparty%2FFORT-validator.git More certificate validation 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. --- diff --git a/src/Makefile.am b/src/Makefile.am index 92a6c2fa..0dd1f32a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 index 00000000..1c26cbd6 --- /dev/null +++ b/src/address.c @@ -0,0 +1,51 @@ +#include "address.h" + +#include + +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 index 00000000..abf44d7f --- /dev/null +++ b/src/address.h @@ -0,0 +1,20 @@ +#ifndef SRC_ADDRESS_H_ +#define SRC_ADDRESS_H_ + +#include +#include + +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_ */ diff --git a/src/asn1/content_info.c b/src/asn1/content_info.c index d363d789..147d1653 100644 --- a/src/asn1/content_info.c +++ b/src/asn1/content_info.c @@ -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; diff --git a/src/asn1/content_info.h b/src/asn1/content_info.h index b8f91d9d..74c104ef 100644 --- a/src/asn1/content_info.h +++ b/src/asn1/content_info.h @@ -4,9 +4,8 @@ /* Some wrappers for libcmscodec's ContentInfo. */ #include -#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_ */ diff --git a/src/asn1/decode.c b/src/asn1/decode.c index 4abd49ba..9aa4cb39 100644 --- a/src/asn1/decode.c +++ b/src/asn1/decode.c @@ -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); } diff --git a/src/asn1/decode.h b/src/asn1/decode.h index 7c68ed5a..e20f878d 100644 --- a/src/asn1/decode.h +++ b/src/asn1/decode.h @@ -4,15 +4,12 @@ #include #include #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_ */ diff --git a/src/asn1/oid.c b/src/asn1/oid.c index 031ce113..6089ba93 100644 --- a/src/asn1/oid.c +++ b/src/asn1/oid.c @@ -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; diff --git a/src/asn1/oid.h b/src/asn1/oid.h index d31b3b4c..8dbc630b 100644 --- a/src/asn1/oid.h +++ b/src/asn1/oid.h @@ -4,7 +4,6 @@ #include #include #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. */ diff --git a/src/asn1/signed_data.c b/src/asn1/signed_data.c index 731084a0..fdc3f9b4 100644 --- a/src/asn1/signed_data.c +++ b/src/asn1/signed_data.c @@ -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); } diff --git a/src/asn1/signed_data.h b/src/asn1/signed_data.h index 6d21361e..f35d2c6b 100644 --- a/src/asn1/signed_data.h +++ b/src/asn1/signed_data.h @@ -4,12 +4,10 @@ /* Some wrappers for libcmscodec's SignedData. */ #include -#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_ */ diff --git a/src/common.c b/src/common.c index 9dbd2e62..48f857d3 100644 --- a/src/common.c +++ b/src/common.c @@ -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; } diff --git a/src/common.h b/src/common.h index 409eef95..80ac2615 100644 --- a/src/common.h +++ b/src/common.h @@ -3,7 +3,6 @@ #include #include -#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_ */ diff --git a/src/file.c b/src/file.c index 310b010b..e896c37f 100644 --- a/src/file.c +++ b/src/file.c @@ -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; diff --git a/src/file.h b/src/file.h index 3e412390..063c85db 100644 --- a/src/file.h +++ b/src/file.h @@ -2,7 +2,6 @@ #define SRC_FILE_H_ #include -#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_ */ diff --git a/src/log.c b/src/log.c index 7919d8d6..c491ee51 100644 --- 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; } diff --git a/src/log.h b/src/log.h index 66970546..be279275 100644 --- 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_ */ diff --git a/src/main.c b/src/main.c index 467b742b..7e8812b3 100644 --- a/src/main.c +++ b/src/main.c @@ -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); diff --git a/src/object/certificate.c b/src/object/certificate.c index 48be0316..6b8f0f3a 100644 --- a/src/object/certificate.c +++ b/src/object/certificate.c @@ -2,8 +2,9 @@ #include #include -#include #include +#include +#include #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; } diff --git a/src/object/certificate.h b/src/object/certificate.h index fc75c83c..f8bc3fbc 100644 --- a/src/object/certificate.h +++ b/src/object/certificate.h @@ -2,11 +2,22 @@ #define SRC_OBJECT_CERTIFICATE_H_ #include +#include +#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_ */ diff --git a/src/object/crl.c b/src/object/crl.c index a86c30c1..81d84de4 100644 --- a/src/object/crl.c +++ b/src/object/crl.c @@ -2,7 +2,6 @@ #include #include -#include #include #include "common.h" @@ -10,155 +9,43 @@ #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; } diff --git a/src/object/crl.h b/src/object/crl.h index b1a99bf3..ed80a37a 100644 --- a/src/object/crl.h +++ b/src/object/crl.h @@ -2,9 +2,9 @@ #define SRC_OBJECT_CRL_H_ #include +#include #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_ */ diff --git a/src/object/manifest.c b/src/object/manifest.c index 87b90748..f614f426 100644 --- a/src/object/manifest.c +++ b/src/object/manifest.c @@ -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; } diff --git a/src/object/manifest.h b/src/object/manifest.h index e957c4c2..c49f28af 100644 --- a/src/object/manifest.h +++ b/src/object/manifest.h @@ -4,7 +4,6 @@ #include #include "state.h" -bool is_manifest(char const *); int handle_manifest(struct validation *, char const *); #endif /* SRC_OBJECT_MANIFEST_H_ */ diff --git a/src/object/roa.c b/src/object/roa.c index 9c6e5b13..f918d401 100644 --- a/src/object/roa.c +++ b/src/object/roa.c @@ -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; } diff --git a/src/object/roa.h b/src/object/roa.h index a2efda47..40b5cd0f 100644 --- a/src/object/roa.h +++ b/src/object/roa.h @@ -2,9 +2,7 @@ #define SRC_OBJECT_ROA_H_ #include -#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_ */ diff --git a/src/object/signed_object.c b/src/object/signed_object.c index d162e767..90e930a6 100644 --- a/src/object/signed_object.c +++ b/src/object/signed_object.c @@ -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); diff --git a/src/object/signed_object.h b/src/object/signed_object.h index 7bc49088..9311d1f6 100644 --- a/src/object/signed_object.h +++ b/src/object/signed_object.h @@ -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 index 00000000..cf1dec1c --- /dev/null +++ b/src/resource.c @@ -0,0 +1,606 @@ +#include "resource.h" + +#include +#include +#include + +#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 index 00000000..5a9ae04b --- /dev/null +++ b/src/resource.h @@ -0,0 +1,30 @@ +#ifndef SRC_RESOURCE_H_ +#define SRC_RESOURCE_H_ + +#include +#include +#include +#include +#include + +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 index 00000000..199dccc5 --- /dev/null +++ b/src/sorted_array.c @@ -0,0 +1,209 @@ +#include "sorted_array.h" + +#include +#include +#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 index 00000000..41f633dc --- /dev/null +++ b/src/sorted_array.h @@ -0,0 +1,76 @@ +#ifndef SRC_SORTED_ARRAY_H_ +#define SRC_SORTED_ARRAY_H_ + +#include +#include + +/* + * 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_ */ diff --git a/src/state.c b/src/state.c index 3485db56..02e7f0e0 100644 --- a/src/state.c +++ b/src/state.c @@ -2,8 +2,8 @@ #include #include -#include #include +#include #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); } diff --git a/src/state.h b/src/state.h index 52218b5a..52a4c3d9 100644 --- a/src/state.h +++ b/src/state.h @@ -2,17 +2,24 @@ #define SRC_STATE_H_ #include +#include +#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_ */ diff --git a/test/Makefile.am b/test/Makefile.am index adbd3c72..5134cfbc 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -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 index 00000000..30e448d0 --- /dev/null +++ b/test/address_test.c @@ -0,0 +1,264 @@ +#include "address.h" + +#include +#include +#include +#include + +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; +} diff --git a/test/tal_test.c b/test/tal_test.c index 4bc0f35a..19da1782 100644 --- a/test/tal_test.c +++ b/test/tal_test.c @@ -1,4 +1,4 @@ -#include "tal.c" +#include "object/tal.c" #include #include @@ -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); }