[net.bugs.4bsd] bug in signals and setuid in 4.2 bsd.

fred@nmtvax.UUCP (04/28/84)

Index: /sys/sys/kern_prot.c 4.2 BSD Vax

Description:

There is a bug with signals and setuid in 4.2 bsd. This may have
existed in 4.1, but I am not sure since I don't have source readily
available at the moment. What happens is when a process first starts
up p_uid in the process structure is set to the effective uid. Then
if you do a setuid (or setreuid), it sets p_uid to your REAL user id.
If you them send a signal to yourself, the system searches for a process
with the same pid as yours and has a uid equal to your effective uid
to send the signal requested and fails.

Repeat-By:

  The following is a small program to show the bug run it on your machine.
It should be run by a normal(non-root) user and made set uid to someone else.
The second kill will fail with no such process.

#include <signal.h>
#include <stdio.h>
#include <errno.h>
int tsig();
main()
{
	int euid,uid;
	signal(SIGALRM,tsig);
	printf("uid: %d,euid: %d\n",getuid(),geteuid());
	if(kill(0,SIGALRM) < 0)
		perror("kill");
	setreuid(-1,-1);
	printf("uid: %d,euid: %d\n",getuid(),geteuid());
	if(kill(0,SIGALRM) < 0)
		perror("kill");
}

tsig()
{
  printf("Caught signal.\n");
}

Fix:

The fix is very simple. Just change setreuid() in /sys/sys/kern_prot.c
so that the p_uid is set to the effective uid rather than the real
user id. The following is a diff of what needs to be corrected.

*** kern_prot.c	Fri Apr 27 19:34:16 1984
--- kern_prot.old Tue Feb 28 11:53:20 1984
***************
*** 133,139
  		qstart(getquota(ruid, 0, 0));
  	}
  #endif
! 	u.u_procp->p_uid = euid;
  	u.u_ruid = ruid;
  	u.u_uid = euid;
  }

--- 133,139 -----
  		qstart(getquota(ruid, 0, 0));
  	}
  #endif
! 	u.u_procp->p_uid = ruid;
  	u.u_ruid = ruid;
  	u.u_uid = euid;
  }
-- 

            Fred Romelfanger
            Computer Science Department
            New Mexico Tech

            ..!ucbvax!unmvax!nmtvax!fred    (uucp)
            ..!cmcl2!lanl-a!nmtvax!fred     (uucp)
            fred.nmt@rand-relay             (arpa)
            fred@nmt                        (CSnet)

matt@oddjob.UChicago.UUCP (Matt Crawford) (04/30/84)

While checking out nmtvax!fred's report, I found another...

Subject: sending SIGCONT to child proc not *always* allowed
Index:	sys/kern_sig.c 4.2BSD

Description:
	The manual for kill(2) states "...the signal SIGCONT ...
     may always	be sent	to any child or	grandchild of the current
     process.", but this is true only if the signal is sent to the
     entire process group via kill(0, SIGCONT) or killpg().

Repeat-By:
	Compile the following programs and make "child" suid to some
	other user than "parent", then run parent (not as root).
------------parent.c-----------
#include <stdio.h>
#include <errno.h>
#include <signal.h>

main()
{
	int	pid;
	
	if ( pid = fork() ) {
		sleep(5);
		if ( kill(pid, SIGCONT) )
		        perror("CONT");
		fprintf(stderr, "Parent exitting.\n");
	}
	else {
		execv("child", 0);
		fprintf(stderr, "Can't exec.\n");
	}
}
------------child.c-----------
#include <stdio.h>
#include <signal.h>

main()
{
	fprintf(stderr, "Child started.\n");
	kill(getpid(), SIGSTOP);
	fprintf(stderr, "Continued OK.\n");
}
-------------------------------

Fix:
	This could be intended to allow suid processes to protect
	themselves from SIGCONT by using setpgrp(0, getpid()), but I
	don't see why...
	If this is not a "feature" then the following >>untested<<
	change to kill1() in sys/kern_sig.c should fix it.  Change:
----------------------
	if (who > 0 && !ispgrp) {
		p = pfind(who);
		if (p == 0)
			return (ESRCH);
*		if (u.u_uid && u.u_uid != p->p_uid)
			return (EPERM);
----------------------
	to:
**********************
	if (who > 0 && !ispgrp) {
		p = pfind(who);
		if (p == 0)
			return (ESRCH);
*		if (u.u_uid && u.u_uid != p->p_uid &&
*		    (signo != SIGCONT || !inferior(p)))
			return (EPERM);
*********************************