b7aa600c41
I have worked hard to reduce diffs against the vendor branch. One notable change in that respect is that we no longer prefer DSA over RSA - the reasons for doing so went away years ago. This may cause some surprises, as ssh will warn about unknown host keys even for hosts whose keys haven't changed. MFC after: 6 weeks
250 lines
6.0 KiB
C
250 lines
6.0 KiB
C
/* $OpenBSD: sshpty.c,v 1.28 2007/09/11 23:49:09 stevesk Exp $ */
|
|
/*
|
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
|
* All rights reserved
|
|
* Allocating a pseudo-terminal, and making it the controlling tty.
|
|
*
|
|
* As far as I am concerned, the code I have written for this software
|
|
* can be used freely for any purpose. Any derived versions of this
|
|
* software must be clearly marked as such, and if the derived work is
|
|
* incompatible with the protocol description in the RFC file, it must be
|
|
* called by a name other than "ssh" or "Secure Shell".
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <signal.h>
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <grp.h>
|
|
#ifdef HAVE_PATHS_H
|
|
# include <paths.h>
|
|
#endif
|
|
#include <pwd.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <termios.h>
|
|
#ifdef HAVE_UTIL_H
|
|
# include <util.h>
|
|
#endif
|
|
#include <unistd.h>
|
|
|
|
#include "sshpty.h"
|
|
#include "log.h"
|
|
#include "misc.h"
|
|
|
|
#ifdef HAVE_PTY_H
|
|
# include <pty.h>
|
|
#endif
|
|
|
|
#ifndef O_NOCTTY
|
|
#define O_NOCTTY 0
|
|
#endif
|
|
|
|
/*
|
|
* Allocates and opens a pty. Returns 0 if no pty could be allocated, or
|
|
* nonzero if a pty was successfully allocated. On success, open file
|
|
* descriptors for the pty and tty sides and the name of the tty side are
|
|
* returned (the buffer must be able to hold at least 64 characters).
|
|
*/
|
|
|
|
int
|
|
pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
|
|
{
|
|
/* openpty(3) exists in OSF/1 and some other os'es */
|
|
char *name;
|
|
int i;
|
|
|
|
i = openpty(ptyfd, ttyfd, NULL, NULL, NULL);
|
|
if (i < 0) {
|
|
error("openpty: %.100s", strerror(errno));
|
|
return 0;
|
|
}
|
|
name = ttyname(*ttyfd);
|
|
if (!name)
|
|
fatal("openpty returns device for which ttyname fails.");
|
|
|
|
strlcpy(namebuf, name, namebuflen); /* possible truncation */
|
|
return 1;
|
|
}
|
|
|
|
/* Releases the tty. Its ownership is returned to root, and permissions to 0666. */
|
|
|
|
void
|
|
pty_release(const char *tty)
|
|
{
|
|
if (chown(tty, (uid_t) 0, (gid_t) 0) < 0)
|
|
error("chown %.100s 0 0 failed: %.100s", tty, strerror(errno));
|
|
if (chmod(tty, (mode_t) 0666) < 0)
|
|
error("chmod %.100s 0666 failed: %.100s", tty, strerror(errno));
|
|
}
|
|
|
|
/* Makes the tty the process's controlling tty and sets it to sane modes. */
|
|
|
|
void
|
|
pty_make_controlling_tty(int *ttyfd, const char *tty)
|
|
{
|
|
int fd;
|
|
#ifdef USE_VHANGUP
|
|
void *old;
|
|
#endif /* USE_VHANGUP */
|
|
|
|
#ifdef _UNICOS
|
|
if (setsid() < 0)
|
|
error("setsid: %.100s", strerror(errno));
|
|
|
|
fd = open(tty, O_RDWR|O_NOCTTY);
|
|
if (fd != -1) {
|
|
signal(SIGHUP, SIG_IGN);
|
|
ioctl(fd, TCVHUP, (char *)NULL);
|
|
signal(SIGHUP, SIG_DFL);
|
|
setpgid(0, 0);
|
|
close(fd);
|
|
} else {
|
|
error("Failed to disconnect from controlling tty.");
|
|
}
|
|
|
|
debug("Setting controlling tty using TCSETCTTY.");
|
|
ioctl(*ttyfd, TCSETCTTY, NULL);
|
|
fd = open("/dev/tty", O_RDWR);
|
|
if (fd < 0)
|
|
error("%.100s: %.100s", tty, strerror(errno));
|
|
close(*ttyfd);
|
|
*ttyfd = fd;
|
|
#else /* _UNICOS */
|
|
|
|
/* First disconnect from the old controlling tty. */
|
|
#ifdef TIOCNOTTY
|
|
fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
|
|
if (fd >= 0) {
|
|
(void) ioctl(fd, TIOCNOTTY, NULL);
|
|
close(fd);
|
|
}
|
|
#endif /* TIOCNOTTY */
|
|
if (setsid() < 0)
|
|
error("setsid: %.100s", strerror(errno));
|
|
|
|
/*
|
|
* Verify that we are successfully disconnected from the controlling
|
|
* tty.
|
|
*/
|
|
fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
|
|
if (fd >= 0) {
|
|
error("Failed to disconnect from controlling tty.");
|
|
close(fd);
|
|
}
|
|
/* Make it our controlling tty. */
|
|
#ifdef TIOCSCTTY
|
|
debug("Setting controlling tty using TIOCSCTTY.");
|
|
if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0)
|
|
error("ioctl(TIOCSCTTY): %.100s", strerror(errno));
|
|
#endif /* TIOCSCTTY */
|
|
#ifdef NEED_SETPGRP
|
|
if (setpgrp(0,0) < 0)
|
|
error("SETPGRP %s",strerror(errno));
|
|
#endif /* NEED_SETPGRP */
|
|
#ifdef USE_VHANGUP
|
|
old = signal(SIGHUP, SIG_IGN);
|
|
vhangup();
|
|
signal(SIGHUP, old);
|
|
#endif /* USE_VHANGUP */
|
|
fd = open(tty, O_RDWR);
|
|
if (fd < 0) {
|
|
error("%.100s: %.100s", tty, strerror(errno));
|
|
} else {
|
|
#ifdef USE_VHANGUP
|
|
close(*ttyfd);
|
|
*ttyfd = fd;
|
|
#else /* USE_VHANGUP */
|
|
close(fd);
|
|
#endif /* USE_VHANGUP */
|
|
}
|
|
/* Verify that we now have a controlling tty. */
|
|
fd = open(_PATH_TTY, O_WRONLY);
|
|
if (fd < 0)
|
|
error("open /dev/tty failed - could not set controlling tty: %.100s",
|
|
strerror(errno));
|
|
else
|
|
close(fd);
|
|
#endif /* _UNICOS */
|
|
}
|
|
|
|
/* Changes the window size associated with the pty. */
|
|
|
|
void
|
|
pty_change_window_size(int ptyfd, u_int row, u_int col,
|
|
u_int xpixel, u_int ypixel)
|
|
{
|
|
struct winsize w;
|
|
|
|
/* may truncate u_int -> u_short */
|
|
w.ws_row = row;
|
|
w.ws_col = col;
|
|
w.ws_xpixel = xpixel;
|
|
w.ws_ypixel = ypixel;
|
|
(void) ioctl(ptyfd, TIOCSWINSZ, &w);
|
|
}
|
|
|
|
void
|
|
pty_setowner(struct passwd *pw, const char *tty)
|
|
{
|
|
struct group *grp;
|
|
gid_t gid;
|
|
mode_t mode;
|
|
struct stat st;
|
|
|
|
/* Determine the group to make the owner of the tty. */
|
|
grp = getgrnam("tty");
|
|
if (grp) {
|
|
gid = grp->gr_gid;
|
|
mode = S_IRUSR | S_IWUSR | S_IWGRP;
|
|
} else {
|
|
gid = pw->pw_gid;
|
|
mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH;
|
|
}
|
|
|
|
/*
|
|
* Change owner and mode of the tty as required.
|
|
* Warn but continue if filesystem is read-only and the uids match/
|
|
* tty is owned by root.
|
|
*/
|
|
if (stat(tty, &st))
|
|
fatal("stat(%.100s) failed: %.100s", tty,
|
|
strerror(errno));
|
|
|
|
#ifdef WITH_SELINUX
|
|
ssh_selinux_setup_pty(pw->pw_name, tty);
|
|
#endif
|
|
|
|
if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
|
|
if (chown(tty, pw->pw_uid, gid) < 0) {
|
|
if (errno == EROFS &&
|
|
(st.st_uid == pw->pw_uid || st.st_uid == 0))
|
|
debug("chown(%.100s, %u, %u) failed: %.100s",
|
|
tty, (u_int)pw->pw_uid, (u_int)gid,
|
|
strerror(errno));
|
|
else
|
|
fatal("chown(%.100s, %u, %u) failed: %.100s",
|
|
tty, (u_int)pw->pw_uid, (u_int)gid,
|
|
strerror(errno));
|
|
}
|
|
}
|
|
|
|
if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
|
|
if (chmod(tty, mode) < 0) {
|
|
if (errno == EROFS &&
|
|
(st.st_mode & (S_IRGRP | S_IROTH)) == 0)
|
|
debug("chmod(%.100s, 0%o) failed: %.100s",
|
|
tty, (u_int)mode, strerror(errno));
|
|
else
|
|
fatal("chmod(%.100s, 0%o) failed: %.100s",
|
|
tty, (u_int)mode, strerror(errno));
|
|
}
|
|
}
|
|
}
|