1/* Copyright (c) 1993-2002
2 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Copyright (c) 1987 Oliver Laumann
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program (see the file COPYING); if not, write to the
18 * Free Software Foundation, Inc.,
19 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
20 *
21 ****************************************************************
22 */
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <sys/ioctl.h>
27#include <fcntl.h>
28#include <signal.h>
29#include "config.h"
30#include "screen.h"
31#include "extern.h"
32
33#include <pwd.h>
34
35static int WriteMessage __P((int, struct msg *));
36static sigret_t AttacherSigInt __P(SIGPROTOARG);
37#if defined(SIGWINCH) && defined(TIOCGWINSZ)
38static sigret_t AttacherWinch __P(SIGPROTOARG);
39#endif
40#ifdef LOCK
41static sigret_t DoLock __P(SIGPROTOARG);
42static void  LockTerminal __P((void));
43static sigret_t LockHup __P(SIGPROTOARG);
44static void  screen_builtin_lck __P((void));
45#endif
46#ifdef DEBUG
47static sigret_t AttacherChld __P(SIGPROTOARG);
48#endif
49#ifdef MULTIUSER
50static sigret_t AttachSigCont __P(SIGPROTOARG);
51#endif
52
53extern int real_uid, real_gid, eff_uid, eff_gid;
54extern char *SockName, *SockMatch, SockPath[];
55extern struct passwd *ppp;
56extern char *attach_tty, *attach_term, *LoginName, *preselect;
57extern int xflag, dflag, rflag, quietflag, adaptflag;
58extern struct mode attach_Mode;
59extern struct NewWindow nwin_options;
60extern int MasterPid;
61
62#ifdef MULTIUSER
63extern char *multi;
64extern int multiattach, multi_uid, own_uid;
65extern int tty_mode, tty_oldmode;
66# ifndef USE_SETEUID
67static int multipipe[2];
68# endif
69#endif
70
71
72#ifdef MULTIUSER
73static int ContinuePlease;
74
75static sigret_t
76AttachSigCont SIGDEFARG
77{
78  debug("SigCont()\n");
79  ContinuePlease = 1;
80  SIGRETURN;
81}
82#endif
83
84
85/*
86 *  Send message to a screen backend.
87 *  returns 1 if we could attach one, or 0 if none.
88 *  Understands  MSG_ATTACH, MSG_DETACH, MSG_POW_DETACH
89 *               MSG_CONT, MSG_WINCH and nothing else!
90 */
91
92static int
93WriteMessage(s, m)
94int s;
95struct msg *m;
96{
97  int r, l = sizeof(*m);
98
99  while(l > 0)
100    {
101      r = write(s, (char *)m + (sizeof(*m) - l), l);
102      if (r == -1 && errno == EINTR)
103	continue;
104      if (r == -1 || r == 0)
105	return -1;
106      l -= r;
107    }
108  return 0;
109}
110
111
112int
113Attach(how)
114int how;
115{
116  int n, lasts;
117  struct msg m;
118  struct stat st;
119  char *s;
120
121  debug2("Attach: how=%d, tty=%s\n", how, attach_tty);
122#ifdef MULTIUSER
123# ifndef USE_SETEUID
124  while ((how == MSG_ATTACH || how == MSG_CONT) && multiattach)
125    {
126      int ret;
127
128      if (pipe(multipipe))
129	Panic(errno, "pipe");
130      if (chmod(attach_tty, 0666))
131	Panic(errno, "chmod %s", attach_tty);
132      tty_oldmode = tty_mode;
133      eff_uid = -1;	/* make UserContext fork */
134      real_uid = multi_uid;
135      if ((ret = UserContext()) <= 0)
136	{
137	  char dummy;
138          eff_uid = 0;
139	  real_uid = own_uid;
140	  if (ret < 0)
141	    Panic(errno, "UserContext");
142	  close(multipipe[1]);
143	  read(multipipe[0], &dummy, 1);
144	  if (tty_oldmode >= 0)
145	    {
146	      chmod(attach_tty, tty_oldmode);
147	      tty_oldmode = -1;
148	    }
149	  ret = UserStatus();
150#ifdef LOCK
151	  if (ret == SIG_LOCK)
152	    LockTerminal();
153	  else
154#endif
155#ifdef SIGTSTP
156	  if (ret == SIG_STOP)
157	    kill(getpid(), SIGTSTP);
158	  else
159#endif
160	  if (ret == SIG_POWER_BYE)
161	    {
162	      int ppid;
163	      setgid(real_gid);
164	      setuid(real_uid);
165	      if ((ppid = getppid()) > 1)
166		Kill(ppid, SIGHUP);
167	      exit(0);
168	    }
169	  else
170            exit(ret);
171	  dflag = 0;
172#ifdef MULTI
173	  xflag = 1;
174#endif
175	  how = MSG_ATTACH;
176	  continue;
177	}
178      close(multipipe[0]);
179      eff_uid  = real_uid;
180      break;
181    }
182# else /* USE_SETEUID */
183  if ((how == MSG_ATTACH || how == MSG_CONT) && multiattach)
184    {
185      real_uid = multi_uid;
186      eff_uid  = own_uid;
187      xseteuid(multi_uid);
188      xseteuid(own_uid);
189      if (chmod(attach_tty, 0666))
190	Panic(errno, "chmod %s", attach_tty);
191      tty_oldmode = tty_mode;
192    }
193# endif /* USE_SETEUID */
194#endif /* MULTIUSER */
195
196  bzero((char *) &m, sizeof(m));
197  m.type = how;
198  m.protocol_revision = MSG_REVISION;
199  strncpy(m.m_tty, attach_tty, sizeof(m.m_tty) - 1);
200  m.m_tty[sizeof(m.m_tty) - 1] = 0;
201
202  if (how == MSG_WINCH)
203    {
204      if ((lasts = MakeClientSocket(0)) >= 0)
205	{
206	  WriteMessage(lasts, &m);
207          close(lasts);
208	}
209      return 0;
210    }
211
212  if (how == MSG_CONT)
213    {
214      if ((lasts = MakeClientSocket(0)) < 0)
215        {
216          Panic(0, "Sorry, cannot contact session \"%s\" again.\r\n",
217                 SockName);
218        }
219    }
220  else
221    {
222      n = FindSocket(&lasts, (int *)0, (int *)0, SockMatch);
223      switch (n)
224	{
225	case 0:
226	  if (rflag && (rflag & 1) == 0)
227	    return 0;
228	  if (quietflag)
229	    eexit(10);
230	  Panic(0, SockMatch && *SockMatch ? "There is no screen to be %sed matching %s." : "There is no screen to be %sed.",
231		xflag ? "attach" :
232		dflag ? "detach" :
233                        "resum", SockMatch);
234	  /* NOTREACHED */
235	case 1:
236	  break;
237	default:
238	  if (rflag < 3)
239	    {
240	      if (quietflag)
241		eexit(10 + n);
242	      Panic(0, "Type \"screen [-d] -r [pid.]tty.host\" to resume one of them.");
243	    }
244	  /* NOTREACHED */
245	}
246    }
247  /*
248   * Go in UserContext. Advantage is, you can kill your attacher
249   * when things go wrong. Any disadvantages? jw.
250   * Do this before the attach to prevent races!
251   */
252#ifdef MULTIUSER
253  if (!multiattach)
254#endif
255    setuid(real_uid);
256#if defined(MULTIUSER) && defined(USE_SETEUID)
257  else
258    {
259      /* This call to xsetuid should also set the saved uid */
260      xseteuid(real_uid); /* multi_uid, allow backend to send signals */
261    }
262#endif
263  setgid(real_gid);
264  eff_uid = real_uid;
265  eff_gid = real_gid;
266
267  debug2("Attach: uid %d euid %d\n", (int)getuid(), (int)geteuid());
268  MasterPid = 0;
269  for (s = SockName; *s; s++)
270    {
271      if (*s > '9' || *s < '0')
272	break;
273      MasterPid = 10 * MasterPid + (*s - '0');
274    }
275  debug1("Attach decided, it is '%s'\n", SockPath);
276  debug1("Attach found MasterPid == %d\n", MasterPid);
277  if (stat(SockPath, &st) == -1)
278    Panic(errno, "stat %s", SockPath);
279  if ((st.st_mode & 0600) != 0600)
280    Panic(0, "Socket is in wrong mode (%03o)", (int)st.st_mode);
281
282  /*
283   * Change: if -x or -r ignore failing -d
284   */
285  if ((xflag || rflag) && dflag && (st.st_mode & 0700) == 0600)
286    dflag = 0;
287
288  /*
289   * Without -x, the mode must match.
290   * With -x the mode is irrelevant unless -d.
291   */
292  if ((dflag || !xflag) && (st.st_mode & 0700) != (dflag ? 0700 : 0600))
293    Panic(0, "That screen is %sdetached.", dflag ? "already " : "not ");
294#ifdef REMOTE_DETACH
295  if (dflag &&
296      (how == MSG_ATTACH || how == MSG_DETACH || how == MSG_POW_DETACH))
297    {
298      m.m.detach.dpid = getpid();
299      strncpy(m.m.detach.duser, LoginName, sizeof(m.m.detach.duser) - 1);
300      m.m.detach.duser[sizeof(m.m.detach.duser) - 1] = 0;
301# ifdef POW_DETACH
302      if (dflag == 2)
303	m.type = MSG_POW_DETACH;
304      else
305# endif
306	m.type = MSG_DETACH;
307      if (WriteMessage(lasts, &m))
308	Panic(errno, "WriteMessage");
309      close(lasts);
310      if (how != MSG_ATTACH)
311	return 0;	/* we detached it. jw. */
312      sleep(1);	/* we dont want to overrun our poor backend. jw. */
313      if ((lasts = MakeClientSocket(0)) == -1)
314	Panic(0, "Cannot contact screen again. Sigh.");
315      m.type = how;
316    }
317#endif
318  ASSERT(how == MSG_ATTACH || how == MSG_CONT);
319  strncpy(m.m.attach.envterm, attach_term, sizeof(m.m.attach.envterm) - 1);
320  m.m.attach.envterm[sizeof(m.m.attach.envterm) - 1] = 0;
321  debug1("attach: sending %d bytes... ", (int)sizeof(m));
322
323  strncpy(m.m.attach.auser, LoginName, sizeof(m.m.attach.auser) - 1);
324  m.m.attach.auser[sizeof(m.m.attach.auser) - 1] = 0;
325  m.m.attach.esc = DefaultEsc;
326  m.m.attach.meta_esc = DefaultMetaEsc;
327  strncpy(m.m.attach.preselect, preselect ? preselect : "", sizeof(m.m.attach.preselect) - 1);
328  m.m.attach.preselect[sizeof(m.m.attach.preselect) - 1] = 0;
329  m.m.attach.apid = getpid();
330  m.m.attach.adaptflag = adaptflag;
331  m.m.attach.lines = m.m.attach.columns = 0;
332  if ((s = getenv("LINES")))
333    m.m.attach.lines = atoi(s);
334  if ((s = getenv("COLUMNS")))
335    m.m.attach.columns = atoi(s);
336  m.m.attach.encoding = nwin_options.encoding > 0 ? nwin_options.encoding + 1 : 0;
337
338#ifdef MULTIUSER
339  /* setup CONT signal handler to repair the terminal mode */
340  if (multi && (how == MSG_ATTACH || how == MSG_CONT))
341    signal(SIGCONT, AttachSigCont);
342#endif
343
344  if (WriteMessage(lasts, &m))
345    Panic(errno, "WriteMessage");
346  close(lasts);
347  debug1("Attach(%d): sent\n", m.type);
348#ifdef MULTIUSER
349  if (multi && (how == MSG_ATTACH || how == MSG_CONT))
350    {
351      while (!ContinuePlease)
352        pause();	/* wait for SIGCONT */
353      signal(SIGCONT, SIG_DFL);
354      ContinuePlease = 0;
355# ifndef USE_SETEUID
356      close(multipipe[1]);
357# else
358      xseteuid(own_uid);
359      if (tty_oldmode >= 0)
360        if (chmod(attach_tty, tty_oldmode))
361          Panic(errno, "chmod %s", attach_tty);
362      tty_oldmode = -1;
363      xseteuid(real_uid);
364# endif
365    }
366#endif
367  rflag = 0;
368  return 1;
369}
370
371
372#if defined(DEBUG) || !defined(DO_NOT_POLL_MASTER)
373static int AttacherPanic = 0;
374#endif
375
376#ifdef DEBUG
377static sigret_t
378AttacherChld SIGDEFARG
379{
380  AttacherPanic = 1;
381  SIGRETURN;
382}
383#endif
384
385static sigret_t
386AttacherSigAlarm SIGDEFARG
387{
388#ifdef DEBUG
389  static int tick_cnt = 0;
390  if ((tick_cnt = (tick_cnt + 1) % 4) == 0)
391    debug("tick\n");
392#endif
393  SIGRETURN;
394}
395
396/*
397 * the frontend's Interrupt handler
398 * we forward SIGINT to the poor backend
399 */
400static sigret_t
401AttacherSigInt SIGDEFARG
402{
403  signal(SIGINT, AttacherSigInt);
404  Kill(MasterPid, SIGINT);
405  SIGRETURN;
406}
407
408/*
409 * Unfortunatelly this is also the SIGHUP handler, so we have to
410 * check if the backend is already detached.
411 */
412
413sigret_t
414AttacherFinit SIGDEFARG
415{
416  struct stat statb;
417  struct msg m;
418  int s;
419
420  debug("AttacherFinit();\n");
421  signal(SIGHUP, SIG_IGN);
422  /* Check if signal comes from backend */
423  if (stat(SockPath, &statb) == 0 && (statb.st_mode & 0777) != 0600)
424    {
425      debug("Detaching backend!\n");
426      bzero((char *) &m, sizeof(m));
427      strncpy(m.m_tty, attach_tty, sizeof(m.m_tty) - 1);
428      m.m_tty[sizeof(m.m_tty) - 1] = 0;
429      debug1("attach_tty is %s\n", attach_tty);
430      m.m.detach.dpid = getpid();
431      m.type = MSG_HANGUP;
432      m.protocol_revision = MSG_REVISION;
433      if ((s = MakeClientSocket(0)) >= 0)
434	{
435          WriteMessage(s, &m);
436	  close(s);
437	}
438    }
439#ifdef MULTIUSER
440  if (tty_oldmode >= 0)
441    {
442      setuid(own_uid);
443      chmod(attach_tty, tty_oldmode);
444    }
445#endif
446  exit(0);
447  SIGRETURN;
448}
449
450#ifdef POW_DETACH
451static sigret_t
452AttacherFinitBye SIGDEFARG
453{
454  int ppid;
455  debug("AttacherFintBye()\n");
456#if defined(MULTIUSER) && !defined(USE_SETEUID)
457  if (multiattach)
458    exit(SIG_POWER_BYE);
459#endif
460  setgid(real_gid);
461#ifdef MULTIUSER
462  setuid(own_uid);
463#else
464  setuid(real_uid);
465#endif
466  /* we don't want to disturb init (even if we were root), eh? jw */
467  if ((ppid = getppid()) > 1)
468    Kill(ppid, SIGHUP);		/* carefully say good bye. jw. */
469  exit(0);
470  SIGRETURN;
471}
472#endif
473
474#if defined(DEBUG) && defined(SIG_NODEBUG)
475static sigret_t
476AttacherNoDebug SIGDEFARG
477{
478  debug("AttacherNoDebug()\n");
479  signal(SIG_NODEBUG, AttacherNoDebug);
480  if (dfp)
481    {
482      debug("debug: closing debug file.\n");
483      fflush(dfp);
484      fclose(dfp);
485      dfp = NULL;
486    }
487  SIGRETURN;
488}
489#endif /* SIG_NODEBUG */
490
491static int SuspendPlease;
492
493static sigret_t
494SigStop SIGDEFARG
495{
496  debug("SigStop()\n");
497  SuspendPlease = 1;
498  SIGRETURN;
499}
500
501#ifdef LOCK
502static int LockPlease;
503
504static sigret_t
505DoLock SIGDEFARG
506{
507# ifdef SYSVSIGS
508  signal(SIG_LOCK, DoLock);
509# endif
510  debug("DoLock()\n");
511  LockPlease = 1;
512  SIGRETURN;
513}
514#endif
515
516#if defined(SIGWINCH) && defined(TIOCGWINSZ)
517static int SigWinchPlease;
518
519static sigret_t
520AttacherWinch SIGDEFARG
521{
522  debug("AttacherWinch()\n");
523  SigWinchPlease = 1;
524  SIGRETURN;
525}
526#endif
527
528
529/*
530 *  Attacher loop - no return
531 */
532
533void
534Attacher()
535{
536  signal(SIGHUP, AttacherFinit);
537  signal(SIG_BYE, AttacherFinit);
538#ifdef POW_DETACH
539  signal(SIG_POWER_BYE, AttacherFinitBye);
540#endif
541#if defined(DEBUG) && defined(SIG_NODEBUG)
542  signal(SIG_NODEBUG, AttacherNoDebug);
543#endif
544#ifdef LOCK
545  signal(SIG_LOCK, DoLock);
546#endif
547  signal(SIGINT, AttacherSigInt);
548#ifdef BSDJOBS
549  signal(SIG_STOP, SigStop);
550#endif
551#if defined(SIGWINCH) && defined(TIOCGWINSZ)
552  signal(SIGWINCH, AttacherWinch);
553#endif
554#ifdef DEBUG
555  signal(SIGCHLD, AttacherChld);
556#endif
557  debug("attacher: going for a nap.\n");
558  dflag = 0;
559#ifdef MULTI
560  xflag = 1;
561#endif
562  for (;;)
563    {
564#ifndef DO_NOT_POLL_MASTER
565      signal(SIGALRM, AttacherSigAlarm);
566      alarm(15);
567      pause();
568      alarm(0);
569      if (kill(MasterPid, 0) < 0 && errno != EPERM)
570        {
571	  debug1("attacher: Panic! MasterPid %d does not exist.\n", MasterPid);
572	  AttacherPanic++;
573	}
574#else
575      pause();
576#endif
577#if defined(DEBUG) || !defined(DO_NOT_POLL_MASTER)
578      if (AttacherPanic)
579        {
580	  fcntl(0, F_SETFL, 0);
581	  SetTTY(0, &attach_Mode);
582	  printf("\nSuddenly the Dungeon collapses!! - You die...\n");
583	  eexit(1);
584        }
585#endif
586#ifdef BSDJOBS
587      if (SuspendPlease)
588	{
589	  SuspendPlease = 0;
590#if defined(MULTIUSER) && !defined(USE_SETEUID)
591	  if (multiattach)
592	    exit(SIG_STOP);
593#endif
594	  signal(SIGTSTP, SIG_DFL);
595	  debug("attacher: killing myself SIGTSTP\n");
596	  kill(getpid(), SIGTSTP);
597	  debug("attacher: continuing from stop\n");
598	  signal(SIG_STOP, SigStop);
599	  (void) Attach(MSG_CONT);
600	}
601#endif
602#ifdef LOCK
603      if (LockPlease)
604	{
605	  LockPlease = 0;
606#if defined(MULTIUSER) && !defined(USE_SETEUID)
607	  if (multiattach)
608	    exit(SIG_LOCK);
609#endif
610	  LockTerminal();
611# ifdef SYSVSIGS
612	  signal(SIG_LOCK, DoLock);
613# endif
614	  (void) Attach(MSG_CONT);
615	}
616#endif	/* LOCK */
617#if defined(SIGWINCH) && defined(TIOCGWINSZ)
618      if (SigWinchPlease)
619	{
620	  SigWinchPlease = 0;
621# ifdef SYSVSIGS
622	  signal(SIGWINCH, AttacherWinch);
623# endif
624	  (void) Attach(MSG_WINCH);
625	}
626#endif	/* SIGWINCH */
627    }
628}
629
630#ifdef LOCK
631
632/* ADDED by Rainer Pruy 10/15/87 */
633/* POLISHED by mls. 03/10/91 */
634
635static char LockEnd[] = "Welcome back to screen !!\n";
636
637static sigret_t
638LockHup SIGDEFARG
639{
640  int ppid = getppid();
641  setgid(real_gid);
642#ifdef MULTIUSER
643  setuid(own_uid);
644#else
645  setuid(real_uid);
646#endif
647  if (ppid > 1)
648    Kill(ppid, SIGHUP);
649  exit(0);
650}
651
652static void
653LockTerminal()
654{
655  char *prg;
656  int sig, pid;
657  sigret_t (*sigs[NSIG])__P(SIGPROTOARG);
658
659  for (sig = 1; sig < NSIG; sig++)
660    sigs[sig] = signal(sig, sig == SIGCHLD ? SIG_DFL : SIG_IGN);
661  signal(SIGHUP, LockHup);
662  printf("\n");
663
664  prg = getenv("LOCKPRG");
665  if (prg && strcmp(prg, "builtin") && !access(prg, X_OK))
666    {
667      signal(SIGCHLD, SIG_DFL);
668      debug1("lockterminal: '%s' seems executable, execl it!\n", prg);
669      if ((pid = fork()) == 0)
670        {
671          /* Child */
672          setgid(real_gid);
673#ifdef MULTIUSER
674          setuid(own_uid);
675#else
676          setuid(real_uid);	/* this should be done already */
677#endif
678          closeallfiles(0);	/* important: /etc/shadow may be open */
679          execl(prg, "SCREEN-LOCK", NULL);
680          exit(errno);
681        }
682      if (pid == -1)
683        Msg(errno, "Cannot lock terminal - fork failed");
684      else
685        {
686#ifdef BSDWAIT
687          union wait wstat;
688#else
689          int wstat;
690#endif
691          int wret;
692
693#ifdef hpux
694          signal(SIGCHLD, SIG_DFL);
695#endif
696          errno = 0;
697          while (((wret = wait(&wstat)) != pid) ||
698	         ((wret == -1) && (errno == EINTR))
699	         )
700	    errno = 0;
701
702          if (errno)
703	    {
704	      Msg(errno, "Lock");
705	      sleep(2);
706	    }
707	  else if (WTERMSIG(wstat) != 0)
708	    {
709	      fprintf(stderr, "Lock: %s: Killed by signal: %d%s\n", prg,
710		      WTERMSIG(wstat), WIFCORESIG(wstat) ? " (Core dumped)" : "");
711	      sleep(2);
712	    }
713	  else if (WEXITSTATUS(wstat))
714	    {
715	      debug2("Lock: %s: return code %d\n", prg, WEXITSTATUS(wstat));
716	    }
717          else
718	    printf(LockEnd);
719        }
720    }
721  else
722    {
723      if (prg)
724	{
725          debug1("lockterminal: '%s' seems NOT executable, we use our builtin\n", prg);
726	}
727      else
728	{
729	  debug("lockterminal: using buitin.\n");
730	}
731      screen_builtin_lck();
732    }
733  /* reset signals */
734  for (sig = 1; sig < NSIG; sig++)
735    {
736      if (sigs[sig] != (sigret_t(*)__P(SIGPROTOARG)) -1)
737	signal(sig, sigs[sig]);
738    }
739}				/* LockTerminal */
740
741#ifdef USE_PAM
742
743/*
744 *  PAM support by Pablo Averbuj <pablo@averbuj.com>
745 */
746
747#include <security/pam_appl.h>
748
749static int PAM_conv __P((int, const struct pam_message **, struct pam_response **, void *));
750
751static int
752PAM_conv(num_msg, msg, resp, appdata_ptr)
753int num_msg;
754const struct pam_message **msg;
755struct pam_response **resp;
756void *appdata_ptr;
757{
758  int replies = 0;
759  struct pam_response *reply = NULL;
760
761  reply = malloc(sizeof(struct pam_response)*num_msg);
762  if (!reply)
763    return PAM_CONV_ERR;
764  #define COPY_STRING(s) (s) ? strdup(s) : NULL
765
766  for (replies = 0; replies < num_msg; replies++)
767    {
768      switch (msg[replies]->msg_style)
769        {
770	case PAM_PROMPT_ECHO_OFF:
771	  /* wants password */
772	  reply[replies].resp_retcode = PAM_SUCCESS;
773	  reply[replies].resp = appdata_ptr ? strdup((char *)appdata_ptr) : 0;
774	  break;
775	case PAM_TEXT_INFO:
776	  /* ignore the informational mesage */
777	  /* but first clear out any drek left by malloc */
778	  reply[replies].resp = NULL;
779	  break;
780	case PAM_PROMPT_ECHO_ON:
781	  /* user name given to PAM already */
782	  /* fall through */
783	default:
784	  /* unknown or PAM_ERROR_MSG */
785	  free(reply);
786	  return PAM_CONV_ERR;
787	}
788    }
789  *resp = reply;
790  return PAM_SUCCESS;
791}
792
793static struct pam_conv PAM_conversation = {
794    &PAM_conv,
795    NULL
796};
797
798
799#endif
800
801/* -- original copyright by Luigi Cannelloni 1985 (luigi@faui70.UUCP) -- */
802static void
803screen_builtin_lck()
804{
805  char fullname[100], *cp1, message[100 + 100];
806#ifdef USE_PAM
807  pam_handle_t *pamh = 0;
808  int pam_error;
809#else
810  char *pass, mypass[16 + 1], salt[3];
811#endif
812
813#ifndef USE_PAM
814  pass = ppp->pw_passwd;
815  if (pass == 0 || *pass == 0)
816    {
817      if ((pass = getpass("Key:   ")))
818        {
819          strncpy(mypass, pass, sizeof(mypass) - 1);
820          mypass[sizeof(mypass) - 1] = 0;
821          if (*mypass == 0)
822            return;
823          if ((pass = getpass("Again: ")))
824            {
825              if (strcmp(mypass, pass))
826                {
827                  fprintf(stderr, "Passwords don't match.\007\n");
828                  sleep(2);
829                  return;
830                }
831            }
832        }
833      if (pass == 0)
834        {
835          fprintf(stderr, "Getpass error.\007\n");
836          sleep(2);
837          return;
838        }
839
840      salt[0] = 'A' + (int)(time(0) % 26);
841      salt[1] = 'A' + (int)((time(0) >> 6) % 26);
842      salt[2] = 0;
843      pass = crypt(mypass, salt);
844      pass = ppp->pw_passwd = SaveStr(pass);
845    }
846#endif
847
848  debug("screen_builtin_lck looking in gcos field\n");
849  strncpy(fullname, ppp->pw_gecos, sizeof(fullname) - 9);
850  fullname[sizeof(fullname) - 9] = 0;
851
852  if ((cp1 = index(fullname, ',')) != NULL)
853    *cp1 = '\0';
854  if ((cp1 = index(fullname, '&')) != NULL)
855    {
856      strncpy(cp1, ppp->pw_name, 8);
857      cp1[8] = 0;
858      if (*cp1 >= 'a' && *cp1 <= 'z')
859	*cp1 -= 'a' - 'A';
860    }
861
862  sprintf(message, "Screen used by %s <%s>.\nPassword:\007",
863          fullname, ppp->pw_name);
864
865  /* loop here to wait for correct password */
866  for (;;)
867    {
868      debug("screen_builtin_lck awaiting password\n");
869      errno = 0;
870      if ((cp1 = getpass(message)) == NULL)
871        {
872          AttacherFinit(SIGARG);
873          /* NOTREACHED */
874        }
875#ifdef USE_PAM
876      PAM_conversation.appdata_ptr = cp1;
877      pam_error = pam_start("screen", ppp->pw_name, &PAM_conversation, &pamh);
878      if (pam_error != PAM_SUCCESS)
879	AttacherFinit(SIGARG);		/* goodbye */
880      pam_error = pam_authenticate(pamh, 0);
881      pam_end(pamh, pam_error);
882      PAM_conversation.appdata_ptr = 0;
883      if (pam_error == PAM_SUCCESS)
884	break;
885#else
886      if (!strncmp(crypt(cp1, pass), pass, strlen(pass)))
887	break;
888#endif
889      debug("screen_builtin_lck: NO!!!!!\n");
890      bzero(cp1, strlen(cp1));
891    }
892  bzero(cp1, strlen(cp1));
893  debug("password ok.\n");
894}
895
896#endif	/* LOCK */
897
898
899void
900SendCmdMessage(sty, match, av)
901char *sty;
902char *match;
903char **av;
904{
905  int i, s;
906  struct msg m;
907  char *p;
908  int len, n;
909
910  if (sty == 0)
911    {
912      i = FindSocket(&s, (int *)0, (int *)0, match);
913      if (i == 0)
914	Panic(0, "No screen session found.");
915      if (i != 1)
916	Panic(0, "Use -S to specify a session.");
917    }
918  else
919    {
920#ifdef NAME_MAX
921      if (strlen(sty) > NAME_MAX)
922	sty[NAME_MAX] = 0;
923#endif
924      if (strlen(sty) > 2 * MAXSTR - 1)
925	sty[2 * MAXSTR - 1] = 0;
926      sprintf(SockPath + strlen(SockPath), "/%s", sty);
927      if ((s = MakeClientSocket(1)) == -1)
928	exit(1);
929    }
930  bzero((char *)&m, sizeof(m));
931  m.type = MSG_COMMAND;
932  if (attach_tty)
933    {
934      strncpy(m.m_tty, attach_tty, sizeof(m.m_tty) - 1);
935      m.m_tty[sizeof(m.m_tty) - 1] = 0;
936    }
937  p = m.m.command.cmd;
938  n = 0;
939  for (; *av && n < MAXARGS - 1; ++av, ++n)
940    {
941      len = strlen(*av) + 1;
942      if (p + len >= m.m.command.cmd + sizeof(m.m.command.cmd) - 1)
943	break;
944      strcpy(p, *av);
945      p += len;
946    }
947  *p = 0;
948  m.m.command.nargs = n;
949  strncpy(m.m.attach.auser, LoginName, sizeof(m.m.attach.auser) - 1);
950  m.m.command.auser[sizeof(m.m.command.auser) - 1] = 0;
951  m.protocol_revision = MSG_REVISION;
952  strncpy(m.m.command.preselect, preselect ? preselect : "", sizeof(m.m.command.preselect) - 1);
953  m.m.command.preselect[sizeof(m.m.command.preselect) - 1] = 0;
954  m.m.command.apid = getpid();
955  debug1("SendCommandMsg writing '%s'\n", m.m.command.cmd);
956  if (WriteMessage(s, &m))
957    Msg(errno, "write");
958  close(s);
959}
960