loginrec.c revision 124211
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 161124211SdesRCSID("$Id: loginrec.c,v 1.52 2003/07/06 05:20:46 dtucker Exp $"); 16299768SdesRCSID("$FreeBSD: head/crypto/openssh/loginrec.c 124211 2004-01-07 11:16:27Z 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 43998937Sdes return 0; 44098937Sdes} 44198937Sdes 44298937Sdes#ifdef LOGIN_NEEDS_UTMPX 44398937Sdesint 44498937Sdeslogin_utmp_only(struct logininfo *li) 44598937Sdes{ 44698937Sdes li->type = LTYPE_LOGIN; 44798937Sdes login_set_current_time(li); 44898937Sdes# ifdef USE_UTMP 44998937Sdes utmp_write_entry(li); 45098937Sdes# endif 45198937Sdes# ifdef USE_WTMP 45298937Sdes wtmp_write_entry(li); 45398937Sdes# endif 45498937Sdes# ifdef USE_UTMPX 45598937Sdes utmpx_write_entry(li); 45698937Sdes# endif 45798937Sdes# ifdef USE_WTMPX 45898937Sdes wtmpx_write_entry(li); 45998937Sdes# endif 46098937Sdes return 0; 46198937Sdes} 46298937Sdes#endif 46398937Sdes 46498937Sdes/** 46598937Sdes ** getlast_entry: Call low-level functions to retrieve the last login 46698937Sdes ** time. 46798937Sdes **/ 46898937Sdes 46998937Sdes/* take the uid in li and return the last login time */ 47098937Sdesint 47198937Sdesgetlast_entry(struct logininfo *li) 47298937Sdes{ 47398937Sdes#ifdef USE_LASTLOG 47498937Sdes return(lastlog_get_entry(li)); 47598937Sdes#else /* !USE_LASTLOG */ 47698937Sdes 47798937Sdes#ifdef DISABLE_LASTLOG 47898937Sdes /* On some systems we shouldn't even try to obtain last login 47998937Sdes * time, e.g. AIX */ 48098937Sdes return 0; 48198937Sdes# else /* DISABLE_LASTLOG */ 48298937Sdes /* Try to retrieve the last login time from wtmp */ 48398937Sdes# if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) 48498937Sdes /* retrieve last login time from utmp */ 48598937Sdes return (wtmp_get_entry(li)); 48698937Sdes# else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */ 48798937Sdes /* If wtmp isn't available, try wtmpx */ 48898937Sdes# if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX)) 48998937Sdes /* retrieve last login time from utmpx */ 49098937Sdes return (wtmpx_get_entry(li)); 49198937Sdes# else 49298937Sdes /* Give up: No means of retrieving last login time */ 49398937Sdes return 0; 49498937Sdes# endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */ 49598937Sdes# endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */ 49698937Sdes# endif /* DISABLE_LASTLOG */ 49798937Sdes#endif /* USE_LASTLOG */ 49898937Sdes} 49998937Sdes 50098937Sdes 50198937Sdes 50298937Sdes/* 50398937Sdes * 'line' string utility functions 50498937Sdes * 50598937Sdes * These functions process the 'line' string into one of three forms: 50698937Sdes * 50798937Sdes * 1. The full filename (including '/dev') 50898937Sdes * 2. The stripped name (excluding '/dev') 50998937Sdes * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00 51098937Sdes * /dev/pts/1 -> ts/1 ) 51198937Sdes * 51298937Sdes * Form 3 is used on some systems to identify a .tmp.? entry when 51398937Sdes * attempting to remove it. Typically both addition and removal is 51498937Sdes * performed by one application - say, sshd - so as long as the choice 51598937Sdes * uniquely identifies a terminal it's ok. 51698937Sdes */ 51798937Sdes 51898937Sdes 51998937Sdes/* line_fullname(): add the leading '/dev/' if it doesn't exist make 52098937Sdes * sure dst has enough space, if not just copy src (ugh) */ 52198937Sdeschar * 52298937Sdesline_fullname(char *dst, const char *src, int dstsize) 52398937Sdes{ 52498937Sdes memset(dst, '\0', dstsize); 52598937Sdes if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) { 52698937Sdes strlcpy(dst, src, dstsize); 52798937Sdes } else { 52898937Sdes strlcpy(dst, "/dev/", dstsize); 52998937Sdes strlcat(dst, src, dstsize); 53098937Sdes } 53198937Sdes return dst; 53298937Sdes} 53398937Sdes 53498937Sdes/* line_stripname(): strip the leading '/dev' if it exists, return dst */ 53598937Sdeschar * 53698937Sdesline_stripname(char *dst, const char *src, int dstsize) 53798937Sdes{ 53898937Sdes memset(dst, '\0', dstsize); 53998937Sdes if (strncmp(src, "/dev/", 5) == 0) 54098937Sdes strlcpy(dst, src + 5, dstsize); 54198937Sdes else 54298937Sdes strlcpy(dst, src, dstsize); 54398937Sdes return dst; 54498937Sdes} 54598937Sdes 54698937Sdes/* line_abbrevname(): Return the abbreviated (usually four-character) 54798937Sdes * form of the line (Just use the last <dstsize> characters of the 54898937Sdes * full name.) 54998937Sdes * 55098937Sdes * NOTE: use strncpy because we do NOT necessarily want zero 55198937Sdes * termination */ 55298937Sdeschar * 55398937Sdesline_abbrevname(char *dst, const char *src, int dstsize) 55498937Sdes{ 55598937Sdes size_t len; 55698937Sdes 55798937Sdes memset(dst, '\0', dstsize); 55898937Sdes 55998937Sdes /* Always skip prefix if present */ 56098937Sdes if (strncmp(src, "/dev/", 5) == 0) 56198937Sdes src += 5; 56298937Sdes 56398937Sdes#ifdef WITH_ABBREV_NO_TTY 56498937Sdes if (strncmp(src, "tty", 3) == 0) 56598937Sdes src += 3; 56698937Sdes#endif 56798937Sdes 56898937Sdes len = strlen(src); 56998937Sdes 57098937Sdes if (len > 0) { 57198937Sdes if (((int)len - dstsize) > 0) 57298937Sdes src += ((int)len - dstsize); 57398937Sdes 57498937Sdes /* note: _don't_ change this to strlcpy */ 57598937Sdes strncpy(dst, src, (size_t)dstsize); 57698937Sdes } 57798937Sdes 57898937Sdes return dst; 57998937Sdes} 58098937Sdes 58198937Sdes/** 58298937Sdes ** utmp utility functions 58398937Sdes ** 58498937Sdes ** These functions manipulate struct utmp, taking system differences 58598937Sdes ** into account. 58698937Sdes **/ 58798937Sdes 58898937Sdes#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN) 58998937Sdes 59098937Sdes/* build the utmp structure */ 59198937Sdesvoid 59298937Sdesset_utmp_time(struct logininfo *li, struct utmp *ut) 59398937Sdes{ 59498937Sdes# ifdef HAVE_TV_IN_UTMP 59598937Sdes ut->ut_tv.tv_sec = li->tv_sec; 59698937Sdes ut->ut_tv.tv_usec = li->tv_usec; 59798937Sdes# else 59898937Sdes# ifdef HAVE_TIME_IN_UTMP 59998937Sdes ut->ut_time = li->tv_sec; 60098937Sdes# endif 60198937Sdes# endif 60298937Sdes} 60398937Sdes 60498937Sdesvoid 60598937Sdesconstruct_utmp(struct logininfo *li, 60698937Sdes struct utmp *ut) 60798937Sdes{ 608113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 609113911Sdes struct sockaddr_in6 *sa6; 610113911Sdes# endif 61198937Sdes memset(ut, '\0', sizeof(*ut)); 61298937Sdes 61398937Sdes /* First fill out fields used for both logins and logouts */ 61498937Sdes 61598937Sdes# ifdef HAVE_ID_IN_UTMP 61698937Sdes line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id)); 61798937Sdes# endif 61898937Sdes 61998937Sdes# ifdef HAVE_TYPE_IN_UTMP 62098937Sdes /* This is done here to keep utmp constants out of struct logininfo */ 62198937Sdes switch (li->type) { 62298937Sdes case LTYPE_LOGIN: 62398937Sdes ut->ut_type = USER_PROCESS; 624106130Sdes#ifdef _UNICOS 62598937Sdes cray_set_tmpdir(ut); 62698937Sdes#endif 62798937Sdes break; 62898937Sdes case LTYPE_LOGOUT: 62998937Sdes ut->ut_type = DEAD_PROCESS; 630106130Sdes#ifdef _UNICOS 63198937Sdes cray_retain_utmp(ut, li->pid); 63298937Sdes#endif 63398937Sdes break; 63498937Sdes } 63598937Sdes# endif 63698937Sdes set_utmp_time(li, ut); 63798937Sdes 63898937Sdes line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line)); 63998937Sdes 64098937Sdes# ifdef HAVE_PID_IN_UTMP 64198937Sdes ut->ut_pid = li->pid; 64298937Sdes# endif 64398937Sdes 64498937Sdes /* If we're logging out, leave all other fields blank */ 64598937Sdes if (li->type == LTYPE_LOGOUT) 64698937Sdes return; 64798937Sdes 64898937Sdes /* 64998937Sdes * These fields are only used when logging in, and are blank 65098937Sdes * for logouts. 65198937Sdes */ 65298937Sdes 65398937Sdes /* Use strncpy because we don't necessarily want null termination */ 65498937Sdes strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username)); 65598937Sdes# ifdef HAVE_HOST_IN_UTMP 65699768Sdes realhostname_sa(ut->ut_host, sizeof ut->ut_host, 65799768Sdes &li->hostaddr.sa, li->hostaddr.sa.sa_len); 65898937Sdes# endif 65998937Sdes# ifdef HAVE_ADDR_IN_UTMP 66098937Sdes /* this is just a 32-bit IP address */ 66198937Sdes if (li->hostaddr.sa.sa_family == AF_INET) 66298937Sdes ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 66398937Sdes# endif 664113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 665113911Sdes /* this is just a 128-bit IPv6 address */ 666113911Sdes if (li->hostaddr.sa.sa_family == AF_INET6) { 667113911Sdes sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 668113911Sdes memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 669113911Sdes if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 670113911Sdes ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; 671113911Sdes ut->ut_addr_v6[1] = 0; 672113911Sdes ut->ut_addr_v6[2] = 0; 673113911Sdes ut->ut_addr_v6[3] = 0; 674113911Sdes } 675113911Sdes } 676113911Sdes# endif 67798937Sdes} 67898937Sdes#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */ 67998937Sdes 68098937Sdes/** 68198937Sdes ** utmpx utility functions 68298937Sdes ** 68398937Sdes ** These functions manipulate struct utmpx, accounting for system 68498937Sdes ** variations. 68598937Sdes **/ 68698937Sdes 68798937Sdes#if defined(USE_UTMPX) || defined (USE_WTMPX) 68898937Sdes/* build the utmpx structure */ 68998937Sdesvoid 69098937Sdesset_utmpx_time(struct logininfo *li, struct utmpx *utx) 69198937Sdes{ 69298937Sdes# ifdef HAVE_TV_IN_UTMPX 69398937Sdes utx->ut_tv.tv_sec = li->tv_sec; 69498937Sdes utx->ut_tv.tv_usec = li->tv_usec; 69598937Sdes# else /* HAVE_TV_IN_UTMPX */ 69698937Sdes# ifdef HAVE_TIME_IN_UTMPX 69798937Sdes utx->ut_time = li->tv_sec; 69898937Sdes# endif /* HAVE_TIME_IN_UTMPX */ 69998937Sdes# endif /* HAVE_TV_IN_UTMPX */ 70098937Sdes} 70198937Sdes 70298937Sdesvoid 70398937Sdesconstruct_utmpx(struct logininfo *li, struct utmpx *utx) 70498937Sdes{ 705113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 706113911Sdes struct sockaddr_in6 *sa6; 707113911Sdes# endif 70898937Sdes memset(utx, '\0', sizeof(*utx)); 70998937Sdes# ifdef HAVE_ID_IN_UTMPX 71098937Sdes line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); 71198937Sdes# endif 71298937Sdes 71398937Sdes /* this is done here to keep utmp constants out of loginrec.h */ 71498937Sdes switch (li->type) { 71598937Sdes case LTYPE_LOGIN: 71698937Sdes utx->ut_type = USER_PROCESS; 71798937Sdes break; 71898937Sdes case LTYPE_LOGOUT: 71998937Sdes utx->ut_type = DEAD_PROCESS; 72098937Sdes break; 72198937Sdes } 72298937Sdes line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line)); 72398937Sdes set_utmpx_time(li, utx); 72498937Sdes utx->ut_pid = li->pid; 72598937Sdes /* strncpy(): Don't necessarily want null termination */ 72698937Sdes strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username)); 72798937Sdes 72898937Sdes if (li->type == LTYPE_LOGOUT) 72998937Sdes return; 73098937Sdes 73198937Sdes /* 73298937Sdes * These fields are only used when logging in, and are blank 73398937Sdes * for logouts. 73498937Sdes */ 73598937Sdes 73698937Sdes# ifdef HAVE_HOST_IN_UTMPX 73798937Sdes strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname)); 73898937Sdes# endif 73998937Sdes# ifdef HAVE_ADDR_IN_UTMPX 74098937Sdes /* this is just a 32-bit IP address */ 74198937Sdes if (li->hostaddr.sa.sa_family == AF_INET) 74298937Sdes utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 74398937Sdes# endif 744113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP 745113911Sdes /* this is just a 128-bit IPv6 address */ 746113911Sdes if (li->hostaddr.sa.sa_family == AF_INET6) { 747113911Sdes sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 748113911Sdes memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 749113911Sdes if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 750113911Sdes ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; 751113911Sdes ut->ut_addr_v6[1] = 0; 752113911Sdes ut->ut_addr_v6[2] = 0; 753113911Sdes ut->ut_addr_v6[3] = 0; 754113911Sdes } 755113911Sdes } 756113911Sdes# endif 75798937Sdes# ifdef HAVE_SYSLEN_IN_UTMPX 75898937Sdes /* ut_syslen is the length of the utx_host string */ 75998937Sdes utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host)); 76098937Sdes# endif 76198937Sdes} 76298937Sdes#endif /* USE_UTMPX || USE_WTMPX */ 76398937Sdes 76498937Sdes/** 76598937Sdes ** Low-level utmp functions 76698937Sdes **/ 76798937Sdes 76898937Sdes/* FIXME: (ATL) utmp_write_direct needs testing */ 76998937Sdes#ifdef USE_UTMP 77098937Sdes 77198937Sdes/* if we can, use pututline() etc. */ 77298937Sdes# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \ 77398937Sdes defined(HAVE_PUTUTLINE) 77498937Sdes# define UTMP_USE_LIBRARY 77598937Sdes# endif 77698937Sdes 77798937Sdes 77898937Sdes/* write a utmp entry with the system's help (pututline() and pals) */ 77998937Sdes# ifdef UTMP_USE_LIBRARY 78098937Sdesstatic int 78198937Sdesutmp_write_library(struct logininfo *li, struct utmp *ut) 78298937Sdes{ 78398937Sdes setutent(); 78498937Sdes pututline(ut); 78598937Sdes 78698937Sdes# ifdef HAVE_ENDUTENT 78798937Sdes endutent(); 78898937Sdes# endif 78998937Sdes return 1; 79098937Sdes} 79198937Sdes# else /* UTMP_USE_LIBRARY */ 79298937Sdes 79398937Sdes/* write a utmp entry direct to the file */ 79498937Sdes/* This is a slightly modification of code in OpenBSD's login.c */ 79598937Sdesstatic int 79698937Sdesutmp_write_direct(struct logininfo *li, struct utmp *ut) 79798937Sdes{ 79898937Sdes struct utmp old_ut; 79998937Sdes register int fd; 80098937Sdes int tty; 80198937Sdes 80298937Sdes /* FIXME: (ATL) ttyslot() needs local implementation */ 80398937Sdes 80498937Sdes#if defined(HAVE_GETTTYENT) 80598937Sdes register struct ttyent *ty; 80698937Sdes 80798937Sdes tty=0; 80898937Sdes 80998937Sdes setttyent(); 81098937Sdes while ((struct ttyent *)0 != (ty = getttyent())) { 81198937Sdes tty++; 81298937Sdes if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line))) 81398937Sdes break; 81498937Sdes } 81598937Sdes endttyent(); 81698937Sdes 81798937Sdes if((struct ttyent *)0 == ty) { 818124211Sdes logit("utmp_write_entry: tty not found"); 81998937Sdes return(1); 82098937Sdes } 82198937Sdes#else /* FIXME */ 82298937Sdes 82398937Sdes tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ 82498937Sdes 82598937Sdes#endif /* HAVE_GETTTYENT */ 82698937Sdes 82798937Sdes if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { 82898937Sdes (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); 82998937Sdes /* 83098937Sdes * Prevent luser from zero'ing out ut_host. 83198937Sdes * If the new ut_line is empty but the old one is not 83298937Sdes * and ut_line and ut_name match, preserve the old ut_line. 83398937Sdes */ 83498937Sdes if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && 83598937Sdes (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && 83698937Sdes (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && 83798937Sdes (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) { 83898937Sdes (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); 83998937Sdes } 84098937Sdes 84198937Sdes (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); 842124211Sdes if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) 843124211Sdes logit("utmp_write_direct: error writing %s: %s", 84498937Sdes UTMP_FILE, strerror(errno)); 84598937Sdes 84698937Sdes (void)close(fd); 84798937Sdes return 1; 84898937Sdes } else { 84998937Sdes return 0; 85098937Sdes } 85198937Sdes} 85298937Sdes# endif /* UTMP_USE_LIBRARY */ 85398937Sdes 85498937Sdesstatic int 85598937Sdesutmp_perform_login(struct logininfo *li) 85698937Sdes{ 85798937Sdes struct utmp ut; 85898937Sdes 85998937Sdes construct_utmp(li, &ut); 86098937Sdes# ifdef UTMP_USE_LIBRARY 86198937Sdes if (!utmp_write_library(li, &ut)) { 862124211Sdes logit("utmp_perform_login: utmp_write_library() failed"); 86398937Sdes return 0; 86498937Sdes } 86598937Sdes# else 86698937Sdes if (!utmp_write_direct(li, &ut)) { 867124211Sdes logit("utmp_perform_login: utmp_write_direct() failed"); 86898937Sdes return 0; 86998937Sdes } 87098937Sdes# endif 87198937Sdes return 1; 87298937Sdes} 87398937Sdes 87498937Sdes 87598937Sdesstatic int 87698937Sdesutmp_perform_logout(struct logininfo *li) 87798937Sdes{ 87898937Sdes struct utmp ut; 87998937Sdes 88098937Sdes construct_utmp(li, &ut); 88198937Sdes# ifdef UTMP_USE_LIBRARY 88298937Sdes if (!utmp_write_library(li, &ut)) { 883124211Sdes logit("utmp_perform_logout: utmp_write_library() failed"); 88498937Sdes return 0; 88598937Sdes } 88698937Sdes# else 88798937Sdes if (!utmp_write_direct(li, &ut)) { 888124211Sdes logit("utmp_perform_logout: utmp_write_direct() failed"); 88998937Sdes return 0; 89098937Sdes } 89198937Sdes# endif 89298937Sdes return 1; 89398937Sdes} 89498937Sdes 89598937Sdes 89698937Sdesint 89798937Sdesutmp_write_entry(struct logininfo *li) 89898937Sdes{ 89998937Sdes switch(li->type) { 90098937Sdes case LTYPE_LOGIN: 90198937Sdes return utmp_perform_login(li); 90298937Sdes 90398937Sdes case LTYPE_LOGOUT: 90498937Sdes return utmp_perform_logout(li); 90598937Sdes 90698937Sdes default: 907124211Sdes logit("utmp_write_entry: invalid type field"); 90898937Sdes return 0; 90998937Sdes } 91098937Sdes} 91198937Sdes#endif /* USE_UTMP */ 91298937Sdes 91398937Sdes 91498937Sdes/** 91598937Sdes ** Low-level utmpx functions 91698937Sdes **/ 91798937Sdes 91898937Sdes/* not much point if we don't want utmpx entries */ 91998937Sdes#ifdef USE_UTMPX 92098937Sdes 92198937Sdes/* if we have the wherewithall, use pututxline etc. */ 92298937Sdes# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \ 92398937Sdes defined(HAVE_PUTUTXLINE) 92498937Sdes# define UTMPX_USE_LIBRARY 92598937Sdes# endif 92698937Sdes 92798937Sdes 92898937Sdes/* write a utmpx entry with the system's help (pututxline() and pals) */ 92998937Sdes# ifdef UTMPX_USE_LIBRARY 93098937Sdesstatic int 93198937Sdesutmpx_write_library(struct logininfo *li, struct utmpx *utx) 93298937Sdes{ 93398937Sdes setutxent(); 93498937Sdes pututxline(utx); 93598937Sdes 93698937Sdes# ifdef HAVE_ENDUTXENT 93798937Sdes endutxent(); 93898937Sdes# endif 93998937Sdes return 1; 94098937Sdes} 94198937Sdes 94298937Sdes# else /* UTMPX_USE_LIBRARY */ 94398937Sdes 94498937Sdes/* write a utmp entry direct to the file */ 94598937Sdesstatic int 94698937Sdesutmpx_write_direct(struct logininfo *li, struct utmpx *utx) 94798937Sdes{ 948124211Sdes logit("utmpx_write_direct: not implemented!"); 94998937Sdes return 0; 95098937Sdes} 95198937Sdes# endif /* UTMPX_USE_LIBRARY */ 95298937Sdes 95398937Sdesstatic int 95498937Sdesutmpx_perform_login(struct logininfo *li) 95598937Sdes{ 95698937Sdes struct utmpx utx; 95798937Sdes 95898937Sdes construct_utmpx(li, &utx); 95998937Sdes# ifdef UTMPX_USE_LIBRARY 96098937Sdes if (!utmpx_write_library(li, &utx)) { 961124211Sdes logit("utmpx_perform_login: utmp_write_library() failed"); 96298937Sdes return 0; 96398937Sdes } 96498937Sdes# else 96598937Sdes if (!utmpx_write_direct(li, &ut)) { 966124211Sdes logit("utmpx_perform_login: utmp_write_direct() failed"); 96798937Sdes return 0; 96898937Sdes } 96998937Sdes# endif 97098937Sdes return 1; 97198937Sdes} 97298937Sdes 97398937Sdes 97498937Sdesstatic int 97598937Sdesutmpx_perform_logout(struct logininfo *li) 97698937Sdes{ 97798937Sdes struct utmpx utx; 97898937Sdes 97998937Sdes construct_utmpx(li, &utx); 98098937Sdes# ifdef HAVE_ID_IN_UTMPX 98198937Sdes line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); 98298937Sdes# endif 98398937Sdes# ifdef HAVE_TYPE_IN_UTMPX 98498937Sdes utx.ut_type = DEAD_PROCESS; 98598937Sdes# endif 98698937Sdes 98798937Sdes# ifdef UTMPX_USE_LIBRARY 98898937Sdes utmpx_write_library(li, &utx); 98998937Sdes# else 99098937Sdes utmpx_write_direct(li, &utx); 99198937Sdes# endif 99298937Sdes return 1; 99398937Sdes} 99498937Sdes 99598937Sdesint 99698937Sdesutmpx_write_entry(struct logininfo *li) 99798937Sdes{ 99898937Sdes switch(li->type) { 99998937Sdes case LTYPE_LOGIN: 100098937Sdes return utmpx_perform_login(li); 100198937Sdes case LTYPE_LOGOUT: 100298937Sdes return utmpx_perform_logout(li); 100398937Sdes default: 1004124211Sdes logit("utmpx_write_entry: invalid type field"); 100598937Sdes return 0; 100698937Sdes } 100798937Sdes} 100898937Sdes#endif /* USE_UTMPX */ 100998937Sdes 101098937Sdes 101198937Sdes/** 101298937Sdes ** Low-level wtmp functions 101398937Sdes **/ 101498937Sdes 101598937Sdes#ifdef USE_WTMP 101698937Sdes 101798937Sdes/* write a wtmp entry direct to the end of the file */ 101898937Sdes/* This is a slight modification of code in OpenBSD's logwtmp.c */ 101998937Sdesstatic int 102098937Sdeswtmp_write(struct logininfo *li, struct utmp *ut) 102198937Sdes{ 102298937Sdes struct stat buf; 102398937Sdes int fd, ret = 1; 102498937Sdes 102598937Sdes if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 1026124211Sdes logit("wtmp_write: problem writing %s: %s", 102798937Sdes WTMP_FILE, strerror(errno)); 102898937Sdes return 0; 102998937Sdes } 103098937Sdes if (fstat(fd, &buf) == 0) 1031124211Sdes if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { 103298937Sdes ftruncate(fd, buf.st_size); 1033124211Sdes logit("wtmp_write: problem writing %s: %s", 103498937Sdes WTMP_FILE, strerror(errno)); 103598937Sdes ret = 0; 103698937Sdes } 103798937Sdes (void)close(fd); 103898937Sdes return ret; 103998937Sdes} 104098937Sdes 104198937Sdesstatic int 104298937Sdeswtmp_perform_login(struct logininfo *li) 104398937Sdes{ 104498937Sdes struct utmp ut; 104598937Sdes 104698937Sdes construct_utmp(li, &ut); 104798937Sdes return wtmp_write(li, &ut); 104898937Sdes} 104998937Sdes 105098937Sdes 105198937Sdesstatic int 105298937Sdeswtmp_perform_logout(struct logininfo *li) 105398937Sdes{ 105498937Sdes struct utmp ut; 105598937Sdes 105698937Sdes construct_utmp(li, &ut); 105798937Sdes return wtmp_write(li, &ut); 105898937Sdes} 105998937Sdes 106098937Sdes 106198937Sdesint 106298937Sdeswtmp_write_entry(struct logininfo *li) 106398937Sdes{ 106498937Sdes switch(li->type) { 106598937Sdes case LTYPE_LOGIN: 106698937Sdes return wtmp_perform_login(li); 106798937Sdes case LTYPE_LOGOUT: 106898937Sdes return wtmp_perform_logout(li); 106998937Sdes default: 1070124211Sdes logit("wtmp_write_entry: invalid type field"); 107198937Sdes return 0; 107298937Sdes } 107398937Sdes} 107498937Sdes 107598937Sdes 107698937Sdes/* Notes on fetching login data from wtmp/wtmpx 107798937Sdes * 107898937Sdes * Logouts are usually recorded with (amongst other things) a blank 107998937Sdes * username on a given tty line. However, some systems (HP-UX is one) 108098937Sdes * leave all fields set, but change the ut_type field to DEAD_PROCESS. 108198937Sdes * 108298937Sdes * Since we're only looking for logins here, we know that the username 108398937Sdes * must be set correctly. On systems that leave it in, we check for 108498937Sdes * ut_type==USER_PROCESS (indicating a login.) 108598937Sdes * 108698937Sdes * Portability: Some systems may set something other than USER_PROCESS 108798937Sdes * to indicate a login process. I don't know of any as I write. Also, 108898937Sdes * it's possible that some systems may both leave the username in 108998937Sdes * place and not have ut_type. 109098937Sdes */ 109198937Sdes 109298937Sdes/* return true if this wtmp entry indicates a login */ 109398937Sdesstatic int 109498937Sdeswtmp_islogin(struct logininfo *li, struct utmp *ut) 109598937Sdes{ 109698937Sdes if (strncmp(li->username, ut->ut_name, 109798937Sdes MIN_SIZEOF(li->username, ut->ut_name)) == 0) { 109898937Sdes# ifdef HAVE_TYPE_IN_UTMP 109998937Sdes if (ut->ut_type & USER_PROCESS) 110098937Sdes return 1; 110198937Sdes# else 110298937Sdes return 1; 110398937Sdes# endif 110498937Sdes } 110598937Sdes return 0; 110698937Sdes} 110798937Sdes 110898937Sdesint 110998937Sdeswtmp_get_entry(struct logininfo *li) 111098937Sdes{ 111198937Sdes struct stat st; 111298937Sdes struct utmp ut; 111398937Sdes int fd, found=0; 111498937Sdes 111598937Sdes /* Clear the time entries in our logininfo */ 111698937Sdes li->tv_sec = li->tv_usec = 0; 111798937Sdes 111898937Sdes if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { 1119124211Sdes logit("wtmp_get_entry: problem opening %s: %s", 112098937Sdes WTMP_FILE, strerror(errno)); 112198937Sdes return 0; 112298937Sdes } 112398937Sdes if (fstat(fd, &st) != 0) { 1124124211Sdes logit("wtmp_get_entry: couldn't stat %s: %s", 112598937Sdes WTMP_FILE, strerror(errno)); 112698937Sdes close(fd); 112798937Sdes return 0; 112898937Sdes } 112998937Sdes 113098937Sdes /* Seek to the start of the last struct utmp */ 113198937Sdes if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { 113298937Sdes /* Looks like we've got a fresh wtmp file */ 113398937Sdes close(fd); 113498937Sdes return 0; 113598937Sdes } 113698937Sdes 113798937Sdes while (!found) { 113898937Sdes if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { 1139124211Sdes logit("wtmp_get_entry: read of %s failed: %s", 114098937Sdes WTMP_FILE, strerror(errno)); 114198937Sdes close (fd); 114298937Sdes return 0; 114398937Sdes } 114498937Sdes if ( wtmp_islogin(li, &ut) ) { 114598937Sdes found = 1; 114698937Sdes /* We've already checked for a time in struct 114798937Sdes * utmp, in login_getlast(). */ 114898937Sdes# ifdef HAVE_TIME_IN_UTMP 114998937Sdes li->tv_sec = ut.ut_time; 115098937Sdes# else 115198937Sdes# if HAVE_TV_IN_UTMP 115298937Sdes li->tv_sec = ut.ut_tv.tv_sec; 115398937Sdes# endif 115498937Sdes# endif 115598937Sdes line_fullname(li->line, ut.ut_line, 115698937Sdes MIN_SIZEOF(li->line, ut.ut_line)); 115798937Sdes# ifdef HAVE_HOST_IN_UTMP 115898937Sdes strlcpy(li->hostname, ut.ut_host, 115998937Sdes MIN_SIZEOF(li->hostname, ut.ut_host)); 116098937Sdes# endif 116198937Sdes continue; 116298937Sdes } 116398937Sdes /* Seek back 2 x struct utmp */ 116498937Sdes if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { 116598937Sdes /* We've found the start of the file, so quit */ 116698937Sdes close (fd); 116798937Sdes return 0; 116898937Sdes } 116998937Sdes } 117098937Sdes 117198937Sdes /* We found an entry. Tidy up and return */ 117298937Sdes close(fd); 117398937Sdes return 1; 117498937Sdes} 117598937Sdes# endif /* USE_WTMP */ 117698937Sdes 117798937Sdes 117898937Sdes/** 117998937Sdes ** Low-level wtmpx functions 118098937Sdes **/ 118198937Sdes 118298937Sdes#ifdef USE_WTMPX 118398937Sdes/* write a wtmpx entry direct to the end of the file */ 118498937Sdes/* This is a slight modification of code in OpenBSD's logwtmp.c */ 118598937Sdesstatic int 118698937Sdeswtmpx_write(struct logininfo *li, struct utmpx *utx) 118798937Sdes{ 118898937Sdes struct stat buf; 118998937Sdes int fd, ret = 1; 119098937Sdes 119198937Sdes if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 1192124211Sdes logit("wtmpx_write: problem opening %s: %s", 119398937Sdes WTMPX_FILE, strerror(errno)); 119498937Sdes return 0; 119598937Sdes } 119698937Sdes 119798937Sdes if (fstat(fd, &buf) == 0) 1198124211Sdes if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) { 119998937Sdes ftruncate(fd, buf.st_size); 1200124211Sdes logit("wtmpx_write: problem writing %s: %s", 120198937Sdes WTMPX_FILE, strerror(errno)); 120298937Sdes ret = 0; 120398937Sdes } 120498937Sdes (void)close(fd); 120598937Sdes 120698937Sdes return ret; 120798937Sdes} 120898937Sdes 120998937Sdes 121098937Sdesstatic int 121198937Sdeswtmpx_perform_login(struct logininfo *li) 121298937Sdes{ 121398937Sdes struct utmpx utx; 121498937Sdes 121598937Sdes construct_utmpx(li, &utx); 121698937Sdes return wtmpx_write(li, &utx); 121798937Sdes} 121898937Sdes 121998937Sdes 122098937Sdesstatic int 122198937Sdeswtmpx_perform_logout(struct logininfo *li) 122298937Sdes{ 122398937Sdes struct utmpx utx; 122498937Sdes 122598937Sdes construct_utmpx(li, &utx); 122698937Sdes return wtmpx_write(li, &utx); 122798937Sdes} 122898937Sdes 122998937Sdes 123098937Sdesint 123198937Sdeswtmpx_write_entry(struct logininfo *li) 123298937Sdes{ 123398937Sdes switch(li->type) { 123498937Sdes case LTYPE_LOGIN: 123598937Sdes return wtmpx_perform_login(li); 123698937Sdes case LTYPE_LOGOUT: 123798937Sdes return wtmpx_perform_logout(li); 123898937Sdes default: 1239124211Sdes logit("wtmpx_write_entry: invalid type field"); 124098937Sdes return 0; 124198937Sdes } 124298937Sdes} 124398937Sdes 124498937Sdes/* Please see the notes above wtmp_islogin() for information about the 124598937Sdes next two functions */ 124698937Sdes 124798937Sdes/* Return true if this wtmpx entry indicates a login */ 124898937Sdesstatic int 124998937Sdeswtmpx_islogin(struct logininfo *li, struct utmpx *utx) 125098937Sdes{ 125198937Sdes if ( strncmp(li->username, utx->ut_name, 125298937Sdes MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) { 125398937Sdes# ifdef HAVE_TYPE_IN_UTMPX 125498937Sdes if (utx->ut_type == USER_PROCESS) 125598937Sdes return 1; 125698937Sdes# else 125798937Sdes return 1; 125898937Sdes# endif 125998937Sdes } 126098937Sdes return 0; 126198937Sdes} 126298937Sdes 126398937Sdes 126498937Sdesint 126598937Sdeswtmpx_get_entry(struct logininfo *li) 126698937Sdes{ 126798937Sdes struct stat st; 126898937Sdes struct utmpx utx; 126998937Sdes int fd, found=0; 127098937Sdes 127198937Sdes /* Clear the time entries */ 127298937Sdes li->tv_sec = li->tv_usec = 0; 127398937Sdes 127498937Sdes if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { 1275124211Sdes logit("wtmpx_get_entry: problem opening %s: %s", 127698937Sdes WTMPX_FILE, strerror(errno)); 127798937Sdes return 0; 127898937Sdes } 127998937Sdes if (fstat(fd, &st) != 0) { 1280124211Sdes logit("wtmpx_get_entry: couldn't stat %s: %s", 1281106130Sdes WTMPX_FILE, strerror(errno)); 128298937Sdes close(fd); 128398937Sdes return 0; 128498937Sdes } 128598937Sdes 128698937Sdes /* Seek to the start of the last struct utmpx */ 128798937Sdes if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { 128898937Sdes /* probably a newly rotated wtmpx file */ 128998937Sdes close(fd); 129098937Sdes return 0; 129198937Sdes } 129298937Sdes 129398937Sdes while (!found) { 129498937Sdes if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { 1295124211Sdes logit("wtmpx_get_entry: read of %s failed: %s", 129698937Sdes WTMPX_FILE, strerror(errno)); 129798937Sdes close (fd); 129898937Sdes return 0; 129998937Sdes } 130098937Sdes /* Logouts are recorded as a blank username on a particular line. 130198937Sdes * So, we just need to find the username in struct utmpx */ 130298937Sdes if ( wtmpx_islogin(li, &utx) ) { 1303106130Sdes found = 1; 130498937Sdes# ifdef HAVE_TV_IN_UTMPX 130598937Sdes li->tv_sec = utx.ut_tv.tv_sec; 130698937Sdes# else 130798937Sdes# ifdef HAVE_TIME_IN_UTMPX 130898937Sdes li->tv_sec = utx.ut_time; 130998937Sdes# endif 131098937Sdes# endif 131198937Sdes line_fullname(li->line, utx.ut_line, sizeof(li->line)); 131298937Sdes# ifdef HAVE_HOST_IN_UTMPX 131398937Sdes strlcpy(li->hostname, utx.ut_host, 131498937Sdes MIN_SIZEOF(li->hostname, utx.ut_host)); 131598937Sdes# endif 131698937Sdes continue; 131798937Sdes } 131898937Sdes if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { 131998937Sdes close (fd); 132098937Sdes return 0; 132198937Sdes } 132298937Sdes } 132398937Sdes 132498937Sdes close(fd); 132598937Sdes return 1; 132698937Sdes} 132798937Sdes#endif /* USE_WTMPX */ 132898937Sdes 132998937Sdes/** 133098937Sdes ** Low-level libutil login() functions 133198937Sdes **/ 133298937Sdes 133398937Sdes#ifdef USE_LOGIN 133498937Sdesstatic int 133598937Sdessyslogin_perform_login(struct logininfo *li) 133698937Sdes{ 133798937Sdes struct utmp *ut; 133898937Sdes 133998937Sdes if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) { 1340124211Sdes logit("syslogin_perform_login: couldn't malloc()"); 134198937Sdes return 0; 134298937Sdes } 134398937Sdes construct_utmp(li, ut); 134498937Sdes login(ut); 1345113911Sdes free(ut); 134698937Sdes 134798937Sdes return 1; 134898937Sdes} 134998937Sdes 135098937Sdesstatic int 135198937Sdessyslogin_perform_logout(struct logininfo *li) 135298937Sdes{ 135398937Sdes# ifdef HAVE_LOGOUT 135498937Sdes char line[8]; 135598937Sdes 135698937Sdes (void)line_stripname(line, li->line, sizeof(line)); 135798937Sdes 135898937Sdes if (!logout(line)) { 1359124211Sdes logit("syslogin_perform_logout: logout() returned an error"); 136098937Sdes# ifdef HAVE_LOGWTMP 136198937Sdes } else { 136298937Sdes logwtmp(line, "", ""); 136398937Sdes# endif 136498937Sdes } 136598937Sdes /* FIXME: (ATL - if the need arises) What to do if we have 136698937Sdes * login, but no logout? what if logout but no logwtmp? All 136798937Sdes * routines are in libutil so they should all be there, 136898937Sdes * but... */ 136998937Sdes# endif 137098937Sdes return 1; 137198937Sdes} 137298937Sdes 137398937Sdesint 137498937Sdessyslogin_write_entry(struct logininfo *li) 137598937Sdes{ 137698937Sdes switch (li->type) { 137798937Sdes case LTYPE_LOGIN: 137898937Sdes return syslogin_perform_login(li); 137998937Sdes case LTYPE_LOGOUT: 138098937Sdes return syslogin_perform_logout(li); 138198937Sdes default: 1382124211Sdes logit("syslogin_write_entry: Invalid type field"); 138398937Sdes return 0; 138498937Sdes } 138598937Sdes} 138698937Sdes#endif /* USE_LOGIN */ 138798937Sdes 138898937Sdes/* end of file log-syslogin.c */ 138998937Sdes 139098937Sdes/** 139198937Sdes ** Low-level lastlog functions 139298937Sdes **/ 139398937Sdes 139498937Sdes#ifdef USE_LASTLOG 139598937Sdes#define LL_FILE 1 139698937Sdes#define LL_DIR 2 139798937Sdes#define LL_OTHER 3 139898937Sdes 139998937Sdesstatic void 140098937Sdeslastlog_construct(struct logininfo *li, struct lastlog *last) 140198937Sdes{ 140298937Sdes /* clear the structure */ 140398937Sdes memset(last, '\0', sizeof(*last)); 140498937Sdes 140598937Sdes (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line)); 140698937Sdes strlcpy(last->ll_host, li->hostname, 140798937Sdes MIN_SIZEOF(last->ll_host, li->hostname)); 140898937Sdes last->ll_time = li->tv_sec; 140998937Sdes} 141098937Sdes 141198937Sdesstatic int 141298937Sdeslastlog_filetype(char *filename) 141398937Sdes{ 141498937Sdes struct stat st; 141598937Sdes 141698937Sdes if (stat(LASTLOG_FILE, &st) != 0) { 1417124211Sdes logit("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE, 141898937Sdes strerror(errno)); 141998937Sdes return 0; 142098937Sdes } 142198937Sdes if (S_ISDIR(st.st_mode)) 142298937Sdes return LL_DIR; 142398937Sdes else if (S_ISREG(st.st_mode)) 142498937Sdes return LL_FILE; 142598937Sdes else 142698937Sdes return LL_OTHER; 142798937Sdes} 142898937Sdes 142998937Sdes 143098937Sdes/* open the file (using filemode) and seek to the login entry */ 143198937Sdesstatic int 143298937Sdeslastlog_openseek(struct logininfo *li, int *fd, int filemode) 143398937Sdes{ 143498937Sdes off_t offset; 143598937Sdes int type; 143698937Sdes char lastlog_file[1024]; 143798937Sdes 143898937Sdes type = lastlog_filetype(LASTLOG_FILE); 143998937Sdes switch (type) { 144098937Sdes case LL_FILE: 144198937Sdes strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); 144298937Sdes break; 144398937Sdes case LL_DIR: 144498937Sdes snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", 144598937Sdes LASTLOG_FILE, li->username); 144698937Sdes break; 144798937Sdes default: 1448124211Sdes logit("lastlog_openseek: %.100s is not a file or directory!", 144998937Sdes LASTLOG_FILE); 145098937Sdes return 0; 145198937Sdes } 145298937Sdes 1453124211Sdes *fd = open(lastlog_file, filemode, 0600); 145498937Sdes if ( *fd < 0) { 145598937Sdes debug("lastlog_openseek: Couldn't open %s: %s", 145698937Sdes lastlog_file, strerror(errno)); 145798937Sdes return 0; 145898937Sdes } 145998937Sdes 146098937Sdes if (type == LL_FILE) { 146198937Sdes /* find this uid's offset in the lastlog file */ 146298937Sdes offset = (off_t) ((long)li->uid * sizeof(struct lastlog)); 146398937Sdes 146498937Sdes if ( lseek(*fd, offset, SEEK_SET) != offset ) { 1465124211Sdes logit("lastlog_openseek: %s->lseek(): %s", 146698937Sdes lastlog_file, strerror(errno)); 146798937Sdes return 0; 146898937Sdes } 146998937Sdes } 147098937Sdes 147198937Sdes return 1; 147298937Sdes} 147398937Sdes 147498937Sdesstatic int 147598937Sdeslastlog_perform_login(struct logininfo *li) 147698937Sdes{ 147798937Sdes struct lastlog last; 147898937Sdes int fd; 147998937Sdes 148098937Sdes /* create our struct lastlog */ 148198937Sdes lastlog_construct(li, &last); 148298937Sdes 148398937Sdes if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) 148498937Sdes return(0); 148598937Sdes 148698937Sdes /* write the entry */ 1487124211Sdes if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) { 148898937Sdes close(fd); 1489124211Sdes logit("lastlog_write_filemode: Error writing to %s: %s", 149098937Sdes LASTLOG_FILE, strerror(errno)); 149198937Sdes return 0; 149298937Sdes } 149398937Sdes 149498937Sdes close(fd); 149598937Sdes return 1; 149698937Sdes} 149798937Sdes 149898937Sdesint 149998937Sdeslastlog_write_entry(struct logininfo *li) 150098937Sdes{ 150198937Sdes switch(li->type) { 150298937Sdes case LTYPE_LOGIN: 150398937Sdes return lastlog_perform_login(li); 150498937Sdes default: 1505124211Sdes logit("lastlog_write_entry: Invalid type field"); 150698937Sdes return 0; 150798937Sdes } 150898937Sdes} 150998937Sdes 151098937Sdesstatic void 151198937Sdeslastlog_populate_entry(struct logininfo *li, struct lastlog *last) 151298937Sdes{ 151398937Sdes line_fullname(li->line, last->ll_line, sizeof(li->line)); 151498937Sdes strlcpy(li->hostname, last->ll_host, 151598937Sdes MIN_SIZEOF(li->hostname, last->ll_host)); 151698937Sdes li->tv_sec = last->ll_time; 151798937Sdes} 151898937Sdes 151998937Sdesint 152098937Sdeslastlog_get_entry(struct logininfo *li) 152198937Sdes{ 152298937Sdes struct lastlog last; 1523113911Sdes int fd, ret; 152498937Sdes 152598937Sdes if (!lastlog_openseek(li, &fd, O_RDONLY)) 1526113911Sdes return (0); 152798937Sdes 1528113911Sdes ret = atomicio(read, fd, &last, sizeof(last)); 1529113911Sdes close(fd); 1530113911Sdes 1531113911Sdes switch (ret) { 1532113911Sdes case 0: 1533113911Sdes memset(&last, '\0', sizeof(last)); 1534113911Sdes /* FALLTHRU */ 1535113911Sdes case sizeof(last): 1536113911Sdes lastlog_populate_entry(li, &last); 1537113911Sdes return (1); 1538113911Sdes case -1: 1539113911Sdes error("%s: Error reading from %s: %s", __func__, 154098937Sdes LASTLOG_FILE, strerror(errno)); 1541113911Sdes return (0); 1542113911Sdes default: 1543113911Sdes error("%s: Error reading from %s: Expecting %d, got %d", 1544113911Sdes __func__, LASTLOG_FILE, sizeof(last), ret); 1545113911Sdes return (0); 154698937Sdes } 154798937Sdes 1548113911Sdes /* NOTREACHED */ 1549113911Sdes return (0); 155098937Sdes} 155198937Sdes#endif /* USE_LASTLOG */ 1552