]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Always pick most recent known manifest
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 3 Dec 2024 19:58:10 +0000 (16:58 -0300)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 3 Dec 2024 19:58:10 +0000 (16:58 -0300)
Strict version.

14 files changed:
src/asn1/asn1c/INTEGER.c
src/asn1/asn1c/INTEGER.h
src/cache.c
src/common.c
src/common.h
src/json_util.c
src/json_util.h
src/object/manifest.c
src/rrdp.c
src/types/rpp.c
src/types/rpp.h
test/Makefile.am
test/asn1/asn1c/INTEGER_t_test.c [new file with mode: 0644]
test/cache_test.c

index 30a5d0410fa8dc9067e5cdbd0d0e2c7de9c8a6f4..7e9539750fde68bd3cc8941a432104eae9880014 100644 (file)
@@ -8,6 +8,8 @@
 #include <assert.h>
 #include <errno.h>
 
+#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);
+}
index b09933a776a73aee54668b2a06cb2d4261b71e36..9178421281df0da811a25d8095b41490c4d52728 100644 (file)
@@ -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_ */
index f3b9e451d67d12ff3b9162040b7eabbcff2bfaa6..6cf6f9bac44716e7d1f95a8146cb44ce64d6200a 100644 (file)
@@ -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);
        }
 }
index bdcbe978b6171cbfe9581d8fc3b084cab9404bf4..2d743e3640961effb7f811ecc0df80507e03d702 100644 (file)
@@ -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;
+}
index 63c37b615f917eaffc547b7cf97d7aa93ca7848c..ae32d40706aae64c68f6d0ac22973bfc0461910d 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <pthread.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
@@ -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_ */
index 5976bf967601dff969a1253a743cd20b6efc2567..0f2a3d705dccd771867e0a38f9c87003e275f6e8 100644 (file)
@@ -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)
 {
index 9ed85cdb5faa7903748eab715a318d59c40bf55c..6dfb7566148509437749ea4cee7d7d21eb426ef0 100644 (file)
@@ -16,6 +16,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#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);
 
index 361142e06bfbc2022c650156d74bcf71fed738c3..09ad6834b813d6ba22c75ea376b10f45673f4ecf 100644 (file)
@@ -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, &current->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);
index d312625687541295e74ea21491f04662a0ff7b2f..4c38952f148129693a9fc73e538d25c2d3aeee5c 100644 (file)
@@ -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
index ed61166209572c755096ec1b51ee709fcedf54fb..8f01b378239fcc3ae56993351fea09c3d2eb83fe 100644 (file)
@@ -18,4 +18,6 @@ rpp_cleanup(struct rpp *rpp)
                X509_CRL_free(rpp->crl.obj);
                rpp->crl.obj = NULL;
        }
+
+       mftm_cleanup(&rpp->mft);
 }
index 97d324b4114cd7a3943b33d6302855ac8eb3023f..1f40e5b3775aa0d8494d6be17d80d01770f76c65 100644 (file)
@@ -4,9 +4,11 @@
 #include <openssl/x509.h>
 #include <time.h>
 
+#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_ */
index ca550ac3d94051b53fa66bcb3969243e92e14185..be654b4d164cffcce73dfa8a3a3feacf76a2803f 100644 (file)
@@ -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 (file)
index 0000000..39d4fc5
--- /dev/null
@@ -0,0 +1,104 @@
+#include <check.h>
+
+#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;
+}
index 89e699a2522fcc411ef2e55028c2630947a89dfb..db5f4f0331ac4b97dc45f3bc707409c9f3aee097 100644 (file)
@@ -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 */