aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog.txt6
-rw-r--r--doc/overview2
-rw-r--r--guts/README93
-rw-r--r--guts/access.c28
-rw-r--r--guts/acct.c11
-rw-r--r--guts/canonicalize_file_name.c11
-rw-r--r--guts/chroot.c16
-rw-r--r--guts/eaccess.c11
-rw-r--r--guts/euidaccess.c11
-rw-r--r--guts/freopen64.c32
-rw-r--r--guts/fts_open.c43
-rw-r--r--guts/ftw.c11
-rw-r--r--guts/ftw64.c11
-rw-r--r--guts/getcwd.c36
-rw-r--r--guts/glob.c11
-rw-r--r--guts/glob64.c11
-rw-r--r--guts/lutimes.c11
-rw-r--r--guts/mkdtemp.c42
-rw-r--r--guts/mkfifo.c11
-rw-r--r--guts/mkfifoat.c11
-rw-r--r--guts/mktemp.c32
-rw-r--r--guts/nftw.c11
-rw-r--r--guts/nftw64.c11
-rw-r--r--guts/opendir.c11
-rw-r--r--guts/pathconf.c11
-rw-r--r--guts/readlink.c11
-rw-r--r--guts/readlinkat.c25
-rw-r--r--guts/realpath.c27
-rw-r--r--guts/remove.c19
-rw-r--r--guts/scandir.c11
-rw-r--r--guts/scandir64.c11
-rw-r--r--guts/symlinkat.c6
-rw-r--r--guts/tempnam.c12
-rw-r--r--guts/tmpnam.c13
-rw-r--r--guts/truncate.c11
-rw-r--r--guts/truncate64.c11
-rw-r--r--guts/utime.c10
-rw-r--r--guts/utimes.c10
-rw-r--r--pseudo.126
-rw-r--r--pseudo.c44
-rw-r--r--pseudo.h28
-rw-r--r--pseudo_client.c84
-rw-r--r--pseudo_client.h28
-rw-r--r--pseudo_db.c1
-rw-r--r--pseudo_table.c1
-rw-r--r--pseudo_util.c76
-rw-r--r--wrapfuncs.in28
-rw-r--r--wrapfuncs64.in6
48 files changed, 893 insertions, 101 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt
index cc7083b..0d5c2dc 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,4 +1,10 @@
2010-03-24:
+ * (seebs) add chroot syscall
+ * (seebs) add chroot handling to path canonicalization
+ * (seebs) add many calls just to get path fixups
+ * (seebs) handle virtualizing rootness of absolute symlinks
+
+2010-03-24:
* (seebs) regenerate wrappers when makewrappers is changed.
* (seebs) begin prep for chroot
* (seebs) standardize path expansion
diff --git a/doc/overview b/doc/overview
index 7341e3c..17049f5 100644
--- a/doc/overview
+++ b/doc/overview
@@ -37,7 +37,7 @@ pseudo client code, and the check for whether or not to use it is controlled
by the mutex (actually by the mutex owner variable, which is protected by
the mutex.) Without that, read operations in another thread during the
"antimagic" part of an operation would bypass pseudo, yielding erratically
-wrong results!
+wrong results! Wrappers are where pathnames get canonicalized.
2. The wrapper function itself. This function may translate a single
operation into two or more logical operations. This function has no awareness
of the database, but can send queries to the general client code.
diff --git a/guts/README b/guts/README
index be00d30..277f712 100644
--- a/guts/README
+++ b/guts/README
@@ -28,26 +28,32 @@ some difference -- the structure they manipulate is not the same.
The following table shows which functions are merely wrappers around
other functions:
- chmod: fchmodat
- chown: fchownat
- creat64: openat
- creat: openat
- __fxstatat: __fxstatat64
- __fxstat: __fxstat64
- __lxstat64: __fxstatat64
- __lxstat: __fxstatat
- mkdir: mkdirat
- open64: openat
- __openat_2: openat
- __openat64_2: openat
- openat64: openat
- open: openat
- rename: renameat
- symlink: symlinkat
- unlink: unlinkat
- __xmknod: __xmknodat
- __xstat64: __fxstatat64
- __xstat: __fxstatat
+ canonicalize_file_name: realpath
+ chmod: fchmodat
+ chown: fchownat
+ creat64: openat
+ creat: openat
+ __fxstatat: __fxstatat64
+ __fxstat: __fxstat64
+ get_current_dir_name: getcwd
+ getwd: getcwd
+ __lxstat64: __fxstatat64
+ __lxstat: __fxstatat
+ mkdir: mkdirat
+ mkfifoat: __xmknodat
+ mkfifo: mkfifoat
+ open64: openat
+ __openat_2: openat
+ __openat64_2: openat
+ openat64: openat
+ open: openat
+ remove: unlink or rmdir
+ rename: renameat
+ symlink: symlinkat
+ unlink: unlinkat
+ __xmknod: __xmknodat
+ __xstat64: __fxstatat64
+ __xstat: __fxstatat
The following functions are full implementations:
@@ -59,10 +65,10 @@ The following functions are full implementations:
fchownat
__fxstat64
__fxstatat64
+ getcwd
lchown
mkdirat
openat
- renameat
rmdir
symlinkat
unlinkat
@@ -71,7 +77,7 @@ The following functions are full implementations:
The following functions provide only partial implementations, to trap special
cases, to track internal data structures (for instance, close() is tracked so
that the path to a file descriptor can be dropped when the file descriptor
-is closed), or to handle functions which may not use the underlying syscall
+is closed), or to handle functions which may not use the underlying syscall
wrappers:
close
@@ -81,6 +87,7 @@ wrappers:
fopen
fopen64
freopen
+ freopen64
mkstemp
fcntl
fork
@@ -107,3 +114,45 @@ but are used to simulate the permissions system:
getresuid
setfsuid
setresuid
+
+The following functions are present only to allow filename mangling
+for chroot(2) implementation. Most of them have no logic beyond
+calling the underlying routine.
+
+ access
+ acct
+ chroot
+ eaccess
+ euidaccess
+ fts_open
+ ftw64
+ ftw
+ glob64
+ glob
+ lutimes
+ mkdtemp
+ mktemp
+ nftw64
+ nftw
+ opendir
+ pathconf
+ readlinkat
+ readlink
+ realpath
+ scandir64
+ scandir
+ truncate64
+ truncate
+ utime
+ utimes
+
+The following functions are unimplemented. renameat could be done now (it
+would have been hard previously due to file name mangling issues), but
+since it's never come up, we haven't done it. The tempnam() functions are
+fairly hard to get right, and perhaps more imporantly, extremely
+dangerous. Since there's no evidence that they're in use anymore, I've
+dummied them out:
+
+ renameat
+ tempnam
+ tmpnam
diff --git a/guts/access.c b/guts/access.c
new file mode 100644
index 0000000..761ae66
--- /dev/null
+++ b/guts/access.c
@@ -0,0 +1,28 @@
+/*
+ * static int
+ * wrap_access(const char *path, int mode) {
+ * int rc = -1;
+ */
+ struct stat64 buf;
+
+ /* note: no attempt to handle the case where user isn't
+ * root.
+ */
+ rc = wrap___fxstatat64(_STAT_VER, AT_FDCWD, path, &buf, 0);
+ if (rc == -1)
+ return rc;
+
+ if (mode & X_OK) {
+ if (buf.st_mode & 0111) {
+ return 0;
+ } else {
+ errno = EPERM;
+ return -1;
+ }
+ } else {
+ return 0;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/acct.c b/guts/acct.c
new file mode 100644
index 0000000..422aa9f
--- /dev/null
+++ b/guts/acct.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_acct(const char *path) {
+ * int rc = -1;
+ */
+
+ rc = real_acct(path);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/canonicalize_file_name.c b/guts/canonicalize_file_name.c
new file mode 100644
index 0000000..198f5fb
--- /dev/null
+++ b/guts/canonicalize_file_name.c
@@ -0,0 +1,11 @@
+/*
+ * static char *
+ * wrap_canonicalize_file_name(const char *filename) {
+ * char * rc = NULL;
+ */
+
+ rc = wrap_realpath(filename, NULL);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/chroot.c b/guts/chroot.c
new file mode 100644
index 0000000..5f68482
--- /dev/null
+++ b/guts/chroot.c
@@ -0,0 +1,16 @@
+/*
+ * static int
+ * wrap_chroot(const char *path) {
+ * int rc = -1;
+ */
+ pseudo_debug(2, "chroot: %s\n", path);
+ if (!pseudo_client_op(OP_CHROOT, -1, -1, path, 0)) {
+ pseudo_debug(1, "chroot failed: %s\n", strerror(errno));
+ rc = -1;
+ } else {
+ rc = 0;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/eaccess.c b/guts/eaccess.c
new file mode 100644
index 0000000..6c9d2cc
--- /dev/null
+++ b/guts/eaccess.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_eaccess(const char *path, int mode) {
+ * int rc = -1;
+ */
+
+ rc = wrap_access(path, mode);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/euidaccess.c b/guts/euidaccess.c
new file mode 100644
index 0000000..c69a7f1
--- /dev/null
+++ b/guts/euidaccess.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_euidaccess(const char *path, int mode) {
+ * int rc = -1;
+ */
+
+ rc = wrap_access(path, mode);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/freopen64.c b/guts/freopen64.c
new file mode 100644
index 0000000..1f83b07
--- /dev/null
+++ b/guts/freopen64.c
@@ -0,0 +1,32 @@
+/*
+ * static FILE *
+ * wrap_freopen64(const char *path, const char *mode, FILE *stream) {
+ * FILE * rc = NULL;
+ */
+ struct stat64 buf;
+ int save_errno;
+ int existed = (real___xstat64(_STAT_VER, path, &buf) != -1);
+
+ rc = real_freopen64(path, mode, stream);
+ save_errno = errno;
+
+ if (rc) {
+ int fd = fileno(rc);
+
+ pseudo_debug(2, "freopen64 '%s': fd %d\n", path, fd);
+ if (real___fxstat64(_STAT_VER, fd, &buf) != -1) {
+ if (!existed) {
+ pseudo_client_op(OP_CREAT, -1, -1, path, &buf);
+ }
+ pseudo_client_op(OP_OPEN, fd, -1, path, &buf);
+ } else {
+ pseudo_debug(1, "fopen (fd %d) succeeded, but stat failed (%s).\n",
+ fd, strerror(errno));
+ pseudo_client_op(OP_OPEN, fd, -1, path, 0);
+ }
+ errno = save_errno;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/fts_open.c b/guts/fts_open.c
new file mode 100644
index 0000000..d244861
--- /dev/null
+++ b/guts/fts_open.c
@@ -0,0 +1,43 @@
+/*
+ * static FTS *
+ * wrap_fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **)) {
+ * FTS * rc = NULL;
+ */
+ char **rpath_argv;
+ size_t args = 0;
+ int errored = 0;
+ int i;
+
+ if (!path_argv) {
+ errno = EFAULT;
+ return NULL;
+ }
+ /* count args */
+ for (i = 0; path_argv[i]; ++i) {
+ ++args;
+ }
+ rpath_argv = malloc((args + 1) * sizeof(*rpath_argv));
+ if (!rpath_argv) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ for (i = 0; i < args; ++i) {
+ rpath_argv[i] = PSEUDO_ROOT_PATH(AT_FDCWD, path_argv[i], AT_SYMLINK_NOFOLLOW);
+ if (!rpath_argv[i])
+ errored = 1;
+ }
+
+ if (errored) {
+ errno = ENOMEM;
+ rc = NULL;
+ } else {
+ rc = real_fts_open(path_argv, options, compar);
+ }
+ for (i = 0; i < args; ++i)
+ free(rpath_argv[i]);
+ free(rpath_argv);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/ftw.c b/guts/ftw.c
new file mode 100644
index 0000000..7b298dd
--- /dev/null
+++ b/guts/ftw.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_ftw(const char *path, int (*fn)(const char *, const struct stat *, int), int nopenfd) {
+ * int rc = -1;
+ */
+
+ rc = real_ftw(path, fn, nopenfd);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/ftw64.c b/guts/ftw64.c
new file mode 100644
index 0000000..10bcdaf
--- /dev/null
+++ b/guts/ftw64.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_ftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int), int nopenfd) {
+ * int rc = -1;
+ */
+
+ rc = real_ftw64(path, fn, nopenfd);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/getcwd.c b/guts/getcwd.c
index d8f4d36..f6a8521 100644
--- a/guts/getcwd.c
+++ b/guts/getcwd.c
@@ -3,7 +3,7 @@
* wrap_getcwd(char *buf, size_t size) {
* char * rc = NULL;
*/
- pseudo_debug(2, "wrap_getcwd: %p, %lu\n",
+ pseudo_debug(3, "wrap_getcwd: %p, %lu\n",
(void *) buf, (unsigned long) size);
if (!pseudo_cwd) {
pseudo_diag("Asked for CWD, but don't have it!\n");
@@ -12,15 +12,26 @@
}
/* emulate Linux semantics in case of non-Linux systems. */
if (!buf) {
- /* if we don't have one, something's very wrong... */
+ /* if we don't have a cwd, something's very wrong... */
if (!size) {
- size = pseudo_cwd_len;
+ size = pseudo_cwd_len + 1;
+ if (pseudo_chroot_len && size >= pseudo_chroot_len &&
+ !memcmp(pseudo_cwd, pseudo_chroot, pseudo_chroot_len)) {
+ size -= pseudo_chroot_len;
+ /* if cwd is precisely the same as chroot, we
+ * actually want a /, not an empty string
+ */
+ if (size < 2)
+ size = 2;
+ }
}
if (size) {
buf = malloc(size);
} else {
- pseudo_diag("can't figure out CWD: length %ld\n",
- (unsigned long) pseudo_cwd_len);
+ pseudo_diag("can't figure out CWD: length %ld + 1 - %ld => %ld\n",
+ (unsigned long) pseudo_cwd_len,
+ (unsigned long) pseudo_chroot_len,
+ (unsigned long) size);
}
if (!buf) {
pseudo_diag("couldn't allocate requested CWD buffer - need %ld byes\n",
@@ -29,8 +40,21 @@
return NULL;
}
}
+ if (pseudo_cwd_len - (pseudo_cwd_rel - pseudo_cwd) >= size) {
+ pseudo_diag("only %ld bytes available, need %ld (%ld + 1 - %ld)\n",
+ (unsigned long) size,
+ (unsigned long) pseudo_cwd_len + 1 - pseudo_chroot_len,
+ (unsigned long) pseudo_cwd_len,
+ (unsigned long) pseudo_chroot_len);
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
rc = buf;
- memcpy(buf, pseudo_cwd, pseudo_cwd_len + 1);
+ pseudo_debug(3, "getcwd: copying %d (%d + 1 - %d) characters from <%s>.\n",
+ (int) ((pseudo_cwd_len + 1) - pseudo_chroot_len),
+ pseudo_cwd_len, pseudo_chroot_len,
+ pseudo_cwd_rel);
+ memcpy(buf, pseudo_cwd_rel, (pseudo_cwd_len + 1) - (pseudo_cwd_rel - pseudo_cwd));
if (!*buf) {
strcpy(buf, "/");
}
diff --git a/guts/glob.c b/guts/glob.c
new file mode 100644
index 0000000..7a12dbe
--- /dev/null
+++ b/guts/glob.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob) {
+ * int rc = -1;
+ */
+
+ rc = real_glob(pattern, flags, errfunc, pglob);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/glob64.c b/guts/glob64.c
new file mode 100644
index 0000000..80571b0
--- /dev/null
+++ b/guts/glob64.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_glob64(const char *pattern, int flags, int (*errfunc)(const char *, int), glob64_t *pglob) {
+ * int rc = -1;
+ */
+
+ rc = real_glob64(pattern, flags, errfunc, pglob);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/lutimes.c b/guts/lutimes.c
new file mode 100644
index 0000000..fcbf363
--- /dev/null
+++ b/guts/lutimes.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_lutimes(const char *path, const struct timeval *tv) {
+ * int rc = -1;
+ */
+
+ rc = real_lutimes(path, tv);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/mkdtemp.c b/guts/mkdtemp.c
new file mode 100644
index 0000000..32fe4ef
--- /dev/null
+++ b/guts/mkdtemp.c
@@ -0,0 +1,42 @@
+/*
+ * static char *
+ * wrap_mkdtemp(char *template) {
+ * char * rc = NULL;
+ */
+ struct stat64 buf;
+ int save_errno;
+ size_t len;
+ char *tmp_template;
+
+ if (!template) {
+ errno = EFAULT;
+ return NULL;
+ }
+
+ len = strlen(template);
+ tmp_template = PSEUDO_ROOT_PATH(AT_FDCWD, template, AT_SYMLINK_NOFOLLOW);
+
+ if (!tmp_template) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ rc = real_mkdtemp(tmp_template);
+
+ if (rc != NULL) {
+ save_errno = errno;
+
+ if (real___xstat64(_STAT_VER, rc, &buf) != -1) {
+ pseudo_client_op(OP_CREAT, -1, -1, tmp_template, &buf);
+ } else {
+ pseudo_debug(1, "mkstemp (path %s) succeeded, but fstat failed (%s).\n",
+ rc, strerror(errno));
+ }
+ errno = save_errno;
+ }
+ /* mkdtemp only changes the XXXXXX at the end. */
+ memcpy(template + len - 6, tmp_template + strlen(tmp_template) - 6, 6);
+ free(tmp_template);
+/* return rc;
+ * }
+ */
diff --git a/guts/mkfifo.c b/guts/mkfifo.c
new file mode 100644
index 0000000..1a8b54e
--- /dev/null
+++ b/guts/mkfifo.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_mkfifo(const char *path, mode_t mode) {
+ * int rc = -1;
+ */
+
+ rc = wrap_mkfifoat(AT_FDCWD, path, mode);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/mkfifoat.c b/guts/mkfifoat.c
new file mode 100644
index 0000000..c52ac21
--- /dev/null
+++ b/guts/mkfifoat.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_mkfifoat(int dirfd, const char *path, mode_t mode) {
+ * int rc = -1;
+ */
+
+ rc = wrap___xmknodat(_STAT_VER, dirfd, path, (mode & 07777) | S_IFIFO, 0);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/mktemp.c b/guts/mktemp.c
new file mode 100644
index 0000000..e59cb28
--- /dev/null
+++ b/guts/mktemp.c
@@ -0,0 +1,32 @@
+/*
+ * static char *
+ * wrap_mktemp(char *template) {
+ * char * rc = NULL;
+ */
+ size_t len;
+ char *tmp_template;
+
+ if (!template) {
+ errno = EFAULT;
+ return NULL;
+ }
+
+ len = strlen(template);
+ tmp_template = PSEUDO_ROOT_PATH(AT_FDCWD, template, AT_SYMLINK_NOFOLLOW);
+
+ if (!tmp_template) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ rc = real_mktemp(tmp_template);
+
+ /* mktemp only changes the XXXXXX at the end, and never created
+ * a file -- note the race condition implied here.
+ */
+ memcpy(template + len - 6, tmp_template + strlen(tmp_template) - 6, 6);
+ free(tmp_template);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/nftw.c b/guts/nftw.c
new file mode 100644
index 0000000..8d535b7
--- /dev/null
+++ b/guts/nftw.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int nopenfd, int flag) {
+ * int rc = -1;
+ */
+
+ rc = real_nftw(path, fn, nopenfd, flag);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/nftw64.c b/guts/nftw64.c
new file mode 100644
index 0000000..e0471f0
--- /dev/null
+++ b/guts/nftw64.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_nftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int, struct FTW *), int nopenfd, int flag) {
+ * int rc = -1;
+ */
+
+ rc = real_nftw64(path, fn, nopenfd, flag);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/opendir.c b/guts/opendir.c
new file mode 100644
index 0000000..19efa84
--- /dev/null
+++ b/guts/opendir.c
@@ -0,0 +1,11 @@
+/*
+ * static DIR *
+ * wrap_opendir(const char *path) {
+ * DIR * rc = NULL;
+ */
+
+ rc = real_opendir(path);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/pathconf.c b/guts/pathconf.c
new file mode 100644
index 0000000..2a59eab
--- /dev/null
+++ b/guts/pathconf.c
@@ -0,0 +1,11 @@
+/*
+ * static long
+ * wrap_pathconf(const char *path, int name) {
+ * long rc = -1;
+ */
+
+ rc = real_pathconf(path, name);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/readlink.c b/guts/readlink.c
new file mode 100644
index 0000000..98931c6
--- /dev/null
+++ b/guts/readlink.c
@@ -0,0 +1,11 @@
+/*
+ * static ssize_t
+ * wrap_readlink(const char *path, char *buf, size_t bufsiz) {
+ * ssize_t rc = -1;
+ */
+
+ rc = wrap_readlinkat(AT_FDCWD, path, buf, bufsiz);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/readlinkat.c b/guts/readlinkat.c
new file mode 100644
index 0000000..13eeeb2
--- /dev/null
+++ b/guts/readlinkat.c
@@ -0,0 +1,25 @@
+/*
+ * static ssize_t
+ * wrap_readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) {
+ * ssize_t rc = -1;
+ */
+ rc = real_readlinkat(dirfd, path, buf, bufsiz);
+
+ if (rc > 0) {
+ /* strip out a leading chrooted part */
+ if (pseudo_chroot_len &&
+ !memcmp(buf, pseudo_chroot, pseudo_chroot_len)) {
+ if (buf[pseudo_chroot_len] == '/') {
+ memmove(buf, buf + pseudo_chroot_len, rc - pseudo_chroot_len);
+ rc -= pseudo_chroot_len;
+ } else if (buf[pseudo_chroot_len] == '\0') {
+ buf[0] = '/';
+ rc = 1;
+ }
+ /* otherwise, it's not really a match... */
+ }
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/realpath.c b/guts/realpath.c
new file mode 100644
index 0000000..5b1a5f8
--- /dev/null
+++ b/guts/realpath.c
@@ -0,0 +1,27 @@
+/*
+ * static char *
+ * wrap_realpath(const char *name, char *resolved_name) {
+ * char * rc = NULL;
+ */
+ char *rname = PSEUDO_ROOT_PATH(AT_FDCWD, name, 0);
+ size_t len;
+ if (!rname) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ if ((len = strlen(rname)) >= pseudo_sys_path_max()) {
+ free(rname);
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ if (resolved_name) {
+ memcpy(resolved_name, rname, len + 1);
+ free(rname);
+ rc = resolved_name;
+ } else {
+ rc = rname;
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/remove.c b/guts/remove.c
new file mode 100644
index 0000000..da6ecc3
--- /dev/null
+++ b/guts/remove.c
@@ -0,0 +1,19 @@
+/*
+ * static int
+ * wrap_remove(const char *path) {
+ * int rc = -1;
+ */
+ struct stat buf;
+ if (real___lxstat(_STAT_VER, path, &buf) == -1) {
+ errno = ENOENT;
+ return -1;
+ }
+ if (S_ISDIR(buf.st_mode)) {
+ rc = wrap_rmdir(path);
+ } else {
+ rc = wrap_unlink(path);
+ }
+
+/* return rc;
+ * }
+ */
diff --git a/guts/scandir.c b/guts/scandir.c
new file mode 100644
index 0000000..d44d60e
--- /dev/null
+++ b/guts/scandir.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_scandir(const char *path, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const void *, const void *)) {
+ * int rc = -1;
+ */
+
+ rc = real_scandir(path, namelist, filter, compar);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/scandir64.c b/guts/scandir64.c
new file mode 100644
index 0000000..9a7f0d8
--- /dev/null
+++ b/guts/scandir64.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_scandir64(const char *path, struct dirent64 ***namelist, int (*filter)(const struct dirent64 *), int (*compar)(const void *, const void *)) {
+ * int rc = -1;
+ */
+
+ rc = real_scandir64(path, namelist, filter, compar);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/symlinkat.c b/guts/symlinkat.c
index df1df06..577b1e7 100644
--- a/guts/symlinkat.c
+++ b/guts/symlinkat.c
@@ -6,6 +6,12 @@
struct stat64 buf;
char *roldname = 0;
+ if (oldname[0] == '/' && pseudo_chroot_len) {
+ size_t len = pseudo_chroot_len + strlen(oldname) + 1;
+ roldname = malloc(len);
+ snprintf(roldname, len, "%s%s", pseudo_chroot, oldname);
+ }
+
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
if (dirfd != AT_FDCWD) {
errno = ENOSYS;
diff --git a/guts/tempnam.c b/guts/tempnam.c
new file mode 100644
index 0000000..4da3c83
--- /dev/null
+++ b/guts/tempnam.c
@@ -0,0 +1,12 @@
+/*
+ * static char *
+ * wrap_tempnam(const char *template, const char *pfx) {
+ * char * rc = NULL;
+ */
+ pseudo_diag("tempnam() is so ludicrously insecure as to defy implementation.");
+ errno = ENOMEM;
+ rc = 0;
+
+/* return rc;
+ * }
+ */
diff --git a/guts/tmpnam.c b/guts/tmpnam.c
new file mode 100644
index 0000000..3e1aab2
--- /dev/null
+++ b/guts/tmpnam.c
@@ -0,0 +1,13 @@
+/*
+ * static char *
+ * wrap_tmpnam(char *s) {
+ * char * rc = NULL;
+ */
+
+ pseudo_diag("tmpnam() is so ludicrously insecure as to defy implementation.");
+ errno = ENOMEM;
+ rc = 0;
+
+/* return rc;
+ * }
+ */
diff --git a/guts/truncate.c b/guts/truncate.c
new file mode 100644
index 0000000..b82c578
--- /dev/null
+++ b/guts/truncate.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_truncate(const char *path, off_t length) {
+ * int rc = -1;
+ */
+
+ rc = real_truncate(path, length);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/truncate64.c b/guts/truncate64.c
new file mode 100644
index 0000000..e79b15a
--- /dev/null
+++ b/guts/truncate64.c
@@ -0,0 +1,11 @@
+/*
+ * static int
+ * wrap_truncate64(const char *path, off64_t length) {
+ * int rc = -1;
+ */
+
+ rc = real_truncate64(path, length);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/utime.c b/guts/utime.c
new file mode 100644
index 0000000..78a01b6
--- /dev/null
+++ b/guts/utime.c
@@ -0,0 +1,10 @@
+/*
+ * static int
+ * wrap_utime(const char *path, const struct utimbuf *buf) {
+ * int rc = -1;
+ */
+ rc = real_utime(path, buf);
+
+/* return rc;
+ * }
+ */
diff --git a/guts/utimes.c b/guts/utimes.c
new file mode 100644
index 0000000..0ca14d4
--- /dev/null
+++ b/guts/utimes.c
@@ -0,0 +1,10 @@
+/*
+ * static int
+ * wrap_utimes(const char *path, const struct timeval *times) {
+ * int rc = -1;
+ */
+ rc = real_utimes(path, times);
+
+/* return rc;
+ * }
+ */
diff --git a/pseudo.1 b/pseudo.1
index 43721ab..e2317f3 100644
--- a/pseudo.1
+++ b/pseudo.1
@@ -20,6 +20,7 @@
.B pseudo
.RB [ \-dflv ]
.RB [ \-P\ prefix ]
+.RB [ \-rR\ root ]
.RB [ \-t\ timeout ]
.RB [ command ]
.PP
@@ -122,17 +123,35 @@ detaches from the calling environment and runs as a daemon. The command
returns successfully if this appears to have succeeded, otherwise it
produces an error message and returns a failure status.
+.TP 8
.BI \-f\ (foreground)
Run in the foreground;
.I pseudo
runs as a server, and does not try to start other commands. This mode
is useful for debugging.
+.TP 8
.BI \-l\ (log)
Enable logging. The
.I pseudo
daemon will log every filesystem transaction in the log database.
+.TP 8
+.BI \-r\ root
+.TP 8
+.BI \-R\ root
+Set the
+.B PSEUDO_CHROOT
+environment variable, running as though the program had called
+.I chroot(2)
+on the specified path. With
+.BR \-r ,
+this implies changing the working directory to the specified directory;
+with
+.BR \-R ,
+it does not.
+
+.TP 8
.B \-t timeout
Set the timeout of the
.I pseudo
@@ -143,6 +162,7 @@ daemon shuts down automatically. The server never shuts down while it
has attached clients. Note that this does not prevent continued use;
new clients can restart the daemon if they need it.
+.TP 8
.BI \-v\ (verbose)
Increase the verbosity of the
.I pseudo
@@ -244,6 +264,12 @@ The following environment variables are used directly by
.IR pseudo :
.TP 8
+.B PSEUDO_CHROOT
+This variable holds the current emulated
+.I chroot(2)
+path. Paths that are relative to this are treated as though they were
+instead relative to the filesystem root.
+.TP 8
.B PSEUDO_DEBUG
This variable holds the "debug level" for
.I pseudo
diff --git a/pseudo.c b/pseudo.c
index 8352de2..eb5400f 100644
--- a/pseudo.c
+++ b/pseudo.c
@@ -42,6 +42,7 @@ int opt_d = 0;
int opt_f = 0;
int opt_l = 0;
long opt_p = 0;
+char *opt_r = NULL;
int opt_S = 0;
static int pseudo_op(pseudo_msg_t *msg, const char *tag);
@@ -49,7 +50,7 @@ static int pseudo_op(pseudo_msg_t *msg, const char *tag);
void
usage(int status) {
FILE *f = status ? stderr : stdout;
- fputs("Usage: pseudo [-dflv] [-P prefix] [-t timeout] [command]\n", f);
+ fputs("Usage: pseudo [-dflv] [-P prefix] [-rR root] [-t timeout] [command]\n", f);
fputs(" pseudo -h\n", f);
fputs(" pseudo [-dflv] [-P prefix] -S\n", f);
fputs(" pseudo [-dflv] [-P prefix] -V\n", f);
@@ -83,12 +84,12 @@ main(int argc, char *argv[]) {
pseudo_debug(2, "can't run daemon with libpseudo in LD_PRELOAD\n");
if (getenv("PSEUDO_RELOADED")) {
pseudo_diag("I can't seem to make LD_PRELOAD go away. Sorry.\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
setenv("PSEUDO_RELOADED", "YES", 1);
pseudo_dropenv();
execve(argv[0], argv, environ);
- exit(1);
+ exit(EXIT_FAILURE);
}
unsetenv("PSEUDO_RELOADED");
@@ -96,7 +97,7 @@ main(int argc, char *argv[]) {
* wrong. The + suppresses this annoying behavior, but may not
* be compatible with sane option libraries.
*/
- while ((o = getopt(argc, argv, "+dfhlP:St:vV")) != -1) {
+ while ((o = getopt(argc, argv, "+dfhlP:r:R:St:vV")) != -1) {
switch (o) {
case 'd':
/* run as daemon */
@@ -115,7 +116,21 @@ main(int argc, char *argv[]) {
opt_l = 1;
break;
case 'P':
- setenv("PSEUDO_PREFIX", optarg, 1);
+ pseudo_client_getcwd();
+ s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW);
+ if (!s)
+ pseudo_diag("Can't resolve path '%s'\n", optarg);
+ setenv("PSEUDO_PREFIX", s, 1);
+ break;
+ case 'r': /* FALLTHROUGH */
+ case 'R':
+ pseudo_client_getcwd();
+ s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW);
+ if (!s)
+ pseudo_diag("Can't resolve path '%s'\n", optarg);
+ setenv("PSEUDO_CHROOT", s, 1);
+ if (o == 'r')
+ opt_r = s;
break;
case 'S':
opt_S = 1;
@@ -150,7 +165,7 @@ main(int argc, char *argv[]) {
if (!pseudo_get_prefix(argv[0])) {
pseudo_diag("Can't figure out prefix. Set PSEUDO_PREFIX or invoke with full path.\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
if (opt_S) {
@@ -159,15 +174,22 @@ main(int argc, char *argv[]) {
if (opt_d && opt_f) {
pseudo_diag("You cannot run a foregrounded daemon.\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
if (opt_f || opt_d) {
if (argc > optind) {
pseudo_diag("pseudo: running program implies spawning background daemon.\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
} else {
+ if (opt_r) {
+ if (chdir(opt_r) == -1) {
+ pseudo_diag("failed to chdir to '%s': %s\n",
+ opt_r, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
if (argc > optind) {
pseudo_debug(2, "running command: %s\n",
argv[optind]);
@@ -190,7 +212,7 @@ main(int argc, char *argv[]) {
pseudo_diag("pseudo: can't run %s: %s\n",
argv[0], strerror(errno));
}
- exit(1);
+ exit(EXIT_FAILURE);
}
/* if we got here, we are not running a command, and we are not in
* a pseudo environment.
@@ -201,13 +223,13 @@ main(int argc, char *argv[]) {
lockname = pseudo_prefix_path(PSEUDO_LOCKFILE);
if (!lockname) {
pseudo_diag("Couldn't allocate a file path.\n");
- exit(1);
+ exit(EXIT_FAILURE);
}
lockfd = open(lockname, O_RDWR | O_CREAT, 0644);
if (lockfd < 0) {
pseudo_diag("Can't open or create lockfile %s: %s\n",
lockname, strerror(errno));
- exit(1);
+ exit(EXIT_FAILURE);
}
free(lockname);
diff --git a/pseudo.h b/pseudo.h
index 57681fb..c3a6f31 100644
--- a/pseudo.h
+++ b/pseudo.h
@@ -18,6 +18,7 @@
*
*/
#include <stdlib.h>
+#include <fcntl.h>
typedef enum {
OP_UNKNOWN = -1,
@@ -25,6 +26,7 @@ typedef enum {
OP_CHDIR,
OP_CHMOD,
OP_CHOWN,
+ OP_CHROOT,
OP_CLOSE,
OP_CREAT,
OP_DUP,
@@ -113,7 +115,7 @@ extern int pseudo_diag(char *, ...) __attribute__ ((format (printf, 1, 2)));
void pseudo_new_pid(void);
/* pseudo_fix_path resolves symlinks up to this depth */
#define PSEUDO_MAX_LINK_RECURSION 16
-extern char *pseudo_fix_path(char *, const char *, size_t, size_t *, int);
+extern char *pseudo_fix_path(const char *, const char *, size_t, size_t, size_t *, int);
extern void pseudo_dropenv(void);
extern void pseudo_setupenv(char *);
extern char *pseudo_prefix_path(char *);
@@ -128,3 +130,27 @@ extern char *pseudo_version;
#define PSEUDO_LOGFILE PSEUDO_DATA "pseudo.log"
#define PSEUDO_PIDFILE PSEUDO_DATA "pseudo.pid"
#define PSEUDO_SOCKET PSEUDO_DATA "pseudo.socket"
+
+/* some systems might not have *at(). We like to define operations in
+ * terms of each other, and for instance, open(...) is the same as
+ * openat(AT_FDCWD, ...). If no AT_FDCWD is provided, any value that can't
+ * be a valid file descriptor will do. Using -2 because -1 could be
+ * mistaken for a failed syscall return. Similarly, any value which isn't
+ * zero will do to fake AT_SYMLINK_NOFOLLOW. Finally, if this happened,
+ * we set our own flag we can use to indicate that dummy implementations
+ * of the _at functions are needed.
+ */
+#ifndef AT_FDCWD
+#define AT_FDCWD -2
+#define AT_SYMLINK_NOFOLLOW 1
+#define PSEUDO_NO_REAL_AT_FUNCTIONS
+#endif
+
+/* Likewise, someone might not have O_LARGEFILE (the flag equivalent to
+ * using open64()). Since open64() is the same as O_LARGEFILE in flags,
+ * we implement it that way... If the system has no O_LARGEFILE, we'll
+ * just call open() with nothing special.
+ */
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
diff --git a/pseudo_client.c b/pseudo_client.c
index 5ada209..ca5c41d 100644
--- a/pseudo_client.c
+++ b/pseudo_client.c
@@ -44,6 +44,9 @@ static int server_pid = 0;
int pseudo_dir_fd = -1;
char *pseudo_cwd = 0;
size_t pseudo_cwd_len;
+char *pseudo_chroot = 0;
+size_t pseudo_chroot_len = 0;
+char *pseudo_cwd_rel = 0;
static char **fd_paths = 0;
static int nfds = 0;
@@ -77,6 +80,32 @@ pseudo_client_touchgid(void) {
setenv("PSEUDO_GIDS", gidbuf, 1);
}
+int
+pseudo_client_chroot(const char *path) {
+ /* free old value */
+ free(pseudo_chroot);
+
+ pseudo_debug(2, "client chroot: %s\n", path);
+ if (!strcmp(path, "/")) {
+ pseudo_chroot_len = 0;
+ pseudo_chroot = 0;
+ unsetenv("PSEUDO_CHROOT");
+ return 0;
+ }
+ /* allocate new value */
+ pseudo_chroot_len = strlen(path);
+ pseudo_chroot = malloc(pseudo_chroot_len + 1);
+ if (!pseudo_chroot) {
+ pseudo_diag("Couldn't allocate chroot directory buffer.\n");
+ pseudo_chroot_len = 0;
+ errno = ENOMEM;
+ return -1;
+ }
+ memcpy(pseudo_chroot, path, pseudo_chroot_len + 1);
+ setenv("PSEUDO_CHROOT", pseudo_chroot, 1);
+ return 0;
+}
+
char *
pseudo_root_path(const char *func, int line, int dirfd, const char *path, int leave_last) {
char *rc;
@@ -102,13 +131,26 @@ pseudo_client_getcwd(void) {
pseudo_diag("Can't allocate CWD buffer!\n");
return -1;
}
- pseudo_debug(2, "getcwd: trying to find cwd.\n");
+ pseudo_debug(3, "getcwd: trying to find cwd.\n");
if (getcwd(cwd, pseudo_path_max())) {
/* cwd now holds a canonical path to current directory */
- pseudo_cwd_len = strlen(cwd);
- pseudo_debug(3, "getcwd okay: [%s] %d bytes\n", cwd, (int) pseudo_cwd_len);
free(pseudo_cwd);
pseudo_cwd = cwd;
+ pseudo_cwd_len = strlen(pseudo_cwd);
+ pseudo_debug(3, "getcwd okay: [%s] %d bytes\n", pseudo_cwd, (int) pseudo_cwd_len);
+ if (pseudo_chroot_len &&
+ pseudo_cwd_len >= pseudo_chroot_len &&
+ !memcmp(pseudo_cwd, pseudo_chroot, pseudo_chroot_len) &&
+ (pseudo_cwd[pseudo_chroot_len] == '\0' ||
+ pseudo_cwd[pseudo_chroot_len] == '/')) {
+ pseudo_cwd_rel = pseudo_cwd + pseudo_chroot_len;
+ } else {
+ pseudo_cwd_rel = pseudo_cwd;
+ }
+ pseudo_debug(4, "abscwd: <%s>\n", pseudo_cwd);
+ if (pseudo_cwd_rel != pseudo_cwd) {
+ pseudo_debug(4, "relcwd: <%s>\n", pseudo_cwd_rel);
+ }
return 0;
} else {
pseudo_diag("Can't get CWD: %s\n", strerror(errno));
@@ -171,7 +213,6 @@ pseudo_client_reset() {
close(connect_fd);
connect_fd = -1;
}
- pseudo_client_getcwd();
if (!pseudo_inited) {
char *env;
@@ -187,8 +228,19 @@ pseudo_client_reset() {
&pseudo_rgid, &pseudo_egid,
&pseudo_sgid, &pseudo_fuid);
+ env = getenv("PSEUDO_CHROOT");
+ if (env) {
+ pseudo_chroot = strdup(env);
+ if (pseudo_chroot) {
+ pseudo_chroot_len = strlen(pseudo_chroot);
+ } else {
+ pseudo_diag("can't store chroot path (%s)\n", env);
+ }
+ }
+
pseudo_inited = 1;
}
+ pseudo_client_getcwd();
pseudo_magic();
}
@@ -592,6 +644,7 @@ static char *
base_path(int dirfd, const char *path, int leave_last) {
char *basepath = 0;
size_t baselen = 0;
+ size_t minlen = 0;
char *newpath;
if (path[0] != '/') {
@@ -610,9 +663,23 @@ base_path(int dirfd, const char *path, int leave_last) {
pseudo_diag("unknown base path for fd %d, path %s\n", dirfd, path);
return 0;
}
+ /* if there's a chroot path, and it's the start of basepath,
+ * flag it for pseudo_fix_path
+ */
+ if (pseudo_chroot_len && baselen >= pseudo_chroot_len &&
+ !memcmp(basepath, pseudo_chroot, pseudo_chroot_len) &&
+ (basepath[pseudo_chroot_len] == '\0' || basepath[pseudo_chroot_len] == '/')) {
+
+ minlen = pseudo_chroot_len;
+ }
+ } else if (pseudo_chroot_len) {
+ /* "absolute" is really relative to chroot path */
+ basepath = pseudo_chroot;
+ baselen = pseudo_chroot_len;
+ minlen = pseudo_chroot_len;
}
- newpath = pseudo_fix_path(basepath, path, baselen, NULL, leave_last);
+ newpath = pseudo_fix_path(basepath, path, minlen, baselen, NULL, leave_last);
pseudo_debug(4, "base_path: %s</>%s\n",
basepath ? basepath : "<nil>",
path ? path : "<nil>");
@@ -717,6 +784,13 @@ pseudo_client_op(op_id_t op, int fd, int dirfd, const char *path, const struct s
pseudo_client_getcwd();
do_request = 0;
break;
+ case OP_CHROOT:
+ if (pseudo_client_chroot(path) == 0) {
+ /* return a non-zero value to show non-failure */
+ result = &msg;
+ }
+ do_request = 0;
+ break;
case OP_OPEN:
pseudo_client_path(fd, path);
do_request = 1;
diff --git a/pseudo_client.h b/pseudo_client.h
index a1e8d45..85bfd8f 100644
--- a/pseudo_client.h
+++ b/pseudo_client.h
@@ -43,10 +43,14 @@ extern int pseudo_dir_fd;
/* support related to chroot/getcwd/etc. */
extern int pseudo_client_getcwd(void);
+extern int pseudo_client_chroot(const char *);
extern char *pseudo_root_path(const char *, int, int, const char *, int);
#define PSEUDO_ROOT_PATH(x, y, z) pseudo_root_path(__func__, __LINE__, (x), (y), (z));
extern char *pseudo_cwd;
extern size_t pseudo_cwd_len;
+extern char *pseudo_cwd_rel;
+extern char *pseudo_chroot;
+extern size_t pseudo_chroot_len;
/* Root can read, write, and execute files which have no read, write,
* or execute permissions.
@@ -63,27 +67,3 @@ extern size_t pseudo_cwd_len;
#define PSEUDO_FS_MODE(mode) ((mode) | S_IRUSR | S_IWUSR | S_IXUSR)
#define PSEUDO_DB_MODE(fs_mode, user_mode) (((fs_mode) & ~0700) | ((user_mode & 0700)))
-/* some systems might not have *at(). We like to define operations in
- * terms of each other, and for instance, open(...) is the same as
- * openat(AT_FDCWD, ...). If no AT_FDCWD is provided, any value that can't
- * be a valid file descriptor will do. Using -2 because -1 could be
- * mistaken for a failed syscall return. Similarly, any value which isn't
- * zero will do to fake AT_SYMLINK_NOFOLLOW. Finally, if this happened,
- * we set our own flag we can use to indicate that dummy implementations
- * of the _at functions are needed.
- */
-#ifndef AT_FDCWD
-#define AT_FDCWD -2
-#define AT_SYMLINK_NOFOLLOW 1
-#define PSEUDO_NO_REAL_AT_FUNCTIONS
-#endif
-
-/* Likewise, someone might not have O_LARGEFILE (the flag equivalent to
- * using open64()). Since open64() is the same as O_LARGEFILE in flags,
- * we implement it that way... If the system has no O_LARGEFILE, we'll
- * just call open() with nothing special.
- */
-#ifndef O_LARGEFILE
-#define O_LARGEFILE 0
-#endif
-
diff --git a/pseudo_db.c b/pseudo_db.c
index 665f823..ebbeb69 100644
--- a/pseudo_db.c
+++ b/pseudo_db.c
@@ -59,6 +59,7 @@ id_row op_rows[] = {
OP_ROW(OP_CHDIR, "chdir"),
OP_ROW(OP_CHMOD, "chmod"),
OP_ROW(OP_CHOWN, "chown"),
+ OP_ROW(OP_CHROOT, "chroot"),
OP_ROW(OP_CLOSE, "close"),
OP_ROW(OP_CREAT, "creat"),
OP_ROW(OP_DUP, "dup"),
diff --git a/pseudo_table.c b/pseudo_table.c
index 7c69037..c5c0aec 100644
--- a/pseudo_table.c
+++ b/pseudo_table.c
@@ -28,6 +28,7 @@ static char *operation_names[] = {
"chdir",
"chmod",
"chown",
+ "chroot",
"close",
"creat",
"dup",
diff --git a/pseudo_util.c b/pseudo_util.c
index 4bc2d5a..dcd571f 100644
--- a/pseudo_util.c
+++ b/pseudo_util.c
@@ -42,8 +42,8 @@ int pseudo_util_debug_fd = 2;
static int debugged_newline = 1;
static char pid_text[32];
static size_t pid_len;
-static int pseudo_append_element(char **newpath, size_t *allocated, char **current, const char *element, size_t elen, int leave_last);
-static int pseudo_append_elements(char **newpath, size_t *allocated, char **current, const char *elements, size_t elen, int leave_last);
+static int pseudo_append_element(char **pnewpath, char **proot, size_t *pallocated, char **pcurrent, const char *element, size_t elen, int leave_this);
+static int pseudo_append_elements(char **newpath, char **root, size_t *allocated, char **current, const char *elements, size_t elen, int leave_last);
extern char **environ;
static ssize_t pseudo_max_pathlen = -1;
static ssize_t pseudo_sys_max_pathlen = -1;
@@ -136,18 +136,22 @@ pseudo_new_pid() {
* the symlink, appending each element in turn the same way.
*/
static int
-pseudo_append_element(char **pnewpath, size_t *pallocated, char **pcurrent, const char *element, size_t elen, int leave_this) {
+pseudo_append_element(char **pnewpath, char **proot, size_t *pallocated, char **pcurrent, const char *element, size_t elen, int leave_this) {
static int link_recursion = 0;
size_t curlen, allocated;
- char *newpath, *current;
+ char *newpath, *current, *root;
struct stat64 buf;
- if (!pnewpath || !*pnewpath || !pallocated || !pcurrent || !*pcurrent || !element) {
+ if (!pnewpath || !*pnewpath ||
+ !pcurrent || !*pcurrent ||
+ !proot || !*proot ||
+ !pallocated || !element) {
pseudo_diag("pseudo_append_element: invalid args.\n");
return -1;
}
newpath = *pnewpath;
allocated = *pallocated;
current = *pcurrent;
+ root = *proot;
/* sanity-check: ignore // or /./ */
if (elen == 0 || (elen == 1 && *element == '.')) {
return 1;
@@ -155,12 +159,12 @@ pseudo_append_element(char **pnewpath, size_t *pallocated, char **pcurrent, cons
/* backtrack for .. */
if (elen == 2 && element[0] == '.' && element[1] == '.') {
/* if newpath's whole contents are '/', do nothing */
- if (current <= newpath + 1)
+ if (current <= root + 1)
return 1;
/* backtrack to the character before the / */
current -= 2;
/* now find the previous slash */
- while (current > newpath && *current != '/') {
+ while (current > root && *current != '/') {
--current;
}
/* and point to the nul just past it */
@@ -181,11 +185,13 @@ pseudo_append_element(char **pnewpath, size_t *pallocated, char **pcurrent, cons
}
memcpy(bigger, newpath, curlen);
current = bigger + curlen;
+ root = bigger + (root - newpath);
free(newpath);
newpath = bigger;
allocated = big;
*pnewpath = newpath;
*pcurrent = current;
+ *proot = root;
*pallocated = allocated;
}
memcpy(current, element, elen);
@@ -225,7 +231,7 @@ pseudo_append_element(char **pnewpath, size_t *pallocated, char **pcurrent, cons
/* append all the elements in series */
*pcurrent = current;
++link_recursion;
- retval = pseudo_append_elements(pnewpath, pallocated, pcurrent, linkbuf, linklen, 0);
+ retval = pseudo_append_elements(pnewpath, proot, pallocated, pcurrent, linkbuf, linklen, 0);
--link_recursion;
return retval;
}
@@ -238,10 +244,13 @@ pseudo_append_element(char **pnewpath, size_t *pallocated, char **pcurrent, cons
}
static int
-pseudo_append_elements(char **newpath, size_t *allocated, char **current, const char *element, size_t elen, int leave_last) {
+pseudo_append_elements(char **newpath, char **root, size_t *allocated, char **current, const char *element, size_t elen, int leave_last) {
int retval = 1;
const char * start = element;
- if (!newpath || !current || !element || !*newpath || !*current) {
+ if (!newpath || !*newpath ||
+ !root || !*root ||
+ !current || !*current ||
+ !element) {
pseudo_diag("pseudo_append_elements: invalid arguments.");
return -1;
}
@@ -259,13 +268,13 @@ pseudo_append_elements(char **newpath, size_t *allocated, char **current, const
break;
case 1: /* path => '?/' */
if (*element != '.') {
- if (pseudo_append_element(newpath, allocated, current, element, this_elen, leave_this) == -1) {
+ if (pseudo_append_element(newpath, root, allocated, current, element, this_elen, leave_this) == -1) {
retval = -1;
}
}
break;
default:
- if (pseudo_append_element(newpath, allocated, current, element, this_elen, leave_this) == -1) {
+ if (pseudo_append_element(newpath, root, allocated, current, element, this_elen, leave_this) == -1) {
retval = -1;
}
break;
@@ -284,35 +293,40 @@ pseudo_append_elements(char **newpath, size_t *allocated, char **current, const
* we ignore base.
*/
char *
-pseudo_fix_path(char *base, const char *path, size_t baselen, size_t *lenp, int leave_last) {
+pseudo_fix_path(const char *base, const char *path, size_t rootlen, size_t baselen, size_t *lenp, int leave_last) {
size_t newpathlen, pathlen;
char *newpath;
char *current;
+ char *effective_root;
if (!path) {
pseudo_diag("can't fix empty path.\n");
return 0;
}
pathlen = strlen(path);
+ newpathlen = pathlen;
+ if (baselen && path[0] != '/') {
+ newpathlen += baselen + 2;
+ }
/* allow a bit of slush. overallocating a bit won't
* hurt. rounding to 256's in the hopes that it makes life
* easier for the library.
*/
- if (path[0] == '/') {
- newpathlen = round_up(pathlen + 1, 256);
- newpath = malloc(newpathlen);
- if (!newpath) {
- pseudo_diag("allocation failed seeking memory for path (%s).\n", path);
- return 0;
- }
- current = newpath;
- ++path;
- } else {
- newpathlen = round_up(baselen + pathlen + 2, 256);
- newpath = malloc(newpathlen);
- memcpy(newpath, base, baselen);
- current = newpath + baselen;
+ newpathlen = round_up(newpathlen, 256);
+ newpath = malloc(newpathlen);
+ if (!newpath) {
+ pseudo_diag("allocation failed seeking memory for path (%s).\n", path);
+ return 0;
+ }
+ current = newpath;
+ if (baselen) {
+ memcpy(current, base, baselen);
+ current += baselen;
}
+ /* "root" is a pointer to the beginning of the *modifiable*
+ * part of the string; you can't back up over it.
+ */
+ effective_root = newpath + rootlen;
*current++ = '/';
*current = '\0';
/* at any given point:
@@ -323,12 +337,12 @@ pseudo_fix_path(char *base, const char *path, size_t baselen, size_t *lenp, int
* oldpath is the starting point of path
* (path - oldpath) is how far into path we are
*/
- if (pseudo_append_elements(&newpath, &newpathlen, &current, path, pathlen, leave_last) != -1) {
+ if (pseudo_append_elements(&newpath, &effective_root, &newpathlen, &current, path, pathlen, leave_last) != -1) {
--current;
- if (*current == '/') {
+ if (*current == '/' && current > effective_root) {
*current = '\0';
}
- pseudo_debug(3, "%s + %s => <%s>\n",
+ pseudo_debug(5, "%s + %s => <%s>\n",
base ? base : "<nil>",
path ? path : "<nil>",
newpath ? newpath : "<nil>");
@@ -478,7 +492,7 @@ pseudo_get_prefix(char *pathname) {
s += snprintf(s, pseudo_path_max() - (s - mypath), "/%s",
pathname);
}
- tmp_path = pseudo_fix_path(NULL, mypath, 0, 0, AT_SYMLINK_NOFOLLOW);
+ tmp_path = pseudo_fix_path(NULL, mypath, 0, 0, 0, AT_SYMLINK_NOFOLLOW);
/* point s to the end of the fixed path */
if (strlen(tmp_path) >= pseudo_path_max()) {
pseudo_diag("Can't expand path '%s' -- expansion exceeds %d.\n",
diff --git a/wrapfuncs.in b/wrapfuncs.in
index 5b262e7..2eabd5d 100644
--- a/wrapfuncs.in
+++ b/wrapfuncs.in
@@ -60,3 +60,31 @@ int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
int setreuid(uid_t ruid, uid_t euid);
int setregid(gid_t rgid, gid_t egid);
int setgroups(size_t size, const gid_t *list);
+# primarily for use with chroot()
+int chroot(const char *path);
+ssize_t readlink(const char *path, char *buf, size_t bufsiz); /* flags=AT_SYMLINK_NOFOLLOW */
+ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz); /* flags=AT_SYMLINK_NOFOLLOW */
+int access(const char *path, int mode);
+int acct(const char *path);
+char *canonicalize_file_name(const char *filename);
+int eaccess(const char *path, int mode);
+int euidaccess(const char *path, int mode);
+FTS *fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **));
+int ftw(const char *path, int (*fn)(const char *, const struct stat *, int), int nopenfd);
+int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int nopenfd, int flag);
+int glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob);
+int lutimes(const char *path, const struct timeval *tv);
+char *mkdtemp(char *template);
+int mkfifo(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */
+int mkfifoat(int dirfd, const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */
+char *mktemp(char *template);
+long pathconf(const char *path, int name);
+char *realpath(const char *name, char *resolved_name);
+int remove(const char *path); /* flags=AT_SYMLINK_NOFOLLOW */
+DIR *opendir(const char *path);
+int scandir(const char *path, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const void *, const void *));
+char *tempnam(const char *template, const char *pfx);
+char *tmpnam(char *s);
+int truncate(const char *path, off_t length);
+int utime(const char *path, const struct utimbuf *buf);
+int utimes(const char *path, const struct timeval *times);
diff --git a/wrapfuncs64.in b/wrapfuncs64.in
index 8470c04..4e6ad34 100644
--- a/wrapfuncs64.in
+++ b/wrapfuncs64.in
@@ -7,3 +7,9 @@ int __lxstat64(int ver, const char *path, struct stat64 *buf); /* flags=AT_SYMLI
int __fxstat64(int ver, int fd, struct stat64 *buf);
int __fxstatat64(int ver, int dirfd, const char *path, struct stat64 *buf, int flags);
FILE *fopen64(const char *path, const char *mode);
+int nftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int, struct FTW *), int nopenfd, int flag);
+FILE *freopen64(const char *path, const char *mode, FILE *stream);
+int ftw64(const char *path, int (*fn)(const char *, const struct stat64 *, int), int nopenfd);
+int glob64(const char *pattern, int flags, int (*errfunc)(const char *, int), glob64_t *pglob);
+int scandir64(const char *path, struct dirent64 ***namelist, int (*filter)(const struct dirent64 *), int (*compar)(const void *, const void *));
+int truncate64(const char *path, off64_t length);