diff options
Diffstat (limited to 'lib/utmp.c')
-rw-r--r-- | lib/utmp.c | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/lib/utmp.c b/lib/utmp.c new file mode 100644 index 0000000..08e9b62 --- /dev/null +++ b/lib/utmp.c @@ -0,0 +1,434 @@ +/* + * SPDX-FileCopyrightText: 1989 - 1994, Julianne Frances Haugh + * SPDX-FileCopyrightText: 1996 - 1999, Marek Michałkiewicz + * SPDX-FileCopyrightText: 2001 - 2005, Tomasz Kłoczko + * SPDX-FileCopyrightText: 2008 - 2009, Nicolas François + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <config.h> + +#include "defines.h" +#include "prototypes.h" +#include "getdef.h" + +#include <utmpx.h> +#include <assert.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <stdio.h> +#include <fcntl.h> + +#include "alloc.h" +#include "sizeof.h" +#include "string/strncpy.h" +#include "string/strtcpy.h" +#include "string/zustr2stp.h" + +#ident "$Id$" + + +#define UTX_LINESIZE NITEMS(memberof(struct utmpx, ut_line)) + + +/* + * is_my_tty -- determine if "tty" is the same TTY stdin is using + */ +static bool +is_my_tty(const char tty[UTX_LINESIZE]) +{ + char full_tty[STRLEN("/dev/") + UTX_LINESIZE + 1]; + /* tmptty shall be bigger than full_tty */ + static char tmptty[sizeof(full_tty) + 1]; + + full_tty[0] = '\0'; + if (tty[0] != '/') + strcpy (full_tty, "/dev/"); + strncat(full_tty, tty, UTX_LINESIZE); + + if ('\0' == tmptty[0]) { + const char *tname = ttyname (STDIN_FILENO); + if (NULL != tname) + STRTCPY(tmptty, tname); + } + + if ('\0' == tmptty[0]) { + (void) puts (_("Unable to determine your tty name.")); + exit (EXIT_FAILURE); + } + + return strcmp (full_tty, tmptty) == 0; +} + + +/* + * failtmp - update the cumulative failure log + * + * failtmp updates the (struct utmpx) formatted failure log which + * maintains a record of all login failures. + */ +static void +failtmp(const char *username, const struct utmpx *failent) +{ + const char *ftmp; + int fd; + + /* + * Get the name of the failure file. If no file has been defined + * in login.defs, don't do this. + */ + + ftmp = getdef_str ("FTMP_FILE"); + if (NULL == ftmp) { + return; + } + + /* + * Open the file for append. It must already exist for this + * feature to be used. + */ + + fd = open (ftmp, O_WRONLY | O_APPEND); + if (-1 == fd) { + if (errno != ENOENT) { + SYSLOG ((LOG_WARN, + "Can't append failure of user %s to %s: %m", + username, ftmp)); + } + return; + } + + /* + * Append the new failure record and close the log file. + */ + + if (write_full(fd, failent, sizeof *failent) == -1) { + goto err_write; + } + + if (close (fd) != 0 && errno != EINTR) { + goto err_close; + } + + return; + +err_write: + { + int saved_errno = errno; + (void) close (fd); + errno = saved_errno; + } +err_close: + SYSLOG ((LOG_WARN, + "Can't append failure of user %s to %s: %m", + username, ftmp)); +} + + +/* + * get_current_utmp - return the most probable utmp entry for the current + * session + * + * The utmp file is scanned for an entry with the same process ID. + * The line entered by the *getty / telnetd, etc. should also match + * the current terminal. + * + * When an entry is returned by get_current_utmp, and if the utmpx + * 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. + */ +static /*@null@*/ /*@only@*/struct utmpx * +get_current_utmp(void) +{ + struct utmpx *ut; + struct utmpx *ret = NULL; + + setutxent(); + + /* First, try to find a valid utmp entry for this process. */ + while ((ut = getutxent()) != NULL) { + if ( (ut->ut_pid == getpid ()) + && ('\0' != ut->ut_id[0]) + && ( (LOGIN_PROCESS == ut->ut_type) + || (USER_PROCESS == ut->ut_type)) + /* A process may have failed to close an entry + * Check if this entry refers to the current tty */ + && is_my_tty(ut->ut_line)) + { + break; + } + } + + if (NULL != ut) { + ret = XMALLOC(1, struct utmpx); + memcpy (ret, ut, sizeof (*ret)); + } + + endutxent(); + + return ret; +} + + +int +get_session_host(char **out) +{ + int ret = 0; + struct utmpx *ut; + + ut = get_current_utmp(); + +#if defined(HAVE_STRUCT_UTMPX_UT_HOST) + if ((ut != NULL) && (ut->ut_host[0] != '\0')) { + char *hostname; + + hostname = XMALLOC(sizeof(ut->ut_host) + 1, char); + ZUSTR2STP(hostname, ut->ut_host); + *out = hostname; + free (ut); + } else { + *out = NULL; + ret = -2; + } +#else + *out = NULL; + ret = -2; +#endif + + return ret; +} + + +#if !defined(USE_PAM) && !defined(HAVE_UPDWTMPX) +/* + * Some systems already have updwtmpx(). Others + * don't, so we re-implement these functions if necessary. + */ +static void +updwtmpx(const char *filename, const struct utmpx *ut) +{ + int fd; + + fd = open (filename, O_APPEND | O_WRONLY, 0); + if (fd >= 0) { + write_full(fd, ut, sizeof(*ut)); + close (fd); + } +} +#endif + + +/* + * prepare_utmp - prepare an utmp entry so that it can be logged in a + * utmp/wtmp file. + * + * It accepts 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 + * If ut is NULL, ut_id will be forged based on the line argument. + * + * The ut_host field of the input structure may also be kept, and is + * used 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. + */ +static /*@only@*/struct utmpx * +prepare_utmp(const char *name, const char *line, const char *host, + /*@null@*/const struct utmpx *ut) +{ + char *hostname = NULL; + struct utmpx *utent; + struct timeval tv; + + assert (NULL != name); + assert (NULL != line); + + + + if ( (NULL != host) + && ('\0' != host[0])) { + hostname = XMALLOC(strlen(host) + 1, char); + strcpy (hostname, host); +#if defined(HAVE_STRUCT_UTMPX_UT_HOST) + } else if ( (NULL != ut) + && ('\0' != ut->ut_host[0])) { + hostname = XMALLOC(NITEMS(ut->ut_host) + 1, char); + ZUSTR2STP(hostname, ut->ut_host); +#endif + } + + if (strncmp(line, "/dev/", 5) == 0) { + line += 5; + } + + + utent = XCALLOC(1, struct utmpx); + + + utent->ut_type = USER_PROCESS; + utent->ut_pid = getpid (); + STRNCPY(utent->ut_line, line); + if (NULL != ut) { + STRNCPY(utent->ut_id, ut->ut_id); + } else { + /* XXX - assumes /dev/tty?? */ + STRNCPY(utent->ut_id, line + 3); + } +#if defined(HAVE_STRUCT_UTMPX_UT_NAME) + STRNCPY(utent->ut_name, name); +#endif + STRNCPY(utent->ut_user, name); + if (NULL != hostname) { + struct addrinfo *info = NULL; +#if defined(HAVE_STRUCT_UTMPX_UT_HOST) + STRNCPY(utent->ut_host, hostname); +#endif +#if defined(HAVE_STRUCT_UTMPX_UT_SYSLEN) + utent->ut_syslen = MIN (strlen (hostname), + sizeof (utent->ut_host)); +#endif +#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; +# if defined(HAVE_STRUCT_UTMPX_UT_ADDR) + memcpy (&(utent->ut_addr), + &(sa->sin_addr), + MIN (sizeof (utent->ut_addr), + sizeof (sa->sin_addr))); +# endif +# if defined(HAVE_STRUCT_UTMPX_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 + } + freeaddrinfo (info); + } +#endif + free (hostname); + } + /* ut_exit is only for DEAD_PROCESS */ + utent->ut_session = getsid (0); + if (gettimeofday (&tv, NULL) == 0) { +#if defined(HAVE_STRUCT_UTMPX_UT_TIME) + utent->ut_time = tv.tv_sec; +#endif +#if defined(HAVE_STRUCT_UTMPX_UT_XTIME) + utent->ut_xtime = tv.tv_usec; +#endif + utent->ut_tv.tv_sec = tv.tv_sec; + utent->ut_tv.tv_usec = tv.tv_usec; + } + + return utent; +} + + +/* + * setutmp - Update an entry in utmp and log an entry in wtmp + * + * Return 1 on failure and 0 on success. + */ +static int +setutmp(struct utmpx *ut) +{ + int err = 0; + + assert (NULL != ut); + + setutxent(); + if (pututxline(ut) == NULL) { + err = 1; + } + endutxent(); + +#if !defined(USE_PAM) + /* This is done by pam_lastlog */ + updwtmpx(_WTMP_FILE, ut); +#endif + + return err; +} + + +int +update_utmp(const char *user, const char *tty, const char *host) +{ + struct utmpx *utent, *ut; + + utent = get_current_utmp (); + ut = prepare_utmp (user, tty, host, utent); + + (void) setutmp (ut); /* make entry in the utmp & wtmp files */ + + free(utent); + free(ut); + + return 0; +} + + +void +record_failure(const char *failent_user, const char *tty, const char *hostname) +{ + struct utmpx *utent, *failent; + + if (getdef_str ("FTMP_FILE") != NULL) { + utent = get_current_utmp (); + failent = prepare_utmp (failent_user, tty, hostname, utent); + failtmp (failent_user, failent); + free (utent); + free (failent); + } +} + + +unsigned long +active_sessions_count(const char *name, unsigned long limit) +{ + struct utmpx *ut; + unsigned long count = 0; + + setutxent(); + while ((ut = getutxent())) + { + if (USER_PROCESS != ut->ut_type) { + continue; + } + if ('\0' == ut->ut_user[0]) { + continue; + } + if (strncmp (name, ut->ut_user, sizeof (ut->ut_user)) != 0) { + continue; + } + count++; + if (count > limit) { + break; + } + } + endutxent(); + + return count; +} |