Subject: Re: Emulating Ultrix setuid semantics
To: Chris G. Demetriou <cgd@alpha.bostic.com>
From: Theo de Raadt <deraadt@fsa.ca>
List: port-pmax
Date: 08/19/1994 22:14:53
> and it should do so _only_ in the ultrix compat code.  (not only

Unfortunately not true. At the very least, SunOS compatibility needs
it fixed as well.

I suspect other compatibility packages will want it too.  Didn't 0.9
have setreuid?  If it did, the emulation in kern_prot.c isn't
sufficient.  (BSDI binaries?)  The code doesn't seem `correct' for the
COMPAT_43 case either.

Actually, I should have run into this problem in SunOS emulation
before.  But.. SunOS binaries don't do setreuid(N, M) as often as
Ultrix does... SunOS doesn't have saved-ids either. I don't think 100%
emulation is neccessary or doable; just a bit more than is currently
in kern_prot.c. The, er, setreuid(N, N) case in particular.

> (not only
> that, if it does it there, it can do the emulation more cleanly,
> and without hacking up any common code.) 

I think all the setreuid/setregid emulations are going to be
identical.  I'm afraid someone might write a setreuid() implimentation
that hacks the euid directly, and create a case of uid != 0, euid == 0.

>	(1) prohibit a person writing compat code from implementing
>		them fully (though i'd argue against that), or

Exactly what I'm afraid of.  I'd rather put a canonical "safe" version
in kern_prot.c. We're going to be leaving the COMPAT_43 code intact
for a time, no?

Here's an attempt at setreuid that I've been playing at..


#if defined(COMPAT_43) || defined(COMPAT_SUNOS) || defined(COMPAT_ULTRIX)
struct setreuid_args {
	int	ruid;
	int	euid;
};
/* ARGSUSED */
osetreuid(p, uap, retval)
	register struct proc *p;
	struct setreuid_args *uap;
	int *retval;
{
	register struct pcred *pc = p->p_cred;
	struct seteuid_args seuidargs;
	struct setuid_args suidargs;

	/*
	 * There are four cases, and we attempt to emulate them in
	 * the following fashion:
	 * -1, -1: return 0. This is correct emulation.
	 * -1,  N: call seteuid(N). This is correct emulation.
	 *  N, -1: if we called setuid(N), our euid would be changed
	 *         to N as well. the theory is that we don't want to
	 * 	   revoke root access yet, so we call seteuid(N)
	 * 	   instead. This is incorrect emulation, but often
	 *	   suffices enough for binary compatibility.
	 *  N,  M: call setuid(N), and assume M==N. This is close to
	 *	   correct emulation.
	 */
	if (uap->ruid == (uid_t)-1) {
		if (uap->euid == (uid_t)-1)
			return (0);			/* -1, -1 */
		seuidargs.euid = uap->ruid;		/* -1,  N */
		return (seteuid(p, &seuidargs, retval));
	}
	if (uap->euid == (uid_t)-1) {
		if (uap->ruid != pc->p_ruid &&		/* N, -1 */
		    uap->ruid != pc->p_svuid)
			return (EPERM);
		seuidargs.euid = uap->ruid;
		return (seteuid(p, &seuidargs, retval));
	}
	suidargs.uid = uap->ruid;			/* N, M */
	return (setuid(p, &suidargs, retval));
}

------------------------------------------------------------------------------