Skip to contentSkip to conversation

new syscall: flink

PATCH Dan Kegel 9 participants 14 messages Apr 6, 2003 – Apr 7, 2003

lore
Share & tools
lore

Dan Kegel

author
#0 lore raw
Quoted 4 lines · Ulrich

I got a couple of requests for a function which isn't support on Linux so far. Also not supportable, i.e., cannot be emulated at userlevel. It has some history in other systems (QNX I think), though, and helps with some security issues.

How does this differ from fattach() in SuSv3
(http://www.opengroup.org/onlinepubs/007904975/functions/fattach.html)?
(i.e. does the fact that fattach() is defined only for streams fds make a difference?)

Out of curiosity, I did some searching for prior mentions of flink. It gets proposed every two years or so, it seems.
There may be some security issues. Here are two posts that might be of interest (I wouldn't know, I'm not a security guru):

http://marc.theaimsgroup.com/?l=linux-kernel&m=88944672732020&w=2

Quoted 6 lines · Malcolm Beattie

SysV calls this fattach() where fd is a STREAMS file descriptor (usually a STREAMS pipe). For general file descriptors, it has security implications. For example, you mustn't let it be legal for a process to get a read-only file descriptor and then link it into the file system because then it could change the file's permissions to read-write.

http://mail-index.netbsd.org/tech-userlevel/2001/09/29/0000.html

Quoted 7 lines · Andrew Brown
# as for flink(2), no.  flink(2) would be a terribly bad idea.  consider
# that when opening a file, *all* the permissions on *all* the inodes in
# the path to the file are considered.  if you were able to get some
# process to hand you an open file descriptor to some file somewhere
# that relies on being protected by permissions in the path and you were
# able to flink(2) it to some arbitrary name, you could bypass the
# permissions set that had been established.

- Dan

Signature · 3 lines
Dan Kegel
http://www.kegel.com
http://counter.li.org/cgi-bin/runscript/display-person.cgi?user=78045
#2 lore raw
In reply to Dan Kegel
Quoted 9 lines
Quoted 7 lines · Andrew Brown
# as for flink(2), no.  flink(2) would be a terribly bad idea.  consider
# that when opening a file, *all* the permissions on *all* the inodes in
# the path to the file are considered.  if you were able to get some
# process to hand you an open file descriptor to some file somewhere
# that relies on being protected by permissions in the path and you were
# able to flink(2) it to some arbitrary name, you could bypass the
# permissions set that had been established.

If you have an fd, the permissions based on the path are already bypassed, whether you can call flink or not, aren't they?

Regards
Oliver

#3 lore raw
Quoted 2 lines · Oliver Neukum

If you have an fd, the permissions based on the path are already bypassed, whether you can call flink or not, aren't they?

Not if it's opened O_RDONLY, for example.

--Malcolm

Signature · 1 line
Malcolm Beattie <[email protected]>
#4 lore raw

Am Sonntag, 6. April 2003 22:08 schrieb Malcolm Beattie:

Quoted 5 lines
Quoted 2 lines · Oliver Neukum

If you have an fd, the permissions based on the path are already bypassed, whether you can call flink or not, aren't they?

Not if it's opened O_RDONLY, for example.

OK, so you could open a file RW even if the fd you get passed is only RO, right?

Regards
Oliver

#5 lore raw
In reply to Oliver Neukum
Quoted 2 lines · Oliver Neukum

If you have an fd, the permissions based on the path are already bypassed, whether you can call flink or not, aren't they?

It is actually rather more complicated. Suppose I give you a pipe pair handle. You can flink what was a private object and has no meaning as a name.

Suppose I give you a socket what does the call man ?

Suppose I give you a handle to an anonymous mapping ?

Suppose I give you a handle to data, how do you know what disk it belongs to ?

Suppose I give you an O_RDONLY handle to a file which you then flink and gain write access too ?

#6 lore raw
Followup to:  <[email protected]>
By author:    Alan Cox <[email protected]>
In newsgroup: linux.dev.kernel
Quoted 9 lines

It is actually rather more complicated. Suppose I give you a pipe pair handle. You can flink what was a private object and has no meaning as a name.

Suppose I give you a socket what does the call man ?

Suppose I give you a handle to an anonymous mapping ?

-EXDEV

Quoted 2 lines

Suppose I give you a handle to data, how do you know what disk it belongs to ?

f_ino->i_sb should give you that information.

Quoted 2 lines

Suppose I give you an O_RDONLY handle to a file which you then flink and gain write access too ?

This, I believe, is the real issue. However, we already have that problem:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <limits.h>

int main(int argc, char *argv[])
{
int rfd, wfd;
char filebuf[PATH_MAX];

rfd = open("testfile", O_RDONLY|O_CREAT, 0666);
/* Now rfd is a read-only file descriptor */

  sprintf(filebuf, "/proc/self/fd/%d", rfd);
  wfd = open(filebuf, O_RDWR);
  /* Now wfd is a read-write file descriptor */

  write(wfd, "Tjo fidelittan hatt!\n", 21);

return 0;
}

-hpa

Signature · 3 lines
<[email protected]> at work, <[email protected]> in private!
"Unix gives you enough rope to shoot yourself in the foot."
Architectures needed: ia64 m68k mips64 ppc ppc64 s390 s390x sh v850 x86-64
#7 lore raw
Quoted 6 lines · H. Peter Anvin
Quoted 2 lines · Alan Cox

Suppose I give you an O_RDONLY handle to a file which you then flink and gain write access too ?

This, I believe, is the real issue. However, we already have that problem:

No, I don't think we already have that problem. I think flink() would introduce a new security hole not already present.

Quoted 5 lines

rfd = open("testfile", O_RDONLY|O_CREAT, 0666);
/* Now rfd is a read-only file descriptor */

sprintf(filebuf, "/proc/self/fd/%d", rfd);
wfd = open(filebuf, O_RDWR);

I don't think this is the same. With /proc/self/fd, I can't escalate privileges on a read-only fd. You did the wrong test, because the user already has write access to "testfile". Change the above to make rfd a read-only file descriptor to a file that I don't have write permission to, and you'll find that the test all of a sudden fails. (For instance, add a chmod("testfile", 0444); between the two open()s, and see what happens.)

I think /proc/self/fd is doing the right thing. /proc/self/fd doesn't seem to be a security hole, unless I'm missing something. I think flink() would be a security risk, though. Or did I overlook something?

#8 lore raw
Quoted 10 lines · David Wagner
Quoted 6 lines · H. Peter Anvin
Quoted 2 lines · Alan Cox

Suppose I give you an O_RDONLY handle to a file which you then flink and gain write access too ?

This, I believe, is the real issue. However, we already have that problem:

No, I don't think we already have that problem. I think flink() would introduce a new security hole not already present.

Here's another example along similar lines: you can open a file O_APPEND and pass the descriptor along to another process (e.g. a security mediator process that hands out a file descriptor to a less-trusted recipient that it can use for appending entries only). fcntl() explicity prevents the clearing of the O_APPEND flag on a file which was opened with O_APPEND. With flink, one could flink() and re-open without O_APPEND: security hole.

--Malcolm

Signature · 1 line
Malcolm Beattie <[email protected]>
#9 lore raw
Quoted 7 lines · Malcolm Beattie

Here's another example along similar lines: you can open a file O_APPEND and pass the descriptor along to another process (e.g. a security mediator process that hands out a file descriptor to a less-trusted recipient that it can use for appending entries only). fcntl() explicity prevents the clearing of the O_APPEND flag on a file which was opened with O_APPEND. With flink, one could flink() and re-open without O_APPEND: security hole.

That would be a big security hole waiting to happen though. Nothing forces the less trusted recipient not to send in zeroes or finish the lines (for a text file) or respect a particular format (for a binary file).

In practice, I tend to think that any secutiry scheme flink breaks is brittle at best. It requires passing a fd to a file which is owned by the same uid than the untrusted process, and rely somehow on the directory structure to prevent direct access to said file. But the trusted process must have had access to the file somehow, so, well, it's really, really brittle.

/proc breaking it already isn't very surprising.

OG.

#10 lore raw
In reply to H. Peter Anvin
Followup to:  <[email protected]>
By author:    "H. Peter Anvin" <[email protected]>
In newsgroup: linux.dev.kernel
Quoted 4 lines

This, I believe, is the real issue. However, we already have that problem:

[Sample code]

Here is a better piece of sample code that actually shows a permissions violation happening:

[...]
mkdir("testdir", 0700) = 0
open("testdir/testfile", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3

write(3, "Ansiktsburk\n", 12)           = 12
close(3)                                = 0
open("testdir/testfile", O_RDONLY)      = 3
chmod("testdir", 0)                     = 0
open("/proc/self/fd/3", O_RDWR)         = 4
write(4, "Tjo fidelittan hatt!\n", 21)  = 21
exit_group(0)                           = ?

----snip----

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <limits.h>

int main(int argc, char *argv[])
{
int sfd, rfd, wfd;
char filebuf[PATH_MAX];

  mkdir("testdir", 0700);

  /* Create the file legitimately */
  sfd = open("testdir/testfile", O_WRONLY|O_CREAT|O_TRUNC, 0666);
  write(sfd, "Ansiktsburk\n", 12);
  close(sfd);

  /* Open read-only file descriptor */
  rfd = open("testdir/testfile", O_RDONLY);

  /* Make directory inaccessible */
  chmod("testdir", 0);

  /* Open read-write file descriptor */
  sprintf(filebuf, "/proc/self/fd/%d", rfd);
  wfd = open(filebuf, O_RDWR);

  /* Clobber file */
  write(wfd, "Tjo fidelittan hatt!\n", 21);

return 0;
}

Signature · 3 lines
<[email protected]> at work, <[email protected]> in private!
"Unix gives you enough rope to shoot yourself in the foot."
Architectures needed: ia64 m68k mips64 ppc ppc64 s390 s390x sh v850 x86-64
#11 lore raw
Quoted 6 lines · H. Peter Anvin

Here is a better piece of sample code that actually shows a permissions violation happening:

[...]
mkdir("testdir", 0700) = 0
open("testdir/testfile", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3

Quoted 6 lines
write(3, "Ansiktsburk\n", 12)           = 12
close(3)                                = 0
open("testdir/testfile", O_RDONLY)      = 3
chmod("testdir", 0)                     = 0
open("/proc/self/fd/3", O_RDWR)         = 4
write(4, "Tjo fidelittan hatt!\n", 21)  = 21

You're right! Good point. I retract the comments in my previous email. (I did try an experiment like this, but apparently not the right one.)

My conclusion: /proc/*/fd is a security hole. It should be fixed. Do you agree?

#12 lore raw
Quoted 14 lines · On 7 Apr 2003 06:43:40 GMT [email protected] (David Wagner) wrote:
Quoted 6 lines · H. Peter Anvin

Here is a better piece of sample code that actually shows a permissions violation happening:

[...]
mkdir("testdir", 0700) = 0
open("testdir/testfile", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3

Quoted 6 lines
write(3, "Ansiktsburk\n", 12)           = 12
close(3)                                = 0
open("testdir/testfile", O_RDONLY)      = 3
chmod("testdir", 0)                     = 0
open("/proc/self/fd/3", O_RDWR)         = 4
write(4, "Tjo fidelittan hatt!\n", 21)  = 21
Quoted 5 lines

You're right! Good point. I retract the comments in my previous email. (I did try an experiment like this, but apparently not the right one.)

My conclusion: /proc/*/fd is a security hole. It should be fixed. Do you agree?

open("/proc/self/fd/3", O_RDWR) -- i thought, it just makes a copy for fd/3, and fd/3 should have the same permissions as it was opened.

Quoted 6 lines

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#13 lore raw
In reply to David Wagner
Quoted 14 lines · David Wagner
Quoted 6 lines · H. Peter Anvin

Here is a better piece of sample code that actually shows a permissions violation happening:

[...]
mkdir("testdir", 0700) = 0
open("testdir/testfile", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3

Quoted 6 lines
write(3, "Ansiktsburk\n", 12)           = 12
close(3)                                = 0
open("testdir/testfile", O_RDONLY)      = 3
chmod("testdir", 0)                     = 0
open("/proc/self/fd/3", O_RDWR)         = 4
write(4, "Tjo fidelittan hatt!\n", 21)  = 21
Quoted 5 lines

You're right! Good point. I retract the comments in my previous email. (I did try an experiment like this, but apparently not the right one.)

My conclusion: /proc/*/fd is a security hole. It should be fixed. Do you agree?

I'm somewhat confused, why don't /proc/*/fd entries behave like normal symbolic links? i.e. shouldn't the inodes just be a symbolic link to the d_path() of the fd? Since symbolic links have to travel the entire path (hence calling fs->permission() or vfs_permission() on each dir) it should catch that problem.

Is my understanding of the design wrong? Or is that right, and it's just the implementation that's broken?

just wondering, thanks,

shaya