loginrec.c revision 137019
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 2898937Sdes/** 2998937Sdes ** loginrec.c: platform-independent login recording and lastlog retrieval 3098937Sdes **/ 3198937Sdes 3298937Sdes/* 3398937Sdes The new login code explained 3498937Sdes ============================ 3598937Sdes 3698937Sdes This code attempts to provide a common interface to login recording 3798937Sdes (utmp and friends) and last login time retrieval. 3898937Sdes 3998937Sdes Its primary means of achieving this is to use 'struct logininfo', a 4098937Sdes union of all the useful fields in the various different types of 4198937Sdes system login record structures one finds on UNIX variants. 4298937Sdes 4398937Sdes We depend on autoconf to define which recording methods are to be 4498937Sdes used, and which fields are contained in the relevant data structures 4598937Sdes on the local system. Many C preprocessor symbols affect which code 4698937Sdes gets compiled here. 4798937Sdes 4898937Sdes The code is designed to make it easy to modify a particular 4998937Sdes recording method, without affecting other methods nor requiring so 5098937Sdes many nested conditional compilation blocks as were commonplace in 5198937Sdes the old code. 5298937Sdes 5398937Sdes For login recording, we try to use the local system's libraries as 5498937Sdes these are clearly most likely to work correctly. For utmp systems 5598937Sdes this usually means login() and logout() or setutent() etc., probably 5698937Sdes in libutil, along with logwtmp() etc. On these systems, we fall back 5798937Sdes to writing the files directly if we have to, though this method 5898937Sdes requires very thorough testing so we do not corrupt local auditing 5998937Sdes information. These files and their access methods are very system 6098937Sdes specific indeed. 6198937Sdes 6298937Sdes For utmpx systems, the corresponding library functions are 6398937Sdes setutxent() etc. To the author's knowledge, all utmpx systems have 6498937Sdes these library functions and so no direct write is attempted. If such 6598937Sdes a system exists and needs support, direct analogues of the [uw]tmp 6698937Sdes code should suffice. 6798937Sdes 6898937Sdes Retrieving the time of last login ('lastlog') is in some ways even 6998937Sdes more problemmatic than login recording. Some systems provide a 7098937Sdes simple table of all users which we seek based on uid and retrieve a 7198937Sdes relatively standard structure. Others record the same information in 7298937Sdes a directory with a separate file, and others don't record the 7398937Sdes information separately at all. For systems in the latter category, 7498937Sdes we look backwards in the wtmp or wtmpx file for the last login entry 7598937Sdes for our user. Naturally this is slower and on busy systems could 7698937Sdes incur a significant performance penalty. 7798937Sdes 7898937Sdes Calling the new code 7998937Sdes -------------------- 8098937Sdes 8198937Sdes In OpenSSH all login recording and retrieval is performed in 8298937Sdes login.c. Here you'll find working examples. Also, in the logintest.c 8398937Sdes program there are more examples. 8498937Sdes 8598937Sdes Internal handler calling method 8698937Sdes ------------------------------- 8798937Sdes 8898937Sdes When a call is made to login_login() or login_logout(), both 8998937Sdes routines set a struct logininfo flag defining which action (log in, 9098937Sdes or log out) is to be taken. They both then call login_write(), which 9198937Sdes calls whichever of the many structure-specific handlers autoconf 9298937Sdes selects for the local system. 9398937Sdes 9498937Sdes The handlers themselves handle system data structure specifics. Both 9598937Sdes struct utmp and struct utmpx have utility functions (see 9698937Sdes construct_utmp*()) to try to make it simpler to add extra systems 9798937Sdes that introduce new features to either structure. 9898937Sdes 9998937Sdes While it may seem terribly wasteful to replicate so much similar 10098937Sdes code for each method, experience has shown that maintaining code to 10198937Sdes write both struct utmp and utmpx in one function, whilst maintaining 10298937Sdes support for all systems whether they have library support or not, is 10398937Sdes a difficult and time-consuming task. 10498937Sdes 10598937Sdes Lastlog support proceeds similarly. Functions login_get_lastlog() 10698937Sdes (and its OpenSSH-tuned friend login_get_lastlog_time()) call 10798937Sdes getlast_entry(), which tries one of three methods to find the last 10898937Sdes login time. It uses local system lastlog support if it can, 10998937Sdes otherwise it tries wtmp or wtmpx before giving up and returning 0, 11098937Sdes meaning "tilt". 11198937Sdes 11298937Sdes Maintenance 11398937Sdes ----------- 11498937Sdes 11598937Sdes In many cases it's possible to tweak autoconf to select the correct 11698937Sdes methods for a particular platform, either by improving the detection 11798937Sdes code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE 11898937Sdes symbols for the platform. 11998937Sdes 12098937Sdes Use logintest to check which symbols are defined before modifying 12198937Sdes configure.ac and loginrec.c. (You have to build logintest yourself 12298937Sdes with 'make logintest' as it's not built by default.) 12398937Sdes 12498937Sdes Otherwise, patches to the specific method(s) are very helpful! 12598937Sdes 12698937Sdes*/ 12798937Sdes 12898937Sdes/** 12998937Sdes ** TODO: 13098937Sdes ** homegrown ttyslot() 13198937Sdes ** test, test, test 13298937Sdes ** 13398937Sdes ** Platform status: 13498937Sdes ** ---------------- 13598937Sdes ** 13698937Sdes ** Known good: 13798937Sdes ** Linux (Redhat 6.2, Debian) 13898937Sdes ** Solaris 13998937Sdes ** HP-UX 10.20 (gcc only) 14098937Sdes ** IRIX 14198937Sdes ** NeXT - M68k/HPPA/Sparc (4.2/3.3) 14298937Sdes ** 14398937Sdes ** Testing required: Please send reports! 14498937Sdes ** NetBSD 14598937Sdes ** HP-UX 11 14698937Sdes ** AIX 14798937Sdes ** 14898937Sdes ** Platforms with known problems: 14998937Sdes ** Some variants of Slackware Linux 15098937Sdes ** 15198937Sdes **/ 15298937Sdes 15398937Sdes#include "includes.h" 15498937Sdes 15598937Sdes#include "ssh.h" 15698937Sdes#include "xmalloc.h" 15798937Sdes#include "loginrec.h" 15898937Sdes#include "log.h" 15998937Sdes#include "atomicio.h" 16098937Sdes 161137019SdesRCSID("$Id: loginrec.c,v 1.58 2004/08/15 09:12:52 djm Exp $"); 162128460SdesRCSID("$FreeBSD: head/crypto/openssh/loginrec.c 137019 2004-10-28 16:11:31Z des $"); 16398937Sdes 16498937Sdes#ifdef HAVE_UTIL_H 16598937Sdes# include <util.h> 16698937Sdes#endif 16798937Sdes 16898937Sdes#ifdef HAVE_LIBUTIL_H 16998937Sdes# include <libutil.h> 17098937Sdes#endif 17198937Sdes 17298937Sdes/** 17398937Sdes ** prototypes for helper functions in this file 17498937Sdes **/ 17598937Sdes 17698937Sdes#if HAVE_UTMP_H 17798937Sdesvoid set_utmp_time(struct logininfo *li, struct utmp *ut); 17898937Sdesvoid construct_utmp(struct logininfo *li, struct utmp *ut); 17998937Sdes#endif 18098937Sdes 18198937Sdes#ifdef HAVE_UTMPX_H 18298937Sdesvoid set_utmpx_time(struct logininfo *li, struct utmpx *ut); 18398937Sdesvoid construct_utmpx(struct logininfo *li, struct utmpx *ut); 18498937Sdes#endif 18598937Sdes 18698937Sdesint utmp_write_entry(struct logininfo *li); 18798937Sdesint utmpx_write_entry(struct logininfo *li); 18898937Sdesint wtmp_write_entry(struct logininfo *li); 18998937Sdesint wtmpx_write_entry(struct logininfo *li); 19098937Sdesint lastlog_write_entry(struct logininfo *li); 19198937Sdesint syslogin_write_entry(struct logininfo *li); 19298937Sdes 19398937Sdesint getlast_entry(struct logininfo *li); 19498937Sdesint lastlog_get_entry(struct logininfo *li); 19598937Sdesint wtmp_get_entry(struct logininfo *li); 19698937Sdesint wtmpx_get_entry(struct logininfo *li); 19798937Sdes 19898937Sdes/* pick the shortest string */ 19998937Sdes#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) ) 20098937Sdes 20198937Sdes/** 20298937Sdes ** platform-independent login functions 20398937Sdes **/ 20498937Sdes 20598937Sdes/* login_login(struct logininfo *) -Record a login 20698937Sdes * 20798937Sdes * Call with a pointer to a struct logininfo initialised with 20898937Sdes * login_init_entry() or login_alloc_entry() 20998937Sdes * 21098937Sdes * Returns: 21198937Sdes * >0 if successful 21298937Sdes * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 21398937Sdes */ 21498937Sdesint 21598937Sdeslogin_login (struct logininfo *li) 21698937Sdes{ 21798937Sdes li->type = LTYPE_LOGIN; 21898937Sdes return login_write(li); 21998937Sdes} 22098937Sdes 22198937Sdes 22298937Sdes/* login_logout(struct logininfo *) - Record a logout 22398937Sdes * 22498937Sdes * Call as with login_login() 22598937Sdes * 22698937Sdes * Returns: 22798937Sdes * >0 if successful 22898937Sdes * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 22998937Sdes */ 23098937Sdesint 23198937Sdeslogin_logout(struct logininfo *li) 23298937Sdes{ 23398937Sdes li->type = LTYPE_LOGOUT; 23498937Sdes return login_write(li); 23598937Sdes} 23698937Sdes 23798937Sdes/* login_get_lastlog_time(int) - Retrieve the last login time 23898937Sdes * 23998937Sdes * Retrieve the last login time for the given uid. Will try to use the 24098937Sdes * system lastlog facilities if they are available, but will fall back 24198937Sdes * to looking in wtmp/wtmpx if necessary 24298937Sdes * 24398937Sdes * Returns: 24498937Sdes * 0 on failure, or if user has never logged in 24598937Sdes * Time in seconds from the epoch if successful 24698937Sdes * 24798937Sdes * Useful preprocessor symbols: 24898937Sdes * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog 24998937Sdes * info 25098937Sdes * USE_LASTLOG: If set, indicates the presence of system lastlog 25198937Sdes * facilities. If this and DISABLE_LASTLOG are not set, 25298937Sdes * try to retrieve lastlog information from wtmp/wtmpx. 25398937Sdes */ 25498937Sdesunsigned int 25598937Sdeslogin_get_lastlog_time(const int uid) 25698937Sdes{ 25798937Sdes struct logininfo li; 25898937Sdes 25998937Sdes if (login_get_lastlog(&li, uid)) 26098937Sdes return li.tv_sec; 26198937Sdes else 26298937Sdes return 0; 26398937Sdes} 26498937Sdes 26598937Sdes/* login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry 26698937Sdes * 26798937Sdes * Retrieve a logininfo structure populated (only partially) with 26898937Sdes * information from the system lastlog data, or from wtmp/wtmpx if no 26998937Sdes * system lastlog information exists. 27098937Sdes * 27198937Sdes * Note this routine must be given a pre-allocated logininfo. 27298937Sdes * 27398937Sdes * Returns: 27498937Sdes * >0: A pointer to your struct logininfo if successful 27598937Sdes * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 27698937Sdes * 27798937Sdes */ 27898937Sdesstruct logininfo * 27998937Sdeslogin_get_lastlog(struct logininfo *li, const int uid) 28098937Sdes{ 28198937Sdes struct passwd *pw; 28298937Sdes 28398937Sdes memset(li, '\0', sizeof(*li)); 28498937Sdes li->uid = uid; 28598937Sdes 28698937Sdes /* 28798937Sdes * If we don't have a 'real' lastlog, we need the username to 28898937Sdes * reliably search wtmp(x) for the last login (see 28998937Sdes * wtmp_get_entry().) 29098937Sdes */ 29198937Sdes pw = getpwuid(uid); 29298937Sdes if (pw == NULL) 29398937Sdes fatal("login_get_lastlog: Cannot find account for uid %i", uid); 29498937Sdes 29598937Sdes /* No MIN_SIZEOF here - we absolutely *must not* truncate the 29698937Sdes * username */ 29798937Sdes strlcpy(li->username, pw->pw_name, sizeof(li->username)); 29898937Sdes 29998937Sdes if (getlast_entry(li)) 30098937Sdes return li; 30198937Sdes else 30298937Sdes return NULL; 30398937Sdes} 30498937Sdes 30598937Sdes 30698937Sdes/* login_alloc_entry(int, char*, char*, char*) - Allocate and initialise 30798937Sdes * a logininfo structure 30898937Sdes * 30998937Sdes * This function creates a new struct logininfo, a data structure 31098937Sdes * meant to carry the information required to portably record login info. 31198937Sdes * 31298937Sdes * Returns a pointer to a newly created struct logininfo. If memory 31398937Sdes * allocation fails, the program halts. 31498937Sdes */ 31598937Sdesstruct 31698937Sdeslogininfo *login_alloc_entry(int pid, const char *username, 31798937Sdes const char *hostname, const char *line) 31898937Sdes{ 31998937Sdes struct logininfo *newli; 32098937Sdes 32198937Sdes newli = (struct logininfo *) xmalloc (sizeof(*newli)); 32298937Sdes (void)login_init_entry(newli, pid, username, hostname, line); 32398937Sdes return newli; 32498937Sdes} 32598937Sdes 32698937Sdes 32798937Sdes/* login_free_entry(struct logininfo *) - free struct memory */ 32898937Sdesvoid 32998937Sdeslogin_free_entry(struct logininfo *li) 33098937Sdes{ 33198937Sdes xfree(li); 33298937Sdes} 33398937Sdes 33498937Sdes 33598937Sdes/* login_init_entry(struct logininfo *, int, char*, char*, char*) 33698937Sdes * - initialise a struct logininfo 33798937Sdes * 33898937Sdes * Populates a new struct logininfo, a data structure meant to carry 33998937Sdes * the information required to portably record login info. 34098937Sdes * 34198937Sdes * Returns: 1 34298937Sdes */ 34398937Sdesint 34498937Sdeslogin_init_entry(struct logininfo *li, int pid, const char *username, 34598937Sdes const char *hostname, const char *line) 34698937Sdes{ 34798937Sdes struct passwd *pw; 34898937Sdes 34998937Sdes memset(li, 0, sizeof(*li)); 35098937Sdes 35198937Sdes li->pid = pid; 35298937Sdes 35398937Sdes /* set the line information */ 35498937Sdes if (line) 35598937Sdes line_fullname(li->line, line, sizeof(li->line)); 35698937Sdes 35798937Sdes if (username) { 35898937Sdes strlcpy(li->username, username, sizeof(li->username)); 35998937Sdes pw = getpwnam(li->username); 36098937Sdes if (pw == NULL) 36198937Sdes fatal("login_init_entry: Cannot find user \"%s\"", li->username); 36298937Sdes li->uid = pw->pw_uid; 36398937Sdes } 36498937Sdes 36598937Sdes if (hostname) 36698937Sdes strlcpy(li->hostname, hostname, sizeof(li->hostname)); 36798937Sdes 36898937Sdes return 1; 36998937Sdes} 37098937Sdes 37198937Sdes/* login_set_current_time(struct logininfo *) - set the current time 37298937Sdes * 37398937Sdes * Set the current time in a logininfo structure. This function is 37498937Sdes * meant to eliminate the need to deal with system dependencies for 37598937Sdes * time handling. 37698937Sdes */ 37798937Sdesvoid 37898937Sdeslogin_set_current_time(struct logininfo *li) 37998937Sdes{ 38098937Sdes struct timeval tv; 38198937Sdes 38298937Sdes gettimeofday(&tv, NULL); 38398937Sdes 38498937Sdes li->tv_sec = tv.tv_sec; 38598937Sdes li->tv_usec = tv.tv_usec; 38698937Sdes} 38798937Sdes 38898937Sdes/* copy a sockaddr_* into our logininfo */ 38998937Sdesvoid 39098937Sdeslogin_set_addr(struct logininfo *li, const struct sockaddr *sa, 39198937Sdes const unsigned int sa_size) 39298937Sdes{ 39398937Sdes unsigned int bufsize = sa_size; 39498937Sdes 39598937Sdes /* make sure we don't overrun our union */ 39698937Sdes if (sizeof(li->hostaddr) < sa_size) 39798937Sdes bufsize = sizeof(li->hostaddr); 39898937Sdes 39998937Sdes memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize); 40098937Sdes} 40198937Sdes 40298937Sdes 40398937Sdes/** 40498937Sdes ** login_write: Call low-level recording functions based on autoconf 40598937Sdes ** results 40698937Sdes **/ 40798937Sdesint 40898937Sdeslogin_write (struct logininfo *li) 40998937Sdes{ 41098937Sdes#ifndef HAVE_CYGWIN 41198937Sdes if ((int)geteuid() != 0) { 412124211Sdes logit("Attempt to write login records by non-root user (aborting)"); 41398937Sdes return 1; 41498937Sdes } 41598937Sdes#endif 41698937Sdes 41798937Sdes /* set the timestamp */ 41898937Sdes login_set_current_time(li); 41998937Sdes#ifdef USE_LOGIN 42098937Sdes syslogin_write_entry(li); 42198937Sdes#endif 42298937Sdes#ifdef USE_LASTLOG 42398937Sdes if (li->type == LTYPE_LOGIN) { 42498937Sdes lastlog_write_entry(li); 42598937Sdes } 42698937Sdes#endif 42798937Sdes#ifdef USE_UTMP 42898937Sdes utmp_write_entry(li); 42998937Sdes#endif 43098937Sdes#ifdef USE_WTMP 43198937Sdes wtmp_write_entry(li); 43298937Sdes#endif 43398937Sdes#ifdef USE_UTMPX 43498937Sdes utmpx_write_entry(li); 43598937Sdes#endif 43698937Sdes#ifdef USE_WTMPX 43798937Sdes wtmpx_write_entry(li); 43898937Sdes#endif 439137019Sdes#ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN 440137019Sdes if (li->type == LTYPE_LOGIN && 441137019Sdes !sys_auth_record_login(li->username,li->hostname,li->line)) 442137019Sdes logit("Writing login record failed for %s", li->username); 443137019Sdes#endif 44498937Sdes return 0; 44598937Sdes} 44698937Sdes 44798937Sdes#ifdef LOGIN_NEEDS_UTMPX 44898937Sdesint 44998937Sdeslogin_utmp_only(struct logininfo *li) 45098937Sdes{ 451126277Sdes li->type = LTYPE_LOGIN; 45298937Sdes login_set_current_time(li); 45398937Sdes# ifdef USE_UTMP 45498937Sdes utmp_write_entry(li); 45598937Sdes# endif 45698937Sdes# ifdef USE_WTMP 45798937Sdes wtmp_write_entry(li); 45898937Sdes# endif 45998937Sdes# ifdef USE_UTMPX 46098937Sdes utmpx_write_entry(li); 46198937Sdes# endif 46298937Sdes# ifdef USE_WTMPX 46398937Sdes wtmpx_write_entry(li); 46498937Sdes# endif 46598937Sdes return 0; 46698937Sdes} 46798937Sdes#endif 46898937Sdes 46998937Sdes/** 47098937Sdes ** getlast_entry: Call low-level functions to retrieve the last login 47198937Sdes ** time. 47298937Sdes **/ 47398937Sdes 47498937Sdes/* take the uid in li and return the last login time */ 47598937Sdesint 47698937Sdesgetlast_entry(struct logininfo *li) 47798937Sdes{ 47898937Sdes#ifdef USE_LASTLOG 47998937Sdes return(lastlog_get_entry(li)); 48098937Sdes#else /* !USE_LASTLOG */ 48198937Sdes 48298937Sdes#ifdef DISABLE_LASTLOG 48398937Sdes /* On some systems we shouldn't even try to obtain last login 48498937Sdes * time, e.g. AIX */ 48598937Sdes return 0; 48698937Sdes# else /* DISABLE_LASTLOG */ 48798937Sdes /* Try to retrieve the last login time from wtmp */ 48898937Sdes# if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) 48998937Sdes /* retrieve last login time from utmp */ 49098937Sdes return (wtmp_get_entry(li)); 49198937Sdes# else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */ 49298937Sdes /* If wtmp isn't available, try wtmpx */ 49398937Sdes# if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX)) 49498937Sdes /* retrieve last login time from utmpx */ 49598937Sdes return (wtmpx_get_entry(li)); 49698937Sdes# else 49798937Sdes /* Give up: No means of retrieving last login time */ 49898937Sdes return 0; 49998937Sdes# endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */ 50098937Sdes# endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */ 50198937Sdes# endif /* DISABLE_LASTLOG */ 50298937Sdes#endif /* USE_LASTLOG */ 50398937Sdes} 50498937Sdes 50598937Sdes 50698937Sdes 50798937Sdes/* 50898937Sdes * 'line' string utility functions 50998937Sdes * 51098937Sdes * These functions process the 'line' string into one of three forms: 51198937Sdes * 51298937Sdes * 1. The full filename (including '/dev') 51398937Sdes * 2. The stripped name (excluding '/dev') 51498937Sdes * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00 51598937Sdes * /dev/pts/1 -> ts/1 ) 51698937Sdes * 51798937Sdes * Form 3 is used on some systems to identify a .tmp.? entry when 51898937Sdes * attempting to remove it. Typically both addition and removal is 51998937Sdes * performed by one application - say, sshd - so as long as the choice 52098937Sdes * uniquely identifies a terminal it's ok. 52198937Sdes */ 52298937Sdes 52398937Sdes 52498937Sdes/* line_fullname(): add the leading '/dev/' if it doesn't exist make 52598937Sdes * sure dst has enough space, if not just copy src (ugh) */ 52698937Sdeschar * 52798937Sdesline_fullname(char *dst, const char *src, int dstsize) 52898937Sdes{ 52998937Sdes memset(dst, '\0', dstsize); 53098937Sdes if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) { 53198937Sdes strlcpy(dst, src, dstsize); 53298937Sdes } else { 53398937Sdes strlcpy(dst, "/dev/", dstsize); 53498937Sdes strlcat(dst, src, dstsize); 53598937Sdes } 53698937Sdes return dst; 53798937Sdes} 53898937Sdes 53998937Sdes/* line_stripname(): strip the leading '/dev' if it exists, return dst */ 54098937Sdeschar * 54198937Sdesline_stripname(char *dst, const char *src, int dstsize) 54298937Sdes{ 54398937Sdes memset(dst, '\0', dstsize); 54498937Sdes if (strncmp(src, "/dev/", 5) == 0) 54598937Sdes strlcpy(dst, src + 5, dstsize); 54698937Sdes else 54798937Sdes strlcpy(dst, src, dstsize); 54898937Sdes return dst; 54998937Sdes} 55098937Sdes 55198937Sdes/* line_abbrevname(): Return the abbreviated (usually four-character) 55298937Sdes * form of the line (Just use the last <dstsize> characters of the 55398937Sdes * full name.) 55498937Sdes * 55598937Sdes * NOTE: use strncpy because we do NOT necessarily want zero 55698937Sdes * termination */ 55798937Sdeschar * 55898937Sdesline_abbrevname(char *dst, const char *src, int dstsize) 55998937Sdes{ 56098937Sdes size_t len; 56198937Sdes 56298937Sdes memset(dst, '\0', dstsize); 56398937Sdes 56498937Sdes /* Always skip prefix if present */ 56598937Sdes if (strncmp(src, "/dev/", 5) == 0) 56698937Sdes src += 5; 56798937Sdes 56898937Sdes#ifdef WITH_ABBREV_NO_TTY 56998937Sdes if (strncmp(src, "tty", 3) == 0) 57098937Sdes src += 3; 57198937Sdes#endif 57298937Sdes 57398937Sdes len = strlen(src); 57498937Sdes 57598937Sdes if (len > 0) { 57698937Sdes if (((int)len - dstsize) > 0) 57798937Sdes src += ((int)len - dstsize); 57898937Sdes 57998937Sdes /* note: _don't_ change this to strlcpy */ 58098937Sdes strncpy(dst, src, (size_t)dstsize); 58198937Sdes } 58298937Sdes 58398937Sdes return dst; 58498937Sdes} 58598937Sdes 58698937Sdes/** 58798937Sdes ** utmp utility functions 58898937Sdes ** 58998937Sdes ** These functions manipulate struct utmp, taking system differences 59098937Sdes ** into account. 59198937Sdes **/ 59298937Sdes 59398937Sdes#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN) 59498937Sdes 59598937Sdes/* build the utmp structure */ 59698937Sdesvoid 59798937Sdesset_utmp_time(struct logininfo *li, struct utmp *ut) 59898937Sdes{ 59998937Sdes# ifdef HAVE_TV_IN_UTMP 60098937Sdes ut->ut_tv.tv_sec = li->tv_sec; 60198937Sdes ut->ut_tv.tv_usec = li->tv_usec; 60298937Sdes# else 60398937Sdes# ifdef HAVE_TIME_IN_UTMP 60498937Sdes ut->ut_time = li->tv_sec; 60598937Sdes# endif 60698937Sdes# endif 60798937Sdes} 60898937Sdes 60998937Sdesvoid 61098937Sdesconstruct_utmp(struct logininfo *li, 61198937Sdes struct utmp *ut) 61298937Sdes{ 613113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 614113911Sdes struct sockaddr_in6 *sa6; 615113911Sdes# endif 61698937Sdes memset(ut, '\0', sizeof(*ut)); 61798937Sdes 61898937Sdes /* First fill out fields used for both logins and logouts */ 61998937Sdes 62098937Sdes# ifdef HAVE_ID_IN_UTMP 62198937Sdes line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id)); 62298937Sdes# endif 62398937Sdes 62498937Sdes# ifdef HAVE_TYPE_IN_UTMP 62598937Sdes /* This is done here to keep utmp constants out of struct logininfo */ 62698937Sdes switch (li->type) { 62798937Sdes case LTYPE_LOGIN: 62898937Sdes ut->ut_type = USER_PROCESS; 629106130Sdes#ifdef _UNICOS 63098937Sdes cray_set_tmpdir(ut); 63198937Sdes#endif 63298937Sdes break; 63398937Sdes case LTYPE_LOGOUT: 63498937Sdes ut->ut_type = DEAD_PROCESS; 635106130Sdes#ifdef _UNICOS 63698937Sdes cray_retain_utmp(ut, li->pid); 63798937Sdes#endif 63898937Sdes break; 63998937Sdes } 64098937Sdes# endif 64198937Sdes set_utmp_time(li, ut); 64298937Sdes 64398937Sdes line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line)); 64498937Sdes 64598937Sdes# ifdef HAVE_PID_IN_UTMP 64698937Sdes ut->ut_pid = li->pid; 64798937Sdes# endif 64898937Sdes 64998937Sdes /* If we're logging out, leave all other fields blank */ 65098937Sdes if (li->type == LTYPE_LOGOUT) 65198937Sdes return; 65298937Sdes 65398937Sdes /* 65498937Sdes * These fields are only used when logging in, and are blank 65598937Sdes * for logouts. 65698937Sdes */ 65798937Sdes 65898937Sdes /* Use strncpy because we don't necessarily want null termination */ 65998937Sdes strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username)); 66098937Sdes# ifdef HAVE_HOST_IN_UTMP 66199768Sdes realhostname_sa(ut->ut_host, sizeof ut->ut_host, 66299768Sdes &li->hostaddr.sa, li->hostaddr.sa.sa_len); 66398937Sdes# endif 66498937Sdes# ifdef HAVE_ADDR_IN_UTMP 66598937Sdes /* this is just a 32-bit IP address */ 66698937Sdes if (li->hostaddr.sa.sa_family == AF_INET) 66798937Sdes ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 66898937Sdes# endif 669113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 670113911Sdes /* this is just a 128-bit IPv6 address */ 671113911Sdes if (li->hostaddr.sa.sa_family == AF_INET6) { 672113911Sdes sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 673113911Sdes memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 674113911Sdes if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 675113911Sdes ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; 676113911Sdes ut->ut_addr_v6[1] = 0; 677113911Sdes ut->ut_addr_v6[2] = 0; 678113911Sdes ut->ut_addr_v6[3] = 0; 679113911Sdes } 680113911Sdes } 681113911Sdes# endif 68298937Sdes} 68398937Sdes#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */ 68498937Sdes 68598937Sdes/** 68698937Sdes ** utmpx utility functions 68798937Sdes ** 68898937Sdes ** These functions manipulate struct utmpx, accounting for system 68998937Sdes ** variations. 69098937Sdes **/ 69198937Sdes 69298937Sdes#if defined(USE_UTMPX) || defined (USE_WTMPX) 69398937Sdes/* build the utmpx structure */ 69498937Sdesvoid 69598937Sdesset_utmpx_time(struct logininfo *li, struct utmpx *utx) 69698937Sdes{ 69798937Sdes# ifdef HAVE_TV_IN_UTMPX 69898937Sdes utx->ut_tv.tv_sec = li->tv_sec; 69998937Sdes utx->ut_tv.tv_usec = li->tv_usec; 70098937Sdes# else /* HAVE_TV_IN_UTMPX */ 70198937Sdes# ifdef HAVE_TIME_IN_UTMPX 70298937Sdes utx->ut_time = li->tv_sec; 70398937Sdes# endif /* HAVE_TIME_IN_UTMPX */ 70498937Sdes# endif /* HAVE_TV_IN_UTMPX */ 70598937Sdes} 70698937Sdes 70798937Sdesvoid 70898937Sdesconstruct_utmpx(struct logininfo *li, struct utmpx *utx) 70998937Sdes{ 710113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 711113911Sdes struct sockaddr_in6 *sa6; 712113911Sdes# endif 71398937Sdes memset(utx, '\0', sizeof(*utx)); 71498937Sdes# ifdef HAVE_ID_IN_UTMPX 71598937Sdes line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); 71698937Sdes# endif 71798937Sdes 71898937Sdes /* this is done here to keep utmp constants out of loginrec.h */ 71998937Sdes switch (li->type) { 72098937Sdes case LTYPE_LOGIN: 72198937Sdes utx->ut_type = USER_PROCESS; 72298937Sdes break; 72398937Sdes case LTYPE_LOGOUT: 72498937Sdes utx->ut_type = DEAD_PROCESS; 72598937Sdes break; 72698937Sdes } 72798937Sdes line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line)); 72898937Sdes set_utmpx_time(li, utx); 72998937Sdes utx->ut_pid = li->pid; 73098937Sdes /* strncpy(): Don't necessarily want null termination */ 73198937Sdes strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username)); 73298937Sdes 73398937Sdes if (li->type == LTYPE_LOGOUT) 73498937Sdes return; 73598937Sdes 73698937Sdes /* 73798937Sdes * These fields are only used when logging in, and are blank 73898937Sdes * for logouts. 73998937Sdes */ 74098937Sdes 74198937Sdes# ifdef HAVE_HOST_IN_UTMPX 74298937Sdes strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname)); 74398937Sdes# endif 74498937Sdes# ifdef HAVE_ADDR_IN_UTMPX 74598937Sdes /* this is just a 32-bit IP address */ 74698937Sdes if (li->hostaddr.sa.sa_family == AF_INET) 74798937Sdes utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 74898937Sdes# endif 749113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 750113911Sdes /* this is just a 128-bit IPv6 address */ 751113911Sdes if (li->hostaddr.sa.sa_family == AF_INET6) { 752113911Sdes sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 753113911Sdes memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 754113911Sdes if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 755113911Sdes ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; 756113911Sdes ut->ut_addr_v6[1] = 0; 757113911Sdes ut->ut_addr_v6[2] = 0; 758113911Sdes ut->ut_addr_v6[3] = 0; 759113911Sdes } 760113911Sdes } 761113911Sdes# endif 76298937Sdes# ifdef HAVE_SYSLEN_IN_UTMPX 76398937Sdes /* ut_syslen is the length of the utx_host string */ 76498937Sdes utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host)); 76598937Sdes# endif 76698937Sdes} 76798937Sdes#endif /* USE_UTMPX || USE_WTMPX */ 76898937Sdes 76998937Sdes/** 77098937Sdes ** Low-level utmp functions 77198937Sdes **/ 77298937Sdes 77398937Sdes/* FIXME: (ATL) utmp_write_direct needs testing */ 77498937Sdes#ifdef USE_UTMP 77598937Sdes 77698937Sdes/* if we can, use pututline() etc. */ 77798937Sdes# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \ 77898937Sdes defined(HAVE_PUTUTLINE) 77998937Sdes# define UTMP_USE_LIBRARY 78098937Sdes# endif 78198937Sdes 78298937Sdes 78398937Sdes/* write a utmp entry with the system's help (pututline() and pals) */ 78498937Sdes# ifdef UTMP_USE_LIBRARY 78598937Sdesstatic int 78698937Sdesutmp_write_library(struct logininfo *li, struct utmp *ut) 78798937Sdes{ 78898937Sdes setutent(); 78998937Sdes pututline(ut); 79098937Sdes 79198937Sdes# ifdef HAVE_ENDUTENT 79298937Sdes endutent(); 79398937Sdes# endif 79498937Sdes return 1; 79598937Sdes} 79698937Sdes# else /* UTMP_USE_LIBRARY */ 79798937Sdes 79898937Sdes/* write a utmp entry direct to the file */ 79998937Sdes/* This is a slightly modification of code in OpenBSD's login.c */ 80098937Sdesstatic int 80198937Sdesutmp_write_direct(struct logininfo *li, struct utmp *ut) 80298937Sdes{ 80398937Sdes struct utmp old_ut; 80498937Sdes register int fd; 80598937Sdes int tty; 80698937Sdes 80798937Sdes /* FIXME: (ATL) ttyslot() needs local implementation */ 80898937Sdes 80998937Sdes#if defined(HAVE_GETTTYENT) 81098937Sdes register struct ttyent *ty; 81198937Sdes 81298937Sdes tty=0; 81398937Sdes 81498937Sdes setttyent(); 81598937Sdes while ((struct ttyent *)0 != (ty = getttyent())) { 81698937Sdes tty++; 81798937Sdes if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line))) 81898937Sdes break; 81998937Sdes } 82098937Sdes endttyent(); 82198937Sdes 82298937Sdes if((struct ttyent *)0 == ty) { 823137019Sdes logit("%s: tty not found", __func__); 824137019Sdes return (0); 82598937Sdes } 82698937Sdes#else /* FIXME */ 82798937Sdes 82898937Sdes tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ 82998937Sdes 83098937Sdes#endif /* HAVE_GETTTYENT */ 83198937Sdes 83298937Sdes if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { 833137019Sdes off_t pos, ret; 834137019Sdes 835137019Sdes pos = (off_t)tty * sizeof(struct utmp); 836137019Sdes if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { 837137019Sdes logit("%s: llseek: %s", strerror(errno)); 838137019Sdes return (0); 839137019Sdes } 840137019Sdes if (ret != pos) { 841137019Sdes logit("%s: Couldn't seek to tty %s slot in %s", tty, 842137019Sdes UTMP_FILE); 843137019Sdes return (0); 844137019Sdes } 84598937Sdes /* 84698937Sdes * Prevent luser from zero'ing out ut_host. 84798937Sdes * If the new ut_line is empty but the old one is not 84898937Sdes * and ut_line and ut_name match, preserve the old ut_line. 84998937Sdes */ 85098937Sdes if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && 85198937Sdes (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && 85298937Sdes (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && 85398937Sdes (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) { 85498937Sdes (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); 85598937Sdes } 85698937Sdes 857137019Sdes if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { 858137019Sdes logit("%s: llseek: %s", __func__, strerror(errno)); 859137019Sdes return (0); 860137019Sdes } 861137019Sdes if (ret != pos) { 862137019Sdes logit("%s: Couldn't seek to tty %s slot in %s", 863137019Sdes __func__, tty, UTMP_FILE); 864137019Sdes return (0); 865137019Sdes } 866124211Sdes if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) 867137019Sdes logit("%s: error writing %s: %s", __func__, 86898937Sdes UTMP_FILE, strerror(errno)); 86998937Sdes 87098937Sdes (void)close(fd); 87198937Sdes return 1; 87298937Sdes } else { 87398937Sdes return 0; 87498937Sdes } 87598937Sdes} 87698937Sdes# endif /* UTMP_USE_LIBRARY */ 87798937Sdes 87898937Sdesstatic int 87998937Sdesutmp_perform_login(struct logininfo *li) 88098937Sdes{ 88198937Sdes struct utmp ut; 88298937Sdes 88398937Sdes construct_utmp(li, &ut); 88498937Sdes# ifdef UTMP_USE_LIBRARY 88598937Sdes if (!utmp_write_library(li, &ut)) { 886124211Sdes logit("utmp_perform_login: utmp_write_library() failed"); 88798937Sdes return 0; 88898937Sdes } 88998937Sdes# else 89098937Sdes if (!utmp_write_direct(li, &ut)) { 891124211Sdes logit("utmp_perform_login: utmp_write_direct() failed"); 89298937Sdes return 0; 89398937Sdes } 89498937Sdes# endif 89598937Sdes return 1; 89698937Sdes} 89798937Sdes 89898937Sdes 89998937Sdesstatic int 90098937Sdesutmp_perform_logout(struct logininfo *li) 90198937Sdes{ 90298937Sdes struct utmp ut; 90398937Sdes 90498937Sdes construct_utmp(li, &ut); 90598937Sdes# ifdef UTMP_USE_LIBRARY 90698937Sdes if (!utmp_write_library(li, &ut)) { 907124211Sdes logit("utmp_perform_logout: utmp_write_library() failed"); 90898937Sdes return 0; 90998937Sdes } 91098937Sdes# else 91198937Sdes if (!utmp_write_direct(li, &ut)) { 912124211Sdes logit("utmp_perform_logout: utmp_write_direct() failed"); 91398937Sdes return 0; 91498937Sdes } 91598937Sdes# endif 91698937Sdes return 1; 91798937Sdes} 91898937Sdes 91998937Sdes 92098937Sdesint 92198937Sdesutmp_write_entry(struct logininfo *li) 92298937Sdes{ 92398937Sdes switch(li->type) { 92498937Sdes case LTYPE_LOGIN: 92598937Sdes return utmp_perform_login(li); 92698937Sdes 92798937Sdes case LTYPE_LOGOUT: 92898937Sdes return utmp_perform_logout(li); 92998937Sdes 93098937Sdes default: 931124211Sdes logit("utmp_write_entry: invalid type field"); 93298937Sdes return 0; 93398937Sdes } 93498937Sdes} 93598937Sdes#endif /* USE_UTMP */ 93698937Sdes 93798937Sdes 93898937Sdes/** 93998937Sdes ** Low-level utmpx functions 94098937Sdes **/ 94198937Sdes 94298937Sdes/* not much point if we don't want utmpx entries */ 94398937Sdes#ifdef USE_UTMPX 94498937Sdes 94598937Sdes/* if we have the wherewithall, use pututxline etc. */ 94698937Sdes# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \ 94798937Sdes defined(HAVE_PUTUTXLINE) 94898937Sdes# define UTMPX_USE_LIBRARY 94998937Sdes# endif 95098937Sdes 95198937Sdes 95298937Sdes/* write a utmpx entry with the system's help (pututxline() and pals) */ 95398937Sdes# ifdef UTMPX_USE_LIBRARY 95498937Sdesstatic int 95598937Sdesutmpx_write_library(struct logininfo *li, struct utmpx *utx) 95698937Sdes{ 95798937Sdes setutxent(); 95898937Sdes pututxline(utx); 95998937Sdes 96098937Sdes# ifdef HAVE_ENDUTXENT 96198937Sdes endutxent(); 96298937Sdes# endif 96398937Sdes return 1; 96498937Sdes} 96598937Sdes 96698937Sdes# else /* UTMPX_USE_LIBRARY */ 96798937Sdes 96898937Sdes/* write a utmp entry direct to the file */ 96998937Sdesstatic int 97098937Sdesutmpx_write_direct(struct logininfo *li, struct utmpx *utx) 97198937Sdes{ 972124211Sdes logit("utmpx_write_direct: not implemented!"); 97398937Sdes return 0; 97498937Sdes} 97598937Sdes# endif /* UTMPX_USE_LIBRARY */ 97698937Sdes 97798937Sdesstatic int 97898937Sdesutmpx_perform_login(struct logininfo *li) 97998937Sdes{ 98098937Sdes struct utmpx utx; 98198937Sdes 98298937Sdes construct_utmpx(li, &utx); 98398937Sdes# ifdef UTMPX_USE_LIBRARY 98498937Sdes if (!utmpx_write_library(li, &utx)) { 985124211Sdes logit("utmpx_perform_login: utmp_write_library() failed"); 98698937Sdes return 0; 98798937Sdes } 98898937Sdes# else 98998937Sdes if (!utmpx_write_direct(li, &ut)) { 990124211Sdes logit("utmpx_perform_login: utmp_write_direct() failed"); 99198937Sdes return 0; 99298937Sdes } 99398937Sdes# endif 99498937Sdes return 1; 99598937Sdes} 99698937Sdes 99798937Sdes 99898937Sdesstatic int 99998937Sdesutmpx_perform_logout(struct logininfo *li) 100098937Sdes{ 100198937Sdes struct utmpx utx; 100298937Sdes 100398937Sdes construct_utmpx(li, &utx); 100498937Sdes# ifdef HAVE_ID_IN_UTMPX 100598937Sdes line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); 100698937Sdes# endif 100798937Sdes# ifdef HAVE_TYPE_IN_UTMPX 100898937Sdes utx.ut_type = DEAD_PROCESS; 100998937Sdes# endif 101098937Sdes 101198937Sdes# ifdef UTMPX_USE_LIBRARY 101298937Sdes utmpx_write_library(li, &utx); 101398937Sdes# else 101498937Sdes utmpx_write_direct(li, &utx); 101598937Sdes# endif 101698937Sdes return 1; 101798937Sdes} 101898937Sdes 101998937Sdesint 102098937Sdesutmpx_write_entry(struct logininfo *li) 102198937Sdes{ 102298937Sdes switch(li->type) { 102398937Sdes case LTYPE_LOGIN: 102498937Sdes return utmpx_perform_login(li); 102598937Sdes case LTYPE_LOGOUT: 102698937Sdes return utmpx_perform_logout(li); 102798937Sdes default: 1028124211Sdes logit("utmpx_write_entry: invalid type field"); 102998937Sdes return 0; 103098937Sdes } 103198937Sdes} 103298937Sdes#endif /* USE_UTMPX */ 103398937Sdes 103498937Sdes 103598937Sdes/** 103698937Sdes ** Low-level wtmp functions 103798937Sdes **/ 103898937Sdes 103998937Sdes#ifdef USE_WTMP 104098937Sdes 104198937Sdes/* write a wtmp entry direct to the end of the file */ 104298937Sdes/* This is a slight modification of code in OpenBSD's logwtmp.c */ 104398937Sdesstatic int 104498937Sdeswtmp_write(struct logininfo *li, struct utmp *ut) 104598937Sdes{ 104698937Sdes struct stat buf; 104798937Sdes int fd, ret = 1; 104898937Sdes 104998937Sdes if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 1050124211Sdes logit("wtmp_write: problem writing %s: %s", 105198937Sdes WTMP_FILE, strerror(errno)); 105298937Sdes return 0; 105398937Sdes } 105498937Sdes if (fstat(fd, &buf) == 0) 1055124211Sdes if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { 105698937Sdes ftruncate(fd, buf.st_size); 1057124211Sdes logit("wtmp_write: problem writing %s: %s", 105898937Sdes WTMP_FILE, strerror(errno)); 105998937Sdes ret = 0; 106098937Sdes } 106198937Sdes (void)close(fd); 106298937Sdes return ret; 106398937Sdes} 106498937Sdes 106598937Sdesstatic int 106698937Sdeswtmp_perform_login(struct logininfo *li) 106798937Sdes{ 106898937Sdes struct utmp ut; 106998937Sdes 107098937Sdes construct_utmp(li, &ut); 107198937Sdes return wtmp_write(li, &ut); 107298937Sdes} 107398937Sdes 107498937Sdes 107598937Sdesstatic int 107698937Sdeswtmp_perform_logout(struct logininfo *li) 107798937Sdes{ 107898937Sdes struct utmp ut; 107998937Sdes 108098937Sdes construct_utmp(li, &ut); 108198937Sdes return wtmp_write(li, &ut); 108298937Sdes} 108398937Sdes 108498937Sdes 108598937Sdesint 108698937Sdeswtmp_write_entry(struct logininfo *li) 108798937Sdes{ 108898937Sdes switch(li->type) { 108998937Sdes case LTYPE_LOGIN: 109098937Sdes return wtmp_perform_login(li); 109198937Sdes case LTYPE_LOGOUT: 109298937Sdes return wtmp_perform_logout(li); 109398937Sdes default: 1094124211Sdes logit("wtmp_write_entry: invalid type field"); 109598937Sdes return 0; 109698937Sdes } 109798937Sdes} 109898937Sdes 109998937Sdes 110098937Sdes/* Notes on fetching login data from wtmp/wtmpx 110198937Sdes * 110298937Sdes * Logouts are usually recorded with (amongst other things) a blank 110398937Sdes * username on a given tty line. However, some systems (HP-UX is one) 110498937Sdes * leave all fields set, but change the ut_type field to DEAD_PROCESS. 110598937Sdes * 110698937Sdes * Since we're only looking for logins here, we know that the username 110798937Sdes * must be set correctly. On systems that leave it in, we check for 110898937Sdes * ut_type==USER_PROCESS (indicating a login.) 110998937Sdes * 111098937Sdes * Portability: Some systems may set something other than USER_PROCESS 111198937Sdes * to indicate a login process. I don't know of any as I write. Also, 111298937Sdes * it's possible that some systems may both leave the username in 111398937Sdes * place and not have ut_type. 111498937Sdes */ 111598937Sdes 111698937Sdes/* return true if this wtmp entry indicates a login */ 111798937Sdesstatic int 111898937Sdeswtmp_islogin(struct logininfo *li, struct utmp *ut) 111998937Sdes{ 112098937Sdes if (strncmp(li->username, ut->ut_name, 112198937Sdes MIN_SIZEOF(li->username, ut->ut_name)) == 0) { 112298937Sdes# ifdef HAVE_TYPE_IN_UTMP 112398937Sdes if (ut->ut_type & USER_PROCESS) 112498937Sdes return 1; 112598937Sdes# else 112698937Sdes return 1; 112798937Sdes# endif 112898937Sdes } 112998937Sdes return 0; 113098937Sdes} 113198937Sdes 113298937Sdesint 113398937Sdeswtmp_get_entry(struct logininfo *li) 113498937Sdes{ 113598937Sdes struct stat st; 113698937Sdes struct utmp ut; 113798937Sdes int fd, found=0; 113898937Sdes 113998937Sdes /* Clear the time entries in our logininfo */ 114098937Sdes li->tv_sec = li->tv_usec = 0; 114198937Sdes 114298937Sdes if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { 1143124211Sdes logit("wtmp_get_entry: problem opening %s: %s", 114498937Sdes WTMP_FILE, strerror(errno)); 114598937Sdes return 0; 114698937Sdes } 114798937Sdes if (fstat(fd, &st) != 0) { 1148124211Sdes logit("wtmp_get_entry: couldn't stat %s: %s", 114998937Sdes WTMP_FILE, strerror(errno)); 115098937Sdes close(fd); 115198937Sdes return 0; 115298937Sdes } 115398937Sdes 115498937Sdes /* Seek to the start of the last struct utmp */ 115598937Sdes if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { 115698937Sdes /* Looks like we've got a fresh wtmp file */ 115798937Sdes close(fd); 115898937Sdes return 0; 115998937Sdes } 116098937Sdes 116198937Sdes while (!found) { 116298937Sdes if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { 1163124211Sdes logit("wtmp_get_entry: read of %s failed: %s", 116498937Sdes WTMP_FILE, strerror(errno)); 116598937Sdes close (fd); 116698937Sdes return 0; 116798937Sdes } 116898937Sdes if ( wtmp_islogin(li, &ut) ) { 116998937Sdes found = 1; 117098937Sdes /* We've already checked for a time in struct 117198937Sdes * utmp, in login_getlast(). */ 117298937Sdes# ifdef HAVE_TIME_IN_UTMP 117398937Sdes li->tv_sec = ut.ut_time; 117498937Sdes# else 117598937Sdes# if HAVE_TV_IN_UTMP 117698937Sdes li->tv_sec = ut.ut_tv.tv_sec; 117798937Sdes# endif 117898937Sdes# endif 117998937Sdes line_fullname(li->line, ut.ut_line, 118098937Sdes MIN_SIZEOF(li->line, ut.ut_line)); 118198937Sdes# ifdef HAVE_HOST_IN_UTMP 118298937Sdes strlcpy(li->hostname, ut.ut_host, 118398937Sdes MIN_SIZEOF(li->hostname, ut.ut_host)); 118498937Sdes# endif 118598937Sdes continue; 118698937Sdes } 118798937Sdes /* Seek back 2 x struct utmp */ 118898937Sdes if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { 118998937Sdes /* We've found the start of the file, so quit */ 119098937Sdes close (fd); 119198937Sdes return 0; 119298937Sdes } 119398937Sdes } 119498937Sdes 119598937Sdes /* We found an entry. Tidy up and return */ 119698937Sdes close(fd); 119798937Sdes return 1; 119898937Sdes} 119998937Sdes# endif /* USE_WTMP */ 120098937Sdes 120198937Sdes 120298937Sdes/** 120398937Sdes ** Low-level wtmpx functions 120498937Sdes **/ 120598937Sdes 120698937Sdes#ifdef USE_WTMPX 120798937Sdes/* write a wtmpx entry direct to the end of the file */ 120898937Sdes/* This is a slight modification of code in OpenBSD's logwtmp.c */ 120998937Sdesstatic int 121098937Sdeswtmpx_write(struct logininfo *li, struct utmpx *utx) 121198937Sdes{ 1212126277Sdes#ifndef HAVE_UPDWTMPX 121398937Sdes struct stat buf; 121498937Sdes int fd, ret = 1; 121598937Sdes 121698937Sdes if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 1217124211Sdes logit("wtmpx_write: problem opening %s: %s", 121898937Sdes WTMPX_FILE, strerror(errno)); 121998937Sdes return 0; 122098937Sdes } 122198937Sdes 122298937Sdes if (fstat(fd, &buf) == 0) 1223124211Sdes if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) { 122498937Sdes ftruncate(fd, buf.st_size); 1225124211Sdes logit("wtmpx_write: problem writing %s: %s", 122698937Sdes WTMPX_FILE, strerror(errno)); 122798937Sdes ret = 0; 122898937Sdes } 122998937Sdes (void)close(fd); 123098937Sdes 123198937Sdes return ret; 1232126277Sdes#else 1233126277Sdes updwtmpx(WTMPX_FILE, utx); 1234126277Sdes return 1; 1235126277Sdes#endif 123698937Sdes} 123798937Sdes 123898937Sdes 123998937Sdesstatic int 124098937Sdeswtmpx_perform_login(struct logininfo *li) 124198937Sdes{ 124298937Sdes struct utmpx utx; 124398937Sdes 124498937Sdes construct_utmpx(li, &utx); 124598937Sdes return wtmpx_write(li, &utx); 124698937Sdes} 124798937Sdes 124898937Sdes 124998937Sdesstatic int 125098937Sdeswtmpx_perform_logout(struct logininfo *li) 125198937Sdes{ 125298937Sdes struct utmpx utx; 125398937Sdes 125498937Sdes construct_utmpx(li, &utx); 125598937Sdes return wtmpx_write(li, &utx); 125698937Sdes} 125798937Sdes 125898937Sdes 125998937Sdesint 126098937Sdeswtmpx_write_entry(struct logininfo *li) 126198937Sdes{ 126298937Sdes switch(li->type) { 126398937Sdes case LTYPE_LOGIN: 126498937Sdes return wtmpx_perform_login(li); 126598937Sdes case LTYPE_LOGOUT: 126698937Sdes return wtmpx_perform_logout(li); 126798937Sdes default: 1268124211Sdes logit("wtmpx_write_entry: invalid type field"); 126998937Sdes return 0; 127098937Sdes } 127198937Sdes} 127298937Sdes 127398937Sdes/* Please see the notes above wtmp_islogin() for information about the 127498937Sdes next two functions */ 127598937Sdes 127698937Sdes/* Return true if this wtmpx entry indicates a login */ 127798937Sdesstatic int 127898937Sdeswtmpx_islogin(struct logininfo *li, struct utmpx *utx) 127998937Sdes{ 128098937Sdes if ( strncmp(li->username, utx->ut_name, 128198937Sdes MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) { 128298937Sdes# ifdef HAVE_TYPE_IN_UTMPX 128398937Sdes if (utx->ut_type == USER_PROCESS) 128498937Sdes return 1; 128598937Sdes# else 128698937Sdes return 1; 128798937Sdes# endif 128898937Sdes } 128998937Sdes return 0; 129098937Sdes} 129198937Sdes 129298937Sdes 129398937Sdesint 129498937Sdeswtmpx_get_entry(struct logininfo *li) 129598937Sdes{ 129698937Sdes struct stat st; 129798937Sdes struct utmpx utx; 129898937Sdes int fd, found=0; 129998937Sdes 130098937Sdes /* Clear the time entries */ 130198937Sdes li->tv_sec = li->tv_usec = 0; 130298937Sdes 130398937Sdes if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { 1304124211Sdes logit("wtmpx_get_entry: problem opening %s: %s", 130598937Sdes WTMPX_FILE, strerror(errno)); 130698937Sdes return 0; 130798937Sdes } 130898937Sdes if (fstat(fd, &st) != 0) { 1309124211Sdes logit("wtmpx_get_entry: couldn't stat %s: %s", 1310106130Sdes WTMPX_FILE, strerror(errno)); 131198937Sdes close(fd); 131298937Sdes return 0; 131398937Sdes } 131498937Sdes 131598937Sdes /* Seek to the start of the last struct utmpx */ 131698937Sdes if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { 131798937Sdes /* probably a newly rotated wtmpx file */ 131898937Sdes close(fd); 131998937Sdes return 0; 132098937Sdes } 132198937Sdes 132298937Sdes while (!found) { 132398937Sdes if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { 1324124211Sdes logit("wtmpx_get_entry: read of %s failed: %s", 132598937Sdes WTMPX_FILE, strerror(errno)); 132698937Sdes close (fd); 132798937Sdes return 0; 132898937Sdes } 132998937Sdes /* Logouts are recorded as a blank username on a particular line. 133098937Sdes * So, we just need to find the username in struct utmpx */ 133198937Sdes if ( wtmpx_islogin(li, &utx) ) { 1332106130Sdes found = 1; 133398937Sdes# ifdef HAVE_TV_IN_UTMPX 133498937Sdes li->tv_sec = utx.ut_tv.tv_sec; 133598937Sdes# else 133698937Sdes# ifdef HAVE_TIME_IN_UTMPX 133798937Sdes li->tv_sec = utx.ut_time; 133898937Sdes# endif 133998937Sdes# endif 134098937Sdes line_fullname(li->line, utx.ut_line, sizeof(li->line)); 134198937Sdes# ifdef HAVE_HOST_IN_UTMPX 134298937Sdes strlcpy(li->hostname, utx.ut_host, 134398937Sdes MIN_SIZEOF(li->hostname, utx.ut_host)); 134498937Sdes# endif 134598937Sdes continue; 134698937Sdes } 134798937Sdes if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { 134898937Sdes close (fd); 134998937Sdes return 0; 135098937Sdes } 135198937Sdes } 135298937Sdes 135398937Sdes close(fd); 135498937Sdes return 1; 135598937Sdes} 135698937Sdes#endif /* USE_WTMPX */ 135798937Sdes 135898937Sdes/** 135998937Sdes ** Low-level libutil login() functions 136098937Sdes **/ 136198937Sdes 136298937Sdes#ifdef USE_LOGIN 136398937Sdesstatic int 136498937Sdessyslogin_perform_login(struct logininfo *li) 136598937Sdes{ 136698937Sdes struct utmp *ut; 136798937Sdes 136898937Sdes if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) { 1369124211Sdes logit("syslogin_perform_login: couldn't malloc()"); 137098937Sdes return 0; 137198937Sdes } 137298937Sdes construct_utmp(li, ut); 137398937Sdes login(ut); 1374113911Sdes free(ut); 137598937Sdes 137698937Sdes return 1; 137798937Sdes} 137898937Sdes 137998937Sdesstatic int 138098937Sdessyslogin_perform_logout(struct logininfo *li) 138198937Sdes{ 138298937Sdes# ifdef HAVE_LOGOUT 1383128460Sdes char line[UT_LINESIZE]; 138498937Sdes 138598937Sdes (void)line_stripname(line, li->line, sizeof(line)); 138698937Sdes 138798937Sdes if (!logout(line)) { 1388124211Sdes logit("syslogin_perform_logout: logout() returned an error"); 138998937Sdes# ifdef HAVE_LOGWTMP 139098937Sdes } else { 139198937Sdes logwtmp(line, "", ""); 139298937Sdes# endif 139398937Sdes } 139498937Sdes /* FIXME: (ATL - if the need arises) What to do if we have 139598937Sdes * login, but no logout? what if logout but no logwtmp? All 139698937Sdes * routines are in libutil so they should all be there, 139798937Sdes * but... */ 139898937Sdes# endif 139998937Sdes return 1; 140098937Sdes} 140198937Sdes 140298937Sdesint 140398937Sdessyslogin_write_entry(struct logininfo *li) 140498937Sdes{ 140598937Sdes switch (li->type) { 140698937Sdes case LTYPE_LOGIN: 140798937Sdes return syslogin_perform_login(li); 140898937Sdes case LTYPE_LOGOUT: 140998937Sdes return syslogin_perform_logout(li); 141098937Sdes default: 1411124211Sdes logit("syslogin_write_entry: Invalid type field"); 141298937Sdes return 0; 141398937Sdes } 141498937Sdes} 141598937Sdes#endif /* USE_LOGIN */ 141698937Sdes 141798937Sdes/* end of file log-syslogin.c */ 141898937Sdes 141998937Sdes/** 142098937Sdes ** Low-level lastlog functions 142198937Sdes **/ 142298937Sdes 142398937Sdes#ifdef USE_LASTLOG 142498937Sdes#define LL_FILE 1 142598937Sdes#define LL_DIR 2 142698937Sdes#define LL_OTHER 3 142798937Sdes 142898937Sdesstatic void 142998937Sdeslastlog_construct(struct logininfo *li, struct lastlog *last) 143098937Sdes{ 143198937Sdes /* clear the structure */ 143298937Sdes memset(last, '\0', sizeof(*last)); 143398937Sdes 143498937Sdes (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line)); 143598937Sdes strlcpy(last->ll_host, li->hostname, 143698937Sdes MIN_SIZEOF(last->ll_host, li->hostname)); 143798937Sdes last->ll_time = li->tv_sec; 143898937Sdes} 143998937Sdes 144098937Sdesstatic int 144198937Sdeslastlog_filetype(char *filename) 144298937Sdes{ 144398937Sdes struct stat st; 144498937Sdes 144598937Sdes if (stat(LASTLOG_FILE, &st) != 0) { 1446124211Sdes logit("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE, 144798937Sdes strerror(errno)); 144898937Sdes return 0; 144998937Sdes } 145098937Sdes if (S_ISDIR(st.st_mode)) 145198937Sdes return LL_DIR; 145298937Sdes else if (S_ISREG(st.st_mode)) 145398937Sdes return LL_FILE; 145498937Sdes else 145598937Sdes return LL_OTHER; 145698937Sdes} 145798937Sdes 145898937Sdes 145998937Sdes/* open the file (using filemode) and seek to the login entry */ 146098937Sdesstatic int 146198937Sdeslastlog_openseek(struct logininfo *li, int *fd, int filemode) 146298937Sdes{ 146398937Sdes off_t offset; 146498937Sdes int type; 146598937Sdes char lastlog_file[1024]; 146698937Sdes 146798937Sdes type = lastlog_filetype(LASTLOG_FILE); 146898937Sdes switch (type) { 146998937Sdes case LL_FILE: 147098937Sdes strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); 147198937Sdes break; 147298937Sdes case LL_DIR: 147398937Sdes snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", 147498937Sdes LASTLOG_FILE, li->username); 147598937Sdes break; 147698937Sdes default: 1477124211Sdes logit("lastlog_openseek: %.100s is not a file or directory!", 147898937Sdes LASTLOG_FILE); 147998937Sdes return 0; 148098937Sdes } 148198937Sdes 1482124211Sdes *fd = open(lastlog_file, filemode, 0600); 148398937Sdes if ( *fd < 0) { 148498937Sdes debug("lastlog_openseek: Couldn't open %s: %s", 148598937Sdes lastlog_file, strerror(errno)); 148698937Sdes return 0; 148798937Sdes } 148898937Sdes 148998937Sdes if (type == LL_FILE) { 149098937Sdes /* find this uid's offset in the lastlog file */ 149198937Sdes offset = (off_t) ((long)li->uid * sizeof(struct lastlog)); 149298937Sdes 149398937Sdes if ( lseek(*fd, offset, SEEK_SET) != offset ) { 1494124211Sdes logit("lastlog_openseek: %s->lseek(): %s", 149598937Sdes lastlog_file, strerror(errno)); 149698937Sdes return 0; 149798937Sdes } 149898937Sdes } 149998937Sdes 150098937Sdes return 1; 150198937Sdes} 150298937Sdes 150398937Sdesstatic int 150498937Sdeslastlog_perform_login(struct logininfo *li) 150598937Sdes{ 150698937Sdes struct lastlog last; 150798937Sdes int fd; 150898937Sdes 150998937Sdes /* create our struct lastlog */ 151098937Sdes lastlog_construct(li, &last); 151198937Sdes 151298937Sdes if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) 151398937Sdes return(0); 151498937Sdes 151598937Sdes /* write the entry */ 1516124211Sdes if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) { 151798937Sdes close(fd); 1518124211Sdes logit("lastlog_write_filemode: Error writing to %s: %s", 151998937Sdes LASTLOG_FILE, strerror(errno)); 152098937Sdes return 0; 152198937Sdes } 152298937Sdes 152398937Sdes close(fd); 152498937Sdes return 1; 152598937Sdes} 152698937Sdes 152798937Sdesint 152898937Sdeslastlog_write_entry(struct logininfo *li) 152998937Sdes{ 153098937Sdes switch(li->type) { 153198937Sdes case LTYPE_LOGIN: 153298937Sdes return lastlog_perform_login(li); 153398937Sdes default: 1534124211Sdes logit("lastlog_write_entry: Invalid type field"); 153598937Sdes return 0; 153698937Sdes } 153798937Sdes} 153898937Sdes 153998937Sdesstatic void 154098937Sdeslastlog_populate_entry(struct logininfo *li, struct lastlog *last) 154198937Sdes{ 154298937Sdes line_fullname(li->line, last->ll_line, sizeof(li->line)); 154398937Sdes strlcpy(li->hostname, last->ll_host, 154498937Sdes MIN_SIZEOF(li->hostname, last->ll_host)); 154598937Sdes li->tv_sec = last->ll_time; 154698937Sdes} 154798937Sdes 154898937Sdesint 154998937Sdeslastlog_get_entry(struct logininfo *li) 155098937Sdes{ 155198937Sdes struct lastlog last; 1552113911Sdes int fd, ret; 155398937Sdes 155498937Sdes if (!lastlog_openseek(li, &fd, O_RDONLY)) 1555113911Sdes return (0); 155698937Sdes 1557113911Sdes ret = atomicio(read, fd, &last, sizeof(last)); 1558113911Sdes close(fd); 1559113911Sdes 1560113911Sdes switch (ret) { 1561113911Sdes case 0: 1562113911Sdes memset(&last, '\0', sizeof(last)); 1563113911Sdes /* FALLTHRU */ 1564113911Sdes case sizeof(last): 1565113911Sdes lastlog_populate_entry(li, &last); 1566113911Sdes return (1); 1567113911Sdes case -1: 1568126277Sdes error("%s: Error reading from %s: %s", __func__, 156998937Sdes LASTLOG_FILE, strerror(errno)); 1570113911Sdes return (0); 1571113911Sdes default: 1572113911Sdes error("%s: Error reading from %s: Expecting %d, got %d", 1573113911Sdes __func__, LASTLOG_FILE, sizeof(last), ret); 1574113911Sdes return (0); 157598937Sdes } 157698937Sdes 1577113911Sdes /* NOTREACHED */ 1578113911Sdes return (0); 157998937Sdes} 158098937Sdes#endif /* USE_LASTLOG */ 1581