]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib/canonicalize: introduce generic drop-permission caller
authorKarel Zak <kzak@redhat.com>
Tue, 1 Jul 2025 10:26:41 +0000 (12:26 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 1 Sep 2025 11:49:48 +0000 (13:49 +0200)
* add ul_restricted_path_oper() to fileutils.c

* use it as backed for canonicalize_path_restricted()

Signed-off-by: Karel Zak <kzak@redhat.com>
include/fileutils.h
lib/Makemodule.am
lib/canonicalize.c
lib/fileutils.c
meson.build

index 6fc93d0db829ed70c207bdce9f19a4b915a45058..672f1c9854d34805fa9479462314b226a2b0c0ef 100644 (file)
@@ -112,8 +112,11 @@ extern void ul_close_all_fds(unsigned int first, unsigned int last);
 #define UL_COPY_WRITE_ERROR (-2)
 int ul_copy_file(int from, int to);
 
-
 extern int ul_reopen(int fd, int flags);
 extern char *ul_basename(char *path);
 
+extern char *ul_restricted_path_oper(const char *path,
+               int (*oper)(const char *path, char **result, void *data),
+               void *data);
+
 #endif /* UTIL_LINUX_FILEUTILS */
index d73ae01b7e2248975855cfa08a43312abd7266b3..f5cc846b58c1086880dd8c4d4cd1632d91a40048 100644 (file)
@@ -203,7 +203,7 @@ test_fileutils_SOURCES = lib/fileutils.c
 test_fileutils_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_FILEUTILS
 test_fileutils_LDADD = $(LDADD) libcommon.la
 
-test_canonicalize_SOURCES = lib/canonicalize.c
+test_canonicalize_SOURCES = lib/canonicalize.c lib/fileutils.c
 test_canonicalize_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_CANONICALIZE
 test_canonicalize_LDADD = $(LDADD) libcommon.la
 
index 9109b36303eeb27aedd9191296b3d4cdeb08dace..ab86a7483c11a04765b0010084170786c14e8354 100644 (file)
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <sys/wait.h>
 
 #include "canonicalize.h"
 #include "pathnames.h"
 #include "all-io.h"
 #include "strutils.h"
+#include "fileutils.h"
 
 /*
  * Converts private "dm-N" names to "/dev/mapper/<name>"
@@ -122,7 +122,8 @@ char *ul_absolute_path(const char *path)
  * Returns: <0 on error, 1 is cannot be canonicalized (errno is set); 0 on success
  */
 static int __attribute__((nonnull(2)))
-do_canonicalize(const char *path, char **result)
+do_canonicalize(const char *path, char **result,
+               void *data __attribute__((__unused__)))
 {
        char *canonical, *dmname;
 
@@ -160,99 +161,21 @@ char *canonicalize_path(const char *path)
 {
        char *canonical = NULL;
 
-       if (do_canonicalize(path, &canonical) == 1)
+       if (do_canonicalize(path, &canonical, NULL) == 1)
                return strdup(path);
 
        return canonical;
 }
+
 /*
- * Almost like canonicalize_path() but drops permissions (suid, etc.) to
- * canonicalize the path. Returns NULL if the path is unreachable
+ * Drop permissions (e.g., suid) and canonicalize the path. If the path is
+ * unreadable (for example, due to missing permissions), it returns NULL.
  */
 char *canonicalize_path_restricted(const char *path)
 {
-       char *canonical = NULL;
-       int errsv = 0;
-       int pipes[2];
-       ssize_t len;
-       pid_t pid;
-
-       if (!path || !*path)
-               return NULL;
-
-       if (pipe(pipes) != 0)
-               return NULL;
-
-       /*
-        * To accurately assume identity of getuid() we must use setuid()
-        * but if we do that, we lose ability to reassume euid of 0, so
-        * we fork to do the check to keep euid intact.
-        */
-       pid = fork();
-       switch (pid) {
-       case -1:
-               close(pipes[0]);
-               close(pipes[1]);
-               return NULL;                    /* fork error */
-       case 0:
-               close(pipes[0]);                /* close unused end */
-               pipes[0] = -1;
-               errno = 0;
-
-               if (drop_permissions() != 0)
-                       canonical = NULL;       /* failed */
-               else
-                       do_canonicalize(path, &canonical);
-
-               len = canonical ?(ssize_t) strlen(canonical) :
-                               errno ? -errno : -EINVAL;
-
-               /* send length or errno */
-               write_all(pipes[1], (char *) &len, sizeof(len));
-               if (canonical)
-                       write_all(pipes[1], canonical, len);
-               _exit(0);
-       default:
-               break;
-       }
-
-       close(pipes[1]);                /* close unused end */
-       pipes[1] = -1;
-
-       /* read size or -errno */
-       if (read_all(pipes[0], (char *) &len, sizeof(len)) != sizeof(len))
-               goto done;
-       if (len < 0) {
-               errsv = -len;
-               goto done;
-       }
-
-       canonical = malloc(len + 1);
-       if (!canonical) {
-               errsv = ENOMEM;
-               goto done;
-       }
-       /* read path */
-       if (read_all(pipes[0], canonical, len) != len) {
-               errsv = errno;
-               goto done;
-       }
-       canonical[len] = '\0';
-done:
-       if (errsv) {
-               free(canonical);
-               canonical = NULL;
-       }
-       close(pipes[0]);
-
-       /* We make a best effort to reap child */
-       ignore_result( waitpid(pid, NULL, 0) );
-
-       errno = errsv;
-       return canonical;
+       return ul_restricted_path_oper(path, do_canonicalize, NULL);
 }
 
-
 #ifdef TEST_PROGRAM_CANONICALIZE
 int main(int argc, char **argv)
 {
index b7acae43082196238a408a9cf5a2aed169a79e06..c4e180617725397a291f948c566b799155361d55 100644 (file)
@@ -12,6 +12,7 @@
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <string.h>
+#include <sys/wait.h>
 
 #include "c.h"
 #include "all-io.h"
@@ -170,6 +171,94 @@ void ul_close_all_fds(unsigned int first, unsigned int last)
        }
 }
 
+/*
+ * Fork, drop permissions, and call oper() and return result.
+ */
+char *ul_restricted_path_oper(const char *path,
+                 int (*oper)(const char *path, char **result, void *data),
+                 void *data)
+{
+       char *result = NULL;
+       int errsv = 0;
+       int pipes[2];
+       ssize_t len;
+       pid_t pid;
+
+       if (!path || !*path)
+               return NULL;
+
+       if (pipe(pipes) != 0)
+               return NULL;
+       /*
+        * To accurately assume identity of getuid() we must use setuid()
+        * but if we do that, we lose ability to reassume euid of 0, so
+        * we fork to do the check to keep euid intact.
+        */
+       pid = fork();
+       switch (pid) {
+       case -1:
+               close(pipes[0]);
+               close(pipes[1]);
+               return NULL;                    /* fork error */
+       case 0:
+               close(pipes[0]);                /* close unused end */
+               pipes[0] = -1;
+               errno = 0;
+
+               if (drop_permissions() != 0)
+                       result = NULL;  /* failed */
+               else
+                       oper(path, &result, data);
+
+               len = result ? (ssize_t) strlen(result) :
+                         errno ? -errno : -EINVAL;
+
+               /* send length or errno */
+               write_all(pipes[1], (char *) &len, sizeof(len));
+               if (result)
+                       write_all(pipes[1], result, len);
+               _exit(0);
+       default:
+               break;
+       }
+
+       close(pipes[1]);                /* close unused end */
+       pipes[1] = -1;
+
+       /* read size or -errno */
+       if (read_all(pipes[0], (char *) &len, sizeof(len)) != sizeof(len))
+               goto done;
+       if (len < 0) {
+               errsv = -len;
+               goto done;
+       }
+
+       result = malloc(len + 1);
+       if (!result) {
+               errsv = ENOMEM;
+               goto done;
+       }
+       /* read path */
+       if (read_all(pipes[0], result, len) != len) {
+               errsv = errno;
+               goto done;
+       }
+       result[len] = '\0';
+done:
+       if (errsv) {
+               free(result);
+               result = NULL;
+       }
+       close(pipes[0]);
+
+       /* We make a best effort to reap child */
+       ignore_result( waitpid(pid, NULL, 0) );
+
+       errno = errsv;
+       return result;
+
+}
+
 #ifdef TEST_PROGRAM_FILEUTILS
 int main(int argc, char *argv[])
 {
index 38adc8d07bac9f920aa7b3e75ae386c857451d27..061e287b4c80b5d57aa0c70576564121112e7e3f 100644 (file)
@@ -3615,6 +3615,7 @@ endif
 exe = executable(
   'test_canonicalize',
   'lib/canonicalize.c',
+  'lib/fileutils.c',
   c_args : ['-DTEST_PROGRAM_CANONICALIZE'],
   include_directories : dir_include,
   link_with : lib_common,