[comp.sys.att] Uniquely identifying a user: is it possible?

thad@cup.portal.com (Thad P Floryan) (01/09/90)

Recently there have been 3 different programs posted which, among other things,
report the last-login date-and-time of a user on a SystemV system:

Greg Woods' lastlog (in alt.sources),
Lenny Tropiano's logininit 2.1 (in comp.sys.att and unix-pc.sources), and
my lastlogin 1.0 (in comp.sys.att and unix-pc.sources).

Each program uses different strategies, and each has a related problem!

If it could be GUARANTEED that any of the programs above would ONLY be
invoked from one's .profile, then they DO work.  But ...

As one is on-line for awhile, circumstances change, and conditions may cause
the programs to fail.  I'm not talking about just the situation where one user
may have multiple simultaneous sessions whose "last logins" affect the results.
No.  Something much more fundamental:

	Failure to UNIQUELY identify the user.

Greg's and Lenny's programs, when this happens, essentially report the failure
with a message similar to "cannot locate utmp entry for tty"; my program fails
by reporting the information for root and alters the non-root user's file with
root's last login date&time.

(Well, whaddya expect for a free program and a 1 pico Second warranty?  :-)

So, what is the source of these failures?  After some experimentation:

1)	using getuid(), or
2)	assuming the user HAS a controlling terminal.
	^^^^^^^^
(There's THAT word again; Benny Hill's probably laughing his head off! :-)

So, how are these failures invoked?

1)	"su", or
2)	redirecting stdin, stdout AND stderr (by whatever means).

I put together a "test" program that exercises the causes and displays the
symptoms of the failures.  The program is ugly as sin as it was pieced together
as I was flipping through all the SysV docs.  If anyone's interested I could
email a copy; the source is only 3K and is uncommented though its output is
nicely formatted.

From my experiments and reading the docs, it appears that the ONLY parameter
one can get with 100% certainty in these regards is the login name of the user.

It does NOT "appear" possible to always identify the specific "terminal" of the
user so as to match the entry(ies) in the /etc/utmp file ... unless, perhaps,
one uses nlist() on /unix and starts peeking through system memory, which is
beyond the capabilities of any program(s) that may be developed by the "normal"
user.

It also appears the only things that "could" be matched in the /etc/utmp file
(using "normally" available information) are the user's login name and the
controlling terminal; all this using the getutent(), getutid() or getutline().

The writeup of getlogin(3C) claims the correct way to determine the login name
is to use cuserid(3S); or getlogin(3C) (if getlogin() fails then getpwuid(),
and if getpwuid() fails (re: getuid()), then one is SOL.)

If one is su'd to root or another user, getuid() does NOT return your UID,
but, instead, that of the user to which you've su'd.

If stdin, stdout and stderr are ALL redirected (e.g." < infil > outfil 2>&1 " )
then ttyslot(3C) and ttyname(3C) both fail if none of the file descriptors
reference a terminal (such as is the case during "total" redirection).

I've also discovered that a portion of the docs for ttyslot(3C) is WRONG
regarding returned value, because a returned value of 0 is a valid "index"
into /etc/utmp.  The value that it DOES return on failure, as shown by
running my test program under one of the failure-mode conditions, is -1.

And before you say "Aw, just some more piddling UNIXPC-specific problems",
let it be known I've tested the program on several implementations of SysV
on AT&T machines and two different implementations of HP-UX; same problem
on all systems to which I have access.  A friend is checking this out for
me on an Amdahl UTS machine as I'm writing this, but I believe she'll find
the same problem (she's also checking out the HDB Dialers & Devices stuff :-)

A simple demonstration of the problem using who(1):

	$ who am i
	thad       p0           Jan  7 16:21
	$ ls -l fish
	-rw-r--r--  1 thad    users         0 Jan  7 18:34 fish
	$ cat fish
	$ who am i < fish > crap 2>&1
	$ cat crap
  ===>	process not attached to terminal
	Usage:	who [-rbtpludAasT] [am i] [utmp_like_file]
	... help output deleted ...

Aha!  Just received a call back from my friend at Amdahl (she works on the
UTS kernel software (SysV-derived)) and:

1)	$ who am i < fish > crap 2>&1
	$ cat crap
  ===>	You must have a terminal attached.

2)	The Dialers and Devices require the same "\M", "\m" and ",M" as do
	the systems we've recently been discussing in another message thread
	and for the same reasons.  Sheesh, a wide-spread problem & "solution".

Pardon the use of "cat crap" in the above example, but my friend has two cats
and she thought it was hilarious!  Then she zingered me with: Amdahl is now
producing their UNIX 7300 systems, running the UTS kernel.  Now I wonder where
they got THAT number ^^^^ ?!?!  :-)

In any event, what this all boils down to is the "simple" question:

  ===>	Is there a way to 100% positively, absolutely, unequivocally,   <===
  ===>	unerringly identify the current login user's /etc/utmp session  <===
  ===>	record under ALL conditions of su'iness and output redirection  <===
  ===>	and multiple simultaneous logins?                               <===

That identification of the login user's record:

1) MUST succeed even if there are multiple same-user sessions online (meaning
   logging in twice from two different terminals),
2) MUST succeed even if the user is su'd, and
3) MUST succeed even if all standard streams are re-directed.

Sounds simple, no?  I've gone through every page of all xxx(2*) and xxx(3*)
man pages looking for an answer and nothing is apparent.

If you have an answer to that "simple" question, that runs non-privileged
(heck, even privileged (at this stage of the game)), I'm sure that enquiring
minds would like to know.

Thad Floryan [ thad@cup.portal.com (OR) ..!sun!portal!cup.portal.com!thad ]

jay@banzai.PCC.COM (Jay Schuster) (01/10/90)

thad@cup.portal.com (Thad P Floryan) writes:
>In any event, what this all boils down to is the "simple" question:
>  ===>	Is there a way to 100% positively, absolutely, unequivocally,   <===
>  ===>	unerringly identify the current login user's /etc/utmp session  <===
>  ===>	record under ALL conditions of su'iness and output redirection  <===
>  ===>	and multiple simultaneous logins?                               <===

Without reading /dev/kmem, I believe not.

If there was some way of getting your tty group ID, you could search
through utmp and find its pid there.  But I don't think there's any
wany of finding your tty group id without searching through /dev/kmem.

End even then, if the process had detached itself (called setpgrp()),
it has no controlling terminal, and you wouldn't be able to find its
tty slot because the kernel will tell you that it has none.

>Sounds simple, no?

It doesn't sound simple, because UNIX doesn't really care what tty
you are logged on to, and loses that information rather easily.  It
cares about the uid you are, but allows you to change it (su).
-- 
Jay Schuster <jay@pcc.COM>	uunet!uvm-gen!banzai!jay, attmail!banzai!jay
The People's Computer Company	`Revolutionary Programming'

comeau@utoday.UUCP (Greg Comeau) (01/11/90)

In article <25730@cup.portal.com> thad@cup.portal.com (Thad P Floryan) writes:
>  ===>	Is there a way to 100% positively, absolutely, unequivocally,   <===
>  ===>	unerringly identify the current login user's /etc/utmp session  <===
>  ===>	record under ALL conditions of su'iness and output redirection  <===
>  ===>	and multiple simultaneous logins?                               <===

It appears you wanted to do more than what is stores in u/w/tmp anyway,
so you nest bet may be to forget that file and create whatever you need
tocreate via /etc/profile (which will only therefore catch interactive
logins and not stuff like uucp which can be handled in another way)
as well as front-ending or re-writing su for the audits you need.
-- 
Greg, Comeau Computing, 91-34 120th Street, Richmond Hill, NY, 11418
Producers of CC C++, SysAdm columnist for UNIX Today!, Microsoft Systems Journal
(C programming), + others. Also, BIX c.language & c.plus.plus conf. moderator.
Here:attmail!csanta!greg / BIX:comeau / CIS:72331, 3421 / voice:718-849-2355

woods@eci386.uucp (Greg A. Woods) (01/12/90)

In article <25730@cup.portal.com> thad@cup.portal.com (Thad P Floryan) writes:
> Recently there have been 3 different programs posted which, among other things,
> report the last-login date-and-time of a user on a SystemV system:
> 
> Each program uses different strategies, and each has a related problem!

My version (lastlog) is complete and sufficient for the purposes it was
designed for, and it is reasonably difficult to subvert its intentions
in a well managed system.

1. lastlog should be placed in /etc/profile, as I suggested in the
readme.  In this environment, it functions correctly and is always
called (except for uucp logins, though this can be remedied by writing
a shell around uucico).  The normal case of a user executing lastlog
after his session has started is harmless, since the effect is nil.

2. lastlog cannot be fooled into reporting on a different utmp line,
since to fool ttyslot(3c), one would have to have read permission on
another tty with a current utmp entry.  I make the assumption that
other ttys should never be readable by the average user.  Further
checking could be done to assert the current uid matched the one in
the utmp entry.

3. If someone fools lastlog by using a different uid, then either your
system has a bad security hole, or that user knows another's password.
In any case there is no loss of information; current information for
the subverted user's id is recorded.  It would be nice if su knew
about lastlog too.

4. lastlog information cannot be deleted by the average user.  This is
the most important point for me.  The SysVr3.2 login keeps similar data
in the ctime of the inode for the .lastlogin file in each user's home
directory.  Although you cannot set this value to represent some date
in the past, this scheme is still not perfect.  If file is deleted,
you know only that a break in to your account was attempted, but it is
not easy to determine when it occured, though the ctime of your home
directory might be a clue.

One of the reasons I wrote lastlog and ported finger was because they
provide one of the simple security related features of BSD that I
really missed in the SysV world.  Perhaps someday I'll write my own
login replacement for SysV (which isn't actually that difficult, I've
done it for V7), and be done with the uncertainty of a user-accessable
lastlog programme.
-- 
						Greg A. Woods

woods@{eci386,gate,robohack,ontmoh,tmsoft,gpu.utcs.UToronto.CA,utorgpu.BITNET}
+1-416-443-1734 [h]  +1-416-595-5425 [w]    VE3-TCP	Toronto, Ontario CANADA

ssb@quest.UUCP (Scott S. Bertilson) (01/12/90)

jay@banzai.PCC.COM (Jay Schuster) writes:

>If there was some way of getting your tty group ID, you could search
>through utmp and find its pid there.  But I don't think there's any
>wany of finding your tty group id without searching through /dev/kmem.
On my SVR2 Altos, there *IS* a "getpgrp".  It would seem to me
that one could grab that and scan the "utmp" file to find what
port the process is on.  If the process has detached via "setpgrp",
it would seem that your "utmp" entry doesn't apply anymore anyway.
(I'd look at my 3b1, but it's at home..)
-- 

Scott S. Bertilson   ...uunet!rosevax!rose3!quest!ssb
			scott@poincare.geom.umn.edu

thad@cup.portal.com (Thad P Floryan) (01/12/90)

comeau@utoday.UUCP (Greg Comeau) in <1143@utoday.UUCP> writes:

	It appears you wanted to do more than what is stores in u/w/tmp anyway,
	so you nest bet may be to forget that file and create whatever you need
	tocreate via /etc/profile (which will only therefore catch interactive
	logins and not stuff like uucp which can be handled in another way)
	as well as front-ending or re-writing su for the audits you need.

regarding my question whether it's possible to uniquely identify a user under
all conceivable circumstances.  "Uniquely identify" pertaining to username and
to "controlling terminal" such that one could, if so desired, locate the specif
c
/etc/utmp entry.

Results so far:

	username:  YES  (via cuserid(3S))
	terminal:  NO   (if all streams are redirected)

This "quest for truth" is solely for my own edification after I discovered
my own lastlogin program "failed" when I was running su'd root:

	I cannot ASSUME the $HOME will always be inviolate.
	I cannot ASSUME the user will never "su".
	I cannot ASSUME the user won't redirect stdin, stdout and stderr.

Due to other posted events since Jan.2, I have now removed the word "ASSUME"
from my vocabulary!  :-)

Examples abound re: nlist()'ing /unix, so that's not the problem.  The question
was whether there's a non-privileged way of accurately identifying the user.
It appears there is NO such way per (email) responses received to date.

My purpose was not to audit, but to write a PD "who" that would highlight the
present user (either with "*" or reverse video or whatever's applicable) per:

	$ who
	guest     tty000      Jan 11 22:43
	thad      w1          Jan 10 01:59
	thad    * p0          Jan 12 00:32
	thad      ph1         Jan 11 23:57

And, don't laugh; I've had over 16 people (myself multiple times, too) logged
into one of my UNIXPCs at ONE TIME.  This was during a party last month when I
became weary of a boor bragging about his system supporting multiple users at
one time (and, no, it was not any AT&T system although its name did begin with
the letter "A") that (thanks to StarLAN) I just started firing up the online
jobs and had them run GNU EMACS, gcc, several graphics demos, etc.  Needless
to say, the boor quickly became quiet!  It became very apparent very quickly
the UNIXPC outperformed a Mac II A/UX Version 1 (esp. with respect to disk
I/O); he brought his machine over from next door and there was simply NO doubt
in anyone's mind which machine was quicker.

Thad Floryan [ thad@cup.portal.com (OR) ..!sun!portal!cup.portal.com!thad ]

comeau@utoday.UUCP (Greg Comeau) (01/13/90)

In article <25845@cup.portal.com> thad@cup.portal.com (Thad P Floryan) writes:
>Examples abound re: nlist()'ing /unix, so that's not the problem.  The question
>was whether there's a non-privileged way of accurately identifying the user.
>It appears there is NO such way per (email) responses received to date.
>My purpose was not to audit, but to write a PD "who" that would highlight the
>present user (either with "*" or reverse video or whatever's applicable) per:
>	$ who
>	thad      w1          Jan 10 01:59
>	thad    * p0          Jan 12 00:32

Um, if that's what you want to do then assuming (there's that work again!)
a non-esotric use of windows why not just cross check the /dev/tty minor/
major with the minor/major of the dev stored in u/w/tmp?

Or you could even
write a shell script comboing probably the who command, the tty command,
and the sed command.

>And, don't laugh; I've had over 16 people (myself multiple times, too) logged
>into one of my UNIXPCs at ONE TIME.

Interesting.  Via what mechanism?
-- 
Greg, Comeau Computing, 91-34 120th Street, Richmond Hill, NY, 11418
Producers of CC C++, SysAdm columnist for UNIX Today!, Microsoft Systems Journal
(C programming), + others. Also, BIX c.language & c.plus.plus conf. moderator.
Here:attmail!csanta!greg / BIX:comeau / CIS:72331, 3421 / voice:718-849-2355

karl@MorningStar.Com (Karl Fox) (01/13/90)

My UNIX-PC (3.51+Development) *does* have getpgrp().  I tried this quick
hack and haven't been able to make it fail yet.  It works while su'd,
nohup'ed and totally redirected.

# include	<sys/types.h>
# include	<utmp.h>

# define	SO	"\033[7m"
# define	SE	"\033[m"

main()
    {
    extern struct utmp *getutent();

    register int pgrp = getpgrp();
    register struct utmp *up;

    while (up = getutent())
	printf("%s%8s %4s %12s %5d %d %d/%d%s\n",
		pgrp == up -> ut_pid ? SO : "",
		up -> ut_user,
		up -> ut_id,
		up -> ut_line,
		up -> ut_pid,
		up -> ut_type,
		up -> ut_exit.e_termination,
		up -> ut_exit.e_exit,
		pgrp == up -> ut_pid ? SE : "");
    }
--
Karl Fox, Morning Star Technologies               karl@MorningStar.Com

thad@cup.portal.com (Thad P Floryan) (01/14/90)

comeau@utoday.UUCP (Greg Comeau) in <1155@utoday.UUCP> writes:

	>And, don't laugh; I've had over 16 people (myself multiple times,
	>too) logged into one of my UNIXPCs at ONE TIME.

	Interesting.  Via what mechanism?

Hmmm, did the article get truncated?  If so, here's the last paragraph again
in which I detail the how's and wherefore's (using StarLAN):

"
And, don't laugh; I've had over 16 people (myself multiple times, too) logged
into one of my UNIXPCs at ONE TIME.  This was during a party last month when I
became weary of a boor bragging about his system supporting multiple users at
one time (and, no, it was not any AT&T system although its name did begin with
the letter "A") that (thanks to StarLAN) I just started firing up the online
                      ^^^^^^^^^^^^^^^^^
jobs and had them run GNU EMACS, gcc, several graphics demos, etc.  Needless
to say, the boor quickly became quiet!  It became very apparent very quickly
the UNIXPC outperformed a Mac II A/UX Version 1 (esp. with respect to disk
I/O); he brought his machine over from next door and there was simply NO doubt
in anyone's mind which machine was quicker.
"

I have a moderate-size StarLAN network to which many things are connected,
including a lot of terminals.  The StarLAN software (for the UNIXPC) claims
up to 32 users may be connected via StarLAN to a single machine (in addition
to which there are the 7 RS-232 ports, the onboard modem, and the console
window, for a max of 41 online users logged-in at one time) but I haven't
tried that many (yet! :-)

Thad Floryan [ thad@cup.portal.com (OR) ..!sun!portal!cup.portal.com!thad ]

thad@cup.portal.com (Thad P Floryan) (01/14/90)

karl@MorningStar.Com (Karl Fox) in <KARL.90Jan13104731@crappie.MorningStar.Com>
writes:

	My UNIX-PC (3.51+Development) *does* have getpgrp(). I tried this quick
	hack and haven't been able to make it fail yet.  It works while su'd,
	nohup'ed and totally redirected.

Believe it or not, I did an almost identical program just minutes after
Scott S. Bertilson mentioned the getpgrp().  The docs for getpgrp(2) didn't
really give any hints as to how it'd be used, but INTRO(2) said the process
group ID is the PID of the "group leader", and examination of a "ps -ef"
output showed the relationship.

The { getpgrp() ... getutent() } strategy "almost" works:

	1. logging in on console (window), all cases work,
	2. logging in through /dev/ph0, all cases work,
	3. logging in through /dev/tty???, all cases work, but
 ===>	4. logging in through StarLAN, -=<NO>=- cases work.  Sigh.  :-(

The "problem" is that /etc/utmp doesn't contain any entry(ies) for a StarLAN
"listener" process.  Doing a "who -a" shows there's no process ID in /etc/utmp
matching what's shown by "ps -ef".

My approach to solving a problem is exemplified by a recent quote posted to
comp.mail.uucp by Erik Naggum (enag@ifi.uio.no):

	``  "get things _right_", not "get things _working_".
	    "Working" is a subset of "right."  ''

At this point, I've started looking at the proc structure <sys/proc.h> and at
the user structure <sys/user.h>, and have so far coded what's shown in the
fragment enclosure.  I need to look carefully at what this new program will do
since I have the distinct impression it's possible a process' entry could
disappear out from under me while reading /dev/kmem.

If anyone has suggestions how to handle THAT problem (browsing dynamically
changing structures), advice would be appreciated; the "ps" program must do
it, but I don't have sources.

Thad

-------------------- program fragment
#include <stdio.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <fcntl.h>
#include <nlist.h>

struct nlist adrget[3] = {
	{ "proc" },		/* info for sys/proc.h */
	{ "u" },		/* info for sys/user.h */
	{ NULL }
};

main(argc, argv)
	int	argc;
	char	*argv[];
{
	void	perror();
	int	close(), fprintf(), getpgrp(), nlist(), open();
	long	lseek();

	int	kmemfd;

	register int pgrp    = getpgrp();
	register int results = nlist("/unix", adrget);

	if (results < 0 || adrget[0].n_value == 0 || adrget[1].n_value == 0) {
		fprintf(stderr, "%s: namelist error for /unix\n", argv[0]);
		exit(1);
	}
	if ((kmemfd = open("/dev/kmem", O_RDONLY)) == -1) {
		perror("/dev/kmem");
		exit(1);
	}
	if (lseek(kmemfd, adrget[0].n_value, 0) < 0) {
		perror("lseek /dev/kmem");
		exit(1);
	}

/*	program "guts" to be inserted here	*/

	close(kmemfd);
}

comeau@utoday.UUCP (Greg Comeau) (01/15/90)

In article <25932@cup.portal.com> thad@cup.portal.com (Thad P Floryan) writes:
>I need to look carefully at what this new program will do
>since I have the distinct impression it's possible a process' entry could
>disappear out from under me while reading /dev/kmem.
>If anyone has suggestions how to handle THAT problem (browsing dynamically
>changing structures), advice would be appreciated; the "ps" program must do
>it, but I don't have sources.
>

Your worry is proper: entries can disappear out from under you.

'ps' *doesn't* do it though.

It is strictly a snapshot and quite often gets things wrong.
Problem is that without a driver, you are not part of the kernel
and therefore there is nothing you can do about it except zoom around.

And have fun with the guts of your program:  you are assured an
interesting coupla days!
-- 
Greg, Comeau Computing, 91-34 120th Street, Richmond Hill, NY, 11418
Producers of CC C++, SysAdm columnist for UNIX Today!, Microsoft Systems Journal
(C programming), + others. Also, BIX c.language & c.plus.plus conf. moderator.
Here:attmail!csanta!greg / BIX:comeau / CIS:72331, 3421 / voice:718-849-2355