From: Alberto Leiva Popper Date: Tue, 3 Dec 2024 19:58:10 +0000 (-0300) Subject: Always pick most recent known manifest X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=49b1c1c0699996261cdae7af1fb246f030c90cea;p=thirdparty%2FFORT-validator.git Always pick most recent known manifest Strict version. --- diff --git a/src/asn1/asn1c/INTEGER.c b/src/asn1/asn1c/INTEGER.c index 30a5d041..7e953975 100644 --- a/src/asn1/asn1c/INTEGER.c +++ b/src/asn1/asn1c/INTEGER.c @@ -8,6 +8,8 @@ #include #include +#include "alloc.h" +#include "common.h" #include "asn1/asn1c/asn_codecs_prim.h" #include "asn1/asn1c/asn_internal.h" #include "json_util.h" @@ -695,6 +697,44 @@ asn_strtoul_lim(const char *str, const char **end, unsigned long *ulp) { return ASN_STRTOX_ERROR_INVAL; } +char * +asn_INTEGER2str(INTEGER_t const *bi) +{ + return bi ? hex2str(bi->buf, bi->size) : NULL; +} + +int +asn_str2INTEGER(char const *str, INTEGER_t *bigint) +{ + size_t slen; + int error; + + if (str == NULL) { + bigint->buf = NULL; + bigint->size = 0; + return 0; + } + + slen = strlen(str); + if (slen > 40) + return EOVERFLOW; + if (slen & 1) + return EINVAL; + + bigint->size = slen / 2; + bigint->buf = pmalloc(bigint->size); + + error = str2hex(str, bigint->buf); + if (error) { + free(bigint->buf); + bigint->buf = NULL; + bigint->size = 0; + return error; + } + + return 0; +} + int INTEGER_compare(const asn_TYPE_descriptor_t *td, const void *aptr, const void *bptr) { @@ -737,3 +777,23 @@ INTEGER_compare(const asn_TYPE_descriptor_t *td, const void *aptr, } } + +int +INTEGER_cmp(INTEGER_t const *a, INTEGER_t const *b) +{ + return INTEGER_compare(&asn_DEF_INTEGER, a, b); +} + +/* Transfers ownership */ +void +INTEGER_move(INTEGER_t *to, INTEGER_t *from) +{ + *to = *from; + memset(from, 0, sizeof(*from)); +} + +void +INTEGER_cleanup(INTEGER_t *st) +{ + INTEGER_free(&asn_DEF_INTEGER, st, ASFM_FREE_UNDERLYING); +} diff --git a/src/asn1/asn1c/INTEGER.h b/src/asn1/asn1c/INTEGER.h index b09933a7..91784212 100644 --- a/src/asn1/asn1c/INTEGER.h +++ b/src/asn1/asn1c/INTEGER.h @@ -83,10 +83,17 @@ enum asn_strtox_result_e asn_strtoimax_lim(const char *str, const char **end, enum asn_strtox_result_e asn_strtoumax_lim(const char *str, const char **end, uintmax_t *l); +char *asn_INTEGER2str(INTEGER_t const *); +int asn_str2INTEGER(char const *, INTEGER_t *); + /* * Convert the integer value into the corresponding enumeration map entry. */ const asn_INTEGER_enum_map_t *INTEGER_map_value2enum( const asn_INTEGER_specifics_t *specs, long value); +int INTEGER_cmp(INTEGER_t const *, INTEGER_t const *); +void INTEGER_move(INTEGER_t *, INTEGER_t *); +void INTEGER_cleanup(INTEGER_t *); + #endif /* _INTEGER_H_ */ diff --git a/src/cache.c b/src/cache.c index f3b9e451..6cf6f9ba 100644 --- a/src/cache.c +++ b/src/cache.c @@ -395,6 +395,9 @@ json2node(json_t *json) error = json_get_ts(json, "success", &node->success_ts); if (error != 0 && error != ENOENT) goto fail; + error = json_get_bigint(json, "mftNum", &node->mft.num); + if (error < 0) + goto fail; error = json_get_ts(json, "mftUpdate", &node->mft.update); if (error < 0) goto fail; @@ -537,6 +540,8 @@ node2json(struct cache_node *node) goto fail; if (node->success_ts && json_add_ts(json, "success", node->success_ts)) goto fail; + if (node->mft.num.size && json_add_bigint(json, "mftNum", &node->mft.num)) + goto fail; if (node->mft.update && json_add_ts(json, "mftUpdate", node->mft.update)) goto fail; if (node->rrdp) @@ -980,8 +985,8 @@ cage_mft_fallback(struct cache_cage *cage) } /* - * Steals ownership of @rpp->files and @rpp->nfiles, but they're not going to be - * modified nor deleted until the cache cleanup. + * Steals ownership of @rpp->files, @rpp->nfiles and @rpp->mft.num, but they're + * not going to be modified nor deleted until the cache cleanup. */ void cache_commit_rpp(char const *rpkiNotify, char const *caRepository, @@ -994,7 +999,8 @@ cache_commit_rpp(char const *rpkiNotify, char const *caRepository, commit->caRepository = pstrdup(caRepository); commit->files = rpp->files; commit->nfiles = rpp->nfiles; - commit->mft = rpp->mft; + INTEGER_move(&commit->mft.num, &rpp->mft.num); + commit->mft.update = rpp->mft.update; mutex_lock(&commits_lock); STAILQ_INSERT_TAIL(&commits, commit, lh); @@ -1016,6 +1022,7 @@ cache_commit_file(struct cache_mapping *map) commit->files[0].url = pstrdup(map->url); commit->files[0].path = pstrdup(map->path); commit->nfiles = 1; + memset(&commit->mft, 0, sizeof(commit->mft)); mutex_lock(&commits_lock); STAILQ_INSERT_TAIL(&commits, commit, lh); @@ -1085,7 +1092,8 @@ commit_rpp(struct cache_commit *commit, struct cache_node *fb) char const *dst; array_index i; - fb->mft = commit->mft; + INTEGER_move(&fb->mft.num, &commit->mft.num); + fb->mft.update = commit->mft.update; for (i = 0; i < commit->nfiles; i++) { src = commit->files + i; @@ -1216,6 +1224,7 @@ skip: free(commit->rpkiNotify); free(commit->files[i].path); } free(commit->files); + mftm_cleanup(&commit->mft); free(commit); } } diff --git a/src/common.c b/src/common.c index bdcbe978..2d743e36 100644 --- a/src/common.c +++ b/src/common.c @@ -307,3 +307,60 @@ str2time(char const *str, time_t *tt) *tt = time; return 0; } + +char *hex2str(uint8_t const *hex, size_t hexlen) +{ + static const char * const H2C = "0123456789ABCDEF"; + char *str; + size_t i; + + if (hex == NULL || hexlen == 0) + return NULL; + + str = pmalloc(2 * hexlen + 1); + for (i = 0; i < hexlen; i++) { + str[2 * i ] = H2C[hex[i] >> 4]; + str[2 * i + 1] = H2C[hex[i] & 0xF]; + } + str[2 * i] = 0; + + return str; +} + +static int +c2h(char c) +{ + if ('0' <= c && c <= '9') + return c - '0'; + if ('A' <= c && c <= 'F') + return c - 'A' + 10; + if ('a' <= c && c <= 'f') + return c - 'a' + 10; + return -1; +} + +/* @hex needs to be already allocated. */ +int str2hex(char const *str, uint8_t *hex) +{ + size_t h; + int digit; + + if (str[0] == 0) + return EINVAL; /* Not a number */ + + for (h = 0; str[2 * h] != 0; h++) { + digit = c2h(str[2 * h]); + if (digit < 0) + return EINVAL; /* Not hexadecimal */ + hex[h] = digit << 4; + + if (str[2 * h + 1] == 0) + return EINVAL; /* Need an even length */ + digit = c2h(str[2 * h + 1]); + if (digit < 0) + return EINVAL; /* Not hexadecimal */ + hex[h] |= digit; + } + + return 0; +} diff --git a/src/common.h b/src/common.h index 63c37b61..ae32d407 100644 --- a/src/common.h +++ b/src/common.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -59,4 +60,7 @@ time_t time_fatal(void); int time2str(time_t, char *); int str2time(char const *, time_t *); +char *hex2str(uint8_t const *, size_t); +int str2hex(char const *, uint8_t *); + #endif /* SRC_RTR_COMMON_H_ */ diff --git a/src/json_util.c b/src/json_util.c index 5976bf96..0f2a3d70 100644 --- a/src/json_util.c +++ b/src/json_util.c @@ -103,6 +103,24 @@ json_get_ulong(json_t *parent, char const *name, unsigned long *result) return 0; } +int +json_get_bigint(json_t *parent, char const *name, INTEGER_t *result) +{ + char const *str; + int error; + + error = json_get_str(parent, name, &str); + if (error) + return error; + + error = asn_str2INTEGER(str, result); + if (error) + pr_op_err("Tag '%s' (%s) cannot be parsed into an integer: %s", + name, str, strerror(error)); + + return error; +} + int json_get_ts(json_t *parent, char const *name, time_t *result) { @@ -188,6 +206,22 @@ json_add_ulong(json_t *parent, char const *name, unsigned long value) return 0; } +int +json_add_bigint(json_t *parent, char const *name, INTEGER_t *value) +{ + char *str; + int error; + + str = asn_INTEGER2str(value); + if (!str) + return pr_op_err("Cannot convert %s to string.", name); + + error = json_add_str(parent, name, str); + + free(str); + return error; +} + int json_add_str(json_t *parent, char const *name, char const *value) { diff --git a/src/json_util.h b/src/json_util.h index 9ed85cdb..6dfb7566 100644 --- a/src/json_util.h +++ b/src/json_util.h @@ -16,6 +16,8 @@ #include #include +#include "asn1/asn1c/INTEGER.h" + /* * Contract of get functions: * @@ -27,6 +29,7 @@ int json_get_int(json_t *, char const *, int *); int json_get_u32(json_t *, char const *, uint32_t *); int json_get_ulong(json_t *, char const *, unsigned long *); +int json_get_bigint(json_t *, char const *, INTEGER_t *); int json_get_ts(json_t *, char const *, time_t *); int json_get_str(json_t *, char const *, char const **); int json_get_array(json_t *, char const *, json_t **); @@ -36,6 +39,7 @@ bool json_valid_members_count(json_t *, size_t); int json_add_int(json_t *, char const *, int); int json_add_ulong(json_t *, char const *, unsigned long); +int json_add_bigint(json_t *, char const *, INTEGER_t *); int json_add_str(json_t *, char const *, char const *); int json_add_ts(json_t *, char const *, time_t); diff --git a/src/object/manifest.c b/src/object/manifest.c index 361142e0..09ad6834 100644 --- a/src/object/manifest.c +++ b/src/object/manifest.c @@ -124,6 +124,8 @@ check_more_recent(struct cache_cage *cage, struct mft_meta *current) if (!prev) return 0; + if (prev->num.size && INTEGER_cmp(&prev->num, ¤t->num) > 0) + return pr_val_err("The fallback manifest has a higher manifestNumber than the downloaded one."); if (prev->update && difftime(prev->update, current->update) > 0) return pr_val_err("The fallback manifest is newer than the downloaded one."); @@ -176,6 +178,7 @@ validate_manifest(struct Manifest *mft, struct cache_cage *cage, */ if (mft->manifestNumber.size > 20) return pr_val_err("Manifest number is larger than 20 octets"); + INTEGER_move(&meta->num, &mft->manifestNumber); /* rfc6486#section-4.4.3 */ error = validate_dates(&mft->thisUpdate, &mft->nextUpdate, meta); diff --git a/src/rrdp.c b/src/rrdp.c index d3126256..4c38952f 100644 --- a/src/rrdp.c +++ b/src/rrdp.c @@ -1395,53 +1395,28 @@ fail: json_decref(json); return NULL; } -static char -hash_c2b(char chara) -{ - if ('a' <= chara && chara <= 'f') - return chara - 'a' + 10; - if ('A' <= chara && chara <= 'F') - return chara - 'A' + 10; - if ('0' <= chara && chara <= '9') - return chara - '0'; - return -1; -} - static int -json2dh(json_t *json, struct rrdp_hash **result) +json2dh(json_t *json, struct rrdp_hash **dh) { - char const *src; - size_t srclen; - struct rrdp_hash *dst; - char digit; - size_t i; + char const *str; + struct rrdp_hash *hash; - src = json_string_value(json); - if (src == NULL) + str = json_string_value(json); + if (str == NULL) return pr_op_err("Hash is not a string."); - srclen = strlen(src); - if (srclen != 2 * RRDP_HASH_LEN) - return pr_op_err("Hash is not %d characters long.", 2 * RRDP_HASH_LEN); + if (strlen(str) != 2 * RRDP_HASH_LEN) + return pr_op_err("Hash is not %d characters long.", + 2 * RRDP_HASH_LEN); - dst = pmalloc(sizeof(struct rrdp_hash)); - for (i = 0; i < RRDP_HASH_LEN; i++) { - digit = hash_c2b(src[2 * i]); - if (digit == -1) - goto bad_char; - dst->bytes[i] = digit << 4; - digit = hash_c2b(src[2 * i + 1]); - if (digit == -1) - goto bad_char; - dst->bytes[i] |= digit; + hash = pmalloc(sizeof(struct rrdp_hash)); + if (str2hex(str, hash->bytes) != 0) { + free(hash); + return pr_op_err("Malformed hash: %s", str); } - *result = dst; + *dh = hash; return 0; - -bad_char: - free(dst); - return pr_op_err("Invalid characters in hash: %c%c", src[2 * i], src[2 * i] + 1); } static void diff --git a/src/types/rpp.c b/src/types/rpp.c index ed611662..8f01b378 100644 --- a/src/types/rpp.c +++ b/src/types/rpp.c @@ -18,4 +18,6 @@ rpp_cleanup(struct rpp *rpp) X509_CRL_free(rpp->crl.obj); rpp->crl.obj = NULL; } + + mftm_cleanup(&rpp->mft); } diff --git a/src/types/rpp.h b/src/types/rpp.h index 97d324b4..1f40e5b3 100644 --- a/src/types/rpp.h +++ b/src/types/rpp.h @@ -4,9 +4,11 @@ #include #include +#include "asn1/asn1c/INTEGER.h" #include "types/map.h" struct mft_meta { + INTEGER_t num; /* Manifest's manifestNumber */ time_t update; /* Manifest's thisUpdate */ }; @@ -23,6 +25,7 @@ struct rpp { struct mft_meta mft; }; +#define mftm_cleanup(m) INTEGER_cleanup(&(m)->num); void rpp_cleanup(struct rpp *); #endif /* SRC_RPP_H_ */ diff --git a/test/Makefile.am b/test/Makefile.am index ca550ac3..be654b4d 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -30,6 +30,10 @@ check_PROGRAMS = address.test address_test_SOURCES = types/address_test.c address_test_LDADD = ${CHECK_LIBS} +check_PROGRAMS += asn1_int.test +asn1_int_test_SOURCES = asn1/asn1c/INTEGER_t_test.c +asn1_int_test_LDADD = ${CHECK_LIBS} + check_PROGRAMS += base64.test base64_test_SOURCES = base64_test.c base64_test_LDADD = ${CHECK_LIBS} diff --git a/test/asn1/asn1c/INTEGER_t_test.c b/test/asn1/asn1c/INTEGER_t_test.c new file mode 100644 index 00000000..39d4fc50 --- /dev/null +++ b/test/asn1/asn1c/INTEGER_t_test.c @@ -0,0 +1,104 @@ +#include + +#include "alloc.c" +#include "common.c" +#include "asn1/asn1c/asn_codecs_prim.c" +#include "asn1/asn1c/INTEGER.c" +#include "mock.c" + +__MOCK_ABORT(asn__format_to_callback, ssize_t, 0, + int (*cb)(const void *, size_t, void *key), void *key, const char *fmt, ...) +MOCK_ABORT_INT(asn_generic_no_constraint, + const asn_TYPE_descriptor_t *td, const void *ptr, + asn_app_constraint_failed_f *cb, void *key) +static asn_dec_rval_t dummy; +__MOCK_ABORT(ber_check_tags, asn_dec_rval_t, dummy, + const asn_codec_ctx_t *opt_codec_ctx, const asn_TYPE_descriptor_t *td, + asn_struct_ctx_t *opt_ctx, const void *ptr, size_t size, int tag_mode, + int last_tag_form, ber_tlv_len_t *last_length, int *opt_tlv_form) +__MOCK_ABORT(der_write_tags, ssize_t, 0, + const asn_TYPE_descriptor_t *sd, size_t struct_length, int tag_mode, + int last_tag_form, ber_tlv_tag_t tag, asn_app_consume_bytes_f *cb, + void *app_key) +__MOCK_ABORT(json_int_new, json_t *, NULL, json_int_t value) + +START_TEST(test_serde) +{ + INTEGER_t bi; + char *str; + + ck_assert_int_eq(0, asn_str2INTEGER(NULL, &bi)); + ck_assert_ptr_eq(NULL, bi.buf); + ck_assert_int_eq(0, bi.size); + ck_assert_ptr_eq(NULL, asn_INTEGER2str(NULL)); + + ck_assert_int_eq(EINVAL, asn_str2INTEGER("", &bi)); + ck_assert_int_eq(EINVAL, asn_str2INTEGER("a", &bi)); + ck_assert_int_eq(EINVAL, asn_str2INTEGER("abc", &bi)); + + ck_assert_int_eq(0, asn_str2INTEGER("ab", &bi)); + ck_assert_int_eq(1, bi.size); + ck_assert_int_eq(0xAB, bi.buf[0]); + str = asn_INTEGER2str(&bi); + ck_assert_str_eq("AB", str); + INTEGER_cleanup(&bi); + free(str); + + ck_assert_int_eq(0, asn_str2INTEGER("abc5", &bi)); + ck_assert_int_eq(2, bi.size); + ck_assert_int_eq(0xAB, bi.buf[0]); + ck_assert_int_eq(0xC5, bi.buf[1]); + str = asn_INTEGER2str(&bi); + ck_assert_str_eq("ABC5", str); + INTEGER_cleanup(&bi); + free(str); + + ck_assert_int_eq(EINVAL, asn_str2INTEGER("abc59", &bi)); + + ck_assert_int_eq(0, asn_str2INTEGER("abcdef0123456789ABCDEFabcdef012345678901", &bi)); + ck_assert_int_eq(20, bi.size); + ck_assert_int_eq(0xAB, bi.buf[0]); + ck_assert_int_eq(0xCD, bi.buf[1]); + ck_assert_int_eq(0x89, bi.buf[18]); + ck_assert_int_eq(0x01, bi.buf[19]); + str = asn_INTEGER2str(&bi); + ck_assert_str_eq("ABCDEF0123456789ABCDEFABCDEF012345678901", str); + INTEGER_cleanup(&bi); + free(str); + + ck_assert_int_eq(EOVERFLOW, asn_str2INTEGER("abcdef0123456789ABCDEFabcdef0123456789012", &bi)); + ck_assert_int_eq(EOVERFLOW, asn_str2INTEGER("abcdef0123456789ABCDEFabcdef0123456789013", &bi)); + ck_assert_int_eq(EINVAL, asn_str2INTEGER("z", &bi)); + ck_assert_int_eq(EINVAL, asn_str2INTEGER("abmd", &bi)); +} +END_TEST + +static Suite *create_suite(void) +{ + Suite *suite; + TCase *serde; + + serde = tcase_create("serde"); + tcase_add_test(serde, test_serde); + + suite = suite_create("INTEGER_t"); + suite_add_tcase(suite, serde); + + return suite; +} + +int main(void) +{ + Suite *suite; + SRunner *runner; + int tests_failed; + + suite = create_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/cache_test.c b/test/cache_test.c index 89e699a2..db5f4f03 100644 --- a/test/cache_test.c +++ b/test/cache_test.c @@ -51,6 +51,9 @@ rsync_download(char const *url, char const *path) MOCK_VOID(__delete_node_cb, struct cache_node const *node) MOCK_VOID(task_wakeup_busy, void) +__MOCK_ABORT(asn_INTEGER2str, char *, NULL, INTEGER_t const *bi) +MOCK_VOID(INTEGER_move, INTEGER_t *to, INTEGER_t *from) +MOCK_VOID(INTEGER_cleanup, INTEGER_t *i) /* Helpers */