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