]> git.seodisparate.com - SimpleArchiver/commitdiff
Impl. safe-link checking for v1 file format
authorStephen Seo <seo.disparate@gmail.com>
Mon, 21 Oct 2024 08:10:34 +0000 (17:10 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Mon, 21 Oct 2024 08:12:48 +0000 (17:12 +0900)
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
src/archiver.c

index 1834af24bbfa4b0bfb62d3b7ef0a2122d58e74b2..f2e935b5a91215da4e25e8d06639eaeff33b442f 100644 (file)
@@ -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
index e093070ca4a75fee3d6a460e3446ecf0e1f170ef..555e802a1dc1c8df5033c55a333a8204615449fa 100644 (file)
@@ -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 ||          \