]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Separate rsync fallbacks from RRDP fallbacks
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 13 Nov 2024 22:25:19 +0000 (16:25 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 13 Nov 2024 22:25:19 +0000 (16:25 -0600)
Both used to be indexed by caRepository, inducing possible collision.
RRDP fallbacks are now indexed by rkiNotify+caRepository, ensuring
they're caged separately.

src/cache.c
src/cache.h
src/object/certificate.c
src/rrdp.c
src/rrdp.h
test/cache_test.c
test/rrdp_update_test.c

index 509aff37af4602212e627923115a8f656fad3736..5ee086b3ad4b3eb695d153b11f219a618291fbaa 100644 (file)
@@ -79,12 +79,17 @@ struct cache_table {
 
 static struct rpki_cache {
        /* Latest view of the remote rsync modules */
+       /* rsync modules (repositories); indexed by plain rsync URL */
        struct cache_table rsync;
        /* Latest view of the remote HTTPS TAs */
+       /* HTTPS files; indexed by plain HTTPS URL */
        struct cache_table https;
        /* Latest view of the remote RRDP cages */
+       /* RRDP modules (repositories); indexed by rpkiNotify */
        struct cache_table rrdp;
-       /* Committed RPPs and TAs (offline fallback hard links) */
+
+       /* Committed (offline fallback hard links) RPPs and TAs */
+       /* RPPs indexed by [rpkiNotif] + caRepo; TAs indexed by plain URL. */
        struct cache_table fallback;
 } cache;
 
@@ -94,9 +99,11 @@ static volatile sig_atomic_t lockfile_owned;
 struct cache_cage {
        struct cache_node const *refresh;
        struct cache_node const *fallback;
+       char const *rpkiNotify;
 };
 
 struct cache_commit {
+       char *rpkiNotify;
        char *caRepository;
        struct cache_mapping *files;
        size_t nfiles;
@@ -611,8 +618,7 @@ dl_rrdp(struct cache_node *notif)
 
        mtim = time_nonfatal();
 
-       error = rrdp_update(&notif->map, notif->mtim, &changed, &cache.rrdp.seq,
-           &notif->rrdp);
+       error = rrdp_update(&notif->map, notif->mtim, &changed, &notif->rrdp);
        if (error)
                return error;
 
@@ -744,16 +750,55 @@ do_refresh(struct cache_table *tbl, char const *uri, struct cache_node **result)
        return 0;
 }
 
+static char *
+get_rrdp_fallback_key(char const *context, char const *caRepository)
+{
+       char *key;
+       size_t keylen;
+       int written;
+
+       keylen = strlen(context) + strlen(caRepository) + 2;
+       key = pmalloc(keylen);
+
+       written = snprintf(key, keylen, "%s\t%s", context, caRepository);
+       if (written != keylen - 1)
+               pr_crit("find_rrdp_fallback_node: %zu %d %s %s",
+                   keylen, written, context, caRepository);
+
+       return key;
+}
+
 static struct cache_node *
-get_fallback(char const *caRepository)
+find_rrdp_fallback_node(struct sia_uris *sias)
 {
-       struct cache_node *node;
+       char *key;
+       struct cache_node *result;
+
+       if (!sias->rpkiNotify || !sias->caRepository)
+               return NULL;
 
-       pr_val_debug("Retrieving %s fallback...", caRepository);
-       node = find_node(&cache.fallback, caRepository, strlen(caRepository));
-       pr_val_debug(node ? "Fallback found." : "Fallback unavailable.");
+       key = get_rrdp_fallback_key(sias->rpkiNotify, sias->caRepository);
+       result = find_node(&cache.fallback, key, strlen(key));
+       free(key);
 
-       return node;
+       return result;
+}
+
+static struct cache_node *
+get_fallback(struct sia_uris *sias)
+{
+       struct cache_node *rrdp;
+       struct cache_node *rsync;
+
+       rrdp = find_rrdp_fallback_node(sias);
+       rsync = find_node(&cache.fallback, sias->caRepository,
+           strlen(sias->caRepository));
+
+       if (rrdp == NULL)
+               return rsync;
+       if (rsync == NULL)
+               return rrdp;
+       return (difftime(rsync->mtim, rrdp->mtim) > 0) ? rsync : rrdp;
 }
 
 /* Do not free nor modify the result. */
@@ -773,7 +818,10 @@ cache_refresh_by_url(char const *url)
        return node ? node->map.path : NULL;
 }
 
-/* Do not free nor modify the result. */
+/*
+ * HTTPS (TAs) and rsync only; don't use this for RRDP.
+ * Do not free nor modify the result.
+ */
 char *
 cache_get_fallback(char const *url)
 {
@@ -807,6 +855,7 @@ cache_refresh_by_sias(struct sia_uris *sias, struct cache_cage **result)
 {
        struct cache_node *node;
        struct cache_cage *cage;
+       char const *rpkiNotify;
 
        // XXX Make sure somewhere validates rpkiManifest matches caRepository.
        // XXX review result signs
@@ -816,6 +865,7 @@ cache_refresh_by_sias(struct sia_uris *sias, struct cache_cage **result)
        if (sias->rpkiNotify) {
                switch (do_refresh(&cache.rrdp, sias->rpkiNotify, &node)) {
                case 0:
+                       rpkiNotify = sias->rpkiNotify;
                        goto refresh_success;
                case EBUSY:
                        return EBUSY;
@@ -825,13 +875,14 @@ cache_refresh_by_sias(struct sia_uris *sias, struct cache_cage **result)
        /* Try rsync + optional fallback */
        switch (do_refresh(&cache.rsync, sias->caRepository, &node)) {
        case 0:
+               rpkiNotify = NULL;
                goto refresh_success;
        case EBUSY:
                return EBUSY;
        }
 
        /* Try fallback only */
-       node = get_fallback(sias->caRepository); /* XXX (test) does this catch notifies? */
+       node = get_fallback(sias);
        if (!node)
                return EINVAL; /* Nothing to work with */
 
@@ -842,8 +893,9 @@ cache_refresh_by_sias(struct sia_uris *sias, struct cache_cage **result)
 
 refresh_success:
        *result = cage = pmalloc(sizeof(struct cache_cage));
+       cage->rpkiNotify = rpkiNotify;
        cage->refresh = node;
-       cage->fallback = get_fallback(sias->caRepository);
+       cage->fallback = get_fallback(sias);
        return 0;
 }
 
@@ -907,12 +959,13 @@ cage_disable_refresh(struct cache_cage *cage)
  * modified nor deleted until the cache cleanup.
  */
 void
-cache_commit_rpp(char const *caRepository, struct rpp *rpp)
+cache_commit_rpp(char const *rpkiNotify, char const *caRepository,
+    struct rpp *rpp)
 {
        struct cache_commit *commit;
 
        commit = pmalloc(sizeof(struct cache_commit));
-       // XXX missing context
+       commit->rpkiNotify = rpkiNotify ? pstrdup(rpkiNotify) : NULL;
        commit->caRepository = pstrdup(caRepository);
        commit->files = rpp->files;
        commit->nfiles = rpp->nfiles;
@@ -931,7 +984,7 @@ cache_commit_file(struct cache_mapping *map)
        struct cache_commit *commit;
 
        commit = pmalloc(sizeof(struct cache_commit));
-       // XXX missing context
+       commit->rpkiNotify = NULL;
        commit->caRepository = NULL;
        commit->files = pmalloc(sizeof(*map));
        commit->files[0].url = pstrdup(map->url);
@@ -943,6 +996,12 @@ cache_commit_file(struct cache_mapping *map)
        mutex_unlock(&commits_lock);
 }
 
+char const *
+cage_rpkiNotify(struct cache_cage *cage)
+{
+       return cage->rpkiNotify;
+}
+
 static void
 cachent_print(struct cache_node *node)
 {
@@ -1170,15 +1229,27 @@ commit_fallbacks(void)
 {
        struct cache_commit *commit;
        struct cache_node *fb, *tmp;
+       time_t now;
        array_index i;
        int error;
 
+       now = time_fatal();
+
        while (!STAILQ_EMPTY(&commits)) {
                commit = STAILQ_FIRST(&commits);
                STAILQ_REMOVE_HEAD(&commits, lh);
 
                if (commit->caRepository) {
-                       fb = provide_node(&cache.fallback, commit->caRepository);
+                       if (commit->rpkiNotify) {
+                               char *key;
+                               key = get_rrdp_fallback_key(commit->rpkiNotify,
+                                   commit->caRepository);
+                               fb = provide_node(&cache.fallback, key);
+                               free(key);
+                       } else {
+                               fb = provide_node(&cache.fallback,
+                                   commit->caRepository);
+                       }
 
                        if (file_mkdir(fb->map.path, true) != 0)
                                goto skip;
@@ -1201,7 +1272,9 @@ commit_fallbacks(void)
                }
 
 freshen:       fb->state = DLS_FRESH;
-skip:          free(commit->caRepository);
+               fb->mtim = now;
+skip:          free(commit->rpkiNotify);
+               free(commit->caRepository);
                for (i = 0; i < commit->nfiles; i++) {
                        free(commit->files[i].url);
                        free(commit->files[i].path);
index c791346a7e53e58839c51e95cb1764761b4b6d63..552b95b5b0cc68cf6c3d21eb4f6879bf968effe6 100644 (file)
@@ -44,9 +44,11 @@ struct cache_cage;
 int cache_refresh_by_sias(struct sia_uris *, struct cache_cage **);
 char const *cage_map_file(struct cache_cage *, char const *);
 bool cage_disable_refresh(struct cache_cage *);
-void cache_commit_rpp(char const *, struct rpp *);
+void cache_commit_rpp(char const *, char const *, struct rpp *);
 void cache_commit_file(struct cache_mapping *);
 
+char const *cage_rpkiNotify(struct cache_cage *);
+
 void cache_print(void);                /* Dump cache in stdout */
 
 #endif /* SRC_CACHE_LOCAL_CACHE_H_ */
index 99fd12852e90e7100ebae98a1c17803d715080f4..d1d28661e678e575e78e32951247b3861879a8c6 100644 (file)
@@ -1950,7 +1950,7 @@ retry:    mft = cage_map_file(cage, ca->sias.rpkiManifest);
 
        if (queued > 0)
                task_wakeup();
-       cache_commit_rpp(ca->sias.caRepository, &ca->rpp);
+       cache_commit_rpp(cage_rpkiNotify(cage), ca->sias.caRepository, &ca->rpp);
 
 end:   free(cage);
        return error;
index fee1de1b234e7b64e3502b7b7caceabb37ef1737..d312625687541295e74ea21491f04662a0ff7b2f 100644 (file)
@@ -1174,7 +1174,7 @@ dl_notif(struct cache_mapping const *map,  time_t mtim, bool *changed,
  */
 int
 rrdp_update(struct cache_mapping const *notif, time_t mtim, bool *changed,
-    struct cache_sequence *rrdp_seq, struct rrdp_state **state)
+    struct rrdp_state **state)
 {
        struct rrdp_state *old;
        struct update_notification new;
@@ -1194,20 +1194,14 @@ rrdp_update(struct cache_mapping const *notif, time_t mtim, bool *changed,
            new.session.serial.str);
 
        if ((*state) == NULL) {
-               char *cage;
-
                pr_val_debug("This is a new Notification.");
 
-               cage = cseq_next(rrdp_seq);
-               if (!cage)
-                       goto clean_notif;
-
                old = pzalloc(sizeof(struct rrdp_state));
                /* session postponed! */
-               cseq_init(&old->seq, cage, true);
+               cseq_init(&old->seq, pstrdup(notif->path), true);
                STAILQ_INIT(&old->delta_hashes);
 
-               error = file_mkdir(cage, false);
+               error = file_mkdir(notif->path, false);
                if (error) {
                        rrdp_state_free(old);
                        goto clean_notif;
index 4d7126fcccf711cf1862246b8da2aadb85aa2a85..311b336b79b80961e8f960a70cb72c2dbf54e06b 100644 (file)
@@ -11,7 +11,7 @@
 struct rrdp_state;
 
 int rrdp_update(struct cache_mapping const *, time_t, bool *,
-    struct cache_sequence *, struct rrdp_state **);
+    struct rrdp_state **);
 char const *rrdp_file(struct rrdp_state const *, char const *);
 
 char const *rrdp_create_fallback(char *, struct rrdp_state **, char const *);
index 6eef59e47cfe356ea2e3043792f9ec2194a44fcf..2fc455d4432f2b1c1c01915060ec2d558af702d4 100644 (file)
@@ -38,9 +38,12 @@ rsync_download(char const *url, char const *path)
 {
        rsync_counter++;
 
-       if (dl_error)
+       if (dl_error) {
+               printf("Simulating failed rsync.\n");
                return dl_error;
+       }
 
+       printf("Simulating rsync: %s -> %s\n", url, path);
        ck_assert_int_eq(0, mkdir(path, CACHE_FILEMODE));
        touch_file(path);
 
@@ -57,8 +60,8 @@ setup_test(void)
 {
        dl_error = 0;
        init_tables();
-       ck_assert_int_eq(0, system("rm -rf rsync/ https/ rrdp/ fallback/"));
-       ck_assert_int_eq(0, system("mkdir rsync/ https/ rrdp/ fallback/"));
+       ck_assert_int_eq(0, system("rm -rf rsync/ https/ rrdp/ fallback/ tmp/"));
+       ck_assert_int_eq(0, system("mkdir rsync/ https/ rrdp/ fallback/ tmp/"));
 }
 
 static struct cache_cage *
@@ -134,7 +137,8 @@ print_tree(void)
 }
 
 static void
-queue_commit(char const *caRepository, char const *path1, char const *path2)
+queue_commit(char const *rpkiNotify, char const *caRepository,
+    char const *path1, char const *path2)
 {
        struct rpp rpp = { 0 };
 
@@ -145,7 +149,7 @@ queue_commit(char const *caRepository, char const *path1, char const *path2)
        rpp.files[1].url = path_join(caRepository, "cert.cer");
        rpp.files[1].path = pstrdup(path2);
 
-       cache_commit_rpp(caRepository, &rpp);
+       cache_commit_rpp(rpkiNotify, caRepository, &rpp);
 }
 
 /* Only validates the first character of the file. */
@@ -464,9 +468,9 @@ START_TEST(test_rsync_commit)
        }
 
        /* Commit 3: Empty -> Populated */
-       queue_commit("rsync://domain/mod/rpp0", "rsync/0/0", "rsync/0/1");
-       queue_commit("rsync://domain/mod/rpp2", "rsync/2/0", "rsync/2/1");
-       queue_commit("rsync://domain/mod/rpp3", "rsync/3/0", "rsync/3/2");
+       queue_commit(NULL, "rsync://domain/mod/rpp0", "rsync/0/0", "rsync/0/1");
+       queue_commit(NULL, "rsync://domain/mod/rpp2", "rsync/2/0", "rsync/2/1");
+       queue_commit(NULL, "rsync://domain/mod/rpp3", "rsync/3/0", "rsync/3/2");
        commit_fallbacks();
        ck_filesystem("fallback",
            /* RPP0 */ "fallback/0/0", "A", "fallback/0/1", "B",
@@ -478,9 +482,9 @@ START_TEST(test_rsync_commit)
 
        /* Commit 4: Populated -> Populated */
        /* XXX check the refresh does, in fact, only return fallbacks when the RPP doesn't change */
-       queue_commit("rsync://domain/mod/rpp0", "fallback/0/0", "fallback/0/1");
-       queue_commit("rsync://domain/mod/rpp1", "rsync/1/0", "rsync/1/1");
-       queue_commit("rsync://domain/mod/rpp3", "fallback/2/0", "rsync/3/1");
+       queue_commit(NULL, "rsync://domain/mod/rpp0", "fallback/0/0", "fallback/0/1");
+       queue_commit(NULL, "rsync://domain/mod/rpp1", "rsync/1/0", "rsync/1/1");
+       queue_commit(NULL, "rsync://domain/mod/rpp3", "fallback/2/0", "rsync/3/1");
        commit_fallbacks();
 
        ck_filesystem("fallback",
@@ -612,6 +616,7 @@ END_TEST
 /* See comments at test_rsync_commit(). */
 START_TEST(test_rrdp_commit)
 {
+       char const *notif = "https://domain/rpki/notif.xml";
        unsigned int i;
 
        setup_test();
@@ -637,9 +642,9 @@ START_TEST(test_rrdp_commit)
        }
 
        /* 3 */
-       queue_commit("rsync://domain/mod/rpp0", "rrdp/0/0", "rrdp/0/1");
-       queue_commit("rsync://domain/mod/rpp2", "rrdp/2/0", "rrdp/2/1");
-       queue_commit("rsync://domain/mod/rpp3", "rrdp/3/0", "rrdp/3/2");
+       queue_commit(notif, "rsync://domain/mod/rpp0", "rrdp/0/0", "rrdp/0/1");
+       queue_commit(notif, "rsync://domain/mod/rpp2", "rrdp/2/0", "rrdp/2/1");
+       queue_commit(notif, "rsync://domain/mod/rpp3", "rrdp/3/0", "rrdp/3/2");
        commit_fallbacks();
        ck_filesystem("fallback",
            "fallback/0/0", "A", "fallback/0/1", "B",
@@ -650,9 +655,9 @@ START_TEST(test_rrdp_commit)
        new_iteration(false);
 
        /* 4 */
-       queue_commit("rsync://domain/mod/rpp0", "fallback/0/0", "fallback/0/1");
-       queue_commit("rsync://domain/mod/rpp1", "rrdp/1/0", "rrdp/1/1");
-       queue_commit("rsync://domain/mod/rpp3", "fallback/2/0", "rrdp/3/1");
+       queue_commit(notif, "rsync://domain/mod/rpp0", "fallback/0/0", "fallback/0/1");
+       queue_commit(notif, "rsync://domain/mod/rpp1", "rrdp/1/0", "rrdp/1/1");
+       queue_commit(notif, "rsync://domain/mod/rpp3", "fallback/2/0", "rrdp/3/1");
        commit_fallbacks();
        ck_filesystem("fallback",
            "fallback/0/0", "A", "fallback/0/1", "B",
@@ -670,12 +675,90 @@ START_TEST(test_rrdp_commit)
 }
 END_TEST
 
+START_TEST(test_context)
+{
+       char *RPKI_NOTIFY =     "https://a.b.c/notif.xml";
+       char *CA_REPOSITORY =   "rsync://x.y.z/mod5/rpp3";
+       char *FILE_URL =        "rsync://x.y.z/mod5/rpp3/a.cer";
+       char *FILE_RRDP_PATH =  "rrdp/0/0";
+       char *FILE_RSYNC_PATH = "rsync/0/rpp3/a.cer";
+
+       struct sia_uris sias = { 0 };
+       struct cache_cage *cage;
+       struct rpp rpp = { 0 };
+
+       ck_assert_int_eq(0, hash_setup());
+       ck_assert_int_eq(0, relax_ng_init());
+       setup_test();
+
+       dls[0] = NHDR("3")
+               NSS("https://a.b.c/3/snapshot.xml",
+                   "25b49ae65eeeda44222d599959086911c65ed4277021cdec456d80a6604b83c9")
+               NTAIL;
+       dls[1] = SHDR("3") PBLSH("rsync://x.y.z/mod5/rpp3/a.cer", "Rm9ydAo=") STAIL;
+       dls[2] = NULL;
+
+       /* 1. 1st CA succeeds on RRDP */
+       sias.rpkiNotify = RPKI_NOTIFY;
+       sias.caRepository = CA_REPOSITORY;
+       ck_assert_int_eq(0, cache_refresh_by_sias(&sias, &cage));
+       ck_assert_str_eq(RPKI_NOTIFY, cage->rpkiNotify);
+       ck_assert_str_eq(FILE_RRDP_PATH, cage_map_file(cage, FILE_URL));
+       ck_assert_int_eq(false, cage_disable_refresh(cage));
+       ck_assert_ptr_eq(NULL, cage_map_file(cage, FILE_URL));
+
+       /*
+        * 2. 2nd CA points to the same caRepository,
+        *    but does not provide RRDP as an option.
+        */
+       sias.rpkiNotify = NULL;
+       ck_assert_int_eq(0, cache_refresh_by_sias(&sias, &cage));
+       ck_assert_ptr_eq(NULL, cage->rpkiNotify);
+       ck_assert_str_eq(FILE_RSYNC_PATH, cage_map_file(cage, FILE_URL));
+       ck_assert_int_eq(false, cage_disable_refresh(cage));
+       ck_assert_ptr_eq(NULL, cage_map_file(cage, FILE_URL));
+
+       /* 3. Commit */
+       rpp.nfiles = 1;
+       rpp.files = pzalloc(sizeof(struct cache_mapping));
+       rpp.files->url = pstrdup(FILE_URL);
+       rpp.files->path = pstrdup(FILE_RRDP_PATH);
+       cache_commit_rpp(RPKI_NOTIFY, CA_REPOSITORY, &rpp);
+
+       rpp.nfiles = 1;
+       rpp.files = pzalloc(sizeof(struct cache_mapping));
+       rpp.files->url = pstrdup(FILE_URL);
+       rpp.files->path = pstrdup(FILE_RSYNC_PATH);
+       cache_commit_rpp(NULL, CA_REPOSITORY, &rpp);
+
+       commit_fallbacks();
+
+       /* 4. Redo both CAs, check the fallbacks too */
+       ck_assert_int_eq(0, cache_refresh_by_sias(&sias, &cage));
+       ck_assert_ptr_eq(NULL, cage->rpkiNotify);
+       ck_assert_str_eq(FILE_RSYNC_PATH, cage_map_file(cage, FILE_URL));
+       ck_assert_int_eq(true, cage_disable_refresh(cage));
+       ck_assert_str_eq("fallback/1/0", cage_map_file(cage, FILE_URL));
+
+       sias.rpkiNotify = RPKI_NOTIFY;
+       ck_assert_int_eq(0, cache_refresh_by_sias(&sias, &cage));
+       ck_assert_str_eq(RPKI_NOTIFY, cage->rpkiNotify);
+       ck_assert_str_eq(FILE_RRDP_PATH, cage_map_file(cage, FILE_URL));
+       ck_assert_int_eq(true, cage_disable_refresh(cage));
+       ck_assert_str_eq("fallback/0/0", cage_map_file(cage, FILE_URL));
+
+       cleanup_test();
+       relax_ng_cleanup();
+       hash_teardown();
+}
+END_TEST
+
 /* Boilerplate */
 
 static Suite *create_suite(void)
 {
        Suite *suite;
-       TCase *rsync, *https, *rrdp;
+       TCase *rsync, *https, *rrdp, *multi;
 
        rsync = tcase_create("rsync");
        tcase_add_test(rsync, test_cache_download_rsync);
@@ -690,10 +773,14 @@ static Suite *create_suite(void)
        rrdp = tcase_create("rrdp");
        tcase_add_test(rrdp, test_rrdp_commit);
 
+       multi = tcase_create("multi-protocol");
+       tcase_add_test(multi, test_context);
+
        suite = suite_create("local-cache");
        suite_add_tcase(suite, rsync);
        suite_add_tcase(suite, https);
        suite_add_tcase(suite, rrdp);
+       suite_add_tcase(suite, multi);
 
        return suite;
 }
index d4637712b8061ad6d48910fc545d1d9d4e1e6a66..5f3468959c6dcc13fe9f47509daa479deb2ca3e5 100644 (file)
@@ -21,8 +21,8 @@
 static void
 setup_test(void)
 {
-       ck_assert_int_eq(0, system("rm -rf tmp/"));
-       ck_assert_int_eq(0, system("mkdir -p tmp/https tmp/rrdp tmp/tmp"));
+       ck_assert_int_eq(0, system("rm -rf https/ rrdp/ tmp/"));
+       ck_assert_int_eq(0, system("mkdir https/ rrdp/ tmp/"));
        ck_assert_int_eq(0, hash_setup());
        ck_assert_int_eq(0, relax_ng_init());
 }
@@ -84,10 +84,10 @@ START_TEST(startup)
        setup_test();
 
        notif.url = "https://host/notification.xml";
-       notif.path = "tmp/https/0";
+       notif.path = "rrdp/0";
 
-       seq.prefix = "tmp/rrdp";
-       seq.next_id = 0;
+       seq.prefix = "rrdp";
+       seq.next_id = 1;
        seq.pathlen = strlen(seq.prefix);
        seq.free_prefix = false;
 
@@ -99,29 +99,30 @@ START_TEST(startup)
        dls[2] = NULL;
        https_counter = 0;
 
-       ck_assert_int_eq(0, rrdp_update(&notif, 0, &changed, &seq, &state));
+       ck_assert_int_eq(0, rrdp_update(&notif, 0, &changed, &state));
        ck_assert_uint_eq(2, https_counter);
        ck_assert_uint_eq(true, changed);
-       ck_file("tmp/rrdp/0/0"); /* "tmp/rrdp/<first-cage>/<c.cer>" */
+       ck_file("rrdp/0/0"); /* "rrdp/<first-cage>/<c.cer>" */
 
        maps[0].url = "rsync://a/b/c.cer";
-       maps[0].path = "tmp/rrdp/0/0";
+       maps[0].path = "rrdp/0/0";
        maps[1].url = NULL;
        ck_state(TEST_SESSION, "3", 1, maps, state);
 
        /* Attempt to update, server hasn't changed anything. */
        dls[1] = NULL; /* Snapshot should not redownload */
        https_counter = 0;
-       ck_assert_int_eq(0, rrdp_update(&notif, 0, &changed, &seq, &state));
+       ck_assert_int_eq(0, rrdp_update(&notif, 0, &changed, &state));
        ck_assert_uint_eq(1, https_counter);
        ck_assert_uint_eq(false, changed);
-       ck_file("tmp/rrdp/0/0");
+       ck_file("rrdp/0/0");
        ck_state(TEST_SESSION, "3", 1, maps, state);
 
        rrdp_state_free(state);
-       cleanup_test();
 
        // XXX Missing a looooooooooooooooooot of tests
+
+       cleanup_test();
 }
 END_TEST
 
@@ -145,6 +146,15 @@ int main(void)
        SRunner *runner;
        int tests_failed;
 
+       if (mkdir("tmp", CACHE_FILEMODE) < 0 && errno != EEXIST) {
+               fprintf(stderr, "mkdir('tmp/'): %s\n", strerror(errno));
+               return 1;
+       }
+       if (chdir("tmp") < 0) {
+               fprintf(stderr, "chdir('tmp/'): %s\n", strerror(errno));
+               return 1;
+       }
+
        suite = create_suite();
 
        runner = srunner_create(suite);