All of lore.kernel.org
 help / color / mirror / Atom feed
* flink (AT_EMPTY_PATH / AT_SYMLINK_FOLLOW) security considerations
@ 2013-08-13 17:21 Andy Lutomirski
  0 siblings, 0 replies; only message in thread
From: Andy Lutomirski @ 2013-08-13 17:21 UTC (permalink / raw)
  To: linux-kernel, Linux FS Devel, Linus Torvalds, spender, Al Viro

[-- Attachment #1: Type: text/plain, Size: 1329 bytes --]

Linux 3.10 (and many earlier kernels) allow flink using an incantation
like linkat(AT_FDCWD, "/proc/self/fd/N", destdirfd, newname,
AT_SYMLINK_FOLLOW);  It's possible to do much the same thing using
linkat(oldfd, "", destdirfd, newname, AT_EMPTY_PATH) if you're
privileged on 3.10, and the requirement for privilege is dropped in
3.11-rc5.

The immediate motivation for dropping the privilege requirement is the
O_TMPFILE changes: you can create a temporary file with O_TMPFILE,
write to it, and then give it a name with linkat(..., AT_EMPTY_PATH).
You can prevent this behavior by using O_TMPFILE | O_EXCL.

Apparently there's some kind of new security issue here [1], but I
don't know what it is.  So I'd like to get other people's thoughts.

Some notes:

All linkat variations do this:

	/* Make sure we don't allow creating hardlink to an unlinked file */
	if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE))
		error =  -ENOENT;

That means that deleted files (except for O_TMPFILE, which sets
I_LINKABLE) can't be flinked.

Both flink variants work on O_PATH fds.

I've attached some test code if you want to play with this stuff.

Possible changes include inspecting f_cred before flink, requiring
I_ILINKABLE if unprivileged, and reverting the 3.11 change.

[1] https://lwn.net/Articles/562488/ -- see the comments

[-- Attachment #2: tmpfile_flink.c --]
[-- Type: text/x-csrc, Size: 811 bytes --]

#include <stdio.h>
#include <err.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define __O_TMPFILE 020000000
#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
#define AT_EMPTY_PATH 0x1000

int main(int argc, char **argv)
{
  char buf[128];

  if (argc != 4)
    errx(1, "Usage: flinktest TMPDIR PATH linkat|proc");

  int fd = open(argv[1], O_TMPFILE | O_RDWR, 010600);
  if (fd == -1)
    err(1, "O_TMPFILE");
  write(fd, "test", 4);

  if (!strcmp(argv[3], "linkat")) {
    if (linkat(fd, "", AT_FDCWD, argv[2], AT_EMPTY_PATH) != 0)
      err(1, "linkat");
  } else if (!strcmp(argv[3], "proc")) {
    sprintf(buf, "/proc/self/fd/%d", fd);
    if (linkat(AT_FDCWD, buf, AT_FDCWD, argv[2], AT_SYMLINK_FOLLOW) != 0)
      err(1, "linkat");
  } else {
    errx(1, "invalid mode");
  }
  return 0;
}

[-- Attachment #3: o_path_flink.c --]
[-- Type: text/x-csrc, Size: 937 bytes --]

#include <stdio.h>
#include <err.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define AT_EMPTY_PATH 0x1000
#ifndef O_PATH
#define O_PATH          010000000
#endif

int main(int argc, char **argv)
{
  char buf[128];

  if (argc != 5)
    errx(1, "Usage: flinktest OLDPATH NEWPATH <normal|O_PATH> AT_EMPTY_PATH|proc");

  int flag;
  if (!strcmp(argv[3], "normal"))
    flag = O_RDONLY;
  else if (!strcmp(argv[3], "O_PATH"))
    flag = O_PATH;
  else
    errx(1, "bad open mode");

  int fd = open(argv[1], flag);
  if (fd == -1)
    err(1, "open");

  if (!strcmp(argv[4], "AT_EMPTY_PATH")) {
    if (linkat(fd, "", AT_FDCWD, argv[2], AT_EMPTY_PATH) != 0)
      err(1, "linkat");
  } else if (!strcmp(argv[4], "proc")) {
    sprintf(buf, "/proc/self/fd/%d", fd);
    if (linkat(AT_FDCWD, buf, AT_FDCWD, argv[2], AT_SYMLINK_FOLLOW) != 0)
      err(1, "linkat");
  } else {
    errx(1, "invalid mode");
  }
  return 0;
}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2013-08-13 17:22 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-13 17:21 flink (AT_EMPTY_PATH / AT_SYMLINK_FOLLOW) security considerations Andy Lutomirski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.