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 getlast_entry(struct logininfo *li);
 202 int lastlog_get_entry(struct logininfo *li);
 203 int wtmp_get_entry(struct logininfo *li);
 204 int wtmpx_get_entry(struct logininfo *li);
 205 
 206 /* pick the shortest string */
 207 #define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
 208 
 209 /**
 210  ** platform-independent login functions
 211  **/
 212 
 213 /* login_login(struct logininfo *)     -Record a login
 214  *
 215  * Call with a pointer to a struct logininfo initialised with
 216  * login_init_entry() or login_alloc_entry()
 217  *
 218  * Returns:
 219  *  >0 if successful
 220  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
 221  */
 222 int
 223 login_login (struct logininfo *li)
 224 {
 225         li->type = LTYPE_LOGIN;
 226         return login_write(li);
 227 }
 228 
 229 
 230 /* login_logout(struct logininfo *)     - Record a logout
 231  *
 232  * Call as with login_login()
 233  *
 234  * Returns:
 235  *  >0 if successful
 236  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
 237  */
 238 int
 239 login_logout(struct logininfo *li)
 240 {
 241         li->type = LTYPE_LOGOUT;
 242         return login_write(li);
 243 }
 244 
 245 /* login_get_lastlog_time(int)           - Retrieve the last login time
 246  *
 247  * Retrieve the last login time for the given uid. Will try to use the
 248  * system lastlog facilities if they are available, but will fall back
 249  * to looking in wtmp/wtmpx if necessary
 250  *
 251  * Returns:
 252  *   0 on failure, or if user has never logged in
 253  *   Time in seconds from the epoch if successful
 254  *
 255  * Useful preprocessor symbols:
 256  *   DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
 257  *                    info
 258  *   USE_LASTLOG: If set, indicates the presence of system lastlog
 259  *                facilities. If this and DISABLE_LASTLOG are not set,
 260  *                try to retrieve lastlog information from wtmp/wtmpx.
 261  */
 262 #if 0
 263 unsigned int
 264 login_get_lastlog_time(const int uid)
 265 {
 266         struct logininfo li;
 267 
 268         if (login_get_lastlog(&li, uid))
 269                 return li.tv_sec;
 270         else
 271                 return 0;
 272 }
 273 #endif
 274 
 275 /* login_get_lastlog(struct logininfo *, int)   - Retrieve a lastlog entry
 276  *
 277  * Retrieve a logininfo structure populated (only partially) with
 278  * information from the system lastlog data, or from wtmp/wtmpx if no
 279  * system lastlog information exists.
 280  *
 281  * Note this routine must be given a pre-allocated logininfo.
 282  *
 283  * Returns:
 284  *  >0: A pointer to your struct logininfo if successful
 285  *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
 286  *
 287  */
 288 struct logininfo *
 289 login_get_lastlog(struct logininfo *li, const int uid)
 290 {
 291         struct passwd *pw;
 292 
 293         (void) memset(li, '\0', sizeof(*li));
 294         li->uid = uid;
 295 
 296         /*
 297          * If we don't have a 'real' lastlog, we need the username to
 298          * reliably search wtmp(x) for the last login (see
 299          * wtmp_get_entry().)
 300          */
 301         pw = getpwuid(uid);
 302         if (pw == NULL)
 303                 fatal("login_get_lastlog: Cannot find account for uid %i", uid);
 304 
 305         /* No MIN_SIZEOF here - we absolutely *must not* truncate the
 306          * username */
 307         (void) strlcpy(li->username, pw->pw_name, sizeof(li->username));
 308 
 309         if (getlast_entry(li))
 310                 return li;
 311         else
 312                 return NULL;
 313 }
 314 
 315 
 316 /* login_alloc_entry()    - Allocate and initialise a logininfo
 317  *                           structure
 318  *
 319  * This function creates a new struct logininfo, a data structure
 320  * meant to carry the information required to portably record login info.
 321  *
 322  * Returns a pointer to a newly created struct logininfo. If memory
 323  * allocation fails, the program halts.
 324  */
 325 struct
 326 logininfo *login_alloc_entry(int pid, const char *username,
 327                              const char *hostname, const char *line,
 328                              const char *progname)
 329 {
 330         struct logininfo *newli;
 331 
 332         newli = (struct logininfo *) xmalloc (sizeof(*newli));
 333         (void)login_init_entry(newli, pid, username, hostname, line, progname);
 334         return newli;
 335 }
 336 
 337 
 338 /* login_free_entry(struct logininfo *)    - free struct memory */
 339 void
 340 login_free_entry(struct logininfo *li)
 341 {
 342         xfree(li);
 343 }
 344 
 345 
 346 /* login_init_entry()
 347  *                                        - initialise a struct logininfo
 348  *
 349  * Populates a new struct logininfo, a data structure meant to carry
 350  * the information required to portably record login info.
 351  *
 352  * Returns: 1
 353  */
 354 int
 355 login_init_entry(struct logininfo *li, int pid, const char *username,
 356                  const char *hostname, const char *line, const char *progname)
 357 {
 358         struct passwd *pw;
 359 
 360         (void) memset(li, 0, sizeof(*li));
 361 
 362         li->pid = pid;
 363 
 364         /* set the line information */
 365         if (line)
 366                 (void) line_fullname(li->line, line, sizeof(li->line));
 367         else
 368                 li->line_null = 1;
 369 
 370         if (progname)
 371                 (void) strlcpy(li->progname, progname, sizeof(li->progname));
 372         else
 373                 li->progname_null = 1;
 374 
 375         if (username) {
 376                 (void) strlcpy(li->username, username, sizeof(li->username));
 377                 pw = getpwnam(li->username);
 378                 if (pw == NULL)
 379                         fatal("login_init_entry: Cannot find user \"%s\"", li->username);
 380                 li->uid = pw->pw_uid;
 381         }
 382 
 383         if (hostname)
 384                 (void) strlcpy(li->hostname, hostname, sizeof(li->hostname));
 385 
 386         return 1;
 387 }
 388 
 389 /* login_set_current_time(struct logininfo *)    - set the current time
 390  *
 391  * Set the current time in a logininfo structure. This function is
 392  * meant to eliminate the need to deal with system dependencies for
 393  * time handling.
 394  */
 395 void
 396 login_set_current_time(struct logininfo *li)
 397 {
 398         struct timeval tv;
 399 
 400         (void) gettimeofday(&tv, NULL);
 401 
 402         li->tv_sec = tv.tv_sec;
 403         li->tv_usec = tv.tv_usec;
 404 }
 405 
 406 /* copy a sockaddr_* into our logininfo */
 407 void
 408 login_set_addr(struct logininfo *li, const struct sockaddr *sa,
 409                const unsigned int sa_size)
 410 {
 411         unsigned int bufsize = sa_size;
 412 
 413         /* make sure we don't overrun our union */
 414         if (sizeof(li->hostaddr) < sa_size)
 415                 bufsize = sizeof(li->hostaddr);
 416 
 417         (void) memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
 418 }
 419 
 420 
 421 /**
 422  ** login_write: Call low-level recording functions based on autoconf
 423  ** results
 424  **/
 425 int
 426 login_write (struct logininfo *li)
 427 {
 428 #ifndef HAVE_CYGWIN
 429         if ((int)geteuid() != 0) {
 430           log("Attempt to write login records by non-root user (aborting)");
 431           return 1;
 432         }
 433 #endif
 434 
 435         /* set the timestamp */
 436         login_set_current_time(li);
 437 #ifdef USE_LOGIN
 438         syslogin_write_entry(li);
 439 #endif
 440 #ifdef USE_LASTLOG
 441         if (li->type == LTYPE_LOGIN) {
 442                 (void) lastlog_write_entry(li);
 443         }
 444 #endif
 445 #ifdef USE_UTMP
 446         utmp_write_entry(li);
 447 #endif
 448 #ifdef USE_WTMP
 449         wtmp_write_entry(li);
 450 #endif
 451 #ifdef USE_UTMPX
 452         (void) utmpx_write_entry(li);
 453 #endif
 454 #ifdef USE_WTMPX
 455         (void) wtmpx_write_entry(li);
 456 #endif
 457         return 0;
 458 }
 459 
 460 /**
 461  ** getlast_entry: Call low-level functions to retrieve the last login
 462  **                time.
 463  **/
 464 
 465 /* take the uid in li and return the last login time */
 466 int
 467 getlast_entry(struct logininfo *li)
 468 {
 469 #ifdef USE_LASTLOG
 470         return(lastlog_get_entry(li));
 471 #else /* !USE_LASTLOG */
 472 
 473 #ifdef DISABLE_LASTLOG
 474         /* On some systems we shouldn't even try to obtain last login
 475          * time, e.g. AIX */
 476         return 0;
 477 # else /* DISABLE_LASTLOG */
 478         /* Try to retrieve the last login time from wtmp */
 479 #  if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
 480         /* retrieve last login time from utmp */
 481         return (wtmp_get_entry(li));
 482 #  else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */
 483         /* If wtmp isn't available, try wtmpx */
 484 #   if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
 485         /* retrieve last login time from utmpx */
 486         return (wtmpx_get_entry(li));
 487 #   else
 488         /* Give up: No means of retrieving last login time */
 489         return 0;
 490 #   endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
 491 #  endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
 492 # endif /* DISABLE_LASTLOG */
 493 #endif /* USE_LASTLOG */
 494 }
 495 
 496 
 497 
 498 /*
 499  * 'line' string utility functions
 500  *
 501  * These functions process the 'line' string into one of three forms:
 502  *
 503  * 1. The full filename (including '/dev')
 504  * 2. The stripped name (excluding '/dev')
 505  * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
 506  *                               /dev/pts/1  -> ts/1 )
 507  *
 508  * Form 3 is used on some systems to identify a .tmp.? entry when
 509  * attempting to remove it. Typically both addition and removal is
 510  * performed by one application - say, sshd - so as long as the choice
 511  * uniquely identifies a terminal it's ok.
 512  */
 513 
 514 
 515 /* line_fullname(): add the leading '/dev/' if it doesn't exist make
 516  * sure dst has enough space, if not just copy src (ugh) */
 517 char *
 518 line_fullname(char *dst, const char *src, int dstsize)
 519 {
 520         (void) memset(dst, '\0', dstsize);
 521         /* "sshd" is special, like "ftp" */
 522         if (strcmp(src, "sshd") ||
 523             ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))) {
 524                 (void) strlcpy(dst, src, dstsize);
 525         } else {
 526                 (void) strlcpy(dst, "/dev/", dstsize);
 527                 (void) strlcat(dst, src, dstsize);
 528         }
 529         return dst;
 530 }
 531 
 532 /* line_stripname(): strip the leading '/dev' if it exists, return dst */
 533 char *
 534 line_stripname(char *dst, const char *src, int dstsize)
 535 {
 536         (void) memset(dst, '\0', dstsize);
 537         if (strncmp(src, "/dev/", 5) == 0)
 538                 (void) strlcpy(dst, src + 5, dstsize);
 539         else
 540                 (void) strlcpy(dst, src, dstsize);
 541         return dst;
 542 }
 543 
 544 /* line_abbrevname(): Return the abbreviated (usually four-character)
 545  * form of the line (Just use the last <dstsize> characters of the
 546  * full name.)
 547  *
 548  * NOTE: use strncpy because we do NOT necessarily want zero
 549  * termination */
 550 char *
 551 line_abbrevname(char *dst, const char *src, int dstsize)
 552 {
 553         size_t len;
 554 
 555         (void) memset(dst, '\0', dstsize);
 556 
 557         /* Always skip prefix if present */
 558         if (strncmp(src, "/dev/", 5) == 0)
 559                 src += 5;
 560 
 561 #ifdef WITH_ABBREV_NO_TTY
 562         if (strncmp(src, "tty", 3) == 0)
 563                 src += 3;
 564 #endif
 565 
 566         len = strlen(src);
 567 
 568         if (len > 0) {
 569                 if (((int)len - dstsize) > 0)
 570                         src +=  ((int)len - dstsize);
 571 
 572                 /* note: _don't_ change this to strlcpy */
 573                 (void) strncpy(dst, src, (size_t)dstsize);
 574         }
 575 
 576         return dst;
 577 }
 578 
 579 /**
 580  ** utmp utility functions
 581  **
 582  ** These functions manipulate struct utmp, taking system differences
 583  ** into account.
 584  **/
 585 
 586 #if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
 587 
 588 /* build the utmp structure */
 589 void
 590 set_utmp_time(struct logininfo *li, struct utmp *ut)
 591 {
 592 # ifdef HAVE_TV_IN_UTMP
 593         ut->ut_tv.tv_sec = li->tv_sec;
 594         ut->ut_tv.tv_usec = li->tv_usec;
 595 # else
 596 #  ifdef HAVE_TIME_IN_UTMP
 597         ut->ut_time = li->tv_sec;
 598 #  endif
 599 # endif
 600 }
 601 
 602 void
 603 construct_utmp(struct logininfo *li,
 604                     struct utmp *ut)
 605 {
 606         (void) memset(ut, '\0', sizeof(*ut));
 607 
 608         /* First fill out fields used for both logins and logouts */
 609 
 610 # ifdef HAVE_ID_IN_UTMP
 611         (void) line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
 612 # endif
 613 
 614 # ifdef HAVE_TYPE_IN_UTMP
 615         /* This is done here to keep utmp constants out of struct logininfo */
 616         switch (li->type) {
 617         case LTYPE_LOGIN:
 618                 ut->ut_type = USER_PROCESS;
 619 #ifdef _UNICOS
 620                 cray_set_tmpdir(ut);
 621 #endif
 622                 break;
 623         case LTYPE_LOGOUT:
 624                 ut->ut_type = DEAD_PROCESS;
 625 #ifdef _UNICOS
 626                 cray_retain_utmp(ut, li->pid);
 627 #endif
 628                 break;
 629         }
 630 # endif
 631         set_utmp_time(li, ut);
 632 
 633         (void) line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
 634 
 635 # ifdef HAVE_PID_IN_UTMP
 636         ut->ut_pid = li->pid;
 637 # endif
 638 
 639         /* If we're logging out, leave all other fields blank */
 640         if (li->type == LTYPE_LOGOUT)
 641           return;
 642 
 643         /*
 644          * These fields are only used when logging in, and are blank
 645          * for logouts.
 646          */
 647 
 648         /* Use strncpy because we don't necessarily want null termination */
 649         (void) strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
 650 # ifdef HAVE_HOST_IN_UTMP
 651         (void) strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
 652 # endif
 653 # ifdef HAVE_ADDR_IN_UTMP
 654         /* this is just a 32-bit IP address */
 655         if (li->hostaddr.sa.sa_family == AF_INET)
 656                 ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
 657 # endif
 658 }
 659 #endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
 660 
 661 /**
 662  ** utmpx utility functions
 663  **
 664  ** These functions manipulate struct utmpx, accounting for system
 665  ** variations.
 666  **/
 667 
 668 #if defined(USE_UTMPX) || defined (USE_WTMPX)
 669 /* build the utmpx structure */
 670 void
 671 set_utmpx_time(struct logininfo *li, struct utmpx *utx)
 672 {
 673 # ifdef HAVE_TV_IN_UTMPX
 674         utx->ut_tv.tv_sec = li->tv_sec;
 675         utx->ut_tv.tv_usec = li->tv_usec;
 676 # else /* HAVE_TV_IN_UTMPX */
 677 #  ifdef HAVE_TIME_IN_UTMPX
 678         utx->ut_time = li->tv_sec;
 679 #  endif /* HAVE_TIME_IN_UTMPX */
 680 # endif /* HAVE_TV_IN_UTMPX */
 681 }
 682 
 683 void
 684 construct_utmpx(struct logininfo *li, struct utmpx *utx)
 685 {
 686         (void) memset(utx, '\0', sizeof(*utx));
 687 # ifdef HAVE_ID_IN_UTMPX
 688         (void) line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
 689 # endif
 690 
 691         /* this is done here to keep utmp constants out of loginrec.h */
 692         switch (li->type) {
 693         case LTYPE_LOGIN:
 694                 utx->ut_type = USER_PROCESS;
 695                 break;
 696         case LTYPE_LOGOUT:
 697                 utx->ut_type = DEAD_PROCESS;
 698                 break;
 699         }
 700         if (!li->line_null)
 701                 (void) line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
 702         else if (!li->progname_null)
 703                 (void) line_stripname(utx->ut_line, li->progname, sizeof(utx->ut_line));
 704 
 705         set_utmpx_time(li, utx);
 706         utx->ut_pid = li->pid;
 707         /* strncpy(): Don't necessarily want null termination */
 708         (void) strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
 709 
 710         if (li->type == LTYPE_LOGOUT)
 711                 return;
 712 
 713         /*
 714          * These fields are only used when logging in, and are blank
 715          * for logouts.
 716          */
 717 
 718 # ifdef HAVE_HOST_IN_UTMPX
 719         (void) strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
 720 # endif
 721 # ifdef HAVE_ADDR_IN_UTMPX
 722         /* this is just a 32-bit IP address */
 723         if (li->hostaddr.sa.sa_family == AF_INET)
 724                 utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
 725 # endif
 726 # ifdef HAVE_SYSLEN_IN_UTMPX
 727         /* ut_syslen is the length of the utx_host string */
 728         utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
 729 # endif
 730 }
 731 #endif /* USE_UTMPX || USE_WTMPX */
 732 
 733 /**
 734  ** Low-level utmp functions
 735  **/
 736 
 737 /* FIXME: (ATL) utmp_write_direct needs testing */
 738 #ifdef USE_UTMP
 739 
 740 /* if we can, use pututline() etc. */
 741 # if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
 742         defined(HAVE_PUTUTLINE)
 743 #  define UTMP_USE_LIBRARY
 744 # endif
 745 
 746 
 747 /* write a utmp entry with the system's help (pututline() and pals) */
 748 # ifdef UTMP_USE_LIBRARY
 749 static int
 750 utmp_write_library(struct logininfo *li, struct utmp *ut)
 751 {
 752         setutent();
 753         pututline(ut);
 754 
 755 #  ifdef HAVE_ENDUTENT
 756         endutent();
 757 #  endif
 758         return 1;
 759 }
 760 # else /* UTMP_USE_LIBRARY */
 761 
 762 /* write a utmp entry direct to the file */
 763 /* This is a slightly modification of code in OpenBSD's login.c */
 764 static int
 765 utmp_write_direct(struct logininfo *li, struct utmp *ut)
 766 {
 767         struct utmp old_ut;
 768         register int fd;
 769         int tty;
 770 
 771         /* FIXME: (ATL) ttyslot() needs local implementation */
 772 
 773 #if defined(HAVE_GETTTYENT)
 774         register struct ttyent *ty;
 775 
 776         tty=0;
 777 
 778         setttyent();
 779         while ((struct ttyent *)0 != (ty = getttyent())) {
 780                 tty++;
 781                 if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
 782                         break;
 783         }
 784         endttyent();
 785 
 786         if((struct ttyent *)0 == ty) {
 787                 log("utmp_write_entry: tty not found");
 788                 return(1);
 789         }
 790 #else /* FIXME */
 791 
 792         tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
 793 
 794 #endif /* HAVE_GETTTYENT */
 795 
 796         if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
 797                 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
 798                 /*
 799                  * Prevent luser from zero'ing out ut_host.
 800                  * If the new ut_line is empty but the old one is not
 801                  * and ut_line and ut_name match, preserve the old ut_line.
 802                  */
 803                 if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
 804                         (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
 805                         (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
 806                         (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
 807                         (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
 808                 }
 809 
 810                 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
 811                 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut))
 812                         log("utmp_write_direct: error writing %s: %s",
 813                             UTMP_FILE, strerror(errno));
 814 
 815                 (void)close(fd);
 816                 return 1;
 817         } else {
 818                 return 0;
 819         }
 820 }
 821 # endif /* UTMP_USE_LIBRARY */
 822 
 823 static int
 824 utmp_perform_login(struct logininfo *li)
 825 {
 826         struct utmp ut;
 827 
 828         construct_utmp(li, &ut);
 829 # ifdef UTMP_USE_LIBRARY
 830         if (!utmp_write_library(li, &ut)) {
 831                 log("utmp_perform_login: utmp_write_library() failed");
 832                 return 0;
 833         }
 834 # else
 835         if (!utmp_write_direct(li, &ut)) {
 836                 log("utmp_perform_login: utmp_write_direct() failed");
 837                 return 0;
 838         }
 839 # endif
 840         return 1;
 841 }
 842 
 843 
 844 static int
 845 utmp_perform_logout(struct logininfo *li)
 846 {
 847         struct utmp ut;
 848 
 849         construct_utmp(li, &ut);
 850 # ifdef UTMP_USE_LIBRARY
 851         if (!utmp_write_library(li, &ut)) {
 852                 log("utmp_perform_logout: utmp_write_library() failed");
 853                 return 0;
 854         }
 855 # else
 856         if (!utmp_write_direct(li, &ut)) {
 857                 log("utmp_perform_logout: utmp_write_direct() failed");
 858                 return 0;
 859         }
 860 # endif
 861         return 1;
 862 }
 863 
 864 
 865 int
 866 utmp_write_entry(struct logininfo *li)
 867 {
 868         if (li->line_null) {
 869                 debug3("not writing utmp entry");
 870                 return 1;
 871         }
 872         debug3("writing utmp entry");
 873 
 874         switch(li->type) {
 875         case LTYPE_LOGIN:
 876                 return utmp_perform_login(li);
 877 
 878         case LTYPE_LOGOUT:
 879                 return utmp_perform_logout(li);
 880 
 881         default:
 882                 log("utmp_write_entry: invalid type field");
 883                 return 0;
 884         }
 885 }
 886 #endif /* USE_UTMP */
 887 
 888 
 889 /**
 890  ** Low-level utmpx functions
 891  **/
 892 
 893 /* not much point if we don't want utmpx entries */
 894 #ifdef USE_UTMPX
 895 
 896 /* if we have the wherewithall, use pututxline etc. */
 897 # if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
 898         defined(HAVE_PUTUTXLINE)
 899 #  define UTMPX_USE_LIBRARY
 900 # endif
 901 
 902 
 903 /* write a utmpx entry with the system's help (pututxline() and pals) */
 904 # ifdef UTMPX_USE_LIBRARY
 905 static int
 906 utmpx_write_library(struct logininfo *li, struct utmpx *utx)
 907 {
 908         setutxent();
 909         (void) pututxline(utx);
 910 
 911 #  ifdef HAVE_ENDUTXENT
 912         endutxent();
 913 #  endif
 914         return 1;
 915 }
 916 
 917 # else /* UTMPX_USE_LIBRARY */
 918 
 919 /* write a utmp entry direct to the file */
 920 static int
 921 utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
 922 {
 923         log("utmpx_write_direct: not implemented!");
 924         return 0;
 925 }
 926 # endif /* UTMPX_USE_LIBRARY */
 927 
 928 static int
 929 utmpx_perform_login(struct logininfo *li)
 930 {
 931         struct utmpx utx;
 932 
 933         construct_utmpx(li, &utx);
 934 # ifdef UTMPX_USE_LIBRARY
 935         if (!utmpx_write_library(li, &utx)) {
 936                 log("tmpx_perform_login: utmp_write_library() failed");
 937                 return 0;
 938         }
 939 # else
 940         if (!utmpx_write_direct(li, &ut)) {
 941                 log("utmpx_perform_login: utmp_write_direct() failed");
 942                 return 0;
 943         }
 944 # endif
 945         return 1;
 946 }
 947 
 948 
 949 static int
 950 utmpx_perform_logout(struct logininfo *li)
 951 {
 952         struct utmpx utx;
 953 
 954         construct_utmpx(li, &utx);
 955 # ifdef HAVE_ID_IN_UTMPX
 956         (void) line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
 957 # endif
 958 # ifdef HAVE_TYPE_IN_UTMPX
 959         utx.ut_type = DEAD_PROCESS;
 960 # endif
 961 
 962 # ifdef UTMPX_USE_LIBRARY
 963         (void) utmpx_write_library(li, &utx);
 964 # else
 965         utmpx_write_direct(li, &utx);
 966 # endif
 967         return 1;
 968 }
 969 
 970 int
 971 utmpx_write_entry(struct logininfo *li)
 972 {
 973         if (li->line_null) {
 974                 debug3("not writing utmpx entry");
 975                 return 1;
 976         }
 977         debug3("writing utmpx entry");
 978 
 979         switch(li->type) {
 980         case LTYPE_LOGIN:
 981                 return utmpx_perform_login(li);
 982         case LTYPE_LOGOUT:
 983                 return utmpx_perform_logout(li);
 984         default:
 985                 log("utmpx_write_entry: invalid type field");
 986                 return 0;
 987         }
 988 }
 989 #endif /* USE_UTMPX */
 990 
 991 
 992 /**
 993  ** Low-level wtmp functions
 994  **/
 995 
 996 #ifdef USE_WTMP
 997 
 998 /* write a wtmp entry direct to the end of the file */
 999 /* This is a slight modification of code in OpenBSD's logwtmp.c */
1000 static int
1001 wtmp_write(struct logininfo *li, struct utmp *ut)
1002 {
1003         struct stat buf;
1004         int fd, ret = 1;
1005 
1006         if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1007                 log("wtmp_write: problem writing %s: %s",
1008                     WTMP_FILE, strerror(errno));
1009                 return 0;
1010         }
1011         if (fstat(fd, &buf) == 0)
1012                 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
1013                         (void) ftruncate(fd, buf.st_size);
1014                         log("wtmp_write: problem writing %s: %s",
1015                             WTMP_FILE, strerror(errno));
1016                         ret = 0;
1017                 }
1018         (void)close(fd);
1019         return ret;
1020 }
1021 
1022 static int
1023 wtmp_perform_login(struct logininfo *li)
1024 {
1025         struct utmp ut;
1026 
1027         construct_utmp(li, &ut);
1028         return wtmp_write(li, &ut);
1029 }
1030 
1031 
1032 static int
1033 wtmp_perform_logout(struct logininfo *li)
1034 {
1035         struct utmp ut;
1036 
1037         construct_utmp(li, &ut);
1038         return wtmp_write(li, &ut);
1039 }
1040 
1041 
1042 int
1043 wtmp_write_entry(struct logininfo *li)
1044 {
1045         switch(li->type) {
1046         case LTYPE_LOGIN:
1047                 return wtmp_perform_login(li);
1048         case LTYPE_LOGOUT:
1049                 return wtmp_perform_logout(li);
1050         default:
1051                 log("wtmp_write_entry: invalid type field");
1052                 return 0;
1053         }
1054 }
1055 
1056 
1057 /* Notes on fetching login data from wtmp/wtmpx
1058  *
1059  * Logouts are usually recorded with (amongst other things) a blank
1060  * username on a given tty line.  However, some systems (HP-UX is one)
1061  * leave all fields set, but change the ut_type field to DEAD_PROCESS.
1062  *
1063  * Since we're only looking for logins here, we know that the username
1064  * must be set correctly. On systems that leave it in, we check for
1065  * ut_type==USER_PROCESS (indicating a login.)
1066  *
1067  * Portability: Some systems may set something other than USER_PROCESS
1068  * to indicate a login process. I don't know of any as I write. Also,
1069  * it's possible that some systems may both leave the username in
1070  * place and not have ut_type.
1071  */
1072 
1073 /* return true if this wtmp entry indicates a login */
1074 static int
1075 wtmp_islogin(struct logininfo *li, struct utmp *ut)
1076 {
1077         if (strncmp(li->username, ut->ut_name,
1078                 MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
1079 # ifdef HAVE_TYPE_IN_UTMP
1080                 if (ut->ut_type & USER_PROCESS)
1081                         return 1;
1082 # else
1083                 return 1;
1084 # endif
1085         }
1086         return 0;
1087 }
1088 
1089 int
1090 wtmp_get_entry(struct logininfo *li)
1091 {
1092         struct stat st;
1093         struct utmp ut;
1094         int fd, found=0;
1095 
1096         /* Clear the time entries in our logininfo */
1097         li->tv_sec = li->tv_usec = 0;
1098 
1099         if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1100                 log("wtmp_get_entry: problem opening %s: %s",
1101                     WTMP_FILE, strerror(errno));
1102                 return 0;
1103         }
1104         if (fstat(fd, &st) != 0) {
1105                 log("wtmp_get_entry: couldn't stat %s: %s",
1106                     WTMP_FILE, strerror(errno));
1107                 (void) close(fd);
1108                 return 0;
1109         }
1110 
1111         /* Seek to the start of the last struct utmp */
1112         if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
1113                 /* Looks like we've got a fresh wtmp file */
1114                 (void) close(fd);
1115                 return 0;
1116         }
1117 
1118         while (!found) {
1119                 if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
1120                         log("wtmp_get_entry: read of %s failed: %s",
1121                             WTMP_FILE, strerror(errno));
1122                         (void) close (fd);
1123                         return 0;
1124                 }
1125                 if ( wtmp_islogin(li, &ut) ) {
1126                         found = 1;
1127                         /* We've already checked for a time in struct
1128                          * utmp, in login_getlast(). */
1129 # ifdef HAVE_TIME_IN_UTMP
1130                         li->tv_sec = ut.ut_time;
1131 # else
1132 #  if HAVE_TV_IN_UTMP
1133                         li->tv_sec = ut.ut_tv.tv_sec;
1134 #  endif
1135 # endif
1136                         (void) line_fullname(li->line, ut.ut_line,
1137                                       MIN_SIZEOF(li->line, ut.ut_line));
1138 # ifdef HAVE_HOST_IN_UTMP
1139                         (void) strlcpy(li->hostname, ut.ut_host,
1140                                 MIN_SIZEOF(li->hostname, ut.ut_host));
1141 # endif
1142                         continue;
1143                 }
1144                 /* Seek back 2 x struct utmp */
1145                 if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
1146                         /* We've found the start of the file, so quit */
1147                         (void) close (fd);
1148                         return 0;
1149                 }
1150         }
1151 
1152         /* We found an entry. Tidy up and return */
1153         (void) close(fd);
1154         return 1;
1155 }
1156 # endif /* USE_WTMP */
1157 
1158 
1159 /**
1160  ** Low-level wtmpx functions
1161  **/
1162 
1163 #ifdef USE_WTMPX
1164 /* write a wtmpx entry direct to the end of the file */
1165 /* This is a slight modification of code in OpenBSD's logwtmp.c */
1166 static int
1167 wtmpx_write(struct logininfo *li, struct utmpx *utx)
1168 {
1169         struct stat buf;
1170         int fd, ret = 1;
1171 
1172         if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1173                 log("wtmpx_write: problem opening %s: %s",
1174                     WTMPX_FILE, strerror(errno));
1175                 return 0;
1176         }
1177 
1178         if (fstat(fd, &buf) == 0)
1179                 if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
1180                         (void) ftruncate(fd, buf.st_size);
1181                         log("wtmpx_write: problem writing %s: %s",
1182                             WTMPX_FILE, strerror(errno));
1183                         ret = 0;
1184                 }
1185         (void)close(fd);
1186 
1187         return ret;
1188 }
1189 
1190 
1191 static int
1192 wtmpx_perform_login(struct logininfo *li)
1193 {
1194         struct utmpx utx;
1195 
1196         construct_utmpx(li, &utx);
1197         return wtmpx_write(li, &utx);
1198 }
1199 
1200 
1201 static int
1202 wtmpx_perform_logout(struct logininfo *li)
1203 {
1204         struct utmpx utx;
1205 
1206         construct_utmpx(li, &utx);
1207         return wtmpx_write(li, &utx);
1208 }
1209 
1210 
1211 int
1212 wtmpx_write_entry(struct logininfo *li)
1213 {
1214         switch(li->type) {
1215         case LTYPE_LOGIN:
1216                 return wtmpx_perform_login(li);
1217         case LTYPE_LOGOUT:
1218                 return wtmpx_perform_logout(li);
1219         default:
1220                 log("wtmpx_write_entry: invalid type field");
1221                 return 0;
1222         }
1223 }
1224 
1225 /* Please see the notes above wtmp_islogin() for information about the
1226    next two functions */
1227 
1228 /* Return true if this wtmpx entry indicates a login */
1229 static int
1230 wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1231 {
1232         if ( strncmp(li->username, utx->ut_name,
1233                 MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
1234 # ifdef HAVE_TYPE_IN_UTMPX
1235                 if (utx->ut_type == USER_PROCESS)
1236                         return 1;
1237 # else
1238                 return 1;
1239 # endif
1240         }
1241         return 0;
1242 }
1243 
1244 
1245 #if 0
1246 int
1247 wtmpx_get_entry(struct logininfo *li)
1248 {
1249         struct stat st;
1250         struct utmpx utx;
1251         int fd, found=0;
1252 
1253         /* Clear the time entries */
1254         li->tv_sec = li->tv_usec = 0;
1255 
1256         if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1257                 log("wtmpx_get_entry: problem opening %s: %s",
1258                     WTMPX_FILE, strerror(errno));
1259                 return 0;
1260         }
1261         if (fstat(fd, &st) != 0) {
1262                 log("wtmpx_get_entry: couldn't stat %s: %s",
1263                     WTMPX_FILE, strerror(errno));
1264                 (void) close(fd);
1265                 return 0;
1266         }
1267 
1268         /* Seek to the start of the last struct utmpx */
1269         if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) {
1270                 /* probably a newly rotated wtmpx file */
1271                 (void) close(fd);
1272                 return 0;
1273         }
1274 
1275         while (!found) {
1276                 if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
1277                         log("wtmpx_get_entry: read of %s failed: %s",
1278                             WTMPX_FILE, strerror(errno));
1279                         (void) close (fd);
1280                         return 0;
1281                 }
1282                 /* Logouts are recorded as a blank username on a particular line.
1283                  * So, we just need to find the username in struct utmpx */
1284                 if ( wtmpx_islogin(li, &utx) ) {
1285                         found = 1;
1286 # ifdef HAVE_TV_IN_UTMPX
1287                         li->tv_sec = utx.ut_tv.tv_sec;
1288 # else
1289 #  ifdef HAVE_TIME_IN_UTMPX
1290                         li->tv_sec = utx.ut_time;
1291 #  endif
1292 # endif
1293                         (void) line_fullname(li->line, utx.ut_line, sizeof(li->line));
1294 # ifdef HAVE_HOST_IN_UTMPX
1295                         (void) strlcpy(li->hostname, utx.ut_host,
1296                                 MIN_SIZEOF(li->hostname, utx.ut_host));
1297 # endif
1298                         continue;
1299                 }
1300                 if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) {
1301                         (void) close (fd);
1302                         return 0;
1303                 }
1304         }
1305 
1306         (void) close(fd);
1307         return 1;
1308 }
1309 #endif
1310 #endif /* USE_WTMPX */
1311 
1312 /**
1313  ** Low-level libutil login() functions
1314  **/
1315 
1316 #ifdef USE_LOGIN
1317 static int
1318 syslogin_perform_login(struct logininfo *li)
1319 {
1320         struct utmp *ut;
1321 
1322         if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
1323                 log("syslogin_perform_login: couldn't malloc()");
1324                 return 0;
1325         }
1326         construct_utmp(li, ut);
1327         login(ut);
1328 
1329         return 1;
1330 }
1331 
1332 static int
1333 syslogin_perform_logout(struct logininfo *li)
1334 {
1335 # ifdef HAVE_LOGOUT
1336         char line[8];
1337 
1338         (void)line_stripname(line, li->line, sizeof(line));
1339 
1340         if (!logout(line)) {
1341                 log("syslogin_perform_logout: logout() returned an error");
1342 #  ifdef HAVE_LOGWTMP
1343         } else {
1344                 logwtmp(line, "", "");
1345 #  endif
1346         }
1347         /* FIXME: (ATL - if the need arises) What to do if we have
1348          * login, but no logout?  what if logout but no logwtmp? All
1349          * routines are in libutil so they should all be there,
1350          * but... */
1351 # endif
1352         return 1;
1353 }
1354 
1355 int
1356 syslogin_write_entry(struct logininfo *li)
1357 {
1358         switch (li->type) {
1359         case LTYPE_LOGIN:
1360                 return syslogin_perform_login(li);
1361         case LTYPE_LOGOUT:
1362                 return syslogin_perform_logout(li);
1363         default:
1364                 log("syslogin_write_entry: Invalid type field");
1365                 return 0;
1366         }
1367 }
1368 #endif /* USE_LOGIN */
1369 
1370 /* end of file log-syslogin.c */
1371 
1372 /**
1373  ** Low-level lastlog functions
1374  **/
1375 
1376 #ifdef USE_LASTLOG
1377 #define LL_FILE 1
1378 #define LL_DIR 2
1379 #define LL_OTHER 3
1380 
1381 static void
1382 lastlog_construct(struct logininfo *li, struct lastlog *last)
1383 {
1384         /* clear the structure */
1385         (void) memset(last, '\0', sizeof(*last));
1386 
1387         (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
1388         (void) strlcpy(last->ll_host, li->hostname,
1389                 MIN_SIZEOF(last->ll_host, li->hostname));
1390         last->ll_time = li->tv_sec;
1391 }
1392 
1393 static int
1394 lastlog_filetype(char *filename)
1395 {
1396         struct stat st;
1397 
1398         if (stat(LASTLOG_FILE, &st) != 0) {
1399                 log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
1400                         strerror(errno));
1401                 return 0;
1402         }
1403         if (S_ISDIR(st.st_mode))
1404                 return LL_DIR;
1405         else if (S_ISREG(st.st_mode))
1406                 return LL_FILE;
1407         else
1408                 return LL_OTHER;
1409 }
1410 
1411 
1412 /* open the file (using filemode) and seek to the login entry */
1413 static int
1414 lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1415 {
1416         off_t offset;
1417         int type;
1418         char lastlog_file[1024];
1419 
1420         type = lastlog_filetype(LASTLOG_FILE);
1421         switch (type) {
1422                 case LL_FILE:
1423                         (void) strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1424                         break;
1425                 case LL_DIR:
1426                         (void) snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1427                                  LASTLOG_FILE, li->username);
1428                         break;
1429                 default:
1430                         log("lastlog_openseek: %.100s is not a file or directory!",
1431                             LASTLOG_FILE);
1432                         return 0;
1433         }
1434 
1435         *fd = open(lastlog_file, filemode);
1436         if ( *fd < 0) {
1437                 debug("lastlog_openseek: Couldn't open %s: %s",
1438                     lastlog_file, strerror(errno));
1439                 return 0;
1440         }
1441 
1442         if (type == LL_FILE) {
1443                 /* find this uid's offset in the lastlog file */
1444                 offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
1445 
1446                 if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1447                         log("lastlog_openseek: %s->lseek(): %s",
1448                          lastlog_file, strerror(errno));
1449                         return 0;
1450                 }
1451         }
1452 
1453         return 1;
1454 }
1455 
1456 static int
1457 lastlog_perform_login(struct logininfo *li)
1458 {
1459         struct lastlog last;
1460         int fd;
1461 
1462         /* create our struct lastlog */
1463         lastlog_construct(li, &last);
1464 
1465         if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
1466                 return(0);
1467 
1468         /* write the entry */
1469         if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) {
1470                 (void) close(fd);
1471                 log("lastlog_write_filemode: Error writing to %s: %s",
1472                     LASTLOG_FILE, strerror(errno));
1473                 return 0;
1474         }
1475 
1476         (void) close(fd);
1477         return 1;
1478 }
1479 
1480 int
1481 lastlog_write_entry(struct logininfo *li)
1482 {
1483         switch(li->type) {
1484         case LTYPE_LOGIN:
1485                 return lastlog_perform_login(li);
1486         default:
1487                 log("lastlog_write_entry: Invalid type field");
1488                 return 0;
1489         }
1490 }
1491 
1492 static void
1493 lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1494 {
1495         (void) line_fullname(li->line, last->ll_line, sizeof(li->line));
1496         (void) strlcpy(li->hostname, last->ll_host,
1497                 MIN_SIZEOF(li->hostname, last->ll_host));
1498         li->tv_sec = last->ll_time;
1499 }
1500 
1501 int
1502 lastlog_get_entry(struct logininfo *li)
1503 {
1504         struct lastlog last;
1505         int fd, ret;
1506 
1507         if (!lastlog_openseek(li, &fd, O_RDONLY))
1508                 return (0);
1509 
1510         ret = atomicio(read, fd, &last, sizeof(last));
1511         close(fd);
1512 
1513         switch (ret) {
1514         case 0:
1515                 memset(&last, '\0', sizeof(last));
1516                 /* FALLTHRU */
1517         case sizeof(last):
1518                 lastlog_populate_entry(li, &last);
1519                 return (1);
1520         case -1:
1521                 error("%s: Error reading from %s: %s", __func__,
1522                     LASTLOG_FILE, strerror(errno));
1523                 return (0);
1524         default:
1525                 error("%s: Error reading from %s: Expecting %d, got %d",
1526                     __func__, LASTLOG_FILE, (int)sizeof(last), ret);
1527                 return (0);
1528         }
1529 
1530         /* NOTREACHED */
1531         return (0);
1532 }
1533 #endif /* USE_LASTLOG */