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