1117 lines
26 KiB
C
1117 lines
26 KiB
C
/* Copyright (c) 2008, 2009
|
|
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
|
|
* Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
|
|
* Micah Cowan (micah@cowan.name)
|
|
* Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
|
|
* Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
|
|
* Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
|
|
* Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
|
|
* Copyright (c) 1987 Oliver Laumann
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program (see the file COPYING); if not, see
|
|
* https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
|
|
*
|
|
****************************************************************
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <stdbool.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include "screen.h"
|
|
#include "extern.h"
|
|
|
|
#include <pwd.h>
|
|
|
|
static int WriteMessage __P((int, struct msg *));
|
|
static sigret_t AttacherSigInt __P(SIGPROTOARG);
|
|
#if defined(SIGWINCH) && defined(TIOCGWINSZ)
|
|
static sigret_t AttacherWinch __P(SIGPROTOARG);
|
|
#endif
|
|
#ifdef LOCK
|
|
static sigret_t DoLock __P(SIGPROTOARG);
|
|
static void LockTerminal __P((void));
|
|
static sigret_t LockHup __P(SIGPROTOARG);
|
|
static void screen_builtin_lck __P((void));
|
|
#endif
|
|
#ifdef DEBUG
|
|
static sigret_t AttacherChld __P(SIGPROTOARG);
|
|
#endif
|
|
static sigret_t AttachSigCont __P(SIGPROTOARG);
|
|
|
|
extern int real_uid, real_gid, eff_uid, eff_gid;
|
|
extern int ServerSocket;
|
|
extern struct display *displays;
|
|
extern char *SockName, *SockMatch, SockPath[];
|
|
extern char HostName[];
|
|
extern struct passwd *ppp;
|
|
extern char *attach_tty, *attach_term, *LoginName, *preselect;
|
|
/* Indicator whether the current tty exists in another namespace. */
|
|
extern bool attach_tty_is_in_new_ns;
|
|
/* Content of the tty symlink when attach_tty_is_in_new_ns == true. */
|
|
extern char attach_tty_name_in_ns[];
|
|
extern int xflag, dflag, rflag, quietflag, adaptflag;
|
|
extern struct mode attach_Mode;
|
|
extern struct NewWindow nwin_options;
|
|
extern int MasterPid, attach_fd;
|
|
|
|
#ifdef MULTIUSER
|
|
extern char *multi;
|
|
extern int multiattach, multi_uid, own_uid;
|
|
extern int tty_mode, tty_oldmode;
|
|
# ifndef USE_SETEUID
|
|
static int multipipe[2];
|
|
# endif
|
|
#endif
|
|
|
|
|
|
static int ContinuePlease;
|
|
|
|
static sigret_t
|
|
AttachSigCont SIGDEFARG
|
|
{
|
|
debug("SigCont()\n");
|
|
ContinuePlease = 1;
|
|
SIGRETURN;
|
|
}
|
|
|
|
static int QueryResult;
|
|
|
|
static sigret_t
|
|
QueryResultSuccess SIGDEFARG
|
|
{
|
|
QueryResult = 1;
|
|
SIGRETURN;
|
|
}
|
|
|
|
static sigret_t
|
|
QueryResultFail SIGDEFARG
|
|
{
|
|
QueryResult = 2;
|
|
SIGRETURN;
|
|
}
|
|
|
|
/*
|
|
* Send message to a screen backend.
|
|
* returns 1 if we could attach one, or 0 if none.
|
|
* Understands MSG_ATTACH, MSG_DETACH, MSG_POW_DETACH
|
|
* MSG_CONT, MSG_WINCH and nothing else!
|
|
*
|
|
* if type == MSG_ATTACH and sockets are used, attaches
|
|
* tty file descriptor.
|
|
*/
|
|
|
|
static int
|
|
WriteMessage(s, m)
|
|
int s;
|
|
struct msg *m;
|
|
{
|
|
int r, l = sizeof(*m);
|
|
bool is_socket;
|
|
|
|
is_socket = IsSocket(SockPath);
|
|
if (is_socket && m->type == MSG_ATTACH)
|
|
return SendAttachMsg(s, m, attach_fd);
|
|
|
|
while(l > 0)
|
|
{
|
|
r = write(s, (char *)m + (sizeof(*m) - l), l);
|
|
if (r == -1 && errno == EINTR)
|
|
continue;
|
|
if (r == -1 || r == 0)
|
|
return -1;
|
|
l -= r;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
Attach(how)
|
|
int how;
|
|
{
|
|
int n, lasts;
|
|
struct msg m;
|
|
struct stat st;
|
|
char *s;
|
|
bool is_socket;
|
|
|
|
debug2("Attach: how=%d, tty=%s\n", how, attach_tty);
|
|
#ifdef MULTIUSER
|
|
# ifndef USE_SETEUID
|
|
while ((how == MSG_ATTACH || how == MSG_CONT) && multiattach)
|
|
{
|
|
int ret;
|
|
|
|
if (pipe(multipipe))
|
|
Panic(errno, "pipe");
|
|
if (chmod(attach_tty, 0666))
|
|
Panic(errno, "chmod %s", attach_tty);
|
|
tty_oldmode = tty_mode;
|
|
eff_uid = -1; /* make UserContext fork */
|
|
real_uid = multi_uid;
|
|
if ((ret = UserContext()) <= 0)
|
|
{
|
|
char dummy;
|
|
eff_uid = 0;
|
|
real_uid = own_uid;
|
|
if (ret < 0)
|
|
Panic(errno, "UserContext");
|
|
close(multipipe[1]);
|
|
read(multipipe[0], &dummy, 1);
|
|
if (tty_oldmode >= 0)
|
|
{
|
|
chmod(attach_tty, tty_oldmode);
|
|
tty_oldmode = -1;
|
|
}
|
|
ret = UserStatus();
|
|
#ifdef LOCK
|
|
if (ret == SIG_LOCK)
|
|
LockTerminal();
|
|
else
|
|
#endif
|
|
#ifdef SIGTSTP
|
|
if (ret == SIG_STOP)
|
|
kill(getpid(), SIGTSTP);
|
|
else
|
|
#endif
|
|
if (ret == SIG_POWER_BYE)
|
|
{
|
|
int ppid;
|
|
if (setgid(real_gid) || setuid(real_uid))
|
|
Panic(errno, "setuid/gid");
|
|
if ((ppid = getppid()) > 1)
|
|
Kill(ppid, SIGHUP);
|
|
exit(0);
|
|
}
|
|
else
|
|
exit(ret);
|
|
dflag = 0;
|
|
#ifdef MULTI
|
|
xflag = 1;
|
|
#endif
|
|
how = MSG_ATTACH;
|
|
continue;
|
|
}
|
|
close(multipipe[0]);
|
|
eff_uid = real_uid;
|
|
break;
|
|
}
|
|
# else /* USE_SETEUID */
|
|
if ((how == MSG_ATTACH || how == MSG_CONT) && multiattach)
|
|
{
|
|
real_uid = multi_uid;
|
|
eff_uid = own_uid;
|
|
#ifdef HAVE_SETRESUID
|
|
if (setresuid(multi_uid, own_uid, multi_uid))
|
|
Panic(errno, "setresuid");
|
|
#else
|
|
xseteuid(multi_uid);
|
|
xseteuid(own_uid);
|
|
#endif
|
|
if (chmod(attach_tty, 0666))
|
|
Panic(errno, "chmod %s", attach_tty);
|
|
tty_oldmode = tty_mode;
|
|
}
|
|
# endif /* USE_SETEUID */
|
|
#endif /* MULTIUSER */
|
|
|
|
bzero((char *) &m, sizeof(m));
|
|
m.type = how;
|
|
m.protocol_revision = MSG_REVISION;
|
|
strncpy(m.m_tty, attach_tty_is_in_new_ns ? attach_tty_name_in_ns : attach_tty, sizeof(m.m_tty) - 1);
|
|
m.m_tty[sizeof(m.m_tty) - 1] = 0;
|
|
|
|
is_socket = IsSocket(SockPath);
|
|
if (how == MSG_WINCH)
|
|
{
|
|
if ((lasts = MakeClientSocket(0, is_socket)) >= 0)
|
|
{
|
|
WriteMessage(lasts, &m);
|
|
close(lasts);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (how == MSG_CONT)
|
|
{
|
|
if ((lasts = MakeClientSocket(0, is_socket)) < 0)
|
|
{
|
|
Panic(0, "Sorry, cannot contact session \"%s\" again.\r\n",
|
|
SockName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
n = FindSocket(&lasts, (int *)0, (int *)0, SockMatch, &is_socket);
|
|
switch (n)
|
|
{
|
|
case 0:
|
|
if (rflag && (rflag & 1) == 0)
|
|
return 0;
|
|
if (quietflag)
|
|
eexit(10);
|
|
if (SockMatch && *SockMatch) {
|
|
Panic(0, "There is no screen to be %sed matching %s.",
|
|
xflag ? "attach" :
|
|
dflag ? "detach" :
|
|
"resum", SockMatch);
|
|
} else {
|
|
Panic(0, "There is no screen to be %sed.",
|
|
xflag ? "attach" :
|
|
dflag ? "detach" :
|
|
"resum");
|
|
}
|
|
/* NOTREACHED */
|
|
case 1:
|
|
break;
|
|
default:
|
|
if (rflag < 3)
|
|
{
|
|
if (quietflag)
|
|
eexit(10 + n);
|
|
Panic(0, "Type \"screen [-d] -r [pid.]tty.host\" to resume one of them.");
|
|
}
|
|
/* NOTREACHED */
|
|
}
|
|
}
|
|
/*
|
|
* Go in UserContext. Advantage is, you can kill your attacher
|
|
* when things go wrong. Any disadvantages? jw.
|
|
* Do this before the attach to prevent races!
|
|
*/
|
|
#ifdef MULTIUSER
|
|
if (!multiattach)
|
|
#endif
|
|
{
|
|
if (setuid(real_uid))
|
|
Panic(errno, "setuid");
|
|
}
|
|
#if defined(MULTIUSER) && defined(USE_SETEUID)
|
|
else
|
|
{
|
|
/* This call to xsetuid should also set the saved uid */
|
|
xseteuid(real_uid); /* multi_uid, allow backend to send signals */
|
|
}
|
|
#endif
|
|
eff_uid = real_uid;
|
|
if (setgid(real_gid))
|
|
Panic(errno, "setgid");
|
|
eff_gid = real_gid;
|
|
|
|
debug2("Attach: uid %d euid %d\n", (int)getuid(), (int)geteuid());
|
|
MasterPid = 0;
|
|
for (s = SockName; *s; s++)
|
|
{
|
|
if (*s > '9' || *s < '0')
|
|
break;
|
|
MasterPid = 10 * MasterPid + (*s - '0');
|
|
}
|
|
debug1("Attach decided, it is '%s'\n", SockPath);
|
|
debug1("Attach found MasterPid == %d\n", MasterPid);
|
|
if (stat(SockPath, &st) == -1)
|
|
Panic(errno, "stat %s", SockPath);
|
|
if ((st.st_mode & 0600) != 0600)
|
|
Panic(0, "Socket is in wrong mode (%03o)", (int)st.st_mode);
|
|
|
|
/*
|
|
* Change: if -x or -r ignore failing -d
|
|
*/
|
|
if ((xflag || rflag) && dflag && (st.st_mode & 0700) == 0600)
|
|
dflag = 0;
|
|
|
|
/*
|
|
* Without -x, the mode must match.
|
|
* With -x the mode is irrelevant unless -d.
|
|
*/
|
|
if ((dflag || !xflag) && (st.st_mode & 0700) != (dflag ? 0700 : 0600))
|
|
Panic(0, "That screen is %sdetached.", dflag ? "already " : "not ");
|
|
#ifdef REMOTE_DETACH
|
|
if (dflag &&
|
|
(how == MSG_DETACH || how == MSG_POW_DETACH))
|
|
{
|
|
m.m.detach.dpid = getpid();
|
|
strncpy(m.m.detach.duser, LoginName, sizeof(m.m.detach.duser) - 1);
|
|
m.m.detach.duser[sizeof(m.m.detach.duser) - 1] = 0;
|
|
# ifdef POW_DETACH
|
|
if (dflag == 2)
|
|
m.type = MSG_POW_DETACH;
|
|
else
|
|
# endif
|
|
m.type = MSG_DETACH;
|
|
/* If there is no password for the session, or the user enters the correct
|
|
* password, then we get a SIGCONT. Otherwise we get a SIG_BYE */
|
|
signal(SIGCONT, AttachSigCont);
|
|
if (WriteMessage(lasts, &m))
|
|
Panic(errno, "WriteMessage");
|
|
close(lasts);
|
|
while (!ContinuePlease)
|
|
pause(); /* wait for SIGCONT */
|
|
signal(SIGCONT, SIG_DFL);
|
|
ContinuePlease = 0;
|
|
if (how != MSG_ATTACH)
|
|
return 0; /* we detached it. jw. */
|
|
sleep(1); /* we dont want to overrun our poor backend. jw. */
|
|
if ((lasts = MakeClientSocket(0, is_socket)) == -1)
|
|
Panic(0, "Cannot contact screen again. Sigh.");
|
|
m.type = how;
|
|
}
|
|
#endif
|
|
ASSERT(how == MSG_ATTACH || how == MSG_CONT);
|
|
strncpy(m.m.attach.envterm, attach_term, MAXTERMLEN);
|
|
m.m.attach.envterm[MAXTERMLEN] = 0;
|
|
debug1("attach: sending %d bytes... ", (int)sizeof(m));
|
|
|
|
strncpy(m.m.attach.auser, LoginName, sizeof(m.m.attach.auser) - 1);
|
|
m.m.attach.auser[sizeof(m.m.attach.auser) - 1] = 0;
|
|
m.m.attach.esc = DefaultEsc;
|
|
m.m.attach.meta_esc = DefaultMetaEsc;
|
|
strncpy(m.m.attach.preselect, preselect ? preselect : "", sizeof(m.m.attach.preselect) - 1);
|
|
m.m.attach.preselect[sizeof(m.m.attach.preselect) - 1] = 0;
|
|
m.m.attach.apid = getpid();
|
|
m.m.attach.adaptflag = adaptflag;
|
|
m.m.attach.lines = m.m.attach.columns = 0;
|
|
if ((s = getenv("LINES")))
|
|
m.m.attach.lines = atoi(s);
|
|
if ((s = getenv("COLUMNS")))
|
|
m.m.attach.columns = atoi(s);
|
|
m.m.attach.encoding = nwin_options.encoding > 0 ? nwin_options.encoding + 1 : 0;
|
|
|
|
#ifdef REMOTE_DETACH
|
|
#ifdef POW_DETACH
|
|
if (dflag == 2)
|
|
m.m.attach.detachfirst = MSG_POW_DETACH;
|
|
else
|
|
#endif
|
|
if (dflag)
|
|
m.m.attach.detachfirst = MSG_DETACH;
|
|
else
|
|
#endif
|
|
m.m.attach.detachfirst = MSG_ATTACH;
|
|
|
|
#ifdef MULTIUSER
|
|
/* setup CONT signal handler to repair the terminal mode */
|
|
if (multi && (how == MSG_ATTACH || how == MSG_CONT))
|
|
signal(SIGCONT, AttachSigCont);
|
|
#endif
|
|
|
|
if (WriteMessage(lasts, &m))
|
|
Panic(errno, "WriteMessage");
|
|
close(lasts);
|
|
debug1("Attach(%d): sent\n", m.type);
|
|
#ifdef MULTIUSER
|
|
if (multi && (how == MSG_ATTACH || how == MSG_CONT))
|
|
{
|
|
while (!ContinuePlease)
|
|
pause(); /* wait for SIGCONT */
|
|
signal(SIGCONT, SIG_DFL);
|
|
ContinuePlease = 0;
|
|
# ifndef USE_SETEUID
|
|
close(multipipe[1]);
|
|
# else
|
|
xseteuid(own_uid);
|
|
if (tty_oldmode >= 0)
|
|
if (chmod(attach_tty, tty_oldmode))
|
|
Panic(errno, "chmod %s", attach_tty);
|
|
tty_oldmode = -1;
|
|
xseteuid(real_uid);
|
|
# endif
|
|
}
|
|
#endif
|
|
rflag = 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int AttacherPanic = 0;
|
|
|
|
#ifdef DEBUG
|
|
static sigret_t
|
|
AttacherChld SIGDEFARG
|
|
{
|
|
AttacherPanic = 1;
|
|
SIGRETURN;
|
|
}
|
|
#endif
|
|
|
|
static sigret_t
|
|
AttacherSigAlarm SIGDEFARG
|
|
{
|
|
#ifdef DEBUG
|
|
static int tick_cnt = 0;
|
|
if ((tick_cnt = (tick_cnt + 1) % 4) == 0)
|
|
debug("tick\n");
|
|
#endif
|
|
SIGRETURN;
|
|
}
|
|
|
|
/*
|
|
* the frontend's Interrupt handler
|
|
* we forward SIGINT to the poor backend
|
|
*/
|
|
static sigret_t
|
|
AttacherSigInt SIGDEFARG
|
|
{
|
|
signal(SIGINT, AttacherSigInt);
|
|
Kill(MasterPid, SIGINT);
|
|
SIGRETURN;
|
|
}
|
|
|
|
/*
|
|
* Unfortunately this is also the SIGHUP handler, so we have to
|
|
* check if the backend is already detached.
|
|
*/
|
|
|
|
sigret_t
|
|
AttacherFinit SIGDEFARG
|
|
{
|
|
struct stat statb;
|
|
struct msg m;
|
|
int s;
|
|
bool is_socket;
|
|
|
|
debug("AttacherFinit();\n");
|
|
signal(SIGHUP, SIG_IGN);
|
|
/* Check if signal comes from backend */
|
|
if (stat(SockPath, &statb) == 0 && (statb.st_mode & 0777) != 0600)
|
|
{
|
|
debug("Detaching backend!\n");
|
|
bzero((char *) &m, sizeof(m));
|
|
strncpy(m.m_tty, attach_tty_is_in_new_ns ? attach_tty_name_in_ns : attach_tty, sizeof(m.m_tty) - 1);
|
|
m.m_tty[sizeof(m.m_tty) - 1] = 0;
|
|
debug1("attach_tty is %s\n", attach_tty);
|
|
m.m.detach.dpid = getpid();
|
|
m.type = MSG_HANGUP;
|
|
m.protocol_revision = MSG_REVISION;
|
|
is_socket = IsSocket(SockPath);
|
|
if ((s = MakeClientSocket(0, is_socket)) >= 0)
|
|
{
|
|
WriteMessage(s, &m);
|
|
close(s);
|
|
}
|
|
}
|
|
#ifdef MULTIUSER
|
|
if (tty_oldmode >= 0)
|
|
{
|
|
if (setuid(own_uid))
|
|
Panic(errno, "setuid");
|
|
chmod(attach_tty, tty_oldmode);
|
|
}
|
|
#endif
|
|
exit(0);
|
|
SIGRETURN;
|
|
}
|
|
|
|
#ifdef POW_DETACH
|
|
static sigret_t
|
|
AttacherFinitBye SIGDEFARG
|
|
{
|
|
int ppid;
|
|
debug("AttacherFintBye()\n");
|
|
#if defined(MULTIUSER) && !defined(USE_SETEUID)
|
|
if (multiattach)
|
|
exit(SIG_POWER_BYE);
|
|
#endif
|
|
if (setgid(real_gid))
|
|
Panic(errno, "setgid");
|
|
#ifdef MULTIUSER
|
|
if (setuid(own_uid))
|
|
Panic(errno, "setuid");
|
|
#else
|
|
if (setuid(real_uid))
|
|
Panic(errno, "setuid");
|
|
#endif
|
|
/* we don't want to disturb init (even if we were root), eh? jw */
|
|
if ((ppid = getppid()) > 1)
|
|
Kill(ppid, SIGHUP); /* carefully say good bye. jw. */
|
|
exit(0);
|
|
SIGRETURN;
|
|
}
|
|
#endif
|
|
|
|
#if defined(DEBUG) && defined(SIG_NODEBUG)
|
|
static sigret_t
|
|
AttacherNoDebug SIGDEFARG
|
|
{
|
|
debug("AttacherNoDebug()\n");
|
|
signal(SIG_NODEBUG, AttacherNoDebug);
|
|
if (dfp)
|
|
{
|
|
debug("debug: closing debug file.\n");
|
|
fflush(dfp);
|
|
fclose(dfp);
|
|
dfp = NULL;
|
|
}
|
|
SIGRETURN;
|
|
}
|
|
#endif /* SIG_NODEBUG */
|
|
|
|
static int SuspendPlease;
|
|
|
|
static sigret_t
|
|
SigStop SIGDEFARG
|
|
{
|
|
debug("SigStop()\n");
|
|
SuspendPlease = 1;
|
|
SIGRETURN;
|
|
}
|
|
|
|
#ifdef LOCK
|
|
static int LockPlease;
|
|
|
|
static sigret_t
|
|
DoLock SIGDEFARG
|
|
{
|
|
# ifdef SYSVSIGS
|
|
signal(SIG_LOCK, DoLock);
|
|
# endif
|
|
debug("DoLock()\n");
|
|
LockPlease = 1;
|
|
SIGRETURN;
|
|
}
|
|
#endif
|
|
|
|
#if defined(SIGWINCH) && defined(TIOCGWINSZ)
|
|
static int SigWinchPlease;
|
|
|
|
static sigret_t
|
|
AttacherWinch SIGDEFARG
|
|
{
|
|
debug("AttacherWinch()\n");
|
|
SigWinchPlease = 1;
|
|
SIGRETURN;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Attacher loop - no return
|
|
*/
|
|
|
|
void
|
|
Attacher()
|
|
{
|
|
signal(SIGHUP, AttacherFinit);
|
|
signal(SIG_BYE, AttacherFinit);
|
|
#ifdef POW_DETACH
|
|
signal(SIG_POWER_BYE, AttacherFinitBye);
|
|
#endif
|
|
#if defined(DEBUG) && defined(SIG_NODEBUG)
|
|
signal(SIG_NODEBUG, AttacherNoDebug);
|
|
#endif
|
|
#ifdef LOCK
|
|
signal(SIG_LOCK, DoLock);
|
|
#endif
|
|
signal(SIGINT, AttacherSigInt);
|
|
#ifdef BSDJOBS
|
|
signal(SIG_STOP, SigStop);
|
|
#endif
|
|
#if defined(SIGWINCH) && defined(TIOCGWINSZ)
|
|
signal(SIGWINCH, AttacherWinch);
|
|
#endif
|
|
#ifdef DEBUG
|
|
signal(SIGCHLD, AttacherChld);
|
|
#endif
|
|
debug("attacher: going for a nap.\n");
|
|
dflag = 0;
|
|
#ifdef MULTI
|
|
xflag = 1;
|
|
#endif
|
|
for (;;)
|
|
{
|
|
signal(SIGALRM, AttacherSigAlarm);
|
|
alarm(15);
|
|
pause();
|
|
alarm(0);
|
|
if (kill(MasterPid, 0) < 0 && errno != EPERM)
|
|
{
|
|
debug1("attacher: Panic! MasterPid %d does not exist.\n", MasterPid);
|
|
AttacherPanic++;
|
|
}
|
|
if (AttacherPanic)
|
|
{
|
|
fcntl(0, F_SETFL, 0);
|
|
SetTTY(0, &attach_Mode);
|
|
printf("\nError: Cannot find master process to attach to!\n");
|
|
eexit(1);
|
|
}
|
|
#ifdef BSDJOBS
|
|
if (SuspendPlease)
|
|
{
|
|
SuspendPlease = 0;
|
|
#if defined(MULTIUSER) && !defined(USE_SETEUID)
|
|
if (multiattach)
|
|
exit(SIG_STOP);
|
|
#endif
|
|
signal(SIGTSTP, SIG_DFL);
|
|
debug("attacher: killing myself SIGTSTP\n");
|
|
kill(getpid(), SIGTSTP);
|
|
debug("attacher: continuing from stop\n");
|
|
signal(SIG_STOP, SigStop);
|
|
(void) Attach(MSG_CONT);
|
|
}
|
|
#endif
|
|
#ifdef LOCK
|
|
if (LockPlease)
|
|
{
|
|
LockPlease = 0;
|
|
#if defined(MULTIUSER) && !defined(USE_SETEUID)
|
|
if (multiattach)
|
|
exit(SIG_LOCK);
|
|
#endif
|
|
LockTerminal();
|
|
# ifdef SYSVSIGS
|
|
signal(SIG_LOCK, DoLock);
|
|
# endif
|
|
(void) Attach(MSG_CONT);
|
|
}
|
|
#endif /* LOCK */
|
|
#if defined(SIGWINCH) && defined(TIOCGWINSZ)
|
|
if (SigWinchPlease)
|
|
{
|
|
SigWinchPlease = 0;
|
|
# ifdef SYSVSIGS
|
|
signal(SIGWINCH, AttacherWinch);
|
|
# endif
|
|
(void) Attach(MSG_WINCH);
|
|
}
|
|
#endif /* SIGWINCH */
|
|
}
|
|
}
|
|
|
|
#ifdef LOCK
|
|
|
|
/* ADDED by Rainer Pruy 10/15/87 */
|
|
/* POLISHED by mls. 03/10/91 */
|
|
|
|
static char LockEnd[] = "Welcome back to screen !!\n";
|
|
|
|
static sigret_t
|
|
LockHup SIGDEFARG
|
|
{
|
|
int ppid = getppid();
|
|
if (setgid(real_gid))
|
|
Panic(errno, "setgid");
|
|
#ifdef MULTIUSER
|
|
if (setuid(own_uid))
|
|
Panic(errno, "setuid");
|
|
#else
|
|
if (setuid(real_uid))
|
|
Panic(errno, "setuid");
|
|
#endif
|
|
if (ppid > 1)
|
|
Kill(ppid, SIGHUP);
|
|
exit(0);
|
|
}
|
|
|
|
static void
|
|
LockTerminal()
|
|
{
|
|
char *prg;
|
|
int sig, pid;
|
|
sigret_t (*sigs[NSIG])__P(SIGPROTOARG);
|
|
|
|
for (sig = 1; sig < NSIG; sig++)
|
|
sigs[sig] = signal(sig, sig == SIGCHLD ? SIG_DFL : SIG_IGN);
|
|
signal(SIGHUP, LockHup);
|
|
printf("\n");
|
|
|
|
prg = getenv("LOCKPRG");
|
|
if (prg && strcmp(prg, "builtin") && !access(prg, X_OK))
|
|
{
|
|
signal(SIGCHLD, SIG_DFL);
|
|
debug1("lockterminal: '%s' seems executable, execl it!\n", prg);
|
|
if ((pid = fork()) == 0)
|
|
{
|
|
/* Child */
|
|
displays = 0; /* beware of Panic() */
|
|
ServerSocket = -1;
|
|
if (setgid(real_gid))
|
|
Panic(errno, "setgid");
|
|
#ifdef MULTIUSER
|
|
if (setuid(own_uid))
|
|
Panic(errno, "setuid");
|
|
#else
|
|
if (setuid(real_uid)) /* this should be done already */
|
|
Panic(errno, "setuid");
|
|
#endif
|
|
closeallfiles(0); /* important: /etc/shadow may be open */
|
|
execl(prg, "SCREEN-LOCK", NULL);
|
|
exit(errno);
|
|
}
|
|
if (pid == -1)
|
|
Msg(errno, "Cannot lock terminal - fork failed");
|
|
else
|
|
{
|
|
#ifdef BSDWAIT
|
|
union wait wstat;
|
|
#else
|
|
int wstat;
|
|
#endif
|
|
int wret;
|
|
|
|
#ifdef hpux
|
|
signal(SIGCHLD, SIG_DFL);
|
|
#endif
|
|
errno = 0;
|
|
while (((wret = wait(&wstat)) != pid) ||
|
|
((wret == -1) && (errno == EINTR))
|
|
)
|
|
errno = 0;
|
|
|
|
if (errno)
|
|
{
|
|
Msg(errno, "Lock");
|
|
sleep(2);
|
|
}
|
|
else if (WTERMSIG(wstat) != 0)
|
|
{
|
|
fprintf(stderr, "Lock: %s: Killed by signal: %d%s\n", prg,
|
|
WTERMSIG(wstat), WIFCORESIG(wstat) ? " (Core dumped)" : "");
|
|
sleep(2);
|
|
}
|
|
else if (WEXITSTATUS(wstat))
|
|
{
|
|
debug2("Lock: %s: return code %d\n", prg, WEXITSTATUS(wstat));
|
|
}
|
|
else
|
|
printf("%s", LockEnd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (prg)
|
|
{
|
|
debug1("lockterminal: '%s' seems NOT executable, we use our builtin\n", prg);
|
|
}
|
|
else
|
|
{
|
|
debug("lockterminal: using buitin.\n");
|
|
}
|
|
screen_builtin_lck();
|
|
}
|
|
/* reset signals */
|
|
for (sig = 1; sig < NSIG; sig++)
|
|
{
|
|
if (sigs[sig] != (sigret_t(*)__P(SIGPROTOARG)) -1)
|
|
signal(sig, sigs[sig]);
|
|
}
|
|
} /* LockTerminal */
|
|
|
|
#ifdef USE_PAM
|
|
|
|
/*
|
|
* PAM support by Pablo Averbuj <pablo@averbuj.com>
|
|
*/
|
|
|
|
#include <security/pam_appl.h>
|
|
|
|
static int PAM_conv __P((int, const struct pam_message **, struct pam_response **, void *));
|
|
|
|
static int
|
|
PAM_conv(num_msg, msg, resp, appdata_ptr)
|
|
int num_msg;
|
|
const struct pam_message **msg;
|
|
struct pam_response **resp;
|
|
void *appdata_ptr;
|
|
{
|
|
int replies = 0;
|
|
struct pam_response *reply = NULL;
|
|
|
|
reply = malloc(sizeof(struct pam_response)*num_msg);
|
|
if (!reply)
|
|
return PAM_CONV_ERR;
|
|
#define COPY_STRING(s) (s) ? strdup(s) : NULL
|
|
|
|
for (replies = 0; replies < num_msg; replies++)
|
|
{
|
|
switch (msg[replies]->msg_style)
|
|
{
|
|
case PAM_PROMPT_ECHO_OFF:
|
|
/* wants password */
|
|
reply[replies].resp_retcode = PAM_SUCCESS;
|
|
reply[replies].resp = appdata_ptr ? strdup((char *)appdata_ptr) : 0;
|
|
break;
|
|
case PAM_TEXT_INFO:
|
|
/* ignore the informational mesage */
|
|
/* but first clear out any drek left by malloc */
|
|
reply[replies].resp = NULL;
|
|
break;
|
|
case PAM_PROMPT_ECHO_ON:
|
|
/* user name given to PAM already */
|
|
/* fall through */
|
|
default:
|
|
/* unknown or PAM_ERROR_MSG */
|
|
free(reply);
|
|
return PAM_CONV_ERR;
|
|
}
|
|
}
|
|
*resp = reply;
|
|
return PAM_SUCCESS;
|
|
}
|
|
|
|
static struct pam_conv PAM_conversation = {
|
|
&PAM_conv,
|
|
NULL
|
|
};
|
|
|
|
|
|
#endif
|
|
|
|
/* -- original copyright by Luigi Cannelloni 1985 (luigi@faui70.UUCP) -- */
|
|
static void
|
|
screen_builtin_lck()
|
|
{
|
|
char fullname[100], *cp1, message[100 + 100];
|
|
#ifdef USE_PAM
|
|
pam_handle_t *pamh = 0;
|
|
int pam_error;
|
|
char *tty_name;
|
|
#endif
|
|
char *pass = 0, mypass[16 + 1], salt[3];
|
|
int using_pam = 1;
|
|
|
|
#ifdef USE_PAM
|
|
if (!ppp->pw_uid)
|
|
{
|
|
#endif
|
|
using_pam = 0;
|
|
pass = ppp->pw_passwd;
|
|
if (pass == 0 || *pass == 0)
|
|
{
|
|
if ((pass = getpass("Key: ")))
|
|
{
|
|
strncpy(mypass, pass, sizeof(mypass) - 1);
|
|
mypass[sizeof(mypass) - 1] = 0;
|
|
if (*mypass == 0)
|
|
return;
|
|
if ((pass = getpass("Again: ")))
|
|
{
|
|
if (strcmp(mypass, pass))
|
|
{
|
|
fprintf(stderr, "Passwords don't match.\007\n");
|
|
sleep(2);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (pass == 0)
|
|
{
|
|
fprintf(stderr, "Getpass error.\007\n");
|
|
sleep(2);
|
|
return;
|
|
}
|
|
|
|
salt[0] = 'A' + (int)(time(0) % 26);
|
|
salt[1] = 'A' + (int)((time(0) >> 6) % 26);
|
|
salt[2] = 0;
|
|
pass = crypt(mypass, salt);
|
|
if (!pass)
|
|
{
|
|
fprintf(stderr, "crypt() error.\007\n");
|
|
sleep(2);
|
|
return;
|
|
}
|
|
pass = ppp->pw_passwd = SaveStr(pass);
|
|
}
|
|
#ifdef USE_PAM
|
|
}
|
|
#endif
|
|
|
|
debug("screen_builtin_lck looking in gcos field\n");
|
|
strncpy(fullname, ppp->pw_gecos, sizeof(fullname) - 9);
|
|
fullname[sizeof(fullname) - 9] = 0;
|
|
|
|
if ((cp1 = index(fullname, ',')) != NULL)
|
|
*cp1 = '\0';
|
|
if ((cp1 = index(fullname, '&')) != NULL)
|
|
{
|
|
strncpy(cp1, ppp->pw_name, 8);
|
|
cp1[8] = 0;
|
|
if (*cp1 >= 'a' && *cp1 <= 'z')
|
|
*cp1 -= 'a' - 'A';
|
|
}
|
|
|
|
sprintf(message, "Screen used by %s%s<%s> on %s.\nPassword:\007",
|
|
fullname, fullname[0] ? " " : "", ppp->pw_name, HostName);
|
|
|
|
/* loop here to wait for correct password */
|
|
for (;;)
|
|
{
|
|
debug("screen_builtin_lck awaiting password\n");
|
|
errno = 0;
|
|
if ((cp1 = getpass(message)) == NULL)
|
|
{
|
|
AttacherFinit(SIGARG);
|
|
/* NOTREACHED */
|
|
}
|
|
if (using_pam)
|
|
{
|
|
#ifdef USE_PAM
|
|
PAM_conversation.appdata_ptr = cp1;
|
|
pam_error = pam_start("screen", ppp->pw_name, &PAM_conversation, &pamh);
|
|
if (pam_error != PAM_SUCCESS)
|
|
AttacherFinit(SIGARG); /* goodbye */
|
|
|
|
if (strncmp(attach_tty, "/dev/", 5) == 0)
|
|
tty_name = attach_tty + 5;
|
|
else
|
|
tty_name = attach_tty;
|
|
pam_error = pam_set_item(pamh, PAM_TTY, tty_name);
|
|
if (pam_error != PAM_SUCCESS)
|
|
AttacherFinit(SIGARG); /* goodbye */
|
|
|
|
pam_error = pam_authenticate(pamh, 0);
|
|
pam_end(pamh, pam_error);
|
|
PAM_conversation.appdata_ptr = 0;
|
|
if (pam_error == PAM_SUCCESS)
|
|
break;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
char *buf = crypt(cp1, pass);
|
|
if (buf && !strncmp(buf, pass, strlen(pass)))
|
|
break;
|
|
}
|
|
debug("screen_builtin_lck: NO!!!!!\n");
|
|
bzero(cp1, strlen(cp1));
|
|
}
|
|
bzero(cp1, strlen(cp1));
|
|
debug("password ok.\n");
|
|
}
|
|
|
|
#endif /* LOCK */
|
|
|
|
|
|
void
|
|
SendCmdMessage(sty, match, av, query)
|
|
char *sty;
|
|
char *match;
|
|
char **av;
|
|
int query;
|
|
{
|
|
int i, s;
|
|
struct msg m;
|
|
char *p;
|
|
int len, n;
|
|
bool is_socket;
|
|
|
|
if (sty == 0)
|
|
{
|
|
i = FindSocket(&s, (int *)0, (int *)0, match, &is_socket);
|
|
if (i == 0)
|
|
Panic(0, "No screen session found.");
|
|
if (i != 1)
|
|
Panic(0, "Use -S to specify a session.");
|
|
}
|
|
else
|
|
{
|
|
#ifdef NAME_MAX
|
|
if (strlen(sty) > NAME_MAX)
|
|
sty[NAME_MAX] = 0;
|
|
#endif
|
|
if (strlen(sty) > 2 * MAXSTR - 1)
|
|
sty[2 * MAXSTR - 1] = 0;
|
|
sprintf(SockPath + strlen(SockPath), "/%s", sty);
|
|
is_socket = IsSocket(SockPath);
|
|
if ((s = MakeClientSocket(1, is_socket)) == -1)
|
|
exit(1);
|
|
}
|
|
bzero((char *)&m, sizeof(m));
|
|
m.type = query ? MSG_QUERY : MSG_COMMAND;
|
|
if (attach_tty)
|
|
{
|
|
strncpy(m.m_tty, attach_tty_is_in_new_ns ? attach_tty_name_in_ns : attach_tty, sizeof(m.m_tty) - 1);
|
|
m.m_tty[sizeof(m.m_tty) - 1] = 0;
|
|
}
|
|
p = m.m.command.cmd;
|
|
n = 0;
|
|
for (; *av && n < MAXARGS - 1; ++av, ++n)
|
|
{
|
|
len = strlen(*av) + 1;
|
|
if (p + len >= m.m.command.cmd + sizeof(m.m.command.cmd) - 1)
|
|
break;
|
|
strcpy(p, *av);
|
|
p += len;
|
|
}
|
|
*p = 0;
|
|
m.m.command.nargs = n;
|
|
strncpy(m.m.attach.auser, LoginName, sizeof(m.m.attach.auser) - 1);
|
|
m.m.command.auser[sizeof(m.m.command.auser) - 1] = 0;
|
|
m.protocol_revision = MSG_REVISION;
|
|
strncpy(m.m.command.preselect, preselect ? preselect : "", sizeof(m.m.command.preselect) - 1);
|
|
m.m.command.preselect[sizeof(m.m.command.preselect) - 1] = 0;
|
|
m.m.command.apid = getpid();
|
|
debug1("SendCommandMsg writing '%s'\n", m.m.command.cmd);
|
|
if (query)
|
|
{
|
|
/* Create a server socket so we can get back the result */
|
|
char *sp = SockPath + strlen(SockPath);
|
|
char query[] = "-queryX";
|
|
char c;
|
|
int r = -1;
|
|
for (c = 'A'; c <= 'Z'; c++)
|
|
{
|
|
query[6] = c;
|
|
strcpy(sp, query); /* XXX: strncpy? */
|
|
if ((r = MakeServerSocket(is_socket)) >= 0)
|
|
break;
|
|
}
|
|
if (r < 0)
|
|
{
|
|
for (c = '0'; c <= '9'; c++)
|
|
{
|
|
query[6] = c;
|
|
strcpy(sp, query);
|
|
if ((r = MakeServerSocket(is_socket)) >= 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (r < 0)
|
|
Panic(0, "Could not create a listening socket to read the results.");
|
|
|
|
strncpy(m.m.command.writeback, SockPath, sizeof(m.m.command.writeback) - 1);
|
|
m.m.command.writeback[sizeof(m.m.command.writeback) - 1] = '\0';
|
|
|
|
/* Send the message, then wait for a response */
|
|
signal(SIGCONT, QueryResultSuccess);
|
|
signal(SIG_BYE, QueryResultFail);
|
|
if (WriteMessage(s, &m))
|
|
Msg(errno, "write");
|
|
close(s);
|
|
while (!QueryResult)
|
|
pause();
|
|
signal(SIGCONT, SIG_DFL);
|
|
signal(SIG_BYE, SIG_DFL);
|
|
|
|
/* Read the result and spit it out to stdout */
|
|
ReceiveRaw(r);
|
|
unlink(SockPath);
|
|
if (QueryResult == 2) /* An error happened */
|
|
exit(1);
|
|
}
|
|
else
|
|
{
|
|
if (WriteMessage(s, &m))
|
|
Msg(errno, "write");
|
|
close(s);
|
|
}
|
|
}
|
|
|