198937Sdes/* 298937Sdes * Copyright (c) 2000 Andre Lucas. All rights reserved. 398937Sdes * Portions copyright (c) 1998 Todd C. Miller 498937Sdes * Portions copyright (c) 1996 Jason Downs 598937Sdes * Portions copyright (c) 1996 Theo de Raadt 698937Sdes * 798937Sdes * Redistribution and use in source and binary forms, with or without 898937Sdes * modification, are permitted provided that the following conditions 998937Sdes * are met: 1098937Sdes * 1. Redistributions of source code must retain the above copyright 1198937Sdes * notice, this list of conditions and the following disclaimer. 1298937Sdes * 2. Redistributions in binary form must reproduce the above copyright 1398937Sdes * notice, this list of conditions and the following disclaimer in the 1498937Sdes * documentation and/or other materials provided with the distribution. 1598937Sdes * 1698937Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1798937Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1898937Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1998937Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2098937Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2198937Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2298937Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2398937Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2498937Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2598937Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2698937Sdes */ 2798937Sdes 28147005Sdes/* 29147005Sdes * The btmp logging code is derived from login.c from util-linux and is under 30147005Sdes * the the following license: 31147005Sdes * 32147005Sdes * Copyright (c) 1980, 1987, 1988 The Regents of the University of California. 33147005Sdes * All rights reserved. 34147005Sdes * 35147005Sdes * Redistribution and use in source and binary forms are permitted 36147005Sdes * provided that the above copyright notice and this paragraph are 37147005Sdes * duplicated in all such forms and that any documentation, 38147005Sdes * advertising materials, and other materials related to such 39147005Sdes * distribution and use acknowledge that the software was developed 40147005Sdes * by the University of California, Berkeley. The name of the 41147005Sdes * University may not be used to endorse or promote products derived 42147005Sdes * from this software without specific prior written permission. 43147005Sdes * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 44147005Sdes * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 45147005Sdes * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 46147005Sdes */ 47147005Sdes 48147005Sdes 4998937Sdes/** 5098937Sdes ** loginrec.c: platform-independent login recording and lastlog retrieval 5198937Sdes **/ 5298937Sdes 5398937Sdes/* 54147005Sdes * The new login code explained 55147005Sdes * ============================ 56147005Sdes * 57147005Sdes * This code attempts to provide a common interface to login recording 58147005Sdes * (utmp and friends) and last login time retrieval. 59147005Sdes * 60147005Sdes * Its primary means of achieving this is to use 'struct logininfo', a 61147005Sdes * union of all the useful fields in the various different types of 62147005Sdes * system login record structures one finds on UNIX variants. 63147005Sdes * 64147005Sdes * We depend on autoconf to define which recording methods are to be 65147005Sdes * used, and which fields are contained in the relevant data structures 66147005Sdes * on the local system. Many C preprocessor symbols affect which code 67147005Sdes * gets compiled here. 68147005Sdes * 69147005Sdes * The code is designed to make it easy to modify a particular 70147005Sdes * recording method, without affecting other methods nor requiring so 71147005Sdes * many nested conditional compilation blocks as were commonplace in 72147005Sdes * the old code. 73147005Sdes * 74147005Sdes * For login recording, we try to use the local system's libraries as 75147005Sdes * these are clearly most likely to work correctly. For utmp systems 76147005Sdes * this usually means login() and logout() or setutent() etc., probably 77147005Sdes * in libutil, along with logwtmp() etc. On these systems, we fall back 78147005Sdes * to writing the files directly if we have to, though this method 79147005Sdes * requires very thorough testing so we do not corrupt local auditing 80147005Sdes * information. These files and their access methods are very system 81147005Sdes * specific indeed. 82147005Sdes * 83147005Sdes * For utmpx systems, the corresponding library functions are 84147005Sdes * setutxent() etc. To the author's knowledge, all utmpx systems have 85147005Sdes * these library functions and so no direct write is attempted. If such 86147005Sdes * a system exists and needs support, direct analogues of the [uw]tmp 87147005Sdes * code should suffice. 88147005Sdes * 89147005Sdes * Retrieving the time of last login ('lastlog') is in some ways even 90147005Sdes * more problemmatic than login recording. Some systems provide a 91147005Sdes * simple table of all users which we seek based on uid and retrieve a 92147005Sdes * relatively standard structure. Others record the same information in 93147005Sdes * a directory with a separate file, and others don't record the 94147005Sdes * information separately at all. For systems in the latter category, 95147005Sdes * we look backwards in the wtmp or wtmpx file for the last login entry 96147005Sdes * for our user. Naturally this is slower and on busy systems could 97147005Sdes * incur a significant performance penalty. 98147005Sdes * 99147005Sdes * Calling the new code 100147005Sdes * -------------------- 101147005Sdes * 102147005Sdes * In OpenSSH all login recording and retrieval is performed in 103147005Sdes * login.c. Here you'll find working examples. Also, in the logintest.c 104147005Sdes * program there are more examples. 105147005Sdes * 106147005Sdes * Internal handler calling method 107147005Sdes * ------------------------------- 108147005Sdes * 109147005Sdes * When a call is made to login_login() or login_logout(), both 110147005Sdes * routines set a struct logininfo flag defining which action (log in, 111147005Sdes * or log out) is to be taken. They both then call login_write(), which 112147005Sdes * calls whichever of the many structure-specific handlers autoconf 113147005Sdes * selects for the local system. 114147005Sdes * 115147005Sdes * The handlers themselves handle system data structure specifics. Both 116147005Sdes * struct utmp and struct utmpx have utility functions (see 117147005Sdes * construct_utmp*()) to try to make it simpler to add extra systems 118147005Sdes * that introduce new features to either structure. 119147005Sdes * 120147005Sdes * While it may seem terribly wasteful to replicate so much similar 121147005Sdes * code for each method, experience has shown that maintaining code to 122147005Sdes * write both struct utmp and utmpx in one function, whilst maintaining 123147005Sdes * support for all systems whether they have library support or not, is 124147005Sdes * a difficult and time-consuming task. 125147005Sdes * 126147005Sdes * Lastlog support proceeds similarly. Functions login_get_lastlog() 127147005Sdes * (and its OpenSSH-tuned friend login_get_lastlog_time()) call 128147005Sdes * getlast_entry(), which tries one of three methods to find the last 129147005Sdes * login time. It uses local system lastlog support if it can, 130147005Sdes * otherwise it tries wtmp or wtmpx before giving up and returning 0, 131147005Sdes * meaning "tilt". 132147005Sdes * 133147005Sdes * Maintenance 134147005Sdes * ----------- 135147005Sdes * 136147005Sdes * In many cases it's possible to tweak autoconf to select the correct 137147005Sdes * methods for a particular platform, either by improving the detection 138147005Sdes * code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE 139147005Sdes * symbols for the platform. 140147005Sdes * 141147005Sdes * Use logintest to check which symbols are defined before modifying 142147005Sdes * configure.ac and loginrec.c. (You have to build logintest yourself 143147005Sdes * with 'make logintest' as it's not built by default.) 144147005Sdes * 145147005Sdes * Otherwise, patches to the specific method(s) are very helpful! 146147005Sdes */ 14798937Sdes 14898937Sdes#include "includes.h" 14998937Sdes 150162856Sdes#include <sys/types.h> 151162856Sdes#include <sys/stat.h> 152162856Sdes#include <sys/socket.h> 153296781Sdes#ifdef HAVE_SYS_TIME_H 154296781Sdes# include <sys/time.h> 155296781Sdes#endif 156162856Sdes 157162856Sdes#include <netinet/in.h> 158162856Sdes 159162856Sdes#include <errno.h> 160162856Sdes#include <fcntl.h> 161162856Sdes#ifdef HAVE_PATHS_H 162162856Sdes# include <paths.h> 163162856Sdes#endif 164162856Sdes#include <pwd.h> 165162856Sdes#include <stdarg.h> 166162856Sdes#include <string.h> 167181111Sdes#include <time.h> 168162856Sdes#include <unistd.h> 169162856Sdes 170162856Sdes#include "xmalloc.h" 171162856Sdes#include "key.h" 172162856Sdes#include "hostfile.h" 17398937Sdes#include "ssh.h" 17498937Sdes#include "loginrec.h" 17598937Sdes#include "log.h" 17698937Sdes#include "atomicio.h" 177147005Sdes#include "packet.h" 178147005Sdes#include "canohost.h" 179147005Sdes#include "auth.h" 180147005Sdes#include "buffer.h" 18198937Sdes 18298937Sdes#ifdef HAVE_UTIL_H 183147005Sdes# include <util.h> 18498937Sdes#endif 18598937Sdes 18698937Sdes/** 18798937Sdes ** prototypes for helper functions in this file 18898937Sdes **/ 18998937Sdes 19098937Sdes#if HAVE_UTMP_H 19198937Sdesvoid set_utmp_time(struct logininfo *li, struct utmp *ut); 19298937Sdesvoid construct_utmp(struct logininfo *li, struct utmp *ut); 19398937Sdes#endif 19498937Sdes 19598937Sdes#ifdef HAVE_UTMPX_H 19698937Sdesvoid set_utmpx_time(struct logininfo *li, struct utmpx *ut); 19798937Sdesvoid construct_utmpx(struct logininfo *li, struct utmpx *ut); 19898937Sdes#endif 19998937Sdes 20098937Sdesint utmp_write_entry(struct logininfo *li); 20198937Sdesint utmpx_write_entry(struct logininfo *li); 20298937Sdesint wtmp_write_entry(struct logininfo *li); 20398937Sdesint wtmpx_write_entry(struct logininfo *li); 20498937Sdesint lastlog_write_entry(struct logininfo *li); 20598937Sdesint syslogin_write_entry(struct logininfo *li); 20698937Sdes 20798937Sdesint getlast_entry(struct logininfo *li); 20898937Sdesint lastlog_get_entry(struct logininfo *li); 209202213Sedint utmpx_get_entry(struct logininfo *li); 21098937Sdesint wtmp_get_entry(struct logininfo *li); 21198937Sdesint wtmpx_get_entry(struct logininfo *li); 21298937Sdes 213147005Sdesextern Buffer loginmsg; 214147005Sdes 21598937Sdes/* pick the shortest string */ 216147005Sdes#define MIN_SIZEOF(s1,s2) (sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2)) 21798937Sdes 21898937Sdes/** 21998937Sdes ** platform-independent login functions 22098937Sdes **/ 22198937Sdes 222147005Sdes/* 223147005Sdes * login_login(struct logininfo *) - Record a login 22498937Sdes * 22598937Sdes * Call with a pointer to a struct logininfo initialised with 22698937Sdes * login_init_entry() or login_alloc_entry() 22798937Sdes * 22898937Sdes * Returns: 22998937Sdes * >0 if successful 23098937Sdes * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 23198937Sdes */ 23298937Sdesint 233147005Sdeslogin_login(struct logininfo *li) 23498937Sdes{ 23598937Sdes li->type = LTYPE_LOGIN; 236147005Sdes return (login_write(li)); 23798937Sdes} 23898937Sdes 23998937Sdes 240147005Sdes/* 241147005Sdes * login_logout(struct logininfo *) - Record a logout 24298937Sdes * 24398937Sdes * Call as with login_login() 24498937Sdes * 24598937Sdes * Returns: 24698937Sdes * >0 if successful 24798937Sdes * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 24898937Sdes */ 24998937Sdesint 25098937Sdeslogin_logout(struct logininfo *li) 25198937Sdes{ 25298937Sdes li->type = LTYPE_LOGOUT; 253147005Sdes return (login_write(li)); 25498937Sdes} 25598937Sdes 256147005Sdes/* 257147005Sdes * login_get_lastlog_time(int) - Retrieve the last login time 25898937Sdes * 25998937Sdes * Retrieve the last login time for the given uid. Will try to use the 26098937Sdes * system lastlog facilities if they are available, but will fall back 26198937Sdes * to looking in wtmp/wtmpx if necessary 26298937Sdes * 26398937Sdes * Returns: 26498937Sdes * 0 on failure, or if user has never logged in 26598937Sdes * Time in seconds from the epoch if successful 26698937Sdes * 26798937Sdes * Useful preprocessor symbols: 26898937Sdes * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog 26998937Sdes * info 27098937Sdes * USE_LASTLOG: If set, indicates the presence of system lastlog 27198937Sdes * facilities. If this and DISABLE_LASTLOG are not set, 27298937Sdes * try to retrieve lastlog information from wtmp/wtmpx. 27398937Sdes */ 27498937Sdesunsigned int 275221420Sdeslogin_get_lastlog_time(const uid_t uid) 27698937Sdes{ 27798937Sdes struct logininfo li; 27898937Sdes 27998937Sdes if (login_get_lastlog(&li, uid)) 280147005Sdes return (li.tv_sec); 28198937Sdes else 282147005Sdes return (0); 28398937Sdes} 28498937Sdes 285147005Sdes/* 286147005Sdes * login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry 28798937Sdes * 28898937Sdes * Retrieve a logininfo structure populated (only partially) with 28998937Sdes * information from the system lastlog data, or from wtmp/wtmpx if no 29098937Sdes * system lastlog information exists. 29198937Sdes * 29298937Sdes * Note this routine must be given a pre-allocated logininfo. 29398937Sdes * 29498937Sdes * Returns: 29598937Sdes * >0: A pointer to your struct logininfo if successful 29698937Sdes * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 29798937Sdes */ 29898937Sdesstruct logininfo * 299221420Sdeslogin_get_lastlog(struct logininfo *li, const uid_t uid) 30098937Sdes{ 30198937Sdes struct passwd *pw; 30298937Sdes 30398937Sdes memset(li, '\0', sizeof(*li)); 30498937Sdes li->uid = uid; 30598937Sdes 30698937Sdes /* 30798937Sdes * If we don't have a 'real' lastlog, we need the username to 30898937Sdes * reliably search wtmp(x) for the last login (see 30998937Sdes * wtmp_get_entry().) 31098937Sdes */ 31198937Sdes pw = getpwuid(uid); 31298937Sdes if (pw == NULL) 313221420Sdes fatal("%s: Cannot find account for uid %ld", __func__, 314221420Sdes (long)uid); 31598937Sdes 316262566Sdes if (strlcpy(li->username, pw->pw_name, sizeof(li->username)) >= 317262566Sdes sizeof(li->username)) { 318262566Sdes error("%s: username too long (%lu > max %lu)", __func__, 319262566Sdes (unsigned long)strlen(pw->pw_name), 320262566Sdes (unsigned long)sizeof(li->username) - 1); 321262566Sdes return NULL; 322262566Sdes } 32398937Sdes 32498937Sdes if (getlast_entry(li)) 325147005Sdes return (li); 32698937Sdes else 327147005Sdes return (NULL); 32898937Sdes} 32998937Sdes 330147005Sdes/* 331147005Sdes * login_alloc_entry(int, char*, char*, char*) - Allocate and initialise 33298937Sdes * a logininfo structure 33398937Sdes * 33498937Sdes * This function creates a new struct logininfo, a data structure 33598937Sdes * meant to carry the information required to portably record login info. 33698937Sdes * 33798937Sdes * Returns a pointer to a newly created struct logininfo. If memory 33898937Sdes * allocation fails, the program halts. 33998937Sdes */ 34098937Sdesstruct 341221420Sdeslogininfo *login_alloc_entry(pid_t pid, const char *username, 342147005Sdes const char *hostname, const char *line) 34398937Sdes{ 34498937Sdes struct logininfo *newli; 34598937Sdes 346147005Sdes newli = xmalloc(sizeof(*newli)); 347147005Sdes login_init_entry(newli, pid, username, hostname, line); 348147005Sdes return (newli); 34998937Sdes} 35098937Sdes 35198937Sdes 35298937Sdes/* login_free_entry(struct logininfo *) - free struct memory */ 35398937Sdesvoid 35498937Sdeslogin_free_entry(struct logininfo *li) 35598937Sdes{ 356255767Sdes free(li); 35798937Sdes} 35898937Sdes 35998937Sdes 36098937Sdes/* login_init_entry(struct logininfo *, int, char*, char*, char*) 36198937Sdes * - initialise a struct logininfo 36298937Sdes * 36398937Sdes * Populates a new struct logininfo, a data structure meant to carry 36498937Sdes * the information required to portably record login info. 36598937Sdes * 36698937Sdes * Returns: 1 36798937Sdes */ 36898937Sdesint 369221420Sdeslogin_init_entry(struct logininfo *li, pid_t pid, const char *username, 370147005Sdes const char *hostname, const char *line) 37198937Sdes{ 37298937Sdes struct passwd *pw; 37398937Sdes 37498937Sdes memset(li, 0, sizeof(*li)); 37598937Sdes 37698937Sdes li->pid = pid; 37798937Sdes 37898937Sdes /* set the line information */ 37998937Sdes if (line) 38098937Sdes line_fullname(li->line, line, sizeof(li->line)); 38198937Sdes 38298937Sdes if (username) { 38398937Sdes strlcpy(li->username, username, sizeof(li->username)); 38498937Sdes pw = getpwnam(li->username); 385147005Sdes if (pw == NULL) { 386149753Sdes fatal("%s: Cannot find user \"%s\"", __func__, 387147005Sdes li->username); 388147005Sdes } 38998937Sdes li->uid = pw->pw_uid; 39098937Sdes } 39198937Sdes 39298937Sdes if (hostname) 39398937Sdes strlcpy(li->hostname, hostname, sizeof(li->hostname)); 39498937Sdes 395147005Sdes return (1); 39698937Sdes} 39798937Sdes 398149753Sdes/* 399147005Sdes * login_set_current_time(struct logininfo *) - set the current time 40098937Sdes * 40198937Sdes * Set the current time in a logininfo structure. This function is 40298937Sdes * meant to eliminate the need to deal with system dependencies for 40398937Sdes * time handling. 40498937Sdes */ 40598937Sdesvoid 40698937Sdeslogin_set_current_time(struct logininfo *li) 40798937Sdes{ 40898937Sdes struct timeval tv; 40998937Sdes 41098937Sdes gettimeofday(&tv, NULL); 41198937Sdes 41298937Sdes li->tv_sec = tv.tv_sec; 41398937Sdes li->tv_usec = tv.tv_usec; 41498937Sdes} 41598937Sdes 41698937Sdes/* copy a sockaddr_* into our logininfo */ 41798937Sdesvoid 41898937Sdeslogin_set_addr(struct logininfo *li, const struct sockaddr *sa, 419147005Sdes const unsigned int sa_size) 42098937Sdes{ 42198937Sdes unsigned int bufsize = sa_size; 42298937Sdes 42398937Sdes /* make sure we don't overrun our union */ 42498937Sdes if (sizeof(li->hostaddr) < sa_size) 42598937Sdes bufsize = sizeof(li->hostaddr); 42698937Sdes 427147005Sdes memcpy(&li->hostaddr.sa, sa, bufsize); 42898937Sdes} 42998937Sdes 43098937Sdes 43198937Sdes/** 43298937Sdes ** login_write: Call low-level recording functions based on autoconf 43398937Sdes ** results 43498937Sdes **/ 43598937Sdesint 436147005Sdeslogin_write(struct logininfo *li) 43798937Sdes{ 43898937Sdes#ifndef HAVE_CYGWIN 439147005Sdes if (geteuid() != 0) { 440147005Sdes logit("Attempt to write login records by non-root user (aborting)"); 441147005Sdes return (1); 44298937Sdes } 44398937Sdes#endif 44498937Sdes 44598937Sdes /* set the timestamp */ 44698937Sdes login_set_current_time(li); 44798937Sdes#ifdef USE_LOGIN 44898937Sdes syslogin_write_entry(li); 44998937Sdes#endif 45098937Sdes#ifdef USE_LASTLOG 451147005Sdes if (li->type == LTYPE_LOGIN) 45298937Sdes lastlog_write_entry(li); 45398937Sdes#endif 45498937Sdes#ifdef USE_UTMP 45598937Sdes utmp_write_entry(li); 45698937Sdes#endif 45798937Sdes#ifdef USE_WTMP 45898937Sdes wtmp_write_entry(li); 45998937Sdes#endif 46098937Sdes#ifdef USE_UTMPX 46198937Sdes utmpx_write_entry(li); 46298937Sdes#endif 46398937Sdes#ifdef USE_WTMPX 46498937Sdes wtmpx_write_entry(li); 46598937Sdes#endif 466137019Sdes#ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN 467149753Sdes if (li->type == LTYPE_LOGIN && 468149753Sdes !sys_auth_record_login(li->username,li->hostname,li->line, 469149753Sdes &loginmsg)) 470137019Sdes logit("Writing login record failed for %s", li->username); 471137019Sdes#endif 472147005Sdes#ifdef SSH_AUDIT_EVENTS 473147005Sdes if (li->type == LTYPE_LOGIN) 474221420Sdes audit_session_open(li); 475147005Sdes else if (li->type == LTYPE_LOGOUT) 476221420Sdes audit_session_close(li); 477147005Sdes#endif 478147005Sdes return (0); 47998937Sdes} 48098937Sdes 48198937Sdes#ifdef LOGIN_NEEDS_UTMPX 48298937Sdesint 48398937Sdeslogin_utmp_only(struct logininfo *li) 48498937Sdes{ 485126277Sdes li->type = LTYPE_LOGIN; 48698937Sdes login_set_current_time(li); 48798937Sdes# ifdef USE_UTMP 48898937Sdes utmp_write_entry(li); 48998937Sdes# endif 49098937Sdes# ifdef USE_WTMP 49198937Sdes wtmp_write_entry(li); 49298937Sdes# endif 49398937Sdes# ifdef USE_UTMPX 49498937Sdes utmpx_write_entry(li); 49598937Sdes# endif 49698937Sdes# ifdef USE_WTMPX 49798937Sdes wtmpx_write_entry(li); 49898937Sdes# endif 499147005Sdes return (0); 50098937Sdes} 50198937Sdes#endif 50298937Sdes 50398937Sdes/** 50498937Sdes ** getlast_entry: Call low-level functions to retrieve the last login 50598937Sdes ** time. 50698937Sdes **/ 50798937Sdes 50898937Sdes/* take the uid in li and return the last login time */ 50998937Sdesint 51098937Sdesgetlast_entry(struct logininfo *li) 51198937Sdes{ 51298937Sdes#ifdef USE_LASTLOG 51398937Sdes return(lastlog_get_entry(li)); 51498937Sdes#else /* !USE_LASTLOG */ 515207319Sdes#if defined(USE_UTMPX) && defined(HAVE_SETUTXDB) && \ 516207319Sdes defined(UTXDB_LASTLOGIN) && defined(HAVE_GETUTXUSER) 517207319Sdes return (utmpx_get_entry(li)); 518207319Sdes#endif 51998937Sdes 520147005Sdes#if defined(DISABLE_LASTLOG) 52198937Sdes /* On some systems we shouldn't even try to obtain last login 52298937Sdes * time, e.g. AIX */ 523147005Sdes return (0); 524147005Sdes# elif defined(USE_WTMP) && \ 525147005Sdes (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) 52698937Sdes /* retrieve last login time from utmp */ 52798937Sdes return (wtmp_get_entry(li)); 528147005Sdes# elif defined(USE_WTMPX) && \ 529147005Sdes (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX)) 53098937Sdes /* If wtmp isn't available, try wtmpx */ 53198937Sdes return (wtmpx_get_entry(li)); 532147005Sdes# else 53398937Sdes /* Give up: No means of retrieving last login time */ 534147005Sdes return (0); 53598937Sdes# endif /* DISABLE_LASTLOG */ 53698937Sdes#endif /* USE_LASTLOG */ 53798937Sdes} 53898937Sdes 53998937Sdes 54098937Sdes 54198937Sdes/* 54298937Sdes * 'line' string utility functions 54398937Sdes * 54498937Sdes * These functions process the 'line' string into one of three forms: 54598937Sdes * 54698937Sdes * 1. The full filename (including '/dev') 54798937Sdes * 2. The stripped name (excluding '/dev') 54898937Sdes * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00 54998937Sdes * /dev/pts/1 -> ts/1 ) 55098937Sdes * 55198937Sdes * Form 3 is used on some systems to identify a .tmp.? entry when 55298937Sdes * attempting to remove it. Typically both addition and removal is 55398937Sdes * performed by one application - say, sshd - so as long as the choice 55498937Sdes * uniquely identifies a terminal it's ok. 55598937Sdes */ 55698937Sdes 55798937Sdes 558147005Sdes/* 559147005Sdes * line_fullname(): add the leading '/dev/' if it doesn't exist make 560147005Sdes * sure dst has enough space, if not just copy src (ugh) 561147005Sdes */ 56298937Sdeschar * 563149753Sdesline_fullname(char *dst, const char *src, u_int dstsize) 56498937Sdes{ 56598937Sdes memset(dst, '\0', dstsize); 566147005Sdes if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) 56798937Sdes strlcpy(dst, src, dstsize); 568147005Sdes else { 56998937Sdes strlcpy(dst, "/dev/", dstsize); 57098937Sdes strlcat(dst, src, dstsize); 57198937Sdes } 572147005Sdes return (dst); 57398937Sdes} 57498937Sdes 57598937Sdes/* line_stripname(): strip the leading '/dev' if it exists, return dst */ 57698937Sdeschar * 57798937Sdesline_stripname(char *dst, const char *src, int dstsize) 57898937Sdes{ 57998937Sdes memset(dst, '\0', dstsize); 58098937Sdes if (strncmp(src, "/dev/", 5) == 0) 58198937Sdes strlcpy(dst, src + 5, dstsize); 58298937Sdes else 58398937Sdes strlcpy(dst, src, dstsize); 584147005Sdes return (dst); 58598937Sdes} 58698937Sdes 587149753Sdes/* 588147005Sdes * line_abbrevname(): Return the abbreviated (usually four-character) 58998937Sdes * form of the line (Just use the last <dstsize> characters of the 59098937Sdes * full name.) 59198937Sdes * 59298937Sdes * NOTE: use strncpy because we do NOT necessarily want zero 593147005Sdes * termination 594147005Sdes */ 59598937Sdeschar * 59698937Sdesline_abbrevname(char *dst, const char *src, int dstsize) 59798937Sdes{ 59898937Sdes size_t len; 59998937Sdes 60098937Sdes memset(dst, '\0', dstsize); 60198937Sdes 60298937Sdes /* Always skip prefix if present */ 60398937Sdes if (strncmp(src, "/dev/", 5) == 0) 60498937Sdes src += 5; 60598937Sdes 60698937Sdes#ifdef WITH_ABBREV_NO_TTY 60798937Sdes if (strncmp(src, "tty", 3) == 0) 60898937Sdes src += 3; 60998937Sdes#endif 61098937Sdes 61198937Sdes len = strlen(src); 61298937Sdes 61398937Sdes if (len > 0) { 61498937Sdes if (((int)len - dstsize) > 0) 61598937Sdes src += ((int)len - dstsize); 61698937Sdes 61798937Sdes /* note: _don't_ change this to strlcpy */ 61898937Sdes strncpy(dst, src, (size_t)dstsize); 61998937Sdes } 62098937Sdes 621147005Sdes return (dst); 62298937Sdes} 62398937Sdes 62498937Sdes/** 62598937Sdes ** utmp utility functions 62698937Sdes ** 62798937Sdes ** These functions manipulate struct utmp, taking system differences 62898937Sdes ** into account. 62998937Sdes **/ 63098937Sdes 63198937Sdes#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN) 63298937Sdes 63398937Sdes/* build the utmp structure */ 63498937Sdesvoid 63598937Sdesset_utmp_time(struct logininfo *li, struct utmp *ut) 63698937Sdes{ 637147005Sdes# if defined(HAVE_TV_IN_UTMP) 63898937Sdes ut->ut_tv.tv_sec = li->tv_sec; 63998937Sdes ut->ut_tv.tv_usec = li->tv_usec; 640147005Sdes# elif defined(HAVE_TIME_IN_UTMP) 64198937Sdes ut->ut_time = li->tv_sec; 64298937Sdes# endif 64398937Sdes} 64498937Sdes 64598937Sdesvoid 64698937Sdesconstruct_utmp(struct logininfo *li, 64798937Sdes struct utmp *ut) 64898937Sdes{ 649113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 650113911Sdes struct sockaddr_in6 *sa6; 651147005Sdes# endif 652147005Sdes 65398937Sdes memset(ut, '\0', sizeof(*ut)); 65498937Sdes 65598937Sdes /* First fill out fields used for both logins and logouts */ 65698937Sdes 65798937Sdes# ifdef HAVE_ID_IN_UTMP 65898937Sdes line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id)); 65998937Sdes# endif 66098937Sdes 66198937Sdes# ifdef HAVE_TYPE_IN_UTMP 66298937Sdes /* This is done here to keep utmp constants out of struct logininfo */ 66398937Sdes switch (li->type) { 66498937Sdes case LTYPE_LOGIN: 66598937Sdes ut->ut_type = USER_PROCESS; 666106130Sdes#ifdef _UNICOS 66798937Sdes cray_set_tmpdir(ut); 66898937Sdes#endif 66998937Sdes break; 67098937Sdes case LTYPE_LOGOUT: 67198937Sdes ut->ut_type = DEAD_PROCESS; 672106130Sdes#ifdef _UNICOS 67398937Sdes cray_retain_utmp(ut, li->pid); 67498937Sdes#endif 67598937Sdes break; 67698937Sdes } 67798937Sdes# endif 67898937Sdes set_utmp_time(li, ut); 67998937Sdes 68098937Sdes line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line)); 68198937Sdes 68298937Sdes# ifdef HAVE_PID_IN_UTMP 68398937Sdes ut->ut_pid = li->pid; 68498937Sdes# endif 68598937Sdes 68698937Sdes /* If we're logging out, leave all other fields blank */ 68798937Sdes if (li->type == LTYPE_LOGOUT) 688147005Sdes return; 68998937Sdes 69098937Sdes /* 69198937Sdes * These fields are only used when logging in, and are blank 69298937Sdes * for logouts. 69398937Sdes */ 69498937Sdes 69598937Sdes /* Use strncpy because we don't necessarily want null termination */ 696147005Sdes strncpy(ut->ut_name, li->username, 697147005Sdes MIN_SIZEOF(ut->ut_name, li->username)); 69898937Sdes# ifdef HAVE_HOST_IN_UTMP 699184122Sdes strncpy(ut->ut_host, li->hostname, 700184122Sdes MIN_SIZEOF(ut->ut_host, li->hostname)); 70198937Sdes# endif 70298937Sdes# ifdef HAVE_ADDR_IN_UTMP 70398937Sdes /* this is just a 32-bit IP address */ 70498937Sdes if (li->hostaddr.sa.sa_family == AF_INET) 70598937Sdes ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 70698937Sdes# endif 707113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 708113911Sdes /* this is just a 128-bit IPv6 address */ 709113911Sdes if (li->hostaddr.sa.sa_family == AF_INET6) { 710113911Sdes sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 711113911Sdes memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 712113911Sdes if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 713113911Sdes ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; 714113911Sdes ut->ut_addr_v6[1] = 0; 715113911Sdes ut->ut_addr_v6[2] = 0; 716113911Sdes ut->ut_addr_v6[3] = 0; 717113911Sdes } 718113911Sdes } 719113911Sdes# endif 72098937Sdes} 72198937Sdes#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */ 72298937Sdes 72398937Sdes/** 72498937Sdes ** utmpx utility functions 72598937Sdes ** 72698937Sdes ** These functions manipulate struct utmpx, accounting for system 72798937Sdes ** variations. 72898937Sdes **/ 72998937Sdes 73098937Sdes#if defined(USE_UTMPX) || defined (USE_WTMPX) 73198937Sdes/* build the utmpx structure */ 73298937Sdesvoid 73398937Sdesset_utmpx_time(struct logininfo *li, struct utmpx *utx) 73498937Sdes{ 735147005Sdes# if defined(HAVE_TV_IN_UTMPX) 73698937Sdes utx->ut_tv.tv_sec = li->tv_sec; 73798937Sdes utx->ut_tv.tv_usec = li->tv_usec; 738147005Sdes# elif defined(HAVE_TIME_IN_UTMPX) 73998937Sdes utx->ut_time = li->tv_sec; 740147005Sdes# endif 74198937Sdes} 74298937Sdes 74398937Sdesvoid 74498937Sdesconstruct_utmpx(struct logininfo *li, struct utmpx *utx) 74598937Sdes{ 746113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 747113911Sdes struct sockaddr_in6 *sa6; 748113911Sdes# endif 74998937Sdes memset(utx, '\0', sizeof(*utx)); 750147005Sdes 75198937Sdes# ifdef HAVE_ID_IN_UTMPX 75298937Sdes line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); 75398937Sdes# endif 75498937Sdes 75598937Sdes /* this is done here to keep utmp constants out of loginrec.h */ 75698937Sdes switch (li->type) { 75798937Sdes case LTYPE_LOGIN: 75898937Sdes utx->ut_type = USER_PROCESS; 75998937Sdes break; 76098937Sdes case LTYPE_LOGOUT: 76198937Sdes utx->ut_type = DEAD_PROCESS; 76298937Sdes break; 76398937Sdes } 76498937Sdes line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line)); 76598937Sdes set_utmpx_time(li, utx); 76698937Sdes utx->ut_pid = li->pid; 767147005Sdes 76898937Sdes /* strncpy(): Don't necessarily want null termination */ 769202213Sed strncpy(utx->ut_user, li->username, 770202213Sed MIN_SIZEOF(utx->ut_user, li->username)); 77198937Sdes 77298937Sdes if (li->type == LTYPE_LOGOUT) 77398937Sdes return; 77498937Sdes 77598937Sdes /* 77698937Sdes * These fields are only used when logging in, and are blank 77798937Sdes * for logouts. 77898937Sdes */ 77998937Sdes 78098937Sdes# ifdef HAVE_HOST_IN_UTMPX 781147005Sdes strncpy(utx->ut_host, li->hostname, 782147005Sdes MIN_SIZEOF(utx->ut_host, li->hostname)); 78398937Sdes# endif 78498937Sdes# ifdef HAVE_ADDR_IN_UTMPX 78598937Sdes /* this is just a 32-bit IP address */ 78698937Sdes if (li->hostaddr.sa.sa_family == AF_INET) 78798937Sdes utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 78898937Sdes# endif 789113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 790113911Sdes /* this is just a 128-bit IPv6 address */ 791113911Sdes if (li->hostaddr.sa.sa_family == AF_INET6) { 792113911Sdes sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 793295367Sdes memcpy(utx->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 794113911Sdes if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 795295367Sdes utx->ut_addr_v6[0] = utx->ut_addr_v6[3]; 796295367Sdes utx->ut_addr_v6[1] = 0; 797295367Sdes utx->ut_addr_v6[2] = 0; 798295367Sdes utx->ut_addr_v6[3] = 0; 799113911Sdes } 800113911Sdes } 801113911Sdes# endif 80298937Sdes# ifdef HAVE_SYSLEN_IN_UTMPX 80398937Sdes /* ut_syslen is the length of the utx_host string */ 80498937Sdes utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host)); 80598937Sdes# endif 80698937Sdes} 80798937Sdes#endif /* USE_UTMPX || USE_WTMPX */ 80898937Sdes 80998937Sdes/** 81098937Sdes ** Low-level utmp functions 81198937Sdes **/ 81298937Sdes 81398937Sdes/* FIXME: (ATL) utmp_write_direct needs testing */ 81498937Sdes#ifdef USE_UTMP 81598937Sdes 81698937Sdes/* if we can, use pututline() etc. */ 81798937Sdes# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \ 81898937Sdes defined(HAVE_PUTUTLINE) 81998937Sdes# define UTMP_USE_LIBRARY 82098937Sdes# endif 82198937Sdes 82298937Sdes 82398937Sdes/* write a utmp entry with the system's help (pututline() and pals) */ 82498937Sdes# ifdef UTMP_USE_LIBRARY 82598937Sdesstatic int 82698937Sdesutmp_write_library(struct logininfo *li, struct utmp *ut) 82798937Sdes{ 82898937Sdes setutent(); 82998937Sdes pututline(ut); 83098937Sdes# ifdef HAVE_ENDUTENT 83198937Sdes endutent(); 83298937Sdes# endif 833147005Sdes return (1); 83498937Sdes} 83598937Sdes# else /* UTMP_USE_LIBRARY */ 83698937Sdes 837149753Sdes/* 838147005Sdes * Write a utmp entry direct to the file 839147005Sdes * This is a slightly modification of code in OpenBSD's login.c 840147005Sdes */ 84198937Sdesstatic int 84298937Sdesutmp_write_direct(struct logininfo *li, struct utmp *ut) 84398937Sdes{ 84498937Sdes struct utmp old_ut; 84598937Sdes register int fd; 84698937Sdes int tty; 84798937Sdes 84898937Sdes /* FIXME: (ATL) ttyslot() needs local implementation */ 84998937Sdes 85098937Sdes#if defined(HAVE_GETTTYENT) 851147005Sdes struct ttyent *ty; 85298937Sdes 85398937Sdes tty=0; 85498937Sdes setttyent(); 855147005Sdes while (NULL != (ty = getttyent())) { 85698937Sdes tty++; 85798937Sdes if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line))) 85898937Sdes break; 85998937Sdes } 86098937Sdes endttyent(); 86198937Sdes 862147005Sdes if (NULL == ty) { 863137019Sdes logit("%s: tty not found", __func__); 864137019Sdes return (0); 86598937Sdes } 86698937Sdes#else /* FIXME */ 86798937Sdes 86898937Sdes tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ 86998937Sdes 87098937Sdes#endif /* HAVE_GETTTYENT */ 87198937Sdes 87298937Sdes if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { 873137019Sdes off_t pos, ret; 874137019Sdes 875137019Sdes pos = (off_t)tty * sizeof(struct utmp); 876137019Sdes if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { 877147005Sdes logit("%s: lseek: %s", __func__, strerror(errno)); 878221420Sdes close(fd); 879137019Sdes return (0); 880137019Sdes } 881137019Sdes if (ret != pos) { 882149753Sdes logit("%s: Couldn't seek to tty %d slot in %s", 883147005Sdes __func__, tty, UTMP_FILE); 884221420Sdes close(fd); 885137019Sdes return (0); 886137019Sdes } 88798937Sdes /* 88898937Sdes * Prevent luser from zero'ing out ut_host. 88998937Sdes * If the new ut_line is empty but the old one is not 89098937Sdes * and ut_line and ut_name match, preserve the old ut_line. 89198937Sdes */ 89298937Sdes if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && 893147005Sdes (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && 894147005Sdes (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && 895147005Sdes (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) 896147005Sdes memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); 89798937Sdes 898137019Sdes if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { 899147005Sdes logit("%s: lseek: %s", __func__, strerror(errno)); 900221420Sdes close(fd); 901137019Sdes return (0); 902137019Sdes } 903137019Sdes if (ret != pos) { 904147005Sdes logit("%s: Couldn't seek to tty %d slot in %s", 905137019Sdes __func__, tty, UTMP_FILE); 906221420Sdes close(fd); 907137019Sdes return (0); 908137019Sdes } 909147005Sdes if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { 910137019Sdes logit("%s: error writing %s: %s", __func__, 91198937Sdes UTMP_FILE, strerror(errno)); 912221420Sdes close(fd); 913221420Sdes return (0); 914147005Sdes } 91598937Sdes 916147005Sdes close(fd); 917147005Sdes return (1); 91898937Sdes } else { 919147005Sdes return (0); 92098937Sdes } 92198937Sdes} 92298937Sdes# endif /* UTMP_USE_LIBRARY */ 92398937Sdes 92498937Sdesstatic int 92598937Sdesutmp_perform_login(struct logininfo *li) 92698937Sdes{ 92798937Sdes struct utmp ut; 92898937Sdes 92998937Sdes construct_utmp(li, &ut); 93098937Sdes# ifdef UTMP_USE_LIBRARY 93198937Sdes if (!utmp_write_library(li, &ut)) { 932147005Sdes logit("%s: utmp_write_library() failed", __func__); 933147005Sdes return (0); 93498937Sdes } 93598937Sdes# else 93698937Sdes if (!utmp_write_direct(li, &ut)) { 937147005Sdes logit("%s: utmp_write_direct() failed", __func__); 938147005Sdes return (0); 93998937Sdes } 94098937Sdes# endif 941147005Sdes return (1); 94298937Sdes} 94398937Sdes 94498937Sdes 94598937Sdesstatic int 94698937Sdesutmp_perform_logout(struct logininfo *li) 94798937Sdes{ 94898937Sdes struct utmp ut; 94998937Sdes 95098937Sdes construct_utmp(li, &ut); 95198937Sdes# ifdef UTMP_USE_LIBRARY 95298937Sdes if (!utmp_write_library(li, &ut)) { 953147005Sdes logit("%s: utmp_write_library() failed", __func__); 954147005Sdes return (0); 95598937Sdes } 95698937Sdes# else 95798937Sdes if (!utmp_write_direct(li, &ut)) { 958147005Sdes logit("%s: utmp_write_direct() failed", __func__); 959147005Sdes return (0); 96098937Sdes } 96198937Sdes# endif 962147005Sdes return (1); 96398937Sdes} 96498937Sdes 96598937Sdes 96698937Sdesint 96798937Sdesutmp_write_entry(struct logininfo *li) 96898937Sdes{ 96998937Sdes switch(li->type) { 97098937Sdes case LTYPE_LOGIN: 971147005Sdes return (utmp_perform_login(li)); 97298937Sdes 97398937Sdes case LTYPE_LOGOUT: 974147005Sdes return (utmp_perform_logout(li)); 97598937Sdes 97698937Sdes default: 977147005Sdes logit("%s: invalid type field", __func__); 978147005Sdes return (0); 97998937Sdes } 98098937Sdes} 98198937Sdes#endif /* USE_UTMP */ 98298937Sdes 98398937Sdes 98498937Sdes/** 98598937Sdes ** Low-level utmpx functions 98698937Sdes **/ 98798937Sdes 98898937Sdes/* not much point if we don't want utmpx entries */ 98998937Sdes#ifdef USE_UTMPX 99098937Sdes 99198937Sdes/* if we have the wherewithall, use pututxline etc. */ 99298937Sdes# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \ 99398937Sdes defined(HAVE_PUTUTXLINE) 99498937Sdes# define UTMPX_USE_LIBRARY 99598937Sdes# endif 99698937Sdes 99798937Sdes 99898937Sdes/* write a utmpx entry with the system's help (pututxline() and pals) */ 99998937Sdes# ifdef UTMPX_USE_LIBRARY 100098937Sdesstatic int 100198937Sdesutmpx_write_library(struct logininfo *li, struct utmpx *utx) 100298937Sdes{ 100398937Sdes setutxent(); 100498937Sdes pututxline(utx); 100598937Sdes 100698937Sdes# ifdef HAVE_ENDUTXENT 100798937Sdes endutxent(); 100898937Sdes# endif 1009147005Sdes return (1); 101098937Sdes} 101198937Sdes 101298937Sdes# else /* UTMPX_USE_LIBRARY */ 101398937Sdes 101498937Sdes/* write a utmp entry direct to the file */ 101598937Sdesstatic int 101698937Sdesutmpx_write_direct(struct logininfo *li, struct utmpx *utx) 101798937Sdes{ 1018147005Sdes logit("%s: not implemented!", __func__); 1019147005Sdes return (0); 102098937Sdes} 102198937Sdes# endif /* UTMPX_USE_LIBRARY */ 102298937Sdes 102398937Sdesstatic int 102498937Sdesutmpx_perform_login(struct logininfo *li) 102598937Sdes{ 102698937Sdes struct utmpx utx; 102798937Sdes 102898937Sdes construct_utmpx(li, &utx); 102998937Sdes# ifdef UTMPX_USE_LIBRARY 103098937Sdes if (!utmpx_write_library(li, &utx)) { 1031147005Sdes logit("%s: utmp_write_library() failed", __func__); 1032147005Sdes return (0); 103398937Sdes } 103498937Sdes# else 103598937Sdes if (!utmpx_write_direct(li, &ut)) { 1036147005Sdes logit("%s: utmp_write_direct() failed", __func__); 1037147005Sdes return (0); 103898937Sdes } 103998937Sdes# endif 1040147005Sdes return (1); 104198937Sdes} 104298937Sdes 104398937Sdes 104498937Sdesstatic int 104598937Sdesutmpx_perform_logout(struct logininfo *li) 104698937Sdes{ 104798937Sdes struct utmpx utx; 104898937Sdes 104998937Sdes construct_utmpx(li, &utx); 105098937Sdes# ifdef HAVE_ID_IN_UTMPX 105198937Sdes line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); 105298937Sdes# endif 105398937Sdes# ifdef HAVE_TYPE_IN_UTMPX 105498937Sdes utx.ut_type = DEAD_PROCESS; 105598937Sdes# endif 105698937Sdes 105798937Sdes# ifdef UTMPX_USE_LIBRARY 105898937Sdes utmpx_write_library(li, &utx); 105998937Sdes# else 106098937Sdes utmpx_write_direct(li, &utx); 106198937Sdes# endif 1062147005Sdes return (1); 106398937Sdes} 106498937Sdes 106598937Sdesint 106698937Sdesutmpx_write_entry(struct logininfo *li) 106798937Sdes{ 106898937Sdes switch(li->type) { 106998937Sdes case LTYPE_LOGIN: 1070147005Sdes return (utmpx_perform_login(li)); 107198937Sdes case LTYPE_LOGOUT: 1072147005Sdes return (utmpx_perform_logout(li)); 107398937Sdes default: 1074147005Sdes logit("%s: invalid type field", __func__); 1075147005Sdes return (0); 107698937Sdes } 107798937Sdes} 107898937Sdes#endif /* USE_UTMPX */ 107998937Sdes 108098937Sdes 108198937Sdes/** 108298937Sdes ** Low-level wtmp functions 108398937Sdes **/ 108498937Sdes 108598937Sdes#ifdef USE_WTMP 108698937Sdes 1087149753Sdes/* 1088147005Sdes * Write a wtmp entry direct to the end of the file 1089147005Sdes * This is a slight modification of code in OpenBSD's logwtmp.c 1090147005Sdes */ 109198937Sdesstatic int 109298937Sdeswtmp_write(struct logininfo *li, struct utmp *ut) 109398937Sdes{ 109498937Sdes struct stat buf; 109598937Sdes int fd, ret = 1; 109698937Sdes 109798937Sdes if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 1098147005Sdes logit("%s: problem writing %s: %s", __func__, 109998937Sdes WTMP_FILE, strerror(errno)); 1100147005Sdes return (0); 110198937Sdes } 110298937Sdes if (fstat(fd, &buf) == 0) 1103124211Sdes if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { 110498937Sdes ftruncate(fd, buf.st_size); 1105147005Sdes logit("%s: problem writing %s: %s", __func__, 110698937Sdes WTMP_FILE, strerror(errno)); 110798937Sdes ret = 0; 110898937Sdes } 1109147005Sdes close(fd); 1110147005Sdes return (ret); 111198937Sdes} 111298937Sdes 111398937Sdesstatic int 111498937Sdeswtmp_perform_login(struct logininfo *li) 111598937Sdes{ 111698937Sdes struct utmp ut; 111798937Sdes 111898937Sdes construct_utmp(li, &ut); 1119147005Sdes return (wtmp_write(li, &ut)); 112098937Sdes} 112198937Sdes 112298937Sdes 112398937Sdesstatic int 112498937Sdeswtmp_perform_logout(struct logininfo *li) 112598937Sdes{ 112698937Sdes struct utmp ut; 112798937Sdes 112898937Sdes construct_utmp(li, &ut); 1129147005Sdes return (wtmp_write(li, &ut)); 113098937Sdes} 113198937Sdes 113298937Sdes 113398937Sdesint 113498937Sdeswtmp_write_entry(struct logininfo *li) 113598937Sdes{ 113698937Sdes switch(li->type) { 113798937Sdes case LTYPE_LOGIN: 1138147005Sdes return (wtmp_perform_login(li)); 113998937Sdes case LTYPE_LOGOUT: 1140147005Sdes return (wtmp_perform_logout(li)); 114198937Sdes default: 1142147005Sdes logit("%s: invalid type field", __func__); 1143147005Sdes return (0); 114498937Sdes } 114598937Sdes} 114698937Sdes 114798937Sdes 1148149753Sdes/* 1149147005Sdes * Notes on fetching login data from wtmp/wtmpx 115098937Sdes * 115198937Sdes * Logouts are usually recorded with (amongst other things) a blank 115298937Sdes * username on a given tty line. However, some systems (HP-UX is one) 115398937Sdes * leave all fields set, but change the ut_type field to DEAD_PROCESS. 115498937Sdes * 115598937Sdes * Since we're only looking for logins here, we know that the username 115698937Sdes * must be set correctly. On systems that leave it in, we check for 115798937Sdes * ut_type==USER_PROCESS (indicating a login.) 115898937Sdes * 115998937Sdes * Portability: Some systems may set something other than USER_PROCESS 116098937Sdes * to indicate a login process. I don't know of any as I write. Also, 116198937Sdes * it's possible that some systems may both leave the username in 116298937Sdes * place and not have ut_type. 116398937Sdes */ 116498937Sdes 116598937Sdes/* return true if this wtmp entry indicates a login */ 116698937Sdesstatic int 116798937Sdeswtmp_islogin(struct logininfo *li, struct utmp *ut) 116898937Sdes{ 116998937Sdes if (strncmp(li->username, ut->ut_name, 1170147005Sdes MIN_SIZEOF(li->username, ut->ut_name)) == 0) { 117198937Sdes# ifdef HAVE_TYPE_IN_UTMP 117298937Sdes if (ut->ut_type & USER_PROCESS) 1173147005Sdes return (1); 117498937Sdes# else 1175147005Sdes return (1); 117698937Sdes# endif 117798937Sdes } 1178147005Sdes return (0); 117998937Sdes} 118098937Sdes 118198937Sdesint 118298937Sdeswtmp_get_entry(struct logininfo *li) 118398937Sdes{ 118498937Sdes struct stat st; 118598937Sdes struct utmp ut; 1186147005Sdes int fd, found = 0; 118798937Sdes 118898937Sdes /* Clear the time entries in our logininfo */ 118998937Sdes li->tv_sec = li->tv_usec = 0; 119098937Sdes 119198937Sdes if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { 1192149753Sdes logit("%s: problem opening %s: %s", __func__, 119398937Sdes WTMP_FILE, strerror(errno)); 1194147005Sdes return (0); 119598937Sdes } 119698937Sdes if (fstat(fd, &st) != 0) { 1197149753Sdes logit("%s: couldn't stat %s: %s", __func__, 119898937Sdes WTMP_FILE, strerror(errno)); 119998937Sdes close(fd); 1200147005Sdes return (0); 120198937Sdes } 120298937Sdes 120398937Sdes /* Seek to the start of the last struct utmp */ 120498937Sdes if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { 120598937Sdes /* Looks like we've got a fresh wtmp file */ 120698937Sdes close(fd); 1207147005Sdes return (0); 120898937Sdes } 120998937Sdes 121098937Sdes while (!found) { 121198937Sdes if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { 1212149753Sdes logit("%s: read of %s failed: %s", __func__, 121398937Sdes WTMP_FILE, strerror(errno)); 121498937Sdes close (fd); 1215147005Sdes return (0); 121698937Sdes } 1217221420Sdes if (wtmp_islogin(li, &ut) ) { 121898937Sdes found = 1; 1219147005Sdes /* 1220147005Sdes * We've already checked for a time in struct 1221147005Sdes * utmp, in login_getlast() 1222147005Sdes */ 122398937Sdes# ifdef HAVE_TIME_IN_UTMP 122498937Sdes li->tv_sec = ut.ut_time; 122598937Sdes# else 122698937Sdes# if HAVE_TV_IN_UTMP 122798937Sdes li->tv_sec = ut.ut_tv.tv_sec; 122898937Sdes# endif 122998937Sdes# endif 123098937Sdes line_fullname(li->line, ut.ut_line, 1231147005Sdes MIN_SIZEOF(li->line, ut.ut_line)); 123298937Sdes# ifdef HAVE_HOST_IN_UTMP 123398937Sdes strlcpy(li->hostname, ut.ut_host, 1234147005Sdes MIN_SIZEOF(li->hostname, ut.ut_host)); 123598937Sdes# endif 123698937Sdes continue; 123798937Sdes } 123898937Sdes /* Seek back 2 x struct utmp */ 123998937Sdes if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { 124098937Sdes /* We've found the start of the file, so quit */ 1241147005Sdes close(fd); 1242147005Sdes return (0); 124398937Sdes } 124498937Sdes } 124598937Sdes 124698937Sdes /* We found an entry. Tidy up and return */ 124798937Sdes close(fd); 1248147005Sdes return (1); 124998937Sdes} 125098937Sdes# endif /* USE_WTMP */ 125198937Sdes 125298937Sdes 125398937Sdes/** 125498937Sdes ** Low-level wtmpx functions 125598937Sdes **/ 125698937Sdes 125798937Sdes#ifdef USE_WTMPX 1258147005Sdes/* 1259147005Sdes * Write a wtmpx entry direct to the end of the file 1260147005Sdes * This is a slight modification of code in OpenBSD's logwtmp.c 1261147005Sdes */ 126298937Sdesstatic int 126398937Sdeswtmpx_write(struct logininfo *li, struct utmpx *utx) 126498937Sdes{ 1265126277Sdes#ifndef HAVE_UPDWTMPX 126698937Sdes struct stat buf; 126798937Sdes int fd, ret = 1; 126898937Sdes 126998937Sdes if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 1270149753Sdes logit("%s: problem opening %s: %s", __func__, 127198937Sdes WTMPX_FILE, strerror(errno)); 1272147005Sdes return (0); 127398937Sdes } 127498937Sdes 127598937Sdes if (fstat(fd, &buf) == 0) 1276124211Sdes if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) { 127798937Sdes ftruncate(fd, buf.st_size); 1278147005Sdes logit("%s: problem writing %s: %s", __func__, 127998937Sdes WTMPX_FILE, strerror(errno)); 128098937Sdes ret = 0; 128198937Sdes } 1282147005Sdes close(fd); 128398937Sdes 1284147005Sdes return (ret); 1285126277Sdes#else 1286126277Sdes updwtmpx(WTMPX_FILE, utx); 1287147005Sdes return (1); 1288126277Sdes#endif 128998937Sdes} 129098937Sdes 129198937Sdes 129298937Sdesstatic int 129398937Sdeswtmpx_perform_login(struct logininfo *li) 129498937Sdes{ 129598937Sdes struct utmpx utx; 129698937Sdes 129798937Sdes construct_utmpx(li, &utx); 1298147005Sdes return (wtmpx_write(li, &utx)); 129998937Sdes} 130098937Sdes 130198937Sdes 130298937Sdesstatic int 130398937Sdeswtmpx_perform_logout(struct logininfo *li) 130498937Sdes{ 130598937Sdes struct utmpx utx; 130698937Sdes 130798937Sdes construct_utmpx(li, &utx); 1308147005Sdes return (wtmpx_write(li, &utx)); 130998937Sdes} 131098937Sdes 131198937Sdes 131298937Sdesint 131398937Sdeswtmpx_write_entry(struct logininfo *li) 131498937Sdes{ 131598937Sdes switch(li->type) { 131698937Sdes case LTYPE_LOGIN: 1317147005Sdes return (wtmpx_perform_login(li)); 131898937Sdes case LTYPE_LOGOUT: 1319147005Sdes return (wtmpx_perform_logout(li)); 132098937Sdes default: 1321147005Sdes logit("%s: invalid type field", __func__); 1322147005Sdes return (0); 132398937Sdes } 132498937Sdes} 132598937Sdes 132698937Sdes/* Please see the notes above wtmp_islogin() for information about the 132798937Sdes next two functions */ 132898937Sdes 132998937Sdes/* Return true if this wtmpx entry indicates a login */ 133098937Sdesstatic int 133198937Sdeswtmpx_islogin(struct logininfo *li, struct utmpx *utx) 133298937Sdes{ 1333204917Sdes if (strncmp(li->username, utx->ut_user, 1334204917Sdes MIN_SIZEOF(li->username, utx->ut_user)) == 0 ) { 133598937Sdes# ifdef HAVE_TYPE_IN_UTMPX 133698937Sdes if (utx->ut_type == USER_PROCESS) 1337147005Sdes return (1); 133898937Sdes# else 1339147005Sdes return (1); 134098937Sdes# endif 134198937Sdes } 1342147005Sdes return (0); 134398937Sdes} 134498937Sdes 134598937Sdes 134698937Sdesint 134798937Sdeswtmpx_get_entry(struct logininfo *li) 134898937Sdes{ 134998937Sdes struct stat st; 135098937Sdes struct utmpx utx; 135198937Sdes int fd, found=0; 135298937Sdes 135398937Sdes /* Clear the time entries */ 135498937Sdes li->tv_sec = li->tv_usec = 0; 135598937Sdes 135698937Sdes if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { 1357149753Sdes logit("%s: problem opening %s: %s", __func__, 135898937Sdes WTMPX_FILE, strerror(errno)); 1359147005Sdes return (0); 136098937Sdes } 136198937Sdes if (fstat(fd, &st) != 0) { 1362149753Sdes logit("%s: couldn't stat %s: %s", __func__, 1363106130Sdes WTMPX_FILE, strerror(errno)); 136498937Sdes close(fd); 1365147005Sdes return (0); 136698937Sdes } 136798937Sdes 136898937Sdes /* Seek to the start of the last struct utmpx */ 136998937Sdes if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { 137098937Sdes /* probably a newly rotated wtmpx file */ 137198937Sdes close(fd); 1372147005Sdes return (0); 137398937Sdes } 137498937Sdes 137598937Sdes while (!found) { 137698937Sdes if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { 1377149753Sdes logit("%s: read of %s failed: %s", __func__, 137898937Sdes WTMPX_FILE, strerror(errno)); 137998937Sdes close (fd); 1380147005Sdes return (0); 138198937Sdes } 1382147005Sdes /* 1383149753Sdes * Logouts are recorded as a blank username on a particular 1384147005Sdes * line. So, we just need to find the username in struct utmpx 1385147005Sdes */ 1386147005Sdes if (wtmpx_islogin(li, &utx)) { 1387106130Sdes found = 1; 1388147005Sdes# if defined(HAVE_TV_IN_UTMPX) 138998937Sdes li->tv_sec = utx.ut_tv.tv_sec; 1390147005Sdes# elif defined(HAVE_TIME_IN_UTMPX) 139198937Sdes li->tv_sec = utx.ut_time; 139298937Sdes# endif 139398937Sdes line_fullname(li->line, utx.ut_line, sizeof(li->line)); 1394147005Sdes# if defined(HAVE_HOST_IN_UTMPX) 139598937Sdes strlcpy(li->hostname, utx.ut_host, 1396147005Sdes MIN_SIZEOF(li->hostname, utx.ut_host)); 139798937Sdes# endif 139898937Sdes continue; 139998937Sdes } 140098937Sdes if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { 1401147005Sdes close(fd); 1402147005Sdes return (0); 140398937Sdes } 140498937Sdes } 140598937Sdes 140698937Sdes close(fd); 1407147005Sdes return (1); 140898937Sdes} 140998937Sdes#endif /* USE_WTMPX */ 141098937Sdes 141198937Sdes/** 141298937Sdes ** Low-level libutil login() functions 141398937Sdes **/ 141498937Sdes 141598937Sdes#ifdef USE_LOGIN 141698937Sdesstatic int 141798937Sdessyslogin_perform_login(struct logininfo *li) 141898937Sdes{ 141998937Sdes struct utmp *ut; 142098937Sdes 1421147005Sdes ut = xmalloc(sizeof(*ut)); 142298937Sdes construct_utmp(li, ut); 142398937Sdes login(ut); 1424113911Sdes free(ut); 142598937Sdes 1426147005Sdes return (1); 142798937Sdes} 142898937Sdes 142998937Sdesstatic int 143098937Sdessyslogin_perform_logout(struct logininfo *li) 143198937Sdes{ 143298937Sdes# ifdef HAVE_LOGOUT 1433128460Sdes char line[UT_LINESIZE]; 143498937Sdes 143598937Sdes (void)line_stripname(line, li->line, sizeof(line)); 143698937Sdes 1437147005Sdes if (!logout(line)) 1438147005Sdes logit("%s: logout() returned an error", __func__); 143998937Sdes# ifdef HAVE_LOGWTMP 1440147005Sdes else 144198937Sdes logwtmp(line, "", ""); 144298937Sdes# endif 144398937Sdes /* FIXME: (ATL - if the need arises) What to do if we have 144498937Sdes * login, but no logout? what if logout but no logwtmp? All 144598937Sdes * routines are in libutil so they should all be there, 144698937Sdes * but... */ 144798937Sdes# endif 1448147005Sdes return (1); 144998937Sdes} 145098937Sdes 145198937Sdesint 145298937Sdessyslogin_write_entry(struct logininfo *li) 145398937Sdes{ 145498937Sdes switch (li->type) { 145598937Sdes case LTYPE_LOGIN: 1456147005Sdes return (syslogin_perform_login(li)); 145798937Sdes case LTYPE_LOGOUT: 1458147005Sdes return (syslogin_perform_logout(li)); 145998937Sdes default: 1460147005Sdes logit("%s: Invalid type field", __func__); 1461147005Sdes return (0); 146298937Sdes } 146398937Sdes} 146498937Sdes#endif /* USE_LOGIN */ 146598937Sdes 146698937Sdes/* end of file log-syslogin.c */ 146798937Sdes 146898937Sdes/** 146998937Sdes ** Low-level lastlog functions 147098937Sdes **/ 147198937Sdes 147298937Sdes#ifdef USE_LASTLOG 147398937Sdes 1474192595Sdes#if !defined(LASTLOG_WRITE_PUTUTXLINE) || !defined(HAVE_GETLASTLOGXBYNAME) 1475192595Sdes/* open the file (using filemode) and seek to the login entry */ 147698937Sdesstatic int 1477192595Sdeslastlog_openseek(struct logininfo *li, int *fd, int filemode) 147898937Sdes{ 1479192595Sdes off_t offset; 1480192595Sdes char lastlog_file[1024]; 148198937Sdes struct stat st; 148298937Sdes 148398937Sdes if (stat(LASTLOG_FILE, &st) != 0) { 1484147005Sdes logit("%s: Couldn't stat %s: %s", __func__, 1485147005Sdes LASTLOG_FILE, strerror(errno)); 1486147005Sdes return (0); 148798937Sdes } 1488192595Sdes if (S_ISDIR(st.st_mode)) { 1489147005Sdes snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", 1490147005Sdes LASTLOG_FILE, li->username); 1491192595Sdes } else if (S_ISREG(st.st_mode)) { 1492192595Sdes strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); 1493192595Sdes } else { 1494147005Sdes logit("%s: %.100s is not a file or directory!", __func__, 1495147005Sdes LASTLOG_FILE); 1496147005Sdes return (0); 149798937Sdes } 149898937Sdes 1499124211Sdes *fd = open(lastlog_file, filemode, 0600); 1500147005Sdes if (*fd < 0) { 1501147005Sdes debug("%s: Couldn't open %s: %s", __func__, 150298937Sdes lastlog_file, strerror(errno)); 1503147005Sdes return (0); 150498937Sdes } 150598937Sdes 1506192595Sdes if (S_ISREG(st.st_mode)) { 150798937Sdes /* find this uid's offset in the lastlog file */ 1508221420Sdes offset = (off_t) ((u_long)li->uid * sizeof(struct lastlog)); 150998937Sdes 1510147005Sdes if (lseek(*fd, offset, SEEK_SET) != offset) { 1511147005Sdes logit("%s: %s->lseek(): %s", __func__, 1512147005Sdes lastlog_file, strerror(errno)); 1513221420Sdes close(*fd); 1514147005Sdes return (0); 151598937Sdes } 151698937Sdes } 151798937Sdes 1518147005Sdes return (1); 151998937Sdes} 1520192595Sdes#endif /* !LASTLOG_WRITE_PUTUTXLINE || !HAVE_GETLASTLOGXBYNAME */ 152198937Sdes 1522192595Sdes#ifdef LASTLOG_WRITE_PUTUTXLINE 1523192595Sdesint 1524192595Sdeslastlog_write_entry(struct logininfo *li) 152598937Sdes{ 1526192595Sdes switch(li->type) { 1527192595Sdes case LTYPE_LOGIN: 1528192595Sdes return 1; /* lastlog written by pututxline */ 1529192595Sdes default: 1530192595Sdes logit("lastlog_write_entry: Invalid type field"); 1531192595Sdes return 0; 153298937Sdes } 153398937Sdes} 1534192595Sdes#else /* LASTLOG_WRITE_PUTUTXLINE */ 153598937Sdesint 153698937Sdeslastlog_write_entry(struct logininfo *li) 153798937Sdes{ 1538192595Sdes struct lastlog last; 1539192595Sdes int fd; 1540192595Sdes 154198937Sdes switch(li->type) { 154298937Sdes case LTYPE_LOGIN: 1543192595Sdes /* create our struct lastlog */ 1544192595Sdes memset(&last, '\0', sizeof(last)); 1545192595Sdes line_stripname(last.ll_line, li->line, sizeof(last.ll_line)); 1546192595Sdes strlcpy(last.ll_host, li->hostname, 1547192595Sdes MIN_SIZEOF(last.ll_host, li->hostname)); 1548192595Sdes last.ll_time = li->tv_sec; 1549192595Sdes 1550192595Sdes if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) 1551192595Sdes return (0); 1552192595Sdes 1553192595Sdes /* write the entry */ 1554192595Sdes if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) { 1555192595Sdes close(fd); 1556192595Sdes logit("%s: Error writing to %s: %s", __func__, 1557192595Sdes LASTLOG_FILE, strerror(errno)); 1558192595Sdes return (0); 1559192595Sdes } 1560192595Sdes 1561192595Sdes close(fd); 1562192595Sdes return (1); 156398937Sdes default: 1564147005Sdes logit("%s: Invalid type field", __func__); 1565147005Sdes return (0); 156698937Sdes } 156798937Sdes} 1568192595Sdes#endif /* LASTLOG_WRITE_PUTUTXLINE */ 156998937Sdes 1570192595Sdes#ifdef HAVE_GETLASTLOGXBYNAME 1571192595Sdesint 1572192595Sdeslastlog_get_entry(struct logininfo *li) 157398937Sdes{ 1574192595Sdes struct lastlogx l, *ll; 1575192595Sdes 1576192595Sdes if ((ll = getlastlogxbyname(li->username, &l)) == NULL) { 1577192595Sdes memset(&l, '\0', sizeof(l)); 1578192595Sdes ll = &l; 1579192595Sdes } 1580192595Sdes line_fullname(li->line, ll->ll_line, sizeof(li->line)); 1581192595Sdes strlcpy(li->hostname, ll->ll_host, 1582192595Sdes MIN_SIZEOF(li->hostname, ll->ll_host)); 1583192595Sdes li->tv_sec = ll->ll_tv.tv_sec; 1584192595Sdes li->tv_usec = ll->ll_tv.tv_usec; 1585192595Sdes return (1); 158698937Sdes} 1587192595Sdes#else /* HAVE_GETLASTLOGXBYNAME */ 158898937Sdesint 158998937Sdeslastlog_get_entry(struct logininfo *li) 159098937Sdes{ 159198937Sdes struct lastlog last; 1592113911Sdes int fd, ret; 159398937Sdes 159498937Sdes if (!lastlog_openseek(li, &fd, O_RDONLY)) 1595113911Sdes return (0); 159698937Sdes 1597113911Sdes ret = atomicio(read, fd, &last, sizeof(last)); 1598113911Sdes close(fd); 1599113911Sdes 1600113911Sdes switch (ret) { 1601113911Sdes case 0: 1602113911Sdes memset(&last, '\0', sizeof(last)); 1603113911Sdes /* FALLTHRU */ 1604113911Sdes case sizeof(last): 1605192595Sdes line_fullname(li->line, last.ll_line, sizeof(li->line)); 1606192595Sdes strlcpy(li->hostname, last.ll_host, 1607192595Sdes MIN_SIZEOF(li->hostname, last.ll_host)); 1608192595Sdes li->tv_sec = last.ll_time; 1609113911Sdes return (1); 1610113911Sdes case -1: 1611126277Sdes error("%s: Error reading from %s: %s", __func__, 161298937Sdes LASTLOG_FILE, strerror(errno)); 1613113911Sdes return (0); 1614113911Sdes default: 1615113911Sdes error("%s: Error reading from %s: Expecting %d, got %d", 1616157019Sdes __func__, LASTLOG_FILE, (int)sizeof(last), ret); 1617113911Sdes return (0); 161898937Sdes } 161998937Sdes 1620113911Sdes /* NOTREACHED */ 1621113911Sdes return (0); 162298937Sdes} 1623192595Sdes#endif /* HAVE_GETLASTLOGXBYNAME */ 162498937Sdes#endif /* USE_LASTLOG */ 1625147005Sdes 1626207319Sdes#if defined(USE_UTMPX) && defined(HAVE_SETUTXDB) && \ 1627207319Sdes defined(UTXDB_LASTLOGIN) && defined(HAVE_GETUTXUSER) 1628202213Sedint 1629202213Sedutmpx_get_entry(struct logininfo *li) 1630202213Sed{ 1631202213Sed struct utmpx *utx; 1632202213Sed 1633202213Sed if (setutxdb(UTXDB_LASTLOGIN, NULL) != 0) 1634202213Sed return (0); 1635202213Sed utx = getutxuser(li->username); 1636202213Sed if (utx == NULL) { 1637202213Sed endutxent(); 1638202213Sed return (0); 1639202213Sed } 1640202213Sed 1641202213Sed line_fullname(li->line, utx->ut_line, 1642202213Sed MIN_SIZEOF(li->line, utx->ut_line)); 1643202213Sed strlcpy(li->hostname, utx->ut_host, 1644202213Sed MIN_SIZEOF(li->hostname, utx->ut_host)); 1645202213Sed li->tv_sec = utx->ut_tv.tv_sec; 1646202213Sed li->tv_usec = utx->ut_tv.tv_usec; 1647202213Sed endutxent(); 1648202213Sed return (1); 1649202213Sed} 1650207319Sdes#endif /* USE_UTMPX && HAVE_SETUTXDB && UTXDB_LASTLOGIN && HAVE_GETUTXUSER */ 1651202213Sed 1652147005Sdes#ifdef USE_BTMP 1653147005Sdes /* 1654147005Sdes * Logs failed login attempts in _PATH_BTMP if that exists. 1655147005Sdes * The most common login failure is to give password instead of username. 1656147005Sdes * So the _PATH_BTMP file checked for the correct permission, so that 1657147005Sdes * only root can read it. 1658147005Sdes */ 1659147005Sdes 1660147005Sdesvoid 1661147005Sdesrecord_failed_login(const char *username, const char *hostname, 1662147005Sdes const char *ttyn) 1663147005Sdes{ 1664147005Sdes int fd; 1665147005Sdes struct utmp ut; 1666147005Sdes struct sockaddr_storage from; 1667157019Sdes socklen_t fromlen = sizeof(from); 1668147005Sdes struct sockaddr_in *a4; 1669147005Sdes struct sockaddr_in6 *a6; 1670147005Sdes time_t t; 1671147005Sdes struct stat fst; 1672147005Sdes 1673147005Sdes if (geteuid() != 0) 1674147005Sdes return; 1675147005Sdes if ((fd = open(_PATH_BTMP, O_WRONLY | O_APPEND)) < 0) { 1676147005Sdes debug("Unable to open the btmp file %s: %s", _PATH_BTMP, 1677147005Sdes strerror(errno)); 1678147005Sdes return; 1679147005Sdes } 1680147005Sdes if (fstat(fd, &fst) < 0) { 1681147005Sdes logit("%s: fstat of %s failed: %s", __func__, _PATH_BTMP, 1682147005Sdes strerror(errno)); 1683147005Sdes goto out; 1684147005Sdes } 1685221420Sdes if((fst.st_mode & (S_IXGRP | S_IRWXO)) || (fst.st_uid != 0)){ 1686147005Sdes logit("Excess permission or bad ownership on file %s", 1687147005Sdes _PATH_BTMP); 1688147005Sdes goto out; 1689147005Sdes } 1690147005Sdes 1691147005Sdes memset(&ut, 0, sizeof(ut)); 1692147005Sdes /* strncpy because we don't necessarily want nul termination */ 1693147005Sdes strncpy(ut.ut_user, username, sizeof(ut.ut_user)); 1694147005Sdes strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line)); 1695147005Sdes 1696147005Sdes time(&t); 1697147005Sdes ut.ut_time = t; /* ut_time is not always a time_t */ 1698147005Sdes ut.ut_type = LOGIN_PROCESS; 1699147005Sdes ut.ut_pid = getpid(); 1700147005Sdes 1701147005Sdes /* strncpy because we don't necessarily want nul termination */ 1702147005Sdes strncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); 1703147005Sdes 1704147005Sdes if (packet_connection_is_on_socket() && 1705147005Sdes getpeername(packet_get_connection_in(), 1706147005Sdes (struct sockaddr *)&from, &fromlen) == 0) { 1707147005Sdes ipv64_normalise_mapped(&from, &fromlen); 1708147005Sdes if (from.ss_family == AF_INET) { 1709147005Sdes a4 = (struct sockaddr_in *)&from; 1710147005Sdes memcpy(&ut.ut_addr, &(a4->sin_addr), 1711147005Sdes MIN_SIZEOF(ut.ut_addr, a4->sin_addr)); 1712147005Sdes } 1713147005Sdes#ifdef HAVE_ADDR_V6_IN_UTMP 1714147005Sdes if (from.ss_family == AF_INET6) { 1715147005Sdes a6 = (struct sockaddr_in6 *)&from; 1716147005Sdes memcpy(&ut.ut_addr_v6, &(a6->sin6_addr), 1717147005Sdes MIN_SIZEOF(ut.ut_addr_v6, a6->sin6_addr)); 1718147005Sdes } 1719147005Sdes#endif 1720147005Sdes } 1721147005Sdes 1722147005Sdes if (atomicio(vwrite, fd, &ut, sizeof(ut)) != sizeof(ut)) 1723147005Sdes error("Failed to write to %s: %s", _PATH_BTMP, 1724147005Sdes strerror(errno)); 1725147005Sdes 1726147005Sdesout: 1727147005Sdes close(fd); 1728147005Sdes} 1729147005Sdes#endif /* USE_BTMP */ 1730