Home | History | Annotate | Download | only in sshd
      1 /*
      2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 /*
      6  * Copyright (c) 2000 Andre Lucas.  All rights reserved.
      7  * Portions copyright (c) 1998 Todd C. Miller
      8  * Portions copyright (c) 1996 Jason Downs
      9  * Portions copyright (c) 1996 Theo de Raadt
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  * 3. All advertising materials mentioning features or use of this software
     20  *    must display the following acknowledgement:
     21  *      This product includes software developed by Markus Friedl.
     22  * 4. The name of the author may not be used to endorse or promote products
     23  *    derived from this software without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     26  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     27  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     28  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     30  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     34  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     35  */
     36 
     37 /**
     38  ** loginrec.c:  platform-independent login recording and lastlog retrieval
     39  **/
     40 
     41 /*
     42   The new login code explained
     43   ============================
     44 
     45   This code attempts to provide a common interface to login recording
     46   (utmp and friends) and last login time retrieval.
     47 
     48   Its primary means of achieving this is to use 'struct logininfo', a
     49   union of all the useful fields in the various different types of
     50   system login record structures one finds on UNIX variants.
     51 
     52   We depend on autoconf to define which recording methods are to be
     53   used, and which fields are contained in the relevant data structures
     54   on the local system. Many C preprocessor symbols affect which code
     55   gets compiled here.
     56 
     57   The code is designed to make it easy to modify a particular
     58   recording method, without affecting other methods nor requiring so
     59   many nested conditional compilation blocks as were commonplace in
     60   the old code.
     61 
     62   For login recording, we try to use the local system's libraries as
     63   these are clearly most likely to work correctly. For utmp systems
     64   this usually means login() and logout() or setutent() etc., probably
     65   in libutil, along with logwtmp() etc. On these systems, we fall back
     66   to writing the files directly if we have to, though this method
     67   requires very thorough testing so we do not corrupt local auditing
     68   information. These files and their access methods are very system
     69   specific indeed.
     70 
     71   For utmpx systems, the corresponding library functions are
     72   setutxent() etc. To the author's knowledge, all utmpx systems have
     73   these library functions and so no direct write is attempted. If such
     74   a system exists and needs support, direct analogues of the [uw]tmp
     75   code should suffice.
     76 
     77   Retrieving the time of last login ('lastlog') is in some ways even
     78   more problemmatic than login recording. Some systems provide a
     79   simple table of all users which we seek based on uid and retrieve a
     80   relatively standard structure. Others record the same information in
     81   a directory with a separate file, and others don't record the
     82   information separately at all. For systems in the latter category,
     83   we look backwards in the wtmp or wtmpx file for the last login entry
     84   for our user. Naturally this is slower and on busy systems could
     85   incur a significant performance penalty.
     86 
     87   Calling the new code
     88   --------------------
     89 
     90   In OpenSSH all login recording and retrieval is performed in
     91   login.c. Here you'll find working examples. Also, in the logintest.c
     92   program there are more examples.
     93 
     94   Internal handler calling method
     95   -------------------------------
     96 
     97   When a call is made to login_login() or login_logout(), both
     98   routines set a struct logininfo flag defining which action (log in,
     99   or log out) is to be taken. They both then call login_write(), which
    100   calls whichever of the many structure-specific handlers autoconf
    101   selects for the local system.
    102 
    103   The handlers themselves handle system data structure specifics. Both
    104   struct utmp and struct utmpx have utility functions (see
    105   construct_utmp*()) to try to make it simpler to add extra systems
    106   that introduce new features to either structure.
    107 
    108   While it may seem terribly wasteful to replicate so much similar
    109   code for each method, experience has shown that maintaining code to
    110   write both struct utmp and utmpx in one function, whilst maintaining
    111   support for all systems whether they have library support or not, is
    112   a difficult and time-consuming task.
    113 
    114   Lastlog support proceeds similarly. Functions login_get_lastlog()
    115   (and its OpenSSH-tuned friend login_get_lastlog_time()) call
    116   getlast_entry(), which tries one of three methods to find the last
    117   login time. It uses local system lastlog support if it can,
    118   otherwise it tries wtmp or wtmpx before giving up and returning 0,
    119   meaning "tilt".
    120 
    121   Maintenance
    122   -----------
    123 
    124   In many cases it's possible to tweak autoconf to select the correct
    125   methods for a particular platform, either by improving the detection
    126   code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE
    127   symbols for the platform.
    128 
    129   Use logintest to check which symbols are defined before modifying
    130   configure.ac and loginrec.c. (You have to build logintest yourself
    131   with 'make logintest' as it's not built by default.)
    132 
    133   Otherwise, patches to the specific method(s) are very helpful!
    134 
    135 */
    136 
    137 /**
    138  ** TODO:
    139  **   homegrown ttyslot()
    140  **   test, test, test
    141  **
    142  ** Platform status:
    143  ** ----------------
    144  **
    145  ** Known good:
    146  **   Linux (Redhat 6.2, Debian)
    147  **   Solaris
    148  **   HP-UX 10.20 (gcc only)
    149  **   IRIX
    150  **   NeXT - M68k/HPPA/Sparc (4.2/3.3)
    151  **
    152  ** Testing required: Please send reports!
    153  **   NetBSD
    154  **   HP-UX 11
    155  **   AIX
    156  **
    157  ** Platforms with known problems:
    158  **   Some variants of Slackware Linux
    159  **
    160  **/
    161 
    162 #include "includes.h"
    163 
    164 #include "ssh.h"
    165 #include "xmalloc.h"
    166 #include "loginrec.h"
    167 #include "log.h"
    168 #include "atomicio.h"
    169 
    170 RCSID("$Id: loginrec.c,v 1.44 2002/09/26 00:38:49 tim Exp $");
    171 
    172 #pragma ident	"%Z%%M%	%I%	%E% SMI"
    173 
    174 #ifdef HAVE_UTIL_H
    175 #  include <util.h>
    176 #endif
    177 
    178 #ifdef HAVE_LIBUTIL_H
    179 #   include <libutil.h>
    180 #endif
    181 
    182 /**
    183  ** prototypes for helper functions in this file
    184  **/
    185 
    186 #if HAVE_UTMP_H
    187 void set_utmp_time(struct logininfo *li, struct utmp *ut);
    188 void construct_utmp(struct logininfo *li, struct utmp *ut);
    189 #endif
    190 
    191 #ifdef HAVE_UTMPX_H
    192 void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
    193 void construct_utmpx(struct logininfo *li, struct utmpx *ut);
    194 #endif
    195 
    196 int utmp_write_entry(struct logininfo *li);
    197 int utmpx_write_entry(struct logininfo *li);
    198 int wtmp_write_entry(struct logininfo *li);
    199 int wtmpx_write_entry(struct logininfo *li);
    200 int lastlog_write_entry(struct logininfo *li);
    201 int syslogin_write_entry(struct logininfo *li);
    202 
    203 int getlast_entry(struct logininfo *li);
    204 int lastlog_get_entry(struct logininfo *li);
    205 int wtmp_get_entry(struct logininfo *li);
    206 int wtmpx_get_entry(struct logininfo *li);
    207 
    208 /* pick the shortest string */
    209 #define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
    210 
    211 /**
    212  ** platform-independent login functions
    213  **/
    214 
    215 /* login_login(struct logininfo *)     -Record a login
    216  *
    217  * Call with a pointer to a struct logininfo initialised with
    218  * login_init_entry() or login_alloc_entry()
    219  *
    220  * Returns:
    221  *  >0 if successful
    222  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
    223  */
    224 int
    225 login_login (struct logininfo *li)
    226 {
    227 	li->type = LTYPE_LOGIN;
    228 	return login_write(li);
    229 }
    230 
    231 
    232 /* login_logout(struct logininfo *)     - Record a logout
    233  *
    234  * Call as with login_login()
    235  *
    236  * Returns:
    237  *  >0 if successful
    238  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
    239  */
    240 int
    241 login_logout(struct logininfo *li)
    242 {
    243 	li->type = LTYPE_LOGOUT;
    244 	return login_write(li);
    245 }
    246 
    247 /* login_get_lastlog_time(int)           - Retrieve the last login time
    248  *
    249  * Retrieve the last login time for the given uid. Will try to use the
    250  * system lastlog facilities if they are available, but will fall back
    251  * to looking in wtmp/wtmpx if necessary
    252  *
    253  * Returns:
    254  *   0 on failure, or if user has never logged in
    255  *   Time in seconds from the epoch if successful
    256  *
    257  * Useful preprocessor symbols:
    258  *   DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
    259  *                    info
    260  *   USE_LASTLOG: If set, indicates the presence of system lastlog
    261  *                facilities. If this and DISABLE_LASTLOG are not set,
    262  *                try to retrieve lastlog information from wtmp/wtmpx.
    263  */
    264 #if 0
    265 unsigned int
    266 login_get_lastlog_time(const int uid)
    267 {
    268 	struct logininfo li;
    269 
    270 	if (login_get_lastlog(&li, uid))
    271 		return li.tv_sec;
    272 	else
    273 		return 0;
    274 }
    275 #endif
    276 
    277 /* login_get_lastlog(struct logininfo *, int)   - Retrieve a lastlog entry
    278  *
    279  * Retrieve a logininfo structure populated (only partially) with
    280  * information from the system lastlog data, or from wtmp/wtmpx if no
    281  * system lastlog information exists.
    282  *
    283  * Note this routine must be given a pre-allocated logininfo.
    284  *
    285  * Returns:
    286  *  >0: A pointer to your struct logininfo if successful
    287  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
    288  *
    289  */
    290 struct logininfo *
    291 login_get_lastlog(struct logininfo *li, const int uid)
    292 {
    293 	struct passwd *pw;
    294 
    295 	(void) memset(li, '\0', sizeof(*li));
    296 	li->uid = uid;
    297 
    298 	/*
    299 	 * If we don't have a 'real' lastlog, we need the username to
    300 	 * reliably search wtmp(x) for the last login (see
    301 	 * wtmp_get_entry().)
    302 	 */
    303 	pw = getpwuid(uid);
    304 	if (pw == NULL)
    305 		fatal("login_get_lastlog: Cannot find account for uid %i", uid);
    306 
    307 	/* No MIN_SIZEOF here - we absolutely *must not* truncate the
    308 	 * username */
    309 	(void) strlcpy(li->username, pw->pw_name, sizeof(li->username));
    310 
    311 	if (getlast_entry(li))
    312 		return li;
    313 	else
    314 		return NULL;
    315 }
    316 
    317 
    318 /* login_alloc_entry()    - Allocate and initialise a logininfo
    319  *                           structure
    320  *
    321  * This function creates a new struct logininfo, a data structure
    322  * meant to carry the information required to portably record login info.
    323  *
    324  * Returns a pointer to a newly created struct logininfo. If memory
    325  * allocation fails, the program halts.
    326  */
    327 struct
    328 logininfo *login_alloc_entry(int pid, const char *username,
    329 			     const char *hostname, const char *line,
    330 			     const char *progname)
    331 {
    332 	struct logininfo *newli;
    333 
    334 	newli = (struct logininfo *) xmalloc (sizeof(*newli));
    335 	(void)login_init_entry(newli, pid, username, hostname, line, progname);
    336 	return newli;
    337 }
    338 
    339 
    340 /* login_free_entry(struct logininfo *)    - free struct memory */
    341 void
    342 login_free_entry(struct logininfo *li)
    343 {
    344 	xfree(li);
    345 }
    346 
    347 
    348 /* login_init_entry()
    349  *                                        - initialise a struct logininfo
    350  *
    351  * Populates a new struct logininfo, a data structure meant to carry
    352  * the information required to portably record login info.
    353  *
    354  * Returns: 1
    355  */
    356 int
    357 login_init_entry(struct logininfo *li, int pid, const char *username,
    358 		 const char *hostname, const char *line, const char *progname)
    359 {
    360 	struct passwd *pw;
    361 
    362 	(void) memset(li, 0, sizeof(*li));
    363 
    364 	li->pid = pid;
    365 
    366 	/* set the line information */
    367 	if (line)
    368 		(void) line_fullname(li->line, line, sizeof(li->line));
    369 	else
    370 		li->line_null = 1;
    371 
    372 	if (progname)
    373 		(void) strlcpy(li->progname, progname, sizeof(li->progname));
    374 	else
    375 		li->progname_null = 1;
    376 
    377 	if (username) {
    378 		(void) strlcpy(li->username, username, sizeof(li->username));
    379 		pw = getpwnam(li->username);
    380 		if (pw == NULL)
    381 			fatal("login_init_entry: Cannot find user \"%s\"", li->username);
    382 		li->uid = pw->pw_uid;
    383 	}
    384 
    385 	if (hostname)
    386 		(void) strlcpy(li->hostname, hostname, sizeof(li->hostname));
    387 
    388 	return 1;
    389 }
    390 
    391 /* login_set_current_time(struct logininfo *)    - set the current time
    392  *
    393  * Set the current time in a logininfo structure. This function is
    394  * meant to eliminate the need to deal with system dependencies for
    395  * time handling.
    396  */
    397 void
    398 login_set_current_time(struct logininfo *li)
    399 {
    400 	struct timeval tv;
    401 
    402 	(void) gettimeofday(&tv, NULL);
    403 
    404 	li->tv_sec = tv.tv_sec;
    405 	li->tv_usec = tv.tv_usec;
    406 }
    407 
    408 /* copy a sockaddr_* into our logininfo */
    409 void
    410 login_set_addr(struct logininfo *li, const struct sockaddr *sa,
    411 	       const unsigned int sa_size)
    412 {
    413 	unsigned int bufsize = sa_size;
    414 
    415 	/* make sure we don't overrun our union */
    416 	if (sizeof(li->hostaddr) < sa_size)
    417 		bufsize = sizeof(li->hostaddr);
    418 
    419 	(void) memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
    420 }
    421 
    422 
    423 /**
    424  ** login_write: Call low-level recording functions based on autoconf
    425  ** results
    426  **/
    427 int
    428 login_write (struct logininfo *li)
    429 {
    430 #ifndef HAVE_CYGWIN
    431 	if ((int)geteuid() != 0) {
    432 	  log("Attempt to write login records by non-root user (aborting)");
    433 	  return 1;
    434 	}
    435 #endif
    436 
    437 	/* set the timestamp */
    438 	login_set_current_time(li);
    439 #ifdef USE_LOGIN
    440 	syslogin_write_entry(li);
    441 #endif
    442 #ifdef USE_LASTLOG
    443 	if (li->type == LTYPE_LOGIN) {
    444 		(void) lastlog_write_entry(li);
    445 	}
    446 #endif
    447 #ifdef USE_UTMP
    448 	utmp_write_entry(li);
    449 #endif
    450 #ifdef USE_WTMP
    451 	wtmp_write_entry(li);
    452 #endif
    453 #ifdef USE_UTMPX
    454 	(void) utmpx_write_entry(li);
    455 #endif
    456 #ifdef USE_WTMPX
    457 	(void) wtmpx_write_entry(li);
    458 #endif
    459 	return 0;
    460 }
    461 
    462 #ifdef LOGIN_NEEDS_UTMPX
    463 int
    464 login_utmp_only(struct logininfo *li)
    465 {
    466 	li->type = LTYPE_LOGIN;
    467 	login_set_current_time(li);
    468 # ifdef USE_UTMP
    469 	utmp_write_entry(li);
    470 # endif
    471 # ifdef USE_WTMP
    472 	wtmp_write_entry(li);
    473 # endif
    474 # ifdef USE_UTMPX
    475 	(void) utmpx_write_entry(li);
    476 # endif
    477 # ifdef USE_WTMPX
    478 	(void) wtmpx_write_entry(li);
    479 # endif
    480 	return 0;
    481 }
    482 #endif
    483 
    484 /**
    485  ** getlast_entry: Call low-level functions to retrieve the last login
    486  **                time.
    487  **/
    488 
    489 /* take the uid in li and return the last login time */
    490 int
    491 getlast_entry(struct logininfo *li)
    492 {
    493 #ifdef USE_LASTLOG
    494 	return(lastlog_get_entry(li));
    495 #else /* !USE_LASTLOG */
    496 
    497 #ifdef DISABLE_LASTLOG
    498 	/* On some systems we shouldn't even try to obtain last login
    499 	 * time, e.g. AIX */
    500 	return 0;
    501 # else /* DISABLE_LASTLOG */
    502 	/* Try to retrieve the last login time from wtmp */
    503 #  if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
    504 	/* retrieve last login time from utmp */
    505 	return (wtmp_get_entry(li));
    506 #  else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */
    507 	/* If wtmp isn't available, try wtmpx */
    508 #   if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
    509 	/* retrieve last login time from utmpx */
    510 	return (wtmpx_get_entry(li));
    511 #   else
    512 	/* Give up: No means of retrieving last login time */
    513 	return 0;
    514 #   endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
    515 #  endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
    516 # endif /* DISABLE_LASTLOG */
    517 #endif /* USE_LASTLOG */
    518 }
    519 
    520 
    521 
    522 /*
    523  * 'line' string utility functions
    524  *
    525  * These functions process the 'line' string into one of three forms:
    526  *
    527  * 1. The full filename (including '/dev')
    528  * 2. The stripped name (excluding '/dev')
    529  * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
    530  *                               /dev/pts/1  -> ts/1 )
    531  *
    532  * Form 3 is used on some systems to identify a .tmp.? entry when
    533  * attempting to remove it. Typically both addition and removal is
    534  * performed by one application - say, sshd - so as long as the choice
    535  * uniquely identifies a terminal it's ok.
    536  */
    537 
    538 
    539 /* line_fullname(): add the leading '/dev/' if it doesn't exist make
    540  * sure dst has enough space, if not just copy src (ugh) */
    541 char *
    542 line_fullname(char *dst, const char *src, int dstsize)
    543 {
    544 	(void) memset(dst, '\0', dstsize);
    545 	/* "sshd" is special, like "ftp" */
    546 	if (strcmp(src, "sshd") ||
    547 	    ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))) {
    548 		(void) strlcpy(dst, src, dstsize);
    549 	} else {
    550 		(void) strlcpy(dst, "/dev/", dstsize);
    551 		(void) strlcat(dst, src, dstsize);
    552 	}
    553 	return dst;
    554 }
    555 
    556 /* line_stripname(): strip the leading '/dev' if it exists, return dst */
    557 char *
    558 line_stripname(char *dst, const char *src, int dstsize)
    559 {
    560 	(void) memset(dst, '\0', dstsize);
    561 	if (strncmp(src, "/dev/", 5) == 0)
    562 		(void) strlcpy(dst, src + 5, dstsize);
    563 	else
    564 		(void) strlcpy(dst, src, dstsize);
    565 	return dst;
    566 }
    567 
    568 /* line_abbrevname(): Return the abbreviated (usually four-character)
    569  * form of the line (Just use the last <dstsize> characters of the
    570  * full name.)
    571  *
    572  * NOTE: use strncpy because we do NOT necessarily want zero
    573  * termination */
    574 char *
    575 line_abbrevname(char *dst, const char *src, int dstsize)
    576 {
    577 	size_t len;
    578 
    579 	(void) memset(dst, '\0', dstsize);
    580 
    581 	/* Always skip prefix if present */
    582 	if (strncmp(src, "/dev/", 5) == 0)
    583 		src += 5;
    584 
    585 #ifdef WITH_ABBREV_NO_TTY
    586 	if (strncmp(src, "tty", 3) == 0)
    587 		src += 3;
    588 #endif
    589 
    590 	len = strlen(src);
    591 
    592 	if (len > 0) {
    593 		if (((int)len - dstsize) > 0)
    594 			src +=  ((int)len - dstsize);
    595 
    596 		/* note: _don't_ change this to strlcpy */
    597 		(void) strncpy(dst, src, (size_t)dstsize);
    598 	}
    599 
    600 	return dst;
    601 }
    602 
    603 /**
    604  ** utmp utility functions
    605  **
    606  ** These functions manipulate struct utmp, taking system differences
    607  ** into account.
    608  **/
    609 
    610 #if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
    611 
    612 /* build the utmp structure */
    613 void
    614 set_utmp_time(struct logininfo *li, struct utmp *ut)
    615 {
    616 # ifdef HAVE_TV_IN_UTMP
    617 	ut->ut_tv.tv_sec = li->tv_sec;
    618 	ut->ut_tv.tv_usec = li->tv_usec;
    619 # else
    620 #  ifdef HAVE_TIME_IN_UTMP
    621 	ut->ut_time = li->tv_sec;
    622 #  endif
    623 # endif
    624 }
    625 
    626 void
    627 construct_utmp(struct logininfo *li,
    628 		    struct utmp *ut)
    629 {
    630 	(void) memset(ut, '\0', sizeof(*ut));
    631 
    632 	/* First fill out fields used for both logins and logouts */
    633 
    634 # ifdef HAVE_ID_IN_UTMP
    635 	(void) line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
    636 # endif
    637 
    638 # ifdef HAVE_TYPE_IN_UTMP
    639 	/* This is done here to keep utmp constants out of struct logininfo */
    640 	switch (li->type) {
    641 	case LTYPE_LOGIN:
    642 		ut->ut_type = USER_PROCESS;
    643 #ifdef _UNICOS
    644 		cray_set_tmpdir(ut);
    645 #endif
    646 		break;
    647 	case LTYPE_LOGOUT:
    648 		ut->ut_type = DEAD_PROCESS;
    649 #ifdef _UNICOS
    650 		cray_retain_utmp(ut, li->pid);
    651 #endif
    652 		break;
    653 	}
    654 # endif
    655 	set_utmp_time(li, ut);
    656 
    657 	(void) line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
    658 
    659 # ifdef HAVE_PID_IN_UTMP
    660 	ut->ut_pid = li->pid;
    661 # endif
    662 
    663 	/* If we're logging out, leave all other fields blank */
    664 	if (li->type == LTYPE_LOGOUT)
    665 	  return;
    666 
    667 	/*
    668 	 * These fields are only used when logging in, and are blank
    669 	 * for logouts.
    670 	 */
    671 
    672 	/* Use strncpy because we don't necessarily want null termination */
    673 	(void) strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
    674 # ifdef HAVE_HOST_IN_UTMP
    675 	(void) strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
    676 # endif
    677 # ifdef HAVE_ADDR_IN_UTMP
    678 	/* this is just a 32-bit IP address */
    679 	if (li->hostaddr.sa.sa_family == AF_INET)
    680 		ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
    681 # endif
    682 }
    683 #endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
    684 
    685 /**
    686  ** utmpx utility functions
    687  **
    688  ** These functions manipulate struct utmpx, accounting for system
    689  ** variations.
    690  **/
    691 
    692 #if defined(USE_UTMPX) || defined (USE_WTMPX)
    693 /* build the utmpx structure */
    694 void
    695 set_utmpx_time(struct logininfo *li, struct utmpx *utx)
    696 {
    697 # ifdef HAVE_TV_IN_UTMPX
    698 	utx->ut_tv.tv_sec = li->tv_sec;
    699 	utx->ut_tv.tv_usec = li->tv_usec;
    700 # else /* HAVE_TV_IN_UTMPX */
    701 #  ifdef HAVE_TIME_IN_UTMPX
    702 	utx->ut_time = li->tv_sec;
    703 #  endif /* HAVE_TIME_IN_UTMPX */
    704 # endif /* HAVE_TV_IN_UTMPX */
    705 }
    706 
    707 void
    708 construct_utmpx(struct logininfo *li, struct utmpx *utx)
    709 {
    710 	(void) memset(utx, '\0', sizeof(*utx));
    711 # ifdef HAVE_ID_IN_UTMPX
    712 	(void) line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
    713 # endif
    714 
    715 	/* this is done here to keep utmp constants out of loginrec.h */
    716 	switch (li->type) {
    717 	case LTYPE_LOGIN:
    718 		utx->ut_type = USER_PROCESS;
    719 		break;
    720 	case LTYPE_LOGOUT:
    721 		utx->ut_type = DEAD_PROCESS;
    722 		break;
    723 	}
    724 	if (!li->line_null)
    725 		(void) line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
    726 	else if (!li->progname_null)
    727 		(void) line_stripname(utx->ut_line, li->progname, sizeof(utx->ut_line));
    728 
    729 	set_utmpx_time(li, utx);
    730 	utx->ut_pid = li->pid;
    731 	/* strncpy(): Don't necessarily want null termination */
    732 	(void) strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
    733 
    734 	if (li->type == LTYPE_LOGOUT)
    735 		return;
    736 
    737 	/*
    738 	 * These fields are only used when logging in, and are blank
    739 	 * for logouts.
    740 	 */
    741 
    742 # ifdef HAVE_HOST_IN_UTMPX
    743 	(void) strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
    744 # endif
    745 # ifdef HAVE_ADDR_IN_UTMPX
    746 	/* this is just a 32-bit IP address */
    747 	if (li->hostaddr.sa.sa_family == AF_INET)
    748 		utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
    749 # endif
    750 # ifdef HAVE_SYSLEN_IN_UTMPX
    751 	/* ut_syslen is the length of the utx_host string */
    752 	utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
    753 # endif
    754 }
    755 #endif /* USE_UTMPX || USE_WTMPX */
    756 
    757 /**
    758  ** Low-level utmp functions
    759  **/
    760 
    761 /* FIXME: (ATL) utmp_write_direct needs testing */
    762 #ifdef USE_UTMP
    763 
    764 /* if we can, use pututline() etc. */
    765 # if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
    766 	defined(HAVE_PUTUTLINE)
    767 #  define UTMP_USE_LIBRARY
    768 # endif
    769 
    770 
    771 /* write a utmp entry with the system's help (pututline() and pals) */
    772 # ifdef UTMP_USE_LIBRARY
    773 static int
    774 utmp_write_library(struct logininfo *li, struct utmp *ut)
    775 {
    776 	setutent();
    777 	pututline(ut);
    778 
    779 #  ifdef HAVE_ENDUTENT
    780 	endutent();
    781 #  endif
    782 	return 1;
    783 }
    784 # else /* UTMP_USE_LIBRARY */
    785 
    786 /* write a utmp entry direct to the file */
    787 /* This is a slightly modification of code in OpenBSD's login.c */
    788 static int
    789 utmp_write_direct(struct logininfo *li, struct utmp *ut)
    790 {
    791 	struct utmp old_ut;
    792 	register int fd;
    793 	int tty;
    794 
    795 	/* FIXME: (ATL) ttyslot() needs local implementation */
    796 
    797 #if defined(HAVE_GETTTYENT)
    798 	register struct ttyent *ty;
    799 
    800 	tty=0;
    801 
    802 	setttyent();
    803 	while ((struct ttyent *)0 != (ty = getttyent())) {
    804 		tty++;
    805 		if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
    806 			break;
    807 	}
    808 	endttyent();
    809 
    810 	if((struct ttyent *)0 == ty) {
    811 		log("utmp_write_entry: tty not found");
    812 		return(1);
    813 	}
    814 #else /* FIXME */
    815 
    816 	tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
    817 
    818 #endif /* HAVE_GETTTYENT */
    819 
    820 	if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
    821 		(void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
    822 		/*
    823 		 * Prevent luser from zero'ing out ut_host.
    824 		 * If the new ut_line is empty but the old one is not
    825 		 * and ut_line and ut_name match, preserve the old ut_line.
    826 		 */
    827 		if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
    828 			(ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
    829 			(strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
    830 			(strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
    831 			(void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
    832 		}
    833 
    834 		(void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
    835 		if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut))
    836 			log("utmp_write_direct: error writing %s: %s",
    837 			    UTMP_FILE, strerror(errno));
    838 
    839 		(void)close(fd);
    840 		return 1;
    841 	} else {
    842 		return 0;
    843 	}
    844 }
    845 # endif /* UTMP_USE_LIBRARY */
    846 
    847 static int
    848 utmp_perform_login(struct logininfo *li)
    849 {
    850 	struct utmp ut;
    851 
    852 	construct_utmp(li, &ut);
    853 # ifdef UTMP_USE_LIBRARY
    854 	if (!utmp_write_library(li, &ut)) {
    855 		log("utmp_perform_login: utmp_write_library() failed");
    856 		return 0;
    857 	}
    858 # else
    859 	if (!utmp_write_direct(li, &ut)) {
    860 		log("utmp_perform_login: utmp_write_direct() failed");
    861 		return 0;
    862 	}
    863 # endif
    864 	return 1;
    865 }
    866 
    867 
    868 static int
    869 utmp_perform_logout(struct logininfo *li)
    870 {
    871 	struct utmp ut;
    872 
    873 	construct_utmp(li, &ut);
    874 # ifdef UTMP_USE_LIBRARY
    875 	if (!utmp_write_library(li, &ut)) {
    876 		log("utmp_perform_logout: utmp_write_library() failed");
    877 		return 0;
    878 	}
    879 # else
    880 	if (!utmp_write_direct(li, &ut)) {
    881 		log("utmp_perform_logout: utmp_write_direct() failed");
    882 		return 0;
    883 	}
    884 # endif
    885 	return 1;
    886 }
    887 
    888 
    889 int
    890 utmp_write_entry(struct logininfo *li)
    891 {
    892 	if (li->line_null) {
    893 		debug3("not writing utmp entry");
    894 		return 1;
    895 	}
    896 	debug3("writing utmp entry");
    897 
    898 	switch(li->type) {
    899 	case LTYPE_LOGIN:
    900 		return utmp_perform_login(li);
    901 
    902 	case LTYPE_LOGOUT:
    903 		return utmp_perform_logout(li);
    904 
    905 	default:
    906 		log("utmp_write_entry: invalid type field");
    907 		return 0;
    908 	}
    909 }
    910 #endif /* USE_UTMP */
    911 
    912 
    913 /**
    914  ** Low-level utmpx functions
    915  **/
    916 
    917 /* not much point if we don't want utmpx entries */
    918 #ifdef USE_UTMPX
    919 
    920 /* if we have the wherewithall, use pututxline etc. */
    921 # if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
    922 	defined(HAVE_PUTUTXLINE)
    923 #  define UTMPX_USE_LIBRARY
    924 # endif
    925 
    926 
    927 /* write a utmpx entry with the system's help (pututxline() and pals) */
    928 # ifdef UTMPX_USE_LIBRARY
    929 static int
    930 utmpx_write_library(struct logininfo *li, struct utmpx *utx)
    931 {
    932 	setutxent();
    933 	(void) pututxline(utx);
    934 
    935 #  ifdef HAVE_ENDUTXENT
    936 	endutxent();
    937 #  endif
    938 	return 1;
    939 }
    940 
    941 # else /* UTMPX_USE_LIBRARY */
    942 
    943 /* write a utmp entry direct to the file */
    944 static int
    945 utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
    946 {
    947 	log("utmpx_write_direct: not implemented!");
    948 	return 0;
    949 }
    950 # endif /* UTMPX_USE_LIBRARY */
    951 
    952 static int
    953 utmpx_perform_login(struct logininfo *li)
    954 {
    955 	struct utmpx utx;
    956 
    957 	construct_utmpx(li, &utx);
    958 # ifdef UTMPX_USE_LIBRARY
    959 	if (!utmpx_write_library(li, &utx)) {
    960 		log("tmpx_perform_login: utmp_write_library() failed");
    961 		return 0;
    962 	}
    963 # else
    964 	if (!utmpx_write_direct(li, &ut)) {
    965 		log("utmpx_perform_login: utmp_write_direct() failed");
    966 		return 0;
    967 	}
    968 # endif
    969 	return 1;
    970 }
    971 
    972 
    973 static int
    974 utmpx_perform_logout(struct logininfo *li)
    975 {
    976 	struct utmpx utx;
    977 
    978 	construct_utmpx(li, &utx);
    979 # ifdef HAVE_ID_IN_UTMPX
    980 	(void) line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
    981 # endif
    982 # ifdef HAVE_TYPE_IN_UTMPX
    983 	utx.ut_type = DEAD_PROCESS;
    984 # endif
    985 
    986 # ifdef UTMPX_USE_LIBRARY
    987 	(void) utmpx_write_library(li, &utx);
    988 # else
    989 	utmpx_write_direct(li, &utx);
    990 # endif
    991 	return 1;
    992 }
    993 
    994 int
    995 utmpx_write_entry(struct logininfo *li)
    996 {
    997 	if (li->line_null) {
    998 		debug3("not writing utmpx entry");
    999 		return 1;
   1000 	}
   1001 	debug3("writing utmpx entry");
   1002 
   1003 	switch(li->type) {
   1004 	case LTYPE_LOGIN:
   1005 		return utmpx_perform_login(li);
   1006 	case LTYPE_LOGOUT:
   1007 		return utmpx_perform_logout(li);
   1008 	default:
   1009 		log("utmpx_write_entry: invalid type field");
   1010 		return 0;
   1011 	}
   1012 }
   1013 #endif /* USE_UTMPX */
   1014 
   1015 
   1016 /**
   1017  ** Low-level wtmp functions
   1018  **/
   1019 
   1020 #ifdef USE_WTMP
   1021 
   1022 /* write a wtmp entry direct to the end of the file */
   1023 /* This is a slight modification of code in OpenBSD's logwtmp.c */
   1024 static int
   1025 wtmp_write(struct logininfo *li, struct utmp *ut)
   1026 {
   1027 	struct stat buf;
   1028 	int fd, ret = 1;
   1029 
   1030 	if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
   1031 		log("wtmp_write: problem writing %s: %s",
   1032 		    WTMP_FILE, strerror(errno));
   1033 		return 0;
   1034 	}
   1035 	if (fstat(fd, &buf) == 0)
   1036 		if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
   1037 			(void) ftruncate(fd, buf.st_size);
   1038 			log("wtmp_write: problem writing %s: %s",
   1039 			    WTMP_FILE, strerror(errno));
   1040 			ret = 0;
   1041 		}
   1042 	(void)close(fd);
   1043 	return ret;
   1044 }
   1045 
   1046 static int
   1047 wtmp_perform_login(struct logininfo *li)
   1048 {
   1049 	struct utmp ut;
   1050 
   1051 	construct_utmp(li, &ut);
   1052 	return wtmp_write(li, &ut);
   1053 }
   1054 
   1055 
   1056 static int
   1057 wtmp_perform_logout(struct logininfo *li)
   1058 {
   1059 	struct utmp ut;
   1060 
   1061 	construct_utmp(li, &ut);
   1062 	return wtmp_write(li, &ut);
   1063 }
   1064 
   1065 
   1066 int
   1067 wtmp_write_entry(struct logininfo *li)
   1068 {
   1069 	switch(li->type) {
   1070 	case LTYPE_LOGIN:
   1071 		return wtmp_perform_login(li);
   1072 	case LTYPE_LOGOUT:
   1073 		return wtmp_perform_logout(li);
   1074 	default:
   1075 		log("wtmp_write_entry: invalid type field");
   1076 		return 0;
   1077 	}
   1078 }
   1079 
   1080 
   1081 /* Notes on fetching login data from wtmp/wtmpx
   1082  *
   1083  * Logouts are usually recorded with (amongst other things) a blank
   1084  * username on a given tty line.  However, some systems (HP-UX is one)
   1085  * leave all fields set, but change the ut_type field to DEAD_PROCESS.
   1086  *
   1087  * Since we're only looking for logins here, we know that the username
   1088  * must be set correctly. On systems that leave it in, we check for
   1089  * ut_type==USER_PROCESS (indicating a login.)
   1090  *
   1091  * Portability: Some systems may set something other than USER_PROCESS
   1092  * to indicate a login process. I don't know of any as I write. Also,
   1093  * it's possible that some systems may both leave the username in
   1094  * place and not have ut_type.
   1095  */
   1096 
   1097 /* return true if this wtmp entry indicates a login */
   1098 static int
   1099 wtmp_islogin(struct logininfo *li, struct utmp *ut)
   1100 {
   1101 	if (strncmp(li->username, ut->ut_name,
   1102 		MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
   1103 # ifdef HAVE_TYPE_IN_UTMP
   1104 		if (ut->ut_type & USER_PROCESS)
   1105 			return 1;
   1106 # else
   1107 		return 1;
   1108 # endif
   1109 	}
   1110 	return 0;
   1111 }
   1112 
   1113 int
   1114 wtmp_get_entry(struct logininfo *li)
   1115 {
   1116 	struct stat st;
   1117 	struct utmp ut;
   1118 	int fd, found=0;
   1119 
   1120 	/* Clear the time entries in our logininfo */
   1121 	li->tv_sec = li->tv_usec = 0;
   1122 
   1123 	if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
   1124 		log("wtmp_get_entry: problem opening %s: %s",
   1125 		    WTMP_FILE, strerror(errno));
   1126 		return 0;
   1127 	}
   1128 	if (fstat(fd, &st) != 0) {
   1129 		log("wtmp_get_entry: couldn't stat %s: %s",
   1130 		    WTMP_FILE, strerror(errno));
   1131 		(void) close(fd);
   1132 		return 0;
   1133 	}
   1134 
   1135 	/* Seek to the start of the last struct utmp */
   1136 	if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
   1137 		/* Looks like we've got a fresh wtmp file */
   1138 		(void) close(fd);
   1139 		return 0;
   1140 	}
   1141 
   1142 	while (!found) {
   1143 		if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
   1144 			log("wtmp_get_entry: read of %s failed: %s",
   1145 			    WTMP_FILE, strerror(errno));
   1146 			(void) close (fd);
   1147 			return 0;
   1148 		}
   1149 		if ( wtmp_islogin(li, &ut) ) {
   1150 			found = 1;
   1151 			/* We've already checked for a time in struct
   1152 			 * utmp, in login_getlast(). */
   1153 # ifdef HAVE_TIME_IN_UTMP
   1154 			li->tv_sec = ut.ut_time;
   1155 # else
   1156 #  if HAVE_TV_IN_UTMP
   1157 			li->tv_sec = ut.ut_tv.tv_sec;
   1158 #  endif
   1159 # endif
   1160 			(void) line_fullname(li->line, ut.ut_line,
   1161 				      MIN_SIZEOF(li->line, ut.ut_line));
   1162 # ifdef HAVE_HOST_IN_UTMP
   1163 			(void) strlcpy(li->hostname, ut.ut_host,
   1164 				MIN_SIZEOF(li->hostname, ut.ut_host));
   1165 # endif
   1166 			continue;
   1167 		}
   1168 		/* Seek back 2 x struct utmp */
   1169 		if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
   1170 			/* We've found the start of the file, so quit */
   1171 			(void) close (fd);
   1172 			return 0;
   1173 		}
   1174 	}
   1175 
   1176 	/* We found an entry. Tidy up and return */
   1177 	(void) close(fd);
   1178 	return 1;
   1179 }
   1180 # endif /* USE_WTMP */
   1181 
   1182 
   1183 /**
   1184  ** Low-level wtmpx functions
   1185  **/
   1186 
   1187 #ifdef USE_WTMPX
   1188 /* write a wtmpx entry direct to the end of the file */
   1189 /* This is a slight modification of code in OpenBSD's logwtmp.c */
   1190 static int
   1191 wtmpx_write(struct logininfo *li, struct utmpx *utx)
   1192 {
   1193 	struct stat buf;
   1194 	int fd, ret = 1;
   1195 
   1196 	if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
   1197 		log("wtmpx_write: problem opening %s: %s",
   1198 		    WTMPX_FILE, strerror(errno));
   1199 		return 0;
   1200 	}
   1201 
   1202 	if (fstat(fd, &buf) == 0)
   1203 		if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
   1204 			(void) ftruncate(fd, buf.st_size);
   1205 			log("wtmpx_write: problem writing %s: %s",
   1206 			    WTMPX_FILE, strerror(errno));
   1207 			ret = 0;
   1208 		}
   1209 	(void)close(fd);
   1210 
   1211 	return ret;
   1212 }
   1213 
   1214 
   1215 static int
   1216