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