loginrec.c revision 113911
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 * 3. All advertising materials mentioning features or use of this software 1698937Sdes * must display the following acknowledgement: 1798937Sdes * This product includes software developed by Markus Friedl. 1898937Sdes * 4. The name of the author may not be used to endorse or promote products 1998937Sdes * derived from this software without specific prior written permission. 2098937Sdes * 2198937Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2298937Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2398937Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2498937Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2598937Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2698937Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2798937Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2898937Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2998937Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3098937Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3198937Sdes */ 3298937Sdes 3398937Sdes/** 3498937Sdes ** loginrec.c: platform-independent login recording and lastlog retrieval 3598937Sdes **/ 3698937Sdes 3798937Sdes/* 3898937Sdes The new login code explained 3998937Sdes ============================ 4098937Sdes 4198937Sdes This code attempts to provide a common interface to login recording 4298937Sdes (utmp and friends) and last login time retrieval. 4398937Sdes 4498937Sdes Its primary means of achieving this is to use 'struct logininfo', a 4598937Sdes union of all the useful fields in the various different types of 4698937Sdes system login record structures one finds on UNIX variants. 4798937Sdes 4898937Sdes We depend on autoconf to define which recording methods are to be 4998937Sdes used, and which fields are contained in the relevant data structures 5098937Sdes on the local system. Many C preprocessor symbols affect which code 5198937Sdes gets compiled here. 5298937Sdes 5398937Sdes The code is designed to make it easy to modify a particular 5498937Sdes recording method, without affecting other methods nor requiring so 5598937Sdes many nested conditional compilation blocks as were commonplace in 5698937Sdes the old code. 5798937Sdes 5898937Sdes For login recording, we try to use the local system's libraries as 5998937Sdes these are clearly most likely to work correctly. For utmp systems 6098937Sdes this usually means login() and logout() or setutent() etc., probably 6198937Sdes in libutil, along with logwtmp() etc. On these systems, we fall back 6298937Sdes to writing the files directly if we have to, though this method 6398937Sdes requires very thorough testing so we do not corrupt local auditing 6498937Sdes information. These files and their access methods are very system 6598937Sdes specific indeed. 6698937Sdes 6798937Sdes For utmpx systems, the corresponding library functions are 6898937Sdes setutxent() etc. To the author's knowledge, all utmpx systems have 6998937Sdes these library functions and so no direct write is attempted. If such 7098937Sdes a system exists and needs support, direct analogues of the [uw]tmp 7198937Sdes code should suffice. 7298937Sdes 7398937Sdes Retrieving the time of last login ('lastlog') is in some ways even 7498937Sdes more problemmatic than login recording. Some systems provide a 7598937Sdes simple table of all users which we seek based on uid and retrieve a 7698937Sdes relatively standard structure. Others record the same information in 7798937Sdes a directory with a separate file, and others don't record the 7898937Sdes information separately at all. For systems in the latter category, 7998937Sdes we look backwards in the wtmp or wtmpx file for the last login entry 8098937Sdes for our user. Naturally this is slower and on busy systems could 8198937Sdes incur a significant performance penalty. 8298937Sdes 8398937Sdes Calling the new code 8498937Sdes -------------------- 8598937Sdes 8698937Sdes In OpenSSH all login recording and retrieval is performed in 8798937Sdes login.c. Here you'll find working examples. Also, in the logintest.c 8898937Sdes program there are more examples. 8998937Sdes 9098937Sdes Internal handler calling method 9198937Sdes ------------------------------- 9298937Sdes 9398937Sdes When a call is made to login_login() or login_logout(), both 9498937Sdes routines set a struct logininfo flag defining which action (log in, 9598937Sdes or log out) is to be taken. They both then call login_write(), which 9698937Sdes calls whichever of the many structure-specific handlers autoconf 9798937Sdes selects for the local system. 9898937Sdes 9998937Sdes The handlers themselves handle system data structure specifics. Both 10098937Sdes struct utmp and struct utmpx have utility functions (see 10198937Sdes construct_utmp*()) to try to make it simpler to add extra systems 10298937Sdes that introduce new features to either structure. 10398937Sdes 10498937Sdes While it may seem terribly wasteful to replicate so much similar 10598937Sdes code for each method, experience has shown that maintaining code to 10698937Sdes write both struct utmp and utmpx in one function, whilst maintaining 10798937Sdes support for all systems whether they have library support or not, is 10898937Sdes a difficult and time-consuming task. 10998937Sdes 11098937Sdes Lastlog support proceeds similarly. Functions login_get_lastlog() 11198937Sdes (and its OpenSSH-tuned friend login_get_lastlog_time()) call 11298937Sdes getlast_entry(), which tries one of three methods to find the last 11398937Sdes login time. It uses local system lastlog support if it can, 11498937Sdes otherwise it tries wtmp or wtmpx before giving up and returning 0, 11598937Sdes meaning "tilt". 11698937Sdes 11798937Sdes Maintenance 11898937Sdes ----------- 11998937Sdes 12098937Sdes In many cases it's possible to tweak autoconf to select the correct 12198937Sdes methods for a particular platform, either by improving the detection 12298937Sdes code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE 12398937Sdes symbols for the platform. 12498937Sdes 12598937Sdes Use logintest to check which symbols are defined before modifying 12698937Sdes configure.ac and loginrec.c. (You have to build logintest yourself 12798937Sdes with 'make logintest' as it's not built by default.) 12898937Sdes 12998937Sdes Otherwise, patches to the specific method(s) are very helpful! 13098937Sdes 13198937Sdes*/ 13298937Sdes 13398937Sdes/** 13498937Sdes ** TODO: 13598937Sdes ** homegrown ttyslot() 13698937Sdes ** test, test, test 13798937Sdes ** 13898937Sdes ** Platform status: 13998937Sdes ** ---------------- 14098937Sdes ** 14198937Sdes ** Known good: 14298937Sdes ** Linux (Redhat 6.2, Debian) 14398937Sdes ** Solaris 14498937Sdes ** HP-UX 10.20 (gcc only) 14598937Sdes ** IRIX 14698937Sdes ** NeXT - M68k/HPPA/Sparc (4.2/3.3) 14798937Sdes ** 14898937Sdes ** Testing required: Please send reports! 14998937Sdes ** NetBSD 15098937Sdes ** HP-UX 11 15198937Sdes ** AIX 15298937Sdes ** 15398937Sdes ** Platforms with known problems: 15498937Sdes ** Some variants of Slackware Linux 15598937Sdes ** 15698937Sdes **/ 15798937Sdes 15898937Sdes#include "includes.h" 15998937Sdes 16098937Sdes#include "ssh.h" 16198937Sdes#include "xmalloc.h" 16298937Sdes#include "loginrec.h" 16398937Sdes#include "log.h" 16498937Sdes#include "atomicio.h" 16598937Sdes 166113911SdesRCSID("$Id: loginrec.c,v 1.47 2003/03/10 00:23:07 djm Exp $"); 16799768SdesRCSID("$FreeBSD: head/crypto/openssh/loginrec.c 113911 2003-04-23 17:13:13Z des $"); 16898937Sdes 16998937Sdes#ifdef HAVE_UTIL_H 17098937Sdes# include <util.h> 17198937Sdes#endif 17298937Sdes 17398937Sdes#ifdef HAVE_LIBUTIL_H 17498937Sdes# include <libutil.h> 17598937Sdes#endif 17698937Sdes 17798937Sdes/** 17898937Sdes ** prototypes for helper functions in this file 17998937Sdes **/ 18098937Sdes 18198937Sdes#if HAVE_UTMP_H 18298937Sdesvoid set_utmp_time(struct logininfo *li, struct utmp *ut); 18398937Sdesvoid construct_utmp(struct logininfo *li, struct utmp *ut); 18498937Sdes#endif 18598937Sdes 18698937Sdes#ifdef HAVE_UTMPX_H 18798937Sdesvoid set_utmpx_time(struct logininfo *li, struct utmpx *ut); 18898937Sdesvoid construct_utmpx(struct logininfo *li, struct utmpx *ut); 18998937Sdes#endif 19098937Sdes 19198937Sdesint utmp_write_entry(struct logininfo *li); 19298937Sdesint utmpx_write_entry(struct logininfo *li); 19398937Sdesint wtmp_write_entry(struct logininfo *li); 19498937Sdesint wtmpx_write_entry(struct logininfo *li); 19598937Sdesint lastlog_write_entry(struct logininfo *li); 19698937Sdesint syslogin_write_entry(struct logininfo *li); 19798937Sdes 19898937Sdesint getlast_entry(struct logininfo *li); 19998937Sdesint lastlog_get_entry(struct logininfo *li); 20098937Sdesint wtmp_get_entry(struct logininfo *li); 20198937Sdesint wtmpx_get_entry(struct logininfo *li); 20298937Sdes 20398937Sdes/* pick the shortest string */ 20498937Sdes#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) ) 20598937Sdes 20698937Sdes/** 20798937Sdes ** platform-independent login functions 20898937Sdes **/ 20998937Sdes 21098937Sdes/* login_login(struct logininfo *) -Record a login 21198937Sdes * 21298937Sdes * Call with a pointer to a struct logininfo initialised with 21398937Sdes * login_init_entry() or login_alloc_entry() 21498937Sdes * 21598937Sdes * Returns: 21698937Sdes * >0 if successful 21798937Sdes * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 21898937Sdes */ 21998937Sdesint 22098937Sdeslogin_login (struct logininfo *li) 22198937Sdes{ 22298937Sdes li->type = LTYPE_LOGIN; 22398937Sdes return login_write(li); 22498937Sdes} 22598937Sdes 22698937Sdes 22798937Sdes/* login_logout(struct logininfo *) - Record a logout 22898937Sdes * 22998937Sdes * Call as with login_login() 23098937Sdes * 23198937Sdes * Returns: 23298937Sdes * >0 if successful 23398937Sdes * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 23498937Sdes */ 23598937Sdesint 23698937Sdeslogin_logout(struct logininfo *li) 23798937Sdes{ 23898937Sdes li->type = LTYPE_LOGOUT; 23998937Sdes return login_write(li); 24098937Sdes} 24198937Sdes 24298937Sdes/* login_get_lastlog_time(int) - Retrieve the last login time 24398937Sdes * 24498937Sdes * Retrieve the last login time for the given uid. Will try to use the 24598937Sdes * system lastlog facilities if they are available, but will fall back 24698937Sdes * to looking in wtmp/wtmpx if necessary 24798937Sdes * 24898937Sdes * Returns: 24998937Sdes * 0 on failure, or if user has never logged in 25098937Sdes * Time in seconds from the epoch if successful 25198937Sdes * 25298937Sdes * Useful preprocessor symbols: 25398937Sdes * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog 25498937Sdes * info 25598937Sdes * USE_LASTLOG: If set, indicates the presence of system lastlog 25698937Sdes * facilities. If this and DISABLE_LASTLOG are not set, 25798937Sdes * try to retrieve lastlog information from wtmp/wtmpx. 25898937Sdes */ 25998937Sdesunsigned int 26098937Sdeslogin_get_lastlog_time(const int uid) 26198937Sdes{ 26298937Sdes struct logininfo li; 26398937Sdes 26498937Sdes if (login_get_lastlog(&li, uid)) 26598937Sdes return li.tv_sec; 26698937Sdes else 26798937Sdes return 0; 26898937Sdes} 26998937Sdes 27098937Sdes/* login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry 27198937Sdes * 27298937Sdes * Retrieve a logininfo structure populated (only partially) with 27398937Sdes * information from the system lastlog data, or from wtmp/wtmpx if no 27498937Sdes * system lastlog information exists. 27598937Sdes * 27698937Sdes * Note this routine must be given a pre-allocated logininfo. 27798937Sdes * 27898937Sdes * Returns: 27998937Sdes * >0: A pointer to your struct logininfo if successful 28098937Sdes * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 28198937Sdes * 28298937Sdes */ 28398937Sdesstruct logininfo * 28498937Sdeslogin_get_lastlog(struct logininfo *li, const int uid) 28598937Sdes{ 28698937Sdes struct passwd *pw; 28798937Sdes 28898937Sdes memset(li, '\0', sizeof(*li)); 28998937Sdes li->uid = uid; 29098937Sdes 29198937Sdes /* 29298937Sdes * If we don't have a 'real' lastlog, we need the username to 29398937Sdes * reliably search wtmp(x) for the last login (see 29498937Sdes * wtmp_get_entry().) 29598937Sdes */ 29698937Sdes pw = getpwuid(uid); 29798937Sdes if (pw == NULL) 29898937Sdes fatal("login_get_lastlog: Cannot find account for uid %i", uid); 29998937Sdes 30098937Sdes /* No MIN_SIZEOF here - we absolutely *must not* truncate the 30198937Sdes * username */ 30298937Sdes strlcpy(li->username, pw->pw_name, sizeof(li->username)); 30398937Sdes 30498937Sdes if (getlast_entry(li)) 30598937Sdes return li; 30698937Sdes else 30798937Sdes return NULL; 30898937Sdes} 30998937Sdes 31098937Sdes 31198937Sdes/* login_alloc_entry(int, char*, char*, char*) - Allocate and initialise 31298937Sdes * a logininfo structure 31398937Sdes * 31498937Sdes * This function creates a new struct logininfo, a data structure 31598937Sdes * meant to carry the information required to portably record login info. 31698937Sdes * 31798937Sdes * Returns a pointer to a newly created struct logininfo. If memory 31898937Sdes * allocation fails, the program halts. 31998937Sdes */ 32098937Sdesstruct 32198937Sdeslogininfo *login_alloc_entry(int pid, const char *username, 32298937Sdes const char *hostname, const char *line) 32398937Sdes{ 32498937Sdes struct logininfo *newli; 32598937Sdes 32698937Sdes newli = (struct logininfo *) xmalloc (sizeof(*newli)); 32798937Sdes (void)login_init_entry(newli, pid, username, hostname, line); 32898937Sdes return newli; 32998937Sdes} 33098937Sdes 33198937Sdes 33298937Sdes/* login_free_entry(struct logininfo *) - free struct memory */ 33398937Sdesvoid 33498937Sdeslogin_free_entry(struct logininfo *li) 33598937Sdes{ 33698937Sdes xfree(li); 33798937Sdes} 33898937Sdes 33998937Sdes 34098937Sdes/* login_init_entry(struct logininfo *, int, char*, char*, char*) 34198937Sdes * - initialise a struct logininfo 34298937Sdes * 34398937Sdes * Populates a new struct logininfo, a data structure meant to carry 34498937Sdes * the information required to portably record login info. 34598937Sdes * 34698937Sdes * Returns: 1 34798937Sdes */ 34898937Sdesint 34998937Sdeslogin_init_entry(struct logininfo *li, int pid, const char *username, 35098937Sdes const char *hostname, const char *line) 35198937Sdes{ 35298937Sdes struct passwd *pw; 35398937Sdes 35498937Sdes memset(li, 0, sizeof(*li)); 35598937Sdes 35698937Sdes li->pid = pid; 35798937Sdes 35898937Sdes /* set the line information */ 35998937Sdes if (line) 36098937Sdes line_fullname(li->line, line, sizeof(li->line)); 36198937Sdes 36298937Sdes if (username) { 36398937Sdes strlcpy(li->username, username, sizeof(li->username)); 36498937Sdes pw = getpwnam(li->username); 36598937Sdes if (pw == NULL) 36698937Sdes fatal("login_init_entry: Cannot find user \"%s\"", li->username); 36798937Sdes li->uid = pw->pw_uid; 36898937Sdes } 36998937Sdes 37098937Sdes if (hostname) 37198937Sdes strlcpy(li->hostname, hostname, sizeof(li->hostname)); 37298937Sdes 37398937Sdes return 1; 37498937Sdes} 37598937Sdes 37698937Sdes/* login_set_current_time(struct logininfo *) - set the current time 37798937Sdes * 37898937Sdes * Set the current time in a logininfo structure. This function is 37998937Sdes * meant to eliminate the need to deal with system dependencies for 38098937Sdes * time handling. 38198937Sdes */ 38298937Sdesvoid 38398937Sdeslogin_set_current_time(struct logininfo *li) 38498937Sdes{ 38598937Sdes struct timeval tv; 38698937Sdes 38798937Sdes gettimeofday(&tv, NULL); 38898937Sdes 38998937Sdes li->tv_sec = tv.tv_sec; 39098937Sdes li->tv_usec = tv.tv_usec; 39198937Sdes} 39298937Sdes 39398937Sdes/* copy a sockaddr_* into our logininfo */ 39498937Sdesvoid 39598937Sdeslogin_set_addr(struct logininfo *li, const struct sockaddr *sa, 39698937Sdes const unsigned int sa_size) 39798937Sdes{ 39898937Sdes unsigned int bufsize = sa_size; 39998937Sdes 40098937Sdes /* make sure we don't overrun our union */ 40198937Sdes if (sizeof(li->hostaddr) < sa_size) 40298937Sdes bufsize = sizeof(li->hostaddr); 40398937Sdes 40498937Sdes memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize); 40598937Sdes} 40698937Sdes 40798937Sdes 40898937Sdes/** 40998937Sdes ** login_write: Call low-level recording functions based on autoconf 41098937Sdes ** results 41198937Sdes **/ 41298937Sdesint 41398937Sdeslogin_write (struct logininfo *li) 41498937Sdes{ 41598937Sdes#ifndef HAVE_CYGWIN 41698937Sdes if ((int)geteuid() != 0) { 41798937Sdes log("Attempt to write login records by non-root user (aborting)"); 41898937Sdes return 1; 41998937Sdes } 42098937Sdes#endif 42198937Sdes 42298937Sdes /* set the timestamp */ 42398937Sdes login_set_current_time(li); 42498937Sdes#ifdef USE_LOGIN 42598937Sdes syslogin_write_entry(li); 42698937Sdes#endif 42798937Sdes#ifdef USE_LASTLOG 42898937Sdes if (li->type == LTYPE_LOGIN) { 42998937Sdes lastlog_write_entry(li); 43098937Sdes } 43198937Sdes#endif 43298937Sdes#ifdef USE_UTMP 43398937Sdes utmp_write_entry(li); 43498937Sdes#endif 43598937Sdes#ifdef USE_WTMP 43698937Sdes wtmp_write_entry(li); 43798937Sdes#endif 43898937Sdes#ifdef USE_UTMPX 43998937Sdes utmpx_write_entry(li); 44098937Sdes#endif 44198937Sdes#ifdef USE_WTMPX 44298937Sdes wtmpx_write_entry(li); 44398937Sdes#endif 44498937Sdes return 0; 44598937Sdes} 44698937Sdes 44798937Sdes#ifdef LOGIN_NEEDS_UTMPX 44898937Sdesint 44998937Sdeslogin_utmp_only(struct logininfo *li) 45098937Sdes{ 45198937Sdes 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) { 82398937Sdes log("utmp_write_entry: tty not found"); 82498937Sdes return(1); 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) { 83398937Sdes (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); 83498937Sdes /* 83598937Sdes * Prevent luser from zero'ing out ut_host. 83698937Sdes * If the new ut_line is empty but the old one is not 83798937Sdes * and ut_line and ut_name match, preserve the old ut_line. 83898937Sdes */ 83998937Sdes if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && 84098937Sdes (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && 84198937Sdes (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && 84298937Sdes (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) { 84398937Sdes (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); 84498937Sdes } 84598937Sdes 84698937Sdes (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); 84798937Sdes if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) 84898937Sdes log("utmp_write_direct: error writing %s: %s", 84998937Sdes UTMP_FILE, strerror(errno)); 85098937Sdes 85198937Sdes (void)close(fd); 85298937Sdes return 1; 85398937Sdes } else { 85498937Sdes return 0; 85598937Sdes } 85698937Sdes} 85798937Sdes# endif /* UTMP_USE_LIBRARY */ 85898937Sdes 85998937Sdesstatic int 86098937Sdesutmp_perform_login(struct logininfo *li) 86198937Sdes{ 86298937Sdes struct utmp ut; 86398937Sdes 86498937Sdes construct_utmp(li, &ut); 86598937Sdes# ifdef UTMP_USE_LIBRARY 86698937Sdes if (!utmp_write_library(li, &ut)) { 86798937Sdes log("utmp_perform_login: utmp_write_library() failed"); 86898937Sdes return 0; 86998937Sdes } 87098937Sdes# else 87198937Sdes if (!utmp_write_direct(li, &ut)) { 87298937Sdes log("utmp_perform_login: utmp_write_direct() failed"); 87398937Sdes return 0; 87498937Sdes } 87598937Sdes# endif 87698937Sdes return 1; 87798937Sdes} 87898937Sdes 87998937Sdes 88098937Sdesstatic int 88198937Sdesutmp_perform_logout(struct logininfo *li) 88298937Sdes{ 88398937Sdes struct utmp ut; 88498937Sdes 88598937Sdes construct_utmp(li, &ut); 88698937Sdes# ifdef UTMP_USE_LIBRARY 88798937Sdes if (!utmp_write_library(li, &ut)) { 88898937Sdes log("utmp_perform_logout: utmp_write_library() failed"); 88998937Sdes return 0; 89098937Sdes } 89198937Sdes# else 89298937Sdes if (!utmp_write_direct(li, &ut)) { 89398937Sdes log("utmp_perform_logout: utmp_write_direct() failed"); 89498937Sdes return 0; 89598937Sdes } 89698937Sdes# endif 89798937Sdes return 1; 89898937Sdes} 89998937Sdes 90098937Sdes 90198937Sdesint 90298937Sdesutmp_write_entry(struct logininfo *li) 90398937Sdes{ 90498937Sdes switch(li->type) { 90598937Sdes case LTYPE_LOGIN: 90698937Sdes return utmp_perform_login(li); 90798937Sdes 90898937Sdes case LTYPE_LOGOUT: 90998937Sdes return utmp_perform_logout(li); 91098937Sdes 91198937Sdes default: 91298937Sdes log("utmp_write_entry: invalid type field"); 91398937Sdes return 0; 91498937Sdes } 91598937Sdes} 91698937Sdes#endif /* USE_UTMP */ 91798937Sdes 91898937Sdes 91998937Sdes/** 92098937Sdes ** Low-level utmpx functions 92198937Sdes **/ 92298937Sdes 92398937Sdes/* not much point if we don't want utmpx entries */ 92498937Sdes#ifdef USE_UTMPX 92598937Sdes 92698937Sdes/* if we have the wherewithall, use pututxline etc. */ 92798937Sdes# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \ 92898937Sdes defined(HAVE_PUTUTXLINE) 92998937Sdes# define UTMPX_USE_LIBRARY 93098937Sdes# endif 93198937Sdes 93298937Sdes 93398937Sdes/* write a utmpx entry with the system's help (pututxline() and pals) */ 93498937Sdes# ifdef UTMPX_USE_LIBRARY 93598937Sdesstatic int 93698937Sdesutmpx_write_library(struct logininfo *li, struct utmpx *utx) 93798937Sdes{ 93898937Sdes setutxent(); 93998937Sdes pututxline(utx); 94098937Sdes 94198937Sdes# ifdef HAVE_ENDUTXENT 94298937Sdes endutxent(); 94398937Sdes# endif 94498937Sdes return 1; 94598937Sdes} 94698937Sdes 94798937Sdes# else /* UTMPX_USE_LIBRARY */ 94898937Sdes 94998937Sdes/* write a utmp entry direct to the file */ 95098937Sdesstatic int 95198937Sdesutmpx_write_direct(struct logininfo *li, struct utmpx *utx) 95298937Sdes{ 95398937Sdes log("utmpx_write_direct: not implemented!"); 95498937Sdes return 0; 95598937Sdes} 95698937Sdes# endif /* UTMPX_USE_LIBRARY */ 95798937Sdes 95898937Sdesstatic int 95998937Sdesutmpx_perform_login(struct logininfo *li) 96098937Sdes{ 96198937Sdes struct utmpx utx; 96298937Sdes 96398937Sdes construct_utmpx(li, &utx); 96498937Sdes# ifdef UTMPX_USE_LIBRARY 96598937Sdes if (!utmpx_write_library(li, &utx)) { 96698937Sdes log("utmpx_perform_login: utmp_write_library() failed"); 96798937Sdes return 0; 96898937Sdes } 96998937Sdes# else 97098937Sdes if (!utmpx_write_direct(li, &ut)) { 97198937Sdes log("utmpx_perform_login: utmp_write_direct() failed"); 97298937Sdes return 0; 97398937Sdes } 97498937Sdes# endif 97598937Sdes return 1; 97698937Sdes} 97798937Sdes 97898937Sdes 97998937Sdesstatic int 98098937Sdesutmpx_perform_logout(struct logininfo *li) 98198937Sdes{ 98298937Sdes struct utmpx utx; 98398937Sdes 98498937Sdes construct_utmpx(li, &utx); 98598937Sdes# ifdef HAVE_ID_IN_UTMPX 98698937Sdes line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); 98798937Sdes# endif 98898937Sdes# ifdef HAVE_TYPE_IN_UTMPX 98998937Sdes utx.ut_type = DEAD_PROCESS; 99098937Sdes# endif 99198937Sdes 99298937Sdes# ifdef UTMPX_USE_LIBRARY 99398937Sdes utmpx_write_library(li, &utx); 99498937Sdes# else 99598937Sdes utmpx_write_direct(li, &utx); 99698937Sdes# endif 99798937Sdes return 1; 99898937Sdes} 99998937Sdes 100098937Sdesint 100198937Sdesutmpx_write_entry(struct logininfo *li) 100298937Sdes{ 100398937Sdes switch(li->type) { 100498937Sdes case LTYPE_LOGIN: 100598937Sdes return utmpx_perform_login(li); 100698937Sdes case LTYPE_LOGOUT: 100798937Sdes return utmpx_perform_logout(li); 100898937Sdes default: 100998937Sdes log("utmpx_write_entry: invalid type field"); 101098937Sdes return 0; 101198937Sdes } 101298937Sdes} 101398937Sdes#endif /* USE_UTMPX */ 101498937Sdes 101598937Sdes 101698937Sdes/** 101798937Sdes ** Low-level wtmp functions 101898937Sdes **/ 101998937Sdes 102098937Sdes#ifdef USE_WTMP 102198937Sdes 102298937Sdes/* write a wtmp entry direct to the end of the file */ 102398937Sdes/* This is a slight modification of code in OpenBSD's logwtmp.c */ 102498937Sdesstatic int 102598937Sdeswtmp_write(struct logininfo *li, struct utmp *ut) 102698937Sdes{ 102798937Sdes struct stat buf; 102898937Sdes int fd, ret = 1; 102998937Sdes 103098937Sdes if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 103198937Sdes log("wtmp_write: problem writing %s: %s", 103298937Sdes WTMP_FILE, strerror(errno)); 103398937Sdes return 0; 103498937Sdes } 103598937Sdes if (fstat(fd, &buf) == 0) 103698937Sdes if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) { 103798937Sdes ftruncate(fd, buf.st_size); 103898937Sdes log("wtmp_write: problem writing %s: %s", 103998937Sdes WTMP_FILE, strerror(errno)); 104098937Sdes ret = 0; 104198937Sdes } 104298937Sdes (void)close(fd); 104398937Sdes return ret; 104498937Sdes} 104598937Sdes 104698937Sdesstatic int 104798937Sdeswtmp_perform_login(struct logininfo *li) 104898937Sdes{ 104998937Sdes struct utmp ut; 105098937Sdes 105198937Sdes construct_utmp(li, &ut); 105298937Sdes return wtmp_write(li, &ut); 105398937Sdes} 105498937Sdes 105598937Sdes 105698937Sdesstatic int 105798937Sdeswtmp_perform_logout(struct logininfo *li) 105898937Sdes{ 105998937Sdes struct utmp ut; 106098937Sdes 106198937Sdes construct_utmp(li, &ut); 106298937Sdes return wtmp_write(li, &ut); 106398937Sdes} 106498937Sdes 106598937Sdes 106698937Sdesint 106798937Sdeswtmp_write_entry(struct logininfo *li) 106898937Sdes{ 106998937Sdes switch(li->type) { 107098937Sdes case LTYPE_LOGIN: 107198937Sdes return wtmp_perform_login(li); 107298937Sdes case LTYPE_LOGOUT: 107398937Sdes return wtmp_perform_logout(li); 107498937Sdes default: 107598937Sdes log("wtmp_write_entry: invalid type field"); 107698937Sdes return 0; 107798937Sdes } 107898937Sdes} 107998937Sdes 108098937Sdes 108198937Sdes/* Notes on fetching login data from wtmp/wtmpx 108298937Sdes * 108398937Sdes * Logouts are usually recorded with (amongst other things) a blank 108498937Sdes * username on a given tty line. However, some systems (HP-UX is one) 108598937Sdes * leave all fields set, but change the ut_type field to DEAD_PROCESS. 108698937Sdes * 108798937Sdes * Since we're only looking for logins here, we know that the username 108898937Sdes * must be set correctly. On systems that leave it in, we check for 108998937Sdes * ut_type==USER_PROCESS (indicating a login.) 109098937Sdes * 109198937Sdes * Portability: Some systems may set something other than USER_PROCESS 109298937Sdes * to indicate a login process. I don't know of any as I write. Also, 109398937Sdes * it's possible that some systems may both leave the username in 109498937Sdes * place and not have ut_type. 109598937Sdes */ 109698937Sdes 109798937Sdes/* return true if this wtmp entry indicates a login */ 109898937Sdesstatic int 109998937Sdeswtmp_islogin(struct logininfo *li, struct utmp *ut) 110098937Sdes{ 110198937Sdes if (strncmp(li->username, ut->ut_name, 110298937Sdes MIN_SIZEOF(li->username, ut->ut_name)) == 0) { 110398937Sdes# ifdef HAVE_TYPE_IN_UTMP 110498937Sdes if (ut->ut_type & USER_PROCESS) 110598937Sdes return 1; 110698937Sdes# else 110798937Sdes return 1; 110898937Sdes# endif 110998937Sdes } 111098937Sdes return 0; 111198937Sdes} 111298937Sdes 111398937Sdesint 111498937Sdeswtmp_get_entry(struct logininfo *li) 111598937Sdes{ 111698937Sdes struct stat st; 111798937Sdes struct utmp ut; 111898937Sdes int fd, found=0; 111998937Sdes 112098937Sdes /* Clear the time entries in our logininfo */ 112198937Sdes li->tv_sec = li->tv_usec = 0; 112298937Sdes 112398937Sdes if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { 112498937Sdes log("wtmp_get_entry: problem opening %s: %s", 112598937Sdes WTMP_FILE, strerror(errno)); 112698937Sdes return 0; 112798937Sdes } 112898937Sdes if (fstat(fd, &st) != 0) { 112998937Sdes log("wtmp_get_entry: couldn't stat %s: %s", 113098937Sdes WTMP_FILE, strerror(errno)); 113198937Sdes close(fd); 113298937Sdes return 0; 113398937Sdes } 113498937Sdes 113598937Sdes /* Seek to the start of the last struct utmp */ 113698937Sdes if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { 113798937Sdes /* Looks like we've got a fresh wtmp file */ 113898937Sdes close(fd); 113998937Sdes return 0; 114098937Sdes } 114198937Sdes 114298937Sdes while (!found) { 114398937Sdes if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { 114498937Sdes log("wtmp_get_entry: read of %s failed: %s", 114598937Sdes WTMP_FILE, strerror(errno)); 114698937Sdes close (fd); 114798937Sdes return 0; 114898937Sdes } 114998937Sdes if ( wtmp_islogin(li, &ut) ) { 115098937Sdes found = 1; 115198937Sdes /* We've already checked for a time in struct 115298937Sdes * utmp, in login_getlast(). */ 115398937Sdes# ifdef HAVE_TIME_IN_UTMP 115498937Sdes li->tv_sec = ut.ut_time; 115598937Sdes# else 115698937Sdes# if HAVE_TV_IN_UTMP 115798937Sdes li->tv_sec = ut.ut_tv.tv_sec; 115898937Sdes# endif 115998937Sdes# endif 116098937Sdes line_fullname(li->line, ut.ut_line, 116198937Sdes MIN_SIZEOF(li->line, ut.ut_line)); 116298937Sdes# ifdef HAVE_HOST_IN_UTMP 116398937Sdes strlcpy(li->hostname, ut.ut_host, 116498937Sdes MIN_SIZEOF(li->hostname, ut.ut_host)); 116598937Sdes# endif 116698937Sdes continue; 116798937Sdes } 116898937Sdes /* Seek back 2 x struct utmp */ 116998937Sdes if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { 117098937Sdes /* We've found the start of the file, so quit */ 117198937Sdes close (fd); 117298937Sdes return 0; 117398937Sdes } 117498937Sdes } 117598937Sdes 117698937Sdes /* We found an entry. Tidy up and return */ 117798937Sdes close(fd); 117898937Sdes return 1; 117998937Sdes} 118098937Sdes# endif /* USE_WTMP */ 118198937Sdes 118298937Sdes 118398937Sdes/** 118498937Sdes ** Low-level wtmpx functions 118598937Sdes **/ 118698937Sdes 118798937Sdes#ifdef USE_WTMPX 118898937Sdes/* write a wtmpx entry direct to the end of the file */ 118998937Sdes/* This is a slight modification of code in OpenBSD's logwtmp.c */ 119098937Sdesstatic int 119198937Sdeswtmpx_write(struct logininfo *li, struct utmpx *utx) 119298937Sdes{ 119398937Sdes struct stat buf; 119498937Sdes int fd, ret = 1; 119598937Sdes 119698937Sdes if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 119798937Sdes log("wtmpx_write: problem opening %s: %s", 119898937Sdes WTMPX_FILE, strerror(errno)); 119998937Sdes return 0; 120098937Sdes } 120198937Sdes 120298937Sdes if (fstat(fd, &buf) == 0) 120398937Sdes if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) { 120498937Sdes ftruncate(fd, buf.st_size); 120598937Sdes log("wtmpx_write: problem writing %s: %s", 120698937Sdes WTMPX_FILE, strerror(errno)); 120798937Sdes ret = 0; 120898937Sdes } 120998937Sdes (void)close(fd); 121098937Sdes 121198937Sdes return ret; 121298937Sdes} 121398937Sdes 121498937Sdes 121598937Sdesstatic int 121698937Sdeswtmpx_perform_login(struct logininfo *li) 121798937Sdes{ 121898937Sdes struct utmpx utx; 121998937Sdes 122098937Sdes construct_utmpx(li, &utx); 122198937Sdes return wtmpx_write(li, &utx); 122298937Sdes} 122398937Sdes 122498937Sdes 122598937Sdesstatic int 122698937Sdeswtmpx_perform_logout(struct logininfo *li) 122798937Sdes{ 122898937Sdes struct utmpx utx; 122998937Sdes 123098937Sdes construct_utmpx(li, &utx); 123198937Sdes return wtmpx_write(li, &utx); 123298937Sdes} 123398937Sdes 123498937Sdes 123598937Sdesint 123698937Sdeswtmpx_write_entry(struct logininfo *li) 123798937Sdes{ 123898937Sdes switch(li->type) { 123998937Sdes case LTYPE_LOGIN: 124098937Sdes return wtmpx_perform_login(li); 124198937Sdes case LTYPE_LOGOUT: 124298937Sdes return wtmpx_perform_logout(li); 124398937Sdes default: 124498937Sdes log("wtmpx_write_entry: invalid type field"); 124598937Sdes return 0; 124698937Sdes } 124798937Sdes} 124898937Sdes 124998937Sdes/* Please see the notes above wtmp_islogin() for information about the 125098937Sdes next two functions */ 125198937Sdes 125298937Sdes/* Return true if this wtmpx entry indicates a login */ 125398937Sdesstatic int 125498937Sdeswtmpx_islogin(struct logininfo *li, struct utmpx *utx) 125598937Sdes{ 125698937Sdes if ( strncmp(li->username, utx->ut_name, 125798937Sdes MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) { 125898937Sdes# ifdef HAVE_TYPE_IN_UTMPX 125998937Sdes if (utx->ut_type == USER_PROCESS) 126098937Sdes return 1; 126198937Sdes# else 126298937Sdes return 1; 126398937Sdes# endif 126498937Sdes } 126598937Sdes return 0; 126698937Sdes} 126798937Sdes 126898937Sdes 126998937Sdesint 127098937Sdeswtmpx_get_entry(struct logininfo *li) 127198937Sdes{ 127298937Sdes struct stat st; 127398937Sdes struct utmpx utx; 127498937Sdes int fd, found=0; 127598937Sdes 127698937Sdes /* Clear the time entries */ 127798937Sdes li->tv_sec = li->tv_usec = 0; 127898937Sdes 127998937Sdes if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { 128098937Sdes log("wtmpx_get_entry: problem opening %s: %s", 128198937Sdes WTMPX_FILE, strerror(errno)); 128298937Sdes return 0; 128398937Sdes } 128498937Sdes if (fstat(fd, &st) != 0) { 128598937Sdes log("wtmpx_get_entry: couldn't stat %s: %s", 1286106130Sdes WTMPX_FILE, strerror(errno)); 128798937Sdes close(fd); 128898937Sdes return 0; 128998937Sdes } 129098937Sdes 129198937Sdes /* Seek to the start of the last struct utmpx */ 129298937Sdes if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { 129398937Sdes /* probably a newly rotated wtmpx file */ 129498937Sdes close(fd); 129598937Sdes return 0; 129698937Sdes } 129798937Sdes 129898937Sdes while (!found) { 129998937Sdes if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { 130098937Sdes log("wtmpx_get_entry: read of %s failed: %s", 130198937Sdes WTMPX_FILE, strerror(errno)); 130298937Sdes close (fd); 130398937Sdes return 0; 130498937Sdes } 130598937Sdes /* Logouts are recorded as a blank username on a particular line. 130698937Sdes * So, we just need to find the username in struct utmpx */ 130798937Sdes if ( wtmpx_islogin(li, &utx) ) { 1308106130Sdes found = 1; 130998937Sdes# ifdef HAVE_TV_IN_UTMPX 131098937Sdes li->tv_sec = utx.ut_tv.tv_sec; 131198937Sdes# else 131298937Sdes# ifdef HAVE_TIME_IN_UTMPX 131398937Sdes li->tv_sec = utx.ut_time; 131498937Sdes# endif 131598937Sdes# endif 131698937Sdes line_fullname(li->line, utx.ut_line, sizeof(li->line)); 131798937Sdes# ifdef HAVE_HOST_IN_UTMPX 131898937Sdes strlcpy(li->hostname, utx.ut_host, 131998937Sdes MIN_SIZEOF(li->hostname, utx.ut_host)); 132098937Sdes# endif 132198937Sdes continue; 132298937Sdes } 132398937Sdes if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { 132498937Sdes close (fd); 132598937Sdes return 0; 132698937Sdes } 132798937Sdes } 132898937Sdes 132998937Sdes close(fd); 133098937Sdes return 1; 133198937Sdes} 133298937Sdes#endif /* USE_WTMPX */ 133398937Sdes 133498937Sdes/** 133598937Sdes ** Low-level libutil login() functions 133698937Sdes **/ 133798937Sdes 133898937Sdes#ifdef USE_LOGIN 133998937Sdesstatic int 134098937Sdessyslogin_perform_login(struct logininfo *li) 134198937Sdes{ 134298937Sdes struct utmp *ut; 134398937Sdes 134498937Sdes if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) { 134598937Sdes log("syslogin_perform_login: couldn't malloc()"); 134698937Sdes return 0; 134798937Sdes } 134898937Sdes construct_utmp(li, ut); 134998937Sdes login(ut); 1350113911Sdes free(ut); 135198937Sdes 135298937Sdes return 1; 135398937Sdes} 135498937Sdes 135598937Sdesstatic int 135698937Sdessyslogin_perform_logout(struct logininfo *li) 135798937Sdes{ 135898937Sdes# ifdef HAVE_LOGOUT 135998937Sdes char line[8]; 136098937Sdes 136198937Sdes (void)line_stripname(line, li->line, sizeof(line)); 136298937Sdes 136398937Sdes if (!logout(line)) { 136498937Sdes log("syslogin_perform_logout: logout() returned an error"); 136598937Sdes# ifdef HAVE_LOGWTMP 136698937Sdes } else { 136798937Sdes logwtmp(line, "", ""); 136898937Sdes# endif 136998937Sdes } 137098937Sdes /* FIXME: (ATL - if the need arises) What to do if we have 137198937Sdes * login, but no logout? what if logout but no logwtmp? All 137298937Sdes * routines are in libutil so they should all be there, 137398937Sdes * but... */ 137498937Sdes# endif 137598937Sdes return 1; 137698937Sdes} 137798937Sdes 137898937Sdesint 137998937Sdessyslogin_write_entry(struct logininfo *li) 138098937Sdes{ 138198937Sdes switch (li->type) { 138298937Sdes case LTYPE_LOGIN: 138398937Sdes return syslogin_perform_login(li); 138498937Sdes case LTYPE_LOGOUT: 138598937Sdes return syslogin_perform_logout(li); 138698937Sdes default: 138798937Sdes log("syslogin_write_entry: Invalid type field"); 138898937Sdes return 0; 138998937Sdes } 139098937Sdes} 139198937Sdes#endif /* USE_LOGIN */ 139298937Sdes 139398937Sdes/* end of file log-syslogin.c */ 139498937Sdes 139598937Sdes/** 139698937Sdes ** Low-level lastlog functions 139798937Sdes **/ 139898937Sdes 139998937Sdes#ifdef USE_LASTLOG 140098937Sdes#define LL_FILE 1 140198937Sdes#define LL_DIR 2 140298937Sdes#define LL_OTHER 3 140398937Sdes 140498937Sdesstatic void 140598937Sdeslastlog_construct(struct logininfo *li, struct lastlog *last) 140698937Sdes{ 140798937Sdes /* clear the structure */ 140898937Sdes memset(last, '\0', sizeof(*last)); 140998937Sdes 141098937Sdes (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line)); 141198937Sdes strlcpy(last->ll_host, li->hostname, 141298937Sdes MIN_SIZEOF(last->ll_host, li->hostname)); 141398937Sdes last->ll_time = li->tv_sec; 141498937Sdes} 141598937Sdes 141698937Sdesstatic int 141798937Sdeslastlog_filetype(char *filename) 141898937Sdes{ 141998937Sdes struct stat st; 142098937Sdes 142198937Sdes if (stat(LASTLOG_FILE, &st) != 0) { 142298937Sdes log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE, 142398937Sdes strerror(errno)); 142498937Sdes return 0; 142598937Sdes } 142698937Sdes if (S_ISDIR(st.st_mode)) 142798937Sdes return LL_DIR; 142898937Sdes else if (S_ISREG(st.st_mode)) 142998937Sdes return LL_FILE; 143098937Sdes else 143198937Sdes return LL_OTHER; 143298937Sdes} 143398937Sdes 143498937Sdes 143598937Sdes/* open the file (using filemode) and seek to the login entry */ 143698937Sdesstatic int 143798937Sdeslastlog_openseek(struct logininfo *li, int *fd, int filemode) 143898937Sdes{ 143998937Sdes off_t offset; 144098937Sdes int type; 144198937Sdes char lastlog_file[1024]; 144298937Sdes 144398937Sdes type = lastlog_filetype(LASTLOG_FILE); 144498937Sdes switch (type) { 144598937Sdes case LL_FILE: 144698937Sdes strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); 144798937Sdes break; 144898937Sdes case LL_DIR: 144998937Sdes snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", 145098937Sdes LASTLOG_FILE, li->username); 145198937Sdes break; 145298937Sdes default: 145398937Sdes log("lastlog_openseek: %.100s is not a file or directory!", 145498937Sdes LASTLOG_FILE); 145598937Sdes return 0; 145698937Sdes } 145798937Sdes 145898937Sdes *fd = open(lastlog_file, filemode); 145998937Sdes if ( *fd < 0) { 146098937Sdes debug("lastlog_openseek: Couldn't open %s: %s", 146198937Sdes lastlog_file, strerror(errno)); 146298937Sdes return 0; 146398937Sdes } 146498937Sdes 146598937Sdes if (type == LL_FILE) { 146698937Sdes /* find this uid's offset in the lastlog file */ 146798937Sdes offset = (off_t) ((long)li->uid * sizeof(struct lastlog)); 146898937Sdes 146998937Sdes if ( lseek(*fd, offset, SEEK_SET) != offset ) { 147098937Sdes log("lastlog_openseek: %s->lseek(): %s", 147198937Sdes lastlog_file, strerror(errno)); 147298937Sdes return 0; 147398937Sdes } 147498937Sdes } 147598937Sdes 147698937Sdes return 1; 147798937Sdes} 147898937Sdes 147998937Sdesstatic int 148098937Sdeslastlog_perform_login(struct logininfo *li) 148198937Sdes{ 148298937Sdes struct lastlog last; 148398937Sdes int fd; 148498937Sdes 148598937Sdes /* create our struct lastlog */ 148698937Sdes lastlog_construct(li, &last); 148798937Sdes 148898937Sdes if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) 148998937Sdes return(0); 149098937Sdes 149198937Sdes /* write the entry */ 149298937Sdes if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) { 149398937Sdes close(fd); 149498937Sdes log("lastlog_write_filemode: Error writing to %s: %s", 149598937Sdes LASTLOG_FILE, strerror(errno)); 149698937Sdes return 0; 149798937Sdes } 149898937Sdes 149998937Sdes close(fd); 150098937Sdes return 1; 150198937Sdes} 150298937Sdes 150398937Sdesint 150498937Sdeslastlog_write_entry(struct logininfo *li) 150598937Sdes{ 150698937Sdes switch(li->type) { 150798937Sdes case LTYPE_LOGIN: 150898937Sdes return lastlog_perform_login(li); 150998937Sdes default: 151098937Sdes log("lastlog_write_entry: Invalid type field"); 151198937Sdes return 0; 151298937Sdes } 151398937Sdes} 151498937Sdes 151598937Sdesstatic void 151698937Sdeslastlog_populate_entry(struct logininfo *li, struct lastlog *last) 151798937Sdes{ 151898937Sdes line_fullname(li->line, last->ll_line, sizeof(li->line)); 151998937Sdes strlcpy(li->hostname, last->ll_host, 152098937Sdes MIN_SIZEOF(li->hostname, last->ll_host)); 152198937Sdes li->tv_sec = last->ll_time; 152298937Sdes} 152398937Sdes 152498937Sdesint 152598937Sdeslastlog_get_entry(struct logininfo *li) 152698937Sdes{ 152798937Sdes struct lastlog last; 1528113911Sdes int fd, ret; 152998937Sdes 153098937Sdes if (!lastlog_openseek(li, &fd, O_RDONLY)) 1531113911Sdes return (0); 153298937Sdes 1533113911Sdes ret = atomicio(read, fd, &last, sizeof(last)); 1534113911Sdes close(fd); 1535113911Sdes 1536113911Sdes switch (ret) { 1537113911Sdes case 0: 1538113911Sdes memset(&last, '\0', sizeof(last)); 1539113911Sdes /* FALLTHRU */ 1540113911Sdes case sizeof(last): 1541113911Sdes lastlog_populate_entry(li, &last); 1542113911Sdes return (1); 1543113911Sdes case -1: 1544113911Sdes error("%s: Error reading from %s: %s", __func__, 154598937Sdes LASTLOG_FILE, strerror(errno)); 1546113911Sdes return (0); 1547113911Sdes default: 1548113911Sdes error("%s: Error reading from %s: Expecting %d, got %d", 1549113911Sdes __func__, LASTLOG_FILE, sizeof(last), ret); 1550113911Sdes return (0); 155198937Sdes } 155298937Sdes 1553113911Sdes /* NOTREACHED */ 1554113911Sdes return (0); 155598937Sdes} 155698937Sdes#endif /* USE_LASTLOG */ 1557