loginrec.c revision 124211
1204076Spjd/* 2204076Spjd * Copyright (c) 2000 Andre Lucas. All rights reserved. 3211877Spjd * Portions copyright (c) 1998 Todd C. Miller 4204076Spjd * Portions copyright (c) 1996 Jason Downs 5204076Spjd * Portions copyright (c) 1996 Theo de Raadt 6204076Spjd * 7204076Spjd * Redistribution and use in source and binary forms, with or without 8204076Spjd * modification, are permitted provided that the following conditions 9204076Spjd * are met: 10204076Spjd * 1. Redistributions of source code must retain the above copyright 11204076Spjd * notice, this list of conditions and the following disclaimer. 12204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 13204076Spjd * notice, this list of conditions and the following disclaimer in the 14204076Spjd * documentation and/or other materials provided with the distribution. 15204076Spjd * 16204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17204076Spjd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18204076Spjd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19204076Spjd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20204076Spjd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21204076Spjd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22204076Spjd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23204076Spjd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24204076Spjd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25204076Spjd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26204076Spjd */ 27204076Spjd 28204076Spjd/** 29204076Spjd ** loginrec.c: platform-independent login recording and lastlog retrieval 30204076Spjd **/ 31204076Spjd 32204076Spjd/* 33204076Spjd The new login code explained 34204076Spjd ============================ 35204076Spjd 36204076Spjd This code attempts to provide a common interface to login recording 37204076Spjd (utmp and friends) and last login time retrieval. 38204076Spjd 39204076Spjd Its primary means of achieving this is to use 'struct logininfo', a 40204076Spjd union of all the useful fields in the various different types of 41204076Spjd system login record structures one finds on UNIX variants. 42204076Spjd 43204076Spjd We depend on autoconf to define which recording methods are to be 44204076Spjd used, and which fields are contained in the relevant data structures 45213009Spjd on the local system. Many C preprocessor symbols affect which code 46204076Spjd gets compiled here. 47204076Spjd 48204076Spjd The code is designed to make it easy to modify a particular 49204076Spjd recording method, without affecting other methods nor requiring so 50204076Spjd many nested conditional compilation blocks as were commonplace in 51204076Spjd the old code. 52204076Spjd 53204076Spjd For login recording, we try to use the local system's libraries as 54204076Spjd these are clearly most likely to work correctly. For utmp systems 55204076Spjd this usually means login() and logout() or setutent() etc., probably 56204076Spjd in libutil, along with logwtmp() etc. On these systems, we fall back 57212038Spjd to writing the files directly if we have to, though this method 58204076Spjd requires very thorough testing so we do not corrupt local auditing 59204076Spjd information. These files and their access methods are very system 60204076Spjd specific indeed. 61211977Spjd 62204076Spjd For utmpx systems, the corresponding library functions are 63204076Spjd setutxent() etc. To the author's knowledge, all utmpx systems have 64204076Spjd these library functions and so no direct write is attempted. If such 65204076Spjd a system exists and needs support, direct analogues of the [uw]tmp 66204076Spjd code should suffice. 67204076Spjd 68219864Spjd Retrieving the time of last login ('lastlog') is in some ways even 69219864Spjd more problemmatic than login recording. Some systems provide a 70204076Spjd simple table of all users which we seek based on uid and retrieve a 71204076Spjd relatively standard structure. Others record the same information in 72204076Spjd a directory with a separate file, and others don't record the 73204076Spjd information separately at all. For systems in the latter category, 74204076Spjd we look backwards in the wtmp or wtmpx file for the last login entry 75204076Spjd for our user. Naturally this is slower and on busy systems could 76204076Spjd incur a significant performance penalty. 77204076Spjd 78211984Spjd Calling the new code 79211984Spjd -------------------- 80204076Spjd 81204076Spjd In OpenSSH all login recording and retrieval is performed in 82204076Spjd login.c. Here you'll find working examples. Also, in the logintest.c 83204076Spjd program there are more examples. 84204076Spjd 85204076Spjd Internal handler calling method 86204076Spjd ------------------------------- 87204076Spjd 88204076Spjd When a call is made to login_login() or login_logout(), both 89204076Spjd routines set a struct logininfo flag defining which action (log in, 90204076Spjd or log out) is to be taken. They both then call login_write(), which 91204076Spjd calls whichever of the many structure-specific handlers autoconf 92204076Spjd selects for the local system. 93204076Spjd 94204076Spjd The handlers themselves handle system data structure specifics. Both 95204076Spjd struct utmp and struct utmpx have utility functions (see 96204076Spjd construct_utmp*()) to try to make it simpler to add extra systems 97204076Spjd that introduce new features to either structure. 98204076Spjd 99204076Spjd While it may seem terribly wasteful to replicate so much similar 100204076Spjd code for each method, experience has shown that maintaining code to 101204076Spjd write both struct utmp and utmpx in one function, whilst maintaining 102204076Spjd support for all systems whether they have library support or not, is 103204076Spjd a difficult and time-consuming task. 104204076Spjd 105204076Spjd Lastlog support proceeds similarly. Functions login_get_lastlog() 106204076Spjd (and its OpenSSH-tuned friend login_get_lastlog_time()) call 107204076Spjd getlast_entry(), which tries one of three methods to find the last 108204076Spjd login time. It uses local system lastlog support if it can, 109204076Spjd otherwise it tries wtmp or wtmpx before giving up and returning 0, 110211877Spjd meaning "tilt". 111211877Spjd 112211877Spjd Maintenance 113211877Spjd ----------- 114211877Spjd 115211877Spjd In many cases it's possible to tweak autoconf to select the correct 116211877Spjd methods for a particular platform, either by improving the detection 117211877Spjd code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE 118211877Spjd symbols for the platform. 119211877Spjd 120211877Spjd Use logintest to check which symbols are defined before modifying 121211877Spjd configure.ac and loginrec.c. (You have to build logintest yourself 122211877Spjd with 'make logintest' as it's not built by default.) 123211877Spjd 124211877Spjd Otherwise, patches to the specific method(s) are very helpful! 125211877Spjd 126211877Spjd*/ 127211877Spjd 128211877Spjd/** 129211877Spjd ** TODO: 130204076Spjd ** homegrown ttyslot() 131204076Spjd ** test, test, test 132204076Spjd ** 133204076Spjd ** Platform status: 134204076Spjd ** ---------------- 135204076Spjd ** 136204076Spjd ** Known good: 137204076Spjd ** Linux (Redhat 6.2, Debian) 138204076Spjd ** Solaris 139204076Spjd ** HP-UX 10.20 (gcc only) 140204076Spjd ** IRIX 141204076Spjd ** NeXT - M68k/HPPA/Sparc (4.2/3.3) 142204076Spjd ** 143204076Spjd ** Testing required: Please send reports! 144204076Spjd ** NetBSD 145204076Spjd ** HP-UX 11 146204076Spjd ** AIX 147204076Spjd ** 148204076Spjd ** Platforms with known problems: 149204076Spjd ** Some variants of Slackware Linux 150204076Spjd ** 151204076Spjd **/ 152204076Spjd 153204076Spjd#include "includes.h" 154204076Spjd 155210879Spjd#include "ssh.h" 156210879Spjd#include "xmalloc.h" 157210879Spjd#include "loginrec.h" 158204076Spjd#include "log.h" 159204076Spjd#include "atomicio.h" 160204076Spjd 161204076SpjdRCSID("$Id: loginrec.c,v 1.52 2003/07/06 05:20:46 dtucker Exp $"); 162210879SpjdRCSID("$FreeBSD: head/crypto/openssh/loginrec.c 124211 2004-01-07 11:16:27Z des $"); 163210879Spjd 164210879Spjd#ifdef HAVE_UTIL_H 165204076Spjd# include <util.h> 166204076Spjd#endif 167204076Spjd 168204076Spjd#ifdef HAVE_LIBUTIL_H 169204076Spjd# include <libutil.h> 170204076Spjd#endif 171204076Spjd 172204076Spjd/** 173204076Spjd ** prototypes for helper functions in this file 174204076Spjd **/ 175204076Spjd 176204076Spjd#if HAVE_UTMP_H 177204076Spjdvoid set_utmp_time(struct logininfo *li, struct utmp *ut); 178204076Spjdvoid construct_utmp(struct logininfo *li, struct utmp *ut); 179204076Spjd#endif 180204076Spjd 181204076Spjd#ifdef HAVE_UTMPX_H 182204076Spjdvoid set_utmpx_time(struct logininfo *li, struct utmpx *ut); 183204076Spjdvoid construct_utmpx(struct logininfo *li, struct utmpx *ut); 184204076Spjd#endif 185204076Spjd 186223181Strocinyint utmp_write_entry(struct logininfo *li); 187220271Spjdint utmpx_write_entry(struct logininfo *li); 188220271Spjdint wtmp_write_entry(struct logininfo *li); 189220271Spjdint wtmpx_write_entry(struct logininfo *li); 190223181Strocinyint lastlog_write_entry(struct logininfo *li); 191220271Spjdint syslogin_write_entry(struct logininfo *li); 192204076Spjd 193204076Spjdint getlast_entry(struct logininfo *li); 194204076Spjdint lastlog_get_entry(struct logininfo *li); 195204076Spjdint wtmp_get_entry(struct logininfo *li); 196204076Spjdint wtmpx_get_entry(struct logininfo *li); 197204076Spjd 198204076Spjd/* pick the shortest string */ 199204076Spjd#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) ) 200204076Spjd 201204076Spjd/** 202204076Spjd ** platform-independent login functions 203204076Spjd **/ 204204076Spjd 205204076Spjd/* login_login(struct logininfo *) -Record a login 206204076Spjd * 207204076Spjd * Call with a pointer to a struct logininfo initialised with 208204076Spjd * login_init_entry() or login_alloc_entry() 209204076Spjd * 210204076Spjd * Returns: 211204076Spjd * >0 if successful 212204076Spjd * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 213204076Spjd */ 214204076Spjdint 215204076Spjdlogin_login (struct logininfo *li) 216204076Spjd{ 217204076Spjd li->type = LTYPE_LOGIN; 218204076Spjd return login_write(li); 219204076Spjd} 220204076Spjd 221204076Spjd 222204076Spjd/* login_logout(struct logininfo *) - Record a logout 223204076Spjd * 224204076Spjd * Call as with login_login() 225204076Spjd * 226204076Spjd * Returns: 227204076Spjd * >0 if successful 228204076Spjd * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 229204076Spjd */ 230204076Spjdint 231204076Spjdlogin_logout(struct logininfo *li) 232204076Spjd{ 233204076Spjd li->type = LTYPE_LOGOUT; 234204076Spjd return login_write(li); 235204076Spjd} 236204076Spjd 237204076Spjd/* login_get_lastlog_time(int) - Retrieve the last login time 238204076Spjd * 239204076Spjd * Retrieve the last login time for the given uid. Will try to use the 240204076Spjd * system lastlog facilities if they are available, but will fall back 241204076Spjd * to looking in wtmp/wtmpx if necessary 242204076Spjd * 243204076Spjd * Returns: 244204076Spjd * 0 on failure, or if user has never logged in 245204076Spjd * Time in seconds from the epoch if successful 246204076Spjd * 247204076Spjd * Useful preprocessor symbols: 248204076Spjd * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog 249204076Spjd * info 250214284Spjd * USE_LASTLOG: If set, indicates the presence of system lastlog 251214284Spjd * facilities. If this and DISABLE_LASTLOG are not set, 252214284Spjd * try to retrieve lastlog information from wtmp/wtmpx. 253214284Spjd */ 254204076Spjdunsigned int 255218138Spjdlogin_get_lastlog_time(const int uid) 256204076Spjd{ 257204076Spjd struct logininfo li; 258204076Spjd 259214284Spjd if (login_get_lastlog(&li, uid)) 260214284Spjd return li.tv_sec; 261214284Spjd else 262214284Spjd return 0; 263214284Spjd} 264214284Spjd 265214284Spjd/* login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry 266220865Spjd * 267204076Spjd * Retrieve a logininfo structure populated (only partially) with 268219830Spjd * information from the system lastlog data, or from wtmp/wtmpx if no 269219830Spjd * system lastlog information exists. 270219830Spjd * 271219830Spjd * Note this routine must be given a pre-allocated logininfo. 272219830Spjd * 273219830Spjd * Returns: 274219830Spjd * >0: A pointer to your struct logininfo if successful 275219830Spjd * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 276219830Spjd * 277219830Spjd */ 278219830Spjdstruct logininfo * 279219830Spjdlogin_get_lastlog(struct logininfo *li, const int uid) 280219831Spjd{ 281219830Spjd struct passwd *pw; 282204076Spjd 283204076Spjd memset(li, '\0', sizeof(*li)); 284204076Spjd li->uid = uid; 285204076Spjd 286219843Spjd /* 287204076Spjd * If we don't have a 'real' lastlog, we need the username to 288204076Spjd * reliably search wtmp(x) for the last login (see 289204076Spjd * wtmp_get_entry().) 290204076Spjd */ 291204076Spjd pw = getpwuid(uid); 292204076Spjd if (pw == NULL) 293204076Spjd fatal("login_get_lastlog: Cannot find account for uid %i", uid); 294204076Spjd 295204076Spjd /* No MIN_SIZEOF here - we absolutely *must not* truncate the 296204076Spjd * username */ 297204076Spjd strlcpy(li->username, pw->pw_name, sizeof(li->username)); 298204076Spjd 299204076Spjd if (getlast_entry(li)) 300204076Spjd return li; 301204076Spjd else 302204076Spjd return NULL; 303204076Spjd} 304204076Spjd 305204076Spjd 306204076Spjd/* login_alloc_entry(int, char*, char*, char*) - Allocate and initialise 307204076Spjd * a logininfo structure 308204076Spjd * 309204076Spjd * This function creates a new struct logininfo, a data structure 310204076Spjd * meant to carry the information required to portably record login info. 311204076Spjd * 312204076Spjd * Returns a pointer to a newly created struct logininfo. If memory 313204076Spjd * allocation fails, the program halts. 314204076Spjd */ 315204076Spjdstruct 316204076Spjdlogininfo *login_alloc_entry(int pid, const char *username, 317204076Spjd const char *hostname, const char *line) 318204076Spjd{ 319204076Spjd struct logininfo *newli; 320204076Spjd 321204076Spjd newli = (struct logininfo *) xmalloc (sizeof(*newli)); 322204076Spjd (void)login_init_entry(newli, pid, username, hostname, line); 323204076Spjd return newli; 324204076Spjd} 325204076Spjd 326204076Spjd 327204076Spjd/* login_free_entry(struct logininfo *) - free struct memory */ 328204076Spjdvoid 329218138Spjdlogin_free_entry(struct logininfo *li) 330204076Spjd{ 331204076Spjd xfree(li); 332204076Spjd} 333204076Spjd 334204076Spjd 335204076Spjd/* login_init_entry(struct logininfo *, int, char*, char*, char*) 336204076Spjd * - initialise a struct logininfo 337204076Spjd * 338204076Spjd * Populates a new struct logininfo, a data structure meant to carry 339204076Spjd * the information required to portably record login info. 340204076Spjd * 341204076Spjd * Returns: 1 342204076Spjd */ 343204076Spjdint 344204076Spjdlogin_init_entry(struct logininfo *li, int pid, const char *username, 345204076Spjd const char *hostname, const char *line) 346204076Spjd{ 347204076Spjd struct passwd *pw; 348220007Spjd 349204076Spjd memset(li, 0, sizeof(*li)); 350214276Spjd 351204076Spjd li->pid = pid; 352204076Spjd 353214275Spjd /* set the line information */ 354214275Spjd if (line) 355209182Spjd line_fullname(li->line, line, sizeof(li->line)); 356223181Strociny 357220271Spjd if (username) { 358220271Spjd strlcpy(li->username, username, sizeof(li->username)); 359220271Spjd pw = getpwnam(li->username); 360223181Strociny if (pw == NULL) 361204076Spjd fatal("login_init_entry: Cannot find user \"%s\"", li->username); 362204076Spjd li->uid = pw->pw_uid; 363204076Spjd } 364212038Spjd 365204076Spjd if (hostname) 366204076Spjd strlcpy(li->hostname, hostname, sizeof(li->hostname)); 367204076Spjd 368204076Spjd return 1; 369204076Spjd} 370204076Spjd 371204076Spjd/* login_set_current_time(struct logininfo *) - set the current time 372213009Spjd * 373204076Spjd * Set the current time in a logininfo structure. This function is 374204076Spjd * meant to eliminate the need to deal with system dependencies for 375219482Strociny * time handling. 376204076Spjd */ 377204076Spjdvoid 378204076Spjdlogin_set_current_time(struct logininfo *li) 379204076Spjd{ 380219818Spjd struct timeval tv; 381204076Spjd 382204076Spjd gettimeofday(&tv, NULL); 383204076Spjd 384204076Spjd li->tv_sec = tv.tv_sec; 385212038Spjd li->tv_usec = tv.tv_usec; 386212038Spjd} 387212038Spjd 388219818Spjd/* copy a sockaddr_* into our logininfo */ 389212038Spjdvoid 390212038Spjdlogin_set_addr(struct logininfo *li, const struct sockaddr *sa, 391212038Spjd const unsigned int sa_size) 392212038Spjd{ 393204076Spjd unsigned int bufsize = sa_size; 394204076Spjd 395204076Spjd /* make sure we don't overrun our union */ 396204076Spjd if (sizeof(li->hostaddr) < sa_size) 397204076Spjd bufsize = sizeof(li->hostaddr); 398204076Spjd 399204076Spjd memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize); 400204076Spjd} 401204076Spjd 402204076Spjd 403204076Spjd/** 404204076Spjd ** login_write: Call low-level recording functions based on autoconf 405204076Spjd ** results 406212038Spjd **/ 407212038Spjdint 408218043Spjdlogin_write (struct logininfo *li) 409218043Spjd{ 410204076Spjd#ifndef HAVE_CYGWIN 411204076Spjd if ((int)geteuid() != 0) { 412204076Spjd logit("Attempt to write login records by non-root user (aborting)"); 413211977Spjd return 1; 414211984Spjd } 415218043Spjd#endif 416219482Strociny 417211984Spjd /* set the timestamp */ 418218043Spjd login_set_current_time(li); 419218043Spjd#ifdef USE_LOGIN 420218043Spjd syslogin_write_entry(li); 421218043Spjd#endif 422218043Spjd#ifdef USE_LASTLOG 423204076Spjd if (li->type == LTYPE_LOGIN) { 424218045Spjd lastlog_write_entry(li); 425218045Spjd } 426218043Spjd#endif 427219482Strociny#ifdef USE_UTMP 428218043Spjd utmp_write_entry(li); 429220005Spjd#endif 430204076Spjd#ifdef USE_WTMP 431213009Spjd wtmp_write_entry(li); 432213009Spjd#endif 433210880Spjd#ifdef USE_UTMPX 434207371Spjd utmpx_write_entry(li); 435219721Strociny#endif 436207371Spjd#ifdef USE_WTMPX 437207371Spjd wtmpx_write_entry(li); 438207371Spjd#endif 439207371Spjd return 0; 440204076Spjd} 441213007Spjd 442213007Spjd#ifdef LOGIN_NEEDS_UTMPX 443221899Spjdint 444218049Spjdlogin_utmp_only(struct logininfo *li) 445218214Spjd{ 446218049Spjd li->type = LTYPE_LOGIN; 447213007Spjd login_set_current_time(li); 448213007Spjd# ifdef USE_UTMP 449213007Spjd utmp_write_entry(li); 450213007Spjd# endif 451213007Spjd# ifdef USE_WTMP 452213007Spjd wtmp_write_entry(li); 453213007Spjd# endif 454213007Spjd# ifdef USE_UTMPX 455213007Spjd utmpx_write_entry(li); 456218138Spjd# endif 457213007Spjd# ifdef USE_WTMPX 458204076Spjd wtmpx_write_entry(li); 459212038Spjd# endif 460204076Spjd return 0; 461204076Spjd} 462218138Spjd#endif 463204076Spjd 464218138Spjd/** 465213007Spjd ** getlast_entry: Call low-level functions to retrieve the last login 466204076Spjd ** time. 467204076Spjd **/ 468204076Spjd 469204076Spjd/* take the uid in li and return the last login time */ 470204076Spjdint 471204076Spjdgetlast_entry(struct logininfo *li) 472204076Spjd{ 473204076Spjd#ifdef USE_LASTLOG 474204076Spjd return(lastlog_get_entry(li)); 475204076Spjd#else /* !USE_LASTLOG */ 476204076Spjd 477204076Spjd#ifdef DISABLE_LASTLOG 478204076Spjd /* On some systems we shouldn't even try to obtain last login 479204076Spjd * time, e.g. AIX */ 480204076Spjd return 0; 481204076Spjd# else /* DISABLE_LASTLOG */ 482204076Spjd /* Try to retrieve the last login time from wtmp */ 483204076Spjd# if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) 484204076Spjd /* retrieve last login time from utmp */ 485204076Spjd return (wtmp_get_entry(li)); 486204076Spjd# else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */ 487204076Spjd /* If wtmp isn't available, try wtmpx */ 488204076Spjd# if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX)) 489204076Spjd /* retrieve last login time from utmpx */ 490204076Spjd return (wtmpx_get_entry(li)); 491204076Spjd# else 492204076Spjd /* Give up: No means of retrieving last login time */ 493204076Spjd return 0; 494204076Spjd# endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */ 495204076Spjd# endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */ 496204076Spjd# endif /* DISABLE_LASTLOG */ 497204076Spjd#endif /* USE_LASTLOG */ 498211882Spjd} 499211882Spjd 500211882Spjd 501204076Spjd 502204076Spjd/* 503204076Spjd * 'line' string utility functions 504204076Spjd * 505204076Spjd * These functions process the 'line' string into one of three forms: 506204076Spjd * 507204076Spjd * 1. The full filename (including '/dev') 508204076Spjd * 2. The stripped name (excluding '/dev') 509204076Spjd * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00 510204076Spjd * /dev/pts/1 -> ts/1 ) 511204076Spjd * 512204076Spjd * Form 3 is used on some systems to identify a .tmp.? entry when 513204076Spjd * attempting to remove it. Typically both addition and removal is 514204076Spjd * performed by one application - say, sshd - so as long as the choice 515204076Spjd * uniquely identifies a terminal it's ok. 516204076Spjd */ 517204076Spjd 518204076Spjd 519204076Spjd/* line_fullname(): add the leading '/dev/' if it doesn't exist make 520204076Spjd * sure dst has enough space, if not just copy src (ugh) */ 521222164Spjdchar * 522211882Spjdline_fullname(char *dst, const char *src, int dstsize) 523211882Spjd{ 524204076Spjd memset(dst, '\0', dstsize); 525204076Spjd if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) { 526204076Spjd strlcpy(dst, src, dstsize); 527204076Spjd } else { 528204076Spjd strlcpy(dst, "/dev/", dstsize); 529204076Spjd strlcat(dst, src, dstsize); 530204076Spjd } 531204076Spjd return dst; 532204076Spjd} 533204076Spjd 534204076Spjd/* line_stripname(): strip the leading '/dev' if it exists, return dst */ 535204076Spjdchar * 536204076Spjdline_stripname(char *dst, const char *src, int dstsize) 537204076Spjd{ 538204076Spjd memset(dst, '\0', dstsize); 539204076Spjd if (strncmp(src, "/dev/", 5) == 0) 540204076Spjd strlcpy(dst, src + 5, dstsize); 541204076Spjd else 542204076Spjd strlcpy(dst, src, dstsize); 543204076Spjd return dst; 544204076Spjd} 545204076Spjd 546204076Spjd/* line_abbrevname(): Return the abbreviated (usually four-character) 547204076Spjd * form of the line (Just use the last <dstsize> characters of the 548204076Spjd * full name.) 549204076Spjd * 550204076Spjd * NOTE: use strncpy because we do NOT necessarily want zero 551204076Spjd * termination */ 552204076Spjdchar * 553204076Spjdline_abbrevname(char *dst, const char *src, int dstsize) 554204076Spjd{ 555204076Spjd size_t len; 556204076Spjd 557204076Spjd memset(dst, '\0', dstsize); 558204076Spjd 559204076Spjd /* Always skip prefix if present */ 560204076Spjd if (strncmp(src, "/dev/", 5) == 0) 561204076Spjd src += 5; 562204076Spjd 563204076Spjd#ifdef WITH_ABBREV_NO_TTY 564204076Spjd if (strncmp(src, "tty", 3) == 0) 565204076Spjd src += 3; 566204076Spjd#endif 567204076Spjd 568204076Spjd len = strlen(src); 569204076Spjd 570204076Spjd if (len > 0) { 571204076Spjd if (((int)len - dstsize) > 0) 572204076Spjd src += ((int)len - dstsize); 573204076Spjd 574204076Spjd /* note: _don't_ change this to strlcpy */ 575204076Spjd strncpy(dst, src, (size_t)dstsize); 576204076Spjd } 577204076Spjd 578204076Spjd return dst; 579204076Spjd} 580204076Spjd 581204076Spjd/** 582212899Spjd ** utmp utility functions 583211984Spjd ** 584211984Spjd ** These functions manipulate struct utmp, taking system differences 585211984Spjd ** into account. 586211984Spjd **/ 587218138Spjd 588211984Spjd#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN) 589211984Spjd 590211984Spjd/* build the utmp structure */ 591212038Spjdvoid 592211984Spjdset_utmp_time(struct logininfo *li, struct utmp *ut) 593211984Spjd{ 594211984Spjd# ifdef HAVE_TV_IN_UTMP 595204076Spjd ut->ut_tv.tv_sec = li->tv_sec; 596204076Spjd ut->ut_tv.tv_usec = li->tv_usec; 597204076Spjd# else 598204076Spjd# ifdef HAVE_TIME_IN_UTMP 599204076Spjd ut->ut_time = li->tv_sec; 600204076Spjd# endif 601204076Spjd# endif 602204076Spjd} 603204076Spjd 604204076Spjdvoid 605204076Spjdconstruct_utmp(struct logininfo *li, 606211877Spjd struct utmp *ut) 607204076Spjd{ 608204076Spjd# ifdef HAVE_ADDR_V6_IN_UTMP 609211984Spjd struct sockaddr_in6 *sa6; 610204076Spjd# endif 611204076Spjd memset(ut, '\0', sizeof(*ut)); 612211877Spjd 613211877Spjd /* First fill out fields used for both logins and logouts */ 614211877Spjd 615211877Spjd# ifdef HAVE_ID_IN_UTMP 616211877Spjd line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id)); 617211877Spjd# endif 618211877Spjd 619222228Spjd# ifdef HAVE_TYPE_IN_UTMP 620222228Spjd /* This is done here to keep utmp constants out of struct logininfo */ 621222228Spjd switch (li->type) { 622222228Spjd case LTYPE_LOGIN: 623222228Spjd ut->ut_type = USER_PROCESS; 624222228Spjd#ifdef _UNICOS 625222228Spjd cray_set_tmpdir(ut); 626222228Spjd#endif 627222228Spjd break; 628222228Spjd case LTYPE_LOGOUT: 629222228Spjd ut->ut_type = DEAD_PROCESS; 630222228Spjd#ifdef _UNICOS 631222228Spjd cray_retain_utmp(ut, li->pid); 632222228Spjd#endif 633204076Spjd break; 634204076Spjd } 635211882Spjd# endif 636211882Spjd set_utmp_time(li, ut); 637211882Spjd 638211882Spjd line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line)); 639211882Spjd 640211882Spjd# ifdef HAVE_PID_IN_UTMP 641211882Spjd ut->ut_pid = li->pid; 642211882Spjd# endif 643204076Spjd 644204076Spjd /* If we're logging out, leave all other fields blank */ 645211984Spjd if (li->type == LTYPE_LOGOUT) 646212051Spjd return; 647204076Spjd 648204076Spjd /* 649204076Spjd * These fields are only used when logging in, and are blank 650204076Spjd * for logouts. 651211877Spjd */ 652204076Spjd 653204076Spjd /* Use strncpy because we don't necessarily want null termination */ 654204076Spjd strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username)); 655204076Spjd# ifdef HAVE_HOST_IN_UTMP 656204076Spjd realhostname_sa(ut->ut_host, sizeof ut->ut_host, 657204076Spjd &li->hostaddr.sa, li->hostaddr.sa.sa_len); 658204076Spjd# endif 659204076Spjd# ifdef HAVE_ADDR_IN_UTMP 660204076Spjd /* this is just a 32-bit IP address */ 661204076Spjd if (li->hostaddr.sa.sa_family == AF_INET) 662204076Spjd ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 663204076Spjd# endif 664204076Spjd# ifdef HAVE_ADDR_V6_IN_UTMP 665204076Spjd /* this is just a 128-bit IPv6 address */ 666204076Spjd if (li->hostaddr.sa.sa_family == AF_INET6) { 667211877Spjd sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 668204076Spjd memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 669204076Spjd if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 670204076Spjd ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; 671204076Spjd ut->ut_addr_v6[1] = 0; 672204076Spjd ut->ut_addr_v6[2] = 0; 673211877Spjd ut->ut_addr_v6[3] = 0; 674204076Spjd } 675204076Spjd } 676204076Spjd# endif 677204076Spjd} 678204076Spjd#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */ 679204076Spjd 680204076Spjd/** 681204076Spjd ** utmpx utility functions 682204076Spjd ** 683204076Spjd ** These functions manipulate struct utmpx, accounting for system 684204076Spjd ** variations. 685204076Spjd **/ 686204076Spjd 687204076Spjd#if defined(USE_UTMPX) || defined (USE_WTMPX) 688204076Spjd/* build the utmpx structure */ 689204076Spjdvoid 690204076Spjdset_utmpx_time(struct logininfo *li, struct utmpx *utx) 691204076Spjd{ 692204076Spjd# ifdef HAVE_TV_IN_UTMPX 693204076Spjd utx->ut_tv.tv_sec = li->tv_sec; 694204076Spjd utx->ut_tv.tv_usec = li->tv_usec; 695204076Spjd# else /* HAVE_TV_IN_UTMPX */ 696204076Spjd# ifdef HAVE_TIME_IN_UTMPX 697204076Spjd utx->ut_time = li->tv_sec; 698204076Spjd# endif /* HAVE_TIME_IN_UTMPX */ 699204076Spjd# endif /* HAVE_TV_IN_UTMPX */ 700204076Spjd} 701204076Spjd 702204076Spjdvoid 703204076Spjdconstruct_utmpx(struct logininfo *li, struct utmpx *utx) 704204076Spjd{ 705204076Spjd# ifdef HAVE_ADDR_V6_IN_UTMP 706204076Spjd struct sockaddr_in6 *sa6; 707204076Spjd# endif 708204076Spjd memset(utx, '\0', sizeof(*utx)); 709204076Spjd# ifdef HAVE_ID_IN_UTMPX 710204076Spjd line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); 711204076Spjd# endif 712204076Spjd 713204076Spjd /* this is done here to keep utmp constants out of loginrec.h */ 714204076Spjd switch (li->type) { 715204076Spjd case LTYPE_LOGIN: 716204076Spjd utx->ut_type = USER_PROCESS; 717204076Spjd break; 718204076Spjd case LTYPE_LOGOUT: 719204076Spjd utx->ut_type = DEAD_PROCESS; 720204076Spjd break; 721204076Spjd } 722204076Spjd line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line)); 723204076Spjd set_utmpx_time(li, utx); 724204076Spjd utx->ut_pid = li->pid; 725204076Spjd /* strncpy(): Don't necessarily want null termination */ 726204076Spjd strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username)); 727204076Spjd 728204076Spjd if (li->type == LTYPE_LOGOUT) 729204076Spjd return; 730204076Spjd 731204076Spjd /* 732204076Spjd * These fields are only used when logging in, and are blank 733204076Spjd * for logouts. 734204076Spjd */ 735204076Spjd 736204076Spjd# ifdef HAVE_HOST_IN_UTMPX 737204076Spjd strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname)); 738204076Spjd# endif 739204076Spjd# ifdef HAVE_ADDR_IN_UTMPX 740204076Spjd /* this is just a 32-bit IP address */ 741204076Spjd if (li->hostaddr.sa.sa_family == AF_INET) 742204076Spjd utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 743204076Spjd# endif 744204076Spjd# ifdef HAVE_ADDR_V6_IN_UTMP 745204076Spjd /* this is just a 128-bit IPv6 address */ 746204076Spjd if (li->hostaddr.sa.sa_family == AF_INET6) { 747204076Spjd sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 748204076Spjd memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 749204076Spjd if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 750204076Spjd ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; 751211877Spjd ut->ut_addr_v6[1] = 0; 752204076Spjd ut->ut_addr_v6[2] = 0; 753204076Spjd ut->ut_addr_v6[3] = 0; 754204076Spjd } 755204076Spjd } 756204076Spjd# endif 757204076Spjd# ifdef HAVE_SYSLEN_IN_UTMPX 758204076Spjd /* ut_syslen is the length of the utx_host string */ 759204076Spjd utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host)); 760204076Spjd# endif 761204076Spjd} 762204076Spjd#endif /* USE_UTMPX || USE_WTMPX */ 763204076Spjd 764204076Spjd/** 765204076Spjd ** Low-level utmp functions 766204076Spjd **/ 767204076Spjd 768204076Spjd/* FIXME: (ATL) utmp_write_direct needs testing */ 769204076Spjd#ifdef USE_UTMP 770204076Spjd 771211877Spjd/* if we can, use pututline() etc. */ 772204076Spjd# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \ 773204076Spjd defined(HAVE_PUTUTLINE) 774204076Spjd# define UTMP_USE_LIBRARY 775204076Spjd# endif 776204076Spjd 777204076Spjd 778204076Spjd/* write a utmp entry with the system's help (pututline() and pals) */ 779204076Spjd# ifdef UTMP_USE_LIBRARY 780204076Spjdstatic int 781204076Spjdutmp_write_library(struct logininfo *li, struct utmp *ut) 782204076Spjd{ 783204076Spjd setutent(); 784204076Spjd pututline(ut); 785204076Spjd 786204076Spjd# ifdef HAVE_ENDUTENT 787204076Spjd endutent(); 788204076Spjd# endif 789204076Spjd return 1; 790204076Spjd} 791204076Spjd# else /* UTMP_USE_LIBRARY */ 792204076Spjd 793204076Spjd/* write a utmp entry direct to the file */ 794204076Spjd/* This is a slightly modification of code in OpenBSD's login.c */ 795204076Spjdstatic int 796204076Spjdutmp_write_direct(struct logininfo *li, struct utmp *ut) 797204076Spjd{ 798204076Spjd struct utmp old_ut; 799204076Spjd register int fd; 800204076Spjd int tty; 801211984Spjd 802204076Spjd /* FIXME: (ATL) ttyslot() needs local implementation */ 803204076Spjd 804209185Spjd#if defined(HAVE_GETTTYENT) 805204076Spjd register struct ttyent *ty; 806204076Spjd 807204076Spjd tty=0; 808211877Spjd 809204076Spjd setttyent(); 810204076Spjd while ((struct ttyent *)0 != (ty = getttyent())) { 811204076Spjd tty++; 812204076Spjd if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line))) 813 break; 814 } 815 endttyent(); 816 817 if((struct ttyent *)0 == ty) { 818 logit("utmp_write_entry: tty not found"); 819 return(1); 820 } 821#else /* FIXME */ 822 823 tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ 824 825#endif /* HAVE_GETTTYENT */ 826 827 if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { 828 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); 829 /* 830 * Prevent luser from zero'ing out ut_host. 831 * If the new ut_line is empty but the old one is not 832 * and ut_line and ut_name match, preserve the old ut_line. 833 */ 834 if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && 835 (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && 836 (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && 837 (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) { 838 (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); 839 } 840 841 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); 842 if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) 843 logit("utmp_write_direct: error writing %s: %s", 844 UTMP_FILE, strerror(errno)); 845 846 (void)close(fd); 847 return 1; 848 } else { 849 return 0; 850 } 851} 852# endif /* UTMP_USE_LIBRARY */ 853 854static int 855utmp_perform_login(struct logininfo *li) 856{ 857 struct utmp ut; 858 859 construct_utmp(li, &ut); 860# ifdef UTMP_USE_LIBRARY 861 if (!utmp_write_library(li, &ut)) { 862 logit("utmp_perform_login: utmp_write_library() failed"); 863 return 0; 864 } 865# else 866 if (!utmp_write_direct(li, &ut)) { 867 logit("utmp_perform_login: utmp_write_direct() failed"); 868 return 0; 869 } 870# endif 871 return 1; 872} 873 874 875static int 876utmp_perform_logout(struct logininfo *li) 877{ 878 struct utmp ut; 879 880 construct_utmp(li, &ut); 881# ifdef UTMP_USE_LIBRARY 882 if (!utmp_write_library(li, &ut)) { 883 logit("utmp_perform_logout: utmp_write_library() failed"); 884 return 0; 885 } 886# else 887 if (!utmp_write_direct(li, &ut)) { 888 logit("utmp_perform_logout: utmp_write_direct() failed"); 889 return 0; 890 } 891# endif 892 return 1; 893} 894 895 896int 897utmp_write_entry(struct logininfo *li) 898{ 899 switch(li->type) { 900 case LTYPE_LOGIN: 901 return utmp_perform_login(li); 902 903 case LTYPE_LOGOUT: 904 return utmp_perform_logout(li); 905 906 default: 907 logit("utmp_write_entry: invalid type field"); 908 return 0; 909 } 910} 911#endif /* USE_UTMP */ 912 913 914/** 915 ** Low-level utmpx functions 916 **/ 917 918/* not much point if we don't want utmpx entries */ 919#ifdef USE_UTMPX 920 921/* if we have the wherewithall, use pututxline etc. */ 922# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \ 923 defined(HAVE_PUTUTXLINE) 924# define UTMPX_USE_LIBRARY 925# endif 926 927 928/* write a utmpx entry with the system's help (pututxline() and pals) */ 929# ifdef UTMPX_USE_LIBRARY 930static int 931utmpx_write_library(struct logininfo *li, struct utmpx *utx) 932{ 933 setutxent(); 934 pututxline(utx); 935 936# ifdef HAVE_ENDUTXENT 937 endutxent(); 938# endif 939 return 1; 940} 941 942# else /* UTMPX_USE_LIBRARY */ 943 944/* write a utmp entry direct to the file */ 945static int 946utmpx_write_direct(struct logininfo *li, struct utmpx *utx) 947{ 948 logit("utmpx_write_direct: not implemented!"); 949 return 0; 950} 951# endif /* UTMPX_USE_LIBRARY */ 952 953static int 954utmpx_perform_login(struct logininfo *li) 955{ 956 struct utmpx utx; 957 958 construct_utmpx(li, &utx); 959# ifdef UTMPX_USE_LIBRARY 960 if (!utmpx_write_library(li, &utx)) { 961 logit("utmpx_perform_login: utmp_write_library() failed"); 962 return 0; 963 } 964# else 965 if (!utmpx_write_direct(li, &ut)) { 966 logit("utmpx_perform_login: utmp_write_direct() failed"); 967 return 0; 968 } 969# endif 970 return 1; 971} 972 973 974static int 975utmpx_perform_logout(struct logininfo *li) 976{ 977 struct utmpx utx; 978 979 construct_utmpx(li, &utx); 980# ifdef HAVE_ID_IN_UTMPX 981 line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); 982# endif 983# ifdef HAVE_TYPE_IN_UTMPX 984 utx.ut_type = DEAD_PROCESS; 985# endif 986 987# ifdef UTMPX_USE_LIBRARY 988 utmpx_write_library(li, &utx); 989# else 990 utmpx_write_direct(li, &utx); 991# endif 992 return 1; 993} 994 995int 996utmpx_write_entry(struct logininfo *li) 997{ 998 switch(li->type) { 999 case LTYPE_LOGIN: 1000 return utmpx_perform_login(li); 1001 case LTYPE_LOGOUT: 1002 return utmpx_perform_logout(li); 1003 default: 1004 logit("utmpx_write_entry: invalid type field"); 1005 return 0; 1006 } 1007} 1008#endif /* USE_UTMPX */ 1009 1010 1011/** 1012 ** Low-level wtmp functions 1013 **/ 1014 1015#ifdef USE_WTMP 1016 1017/* write a wtmp entry direct to the end of the file */ 1018/* This is a slight modification of code in OpenBSD's logwtmp.c */ 1019static int 1020wtmp_write(struct logininfo *li, struct utmp *ut) 1021{ 1022 struct stat buf; 1023 int fd, ret = 1; 1024 1025 if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 1026 logit("wtmp_write: problem writing %s: %s", 1027 WTMP_FILE, strerror(errno)); 1028 return 0; 1029 } 1030 if (fstat(fd, &buf) == 0) 1031 if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { 1032 ftruncate(fd, buf.st_size); 1033 logit("wtmp_write: problem writing %s: %s", 1034 WTMP_FILE, strerror(errno)); 1035 ret = 0; 1036 } 1037 (void)close(fd); 1038 return ret; 1039} 1040 1041static int 1042wtmp_perform_login(struct logininfo *li) 1043{ 1044 struct utmp ut; 1045 1046 construct_utmp(li, &ut); 1047 return wtmp_write(li, &ut); 1048} 1049 1050 1051static int 1052wtmp_perform_logout(struct logininfo *li) 1053{ 1054 struct utmp ut; 1055 1056 construct_utmp(li, &ut); 1057 return wtmp_write(li, &ut); 1058} 1059 1060 1061int 1062wtmp_write_entry(struct logininfo *li) 1063{ 1064 switch(li->type) { 1065 case LTYPE_LOGIN: 1066 return wtmp_perform_login(li); 1067 case LTYPE_LOGOUT: 1068 return wtmp_perform_logout(li); 1069 default: 1070 logit("wtmp_write_entry: invalid type field"); 1071 return 0; 1072 } 1073} 1074 1075 1076/* Notes on fetching login data from wtmp/wtmpx 1077 * 1078 * Logouts are usually recorded with (amongst other things) a blank 1079 * username on a given tty line. However, some systems (HP-UX is one) 1080 * leave all fields set, but change the ut_type field to DEAD_PROCESS. 1081 * 1082 * Since we're only looking for logins here, we know that the username 1083 * must be set correctly. On systems that leave it in, we check for 1084 * ut_type==USER_PROCESS (indicating a login.) 1085 * 1086 * Portability: Some systems may set something other than USER_PROCESS 1087 * to indicate a login process. I don't know of any as I write. Also, 1088 * it's possible that some systems may both leave the username in 1089 * place and not have ut_type. 1090 */ 1091 1092/* return true if this wtmp entry indicates a login */ 1093static int 1094wtmp_islogin(struct logininfo *li, struct utmp *ut) 1095{ 1096 if (strncmp(li->username, ut->ut_name, 1097 MIN_SIZEOF(li->username, ut->ut_name)) == 0) { 1098# ifdef HAVE_TYPE_IN_UTMP 1099 if (ut->ut_type & USER_PROCESS) 1100 return 1; 1101# else 1102 return 1; 1103# endif 1104 } 1105 return 0; 1106} 1107 1108int 1109wtmp_get_entry(struct logininfo *li) 1110{ 1111 struct stat st; 1112 struct utmp ut; 1113 int fd, found=0; 1114 1115 /* Clear the time entries in our logininfo */ 1116 li->tv_sec = li->tv_usec = 0; 1117 1118 if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { 1119 logit("wtmp_get_entry: problem opening %s: %s", 1120 WTMP_FILE, strerror(errno)); 1121 return 0; 1122 } 1123 if (fstat(fd, &st) != 0) { 1124 logit("wtmp_get_entry: couldn't stat %s: %s", 1125 WTMP_FILE, strerror(errno)); 1126 close(fd); 1127 return 0; 1128 } 1129 1130 /* Seek to the start of the last struct utmp */ 1131 if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { 1132 /* Looks like we've got a fresh wtmp file */ 1133 close(fd); 1134 return 0; 1135 } 1136 1137 while (!found) { 1138 if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { 1139 logit("wtmp_get_entry: read of %s failed: %s", 1140 WTMP_FILE, strerror(errno)); 1141 close (fd); 1142 return 0; 1143 } 1144 if ( wtmp_islogin(li, &ut) ) { 1145 found = 1; 1146 /* We've already checked for a time in struct 1147 * utmp, in login_getlast(). */ 1148# ifdef HAVE_TIME_IN_UTMP 1149 li->tv_sec = ut.ut_time; 1150# else 1151# if HAVE_TV_IN_UTMP 1152 li->tv_sec = ut.ut_tv.tv_sec; 1153# endif 1154# endif 1155 line_fullname(li->line, ut.ut_line, 1156 MIN_SIZEOF(li->line, ut.ut_line)); 1157# ifdef HAVE_HOST_IN_UTMP 1158 strlcpy(li->hostname, ut.ut_host, 1159 MIN_SIZEOF(li->hostname, ut.ut_host)); 1160# endif 1161 continue; 1162 } 1163 /* Seek back 2 x struct utmp */ 1164 if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { 1165 /* We've found the start of the file, so quit */ 1166 close (fd); 1167 return 0; 1168 } 1169 } 1170 1171 /* We found an entry. Tidy up and return */ 1172 close(fd); 1173 return 1; 1174} 1175# endif /* USE_WTMP */ 1176 1177 1178/** 1179 ** Low-level wtmpx functions 1180 **/ 1181 1182#ifdef USE_WTMPX 1183/* write a wtmpx entry direct to the end of the file */ 1184/* This is a slight modification of code in OpenBSD's logwtmp.c */ 1185static int 1186wtmpx_write(struct logininfo *li, struct utmpx *utx) 1187{ 1188 struct stat buf; 1189 int fd, ret = 1; 1190 1191 if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 1192 logit("wtmpx_write: problem opening %s: %s", 1193 WTMPX_FILE, strerror(errno)); 1194 return 0; 1195 } 1196 1197 if (fstat(fd, &buf) == 0) 1198 if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) { 1199 ftruncate(fd, buf.st_size); 1200 logit("wtmpx_write: problem writing %s: %s", 1201 WTMPX_FILE, strerror(errno)); 1202 ret = 0; 1203 } 1204 (void)close(fd); 1205 1206 return ret; 1207} 1208 1209 1210static int 1211wtmpx_perform_login(struct logininfo *li) 1212{ 1213 struct utmpx utx; 1214 1215 construct_utmpx(li, &utx); 1216 return wtmpx_write(li, &utx); 1217} 1218 1219 1220static int 1221wtmpx_perform_logout(struct logininfo *li) 1222{ 1223 struct utmpx utx; 1224 1225 construct_utmpx(li, &utx); 1226 return wtmpx_write(li, &utx); 1227} 1228 1229 1230int 1231wtmpx_write_entry(struct logininfo *li) 1232{ 1233 switch(li->type) { 1234 case LTYPE_LOGIN: 1235 return wtmpx_perform_login(li); 1236 case LTYPE_LOGOUT: 1237 return wtmpx_perform_logout(li); 1238 default: 1239 logit("wtmpx_write_entry: invalid type field"); 1240 return 0; 1241 } 1242} 1243 1244/* Please see the notes above wtmp_islogin() for information about the 1245 next two functions */ 1246 1247/* Return true if this wtmpx entry indicates a login */ 1248static int 1249wtmpx_islogin(struct logininfo *li, struct utmpx *utx) 1250{ 1251 if ( strncmp(li->username, utx->ut_name, 1252 MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) { 1253# ifdef HAVE_TYPE_IN_UTMPX 1254 if (utx->ut_type == USER_PROCESS) 1255 return 1; 1256# else 1257 return 1; 1258# endif 1259 } 1260 return 0; 1261} 1262 1263 1264int 1265wtmpx_get_entry(struct logininfo *li) 1266{ 1267 struct stat st; 1268 struct utmpx utx; 1269 int fd, found=0; 1270 1271 /* Clear the time entries */ 1272 li->tv_sec = li->tv_usec = 0; 1273 1274 if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { 1275 logit("wtmpx_get_entry: problem opening %s: %s", 1276 WTMPX_FILE, strerror(errno)); 1277 return 0; 1278 } 1279 if (fstat(fd, &st) != 0) { 1280 logit("wtmpx_get_entry: couldn't stat %s: %s", 1281 WTMPX_FILE, strerror(errno)); 1282 close(fd); 1283 return 0; 1284 } 1285 1286 /* Seek to the start of the last struct utmpx */ 1287 if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { 1288 /* probably a newly rotated wtmpx file */ 1289 close(fd); 1290 return 0; 1291 } 1292 1293 while (!found) { 1294 if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { 1295 logit("wtmpx_get_entry: read of %s failed: %s", 1296 WTMPX_FILE, strerror(errno)); 1297 close (fd); 1298 return 0; 1299 } 1300 /* Logouts are recorded as a blank username on a particular line. 1301 * So, we just need to find the username in struct utmpx */ 1302 if ( wtmpx_islogin(li, &utx) ) { 1303 found = 1; 1304# ifdef HAVE_TV_IN_UTMPX 1305 li->tv_sec = utx.ut_tv.tv_sec; 1306# else 1307# ifdef HAVE_TIME_IN_UTMPX 1308 li->tv_sec = utx.ut_time; 1309# endif 1310# endif 1311 line_fullname(li->line, utx.ut_line, sizeof(li->line)); 1312# ifdef HAVE_HOST_IN_UTMPX 1313 strlcpy(li->hostname, utx.ut_host, 1314 MIN_SIZEOF(li->hostname, utx.ut_host)); 1315# endif 1316 continue; 1317 } 1318 if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { 1319 close (fd); 1320 return 0; 1321 } 1322 } 1323 1324 close(fd); 1325 return 1; 1326} 1327#endif /* USE_WTMPX */ 1328 1329/** 1330 ** Low-level libutil login() functions 1331 **/ 1332 1333#ifdef USE_LOGIN 1334static int 1335syslogin_perform_login(struct logininfo *li) 1336{ 1337 struct utmp *ut; 1338 1339 if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) { 1340 logit("syslogin_perform_login: couldn't malloc()"); 1341 return 0; 1342 } 1343 construct_utmp(li, ut); 1344 login(ut); 1345 free(ut); 1346 1347 return 1; 1348} 1349 1350static int 1351syslogin_perform_logout(struct logininfo *li) 1352{ 1353# ifdef HAVE_LOGOUT 1354 char line[8]; 1355 1356 (void)line_stripname(line, li->line, sizeof(line)); 1357 1358 if (!logout(line)) { 1359 logit("syslogin_perform_logout: logout() returned an error"); 1360# ifdef HAVE_LOGWTMP 1361 } else { 1362 logwtmp(line, "", ""); 1363# endif 1364 } 1365 /* FIXME: (ATL - if the need arises) What to do if we have 1366 * login, but no logout? what if logout but no logwtmp? All 1367 * routines are in libutil so they should all be there, 1368 * but... */ 1369# endif 1370 return 1; 1371} 1372 1373int 1374syslogin_write_entry(struct logininfo *li) 1375{ 1376 switch (li->type) { 1377 case LTYPE_LOGIN: 1378 return syslogin_perform_login(li); 1379 case LTYPE_LOGOUT: 1380 return syslogin_perform_logout(li); 1381 default: 1382 logit("syslogin_write_entry: Invalid type field"); 1383 return 0; 1384 } 1385} 1386#endif /* USE_LOGIN */ 1387 1388/* end of file log-syslogin.c */ 1389 1390/** 1391 ** Low-level lastlog functions 1392 **/ 1393 1394#ifdef USE_LASTLOG 1395#define LL_FILE 1 1396#define LL_DIR 2 1397#define LL_OTHER 3 1398 1399static void 1400lastlog_construct(struct logininfo *li, struct lastlog *last) 1401{ 1402 /* clear the structure */ 1403 memset(last, '\0', sizeof(*last)); 1404 1405 (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line)); 1406 strlcpy(last->ll_host, li->hostname, 1407 MIN_SIZEOF(last->ll_host, li->hostname)); 1408 last->ll_time = li->tv_sec; 1409} 1410 1411static int 1412lastlog_filetype(char *filename) 1413{ 1414 struct stat st; 1415 1416 if (stat(LASTLOG_FILE, &st) != 0) { 1417 logit("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE, 1418 strerror(errno)); 1419 return 0; 1420 } 1421 if (S_ISDIR(st.st_mode)) 1422 return LL_DIR; 1423 else if (S_ISREG(st.st_mode)) 1424 return LL_FILE; 1425 else 1426 return LL_OTHER; 1427} 1428 1429 1430/* open the file (using filemode) and seek to the login entry */ 1431static int 1432lastlog_openseek(struct logininfo *li, int *fd, int filemode) 1433{ 1434 off_t offset; 1435 int type; 1436 char lastlog_file[1024]; 1437 1438 type = lastlog_filetype(LASTLOG_FILE); 1439 switch (type) { 1440 case LL_FILE: 1441 strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); 1442 break; 1443 case LL_DIR: 1444 snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", 1445 LASTLOG_FILE, li->username); 1446 break; 1447 default: 1448 logit("lastlog_openseek: %.100s is not a file or directory!", 1449 LASTLOG_FILE); 1450 return 0; 1451 } 1452 1453 *fd = open(lastlog_file, filemode, 0600); 1454 if ( *fd < 0) { 1455 debug("lastlog_openseek: Couldn't open %s: %s", 1456 lastlog_file, strerror(errno)); 1457 return 0; 1458 } 1459 1460 if (type == LL_FILE) { 1461 /* find this uid's offset in the lastlog file */ 1462 offset = (off_t) ((long)li->uid * sizeof(struct lastlog)); 1463 1464 if ( lseek(*fd, offset, SEEK_SET) != offset ) { 1465 logit("lastlog_openseek: %s->lseek(): %s", 1466 lastlog_file, strerror(errno)); 1467 return 0; 1468 } 1469 } 1470 1471 return 1; 1472} 1473 1474static int 1475lastlog_perform_login(struct logininfo *li) 1476{ 1477 struct lastlog last; 1478 int fd; 1479 1480 /* create our struct lastlog */ 1481 lastlog_construct(li, &last); 1482 1483 if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) 1484 return(0); 1485 1486 /* write the entry */ 1487 if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) { 1488 close(fd); 1489 logit("lastlog_write_filemode: Error writing to %s: %s", 1490 LASTLOG_FILE, strerror(errno)); 1491 return 0; 1492 } 1493 1494 close(fd); 1495 return 1; 1496} 1497 1498int 1499lastlog_write_entry(struct logininfo *li) 1500{ 1501 switch(li->type) { 1502 case LTYPE_LOGIN: 1503 return lastlog_perform_login(li); 1504 default: 1505 logit("lastlog_write_entry: Invalid type field"); 1506 return 0; 1507 } 1508} 1509 1510static void 1511lastlog_populate_entry(struct logininfo *li, struct lastlog *last) 1512{ 1513 line_fullname(li->line, last->ll_line, sizeof(li->line)); 1514 strlcpy(li->hostname, last->ll_host, 1515 MIN_SIZEOF(li->hostname, last->ll_host)); 1516 li->tv_sec = last->ll_time; 1517} 1518 1519int 1520lastlog_get_entry(struct logininfo *li) 1521{ 1522 struct lastlog last; 1523 int fd, ret; 1524 1525 if (!lastlog_openseek(li, &fd, O_RDONLY)) 1526 return (0); 1527 1528 ret = atomicio(read, fd, &last, sizeof(last)); 1529 close(fd); 1530 1531 switch (ret) { 1532 case 0: 1533 memset(&last, '\0', sizeof(last)); 1534 /* FALLTHRU */ 1535 case sizeof(last): 1536 lastlog_populate_entry(li, &last); 1537 return (1); 1538 case -1: 1539 error("%s: Error reading from %s: %s", __func__, 1540 LASTLOG_FILE, strerror(errno)); 1541 return (0); 1542 default: 1543 error("%s: Error reading from %s: Expecting %d, got %d", 1544 __func__, LASTLOG_FILE, sizeof(last), ret); 1545 return (0); 1546 } 1547 1548 /* NOTREACHED */ 1549 return (0); 1550} 1551#endif /* USE_LASTLOG */ 1552