[2740] in Kerberos-V5-bugs

home help back first fref pref prev next nref lref last post

krb5-appl/432: bsd/login.c steals SIGHUP

daemon@ATHENA.MIT.EDU (donn@u.washington.edu)
Fri May 23 15:03:47 1997

Resent-From: gnats@rt-11.MIT.EDU (GNATS Management)
Resent-To: krb5-unassigned@RT-11.MIT.EDU
Resent-Reply-To: krb5-bugs@MIT.EDU, donn@u.washington.edu
Date: Fri, 23 May 1997 11:58:05 -0700
From: donn@u.washington.edu
Reply-To: donn@u.washington.edu
To: krb5-bugs@MIT.EDU


>Number:         432
>Category:       krb5-appl
>Synopsis:       login.krb5 gets tty SIGHUP, instead of shell.
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    krb5-unassigned
>State:          open
>Class:          sw-bug
>Submitter-Id:   unknown
>Arrival-Date:   Fri May 23 14:59:00 EDT 1997
>Last-Modified:
>Originator:     Donn Cave
>Organization:
	University Computing Services
	University of Washington
>Release:        1.0pl1
>Environment:
	Digital UNIX 4.0a, AIX 4.2
System: AIX melville 2 4 000010504900


>Description:
	Because login.krb5 execs the shell in a child process, the login.krb5
	parent process remains the tty session leader and receives the SIGHUP
	when the connection closes.  Normally, the shell receives this signal,
	and passes it on to its child process groups (if it's a job control
	shell that would have child processes in different process groups.)
	This is needed to terminate processes that would otherwise loop on
	a non-blocking tty read, processes that would keep lock files and other
	resources.  I believe login.krb5 is also unable to clean up the
	Kerberos credential cache anyway, because it dies on the SIGHUP.
>How-To-Repeat:
	Run the following program (e.g., ./a.out > hupout 2>&1) and then
	close the connection (I did this from an NCD X terminal builtin
	telnet, which provides a menu with that option.)  In an environment
	that uses the standard login, a.out will abort due to signal 1, and
	the shell will also be gone.  In the krb5-1.0pl1 environment,
	login.krb5 will disappear but the shell and a.out will still be
	running.
		#include <signal.h>
		#include <stdio.h>

		static void
		catch(int sig)
		{
			printf("Signal %d\n", sig);
			exit(0);
		}

		main(int argc, char **argv)
		{
			sigset_t nmask;

			signal(SIGHUP, catch);
			sigemptyset(&nmask);
			sigsuspend(&nmask);
			perror("sigsuspend");
		}
>Fix:
	The appended patch takes what I think is the most direct approach,
	catching the SIGHUP and passing it to the child process group.
	(NB., process group not process - process will work with job control
	shells like ksh & csh, because they emulate a process group kill
	by passing the HUP along to at least the foreground job.  Process
	*group* is required for non-job-control shells, e.g. miscellaneous
	menus and whatnot running as login shells.)  This may take an
	autoconfig dependency like HAVE_KILLPG (kill(-pid, SIGHUP) is
	the same effect, but maybe harder to autoconfig.)

	I tried pretty hard to transfer the tty session leadership to
	the shell process.  That would probably be the ideal thing, but
	I couldn't get it to happen, on Digital UNIX at any rate.  I think
	it's not going to be possible to make this work on all UNIXes.

	Donn Cave, University Computing Services, University of Washington
	donn@u.washington.edu
-----------------------------
*** src/appl/bsd/login.c.dist	Wed Apr  2 23:35:11 1997
--- src/appl/bsd/login.c	Fri May 23 11:13:14 1997
***************
*** 2386,2391 ****
--- 2386,2398 ----
  }
  
  #if defined(KRB4_GET_TICKETS) || defined(KRB5_GET_TICKETS)
+ 
+ static int hungup = 0;
+ static sigtype
+ sighup() {
+     hungup = 1;
+ }
+ 
  /* call already conditionalized on login_krb4_get_tickets */
  /*
   * This routine handles cleanup stuff, and the like.
***************
*** 2396,2401 ****
--- 2403,2409 ----
  dofork()
  {
      int child;
+     handler sa;
  
  #ifdef _IBMR2
      update_ref_count(1);
***************
*** 2433,2447 ****
      (void) chdir("/");	/* Let's not keep the fs busy... */
      
      /* If we're the parent, watch the child until it dies */
  #ifdef HAVE_WAITPID
!     (void)waitpid(child, 0, 0);
  #else
  #ifdef WAIT_USES_INT
!     while(wait((int *)0) != child) /*void*/ ;
  #else
!     while(wait((union wait *)0) != child) /*void*/ ;
  #endif
  #endif
      
      /* Cleanup stuff */
      /* Run destroy_tickets to destroy tickets */
--- 2441,2463 ----
      (void) chdir("/");	/* Let's not keep the fs busy... */
      
      /* If we're the parent, watch the child until it dies */
+     /* On receipt of SIGHUP, pass that along to child's process group. */
+ 
+     handler_init (sa, sighup);
+     handler_set (SIGHUP, sa);
+ 
  #ifdef HAVE_WAITPID
!     if (waitpid(child, 0, 0) < 0) {
  #else
  #ifdef WAIT_USES_INT
!     while(wait((int *)0) != child) {
  #else
!     while(wait((union wait *)0) != child) {
  #endif
  #endif
+ 	if (hungup)
+ 	    killpg(child, SIGHUP);
+     }
      
      /* Cleanup stuff */
      /* Run destroy_tickets to destroy tickets */
>Audit-Trail:
>Unformatted:

home help back first fref pref prev next nref lref last post