diff options
| author | Karel Zak <kzak@redhat.com> | 2025-09-03 09:47:04 +0200 |
|---|---|---|
| committer | Karel Zak <kzak@redhat.com> | 2025-09-03 09:47:04 +0200 |
| commit | 1c28f8f5f391c00a3eb5c78f6a50bc520121e53b (patch) | |
| tree | a05a9057bf100593adb823a91b17cf496644fd10 | |
| parent | c407a13fc1f5842881c967d0132035c7db442d4a (diff) | |
| parent | 6585841587c4664ee0941611b682c28d3f36c7a2 (diff) | |
| download | util-linux-1c28f8f5f391c00a3eb5c78f6a50bc520121e53b.tar.gz | |
Merge branch 'PR/libmount-canonicalize-cleanup' of https://github.com/karelzak/util-linux-work
* 'PR/libmount-canonicalize-cleanup' of https://github.com/karelzak/util-linux-work:
lib/canonicalize: use ul_ prefix
lib/canonicalize: introduce generic drop-permission caller
meson: cleanup tests to use libcommon.la
autotools: cleanup tests to use libcommon.la
tests: add canonicalize test
lib/canonicalize: refactor canonicalize_path()
lib/canonicalize: rename to ul_absolute_path()
| -rw-r--r-- | disk-utils/fdisk-list.c | 2 | ||||
| -rw-r--r-- | include/canonicalize.h | 12 | ||||
| -rw-r--r-- | include/fileutils.h | 5 | ||||
| -rw-r--r-- | lib/Makemodule.am | 47 | ||||
| -rw-r--r-- | lib/canonicalize.c | 157 | ||||
| -rw-r--r-- | lib/env.c | 2 | ||||
| -rw-r--r-- | lib/fileutils.c | 89 | ||||
| -rw-r--r-- | lib/loopdev.c | 4 | ||||
| -rw-r--r-- | libblkid/src/devname.c | 4 | ||||
| -rw-r--r-- | libblkid/src/evaluate.c | 4 | ||||
| -rw-r--r-- | libfdisk/src/utils.c | 2 | ||||
| -rw-r--r-- | libmount/src/cache.c | 2 | ||||
| -rw-r--r-- | libmount/src/tab.c | 2 | ||||
| -rw-r--r-- | libmount/src/utils.c | 4 | ||||
| -rw-r--r-- | login-utils/sulogin-consoles.c | 2 | ||||
| -rw-r--r-- | meson.build | 15 | ||||
| -rw-r--r-- | misc-utils/lsblk.c | 2 | ||||
| -rw-r--r-- | misc-utils/whereis.c | 2 | ||||
| -rw-r--r-- | sys-utils/losetup.c | 4 | ||||
| -rw-r--r-- | sys-utils/mount.c | 4 | ||||
| -rw-r--r-- | sys-utils/umount.c | 2 | ||||
| -rw-r--r-- | tests/commands.sh | 1 | ||||
| -rw-r--r-- | tests/expected/misc/canonicalize-non-root-user | 3 | ||||
| -rw-r--r-- | tests/expected/misc/canonicalize-root-user | 3 | ||||
| -rwxr-xr-x | tests/ts/misc/canonicalize | 80 |
25 files changed, 291 insertions, 163 deletions
diff --git a/disk-utils/fdisk-list.c b/disk-utils/fdisk-list.c index 24c4e04044..04ee2b431f 100644 --- a/disk-utils/fdisk-list.c +++ b/disk-utils/fdisk-list.c @@ -421,7 +421,7 @@ char *next_proc_partition(FILE **f) if (!sysfs_devno_to_devpath(devno, buf, sizeof(buf))) continue; - cn = canonicalize_path(buf); + cn = ul_canonicalize_path(buf); if (!cn) continue; diff --git a/include/canonicalize.h b/include/canonicalize.h index ff6ef0dd6e..08cdb37ecc 100644 --- a/include/canonicalize.h +++ b/include/canonicalize.h @@ -15,14 +15,14 @@ #include "c.h" /* for PATH_MAX */ #include "strutils.h" -extern char *canonicalize_path(const char *path); -extern char *canonicalize_path_restricted(const char *path); -extern char *canonicalize_dm_name(const char *ptname); -extern char *__canonicalize_dm_name(const char *prefix, const char *ptname); +extern char *ul_canonicalize_path(const char *path); +extern char *ul_canonicalize_path_restricted(const char *path); +extern char *ul_canonicalize_dm_name(const char *ptname); +extern char *ul_canonicalize_dm_name_prefixed(const char *prefix, const char *ptname); -extern char *absolute_path(const char *path); +extern char *ul_absolute_path(const char *path); -static inline int is_relative_path(const char *path) +static inline int ul_is_relative_path(const char *path) { if (!path || *path == '/') return 0; diff --git a/include/fileutils.h b/include/fileutils.h index 6fc93d0db8..672f1c9854 100644 --- a/include/fileutils.h +++ b/include/fileutils.h @@ -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 */ diff --git a/lib/Makemodule.am b/lib/Makemodule.am index bf24b6bee8..f5cc846b58 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -140,6 +140,7 @@ test_mangle_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_MANGLE test_strutils_SOURCES = lib/strutils.c test_strutils_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_STRUTILS +test_strutils_LDADD = $(LDADD) libcommon.la test_c_strtod_SOURCES = lib/c_strtod.c test_c_strtod_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM @@ -158,7 +159,7 @@ if HAVE_CPU_SET_T test_path_SOURCES += lib/cpuset.c endif test_path_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_PATH -test_path_LDADD = $(LDADD) +test_path_LDADD = $(LDADD) libcommon.la endif endif @@ -174,26 +175,25 @@ endif if LINUX test_cpuset_SOURCES = lib/cpuset.c test_cpuset_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_CPUSET +test_cpuset_LDADD = $(LDADD) libcommon.la -test_sysfs_SOURCES = lib/sysfs.c lib/path.c lib/fileutils.c lib/buffer.c lib/mbsalign.c -if HAVE_CPU_SET_T -test_sysfs_SOURCES += lib/cpuset.c -endif +test_sysfs_SOURCES = lib/sysfs.c test_sysfs_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_SYSFS -test_sysfs_LDADD = $(LDADD) +test_sysfs_LDADD = $(LDADD) libcommon.la -test_procfs_SOURCES = lib/procfs.c lib/path.c lib/fileutils.c lib/strutils.c -if HAVE_CPU_SET_T -test_procfs_SOURCES += lib/cpuset.c -endif +test_procfs_SOURCES = lib/procfs.c test_procfs_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_PROCFS -test_procfs_LDADD = $(LDADD) +test_procfs_LDADD = $(LDADD) libcommon.la test_pager_SOURCES = lib/pager.c test_pager_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_PAGER test_linux_version_SOURCES = lib/linux_version.c test_linux_version_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_LINUXVERSION + +test_loopdev_SOURCES = lib/loopdev.c +test_loopdev_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_LOOPDEV +test_loopdev_LDADD = $(LDADD) libcommon.la endif test_fileeq_SOURCES = lib/fileeq.c @@ -201,31 +201,26 @@ test_fileeq_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_FILEEQ 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 -test_timeutils_SOURCES = lib/timeutils.c lib/strutils.c +test_timeutils_SOURCES = lib/timeutils.c test_timeutils_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_TIMEUTILS +test_timeutils_LDADD = $(LDADD) libcommon.la test_pwdutils_SOURCES = lib/pwdutils.c test_pwdutils_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM -test_remove_env_SOURCES = lib/env.c lib/strv.c lib/strutils.c -test_remove_env_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM +test_remove_env_SOURCES = lib/env.c +test_remove_env_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_ENV +test_remove_env_LDADD = $(LDADD) libcommon.la -test_buffer_SOURCES = lib/buffer.c lib/mbsalign.c +test_buffer_SOURCES = lib/buffer.c test_buffer_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_BUFFER - -if LINUX -test_loopdev_SOURCES = lib/loopdev.c \ - lib/blkdev.c \ - lib/linux_version.c \ - $(test_sysfs_SOURCES) \ - $(test_canonicalize_SOURCES) -test_loopdev_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_LOOPDEV -endif - +test_buffer_LDADD = $(LDADD) libcommon.la test_logindefs_SOURCES = lib/logindefs.c test_logindefs_CPPFLAGS = -DTEST_PROGRAM $(AM_CPPFLAGS) diff --git a/lib/canonicalize.c b/lib/canonicalize.c index 6e70afe18a..854e611465 100644 --- a/lib/canonicalize.c +++ b/lib/canonicalize.c @@ -14,20 +14,23 @@ #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>" * * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs * provides the real DM device names in /sys/block/<ptname>/dm/name + * + * The @prefix allows /sys to be mounted or stored outside the system root + * (/prefix/sys/...). */ -char *__canonicalize_dm_name(const char *prefix, const char *ptname) +char *ul_canonicalize_dm_name_prefixed(const char *prefix, const char *ptname) { FILE *f; size_t sz; @@ -55,9 +58,9 @@ char *__canonicalize_dm_name(const char *prefix, const char *ptname) return res; } -char *canonicalize_dm_name(const char *ptname) +char *ul_canonicalize_dm_name(const char *ptname) { - return __canonicalize_dm_name(NULL, ptname); + return ul_canonicalize_dm_name_prefixed(NULL, ptname); } static int is_dm_devname(char *canonical, char **name) @@ -83,12 +86,12 @@ static int is_dm_devname(char *canonical, char **name) * relative path. If the path is no relative than returns NULL. The path does * not have to exist. */ -char *absolute_path(const char *path) +char *ul_absolute_path(const char *path) { char cwd[PATH_MAX], *res, *p; size_t psz, csz; - if (!is_relative_path(path)) { + if (!ul_is_relative_path(path)) { errno = EINVAL; return NULL; } @@ -118,132 +121,84 @@ char *absolute_path(const char *path) return res; } -char *canonicalize_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, + void *data __attribute__((__unused__))) { char *canonical, *dmname; - if (!path || !*path) - return NULL; + *result = NULL; + if (!path || !*path) { + errno = EINVAL; + return -errno; + } + + errno = 0; canonical = realpath(path, NULL); if (!canonical) - return strdup(path); + return 1; if (is_dm_devname(canonical, &dmname)) { - char *dm = canonicalize_dm_name(dmname); + char *dm = ul_canonicalize_dm_name(dmname); if (dm) { free(canonical); - return dm; + canonical = dm; } } - return canonical; + if (canonical) + *result = canonical; + return 0; } -char *canonicalize_path_restricted(const char *path) +/* + * Always returns a newly allocated string or NULL in case of an error. An + * unreachable path is not an error (!), and in this case, it just duplicates + * @path. + */ +char *ul_canonicalize_path(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 { - char *dmname = NULL; - - canonical = realpath(path, NULL); - if (canonical && is_dm_devname(canonical, &dmname)) { - char *dm = canonicalize_dm_name(dmname); - if (dm) { - free(canonical); - canonical = dm; - } - } - } - 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) ); + if (do_canonicalize(path, &canonical, NULL) == 1) + return strdup(path); - errno = errsv; return canonical; } +/* + * Drop permissions (e.g., suid) and canonicalize the path. If the path is + * unreadable (for example, due to missing permissions), it returns NULL. + */ +char *ul_canonicalize_path_restricted(const char *path) +{ + return ul_restricted_path_oper(path, do_canonicalize, NULL); +} #ifdef TEST_PROGRAM_CANONICALIZE int main(int argc, char **argv) { + char *p; + if (argc < 2) { fprintf(stderr, "usage: %s <device>\n", argv[0]); exit(EXIT_FAILURE); } - fprintf(stdout, "orig: %s\n", argv[1]); - fprintf(stdout, "real: %s\n", canonicalize_path(argv[1])); + fprintf(stdout, "orig: %s\n", argv[1]); + + p = ul_canonicalize_path(argv[1]); + fprintf(stdout, "real: %s\n", p); + free(p); + + p = ul_canonicalize_path_restricted(argv[1]); + fprintf(stdout, "real-restricted: %s\n", p); + free(p); + exit(EXIT_SUCCESS); } #endif @@ -280,7 +280,7 @@ return secure_getenv(arg); #endif } -#ifdef TEST_PROGRAM +#ifdef TEST_PROGRAM_ENV int main(void) { char *const *bad; diff --git a/lib/fileutils.c b/lib/fileutils.c index b7acae4308..c4e1806177 100644 --- a/lib/fileutils.c +++ b/lib/fileutils.c @@ -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[]) { diff --git a/lib/loopdev.c b/lib/loopdev.c index 8c5beaeb7e..8a51e5e5f4 100644 --- a/lib/loopdev.c +++ b/lib/loopdev.c @@ -692,7 +692,7 @@ int is_loopdev(const char *device) snprintf(name, sizeof(name), _PATH_SYS_DEVBLOCK "/%d:%d", major(st.st_rdev), minor(st.st_rdev)); - cn = canonicalize_path(name); + cn = ul_canonicalize_path(name); if (cn) p = stripoff_last_component(cn); rc = p && ul_startswith(p, "loop"); @@ -1257,7 +1257,7 @@ int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename) if (!lc) return -EINVAL; - lc->filename = canonicalize_path(filename); + lc->filename = ul_canonicalize_path(filename); if (!lc->filename) return -errno; diff --git a/libblkid/src/devname.c b/libblkid/src/devname.c index 0fb25325ef..b9717e23ab 100644 --- a/libblkid/src/devname.c +++ b/libblkid/src/devname.c @@ -66,7 +66,7 @@ blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags) } /* try canonicalize the name */ - if (!dev && (cn = canonicalize_path(devname))) { + if (!dev && (cn = ul_canonicalize_path(devname))) { if (strcmp(cn, devname) != 0) { DBG(DEVNAME, ul_debug("search canonical %s", cn)); list_for_each(p, &cache->bic_devs) { @@ -200,7 +200,7 @@ static void probe_one(blkid_cache cache, const char *ptname, * to standard /dev/mapper/<name>. */ if (!strncmp(ptname, "dm-", 3) && isdigit(ptname[3])) { - devname = canonicalize_dm_name(ptname); + devname = ul_canonicalize_dm_name(ptname); if (!devname) blkid__scan_dir("/dev/mapper", devno, NULL, &devname); if (devname) diff --git a/libblkid/src/evaluate.c b/libblkid/src/evaluate.c index 26e556c089..a4d04bca93 100644 --- a/libblkid/src/evaluate.c +++ b/libblkid/src/evaluate.c @@ -171,7 +171,7 @@ static char *evaluate_by_udev(const char *token, const char *value, int uevent) if (!S_ISBLK(st.st_mode)) return NULL; - path = canonicalize_path(dev); + path = ul_canonicalize_path(dev); if (!path) return NULL; @@ -298,7 +298,7 @@ char *blkid_evaluate_spec(const char *spec, blkid_cache *cache) if (v) res = blkid_evaluate_tag(t, v, cache); else - res = canonicalize_path(spec); + res = ul_canonicalize_path(spec); free(t); free(v); diff --git a/libfdisk/src/utils.c b/libfdisk/src/utils.c index ef2663737d..a3397ab523 100644 --- a/libfdisk/src/utils.c +++ b/libfdisk/src/utils.c @@ -128,7 +128,7 @@ char *fdisk_partname(const char *dev, size_t partno) /* It is impossible to predict /dev/dm-N partition names. */ if (strncmp(dev, "/dev/dm-", sizeof("/dev/dm-") - 1) == 0) { - dev_mapped = canonicalize_dm_name (dev + 5); + dev_mapped = ul_canonicalize_dm_name (dev + 5); if (dev_mapped) dev = dev_mapped; } diff --git a/libmount/src/cache.c b/libmount/src/cache.c index b7956346f8..2942654491 100644 --- a/libmount/src/cache.c +++ b/libmount/src/cache.c @@ -518,7 +518,7 @@ static char *canonicalize_path_and_cache(const char *path, char *value; DBG(CACHE, ul_debugobj(cache, "canonicalize path %s", path)); - p = canonicalize_path(path); + p = ul_canonicalize_path(path); if (p && cache) { value = p; diff --git a/libmount/src/tab.c b/libmount/src/tab.c index 4209d697a5..9a8033c256 100644 --- a/libmount/src/tab.c +++ b/libmount/src/tab.c @@ -1161,7 +1161,7 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat } /* try absolute path */ - if (is_relative_path(path) && (cn = absolute_path(path))) { + if (ul_is_relative_path(path) && (cn = ul_absolute_path(path))) { DBG(TAB, ul_debugobj(tb, "lookup absolute TARGET: '%s'", cn)); mnt_reset_iter(&itr, direction); while (mnt_table_next_fs(tb, &itr, &fs) == 0) { diff --git a/libmount/src/utils.c b/libmount/src/utils.c index 46050ff51f..7626b6f91e 100644 --- a/libmount/src/utils.c +++ b/libmount/src/utils.c @@ -1416,7 +1416,7 @@ static int test_mountpoint(struct libmnt_test *ts __attribute__((unused)), if (argc != 2) return -1; - char *path = canonicalize_path(argv[1]), + char *path = ul_canonicalize_path(argv[1]), *mnt = path ? mnt_get_mountpoint(path) : NULL; printf("%s: %s\n", argv[1], mnt ? : "unknown"); @@ -1451,7 +1451,7 @@ static int test_chdir(struct libmnt_test *ts __attribute__((unused)), return -1; int rc; - char *path = canonicalize_path(argv[1]), + char *path = ul_canonicalize_path(argv[1]), *last = NULL; if (!path) diff --git a/login-utils/sulogin-consoles.c b/login-utils/sulogin-consoles.c index cb9eb02bb8..93eeab111e 100644 --- a/login-utils/sulogin-consoles.c +++ b/login-utils/sulogin-consoles.c @@ -227,7 +227,7 @@ dev_t devattr(const char * const tty) /* * Search below /dev for the character device in `dev_t comparedev' variable. * Note that realpath(3) is used here to avoid not existent devices due the - * strdup(3) used in our canonicalize_path()! + * strdup(3) used in our ul_canonicalize_path()! */ static #ifdef __GNUC__ diff --git a/meson.build b/meson.build index e3038240a8..061e287b4c 100644 --- a/meson.build +++ b/meson.build @@ -3487,6 +3487,7 @@ exe = executable( 'lib/strutils.c', c_args : ['-DTEST_PROGRAM_STRUTILS'], include_directories : dir_include, + link_with : lib_common, build_by_default: program_tests) if not is_disabler(exe) exes += exe @@ -3530,8 +3531,6 @@ if conf.get('HAVE_OPENAT').to_string() == '1' \ exe = executable( 'test_path', 'lib/path.c', - 'lib/fileutils.c', - have_cpu_set_t ? 'lib/cpuset.c' : [], c_args : ['-DTEST_PROGRAM_PATH'], include_directories : dir_include, link_with : lib_common, @@ -3564,6 +3563,7 @@ if LINUX 'lib/cpuset.c', c_args : ['-DTEST_PROGRAM_CPUSET'], include_directories : dir_include, + link_with : lib_common, build_by_default: program_tests) if not is_disabler(exe) exes += exe @@ -3573,13 +3573,9 @@ endif exe = executable( 'test_sysfs', 'lib/sysfs.c', - 'lib/path.c', - 'lib/buffer.c', - 'lib/mbsalign.c', - 'lib/fileutils.c', - have_cpu_set_t ? 'lib/cpuset.c' : [], c_args : ['-DTEST_PROGRAM_SYSFS'], include_directories : dir_include, + link_with : lib_common, build_by_default: program_tests) if not is_disabler(exe) exes += exe @@ -3610,6 +3606,7 @@ exe = executable( 'lib/fileutils.c', c_args : ['-DTEST_PROGRAM_FILEUTILS'], include_directories : dir_include, + link_with : lib_common, build_by_default: program_tests) if not is_disabler(exe) exes += exe @@ -3618,8 +3615,10 @@ endif exe = executable( 'test_canonicalize', 'lib/canonicalize.c', + 'lib/fileutils.c', c_args : ['-DTEST_PROGRAM_CANONICALIZE'], include_directories : dir_include, + link_with : lib_common, build_by_default: program_tests) if not is_disabler(exe) exes += exe @@ -3628,9 +3627,9 @@ endif exe = executable( 'test_timeutils', 'lib/timeutils.c', - 'lib/strutils.c', c_args : ['-DTEST_PROGRAM_TIMEUTILS'], include_directories : dir_include, + link_with : lib_common, build_by_default: program_tests) if not is_disabler(exe) exes += exe diff --git a/misc-utils/lsblk.c b/misc-utils/lsblk.c index 4690083cde..39a8ca83f4 100644 --- a/misc-utils/lsblk.c +++ b/misc-utils/lsblk.c @@ -401,7 +401,7 @@ static char *get_device_path(struct lsblk_device *dev) assert(dev->name); if (is_dm(dev->name)) - return __canonicalize_dm_name(lsblk->sysroot, dev->name); + return ul_canonicalize_dm_name_prefixed(lsblk->sysroot, dev->name); snprintf(path, sizeof(path), "/dev/%s", dev->name); sysfs_devname_sys_to_dev(path); diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c index b575e57aee..8e500c0098 100644 --- a/misc-utils/whereis.c +++ b/misc-utils/whereis.c @@ -251,7 +251,7 @@ static void dirlist_add_dir(struct wh_dirlist **ls0, int type, const char *dir) ls->st_ino = st.st_ino; ls->st_dev = st.st_dev; ls->type = type; - ls->path = canonicalize_path(dir); + ls->path = ul_canonicalize_path(dir); if (!*ls0) *ls0 = ls; /* first in the list */ diff --git a/sys-utils/losetup.c b/sys-utils/losetup.c index 1a66173865..f35bd79fc4 100644 --- a/sys-utils/losetup.c +++ b/sys-utils/losetup.c @@ -209,7 +209,7 @@ static int show_all_loops(struct loopdev_cxt *lc, const char *file, used = loopcxt_is_used(lc, st, bf, offset, 0, flags); if (!used && !cn_file) { - bf = cn_file = canonicalize_path(file); + bf = cn_file = ul_canonicalize_path(file); used = loopcxt_is_used(lc, st, bf, offset, 0, flags); } if (!used) @@ -430,7 +430,7 @@ static int show_table(struct loopdev_cxt *lc, used = loopcxt_is_used(lc, st, bf, offset, 0, flags); if (!used && !cn_file) { - bf = cn_file = canonicalize_path(file); + bf = cn_file = ul_canonicalize_path(file); used = loopcxt_is_used(lc, st, bf, offset, 0, flags); } if (!used) diff --git a/sys-utils/mount.c b/sys-utils/mount.c index d6dd2805af..2450228d04 100644 --- a/sys-utils/mount.c +++ b/sys-utils/mount.c @@ -496,7 +496,7 @@ static int sanitize_paths(struct libmnt_context *cxt) p = mnt_fs_get_target(fs); if (p) { - char *np = canonicalize_path_restricted(p); + char *np = ul_canonicalize_path_restricted(p); if (!np) return -EPERM; mnt_fs_set_target(fs, np); @@ -505,7 +505,7 @@ static int sanitize_paths(struct libmnt_context *cxt) p = mnt_fs_get_srcpath(fs); if (p) { - char *np = canonicalize_path_restricted(p); + char *np = ul_canonicalize_path_restricted(p); if (!np) return -EPERM; mnt_fs_set_source(fs, np); diff --git a/sys-utils/umount.c b/sys-utils/umount.c index c115f44a0e..18824e09f2 100644 --- a/sys-utils/umount.c +++ b/sys-utils/umount.c @@ -446,7 +446,7 @@ static char *sanitize_path(const char *path) if (!path) return NULL; - p = canonicalize_path_restricted(path); + p = ul_canonicalize_path_restricted(path); if (!p) err(MNT_EX_USAGE, "%s", path); diff --git a/tests/commands.sh b/tests/commands.sh index fe989a541a..00be966554 100644 --- a/tests/commands.sh +++ b/tests/commands.sh @@ -4,6 +4,7 @@ TS_TESTUSER=${TS_TESTUSER:-"nobody"} # helpers TS_HELPER_BOILERPLATE="${ts_helpersdir}test_boilerplate" TS_HELPER_BYTESWAP="${ts_helpersdir}test_byteswap" +TS_HELPER_CANONICALIZE="${ts_helpersdir}test_canonicalize" TS_HELPER_COLORS="${ts_helpersdir}test_colors" TS_HELPER_CPUSET="${ts_helpersdir}test_cpuset" TS_HELPER_CAP="${ts_helpersdir}test_cap" diff --git a/tests/expected/misc/canonicalize-non-root-user b/tests/expected/misc/canonicalize-non-root-user new file mode 100644 index 0000000000..204e83b67a --- /dev/null +++ b/tests/expected/misc/canonicalize-non-root-user @@ -0,0 +1,3 @@ +orig: /root-sym/foo +real: /root/foo +real-restricted: (null) diff --git a/tests/expected/misc/canonicalize-root-user b/tests/expected/misc/canonicalize-root-user new file mode 100644 index 0000000000..87213a7039 --- /dev/null +++ b/tests/expected/misc/canonicalize-root-user @@ -0,0 +1,3 @@ +orig: /aaa/bbb/ccc-sym/ddd +real: /ccc/ddd +real-restricted: /ccc/ddd diff --git a/tests/ts/misc/canonicalize b/tests/ts/misc/canonicalize new file mode 100755 index 0000000000..2e111659c9 --- /dev/null +++ b/tests/ts/misc/canonicalize @@ -0,0 +1,80 @@ +#!/bin/bash + +# Copyright (C) 2025 Karel Zak <kzak@redhat.com> +# +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +TS_TOPDIR="${0%/*}/../.." +TS_DESC="canonicalize" + +. "$TS_TOPDIR"/functions.sh +ts_init "$*" + +ts_skip_nonroot + +ts_check_prog "getent" +ts_check_test_command "$TS_CMD_MOUNT" +ts_check_test_command "$TS_CMD_UMOUNT" +ts_check_test_command "$TS_CMD_SETPRIV" +ts_check_test_command "$TS_HELPER_CANONICALIZE" + +grep -q 'nodev[[:space:]]*tmpfs' /proc/filesystems || \ + ts_skip_subtest "tmpfs unsupported" + +mkdir -p $TS_MOUNTPOINT &> /dev/null +$TS_CMD_MOUNT -t tmpfs tmpfs $TS_MOUNTPOINT >> $TS_OUTPUT 2>> $TS_ERRLOG +[ $? -eq 0 ] || ts_skip "tmpfs mount failed" + +# reuse the same TS_MOUNTPOINT in all subtests +BASE=$TS_MOUNTPOINT + +# setup suid binary +TESTPROG=${BASE}/$(basename $TS_HELPER_CANONICALIZE) +cp $TS_HELPER_CANONICALIZE $TESTPROG +chown root:root $TESTPROG +chmod +x,u+s $TESTPROG + +if [ ! -u "$TESTPROG" ] || [ ! -x "$TESTPROG" ]; then + $TS_CMD_UMOUNT $BASE + ts_skip "cannot setup suid test" +fi + + +ts_init_subtest root-user +mkdir -p ${BASE}/aaa/bbb +mkdir -p ${BASE}/ccc/ddd +ln -s ${BASE}/ccc ${BASE}/aaa/bbb/ccc-sym +$TESTPROG ${BASE}/aaa/bbb/ccc-sym/ddd | sed "s:${BASE}::g" >> $TS_OUTPUT 2>> $TS_ERRLOG +ts_finalize_subtest + + +ts_init_subtest non-root-user +uid="nobody" +if id $uid &>/dev/null; then + mkdir -p ${BASE}/root/foo + chown -R root:root ${BASE}/root + chmod -R o-rwx ${BASE}/root + ln -s ${BASE}/root ${BASE}/root-sym + + gid=$(getent passwd "$uid" | cut -d: -f4) + + $TS_CMD_SETPRIV --reuid="$uid" --regid="$gid" --clear-groups \ + --inh-caps=-all --reset-env \ + -- $TESTPROG ${BASE}/root-sym/foo \ + | sed "s:${BASE}::g" >> $TS_OUTPUT 2>> $TS_ERRLOG + + ts_finalize_subtest +else + ts_skip_subtest "nobody user is missing" +fi + + +# cleanup +$TS_CMD_UMOUNT $BASE &> /dev/null + +ts_finalize |
