1
0
Fork 0
screen/attacher.c
Daniel Baumann e88291c4cd
Adding upstream version 4.9.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 15:19:57 +02:00

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);
}
}