aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makemodule.am47
-rw-r--r--lib/canonicalize.c157
-rw-r--r--lib/env.c2
-rw-r--r--lib/fileutils.c89
-rw-r--r--lib/loopdev.c4
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
diff --git a/lib/env.c b/lib/env.c
index cdd8f8c587..039fad0dc5 100644
--- a/lib/env.c
+++ b/lib/env.c
@@ -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;