diff --git a/configure.in b/configure.in index 073eb27c..46d37f90 100644 --- a/configure.in +++ b/configure.in @@ -41,7 +41,7 @@ AC_CHECK_HEADER([shadow.h],,[AC_MSG_ERROR([You need a libc with shadow.h])]) AC_CHECK_FUNCS(l64a fchmod fchown fsync futimes getgroups gethostname getspnam \ gettimeofday getusershell getutent initgroups lchown lckpwdf lstat \ lutimes memcpy memset setgroups sigaction strchr updwtmp updwtmpx innetgr \ - getpwnam_r getpwuid_r getgrnam_r getgrgid_r getspnam_r) + getpwnam_r getpwuid_r getgrnam_r getgrgid_r getspnam_r getaddrinfo) AC_SYS_LARGEFILE dnl Checks for typedefs, structures, and compiler characteristics. @@ -59,38 +59,32 @@ AC_CHECK_MEMBERS([struct stat.st_mtimensec]) AC_HEADER_TIME AC_STRUCT_TM -if test "$ac_cv_header_utmp_h" = "yes"; then - AC_CACHE_CHECK(for ut_host in struct utmp, - ac_cv_struct_utmp_ut_host, - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([#include ], - [struct utmp ut; char *cp = ut.ut_host;] - )], - [ac_cv_struct_utmp_ut_host=yes], - [ac_cv_struct_utmp_ut_host=no] - ) - ) - - if test "$ac_cv_struct_utmp_ut_host" = "yes"; then - AC_DEFINE(UT_HOST, 1, [Define if you have ut_host in struct utmp.]) - fi - - AC_CACHE_CHECK(for ut_user in struct utmp, - ac_cv_struct_utmp_ut_user, - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include ], - [struct utmp ut; char *cp = ut.ut_user;] - )], - [ac_cv_struct_utmp_ut_user=yes], - [ac_cv_struct_utmp_ut_user=no] - ) - ) - - if test "$ac_cv_struct_utmp_ut_user" = "no"; then - AC_DEFINE(ut_user, ut_name, - [Define to ut_name if struct utmp has ut_name (not ut_user).]) - fi +AC_CHECK_MEMBERS([struct utmp.ut_type, + struct utmp.ut_id, + struct utmp.ut_name, + struct utmp.ut_user, + struct utmp.ut_host, + struct utmp.ut_syslen, + struct utmp.ut_addr, + struct utmp.ut_addr_v6, + struct utmp.ut_time, + struct utmp.ut_xtime, + struct utmp.ut_tv],,,[[#include ]]) +dnl There are dependencies: +dnl If UTMPX has to be used, the utmp structure shall have a ut_id field. +if test "$ac_cv_header_utmpx_h" = "yes" && + test "$ac_cv_member_struct_utmp_ut_id" != "yes"; then + AC_MSG_ERROR(Systems with UTMPX and no ut_id field in the utmp structure are not supported) fi +AC_CHECK_MEMBERS([struct utmpx.ut_name, + struct utmpx.ut_host, + struct utmpx.ut_syslen, + struct utmpx.ut_addr, + struct utmpx.ut_addr_v6, + struct utmpx.ut_time, + struct utmpx.ut_xtime],,,[[#include ]]) + if test "$ac_cv_header_lastlog_h" = "yes"; then AC_CACHE_CHECK(for ll_host in struct lastlog, ac_cv_struct_lastlog_ll_host, diff --git a/lib/prototypes.h b/lib/prototypes.h index 56ab225c..bc2dabd9 100644 --- a/lib/prototypes.h +++ b/lib/prototypes.h @@ -345,8 +345,19 @@ extern char *tz (const char *); extern int set_filesize_limit (int blocks); /* utmp.c */ -extern void checkutmp (bool picky); -extern int setutmp (const char *, const char *, const char *); +extern struct utmp *get_current_utmp (void); +extern struct utmp *prepare_utmp (const char *name, + const char *line, + const char *host, + struct utmp *ut); +extern int setutmp (struct utmp *ut); +#ifdef HAVE_UTMPX_H +extern struct utmpx *prepare_utmpx (const char *name, + const char *line, + const char *host, + struct utmp *ut); +extern int setutmpx (struct utmpx *utx); +#endif /* valid.c */ extern bool valid (const char *, const struct passwd *); diff --git a/libmisc/utmp.c b/libmisc/utmp.c index 0c7a5d14..32843a1c 100644 --- a/libmisc/utmp.c +++ b/libmisc/utmp.c @@ -2,7 +2,7 @@ * Copyright (c) 1989 - 1994, Julianne Frances Haugh * Copyright (c) 1996 - 1999, Marek Michałkiewicz * Copyright (c) 2001 - 2005, Tomasz Kłoczko - * Copyright (c) 2008 , Nicolas François + * Copyright (c) 2008 - 2009, Nicolas François * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,24 +37,19 @@ #include +// FIXME: disable UTMPX on Linux in configure.in +// Maybe define an intermediate USE_UTMPX to replace HAVE_UTMPX_H, +// which would be defined if HAVE_UTMPX_H is defined on non-Linux. #if HAVE_UTMPX_H #include #endif -#include +#include +#include #include #ident "$Id$" -#if HAVE_UTMPX_H -struct utmpx utxent; -#endif -struct utmp utent; - -#define NO_UTENT \ - _("No utmp entry. You must exec \"login\" from the lowest level \"sh\"") -#define NO_TTY \ - _("Unable to determine your tty name.") /* * is_my_tty -- determine if "tty" is the same TTY stdin is using @@ -62,19 +57,21 @@ struct utmp utent; static bool is_my_tty (const char *tty) { char full_tty[200]; - struct stat by_name, by_fd; + static const char *tmptty = NULL; if ('/' != *tty) { snprintf (full_tty, sizeof full_tty, "/dev/%s", tty); tty = full_tty; } - if ( (stat (tty, &by_name) != 0) - || (fstat (STDIN_FILENO, &by_fd) != 0)) { - return false; + if (NULL == tmptty) { + tmptty = ttyname (STDIN_FILENO); } - if (by_name.st_rdev != by_fd.st_rdev) { + if (NULL == tmptty) { + (void) puts (_("Unable to determine your tty name.")); + exit (EXIT_FAILURE); + } else if (strcmp (tty, tmptty) != 0) { return false; } else { return true; @@ -82,36 +79,36 @@ static bool is_my_tty (const char *tty) } /* - * checkutmp - see if utmp file is correct for this process + * get_current_utmp - return the most probable utmp entry for the current + * session * - * System V is very picky about the contents of the utmp file - * and requires that a slot for the current process exist. - * The utmp file is scanned for an entry with the same process - * ID. If no entry exists the process exits with a message. + * The utmp file is scanned for an entry with the same process ID. + * The line enterred by the *getty / telnetd, etc. should also match + * the current terminal. * - * The "picky" flag is for network and other logins that may - * use special flags. It allows the pid checks to be overridden. - * This means that getty should never invoke login with any - * command line flags. + * When an entry is returned by get_current_utmp, and if the utmp + * structure has a ut_id field, this field should be used to update + * the entry information. + * + * Return NULL if no entries exist in utmp for the current process. */ - -#if defined(__linux__) /* XXX */ - -void checkutmp (bool picky) +struct utmp *get_current_utmp (void) { - char *line; struct utmp *ut; - pid_t pid = getpid (); + struct utmp *ret = NULL; setutent (); /* First, try to find a valid utmp entry for this process. */ while ((ut = getutent ()) != NULL) { - if ( (ut->ut_pid == pid) - && ('\0' != ut->ut_line[0]) + if ( (ut->ut_pid == getpid ()) +#ifdef HAVE_STRUCT_UTMP_UT_ID && ('\0' != ut->ut_id[0]) +#endif +#ifdef HAVE_STRUCT_UTMP_UT_TYPE && ( (LOGIN_PROCESS == ut->ut_type) || (USER_PROCESS == ut->ut_type)) +#endif /* A process may have failed to close an entry * Check if this entry refers to the current tty */ && is_my_tty (ut->ut_line)) { @@ -119,173 +116,20 @@ void checkutmp (bool picky) } } - /* We cannot trust the ut_line field. Prepare the new value. */ - line = ttyname (0); - if (NULL == line) { - (void) puts (NO_TTY); - exit (EXIT_FAILURE); - } - if (strncmp (line, "/dev/", 5) == 0) { - line += 5; - } - - /* If there is one, just use it, otherwise create a new one. */ if (NULL != ut) { - utent = *ut; - } else { - if (picky) { - (void) puts (NO_UTENT); - exit (EXIT_FAILURE); - } - memset ((void *) &utent, 0, sizeof utent); - utent.ut_type = LOGIN_PROCESS; - utent.ut_pid = pid; - /* XXX - assumes /dev/tty?? or /dev/pts/?? */ - strncpy (utent.ut_id, line + 3, sizeof utent.ut_id); - strcpy (utent.ut_user, "LOGIN"); - utent.ut_time = time (NULL); + ret = malloc (sizeof (*ret)); + memcpy (ret, ut, sizeof (*ret)); } - /* Sanitize / set the ut_line field */ - strncpy (utent.ut_line, line, sizeof utent.ut_line); - endutent (); + + return ret; } -#elif defined(LOGIN_PROCESS) - -void checkutmp (bool picky) -{ - char *line; - struct utmp *ut; - -#if HAVE_UTMPX_H - struct utmpx *utx; -#endif - pid_t pid = getpid (); - -#if HAVE_UTMPX_H - setutxent (); -#endif - setutent (); - - if (picky) { -#if HAVE_UTMPX_H - while ((utx = getutxent ()) != NULL) { - if (utx->ut_pid == pid) { - break; - } - } - - if (NULL != utx) { - utxent = *utx; - } -#endif - while ((ut = getutent ()) != NULL) { - if (ut->ut_pid == pid) { - break; - } - } - - if (NULL != ut) { - utent = *ut; - } - -#if HAVE_UTMPX_H - endutxent (); -#endif - endutent (); - - if (NULL == ut) { - (void) puts (NO_UTENT); - exit (EXIT_FAILURE); - } -#ifndef UNIXPC - - /* - * If there is no ut_line value in this record, fill - * it in by getting the TTY name and stuffing it in - * the structure. The UNIX/PC is broken in this regard - * and needs help ... - */ - /* XXX: The ut_line may not match with the current tty. - * ut_line will be set by setutmp anyway, but ut_id - * will not be set, and the wrong utmp entry may be - * updated. -- nekral */ - - if (utent.ut_line[0] == '\0') -#endif /* !UNIXPC */ - { - line = ttyname (0); - if (NULL == line) { - (void) puts (NO_TTY); - exit (EXIT_FAILURE); - } - if (strncmp (line, "/dev/", 5) == 0) { - line += 5; - } - strncpy (utent.ut_line, line, sizeof utent.ut_line); -#if HAVE_UTMPX_H - strncpy (utxent.ut_line, line, sizeof utxent.ut_line); -#endif - } - } else { - line = ttyname (0); - if (NULL == line) { - (void) puts (NO_TTY); - exit (EXIT_FAILURE); - } - if (strncmp (line, "/dev/", 5) == 0) { - line += 5; - } - - strncpy (utent.ut_line, line, sizeof utent.ut_line); - ut = getutline (&utent); - if (NULL != ut) { - strncpy (utent.ut_id, ut->ut_id, sizeof ut->ut_id); - } - - strcpy (utent.ut_user, "LOGIN"); - utent.ut_pid = getpid (); - utent.ut_type = LOGIN_PROCESS; - utent.ut_time = time (NULL); -#if HAVE_UTMPX_H - strncpy (utxent.ut_line, line, sizeof utxent.ut_line); - utx = getutxline (&utxent); - if (NULL != utx) { - strncpy (utxent.ut_id, utx->ut_id, sizeof utxent.ut_id); - } - - strcpy (utxent.ut_user, "LOGIN"); - utxent.ut_pid = utent.ut_pid; - utxent.ut_type = utent.ut_type; - if (sizeof (utxent.ut_tv) == sizeof (struct timeval)) { - gettimeofday ((struct timeval *) &utxent.ut_tv, NULL); - } else { - struct timeval tv; - - gettimeofday (&tv, NULL); - utxent.ut_tv.tv_sec = tv.tv_sec; - utxent.ut_tv.tv_usec = tv.tv_usec; - } - utent.ut_time = utxent.ut_tv.tv_sec; -#endif - } - -#if HAVE_UTMPX_H - endutxent (); -#endif - endutent (); -} - -#endif - - /* * Some systems already have updwtmp() and possibly updwtmpx(). Others - * don't, so we re-implement these functions if necessary. --marekm + * don't, so we re-implement these functions if necessary. */ - #ifndef HAVE_UPDWTMP static void updwtmp (const char *filename, const struct utmp *ut) { @@ -319,140 +163,293 @@ static void updwtmpx (const char *filename, const struct utmpx *utx) * setutmp - put a USER_PROCESS entry in the utmp file * * setutmp changes the type of the current utmp entry to - * USER_PROCESS. the wtmp file will be updated as well. + * USER_PROCESS. + * The wtmp file will be updated as well. + * + * ut, as returned by get_current_utmp + * + * We reuse the ut_id and ut_host fields + * + * The returned structure shall be freed by the caller. */ -#if defined(__linux__) /* XXX */ - -int setutmp (const char *name, const char unused(*line), const char unused(*host)) +/* + * prepare_utmp - prepare an utmp entry so that it can be logged in a + * utmp/wtmp file. + * + * It requires an utmp entry in input (ut) to return an entry with + * the right ut_id. This is typically an entry returned by + * get_current_utmp + * + * The ut_host field of the input structure may also be kept, and to + * define the ut_addr/ut_addr_v6 fields. (if these fields exist) + * + * Other fields are discarded and filed with new values (if they + * exist). + * + * The returned structure shall be freed by the caller. + */ +struct utmp *prepare_utmp (const char *name, + const char *line, + const char *host, + struct utmp *ut) { - int err = 0; - utent.ut_type = USER_PROCESS; - strncpy (utent.ut_user, name, sizeof utent.ut_user); - utent.ut_time = time (NULL); - /* other fields already filled in by checkutmp above */ - setutent (); - if (pututline (&utent) == NULL) { - err = 1; + struct timeval tv; + char *hostname = NULL; + struct utmp *utent; + + assert (NULL != name); + assert (NULL != line); + assert (NULL != ut); + + + + if ( (NULL != host) + && ('\0' != host)) { + hostname = (char *) xmalloc (strlen (host) + 1); + strcpy (hostname, host); +#ifdef HAVE_STRUCT_UTMP_UT_HOST + } else if ( (NULL != ut->ut_host) + && ('\0' != ut->ut_host[0])) { + hostname = (char *) xmalloc (sizeof (ut->ut_host) + 1); + strncpy (hostname, ut->ut_host, sizeof (ut->ut_host)); + hostname[sizeof (ut->ut_host)] = '\0'; +#endif /* HAVE_STRUCT_UTMP_UT_HOST */ } - endutent (); - updwtmp (_WTMP_FILE, &utent); - return err; -} - -#elif HAVE_UTMPX_H - -int setutmp (const char *name, const char *line, const char *host) -{ - struct utmp *utmp, utline; - struct utmpx *utmpx, utxline; - pid_t pid = getpid (); - bool found_utmpx = false; - bool found_utmp = false; - int err = 0; - - /* - * The canonical device name doesn't include "/dev/"; skip it - * if it is already there. - */ - - if (strncmp (line, "/dev/", 5) == 0) { + if (strncmp(line, "/dev/", 5) == 0) { line += 5; } - /* - * Update utmpx. We create an empty entry in case there is - * no matching entry in the utmpx file. - */ - setutxent (); + utent = (struct utmp *) xmalloc (sizeof (*utent)); + memzero (utent, sizeof (*utent)); + + + +#ifdef HAVE_STRUCT_UTMP_UT_TYPE + utent->ut_type = USER_PROCESS; +#endif /* HAVE_STRUCT_UTMP_UT_TYPE */ + utent->ut_pid = getpid (); + strncpy (utent->ut_line, line, sizeof (utent->ut_line)); +#ifdef HAVE_STRUCT_UTMP_UT_ID + strncpy (utent->ut_id, ut->ut_id, sizeof (utent->ut_id)); +#endif /* HAVE_STRUCT_UTMP_UT_ID */ +#ifdef HAVE_STRUCT_UTMP_UT_NAME + strncpy (utent->ut_name, name, sizeof (utent->ut_name)); +#endif /* HAVE_STRUCT_UTMP_UT_NAME */ +#ifdef HAVE_STRUCT_UTMP_UT_USER + strncpy (utent->ut_user, name, sizeof (utent->ut_user)); +#endif /* HAVE_STRUCT_UTMP_UT_USER */ + if (NULL != hostname) { + struct addrinfo *info = NULL; +#ifdef HAVE_STRUCT_UTMP_UT_HOST + strncpy (utent->ut_host, hostname, sizeof (utent->ut_host)); +#endif /* HAVE_STRUCT_UTMP_UT_HOST */ +#ifdef HAVE_STRUCT_UTMP_UT_SYSLEN + utent->ut_syslen = MIN (strlen (hostname), + sizeof (utent->ut_host)); +#endif /* HAVE_STRUCT_UTMP_UT_SYSLEN */ +#if defined(HAVE_STRUCT_UTMP_UT_ADDR) || defined(HAVE_STRUCT_UTMP_UT_ADDR_V6) + if (getaddrinfo (hostname, NULL, NULL, &info) == 0) { + /* getaddrinfo might not be reliable. + * Just try to log what may be useful. + */ + if (info->ai_family == AF_INET) { + struct sockaddr_in *sa = + (struct sockaddr_in *) info->ai_addr; +#ifdef HAVE_STRUCT_UTMP_UT_ADDR + memcpy (&(utent->ut_addr), + &(sa->sin_addr), + MIN (sizeof (utent->ut_addr), + sizeof (sa->sin_addr))); +#endif /* HAVE_STRUCT_UTMP_UT_ADDR */ +#ifdef HAVE_STRUCT_UTMP_UT_ADDR_V6 + memcpy (utent->ut_addr_v6, + &(sa->sin_addr), + MIN (sizeof (utent->ut_addr_v6), + sizeof (sa->sin_addr))); + } else if (info->ai_family == AF_INET6) { + struct sockaddr_in6 *sa = + (struct sockaddr_in6 *) info->ai_addr; + memcpy (utent->ut_addr_v6, + &(sa->sin6_addr), + MIN (sizeof (utent->ut_addr_v6), + sizeof (sa->sin6_addr))); +#endif /* HAVE_STRUCT_UTMP_UT_ADDR_V6 */ + } + freeaddrinfo (info); + } +#endif /* HAVE_STRUCT_UTMP_UT_ADDR || HAVE_STRUCT_UTMP_UT_ADDR_V6 */ + free (hostname); + } + /* ut_exit is only for DEAD_PROCESS */ + utent->ut_session = getsid (0); + gettimeofday (&tv, NULL); +#ifdef HAVE_STRUCT_UTMP_UT_TIME + utent->ut_time = tv.tv_sec; +#endif /* HAVE_STRUCT_UTMP_UT_TIME */ +#ifdef HAVE_STRUCT_UTMP_UT_XTIME + utent->ut_xtime = tv.tv_usec; +#endif /* HAVE_STRUCT_UTMP_UT_XTIME */ +#ifdef HAVE_STRUCT_UTMP_UT_TV + utent->ut_tv.tv_sec = tv.tv_sec; + utent->ut_tv.tv_usec = tv.tv_usec; +#endif /* HAVE_STRUCT_UTMP_UT_TV */ + + return utent; +} + +/* + * setutmp - Update an entry in utmp and log an entry in wtmp + * + * Return 1 on failure and 0 on success. + */ +int setutmp (struct utmp *ut) +{ + int err = 0; + + assert (NULL != ut); + setutent (); - - while ((utmpx = getutxent ()) != NULL) { - if (utmpx->ut_pid == pid) { - found_utmpx = true; - break; - } - } - while ((utmp = getutent ()) != NULL) { - if (utmp->ut_pid == pid) { - found_utmp = true; - break; - } - } - - /* - * If the entry matching `pid' cannot be found, create a new - * entry with the device name in it. - */ - - if (!found_utmpx) { - memset ((void *) &utxline, 0, sizeof utxline); - strncpy (utxline.ut_line, line, sizeof utxline.ut_line); - utxline.ut_pid = getpid (); - } else { - utxline = *utmpx; - if (strncmp (utxline.ut_line, "/dev/", 5) == 0) { - memmove (utxline.ut_line, utxline.ut_line + 5, - sizeof utxline.ut_line - 5); - utxline.ut_line[sizeof utxline.ut_line - 5] = '\0'; - } - } - if (!found_utmp) { - memset ((void *) &utline, 0, sizeof utline); - strncpy (utline.ut_line, utxline.ut_line, - sizeof utline.ut_line); - utline.ut_pid = utxline.ut_pid; - } else { - utline = *utmp; - if (strncmp (utline.ut_line, "/dev/", 5) == 0) { - memmove (utline.ut_line, utline.ut_line + 5, - sizeof utline.ut_line - 5); - utline.ut_line[sizeof utline.ut_line - 5] = '\0'; - } - } - - /* - * Fill in the fields in the utmpx entry and write it out. Do - * the utmp entry at the same time to make sure things don't - * get messed up. - */ - - strncpy (utxline.ut_user, name, sizeof utxline.ut_user); - strncpy (utline.ut_user, name, sizeof utline.ut_user); - - utline.ut_type = utxline.ut_type = USER_PROCESS; - - if (sizeof (utxline.ut_tv) == sizeof (struct timeval)) { - gettimeofday ((struct timeval *) &utxline.ut_tv, NULL); - } else { - struct timeval tv; - - gettimeofday (&tv, NULL); - utxline.ut_tv.tv_sec = tv.tv_sec; - utxline.ut_tv.tv_usec = tv.tv_usec; - } - utline.ut_time = utxline.ut_tv.tv_sec; - - strncpy (utxline.ut_host, (NULL != host) ? host : "", - sizeof utxline.ut_host); - - if ( (pututxline (&utxline) == NULL) - || (pututline (&utline) == NULL)) { + if (pututline (ut) == NULL) { err = 1; } - - updwtmpx (_WTMP_FILE "x", &utxline); - updwtmp (_WTMP_FILE, &utline); - - utxent = utxline; - utent = utline; - - endutxent (); endutent (); + updwtmp (_WTMP_FILE, ut); + return err; } -#endif +#ifdef HAVE_UTMPX_H +/* + * prepare_utmpx - the UTMPX version for prepare_utmp + */ +struct utmpx *prepare_utmpx (const char *name, + const char *line, + const char *host, + struct utmp *ut) +{ + struct timeval tv; + char *hostname = NULL; + struct utmpx *utxent; + + assert (NULL != name); + assert (NULL != line); + assert (NULL != ut); + + + + if ( (NULL != host) + && ('\0' != host)) { + hostname = (char *) xmalloc (strlen (host) + 1); + strcpy (hostname, host); +#ifdef HAVE_STRUCT_UTMP_UT_HOST + } else if ( (NULL != ut->ut_host) + && ('\0' != ut->ut_host[0])) { + hostname = (char *) xmalloc (sizeof (ut->ut_host) + 1); + strncpy (hostname, ut->ut_host, sizeof (ut->ut_host)); + hostname[sizeof (ut->ut_host)] = '\0'; +#endif /* HAVE_STRUCT_UTMP_UT_TYPE */ + } + + if (strncmp(line, "/dev/", 5) == 0) { + line += 5; + } + + utxent = (struct utmpx *) xmalloc (sizeof (*utxent)); + memzero (utxent, sizeof (*utxent)); + + + + utxent->ut_type = USER_PROCESS; + utxent->ut_pid = getpid (); + strncpy (utxent->ut_line, line, sizeof (utxent->ut_line)); +#ifndef HAVE_STRUCT_UTMP_UT_ID +// FIXME: move to configure.in +# error "No support for systems with utmpx and no ut_id field in utmp" +#endif /* !HAVE_STRUCT_UTMP_UT_ID */ + strncpy (utxent->ut_id, ut->ut_id, sizeof (utxent->ut_id)); +#ifdef HAVE_STRUCT_UTMPX_UT_NAME + strncpy (utxent->ut_name, name, sizeof (utxent->ut_name)); +#endif /* HAVE_STRUCT_UTMPX_UT_NAME */ + strncpy (utxent->ut_user, name, sizeof (utxent->ut_user)); + if (NULL != hostname) { + struct addrinfo *info = NULL; +#ifdef HAVE_STRUCT_UTMPX_UT_HOST + strncpy (utxent->ut_host, hostname, sizeof (utxent->ut_host)); +#endif /* HAVE_STRUCT_UTMPX_UT_HOST */ +#ifdef HAVE_STRUCT_UTMPX_UT_SYSLEN + utxent->ut_syslen = MIN (strlen (hostname), + sizeof (utxent->ut_host)); +#endif /* HAVE_STRUCT_UTMPX_UT_SYSLEN */ +#if defined(HAVE_STRUCT_UTMPX_UT_ADDR) || defined(HAVE_STRUCT_UTMPX_UT_ADDR_V6) + if (getaddrinfo (hostname, NULL, NULL, &info) == 0) { + /* getaddrinfo might not be reliable. + * Just try to log what may be useful. + */ + if (info->ai_family == AF_INET) { + struct sockaddr_in *sa = + (struct sockaddr_in *) info->ai_addr; +#ifdef HAVE_STRUCT_UTMPX_UT_ADDR + memcpy (utxent->ut_addr, + &(sa->sin_addr), + MIN (sizeof (utxent->ut_addr), + sizeof (sa->sin_addr))); +#endif /* HAVE_STRUCT_UTMPX_UT_ADDR */ +#ifdef HAVE_STRUCT_UTMPX_UT_ADDR_V6 + memcpy (utxent->ut_addr_v6, + &(sa->sin_addr), + MIN (sizeof (utxent->ut_addr_v6), + sizeof (sa->sin_addr))); + } else if (info->ai_family == AF_INET6) { + struct sockaddr_in6 *sa = + (struct sockaddr_in6 *) info->ai_addr; + memcpy (utxent->ut_addr_v6, + &(sa->sin6_addr), + MIN (sizeof (utxent->ut_addr_v6), + sizeof (sa->sin6_addr))); +#endif /* HAVE_STRUCT_UTMPX_UT_ADDR_V6 */ + } + freeaddrinfo (info); + } +#endif /* HAVE_STRUCT_UTMPX_UT_ADDR || HAVE_STRUCT_UTMPX_UT_ADDR_V6 */ + free (hostname); + } + /* ut_exit is only for DEAD_PROCESS */ + utxent->ut_session = getsid (0); + gettimeofday (&tv, NULL); +#ifdef HAVE_STRUCT_UTMPX_UT_TIME + utxent->ut_time = tv.tv_sec; +#endif /* HAVE_STRUCT_UTMPX_UT_TIME */ +#ifdef HAVE_STRUCT_UTMPX_UT_XTIME + utxent->ut_xtime = tv.tv_usec; +#endif /* HAVE_STRUCT_UTMPX_UT_XTIME */ + utxent->ut_tv.tv_sec = tv.tv_sec; + utxent->ut_tv.tv_usec = tv.tv_usec; + + return utxent; +} + +/* + * setutmpx - the UTMPX version for setutmp + */ +int setutmpx (struct utmpx *utx) +{ + int err = 0; + + assert (NULL != utx); + + setutxent (); + if (pututxline (utx) == NULL) { + err = 1; + } + endutxent (); + + updwtmpx (_WTMP_FILE "x", utx); + + return err; +} +#endif /* HAVE_UTMPX_H */ + diff --git a/src/login.c b/src/login.c index 2f4f29a6..1e0a39ed 100644 --- a/src/login.c +++ b/src/login.c @@ -85,14 +85,6 @@ static const char *hostname = ""; static char *username = NULL; static int reason = PW_LOGIN; -#if HAVE_UTMPX_H -extern struct utmpx utxent; -struct utmpx failent; -#else -struct utmp failent; -#endif -extern struct utmp utent; - struct lastlog lastlog; static bool pflg = false; static bool fflg = false; @@ -128,7 +120,7 @@ extern char **environ; static void usage (void); static void setup_tty (void); static void process_flags (int, char *const *); -static const char *get_failent_user (const char *user) +static const char *get_failent_user (const char *user); #ifndef USE_PAM static struct faillog faillog; @@ -494,6 +486,7 @@ int main (int argc, char **argv) static char temp_shell[] = "/bin/sh"; #endif const char *failent_user; + struct utmp *utent; #ifdef USE_PAM int retcode; @@ -523,6 +516,7 @@ int main (int argc, char **argv) exit (1); /* must be a terminal */ } + utent = get_current_utmp (); /* * Be picky if run by normal users (possible if installed setuid * root), but not if run by root. This way it still allows logins @@ -530,7 +524,10 @@ int main (int argc, char **argv) * but users must "exec login" which will use the existing utmp * entry (will not overwrite remote hostname). --marekm */ - checkutmp (!amroot); + if (!amroot && (NULL == utent)) { + (void) puts (_("No utmp entry. You must exec \"login\" from the lowest level \"sh\"")); + exit (1); + } tmptty = ttyname (0); if (NULL == tmptty) { @@ -543,44 +540,12 @@ int main (int argc, char **argv) #endif if (rflg || hflg) { -#ifdef UT_ADDR - struct hostent *he; - - /* - * Fill in the ut_addr field (remote login IP address). XXX - * - login from util-linux does it, but this is not the - * right place to do it. The program that starts login - * (telnetd, rlogind) knows the IP address, so it should - * create the utmp entry and fill in ut_addr. - * gethostbyname() is not 100% reliable (the remote host may - * be unknown, etc.). --marekm - */ - he = gethostbyname (hostname); - if (NULL != he) { - utent.ut_addr = *((int32_t *) (he->h_addr_list[0])); - } -#endif -#ifdef UT_HOST - strncpy (utent.ut_host, hostname, sizeof (utent.ut_host)); -#endif -#if HAVE_UTMPX_H - strncpy (utxent.ut_host, hostname, sizeof (utxent.ut_host)); -#endif /* * Add remote hostname to the environment. I think * (not sure) I saw it once on Irix. --marekm */ addenv ("REMOTEHOST", hostname); } -#ifdef __linux__ - /* - * workaround for init/getty leaving junk in ut_host at least in - * some version of RedHat. --marekm - */ - else if (amroot) { - memzero (utent.ut_host, sizeof utent.ut_host); - } -#endif if (fflg) { preauth_flag = true; } @@ -656,18 +621,11 @@ int main (int argc, char **argv) if (rflg || hflg) { cp = hostname; } else { - /* FIXME: What is the priority: - * UT_HOST or HAVE_UTMPX_H? */ -#ifdef UT_HOST - if ('\0' != utent.ut_host[0]) { - cp = utent.ut_host; +#ifdef HAVE_STRUCT_UTMP_UT_HOST + if ('\0' != utent->ut_host[0]) { + cp = utent->ut_host; } else -#endif -#if HAVE_UTMPX_H - if ('\0' != utxent.ut_host[0]) { - cp = utxent.ut_host; - } else -#endif +#endif /* HAVE_STRUCT_UTMP_UT_HOST */ { cp = ""; } @@ -888,17 +846,17 @@ int main (int argc, char **argv) while (true) { /* repeatedly get login/password pairs */ /* user_passwd is always a pointer to this constant string * or a passwd or shadow password that will be memzero by - * passwd_free / shadow_free. + * pw_free / spw_free. * Do not free() user_passwd. */ const char *user_passwd = "!"; /* Do some cleanup to avoid keeping entries we do not need * anymore. */ if (NULL != pwd) { - passwd_free (pwd); + pw_free (pwd); } if (NULL != spwd) { - shadow_free (spwd); + spw_free (spwd); spwd = NULL; } @@ -1007,26 +965,21 @@ int main (int argc, char **argv) failure (pwd->pw_uid, tty, &faillog); } if (getdef_str ("FTMP_FILE") != NULL) { -#if HAVE_UTMPX_H - failent = utxent; - if (sizeof (failent.ut_tv) == sizeof (struct timeval)) { - gettimeofday ((struct timeval *) &failent.ut_tv, - NULL); - } else { - struct timeval tv; - - gettimeofday (&tv, NULL); - failent.ut_tv.tv_sec = tv.tv_sec; - failent.ut_tv.tv_usec = tv.tv_usec; - } +#ifdef HAVE_UTMPX_H + struct utmpx *failent = + prepare_utmpx (failent_user, + tty, + /* FIXME: or fromhost? */hostname, + utent); #else - failent = utent; - failent.ut_time = time (NULL); + struct utmp *failent = + prepare_utmp (failent_user, + tty, + hostname, + utent); #endif - strncpy (failent.ut_user, failent_user, - sizeof (failent.ut_user)); - failent.ut_type = USER_PROCESS; - failtmp (failent_user, &failent); + failtmp (failent_user, failent); + free (failent); } retries--; @@ -1096,7 +1049,15 @@ int main (int argc, char **argv) addenv ("IFS= \t\n", NULL); /* ... instead, set a safe IFS */ } - setutmp (username, tty, hostname); /* make entry in utmp & wtmp files */ + struct utmp *ut = prepare_utmp (username, tty, hostname, utent); + (void) setutmp (ut); /* make entry in the utmp & wtmp files */ + free (ut); +#ifdef HAVE_UTMPX_H + struct utmpx *utx = prepare_utmpx (username, tty, hostname, utent); + (void) setutmpx (utx); /* make entry in the utmpx & wtmpx files */ + free (utx); +#endif /* HAVE_UTMPX_H */ + if (pwd->pw_shell[0] == '*') { /* subsystem root */ pwd->pw_shell++; /* skip the '*' */ subsystem (pwd); /* figure out what to execute */ @@ -1144,7 +1105,7 @@ int main (int argc, char **argv) * entry for a long time, and there might be other * getxxyy in between. */ - passwd_free (pwd); + pw_free (pwd); pwd = xgetpwnam (username); if (NULL == pwd) { SYSLOG ((LOG_ERR, @@ -1152,7 +1113,7 @@ int main (int argc, char **argv) username)); exit (1); } - shadow_free (spwd); + spw_free (spwd); spwd = xgetspnam (username); } }