Fix permission checks on open("/proc/self/fd/N", O_RDWR)
Subsystems (2)
cover Fix permission checks on open("/proc/self/fd/N", O_RDWR)
This is a prototype of a real fix for a longstanding issue. See patch 2 for the full descripton.
Andy Lutomirski (2):
fs,proc: Pass nameidata to proc_get_link implementations fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
fs/namei.c | 3 +++ fs/proc/base.c | 13 ++++++++----- fs/proc/fd.c | 22 ++++++++++++++++++---- fs/proc/internal.h | 3 ++- include/linux/namei.h | 1 + 5 files changed, 32 insertions(+), 10 deletions(-)
Signature · 1 line
1.9.0
RFC 1/2 fs,proc: Pass nameidata to proc_get_link implementations
proc_fd_link should respect fs modes, and it needs to know the open mode to do so.
--- fs/proc/base.c | 13 ++++++++----- fs/proc/fd.c | 3 ++- fs/proc/internal.h | 3 ++- 3 files changed, 12 insertions(+), 7 deletions(-)
fs/proc/base.c +8 -5
@@ -171,7 +171,8 @@ static int get_task_root(struct task_struct *task, struct path *root)return result; } -static int proc_cwd_link (struct dentry *dentry,struct path *path) +static int proc_cwd_link (struct nameidata *nd, +struct dentry *dentry,struct path *path) {struct task_struct *task =get_proc_task (dentry->d_inode);int result = -ENOENT ;
@@ -188,7 +189,8 @@ static int proc_cwd_link(struct dentry *dentry, struct path *path)return result; } -static int proc_root_link (struct dentry *dentry,struct path *path) +static int proc_root_link (struct nameidata *nd, +struct dentry *dentry,struct path *path) {struct task_struct *task =get_proc_task (dentry->d_inode);int result = -ENOENT ;
@@ -1407,7 +1409,8 @@ static const struct file_operations proc_pid_set_comm_operations = { .release = single_release, }; -static int proc_exe_link (struct dentry *dentry,struct path *exe_path) +static int proc_exe_link (struct nameidata *nd, +struct dentry *dentry,struct path *exe_path) {struct task_struct *task;struct mm_struct *mm;
@@ -1441,7 +1444,7 @@ static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)if (!proc_fd_access_allowed (inode)) goto out; - error =PROC_I (inode)->op.proc_get_link (dentry, &path); + error =PROC_I (inode)->op.proc_get_link (nd, dentry, &path);if (error) goto out;
@@ -1485,7 +1488,7 @@ static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int bif (!proc_fd_access_allowed (inode)) goto out; - error =PROC_I (inode)->op.proc_get_link (dentry, &path); + error =PROC_I (inode)->op.proc_get_link (NULL , dentry, &path);if (error) goto out;
fs/proc/fd.c +2 -1
@@ -139,7 +139,8 @@ static const struct dentry_operations tid_fd_dentry_operations = { .d_delete = pid_delete_dentry, }; -static int proc_fd_link (struct dentry *dentry,struct path *path) +static int proc_fd_link (struct nameidata *nd, +struct dentry *dentry,struct path *path) {struct files_struct *files =NULL ;struct task_struct *task;
fs/proc/internal.h +2 -1
@@ -51,7 +51,8 @@ struct proc_dir_entry { };union proc_op { -int (*proc_get_link)(struct dentry *,struct path *); +int (*proc_get_link)(struct nameidata *, +struct dentry *,struct path *);int (*proc_read)(struct task_struct *task,char *page); int (*proc_show)(struct seq_file *m, struct pid_namespace *ns, struct pid *pid,
Signature · 1 line
1.9.0
Al Viro
maintainerRe: [RFC 1/2] fs,proc: Pass nameidata to proc_get_link implementations
Quoted 2 lines · Andy Lutomirski
proc_fd_linkshould respect fs modes, and it needs to know the open mode to do so.
So pass it by value. NAK in that form.
RFC 2/2 fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
In reply to Andy LutomirskiThis patch does this:
# (echo asdf >/proc/self/fd/3) 3</dev/null
bash: /proc/self/fd/3: Permission denied
I'm not confident that my use of nd->flags is correct. I'm also not confident that this hooks in to path lookup in the right place.
It is not complete. It doesn't handle FMODE_READ. It also doesn't handle FMODE_EXEC or FMODE_PATH, but the desired semantics there are less clear. For example, this should probably continue to work:
(/proc/self/fd/3 Hello) 3</bin/echo
This patch does not do anything to /proc/self/exe. It probably should.
Something like this can possibly fix the accidental flink-via-proc thing, too.
This may break userspace. If so, I would guess that anything broken by it is either an actual exploit or is so broken that it doesn't deserve to continue working. If it breaks something important, then maybe it will need a sysctl.
--- fs/namei.c | 3 +++ fs/proc/fd.c | 19 ++++++++++++++++--- include/linux/namei.h | 1 + 3 files changed, 20 insertions(+), 3 deletions(-)
fs/namei.c +3 -0
@@ -2894,6 +2894,9 @@ static int do_last(struct nameidata *nd, struct path *path, goto finish_open; } +if (open_flag & (O_WRONLY |O_RDWR )) + nd->flags |=LOOKUP_WRITE ; +if (!(open_flag &O_CREAT )) {if (nd->last.name[nd->last.len]) nd->flags |=LOOKUP_FOLLOW |LOOKUP_DIRECTORY ;
fs/proc/fd.c +16 -3
@@ -139,6 +139,17 @@ static const struct dentry_operations tid_fd_dentry_operations = { .d_delete = pid_delete_dentry, }; +static int proc_may_follow (struct nameidata *nd,struct file *f) +{ +if (!nd) +return 0 ;/* This is readlink, */ + +if ((nd->flags &LOOKUP_WRITE ) && !(f->f_mode &FMODE_WRITE )) +return -EACCES ; + +return 0 ; +} +static int proc_fd_link (struct nameidata *nd,struct dentry *dentry,struct path *path) {
@@ -159,9 +170,11 @@ static int proc_fd_link(struct nameidata *nd,spin_lock (&files->file_lock); fd_file =fcheck_files (files, fd);if (fd_file) { - *path = fd_file->f_path; -path_get (&fd_file->f_path); - ret =0 ; + ret =proc_may_follow (nd, fd_file); +if (ret ==0 ) { + *path = fd_file->f_path; +path_get (&fd_file->f_path); + } }spin_unlock (&files->file_lock);put_files_struct (files);
include/linux/namei.h +1 -0
@@ -51,6 +51,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};#define LOOKUP_CREATE 0x0200#define LOOKUP_EXCL 0x0400#define LOOKUP_RENAME_TARGET 0x0800 +#define LOOKUP_WRITE 0x8000#define LOOKUP_JUMPED 0x1000#define LOOKUP_ROOT 0x2000
Signature · 1 line
1.9.0
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
fs/proc/fd.c static const struct dentry_operations tid_fd_dentry_operations = { · line 151
Quoted 10 lines · Andy Lutomirski
fs/proc/fd.c +10
-0+static int proc_may_follow (struct nameidata *nd,struct file *f) +{ +if (!nd) +return 0 ;/* This is readlink, */ + +if ((nd->flags &LOOKUP_WRITE ) && !(f->f_mode &FMODE_WRITE )) +return -EACCES ; + +return 0 ; +}
And this is just plain wrong. WTF are you making the traversal of symlink in the middle of pathname dependent on the open flags?
NAK.
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Quoted 15 lines · Al Viro
fs/proc/fd.c static const struct dentry_operations tid_fd_dentry_operations = { · line 151
Quoted 10 lines · Andy Lutomirski
fs/proc/fd.c +10
-0+static int proc_may_follow (struct nameidata *nd,struct file *f) +{ +if (!nd) +return 0 ;/* This is readlink, */ + +if ((nd->flags &LOOKUP_WRITE ) && !(f->f_mode &FMODE_WRITE )) +return -EACCES ; + +return 0 ; +}And this is just plain wrong. WTF are you making the traversal of symlink in the middle of pathname dependent on the open flags?
Can you give me a hint? There are three cases that I need to distinguish, I think:
1. readlink. Currently handled by nd == NULL. It's ugly, and I'll clean it up.
2. Traversal in the middle of a path. This can be either literally in the middle (e.g. "/proc/self/fd/3/something_else") or in a symlink that's the last component of the literal path (e.g. "fd3null" where "fd3null" is a symlink to "/proc/self/fd/3/null" and "null" is either a file or a symlink to /dev/null). I have the latter type wrong in this patch.
3. Actually opening /proc/self/fd/N. This can be direct or by opening a symlink to /proc/self/fd/N. I think I have this case correct.
What's the best way to fix this? Should I be checking nd->depth?
--Andy
Quoted 2 lines
NAK.
Signature · 2 lines
Andy Lutomirski AMA Capital Management, LLC
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Quoted 29 lines · Andy Lutomirski
Quoted 15 lines · Al Viro
fs/proc/fd.c static const struct dentry_operations tid_fd_dentry_operations = { · line 151
Quoted 10 lines · Andy Lutomirski
fs/proc/fd.c +10
-0+static int proc_may_follow (struct nameidata *nd,struct file *f) +{ +if (!nd) +return 0 ;/* This is readlink, */ + +if ((nd->flags &LOOKUP_WRITE ) && !(f->f_mode &FMODE_WRITE )) +return -EACCES ; + +return 0 ; +}And this is just plain wrong. WTF are you making the traversal of symlink in the middle of pathname dependent on the open flags?
Can you give me a hint? There are three cases that I need to distinguish, I think:
1. readlink. Currently handled by nd == NULL. It's ugly, and I'll clean it up.
2. Traversal in the middle of a path. This can be either literally in the middle (e.g. "
/proc/self/fd/3/something_else") or in a symlink that's the last component of the literal path (e.g. "fd3null" where "fd3null" is a symlink to "/proc/self/fd/3/null" and "null" is either a file or a symlink to/dev/null). I have the latter type wrong in this patch.
Quoted 4 lines
3. Actually opening
/proc/self/fd/N. This can be direct or by opening a symlink to/proc/self/fd/N. I think I have this case correct.What's the best way to fix this? Should I be checking
nd->depth?
No, I think that's wrong, too. I think that will cause me to screw up symlinks to /proc/self/fd/3. What's the right way to tell that follow_link is happening on the very last pathname component?
Hmm. I wonder what happens, or even what should happen, if the file descriptor is a symlink opened with O_PATH | O_NOFOLLOW.
--Andy
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Quoted 33 lines · Andy Lutomirski
Quoted 32 lines · Andy Lutomirski
Quoted 15 lines · Al Viro
fs/proc/fd.c static const struct dentry_operations tid_fd_dentry_operations = { · line 151
Quoted 10 lines · Andy Lutomirski
fs/proc/fd.c +10
-0+static int proc_may_follow (struct nameidata *nd,struct file *f) +{ +if (!nd) +return 0 ;/* This is readlink, */ + +if ((nd->flags &LOOKUP_WRITE ) && !(f->f_mode &FMODE_WRITE )) +return -EACCES ; + +return 0 ; +}And this is just plain wrong. WTF are you making the traversal of symlink in the middle of pathname dependent on the open flags?
Can you give me a hint? There are three cases that I need to distinguish, I think:
1. readlink. Currently handled by nd == NULL. It's ugly, and I'll clean it up.
2. Traversal in the middle of a path. This can be either literally in the middle (e.g. "
/proc/self/fd/3/something_else") or in a symlink that's the last component of the literal path (e.g. "fd3null" where "fd3null" is a symlink to "/proc/self/fd/3/null" and "null" is either a file or a symlink to/dev/null). I have the latter type wrong in this patch.3. Actually opening
/proc/self/fd/N. This can be direct or by opening a symlink to/proc/self/fd/N. I think I have this case correct.
Quoted 8 lines
Quoted 1 line
What's the best way to fix this? Should I be checking
nd->depth?No, I think that's wrong, too. I think that will cause me to screw up symlinks to
/proc/self/fd/3. What's the right way to tell thatfollow_linkis happening on the very last pathname component?Hmm. I wonder what happens, or even what should happen, if the file descriptor is a symlink opened with
O_PATH|O_NOFOLLOW.
Here's a test case:
https://github.com/amluto/procfd_test
It doesn't cover O_PATH yet.
Both my code and current kernels fail.
--Andy
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
In reply to Andy LutomirskiHi
Quoted 1 line · Andy Lutomirski
This patch does this:
I can see _what_ the patch does, but your patch lacks any discussion _why_ it is needed. Can you provide at least one real example where this fixes a security issue?
Quoted 4 lines
This may break userspace. If so, I would guess that anything broken by it is either an actual exploit or is so broken that it doesn't deserve to continue working. If it breaks something important, then maybe it will need a sysctl.
This patch breaks the following use-case:
fd = open("/run", O_RDWR | O_TMPFILE);sprintf(path, "/proc/self/fd/%d", fd);
fd2 = open(buf, O_RDONLY);sprintf(path, "/proc/self/fd/%d", fd2);linkat(AT_FDCWD, path, AT_FDCWD, "/run/some_lock_file", AT_FOLLOW_SYMLINK);
I mean I explicitly create the object as _writable_ but then keep a read-only descriptor for debugging purposes (to make sure that the program no longer writes to the file). This is no security feature, but only a safety feature in case something goes wrong. But I still want to be able to create hard-links (I _do_ have write-permissions on the object/inode).
Thanks
David
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Quoted 8 lines · David Herrmann
Hi
Quoted 1 line · Andy Lutomirski
This patch does this:
I can see _what_ the patch does, but your patch lacks any discussion _why_ it is needed. Can you provide at least one real example where this fixes a security issue?
Anyone who opens a file read-only and sends it over SCM_RIGHTS is likely broken. They may think that it's read-only, so it can't be written, but this /proc/fd issue means that whoever receives it can reopen it.
It's true that, if the inode doesn't allow the recipient write access, then the recipient can't reopen, but there are lots of cases where the inode can't reliably be expected not to allow write. For example, the inode could be unlinked, an O_TMPFILE file, a memfd handle, or in a non-world-executable directory, and the file mode should be respected.
Quoted 7 lines
Quoted 5 lines
This may break userspace. If so, I would guess that anything broken by it is either an actual exploit or is so broken that it doesn't deserve to continue working. If it breaks something important, then maybe it will need a sysctl.
This patch breaks the following use-case:
This is totally broken.
Quoted 2 lines
fd =
open("/run", O_RDWR | O_TMPFILE);
Did you mean fd = open("/run", O_RDWR | O_TMPFILE, 0666)? 0600? Because currently you don't actually know that you have write access to the inode unless you have CAP_DAC_OVERRIDE.
This should fail to compile. It doesn't because POSIX is dumb.
Quoted 2 lines
sprintf(path, "/proc/self/fd/%d", fd);
fd2 =open(buf, O_RDONLY);
And this is the whole point of this patch.
Quoted 2 lines
sprintf(path, "/proc/self/fd/%d", fd2);linkat(AT_FDCWD, path, AT_FDCWD, "/run/some_lock_file", AT_FOLLOW_SYMLINK);
Seriously? fd2, not fd? I didn't intend to change flink behavior in this patch, but you shouldn't be able to bypass file descriptor modes by flinking the fd either.
Imagine that you gave fd2 to someone else. Do you really want them flinking and then reopening for write?
Quoted 7 lines
I mean I explicitly create the object as _writable_ but then keep a read-only descriptor for debugging purposes (to make sure that the program no longer writes to the file). This is no security feature, but only a safety feature in case something goes wrong. But I still want to be able to create hard-links (I _do_ have write-permissions on the object/inode).
Then use fd for the flink step.
IMO it's an accident that this code ever worked. The "linkable" attribute is an inode flag for no particularly good reason, and IMO your example indicates that it should have been a struct file flag from day one.
If you are doing this, can you please fix it so it will keep working even if the current flink insanity goes away? Your code is already screwed on current kernels, because you might accidentally create a world-writable file in /run depending on what's in your third argument register.
--Andy
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Hi
Quoted 10 lines · Andy Lutomirski
Anyone who opens a file read-only and sends it over
SCM_RIGHTSis likely broken. They may think that it's read-only, so it can't be written, but this/proc/fdissue means that whoever receives it can reopen it.It's true that, if the inode doesn't allow the recipient write access, then the recipient can't reopen, but there are lots of cases where the inode can't reliably be expected not to allow write. For example, the inode could be unlinked, an
O_TMPFILEfile, a memfd handle, or in a non-world-executable directory, and the file mode should be respected.
I think it's safe to assume that any object you create is never world-accessible. So the worst you can get is 0600. So if we now take your example, your patch doesn't fix the problem at all. Imagine two processes, $sender and $receiver. If the receiver runs as a different user as the sender, it cannot open /proc/self/fd/ writable due to 0600. So the only problematic case is if both run as the same user. However, in that case, the receiver can _always_ access /proc/$sender/fd/ and thus still gain writable access to the object, even if its own fd is read-only and your patch was applied. (ignoring the fact that they can kill() and ptrace each other..)
Protecting world-accessible objects by hiding them is imho wrong. And protecting users against themselves is even worse.
Quoted 4 lines
Quoted 2 lines
fd =
open("/run", O_RDWR | O_TMPFILE);Did you mean fd =
open("/run", O_RDWR | O_TMPFILE, 0666)? 0600?
Sorry, I meant S_IWUSR | S_IRUSR (0600).
Thanks
David
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Quoted 8 lines · David Herrmann
Hi
Quoted 5 lines · Andy Lutomirski
Anyone who opens a file read-only and sends it over
SCM_RIGHTSis likely broken. They may think that it's read-only, so it can't be written, but this/proc/fdissue means that whoever receives it can reopen it.
Quoted 8 lines
Quoted 5 lines
It's true that, if the inode doesn't allow the recipient write access, then the recipient can't reopen, but there are lots of cases where the inode can't reliably be expected not to allow write. For example, the inode could be unlinked, an
O_TMPFILEfile, a memfd handle, or in a non-world-executable directory, and the file mode should be respected.I think it's safe to assume that any object you create is never world-accessible. So the worst you can get is 0600.
Can you explain what you mean? I think that it's completely *unsafe* to make this assumption unless you actually take some explicit action to make sure it's correct.
Quoted 9 lines
So if we now take
your example, your patch doesn't fix the problem at all. Imagine two processes, $sender and $receiver. If the receiver runs as a different user as the sender, it cannot open/proc/self/fd/writable due to 0600. So the only problematic case is if both run as the same user. However, in that case, the receiver can _always_ access/proc/$sender/fd/and thus still gain writable access to the object, even if its own fd is read-only and your patch was applied. (ignoring the fact that they cankill()and ptrace each other..)
Incorrect. That is exactly what my patch changes.
Quoted 3 lines
Protecting world-accessible objects by hiding them is imho wrong. And protecting users against themselves is even worse.
Protecting users against themselves (i.e. other things with the same uid) when they create namespaces and use seccomp filters is important.
--Andy
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Hi
Quoted 7 lines · Andy Lutomirski
Quoted 2 lines · David Herrmann
I think it's safe to assume that any object you create is never world-accessible. So the worst you can get is 0600.
Can you explain what you mean? I think that it's completely *unsafe* to make this assumption unless you actually take some explicit action to make sure it's correct.
Which kernel-interface creates world-writable objects if a reasonable umask like 022 is set?
Quoted 11 lines
Quoted 9 lines
So if we now take
your example, your patch doesn't fix the problem at all. Imagine two processes, $sender and $receiver. If the receiver runs as a different user as the sender, it cannot open/proc/self/fd/writable due to 0600. So the only problematic case is if both run as the same user. However, in that case, the receiver can _always_ access/proc/$sender/fd/and thus still gain writable access to the object, even if its own fd is read-only and your patch was applied. (ignoring the fact that they cankill()and ptrace each other..)Incorrect. That is exactly what my patch changes.
Are you sure? Note I wrote /proc/$sender/fd/ not /proc/$receiver/fd/. The lookup on /proc/$sender/fd/ is done with the file of the _sender_, which obviously is writable.
Thanks
David
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Quoted 7 lines · David Herrmann
Hi
Quoted 4 lines · Andy Lutomirski
Quoted 2 lines · David Herrmann
I think it's safe to assume that any object you create is never world-accessible. So the worst you can get is 0600.
Quoted 6 lines
Quoted 3 lines
Can you explain what you mean? I think that it's completely *unsafe* to make this assumption unless you actually take some explicit action to make sure it's correct.
Which kernel-interface creates world-writable objects if a reasonable umask like 022 is set?
That is an incredibly large "if". Does Xorg change its umask when it starts? Whenever you write a setuid program, do you reset the umask? Will every user of memfd_create who makes a read-only fd pointing at the same thing remember to reset the umask? How about libraries that *can't* safely mess with umask because it's not thread-local?
Quoted 11 lines
Quoted 11 lines
Quoted 10 lines
So if we now take
your example, your patch doesn't fix the problem at all. Imagine two processes, $sender and $receiver. If the receiver runs as a different user as the sender, it cannot open/proc/self/fd/writable due to 0600. So the only problematic case is if both run as the same user. However, in that case, the receiver can _always_ access/proc/$sender/fd/and thus still gain writable access to the object, even if its own fd is read-only and your patch was applied. (ignoring the fact that they cankill()and ptrace each other..)
Quoted 5 lines
Quoted 1 line
Incorrect. That is exactly what my patch changes.
Are you sure? Note I wrote
/proc/$sender/fd/not/proc/$receiver/fd/. The lookup on/proc/$sender/fd/is done with the file of the _sender_, which obviously is writable.
Sorry, I misread that. This is a problem that people who design non-uid-manipulating sandboxes have to handle. Or they can use pid namespaces.
--Andy
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
In reply to David HerrmannQuoted 25 lines · David Herrmann
Hi
Quoted 10 lines · Andy Lutomirski
Anyone who opens a file read-only and sends it over
SCM_RIGHTSis likely broken. They may think that it's read-only, so it can't be written, but this/proc/fdissue means that whoever receives it can reopen it.It's true that, if the inode doesn't allow the recipient write access, then the recipient can't reopen, but there are lots of cases where the inode can't reliably be expected not to allow write. For example, the inode could be unlinked, an
O_TMPFILEfile, a memfd handle, or in a non-world-executable directory, and the file mode should be respected.I think it's safe to assume that any object you create is never world-accessible. So the worst you can get is 0600. So if we now take your example, your patch doesn't fix the problem at all. Imagine two processes, $sender and $receiver. If the receiver runs as a different user as the sender, it cannot open
/proc/self/fd/writable due to 0600. So the only problematic case is if both run as the same user. However, in that case, the receiver can _always_ access/proc/$sender/fd/and thus still gain writable access to the object, even if its own fd is read-only and your patch was applied. (ignoring the fact that they cankill()and ptrace each other..)
Quoted 2 lines
Protecting world-accessible objects by hiding them is imho wrong. And protecting users against themselves is even worse.
See the link for example where this matters in multi-user setting.
Agreed, protecting file using its directory's permissions is not practice to be recommended, but we should not be breaking it. (And we are not breaking it if /proc is not mounted...)
Enforcing read-only on file descriptor is IMO useful, and being able to work around it using /proc/self/fd is a security problem...
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
In reply to David HerrmannHi!
Quoted 6 lines · Andy Lutomirski
Quoted 1 line
This patch does this:
I can see _what_ the patch does, but your patch lacks any discussion _why_ it is needed. Can you provide at least one real example where this fixes a security issue?
Such as here?
http://www.securityfocus.com/archive/1/507386
Quoted 10 lines
Quoted 4 lines
This may break userspace. If so, I would guess that anything broken by it is either an actual exploit or is so broken that it doesn't deserve to continue working. If it breaks something important, then maybe it will need a sysctl.
This patch breaks the following use-case:
fd =
open("/run", O_RDWR | O_TMPFILE);sprintf(path, "/proc/self/fd/%d", fd);
fd2 =open(buf, O_RDONLY);
You meant open(path, ) here?
Quoted 9 lines
sprintf(path, "/proc/self/fd/%d", fd2);linkat(AT_FDCWD, path, AT_FDCWD, "/run/some_lock_file", AT_FOLLOW_SYMLINK);I mean I explicitly create the object as _writable_ but then keep a read-only descriptor for debugging purposes (to make sure that the program no longer writes to the file). This is no security feature, but only a safety feature in case something goes wrong. But I still want to be able to create hard-links (I _do_ have write-permissions on the object/inode).
Does some real code do it? I believe this deserves to be broken -- you explicitely opened that read-only...
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Hi
Quoted 3 lines · Pavel Machek
Such as here?
Thanks, that's the first real example someone mentioned.
Quoted from your link:
Quoted 3 lines
The reopen does check the inode permission, but it does not require you have any reachable path to the file. Someone _might_ use that as a traditional unix security mechanism, but if so it's probably quite rare.
In other words, the bug you describe is that /proc/pid/fd/ allows access to objects without a reachable path to the only _real_ filesystem link. But isn't the same true for openat()?
Thanks
David
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Quoted 11 lines · David Herrmann
Hi
Quoted 3 lines · Pavel Machek
Such as here?
Thanks, that's the first real example someone mentioned.
Quoted from your link:
Quoted 7 lines
Quoted 3 lines
The reopen does check the inode permission, but it does not require you have any reachable path to the file. Someone _might_ use that as a traditional unix security mechanism, but if so it's probably quite rare.
In other words, the bug you describe is that
/proc/pid/fd/allows access to objects without a reachable path to the only _real_ filesystem link. But isn't the same true foropenat()?
I don't think so. openat doesn't work on fds for things that aren't directories.
--Andy
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Hi
Quoted 7 lines · Andy Lutomirski
Quoted 3 lines · David Herrmann
In other words, the bug you describe is that
/proc/pid/fd/allows access to objects without a reachable path to the only _real_ filesystem link. But isn't the same true foropenat()?I don't think so. openat doesn't work on fds for things that aren't directories.
Sorry, I wasn't precise enough: I meant the same 'leak' occurs if you keep a dir-fd on the directory in question _before_ it is set to 0600. Just like the example race keeps a file-fd to the file in question. So after the directory is set to 0600 you can use that dir-fd via openat() to avoid the whole path-lookup just like you do it via /proc.
Thanks
David
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Quoted 8 lines · David Herrmann
Hi
Quoted 5 lines · Andy Lutomirski
Quoted 3 lines · David Herrmann
In other words, the bug you describe is that
/proc/pid/fd/allows access to objects without a reachable path to the only _real_ filesystem link. But isn't the same true foropenat()?
Quoted 9 lines
Quoted 2 lines
I don't think so. openat doesn't work on fds for things that aren't directories.
Sorry, I wasn't precise enough: I meant the same 'leak' occurs if you keep a dir-fd on the directory in question _before_ it is set to 0600. Just like the example race keeps a file-fd to the file in question. So after the directory is set to 0600 you can use that dir-fd via
openat()to avoid the whole path-lookup just like you do it via /proc.
I think this doesn't work -- fds pointing at directories don't capture execute permission the way fds pointing at files can capture write permission. This is similar to the way that ls fails if you chmod 000 '.'. Yes, it's weird.
--Andy
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
In reply to David HerrmannQuoted 11 lines · David Herrmann
Hi
Quoted 3 lines · Pavel Machek
Such as here?
Thanks, that's the first real example someone mentioned.
Quoted from your link:
Quoted 7 lines
Quoted 3 lines
The reopen does check the inode permission, but it does not require you have any reachable path to the file. Someone _might_ use that as a traditional unix security mechanism, but if so it's probably quite rare.
In other words, the bug you describe is that
/proc/pid/fd/allows access to objects without a reachable path to the only _real_ filesystem link. But isn't the same true foropenat()?
I don't think openat helps you. This is what we are talking about, it is easy to reproduce. Can you reproduce it without /proc mounted?
I think that chmod 700 . should stop you. Openat seems no worse than just placing cwd there...
pavel@toy:/tmp$ uname -a Linux toy.ucw.cz 2.6.32-rc3 #21 Mon Oct 19 07:32:02 CEST 2009 armv5tel GNU/Linux pavel@toy:/tmp mkdir my_priv; cd my_priv pavel@toy:/tmp/my_priv$ echo this file should never be writable > unwritable_file # lock down directory pavel@toy:/tmp/my_priv$ chmod 700 . # relax file permissions, directory is private, so this is safe # check link count on unwritable_file. We would not want someone # to have a hard link to work around our permissions, would we? pavel@toy:/tmp/my_priv$ chmod 666 unwritable_file pavel@toy:/tmp/my_priv$ cat unwritable_file this file should never be writable pavel@toy:/tmp/my_priv$ cat unwritable_file got you # Security problem here Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Hi
Quoted 5 lines · Pavel Machek
I don't think openat helps you. This is what we are talking about, it is easy to reproduce. Can you reproduce it without /proc mounted?
I think that chmod 700 . should stop you. Openat seems no worse than just placing cwd there...
Example1:
$ mkdir -p subdir/next $ chmod 000 subdir $ touch subdir/next/test => EACCES $ cd subdir => EACCES
Example2:
$ mkdir -p subdir/next $ cd subdir/next $ chmod 000 .. $ touch test => SUCCESS
This is the exact same situation. The filesystem tree is exactly the same in both situations, but in the first example CWD is outside of "subdir", in the second example CWD is inside of "subdir". Thus, they can create files in that directory, even though they have no access to _any_ absolute path to that directory.
This is the exact same race that you describe via /proc/self/fd/. But instead of keeping a ref to the dir via CWD, in your example you keep the ref via a FD in that exact same directory and access it via /proc.
(Hint: instead of using CWD, you can also keep an FD via open(O_PATH) and pass it to openat())
Thanks
David
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
Quoted 30 lines · David Herrmann
Hi
Quoted 5 lines · Pavel Machek
I don't think openat helps you. This is what we are talking about, it is easy to reproduce. Can you reproduce it without /proc mounted?
I think that chmod 700 . should stop you. Openat seems no worse than just placing cwd there...
Example1:
$ mkdir -p subdir/next $ chmod 000 subdir $ touch subdir/next/test => EACCES $ cd subdir => EACCESExample2:
$ mkdir -p subdir/next $ cd subdir/next $ chmod 000 .. $ touch test => SUCCESSThis is the exact same situation. The filesystem tree is exactly the same in both situations, but in the first example CWD is outside of "subdir", in the second example CWD is inside of "subdir". Thus, they can create files in that directory, even though they have no access to _any_ absolute path to that directory.
Quoted 6 lines
This is the exact same race that you describe via
/proc/self/fd/. But instead of keeping a ref to the dir via CWD, in your example you keep the ref via a FD in that exact same directory and access it via /proc.(Hint: instead of using CWD, you can also keep an FD via
open(O_PATH)and pass it toopenat())
So what?
It's well-known that the execute bit is only checked when you look something up, and it's not checked all the way back to the root. It's not well known, nor is it POSIX, that you can fudge it via proc.
--Andy
Re: [RFC 2/2] fs,proc: Respect FMODE_WRITE when opening /proc/pid/fd/N
In reply to David HerrmannQuoted 30 lines · David Herrmann
Hi
Quoted 5 lines · Pavel Machek
I don't think openat helps you. This is what we are talking about, it is easy to reproduce. Can you reproduce it without /proc mounted?
I think that chmod 700 . should stop you. Openat seems no worse than just placing cwd there...
Example1:
$ mkdir -p subdir/next $ chmod 000 subdir $ touch subdir/next/test => EACCES $ cd subdir => EACCESExample2:
$ mkdir -p subdir/next $ cd subdir/next $ chmod 000 .. $ touch test => SUCCESSThis is the exact same situation. The filesystem tree is exactly the same in both situations, but in the first example CWD is outside of "subdir", in the second example CWD is inside of "subdir". Thus, they can create files in that directory, even though they have no access to _any_ absolute path to that directory.
Quoted 4 lines
This is the exact same race that you describe via
/proc/self/fd/. But instead of keeping a ref to the dir via CWD, in your example you keep the ref via a FD in that exact same directory and access it via /proc.
Yes, that's how permissions work. You snipped my example. Can you show how to write to "unwritable_file" below without /proc? Because I believe I got my chmods right.
pavel@toy:/tmp$ uname -a Linux toy.ucw.cz 2.6.32-rc3 #21 Mon Oct 19 07:32:02 CEST 2009 armv5tel GNU/Linux pavel@toy:/tmp mkdir my_priv; cd my_priv pavel@toy:/tmp/my_priv$ echo this file should never be writable > unwritable_file # lock down directory pavel@toy:/tmp/my_priv$ chmod 700 . # relax file permissions, directory is private, so this is safe # check link count on unwritable_file. We would not want someone # to have a hard link to work around our permissions, would we? pavel@toy:/tmp/my_priv$ chmod 666 unwritable_file pavel@toy:/tmp/my_priv$ cat unwritable_file this file should never be writable pavel@toy:/tmp/my_priv$ cat unwritable_file got you # Security problem here
Quoted 2 lines
(Hint: instead of using CWD, you can also keep an FD via
open(O_PATH)and pass it toopenat())
Feel free to use openat(), too.
Thanks,
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html