From 66bb8267103df6658ee984a89f1d8445f42d072e Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 30 Mar 2011 13:00:03 +0200 Subject: libmount: use chdir() and NOFOLLOW umount flag for umount operation Signed-off-by: Karel Zak --- shlibs/mount/src/context_mount.c | 2 +- shlibs/mount/src/context_umount.c | 41 ++++++++++++++++++++-- shlibs/mount/src/mountP.h | 1 + shlibs/mount/src/utils.c | 73 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 3 deletions(-) (limited to 'shlibs') diff --git a/shlibs/mount/src/context_mount.c b/shlibs/mount/src/context_mount.c index eaba33c05..58c3ec10e 100644 --- a/shlibs/mount/src/context_mount.c +++ b/shlibs/mount/src/context_mount.c @@ -392,7 +392,7 @@ static int do_mount(struct libmnt_context *cxt, const char *try_type) DBG(CXT, mnt_debug_h(cxt, "%smount(2) " "[source=%s, target=%s, type=%s, " - " mountflags=%08lx, mountdata=%s]", + " mountflags=0x%08lx, mountdata=%s]", (cxt->flags & MNT_FL_FAKE) ? "(FAKE) " : "", src, target, type, flags, cxt->mountdata ? "yes" : "")); diff --git a/shlibs/mount/src/context_umount.c b/shlibs/mount/src/context_umount.c index cdf919854..0e69fe5f9 100644 --- a/shlibs/mount/src/context_umount.c +++ b/shlibs/mount/src/context_umount.c @@ -424,10 +424,25 @@ int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg) return rc; } +/* Check whether the kernel supports UMOUNT_NOFOLLOW flag */ +static int umount_nofollow_support(void) +{ + int res = umount2("", UMOUNT_UNUSED); + if (res != -1 || errno != EINVAL) + return 0; + + res = umount2("", UMOUNT_NOFOLLOW); + if (res != -1 || errno != ENOENT) + return 0; + + return 1; +} + static int do_umount(struct libmnt_context *cxt) { int rc = 0, flags = 0; const char *src, *target; + char *tgtbuf = NULL; assert(cxt); assert(cxt->fs); @@ -446,16 +461,38 @@ static int do_umount(struct libmnt_context *cxt) if (cxt->flags & MNT_FL_FAKE) return 0; + DBG(CXT, mnt_debug_h(cxt, "do umount")); + + if (cxt->restricted) { + /* + * extra paranoa for non-root users + * -- chdir to the parent of the mountpoint and use NOFOLLOW + * flag to avoid races and symlink attacks. + */ + if (umount_nofollow_support()) + flags |= UMOUNT_NOFOLLOW; + + rc = mnt_chdir_to_parent(target, &tgtbuf); + if (rc) + return rc; + target = tgtbuf; + } + if (cxt->flags & MNT_FL_LAZY) flags |= MNT_DETACH; else if (cxt->flags & MNT_FL_FORCE) flags |= MNT_FORCE; - rc = flasg ? umount2(target, flags) : umount(target); + DBG(CXT, mnt_debug_h(cxt, "umount(2) [target='%s', flags=0x%08x]", + target, flags)); + + rc = flags ? umount2(target, flags) : umount(target); if (rc < 0) cxt->syscall_status = -errno; + free(tgtbuf); + /* * try remount read-only */ @@ -468,7 +505,7 @@ static int do_umount(struct libmnt_context *cxt) "umount(2) failed [errno=%d] -- tring remount read-only", -cxt->syscall_status)); - rc = mount(src, target, NULL, + rc = mount(src, mnt_fs_get_target(cxt->fs), NULL, MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); if (rc < 0) { cxt->syscall_status = -errno; diff --git a/shlibs/mount/src/mountP.h b/shlibs/mount/src/mountP.h index 0d773ba0a..abaa6b2c6 100644 --- a/shlibs/mount/src/mountP.h +++ b/shlibs/mount/src/mountP.h @@ -111,6 +111,7 @@ extern int mnt_run_test(struct libmnt_test *tests, int argc, char *argv[]); extern int endswith(const char *s, const char *sx); extern int startswith(const char *s, const char *sx); +extern int mnt_chdir_to_parent(const char *target, char **filename); extern char *mnt_get_username(const uid_t uid); extern int mnt_get_uid(const char *username, uid_t *uid); extern int mnt_get_gid(const char *groupname, gid_t *gid); diff --git a/shlibs/mount/src/utils.c b/shlibs/mount/src/utils.c index b02b6fe4d..1261a2934 100644 --- a/shlibs/mount/src/utils.c +++ b/shlibs/mount/src/utils.c @@ -71,6 +71,58 @@ static char *stripoff_last_component(char *path) return ++p; } +/* Note that the @target has to be absolute path (so at least "/") + */ +int mnt_chdir_to_parent(const char *target, char **filename) +{ + char *path, *last = NULL; + char cwd[PATH_MAX]; + int rc = -EINVAL; + + if (!target || *target != '/') + return -EINVAL; + + path = strdup(target); + if (!path) + return -ENOMEM; + + if (*(path + 1) != '\0') { + last = stripoff_last_component(path); + if (!last) + goto err; + } + if (!*path) + *path = '/'; /* root */ + + if (chdir(path) == -1) { + DBG(UTILS, mnt_debug("failed to chdir to %s: %m", path)); + rc = -errno; + goto err; + } + if (!getcwd(cwd, sizeof(cwd))) { + DBG(UTILS, mnt_debug("failed to obtain current directory: %m")); + rc = -errno; + goto err; + } + if (strcmp(cwd, path) != 0) { + DBG(UTILS, mnt_debug("path moved (%s -> %s)", path, cwd)); + goto err; + } + + DBG(CXT, mnt_debug("current directory moved to %s", path)); + + *filename = path; + + if (!last || !*last) + memcpy(*filename, ".", 2); + else + memcpy(*filename, last, strlen(last) + 1); + return 0; +err: + free(path); + return rc; +} + /** * mnt_mangle: * @str: string @@ -800,6 +852,26 @@ int test_filesystems(struct libmnt_test *ts, int argc, char *argv[]) return rc; } +int test_chdir(struct libmnt_test *ts, int argc, char *argv[]) +{ + int rc; + char *path = canonicalize_path(argv[1]), + *last = NULL; + + if (!path) + return -errno; + + rc = mnt_chdir_to_parent(path, &last); + if (!rc) { + printf("path='%s', abs='%s', last='%s'\n", + argv[1], path, last); + } + free(path); + free(last); + return rc; +} + + int main(int argc, char *argv[]) { struct libmnt_test tss[] = { @@ -810,6 +882,7 @@ int main(int argc, char *argv[]) { "--ends-with", test_endswith, " " }, { "--mountpoint", test_mountpoint, "" }, { "--fs-root", test_fsroot, "" }, + { "--cd-parent", test_chdir, "" }, { NULL } }; -- cgit v1.2.3-55-g7522