
ptys apparently don't behave the same as BSD in regards to select(). No need to flush remaining output to the transcript, only to stdout. Add back code to check the master pty for additional data when we exit the main select loop.
706 lines
17 KiB
C
706 lines
17 KiB
C
/*
|
|
* Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/ioctl.h>
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
#include <sys/select.h>
|
|
#endif /* HAVE_SYS_SELECT_H */
|
|
#include <stdio.h>
|
|
#ifdef STDC_HEADERS
|
|
# include <stdlib.h>
|
|
# include <stddef.h>
|
|
#else
|
|
# ifdef HAVE_STDLIB_H
|
|
# include <stdlib.h>
|
|
# endif
|
|
#endif /* STDC_HEADERS */
|
|
#ifdef HAVE_STRING_H
|
|
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
|
|
# include <memory.h>
|
|
# endif
|
|
# include <string.h>
|
|
#else
|
|
# ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
# endif
|
|
#endif /* HAVE_STRING_H */
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif /* HAVE_UNISTD_H */
|
|
#if TIME_WITH_SYS_TIME
|
|
# include <time.h>
|
|
#endif
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <grp.h>
|
|
#include <pwd.h>
|
|
#include <signal.h>
|
|
|
|
#ifdef HAVE_UTIL_H
|
|
# include <util.h>
|
|
#endif
|
|
#ifdef HAVE_PTY_H
|
|
# include <pty.h>
|
|
#endif
|
|
|
|
#include "sudo.h"
|
|
|
|
#ifndef lint
|
|
__unused static const char rcsid[] = "$Sudo$";
|
|
#endif /* lint */
|
|
|
|
#define SFD_MASTER 0
|
|
#define SFD_SLAVE 1
|
|
#define SFD_LOG 2
|
|
#define SFD_OUTPUT 3
|
|
#define SFD_TIMING 4
|
|
|
|
int script_fds[5];
|
|
|
|
static sig_atomic_t alive = 1;
|
|
|
|
static pid_t child;
|
|
static int child_status;
|
|
|
|
static void script_child __P((const char *path, char *const argv[]));
|
|
static void sync_winsize __P((int src, int dst));
|
|
static void sigchild __P((int signo));
|
|
static void sigwinch __P((int signo));
|
|
static int get_pty __P((int *master, int *slave));
|
|
|
|
/*
|
|
* TODO: run monitor as root?
|
|
*/
|
|
|
|
struct script_buf {
|
|
int len; /* buffer length (how much read in) */
|
|
int off; /* write position (how much already consumed) */
|
|
char buf[16 * 1024];
|
|
};
|
|
|
|
static int
|
|
fdcompar(v1, v2)
|
|
const void *v1;
|
|
const void *v2;
|
|
{
|
|
int i = *(int *)v1;
|
|
int j = *(int *)v2;
|
|
|
|
return(script_fds[i] - script_fds[j]);
|
|
}
|
|
|
|
void
|
|
script_nextid()
|
|
{
|
|
struct stat sb;
|
|
char buf[32], *ep;
|
|
int fd, i, ch;
|
|
unsigned long id = 0;
|
|
int len;
|
|
ssize_t nread;
|
|
char pathbuf[PATH_MAX];
|
|
|
|
/*
|
|
* Create _PATH_SUDO_SESSDIR if it doesn't already exist.
|
|
*/
|
|
if (stat(_PATH_SUDO_SESSDIR, &sb) != 0) {
|
|
if (mkdir(_PATH_SUDO_SESSDIR, S_IRWXU) != 0)
|
|
log_error(USE_ERRNO, "Can't mkdir %s", _PATH_SUDO_SESSDIR);
|
|
} else if (!S_ISDIR(sb.st_mode)) {
|
|
log_error(0, "%s exists but is not a directory (0%o)",
|
|
_PATH_SUDO_SESSDIR, (unsigned int) sb.st_mode);
|
|
}
|
|
|
|
/*
|
|
* Open sequence file
|
|
*/
|
|
len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", _PATH_SUDO_SESSDIR);
|
|
if (len <= 0 || len >= sizeof(pathbuf)) {
|
|
errno = ENAMETOOLONG;
|
|
log_error(USE_ERRNO, "%s/seq", pathbuf);
|
|
}
|
|
fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
|
|
if (fd == -1)
|
|
log_error(USE_ERRNO, "cannot open %s", pathbuf);
|
|
lock_file(fd, SUDO_LOCK);
|
|
|
|
/* Read seq number (base 36). */
|
|
nread = read(fd, buf, sizeof(buf));
|
|
if (nread != 0) {
|
|
if (nread == -1)
|
|
log_error(USE_ERRNO, "cannot read %s", pathbuf);
|
|
id = strtoul(buf, &ep, 36);
|
|
if (buf == ep || id >= 2176782336U)
|
|
log_error(0, "invalid sequence number %s", pathbuf);
|
|
}
|
|
id++;
|
|
|
|
/*
|
|
* Convert id to a string and stash in sudo_user.sessid.
|
|
* Note that that least significant digits go at the end of the string.
|
|
*/
|
|
for (i = 5; i >= 0; i--) {
|
|
ch = id % 36;
|
|
id /= 36;
|
|
buf[i] = ch < 10 ? ch + '0' : ch - 10 + 'A';
|
|
}
|
|
buf[6] = '\n';
|
|
|
|
/* Stash id logging purposes */
|
|
memcpy(sudo_user.sessid, buf, 6);
|
|
sudo_user.sessid[6] = '\0';
|
|
|
|
/* Rewind and overwrite old seq file. */
|
|
if (lseek(fd, 0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7)
|
|
log_error(USE_ERRNO, "Can't write to %s", pathbuf);
|
|
close(fd);
|
|
}
|
|
|
|
static int
|
|
build_idpath(pathbuf)
|
|
char *pathbuf;
|
|
{
|
|
struct stat sb;
|
|
int i, len;
|
|
|
|
if (sudo_user.sessid[0] == '\0')
|
|
log_error(0, "tried to build a session id path without a session id");
|
|
|
|
/*
|
|
* Path is of the form /var/log/sudo-session/00/00/01.
|
|
*/
|
|
len = snprintf(pathbuf, PATH_MAX, "%s/%c%c/%c%c/%c%c", _PATH_SUDO_SESSDIR,
|
|
sudo_user.sessid[0], sudo_user.sessid[1], sudo_user.sessid[2],
|
|
sudo_user.sessid[3], sudo_user.sessid[4], sudo_user.sessid[5]);
|
|
if (len <= 0 && len >= PATH_MAX) {
|
|
errno = ENAMETOOLONG;
|
|
log_error(USE_ERRNO, "%s/%s", _PATH_SUDO_SESSDIR, sudo_user.sessid);
|
|
}
|
|
|
|
/*
|
|
* Create the intermediate subdirs as needed.
|
|
*/
|
|
for (i = 6; i > 0; i -= 3) {
|
|
pathbuf[len - i] = '\0';
|
|
if (stat(pathbuf, &sb) != 0) {
|
|
if (mkdir(pathbuf, S_IRWXU) != 0)
|
|
log_error(USE_ERRNO, "Can't mkdir %s", pathbuf);
|
|
} else if (!S_ISDIR(sb.st_mode)) {
|
|
log_error(0, "%s: %s", pathbuf, strerror(ENOTDIR));
|
|
}
|
|
pathbuf[len - i] = '/';
|
|
}
|
|
|
|
return(len);
|
|
}
|
|
|
|
void
|
|
script_setup()
|
|
{
|
|
char pathbuf[PATH_MAX];
|
|
int len;
|
|
|
|
if (!isatty(STDIN_FILENO))
|
|
log_error(USE_ERRNO, "Standard input is not a tty");
|
|
|
|
if (!get_pty(&script_fds[SFD_MASTER], &script_fds[SFD_SLAVE]))
|
|
log_error(USE_ERRNO, "Can't get pty");
|
|
|
|
/* Copy terminal attrs from stdin -> pty slave. */
|
|
if (!term_copy(STDIN_FILENO, script_fds[SFD_SLAVE])) {
|
|
log_error(USE_ERRNO, "Can't copy terminal attributes");
|
|
}
|
|
sync_winsize(STDIN_FILENO, script_fds[SFD_SLAVE]);
|
|
|
|
if (!term_raw(STDIN_FILENO))
|
|
log_error(USE_ERRNO, "Can't set terminal to raw mode");
|
|
|
|
/*
|
|
* Build a path containing the session id split into two-digit subdirs,
|
|
* so ID 000001 becomes /var/log/sudo-session/00/00/01.
|
|
*/
|
|
len = build_idpath(pathbuf);
|
|
|
|
/*
|
|
* We create 3 files: a log file, one for the raw session data,
|
|
* and one for the timing info.
|
|
*/
|
|
script_fds[SFD_LOG] = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
|
|
if (script_fds[SFD_LOG] == -1)
|
|
log_error(USE_ERRNO, "Can't create %s", pathbuf);
|
|
|
|
strlcat(pathbuf, ".scr", sizeof(pathbuf));
|
|
script_fds[SFD_OUTPUT] = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY,
|
|
S_IRUSR|S_IWUSR);
|
|
if (script_fds[SFD_OUTPUT] == -1)
|
|
log_error(USE_ERRNO, "Can't create %s", pathbuf);
|
|
|
|
pathbuf[len] = '\0';
|
|
strlcat(pathbuf, ".tim", sizeof(pathbuf));
|
|
script_fds[SFD_TIMING] = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
|
|
if (script_fds[SFD_TIMING] == -1)
|
|
log_error(USE_ERRNO, "Can't create %s", pathbuf);
|
|
}
|
|
|
|
int
|
|
script_duplow(fd)
|
|
int fd;
|
|
{
|
|
int i, j, indices[5];
|
|
|
|
/* sort fds so we can dup them safely */
|
|
for (i = 0; i < 5; i++)
|
|
indices[i] = i;
|
|
qsort(indices, 5, sizeof(int), fdcompar);
|
|
|
|
/* Move pty master/slave and session fds to low numbered fds. */
|
|
for (i = 0; i < 5; i++) {
|
|
j = indices[i];
|
|
if (script_fds[j] != fd) {
|
|
#ifdef HAVE_DUP2
|
|
dup2(script_fds[j], fd);
|
|
#else
|
|
close(fd);
|
|
dup(script_fds[j]);
|
|
close(script_fds[j]);
|
|
#endif
|
|
}
|
|
script_fds[j] = fd++;
|
|
}
|
|
return(fd);
|
|
}
|
|
|
|
/* Update output and timing files. */
|
|
static void
|
|
log_output(output, n, then, now, ofile, tfile)
|
|
struct script_buf *output;
|
|
int n;
|
|
struct timeval *then;
|
|
struct timeval *now;
|
|
FILE *ofile;
|
|
FILE *tfile;
|
|
{
|
|
struct timeval tv;
|
|
|
|
fwrite(output->buf + output->off, 1, n, ofile);
|
|
timersub(now, then, &tv);
|
|
fprintf(tfile, "%f %d\n",
|
|
tv.tv_sec + ((double)tv.tv_usec / 1000000), n);
|
|
then->tv_sec = now->tv_sec;
|
|
then->tv_usec = now->tv_usec;
|
|
}
|
|
|
|
int
|
|
script_execv(path, argv)
|
|
const char *path;
|
|
char *const argv[];
|
|
{
|
|
int n, nready;
|
|
fd_set *fdsr, *fdsw;
|
|
struct script_buf input, output;
|
|
struct timeval now, then;
|
|
sigaction_t sa;
|
|
FILE *idfile, *ofile, *tfile;
|
|
|
|
if ((idfile = fdopen(script_fds[SFD_LOG], "w")) == NULL)
|
|
log_error(USE_ERRNO, "fdopen");
|
|
if ((ofile = fdopen(script_fds[SFD_OUTPUT], "w")) == NULL)
|
|
log_error(USE_ERRNO, "fdopen");
|
|
if ((tfile = fdopen(script_fds[SFD_TIMING], "w")) == NULL)
|
|
log_error(USE_ERRNO, "fdopen");
|
|
|
|
child = fork();
|
|
if (child == -1)
|
|
log_error(USE_ERRNO, "Can't fork");
|
|
if (child == 0) {
|
|
/* fork child, setup tty and exec command */
|
|
script_child(path, argv);
|
|
return(-1); /* execv failure */
|
|
}
|
|
|
|
/* Setup signal handlers for child exit and window size changes. */
|
|
zero_bytes(&sa, sizeof(sa));
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_handler = sigchild;
|
|
sa.sa_flags = SA_RESTART|SA_NOCLDSTOP;
|
|
sigaction(SIGCHLD, &sa, NULL);
|
|
sa.sa_handler = sigwinch;
|
|
sa.sa_flags = SA_RESTART;
|
|
sigaction(SIGWINCH, &sa, NULL);
|
|
|
|
gettimeofday(&then, NULL);
|
|
|
|
/* XXX - log more stuff? environment too? */
|
|
fprintf(idfile, "%ld:%s:%s:%s:%s\n", then.tv_sec, user_name,
|
|
runas_pw->pw_name, runas_gr ? runas_gr->gr_name : "", user_tty);
|
|
fprintf(idfile, "%s\n", user_cwd);
|
|
fprintf(idfile, "%s%s%s\n", user_cmnd, user_args ? " " : "",
|
|
user_args ? user_args : "");
|
|
fclose(idfile);
|
|
|
|
n = fcntl(script_fds[SFD_MASTER], F_GETFL, 0);
|
|
if (n != -1) {
|
|
n |= O_NONBLOCK;
|
|
(void) fcntl(script_fds[SFD_MASTER], F_SETFL, n);
|
|
}
|
|
n = fcntl(STDIN_FILENO, F_GETFL, 0);
|
|
if (n != -1) {
|
|
n |= O_NONBLOCK;
|
|
(void) fcntl(STDIN_FILENO, F_SETFL, n);
|
|
}
|
|
n = fcntl(STDOUT_FILENO, F_GETFL, 0);
|
|
if (n != -1) {
|
|
n |= O_NONBLOCK;
|
|
(void) fcntl(STDOUT_FILENO, F_SETFL, n);
|
|
}
|
|
|
|
/*
|
|
* In the event loop we pass input from stdin to master
|
|
* and pass output from master to stdout and ofile.
|
|
* Note that we've set things up such that master is above
|
|
* stdin and stdout (see sudo.c).
|
|
*/
|
|
fdsr = (fd_set *)emalloc2(howmany(script_fds[SFD_MASTER] + 1, NFDBITS),
|
|
sizeof(fd_mask));
|
|
fdsw = (fd_set *)emalloc2(howmany(script_fds[SFD_MASTER] + 1, NFDBITS),
|
|
sizeof(fd_mask));
|
|
zero_bytes(&input, sizeof(input));
|
|
zero_bytes(&output, sizeof(output));
|
|
while (alive) {
|
|
if (input.off == input.len)
|
|
input.off = input.len = 0;
|
|
if (output.off == output.len)
|
|
output.off = output.len = 0;
|
|
|
|
zero_bytes(fdsw, howmany(script_fds[SFD_MASTER] + 1, NFDBITS) * sizeof(fd_mask));
|
|
zero_bytes(fdsr, howmany(script_fds[SFD_MASTER] + 1, NFDBITS) * sizeof(fd_mask));
|
|
if (input.len != sizeof(input.buf))
|
|
FD_SET(STDIN_FILENO, fdsr);
|
|
if (output.len != sizeof(output.buf))
|
|
FD_SET(script_fds[SFD_MASTER], fdsr);
|
|
if (output.len > output.off)
|
|
FD_SET(STDOUT_FILENO, fdsw);
|
|
if (input.len > input.off)
|
|
FD_SET(script_fds[SFD_MASTER], fdsw);
|
|
|
|
nready = select(script_fds[SFD_MASTER] + 1, fdsr, fdsw, NULL, NULL);
|
|
if (nready == -1) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
log_error(USE_ERRNO, "select failed");
|
|
}
|
|
if (FD_ISSET(STDIN_FILENO, fdsr)) {
|
|
n = read(STDIN_FILENO, input.buf + input.len,
|
|
sizeof(input.buf) - input.len);
|
|
if (n == -1) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
if (errno != EAGAIN)
|
|
break;
|
|
} else {
|
|
if (n == 0)
|
|
break; /* got EOF */
|
|
input.len += n;
|
|
}
|
|
}
|
|
if (FD_ISSET(script_fds[SFD_MASTER], fdsw)) {
|
|
n = write(script_fds[SFD_MASTER], input.buf + input.off,
|
|
input.len - input.off);
|
|
if (n == -1) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
if (errno != EAGAIN)
|
|
break;
|
|
} else {
|
|
input.off += n;
|
|
}
|
|
}
|
|
if (FD_ISSET(script_fds[SFD_MASTER], fdsr)) {
|
|
gettimeofday(&now, NULL);
|
|
n = read(script_fds[SFD_MASTER], output.buf + output.len,
|
|
sizeof(output.buf) - output.len);
|
|
if (n == -1) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
if (errno != EAGAIN)
|
|
break;
|
|
} else {
|
|
if (n == 0)
|
|
break; /* got EOF */
|
|
output.len += n;
|
|
|
|
/* Update output and timing files. */
|
|
log_output(&output, n, &then, &now, ofile, tfile);
|
|
}
|
|
}
|
|
if (FD_ISSET(STDOUT_FILENO, fdsw)) {
|
|
n = write(STDOUT_FILENO, output.buf + output.off,
|
|
output.len - output.off);
|
|
if (n == -1) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
if (errno != EAGAIN)
|
|
break;
|
|
} else {
|
|
output.off += n;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Flush any remaining output to stdout (already updated output file). */
|
|
n = fcntl(STDOUT_FILENO, F_GETFL, 0);
|
|
if (n != -1) {
|
|
n &= ~O_NONBLOCK;
|
|
(void) fcntl(STDOUT_FILENO, F_SETFL, n);
|
|
}
|
|
do {
|
|
n = write(STDOUT_FILENO, output.buf + output.off,
|
|
output.len - output.off);
|
|
if (n <= 0)
|
|
break;
|
|
output.off += n;
|
|
} while (output.len > output.off);
|
|
|
|
/* Make sure there is no output remaining on the master pty. */
|
|
for (;;) {
|
|
n = read(script_fds[SFD_MASTER], output.buf, sizeof(output.buf));
|
|
if (n <= 0)
|
|
break;
|
|
output.off = 0;
|
|
output.len = n;
|
|
log_output(&output, output.len, &then, &now, ofile, tfile);
|
|
do {
|
|
n = write(STDOUT_FILENO, output.buf + output.off,
|
|
output.len - output.off);
|
|
if (n <= 0)
|
|
break;
|
|
output.off += n;
|
|
} while (output.len > output.off);
|
|
}
|
|
|
|
term_restore(STDIN_FILENO);
|
|
|
|
if (WIFEXITED(child_status))
|
|
exit(WEXITSTATUS(child_status));
|
|
if (WIFSIGNALED(child_status))
|
|
exit(128 | WSTOPSIG(child_status));
|
|
exit(1);
|
|
}
|
|
|
|
static void
|
|
script_child(path, argv)
|
|
const char *path;
|
|
char *const argv[];
|
|
{
|
|
/*
|
|
* Create new session, make slave controlling terminal and
|
|
* point std{in,out,err} to it.
|
|
*/
|
|
#ifdef HAVE_SETSID
|
|
setsid();
|
|
#else
|
|
setpgrp(0, 0);
|
|
#endif
|
|
#ifdef TIOCSCTTY
|
|
if (ioctl(script_fds[SFD_SLAVE], TIOCSCTTY, NULL) != 0) {
|
|
warning("unable to set controlling tty");
|
|
return;
|
|
}
|
|
#endif
|
|
dup2(script_fds[SFD_SLAVE], STDIN_FILENO);
|
|
dup2(script_fds[SFD_SLAVE], STDOUT_FILENO);
|
|
dup2(script_fds[SFD_SLAVE], STDERR_FILENO);
|
|
|
|
/*
|
|
* Close old fds and exec command.
|
|
*/
|
|
close(script_fds[SFD_MASTER]);
|
|
close(script_fds[SFD_SLAVE]);
|
|
close(script_fds[SFD_LOG]);
|
|
close(script_fds[SFD_OUTPUT]);
|
|
close(script_fds[SFD_TIMING]);
|
|
execv(path, argv);
|
|
}
|
|
|
|
static void
|
|
sync_winsize(src, dst)
|
|
int src;
|
|
int dst;
|
|
{
|
|
#ifdef TIOCGWINSZ
|
|
struct winsize win;
|
|
pid_t pgrp;
|
|
|
|
if (ioctl(src, TIOCGWINSZ, &win) == 0) {
|
|
ioctl(dst, TIOCSWINSZ, &win);
|
|
#ifdef TIOCGPGRP
|
|
if (ioctl(dst, TIOCGPGRP, &pgrp) == 0)
|
|
killpg(pgrp, SIGWINCH);
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
sigchild(signo)
|
|
int signo;
|
|
{
|
|
pid_t pid;
|
|
|
|
#ifdef sudo_waitpid
|
|
do {
|
|
pid = sudo_waitpid(child, &child_status, WNOHANG);
|
|
if (pid == child) {
|
|
alive = 0;
|
|
break;
|
|
}
|
|
} while (pid > 0 || (pid == -1 && errno == EINTR));
|
|
#else
|
|
do {
|
|
pid = wait(&child_status);
|
|
} while (pid == -1 && errno == EINTR);
|
|
alive = 0;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
sigwinch(signo)
|
|
int signo;
|
|
{
|
|
int serrno = errno;
|
|
|
|
sync_winsize(STDIN_FILENO, script_fds[SFD_SLAVE]);
|
|
errno = serrno;
|
|
}
|
|
|
|
#ifdef HAVE_OPENPTY
|
|
static int
|
|
get_pty(master, slave)
|
|
int *master;
|
|
int *slave;
|
|
{
|
|
char line[PATH_MAX];
|
|
struct group *gr;
|
|
gid_t ttygid = -1;
|
|
|
|
if ((gr = sudo_getgrnam("tty")) != NULL)
|
|
ttygid = gr->gr_gid;
|
|
|
|
if (openpty(master, slave, line, NULL, NULL) != 0)
|
|
return(0);
|
|
(void) chown(line, runas_pw->pw_uid, ttygid);
|
|
return(1);
|
|
}
|
|
|
|
#else
|
|
# ifdef HAVE_GRANTPT
|
|
|
|
# ifndef HAVE_POSIX_OPENPT
|
|
static int
|
|
posix_openpt(oflag)
|
|
int oflag;
|
|
{
|
|
int fd;
|
|
|
|
# ifdef _AIX
|
|
fd = open("/dev/ptc", oflag);
|
|
# else
|
|
fd = open("/dev/ptmx", oflag);
|
|
# endif
|
|
return(fd);
|
|
}
|
|
# endif /* HAVE_POSIX_OPENPT */
|
|
|
|
static int
|
|
get_pty(master, slave)
|
|
int *master;
|
|
int *slave;
|
|
{
|
|
char *line;
|
|
|
|
*master = posix_openpt(O_RDWR);
|
|
if (*master == -1)
|
|
return(0);
|
|
|
|
if (unlockpt(*master) != 0) {
|
|
close(*master);
|
|
return(0);
|
|
}
|
|
(void) grantpt(*master);
|
|
line = ptsname(*master);
|
|
if (line == NULL) {
|
|
close(*master);
|
|
return(0);
|
|
}
|
|
*slave = open(line, O_RDWR, 0);
|
|
if (*slave == -1) {
|
|
close(*master);
|
|
return(0);
|
|
}
|
|
(void) chown(line, runas_pw->pw_uid, -1);
|
|
return(1);
|
|
}
|
|
|
|
# else /* !HAVE_GRANTPT */
|
|
|
|
static char line[] = "/dev/ptyXX";
|
|
static int
|
|
get_pty(master, slave)
|
|
int *master;
|
|
int *slave;
|
|
{
|
|
char *bank, *cp;
|
|
struct group *gr;
|
|
gid_t ttygid = -1;
|
|
|
|
if ((gr = sudo_getgrnam("tty")) != NULL)
|
|
ttygid = gr->gr_gid;
|
|
|
|
for (bank = "pqrs"; *bank != '\0'; bank++) {
|
|
line[sizeof("/dev/ptyX") - 2] = *bank;
|
|
for (cp = "0123456789abcdef"; *cp != '\0'; cp++) {
|
|
line[sizeof("/dev/ptyXX") - 2] = *cp;
|
|
*master = open(line, O_RDWR, 0);
|
|
if (*master == -1) {
|
|
if (errno == ENOENT)
|
|
return(0); /* out of ptys */
|
|
continue; /* already in use */
|
|
}
|
|
line[sizeof("/dev/p") - 2] = 't';
|
|
(void) chown(line, runas_pw->pw_uid, ttygid);
|
|
(void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
|
|
# ifdef HAVE_REVOKE
|
|
(void) revoke(line);
|
|
# endif
|
|
*slave = open(line, O_RDWR, 0);
|
|
if (*slave != -1)
|
|
return(1); /* success */
|
|
(void) close(*master);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
# endif /* HAVE_GRANTPT */
|
|
#endif /* HAVE_OPENPTY */
|