2009-01-01 05:22:00

by Tetsuo Handa

[permalink] [raw]
Subject: [TOMOYO #14 (mmotm 2008-12-30-16-05) 03/10] Introduce d_realpath().

To remove factors that make pathname based access control difficult
(e.g. symbolic links, "..", "//", chroot() etc.), a variant of d_path()
which traverses up to the root of the namespace is needed.

This patch introduces d_realpath(), a variant of d_path().
While d_path() stops traversing at current->fs->root,
d_realpath() doesn't stop traversiong at current->fs->root.

Three differences compared to d_path().
(1) Ignores current process's root directory.
(2) Trailing '/' is added if the pathname refers to a directory.
(3) /proc/PID/ is represented as /proc/self/ if PID equals current->tgid.

Signed-off-by: Kentaro Takeda <[email protected]>
Signed-off-by: Tetsuo Handa <[email protected]>
Signed-off-by: Toshiharu Harada <[email protected]>
---
fs/dcache.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/dcache.h | 1
2 files changed, 85 insertions(+)

--- linux-2.6.28-mm1.orig/fs/dcache.c
+++ linux-2.6.28-mm1/fs/dcache.c
@@ -32,6 +32,7 @@
#include <linux/seqlock.h>
#include <linux/swap.h>
#include <linux/bootmem.h>
+#include <linux/magic.h>
#include "internal.h"


@@ -1978,6 +1979,89 @@ Elong:
}

/**
+ * d_realpath - Get the realpath of a dentry.
+ *
+ * @path: Pointer to "struct path".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns pointer to the realpath on success, an error code othersize.
+ *
+ * If @dentry is a directory, trailing '/' is appended.
+ * /proc/PID/ is replaced by /proc/self/ if PID == task_tgid_nr_ns(current).
+ */
+char *d_realpath(struct path *path, char *buffer, int buflen)
+{
+ struct dentry *dentry = path->dentry;
+ struct vfsmount *vfsmnt = path->mnt;
+ char *end = buffer + buflen;
+
+ spin_lock(&dcache_lock);
+ spin_lock(&vfsmount_lock);
+ if (buflen < 1 || prepend(&end, &buflen, "", 1))
+ goto Elong;
+ /*
+ * Exception: Add trailing '/' for directory.
+ */
+ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode) &&
+ prepend(&end, &buflen, "/", 1))
+ goto Elong;
+ for (;;) {
+ struct dentry *parent;
+ const char *name;
+ int name_len;
+ unsigned long pid;
+
+ if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
+ /* Global root? */
+ if (vfsmnt->mnt_parent == vfsmnt)
+ break;
+ dentry = vfsmnt->mnt_mountpoint;
+ vfsmnt = vfsmnt->mnt_parent;
+ continue;
+ }
+ parent = dentry->d_parent;
+ prefetch(parent);
+ /*
+ * Exception: Use /proc/self/ rather than /proc/\$/
+ * for current process.
+ */
+ name = dentry->d_name.name;
+ name_len = dentry->d_name.len;
+ if (IS_ROOT(parent) &&
+ parent->d_sb->s_magic == PROC_SUPER_MAGIC &&
+ !strict_strtoul(name, 10, &pid)) {
+ const pid_t tgid
+ = task_tgid_nr_ns(current,
+ dentry->d_sb->s_fs_info);
+ if (tgid && (pid_t) pid == tgid) {
+ name = "self";
+ name_len = 4;
+ }
+ }
+ if (prepend(&end, &buflen, name, name_len))
+ goto Elong;
+ if (prepend(&end, &buflen, "/", 1))
+ goto Elong;
+ dentry = parent;
+ }
+ if (*end == '/') {
+ /* hit the slash */
+ buflen++;
+ end++;
+ }
+ if (prepend_name(&end, &buflen, &dentry->d_name))
+ goto Elong;
+ out:
+ spin_unlock(&vfsmount_lock);
+ spin_unlock(&dcache_lock);
+ return end;
+ Elong:
+ end = ERR_PTR(-ENAMETOOLONG);
+ goto out;
+}
+
+/**
* d_path - return the path of a dentry
* @path: path to report
* @buf: buffer to return value in
--- linux-2.6.28-mm1.orig/include/linux/dcache.h
+++ linux-2.6.28-mm1/include/linux/dcache.h
@@ -305,6 +305,7 @@ extern char *dynamic_dname(struct dentry
extern char *__d_path(const struct path *path, struct path *root, char *, int);
extern char *d_path(const struct path *, char *, int);
extern char *dentry_path(struct dentry *, char *, int);
+extern char *d_realpath(struct path *, char *, int);

/* Allocation counts.. */


--


2009-01-05 09:10:03

by James Morris

[permalink] [raw]
Subject: Re: [TOMOYO #14 (mmotm 2008-12-30-16-05) 03/10] Introduce d_realpath().

On Thu, 1 Jan 2009, Tetsuo Handa wrote:

> To remove factors that make pathname based access control difficult
> (e.g. symbolic links, "..", "//", chroot() etc.), a variant of d_path()
> which traverses up to the root of the namespace is needed.
>
> This patch introduces d_realpath(), a variant of d_path().
> While d_path() stops traversing at current->fs->root,
> d_realpath() doesn't stop traversiong at current->fs->root.
>
> Three differences compared to d_path().
> (1) Ignores current process's root directory.
> (2) Trailing '/' is added if the pathname refers to a directory.
> (3) /proc/PID/ is represented as /proc/self/ if PID equals current->tgid.
>

This needs an ack from Al and/or Christoph.

> Signed-off-by: Kentaro Takeda <[email protected]>
> Signed-off-by: Tetsuo Handa <[email protected]>
> Signed-off-by: Toshiharu Harada <[email protected]>
> ---
> fs/dcache.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/dcache.h | 1
> 2 files changed, 85 insertions(+)
>
> --- linux-2.6.28-mm1.orig/fs/dcache.c
> +++ linux-2.6.28-mm1/fs/dcache.c
> @@ -32,6 +32,7 @@
> #include <linux/seqlock.h>
> #include <linux/swap.h>
> #include <linux/bootmem.h>
> +#include <linux/magic.h>
> #include "internal.h"
>
>
> @@ -1978,6 +1979,89 @@ Elong:
> }
>
> /**
> + * d_realpath - Get the realpath of a dentry.
> + *
> + * @path: Pointer to "struct path".
> + * @buffer: Pointer to buffer to return value in.
> + * @buflen: Sizeof @buffer.
> + *
> + * Returns pointer to the realpath on success, an error code othersize.
> + *
> + * If @dentry is a directory, trailing '/' is appended.
> + * /proc/PID/ is replaced by /proc/self/ if PID == task_tgid_nr_ns(current).
> + */
> +char *d_realpath(struct path *path, char *buffer, int buflen)
> +{
> + struct dentry *dentry = path->dentry;
> + struct vfsmount *vfsmnt = path->mnt;
> + char *end = buffer + buflen;
> +
> + spin_lock(&dcache_lock);
> + spin_lock(&vfsmount_lock);
> + if (buflen < 1 || prepend(&end, &buflen, "", 1))
> + goto Elong;
> + /*
> + * Exception: Add trailing '/' for directory.
> + */
> + if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode) &&
> + prepend(&end, &buflen, "/", 1))
> + goto Elong;
> + for (;;) {
> + struct dentry *parent;
> + const char *name;
> + int name_len;
> + unsigned long pid;
> +
> + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
> + /* Global root? */
> + if (vfsmnt->mnt_parent == vfsmnt)
> + break;
> + dentry = vfsmnt->mnt_mountpoint;
> + vfsmnt = vfsmnt->mnt_parent;
> + continue;
> + }
> + parent = dentry->d_parent;
> + prefetch(parent);
> + /*
> + * Exception: Use /proc/self/ rather than /proc/\$/
> + * for current process.
> + */
> + name = dentry->d_name.name;
> + name_len = dentry->d_name.len;
> + if (IS_ROOT(parent) &&
> + parent->d_sb->s_magic == PROC_SUPER_MAGIC &&
> + !strict_strtoul(name, 10, &pid)) {
> + const pid_t tgid
> + = task_tgid_nr_ns(current,
> + dentry->d_sb->s_fs_info);
> + if (tgid && (pid_t) pid == tgid) {
> + name = "self";
> + name_len = 4;
> + }
> + }
> + if (prepend(&end, &buflen, name, name_len))
> + goto Elong;
> + if (prepend(&end, &buflen, "/", 1))
> + goto Elong;
> + dentry = parent;
> + }
> + if (*end == '/') {
> + /* hit the slash */
> + buflen++;
> + end++;
> + }
> + if (prepend_name(&end, &buflen, &dentry->d_name))
> + goto Elong;
> + out:
> + spin_unlock(&vfsmount_lock);
> + spin_unlock(&dcache_lock);
> + return end;
> + Elong:
> + end = ERR_PTR(-ENAMETOOLONG);
> + goto out;
> +}
> +
> +/**
> * d_path - return the path of a dentry
> * @path: path to report
> * @buf: buffer to return value in
> --- linux-2.6.28-mm1.orig/include/linux/dcache.h
> +++ linux-2.6.28-mm1/include/linux/dcache.h
> @@ -305,6 +305,7 @@ extern char *dynamic_dname(struct dentry
> extern char *__d_path(const struct path *path, struct path *root, char *, int);
> extern char *d_path(const struct path *, char *, int);
> extern char *dentry_path(struct dentry *, char *, int);
> +extern char *d_realpath(struct path *, char *, int);
>
> /* Allocation counts.. */
>
>
> --
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>

--
James Morris
<[email protected]>

2009-01-14 08:59:12

by Tetsuo Handa

[permalink] [raw]
Subject: Re: [TOMOYO #14 (mmotm 2008-12-30-16-05) 03/10] Introduce d_realpath().

James Morris wrote:
> > (3) /proc/PID/ is represented as /proc/self/ if PID equals current->tgid.
>
> This needs an ack from Al and/or Christoph.
>
It is a sad thing that I cannot use /proc/self/ (which is the only part where
a pathname based access control can prevent current process from accessing
other process's information), but I purged d_realpath() for now.

Next posting (#15) will embed AppArmor's d_namespace_path()-like function
into TOMOYO's code.

/* d_namespace_path() in OpenSUSE 11.1's 2.6.27.7-9.1 kernel. */
char *d_namespace_path(struct dentry *dentry, struct vfsmount *vfsmnt,
char *buf, int buflen)
{
struct path root, tmp, ns_root = { };
struct path path = { .mnt = vfsmnt, .dentry = dentry };
char *res;

read_lock(&current->fs->lock);
root = current->fs->root;
path_get(&current->fs->root);
read_unlock(&current->fs->lock);
spin_lock(&vfsmount_lock);
if (root.mnt && root.mnt->mnt_ns)
ns_root.mnt = mntget(root.mnt->mnt_ns->root);
if (ns_root.mnt)
ns_root.dentry = dget(ns_root.mnt->mnt_root);
spin_unlock(&vfsmount_lock);
tmp = ns_root;
res = __d_path(&path, &tmp, buf, buflen,
D_PATH_FAIL_DELETED | D_PATH_DISCONNECT);
path_put(&root);
path_put(&ns_root);

/* Prevent empty path for lazily unmounted filesystems. */
if (!IS_ERR(res) && *res == '\0')
*--res = '.';
return res;
}

Thanks.