[comp.unix.internals] SunOS and shared libraries, security aspects

guy@auspex.auspex.com (Guy Harris) (09/01/90)

>It seems, however, that Sun has again hacked together and included in
>their OS something without taking security aspects into account.  The
>uid != euid check is not enough in current Unix systems; some programs
>which are setuid set their euid to the uid to which they're suid under

Err, well, if it's set-uid, when it starts up its euid already *is* the
uid to which it's suid.  I don't think that's what you meant to say....

>and so the linker happily links in the user's own library

The libraries are mapped in before "main()" is called, so whether
LD_LIBRARY_PATH is ignored depends only on the settings of the effective
and real UID and GID at the time the program is initially started up,
and is independent of what the program does after that.  (Yes, I tried
it.)

>I tried to think of a few quick ways to exploit it and came up with only
>/bin/login -p sync (preserver environment), if sync is an account with no
>password and daemon id as a common convention is.

OK, so the problem is something quite different from what you were
saying it is.  The problem isn't that "/bin/login "set(s) (its) euid to
the uid to which it's suid", it's that, when invoked with the "-p" flag
it passes LD_LIBRARY_PATH (and possibly other dangerous environment
variables) through when it runs the login shell of an account with no
password and that has a reasonably-privileged user ID.  The fix ain't to
change "ld.so"s rules for when it should honor LD_LIBRARY_PATH, it's to
change "login" to be more selective about which environment variables
it'll pass through.

jkp@cs.HUT.FI (Jyrki Kuoppala) (09/02/90)

In article <3991@auspex.auspex.com>, guy@auspex (Guy Harris) writes:
>>It seems, however, that Sun has again hacked together and included in
>>their OS something without taking security aspects into account.  The
>>uid != euid check is not enough in current Unix systems; some programs
>>which are setuid set their euid to the uid to which they're suid under
>
>Err, well, if it's set-uid, when it starts up its euid already *is* the
>uid to which it's suid.  I don't think that's what you meant to say....

Oops, sorry, of course I meant to say that some programs set their
real uid to the euid (the uid to which they're suid under), and this
is the problem.

>>and so the linker happily links in the user's own library
>
>The libraries are mapped in before "main()" is called, so whether
>LD_LIBRARY_PATH is ignored depends only on the settings of the effective
>and real UID and GID at the time the program is initially started up,
>and is independent of what the program does after that.  (Yes, I tried
>it.)

Yes, but some programs start _other_ programs after setting uid to
euid; in this case, euid == uid and when this other program (like when
/bin/login starts the login shell /bin/sync) is loaded the
LD_LIBRARY_PATH is used.  This is the problem, sorry if I was a bit
unclear.

>>I tried to think of a few quick ways to exploit it and came up with only
>>/bin/login -p sync (preserver environment), if sync is an account with no
>>password and daemon id as a common convention is.
>
>OK, so the problem is something quite different from what you were
>saying it is.  The problem isn't that "/bin/login "set(s) (its) euid to
>the uid to which it's suid", it's that, when invoked with the "-p" flag
>it passes LD_LIBRARY_PATH (and possibly other dangerous environment
>variables) through when it runs the login shell of an account with no
>password and that has a reasonably-privileged user ID.  The fix ain't to
>change "ld.so"s rules for when it should honor LD_LIBRARY_PATH, it's to
>change "login" to be more selective about which environment variables
>it'll pass through.

Yes; and change all those dozen's of other programs (like the ones
which come from the Usenet) to also unsetenv LD_LIBRARY_PATH when they
do setuid(geteuid()).  No, I don't really know if this is common but
it's hard to know exactly what programs do this (especially for SunOS
binary users) and so the problem is hard to solve this way.

And I haven't figured out a good way to change LD_LIBRARY_PATH
semantics.  Perhaps a kludge to the setuid() function which unsetenv's
LD_LIBRARY_PATH when euid != uid and setuid() is used to set it to the
real uid ?  This would solve the problem in the general case, you
would just have to recompile (umm, with shared libraries you wouldn't
have to recompile, something useful about them too ;-)

However, the security aspects of LD_LIBRARY_PATH should be documented
(well, I don't know if they are, I haven't read all the SunOS
manuals).

So, anyone see somthing wrong with the solution: We write a kludged
setuid() to unsetenv LD_LIBRARY_PATH and distribute it with
instructions of how to install it in your machine's shared library.
We also find out which staticcally-linked programs have the problem
and instruct users to recompile them and distribute fixed binaries for
binary-only users.

//Jyrki

[ I vote for creation of a newsgroup called comp.unix.wizards ]

guy@auspex.auspex.com (Guy Harris) (09/03/90)

>Rather, it's to make login non-setuid in the first place. The only time
>login should run as root is from a controlled daemon, such as telnetd or
>getty.

I've no problem with that; others used to doing "login" from their
sessions might, but, well, you know what happens if you can't take a
joke....

However, "login" ain't the only program that will pass environment
variables through when it runs some program under another user ID (real
*and* effective *and* saved set-user), so making "login" non-set-UID
doesn't completely close the hole....

gt0178a@prism.gatech.EDU (BURNS,JIM) (09/03/90)

in article <4006@auspex.auspex.com>, guy@auspex.auspex.com (Guy Harris) says:

>>Rather, it's to make login non-setuid in the first place. The only time
>>login should run as root is from a controlled daemon, such as telnetd or
>>getty.

> I've no problem with that

Excuse me, but I don't understand how login (su, rsh, rlogin) would be
able to change your uid without using setuid(3) which is documented as
needing superuser status:

NAME
     setuid, seteuid, setruid, setgid, setegid, setrgid - set
     user and group ID
[...]
     These calls are only permitted to the super-user or if the
     argument is the real or effective ID.
-- 
BURNS,JIM
Georgia Institute of Technology, Box 30178, Atlanta Georgia, 30332
uucp:	  ...!{decvax,hplabs,ncar,purdue,rutgers}!gatech!prism!gt0178a
Internet: gt0178a@prism.gatech.edu

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (09/03/90)

In article <13283@hydra.gatech.EDU> gt0178a@prism.gatech.EDU (BURNS,JIM) writes:
> in article <4006@auspex.auspex.com>, guy@auspex.auspex.com (Guy Harris) says:
> >>Rather, it's to make login non-setuid in the first place. The only time
> >>login should run as root is from a controlled daemon, such as telnetd or
> >>getty.
> > I've no problem with that
> Excuse me, but I don't understand how login (su, rsh, rlogin) would be
> able to change your uid without using setuid(3) which is documented as
> needing superuser status:

It can't. All it can do without privileges is log you in as yourself
again, and not even that on some systems. As I said in the triple-quoted
comments, login should only run as root if it's run from a controlled
(root) daemon: this is necessary for security.

---Dan

guy@auspex.auspex.com (Guy Harris) (09/04/90)

>> Excuse me, but I don't understand how login (su, rsh, rlogin) would be
>> able to change your uid without using setuid(3) which is documented as
>> needing superuser status:
>
>It can't. All it can do without privileges is log you in as yourself
>again, and not even that on some systems. As I said in the triple-quoted
>comments, login should only run as root if it's run from a controlled
>(root) daemon: this is necessary for security.

Yup, and as for the other commands:

	1) "rsh" and "rlogin" use super-user privileges to get "privileged"
	   ports, not to change your uid; "rshd" and "rlogind", the
	   daemons, are the ones that change the UID, and they're not
	   set-UID;

	2) "su", however, *does* need to be setuid in order to be
	   useful, but also passes environment variables through....

djw@bbn.com (David Waitzman) (09/04/90)

guy@auspex.auspex.com (Guy Harris) writes:

}	1) "rsh" and "rlogin" use super-user privileges to get "privileged"
}	   ports, not to change your uid; "rshd" and "rlogind"...

I occasionally write and distribute programs that need to use raw IP
sockets, roughly the same thing, protection-wise, as a program using
"privileged" ports.  I dislike needing to run the programs set-uid root
(or by root) just to do this one special privileged thing.

How do you all feel about the practice of using a special group that
allows one to access privileged ports or raw IP sockets?  The programs
can then just be run setgid to that group.  The kernel socket opening
code would then allow the opening of privileged or raw sockets to
either user==root or groups includes "priv_socket_group".

Would vendors support this?  We don't have access to Sun kernel source
code anymore here.

I understand that some people may have objections to the piece-meal
addition of finer-granularity access rights to Unix.

thanks,
-david

gt0178a@prism.gatech.EDU (BURNS,JIM) (09/05/90)

in article <59263@bbn.BBN.COM>, djw@bbn.com (David Waitzman) says:
> I understand that some people may have objections to the piece-meal
> addition of finer-granularity access rights to Unix.

SysV (or alt least HP-UX 7.0) already supports this with CHMOD, MLOCK,
RTPRIO, etc., so those that use this scheme and also support sockets could
add this easily.
-- 
BURNS,JIM
Georgia Institute of Technology, Box 30178, Atlanta Georgia, 30332
uucp:	  ...!{decvax,hplabs,ncar,purdue,rutgers}!gatech!prism!gt0178a
Internet: gt0178a@prism.gatech.edu

jik@athena.mit.edu (Jonathan I. Kamens) (09/05/90)

In article <13340@hydra.gatech.EDU>, gt0178a@prism.gatech.EDU (BURNS,JIM) writes:
|> Thanx, but I'm still a little confused - somehow, when you login to the
|> remote host, it has to set your uid. If rshd and rlogind don't use
|> setuid(3) (can't, if they aren't set-uid) to set your privileges, what
|> does?

  They don't have to be setuid to use setuid(3).  They have to run as root.

  If they are run by inetd, for example, they can be run as root even if they
are not setuid root, since inetd runs as root.

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8495			      Home: 617-782-0710

gt0178a@prism.gatech.EDU (BURNS,JIM) (09/05/90)

in article <1990Sep4.235353.18586@athena.mit.edu>, jik@athena.mit.edu (Jonathan I. Kamens) says:
>   They don't have to be setuid to use setuid(3).  They have to run as root.
> 
>   If they are run by inetd, for example, they can be run as root even if they
> are not setuid root, since inetd runs as root.

Thanx.
-- 
BURNS,JIM
Georgia Institute of Technology, Box 30178, Atlanta Georgia, 30332
uucp:	  ...!{decvax,hplabs,ncar,purdue,rutgers}!gatech!prism!gt0178a
Internet: gt0178a@prism.gatech.edu

gla@nixpbe.UUCP (R. Glaschick) (09/05/90)

In <59263@bbn.BBN.COM> djw@bbn.com (David Waitzman) writes:

>I occasionally write and distribute programs that need to use raw IP
>sockets, roughly the same thing, protection-wise, as a program using
>"privileged" ports.  I dislike needing to run the programs set-uid root
>(or by root) just to do this one special privileged thing.

I fully agree. See e.g. mail, that does not have super user rights.

>How do you all feel about the practice of using a special group that
>allows one to access privileged ports or raw IP sockets?  The programs
>can then just be run setgid to that group.  The kernel socket opening
>code would then allow the opening of privileged or raw sockets to
>either user==root or groups includes "priv_socket_group".

This is the right way to do it.
But beware of the subtelties of the setuid() function. It works
different under root and a normal user-id.

? Does someone have a precise description how to use setuid() ?

>Would vendors support this?  We don't have access to Sun kernel source
>code anymore here.

Vendor support does not depend wheather you have the source code.
If you stick to e.g. X/OPEN, you will have vendor support.
The X/OPEN XPG 3 specification is the same as in System V.3, but not
complete and partially misleading.

>I understand that some people may have objections to the piece-meal
>addition of finer-granularity access rights to Unix.

I think that this is correct usage of the existing security features,
and nothing else.
All those people using root whenever they need privileges without
considering 'least privileges' are misusing the system.
--
Rainer Glaschick, NIXDORF Computer AG, Paderborn, W-Germany
EMail: glaschick.pad@nixdorf.com (US) or  glaschick.pad@nixdorf.de (EU)
Tel. +49 5251 14 6150 (office) +49 5254 6238  (home) Fax: +49 5251 14 6569

gt0178a@prism.gatech.EDU (BURNS,JIM) (09/11/90)

in article <1990Sep2.093254.11284@santra.uucp>, jkp@cs.HUT.FI (Jyrki Kuoppala) says:
> So, anyone see somthing wrong with the solution: We write a kludged
> setuid() to unsetenv LD_LIBRARY_PATH and distribute it with
> instructions of how to install it in your machine's shared library.
> We also find out which staticcally-linked programs have the problem
> and instruct users to recompile them and distribute fixed binaries for
> binary-only users.

If I've followed this correctly, the problem is not that 'login' sets uid=
euid, since 'login' is set-uid to root (which has been complained about
previously in this thread) and login certainly isn't setting uid=root.
It's setting uid= to the uid field of the record in /etc/passwd that you
supplied the correct account & password for. Of course, 'login -p' could
still pass a Trojan Horse library in to a relatively more privileged
account that you DO know the password for (as opposed to your suggestion
that the privileged account has no password - a definite no-no in the
first place.) (I would imagine that 'su' w/o the '-' would work the same
way.) Presumably then 'login' has to set uid=setuid(found uid),
and THEN set euid=seteuid(uid). The reverse order wouldn't work, since if
euid was no longer root, then setuid() would fail. Your suggestion then
that, since euid=root!=uid before the setuid(), setuid() should unset
LD_LIBRARY_PATH would appear to work. However, getting all those users to
recompile or download their binaries is a bit of a problem. How many
machines at your site don't even run the latest version of the OS, let
alone have the latest patches? And who's going to determine which of those
"statically-linked programs" needs to be re-compiled? The vendor? In what
year?
-- 
BURNS,JIM
Georgia Institute of Technology, Box 30178, Atlanta Georgia, 30332
uucp:	  ...!{decvax,hplabs,ncar,purdue,rutgers}!gatech!prism!gt0178a
Internet: gt0178a@prism.gatech.edu

lyndon@cs.athabascau.ca (Lyndon Nerenberg) (09/12/90)

jkp@cs.HUT.FI (Jyrki Kuoppala) writes:

>So, anyone see somthing wrong with the solution: We write a kludged
>setuid() to unsetenv LD_LIBRARY_PATH and distribute it with
>instructions of how to install it in your machine's shared library.
>We also find out which staticcally-linked programs have the problem
>and instruct users to recompile them and distribute fixed binaries for
>binary-only users.

Yes. A kludge is a kludge, and should be avoided at all costs. What we
really need is an option to ldconfig that allows the system administrator
to specify path components in LD_LIBRARY_PATH that will be honoured by
setuid programs. Or perhaps the option should just hardwire a value
for LD_LIBRARY_PATH that's used by setuid programs. The latter isn't quite
as nice a solution, though (IMHO).

Something like:

  ldconfig [ directory ... ] [ [ -T directory ] ... ]

where the -T flag is used to specify directories that are "trusted" in the
sense that they would be searched by ld when running a setuid program.

-- 
    Lyndon Nerenberg  VE6BBM / Computing Services / Athabasca University
        {alberta,cbmvax,mips}!atha!lyndon || lyndon@cs.athabascau.ca

      The only thing open about OSF is their mouth.  --Chuck Musciano

guy@auspex.auspex.com (Guy Harris) (09/13/90)

>Yes. A kludge is a kludge, and should be avoided at all costs. What we
>really need is an option to ldconfig that allows the system administrator
>to specify path components in LD_LIBRARY_PATH that will be honoured by
>setuid programs.

Yes, some mechanism such as that - which might *also* want to, e.g.,
allow the system administrator to specify path components in boring old
PATH that set-UID programs can trust - might be nice.

It would, however, *NOT* affect the problem Jyrki is discussing!  The
problem there isn't with setUID programs, it's with programs that

	1) let you run e.g. some user's login shell, under their UID,
	   even if that user's account has no password (in which case
	   it won't ask you for a password),

and

	2) pass LD_LIBRARY_PATH (or any of various other environment
	   variables, although the others can generally be reamed out
	   of the environment very early on in "main()", while
	   LD_LIBRARY_PATH is used before "main()" is even called)
	   through unmolested when running said login shells.