[net.unix-wizards] getlogin

idallen (07/10/82)

I have discovered, to my surprise, that the C Library function
getlogin() is not a secure way of determining the name of the person
running the current process.  We need such a method here at Waterloo,
so I solicit comments on the following discussion.

Getlogin() calls ttyslot() to find the login name of the running
process.  It is possible to make getlogin() return the name of any
user currently logged on, and I don't know of an easy way to fix
this flaw.

The problem lies with the use of ttyslot().

It is claimed ("man ttyname") that ttyslot() returns the index of the
control terminal for the current process.  This is not strictly true.
Ttyslot() calls ttyname() on each of the units 0, 1, and 2 (in that
order) until it finds one which is attached to a terminal.  But --
there is no guarantee that the first terminal it finds is the real
controlling terminal.  Indeed, it could be *any* terminal controlled by
any currently active user!

You can fool getlogin() into thinking you are someone else by
redirecting I/O units to or from the other user's terminal.  For nice,
friendly systems, this is not a problem.  For accounting purposes,
however, it is not acceptable, since this makes it possible to charge
resources or submit requests as if you were that other user.

To find the name of the current user, documentation tells me to use
getlogin() first, and, if that fails, use getpwent().  Since many,
many, programs do this, I am amazed to discover that getlogin() can be
fooled.

Apparently there is *no* secure way of determining the controlling
terminal of the current process (the terminal from which the current
process was submitted).  Am I right?  As it stands, getlogin() cannot
be used because it can be fooled.  I don't see any use for getlogin()
because of this flaw, and I am amazed that any program uses it.

Why does ttyslot() use such an ad-hoc method of determining the
controlling terminal?  Running down the I/O units and taking the first
one that happens to be a terminal seems wrong.

Comments on my view of this problem, and suggested fixes, are welcome.

	-IAN!    U of Waterloo    (decvax!watmath!idallen)

solomon (07/11/82)

Getlogin is very nearly worthless for all the reasons cited by watmath!idallen
as well as many others.  For example, we have a login named "who" with
no password and login "shell" /usr/ucb/w.  All you have to do is
type "login who&", and from now on, getlogin() will say you are "who".
Then, if you send mail, it will be from "who", since ucbmail, binmail,
and delivermail (all of whom may, under certain circumstances add
a From: line) all use getlogin().  Another nice feature of getlogin()
is that it is NOT modified by "su".  So you "su" on top of somebody
else and then mail from you is labelled as being from him.
Finally, you may get "You have new mail" and then have "mail" say
"No mail", since one case is using getlogin() and the other is using
getuid().

Now here is the fix:  modify the man page "GETLOGIN(3)" to remove
the ridiculous advice "The correct procedure for determining the login
name is to first call \getlogin/ and if it fails, to call \getpwuid/."
and perhaps replace it with a waring how getlogin() is of very limited
usefulness.  Then go through all software that was written following
this advice and change it.  The change is usually quite simple, since
you will find something like
	if ((foo=getlogin())==0)  ...
(or ==NULL), and you can just remove the line.

end of flame
Marvin Solomon
(solomon@uwisc, ...!harpo!uwvax!solomon)

mash (07/17/82)

1) The login name is not, and never has been, a first-class entity in
any UNIX that I know of, except the PWB/UNIX 1.x systems, circa 1975-1978.
(Their ublocks had an extra 32 bytes that carried around login name,
tty letter, and login directory. The login name was mainly added to
survive having >256 people on the complex of machines at Piscataway,
while not duplicating uids across machines for reasons of inter-machine
backup, protection, and balancing.)

2) The extra information above disappeared into the environment variables
added in V7 (i.e., $HOME on most systems, $LOGNAME on later derived
versions of V7).  Even with all software geared to use $LOGNAME when
approrpiate, one can still fool it by modifying LOGNAME, although
at least the ridiculous conttoritons of getlogin() are unnecessary.

3) The only way to really be safe with LOGNAME is to make it part of the
per-process information (as in older PWB/UNIXs) and people generally
seemed unwilling to do that, especially when 16-bit uids became available.
At point in the discussions leading to the design of the current
environment variable approach, we'd actually worked out an elaborate
scheme involving (name, value, attribute) tuples, where the attribute
information included local/global, and read-only bits.  The implications
of all this (extra systems calls, bizarre interactions with shell, etc)
scared us off, especially since the only item we really thought
needed read-only was LOGNAME.

4) Thus, don't expect this situation to improve -- at least UNIX III
and later use LOGNAME somewhat more consistently.

-mashey

dan@BBN-UNIX@sri-unix (08/21/82)

From: Dan Franklin <dan at BBN-UNIX>
Date: 10 Aug 1982  9:41:02 EDT (Tuesday)
Oops. My apologies to anyone who was misled by my previous
message about calling ttyname on /dev/tty; I had forgotten
that we replaced our /dev/tty driver several years ago
with something completely different inside, for which the trick I
described does work--but it won't work on an ordinary V7 /dev/tty
driver.

greep@Rand-Unix@sri-unix (08/31/82)

Date: Wednesday, 25 Aug 1982 16:50-PDT
However, this only works if there is a tty open, or if there is some
way of finding out the "controlling tty", and even then doesn't work if
the process was started up by some background program (eg "at").

richl@daemon.UUCP (Rick Lindsley) (01/17/85)

Has anyone addressed the problems that getlogin() has when used from a
pty that is NOT a login pty? Script is one prime example, but a growing
number of other utilities also use ptys.

One possibility is to have getlogin trace parent process ids to arrive
at the TRUE login of a person; but this has the unpleasant side effect
of forcing any program which uses getlogin() to be setgid sys (we've
altered the perms on kmem for security reasons).

Another possibility is to have getlogin do a getuid() and getpwnam() if
it can't get a name out of utmp.

A third possibility is to ask the calling program to perform the above
check. (Getlogin will return a null string).

Before we go and choose an option, I would be interested in determining
if others have chosen a solution to this problem. I will be happy to
summarize to the net if necessary.

Rick Lindsley
Small Systems Support
Tektronix
...!{ihnp4,allegra,decvax}!tektronix!daemon!richl

richl@daemon.UUCP (Rick Lindsley) (02/05/85)

A while back I posted a question on what to do with getlogin(3) (it
fails miserably on pty's that are not login ptys's). I promised a
summary of responses; here they are. Only 3 responses though.. I'd say
that in the interests of speed and compatibility that we will likely
just fix the individual programs to check the result from getlogin(),
and change getlogin only as indicated in the first letter (we have
people who have to have the original functionality (buggy though it may
be) and so can't really change the behavior of getlogin() too much).

--------

    From: dce@hammer (David Elliott)
    To: daemon!richl
    Date: Thu, 17 Jan 85 00:52:36 PST
    Organization: Tektronix, Wilsonville OR


    We went ahead and changed getlogin() to return NULL when it would have
    returned a "". This made su and a couple of other programs work
    correctly for us.

			    David


--------

    From: tektronix!decvax!mcnc!jte
    To: decvax!tektronix!daemon!richl
    Date: Thu, 17 Jan 85 12:58:45 est
    Original-From: James Ellis <jte@mcnc>
    Organization: Microelectronics Center of NC, RTP, NC

    Some sites have modified the kernel to track the user's name and
    make it available via a system call. If you don't have such mods
    or are not inclined to put them in, I recommend the following:

	    If getpwnam(getlogin()) returns the same uid as getuid(),
	    then use getlogin().
	    Otherwise, use the name returned by getpwuid(getuid()).

    Getlogin is not to be trusted as an identifier of "who" one "is".  A
    user should be considered to be who his permissions are - and that is
    defined by the uid. The only complication is that it is reasonable for
    a uid to be shared by several different logins, wherefore the first
    case above.

    Note that this is my opinion which is not, unfortunately, shared by
    all.  Berkeley in particular prefers to consider that you "are" your
    uid in some cases (permission checks) and you "are" who you logged in
    as in other cases (e.g. mail).  This allows folks to login as foo, su
    to root, and send mail that looks like it came from foo.  This is fine
    and well, but I also think it is wrong.  Since I don't have time to
    re-write mail myself, I don't complain too loudly.  I'd be interested
    to know what you finally decide to use.

				    Jim Ellis


-------

    From: j@utah-cs.arpa
    To: richl@tektronix.csnet
    Date: Fri, 18 Jan 85 12:33:11 MST

    Over all those alternatives I would choose:
    Find it via getpwuid.  Only if that fails (hardly ever), then look at
    utmp. Looking at utmp first gives the wrong result for programs which
    have been left in the background and then someone else logs in. I believe
    dmr recommended this himself a long time ago.

    I haven't done it here yet tho, as a problem may be that this might change
    getlogin's behavior in some instances such as mail's determining the
    sender, when the sender may be su'ed or something of that sort, or a
    privileged sender, etc.  Haven't really examined these potential problems
    though.

    {ihnp4,decvax}!utah-cs!lepreau