tty: add TIOCGPTPEER ioctl
authorAleksa Sarai <asarai@suse.de>
Sat, 3 Jun 2017 14:15:15 +0000 (00:15 +1000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 9 Jun 2017 10:27:54 +0000 (12:27 +0200)
When opening the slave end of a PTY, it is not possible for userspace to
safely ensure that /dev/pts/$num is actually a slave (in cases where the
mount namespace in which devpts was mounted is controlled by an
untrusted process). In addition, there are several unresolvable
race conditions if userspace were to attempt to detect attacks through
stat(2) and other similar methods [in addition it is not clear how
userspace could detect attacks involving FUSE].

Resolve this by providing an interface for userpace to safely open the
"peer" end of a PTY file descriptor by using the dentry cached by
devpts. Since it is not possible to have an open master PTY without
having its slave exposed in /dev/pts this interface is safe. This
interface currently does not provide a way to get the master pty (since
it is not clear whether such an interface is safe or even useful).

Cc: Christian Brauner <christian.brauner@ubuntu.com>
Cc: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/alpha/include/uapi/asm/ioctls.h
arch/mips/include/uapi/asm/ioctls.h
arch/parisc/include/uapi/asm/ioctls.h
arch/powerpc/include/uapi/asm/ioctls.h
arch/sh/include/uapi/asm/ioctls.h
arch/sparc/include/uapi/asm/ioctls.h
arch/xtensa/include/uapi/asm/ioctls.h
drivers/tty/pty.c
include/uapi/asm-generic/ioctls.h

index f30c94ae1bdb19499af9b21c0006f06f6756d43f..ff67b8373bf78bb24a72df561390236842707a6a 100644 (file)
 #define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
+#define TIOCGPTPEER    _IOR('T', 0x41, int) /* Safely open the slave */
 
 #define TIOCSERCONFIG  0x5453
 #define TIOCSERGWILD   0x5454
index 740219c2c8943df14abe39c7e3dde6c4d2149f0d..68e19b689a00c4d783cdde376ca40c95a48c3f3c 100644 (file)
@@ -91,6 +91,7 @@
 #define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
+#define TIOCGPTPEER    _IOR('T', 0x41, int) /* Safely open the slave */
 
 /* I hope the range from 0x5480 on is free ... */
 #define TIOCSCTTY      0x5480          /* become controlling tty */
index b6572f051b6739d5aa94035224e5928ca3a470cf..674c68a5bbd035c86bee42834d010ba20eb06464 100644 (file)
@@ -60,6 +60,7 @@
 #define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
+#define TIOCGPTPEER    _IOR('T', 0x41, int) /* Safely open the slave */
 
 #define FIONCLEX       0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX                0x5451
index 49a25796a61af05b6d61c27bf9e507977711c3ef..bfd609a3e928f8b8536567e4e030cf8ff05643ff 100644 (file)
 #define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
+#define TIOCGPTPEER    _IOR('T', 0x41, int) /* Safely open the slave */
 
 #define TIOCSERCONFIG  0x5453
 #define TIOCSERGWILD   0x5454
index c9903e56ccf4381b99bdf6f042e70af1882841f9..eec7901e9e658b18e0eadeeb2af65966cc15993d 100644 (file)
@@ -93,6 +93,7 @@
 #define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
+#define TIOCGPTPEER    _IOR('T', 0x41, int) /* Safely open the slave */
 
 #define TIOCSERCONFIG  _IO('T', 83) /* 0x5453 */
 #define TIOCSERGWILD   _IOR('T', 84,  int) /* 0x5454 */
index 06b3f6c3bb9aa353362cc90acf2585b11227dd1d..6d27398632eacd22a72e0437ea57d1c048930d11 100644 (file)
@@ -27,7 +27,7 @@
 #define TIOCGRS485     _IOR('T', 0x41, struct serial_rs485)
 #define TIOCSRS485     _IOWR('T', 0x42, struct serial_rs485)
 
-/* Note that all the ioctls that are not available in Linux have a 
+/* Note that all the ioctls that are not available in Linux have a
  * double underscore on the front to: a) avoid some programs to
  * think we support some ioctls under Linux (autoconfiguration stuff)
  */
@@ -88,6 +88,7 @@
 #define TIOCGPTN       _IOR('t', 134, unsigned int) /* Get Pty Number */
 #define TIOCSPTLCK     _IOW('t', 135, int) /* Lock/unlock PTY */
 #define TIOCSIG                _IOW('t', 136, int) /* Generate signal on Pty slave */
+#define TIOCGPTPEER    _IOR('t', 137, int) /* Safely open the slave */
 
 /* Little f */
 #define FIOCLEX                _IO('f', 1)
index 518954e74e6d5e49e77c14a6eecb104319019edd..98b004e24e8523b735d60937c56e22b2b0f7d2bd 100644 (file)
 #define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
+#define TIOCGPTPEER    _IOR('T', 0x41, int) /* Safely open the slave */
 
 #define TIOCSERCONFIG  _IO('T', 83)
 #define TIOCSERGWILD   _IOR('T', 84,  int)
index 2a6bd9ae3f8b2f341fd4d3ddfe0af39cf6478f9d..d1399aac05a17e96ead10d5bd616a5a780ce9237 100644 (file)
@@ -24,6 +24,9 @@
 #include <linux/slab.h>
 #include <linux/mutex.h>
 #include <linux/poll.h>
+#include <linux/mount.h>
+#include <linux/file.h>
+#include <linux/ioctl.h>
 
 #undef TTY_DEBUG_HANGUP
 #ifdef TTY_DEBUG_HANGUP
@@ -66,8 +69,13 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
 #ifdef CONFIG_UNIX98_PTYS
                if (tty->driver == ptm_driver) {
                        mutex_lock(&devpts_mutex);
-                       if (tty->link->driver_data)
-                               devpts_pty_kill(tty->link->driver_data);
+                       if (tty->link->driver_data) {
+                               struct path *path = tty->link->driver_data;
+
+                               devpts_pty_kill(path->dentry);
+                               path_put(path);
+                               kfree(path);
+                       }
                        mutex_unlock(&devpts_mutex);
                }
 #endif
@@ -440,6 +448,48 @@ err:
        return retval;
 }
 
+/**
+ *     pty_open_peer - open the peer of a pty
+ *     @tty: the peer of the pty being opened
+ *
+ *     Open the cached dentry in tty->link, providing a safe way for userspace
+ *     to get the slave end of a pty (where they have the master fd and cannot
+ *     access or trust the mount namespace /dev/pts was mounted inside).
+ */
+static struct file *pty_open_peer(struct tty_struct *tty, int flags)
+{
+       if (tty->driver->subtype != PTY_TYPE_MASTER)
+               return ERR_PTR(-EIO);
+       return dentry_open(tty->link->driver_data, flags, current_cred());
+}
+
+static int pty_get_peer(struct tty_struct *tty, int flags)
+{
+       int fd = -1;
+       struct file *filp = NULL;
+       int retval = -EINVAL;
+
+       fd = get_unused_fd_flags(0);
+       if (fd < 0) {
+               retval = fd;
+               goto err;
+       }
+
+       filp = pty_open_peer(tty, flags);
+       if (IS_ERR(filp)) {
+               retval = PTR_ERR(filp);
+               goto err_put;
+       }
+
+       fd_install(fd, filp);
+       return fd;
+
+err_put:
+       put_unused_fd(fd);
+err:
+       return retval;
+}
+
 static void pty_cleanup(struct tty_struct *tty)
 {
        tty_port_put(tty->port);
@@ -613,6 +663,8 @@ static int pty_unix98_ioctl(struct tty_struct *tty,
                return pty_get_pktmode(tty, (int __user *)arg);
        case TIOCGPTN: /* Get PT Number */
                return put_user(tty->index, (unsigned int __user *)arg);
+       case TIOCGPTPEER: /* Open the other end */
+               return pty_get_peer(tty, (int) arg);
        case TIOCSIG:    /* Send signal to other side of pty */
                return pty_signal(tty, (int) arg);
        }
@@ -740,6 +792,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
 {
        struct pts_fs_info *fsi;
        struct tty_struct *tty;
+       struct path *pts_path;
        struct dentry *dentry;
        int retval;
        int index;
@@ -793,16 +846,26 @@ static int ptmx_open(struct inode *inode, struct file *filp)
                retval = PTR_ERR(dentry);
                goto err_release;
        }
-       tty->link->driver_data = dentry;
+       /* We need to cache a fake path for TIOCGPTPEER. */
+       pts_path = kmalloc(sizeof(struct path), GFP_KERNEL);
+       if (!pts_path)
+               goto err_release;
+       pts_path->mnt = filp->f_path.mnt;
+       pts_path->dentry = dentry;
+       path_get(pts_path);
+       tty->link->driver_data = pts_path;
 
        retval = ptm_driver->ops->open(tty, filp);
        if (retval)
-               goto err_release;
+               goto err_path_put;
 
        tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
 
        tty_unlock(tty);
        return 0;
+err_path_put:
+       path_put(pts_path);
+       kfree(pts_path);
 err_release:
        tty_unlock(tty);
        // This will also put-ref the fsi
index 143dacbb7d9ae452bc8e27d56d4c7f8485ac8acc..06d5f7ddf84e73bc08e1719c1c97795ada561fff 100644 (file)
@@ -77,6 +77,7 @@
 #define TIOCGPKT       _IOR('T', 0x38, int) /* Get packet mode state */
 #define TIOCGPTLCK     _IOR('T', 0x39, int) /* Get Pty lock state */
 #define TIOCGEXCL      _IOR('T', 0x40, int) /* Get exclusive mode state */
+#define TIOCGPTPEER    _IOR('T', 0x41, int) /* Safely open the slave */
 
 #define FIONCLEX       0x5450
 #define FIOCLEX                0x5451