From 50b1f4b2741361a19411e0c434b7009d63ac84e9 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Mon, 21 Oct 2024 17:10:34 +0900 Subject: [PATCH] Impl. safe-link checking for v1 file format As mentioned in the previous commit, "safe links" is on by default, meaning that any symlinks pointing to outside of archived files (or invalid) will not be stored. To store such symlinks, "--no-safe-links" must be specified. This commit implements "safe links" for v1 of the file format. --- file_format.md | 3 +++ src/archiver.c | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/file_format.md b/file_format.md index 1834af2..f2e935b 100644 --- a/file_format.md +++ b/file_format.md @@ -133,6 +133,9 @@ Following the link-count bytes, the following bytes are added for each symlink: 2. The second byte. 1. The first bit is "other write permission". 2. The second bit is "other execute permission". + 3. If this bit is set, then this entry is marked invalid. The link name + will be preserved in this entry, but the following link target paths + will be set to zero-length and will not be stored. 2. 2 bytes 16-bit unsigned integer "link name" in big-endian. This does not include the NULL at the end of the string. Must not be zero. 3. X bytes of link-name (length defined by previous value). Is a NULL-terminated diff --git a/src/archiver.c b/src/archiver.c index e093070..555e802 100644 --- a/src/archiver.c +++ b/src/archiver.c @@ -2079,11 +2079,25 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state, abs_path); } } + + uint_fast8_t is_invalid = 0; + if (abs_path && (state->parsed->flags & 0x20) == 0 && !simple_archiver_hash_map_get(abs_filenames, abs_path, strlen(abs_path) + 1)) { - // Is not a filename being archived, set preference to absolute path. - buf[0] |= 1; + // Is not a filename being archived. + if ((state->parsed->flags & 0x80) == 0) { + // Not a "safe link", mark invalid and continue. + is_invalid = 1; + fprintf(stderr, + "WARNING: \"%s\" points to outside of archived files (or is " + "invalid) and \"--no-safe-links\" not specified, will not " + "store abs/rel-links to this entry!\n", + (const char *)node->data); + } else { + // Safe links disabled, set preference to absolute path. + buf[0] |= 1; + } } // Get symlink stats for permissions. @@ -2126,6 +2140,11 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state, buf[0] = 0xFE; buf[1] = 3; #endif + + if (is_invalid) { + buf[1] |= 4; + } + if (fwrite(buf, 1, 2, out_f) != 2) { return SDAS_FAILED_TO_WRITE; } @@ -2146,7 +2165,7 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state, return SDAS_FAILED_TO_WRITE; } - if (abs_path && (state->parsed->flags & 0x20) == 0) { + if (abs_path && (state->parsed->flags & 0x20) == 0 && !is_invalid) { len = strlen(abs_path); if (len >= 0xFFFF) { fprintf(stderr, @@ -2170,7 +2189,7 @@ int simple_archiver_write_v1(FILE *out_f, SDArchiverState *state, } } - if (rel_path) { + if (rel_path && !is_invalid) { len = strlen(rel_path); if (len >= 0xFFFF) { fprintf(stderr, @@ -3682,6 +3701,13 @@ int simple_archiver_parse_archive_version_1(FILE *in_f, int_fast8_t do_extract, return SDAS_INVALID_FILE; } const uint_fast8_t absolute_preferred = (buf[0] & 1) ? 1 : 0; + const uint_fast8_t is_invalid = (buf[1] & 4) ? 1 : 0; + + if (is_invalid) { + fprintf(stderr, + " WARNING: This symlink entry was marked invalid (not a safe " + "link)!\n"); + } #if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \ SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \