aboutsummaryrefslogtreecommitdiffstats
path: root/lib/canonicalize.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/canonicalize.c')
-rw-r--r--lib/canonicalize.c157
1 files changed, 56 insertions, 101 deletions
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