X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/9f32ceb4d44ae42498813f78269fcf21f9a56b10..4936fcee2910dc4c34d5c3cce4dc23dbe70ca604:/loginrec.c diff --git a/loginrec.c b/loginrec.c index 384136e5..87c336df 100644 --- a/loginrec.c +++ b/loginrec.c @@ -30,7 +30,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/** +/** ** loginrec.c: platform-independent login recording and lastlog retrieval **/ @@ -63,7 +63,7 @@ requires very thorough testing so we do not corrupt local auditing information. These files and their access methods are very system specific indeed. - + For utmpx systems, the corresponding library functions are setutxent() etc. To the author's knowledge, all utmpx systems have these library functions and so no direct write is attempted. If such @@ -82,14 +82,14 @@ Calling the new code -------------------- - + In OpenSSH all login recording and retrieval is performed in login.c. Here you'll find working examples. Also, in the logintest.c program there are more examples. Internal handler calling method ------------------------------- - + When a call is made to login_login() or login_logout(), both routines set a struct logininfo flag defining which action (log in, or log out) is to be taken. They both then call login_write(), which @@ -123,55 +123,56 @@ symbols for the platform. Use logintest to check which symbols are defined before modifying - configure.in and loginrec.c. (You have to build logintest yourself + configure.ac and loginrec.c. (You have to build logintest yourself with 'make logintest' as it's not built by default.) Otherwise, patches to the specific method(s) are very helpful! - + */ /** ** TODO: - ** homegrown ttyslot()q + ** homegrown ttyslot() ** test, test, test ** ** Platform status: ** ---------------- ** ** Known good: - ** Linux (Redhat 6.2, need more variants) + ** Linux (Redhat 6.2, Debian) + ** Solaris ** HP-UX 10.20 (gcc only) ** IRIX + ** NeXT - M68k/HPPA/Sparc (4.2/3.3) ** ** Testing required: Please send reports! - ** Solaris ** NetBSD ** HP-UX 11 ** AIX ** ** Platforms with known problems: - ** NeXT + ** Some variants of Slackware Linux ** **/ #include "includes.h" -#if HAVE_UTMP_H -# include -#endif -#if HAVE_UTMPX_H -# include -#endif -#if HAVE_LASTLOG_H -# include -#endif - #include "ssh.h" #include "xmalloc.h" #include "loginrec.h" +#include "log.h" +#include "atomicio.h" RCSID("$Id$"); +#ifdef HAVE_UTIL_H +# include +#endif + +#ifdef HAVE_LIBUTIL_H +# include +#endif + /** ** prototypes for helper functions in this file **/ @@ -206,7 +207,7 @@ int wtmpx_get_entry(struct logininfo *li); **/ /* login_login(struct logininfo *) -Record a login - * + * * Call with a pointer to a struct logininfo initialised with * login_init_entry() or login_alloc_entry() * @@ -283,20 +284,20 @@ login_get_lastlog(struct logininfo *li, const int uid) { struct passwd *pw; - memset(li, '\0', sizeof(struct logininfo)); + memset(li, '\0', sizeof(*li)); li->uid = uid; - /* + /* * If we don't have a 'real' lastlog, we need the username to * reliably search wtmp(x) for the last login (see - * wtmp_get_entry().) + * wtmp_get_entry().) */ pw = getpwuid(uid); if (pw == NULL) fatal("login_get_lastlog: Cannot find account for uid %i", uid); - + /* No MIN_SIZEOF here - we absolutely *must not* truncate the - * username */ + * username */ strlcpy(li->username, pw->pw_name, sizeof(li->username)); if (getlast_entry(li)) @@ -307,8 +308,8 @@ login_get_lastlog(struct logininfo *li, const int uid) /* login_alloc_entry(int, char*, char*, char*) - Allocate and initialise - * a logininfo structure - * + * a logininfo structure + * * This function creates a new struct logininfo, a data structure * meant to carry the information required to portably record login info. * @@ -321,7 +322,7 @@ logininfo *login_alloc_entry(int pid, const char *username, { struct logininfo *newli; - newli = (struct logininfo *) xmalloc (sizeof(struct logininfo)); + newli = (struct logininfo *) xmalloc (sizeof(*newli)); (void)login_init_entry(newli, pid, username, hostname, line); return newli; } @@ -337,20 +338,20 @@ login_free_entry(struct logininfo *li) /* login_init_entry(struct logininfo *, int, char*, char*, char*) * - initialise a struct logininfo - * + * * Populates a new struct logininfo, a data structure meant to carry * the information required to portably record login info. * * Returns: 1 */ int -login_init_entry(struct logininfo *li, int pid, const char *username, +login_init_entry(struct logininfo *li, int pid, const char *username, const char *hostname, const char *line) { struct passwd *pw; - - memset(li, 0, sizeof(struct logininfo)); - + + memset(li, 0, sizeof(*li)); + li->pid = pid; /* set the line information */ @@ -383,7 +384,7 @@ login_set_current_time(struct logininfo *li) struct timeval tv; gettimeofday(&tv, NULL); - + li->tv_sec = tv.tv_sec; li->tv_usec = tv.tv_usec; } @@ -410,10 +411,12 @@ login_set_addr(struct logininfo *li, const struct sockaddr *sa, int login_write (struct logininfo *li) { +#ifndef HAVE_CYGWIN if ((int)geteuid() != 0) { log("Attempt to write login records by non-root user (aborting)"); return 1; } +#endif /* set the timestamp */ login_set_current_time(li); @@ -440,6 +443,28 @@ login_write (struct logininfo *li) return 0; } +#ifdef LOGIN_NEEDS_UTMPX +int +login_utmp_only(struct logininfo *li) +{ + li->type = LTYPE_LOGIN; + login_set_current_time(li); +# ifdef USE_UTMP + utmp_write_entry(li); +# endif +# ifdef USE_WTMP + wtmp_write_entry(li); +# endif +# ifdef USE_UTMPX + utmpx_write_entry(li); +# endif +# ifdef USE_WTMPX + wtmpx_write_entry(li); +# endif + return 0; +} +#endif + /** ** getlast_entry: Call low-level functions to retrieve the last login ** time. @@ -454,7 +479,7 @@ getlast_entry(struct logininfo *li) #else /* !USE_LASTLOG */ #ifdef DISABLE_LASTLOG - /* On some systems we shouldn't even try to obtain last login + /* On some systems we shouldn't even try to obtain last login * time, e.g. AIX */ return 0; # else /* DISABLE_LASTLOG */ @@ -472,7 +497,7 @@ getlast_entry(struct logininfo *li) return 0; # endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */ # endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */ -# endif /* DISABLE_LASTLOG */ +# endif /* DISABLE_LASTLOG */ #endif /* USE_LASTLOG */ } @@ -501,9 +526,9 @@ char * line_fullname(char *dst, const char *src, int dstsize) { memset(dst, '\0', dstsize); - if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) + if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) { strlcpy(dst, src, dstsize); - else { + } else { strlcpy(dst, "/dev/", dstsize); strlcat(dst, src, dstsize); } @@ -516,7 +541,7 @@ line_stripname(char *dst, const char *src, int dstsize) { memset(dst, '\0', dstsize); if (strncmp(src, "/dev/", 5) == 0) - strlcpy(dst, &src[5], dstsize); + strlcpy(dst, src + 5, dstsize); else strlcpy(dst, src, dstsize); return dst; @@ -529,19 +554,26 @@ line_stripname(char *dst, const char *src, int dstsize) * NOTE: use strncpy because we do NOT necessarily want zero * termination */ char * -line_abbrevname(char *dst, const char *src, int dstsize) +line_abbrevname(char *dst, const char *src, int dstsize) { size_t len; - + memset(dst, '\0', dstsize); - + + /* Always skip prefix if present */ + if (strncmp(src, "/dev/", 5) == 0) + src += 5; + len = strlen(src); - if (len <= 0) { - src += (len - dstsize); - strncpy(dst, src, dstsize); /* note: _don't_ change this to strlcpy */ + if (len > 0) { + if (((int)len - dstsize) > 0) + src += ((int)len - dstsize); + + /* note: _don't_ change this to strlcpy */ + strncpy(dst, src, (size_t)dstsize); } - + return dst; } @@ -572,7 +604,7 @@ void construct_utmp(struct logininfo *li, struct utmp *ut) { - memset(ut, '\0', sizeof(struct utmp)); + memset(ut, '\0', sizeof(*ut)); /* First fill out fields used for both logins and logouts */ @@ -585,9 +617,15 @@ construct_utmp(struct logininfo *li, switch (li->type) { case LTYPE_LOGIN: ut->ut_type = USER_PROCESS; +#ifdef _CRAY + cray_set_tmpdir(ut); +#endif break; case LTYPE_LOGOUT: ut->ut_type = DEAD_PROCESS; +#ifdef _CRAY + cray_retain_utmp(ut, li->pid); +#endif break; } # endif @@ -605,11 +643,11 @@ construct_utmp(struct logininfo *li, /* * These fields are only used when logging in, and are blank - * for logouts. + * for logouts. */ /* Use strncpy because we don't necessarily want null termination */ - strncpy(ut->ut_user, li->username, MIN_SIZEOF(ut->ut_user, li->username)); + strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username)); # ifdef HAVE_HOST_IN_UTMP strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname)); # endif @@ -617,7 +655,7 @@ construct_utmp(struct logininfo *li, /* this is just a 32-bit IP address */ if (li->hostaddr.sa.sa_family == AF_INET) ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; -# endif +# endif } #endif /* USE_UTMP || USE_WTMP || USE_LOGIN */ @@ -646,8 +684,10 @@ set_utmpx_time(struct logininfo *li, struct utmpx *utx) void construct_utmpx(struct logininfo *li, struct utmpx *utx) { - memset(utx, '\0', sizeof(struct utmpx)); + memset(utx, '\0', sizeof(*utx)); +# ifdef HAVE_ID_IN_UTMPX line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); +# endif /* this is done here to keep utmp constants out of loginrec.h */ switch (li->type) { @@ -667,7 +707,7 @@ construct_utmpx(struct logininfo *li, struct utmpx *utx) /* * These fields are only used when logging in, and are blank - * for logouts. + * for logouts. */ /* strncpy(): Don't necessarily want null termination */ @@ -676,7 +716,9 @@ construct_utmpx(struct logininfo *li, struct utmpx *utx) strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname)); # endif # ifdef HAVE_ADDR_IN_UTMPX - /* FIXME: (ATL) not supported yet */ + /* this is just a 32-bit IP address */ + if (li->hostaddr.sa.sa_family == AF_INET) + utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; # endif # ifdef HAVE_SYSLEN_IN_UTMPX /* ut_syslen is the length of the utx_host string */ @@ -724,8 +766,30 @@ utmp_write_direct(struct logininfo *li, struct utmp *ut) int tty; /* FIXME: (ATL) ttyslot() needs local implementation */ + +#if defined(HAVE_GETTTYENT) + register struct ttyent *ty; + + tty=0; + + setttyent(); + while ((struct ttyent *)0 != (ty = getttyent())) { + tty++; + if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line))) + break; + } + endttyent(); + + if((struct ttyent *)0 == ty) { + log("utmp_write_entry: tty not found"); + return(1); + } +#else /* FIXME */ + tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ +#endif /* HAVE_GETTTYENT */ + if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); /* @@ -733,18 +797,18 @@ utmp_write_direct(struct logininfo *li, struct utmp *ut) * If the new ut_line is empty but the old one is not * and ut_line and ut_name match, preserve the old ut_line. */ - if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && - (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && - (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && + if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && + (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && + (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) { (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); } - + (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); - if (atomicio(write, fd, ut, sizeof(ut)) != sizeof(ut)) + if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) log("utmp_write_direct: error writing %s: %s", UTMP_FILE, strerror(errno)); - + (void)close(fd); return 1; } else { @@ -846,7 +910,7 @@ utmpx_write_library(struct logininfo *li, struct utmpx *utx) /* write a utmp entry direct to the file */ static int utmpx_write_direct(struct logininfo *li, struct utmpx *utx) -{ +{ log("utmpx_write_direct: not implemented!"); return 0; } @@ -916,7 +980,7 @@ utmpx_write_entry(struct logininfo *li) ** Low-level wtmp functions **/ -#ifdef USE_WTMP +#ifdef USE_WTMP /* write a wtmp entry direct to the end of the file */ /* This is a slight modification of code in OpenBSD's logwtmp.c */ @@ -931,7 +995,7 @@ wtmp_write(struct logininfo *li, struct utmp *ut) WTMP_FILE, strerror(errno)); return 0; } - if (fstat(fd, &buf) == 0) + if (fstat(fd, &buf) == 0) if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) { ftruncate(fd, buf.st_size); log("wtmp_write: problem writing %s: %s", @@ -978,7 +1042,7 @@ wtmp_write_entry(struct logininfo *li) /* Notes on fetching login data from wtmp/wtmpx - * + * * Logouts are usually recorded with (amongst other things) a blank * username on a given tty line. However, some systems (HP-UX is one) * leave all fields set, but change the ut_type field to DEAD_PROCESS. @@ -997,8 +1061,8 @@ wtmp_write_entry(struct logininfo *li) static int wtmp_islogin(struct logininfo *li, struct utmp *ut) { - if (strncmp(li->username, ut->ut_user, - MIN_SIZEOF(li->username, ut->ut_user)) == 0) { + if (strncmp(li->username, ut->ut_name, + MIN_SIZEOF(li->username, ut->ut_name)) == 0) { # ifdef HAVE_TYPE_IN_UTMP if (ut->ut_type & USER_PROCESS) return 1; @@ -1024,7 +1088,7 @@ wtmp_get_entry(struct logininfo *li) WTMP_FILE, strerror(errno)); return 0; } - if (fstat(fd, &st) != 0) { + if (fstat(fd, &st) != 0) { log("wtmp_get_entry: couldn't stat %s: %s", WTMP_FILE, strerror(errno)); close(fd); @@ -1032,7 +1096,7 @@ wtmp_get_entry(struct logininfo *li) } /* Seek to the start of the last struct utmp */ - if (lseek(fd, (off_t)(0-sizeof(struct utmp)), SEEK_END) == -1) { + if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { /* Looks like we've got a fresh wtmp file */ close(fd); return 0; @@ -1065,7 +1129,7 @@ wtmp_get_entry(struct logininfo *li) continue; } /* Seek back 2 x struct utmp */ - if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) { + if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { /* We've found the start of the file, so quit */ close (fd); return 0; @@ -1098,7 +1162,7 @@ wtmpx_write(struct logininfo *li, struct utmpx *utx) return 0; } - if (fstat(fd, &buf) == 0) + if (fstat(fd, &buf) == 0) if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) { ftruncate(fd, buf.st_size); log("wtmpx_write: problem writing %s: %s", @@ -1152,8 +1216,8 @@ wtmpx_write_entry(struct logininfo *li) static int wtmpx_islogin(struct logininfo *li, struct utmpx *utx) { - if ( strncmp(li->username, utx->ut_user, - MIN_SIZEOF(li->username, utx->ut_user)) == 0 ) { + if ( strncmp(li->username, utx->ut_name, + MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) { # ifdef HAVE_TYPE_IN_UTMPX if (utx->ut_type == USER_PROCESS) return 1; @@ -1180,15 +1244,15 @@ wtmpx_get_entry(struct logininfo *li) WTMPX_FILE, strerror(errno)); return 0; } - if (fstat(fd, &st) != 0) { + if (fstat(fd, &st) != 0) { log("wtmpx_get_entry: couldn't stat %s: %s", WTMP_FILE, strerror(errno)); close(fd); return 0; } - + /* Seek to the start of the last struct utmpx */ - if (lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END) == -1 ) { + if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { /* probably a newly rotated wtmpx file */ close(fd); return 0; @@ -1218,7 +1282,7 @@ wtmpx_get_entry(struct logininfo *li) # endif continue; } - if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) { + if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { close (fd); return 0; } @@ -1239,7 +1303,7 @@ syslogin_perform_login(struct logininfo *li) { struct utmp *ut; - if (! (ut = (struct utmp *)malloc(sizeof(struct utmp)))) { + if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) { log("syslogin_perform_login: couldn't malloc()"); return 0; } @@ -1254,7 +1318,7 @@ syslogin_perform_logout(struct logininfo *li) { # ifdef HAVE_LOGOUT char line[8]; - + (void)line_stripname(line, li->line, sizeof(line)); if (!logout(line)) { @@ -1262,8 +1326,8 @@ syslogin_perform_logout(struct logininfo *li) # ifdef HAVE_LOGWTMP } else { logwtmp(line, "", ""); - } # endif + } /* FIXME: (ATL - if the need arises) What to do if we have * login, but no logout? what if logout but no logwtmp? All * routines are in libutil so they should all be there, @@ -1302,8 +1366,8 @@ static void lastlog_construct(struct logininfo *li, struct lastlog *last) { /* clear the structure */ - memset(last, '\0', sizeof(struct lastlog)); - + memset(last, '\0', sizeof(*last)); + (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line)); strlcpy(last->ll_host, li->hostname, MIN_SIZEOF(last->ll_host, li->hostname)); @@ -1316,7 +1380,7 @@ lastlog_filetype(char *filename) struct stat st; if (stat(LASTLOG_FILE, &st) != 0) { - log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE, + log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE, strerror(errno)); return 0; } @@ -1358,15 +1422,18 @@ lastlog_openseek(struct logininfo *li, int *fd, int filemode) lastlog_file, strerror(errno)); return 0; } - - /* find this uid's offset in the lastlog file */ - offset = (off_t) ( (long)li->uid * sizeof(struct lastlog)); - if ( lseek(*fd, offset, SEEK_SET) != offset ) { - log("lastlog_openseek: %s->lseek(): %s", - lastlog_file, strerror(errno)); - return 0; + if (type == LL_FILE) { + /* find this uid's offset in the lastlog file */ + offset = (off_t) ((long)li->uid * sizeof(struct lastlog)); + + if ( lseek(*fd, offset, SEEK_SET) != offset ) { + log("lastlog_openseek: %s->lseek(): %s", + lastlog_file, strerror(errno)); + return 0; + } } + return 1; } @@ -1379,17 +1446,19 @@ lastlog_perform_login(struct logininfo *li) /* create our struct lastlog */ lastlog_construct(li, &last); + if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) + return(0); + /* write the entry */ - if (lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) { - if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) { - log("lastlog_write_filemode: Error writing to %s: %s", - LASTLOG_FILE, strerror(errno)); - return 0; - } - return 1; - } else { + if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) { + close(fd); + log("lastlog_write_filemode: Error writing to %s: %s", + LASTLOG_FILE, strerror(errno)); return 0; } + + close(fd); + return 1; } int @@ -1408,7 +1477,7 @@ static void lastlog_populate_entry(struct logininfo *li, struct lastlog *last) { line_fullname(li->line, last->ll_line, sizeof(li->line)); - strlcpy(li->hostname, last->ll_host, + strlcpy(li->hostname, last->ll_host, MIN_SIZEOF(li->hostname, last->ll_host)); li->tv_sec = last->ll_time; } @@ -1419,17 +1488,20 @@ lastlog_get_entry(struct logininfo *li) struct lastlog last; int fd; - if (lastlog_openseek(li, &fd, O_RDONLY)) { - if (atomicio(read, fd, &last, sizeof(last)) != sizeof(last)) { - log("lastlog_get_entry: Error reading from %s: %s", - LASTLOG_FILE, strerror(errno)); - return 0; - } else { - lastlog_populate_entry(li, &last); - return 1; - } - } else { - return 0; + if (!lastlog_openseek(li, &fd, O_RDONLY)) + return 0; + + if (atomicio(read, fd, &last, sizeof(last)) != sizeof(last)) { + close(fd); + log("lastlog_get_entry: Error reading from %s: %s", + LASTLOG_FILE, strerror(errno)); + return 0; } + + close(fd); + + lastlog_populate_entry(li, &last); + + return 1; } #endif /* USE_LASTLOG */