Commit 54ebbfb1 authored by Aleksa Sarai's avatar Aleksa Sarai Committed by Greg Kroah-Hartman

tty: add TIOCGPTPEER ioctl

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: default avatarAleksa Sarai <asarai@suse.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 5f0f187f
...@@ -100,6 +100,7 @@ ...@@ -100,6 +100,7 @@
#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode 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 TIOCSERCONFIG 0x5453
#define TIOCSERGWILD 0x5454 #define TIOCSERGWILD 0x5454
......
...@@ -91,6 +91,7 @@ ...@@ -91,6 +91,7 @@
#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode 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 ... */ /* I hope the range from 0x5480 on is free ... */
#define TIOCSCTTY 0x5480 /* become controlling tty */ #define TIOCSCTTY 0x5480 /* become controlling tty */
......
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode 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 FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451 #define FIOCLEX 0x5451
......
...@@ -100,6 +100,7 @@ ...@@ -100,6 +100,7 @@
#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode 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 TIOCSERCONFIG 0x5453
#define TIOCSERGWILD 0x5454 #define TIOCSERGWILD 0x5454
......
...@@ -93,6 +93,7 @@ ...@@ -93,6 +93,7 @@
#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode 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 TIOCSERCONFIG _IO('T', 83) /* 0x5453 */
#define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */ #define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
#define TIOCGRS485 _IOR('T', 0x41, struct serial_rs485) #define TIOCGRS485 _IOR('T', 0x41, struct serial_rs485)
#define TIOCSRS485 _IOWR('T', 0x42, 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 * double underscore on the front to: a) avoid some programs to
* think we support some ioctls under Linux (autoconfiguration stuff) * think we support some ioctls under Linux (autoconfiguration stuff)
*/ */
...@@ -88,6 +88,7 @@ ...@@ -88,6 +88,7 @@
#define TIOCGPTN _IOR('t', 134, unsigned int) /* Get Pty Number */ #define TIOCGPTN _IOR('t', 134, unsigned int) /* Get Pty Number */
#define TIOCSPTLCK _IOW('t', 135, int) /* Lock/unlock PTY */ #define TIOCSPTLCK _IOW('t', 135, int) /* Lock/unlock PTY */
#define TIOCSIG _IOW('t', 136, int) /* Generate signal on Pty slave */ #define TIOCSIG _IOW('t', 136, int) /* Generate signal on Pty slave */
#define TIOCGPTPEER _IOR('t', 137, int) /* Safely open the slave */
/* Little f */ /* Little f */
#define FIOCLEX _IO('f', 1) #define FIOCLEX _IO('f', 1)
......
...@@ -105,6 +105,7 @@ ...@@ -105,6 +105,7 @@
#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode 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 TIOCSERCONFIG _IO('T', 83)
#define TIOCSERGWILD _IOR('T', 84, int) #define TIOCSERGWILD _IOR('T', 84, int)
......
...@@ -24,6 +24,9 @@ ...@@ -24,6 +24,9 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/mount.h>
#include <linux/file.h>
#include <linux/ioctl.h>
#undef TTY_DEBUG_HANGUP #undef TTY_DEBUG_HANGUP
#ifdef TTY_DEBUG_HANGUP #ifdef TTY_DEBUG_HANGUP
...@@ -66,8 +69,13 @@ static void pty_close(struct tty_struct *tty, struct file *filp) ...@@ -66,8 +69,13 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
#ifdef CONFIG_UNIX98_PTYS #ifdef CONFIG_UNIX98_PTYS
if (tty->driver == ptm_driver) { if (tty->driver == ptm_driver) {
mutex_lock(&devpts_mutex); mutex_lock(&devpts_mutex);
if (tty->link->driver_data) if (tty->link->driver_data) {
devpts_pty_kill(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); mutex_unlock(&devpts_mutex);
} }
#endif #endif
...@@ -440,6 +448,48 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, ...@@ -440,6 +448,48 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
return retval; 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) static void pty_cleanup(struct tty_struct *tty)
{ {
tty_port_put(tty->port); tty_port_put(tty->port);
...@@ -613,6 +663,8 @@ static int pty_unix98_ioctl(struct tty_struct *tty, ...@@ -613,6 +663,8 @@ static int pty_unix98_ioctl(struct tty_struct *tty,
return pty_get_pktmode(tty, (int __user *)arg); return pty_get_pktmode(tty, (int __user *)arg);
case TIOCGPTN: /* Get PT Number */ case TIOCGPTN: /* Get PT Number */
return put_user(tty->index, (unsigned int __user *)arg); 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 */ case TIOCSIG: /* Send signal to other side of pty */
return pty_signal(tty, (int) arg); return pty_signal(tty, (int) arg);
} }
...@@ -740,6 +792,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) ...@@ -740,6 +792,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
{ {
struct pts_fs_info *fsi; struct pts_fs_info *fsi;
struct tty_struct *tty; struct tty_struct *tty;
struct path *pts_path;
struct dentry *dentry; struct dentry *dentry;
int retval; int retval;
int index; int index;
...@@ -793,16 +846,26 @@ static int ptmx_open(struct inode *inode, struct file *filp) ...@@ -793,16 +846,26 @@ static int ptmx_open(struct inode *inode, struct file *filp)
retval = PTR_ERR(dentry); retval = PTR_ERR(dentry);
goto err_release; 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); retval = ptm_driver->ops->open(tty, filp);
if (retval) if (retval)
goto err_release; goto err_path_put;
tty_debug_hangup(tty, "opening (count=%d)\n", tty->count); tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
tty_unlock(tty); tty_unlock(tty);
return 0; return 0;
err_path_put:
path_put(pts_path);
kfree(pts_path);
err_release: err_release:
tty_unlock(tty); tty_unlock(tty);
// This will also put-ref the fsi // This will also put-ref the fsi
......
...@@ -77,6 +77,7 @@ ...@@ -77,6 +77,7 @@
#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ #define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */
#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode 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 FIONCLEX 0x5450
#define FIOCLEX 0x5451 #define FIOCLEX 0x5451
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment