minix/lib/libutil/pty.c
David van Moolenbroek da21d85025 Add PTYFS, Unix98 pseudo terminal support
This patch adds support for Unix98 pseudo terminals, that is,
posix_openpt(3), grantpt(3), unlockpt(3), /dev/ptmx, and /dev/pts/.
The latter is implemented with a new pseudo file system, PTYFS.

In effect, this patch adds secure support for unprivileged pseudo
terminal allocation, allowing programs such as tmux(1) to be used by
non-root users as well.  Test77 has been extended with new tests, and
no longer needs to run as root.

The new functionality is optional.  To revert to the old behavior,
remove the "ptyfs" entry from /etc/fstab.

Technical nodes:

o The reason for not implementing the NetBSD /dev/ptm approach is that
  implementing the corresponding ioctl (TIOCPTMGET) would require
  adding a number of extremely hairy exceptions to VFS, including the
  PTY driver having to create new file descriptors for its own device
  nodes.

o PTYFS is required for Unix98 PTYs in order to avoid that the PTY
  driver has to be aware of old-style PTY naming schemes and even has
  to call chmod(2) on a disk-backed file system.  PTY cannot be its
  own PTYFS since a character driver may currently not also be a file
  system.  However, PTYFS may be subsumed into a DEVFS in the future.

o The Unix98 PTY behavior differs somewhat from NetBSD's, in that
  slave nodes are created on ptyfs only upon the first call to
  grantpt(3).  This approach obviates the need to revoke access as
  part of the grantpt(3) call.

o Shutting down PTY may leave slave nodes on PTYFS, but once PTY is
  restarted, these leftover slave nodes will be removed before they
  create a security risk.  Unmounting PTYFS will make existing PTY
  slaves permanently unavailable, and absence of PTYFS will block
  allocation of new Unix98 PTYs until PTYFS is (re)mounted.

Change-Id: I822b43ba32707c8815fd0f7d5bb7a438f51421c1
2015-06-23 17:43:46 +00:00

196 lines
5.3 KiB
C

/* $NetBSD: pty.c,v 1.31 2009/02/20 16:44:06 christos Exp $ */
/*-
* Copyright (c) 1990, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)pty.c 8.3 (Berkeley) 5/16/94";
#else
__RCSID("$NetBSD: pty.c,v 1.31 2009/02/20 16:44:06 christos Exp $");
#endif
#endif /* LIBC_SCCS and not lint */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#ifdef __minix
#include <stdlib.h>
#endif /* __minix */
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <util.h>
/*
* XXX: `v' removed until no ports are using console devices which use ttyv0
*/
#define TTY_LETTERS "pqrstuwxyzPQRST"
#define TTY_OLD_SUFFIX "0123456789abcdef"
#define TTY_NEW_SUFFIX "ghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
int
openpty(int *amaster, int *aslave, char *name, struct termios *term,
struct winsize *winp)
{
char line[] = "/dev/XtyXX";
const char *cp1, *cp2, *cp, *linep;
int master, slave;
gid_t ttygid;
mode_t mode;
struct group grs, *grp;
char grbuf[1024];
_DIAGASSERT(amaster != NULL);
_DIAGASSERT(aslave != NULL);
/* name may be NULL */
/* term may be NULL */
/* winp may be NULL */
#if !defined(__minix)
if ((master = open("/dev/ptm", O_RDWR)) != -1) {
struct ptmget pt;
if (ioctl(master, TIOCPTMGET, &pt) != -1) {
(void)close(master);
master = pt.cfd;
slave = pt.sfd;
linep = pt.sn;
goto gotit;
}
(void)close(master);
}
#else /* defined(__minix) */
/*
* On MINIX3, we implement non-root openpty(3) using Unix98 PTYs.
* If this fails, the fallback code below works for root only.
*/
if ((master = posix_openpt(O_RDWR | O_NOCTTY)) != -1) {
if (grantpt(master) != -1 && unlockpt(master) != -1 &&
(linep = ptsname(master)) != NULL &&
(slave = open(linep, O_RDWR | O_NOCTTY)) != -1)
goto gotit;
(void)close(master);
}
#endif /* defined(__minix) */
(void)getgrnam_r("tty", &grs, grbuf, sizeof(grbuf), &grp);
if (grp != NULL) {
ttygid = grp->gr_gid;
mode = S_IRUSR|S_IWUSR|S_IWGRP;
} else {
ttygid = getgid();
mode = S_IRUSR|S_IWUSR;
}
for (cp1 = TTY_LETTERS; *cp1; cp1++) {
line[8] = *cp1;
for (cp = cp2 = TTY_OLD_SUFFIX TTY_NEW_SUFFIX; *cp2; cp2++) {
line[5] = 'p';
line[9] = *cp2;
#ifdef __minix
if ((master = open(line, O_RDWR | O_NOCTTY, 0)) == -1) {
#else
if ((master = open(line, O_RDWR, 0)) == -1) {
#endif
if (errno != ENOENT)
continue; /* busy */
if ((size_t)(cp2 - cp + 1) < sizeof(TTY_OLD_SUFFIX))
return -1; /* out of ptys */
else
break; /* out of ptys in this group */
}
line[5] = 't';
linep = line;
if (chown(line, getuid(), ttygid) == 0 &&
chmod(line, mode) == 0 &&
#if !defined(__minix)
revoke(line) == 0 &&
#endif /* !defined(__minix) */
#ifdef __minix
(slave = open(line, O_RDWR | O_NOCTTY, 0)) != -1) {
#else
(slave = open(line, O_RDWR, 0)) != -1) {
#endif
gotit:
*amaster = master;
*aslave = slave;
if (name)
(void)strcpy(name, linep);
if (term)
(void)tcsetattr(slave, TCSAFLUSH, term);
if (winp)
(void)ioctl(slave, TIOCSWINSZ, winp);
return 0;
}
(void)close(master);
}
}
errno = ENOENT; /* out of ptys */
return -1;
}
pid_t
forkpty(int *amaster, char *name, struct termios *term, struct winsize *winp)
{
int master, slave;
pid_t pid;
_DIAGASSERT(amaster != NULL);
/* name may be NULL */
/* term may be NULL */
/* winp may be NULL */
if (openpty(&master, &slave, name, term, winp) == -1)
return -1;
switch (pid = fork()) {
case -1:
return -1;
case 0:
/*
* child
*/
(void)close(master);
login_tty(slave);
return 0;
}
/*
* parent
*/
*amaster = master;
(void)close(slave);
return pid;
}