From: Alberto Leiva Popper Date: Tue, 12 Feb 2019 17:56:16 +0000 (-0600) Subject: Implement RFC 8360 X-Git-Tag: v0.0.2~92 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cdb94bdc577b348631818b1b44d02a73a4ec8ef3;p=thirdparty%2FFORT-validator.git Implement RFC 8360 Not really tested, because it doesn't look like there are people using this feature yet. Also: - refactor inet_ntop() usage so we don't have to clutter the stack with string buffers every time we want to print. - Patch sometimes undefined behavior `0xFFFFFFFFu >> 32`. --- diff --git a/src/Makefile.am b/src/Makefile.am index 4ea82830..5379704e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,6 +17,7 @@ rpki_validator_SOURCES += extension.h extension.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 += nid.h nid.c rpki_validator_SOURCES += random.h random.c rpki_validator_SOURCES += resource.h resource.c rpki_validator_SOURCES += rpp.h rpp.c diff --git a/src/address.c b/src/address.c index 0bcbe1ca..63da1971 100644 --- a/src/address.c +++ b/src/address.c @@ -4,56 +4,65 @@ #include #include /* inet_ntop */ #include "log.h" +#include "thread_var.h" -static char const * -addr2str4(struct in_addr *addr, char *buffer) -{ - return inet_ntop(AF_INET, addr, buffer, INET_ADDRSTRLEN); -} - -static char const * -addr2str6(struct in6_addr *addr, char *buffer) +/* + * Returns a mask you can use to extract the suffix bits of a 32-bit unsigned + * number whose prefix lengths @prefix_len. + * For example: Suppose that your number is 192.0.2.0/24. + * u32_suffix_mask(24) returns 0.0.0.255. + * + * The result is in host byte order. + */ +uint32_t +u32_suffix_mask(unsigned int prefix_len) { - return inet_ntop(AF_INET6, addr, buffer, INET6_ADDRSTRLEN); + /* `a >> 32` is undefined if `a` is 32 bits. */ + return (prefix_len < 32) ? (0xFFFFFFFFu >> prefix_len) : 0; } /** - * Returns a mask you can use to extract the suffix bits of an address whose - * prefix lengths @prefix_len. - * For example: Suppose that your address is 192.0.2.0/24. - * addr4_suffix_mask(24) returns 0.0.0.255. + * Same as u32_suffix_mask(), except the result is in network byte order + * ("be", for "big endian"). */ uint32_t -ipv4_suffix_mask(unsigned int prefix_len) +be32_suffix_mask(unsigned int prefix_len) { - return htonl(0xFFFFFFFFu >> prefix_len); + return htonl(u32_suffix_mask(prefix_len)); } +/** + * Enables all the suffix bits of @result (assuming its prefix length is + * @prefix_len). + * @result's prefix bits will not be modified. + */ void ipv6_suffix_mask(unsigned int prefix_len, struct in6_addr *result) { if (prefix_len < 32) { - result->s6_addr32[0] |= htonl(0xFFFFFFFFu >> prefix_len); + result->s6_addr32[0] |= be32_suffix_mask(prefix_len); result->s6_addr32[1] = 0xFFFFFFFFu; result->s6_addr32[2] = 0xFFFFFFFFu; result->s6_addr32[3] = 0xFFFFFFFFu; } else if (prefix_len < 64) { - result->s6_addr32[1] |= htonl(0xFFFFFFFFu >> (prefix_len - 32)); + result->s6_addr32[1] |= be32_suffix_mask(prefix_len - 32); result->s6_addr32[2] = 0xFFFFFFFFu; result->s6_addr32[3] = 0xFFFFFFFFu; } else if (prefix_len < 96) { - result->s6_addr32[2] |= htonl(0xFFFFFFFFu >> (prefix_len - 64)); + result->s6_addr32[2] |= be32_suffix_mask(prefix_len - 64); result->s6_addr32[3] = 0xFFFFFFFFu; } else { - result->s6_addr32[3] |= htonl(0xFFFFFFFFu >> (prefix_len - 96)); + result->s6_addr32[3] |= be32_suffix_mask(prefix_len - 96); } } +/** + * Translates an `IPAddress2_t` to its equivalent `struct ipv4_prefix`. + */ int prefix4_decode(IPAddress2_t *str, struct ipv4_prefix *result) { int len; - char buffer[INET_ADDRSTRLEN]; if (str->size > 4) { return pr_err("IPv4 address has too many octets. (%u)", @@ -75,20 +84,22 @@ prefix4_decode(IPAddress2_t *str, struct ipv4_prefix *result) result->len = len; - if ((result->addr.s_addr & ipv4_suffix_mask(result->len)) != 0) { - return pr_err("IPv4 prefix %s/%u has enabled suffix bits.", - addr2str4(&result->addr, buffer), result->len); + if ((result->addr.s_addr & be32_suffix_mask(result->len)) != 0) { + return pr_err("IPv4 prefix '%s/%u' has enabled suffix bits.", + v4addr2str(&result->addr), result->len); } return 0; } +/** + * Translates an `IPAddress2_t` to its equivalent `struct ipv6_prefix`. + */ int prefix6_decode(IPAddress2_t *str, struct ipv6_prefix *result) { struct in6_addr suffix; int len; - char buffer[INET6_ADDRSTRLEN]; if (str->size > 16) { return pr_err("IPv6 address has too many octets. (%u)", @@ -116,8 +127,8 @@ prefix6_decode(IPAddress2_t *str, struct ipv6_prefix *result) || (result->addr.s6_addr32[1] & suffix.s6_addr32[1]) || (result->addr.s6_addr32[2] & suffix.s6_addr32[2]) || (result->addr.s6_addr32[3] & suffix.s6_addr32[3])) { - return pr_err("IPv6 prefix %s/%u has enabled suffix bits.", - addr2str6(&result->addr, buffer), result->len); + return pr_err("IPv6 prefix '%s/%u' has enabled suffix bits.", + v6addr2str(&result->addr), result->len); } return 0; @@ -126,13 +137,9 @@ prefix6_decode(IPAddress2_t *str, struct ipv6_prefix *result) static int check_order4(struct ipv4_range *result) { - char buffer_min[INET_ADDRSTRLEN]; - char buffer_max[INET_ADDRSTRLEN]; - if (ntohl(result->min.s_addr) > ntohl(result->max.s_addr)) { - return pr_err("The IPv4 range %s-%s is inverted.", - addr2str4(&result->min, buffer_min), - addr2str4(&result->max, buffer_max)); + return pr_err("The IPv4 range '%s-%s' is inverted.", + v4addr2str(&result->min), v4addr2str2(&result->max)); } return 0; @@ -149,8 +156,6 @@ check_encoding4(struct ipv4_range *range) const uint32_t MIN = ntohl(range->min.s_addr); const uint32_t MAX = ntohl(range->max.s_addr); uint32_t mask; - char buffer_min[INET_ADDRSTRLEN]; - char buffer_max[INET_ADDRSTRLEN]; for (mask = 0x80000000u; mask != 0; mask >>= 1) if ((MIN & mask) != (MAX & mask)) @@ -160,11 +165,13 @@ check_encoding4(struct ipv4_range *range) if (((MIN & mask) != 0) || ((MAX & mask) == 0)) return 0; - return pr_err("IPAddressRange %s-%s is a range, but should have been encoded as a prefix.", - addr2str4(&range->min, buffer_min), - addr2str4(&range->min, buffer_max)); + return pr_err("IPAddressRange '%s-%s' is a range, but should have been encoded as a prefix.", + v4addr2str(&range->min), v4addr2str2(&range->max)); } +/** + * Translates an `IPAddressRange_t` to its equivalent `struct ipv4_range`. + */ int range4_decode(IPAddressRange_t *input, struct ipv4_range *result) { @@ -179,7 +186,7 @@ range4_decode(IPAddressRange_t *input, struct ipv4_range *result) error = prefix4_decode(&input->max, &prefix); if (error) return error; - result->max.s_addr = prefix.addr.s_addr | ipv4_suffix_mask(prefix.len); + result->max.s_addr = prefix.addr.s_addr | be32_suffix_mask(prefix.len); error = check_order4(result); if (error) @@ -194,16 +201,14 @@ check_order6(struct ipv6_range *result) uint32_t min; uint32_t max; unsigned int quadrant; - char buffer_min[INET6_ADDRSTRLEN]; - char buffer_max[INET6_ADDRSTRLEN]; for (quadrant = 0; quadrant < 4; quadrant++) { min = ntohl(result->min.s6_addr32[quadrant]); max = ntohl(result->max.s6_addr32[quadrant]); if (min > max) { - return pr_err("The IPv6 range %s-%s is inverted.", - addr2str6(&result->min, buffer_min), - addr2str6(&result->max, buffer_max)); + return pr_err("The IPv6 range '%s-%s' is inverted.", + v6addr2str(&result->min), + v6addr2str2(&result->max)); } else if (min < max) { return 0; /* result->min < result->max */ } @@ -215,15 +220,13 @@ check_order6(struct ipv6_range *result) static int pr_bad_encoding(struct ipv6_range *range) { - char buffer_min[INET6_ADDRSTRLEN]; - char buffer_max[INET6_ADDRSTRLEN]; return pr_err("IPAddressRange %s-%s is a range, but should have been encoded as a prefix.", - addr2str6(&range->min, buffer_min), - addr2str6(&range->max, buffer_max)); + v6addr2str(&range->min), v6addr2str2(&range->max)); } static int -thingy(struct ipv6_range *range, unsigned int quadrant, uint32_t mask) +__check_encoding6(struct ipv6_range *range, unsigned int quadrant, + uint32_t mask) { uint32_t min; uint32_t max; @@ -253,12 +256,15 @@ check_encoding6(struct ipv6_range *range) max = ntohl(range->max.s6_addr32[quadrant]); for (mask = 0x80000000u; mask != 0; mask >>= 1) if ((min & mask) != (max & mask)) - return thingy(range, quadrant, mask); + return __check_encoding6(range, quadrant, mask); } return pr_bad_encoding(range); } +/** + * Translates an `IPAddressRange_t` to its equivalent `struct ipv6_range`. + */ int range6_decode(IPAddressRange_t *input, struct ipv6_range *result) { diff --git a/src/address.h b/src/address.h index cb5803a1..5d8e24eb 100644 --- a/src/address.h +++ b/src/address.h @@ -26,7 +26,8 @@ struct ipv6_range { struct in6_addr max; }; -uint32_t ipv4_suffix_mask(unsigned int); +uint32_t u32_suffix_mask(unsigned int); +uint32_t be32_suffix_mask(unsigned int); void ipv6_suffix_mask(unsigned int, struct in6_addr *); int prefix4_decode(IPAddress2_t *, struct ipv4_prefix *); diff --git a/src/asn1/signed_data.c b/src/asn1/signed_data.c index d00c68f9..5375d0ba 100644 --- a/src/asn1/signed_data.c +++ b/src/asn1/signed_data.c @@ -58,6 +58,7 @@ handle_sdata_certificate(ANY_t *any, struct signed_object_args *args, struct validation *state; const unsigned char *tmp; X509 *cert; + enum rpki_policy policy; int error; state = state_retrieve(); @@ -89,15 +90,19 @@ handle_sdata_certificate(ANY_t *any, struct signed_object_args *args, error = certificate_validate_rfc6487(cert, false); if (error) goto end2; + error = certificate_validate_extensions_ee(cert, sid, &args->refs, + &policy); + if (error) + goto end2; if (args->res != NULL) { + /* TODO validate resources even if the SO lacks them */ + resources_set_policy(args->res, policy); error = certificate_get_resources(cert, args->res); if (error) goto end2; } - error = certificate_validate_extensions_ee(cert, sid, &args->refs); - end2: X509_free(cert); end1: diff --git a/src/certificate_refs.c b/src/certificate_refs.c index 45c91cb4..13db4d57 100644 --- a/src/certificate_refs.c +++ b/src/certificate_refs.c @@ -24,11 +24,11 @@ validate_cdp(struct certificate_refs *refs, struct rpp const *pp) struct rpki_uri const *pp_crl; if (refs->crldp == NULL) - return pr_err("Programming error: Certificate's CRL Distribution Point was not recorded."); + return pr_crit("Certificate's CRL Distribution Point was not recorded."); pp_crl = rpp_get_crl(pp); if (pp_crl == NULL) - return pr_err("Programming error: Manifest's CRL was not recorded."); + return pr_crit("Manifest's CRL was not recorded."); if (strcmp(refs->crldp, pp_crl->global) != 0) { return pr_err("Certificate's CRL Distribution Point ('%s') does not match manifest's CRL ('%s').", @@ -45,14 +45,14 @@ validate_aia(struct certificate_refs *refs) struct rpki_uri const *parent; if (refs->caIssuers == NULL) - return pr_err("Programming error: Certificate's AIA was not recorded."); + return pr_crit("Certificate's AIA was not recorded."); state = state_retrieve(); if (state == NULL) return -EINVAL; parent = validation_peek_cert_uri(state); if (parent == NULL) - return pr_err("Programming error: CA certificate has no parent."); + return pr_crit("CA certificate has no parent."); if (strcmp(refs->caIssuers, parent->global) != 0) { return pr_err("Certificate's AIA ('%s') does not match parent's URI ('%s').", @@ -67,7 +67,7 @@ validate_signedObject(struct certificate_refs *refs, struct rpki_uri const *signedObject_uri) { if (refs->signedObject == NULL) - return pr_err("Programming error: Certificate's signedObject was not recorded."); + return pr_crit("Certificate's signedObject was not recorded."); if (strcmp(refs->signedObject, signedObject_uri->global) != 0) { return pr_err("Certificate's signedObject ('%s') does not match the URI of its own signed object (%s).", @@ -95,7 +95,7 @@ refs_validate_ca(struct certificate_refs *refs, bool is_ta, return error; if (refs->signedObject != NULL) { - return pr_err("Programming error: CA summary has a signedObject ('%s').", + return pr_crit("CA summary has a signedObject ('%s').", refs->signedObject); } diff --git a/src/common.c b/src/common.c index 6ffb3cf8..a35c6743 100644 --- a/src/common.c +++ b/src/common.c @@ -5,12 +5,6 @@ #include "log.h" #include "thread_var.h" -int NID_rpkiManifest; -int NID_signedObject; -int NID_rpkiNotify; -int NID_certPolicyRpki; -int NID_certPolicyRpkiV2; - /** * Does not assume that @string is NULL-terminated. */ diff --git a/src/common.h b/src/common.h index 117f6eef..e109d437 100644 --- a/src/common.h +++ b/src/common.h @@ -16,12 +16,6 @@ */ #define ENOTRSYNC 3174 -extern int NID_rpkiManifest; -extern int NID_signedObject; -extern int NID_rpkiNotify; -extern int NID_certPolicyRpki; -extern int NID_certPolicyRpkiV2; - #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0])) int string_clone(void const *, size_t, char **); diff --git a/src/extension.c b/src/extension.c index 25535baa..18ea9454 100644 --- a/src/extension.c +++ b/src/extension.c @@ -1,65 +1,150 @@ #include "extension.h" #include +#include "common.h" #include "log.h" +#include "nid.h" #include "thread_var.h" #include "crypto/hash.h" -const struct extension_metadata BC = { - "Basic Constraints", - NID_basic_constraints, +static struct extension_metadata IR2 = { + "Amended IP Resources", + /* TODO NID_ipAddrBlocksv2 */ -1, true, }; -const struct extension_metadata SKI = { - "Subject Key Identifier", - NID_subject_key_identifier, - false, -}; -const struct extension_metadata AKI = { - "Authority Key Identifier", - NID_authority_key_identifier, - false, -}; -const struct extension_metadata KU = { - "Key Usage", - NID_key_usage, - true, -}; -const struct extension_metadata CDP = { - "CRL Distribution Points", - NID_crl_distribution_points, - false, -}; -const struct extension_metadata AIA = { - "Authority Information Access", - NID_info_access, - false, -}; -const struct extension_metadata SIA = { - "Subject Information Access", - NID_sinfo_access , - false, -}; -const struct extension_metadata CP = { - "Certificate Policies", - NID_certificate_policies, - true, -}; -const struct extension_metadata IR = { - "IP Resources", - NID_sbgp_ipAddrBlock, - true, -}; -const struct extension_metadata AR = { - "AS Resources", - NID_sbgp_autonomousSysNum, + +static struct extension_metadata AR2 = { + "Amended AS Resources", + /* TODO NID_autonomousSysIdsv2 */ -1, true, }; -const struct extension_metadata CN = { - "CRL Number", - NID_crl_number, - false, -}; + +int extension_init(void) +{ + IR2.nid = nid_ipAddrBlocksv2(); + AR2.nid = nid_autonomousSysIdsv2(); + return 0; +} + +struct extension_metadata const *ext_bc(void) +{ + static const struct extension_metadata BC = { + "Basic Constraints", + NID_basic_constraints, + true, + }; + return &BC; +} + +struct extension_metadata const *ext_ski(void) +{ + static const struct extension_metadata SKI = { + "Subject Key Identifier", + NID_subject_key_identifier, + false, + }; + return &SKI; +} + +struct extension_metadata const *ext_aki(void) +{ + static const struct extension_metadata AKI = { + "Authority Key Identifier", + NID_authority_key_identifier, + false, + }; + return &AKI; +} + +struct extension_metadata const *ext_ku(void) +{ + static const struct extension_metadata KU = { + "Key Usage", + NID_key_usage, + true, + }; + return &KU; +} + +struct extension_metadata const *ext_cdp(void) +{ + static const struct extension_metadata CDP = { + "CRL Distribution Points", + NID_crl_distribution_points, + false, + }; + return &CDP; +} + +struct extension_metadata const *ext_aia(void) +{ + static const struct extension_metadata AIA = { + "Authority Information Access", + NID_info_access, + false, + }; + return &AIA; +} + +struct extension_metadata const *ext_sia(void) +{ + static const struct extension_metadata SIA = { + "Subject Information Access", + NID_sinfo_access , + false, + }; + return &SIA; +} + +struct extension_metadata const *ext_cp(void) +{ + static const struct extension_metadata CP = { + "Certificate Policies", + NID_certificate_policies, + true, + }; + return &CP; +} + +struct extension_metadata const *ext_ir(void) +{ + static const struct extension_metadata IR = { + "IP Resources", + NID_sbgp_ipAddrBlock, + true, + }; + return &IR; +} + +struct extension_metadata const *ext_ar(void) +{ + static const struct extension_metadata AR = { + "AS Resources", + NID_sbgp_autonomousSysNum, + true, + }; + return &AR; +} + +struct extension_metadata const *ext_ir2(void) +{ + return &IR2; +} + +struct extension_metadata const *ext_ar2(void) +{ + return &AR2; +} + +struct extension_metadata const *ext_cn(void) +{ + static const struct extension_metadata CN = { + "CRL Number", + NID_crl_number, + false, + }; + return &CN; +} static int handle_extension(struct extension_handler *handlers, X509_EXTENSION *ext) @@ -111,8 +196,6 @@ handle_extensions(struct extension_handler *handlers, int e; int error; - /* TODO check that no other extensions are present? */ - for (e = 0; e < sk_X509_EXTENSION_num(extensions); e++) { error = handle_extension(handlers, sk_X509_EXTENSION_value(extensions, e)); @@ -192,7 +275,7 @@ validate_public_key_hash(X509 *cert, ASN1_OCTET_STRING *hash) /* Hash the SPK, compare SPK hash with the SKI */ if (hash->length < 0 || SIZE_MAX < hash->length) { return pr_err("%s length (%d) is out of bounds. (0-%zu)", - SKI.name, hash->length, SIZE_MAX); + ext_ski()->name, hash->length, SIZE_MAX); } if (spk_len < 0 || SIZE_MAX < spk_len) { return pr_err("Subject Public Key length (%d) is out of bounds. (0-%zu)", @@ -202,7 +285,7 @@ validate_public_key_hash(X509 *cert, ASN1_OCTET_STRING *hash) error = hash_validate("sha1", hash->data, hash->length, spk, spk_len); if (error) { pr_err("The Subject Public Key's hash does not match the %s.", - SKI.name); + ext_ski()->name); } return error; @@ -218,16 +301,16 @@ handle_aki(X509_EXTENSION *ext, void *arg) aki = X509V3_EXT_d2i(ext); if (aki == NULL) - return cannot_decode(&AKI); + return cannot_decode(ext_aki()); if (aki->issuer != NULL) { error = pr_err("%s extension contains an authorityCertIssuer.", - AKI.name); + ext_aki()->name); goto end; } if (aki->serial != NULL) { error = pr_err("%s extension contains an authorityCertSerialNumber.", - AKI.name); + ext_aki()->name); goto end; } diff --git a/src/extension.h b/src/extension.h index e8cd9cde..707c1991 100644 --- a/src/extension.h +++ b/src/extension.h @@ -22,17 +22,21 @@ struct extension_handler { bool found; }; -extern const struct extension_metadata BC; -extern const struct extension_metadata SKI; -extern const struct extension_metadata AKI; -extern const struct extension_metadata KU; -extern const struct extension_metadata CDP; -extern const struct extension_metadata AIA; -extern const struct extension_metadata SIA; -extern const struct extension_metadata CP; -extern const struct extension_metadata IR; -extern const struct extension_metadata AR; -extern const struct extension_metadata CN; +int extension_init(void); + +struct extension_metadata const *ext_bc(void); +struct extension_metadata const *ext_ski(void); +struct extension_metadata const *ext_aki(void); +struct extension_metadata const *ext_ku(void); +struct extension_metadata const *ext_cdp(void); +struct extension_metadata const *ext_aia(void); +struct extension_metadata const *ext_sia(void); +struct extension_metadata const *ext_cp(void); +struct extension_metadata const *ext_ir(void); +struct extension_metadata const *ext_ar(void); +struct extension_metadata const *ext_ir2(void); +struct extension_metadata const *ext_ar2(void); +struct extension_metadata const *ext_cn(void); int handle_extensions(struct extension_handler *, STACK_OF(X509_EXTENSION) const *); diff --git a/src/log.c b/src/log.c index a1343da2..cfc06c42 100644 --- a/src/log.c +++ b/src/log.c @@ -45,21 +45,16 @@ pr_rm_indent(void) } -#endif - void pr_debug_prefix(void) { -#ifdef DEBUG fprintf(STDOUT, "DBG: "); pr_indent(STDOUT); -#endif } void pr_debug(const char *format, ...) { -#ifdef DEBUG va_list args; pr_debug_prefix(); @@ -68,13 +63,11 @@ pr_debug(const char *format, ...) vfprintf(STDOUT, format, args); va_end(args); fprintf(STDOUT, "\n"); -#endif } void pr_debug_add(const char *format, ...) { -#ifdef DEBUG va_list args; pr_debug_prefix(); @@ -85,13 +78,11 @@ pr_debug_add(const char *format, ...) fprintf(STDOUT, "\n"); pr_add_indent(); -#endif } void pr_debug_rm(const char *format, ...) { -#ifdef DEBUG va_list args; pr_rm_indent(); @@ -102,18 +93,19 @@ pr_debug_rm(const char *format, ...) vfprintf(STDOUT, format, args); va_end(args); fprintf(STDOUT, "\n"); -#endif } +#endif + static void -pr_err_prefix(void) +pr_prefix(char const *level) { - fprintf(STDERR, "ERR: "); + fprintf(STDERR, "%s: ", level); pr_indent(STDERR); } -#define PR_ERR(args) do { \ - pr_err_prefix(); \ +#define PR_PREFIX(level, args) do { \ + pr_prefix(level); \ pr_file_name(STDERR); \ \ va_start(args, format); \ @@ -121,6 +113,19 @@ pr_err_prefix(void) va_end(args); \ } while (0) +/** + * Always appends a newline at the end. Always returs 0. (So you can interrupt + * whatever you're doing without failing validation.) + */ +int +pr_warn(const char *format, ...) +{ + va_list args; + PR_PREFIX("WRN", args); + fprintf(STDERR, "\n"); + return 0; +} + /** * Always appends a newline at the end. Always returs -EINVAL. */ @@ -128,7 +133,7 @@ int pr_err(const char *format, ...) { va_list args; - PR_ERR(args); + PR_PREFIX("ERR", args); fprintf(STDERR, "\n"); return -EINVAL; } @@ -152,7 +157,7 @@ pr_errno(int error, const char *format, ...) { va_list args; - PR_ERR(args); + PR_PREFIX("ERR", args); if (error) { fprintf(STDERR, ": %s", strerror(error)); @@ -188,7 +193,7 @@ crypto_err(const char *format, ...) va_list args; int error; - PR_ERR(args); + PR_PREFIX("ERR", args); fprintf(STDERR, ": "); error = ERR_GET_REASON(ERR_peek_last_error()); @@ -223,7 +228,7 @@ pr_crit(const char *format, ...) { va_list args; - pr_err_prefix(); + pr_prefix("CRT"); pr_file_name(STDERR); fprintf(STDERR, "Programming error: "); diff --git a/src/log.h b/src/log.h index 365ec403..fa002c17 100644 --- a/src/log.h +++ b/src/log.h @@ -10,11 +10,23 @@ * to ease debugging. */ +#ifdef DEBUG + void pr_debug(const char *, ...); void pr_debug_add(const char *, ...); void pr_debug_rm(const char *, ...); void pr_debug_prefix(void); +#else + +#define pr_debug(...) +#define pr_debug_add(...) +#define pr_debug_rm(...) +#define pr_debug_prefix + +#endif + +int pr_warn(const char *, ...); int pr_err(const char *, ...); int pr_errno(int, const char *, ...); int crypto_err(const char *, ...); diff --git a/src/main.c b/src/main.c index 0ec4ba5a..c729b43e 100644 --- a/src/main.c +++ b/src/main.c @@ -1,12 +1,13 @@ #include #include #include -#include #include "common.h" #include "config.h" #include "debug.h" +#include "extension.h" #include "log.h" +#include "nid.h" #include "rpp.h" #include "thread_var.h" #include "object/certificate.h" @@ -14,41 +15,6 @@ #include "object/tal.h" #include "rsync/rsync.h" -/** - * Registers the RPKI-specific OIDs in the SSL library. - * LibreSSL needs it; not sure about OpenSSL. - */ -static void -add_rpki_oids(void) -{ - NID_rpkiManifest = OBJ_create("1.3.6.1.5.5.7.48.10", - "rpkiManifest", - "RPKI Manifest (RFC 6487)"); - printf("rpkiManifest registered. Its nid is %d.\n", NID_rpkiManifest); - - NID_signedObject = OBJ_create("1.3.6.1.5.5.7.48.11", - "signedObject", - "RPKI Signed Object (RFC 6487)"); - printf("signedObject registered. Its nid is %d.\n", NID_signedObject); - - NID_rpkiNotify = OBJ_create("1.3.6.1.5.5.7.48.13", - "rpkiNotify", - "RPKI Update Notification File (RFC 8182)"); - printf("rpkiNotify registered. Its nid is %d.\n", NID_rpkiNotify); - - NID_certPolicyRpki = OBJ_create("1.3.6.1.5.5.7.14.2", - "id-cp-ipAddr-asNumber (RFC 6484)", - "Certificate Policy (CP) for the Resource PKI (RPKI)"); - printf("certPolicyRpki registered. Its nid is %d.\n", NID_certPolicyRpki); - - /* TODO implement RFC 8360 */ - NID_certPolicyRpkiV2 = OBJ_create("1.3.6.1.5.5.7.14.3", - "id-cp-ipAddr-asNumber-v2 (RFC 8360)", - "Certificate Policy for Use with Validation Reconsidered in the RPKI"); - printf("certPolicyRpkiV2 registered. Its nid is %d.\n", - NID_certPolicyRpkiV2); -} - /** * Performs the whole validation walkthrough on uri @uri, which is assumed to * have been extracted from a TAL. @@ -154,7 +120,7 @@ handle_args(int argc, char **argv) config.shuffle_uris = false; config.local_repository = NULL; config.tal = NULL; - config.maximum_certificate_depth = 32; + config.maximum_certificate_depth = 64; while ((opt = getopt_long(argc, argv, "t:l:rsm:", long_options, NULL)) != -1) { @@ -217,7 +183,12 @@ main(int argc, char **argv) if (error) return error; - add_rpki_oids(); + error = nid_init(); + if (error) + goto end; + error = extension_init(); + if (error) + goto end; thvar_init(); fnstack_store(); fnstack_push(config_get_tal()); @@ -232,6 +203,7 @@ main(int argc, char **argv) tal_destroy(tal); } +end: rsync_destroy(); return error; } diff --git a/src/nid.c b/src/nid.c new file mode 100644 index 00000000..5decf801 --- /dev/null +++ b/src/nid.c @@ -0,0 +1,114 @@ +#include "nid.h" + +#include +#include + +#include "log.h" + +static int NID_rpkiManifest; +static int NID_signedObject; +static int NID_rpkiNotify; +static int NID_certPolicyRpki; +static int NID_certPolicyRpkiV2; +static int NID_ipAddrBlocksv2; +static int NID_autonomousSysIdsv2; + +static int +register_oid(const char *oid, const char *sn, const char *ln) +{ + int nid; + + nid = OBJ_create(oid, sn, ln); + if (nid == 0) + return crypto_err("Unable to register the %s NID.", sn); + + printf("%s registered. Its nid is %d.\n", sn, nid); + return nid; +} + +/** + * Registers the RPKI-specific OIDs in the SSL library. + * LibreSSL needs it; not sure about OpenSSL. + */ +int +nid_init(void) +{ + NID_rpkiManifest = register_oid("1.3.6.1.5.5.7.48.10", + "rpkiManifest", + "RPKI Manifest (RFC 6487)"); + if (NID_rpkiManifest == 0) + return -EINVAL; + + NID_signedObject = register_oid("1.3.6.1.5.5.7.48.11", + "signedObject", + "RPKI Signed Object (RFC 6487)"); + if (NID_signedObject == 0) + return -EINVAL; + + NID_rpkiNotify = register_oid("1.3.6.1.5.5.7.48.13", + "rpkiNotify", + "RPKI Update Notification File (RFC 8182)"); + if (NID_rpkiNotify == 0) + return -EINVAL; + + NID_certPolicyRpki = register_oid("1.3.6.1.5.5.7.14.2", + "id-cp-ipAddr-asNumber (RFC 6484)", + "Certificate Policy (CP) for the Resource PKI (RPKI)"); + if (NID_certPolicyRpki == 0) + return -EINVAL; + + NID_certPolicyRpkiV2 = register_oid("1.3.6.1.5.5.7.14.3", + "id-cp-ipAddr-asNumber-v2 (RFC 8360)", + "Certificate Policy for Use with Validation Reconsidered in the RPKI"); + if (NID_certPolicyRpkiV2 == 0) + return -EINVAL; + + NID_ipAddrBlocksv2 = register_oid("1.3.6.1.5.5.7.1.28", + "id-pe-ipAddrBlocks-v2", + "Amended IP Resources (RFC 8360)"); + if (NID_ipAddrBlocksv2 == 0) + return -EINVAL; + + NID_autonomousSysIdsv2 = register_oid("1.3.6.1.5.5.7.1.29", + "id-pe-autonomousSysIds-v2", + "Amended AS Resources (RFC 8360)"); + if (NID_autonomousSysIdsv2 == 0) + return -EINVAL; + + return 0; +} + +int nid_rpkiManifest(void) +{ + return NID_rpkiManifest; +} + +int nid_signedObject(void) +{ + return NID_signedObject; +} + +int nid_rpkiNotify(void) +{ + return NID_rpkiNotify; +} + +int nid_certPolicyRpki(void) +{ + return NID_certPolicyRpki; +} + +int nid_certPolicyRpkiV2(void) +{ + return NID_certPolicyRpkiV2; +} + +int nid_ipAddrBlocksv2(void) +{ + return NID_ipAddrBlocksv2; +} + +int nid_autonomousSysIdsv2(void) +{ + return NID_autonomousSysIdsv2; +} diff --git a/src/nid.h b/src/nid.h new file mode 100644 index 00000000..64cdac86 --- /dev/null +++ b/src/nid.h @@ -0,0 +1,14 @@ +#ifndef SRC_NID_H_ +#define SRC_NID_H_ + +int nid_init(void); + +int nid_rpkiManifest(void); +int nid_signedObject(void); +int nid_rpkiNotify(void); +int nid_certPolicyRpki(void); +int nid_certPolicyRpkiV2(void); +int nid_ipAddrBlocksv2(void); +int nid_autonomousSysIdsv2(void); + +#endif /* SRC_NID_H_ */ diff --git a/src/object/certificate.c b/src/object/certificate.c index c8741bec..34bd3afe 100644 --- a/src/object/certificate.c +++ b/src/object/certificate.c @@ -12,6 +12,7 @@ #include "extension.h" #include "log.h" #include "manifest.h" +#include "nid.h" #include "thread_var.h" #include "asn1/decode.h" #include "asn1/oid.h" @@ -470,9 +471,12 @@ handle_asn_extension(X509_EXTENSION *ext, struct resources *resources) } int -certificate_get_resources(X509 *cert, struct resources *resources) +__certificate_get_resources(X509 *cert, struct resources *resources, + int addr_nid, int asn_nid, int bad_addr_nid, int bad_asn_nid, + char const *policy_rfc, char const *bad_ext_rfc) { X509_EXTENSION *ext; + int nid; int i; int error; bool ip_ext_found = false; @@ -483,9 +487,9 @@ certificate_get_resources(X509 *cert, struct resources *resources) for (i = 0; i < X509_get_ext_count(cert); i++) { ext = X509_get_ext(cert, i); + nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); - switch (OBJ_obj2nid(X509_EXTENSION_get_object(ext))) { - case NID_sbgp_ipAddrBlock: + if (nid == addr_nid) { if (ip_ext_found) return pr_err("Multiple IP extensions found."); if (!X509_EXTENSION_get_critical(ext)) @@ -498,9 +502,8 @@ certificate_get_resources(X509 *cert, struct resources *resources) if (error) return error; - break; - case NID_sbgp_autonomousSysNum: + } else if (nid == asn_nid) { if (asn_ext_found) return pr_err("Multiple AS extensions found."); if (!X509_EXTENSION_get_critical(ext)) @@ -513,7 +516,13 @@ certificate_get_resources(X509 *cert, struct resources *resources) if (error) return error; - break; + + } else if (nid == bad_addr_nid) { + return pr_err("Certificate has an RFC%s policy, but contains an RFC%s IP extension.", + policy_rfc, bad_ext_rfc); + } else if (nid == bad_asn_nid) { + return pr_err("Certificate has an RFC%s policy, but contains an RFC%s ASN extension.", + policy_rfc, bad_ext_rfc); } } @@ -523,6 +532,28 @@ certificate_get_resources(X509 *cert, struct resources *resources) return 0; } +int +certificate_get_resources(X509 *cert, struct resources *resources) +{ + enum rpki_policy policy; + + policy = resources_get_policy(resources); + switch (policy) { + case RPKI_POLICY_RFC6484: + return __certificate_get_resources(cert, resources, + NID_sbgp_ipAddrBlock, NID_sbgp_autonomousSysNum, + nid_ipAddrBlocksv2(), nid_autonomousSysIdsv2(), + "6484", "8360"); + case RPKI_POLICY_RFC8360: + return __certificate_get_resources(cert, resources, + nid_ipAddrBlocksv2(), nid_autonomousSysIdsv2(), + NID_sbgp_ipAddrBlock, NID_sbgp_autonomousSysNum, + "8360", "6484"); + } + + return pr_crit("Unknown policy: %u", policy); +} + static bool is_rsync(ASN1_IA5STRING *uri) { @@ -575,7 +606,7 @@ handle_bc(X509_EXTENSION *ext, void *arg) bc = X509V3_EXT_d2i(ext); if (bc == NULL) - return cannot_decode(&BC); + return cannot_decode(ext_bc()); /* * 'The issuer determines whether the "cA" boolean is set.' @@ -585,7 +616,8 @@ handle_bc(X509_EXTENSION *ext, void *arg) error = (bc->pathlen == NULL) ? 0 - : pr_err("%s extension contains a Path Length Constraint.", BC.name); + : pr_err("%s extension contains a Path Length Constraint.", + ext_bc()->name); BASIC_CONSTRAINTS_free(bc); return error; @@ -599,7 +631,7 @@ handle_ski_ca(X509_EXTENSION *ext, void *arg) ski = X509V3_EXT_d2i(ext); if (ski == NULL) - return cannot_decode(&SKI); + return cannot_decode(ext_ski()); error = validate_public_key_hash(arg, ski); @@ -617,7 +649,7 @@ handle_ski_ee(X509_EXTENSION *ext, void *arg) ski = X509V3_EXT_d2i(ext); if (ski == NULL) - return cannot_decode(&SKI); + return cannot_decode(ext_ski()); args = arg; error = validate_public_key_hash(args->cert, ski); @@ -673,16 +705,16 @@ handle_aki_ta(X509_EXTENSION *aki, void *arg) for (i = 0; i < X509_get_ext_count(cert); i++) { other = X509_get_ext(cert, i); - if (OBJ_obj2nid(X509_EXTENSION_get_object(other)) == SKI.nid) { + if (OBJ_obj2nid(X509_EXTENSION_get_object(other)) == ext_ski()->nid) { if (extension_equals(aki, other)) return 0; return pr_err("The '%s' does not equal the '%s'.", - AKI.name, SKI.name); + ext_aki()->name, ext_ski()->name); } } - pr_err("Certificate lacks the '%s' extension.", SKI.name); + pr_err("Certificate lacks the '%s' extension.", ext_ski()->name); return -ESRCH; } @@ -701,10 +733,11 @@ handle_ku(X509_EXTENSION *ext, unsigned char byte1) ku = X509V3_EXT_d2i(ext); if (ku == NULL) - return cannot_decode(&KU); + return cannot_decode(ext_ku()); if (ku->length == 0) { - error = pr_err("%s bit string has no enabled bits.", KU.name); + error = pr_err("%s bit string has no enabled bits.", + ext_ku()->name); goto end; } @@ -752,11 +785,11 @@ handle_cdp(X509_EXTENSION *ext, void *arg) crldp = X509V3_EXT_d2i(ext); if (crldp == NULL) - return cannot_decode(&CDP); + return cannot_decode(ext_cdp()); if (sk_DIST_POINT_num(crldp) != 1) { error = pr_err("The %s extension has %u distribution points. (1 expected)", - CDP.name, sk_DIST_POINT_num(crldp)); + ext_cdp()->name, sk_DIST_POINT_num(crldp)); goto end; } @@ -814,8 +847,8 @@ handle_cdp(X509_EXTENSION *ext, void *arg) error_msg = "lacks an RSYNC URI"; dist_point_error: - error = pr_err("The %s extension's distribution point %s.", CDP.name, - error_msg); + error = pr_err("The %s extension's distribution point %s.", + ext_cdp()->name, error_msg); end: sk_DIST_POINT_pop_free(crldp, DIST_POINT_free); @@ -910,7 +943,7 @@ handle_aia(X509_EXTENSION *ext, void *arg) aia = X509V3_EXT_d2i(ext); if (aia == NULL) - return cannot_decode(&AIA); + return cannot_decode(ext_aia()); error = handle_ad("AIA", aia, "caIssuers", NID_ad_ca_issuers, handle_caIssuers, arg); @@ -927,7 +960,7 @@ handle_sia_ca(X509_EXTENSION *ext, void *arg) sia = X509V3_EXT_d2i(ext); if (sia == NULL) - return cannot_decode(&SIA); + return cannot_decode(ext_sia()); /* rsync */ error = handle_ad("SIA", sia, "caRepository", NID_caRepository, @@ -940,7 +973,7 @@ handle_sia_ca(X509_EXTENSION *ext, void *arg) * (We won't actually touch the manifest until we know the certificate * is fully valid.) */ - error = handle_ad("SIA", sia, "rpkiManifest",NID_rpkiManifest, + error = handle_ad("SIA", sia, "rpkiManifest", nid_rpkiManifest(), handle_rpkiManifest, arg); end: @@ -956,9 +989,9 @@ handle_sia_ee(X509_EXTENSION *ext, void *arg) sia = X509V3_EXT_d2i(ext); if (sia == NULL) - return cannot_decode(&SIA); + return cannot_decode(ext_sia()); - error = handle_ad("SIA", sia, "signedObject", NID_signedObject, + error = handle_ad("SIA", sia, "signedObject", nid_signedObject(), handle_signedObject, arg); AUTHORITY_INFO_ACCESS_free(sia); @@ -968,6 +1001,7 @@ handle_sia_ee(X509_EXTENSION *ext, void *arg) static int handle_cp(X509_EXTENSION *ext, void *arg) { + enum rpki_policy *policy = arg; CERTIFICATEPOLICIES *cp; POLICYINFO *pi; POLICYQUALINFO *pqi; @@ -976,21 +1010,29 @@ handle_cp(X509_EXTENSION *ext, void *arg) error = 0; cp = X509V3_EXT_d2i(ext); if (cp == NULL) - return cannot_decode(&CP); + return cannot_decode(ext_cp()); if (sk_POLICYINFO_num(cp) != 1) { error = pr_err("The %s extension has %u policy information's. (1 expected)", - CP.name, sk_POLICYINFO_num(cp)); + ext_cp()->name, sk_POLICYINFO_num(cp)); goto end; } /* rfc7318#section-2 and consider rfc8360#section-4.2.1 */ pi = sk_POLICYINFO_value(cp, 0); nid_cp = OBJ_obj2nid(pi->policyid); - if (nid_cp != NID_certPolicyRpki && nid_cp != NID_certPolicyRpkiV2) { + if (nid_cp == nid_certPolicyRpki()) { + if (policy != NULL) + *policy = RPKI_POLICY_RFC6484; + } else if (nid_cp == nid_certPolicyRpkiV2()) { + pr_debug("Found RFC8360 policy!"); + if (policy != NULL) + *policy = RPKI_POLICY_RFC8360; + } else { error = pr_err("Invalid certificate policy OID, isn't 'id-cp-ipAddr-asNumber' nor 'id-cp-ipAddr-asNumber-v2'"); goto end; } + /* Exactly one policy qualifier MAY be included (so none is also valid) */ if (pi->qualifiers == NULL) goto end; @@ -1000,7 +1042,7 @@ handle_cp(X509_EXTENSION *ext, void *arg) goto end; if (pqi_num != 1) { error = pr_err("The %s extension has %d policy qualifiers. (none or only 1 expected)", - CP.name, pqi_num); + ext_cp()->name, pqi_num); goto end; } @@ -1028,18 +1070,21 @@ handle_ar(X509_EXTENSION *ext, void *arg) } int -certificate_validate_extensions_ta(X509 *cert, struct rpki_uri *mft) +certificate_validate_extensions_ta(X509 *cert, struct rpki_uri *mft, + enum rpki_policy *policy) { struct extension_handler handlers[] = { - /* ext reqd handler arg */ - { &BC, true, handle_bc, }, - { &SKI, true, handle_ski_ca, cert }, - { &AKI, false, handle_aki_ta, cert }, - { &KU, true, handle_ku_ca, }, - { &SIA, true, handle_sia_ca, mft }, - { &CP, true, handle_cp, }, - { &IR, false, handle_ir, }, - { &AR, false, handle_ar, }, + /* ext reqd handler arg */ + { ext_bc(), true, handle_bc, }, + { ext_ski(), true, handle_ski_ca, cert }, + { ext_aki(), false, handle_aki_ta, cert }, + { ext_ku(), true, handle_ku_ca, }, + { ext_sia(), true, handle_sia_ca, mft }, + { ext_cp(), true, handle_cp, }, + { ext_ir(), false, handle_ir, }, + { ext_ar(), false, handle_ar, }, + { ext_ir2(), false, handle_ir, }, + { ext_ar2(), false, handle_ar, }, { NULL }, }; @@ -1048,20 +1093,22 @@ certificate_validate_extensions_ta(X509 *cert, struct rpki_uri *mft) int certificate_validate_extensions_ca(X509 *cert, struct rpki_uri *mft, - struct certificate_refs *refs) + struct certificate_refs *refs, enum rpki_policy *policy) { struct extension_handler handlers[] = { - /* ext reqd handler arg */ - { &BC, true, handle_bc, }, - { &SKI, true, handle_ski_ca, cert }, - { &AKI, true, handle_aki, }, - { &KU, true, handle_ku_ca, }, - { &CDP, true, handle_cdp, refs }, - { &AIA, true, handle_aia, refs }, - { &SIA, true, handle_sia_ca, mft }, - { &CP, true, handle_cp, }, - { &IR, false, handle_ir, }, - { &AR, false, handle_ar, }, + /* ext reqd handler arg */ + { ext_bc(), true, handle_bc, }, + { ext_ski(), true, handle_ski_ca, cert }, + { ext_aki(), true, handle_aki, }, + { ext_ku(), true, handle_ku_ca, }, + { ext_cdp(), true, handle_cdp, refs }, + { ext_aia(), true, handle_aia, refs }, + { ext_sia(), true, handle_sia_ca, mft }, + { ext_cp(), true, handle_cp, }, + { ext_ir(), false, handle_ir, }, + { ext_ar(), false, handle_ar, }, + { ext_ir2(), false, handle_ir, }, + { ext_ar2(), false, handle_ar, }, { NULL }, }; @@ -1070,20 +1117,22 @@ certificate_validate_extensions_ca(X509 *cert, struct rpki_uri *mft, int certificate_validate_extensions_ee(X509 *cert, OCTET_STRING_t *sid, - struct certificate_refs *refs) + struct certificate_refs *refs, enum rpki_policy *policy) { struct ski_arguments ski_args; struct extension_handler handlers[] = { - /* ext reqd handler arg */ - { &SKI, true, handle_ski_ee, &ski_args }, - { &AKI, true, handle_aki, }, - { &KU, true, handle_ku_ee, }, - { &CDP, true, handle_cdp, refs }, - { &AIA, true, handle_aia, refs }, - { &SIA, true, handle_sia_ee, refs }, - { &CP, true, handle_cp, }, - { &IR, false, handle_ir, }, - { &AR, false, handle_ar, }, + /* ext reqd handler arg */ + { ext_ski(), true, handle_ski_ee, &ski_args }, + { ext_aki(), true, handle_aki, }, + { ext_ku(), true, handle_ku_ee, }, + { ext_cdp(), true, handle_cdp, refs }, + { ext_aia(), true, handle_aia, refs }, + { ext_sia(), true, handle_sia_ee, refs }, + { ext_cp(), true, handle_cp, policy }, + { ext_ir(), false, handle_ir, }, + { ext_ar(), false, handle_ar, }, + { ext_ir2(), false, handle_ir, }, + { ext_ar2(), false, handle_ar, }, { NULL }, }; @@ -1102,6 +1151,7 @@ certificate_traverse(struct rpp *rpp_parent, struct rpki_uri const *cert_uri, X509 *cert; struct rpki_uri mft; struct certificate_refs refs; + enum rpki_policy policy; struct rpp *pp; int error; @@ -1129,8 +1179,8 @@ certificate_traverse(struct rpp *rpp_parent, struct rpki_uri const *cert_uri, if (error) goto end2; error = is_ta - ? certificate_validate_extensions_ta(cert, &mft) - : certificate_validate_extensions_ca(cert, &mft, &refs); + ? certificate_validate_extensions_ta(cert, &mft, &policy) + : certificate_validate_extensions_ca(cert, &mft, &refs, &policy); if (error) goto end2; @@ -1139,7 +1189,7 @@ certificate_traverse(struct rpp *rpp_parent, struct rpki_uri const *cert_uri, goto end3; /* -- Validate the manifest (@mft) pointed by the certificate -- */ - error = validation_push_cert(state, cert_uri, cert, is_ta); + error = validation_push_cert(state, cert_uri, cert, policy, is_ta); if (error) goto end3; diff --git a/src/object/certificate.h b/src/object/certificate.h index 57456834..5b87e3ea 100644 --- a/src/object/certificate.h +++ b/src/object/certificate.h @@ -34,7 +34,8 @@ int certificate_get_resources(X509 *, struct resources *); * Also initializes the second argument as the URI of the rpkiManifest Access * Description from the SIA extension. */ -int certificate_validate_extensions_ta(X509 *, struct rpki_uri *); +int certificate_validate_extensions_ta(X509 *, struct rpki_uri *, + enum rpki_policy *); /** * Validates the certificate extensions, (intermediate) Certificate Authority * style. @@ -45,7 +46,7 @@ int certificate_validate_extensions_ta(X509 *, struct rpki_uri *); * extensions. */ int certificate_validate_extensions_ca(X509 *, struct rpki_uri *, - struct certificate_refs *); + struct certificate_refs *, enum rpki_policy *); /** * Validates the certificate extensions, End-Entity style. * @@ -53,7 +54,7 @@ int certificate_validate_extensions_ca(X509 *, struct rpki_uri *, * extensions. */ int certificate_validate_extensions_ee(X509 *, OCTET_STRING_t *, - struct certificate_refs *); + struct certificate_refs *, enum rpki_policy *); int certificate_traverse(struct rpp *, struct rpki_uri const *, STACK_OF(X509_CRL) *, bool); diff --git a/src/object/crl.c b/src/object/crl.c index 913386bc..8bd2caa7 100644 --- a/src/object/crl.c +++ b/src/object/crl.c @@ -49,7 +49,7 @@ validate_revoked(X509_CRL *crl) revoked_stack = X509_CRL_get_REVOKED(crl); if (revoked_stack == NULL) - return pr_err("Likely programming error: CRL revoked stack is NULL."); + return 0; /* Guess the RFC doesn't enforce this thing. */ for (i = 0; i < sk_X509_REVOKED_num(revoked_stack); i++) { revoked = sk_X509_REVOKED_value(revoked_stack, i); @@ -102,8 +102,8 @@ validate_extensions(X509_CRL *crl) { struct extension_handler handlers[] = { /* ext reqd handler arg */ - { &AKI, true, handle_aki, }, - { &CN, true, handle_crlnum, }, + { ext_aki(), true, handle_aki, }, + { ext_cn(), true, handle_crlnum, }, { NULL }, }; diff --git a/src/object/roa.c b/src/object/roa.c index 0248f574..e31a4844 100644 --- a/src/object/roa.c +++ b/src/object/roa.c @@ -31,7 +31,7 @@ print_addr4(struct resources *parent, unsigned long asn, return pr_err("The ROA's IPv4 maxLength isn't a valid unsigned long"); } - if (max_length < 0 || 32 < max_length) { + if (max_length > 32) { return pr_err("maxLength (%lu) is out of bounds (0-32).", max_length); } @@ -83,7 +83,7 @@ print_addr6(struct resources *parent, unsigned long asn, return pr_err("The ROA's IPv6 maxLength isn't a valid unsigned long"); } - if (max_length < 0 || 128 < max_length) { + if (max_length > 128) { return pr_err("maxLength (%lu) is out of bounds (0-128).", max_length); } diff --git a/src/resource.c b/src/resource.c index 83ba33f0..dd3733b3 100644 --- a/src/resource.c +++ b/src/resource.c @@ -1,7 +1,6 @@ #include "resource.h" #include -#include #include /* UINT32_MAX */ #include "address.h" @@ -17,6 +16,7 @@ struct resources { struct resources_ipv4 *ip4s; struct resources_ipv6 *ip6s; struct resources_asn *asns; + enum rpki_policy policy; }; struct resources * @@ -31,6 +31,7 @@ resources_create(void) result->ip4s = NULL; result->ip6s = NULL; result->asns = NULL; + result->policy = RPKI_POLICY_RFC6484; return result; } @@ -78,48 +79,6 @@ get_parent_resources(void) return (state != NULL) ? validation_peek_resource(state) : NULL; } -static void -pr_debug_ip_prefix(int family, void *addr, unsigned 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 void -pr_debug_range(int family, void *min, void *max) -{ -#ifdef DEBUG - char buffer_min[INET6_ADDRSTRLEN]; - char buffer_max[INET6_ADDRSTRLEN]; - char const *string_min; - char const *string_max; - - string_min = inet_ntop(family, min, buffer_min, sizeof(buffer_min)); - if (string_min == NULL) - goto fail; - - string_max = inet_ntop(family, max, buffer_max, sizeof(buffer_max)); - if (string_max == NULL) - goto fail; - - pr_debug("Range: %s-%s", string_min, string_max); - return; - -fail: - pr_debug("Range: (Cannot convert to string. Errcode %d)", errno); -#endif -} - static int inherit_aors(struct resources *resources, int family) { @@ -170,8 +129,16 @@ add_prefix4(struct resources *resources, IPAddress2_t *addr) if (error) return error; - if (parent && !res4_contains_prefix(parent->ip4s, &prefix)) - return pr_err("Parent certificate doesn't own child's IPv4 resource."); + if (parent && !res4_contains_prefix(parent->ip4s, &prefix)) { + switch (resources->policy) { + case RPKI_POLICY_RFC6484: + return pr_err("Parent certificate doesn't own IPv4 prefix '%s/%u'.", + v4addr2str(&prefix.addr), prefix.len); + case RPKI_POLICY_RFC8360: + return pr_warn("Certificate is overclaiming the IPv4 prefix '%s/%u'.", + v4addr2str(&prefix.addr), prefix.len); + } + } if (resources->ip4s == NULL) { resources->ip4s = res4_create(); @@ -181,12 +148,13 @@ add_prefix4(struct resources *resources, IPAddress2_t *addr) error = res4_add_prefix(resources->ip4s, &prefix); if (error) { - pr_err("Error adding IPv4 prefix to certificate resources: %s", + pr_err("Error adding IPv4 prefix '%s/%u' to certificate resources: %s", + v4addr2str(&prefix.addr), prefix.len, sarray_err2str(error)); return error; } - pr_debug_ip_prefix(AF_INET, &prefix.addr, prefix.len); + pr_debug("Prefix: %s/%u", v4addr2str(&prefix.addr), prefix.len); return 0; } @@ -206,8 +174,16 @@ add_prefix6(struct resources *resources, IPAddress2_t *addr) if (error) return error; - if (parent && !res6_contains_prefix(parent->ip6s, &prefix)) - return pr_err("Parent certificate doesn't own child's IPv6 resource."); + if (parent && !res6_contains_prefix(parent->ip6s, &prefix)) { + switch (resources->policy) { + case RPKI_POLICY_RFC6484: + return pr_err("Parent certificate doesn't own IPv6 prefix '%s/%u'.", + v6addr2str(&prefix.addr), prefix.len); + case RPKI_POLICY_RFC8360: + return pr_warn("Certificate is overclaiming the IPv6 prefix '%s/%u'.", + v6addr2str(&prefix.addr), prefix.len); + } + } if (resources->ip6s == NULL) { resources->ip6s = res6_create(); @@ -217,12 +193,13 @@ add_prefix6(struct resources *resources, IPAddress2_t *addr) error = res6_add_prefix(resources->ip6s, &prefix); if (error) { - pr_err("Error adding IPv6 prefix to certificate resources: %s", + pr_err("Error adding IPv6 prefix '%s/%u' to certificate resources: %s", + v6addr2str(&prefix.addr), prefix.len, sarray_err2str(error)); return error; } - pr_debug_ip_prefix(AF_INET6, &prefix.addr, prefix.len); + pr_debug("Prefix: %s/%u", v6addr2str(&prefix.addr), prefix.len); return 0; } @@ -255,8 +232,16 @@ add_range4(struct resources *resources, IPAddressRange_t *input) if (error) return error; - if (parent && !res4_contains_range(parent->ip4s, &range)) - return pr_err("Parent certificate doesn't own child's IPv4 resource."); + if (parent && !res4_contains_range(parent->ip4s, &range)) { + switch (resources->policy) { + case RPKI_POLICY_RFC6484: + return pr_err("Parent certificate doesn't own IPv4 range '%s-%s'.", + v4addr2str(&range.min), v4addr2str2(&range.max)); + case RPKI_POLICY_RFC8360: + return pr_warn("Certificate is overclaiming the IPv4 range '%s-%s'.", + v4addr2str(&range.min), v4addr2str2(&range.max)); + } + } if (resources->ip4s == NULL) { resources->ip4s = res4_create(); @@ -266,12 +251,14 @@ add_range4(struct resources *resources, IPAddressRange_t *input) error = res4_add_range(resources->ip4s, &range); if (error) { - pr_err("Error adding IPv4 range to certificate resources: %s", + pr_err("Error adding IPv4 range '%s-%s' to certificate resources: %s", + v4addr2str(&range.min), v4addr2str2(&range.max), sarray_err2str(error)); return error; } - pr_debug_range(AF_INET, &range.min, &range.max); + pr_debug("Range: %s-%s", v4addr2str(&range.min), + v4addr2str2(&range.max)); return 0; } @@ -291,8 +278,16 @@ add_range6(struct resources *resources, IPAddressRange_t *input) if (error) return error; - if (parent && !res6_contains_range(parent->ip6s, &range)) - return pr_err("Parent certificate doesn't own child's IPv6 resource."); + if (parent && !res6_contains_range(parent->ip6s, &range)) { + switch (resources->policy) { + case RPKI_POLICY_RFC6484: + return pr_err("Parent certificate doesn't own IPv6 range '%s-%s'.", + v6addr2str(&range.min), v6addr2str2(&range.max)); + case RPKI_POLICY_RFC8360: + return pr_warn("Certificate is overclaiming the IPv6 range '%s-%s'.", + v6addr2str(&range.min), v6addr2str2(&range.max)); + } + } if (resources->ip6s == NULL) { resources->ip6s = res6_create(); @@ -302,12 +297,14 @@ add_range6(struct resources *resources, IPAddressRange_t *input) error = res6_add_range(resources->ip6s, &range); if (error) { - pr_err("Error adding IPv6 range to certificate resources: %s", + pr_err("Error adding IPv6 range '%s-%s' to certificate resources: %s", + v6addr2str(&range.min), v6addr2str2(&range.max), sarray_err2str(error)); return error; } - pr_debug_range(AF_INET6, &range.min, &range.max); + pr_debug("Range: %s-%s", v6addr2str(&range.min), + v6addr2str2(&range.max)); return 0; } @@ -332,6 +329,9 @@ add_aors(struct resources *resources, int family, int i; int error; + if (aors->list.count == 0) + return pr_err("IP extension's set of IP address records is empty."); + for (i = 0; i < aors->list.count; i++) { aor = aors->list.array[i]; switch (aor->present) { @@ -431,8 +431,16 @@ add_asn(struct resources *resources, unsigned long min, unsigned long max, if (min > max) return pr_err("The ASN range %lu-%lu is inverted.", min, max); - if (parent && !rasn_contains(parent->asns, min, max)) - return pr_err("Parent certificate doesn't own child's ASN resource."); + if (parent && !rasn_contains(parent->asns, min, max)) { + switch (resources->policy) { + case RPKI_POLICY_RFC6484: + return pr_err("Parent certificate doesn't own ASN range '%lu-%lu'.", + min, max); + case RPKI_POLICY_RFC8360: + return pr_warn("Certificate is overclaiming the ASN range '%lu-%lu'.", + min, max); + } + } if (resources->asns == NULL) { resources->asns = rasn_create(); @@ -442,8 +450,8 @@ add_asn(struct resources *resources, unsigned long min, unsigned long max, error = rasn_add(resources->asns, min, max); if (error){ - pr_err("Error adding ASN range to certificate resources: %s", - sarray_err2str(error)); + pr_err("Error adding ASN range '%lu-%lu' to certificate resources: %s", + min, max, sarray_err2str(error)); return error; } @@ -507,6 +515,8 @@ resources_add_asn(struct resources *resources, struct ASIdentifiers *ids) return inherit_asiors(resources); case ASIdentifierChoice_PR_asIdsOrRanges: iors = &ids->asnum->choice.asIdsOrRanges; + if (iors->list.count == 0) + return pr_err("AS extension's set of AS number records is empty."); for (i = 0; i < iors->list.count; i++) { error = add_asior(resources, iors->list.array[i]); if (error) @@ -547,3 +557,14 @@ resources_contains_ipv6(struct resources *res, struct ipv6_prefix *prefix) return res6_contains_prefix(res->ip6s, prefix); } +enum rpki_policy +resources_get_policy(struct resources *res) +{ + return res->policy; +} + +void +resources_set_policy(struct resources *res, enum rpki_policy policy) +{ + res->policy = policy; +} diff --git a/src/resource.h b/src/resource.h index fa1acb87..62101da5 100644 --- a/src/resource.h +++ b/src/resource.h @@ -6,6 +6,19 @@ #include #include "address.h" +enum rpki_policy { + /** + * If certificate `x`'s resources are not a subset of `x - 1`'s + * resources, then `x` is to be rejected. + */ + RPKI_POLICY_RFC6484, + /** + * If certificate `x`'s resources are not a subset of `x - 1`'s + * resources, then the overclaiming resources are to be ignored. + */ + RPKI_POLICY_RFC8360, +}; + int get_addr_family(OCTET_STRING_t *); struct resources; @@ -21,4 +34,7 @@ bool resources_contains_asn(struct resources *, unsigned long); bool resources_contains_ipv4(struct resources *, struct ipv4_prefix *); bool resources_contains_ipv6(struct resources *, struct ipv6_prefix *); +enum rpki_policy resources_get_policy(struct resources *); +void resources_set_policy(struct resources *, enum rpki_policy); + #endif /* SRC_RESOURCE_H_ */ diff --git a/src/resource/ip4.c b/src/resource/ip4.c index 140f0961..f1902e67 100644 --- a/src/resource/ip4.c +++ b/src/resource/ip4.c @@ -32,7 +32,7 @@ static void pton(struct ipv4_prefix *p, struct r4_node *n) { n->min = ntohl(p->addr.s_addr); - n->max = n->min | (0xFFFFFFFFu >> p->len); + n->max = n->min | u32_suffix_mask(p->len); } static void diff --git a/src/state.c b/src/state.c index ccb7c509..a8b94650 100644 --- a/src/state.c +++ b/src/state.c @@ -62,6 +62,16 @@ struct validation { /* Did the TAL's public key match the root certificate's public key? */ enum pubkey_state pubkey_state; + + /** + * Two buffers calling code will store stringified IP addresses in, + * to prevent proliferation of similar buffers on the stack. + * + * They are meant to be large enough to contain both IPv4 and IPv6 + * addresses. + */ + char addr_buffer1[INET6_ADDRSTRLEN]; + char addr_buffer2[INET6_ADDRSTRLEN]; }; /* @@ -224,7 +234,7 @@ enum pubkey_state validation_pubkey_state(struct validation *state) */ int validation_push_cert(struct validation *state, struct rpki_uri const *cert_uri, - X509 *x509, bool is_ta) + X509 *x509, enum rpki_policy policy, bool is_ta) { struct certificate *cert; int ok; @@ -249,6 +259,7 @@ validation_push_cert(struct validation *state, struct rpki_uri const *cert_uri, goto end4; } + resources_set_policy(cert->resources, policy); error = certificate_get_resources(x509, cert->resources); if (error) goto end5; @@ -376,3 +387,15 @@ validation_store_subject(struct validation *state, char *subject) return error; } + +char * +validation_get_ip_buffer1(struct validation *state) +{ + return state->addr_buffer1; +} + +char * +validation_get_ip_buffer2(struct validation *state) +{ + return state->addr_buffer2; +} diff --git a/src/state.h b/src/state.h index 4e3b1506..9ff7809b 100644 --- a/src/state.h +++ b/src/state.h @@ -25,7 +25,7 @@ void validation_pubkey_invalid(struct validation *); enum pubkey_state validation_pubkey_state(struct validation *); int validation_push_cert(struct validation *, struct rpki_uri const *, X509 *, - bool); + enum rpki_policy, bool); int validation_pop_cert(struct validation *); X509 *validation_peek_cert(struct validation *); struct rpki_uri const *validation_peek_cert_uri(struct validation *); @@ -35,4 +35,7 @@ struct resources *validation_peek_resource(struct validation *); int validation_store_serial_number(struct validation *, BIGNUM *); int validation_store_subject(struct validation *, char *); +char *validation_get_ip_buffer1(struct validation *); +char *validation_get_ip_buffer2(struct validation *); + #endif /* SRC_STATE_H_ */ diff --git a/src/thread_var.c b/src/thread_var.c index dd7ca943..056be6f3 100644 --- a/src/thread_var.c +++ b/src/thread_var.c @@ -5,6 +5,7 @@ #include #include #include +#include static pthread_key_t state_key; static pthread_key_t filenames_key; @@ -173,3 +174,58 @@ fnstack_pop(void) files->len--; } + +static char const * +addr2str(int af, void *addr, char *(*buffer_cb)(struct validation *)) +{ + struct validation *state; + + state = state_retrieve(); + if (!state) + return NULL; + + return inet_ntop(af, addr, buffer_cb(state), INET6_ADDRSTRLEN); +} + +/** + * Returns @addr, converted to a printable string. Intended for minimal clutter + * address printing. + * + * The buffer the string is stored in was allocated in a thread variable, so it + * will be overridden the next time you call this function. Also, you should not + * free it. + * + * The buffer is the same as v6addr2str()'s, so don't mix them either. + */ +char const * +v4addr2str(struct in_addr *addr) +{ + return addr2str(AF_INET, addr, validation_get_ip_buffer1); +} + +/** + * Same as v4addr2str(), except a different buffer is used. + */ +char const * +v4addr2str2(struct in_addr *addr) +{ + return addr2str(AF_INET, addr, validation_get_ip_buffer2); +} + +/** + * See v4addr2str(). + */ +char const * +v6addr2str(struct in6_addr *addr) +{ + return addr2str(AF_INET6, addr, validation_get_ip_buffer1); +} + +/** + * See v4addr2str2(). + */ +char const * +v6addr2str2(struct in6_addr *addr) +{ + return addr2str(AF_INET6, addr, validation_get_ip_buffer2); +} diff --git a/src/thread_var.h b/src/thread_var.h index c0cea0be..868cc6ea 100644 --- a/src/thread_var.h +++ b/src/thread_var.h @@ -13,4 +13,10 @@ void fnstack_push(char const *); char const *fnstack_peek(void); void fnstack_pop(void); +/* TODO use these more, to print better error messages. */ +char const *v4addr2str(struct in_addr *addr); +char const *v4addr2str2(struct in_addr *addr); +char const *v6addr2str(struct in6_addr *addr); +char const *v6addr2str2(struct in6_addr *addr); + #endif /* SRC_THREAD_VAR_H_ */