diff options
author | jmallett <jmallett@FreeBSD.org> | 2003-01-02 20:44:41 +0000 |
---|---|---|
committer | jmallett <jmallett@FreeBSD.org> | 2003-01-02 20:44:41 +0000 |
commit | a33543dd9281563dbf12b04862c79e090d59086b (patch) | |
tree | b73789d43d391b64d579648dab88f31e3703a140 /lib/libc/stdlib/grantpt.c | |
parent | e7208853a8e3200ee2874614736c059b0c1a0611 (diff) | |
download | FreeBSD-src-a33543dd9281563dbf12b04862c79e090d59086b.zip FreeBSD-src-a33543dd9281563dbf12b04862c79e090d59086b.tar.gz |
Implement POSIX grantpt(3) functionality, and add a pt_chown utility (akin
to Solaris, it is in /usr/libexec) to perform the handing over of tty nodes
to the user being granted the pty.
Submitted by: Ryan Younce <ryany@pobox.com>
Reviewed by: security-officer@, standards@, mike@
Diffstat (limited to 'lib/libc/stdlib/grantpt.c')
-rw-r--r-- | lib/libc/stdlib/grantpt.c | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/lib/libc/stdlib/grantpt.c b/lib/libc/stdlib/grantpt.c new file mode 100644 index 0000000..3e69f17 --- /dev/null +++ b/lib/libc/stdlib/grantpt.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2002 The FreeBSD Project, Inc. + * All rights reserved. + * + * This software includes code contributed to the FreeBSD Project + * by Ryan Younce of North Carolina State University. + * + * 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 FreeBSD Project 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 FREEBSD PROJECT 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 FREEBSD PROJECT OR ITS 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> +#ifndef lint +__FBSDID("$FreeBSD$"); +#endif /* not lint */ + +#include "namespace.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> + +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <paths.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#include "un-namespace.h" + +#define PTM_MAJOR 6 /* pseudo tty master major */ +#define PTS_MAJOR 5 /* pseudo tty slave major */ +#define PTM_PREFIX "pty" /* pseudo tty master naming convention */ +#define PTS_PREFIX "tty" /* pseudo tty slave naming convention */ + +/* + * The following are range values for pseudo TTY devices. Pseudo TTYs have a + * name of /dev/[pt]ty[p-sP-S][0-9a-v], yielding 256 combinations per major. + */ +#define PT_MAX 256 +#define PT_DEV1 "pqrsPQRS" +#define PT_DEV2 "0123456789abcdefghijklmnopqrstuv" + +/* + * grantpt(3) support utility. + */ +#define _PATH_PTCHOWN "/usr/libexec/pt_chown" + +/* + * ISPTM(x) returns 0 for struct stat x if x is not a pty master. + * The bounds checking may be unnecessary but it does eliminate doubt. + */ +#define ISPTM(x) (S_ISCHR((x).st_mode) && \ + major((x).st_rdev) == PTM_MAJOR && \ + minor((x).st_rdev) >= 0 && \ + minor((x).st_rdev) < PT_MAX) + +/* + * grantpt(): grant ownership of a slave pseudo-terminal device to the + * current user. + */ + +int +grantpt(int fildes) +{ + int retval, serrno, status; + pid_t pid, spid; + gid_t gid; + char *slave; + sigset_t oblock, nblock; + struct stat sbuf; + struct group *grp; + + retval = -1; + serrno = errno; + + if ((slave = ptsname(fildes)) != NULL) { + /* + * Block SIGCHLD. + */ + (void)sigemptyset(&nblock); + (void)sigaddset(&nblock, SIGCHLD); + (void)_sigprocmask(SIG_BLOCK, &nblock, &oblock); + + switch (pid = fork()) { + case -1: + break; + case 0: /* child */ + /* + * pt_chown expects the master pseudo TTY to be its + * standard input. + */ + (void)_dup2(fildes, STDIN_FILENO); + (void)_sigprocmask(SIG_SETMASK, &oblock, NULL); + execl(_PATH_PTCHOWN, _PATH_PTCHOWN, (char *)NULL); + _exit(EX_UNAVAILABLE); + /* NOTREACHED */ + default: /* parent */ + /* + * Just wait for the process. Error checking is + * done below. + */ + while ((spid = _waitpid(pid, &status, 0)) == -1 && + (errno == EINTR)) + ; + if (spid != -1 && WIFEXITED(status) && + WEXITSTATUS(status) == EX_OK) + retval = 0; + else + errno = EACCES; + break; + } + + /* + * Restore process's signal mask. + */ + (void)_sigprocmask(SIG_SETMASK, &oblock, NULL); + + if (retval) { + /* + * pt_chown failed. Try to manually change the + * permissions for the slave. + */ + gid = (grp = getgrnam("tty")) ? grp->gr_gid : -1; + if (chown(slave, getuid(), gid) == -1 || + chmod(slave, S_IRUSR | S_IWUSR | S_IWGRP) == -1) + errno = EACCES; + else + retval = 0; + } + } + + if (!retval) + errno = serrno; + + return (retval); +} + +/* + * posix_openpt(): open the first available master pseudo-terminal device + * and return descriptor. + */ +int +posix_openpt(int oflag) +{ + char *mc1, *mc2, master[] = _PATH_DEV PTM_PREFIX "XY"; + const char *pc1, *pc2; + int fildes, bflag, serrno; + + fildes = -1; + bflag = 0; + serrno = errno; + + /* + * Check flag validity. POSIX doesn't require it, + * but we still do so. + */ + if (oflag & ~(O_RDWR | O_NOCTTY)) + errno = EINVAL; + else { + mc1 = master + strlen(_PATH_DEV PTM_PREFIX); + mc2 = mc1 + 1; + + /* Cycle through all possible master PTY devices. */ + for (pc1 = PT_DEV1; !bflag && (*mc1 = *pc1); ++pc1) + for (pc2 = PT_DEV2; *mc2 = *pc2; ++pc2) { + /* + * Break out if we successfully open a PTY, + * or if open() fails due to limits. + */ + if ((fildes = _open(master, oflag)) != -1 || + (errno == EMFILE || errno == ENFILE)) { + ++bflag; + break; + } + } + + if (fildes != -1) + errno = serrno; + else if (!bflag) + errno = EAGAIN; + } + + return (fildes); +} + +/* + * ptsname(): return the pathname of the slave pseudo-terminal device + * associated with the specified master. + */ +char * +ptsname(int fildes) +{ + static char slave[] = _PATH_DEV PTS_PREFIX "XY"; + char *retval; + struct stat sbuf; + + retval = NULL; + + if (_fstat(fildes, &sbuf) == 0) { + if (!ISPTM(sbuf)) + errno = EINVAL; + else { + (void)sprintf(slave, _PATH_DEV PTS_PREFIX "%c%c", + PT_DEV1[minor(sbuf.st_rdev) / 32], + PT_DEV2[minor(sbuf.st_rdev) % 32]); + retval = slave; + } + } + + return (retval); +} + +/* + * unlockpt(): unlock a pseudo-terminal device pair. + */ +int +unlockpt(int fildes) +{ + int retval; + struct stat sbuf; + + /* + * Unlocking a master/slave pseudo-terminal pair has no meaning in a + * non-streams PTY environment. However, we do ensure fildes is a + * valid master pseudo-terminal device. + */ + if ((retval = _fstat(fildes, &sbuf)) == 0 && !ISPTM(sbuf)) { + errno = EINVAL; + retval = -1; + } + + return (retval); +} |