#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 */
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
#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>"
* 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;
{
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)
{
#include <sys/time.h>
#include <sys/resource.h>
#include <string.h>
+#include <sys/wait.h>
#include "c.h"
#include "all-io.h"
}
}
+/*
+ * 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[])
{
exe = executable(
'test_canonicalize',
'lib/canonicalize.c',
+ 'lib/fileutils.c',
c_args : ['-DTEST_PROGRAM_CANONICALIZE'],
include_directories : dir_include,
link_with : lib_common,