From 1d7b9b20ec4aa54ef1822cb776166bca41c25d54 Mon Sep 17 00:00:00 2001 From: andre Date: Sat, 3 Jun 2000 14:57:40 +0000 Subject: [PATCH] Added new login recording code Added test program for login code (make logintest) --- Makefile.in | 8 +- acconfig.h | 44 +- bsd-login.c | 195 ------- bsd-login.h | 22 - configure.in | 268 +++++++-- defines.h | 125 +++-- login.c | 275 +-------- loginrec.c | 1385 ++++++++++++++++++++++++++++++++++++++++++++++ loginrec.h | 167 ++++++ logintest.c | 307 ++++++++++ openbsd-compat.h | 1 - 11 files changed, 2211 insertions(+), 586 deletions(-) delete mode 100644 bsd-login.c delete mode 100644 bsd-login.h create mode 100644 loginrec.c create mode 100644 loginrec.h create mode 100644 logintest.c diff --git a/Makefile.in b/Makefile.in index 584f3054..0a2a2cfc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -40,7 +40,7 @@ LIBOPENBSD_COMPAT_OBJS=bsd-base64.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o b SSHOBJS= ssh.o sshconnect.o sshconnect1.o sshconnect2.o log-client.o readconf.o clientloop.o -SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-rhosts.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o serverloop.o bsd-login.o md5crypt.o session.o +SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-rhosts.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o loginrec.o servconf.o serverloop.o md5crypt.o session.o TROFFMAN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh.1 sshd.8 CATMAN = scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh.0 sshd.0 @@ -86,11 +86,15 @@ ssh-agent: libopenbsd-compat.a libssh.a ssh-agent.o log-client.o ssh-keygen: libopenbsd-compat.a libssh.a ssh-keygen.o log-client.o $(LD) -o $@ ssh-keygen.o log-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) +# test driver for the loginrec code - not built by default +logintest: logintest.o libopenbsd-compat.a libssh.a log-client.o loginrec.o + $(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh log-client.o $(LIBS) + $(MANPAGES) $(CONFIGFILES):: $(FIXPATHSCMD) $(srcdir)/$@ clean: - rm -f *.o *.a $(TARGETS) config.cache config.log + rm -f *.o *.a $(TARGETS) logintest config.cache config.log rm -f *.out core distclean: clean diff --git a/acconfig.h b/acconfig.h index 308919f9..8720dd66 100644 --- a/acconfig.h +++ b/acconfig.h @@ -52,10 +52,50 @@ #undef HAVE_TYPE_IN_UTMP #undef HAVE_TYPE_IN_UTMPX #undef HAVE_TV_IN_UTMP +#undef HAVE_TV_IN_UTMPX #undef HAVE_ID_IN_UTMP +#undef HAVE_EXIT_IN_UTMP +#undef HAVE_TIME_IN_UTMP +#undef HAVE_TIME_IN_UTMPX -/* Define if you want to use utmpx */ -#undef USE_UTMPX +/* Define if you don't want to use your system's login() call */ +#undef DISABLE_LOGIN + +/* Define if you don't want to use pututline() etc. to write [uw]tmp */ +#undef DISABLE_PUTUTLINE + +/* Define if you don't want to use pututxline() etc. to write [uw]tmpx */ +#undef DISABLE_PUTUTXLINE + +/* Define if you don't want to use lastlog */ +#undef DISABLE_LASTLOG + +/* Define if you don't want to use utmp */ +#undef DISABLE_UTMP + +/* Define if you don't want to use utmpx */ +#undef DISABLE_UTMPX + +/* Define if you don't want to use wtmp */ +#undef DISABLE_WTMP + +/* Define if you don't want to use wtmpx */ +#undef DISABLE_WTMPX + +/* Define if you want to specify the path to your lastlog file */ +#undef CONF_LASTLOG_FILE + +/* Define if you want to specify the path to your utmp file */ +#undef CONF_UTMP_FILE + +/* Define if you want to specify the path to your wtmp file */ +#undef CONF_WTMP_FILE + +/* Define if you want to specify the path to your utmpx file */ +#undef CONF_UTMPX_FILE + +/* Define if you want to specify the path to your wtmpx file */ +#undef CONF_WTMPX_FILE /* Define is libutil has login() function */ #undef HAVE_LIBUTIL_LOGIN diff --git a/bsd-login.c b/bsd-login.c deleted file mode 100644 index a6f4acca..00000000 --- a/bsd-login.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * This file has been heavily modified from the original OpenBSD version - */ - -/* $OpenBSD: login.c,v 1.5 1998/07/13 02:11:12 millert Exp $ */ -/* - * Copyright (c) 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" -#ifndef HAVE_LOGIN - -#include - -#if defined(LIBC_SCCS) && !defined(lint) -/* from: static char sccsid[] = "@(#)login.c 8.1 (Berkeley) 6/4/93"; */ -static char *rcsid = "$OpenBSD: login.c,v 1.5 1998/07/13 02:11:12 millert Exp $"; -#endif /* LIBC_SCCS and not lint */ - -#include - -#include -#include -#include -#if defined(HAVE_UTMPX_H) && defined(USE_UTMPX) -# include -#endif -#ifdef HAVE_UTMP_H -# include -#endif -#include -#include - -#ifdef USER_PROCESS -/* - * find first matching slot in utmp, or "-1" for none - * - * algorithm: for USER_PROCESS, check tty name - * for DEAD_PROCESS, check PID and tty name - * - */ -int find_tty_slot( utp ) -struct utmp * utp; -{ - int t = 0; - struct utmp * u; - -# if defined(HAVE_TYPE_IN_UTMP) || defined(HAVE_TYPE_IN_UTMPX) - setutent(); - - while((u = getutent()) != NULL) { - if (utp->ut_type == USER_PROCESS && - (strncmp(utp->ut_line, u->ut_line, sizeof(utp->ut_line)) == 0)) { - endutent(); - return(t); - } - - if ((utp->ut_type == DEAD_PROCESS) && (utp->ut_pid == u->ut_pid) && - (strncmp(utp->ut_line, u->ut_line, sizeof(utp->ut_line)) == 0 )) { - endutent(); - return(t); - } - t++; - } - - endutent(); -# endif /* defined(HAVE_TYPE_IN_UTMP) || defined(HAVE_TYPE_IN_UTMPX) */ - return(-1); -} -#else /* USER_PROCESS */ -int find_tty_slot(struct utmp *utp) -{ - return(ttyslot()); -} -#endif /* USER_PROCESS */ - -#if defined(HAVE_UTMPX_H) && defined(USE_UTMPX) -void login(struct utmpx *utx) -#else /* defined(HAVE_UTMPX_H) && defined(USE_UTMPX) */ -void login(struct utmp *utp) -#endif /* defined(HAVE_UTMPX_H) && defined(USE_UTMPX) */ -{ - /* Use proper API if we have it */ -#if defined(USE_UTMPX) -# if defined(HAVE_PUTUTXLINE) - setutxent(); - pututxline(utx); - endutxent(); -# endif /* defined(HAVE_PUTUTXLINE) */ -# if defined(HAVE_UPDWTMPX) - updwtmpx(_PATH_WTMPX, utx); -# endif /* defined(HAVE_UPDWTMPX) */ -#else /* defined(USE_UTMPX) */ -# if defined(HAVE_PUTUTLINE) - setutent(); - pututline(utp); - endutent(); -# endif /* defined(HAVE_PUTUTLINE) */ -# if defined(HAVE_UPDWTMPX) - updwtmp(_PATH_WTMP, utp); -# endif /* defined(HAVE_UPDWTMP) */ -#endif /* defined(USE_UTMPX) */ - - /* Otherwise DIY */ -#if (defined(USE_UTMPX) && !defined(HAVE_PUTUTXLINE)) || \ - (!defined(USE_UTMPX) && !defined(HAVE_PUTUTLINE)) - int fd; - int tty; - - /* can't use ttyslot here, as that will not work for logout - * (record_logout() is called from the master sshd, which does - * not have the correct tty on stdin/out, so ttyslot will return - * "-1" or (worse) a wrong number - */ - tty = find_tty_slot(utp); - -#ifdef USE_UTMPX - /* If no tty was found, append it to utmpx */ - if (tty == -1) { - if ((fd = open(_PATH_UTMPX, O_WRONLY|O_APPEND, 0)) >= 0) { - (void)write(fd, utp, sizeof(struct utmp)); - (void)close(fd); - return; - } - } - /* Otherwise, tty was found - update at its location */ - fd = open(_PATH_UTMPX, O_RDWR|O_CREAT, 0644); - if (fd == -1) { - log("Couldn't open %s: %s", _PATH_UTMPX, strerror(errno)); - return; - } - lseek(fd, (off_t)(tty * sizeof(struct utmpx)), SEEK_SET); - write(fd, utx, sizeof(struct utmpx)); - close(fd); - if ((fd = open(_PATH_WTMPX, O_WRONLY|O_APPEND, 0)) >= 0) { - (void)write(fd, utx, sizeof(struct utmpx)); - (void)close(fd); - } -#else /* USE_UTMPX */ - /* If no tty was found, append it to utmp */ - if (tty == -1) { - if ((fd = open(_PATH_UTMP, O_WRONLY|O_APPEND, 0)) >= 0) { - (void)write(fd, utp, sizeof(struct utmp)); - (void)close(fd); - return; - } - } - /* Otherwise, tty was found - update at its location */ - fd = open(_PATH_UTMP, O_RDWR|O_CREAT, 0644); - if (fd == -1) { - log("Couldn't open %s: %s", _PATH_UTMP, strerror(errno)); - return; - } - lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); - write(fd, utp, sizeof(struct utmp)); - close(fd); - if ((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) >= 0) { - (void)write(fd, utp, sizeof(struct utmp)); - (void)close(fd); - } -#endif /* USE_UTMPX */ -#endif /* (defined(USE_UTMPX) && !defined(HAVE_PUTUTXLINE)) || \ - (!defined(USE_UTMPX) && !defined(HAVE_PUTUTLINE)) */ -} - -#endif /* HAVE_LOGIN */ diff --git a/bsd-login.h b/bsd-login.h deleted file mode 100644 index f26f4708..00000000 --- a/bsd-login.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _BSD_LOGIN_H -# define _BSD_LOGIN_H - -# include "config.h" -# ifndef HAVE_LOGIN - -# include - -# if defined(HAVE_UTMPX_H) && defined(USE_UTMPX) -# include - -void login(struct utmp *utp, struct utmpx *utx); - -# else /* defined(HAVE_UTMPX_H) && defined(USE_UTMPX) */ - -void login(struct utmp *utp); - -# endif /* defined(HAVE_UTMPX_H) && defined(USE_UTMPX) */ - -# endif /* !HAVE_LOGIN */ - -#endif /* _BSD_LOGIN_H */ diff --git a/configure.in b/configure.in index 86284aa2..8bb647c1 100644 --- a/configure.in +++ b/configure.in @@ -43,7 +43,6 @@ case "$host" in fi CFLAGS="$CFLAGS -D_HPUX_SOURCE" AC_DEFINE(IPADDR_IN_DISPLAY) - AC_DEFINE(USE_UTMPX) AC_MSG_CHECKING(for HPUX trusted system password database) if test -f /tcb/files/auth/system/default; then AC_MSG_RESULT(yes) @@ -63,7 +62,6 @@ case "$host" in fi CFLAGS="$CFLAGS -D_HPUX_SOURCE" AC_DEFINE(IPADDR_IN_DISPLAY) - AC_DEFINE(USE_UTMPX) AC_MSG_CHECKING(for HPUX trusted system password database) if test -f /tcb/files/auth/system/default; then AC_MSG_RESULT(yes) @@ -104,7 +102,8 @@ case "$host" in CFLAGS="$CFLAGS -I/usr/local/include" LDFLAGS="$LDFLAGS -L/usr/local/lib -R/usr/local/lib -L/usr/ucblib -R/usr/ucblib" need_dash_r=1 - AC_DEFINE(USE_UTMPX) + # hardwire lastlog location (can't detect it on some versions) + conf_lastlog_location="/var/adm/lastlog" ;; *-*-sunos4*) CFLAGS="$CFLAGS -DSUNOS4" @@ -113,7 +112,6 @@ case "$host" in *-*-sysv*) CFLAGS="$CFLAGS -I/usr/local/include" LDFLAGS="$LDFLAGS -L/usr/local/lib" - AC_DEFINE(USE_UTMPX) MANTYPE='$(CATMAN)' mansubdir=cat LIBS="$LIBS -lgen -lsocket" @@ -132,10 +130,20 @@ if test -z "$no_libnsl" ; then fi # Checks for header files. -AC_CHECK_HEADERS(bstring.h endian.h lastlog.h login.h maillock.h netdb.h netgroup.h netinet/in_systm.h paths.h poll.h pty.h shadow.h security/pam_appl.h sys/bitypes.h sys/bsdtty.h sys/cdefs.h sys/poll.h sys/select.h sys/stropts.h sys/sysmacros.h sys/time.h sys/ttcompat.h stddef.h util.h utmp.h utmpx.h) +AC_CHECK_HEADERS(bstring.h endian.h lastlog.h login.h maillock.h netdb.h netgroup.h netinet/in_systm.h paths.h poll.h pty.h shadow.h security/pam_appl.h sys/bitypes.h sys/bsdtty.h sys/cdefs.h sys/poll.h sys/select.h sys/stropts.h sys/sysmacros.h sys/time.h sys/ttcompat.h stddef.h time.h util.h utmp.h utmpx.h) # Checks for library functions. -AC_CHECK_FUNCS(arc4random atexit b64_ntop bcopy bindresvport_af clock freeaddrinfo gai_strerror getaddrinfo getnameinfo getrusage innetgr md5_crypt memmove mkdtemp on_exit openpty pututline pututxline rresvport_af setenv seteuid setlogin setproctitle setreuid snprintf strlcat strlcpy updwtmp updwtmpx vsnprintf vhangup _getpty __b64_ntop) +AC_CHECK_FUNCS(arc4random atexit b64_ntop bcopy bindresvport_af clock freeaddrinfo gai_strerror getaddrinfo getnameinfo getrusage innetgr md5_crypt memmove mkdtemp on_exit openpty rresvport_af setenv seteuid setlogin setproctitle setreuid snprintf strlcat strlcpy vsnprintf vhangup _getpty __b64_ntop) +dnl checks for time functions +AC_CHECK_FUNCS(gettimeofday time) +dnl checks for libutil functions +AC_CHECK_FUNCS(login logout updwtmp logwtmp) +dnl checks for utmp functions +AC_CHECK_FUNCS(entutent getutent getutid getutline pututline setutent) +AC_CHECK_FUNCS(utmpname) +dnl checks for utmpx functions +AC_CHECK_FUNCS(entutxent getutxent getutxid getutxline pututxline ) +AC_CHECK_FUNCS(setutxent utmpxname) AC_CHECK_FUNC(login, [AC_DEFINE(HAVE_LOGIN)], @@ -501,6 +509,11 @@ OSSH_CHECK_HEADER_FOR_FIELD(ut_addr, utmp.h, HAVE_ADDR_IN_UTMP) OSSH_CHECK_HEADER_FOR_FIELD(ut_addr, utmpx.h, HAVE_ADDR_IN_UTMPX) OSSH_CHECK_HEADER_FOR_FIELD(ut_addr_v6, utmp.h, HAVE_ADDR_V6_IN_UTMP) OSSH_CHECK_HEADER_FOR_FIELD(ut_addr_v6, utmpx.h, HAVE_ADDR_V6_IN_UTMPX) +OSSH_CHECK_HEADER_FOR_FIELD(ut_exit, utmp.h, HAVE_EXIT_IN_UTMP) +OSSH_CHECK_HEADER_FOR_FIELD(ut_time, utmp.h, HAVE_TIME_IN_UTMP) +OSSH_CHECK_HEADER_FOR_FIELD(ut_time, utmpx.h, HAVE_TIME_IN_UTMPX) +OSSH_CHECK_HEADER_FOR_FIELD(ut_tv, utmpx.h, HAVE_TV_IN_UTMPX) + AC_CACHE_CHECK([for ss_family field in struct sockaddr_storage], @@ -590,48 +603,6 @@ if test ! -z "$MAIL" ; then AC_DEFINE_UNQUOTED(MAIL_DIRECTORY, "$maildir") fi -# Look for lastlog location -AC_ARG_WITH(lastlog, - [ --with-lastlog=FILE Location of lastlog file], - [ - if test "x$withval" = "xno" ; then - AC_DEFINE(DISABLE_LASTLOG) - else - AC_DEFINE_UNQUOTED(LASTLOG_LOCATION, "$withval") - fi - ], - [ - AC_MSG_CHECKING([location of lastlog file]) - for lastlog in /var/log/lastlog /var/adm/lastlog /usr/adm/lastlog /etc/security/lastlog ; do - if test -f $lastlog ; then - gotlastlog="file" - break - fi - if test -d $lastlog ; then - gotlastlog="dir" - break - fi - done - if test -z "$gotlastlog" ; then - AC_MSG_RESULT(not found) - nolastlog=1 - else - if test "x$gotlastlog" = "xdir" ; then - AC_MSG_RESULT(${lastlog}/) - AC_DEFINE(LASTLOG_IS_DIR) - else - AC_MSG_RESULT($lastlog) - AC_DEFINE_UNQUOTED(LASTLOG_LOCATION, "$lastlog") - fi - fi - ] -) - -if test ! -z "$nolastlog" ; then - AC_MSG_WARN([*** Disabling lastlog support *** ]) - AC_DEFINE(DISABLE_LASTLOG) -fi - if test -z "$no_dev_ptmx" ; then AC_CHECK_FILE("/dev/ptmx", [ @@ -838,16 +809,6 @@ AC_ARG_WITH(md5-passwords, ] ) -# Check whether to enable utmpx support -AC_ARG_WITH(utmpx, - [ --with-utmpx Enable utmpx support], - [ - if test "x$withval" != "xno" ; then - AC_DEFINE(USE_UTMPX) - fi - ] -) - # Whether to disable shadow password support AC_ARG_WITH(shadow, [ --without-shadow Disable shadow password support], @@ -922,6 +883,197 @@ AC_ARG_WITH(pid-dir, AC_DEFINE_UNQUOTED(PIDDIR, "$piddir") AC_SUBST(piddir) +dnl allow user to disable some login recording features +AC_ARG_ENABLE(lastlog, + [ --disable-lastlog disable use of lastlog even if detected [no]], + [ AC_DEFINE(DISABLE_LASTLOG) ] +) +AC_ARG_ENABLE(utmp, + [ --disable-utmp disable use of utmp even if detected [no]], + [ AC_DEFINE(DISABLE_UTMP) ] +) +AC_ARG_ENABLE(utmpx, + [ --disable-utmpx disable use of utmpx even if detected [no]], + [ AC_DEFINE(DISABLE_UTMPX) ] +) +AC_ARG_ENABLE(wtmp, + [ --disable-wtmp disable use of wtmp even if detected [no]], + [ AC_DEFINE(DISABLE_WTMP) ] +) +AC_ARG_ENABLE(wtmpx, + [ --disable-wtmpx disable use of wtmpx even if detected [no]], + [ AC_DEFINE(DISABLE_WTMPX) ] +) +AC_ARG_ENABLE(libutil, + [ --disable-libutil disable use of libutil (login() etc.) [no]], + [ AC_DEFINE(DISABLE_LOGIN) ] +) +AC_ARG_ENABLE(pututline, + [ --disable-pututline disable use of pututline() etc. ([uw]tmp) [no]], + [ AC_DEFINE(DISABLE_PUTUTLINE) ] +) +AC_ARG_ENABLE(pututxline, + [ --disable-pututxline disable use of pututxline() etc. ([uw]tmpx) [no]], + [ AC_DEFINE(DISABLE_PUTUTXLINE) ] +) +AC_ARG_WITH(lastlog, + [ --with-lastlog=FILE|DIR specify lastlog location [common locations]], + [ conf_lastlog_location="$withval"; ],) + +dnl lastlog, [uw]tmpx? detection +dnl NOTE: set the paths in the platform section to avoid the +dnl need for command-line parameters +dnl lastlog and [uw]tmp are subject to a file search if all else fails + +dnl lastlog detection +dnl NOTE: the code itself will detect if lastlog is a directory +AC_MSG_CHECKING([if your system defines LASTLOG_FILE]) +AC_TRY_COMPILE([ +#include +#include +#ifdef HAVE_LASTLOG_H +# include +#endif +#ifdef PATHS_H +# include +#endif + ], + [ char *lastlog = LASTLOG_FILE; ], + [ AC_MSG_RESULT(yes) ], + [ AC_MSG_RESULT(no) + system_lastlog_path=no ] +) +if test -z "$conf_lastlog_location"; then + if test x"$system_lastlog_path" = x"no" ; then + for f in /var/log/lastlog /usr/adm/lastlog /var/adm/lastlog /etc/security/lastlog ; do + if test -e $f ; then + conf_lastlog_location=$f + fi + done + if test -z "$conf_lastlog_location"; then + AC_MSG_WARN([** Cannot find lastlog - disabling feature **]) + AC_DEFINE(DISABLE_LASTLOG) + fi + fi +fi + +if test -n "$conf_lastlog_location"; then + AC_DEFINE_UNQUOTED(CONF_LASTLOG_FILE, "$conf_lastlog_location") +fi + +dnl utmp detection +AC_MSG_CHECKING([if your system defines UTMP_FILE]) +AC_TRY_COMPILE([ +#include +#include +#ifdef PATHS_H +# include +#endif + ], + [ char *utmp = UTMP_FILE; ], + [ AC_MSG_RESULT(yes) ], + [ AC_MSG_RESULT(no) + system_utmp_path=no ] +) +if test -z "$conf_utmp_location"; then + if test x"$system_utmp_path" = x"no" ; then + for f in /etc/utmp /usr/adm/utmp /var/run/utmp; do + if test -f $f ; then + conf_utmp_location=$f + fi + done + if test -z "$conf_utmp_location"; then + AC_DEFINE(DISABLE_UTMP) + fi + fi +fi +if test -n "$conf_utmp_location"; then + AC_DEFINE_UNQUOTED(CONF_UTMP_FILE, "$conf_utmp_location") +fi + +dnl wtmp detection +AC_MSG_CHECKING([if your system defines WTMP_FILE]) +AC_TRY_COMPILE([ +#include +#include +#ifdef PATHS_H +# include +#endif + ], + [ char *wtmp = WTMP_FILE; ], + [ AC_MSG_RESULT(yes) ], + [ AC_MSG_RESULT(no) + system_wtmp_path=no ] +) +if test -z "$conf_wtmp_location"; then + if test x"$system_wtmp_path" = x"no" ; then + for f in /usr/adm/wtmp /var/log/wtmp; do + if test -f $f ; then + conf_wtmp_location=$f + fi + done + if test -z "$conf_wtmp_location"; then + AC_DEFINE(DISABLE_WTMP) + fi + fi +fi +if test -n "$conf_wtmp_location"; then + AC_DEFINE_UNQUOTED(CONF_WTMP_FILE, "$conf_wtmp_location") +fi + + +dnl utmpx detection - I don't know any system so perverse as to require +dnl utmpx, but not define UTMPX_FILE (ditto wtmpx.) No doubt it's out +dnl there, though. +AC_MSG_CHECKING([if your system defines UTMPX_FILE]) +AC_TRY_COMPILE([ +#include +#include +#ifdef HAVE_UTMPX_H +#include +#endif +#ifdef PATHS_H +# include +#endif + ], + [ char *utmpx = UTMPX_FILE; ], + [ AC_MSG_RESULT(yes) ], + [ AC_MSG_RESULT(no) + system_utmpx_path=no ] +) +if test -z "$conf_utmpx_location"; then + if test x"$system_utmpx_path" = x"no" ; then + AC_DEFINE(DISABLE_UTMPX) + fi +else + AC_DEFINE_UNQUOTED(CONF_UTMPX_FILE, "$conf_utmpx_location") +fi + +dnl wtmpx detection +AC_MSG_CHECKING([if your system defines WTMPX_FILE]) +AC_TRY_COMPILE([ +#include +#include +#ifdef HAVE_UTMPX_H +#include +#endif +#ifdef PATHS_H +# include +#endif + ], + [ char *wtmpx = WTMPX_FILE; ], + [ AC_MSG_RESULT(yes) ], + [ AC_MSG_RESULT(no) + system_wtmpx_path=no ] +) +if test -z "$conf_wtmpx_location"; then + if test x"$system_wtmpx_path" = x"no" ; then + AC_DEFINE(DISABLE_WTMPX) + fi +else + AC_DEFINE_UNQUOTED(CONF_WTMPX_FILE, "$conf_wtmpx_location") +fi + # Change default command timeout for builtin PRNG entropy_timeout=100 diff --git a/defines.h b/defines.h index ef913098..52f6c9f0 100644 --- a/defines.h +++ b/defines.h @@ -19,14 +19,6 @@ # include /* For _PATH_XXX */ #endif -#ifdef HAVE_UTMP_H -# include /* For _PATH_XXX */ -#endif - -#if defined(HAVE_UTMPX_H) && defined(USE_UTMPX) -# include /* For _PATH_XXX */ -#endif - #ifdef HAVE_SYS_TIME_H # include /* For timersub */ #endif @@ -161,47 +153,6 @@ typedef int ssize_t; /* Paths */ -/* If _PATH_LASTLOG is not defined by system headers, set it to the */ -/* lastlog file detected by autoconf */ -#ifndef _PATH_LASTLOG -# ifdef LASTLOG_LOCATION -# define _PATH_LASTLOG LASTLOG_LOCATION -# endif -#endif - -#ifndef _PATH_UTMP -# ifdef UTMP_FILE -# define _PATH_UTMP UTMP_FILE -# else -# define _PATH_UTMP "/var/adm/utmp" -# endif -#endif - -#ifndef _PATH_WTMP -# ifdef WTMP_FILE -# define _PATH_WTMP WTMP_FILE -# else -# define _PATH_WTMP "/var/adm/wtmp" -# endif -#endif - -#if defined(HAVE_UTMPX_H) && defined(USE_UTMPX) -# ifndef _PATH_UTMPX -# ifdef UTMPX_FILE -# define _PATH_UTMPX UTMPX_FILE -# else -# define _PATH_UTMPX "/var/adm/utmpx" -# endif -# endif -# ifndef _PATH_WTMPX -# ifdef WTMPX_FILE -# define _PATH_WTMPX WTMPX_FILE -# else -# define _PATH_WTMPX "/var/adm/wtmp" -# endif -# endif -#endif - #ifndef _PATH_BSHELL # define _PATH_BSHELL "/bin/sh" #endif @@ -297,4 +248,80 @@ typedef int ssize_t; # define atexit(a) on_exit(a) #endif /* !defined(HAVE_ATEXIT) && defined(HAVE_ON_EXIT) */ +/** + ** login recorder definitions + **/ + +/* preprocess */ + +#ifdef HAVE_UTMP_H +# ifdef HAVE_TIME_IN_UTMP +# include +# endif +# include +#endif +#ifdef HAVE_UTMPX_H +# ifdef HAVE_TV_IN_UTMPX +# include +# endif +# include +#endif +#ifdef HAVE_LASTLOG_H +# include +#endif +#ifdef HAVE_PATHS_H +# include +#endif + +/* FIXME: put default paths back in */ +#if !defined(UTMP_FILE) && defined(_PATH_UTMP) +# define UTMP_FILE _PATH_UTMP +#endif +#if !defined(WTMP_FILE) && defined(_PATH_WTMP) +# define WTMP_FILE _PATH_WTMP +#endif +/* pick up the user's location for lastlog if given */ +#ifdef CONF_LASTLOG_FILE +# define LASTLOG_FILE CONF_LASTLOG_FILE +#endif +#if !defined(LASTLOG_FILE) && defined(_PATH_LASTLOG) +# define LASTLOG_FILE _PATH_LASTLOG +#endif + + +/* The login() library function in libutil is first choice */ +#if defined(HAVE_LOGIN) && !defined(DISABLE_LOGIN) +# define USE_LOGIN + +#else +/* Simply select your favourite login types. */ +/* Can't do if-else because some systems use several... */ +# if defined(UTMPX_FILE) && !defined(DISABLE_UTMPX) +# define USE_UTMPX +# endif +# if defined(UTMP_FILE) && !defined(DISABLE_UTMP) +# define USE_UTMP +# endif +# if defined(WTMPX_FILE) && !defined(DISABLE_WTMPX) +# define USE_WTMPX +# endif +# if defined(WTMP_FILE) && !defined(DISABLE_WTMP) +# define USE_WTMP +# endif + +#endif + +/* I hope that the presence of LASTLOG_FILE is enough to detect this */ +#if defined(LASTLOG_FILE) && !defined(DISABLE_LASTLOG) +# define USE_LASTLOG +#endif + +/* which type of time to use? (api.c) */ +#ifdef HAVE_SYS_TIME_H +# define USE_TIMEVAL +#endif + +/** end of login recorder definitions */ + + #endif /* _DEFINES_H */ diff --git a/login.c b/login.c index a1a33614..10b638d8 100644 --- a/login.c +++ b/login.c @@ -20,28 +20,7 @@ #include "includes.h" RCSID("$Id$"); -#if defined(HAVE_UTMPX_H) && defined(USE_UTMPX) -# include -#endif -#ifdef HAVE_UTMP_H -# include -#endif -#include "ssh.h" - -#ifdef HAVE_UTIL_H -# include -#endif -#ifdef HAVE_LASTLOG_H -# include -#endif -#ifdef HAVE_LOGIN_H -# include -#endif - -#ifdef WITH_AIXAUTHENTICATE -/* This is done in do_authentication */ -# define DISABLE_LASTLOG -#endif /* WITH_AIXAUTHENTICATE */ +#include "loginrec.h" /* * Returns the time when the user last logged in. Returns 0 if the @@ -49,248 +28,32 @@ RCSID("$Id$"); * The host the user logged in from will be returned in buf. */ -/* - * Returns the time when the user last logged in (or 0 if no previous login - * is found). The name of the host used last time is returned in buf. - */ - unsigned long get_last_login_time(uid_t uid, const char *logname, char *buf, unsigned int bufsize) { -#if defined(_PATH_LASTLOG) && !defined(DISABLE_LASTLOG) - struct lastlog ll; - int fd; -# ifdef LASTLOG_IS_DIR - char lbuf[1024]; - - snprintf(lbuf, sizeof(buf), "%s/%s", _PATH_LASTLOG, logname); - if ((fd = open(lbuf, O_RDONLY)) < 0) - return 0; -# else /* LASTLOG_IS_DIR */ - buf[0] = '\0'; - - if ((fd = open(_PATH_LASTLOG, O_RDONLY)) < 0) - return 0; - - lseek(fd, (off_t) ((long) uid * sizeof(ll)), SEEK_SET); -# endif /* LASTLOG_IS_DIR */ - if (read(fd, &ll, sizeof(ll)) != sizeof(ll)) { - close(fd); - return 0; - } - - close(fd); - - if (bufsize > sizeof(ll.ll_host) + 1) - bufsize = sizeof(ll.ll_host) + 1; - strncpy(buf, ll.ll_host, bufsize - 1); - buf[bufsize - 1] = 0; - - return ll.ll_time; -#else /* defined(_PATH_LASTLOG) && !defined(DISABLE_LASTLOG) */ -# ifdef HAVE_TYPE_IN_UTMP - /* Look in wtmp for the last login */ - struct utmp wt; - int fd1; - unsigned long t = 0; - - if ((fd1 = open(_PATH_WTMP, O_RDONLY)) < 0) { - error("Couldn't open %.100s to find last login time.", _PATH_WTMP); - return 0; - } - - /* seek to last record of file */ - lseek(fd1, (off_t)(0 - sizeof(struct utmp)), SEEK_END); - - /* loop through wtmp for our last user login record */ - do { - if (read(fd1, &wt, sizeof(wt)) != sizeof(wt)) { - close(fd1); - return 0; - } - - if (wt.ut_type == USER_PROCESS) { - if (!strncmp(logname, wt.ut_user, 8)) { - t = (unsigned long)wt.ut_time; -# ifdef HAVE_HOST_IN_UTMP - if (bufsize > sizeof(wt.ut_host) + 1) - bufsize = sizeof(wt.ut_host) + 1; - strncpy(buf, wt.ut_host, bufsize - 1); - buf[bufsize - 1] = 0; -# else /* HAVE_HOST_IN_UTMP */ - buf[0] = 0; -# endif /* HAVE_HOST_IN_UTMP */ - } - } + struct logininfo li; - if (lseek(fd1, (off_t)(0 - (2 * sizeof(struct utmp))), SEEK_CUR) < 0) - break; - } while (t == 0); - - return t; -# else /* HAVE_TYPE_IN_UTMP */ - return 0; -# endif /* HAVE_TYPE_IN_UTMP */ -#endif /* defined(_PATH_LASTLOG) && !defined(DISABLE_LASTLOG) */ + login_getlastentry_uid(&li, uid); + strncpy(buf, li.hostname, bufsize); + return li.tv_sec; } /* - * Records that the user has logged in. I wish these parts of operating - * systems were more standardized. + * Records that the user has logged in. I these parts of operating systems + * were more standardized. */ + void record_login(pid_t pid, const char *ttyname, const char *user, uid_t uid, const char *host, struct sockaddr * addr) { -#if defined(_PATH_LASTLOG) && !defined(DISABLE_LASTLOG) - struct lastlog ll; -# ifdef LASTLOG_IS_DIR - char buf[1024]; -# endif /* LASTLOG_IS_DIR */ -#endif /* defined(_PATH_LASTLOG) && !defined(DISABLE_LASTLOG) */ - struct utmp u; -#if defined(HAVE_UTMPX_H) && defined(USE_UTMPX) - struct utmpx utx; -#endif - - /* Construct an utmp/wtmp entry. */ - memset(&u, 0, sizeof(u)); - strncpy(u.ut_line, ttyname + 5, sizeof(u.ut_line)); - -#if defined(HAVE_ID_IN_UTMP) -# ifdef _AIX - strncpy(u.ut_id, ttyname + 5, sizeof(u.ut_id)); -# else /* !AIX */ - strncpy(u.ut_id, ttyname + 8, sizeof(u.ut_id)); -# endif -#endif /* defined(HAVE_ID_IN_UTMP) */ - - strncpy(u.ut_name, user, sizeof(u.ut_name)); - -#if defined(HAVE_TV_IN_UTMP) - (void)gettimeofday(&u.ut_tv, NULL); -#else /* defined(HAVE_TV_IN_UTMP) */ - u.ut_time = time(NULL); -#endif /* defined(HAVE_TV_IN_UTMP) */ - -#if defined(HAVE_PID_IN_UTMP) - u.ut_pid = (pid_t)pid; -#endif /* HAVE_PID_IN_UTMP */ - -#if defined(HAVE_TYPE_IN_UTMP) - u.ut_type = (uid == -1)?DEAD_PROCESS:USER_PROCESS; -#endif /* HAVE_TYPE_IN_UTMP */ - -#if defined(HAVE_HOST_IN_UTMP) - strncpy(u.ut_host, host, sizeof(u.ut_host)); -#endif - -#if defined(HAVE_ADDR_IN_UTMP) - if (addr) { - switch (addr->sa_family) { - case AF_INET: { - struct sockaddr_in *in = (struct sockaddr_in*)addr; - memcpy(&(u.ut_addr), &(in->sin_addr), sizeof(&(in->sin_addr))); - break; - } -# if defined(HAVE_ADDR_V6_IN_UTMP) - case AF_INET6: { - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; - memcpy(u.ut_addr_v6, &(in6->sin6_addr), sizeof(&(in6->sin6_addr))); - break; - } -# endif /* defined(HAVE_ADDR_V6_IN_UTMP) */ - default: - break; - } - } -#endif /* defined(HAVE_ADDR_IN_UTMP) */ + struct logininfo *li; -#if defined(HAVE_UTMPX_H) && defined(USE_UTMPX) - memset(&utx, 0, sizeof(utx)); - - strncpy(utx.ut_user, user, sizeof(utx.ut_name)); - strncpy(utx.ut_line, ttyname + 5, sizeof(utx.ut_line)); - strncpy(utx.ut_id, ttyname + 8, sizeof(utx.ut_id)); - - utx.ut_pid = (pid_t)pid; - (void)gettimeofday(&utx.ut_tv, NULL); - - utx.ut_type = (uid == -1)?DEAD_PROCESS:USER_PROCESS; -# ifdef HAVE_HOST_IN_UTMPX -# ifdef HAVE_SYSLEN_IN_UTMPX - utx.ut_syslen = strlen(host); - if (utx.ut_syslen + 1 > sizeof(utx.ut_host)) - utx.ut_syslen = sizeof(utx.ut_host); - strncpy(utx.ut_host, host, utx.ut_syslen); -# else - strncpy(utx.ut_host, host, sizeof(utx.ut_host)); -# endif /* HAVE_SYSLEN_IN_UTMPX */ - utx.ut_host[sizeof(utx.ut_host)-1] = '\0'; -# endif - -# if defined(HAVE_ADDR_IN_UTMPX) - if (addr) { - switch (addr->sa_family) { - case AF_INET: { - struct sockaddr_in *in = (struct sockaddr_in*)addr; - memcpy(&(utx.ut_addr), &(in->sin_addr), sizeof(&(in->sin_addr))); - break; - } -# if defined(HAVE_ADDR_V6_IN_UTMPX) - case AF_INET6: { - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; - memcpy(utx.ut_addr_v6, &(in6->sin6_addr), sizeof(&(in6->sin6_addr))); - break; - } -# endif /* defined(HAVE_ADDR_V6_IN_UTMPX) */ - default: - break; - } - } -# endif /* defined(HAVE_ADDR_IN_UTMPX) */ -#endif /* defined(HAVE_UTMPX_H) && defined(USE_UTMPX) */ - -#if defined(HAVE_UTMPX_H) && defined(USE_UTMPX) - login(&utx); -#else /* defined(HAVE_UTMPX_H) && defined(USE_UTMPX) */ - login(&u); -#endif /* defined(HAVE_UTMPX_H) && defined(USE_UTMPX) */ - -#if defined(_PATH_LASTLOG) && !defined(DISABLE_LASTLOG) - /* Update lastlog unless actually recording a logout. */ - if (strcmp(user, "") != 0) { - int fd; - /* - * It is safer to bzero the lastlog structure first because - * some systems might have some extra fields in it (e.g. SGI) - */ - memset(&ll, 0, sizeof(ll)); - - /* Update lastlog. */ - ll.ll_time = time(NULL); - strncpy(ll.ll_line, ttyname + 5, sizeof(ll.ll_line)); - strncpy(ll.ll_host, host, sizeof(ll.ll_host)); -# ifdef LASTLOG_IS_DIR - snprintf(buf, sizeof(buf), "%s/%s", _PATH_LASTLOG, user); - if ((fd = open(buf, O_RDWR)) >= 0) { - if (write(fd, &ll, sizeof(ll)) != sizeof(ll)) - log("Could not write %.100s: %.100s", buf, strerror(errno)); - close(fd); - } -# else /* LASTLOG_IS_DIR */ - if ((fd = open(_PATH_LASTLOG, O_RDWR)) >= 0) { - lseek(fd, (off_t) ((long) uid * sizeof(ll)), SEEK_SET); - if (write(fd, &ll, sizeof(ll)) != sizeof(ll)) { - log("Could not write %.100s: %.100s", _PATH_LASTLOG, - strerror(errno)); - } - close(fd); - } -# endif /* LASTLOG_IS_DIR */ - } -#endif /* defined(_PATH_LASTLOG) && !defined(DISABLE_LASTLOG) */ + li = login_alloc_entry(pid, user, host, ttyname); + login_set_ip4(li, (struct sockaddr_in *)addr); + login_login(li); + login_free_entry(li); } /* Records that the user has logged out. */ @@ -298,11 +61,9 @@ record_login(pid_t pid, const char *ttyname, const char *user, uid_t uid, void record_logout(pid_t pid, const char *ttyname) { -#ifdef HAVE_LIBUTIL_LOGIN - const char *line = ttyname + 5; /* /dev/ttyq8 -> ttyq8 */ - if (logout(line)) - logwtmp(line, "", ""); -#else /* HAVE_LIBUTIL_LOGIN */ - record_login(pid, ttyname, "", -1, "", NULL); -#endif /* HAVE_LIBUTIL_LOGIN */ + struct logininfo *li; + + li = login_alloc_entry(pid, NULL, NULL, ttyname); + login_logout(li); + login_free_entry(li); } diff --git a/loginrec.c b/loginrec.c new file mode 100644 index 00000000..432e6f87 --- /dev/null +++ b/loginrec.c @@ -0,0 +1,1385 @@ +/* + * Copyright (c) 2000 Andre Lucas. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + ** loginrec.c: platform-independent login recording and lastlog retrieval + **/ + +/** + ** TODO: + ** sockaddr_* stuff isn't finished + ** + ** Platform status: + ** ---------------- + ** + ** Known good: + ** Linux (Redhat 6.2, need more variants) + ** HP-UX 10.20 (gcc only) + ** + ** Testing required: Please send reports! + ** Solaris + ** IRIX + ** NetBSD + ** HP-UX 11 + ** + ** Platforms with known problems: + ** AIX (need to port AIX stuff from old login code + ** NeXT + ** + **/ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_PWD_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#else +# include +#endif + +#include "ssh.h" +#include "xmalloc.h" +#include "loginrec.h" + +RCSID("$Id$"); + + +/** + ** prototypes for helper functions in this file + **/ + +#if HAVE_UTMP_H +# include +void set_utmp_time(struct logininfo *li, struct utmp *ut); +void construct_utmp(struct logininfo *li, struct utmp *ut); +#endif + +#ifdef HAVE_UTMPX_H +# include +void set_utmpx_time(struct logininfo *li, struct utmpx *ut); +void construct_utmpx(struct logininfo *li, struct utmpx *ut); +#endif + +int utmp_write_entry(struct logininfo *li); +int utmpx_write_entry(struct logininfo *li); +int wtmp_write_entry(struct logininfo *li); +int wtmpx_write_entry(struct logininfo *li); +int lastlog_write_entry(struct logininfo *li); +int syslogin_write_entry(struct logininfo *li); + +int getlast_entry(struct logininfo *li); +int lastlog_get_entry(struct logininfo *li); +int wtmp_get_entry(struct logininfo *li); +int wtmpx_get_entry(struct logininfo *li); + + +/** + ** platform-independent login functions + **/ + +/* login_alloc_entry() - allocate and initialise a logininfo */ +struct logininfo *login_alloc_entry(int pid, const char *username, + const char *hostname, + const char *line) { + struct logininfo *newli; + + newli = (struct logininfo *) xmalloc (sizeof(struct logininfo)); + + if (login_init_entry(newli, pid, username, hostname, line)) + return newli; + else + return 0; /* fail */ +} /* login_alloc_entry() */ + + +/* login_free_entry() - free struct memory (duh) */ +void login_free_entry(struct logininfo *li) { + if (li && (li->line[0] != '\0')) + free ((void *)li); + else + log("login_free_entry: attempt to free invalid entry (warning)"); +} /* login_free_entry() */ + +/* login_init_entry() - initialise a struct logininfo */ +int login_init_entry(struct logininfo *li, + int pid, const char *username, + const char *hostname, const char *line) { + + /* zero the structure */ + memset(li, 0, sizeof(struct logininfo)); + + /* progname should be set outside this call */ + /* type stays null by default */ + login_set_pid(li, pid); + /* set the line information */ + login_set_line(li, line); + login_set_username(li, username); + login_set_hostname(li, hostname); + /* exit status and termination stay null by default */ + login_set_current_time(li); + /* sockaddr_* stuff must be set separately (for now) */ + return 1; +} /* login_init_entry() */ + + +void +login_set_progname(struct logininfo *li, const char *progname) { + memset(li->progname, '\0', sizeof(li->progname)); + if (progname) + strlcpy(li->progname, progname, sizeof(li->progname)); + else + li->progname[0] = '\0'; /* set to null */ +} + +void +login_set_type(struct logininfo *li, int type) { + li->type = type; +} + +void +login_set_pid(struct logininfo *li, int pid) { + if (!pid) + li->pid = (int)getpid(); + else + li->pid = pid; +} + +void +login_set_uid(struct logininfo *li, int uid) { + struct passwd *pw; + + li->uid = uid; + /* now update the username */ + pw = getpwuid(uid); + strlcpy(li->username, pw->pw_name, sizeof(li->username)); +} + +void +login_set_line(struct logininfo *li, const char *line) { + if (line) { + /* canonical form is the full name, i.e. including '/dev' */ + line_fullname(li->line, line, sizeof(li->line)); + } else + li->line[0] = '\0'; +} + +void +login_set_username(struct logininfo *li, const char *username) { + struct passwd *pw; + + if (!username) { + li->username[0] = '\0'; + li->uid = -1; /* hmm... */ + } else { + strlcpy(li->username, username, sizeof(li->username)); + /* now update the uid */ + pw = getpwnam(username); + li->uid = pw->pw_uid; + } +} + + +void +login_set_hostname(struct logininfo *li, const char *hostname) { + if (hostname) { /* can be null */ + strlcpy(li->hostname, hostname, sizeof(li->hostname)); + } +} + + +void +login_set_exitstatus(struct logininfo *li, + int exit, int termination) { + /* FIXME: (ATL) And? */ +} + + +/* tv_usec should be null on systems without struct timeval */ +void +login_set_time(struct logininfo *li, + unsigned int tv_sec, unsigned int tv_usec) { + li->tv_sec = tv_sec; + li->tv_usec = tv_usec; +} + + +void +login_set_current_time(struct logininfo *li) { +#ifdef HAVE_SYS_TIME_H + struct timeval tv; + + gettimeofday(&tv, NULL); + li->tv_sec = tv.tv_sec ; li->tv_usec = tv.tv_usec; +#else + time_t t = time(0); + + li->tv_sec = t; li->tv_usec = 0; +#endif +} + +void +login_set_ip4(struct logininfo *li, + const struct sockaddr_in *sa_in4) { + memcpy((void *)&(li->hostaddr.sa_in4), (const void *)sa_in4, + sizeof(struct sockaddr_in)); +} + +#ifdef HAVE_IP6 +void +login_set_ip6(struct logininfo *li, + const struct sockaddr_in6 *sa_in6) { + memcpy((void *)&(li->hostaddr.sa_in4), (const void *)sa_in6, + sizeof(struct sockaddr_in6)); +} +#endif + +/* + * record the entry + */ + +int +login_write (struct logininfo *li) { + + if ((int)geteuid() != 0) { + log("Attempt to write login records by non-root user (aborting)"); + return 1; + } + /* set the timestamp */ + login_set_current_time(li); +#ifdef USE_LOGIN + syslogin_write_entry(li); +#endif +#ifdef USE_LASTLOG + if (li->type == LTYPE_LOGIN) { + lastlog_write_entry(li); + } +#endif +#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; +} + +int +login_login (struct logininfo *li) { + li->type = LTYPE_LOGIN; + return login_write(li); +} + +int +login_logout(struct logininfo *li) { + li->type = LTYPE_LOGOUT; + return login_write(li); +} + +int +login_log_entry(struct logininfo *li) { + return login_write(li); +} + + +unsigned int +login_getlasttime_name(const char *username) { + struct logininfo li; + + memset(&li, '\0', sizeof(li)); + login_set_username(&li, username); + if (getlast_entry(&li)) + return li.tv_sec; + else + return 0; +} /* login_getlasttime_name() */ + + +unsigned int +login_getlasttime_uid(const int uid) { + struct logininfo li; + + memset(&li, '\0', sizeof(li)); + login_set_uid(&li, uid); + if (getlast_entry(&li)) + return li.tv_sec; + else + return 0; +} /* login_getlasttime_uid() */ + + +struct logininfo * +login_getlastentry_name(struct logininfo *li, + const char *username) { + login_set_username(li, username); + if (getlast_entry(li)) + return li; + else + return 0; +} /* login_getlastentry_name() */ + +struct logininfo * +login_getlastentry_uid(struct logininfo *li, + const int uid) { + login_set_uid(li, uid); + if (getlast_entry(li)) + return li; + else + return 0; +} /* login_getlastentry_uid() */ + + +/** + ** 'line' string utility functions + **/ + +/* + * process the 'line' string into three forms: + * 1. The full filename (including '/dev') + * 2. The stripped name (excluding '/dev') + * 3. The abbreviated name (e.g. /dev/ttyp00 + * + * Form 3 is used on some systems to identify a .tmp.? entry when + * attempting to remove it. Typically both addition and removal is + * performed by one application - say, sshd - so as long as the + * choice uniquely identifies a terminal and is the same at login and + * logout time, we're in good shape. + * + * NOTE: None of these calls actually allocate any memory - + * since their target is probably a structure, they don't + * need to. + */ + + +/* add the leading '/dev/' if it doesn't exist + * make sure dst has enough space, if not just copy src (ugh) */ +char * +line_fullname(char *dst, const char *src, int dstsize) { + memset(dst, '\0', dstsize); + if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) + strlcpy(dst, src, dstsize); + else { + strlcpy(dst, "/dev/", 5); + strlcat(dst, src, dstsize); + } + return dst; +} + +/* strip the leading '/dev' if it exists, return dst */ +char * +line_stripname(char *dst, const char *src, int dstsize) { + memset(dst, '\0', dstsize); + if (strncmp(src, "/dev/", 5) == 0) + strlcpy(dst, &src[5], dstsize); + else + strlcpy(dst, src, dstsize); + return dst; +} /* stripdev() */ + +/* return the abbreviated (usually four-character) form * + * simple algorithm for making name: + * - first character is 'L' (arbitrary - 'lib(L)ogin' :-) ) + * - remaining n characters are last n characters of line + * This is good for up to 999 ptys, I hope that's enough... + */ +char * +line_abbrevname(char *dst, const char *src, int dstsize) { + memset(dst, '\0', dstsize); + dst[0]='L'; + strlcpy(dst+1, &src[strlen(src)-(dstsize)], dstsize); + return dst; +} + + +/** + ** utmp utility functions + **/ + +#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN) + +#ifdef HAVE_UTMP_H +# include +#endif +#ifdef USE_TIMEVAL +# include +#else +# include +#endif + +/* build the utmp structure */ +void +set_utmp_time(struct logininfo *li, struct utmp *ut) { +#ifdef HAVE_TV_IN_UTMP + ut->ut_tv.tv_sec = li->tv_sec; + ut->ut_tv.tv_usec = li->tv_usec; +#else +# ifdef HAVE_TIME_IN_UTMP + ut->ut_time = li->tv_sec; +# endif +#endif +} + +void +construct_utmp(struct logininfo *li, + struct utmp *ut) { + memset(ut, '\0', sizeof(struct utmp)); + +#ifdef HAVE_ID_IN_UTMP + line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id)); +#endif + +#ifdef HAVE_TYPE_IN_UTMP + /* this is done here to keep utmp constants out of login.h */ + switch (li->type) { + case LTYPE_LOGIN: + ut->ut_type = USER_PROCESS; + break; + case LTYPE_LOGOUT: + ut->ut_type = DEAD_PROCESS; + break; + } +#endif + +#ifdef HAVE_PID_IN_UTMP + ut->ut_pid = li->pid; +#endif + line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line)); + strlcpy(ut->ut_name, li->username, sizeof(ut->ut_name)); + set_utmp_time(li, ut); +#ifdef HAVE_HOST_IN_UTMP + strlcpy(ut->ut_host, li->hostname, sizeof(ut->ut_host)); +#endif +#ifdef HAVE_ADDR_IN_UTMP + /* !!! not supported yet (can't see its big use either) */ +#endif + +} /* construct_utmp() */ + +#endif +/* USE_UTMP || USE_WTMP || USE_LOGIN */ + +/** + ** utmpx utility functions + **/ + +#if defined(USE_UTMPX) || defined (USE_WTMPX) + +#ifdef HAVE_UTMPX_H +# include +#endif +#ifdef USE_TIMEVAL +# include +#else +# include +#endif + +/* build the utmpx structure */ +void +set_utmpx_time(struct logininfo *li, struct utmpx *utx) { +#ifdef HAVE_TV_IN_UTMPX + utx->ut_tv.tv_sec = li->tv_sec; + utx->ut_tv.tv_usec = li->tv_usec; +#else +# ifdef HAVE_TIME_IN_UTMPX + utx->ut_time = li->tv_sec; +# endif +#endif +} + +void +construct_utmpx(struct logininfo *li, + struct utmpx *utx) { + memset(utx, '\0', sizeof(struct utmpx)); + + line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); + + /* this is done here to keep utmp constants out of loginrec.h */ + switch (li->type) { + case LTYPE_LOGIN: + utx->ut_type = USER_PROCESS; + break; + case LTYPE_LOGOUT: + utx->ut_type = DEAD_PROCESS; + break; + } + + utx->ut_pid = li->pid; + line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line)); + strlcpy(utx->ut_name, li->username, sizeof(utx->ut_name)); + set_utmpx_time(li, utx); +#ifdef HAVE_HOST_IN_UTMPX + strlcpy(utx->ut_host, li->hostname, sizeof(utx->ut_host)); +#endif +#ifdef HAVE_ADDR_IN_UTMPX + /* !!! not supported yet (some issues with types of addresses) */ +#endif +#ifdef HAVE_SYSLEN_IN_UTMPX + /* this is safe because of the extra nulls in logininfo */ + utx->ut_syslen = strlen(li->hostname); +#endif +} /* construct_utmpx() */ + +#endif +/* USE_UTMPX || USE_WTMPX */ + + + +/** + ** utmp functions + **/ + +/* FIXME: (ATL) utmp_write_direct needs testing */ + +#ifdef USE_UTMP + +#include + +/* if we can, use pututline() etc. */ +#if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \ + defined(HAVE_PUTUTLINE) +# define UTMP_USE_LIBRARY +#endif + + +/* write a utmp entry with the system's help (pututline() and pals) */ +#ifdef UTMP_USE_LIBRARY +static int +utmp_write_library(struct logininfo *li, struct utmp *ut) { + + setutent(); + pututline(ut); + +#ifdef HAVE_ENDUTENT + endutent(); +#endif + return 1; +} /* utmp_write_library() */ + +#else + +/* write a utmp entry direct to the file */ +/* This code is a slightly modification of code in OpenBSD's login.c + * (in libutil) and so is subject to the OpenBSD Licensing terms. */ +static int +utmp_write_direct(struct logininfo *li, struct utmp *ut) { + struct utmp old_ut; + register int fd; + int tty; + + tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ + + if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { + (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); + /* + * Prevent luser from zero'ing out ut_host. + * 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 ( read(fd, &old_ut, sizeof(struct utmp)) == sizeof(struct utmp) + && 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 (write(fd, ut, sizeof(struct utmp))==-1) + log("utmp_write_direct: error writing %s: %s", + UTMP_FILE, strerror(errno)); + + (void)close(fd); + return 1; + } else + return 0; +} /* utmp_write_direct() */ + +#endif /* UTMP_USE_LIBRARY */ + + +static int +utmp_perform_login(struct logininfo *li) { + struct utmp ut; + + construct_utmp(li, &ut); + +#ifdef UTMP_USE_LIBRARY + if (!utmp_write_library(li, &ut)) { + log("utmp_perform_login: utmp_write_library() failed"); + return 0; + } +#else + if (!utmp_write_direct(li, &ut)) { + log("utmp_perform_login: utmp_write_direct() failed"); + return 0; + } +#endif + return 1; +} /* utmp_perform_login() */ + + +static int +utmp_perform_logout(struct logininfo *li) { + struct utmp ut; + + memset(&ut, '\0', sizeof(ut)); + set_utmp_time(li, &ut); + line_stripname(ut.ut_line, li->line, sizeof(ut.ut_line)); +#ifdef HAVE_ID_IN_UTMP + line_abbrevname(ut.ut_id, li->line, sizeof(ut.ut_id)); +#endif +#ifdef HAVE_TYPE_IN_UTMP + ut.ut_type = DEAD_PROCESS; +#endif + +#if !defined(DISABLE_PUTUTLINE) \ + && defined(HAVE_SETUTENT) && defined(HAVE_PUTUTLINE) + utmp_write_library(li, &ut); +#else + utmp_write_direct(li, &ut); +#endif + + return 1; +} /* utmp_perform_logout() */ + + +int +utmp_write_entry(struct logininfo *li) { + + switch(li->type) { + case LTYPE_LOGIN: + return utmp_perform_login(li); + + case LTYPE_LOGOUT: + return utmp_perform_logout(li); + + default: + log("utmp_write_entry: invalid type field"); + return 0; + } +} /* utmp_write_entry() */ + + +#endif +/* USE_UTMP */ + + +/** + ** utmpx functions + **/ + +/* not much point if we don't want utmpx entries */ +#ifdef USE_UTMPX + +#include + +/* if we have the wherewithall, use pututxline etc. */ +#if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) \ + && defined(HAVE_PUTUTXLINE) +# define UTMPX_USE_LIBRARY +#endif + + +/* write a utmpx entry with the system's help (pututxline() and pals) */ +#ifdef UTMPX_USE_LIBRARY +static int +utmpx_write_library(struct logininfo *li, struct utmpx *utx) { + + setutxent(); + pututxline(utx); + +#ifdef HAVE_ENDUTXENT + endutxent(); +#endif + return 1; +} /* utmpx_write_library() */ + +#else +/* UTMPX_USE_LIBRARY */ + + +/* 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; + } /* utmpx_write_direct() */ + +#endif +/* UTMPX_USE_LIBRARY */ + +static int +utmpx_perform_login(struct logininfo *li) { + struct utmpx utx; + + construct_utmpx(li, &utx); + +#ifdef UTMPX_USE_LIBRARY + if (!utmpx_write_library(li, &utx)) { + log("utmpx_perform_login: utmp_write_library() failed"); + return 0; + } +#else + if (!utmpx_write_direct(li, &ut)) { + log("utmpx_perform_login: utmp_write_direct() failed"); + return 0; + } +#endif + return 1; +} /* utmpx_perform_login() */ + + +static int +utmpx_perform_logout(struct logininfo *li) { + struct utmpx utx; + + memset(&utx, '\0', sizeof(utx)); + set_utmpx_time(li, &utx); + line_stripname(utx.ut_line, li->line, sizeof(utx.ut_line)); +#ifdef HAVE_ID_IN_UTMPX + line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); +#endif +#ifdef HAVE_TYPE_IN_UTMPX + utx.ut_type = DEAD_PROCESS; +#endif + +#ifdef UTMPX_USE_LIBRARY + utmpx_write_library(li, &utx); +#else + utmpx_write_direct(li, &utx); +#endif + + return 1; +} /* utmpx_perform_logout() */ + + +int +utmpx_write_entry(struct logininfo *li) { + + switch(li->type) { + case LTYPE_LOGIN: + return utmpx_perform_login(li); + case LTYPE_LOGOUT: + return utmpx_perform_logout(li); + default: + log("utmpx_write_entry: invalid type field"); + return 0; + } +} /* utmpx_write_entry() */ + + +#endif +/* USE_UTMPX */ + + +/** + ** wtmp functions + **/ + +#ifdef USE_WTMP + +# include + +/* write a wtmp entry direct to the end of the file */ +/* This code is a slight modification of code in OpenBSD's logwtmp.c + * (in libutil) and so is subject to the OpenBSD licensing terms */ +static int +wtmp_write(struct logininfo *li, struct utmp *ut) { + struct stat buf; + int fd, ret = 1; + + if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { + log("wtmp_write: problem writing %s: %s", + WTMP_FILE, strerror(errno)); + return 0; + } + + if (fstat(fd, &buf) == 0) + if (write(fd, (char *)ut, sizeof(struct utmp)) != + sizeof(struct utmp)) { + ftruncate(fd, buf.st_size); + log("wtmp_write: problem writing %s: %s", + WTMP_FILE, strerror(errno)); + ret = 0; + } + (void)close(fd); + + return ret; +} /* wtmp_write() */ + + + +static int +wtmp_perform_login(struct logininfo *li) { + struct utmp ut; + + construct_utmp(li, &ut); + return wtmp_write(li, &ut); +} /* wtmp_perform_login() */ + + +static int +wtmp_perform_logout(struct logininfo *li) { + struct utmp ut; + + construct_utmp(li, &ut); + /* blank out unnecessary fields */ + memset(&(ut.ut_name), '\0', sizeof(ut.ut_name)); +#ifdef HAVE_ID_IN_UTMP + memset(&(ut.ut_id), '\0', sizeof(ut.ut_id)); +#endif +#ifdef HAVE_HOST_IN_UTMP + memset(&(ut.ut_host), '\0', sizeof(ut.ut_host)); +#endif +#ifdef HAVE_ADDR_IN_UTMP + memset(&(ut.ut_addr), '\0', sizeof(ut.ut_addr)); +#endif + return wtmp_write(li, &ut); +} /* wtmp_perform_logout() */ + + +int +wtmp_write_entry(struct logininfo *li) { + + switch(li->type) { + case LTYPE_LOGIN: + return wtmp_perform_login(li); + case LTYPE_LOGOUT: + return wtmp_perform_logout(li); + default: + log("wtmp_write_entry: invalid type field"); + return 0; + } +} /* wtmp_write_entry() */ + + + +int +wtmp_get_entry(struct logininfo *li) { + struct stat st; + struct utmp ut; + int fd; + + if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { + log("wtmp_get_entry: problem opening %s: %s", + WTMP_FILE, strerror(errno)); + return 0; + } + + if (fstat(fd, &st) != 0) { + log("wtmp_get_entry: couldn't stat %s: %s", + WTMP_FILE, strerror(errno)); + close(fd); + return 0; + } + + (void)lseek(fd, (off_t)(0-sizeof(struct utmp)), SEEK_END); + + do { + if (read(fd, &ut, sizeof(ut)) != sizeof(ut)) { + log("wtmp_get_entry: read of %s failed: %s", + WTMP_FILE, strerror(errno)); + close (fd); + return 0; + } + + /* Logouts are recorded as a blank username on a particular line. + * So, we just need to find the username in struct utmp */ + if ( strncmp(li->username, ut.ut_user, 8) == 0 ) { + /* note we've already made sure there's a time in struct utmp */ +#ifdef HAVE_TIME_IN_UTMP + li->tv_sec = ut.ut_time; +#else +# if HAVE_TV_IN_UTMP + li->tv_sec = ut.ut_tv.tv_sec; +# endif +#endif + line_fullname(li->line, ut.ut_line, sizeof(ut.ut_line)); +#ifdef HAVE_HOST_IN_UTMP + strlcpy(li->hostname, ut.ut_host, sizeof(ut.ut_host)); +#endif + } + if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) { + close (fd); + return 0; + } + } while (li->tv_sec == 0); + + return 1; +} /* wtmp_get_entry() */ + + +#endif +/* USE_WTMP */ + + +/** + ** wtmpx functions + **/ + +#ifdef USE_WTMPX + +# include + +/* write a wtmpx entry direct to the end of the file */ +/* This code is a slight modification of code in OpenBSD's logwtmp.c + * (in libutil) and so is subject to the OpenBSD licensing terms */ +static int +wtmpx_write(struct logininfo *li, struct utmpx *utx) { + struct stat buf; + int fd, ret = 1; + + if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { + log("wtmpx_write: problem opening %s: %s", + WTMPX_FILE, strerror(errno)); + return 0; + } + + if (fstat(fd, &buf) == 0) + if (write(fd, (char *)utx, sizeof(struct utmpx)) != + sizeof(struct utmpx)) { + ftruncate(fd, buf.st_size); + log("wtmpx_write: problem writing %s: %s", + WTMPX_FILE, strerror(errno)); + ret = 0; + } + (void)close(fd); + + return ret; +} /* wtmpx_write() */ + + + +static int +wtmpx_perform_login(struct logininfo *li) { + struct utmpx utx; + + construct_utmpx(li, &utx); + return wtmpx_write(li, &utx); +} /* wtmpx_perform_login() */ + + +static int +wtmpx_perform_logout(struct logininfo *li) { + struct utmpx utx; + + construct_utmpx(li, &utx); + /* blank out unnecessary fields */ + memset(&(utx.ut_name), '\0', sizeof(utx.ut_name)); +#ifdef HAVE_ID_IN_UTMPX + memset(&(utx.ut_id), '\0', sizeof(utx.ut_id)); +#endif +#ifdef HAVE_HOST_IN_UTMPX + memset(&(utx.ut_host), '\0', sizeof(utx.ut_host)); +#endif +#ifdef HAVE_ADDR_IN_UTMPX + memset(&(utx.ut_addr), '\0', sizeof(utx.ut_addr)); +#endif + return wtmpx_write(li, &utx); + +} /* wtmpx_perform_logout() */ + + +int +wtmpx_write_entry(struct logininfo *li) { + + switch(li->type) { + case LTYPE_LOGIN: + return wtmpx_perform_login(li); + case LTYPE_LOGOUT: + return wtmpx_perform_logout(li); + default: + log("wtmpx_write_entry: invalid type field"); + return 0; + } +} /* wtmpx_write_entry() */ + + + +int +wtmpx_get_entry(struct logininfo *li) { + struct stat st; + struct utmpx utx; + int fd; + + if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { + log("wtmpx_get_entry: problem opening %s: %s", + WTMPX_FILE, strerror(errno)); + return 0; + } + + if (fstat(fd, &st) != 0) { + log("wtmpx_get_entry: couldn't stat %s: %s", + WTMP_FILE, strerror(errno)); + close(fd); + return 0; + } + + (void)lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END); + + do { + if (read(fd, &utx, sizeof(utx)) != sizeof(utx)) { + log("wtmpx_get_entry: read of %s failed: %s", + WTMPX_FILE, strerror(errno)); + close (fd); + return 0; + } + + /* Logouts are recorded as a blank username on a particular line. + * So, we just need to find the username in struct utmpx */ + if ( strncmp(li->username, utx.ut_user, 8) == 0 ) { + /* note we've already made sure there's a time in struct utmp */ +#ifdef HAVE_TV_IN_UTMPX + li->tv_sec = utx.ut_tv.tv_sec; +#else +# ifdef HAVE_TIME_IN_UTMPX + li->tv_sec = utx.ut_time; +# endif +#endif + line_fullname(li->line, utx.ut_line, sizeof(utx.ut_line)); +#ifdef HAVE_HOST_IN_UTMPX + strlcpy(li->hostname, utx.ut_host, sizeof(utx.ut_line)); +#endif + } + if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) { + close (fd); + return 0; + } + } while (li->tv_sec == 0); + return 1; +} /* wtmpx_get_entry() */ + + + +#endif +/* USE_WTMPX */ + + + +/** + ** libutil login() functions + **/ + +#ifdef USE_LOGIN + +#ifdef HAVE_UTMP_H +# include +#endif +#ifdef HAVE_UTIL_H +# include +#endif +#ifdef USE_TIMEVAL +# include +#else +# include +#endif + +static int +syslogin_perform_login(struct logininfo *li) { + struct utmp *ut; + + if (! (ut = (struct utmp *)malloc(sizeof(struct utmp)))) { + log("syslogin_perform_login: couldn't malloc()"); + return 0; + } + construct_utmp(li, ut); + login(ut); + + return 1; +} /* syslogin_perform_login() */ + +static int +syslogin_perform_logout(struct logininfo *li) { + +#ifdef HAVE_LOGOUT + char line[8]; + + (void)line_stripname(line, li->line, sizeof(line)); + + if (!logout(line)) { + log("syslogin_perform_logout: logout() returned an error"); +# ifdef HAVE_LOGWTMP + } else { + logwtmp(line, "", ""); + } +# endif + /* TODO: 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, but... */ +#endif + return 1; +} /* syslogin_perform_logout() */ + + + +int +syslogin_write_entry(struct logininfo *li) { + + switch (li->type) { + case LTYPE_LOGIN: + return syslogin_perform_login(li); + case LTYPE_LOGOUT: + return syslogin_perform_logout(li); + default: + log("syslogin_write_entry: Invalid type field"); + return 0; + } +} /* utmp_write_entry() */ + + +#endif +/* USE_LOGIN */ + +/* end of file log-syslogin.c */ + + +/** + ** lastlog functions + **/ + +#ifdef USE_LASTLOG + +#ifdef HAVE_LASTLOG_H +# include +#else +# if !defined(USE_UTMP) && !defined(USE_WTMP) +# include +# endif +#endif + + +static void +lastlog_construct(struct logininfo *li, + struct lastlog *last) { + /* clear the structure */ + memset(last, '\0', sizeof(struct lastlog)); + + (void)line_stripname(last->ll_line, li->line, + sizeof(last->ll_line)); + strlcpy(last->ll_host, li->hostname, sizeof(last->ll_host)); + last->ll_time = li->tv_sec; +} /* lastlog_construct() */ + + +#define LL_FILE 1 +#define LL_DIR 2 +#define LL_OTHER 3 + +static int +lastlog_filetype(char *filename) { + struct stat st; + + if ( stat(LASTLOG_FILE, &st) != 0) { + log("lastlog_perform_login: Couldn't stat %s: %s", + LASTLOG_FILE, strerror(errno)); + return 0; + } + + if (S_ISDIR(st.st_mode)) + return LL_DIR; + else if (S_ISREG(st.st_mode)) + return LL_FILE; + else + return LL_OTHER; +} /* lastlog_filetype() */ + + +/* open the file (using filemode) and seek to the login entry */ +static int +lastlog_openseek(struct logininfo *li, int *fd, int filemode) { + + off_t offset; + int type; + char lastlog_file[1024]; + + type = lastlog_filetype(LASTLOG_FILE); + switch (type) { + case LL_FILE: + strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); + break; + case LL_DIR: + snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", + LASTLOG_FILE, li->username); + break; + default: + log("lastlog_openseek: %.100s is not a file or directory!", + LASTLOG_FILE); + return 0; + } /* switch */ + + *fd = open(lastlog_file, filemode); + if ( *fd < 0) { + log("lastlog_openseek: Couldn't open %s: %s", + 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; + } + return 1; +} /* lastlog_openseek() */ + +static int +lastlog_perform_login(struct logininfo *li) { + struct lastlog last; + int fd; + + /* create our struct lastlog */ + lastlog_construct(li, &last); + + /* write the entry */ + if (lastlog_openseek(li, &fd, O_RDWR)) { + if ( write(fd, &last, sizeof(struct lastlog)) + != sizeof(struct lastlog) ) { + log("lastlog_write_filemode: Error writing to %s: %s", + LASTLOG_FILE, strerror(errno)); + return 0; + } + return 1; + } else + return 0; +} /* lastlog_perform_login() */ + + +int +lastlog_write_entry(struct logininfo *li) { + + switch(li->type) { + case LTYPE_LOGIN: + return lastlog_perform_login(li); + default: + log("lastlog_write_entry: Invalid type field"); + return 0; + } +} /* lastlog_write_entry() */ + + + +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, sizeof(li->hostname)); + li->tv_sec = last->ll_time; +} /* lastlog_populate_entry() */ + + + +int +lastlog_get_entry(struct logininfo *li) { + struct lastlog last; + int fd; + + if (lastlog_openseek(li, &fd, O_RDONLY)) { + if ( read(fd, &last, sizeof(struct lastlog)) + != sizeof(struct lastlog) ) { + log("lastlog_write_filemode: Error reading from %s: %s", + LASTLOG_FILE, strerror(errno)); + return 0; + } else { + lastlog_populate_entry(li, &last); + return 1; + } + + } else + return 0; +} /* lastlog_get_entry() */ + + +#endif +/* USE_LASTLOG */ + + +/** + ** lastlog retrieval functions + **/ + +/* take the uid in li and return the last login time */ +int +getlast_entry(struct logininfo *li) { + +#ifdef USE_LASTLOG + if (lastlog_get_entry(li)) + return 1; + else + return 0; +#else + /* !USE_LASTLOG */ + /* Try to retrieve the last login time from another source */ + +# if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) + + /* retrieve last login time from utmp */ + if (wtmp_get_entry(li)) + return 1; + else + return 0; + +# else +# if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX)) + + /* retrieve last login time from utmpx */ + if (wtmpx_get_entry(li)) + return 1; + else + return 0; + +# else + + /* no means of retrieving last login time */ + return 0; +# endif +# endif + +#endif + /* USE_LASTLOG */ + +} diff --git a/loginrec.h b/loginrec.h new file mode 100644 index 00000000..2a7ff8f7 --- /dev/null +++ b/loginrec.h @@ -0,0 +1,167 @@ +#ifndef _HAVE_LOGINREC_H_ +#define _HAVE_LOGINREC_H_ + +/* + * Copyright (c) 2000 Andre Lucas. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + ** loginrec.h: platform-independent login recording and lastlog retrieval + **/ + +#include "includes.h" + +#include +#include +#include + +/* RCSID("$Id$"); */ + +/** + ** you should use the login_* calls to work around platform dependencies + **/ + +/* check if we have IP6 on this system */ +#if defined(AF_INET6) || defined(INET6_ADDRSTRLEN) +# define LOGIN_HAVE_IP6 +#endif + +/* + * login_netinfo structure + */ + +struct login_netinfo { + struct sockaddr_in sa_in4; +#ifdef LOGIN_HAVE_IP6 + struct sockaddr_in6 sa_in6; +#endif + +}; /* struct login_netinfo */ + + +/* + * * logininfo structure * + */ + +/* types - different to utmp.h 'type' macros */ +/* (though set to the same value as linux, openbsd and others...) */ +#define LTYPE_LOGIN 7 +#define LTYPE_LOGOUT 8 + +/* string lengths - set very long */ +#define LINFO_PROGSIZE 64 +#define LINFO_LINESIZE 64 +#define LINFO_NAMESIZE 64 +#define LINFO_HOSTSIZE 256 + +struct logininfo { + + char progname[LINFO_PROGSIZE]; /* name of program (for PAM) */ + int progname_null; + + short int type; /* type of login (LTYPE_*) */ + + int pid; /* PID of login process */ + int uid; /* UID of this user */ + char line[LINFO_LINESIZE]; /* tty/pty name */ + char username[LINFO_NAMESIZE]; /* login username */ + char hostname[LINFO_HOSTSIZE]; /* remote hostname */ + + /* 'exit_status' structure components */ + int exit; /* process exit status */ + int termination; /* process termination status */ + + /* struct timeval (sys/time.h) isn't always available, if it isn't we'll + * use time_t's value as tv_sec and set tv_usec to 0 + */ + unsigned int tv_sec; + unsigned int tv_usec; + + struct login_netinfo hostaddr; /* caller's host address(es) */ + +}; /* struct logininfo */ + + +/* + * login recording functions + */ +/* construct a new login entry */ +struct logininfo *login_alloc_entry(int pid, + const char *username, + const char *hostname, const char *line); +void login_free_entry(struct logininfo *li); +int login_init_entry(struct logininfo *li, + int pid, const char *username, + const char *hostname, const char *line); +void login_set_progname(struct logininfo *li, + const char *progname); +/* set the type field (skip if using ...login or ...logout) */ +void login_set_type(struct logininfo *li, int type); +void login_set_pid(struct logininfo *li, int pid); +void login_set_uid(struct logininfo *li, int uid); +void login_set_line(struct logininfo *li, const char *line); +void login_set_username(struct logininfo *li, const char *username); +void login_set_hostname(struct logininfo *li, const char *hostname); +/* set the exit status (used by [uw]tmpx) */ +void login_set_exitstatus(struct logininfo *li, int exit, int termination); +void login_set_time(struct logininfo *li, unsigned int tv_sec, + unsigned int tv_usec); +void login_set_current_time(struct logininfo *li); +/* set the network address based on network address type */ +void login_set_ip4(struct logininfo *li, + const struct sockaddr_in *sa_in4); +# ifdef LOGIN_HAVE_IP6 +void login_set_ip6(struct logininfo *li, + const struct sockaddr_in6 *sa_in6); +# endif /* LOGIN_HAVE_IP6 */ +/* record the entry */ +int login_write (struct logininfo *li); +int login_login (struct logininfo *li); +int login_logout(struct logininfo *li); +int login_log_entry(struct logininfo *li); + +/* + * login record retrieval functions + */ +/* lastlog *entry* functions fill out a logininfo */ +struct logininfo *login_getlastentry_name(struct logininfo *li, + const char *username); +struct logininfo *login_getlastentry_uid(struct logininfo *li, + const int pid); +/* lastlog *time* functions return time_t equivalent (uint) */ +unsigned int login_getlasttime_name(const char *username); +unsigned int login_getlasttime_uid(const int pid); + +/* produce various forms of the line filename */ +char *line_fullname(char *dst, const char *src, int dstsize); +char *line_stripname(char *dst, const char *src, int dstsize); +char *line_abbrevname(char *dst, const char *src, int dstsize); + + +#endif /* _HAVE_LOGINREC_H_ */ + diff --git a/logintest.c b/logintest.c new file mode 100644 index 00000000..b2d82c79 --- /dev/null +++ b/logintest.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2000 Andre Lucas. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + ** logintest.c: simple test driver for platform-independent login recording + ** and lastlog retrieval + **/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_TIME_H +#include +#endif + +#include "loginrec.h" + +RCSID("$Id$"); + + +int nologtest = 0; +int compile_opts_only = 0; +int be_verbose = 0; + + +#define DOTQUAD_MAXSIZE 17 +void dump_dotquad(char *s, struct in_addr *sin4) { + unsigned int addr; + + addr = ntohl(sin4->s_addr); + snprintf(s, DOTQUAD_MAXSIZE, "%d.%d.%d.%d", + (addr >> 24)& 0xff, (addr >>16) & 0xff, + (addr >>8) & 0xff, addr & 0xff ); +} /* dump_dotquad */ + + +/* Dump a logininfo to stdout. Assumes a tab size of 8 chars. */ +void dump_logininfo(struct logininfo *li, char *descname) { + char a4[DOTQUAD_MAXSIZE]; + + dump_dotquad(a4, &(li->hostaddr.sa_in4.sin_addr)); + + /* yes I know how nasty this is */ + printf("struct logininfo %s = {\n\t" + "progname\t'%s'\n\ttype\t\t%d\n\t" + "pid\t\t%d\n\tuid\t\t%d\n\t" + "line\t\t'%s'\n\tusername\t'%s'\n\t" + "hostname\t'%s'\n\texit\t\t%d\n\ttermination\t%d\n\t" + "tv_sec\t%d\n\ttv_usec\t%d\n\t" + "struct login_netinfo hostaddr {\n\t\t" + "struct sockaddr_in sa_in4 {\n" + "\t\t\tsin_port\t%d\n\t\t\t*sin_addr\t%d(%s)\n\t\t}\n" + "\t\t** !!! IP6 stuff not supported yet **\n" + "\t}\n" + "}\n", + descname, li->progname, li->type, + li->pid, li->uid, li->line, + li->username, li->hostname, li->exit, + li->termination, li->tv_sec, li->tv_usec, + ntohs(li->hostaddr.sa_in4.sin_port), + ntohl(li->hostaddr.sa_in4.sin_addr.s_addr), a4); + /* FIXME: (ATL) print sockaddr_in6 stuff */ +} + + +int testAPI() { + struct logininfo *li1; + struct passwd *pw; + struct hostent *he; + struct sockaddr_in sa_in4; + char cmdstring[256], stripline[8]; + char username[32]; +#ifdef HAVE_TIME_H + time_t t0, t1, t2; + char s_t0[64],s_t1[64],s_t2[64]; /* ctime() strings */ +#endif + + printf("**\n** Testing the API...\n**\n"); + + pw = getpwuid(getuid()); + strlcpy(username, pw->pw_name, sizeof(username)); + + /* gethostname(hostname, sizeof(hostname)); */ + + printf("login_alloc_entry test (no host info):\n"); + /* !!! fake tty more effectively */ + li1 = login_alloc_entry((int)getpid(), username, NULL, ttyname(0)); + login_set_progname(li1, "testlogin"); + + if (be_verbose) + dump_logininfo(li1, "li1"); + + printf("Setting IPv4 host info for 'localhost' (may call out):\n"); + if (! (he = gethostbyname("localhost"))) { + printf("Couldn't set hostname(lookup failed)\n"); + } else { + /* NOTE: this is messy, but typically a program wouldn't have to set + * any of this, a sockaddr_in* would be already prepared */ + memcpy((void *)&(sa_in4.sin_addr), (void *)&(he->h_addr_list[0][0]), + sizeof(struct in_addr)); + login_set_ip4(li1, &sa_in4); + login_set_hostname(li1, "localhost"); + } + if (be_verbose) + dump_logininfo(li1, "li1"); + + if ((int)geteuid() != 0) { + printf("NOT RUNNING LOGIN TESTS - you are not root!\n"); + return 1; /* this isn't necessarily an error */ + } + + if (nologtest) + return 1; + + line_stripname(stripline, li1->line, sizeof(stripline)); + + printf("Performing an invalid login attempt (no type field)\n--\n"); + login_write(li1); + printf("--\n(Should have written an error to stderr)\n"); + +#ifdef HAVE_TIME_H + (void)time(&t0); + strlcpy(s_t0, ctime(&t0), sizeof(s_t0)); + t1 = login_getlasttime_uid(getuid()); + strlcpy(s_t1, ctime(&t1), sizeof(s_t1)); + printf("Before logging in:\n\tcurrent time is %d - %s\t" + "lastlog time is %d - %s\n", + (int)t0, s_t0, (int)t1, s_t1); +#endif + + printf("Performing a login on line %s...\n--\n", stripline); + login_login(li1); + + snprintf(cmdstring, sizeof(cmdstring), "who | grep '%s '", + stripline); + system(cmdstring); + + printf("--\nWaiting for a few seconds...\n"); + sleep(2); + + printf("Performing a logout (the root login " + "shown above should be gone)\n" + "If the root login hasn't gone, but another user on the same\n" + "pty has, this is OK - we're hacking it here, and there\n" + "shouldn't be two users on one pty in reality...\n" + "-- ('who' output follows)\n"); + login_logout(li1); + + system(cmdstring); + printf("-- ('who' output ends)\n"); + +#ifdef HAVE_TIME_H + t2 = login_getlasttime_uid(getuid()); + strlcpy(s_t2, ctime(&t2), sizeof(s_t2)); + printf("After logging in, lastlog time is %d - %s\n", (int)t2, s_t2); + if (t1 == t2) + printf("The lastlog times before and after logging in are the " + "same.\nThis indicates that lastlog is ** NOT WORKING " + "CORRECTLY **\n"); + else if (t0 != t2) + printf("** The login time and the lastlog time differ.\n" + "** This indicates that lastlog is either recording the " + "wrong time,\n** or retrieving the wrong entry.\n"); + else + printf("lastlog agrees with the login time. This is a good thing.\n"); + +#endif + + printf("--\nThe output of 'last' shown next should have " + "an entry for root \n on %s for the time shown above:\n--\n", + stripline); + snprintf(cmdstring, sizeof(cmdstring), "last | grep '%s ' | head -3", + stripline); + system(cmdstring); + + printf("--\nEnd of login test.\n"); + + login_free_entry(li1); + + return 1; +} /* testAPI() */ + + +void testLineName(char *line) { + /* have to null-terminate - these functions are designed for + * structures with fixed-length char arrays, and don't null-term.*/ + char full[17], strip[9], abbrev[5]; + + memset(full, '\0', sizeof(full)); + memset(strip, '\0', sizeof(strip)); + memset(abbrev, '\0', sizeof(abbrev)); + + line_fullname(full, line, sizeof(full)-1); + line_stripname(strip, full, sizeof(strip)-1); + line_abbrevname(abbrev, full, sizeof(abbrev)-1); + printf("%s: %s, %s, %s\n", line, full, strip, abbrev); + +} /* testLineName() */ + + +int testOutput() { + printf("**\n** Testing linename functions\n**\n"); + testLineName("/dev/pts/1"); + testLineName("pts/1"); + testLineName("pts/999"); + testLineName("/dev/ttyp00"); + testLineName("ttyp00"); + + return 1; +} /* testOutput() */ + + +/* show which options got compiled in */ +void showOptions(void) { + + printf("**\n** Compile-time options\n**\n"); + + printf("login recording methods selected:\n"); +#ifdef USE_LOGIN + printf("\tUSE_LOGIN\n"); +#endif +#ifdef USE_UTMP + printf("\tUSE_UTMP (UTMP_FILE=%s)\n", UTMP_FILE); +#endif +#ifdef USE_UTMPX + printf("\tUSE_UTMPX (UTMPX_FILE=%s)\n", UTMPX_FILE); +#endif +#ifdef USE_WTMP + printf("\tUSE_WTMP (WTMP_FILE=%s)\n", WTMP_FILE); +#endif +#ifdef USE_WTMPX + printf("\tUSE_WTMPX (WTMPX_FILE=%s)\n", WTMPX_FILE); +#endif +#ifdef USE_LASTLOG + printf("\tUSE_LASTLOG (LASTLOG_FILE=%s)\n", LASTLOG_FILE); +#endif + printf("\n"); + + printf("IP6 support: %s\n", +#ifdef HAVE_IP6 + "enabled" +#else + "disabled" +#endif + ); + + +} /* showOptions() */ + + +int main(int argc, char *argv[]) { + + printf("Platform-independent login recording test driver"); + + if (argc == 2) { + if (strncmp(argv[1], "-i", 3) == 0) + compile_opts_only = 1; + else if (strncmp(argv[1], "-v", 3) == 0) + be_verbose=1; + } + + if (!compile_opts_only) { + if (be_verbose && !testOutput()) + return 1; + + if (!testAPI()) + return 1; + } + + showOptions(); + + return 0; +} /* main() */ + diff --git a/openbsd-compat.h b/openbsd-compat.h index eadcf794..bef9c852 100644 --- a/openbsd-compat.h +++ b/openbsd-compat.h @@ -12,7 +12,6 @@ #include "bsd-mktemp.h" #include "bsd-snprintf.h" #include "bsd-daemon.h" -#include "bsd-login.h" #include "bsd-base64.h" /* rfc2553 socket API replacements */ -- 2.45.1