loginrec.c revision 184122
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> 153162856Sdes 154162856Sdes#include <netinet/in.h> 155162856Sdes 156162856Sdes#include <errno.h> 157162856Sdes#include <fcntl.h> 158162856Sdes#ifdef HAVE_PATHS_H 159162856Sdes# include <paths.h> 160162856Sdes#endif 161162856Sdes#include <pwd.h> 162162856Sdes#include <stdarg.h> 163162856Sdes#include <string.h> 164181111Sdes#include <time.h> 165162856Sdes#include <unistd.h> 166162856Sdes 167162856Sdes#include "xmalloc.h" 168162856Sdes#include "key.h" 169162856Sdes#include "hostfile.h" 17098937Sdes#include "ssh.h" 17198937Sdes#include "loginrec.h" 17298937Sdes#include "log.h" 17398937Sdes#include "atomicio.h" 174147005Sdes#include "packet.h" 175147005Sdes#include "canohost.h" 176147005Sdes#include "auth.h" 177147005Sdes#include "buffer.h" 17898937Sdes 17998937Sdes#ifdef HAVE_UTIL_H 180147005Sdes# include <util.h> 18198937Sdes#endif 18298937Sdes 18398937Sdes#ifdef HAVE_LIBUTIL_H 184147005Sdes# include <libutil.h> 18598937Sdes#endif 18698937Sdes 18798937Sdes/** 18898937Sdes ** prototypes for helper functions in this file 18998937Sdes **/ 19098937Sdes 19198937Sdes#if HAVE_UTMP_H 19298937Sdesvoid set_utmp_time(struct logininfo *li, struct utmp *ut); 19398937Sdesvoid construct_utmp(struct logininfo *li, struct utmp *ut); 19498937Sdes#endif 19598937Sdes 19698937Sdes#ifdef HAVE_UTMPX_H 19798937Sdesvoid set_utmpx_time(struct logininfo *li, struct utmpx *ut); 19898937Sdesvoid construct_utmpx(struct logininfo *li, struct utmpx *ut); 19998937Sdes#endif 20098937Sdes 20198937Sdesint utmp_write_entry(struct logininfo *li); 20298937Sdesint utmpx_write_entry(struct logininfo *li); 20398937Sdesint wtmp_write_entry(struct logininfo *li); 20498937Sdesint wtmpx_write_entry(struct logininfo *li); 20598937Sdesint lastlog_write_entry(struct logininfo *li); 20698937Sdesint syslogin_write_entry(struct logininfo *li); 20798937Sdes 20898937Sdesint getlast_entry(struct logininfo *li); 20998937Sdesint lastlog_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 27598937Sdeslogin_get_lastlog_time(const int 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 * 29998937Sdeslogin_get_lastlog(struct logininfo *li, const int 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) 313147005Sdes fatal("%s: Cannot find account for uid %i", __func__, uid); 31498937Sdes 31598937Sdes /* No MIN_SIZEOF here - we absolutely *must not* truncate the 316147005Sdes * username (XXX - so check for trunc!) */ 31798937Sdes strlcpy(li->username, pw->pw_name, sizeof(li->username)); 31898937Sdes 31998937Sdes if (getlast_entry(li)) 320147005Sdes return (li); 32198937Sdes else 322147005Sdes return (NULL); 32398937Sdes} 32498937Sdes 32598937Sdes 326147005Sdes/* 327147005Sdes * login_alloc_entry(int, char*, char*, char*) - Allocate and initialise 32898937Sdes * a logininfo structure 32998937Sdes * 33098937Sdes * This function creates a new struct logininfo, a data structure 33198937Sdes * meant to carry the information required to portably record login info. 33298937Sdes * 33398937Sdes * Returns a pointer to a newly created struct logininfo. If memory 33498937Sdes * allocation fails, the program halts. 33598937Sdes */ 33698937Sdesstruct 33798937Sdeslogininfo *login_alloc_entry(int pid, const char *username, 338147005Sdes const char *hostname, const char *line) 33998937Sdes{ 34098937Sdes struct logininfo *newli; 34198937Sdes 342147005Sdes newli = xmalloc(sizeof(*newli)); 343147005Sdes login_init_entry(newli, pid, username, hostname, line); 344147005Sdes return (newli); 34598937Sdes} 34698937Sdes 34798937Sdes 34898937Sdes/* login_free_entry(struct logininfo *) - free struct memory */ 34998937Sdesvoid 35098937Sdeslogin_free_entry(struct logininfo *li) 35198937Sdes{ 35298937Sdes xfree(li); 35398937Sdes} 35498937Sdes 35598937Sdes 35698937Sdes/* login_init_entry(struct logininfo *, int, char*, char*, char*) 35798937Sdes * - initialise a struct logininfo 35898937Sdes * 35998937Sdes * Populates a new struct logininfo, a data structure meant to carry 36098937Sdes * the information required to portably record login info. 36198937Sdes * 36298937Sdes * Returns: 1 36398937Sdes */ 36498937Sdesint 36598937Sdeslogin_init_entry(struct logininfo *li, int pid, const char *username, 366147005Sdes const char *hostname, const char *line) 36798937Sdes{ 36898937Sdes struct passwd *pw; 36998937Sdes 37098937Sdes memset(li, 0, sizeof(*li)); 37198937Sdes 37298937Sdes li->pid = pid; 37398937Sdes 37498937Sdes /* set the line information */ 37598937Sdes if (line) 37698937Sdes line_fullname(li->line, line, sizeof(li->line)); 37798937Sdes 37898937Sdes if (username) { 37998937Sdes strlcpy(li->username, username, sizeof(li->username)); 38098937Sdes pw = getpwnam(li->username); 381147005Sdes if (pw == NULL) { 382149753Sdes fatal("%s: Cannot find user \"%s\"", __func__, 383147005Sdes li->username); 384147005Sdes } 38598937Sdes li->uid = pw->pw_uid; 38698937Sdes } 38798937Sdes 38898937Sdes if (hostname) 38998937Sdes strlcpy(li->hostname, hostname, sizeof(li->hostname)); 39098937Sdes 391147005Sdes return (1); 39298937Sdes} 39398937Sdes 394149753Sdes/* 395147005Sdes * login_set_current_time(struct logininfo *) - set the current time 39698937Sdes * 39798937Sdes * Set the current time in a logininfo structure. This function is 39898937Sdes * meant to eliminate the need to deal with system dependencies for 39998937Sdes * time handling. 40098937Sdes */ 40198937Sdesvoid 40298937Sdeslogin_set_current_time(struct logininfo *li) 40398937Sdes{ 40498937Sdes struct timeval tv; 40598937Sdes 40698937Sdes gettimeofday(&tv, NULL); 40798937Sdes 40898937Sdes li->tv_sec = tv.tv_sec; 40998937Sdes li->tv_usec = tv.tv_usec; 41098937Sdes} 41198937Sdes 41298937Sdes/* copy a sockaddr_* into our logininfo */ 41398937Sdesvoid 41498937Sdeslogin_set_addr(struct logininfo *li, const struct sockaddr *sa, 415147005Sdes const unsigned int sa_size) 41698937Sdes{ 41798937Sdes unsigned int bufsize = sa_size; 41898937Sdes 41998937Sdes /* make sure we don't overrun our union */ 42098937Sdes if (sizeof(li->hostaddr) < sa_size) 42198937Sdes bufsize = sizeof(li->hostaddr); 42298937Sdes 423147005Sdes memcpy(&li->hostaddr.sa, sa, bufsize); 42498937Sdes} 42598937Sdes 42698937Sdes 42798937Sdes/** 42898937Sdes ** login_write: Call low-level recording functions based on autoconf 42998937Sdes ** results 43098937Sdes **/ 43198937Sdesint 432147005Sdeslogin_write(struct logininfo *li) 43398937Sdes{ 43498937Sdes#ifndef HAVE_CYGWIN 435147005Sdes if (geteuid() != 0) { 436147005Sdes logit("Attempt to write login records by non-root user (aborting)"); 437147005Sdes return (1); 43898937Sdes } 43998937Sdes#endif 44098937Sdes 44198937Sdes /* set the timestamp */ 44298937Sdes login_set_current_time(li); 44398937Sdes#ifdef USE_LOGIN 44498937Sdes syslogin_write_entry(li); 44598937Sdes#endif 44698937Sdes#ifdef USE_LASTLOG 447147005Sdes if (li->type == LTYPE_LOGIN) 44898937Sdes lastlog_write_entry(li); 44998937Sdes#endif 45098937Sdes#ifdef USE_UTMP 45198937Sdes utmp_write_entry(li); 45298937Sdes#endif 45398937Sdes#ifdef USE_WTMP 45498937Sdes wtmp_write_entry(li); 45598937Sdes#endif 45698937Sdes#ifdef USE_UTMPX 45798937Sdes utmpx_write_entry(li); 45898937Sdes#endif 45998937Sdes#ifdef USE_WTMPX 46098937Sdes wtmpx_write_entry(li); 46198937Sdes#endif 462137019Sdes#ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN 463149753Sdes if (li->type == LTYPE_LOGIN && 464149753Sdes !sys_auth_record_login(li->username,li->hostname,li->line, 465149753Sdes &loginmsg)) 466137019Sdes logit("Writing login record failed for %s", li->username); 467137019Sdes#endif 468147005Sdes#ifdef SSH_AUDIT_EVENTS 469147005Sdes if (li->type == LTYPE_LOGIN) 470147005Sdes audit_session_open(li->line); 471147005Sdes else if (li->type == LTYPE_LOGOUT) 472147005Sdes audit_session_close(li->line); 473147005Sdes#endif 474147005Sdes return (0); 47598937Sdes} 47698937Sdes 47798937Sdes#ifdef LOGIN_NEEDS_UTMPX 47898937Sdesint 47998937Sdeslogin_utmp_only(struct logininfo *li) 48098937Sdes{ 481126277Sdes li->type = LTYPE_LOGIN; 48298937Sdes login_set_current_time(li); 48398937Sdes# ifdef USE_UTMP 48498937Sdes utmp_write_entry(li); 48598937Sdes# endif 48698937Sdes# ifdef USE_WTMP 48798937Sdes wtmp_write_entry(li); 48898937Sdes# endif 48998937Sdes# ifdef USE_UTMPX 49098937Sdes utmpx_write_entry(li); 49198937Sdes# endif 49298937Sdes# ifdef USE_WTMPX 49398937Sdes wtmpx_write_entry(li); 49498937Sdes# endif 495147005Sdes return (0); 49698937Sdes} 49798937Sdes#endif 49898937Sdes 49998937Sdes/** 50098937Sdes ** getlast_entry: Call low-level functions to retrieve the last login 50198937Sdes ** time. 50298937Sdes **/ 50398937Sdes 50498937Sdes/* take the uid in li and return the last login time */ 50598937Sdesint 50698937Sdesgetlast_entry(struct logininfo *li) 50798937Sdes{ 50898937Sdes#ifdef USE_LASTLOG 50998937Sdes return(lastlog_get_entry(li)); 51098937Sdes#else /* !USE_LASTLOG */ 51198937Sdes 512147005Sdes#if defined(DISABLE_LASTLOG) 51398937Sdes /* On some systems we shouldn't even try to obtain last login 51498937Sdes * time, e.g. AIX */ 515147005Sdes return (0); 516147005Sdes# elif defined(USE_WTMP) && \ 517147005Sdes (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) 51898937Sdes /* retrieve last login time from utmp */ 51998937Sdes return (wtmp_get_entry(li)); 520147005Sdes# elif defined(USE_WTMPX) && \ 521147005Sdes (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX)) 52298937Sdes /* If wtmp isn't available, try wtmpx */ 52398937Sdes return (wtmpx_get_entry(li)); 524147005Sdes# else 52598937Sdes /* Give up: No means of retrieving last login time */ 526147005Sdes return (0); 52798937Sdes# endif /* DISABLE_LASTLOG */ 52898937Sdes#endif /* USE_LASTLOG */ 52998937Sdes} 53098937Sdes 53198937Sdes 53298937Sdes 53398937Sdes/* 53498937Sdes * 'line' string utility functions 53598937Sdes * 53698937Sdes * These functions process the 'line' string into one of three forms: 53798937Sdes * 53898937Sdes * 1. The full filename (including '/dev') 53998937Sdes * 2. The stripped name (excluding '/dev') 54098937Sdes * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00 54198937Sdes * /dev/pts/1 -> ts/1 ) 54298937Sdes * 54398937Sdes * Form 3 is used on some systems to identify a .tmp.? entry when 54498937Sdes * attempting to remove it. Typically both addition and removal is 54598937Sdes * performed by one application - say, sshd - so as long as the choice 54698937Sdes * uniquely identifies a terminal it's ok. 54798937Sdes */ 54898937Sdes 54998937Sdes 550147005Sdes/* 551147005Sdes * line_fullname(): add the leading '/dev/' if it doesn't exist make 552147005Sdes * sure dst has enough space, if not just copy src (ugh) 553147005Sdes */ 55498937Sdeschar * 555149753Sdesline_fullname(char *dst, const char *src, u_int dstsize) 55698937Sdes{ 55798937Sdes memset(dst, '\0', dstsize); 558147005Sdes if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) 55998937Sdes strlcpy(dst, src, dstsize); 560147005Sdes else { 56198937Sdes strlcpy(dst, "/dev/", dstsize); 56298937Sdes strlcat(dst, src, dstsize); 56398937Sdes } 564147005Sdes return (dst); 56598937Sdes} 56698937Sdes 56798937Sdes/* line_stripname(): strip the leading '/dev' if it exists, return dst */ 56898937Sdeschar * 56998937Sdesline_stripname(char *dst, const char *src, int dstsize) 57098937Sdes{ 57198937Sdes memset(dst, '\0', dstsize); 57298937Sdes if (strncmp(src, "/dev/", 5) == 0) 57398937Sdes strlcpy(dst, src + 5, dstsize); 57498937Sdes else 57598937Sdes strlcpy(dst, src, dstsize); 576147005Sdes return (dst); 57798937Sdes} 57898937Sdes 579149753Sdes/* 580147005Sdes * line_abbrevname(): Return the abbreviated (usually four-character) 58198937Sdes * form of the line (Just use the last <dstsize> characters of the 58298937Sdes * full name.) 58398937Sdes * 58498937Sdes * NOTE: use strncpy because we do NOT necessarily want zero 585147005Sdes * termination 586147005Sdes */ 58798937Sdeschar * 58898937Sdesline_abbrevname(char *dst, const char *src, int dstsize) 58998937Sdes{ 59098937Sdes size_t len; 59198937Sdes 59298937Sdes memset(dst, '\0', dstsize); 59398937Sdes 59498937Sdes /* Always skip prefix if present */ 59598937Sdes if (strncmp(src, "/dev/", 5) == 0) 59698937Sdes src += 5; 59798937Sdes 59898937Sdes#ifdef WITH_ABBREV_NO_TTY 59998937Sdes if (strncmp(src, "tty", 3) == 0) 60098937Sdes src += 3; 60198937Sdes#endif 60298937Sdes 60398937Sdes len = strlen(src); 60498937Sdes 60598937Sdes if (len > 0) { 60698937Sdes if (((int)len - dstsize) > 0) 60798937Sdes src += ((int)len - dstsize); 60898937Sdes 60998937Sdes /* note: _don't_ change this to strlcpy */ 61098937Sdes strncpy(dst, src, (size_t)dstsize); 61198937Sdes } 61298937Sdes 613147005Sdes return (dst); 61498937Sdes} 61598937Sdes 61698937Sdes/** 61798937Sdes ** utmp utility functions 61898937Sdes ** 61998937Sdes ** These functions manipulate struct utmp, taking system differences 62098937Sdes ** into account. 62198937Sdes **/ 62298937Sdes 62398937Sdes#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN) 62498937Sdes 62598937Sdes/* build the utmp structure */ 62698937Sdesvoid 62798937Sdesset_utmp_time(struct logininfo *li, struct utmp *ut) 62898937Sdes{ 629147005Sdes# if defined(HAVE_TV_IN_UTMP) 63098937Sdes ut->ut_tv.tv_sec = li->tv_sec; 63198937Sdes ut->ut_tv.tv_usec = li->tv_usec; 632147005Sdes# elif defined(HAVE_TIME_IN_UTMP) 63398937Sdes ut->ut_time = li->tv_sec; 63498937Sdes# endif 63598937Sdes} 63698937Sdes 63798937Sdesvoid 63898937Sdesconstruct_utmp(struct logininfo *li, 63998937Sdes struct utmp *ut) 64098937Sdes{ 641113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 642113911Sdes struct sockaddr_in6 *sa6; 643147005Sdes# endif 644147005Sdes 64598937Sdes memset(ut, '\0', sizeof(*ut)); 64698937Sdes 64798937Sdes /* First fill out fields used for both logins and logouts */ 64898937Sdes 64998937Sdes# ifdef HAVE_ID_IN_UTMP 65098937Sdes line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id)); 65198937Sdes# endif 65298937Sdes 65398937Sdes# ifdef HAVE_TYPE_IN_UTMP 65498937Sdes /* This is done here to keep utmp constants out of struct logininfo */ 65598937Sdes switch (li->type) { 65698937Sdes case LTYPE_LOGIN: 65798937Sdes ut->ut_type = USER_PROCESS; 658106130Sdes#ifdef _UNICOS 65998937Sdes cray_set_tmpdir(ut); 66098937Sdes#endif 66198937Sdes break; 66298937Sdes case LTYPE_LOGOUT: 66398937Sdes ut->ut_type = DEAD_PROCESS; 664106130Sdes#ifdef _UNICOS 66598937Sdes cray_retain_utmp(ut, li->pid); 66698937Sdes#endif 66798937Sdes break; 66898937Sdes } 66998937Sdes# endif 67098937Sdes set_utmp_time(li, ut); 67198937Sdes 67298937Sdes line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line)); 67398937Sdes 67498937Sdes# ifdef HAVE_PID_IN_UTMP 67598937Sdes ut->ut_pid = li->pid; 67698937Sdes# endif 67798937Sdes 67898937Sdes /* If we're logging out, leave all other fields blank */ 67998937Sdes if (li->type == LTYPE_LOGOUT) 680147005Sdes return; 68198937Sdes 68298937Sdes /* 68398937Sdes * These fields are only used when logging in, and are blank 68498937Sdes * for logouts. 68598937Sdes */ 68698937Sdes 68798937Sdes /* Use strncpy because we don't necessarily want null termination */ 688147005Sdes strncpy(ut->ut_name, li->username, 689147005Sdes MIN_SIZEOF(ut->ut_name, li->username)); 69098937Sdes# ifdef HAVE_HOST_IN_UTMP 691184122Sdes strncpy(ut->ut_host, li->hostname, 692184122Sdes MIN_SIZEOF(ut->ut_host, li->hostname)); 69398937Sdes# endif 69498937Sdes# ifdef HAVE_ADDR_IN_UTMP 69598937Sdes /* this is just a 32-bit IP address */ 69698937Sdes if (li->hostaddr.sa.sa_family == AF_INET) 69798937Sdes ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 69898937Sdes# endif 699113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 700113911Sdes /* this is just a 128-bit IPv6 address */ 701113911Sdes if (li->hostaddr.sa.sa_family == AF_INET6) { 702113911Sdes sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 703113911Sdes memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 704113911Sdes if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 705113911Sdes ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; 706113911Sdes ut->ut_addr_v6[1] = 0; 707113911Sdes ut->ut_addr_v6[2] = 0; 708113911Sdes ut->ut_addr_v6[3] = 0; 709113911Sdes } 710113911Sdes } 711113911Sdes# endif 71298937Sdes} 71398937Sdes#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */ 71498937Sdes 71598937Sdes/** 71698937Sdes ** utmpx utility functions 71798937Sdes ** 71898937Sdes ** These functions manipulate struct utmpx, accounting for system 71998937Sdes ** variations. 72098937Sdes **/ 72198937Sdes 72298937Sdes#if defined(USE_UTMPX) || defined (USE_WTMPX) 72398937Sdes/* build the utmpx structure */ 72498937Sdesvoid 72598937Sdesset_utmpx_time(struct logininfo *li, struct utmpx *utx) 72698937Sdes{ 727147005Sdes# if defined(HAVE_TV_IN_UTMPX) 72898937Sdes utx->ut_tv.tv_sec = li->tv_sec; 72998937Sdes utx->ut_tv.tv_usec = li->tv_usec; 730147005Sdes# elif defined(HAVE_TIME_IN_UTMPX) 73198937Sdes utx->ut_time = li->tv_sec; 732147005Sdes# endif 73398937Sdes} 73498937Sdes 73598937Sdesvoid 73698937Sdesconstruct_utmpx(struct logininfo *li, struct utmpx *utx) 73798937Sdes{ 738113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 739113911Sdes struct sockaddr_in6 *sa6; 740113911Sdes# endif 74198937Sdes memset(utx, '\0', sizeof(*utx)); 742147005Sdes 74398937Sdes# ifdef HAVE_ID_IN_UTMPX 74498937Sdes line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); 74598937Sdes# endif 74698937Sdes 74798937Sdes /* this is done here to keep utmp constants out of loginrec.h */ 74898937Sdes switch (li->type) { 74998937Sdes case LTYPE_LOGIN: 75098937Sdes utx->ut_type = USER_PROCESS; 75198937Sdes break; 75298937Sdes case LTYPE_LOGOUT: 75398937Sdes utx->ut_type = DEAD_PROCESS; 75498937Sdes break; 75598937Sdes } 75698937Sdes line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line)); 75798937Sdes set_utmpx_time(li, utx); 75898937Sdes utx->ut_pid = li->pid; 759147005Sdes 76098937Sdes /* strncpy(): Don't necessarily want null termination */ 761147005Sdes strncpy(utx->ut_name, li->username, 762147005Sdes MIN_SIZEOF(utx->ut_name, li->username)); 76398937Sdes 76498937Sdes if (li->type == LTYPE_LOGOUT) 76598937Sdes return; 76698937Sdes 76798937Sdes /* 76898937Sdes * These fields are only used when logging in, and are blank 76998937Sdes * for logouts. 77098937Sdes */ 77198937Sdes 77298937Sdes# ifdef HAVE_HOST_IN_UTMPX 773147005Sdes strncpy(utx->ut_host, li->hostname, 774147005Sdes MIN_SIZEOF(utx->ut_host, li->hostname)); 77598937Sdes# endif 77698937Sdes# ifdef HAVE_ADDR_IN_UTMPX 77798937Sdes /* this is just a 32-bit IP address */ 77898937Sdes if (li->hostaddr.sa.sa_family == AF_INET) 77998937Sdes utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 78098937Sdes# endif 781113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 782113911Sdes /* this is just a 128-bit IPv6 address */ 783113911Sdes if (li->hostaddr.sa.sa_family == AF_INET6) { 784113911Sdes sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 785113911Sdes memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 786113911Sdes if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 787113911Sdes ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; 788113911Sdes ut->ut_addr_v6[1] = 0; 789113911Sdes ut->ut_addr_v6[2] = 0; 790113911Sdes ut->ut_addr_v6[3] = 0; 791113911Sdes } 792113911Sdes } 793113911Sdes# endif 79498937Sdes# ifdef HAVE_SYSLEN_IN_UTMPX 79598937Sdes /* ut_syslen is the length of the utx_host string */ 79698937Sdes utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host)); 79798937Sdes# endif 79898937Sdes} 79998937Sdes#endif /* USE_UTMPX || USE_WTMPX */ 80098937Sdes 80198937Sdes/** 80298937Sdes ** Low-level utmp functions 80398937Sdes **/ 80498937Sdes 80598937Sdes/* FIXME: (ATL) utmp_write_direct needs testing */ 80698937Sdes#ifdef USE_UTMP 80798937Sdes 80898937Sdes/* if we can, use pututline() etc. */ 80998937Sdes# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \ 81098937Sdes defined(HAVE_PUTUTLINE) 81198937Sdes# define UTMP_USE_LIBRARY 81298937Sdes# endif 81398937Sdes 81498937Sdes 81598937Sdes/* write a utmp entry with the system's help (pututline() and pals) */ 81698937Sdes# ifdef UTMP_USE_LIBRARY 81798937Sdesstatic int 81898937Sdesutmp_write_library(struct logininfo *li, struct utmp *ut) 81998937Sdes{ 82098937Sdes setutent(); 82198937Sdes pututline(ut); 82298937Sdes# ifdef HAVE_ENDUTENT 82398937Sdes endutent(); 82498937Sdes# endif 825147005Sdes return (1); 82698937Sdes} 82798937Sdes# else /* UTMP_USE_LIBRARY */ 82898937Sdes 829149753Sdes/* 830147005Sdes * Write a utmp entry direct to the file 831147005Sdes * This is a slightly modification of code in OpenBSD's login.c 832147005Sdes */ 83398937Sdesstatic int 83498937Sdesutmp_write_direct(struct logininfo *li, struct utmp *ut) 83598937Sdes{ 83698937Sdes struct utmp old_ut; 83798937Sdes register int fd; 83898937Sdes int tty; 83998937Sdes 84098937Sdes /* FIXME: (ATL) ttyslot() needs local implementation */ 84198937Sdes 84298937Sdes#if defined(HAVE_GETTTYENT) 843147005Sdes struct ttyent *ty; 84498937Sdes 84598937Sdes tty=0; 84698937Sdes setttyent(); 847147005Sdes while (NULL != (ty = getttyent())) { 84898937Sdes tty++; 84998937Sdes if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line))) 85098937Sdes break; 85198937Sdes } 85298937Sdes endttyent(); 85398937Sdes 854147005Sdes if (NULL == ty) { 855137019Sdes logit("%s: tty not found", __func__); 856137019Sdes return (0); 85798937Sdes } 85898937Sdes#else /* FIXME */ 85998937Sdes 86098937Sdes tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ 86198937Sdes 86298937Sdes#endif /* HAVE_GETTTYENT */ 86398937Sdes 86498937Sdes if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { 865137019Sdes off_t pos, ret; 866137019Sdes 867137019Sdes pos = (off_t)tty * sizeof(struct utmp); 868137019Sdes if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { 869147005Sdes logit("%s: lseek: %s", __func__, strerror(errno)); 870137019Sdes return (0); 871137019Sdes } 872137019Sdes if (ret != pos) { 873149753Sdes logit("%s: Couldn't seek to tty %d slot in %s", 874147005Sdes __func__, tty, UTMP_FILE); 875137019Sdes return (0); 876137019Sdes } 87798937Sdes /* 87898937Sdes * Prevent luser from zero'ing out ut_host. 87998937Sdes * If the new ut_line is empty but the old one is not 88098937Sdes * and ut_line and ut_name match, preserve the old ut_line. 88198937Sdes */ 88298937Sdes if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && 883147005Sdes (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && 884147005Sdes (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && 885147005Sdes (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) 886147005Sdes memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); 88798937Sdes 888137019Sdes if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { 889147005Sdes logit("%s: lseek: %s", __func__, strerror(errno)); 890137019Sdes return (0); 891137019Sdes } 892137019Sdes if (ret != pos) { 893147005Sdes logit("%s: Couldn't seek to tty %d slot in %s", 894137019Sdes __func__, tty, UTMP_FILE); 895137019Sdes return (0); 896137019Sdes } 897147005Sdes if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { 898137019Sdes logit("%s: error writing %s: %s", __func__, 89998937Sdes UTMP_FILE, strerror(errno)); 900147005Sdes } 90198937Sdes 902147005Sdes close(fd); 903147005Sdes return (1); 90498937Sdes } else { 905147005Sdes return (0); 90698937Sdes } 90798937Sdes} 90898937Sdes# endif /* UTMP_USE_LIBRARY */ 90998937Sdes 91098937Sdesstatic int 91198937Sdesutmp_perform_login(struct logininfo *li) 91298937Sdes{ 91398937Sdes struct utmp ut; 91498937Sdes 91598937Sdes construct_utmp(li, &ut); 91698937Sdes# ifdef UTMP_USE_LIBRARY 91798937Sdes if (!utmp_write_library(li, &ut)) { 918147005Sdes logit("%s: utmp_write_library() failed", __func__); 919147005Sdes return (0); 92098937Sdes } 92198937Sdes# else 92298937Sdes if (!utmp_write_direct(li, &ut)) { 923147005Sdes logit("%s: utmp_write_direct() failed", __func__); 924147005Sdes return (0); 92598937Sdes } 92698937Sdes# endif 927147005Sdes return (1); 92898937Sdes} 92998937Sdes 93098937Sdes 93198937Sdesstatic int 93298937Sdesutmp_perform_logout(struct logininfo *li) 93398937Sdes{ 93498937Sdes struct utmp ut; 93598937Sdes 93698937Sdes construct_utmp(li, &ut); 93798937Sdes# ifdef UTMP_USE_LIBRARY 93898937Sdes if (!utmp_write_library(li, &ut)) { 939147005Sdes logit("%s: utmp_write_library() failed", __func__); 940147005Sdes return (0); 94198937Sdes } 94298937Sdes# else 94398937Sdes if (!utmp_write_direct(li, &ut)) { 944147005Sdes logit("%s: utmp_write_direct() failed", __func__); 945147005Sdes return (0); 94698937Sdes } 94798937Sdes# endif 948147005Sdes return (1); 94998937Sdes} 95098937Sdes 95198937Sdes 95298937Sdesint 95398937Sdesutmp_write_entry(struct logininfo *li) 95498937Sdes{ 95598937Sdes switch(li->type) { 95698937Sdes case LTYPE_LOGIN: 957147005Sdes return (utmp_perform_login(li)); 95898937Sdes 95998937Sdes case LTYPE_LOGOUT: 960147005Sdes return (utmp_perform_logout(li)); 96198937Sdes 96298937Sdes default: 963147005Sdes logit("%s: invalid type field", __func__); 964147005Sdes return (0); 96598937Sdes } 96698937Sdes} 96798937Sdes#endif /* USE_UTMP */ 96898937Sdes 96998937Sdes 97098937Sdes/** 97198937Sdes ** Low-level utmpx functions 97298937Sdes **/ 97398937Sdes 97498937Sdes/* not much point if we don't want utmpx entries */ 97598937Sdes#ifdef USE_UTMPX 97698937Sdes 97798937Sdes/* if we have the wherewithall, use pututxline etc. */ 97898937Sdes# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \ 97998937Sdes defined(HAVE_PUTUTXLINE) 98098937Sdes# define UTMPX_USE_LIBRARY 98198937Sdes# endif 98298937Sdes 98398937Sdes 98498937Sdes/* write a utmpx entry with the system's help (pututxline() and pals) */ 98598937Sdes# ifdef UTMPX_USE_LIBRARY 98698937Sdesstatic int 98798937Sdesutmpx_write_library(struct logininfo *li, struct utmpx *utx) 98898937Sdes{ 98998937Sdes setutxent(); 99098937Sdes pututxline(utx); 99198937Sdes 99298937Sdes# ifdef HAVE_ENDUTXENT 99398937Sdes endutxent(); 99498937Sdes# endif 995147005Sdes return (1); 99698937Sdes} 99798937Sdes 99898937Sdes# else /* UTMPX_USE_LIBRARY */ 99998937Sdes 100098937Sdes/* write a utmp entry direct to the file */ 100198937Sdesstatic int 100298937Sdesutmpx_write_direct(struct logininfo *li, struct utmpx *utx) 100398937Sdes{ 1004147005Sdes logit("%s: not implemented!", __func__); 1005147005Sdes return (0); 100698937Sdes} 100798937Sdes# endif /* UTMPX_USE_LIBRARY */ 100898937Sdes 100998937Sdesstatic int 101098937Sdesutmpx_perform_login(struct logininfo *li) 101198937Sdes{ 101298937Sdes struct utmpx utx; 101398937Sdes 101498937Sdes construct_utmpx(li, &utx); 101598937Sdes# ifdef UTMPX_USE_LIBRARY 101698937Sdes if (!utmpx_write_library(li, &utx)) { 1017147005Sdes logit("%s: utmp_write_library() failed", __func__); 1018147005Sdes return (0); 101998937Sdes } 102098937Sdes# else 102198937Sdes if (!utmpx_write_direct(li, &ut)) { 1022147005Sdes logit("%s: utmp_write_direct() failed", __func__); 1023147005Sdes return (0); 102498937Sdes } 102598937Sdes# endif 1026147005Sdes return (1); 102798937Sdes} 102898937Sdes 102998937Sdes 103098937Sdesstatic int 103198937Sdesutmpx_perform_logout(struct logininfo *li) 103298937Sdes{ 103398937Sdes struct utmpx utx; 103498937Sdes 103598937Sdes construct_utmpx(li, &utx); 103698937Sdes# ifdef HAVE_ID_IN_UTMPX 103798937Sdes line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); 103898937Sdes# endif 103998937Sdes# ifdef HAVE_TYPE_IN_UTMPX 104098937Sdes utx.ut_type = DEAD_PROCESS; 104198937Sdes# endif 104298937Sdes 104398937Sdes# ifdef UTMPX_USE_LIBRARY 104498937Sdes utmpx_write_library(li, &utx); 104598937Sdes# else 104698937Sdes utmpx_write_direct(li, &utx); 104798937Sdes# endif 1048147005Sdes return (1); 104998937Sdes} 105098937Sdes 105198937Sdesint 105298937Sdesutmpx_write_entry(struct logininfo *li) 105398937Sdes{ 105498937Sdes switch(li->type) { 105598937Sdes case LTYPE_LOGIN: 1056147005Sdes return (utmpx_perform_login(li)); 105798937Sdes case LTYPE_LOGOUT: 1058147005Sdes return (utmpx_perform_logout(li)); 105998937Sdes default: 1060147005Sdes logit("%s: invalid type field", __func__); 1061147005Sdes return (0); 106298937Sdes } 106398937Sdes} 106498937Sdes#endif /* USE_UTMPX */ 106598937Sdes 106698937Sdes 106798937Sdes/** 106898937Sdes ** Low-level wtmp functions 106998937Sdes **/ 107098937Sdes 107198937Sdes#ifdef USE_WTMP 107298937Sdes 1073149753Sdes/* 1074147005Sdes * Write a wtmp entry direct to the end of the file 1075147005Sdes * This is a slight modification of code in OpenBSD's logwtmp.c 1076147005Sdes */ 107798937Sdesstatic int 107898937Sdeswtmp_write(struct logininfo *li, struct utmp *ut) 107998937Sdes{ 108098937Sdes struct stat buf; 108198937Sdes int fd, ret = 1; 108298937Sdes 108398937Sdes if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 1084147005Sdes logit("%s: problem writing %s: %s", __func__, 108598937Sdes WTMP_FILE, strerror(errno)); 1086147005Sdes return (0); 108798937Sdes } 108898937Sdes if (fstat(fd, &buf) == 0) 1089124211Sdes if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { 109098937Sdes ftruncate(fd, buf.st_size); 1091147005Sdes logit("%s: problem writing %s: %s", __func__, 109298937Sdes WTMP_FILE, strerror(errno)); 109398937Sdes ret = 0; 109498937Sdes } 1095147005Sdes close(fd); 1096147005Sdes return (ret); 109798937Sdes} 109898937Sdes 109998937Sdesstatic int 110098937Sdeswtmp_perform_login(struct logininfo *li) 110198937Sdes{ 110298937Sdes struct utmp ut; 110398937Sdes 110498937Sdes construct_utmp(li, &ut); 1105147005Sdes return (wtmp_write(li, &ut)); 110698937Sdes} 110798937Sdes 110898937Sdes 110998937Sdesstatic int 111098937Sdeswtmp_perform_logout(struct logininfo *li) 111198937Sdes{ 111298937Sdes struct utmp ut; 111398937Sdes 111498937Sdes construct_utmp(li, &ut); 1115147005Sdes return (wtmp_write(li, &ut)); 111698937Sdes} 111798937Sdes 111898937Sdes 111998937Sdesint 112098937Sdeswtmp_write_entry(struct logininfo *li) 112198937Sdes{ 112298937Sdes switch(li->type) { 112398937Sdes case LTYPE_LOGIN: 1124147005Sdes return (wtmp_perform_login(li)); 112598937Sdes case LTYPE_LOGOUT: 1126147005Sdes return (wtmp_perform_logout(li)); 112798937Sdes default: 1128147005Sdes logit("%s: invalid type field", __func__); 1129147005Sdes return (0); 113098937Sdes } 113198937Sdes} 113298937Sdes 113398937Sdes 1134149753Sdes/* 1135147005Sdes * Notes on fetching login data from wtmp/wtmpx 113698937Sdes * 113798937Sdes * Logouts are usually recorded with (amongst other things) a blank 113898937Sdes * username on a given tty line. However, some systems (HP-UX is one) 113998937Sdes * leave all fields set, but change the ut_type field to DEAD_PROCESS. 114098937Sdes * 114198937Sdes * Since we're only looking for logins here, we know that the username 114298937Sdes * must be set correctly. On systems that leave it in, we check for 114398937Sdes * ut_type==USER_PROCESS (indicating a login.) 114498937Sdes * 114598937Sdes * Portability: Some systems may set something other than USER_PROCESS 114698937Sdes * to indicate a login process. I don't know of any as I write. Also, 114798937Sdes * it's possible that some systems may both leave the username in 114898937Sdes * place and not have ut_type. 114998937Sdes */ 115098937Sdes 115198937Sdes/* return true if this wtmp entry indicates a login */ 115298937Sdesstatic int 115398937Sdeswtmp_islogin(struct logininfo *li, struct utmp *ut) 115498937Sdes{ 115598937Sdes if (strncmp(li->username, ut->ut_name, 1156147005Sdes MIN_SIZEOF(li->username, ut->ut_name)) == 0) { 115798937Sdes# ifdef HAVE_TYPE_IN_UTMP 115898937Sdes if (ut->ut_type & USER_PROCESS) 1159147005Sdes return (1); 116098937Sdes# else 1161147005Sdes return (1); 116298937Sdes# endif 116398937Sdes } 1164147005Sdes return (0); 116598937Sdes} 116698937Sdes 116798937Sdesint 116898937Sdeswtmp_get_entry(struct logininfo *li) 116998937Sdes{ 117098937Sdes struct stat st; 117198937Sdes struct utmp ut; 1172147005Sdes int fd, found = 0; 117398937Sdes 117498937Sdes /* Clear the time entries in our logininfo */ 117598937Sdes li->tv_sec = li->tv_usec = 0; 117698937Sdes 117798937Sdes if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { 1178149753Sdes logit("%s: problem opening %s: %s", __func__, 117998937Sdes WTMP_FILE, strerror(errno)); 1180147005Sdes return (0); 118198937Sdes } 118298937Sdes if (fstat(fd, &st) != 0) { 1183149753Sdes logit("%s: couldn't stat %s: %s", __func__, 118498937Sdes WTMP_FILE, strerror(errno)); 118598937Sdes close(fd); 1186147005Sdes return (0); 118798937Sdes } 118898937Sdes 118998937Sdes /* Seek to the start of the last struct utmp */ 119098937Sdes if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { 119198937Sdes /* Looks like we've got a fresh wtmp file */ 119298937Sdes close(fd); 1193147005Sdes return (0); 119498937Sdes } 119598937Sdes 119698937Sdes while (!found) { 119798937Sdes if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { 1198149753Sdes logit("%s: read of %s failed: %s", __func__, 119998937Sdes WTMP_FILE, strerror(errno)); 120098937Sdes close (fd); 1201147005Sdes return (0); 120298937Sdes } 120398937Sdes if ( wtmp_islogin(li, &ut) ) { 120498937Sdes found = 1; 1205147005Sdes /* 1206147005Sdes * We've already checked for a time in struct 1207147005Sdes * utmp, in login_getlast() 1208147005Sdes */ 120998937Sdes# ifdef HAVE_TIME_IN_UTMP 121098937Sdes li->tv_sec = ut.ut_time; 121198937Sdes# else 121298937Sdes# if HAVE_TV_IN_UTMP 121398937Sdes li->tv_sec = ut.ut_tv.tv_sec; 121498937Sdes# endif 121598937Sdes# endif 121698937Sdes line_fullname(li->line, ut.ut_line, 1217147005Sdes MIN_SIZEOF(li->line, ut.ut_line)); 121898937Sdes# ifdef HAVE_HOST_IN_UTMP 121998937Sdes strlcpy(li->hostname, ut.ut_host, 1220147005Sdes MIN_SIZEOF(li->hostname, ut.ut_host)); 122198937Sdes# endif 122298937Sdes continue; 122398937Sdes } 122498937Sdes /* Seek back 2 x struct utmp */ 122598937Sdes if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { 122698937Sdes /* We've found the start of the file, so quit */ 1227147005Sdes close(fd); 1228147005Sdes return (0); 122998937Sdes } 123098937Sdes } 123198937Sdes 123298937Sdes /* We found an entry. Tidy up and return */ 123398937Sdes close(fd); 1234147005Sdes return (1); 123598937Sdes} 123698937Sdes# endif /* USE_WTMP */ 123798937Sdes 123898937Sdes 123998937Sdes/** 124098937Sdes ** Low-level wtmpx functions 124198937Sdes **/ 124298937Sdes 124398937Sdes#ifdef USE_WTMPX 1244147005Sdes/* 1245147005Sdes * Write a wtmpx entry direct to the end of the file 1246147005Sdes * This is a slight modification of code in OpenBSD's logwtmp.c 1247147005Sdes */ 124898937Sdesstatic int 124998937Sdeswtmpx_write(struct logininfo *li, struct utmpx *utx) 125098937Sdes{ 1251126277Sdes#ifndef HAVE_UPDWTMPX 125298937Sdes struct stat buf; 125398937Sdes int fd, ret = 1; 125498937Sdes 125598937Sdes if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 1256149753Sdes logit("%s: problem opening %s: %s", __func__, 125798937Sdes WTMPX_FILE, strerror(errno)); 1258147005Sdes return (0); 125998937Sdes } 126098937Sdes 126198937Sdes if (fstat(fd, &buf) == 0) 1262124211Sdes if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) { 126398937Sdes ftruncate(fd, buf.st_size); 1264147005Sdes logit("%s: problem writing %s: %s", __func__, 126598937Sdes WTMPX_FILE, strerror(errno)); 126698937Sdes ret = 0; 126798937Sdes } 1268147005Sdes close(fd); 126998937Sdes 1270147005Sdes return (ret); 1271126277Sdes#else 1272126277Sdes updwtmpx(WTMPX_FILE, utx); 1273147005Sdes return (1); 1274126277Sdes#endif 127598937Sdes} 127698937Sdes 127798937Sdes 127898937Sdesstatic int 127998937Sdeswtmpx_perform_login(struct logininfo *li) 128098937Sdes{ 128198937Sdes struct utmpx utx; 128298937Sdes 128398937Sdes construct_utmpx(li, &utx); 1284147005Sdes return (wtmpx_write(li, &utx)); 128598937Sdes} 128698937Sdes 128798937Sdes 128898937Sdesstatic int 128998937Sdeswtmpx_perform_logout(struct logininfo *li) 129098937Sdes{ 129198937Sdes struct utmpx utx; 129298937Sdes 129398937Sdes construct_utmpx(li, &utx); 1294147005Sdes return (wtmpx_write(li, &utx)); 129598937Sdes} 129698937Sdes 129798937Sdes 129898937Sdesint 129998937Sdeswtmpx_write_entry(struct logininfo *li) 130098937Sdes{ 130198937Sdes switch(li->type) { 130298937Sdes case LTYPE_LOGIN: 1303147005Sdes return (wtmpx_perform_login(li)); 130498937Sdes case LTYPE_LOGOUT: 1305147005Sdes return (wtmpx_perform_logout(li)); 130698937Sdes default: 1307147005Sdes logit("%s: invalid type field", __func__); 1308147005Sdes return (0); 130998937Sdes } 131098937Sdes} 131198937Sdes 131298937Sdes/* Please see the notes above wtmp_islogin() for information about the 131398937Sdes next two functions */ 131498937Sdes 131598937Sdes/* Return true if this wtmpx entry indicates a login */ 131698937Sdesstatic int 131798937Sdeswtmpx_islogin(struct logininfo *li, struct utmpx *utx) 131898937Sdes{ 1319147005Sdes if (strncmp(li->username, utx->ut_name, 1320147005Sdes MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) { 132198937Sdes# ifdef HAVE_TYPE_IN_UTMPX 132298937Sdes if (utx->ut_type == USER_PROCESS) 1323147005Sdes return (1); 132498937Sdes# else 1325147005Sdes return (1); 132698937Sdes# endif 132798937Sdes } 1328147005Sdes return (0); 132998937Sdes} 133098937Sdes 133198937Sdes 133298937Sdesint 133398937Sdeswtmpx_get_entry(struct logininfo *li) 133498937Sdes{ 133598937Sdes struct stat st; 133698937Sdes struct utmpx utx; 133798937Sdes int fd, found=0; 133898937Sdes 133998937Sdes /* Clear the time entries */ 134098937Sdes li->tv_sec = li->tv_usec = 0; 134198937Sdes 134298937Sdes if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { 1343149753Sdes logit("%s: problem opening %s: %s", __func__, 134498937Sdes WTMPX_FILE, strerror(errno)); 1345147005Sdes return (0); 134698937Sdes } 134798937Sdes if (fstat(fd, &st) != 0) { 1348149753Sdes logit("%s: couldn't stat %s: %s", __func__, 1349106130Sdes WTMPX_FILE, strerror(errno)); 135098937Sdes close(fd); 1351147005Sdes return (0); 135298937Sdes } 135398937Sdes 135498937Sdes /* Seek to the start of the last struct utmpx */ 135598937Sdes if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { 135698937Sdes /* probably a newly rotated wtmpx file */ 135798937Sdes close(fd); 1358147005Sdes return (0); 135998937Sdes } 136098937Sdes 136198937Sdes while (!found) { 136298937Sdes if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { 1363149753Sdes logit("%s: read of %s failed: %s", __func__, 136498937Sdes WTMPX_FILE, strerror(errno)); 136598937Sdes close (fd); 1366147005Sdes return (0); 136798937Sdes } 1368147005Sdes /* 1369149753Sdes * Logouts are recorded as a blank username on a particular 1370147005Sdes * line. So, we just need to find the username in struct utmpx 1371147005Sdes */ 1372147005Sdes if (wtmpx_islogin(li, &utx)) { 1373106130Sdes found = 1; 1374147005Sdes# if defined(HAVE_TV_IN_UTMPX) 137598937Sdes li->tv_sec = utx.ut_tv.tv_sec; 1376147005Sdes# elif defined(HAVE_TIME_IN_UTMPX) 137798937Sdes li->tv_sec = utx.ut_time; 137898937Sdes# endif 137998937Sdes line_fullname(li->line, utx.ut_line, sizeof(li->line)); 1380147005Sdes# if defined(HAVE_HOST_IN_UTMPX) 138198937Sdes strlcpy(li->hostname, utx.ut_host, 1382147005Sdes MIN_SIZEOF(li->hostname, utx.ut_host)); 138398937Sdes# endif 138498937Sdes continue; 138598937Sdes } 138698937Sdes if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { 1387147005Sdes close(fd); 1388147005Sdes return (0); 138998937Sdes } 139098937Sdes } 139198937Sdes 139298937Sdes close(fd); 1393147005Sdes return (1); 139498937Sdes} 139598937Sdes#endif /* USE_WTMPX */ 139698937Sdes 139798937Sdes/** 139898937Sdes ** Low-level libutil login() functions 139998937Sdes **/ 140098937Sdes 140198937Sdes#ifdef USE_LOGIN 140298937Sdesstatic int 140398937Sdessyslogin_perform_login(struct logininfo *li) 140498937Sdes{ 140598937Sdes struct utmp *ut; 140698937Sdes 1407147005Sdes ut = xmalloc(sizeof(*ut)); 140898937Sdes construct_utmp(li, ut); 140998937Sdes login(ut); 1410113911Sdes free(ut); 141198937Sdes 1412147005Sdes return (1); 141398937Sdes} 141498937Sdes 141598937Sdesstatic int 141698937Sdessyslogin_perform_logout(struct logininfo *li) 141798937Sdes{ 141898937Sdes# ifdef HAVE_LOGOUT 1419128460Sdes char line[UT_LINESIZE]; 142098937Sdes 142198937Sdes (void)line_stripname(line, li->line, sizeof(line)); 142298937Sdes 1423147005Sdes if (!logout(line)) 1424147005Sdes logit("%s: logout() returned an error", __func__); 142598937Sdes# ifdef HAVE_LOGWTMP 1426147005Sdes else 142798937Sdes logwtmp(line, "", ""); 142898937Sdes# endif 142998937Sdes /* FIXME: (ATL - if the need arises) What to do if we have 143098937Sdes * login, but no logout? what if logout but no logwtmp? All 143198937Sdes * routines are in libutil so they should all be there, 143298937Sdes * but... */ 143398937Sdes# endif 1434147005Sdes return (1); 143598937Sdes} 143698937Sdes 143798937Sdesint 143898937Sdessyslogin_write_entry(struct logininfo *li) 143998937Sdes{ 144098937Sdes switch (li->type) { 144198937Sdes case LTYPE_LOGIN: 1442147005Sdes return (syslogin_perform_login(li)); 144398937Sdes case LTYPE_LOGOUT: 1444147005Sdes return (syslogin_perform_logout(li)); 144598937Sdes default: 1446147005Sdes logit("%s: Invalid type field", __func__); 1447147005Sdes return (0); 144898937Sdes } 144998937Sdes} 145098937Sdes#endif /* USE_LOGIN */ 145198937Sdes 145298937Sdes/* end of file log-syslogin.c */ 145398937Sdes 145498937Sdes/** 145598937Sdes ** Low-level lastlog functions 145698937Sdes **/ 145798937Sdes 145898937Sdes#ifdef USE_LASTLOG 145998937Sdes#define LL_FILE 1 146098937Sdes#define LL_DIR 2 146198937Sdes#define LL_OTHER 3 146298937Sdes 146398937Sdesstatic void 146498937Sdeslastlog_construct(struct logininfo *li, struct lastlog *last) 146598937Sdes{ 146698937Sdes /* clear the structure */ 146798937Sdes memset(last, '\0', sizeof(*last)); 146898937Sdes 1469147005Sdes line_stripname(last->ll_line, li->line, sizeof(last->ll_line)); 147098937Sdes strlcpy(last->ll_host, li->hostname, 147198937Sdes MIN_SIZEOF(last->ll_host, li->hostname)); 147298937Sdes last->ll_time = li->tv_sec; 147398937Sdes} 147498937Sdes 147598937Sdesstatic int 147698937Sdeslastlog_filetype(char *filename) 147798937Sdes{ 147898937Sdes struct stat st; 147998937Sdes 148098937Sdes if (stat(LASTLOG_FILE, &st) != 0) { 1481147005Sdes logit("%s: Couldn't stat %s: %s", __func__, 1482147005Sdes LASTLOG_FILE, strerror(errno)); 1483147005Sdes return (0); 148498937Sdes } 148598937Sdes if (S_ISDIR(st.st_mode)) 1486147005Sdes return (LL_DIR); 148798937Sdes else if (S_ISREG(st.st_mode)) 1488147005Sdes return (LL_FILE); 148998937Sdes else 1490147005Sdes return (LL_OTHER); 149198937Sdes} 149298937Sdes 149398937Sdes 149498937Sdes/* open the file (using filemode) and seek to the login entry */ 149598937Sdesstatic int 149698937Sdeslastlog_openseek(struct logininfo *li, int *fd, int filemode) 149798937Sdes{ 149898937Sdes off_t offset; 149998937Sdes int type; 150098937Sdes char lastlog_file[1024]; 150198937Sdes 150298937Sdes type = lastlog_filetype(LASTLOG_FILE); 150398937Sdes switch (type) { 1504147005Sdes case LL_FILE: 1505147005Sdes strlcpy(lastlog_file, LASTLOG_FILE, 1506147005Sdes sizeof(lastlog_file)); 1507147005Sdes break; 1508147005Sdes case LL_DIR: 1509147005Sdes snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", 1510147005Sdes LASTLOG_FILE, li->username); 1511147005Sdes break; 1512147005Sdes default: 1513147005Sdes logit("%s: %.100s is not a file or directory!", __func__, 1514147005Sdes LASTLOG_FILE); 1515147005Sdes return (0); 151698937Sdes } 151798937Sdes 1518124211Sdes *fd = open(lastlog_file, filemode, 0600); 1519147005Sdes if (*fd < 0) { 1520147005Sdes debug("%s: Couldn't open %s: %s", __func__, 152198937Sdes lastlog_file, strerror(errno)); 1522147005Sdes return (0); 152398937Sdes } 152498937Sdes 152598937Sdes if (type == LL_FILE) { 152698937Sdes /* find this uid's offset in the lastlog file */ 152798937Sdes offset = (off_t) ((long)li->uid * sizeof(struct lastlog)); 152898937Sdes 1529147005Sdes if (lseek(*fd, offset, SEEK_SET) != offset) { 1530147005Sdes logit("%s: %s->lseek(): %s", __func__, 1531147005Sdes lastlog_file, strerror(errno)); 1532147005Sdes return (0); 153398937Sdes } 153498937Sdes } 153598937Sdes 1536147005Sdes return (1); 153798937Sdes} 153898937Sdes 153998937Sdesstatic int 154098937Sdeslastlog_perform_login(struct logininfo *li) 154198937Sdes{ 154298937Sdes struct lastlog last; 154398937Sdes int fd; 154498937Sdes 154598937Sdes /* create our struct lastlog */ 154698937Sdes lastlog_construct(li, &last); 154798937Sdes 154898937Sdes if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) 1549147005Sdes return (0); 155098937Sdes 155198937Sdes /* write the entry */ 1552124211Sdes if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) { 155398937Sdes close(fd); 1554147005Sdes logit("%s: Error writing to %s: %s", __func__, 155598937Sdes LASTLOG_FILE, strerror(errno)); 1556147005Sdes return (0); 155798937Sdes } 155898937Sdes 155998937Sdes close(fd); 1560147005Sdes return (1); 156198937Sdes} 156298937Sdes 156398937Sdesint 156498937Sdeslastlog_write_entry(struct logininfo *li) 156598937Sdes{ 156698937Sdes switch(li->type) { 156798937Sdes case LTYPE_LOGIN: 1568147005Sdes return (lastlog_perform_login(li)); 156998937Sdes default: 1570147005Sdes logit("%s: Invalid type field", __func__); 1571147005Sdes return (0); 157298937Sdes } 157398937Sdes} 157498937Sdes 157598937Sdesstatic void 157698937Sdeslastlog_populate_entry(struct logininfo *li, struct lastlog *last) 157798937Sdes{ 157898937Sdes line_fullname(li->line, last->ll_line, sizeof(li->line)); 157998937Sdes strlcpy(li->hostname, last->ll_host, 1580147005Sdes MIN_SIZEOF(li->hostname, last->ll_host)); 158198937Sdes li->tv_sec = last->ll_time; 158298937Sdes} 158398937Sdes 158498937Sdesint 158598937Sdeslastlog_get_entry(struct logininfo *li) 158698937Sdes{ 158798937Sdes struct lastlog last; 1588113911Sdes int fd, ret; 158998937Sdes 159098937Sdes if (!lastlog_openseek(li, &fd, O_RDONLY)) 1591113911Sdes return (0); 159298937Sdes 1593113911Sdes ret = atomicio(read, fd, &last, sizeof(last)); 1594113911Sdes close(fd); 1595113911Sdes 1596113911Sdes switch (ret) { 1597113911Sdes case 0: 1598113911Sdes memset(&last, '\0', sizeof(last)); 1599113911Sdes /* FALLTHRU */ 1600113911Sdes case sizeof(last): 1601113911Sdes lastlog_populate_entry(li, &last); 1602113911Sdes return (1); 1603113911Sdes case -1: 1604126277Sdes error("%s: Error reading from %s: %s", __func__, 160598937Sdes LASTLOG_FILE, strerror(errno)); 1606113911Sdes return (0); 1607113911Sdes default: 1608113911Sdes error("%s: Error reading from %s: Expecting %d, got %d", 1609157019Sdes __func__, LASTLOG_FILE, (int)sizeof(last), ret); 1610113911Sdes return (0); 161198937Sdes } 161298937Sdes 1613113911Sdes /* NOTREACHED */ 1614113911Sdes return (0); 161598937Sdes} 161698937Sdes#endif /* USE_LASTLOG */ 1617147005Sdes 1618147005Sdes#ifdef USE_BTMP 1619147005Sdes /* 1620147005Sdes * Logs failed login attempts in _PATH_BTMP if that exists. 1621147005Sdes * The most common login failure is to give password instead of username. 1622147005Sdes * So the _PATH_BTMP file checked for the correct permission, so that 1623147005Sdes * only root can read it. 1624147005Sdes */ 1625147005Sdes 1626147005Sdesvoid 1627147005Sdesrecord_failed_login(const char *username, const char *hostname, 1628147005Sdes const char *ttyn) 1629147005Sdes{ 1630147005Sdes int fd; 1631147005Sdes struct utmp ut; 1632147005Sdes struct sockaddr_storage from; 1633157019Sdes socklen_t fromlen = sizeof(from); 1634147005Sdes struct sockaddr_in *a4; 1635147005Sdes struct sockaddr_in6 *a6; 1636147005Sdes time_t t; 1637147005Sdes struct stat fst; 1638147005Sdes 1639147005Sdes if (geteuid() != 0) 1640147005Sdes return; 1641147005Sdes if ((fd = open(_PATH_BTMP, O_WRONLY | O_APPEND)) < 0) { 1642147005Sdes debug("Unable to open the btmp file %s: %s", _PATH_BTMP, 1643147005Sdes strerror(errno)); 1644147005Sdes return; 1645147005Sdes } 1646147005Sdes if (fstat(fd, &fst) < 0) { 1647147005Sdes logit("%s: fstat of %s failed: %s", __func__, _PATH_BTMP, 1648147005Sdes strerror(errno)); 1649147005Sdes goto out; 1650147005Sdes } 1651147005Sdes if((fst.st_mode & (S_IRWXG | S_IRWXO)) || (fst.st_uid != 0)){ 1652147005Sdes logit("Excess permission or bad ownership on file %s", 1653147005Sdes _PATH_BTMP); 1654147005Sdes goto out; 1655147005Sdes } 1656147005Sdes 1657147005Sdes memset(&ut, 0, sizeof(ut)); 1658147005Sdes /* strncpy because we don't necessarily want nul termination */ 1659147005Sdes strncpy(ut.ut_user, username, sizeof(ut.ut_user)); 1660147005Sdes strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line)); 1661147005Sdes 1662147005Sdes time(&t); 1663147005Sdes ut.ut_time = t; /* ut_time is not always a time_t */ 1664147005Sdes ut.ut_type = LOGIN_PROCESS; 1665147005Sdes ut.ut_pid = getpid(); 1666147005Sdes 1667147005Sdes /* strncpy because we don't necessarily want nul termination */ 1668147005Sdes strncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); 1669147005Sdes 1670147005Sdes if (packet_connection_is_on_socket() && 1671147005Sdes getpeername(packet_get_connection_in(), 1672147005Sdes (struct sockaddr *)&from, &fromlen) == 0) { 1673147005Sdes ipv64_normalise_mapped(&from, &fromlen); 1674147005Sdes if (from.ss_family == AF_INET) { 1675147005Sdes a4 = (struct sockaddr_in *)&from; 1676147005Sdes memcpy(&ut.ut_addr, &(a4->sin_addr), 1677147005Sdes MIN_SIZEOF(ut.ut_addr, a4->sin_addr)); 1678147005Sdes } 1679147005Sdes#ifdef HAVE_ADDR_V6_IN_UTMP 1680147005Sdes if (from.ss_family == AF_INET6) { 1681147005Sdes a6 = (struct sockaddr_in6 *)&from; 1682147005Sdes memcpy(&ut.ut_addr_v6, &(a6->sin6_addr), 1683147005Sdes MIN_SIZEOF(ut.ut_addr_v6, a6->sin6_addr)); 1684147005Sdes } 1685147005Sdes#endif 1686147005Sdes } 1687147005Sdes 1688147005Sdes if (atomicio(vwrite, fd, &ut, sizeof(ut)) != sizeof(ut)) 1689147005Sdes error("Failed to write to %s: %s", _PATH_BTMP, 1690147005Sdes strerror(errno)); 1691147005Sdes 1692147005Sdesout: 1693147005Sdes close(fd); 1694147005Sdes} 1695147005Sdes#endif /* USE_BTMP */ 1696