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