diff options
Diffstat (limited to 'lib')
| -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 |
5 files changed, 169 insertions, 130 deletions
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; |
