[Pkg-shadow-devel] Bug#505071: login tty mis-determination (see bug#332198)
Paul Szabo
psz at maths.usyd.edu.au
Fri Jan 23 02:10:24 UTC 2009
We had discussed whether login fails often due to left-over utmp
entries. I guess that depends on how likely it is that processes die
without cleanup; and how important it is that login should "work".
I now see that there is the possibility for a DoS attack, by filling
up utmp with left-over entries to cover all PIDs (or a significant
proportion of PID space). For example, we can cause one left-over
entry with
run xterm and within that xterm use "kill -9 $PPID"
and might try that repeatedly to exhaust PID space; except xterm
reuses ptys and re-writes utmp entries, and the pty space is smaller
than PID space...
Until this is fixed, utmp should be sanitized regularly to remove
unused entries. I now use the following, often (i.e. nightly).
Cheers,
Paul Szabo psz at maths.usyd.edu.au http://www.maths.usyd.edu.au/u/psz/
School of Mathematics and Statistics University of Sydney Australia
---
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <utmp.h>
#include <time.h>
/*
#include <signal.h>
#include <errno.h>
*/
int main( int argc, char *argv[])
{
char *usage = "Usage:\n\n\
clear-dead-utmp-entries [-quiet] [-really]\n\n\
to clear left-over utmp entries.\n\n";
int debug = 0;
int quiet = 0;
int really = 0;
int cleared = 0;
int clearable = 0;
int header = 0;
int i;
struct utmp *utmpp;
struct tm *tmmp;
#define buflen 256
char buf[buflen];
struct stat strst;
for (i=1; i<argc; i++) {
if (!strcmp(argv[i], "-debug")) {
debug++;
}
else if (!strcmp(argv[i], "-quiet")) {
quiet++;
}
else if (!strcmp(argv[i], "-really")) {
really++;
}
else {
printf ("\nBad option %s. %s", argv[i], usage);
return 1;
}
}
setutent();
while (utmpp = getutent()) {
if (utmpp->ut_type != INIT_PROCESS &&
utmpp->ut_type != LOGIN_PROCESS &&
utmpp->ut_type != USER_PROCESS) continue;
if (utmpp->ut_pid == 0) continue;
/* Is this PID "alive"? */
/* Seems that /usr/bin/who uses
if (kill(utmpp->ut_pid, 0) < 0 && errno == ESRCH) continue;
but "plain" psz gets EPERM, while root gets success with ENOENT.
*/
sprintf(buf, "/proc/%d", (int)utmpp->ut_pid);
if (!stat(buf, &strst)) continue;
tmmp = localtime(&utmpp->ut_time);
strftime(buf, buflen, "%b %d %H:%M", tmmp);
if (debug) {
printf ("\nutmp line:\n");
printf (" ut_user: %.*s\n", sizeof(utmpp->ut_user), utmpp->ut_user);
printf (" ut_id: %.*s\n", sizeof(utmpp->ut_id), utmpp->ut_id);
printf (" ut_line: %.*s\n", sizeof(utmpp->ut_line), utmpp->ut_line);
printf (" ut_type: %d\n", (int)utmpp->ut_type);
printf (" ut_pid: %d\n", (int)utmpp->ut_pid);
printf (" e_term: %d\n", (int)utmpp->ut_exit.e_termination);
printf (" e_exit: %d\n", (int)utmpp->ut_exit.e_exit);
printf (" ut_time: %s\n", buf);
printf (" ut_host: %.*s\n", sizeof(utmpp->ut_host), utmpp->ut_host);
printf ("\n");
}
if (!header) {
printf ("T PID Name Line Time Hostname\n");
header++;
}
printf ("%-2d%-6d%-10.10s%-8.8s%-13.13s%.40s\n",
(int)utmpp->ut_type,
(int)utmpp->ut_pid,
utmpp->ut_user,
utmpp->ut_line,
buf,
utmpp->ut_host);
if (!really) {
clearable++;
continue;
}
utmpp->ut_type = DEAD_PROCESS;
if (! pututline(utmpp)) {
printf ("\nERROR: Cannot clear utmp entry above\n\n");
break;
};
cleared++;
}
if (cleared==1) {
printf ("Cleared one utmp entry shown above\n");
}
else if (cleared>1) {
printf ("Cleared %d utmp entries shown above\n", cleared);
}
else {
if (clearable) printf ("No utmp entries cleared, need '-really' option for that\n");
else if (!quiet) printf ("No utmp entries cleared\n");
}
}
More information about the Pkg-shadow-devel
mailing list