Home | History | Annotate | Download | only in openssh
      1 /*
      2  * Copyright (c) 2000 Andre Lucas.  All rights reserved.
      3  * Portions copyright (c) 1998 Todd C. Miller
      4  * Portions copyright (c) 1996 Jason Downs
      5  * Portions copyright (c) 1996 Theo de Raadt
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 /*
     29  * The btmp logging code is derived from login.c from util-linux and is under
     30  * the the following license:
     31  *
     32  * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
     33  * All rights reserved.
     34  *
     35  * Redistribution and use in source and binary forms are permitted
     36  * provided that the above copyright notice and this paragraph are
     37  * duplicated in all such forms and that any documentation,
     38  * advertising materials, and other materials related to such
     39  * distribution and use acknowledge that the software was developed
     40  * by the University of California, Berkeley.  The name of the
     41  * University may not be used to endorse or promote products derived
     42  * from this software without specific prior written permission.
     43  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
     44  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
     45  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     46  */
     47 
     48 
     49 /**
     50  ** loginrec.c:  platform-independent login recording and lastlog retrieval
     51  **/
     52 
     53 /*
     54  *  The new login code explained
     55  *  ============================
     56  *
     57  *  This code attempts to provide a common interface to login recording
     58  *  (utmp and friends) and last login time retrieval.
     59  *
     60  *  Its primary means of achieving this is to use 'struct logininfo', a
     61  *  union of all the useful fields in the various different types of
     62  *  system login record structures one finds on UNIX variants.
     63  *
     64  *  We depend on autoconf to define which recording methods are to be
     65  *  used, and which fields are contained in the relevant data structures
     66  *  on the local system. Many C preprocessor symbols affect which code
     67  *  gets compiled here.
     68  *
     69  *  The code is designed to make it easy to modify a particular
     70  *  recording method, without affecting other methods nor requiring so
     71  *  many nested conditional compilation blocks as were commonplace in
     72  *  the old code.
     73  *
     74  *  For login recording, we try to use the local system's libraries as
     75  *  these are clearly most likely to work correctly. For utmp systems
     76  *  this usually means login() and logout() or setutent() etc., probably
     77  *  in libutil, along with logwtmp() etc. On these systems, we fall back
     78  *  to writing the files directly if we have to, though this method
     79  *  requires very thorough testing so we do not corrupt local auditing
     80  *  information. These files and their access methods are very system
     81  *  specific indeed.
     82  *
     83  *  For utmpx systems, the corresponding library functions are
     84  *  setutxent() etc. To the author's knowledge, all utmpx systems have
     85  *  these library functions and so no direct write is attempted. If such
     86  *  a system exists and needs support, direct analogues of the [uw]tmp
     87  *  code should suffice.
     88  *
     89  *  Retrieving the time of last login ('lastlog') is in some ways even
     90  *  more problemmatic than login recording. Some systems provide a
     91  *  simple table of all users which we seek based on uid and retrieve a
     92  *  relatively standard structure. Others record the same information in
     93  *  a directory with a separate file, and others don't record the
     94  *  information separately at all. For systems in the latter category,
     95  *  we look backwards in the wtmp or wtmpx file for the last login entry
     96  *  for our user. Naturally this is slower and on busy systems could
     97  *  incur a significant performance penalty.
     98  *
     99  *  Calling the new code
    100  *  --------------------
    101  *
    102  *  In OpenSSH all login recording and retrieval is performed in
    103  *  login.c. Here you'll find working examples. Also, in the logintest.c
    104  *  program there are more examples.
    105  *
    106  *  Internal handler calling method
    107  *  -------------------------------
    108  *
    109  *  When a call is made to login_login() or login_logout(), both
    110  *  routines set a struct logininfo flag defining which action (log in,
    111  *  or log out) is to be taken. They both then call login_write(), which
    112  *  calls whichever of the many structure-specific handlers autoconf
    113  *  selects for the local system.
    114  *
    115  *  The handlers themselves handle system data structure specifics. Both
    116  *  struct utmp and struct utmpx have utility functions (see
    117  *  construct_utmp*()) to try to make it simpler to add extra systems
    118  *  that introduce new features to either structure.
    119  *
    120  *  While it may seem terribly wasteful to replicate so much similar
    121  *  code for each method, experience has shown that maintaining code to
    122  *  write both struct utmp and utmpx in one function, whilst maintaining
    123  *  support for all systems whether they have library support or not, is
    124  *  a difficult and time-consuming task.
    125  *
    126  *  Lastlog support proceeds similarly. Functions login_get_lastlog()
    127  *  (and its OpenSSH-tuned friend login_get_lastlog_time()) call
    128  *  getlast_entry(), which tries one of three methods to find the last
    129  *  login time. It uses local system lastlog support if it can,
    130  *  otherwise it tries wtmp or wtmpx before giving up and returning 0,
    131  *  meaning "tilt".
    132  *
    133  *  Maintenance
    134  *  -----------
    135  *
    136  *  In many cases it's possible to tweak autoconf to select the correct
    137  *  methods for a particular platform, either by improving the detection
    138  *  code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE
    139  *  symbols for the platform.
    140  *
    141  *  Use logintest to check which symbols are defined before modifying
    142  *  configure.ac and loginrec.c. (You have to build logintest yourself
    143  *  with 'make logintest' as it's not built by default.)
    144  *
    145  *  Otherwise, patches to the specific method(s) are very helpful!
    146  */
    147 
    148 #include "includes.h"
    149 
    150 #include <sys/types.h>
    151 #include <sys/stat.h>
    152 #include <sys/socket.h>
    153 
    154 #include <netinet/in.h>
    155 
    156 #include <errno.h>
    157 #include <fcntl.h>
    158 #ifdef HAVE_PATHS_H
    159 # include <paths.h>
    160 #endif
    161 #include <pwd.h>
    162 #include <stdarg.h>
    163 #include <string.h>
    164 #include <time.h>
    165 #include <unistd.h>
    166 
    167 #include "xmalloc.h"
    168 #include "key.h"
    169 #include "hostfile.h"
    170 #include "ssh.h"
    171 #include "loginrec.h"
    172 #include "log.h"
    173 #include "atomicio.h"
    174 #include "packet.h"
    175 #include "canohost.h"
    176 #include "auth.h"
    177 #include "buffer.h"
    178 
    179 #ifdef HAVE_UTIL_H
    180 # include <util.h>
    181 #endif
    182 
    183 #ifdef HAVE_LIBUTIL_H
    184 # include <libutil.h>
    185 #endif
    186 
    187 /**
    188  ** prototypes for helper functions in this file
    189  **/
    190 
    191 #if HAVE_UTMP_H
    192 void set_utmp_time(struct logininfo *li, struct utmp *ut);
    193 void construct_utmp(struct logininfo *li, struct utmp *ut);
    194 #endif
    195 
    196 #ifdef HAVE_UTMPX_H
    197 void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
    198 void construct_utmpx(struct logininfo *li, struct utmpx *ut);
    199 #endif
    200 
    201 int utmp_write_entry(struct logininfo *li);
    202 int utmpx_write_entry(struct logininfo *li);
    203 int wtmp_write_entry(struct logininfo *li);
    204 int wtmpx_write_entry(struct logininfo *li);
    205 int lastlog_write_entry(struct logininfo *li);
    206 int syslogin_write_entry(struct logininfo *li);
    207 
    208 int getlast_entry(struct logininfo *li);
    209 int lastlog_get_entry(struct logininfo *li);
    210 int utmpx_get_entry(struct logininfo *li);
    211 int wtmp_get_entry(struct logininfo *li);
    212 int wtmpx_get_entry(struct logininfo *li);
    213 
    214 extern Buffer loginmsg;
    215 
    216 /* pick the shortest string */
    217 #define MIN_SIZEOF(s1,s2) (sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2))
    218 
    219 /**
    220  ** platform-independent login functions
    221  **/
    222 
    223 /*
    224  * login_login(struct logininfo *) - Record a login
    225  *
    226  * Call with a pointer to a struct logininfo initialised with
    227  * login_init_entry() or login_alloc_entry()
    228  *
    229  * Returns:
    230  *  >0 if successful
    231  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
    232  */
    233 int
    234 login_login(struct logininfo *li)
    235 {
    236 	li->type = LTYPE_LOGIN;
    237 	return (login_write(li));
    238 }
    239 
    240 
    241 /*
    242  * login_logout(struct logininfo *) - Record a logout
    243  *
    244  * Call as with login_login()
    245  *
    246  * Returns:
    247  *  >0 if successful
    248  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
    249  */
    250 int
    251 login_logout(struct logininfo *li)
    252 {
    253 	li->type = LTYPE_LOGOUT;
    254 	return (login_write(li));
    255 }
    256 
    257 /*
    258  * login_get_lastlog_time(int) - Retrieve the last login time
    259  *
    260  * Retrieve the last login time for the given uid. Will try to use the
    261  * system lastlog facilities if they are available, but will fall back
    262  * to looking in wtmp/wtmpx if necessary
    263  *
    264  * Returns:
    265  *   0 on failure, or if user has never logged in
    266  *   Time in seconds from the epoch if successful
    267  *
    268  * Useful preprocessor symbols:
    269  *   DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
    270  *                    info
    271  *   USE_LASTLOG: If set, indicates the presence of system lastlog
    272  *                facilities. If this and DISABLE_LASTLOG are not set,
    273  *                try to retrieve lastlog information from wtmp/wtmpx.
    274  */
    275 unsigned int
    276 login_get_lastlog_time(const uid_t uid)
    277 {
    278 	struct logininfo li;
    279 
    280 	if (login_get_lastlog(&li, uid))
    281 		return (li.tv_sec);
    282 	else
    283 		return (0);
    284 }
    285 
    286 /*
    287  * login_get_lastlog(struct logininfo *, int)   - Retrieve a lastlog entry
    288  *
    289  * Retrieve a logininfo structure populated (only partially) with
    290  * information from the system lastlog data, or from wtmp/wtmpx if no
    291  * system lastlog information exists.
    292  *
    293  * Note this routine must be given a pre-allocated logininfo.
    294  *
    295  * Returns:
    296  *  >0: A pointer to your struct logininfo if successful
    297  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
    298  */
    299 struct logininfo *
    300 login_get_lastlog(struct logininfo *li, const uid_t uid)
    301 {
    302 	struct passwd *pw;
    303 
    304 	memset(li, '\0', sizeof(*li));
    305 	li->uid = uid;
    306 
    307 	/*
    308 	 * If we don't have a 'real' lastlog, we need the username to
    309 	 * reliably search wtmp(x) for the last login (see
    310 	 * wtmp_get_entry().)
    311 	 */
    312 	pw = getpwuid(uid);
    313 	if (pw == NULL)
    314 		fatal("%s: Cannot find account for uid %ld", __func__,
    315 		    (long)uid);
    316 
    317 	/* No MIN_SIZEOF here - we absolutely *must not* truncate the
    318 	 * username (XXX - so check for trunc!) */
    319 	strlcpy(li->username, pw->pw_name, sizeof(li->username));
    320 
    321 	if (getlast_entry(li))
    322 		return (li);
    323 	else
    324 		return (NULL);
    325 }
    326 
    327 
    328 /*
    329  * login_alloc_entry(int, char*, char*, char*)    - Allocate and initialise
    330  *                                                  a logininfo structure
    331  *
    332  * This function creates a new struct logininfo, a data structure
    333  * meant to carry the information required to portably record login info.
    334  *
    335  * Returns a pointer to a newly created struct logininfo. If memory
    336  * allocation fails, the program halts.
    337  */
    338 struct
    339 logininfo *login_alloc_entry(pid_t pid, const char *username,
    340     const char *hostname, const char *line)
    341 {
    342 	struct logininfo *newli;
    343 
    344 	newli = xmalloc(sizeof(*newli));
    345 	login_init_entry(newli, pid, username, hostname, line);
    346 	return (newli);
    347 }
    348 
    349 
    350 /* login_free_entry(struct logininfo *)    - free struct memory */
    351 void
    352 login_free_entry(struct logininfo *li)
    353 {
    354 	xfree(li);
    355 }
    356 
    357 
    358 /* login_init_entry(struct logininfo *, int, char*, char*, char*)
    359  *                                        - initialise a struct logininfo
    360  *
    361  * Populates a new struct logininfo, a data structure meant to carry
    362  * the information required to portably record login info.
    363  *
    364  * Returns: 1
    365  */
    366 int
    367 login_init_entry(struct logininfo *li, pid_t pid, const char *username,
    368     const char *hostname, const char *line)
    369 {
    370 	struct passwd *pw;
    371 
    372 	memset(li, 0, sizeof(*li));
    373 
    374 	li->pid = pid;
    375 
    376 	/* set the line information */
    377 	if (line)
    378 		line_fullname(li->line, line, sizeof(li->line));
    379 
    380 	if (username) {
    381 		strlcpy(li->username, username, sizeof(li->username));
    382 		pw = getpwnam(li->username);
    383 		if (pw == NULL) {
    384 			fatal("%s: Cannot find user \"%s\"", __func__,
    385 			    li->username);
    386 		}
    387 		li->uid = pw->pw_uid;
    388 	}
    389 
    390 	if (hostname)
    391 		strlcpy(li->hostname, hostname, sizeof(li->hostname));
    392 
    393 	return (1);
    394 }
    395 
    396 /*
    397  * login_set_current_time(struct logininfo *)    - set the current time
    398  *
    399  * Set the current time in a logininfo structure. This function is
    400  * meant to eliminate the need to deal with system dependencies for
    401  * time handling.
    402  */
    403 void
    404 login_set_current_time(struct logininfo *li)
    405 {
    406 	struct timeval tv;
    407 
    408 	gettimeofday(&tv, NULL);
    409 
    410 	li->tv_sec = tv.tv_sec;
    411 	li->tv_usec = tv.tv_usec;
    412 }
    413 
    414 /* copy a sockaddr_* into our logininfo */
    415 void
    416 login_set_addr(struct logininfo *li, const struct sockaddr *sa,
    417     const unsigned int sa_size)
    418 {
    419 	unsigned int bufsize = sa_size;
    420 
    421 	/* make sure we don't overrun our union */
    422 	if (sizeof(li->hostaddr) < sa_size)
    423 		bufsize = sizeof(li->hostaddr);
    424 
    425 	memcpy(&li->hostaddr.sa, sa, bufsize);
    426 }
    427 
    428 
    429 /**
    430  ** login_write: Call low-level recording functions based on autoconf
    431  ** results
    432  **/
    433 int
    434 login_write(struct logininfo *li)
    435 {
    436 #ifndef HAVE_CYGWIN
    437 	if (geteuid() != 0) {
    438 		logit("Attempt to write login records by non-root user (aborting)");
    439 		return (1);
    440 	}
    441 #endif
    442 
    443 	/* set the timestamp */
    444 	login_set_current_time(li);
    445 #ifdef USE_LOGIN
    446 	syslogin_write_entry(li);
    447 #endif
    448 #ifdef USE_LASTLOG
    449 	if (li->type == LTYPE_LOGIN)
    450 		lastlog_write_entry(li);
    451 #endif
    452 #ifdef USE_UTMP
    453 	utmp_write_entry(li);
    454 #endif
    455 #ifdef USE_WTMP
    456 	wtmp_write_entry(li);
    457 #endif
    458 #ifdef USE_UTMPX
    459 	utmpx_write_entry(li);
    460 #endif
    461 #ifdef USE_WTMPX
    462 	wtmpx_write_entry(li);
    463 #endif
    464 #ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN
    465 	if (li->type == LTYPE_LOGIN &&
    466 	    !sys_auth_record_login(li->username,li->hostname,li->line,
    467 	    &loginmsg))
    468 		logit("Writing login record failed for %s", li->username);
    469 #endif
    470 #ifdef SSH_AUDIT_EVENTS
    471 	if (li->type == LTYPE_LOGIN)
    472 		audit_session_open(li);
    473 	else if (li->type == LTYPE_LOGOUT)
    474 		audit_session_close(li);
    475 #endif
    476 	return (0);
    477 }
    478 
    479 #ifdef LOGIN_NEEDS_UTMPX
    480 int
    481 login_utmp_only(struct logininfo *li)
    482 {
    483 	li->type = LTYPE_LOGIN;
    484 	login_set_current_time(li);
    485 # ifdef USE_UTMP
    486 	utmp_write_entry(li);
    487 # endif
    488 # ifdef USE_WTMP
    489 	wtmp_write_entry(li);
    490 # endif
    491 # ifdef USE_UTMPX
    492 	utmpx_write_entry(li);
    493 # endif
    494 # ifdef USE_WTMPX
    495 	wtmpx_write_entry(li);
    496 # endif
    497 	return (0);
    498 }
    499 #endif
    500 
    501 /**
    502  ** getlast_entry: Call low-level functions to retrieve the last login
    503  **                time.
    504  **/
    505 
    506 /* take the uid in li and return the last login time */
    507 int
    508 getlast_entry(struct logininfo *li)
    509 {
    510 #ifdef USE_LASTLOG
    511 	return(lastlog_get_entry(li));
    512 #else /* !USE_LASTLOG */
    513 #if defined(USE_UTMPX) && defined(HAVE_SETUTXDB) && \
    514     defined(UTXDB_LASTLOGIN) && defined(HAVE_GETUTXUSER)
    515 	return (utmpx_get_entry(li));
    516 #endif
    517 
    518 #if defined(DISABLE_LASTLOG)
    519 	/* On some systems we shouldn't even try to obtain last login
    520 	 * time, e.g. AIX */
    521 	return (0);
    522 # elif defined(USE_WTMP) && \
    523     (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
    524 	/* retrieve last login time from utmp */
    525 	return (wtmp_get_entry(li));
    526 # elif defined(USE_WTMPX) && \
    527     (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
    528 	/* If wtmp isn't available, try wtmpx */
    529 	return (wtmpx_get_entry(li));
    530 # else
    531 	/* Give up: No means of retrieving last login time */
    532 	return (0);
    533 # endif /* DISABLE_LASTLOG */
    534 #endif /* USE_LASTLOG */
    535 }
    536 
    537 
    538 
    539 /*
    540  * 'line' string utility functions
    541  *
    542  * These functions process the 'line' string into one of three forms:
    543  *
    544  * 1. The full filename (including '/dev')
    545  * 2. The stripped name (excluding '/dev')
    546  * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
    547  *                               /dev/pts/1  -> ts/1 )
    548  *
    549  * Form 3 is used on some systems to identify a .tmp.? entry when
    550  * attempting to remove it. Typically both addition and removal is
    551  * performed by one application - say, sshd - so as long as the choice
    552  * uniquely identifies a terminal it's ok.
    553  */
    554 
    555 
    556 /*
    557  * line_fullname(): add the leading '/dev/' if it doesn't exist make
    558  * sure dst has enough space, if not just copy src (ugh)
    559  */
    560 char *
    561 line_fullname(char *dst, const char *src, u_int dstsize)
    562 {
    563 	memset(dst, '\0', dstsize);
    564 	if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))
    565 		strlcpy(dst, src, dstsize);
    566 	else {
    567 		strlcpy(dst, "/dev/", dstsize);
    568 		strlcat(dst, src, dstsize);
    569 	}
    570 	return (dst);
    571 }
    572 
    573 /* line_stripname(): strip the leading '/dev' if it exists, return dst */
    574 char *
    575 line_stripname(char *dst, const char *src, int dstsize)
    576 {
    577 	memset(dst, '\0', dstsize);
    578 	if (strncmp(src, "/dev/", 5) == 0)
    579 		strlcpy(dst, src + 5, dstsize);
    580 	else
    581 		strlcpy(dst, src, dstsize);
    582 	return (dst);
    583 }
    584 
    585 /*
    586  * line_abbrevname(): Return the abbreviated (usually four-character)
    587  * form of the line (Just use the last <dstsize> characters of the
    588  * full name.)
    589  *
    590  * NOTE: use strncpy because we do NOT necessarily want zero
    591  * termination
    592  */
    593 char *
    594 line_abbrevname(char *dst, const char *src, int dstsize)
    595 {
    596 	size_t len;
    597 
    598 	memset(dst, '\0', dstsize);
    599 
    600 	/* Always skip prefix if present */
    601 	if (strncmp(src, "/dev/", 5) == 0)
    602 		src += 5;
    603 
    604 #ifdef WITH_ABBREV_NO_TTY
    605 	if (strncmp(src, "tty", 3) == 0)
    606 		src += 3;
    607 #endif
    608 
    609 	len = strlen(src);
    610 
    611 	if (len > 0) {
    612 		if (((int)len - dstsize) > 0)
    613 			src +=  ((int)len - dstsize);
    614 
    615 		/* note: _don't_ change this to strlcpy */
    616 		strncpy(dst, src, (size_t)dstsize);
    617 	}
    618 
    619 	return (dst);
    620 }
    621 
    622 /**
    623  ** utmp utility functions
    624  **
    625  ** These functions manipulate struct utmp, taking system differences
    626  ** into account.
    627  **/
    628 
    629 #if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
    630 
    631 /* build the utmp structure */
    632 void
    633 set_utmp_time(struct logininfo *li, struct utmp *ut)
    634 {
    635 # if defined(HAVE_TV_IN_UTMP)
    636 	ut->ut_tv.tv_sec = li->tv_sec;
    637 	ut->ut_tv.tv_usec = li->tv_usec;
    638 # elif defined(HAVE_TIME_IN_UTMP)
    639 	ut->ut_time = li->tv_sec;
    640 # endif
    641 }
    642 
    643 void
    644 construct_utmp(struct logininfo *li,
    645 		    struct utmp *ut)
    646 {
    647 # ifdef HAVE_ADDR_V6_IN_UTMP
    648 	struct sockaddr_in6 *sa6;
    649 # endif
    650 
    651 	memset(ut, '\0', sizeof(*ut));
    652 
    653 	/* First fill out fields used for both logins and logouts */
    654 
    655 # ifdef HAVE_ID_IN_UTMP
    656 	line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
    657 # endif
    658 
    659 # ifdef HAVE_TYPE_IN_UTMP
    660 	/* This is done here to keep utmp constants out of struct logininfo */
    661 	switch (li->type) {
    662 	case LTYPE_LOGIN:
    663 		ut->ut_type = USER_PROCESS;
    664 #ifdef _UNICOS
    665 		cray_set_tmpdir(ut);
    666 #endif
    667 		break;
    668 	case LTYPE_LOGOUT:
    669 		ut->ut_type = DEAD_PROCESS;
    670 #ifdef _UNICOS
    671 		cray_retain_utmp(ut, li->pid);
    672 #endif
    673 		break;
    674 	}
    675 # endif
    676 	set_utmp_time(li, ut);
    677 
    678 	line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
    679 
    680 # ifdef HAVE_PID_IN_UTMP
    681 	ut->ut_pid = li->pid;
    682 # endif
    683 
    684 	/* If we're logging out, leave all other fields blank */
    685 	if (li->type == LTYPE_LOGOUT)
    686 		return;
    687 
    688 	/*
    689 	 * These fields are only used when logging in, and are blank
    690 	 * for logouts.
    691 	 */
    692 
    693 	/* Use strncpy because we don't necessarily want null termination */
    694 	strncpy(ut->ut_name, li->username,
    695 	    MIN_SIZEOF(ut->ut_name, li->username));
    696 # ifdef HAVE_HOST_IN_UTMP
    697 	strncpy(ut->ut_host, li->hostname,
    698 	    MIN_SIZEOF(ut->ut_host, li->hostname));
    699 # endif
    700 # ifdef HAVE_ADDR_IN_UTMP
    701 	/* this is just a 32-bit IP address */
    702 	if (li->hostaddr.sa.sa_family == AF_INET)
    703 		ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
    704 # endif
    705 # ifdef HAVE_ADDR_V6_IN_UTMP
    706 	/* this is just a 128-bit IPv6 address */
    707 	if (li->hostaddr.sa.sa_family == AF_INET6) {
    708 		sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
    709 		memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
    710 		if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
    711 			ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
    712 			ut->ut_addr_v6[1] = 0;
    713 			ut->ut_addr_v6[2] = 0;
    714 			ut->ut_addr_v6[3] = 0;
    715 		}
    716 	}
    717 # endif
    718 }
    719 #endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
    720 
    721 /**
    722  ** utmpx utility functions
    723  **
    724  ** These functions manipulate struct utmpx, accounting for system
    725  ** variations.
    726  **/
    727 
    728 #if defined(USE_UTMPX) || defined (USE_WTMPX)
    729 /* build the utmpx structure */
    730 void
    731 set_utmpx_time(struct logininfo *li, struct utmpx *utx)
    732 {
    733 # if defined(HAVE_TV_IN_UTMPX)
    734 	utx->ut_tv.tv_sec = li->tv_sec;
    735 	utx->ut_tv.tv_usec = li->tv_usec;
    736 # elif defined(HAVE_TIME_IN_UTMPX)
    737 	utx->ut_time = li->tv_sec;
    738 # endif
    739 }
    740 
    741 void
    742 construct_utmpx(struct logininfo *li, struct utmpx *utx)
    743 {
    744 # ifdef HAVE_ADDR_V6_IN_UTMP
    745 	struct sockaddr_in6 *sa6;
    746 #  endif
    747 	memset(utx, '\0', sizeof(*utx));
    748 
    749 # ifdef HAVE_ID_IN_UTMPX
    750 	line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
    751 # endif
    752 
    753 	/* this is done here to keep utmp constants out of loginrec.h */
    754 	switch (li->type) {
    755 	case LTYPE_LOGIN:
    756 		utx->ut_type = USER_PROCESS;
    757 		break;
    758 	case LTYPE_LOGOUT:
    759 		utx->ut_type = DEAD_PROCESS;
    760 		break;
    761 	}
    762 	line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
    763 	set_utmpx_time(li, utx);
    764 	utx->ut_pid = li->pid;
    765 
    766 	/* strncpy(): Don't necessarily want null termination */
    767 	strncpy(utx->ut_user, li->username,
    768 	    MIN_SIZEOF(utx->ut_user, li->username));
    769 
    770 	if (li->type == LTYPE_LOGOUT)
    771 		return;
    772 
    773 	/*
    774 	 * These fields are only used when logging in, and are blank
    775 	 * for logouts.
    776 	 */
    777 
    778 # ifdef HAVE_HOST_IN_UTMPX
    779 	strncpy(utx->ut_host, li->hostname,
    780 	    MIN_SIZEOF(utx->ut_host, li->hostname));
    781 # endif
    782 # ifdef HAVE_ADDR_IN_UTMPX
    783 	/* this is just a 32-bit IP address */
    784 	if (li->hostaddr.sa.sa_family == AF_INET)
    785 		utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
    786 # endif
    787 # ifdef HAVE_ADDR_V6_IN_UTMP
    788 	/* this is just a 128-bit IPv6 address */
    789 	if (li->hostaddr.sa.sa_family == AF_INET6) {
    790 		sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
    791 		memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
    792 		if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
    793 			ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
    794 			ut->ut_addr_v6[1] = 0;
    795 			ut->ut_addr_v6[2] = 0;
    796 			ut->ut_addr_v6[3] = 0;
    797 		}
    798 	}
    799 # endif
    800 # ifdef HAVE_SYSLEN_IN_UTMPX
    801 	/* ut_syslen is the length of the utx_host string */
    802 	utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
    803 # endif
    804 }
    805 #endif /* USE_UTMPX || USE_WTMPX */
    806 
    807 /**
    808  ** Low-level utmp functions
    809  **/
    810 
    811 /* FIXME: (ATL) utmp_write_direct needs testing */
    812 #ifdef USE_UTMP
    813 
    814 /* if we can, use pututline() etc. */
    815 # if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
    816 	defined(HAVE_PUTUTLINE)
    817 #  define UTMP_USE_LIBRARY
    818 # endif
    819 
    820 
    821 /* write a utmp entry with the system's help (pututline() and pals) */
    822 # ifdef UTMP_USE_LIBRARY
    823 static int
    824 utmp_write_library(struct logininfo *li, struct utmp *ut)
    825 {
    826 	setutent();
    827 	pututline(ut);
    828 #  ifdef HAVE_ENDUTENT
    829 	endutent();
    830 #  endif
    831 	return (1);
    832 }
    833 # else /* UTMP_USE_LIBRARY */
    834 
    835 /*
    836  * Write a utmp entry direct to the file
    837  * This is a slightly modification of code in OpenBSD's login.c
    838  */
    839 static int
    840 utmp_write_direct(struct logininfo *li, struct utmp *ut)
    841 {
    842 	struct utmp old_ut;
    843 	register int fd;
    844 	int tty;
    845 
    846 	/* FIXME: (ATL) ttyslot() needs local implementation */
    847 
    848 #if defined(HAVE_GETTTYENT)
    849 	struct ttyent *ty;
    850 
    851 	tty=0;
    852 	setttyent();
    853 	while (NULL != (ty = getttyent())) {
    854 		tty++;
    855 		if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
    856 			break;
    857 	}
    858 	endttyent();
    859 
    860 	if (NULL == ty) {
    861 		logit("%s: tty not found", __func__);
    862 		return (0);
    863 	}
    864 #else /* FIXME */
    865 
    866 	tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
    867 
    868 #endif /* HAVE_GETTTYENT */
    869 
    870 	if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
    871 		off_t pos, ret;
    872 
    873 		pos = (off_t)tty * sizeof(struct utmp);
    874 		if ((ret = lseek(fd, pos, SEEK_SET)) == -1) {
    875 			logit("%s: lseek: %s", __func__, strerror(errno));
    876 			close(fd);
    877 			return (0);
    878 		}
    879 		if (ret != pos) {
    880 			logit("%s: Couldn't seek to tty %d slot in %s",
    881 			    __func__, tty, UTMP_FILE);
    882 			close(fd);
    883 			return (0);
    884 		}
    885 		/*
    886 		 * Prevent luser from zero'ing out ut_host.
    887 		 * If the new ut_line is empty but the old one is not
    888 		 * and ut_line and ut_name match, preserve the old ut_line.
    889 		 */
    890 		if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
    891 		    (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
    892 		    (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
    893 		    (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0))
    894 			memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
    895 
    896 		if ((ret = lseek(fd, pos, SEEK_SET)) == -1) {
    897 			logit("%s: lseek: %s", __func__, strerror(errno));
    898 			close(fd);
    899 			return (0);
    900 		}
    901 		if (ret != pos) {
    902 			logit("%s: Couldn't seek to tty %d slot in %s",
    903 			    __func__, tty, UTMP_FILE);
    904 			close(fd);
    905 			return (0);
    906 		}
    907 		if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
    908 			logit("%s: error writing %s: %s", __func__,
    909 			    UTMP_FILE, strerror(errno));
    910 			close(fd);
    911 			return (0);
    912 		}
    913 
    914 		close(fd);
    915 		return (1);
    916 	} else {
    917 		return (0);
    918 	}
    919 }
    920 # endif /* UTMP_USE_LIBRARY */
    921 
    922 static int
    923 utmp_perform_login(struct logininfo *li)
    924 {
    925 	struct utmp ut;
    926 
    927 	construct_utmp(li, &ut);
    928 # ifdef UTMP_USE_LIBRARY
    929 	if (!utmp_write_library(li, &ut)) {
    930 		logit("%s: utmp_write_library() failed", __func__);
    931 		return (0);
    932 	}
    933 # else
    934 	if (!utmp_write_direct(li, &ut)) {
    935 		logit("%s: utmp_write_direct() failed", __func__);
    936 		return (0);
    937 	}
    938 # endif
    939 	return (1);
    940 }
    941 
    942 
    943 static int
    944 utmp_perform_logout(struct logininfo *li)
    945 {
    946 	struct utmp ut;
    947 
    948 	construct_utmp(li, &ut);
    949 # ifdef UTMP_USE_LIBRARY
    950 	if (!utmp_write_library(li, &ut)) {
    951 		logit("%s: utmp_write_library() failed", __func__);
    952 		return (0);
    953 	}
    954 # else
    955 	if (!utmp_write_direct(li, &ut)) {
    956 		logit("%s: utmp_write_direct() failed", __func__);
    957 		return (0);
    958 	}
    959 # endif
    960 	return (1);
    961 }
    962 
    963 
    964 int
    965 utmp_write_entry(struct logininfo *li)
    966 {
    967 	switch(li->type) {
    968 	case LTYPE_LOGIN:
    969 		return (utmp_perform_login(li));
    970 
    971 	case LTYPE_LOGOUT:
    972 		return (utmp_perform_logout(li));
    973 
    974 	default:
    975 		logit("%s: invalid type field", __func__);
    976 		return (0);
    977 	}
    978 }
    979 #endif /* USE_UTMP */
    980 
    981 
    982 /**
    983  ** Low-level utmpx functions
    984  **/
    985 
    986 /* not much point if we don't want utmpx entries */
    987 #ifdef USE_UTMPX
    988 
    989 /* if we have the wherewithall, use pututxline etc. */
    990 # if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
    991 	defined(HAVE_PUTUTXLINE)
    992 #  define UTMPX_USE_LIBRARY
    993 # endif
    994 
    995 
    996 /* write a utmpx entry with the system's help (pututxline() and pals) */
    997 # ifdef UTMPX_USE_LIBRARY
    998 static int
    999 utmpx_write_library(struct logininfo *li, struct utmpx *utx)
   1000 {
   1001 	setutxent();
   1002 	pututxline(utx);
   1003 
   1004 #  ifdef HAVE_ENDUTXENT
   1005 	endutxent();
   1006 #  endif
   1007 	return (1);
   1008 }
   1009 
   1010 # else /* UTMPX_USE_LIBRARY */
   1011 
   1012 /* write a utmp entry direct to the file */
   1013 static int
   1014 utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
   1015 {
   1016 	logit("%s: not implemented!", __func__);
   1017 	return (0);
   1018 }
   1019 # endif /* UTMPX_USE_LIBRARY */
   1020 
   1021 static int
   1022 utmpx_perform_login(struct logininfo *li)
   1023 {
   1024 	struct utmpx utx;
   1025 
   1026 	construct_utmpx(li, &utx);
   1027 # ifdef UTMPX_USE_LIBRARY
   1028 	if (!utmpx_write_library(li, &utx)) {
   1029 		logit("%s: utmp_write_library() failed", __func__);
   1030 		return (0);
   1031 	}
   1032 # else
   1033 	if (!utmpx_write_direct(li, &ut)) {
   1034 		logit("%s: utmp_write_direct() failed", __func__);
   1035 		return (0);
   1036 	}
   1037 # endif
   1038 	return (1);
   1039 }
   1040 
   1041 
   1042 static int
   1043 utmpx_perform_logout(struct logininfo *li)
   1044 {
   1045 	struct utmpx utx;
   1046 
   1047 	construct_utmpx(li, &utx);
   1048 # ifdef HAVE_ID_IN_UTMPX
   1049 	line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
   1050 # endif
   1051 # ifdef HAVE_TYPE_IN_UTMPX
   1052 	utx.ut_type = DEAD_PROCESS;
   1053 # endif
   1054 
   1055 # ifdef UTMPX_USE_LIBRARY
   1056 	utmpx_write_library(li, &utx);
   1057 # else
   1058 	utmpx_write_direct(li, &utx);
   1059 # endif
   1060 	return (1);
   1061 }
   1062 
   1063 int
   1064 utmpx_write_entry(struct logininfo *li)
   1065 {
   1066 	switch(li->type) {
   1067 	case LTYPE_LOGIN:
   1068 		return (utmpx_perform_login(li));
   1069 	case LTYPE_LOGOUT:
   1070 		return (utmpx_perform_logout(li));
   1071 	default:
   1072 		logit("%s: invalid type field", __func__);
   1073 		return (0);
   1074 	}
   1075 }
   1076 #endif /* USE_UTMPX */
   1077 
   1078 
   1079 /**
   1080  ** Low-level wtmp functions
   1081  **/
   1082 
   1083 #ifdef USE_WTMP
   1084 
   1085 /*
   1086  * Write a wtmp entry direct to the end of the file
   1087  * This is a slight modification of code in OpenBSD's logwtmp.c
   1088  */
   1089 static int
   1090 wtmp_write(struct logininfo *li, struct utmp *ut)
   1091 {
   1092 	struct stat buf;
   1093 	int fd, ret = 1;
   1094 
   1095 	if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
   1096 		logit("%s: problem writing %s: %s", __func__,
   1097 		    WTMP_FILE, strerror(errno));
   1098 		return (0);
   1099 	}
   1100 	if (fstat(fd, &buf) == 0)
   1101 		if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
   1102 			ftruncate(fd, buf.st_size);
   1103 			logit("%s: problem writing %s: %s", __func__,
   1104 			    WTMP_FILE, strerror(errno));
   1105 			ret = 0;
   1106 		}
   1107 	close(fd);
   1108 	return (ret);
   1109 }
   1110 
   1111 static int
   1112 wtmp_perform_login(struct logininfo *li)
   1113 {
   1114 	struct utmp ut;
   1115 
   1116 	construct_utmp(li, &ut);
   1117 	return (wtmp_write(li, &ut));
   1118 }
   1119 
   1120 
   1121 static int
   1122 wtmp_perform_logout(struct logininfo *li)
   1123 {
   1124 	struct utmp ut;
   1125 
   1126 	construct_utmp(li, &ut);
   1127 	return (wtmp_write(li, &ut));
   1128 }
   1129 
   1130 
   1131 int
   1132 wtmp_write_entry(struct logininfo *li)
   1133 {
   1134 	switch(li->type) {
   1135 	case LTYPE_LOGIN:
   1136 		return (wtmp_perform_login(li));
   1137 	case LTYPE_LOGOUT:
   1138 		return (wtmp_perform_logout(li));
   1139 	default:
   1140 		logit("%s: invalid type field", __func__);
   1141 		return (0);
   1142 	}
   1143 }
   1144 
   1145 
   1146 /*
   1147  * Notes on fetching login data from wtmp/wtmpx
   1148  *
   1149  * Logouts are usually recorded with (amongst other things) a blank
   1150  * username on a given tty line.  However, some systems (HP-UX is one)
   1151  * leave all fields set, but change the ut_type field to DEAD_PROCESS.
   1152  *
   1153  * Since we're only looking for logins here, we know that the username
   1154  * must be set correctly. On systems that leave it in, we check for
   1155  * ut_type==USER_PROCESS (indicating a login.)
   1156  *
   1157  * Portability: Some systems may set something other than USER_PROCESS
   1158  * to indicate a login process. I don't know of any as I write. Also,
   1159  * it's possible that some systems may both leave the username in
   1160  * place and not have ut_type.
   1161  */
   1162 
   1163 /* return true if this wtmp entry indicates a login */
   1164 static int
   1165 wtmp_islogin(struct logininfo *li, struct utmp *ut)
   1166 {
   1167 	if (strncmp(li->username, ut->ut_name,
   1168 	    MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
   1169 # ifdef HAVE_TYPE_IN_UTMP
   1170 		if (ut->ut_type & USER_PROCESS)
   1171 			return (1);
   1172 # else
   1173 		return (1);
   1174 # endif
   1175 	}
   1176 	return (0);
   1177 }
   1178 
   1179 int
   1180 wtmp_get_entry(struct logininfo *li)
   1181 {
   1182 	struct stat st;
   1183 	struct utmp ut;
   1184 	int fd, found = 0;
   1185 
   1186 	/* Clear the time entries in our logininfo */
   1187 	li->tv_sec = li->tv_usec = 0;
   1188 
   1189 	if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
   1190 		logit("%s: problem opening %s: %s", __func__,
   1191 		    WTMP_FILE, strerror(errno));
   1192 		return (0);
   1193 	}
   1194 	if (fstat(fd, &st) != 0) {
   1195 		logit("%s: couldn't stat %s: %s", __func__,
   1196 		    WTMP_FILE, strerror(errno));
   1197 		close(fd);
   1198 		return (0);
   1199 	}
   1200 
   1201 	/* Seek to the start of the last struct utmp */
   1202 	if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
   1203 		/* Looks like we've got a fresh wtmp file */
   1204 		close(fd);
   1205 		return (0);
   1206 	}
   1207 
   1208 	while (!found) {
   1209 		if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
   1210 			logit("%s: read of %s failed: %s", __func__,
   1211 			    WTMP_FILE, strerror(errno));
   1212 			close (fd);
   1213 			return (0);
   1214 		}
   1215 		if (wtmp_islogin(li, &ut) ) {
   1216 			found = 1;
   1217 			/*
   1218 			 * We've already checked for a time in struct
   1219 			 * utmp, in login_getlast()
   1220 			 */
   1221 # ifdef HAVE_TIME_IN_UTMP
   1222 			li->tv_sec = ut.ut_time;
   1223 # else
   1224 #  if HAVE_TV_IN_UTMP
   1225 			li->tv_sec = ut.ut_tv.tv_sec;
   1226 #  endif
   1227 # endif
   1228 			line_fullname(li->line, ut.ut_line,
   1229 			    MIN_SIZEOF(li->line, ut.ut_line));
   1230 # ifdef HAVE_HOST_IN_UTMP
   1231 			strlcpy(li->hostname, ut.ut_host,
   1232 			    MIN_SIZEOF(li->hostname, ut.ut_host));
   1233 # endif
   1234 			continue;
   1235 		}
   1236 		/* Seek back 2 x struct utmp */
   1237 		if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
   1238 			/* We've found the start of the file, so quit */
   1239 			close(fd);
   1240 			return (0);
   1241 		}
   1242 	}
   1243 
   1244 	/* We found an entry. Tidy up and return */
   1245 	close(fd);
   1246 	return (1);
   1247 }
   1248 # endif /* USE_WTMP */
   1249 
   1250 
   1251 /**
   1252  ** Low-level wtmpx functions
   1253  **/
   1254 
   1255 #ifdef USE_WTMPX
   1256 /*
   1257  * Write a wtmpx entry direct to the end of the file
   1258  * This is a slight modification of code in OpenBSD's logwtmp.c
   1259  */
   1260 static int
   1261 wtmpx_write(struct logininfo *li, struct utmpx *utx)
   1262 {
   1263 #ifndef HAVE_UPDWTMPX
   1264 	struct stat buf;
   1265 	int fd, ret = 1;
   1266 
   1267 	if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
   1268 		logit("%s: problem opening %s: %s", __func__,
   1269 		    WTMPX_FILE, strerror(errno));
   1270 		return (0);
   1271 	}
   1272 
   1273 	if (fstat(fd, &buf) == 0)
   1274 		if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
   1275 			ftruncate(fd, buf.st_size);
   1276 			logit("%s: problem writing %s: %s", __func__,
   1277 			    WTMPX_FILE, strerror(errno));
   1278 			ret = 0;
   1279 		}
   1280 	close(fd);
   1281 
   1282 	return (ret);
   1283 #else
   1284 	updwtmpx(WTMPX_FILE, utx);
   1285 	return (1);
   1286 #endif
   1287 }
   1288 
   1289 
   1290 static int
   1291 wtmpx_perform_login(struct logininfo *li)
   1292 {
   1293 	struct utmpx utx;
   1294 
   1295 	construct_utmpx(li, &utx);
   1296 	return (wtmpx_write(li, &utx));
   1297 }
   1298 
   1299 
   1300 static int
   1301 wtmpx_perform_logout(struct logininfo *li)
   1302 {
   1303 	struct utmpx utx;
   1304 
   1305 	construct_utmpx(li, &utx);
   1306 	return (wtmpx_write(li, &utx));
   1307 }
   1308 
   1309 
   1310 int
   1311 wtmpx_write_entry(struct logininfo *li)
   1312 {
   1313 	switch(li->type) {
   1314 	case LTYPE_LOGIN:
   1315 		return (wtmpx_perform_login(li));
   1316 	case LTYPE_LOGOUT:
   1317 		return (wtmpx_perform_logout(li));
   1318 	default:
   1319 		logit("%s: invalid type field", __func__);
   1320 		return (0);
   1321 	}
   1322 }
   1323 
   1324 /* Please see the notes above wtmp_islogin() for information about the
   1325    next two functions */
   1326 
   1327 /* Return true if this wtmpx entry indicates a login */
   1328 static int
   1329 wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
   1330 {
   1331 	if (strncmp(li->username, utx->ut_user,
   1332 	    MIN_SIZEOF(li->username, utx->ut_user)) == 0 ) {
   1333 # ifdef HAVE_TYPE_IN_UTMPX
   1334 		if (utx->ut_type == USER_PROCESS)
   1335 			return (1);
   1336 # else
   1337 		return (1);
   1338 # endif
   1339 	}
   1340 	return (0);
   1341 }
   1342 
   1343 
   1344 int
   1345 wtmpx_get_entry(struct logininfo *li)
   1346 {
   1347 	struct stat st;
   1348 	struct utmpx utx;
   1349 	int fd, found=0;
   1350 
   1351 	/* Clear the time entries */
   1352 	li->tv_sec = li->tv_usec = 0;
   1353 
   1354 	if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
   1355 		logit("%s: problem opening %s: %s", __func__,
   1356 		    WTMPX_FILE, strerror(errno));
   1357 		return (0);
   1358 	}
   1359 	if (fstat(fd, &st) != 0) {
   1360 		logit("%s: couldn't stat %s: %s", __func__,
   1361 		    WTMPX_FILE, strerror(errno));
   1362 		close(fd);
   1363 		return (0);
   1364 	}
   1365 
   1366 	/* Seek to the start of the last struct utmpx */
   1367 	if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) {
   1368 		/* probably a newly rotated wtmpx file */
   1369 		close(fd);
   1370 		return (0);
   1371 	}
   1372 
   1373 	while (!found) {
   1374 		if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
   1375 			logit("%s: read of %s failed: %s", __func__,
   1376 			    WTMPX_FILE, strerror(errno));
   1377 			close (fd);
   1378 			return (0);
   1379 		}
   1380 		/*
   1381 		 * Logouts are recorded as a blank username on a particular
   1382 		 * line. So, we just need to find the username in struct utmpx
   1383 		 */
   1384 		if (wtmpx_islogin(li, &utx)) {
   1385 			found = 1;
   1386 # if defined(HAVE_TV_IN_UTMPX)
   1387 			li->tv_sec = utx.ut_tv.tv_sec;
   1388 # elif defined(HAVE_TIME_IN_UTMPX)
   1389 			li->tv_sec = utx.ut_time;
   1390 # endif
   1391 			line_fullname(li->line, utx.ut_line, sizeof(li->line));
   1392 # if defined(HAVE_HOST_IN_UTMPX)
   1393 			strlcpy(li->hostname, utx.ut_host,
   1394 			    MIN_SIZEOF(li->hostname, utx.ut_host));
   1395 # endif
   1396 			continue;
   1397 		}
   1398 		if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) {
   1399 			close(fd);
   1400 			return (0);
   1401 		}
   1402 	}
   1403 
   1404 	close(fd);
   1405 	return (1);
   1406 }
   1407 #endif /* USE_WTMPX */
   1408 
   1409 /**
   1410  ** Low-level libutil login() functions
   1411  **/
   1412 
   1413 #ifdef USE_LOGIN
   1414 static int
   1415 syslogin_perform_login(struct logininfo *li)
   1416 {
   1417 	struct utmp *ut;
   1418 
   1419 	ut = xmalloc(sizeof(*ut));
   1420 	construct_utmp(li, ut);
   1421 	login(ut);
   1422 	free(ut);
   1423 
   1424 	return (1);
   1425 }
   1426 
   1427 static int
   1428 syslogin_perform_logout(struct logininfo *li)
   1429 {
   1430 # ifdef HAVE_LOGOUT
   1431 	char line[UT_LINESIZE];
   1432 
   1433 	(void)line_stripname(line, li->line, sizeof(line));
   1434 
   1435 	if (!logout(line))
   1436 		logit("%s: logout() returned an error", __func__);
   1437 #  ifdef HAVE_LOGWTMP
   1438 	else
   1439 		logwtmp(line, "", "");
   1440 #  endif
   1441 	/* FIXME: (ATL - if the need arises) What to do if we have
   1442 	 * login, but no logout?  what if logout but no logwtmp? All
   1443 	 * routines are in libutil so they should all be there,
   1444 	 * but... */
   1445 # endif
   1446 	return (1);
   1447 }
   1448 
   1449 int
   1450 syslogin_write_entry(struct logininfo *li)
   1451 {
   1452 	switch (li->type) {
   1453 	case LTYPE_LOGIN:
   1454 		return (syslogin_perform_login(li));
   1455 	case LTYPE_LOGOUT:
   1456 		return (syslogin_perform_logout(li));
   1457 	default:
   1458 		logit("%s: Invalid type field", __func__);
   1459 		return (0);
   1460 	}
   1461 }
   1462 #endif /* USE_LOGIN */
   1463 
   1464 /* end of file log-syslogin.c */
   1465 
   1466 /**
   1467  ** Low-level lastlog functions
   1468  **/
   1469 
   1470 #ifdef USE_LASTLOG
   1471 
   1472 #if !defined(LASTLOG_WRITE_PUTUTXLINE) || !defined(HAVE_GETLASTLOGXBYNAME)
   1473 /* open the file (using filemode) and seek to the login entry */
   1474 static int
   1475 lastlog_openseek(struct logininfo *li, int *fd, int filemode)
   1476 {
   1477 	off_t offset;
   1478 	char lastlog_file[1024];
   1479 	struct stat st;
   1480 
   1481 	if (stat(LASTLOG_FILE, &st) != 0) {
   1482 		logit("%s: Couldn't stat %s: %s", __func__,
   1483 		    LASTLOG_FILE, strerror(errno));
   1484 		return (0);
   1485 	}
   1486 	if (S_ISDIR(st.st_mode)) {
   1487 		snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
   1488 		    LASTLOG_FILE, li->username);
   1489 	} else if (S_ISREG(st.st_mode)) {
   1490 		strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
   1491 	} else {
   1492 		logit("%s: %.100s is not a file or directory!", __func__,
   1493 		    LASTLOG_FILE);
   1494 		return (0);
   1495 	}
   1496 
   1497 	*fd = open(lastlog_file, filemode, 0600);
   1498 	if (*fd < 0) {
   1499 		debug("%s: Couldn't open %s: %s", __func__,
   1500 		    lastlog_file, strerror(errno));
   1501 		return (0);
   1502 	}
   1503 
   1504 	if (S_ISREG(st.st_mode)) {
   1505 		/* find this uid's offset in the lastlog file */
   1506 		offset = (off_t) ((u_long)li->uid * sizeof(struct lastlog));
   1507 
   1508 		if (lseek(*fd, offset, SEEK_SET) != offset) {
   1509 			logit("%s: %s->lseek(): %s", __func__,
   1510 			    lastlog_file, strerror(errno));
   1511 			close(*fd);
   1512 			return (0);
   1513 		}
   1514 	}
   1515 
   1516 	return (1);
   1517 }
   1518 #endif /* !LASTLOG_WRITE_PUTUTXLINE || !HAVE_GETLASTLOGXBYNAME */
   1519 
   1520 #ifdef LASTLOG_WRITE_PUTUTXLINE
   1521 int
   1522 lastlog_write_entry(struct logininfo *li)
   1523 {
   1524 	switch(li->type) {
   1525 	case LTYPE_LOGIN:
   1526 		return 1; /* lastlog written by pututxline */
   1527 	default:
   1528 		logit("lastlog_write_entry: Invalid type field");
   1529 		return 0;
   1530 	}
   1531 }
   1532 #else /* LASTLOG_WRITE_PUTUTXLINE */
   1533 int
   1534 lastlog_write_entry(struct logininfo *li)
   1535 {
   1536 	struct lastlog last;
   1537 	int fd;
   1538 
   1539 	switch(li->type) {
   1540 	case LTYPE_LOGIN:
   1541 		/* create our struct lastlog */
   1542 		memset(&last, '\0', sizeof(last));
   1543 		line_stripname(last.ll_line, li->line, sizeof(last.ll_line));
   1544 		strlcpy(last.ll_host, li->hostname,
   1545 		    MIN_SIZEOF(last.ll_host, li->hostname));
   1546 		last.ll_time = li->tv_sec;
   1547 
   1548 		if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
   1549 			return (0);
   1550 
   1551 		/* write the entry */
   1552 		if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) {
   1553 			close(fd);
   1554 			logit("%s: Error writing to %s: %s", __func__,
   1555 			    LASTLOG_FILE, strerror(errno));
   1556 			return (0);
   1557 		}
   1558 
   1559 		close(fd);
   1560 		return (1);
   1561 	default:
   1562 		logit("%s: Invalid type field", __func__);
   1563 		return (0);
   1564 	}
   1565 }
   1566 #endif /* LASTLOG_WRITE_PUTUTXLINE */
   1567 
   1568 #ifdef HAVE_GETLASTLOGXBYNAME
   1569 int
   1570 lastlog_get_entry(struct logininfo *li)
   1571 {
   1572 	struct lastlogx l, *ll;
   1573 
   1574 	if ((ll = getlastlogxbyname(li->username, &l)) == NULL) {
   1575 		memset(&l, '\0', sizeof(l));
   1576 		ll = &l;
   1577 	}
   1578 	line_fullname(li->line, ll->ll_line, sizeof(li->line));
   1579 	strlcpy(li->hostname, ll->ll_host,
   1580 		MIN_SIZEOF(li->hostname, ll->ll_host));
   1581 	li->tv_sec = ll->ll_tv.tv_sec;
   1582 	li->tv_usec = ll->ll_tv.tv_usec;
   1583 	return (1);
   1584 }
   1585 #else /* HAVE_GETLASTLOGXBYNAME */
   1586 int
   1587 lastlog_get_entry(struct logininfo *li)
   1588 {
   1589 	struct lastlog last;
   1590 	int fd, ret;
   1591 
   1592 	if (!lastlog_openseek(li, &fd, O_RDONLY))
   1593 		return (0);
   1594 
   1595 	ret = atomicio(read, fd, &last, sizeof(last));
   1596 	close(fd);
   1597 
   1598 	switch (ret) {
   1599 	case 0:
   1600 		memset(&last, '\0', sizeof(last));
   1601 		/* FALLTHRU */
   1602 	case sizeof(last):
   1603 		line_fullname(li->line, last.ll_line, sizeof(li->line));
   1604 		strlcpy(li->hostname, last.ll_host,
   1605 		    MIN_SIZEOF(li->hostname, last.ll_host));
   1606 		li->tv_sec = last.ll_time;
   1607 		return (1);
   1608 	case -1:
   1609 		error("%s: Error reading from %s: %s", __func__,
   1610 		    LASTLOG_FILE, strerror(errno));
   1611 		return (0);
   1612 	default:
   1613 		error("%s: Error reading from %s: Expecting %d, got %d",
   1614 		    __func__, LASTLOG_FILE, (int)sizeof(last), ret);
   1615 		return (0);
   1616 	}
   1617 
   1618 	/* NOTREACHED */
   1619 	return (0);
   1620 }
   1621 #endif /* HAVE_GETLASTLOGXBYNAME */
   1622 #endif /* USE_LASTLOG */
   1623 
   1624 #if defined(USE_UTMPX) && defined(HAVE_SETUTXDB) && \
   1625     defined(UTXDB_LASTLOGIN) && defined(HAVE_GETUTXUSER)
   1626 int
   1627 utmpx_get_entry(struct logininfo *li)
   1628 {
   1629 	struct utmpx *utx;
   1630 
   1631 	if (setutxdb(UTXDB_LASTLOGIN, NULL) != 0)
   1632 		return (0);
   1633 	utx = getutxuser(li->username);
   1634 	if (utx == NULL) {
   1635 		endutxent();
   1636 		return (0);
   1637 	}
   1638 
   1639 	line_fullname(li->line, utx->ut_line,
   1640 	    MIN_SIZEOF(li->line, utx->ut_line));
   1641 	strlcpy(li->hostname, utx->ut_host,
   1642 	    MIN_SIZEOF(li->hostname, utx->ut_host));
   1643 	li->tv_sec = utx->ut_tv.tv_sec;
   1644 	li->tv_usec = utx->ut_tv.tv_usec;
   1645 	endutxent();
   1646 	return (1);
   1647 }
   1648 #endif /* USE_UTMPX && HAVE_SETUTXDB && UTXDB_LASTLOGIN && HAVE_GETUTXUSER */
   1649 
   1650 #ifdef USE_BTMP
   1651   /*
   1652    * Logs failed login attempts in _PATH_BTMP if that exists.
   1653    * The most common login failure is to give password instead of username.
   1654    * So the _PATH_BTMP file checked for the correct permission, so that
   1655    * only root can read it.
   1656    */
   1657 
   1658 void
   1659 record_failed_login(const char *username, const char *hostname,
   1660     const char *ttyn)
   1661 {
   1662 	int fd;
   1663 	struct utmp ut;
   1664 	struct sockaddr_storage from;
   1665 	socklen_t fromlen = sizeof(from);
   1666 	struct sockaddr_in *a4;
   1667 	struct sockaddr_in6 *a6;
   1668 	time_t t;
   1669 	struct stat fst;
   1670 
   1671 	if (geteuid() != 0)
   1672 		return;
   1673 	if ((fd = open(_PATH_BTMP, O_WRONLY | O_APPEND)) < 0) {
   1674 		debug("Unable to open the btmp file %s: %s", _PATH_BTMP,
   1675 		    strerror(errno));
   1676 		return;
   1677 	}
   1678 	if (fstat(fd, &fst) < 0) {
   1679 		logit("%s: fstat of %s failed: %s", __func__, _PATH_BTMP,
   1680 		    strerror(errno));
   1681 		goto out;
   1682 	}
   1683 	if((fst.st_mode & (S_IXGRP | S_IRWXO)) || (fst.st_uid != 0)){
   1684 		logit("Excess permission or bad ownership on file %s",
   1685 		    _PATH_BTMP);
   1686 		goto out;
   1687 	}
   1688 
   1689 	memset(&ut, 0, sizeof(ut));
   1690 	/* strncpy because we don't necessarily want nul termination */
   1691 	strncpy(ut.ut_user, username, sizeof(ut.ut_user));
   1692 	strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line));
   1693 
   1694 	time(&t);
   1695 	ut.ut_time = t;     /* ut_time is not always a time_t */
   1696 	ut.ut_type = LOGIN_PROCESS;
   1697 	ut.ut_pid = getpid();
   1698 
   1699 	/* strncpy because we don't necessarily want nul termination */
   1700 	strncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
   1701 
   1702 	if (packet_connection_is_on_socket() &&
   1703 	    getpeername(packet_get_connection_in(),
   1704 	    (struct sockaddr *)&from, &fromlen) == 0) {
   1705 		ipv64_normalise_mapped(&from, &fromlen);
   1706 		if (from.ss_family == AF_INET) {
   1707 			a4 = (struct sockaddr_in *)&from;
   1708 			memcpy(&ut.ut_addr, &(a4->sin_addr),
   1709 			    MIN_SIZEOF(ut.ut_addr, a4->sin_addr));
   1710 		}
   1711 #ifdef HAVE_ADDR_V6_IN_UTMP
   1712 		if (from.ss_family == AF_INET6) {
   1713 			a6 = (struct sockaddr_in6 *)&from;
   1714 			memcpy(&ut.ut_addr_v6, &(a6->sin6_addr),
   1715 			    MIN_SIZEOF(ut.ut_addr_v6, a6->sin6_addr));
   1716 		}
   1717 #endif
   1718 	}
   1719 
   1720 	if (atomicio(vwrite, fd, &ut, sizeof(ut)) != sizeof(ut))
   1721 		error("Failed to write to %s: %s", _PATH_BTMP,
   1722 		    strerror(errno));
   1723 
   1724 out:
   1725 	close(fd);
   1726 }
   1727 #endif	/* USE_BTMP */
   1728