118de8d7fSPeter Avalos /* 218de8d7fSPeter Avalos * Copyright (c) 2000 Andre Lucas. All rights reserved. 318de8d7fSPeter Avalos * Portions copyright (c) 1998 Todd C. Miller 418de8d7fSPeter Avalos * Portions copyright (c) 1996 Jason Downs 518de8d7fSPeter Avalos * Portions copyright (c) 1996 Theo de Raadt 618de8d7fSPeter Avalos * 718de8d7fSPeter Avalos * Redistribution and use in source and binary forms, with or without 818de8d7fSPeter Avalos * modification, are permitted provided that the following conditions 918de8d7fSPeter Avalos * are met: 1018de8d7fSPeter Avalos * 1. Redistributions of source code must retain the above copyright 1118de8d7fSPeter Avalos * notice, this list of conditions and the following disclaimer. 1218de8d7fSPeter Avalos * 2. Redistributions in binary form must reproduce the above copyright 1318de8d7fSPeter Avalos * notice, this list of conditions and the following disclaimer in the 1418de8d7fSPeter Avalos * documentation and/or other materials provided with the distribution. 1518de8d7fSPeter Avalos * 1618de8d7fSPeter Avalos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1718de8d7fSPeter Avalos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1818de8d7fSPeter Avalos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1918de8d7fSPeter Avalos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2018de8d7fSPeter Avalos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2118de8d7fSPeter Avalos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2218de8d7fSPeter Avalos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2318de8d7fSPeter Avalos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2418de8d7fSPeter Avalos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2518de8d7fSPeter Avalos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2618de8d7fSPeter Avalos */ 2718de8d7fSPeter Avalos 2818de8d7fSPeter Avalos /* 2918de8d7fSPeter Avalos * The btmp logging code is derived from login.c from util-linux and is under 3018de8d7fSPeter Avalos * the the following license: 3118de8d7fSPeter Avalos * 3218de8d7fSPeter Avalos * Copyright (c) 1980, 1987, 1988 The Regents of the University of California. 3318de8d7fSPeter Avalos * All rights reserved. 3418de8d7fSPeter Avalos * 3518de8d7fSPeter Avalos * Redistribution and use in source and binary forms are permitted 3618de8d7fSPeter Avalos * provided that the above copyright notice and this paragraph are 3718de8d7fSPeter Avalos * duplicated in all such forms and that any documentation, 3818de8d7fSPeter Avalos * advertising materials, and other materials related to such 3918de8d7fSPeter Avalos * distribution and use acknowledge that the software was developed 4018de8d7fSPeter Avalos * by the University of California, Berkeley. The name of the 4118de8d7fSPeter Avalos * University may not be used to endorse or promote products derived 4218de8d7fSPeter Avalos * from this software without specific prior written permission. 4318de8d7fSPeter Avalos * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 4418de8d7fSPeter Avalos * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 4518de8d7fSPeter Avalos * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 4618de8d7fSPeter Avalos */ 4718de8d7fSPeter Avalos 4818de8d7fSPeter Avalos 4918de8d7fSPeter Avalos /** 5018de8d7fSPeter Avalos ** loginrec.c: platform-independent login recording and lastlog retrieval 5118de8d7fSPeter Avalos **/ 5218de8d7fSPeter Avalos 5318de8d7fSPeter Avalos /* 5418de8d7fSPeter Avalos * The new login code explained 5518de8d7fSPeter Avalos * ============================ 5618de8d7fSPeter Avalos * 5718de8d7fSPeter Avalos * This code attempts to provide a common interface to login recording 5818de8d7fSPeter Avalos * (utmp and friends) and last login time retrieval. 5918de8d7fSPeter Avalos * 6018de8d7fSPeter Avalos * Its primary means of achieving this is to use 'struct logininfo', a 6118de8d7fSPeter Avalos * union of all the useful fields in the various different types of 6218de8d7fSPeter Avalos * system login record structures one finds on UNIX variants. 6318de8d7fSPeter Avalos * 6418de8d7fSPeter Avalos * We depend on autoconf to define which recording methods are to be 6518de8d7fSPeter Avalos * used, and which fields are contained in the relevant data structures 6618de8d7fSPeter Avalos * on the local system. Many C preprocessor symbols affect which code 6718de8d7fSPeter Avalos * gets compiled here. 6818de8d7fSPeter Avalos * 6918de8d7fSPeter Avalos * The code is designed to make it easy to modify a particular 7018de8d7fSPeter Avalos * recording method, without affecting other methods nor requiring so 7118de8d7fSPeter Avalos * many nested conditional compilation blocks as were commonplace in 7218de8d7fSPeter Avalos * the old code. 7318de8d7fSPeter Avalos * 7418de8d7fSPeter Avalos * For login recording, we try to use the local system's libraries as 7518de8d7fSPeter Avalos * these are clearly most likely to work correctly. For utmp systems 7618de8d7fSPeter Avalos * this usually means login() and logout() or setutent() etc., probably 7718de8d7fSPeter Avalos * in libutil, along with logwtmp() etc. On these systems, we fall back 7818de8d7fSPeter Avalos * to writing the files directly if we have to, though this method 7918de8d7fSPeter Avalos * requires very thorough testing so we do not corrupt local auditing 8018de8d7fSPeter Avalos * information. These files and their access methods are very system 8118de8d7fSPeter Avalos * specific indeed. 8218de8d7fSPeter Avalos * 8318de8d7fSPeter Avalos * For utmpx systems, the corresponding library functions are 8418de8d7fSPeter Avalos * setutxent() etc. To the author's knowledge, all utmpx systems have 8518de8d7fSPeter Avalos * these library functions and so no direct write is attempted. If such 8618de8d7fSPeter Avalos * a system exists and needs support, direct analogues of the [uw]tmp 8718de8d7fSPeter Avalos * code should suffice. 8818de8d7fSPeter Avalos * 8918de8d7fSPeter Avalos * Retrieving the time of last login ('lastlog') is in some ways even 9018de8d7fSPeter Avalos * more problemmatic than login recording. Some systems provide a 9118de8d7fSPeter Avalos * simple table of all users which we seek based on uid and retrieve a 9218de8d7fSPeter Avalos * relatively standard structure. Others record the same information in 9318de8d7fSPeter Avalos * a directory with a separate file, and others don't record the 9418de8d7fSPeter Avalos * information separately at all. For systems in the latter category, 9518de8d7fSPeter Avalos * we look backwards in the wtmp or wtmpx file for the last login entry 9618de8d7fSPeter Avalos * for our user. Naturally this is slower and on busy systems could 9718de8d7fSPeter Avalos * incur a significant performance penalty. 9818de8d7fSPeter Avalos * 9918de8d7fSPeter Avalos * Calling the new code 10018de8d7fSPeter Avalos * -------------------- 10118de8d7fSPeter Avalos * 10218de8d7fSPeter Avalos * In OpenSSH all login recording and retrieval is performed in 10318de8d7fSPeter Avalos * login.c. Here you'll find working examples. Also, in the logintest.c 10418de8d7fSPeter Avalos * program there are more examples. 10518de8d7fSPeter Avalos * 10618de8d7fSPeter Avalos * Internal handler calling method 10718de8d7fSPeter Avalos * ------------------------------- 10818de8d7fSPeter Avalos * 10918de8d7fSPeter Avalos * When a call is made to login_login() or login_logout(), both 11018de8d7fSPeter Avalos * routines set a struct logininfo flag defining which action (log in, 11118de8d7fSPeter Avalos * or log out) is to be taken. They both then call login_write(), which 11218de8d7fSPeter Avalos * calls whichever of the many structure-specific handlers autoconf 11318de8d7fSPeter Avalos * selects for the local system. 11418de8d7fSPeter Avalos * 11518de8d7fSPeter Avalos * The handlers themselves handle system data structure specifics. Both 11618de8d7fSPeter Avalos * struct utmp and struct utmpx have utility functions (see 11718de8d7fSPeter Avalos * construct_utmp*()) to try to make it simpler to add extra systems 11818de8d7fSPeter Avalos * that introduce new features to either structure. 11918de8d7fSPeter Avalos * 12018de8d7fSPeter Avalos * While it may seem terribly wasteful to replicate so much similar 12118de8d7fSPeter Avalos * code for each method, experience has shown that maintaining code to 12218de8d7fSPeter Avalos * write both struct utmp and utmpx in one function, whilst maintaining 12318de8d7fSPeter Avalos * support for all systems whether they have library support or not, is 12418de8d7fSPeter Avalos * a difficult and time-consuming task. 12518de8d7fSPeter Avalos * 12618de8d7fSPeter Avalos * Lastlog support proceeds similarly. Functions login_get_lastlog() 12718de8d7fSPeter Avalos * (and its OpenSSH-tuned friend login_get_lastlog_time()) call 12818de8d7fSPeter Avalos * getlast_entry(), which tries one of three methods to find the last 12918de8d7fSPeter Avalos * login time. It uses local system lastlog support if it can, 13018de8d7fSPeter Avalos * otherwise it tries wtmp or wtmpx before giving up and returning 0, 13118de8d7fSPeter Avalos * meaning "tilt". 13218de8d7fSPeter Avalos * 13318de8d7fSPeter Avalos * Maintenance 13418de8d7fSPeter Avalos * ----------- 13518de8d7fSPeter Avalos * 13618de8d7fSPeter Avalos * In many cases it's possible to tweak autoconf to select the correct 13718de8d7fSPeter Avalos * methods for a particular platform, either by improving the detection 13818de8d7fSPeter Avalos * code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE 13918de8d7fSPeter Avalos * symbols for the platform. 14018de8d7fSPeter Avalos * 14118de8d7fSPeter Avalos * Use logintest to check which symbols are defined before modifying 14218de8d7fSPeter Avalos * configure.ac and loginrec.c. (You have to build logintest yourself 14318de8d7fSPeter Avalos * with 'make logintest' as it's not built by default.) 14418de8d7fSPeter Avalos * 14518de8d7fSPeter Avalos * Otherwise, patches to the specific method(s) are very helpful! 14618de8d7fSPeter Avalos */ 14718de8d7fSPeter Avalos 14818de8d7fSPeter Avalos #include "includes.h" 14918de8d7fSPeter Avalos 15018de8d7fSPeter Avalos #include <sys/types.h> 15118de8d7fSPeter Avalos #include <sys/stat.h> 15218de8d7fSPeter Avalos #include <sys/socket.h> 153e9778795SPeter Avalos #ifdef HAVE_SYS_TIME_H 154e9778795SPeter Avalos # include <sys/time.h> 155e9778795SPeter Avalos #endif 15618de8d7fSPeter Avalos 15718de8d7fSPeter Avalos #include <netinet/in.h> 15818de8d7fSPeter Avalos 159*0cbfa66cSDaniel Fojt #include <stdlib.h> 16018de8d7fSPeter Avalos #include <errno.h> 16118de8d7fSPeter Avalos #include <fcntl.h> 16218de8d7fSPeter Avalos #ifdef HAVE_PATHS_H 16318de8d7fSPeter Avalos # include <paths.h> 16418de8d7fSPeter Avalos #endif 16518de8d7fSPeter Avalos #include <pwd.h> 16618de8d7fSPeter Avalos #include <stdarg.h> 167*0cbfa66cSDaniel Fojt #include <stdio.h> 16818de8d7fSPeter Avalos #include <string.h> 16918de8d7fSPeter Avalos #include <time.h> 17018de8d7fSPeter Avalos #include <unistd.h> 17118de8d7fSPeter Avalos 17218de8d7fSPeter Avalos #include "xmalloc.h" 173664f4763Szrj #include "sshkey.h" 17418de8d7fSPeter Avalos #include "hostfile.h" 17518de8d7fSPeter Avalos #include "ssh.h" 17618de8d7fSPeter Avalos #include "loginrec.h" 17718de8d7fSPeter Avalos #include "log.h" 17818de8d7fSPeter Avalos #include "atomicio.h" 17918de8d7fSPeter Avalos #include "packet.h" 18018de8d7fSPeter Avalos #include "canohost.h" 18118de8d7fSPeter Avalos #include "auth.h" 182664f4763Szrj #include "sshbuf.h" 183664f4763Szrj #include "ssherr.h" 18418de8d7fSPeter Avalos 18518de8d7fSPeter Avalos #ifdef HAVE_UTIL_H 18618de8d7fSPeter Avalos # include <util.h> 18718de8d7fSPeter Avalos #endif 18818de8d7fSPeter Avalos 18918de8d7fSPeter Avalos /** 19018de8d7fSPeter Avalos ** prototypes for helper functions in this file 19118de8d7fSPeter Avalos **/ 19218de8d7fSPeter Avalos 19318de8d7fSPeter Avalos #if HAVE_UTMP_H 19418de8d7fSPeter Avalos void set_utmp_time(struct logininfo *li, struct utmp *ut); 19518de8d7fSPeter Avalos void construct_utmp(struct logininfo *li, struct utmp *ut); 19618de8d7fSPeter Avalos #endif 19718de8d7fSPeter Avalos 19818de8d7fSPeter Avalos #ifdef HAVE_UTMPX_H 19918de8d7fSPeter Avalos void set_utmpx_time(struct logininfo *li, struct utmpx *ut); 20018de8d7fSPeter Avalos void construct_utmpx(struct logininfo *li, struct utmpx *ut); 20118de8d7fSPeter Avalos #endif 20218de8d7fSPeter Avalos 20318de8d7fSPeter Avalos int utmp_write_entry(struct logininfo *li); 20418de8d7fSPeter Avalos int utmpx_write_entry(struct logininfo *li); 20518de8d7fSPeter Avalos int wtmp_write_entry(struct logininfo *li); 20618de8d7fSPeter Avalos int wtmpx_write_entry(struct logininfo *li); 20718de8d7fSPeter Avalos int lastlog_write_entry(struct logininfo *li); 20818de8d7fSPeter Avalos int syslogin_write_entry(struct logininfo *li); 20918de8d7fSPeter Avalos 21018de8d7fSPeter Avalos int getlast_entry(struct logininfo *li); 21118de8d7fSPeter Avalos int lastlog_get_entry(struct logininfo *li); 212856ea928SPeter Avalos int utmpx_get_entry(struct logininfo *li); 21318de8d7fSPeter Avalos int wtmp_get_entry(struct logininfo *li); 21418de8d7fSPeter Avalos int wtmpx_get_entry(struct logininfo *li); 21518de8d7fSPeter Avalos 216664f4763Szrj extern struct sshbuf *loginmsg; 21718de8d7fSPeter Avalos 21818de8d7fSPeter Avalos /* pick the shortest string */ 21918de8d7fSPeter Avalos #define MIN_SIZEOF(s1,s2) (sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2)) 22018de8d7fSPeter Avalos 22118de8d7fSPeter Avalos /** 22218de8d7fSPeter Avalos ** platform-independent login functions 22318de8d7fSPeter Avalos **/ 22418de8d7fSPeter Avalos 22518de8d7fSPeter Avalos /* 22618de8d7fSPeter Avalos * login_login(struct logininfo *) - Record a login 22718de8d7fSPeter Avalos * 22818de8d7fSPeter Avalos * Call with a pointer to a struct logininfo initialised with 22918de8d7fSPeter Avalos * login_init_entry() or login_alloc_entry() 23018de8d7fSPeter Avalos * 23118de8d7fSPeter Avalos * Returns: 23218de8d7fSPeter Avalos * >0 if successful 23318de8d7fSPeter Avalos * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 23418de8d7fSPeter Avalos */ 23518de8d7fSPeter Avalos int 23618de8d7fSPeter Avalos login_login(struct logininfo *li) 23718de8d7fSPeter Avalos { 23818de8d7fSPeter Avalos li->type = LTYPE_LOGIN; 23918de8d7fSPeter Avalos return (login_write(li)); 24018de8d7fSPeter Avalos } 24118de8d7fSPeter Avalos 24218de8d7fSPeter Avalos 24318de8d7fSPeter Avalos /* 24418de8d7fSPeter Avalos * login_logout(struct logininfo *) - Record a logout 24518de8d7fSPeter Avalos * 24618de8d7fSPeter Avalos * Call as with login_login() 24718de8d7fSPeter Avalos * 24818de8d7fSPeter Avalos * Returns: 24918de8d7fSPeter Avalos * >0 if successful 25018de8d7fSPeter Avalos * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 25118de8d7fSPeter Avalos */ 25218de8d7fSPeter Avalos int 25318de8d7fSPeter Avalos login_logout(struct logininfo *li) 25418de8d7fSPeter Avalos { 25518de8d7fSPeter Avalos li->type = LTYPE_LOGOUT; 25618de8d7fSPeter Avalos return (login_write(li)); 25718de8d7fSPeter Avalos } 25818de8d7fSPeter Avalos 25918de8d7fSPeter Avalos /* 26018de8d7fSPeter Avalos * login_get_lastlog_time(int) - Retrieve the last login time 26118de8d7fSPeter Avalos * 26218de8d7fSPeter Avalos * Retrieve the last login time for the given uid. Will try to use the 26318de8d7fSPeter Avalos * system lastlog facilities if they are available, but will fall back 26418de8d7fSPeter Avalos * to looking in wtmp/wtmpx if necessary 26518de8d7fSPeter Avalos * 26618de8d7fSPeter Avalos * Returns: 26718de8d7fSPeter Avalos * 0 on failure, or if user has never logged in 26818de8d7fSPeter Avalos * Time in seconds from the epoch if successful 26918de8d7fSPeter Avalos * 27018de8d7fSPeter Avalos * Useful preprocessor symbols: 27118de8d7fSPeter Avalos * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog 27218de8d7fSPeter Avalos * info 27318de8d7fSPeter Avalos * USE_LASTLOG: If set, indicates the presence of system lastlog 27418de8d7fSPeter Avalos * facilities. If this and DISABLE_LASTLOG are not set, 27518de8d7fSPeter Avalos * try to retrieve lastlog information from wtmp/wtmpx. 27618de8d7fSPeter Avalos */ 27718de8d7fSPeter Avalos unsigned int 2789f304aafSPeter Avalos login_get_lastlog_time(const uid_t uid) 27918de8d7fSPeter Avalos { 28018de8d7fSPeter Avalos struct logininfo li; 28118de8d7fSPeter Avalos 28218de8d7fSPeter Avalos if (login_get_lastlog(&li, uid)) 28318de8d7fSPeter Avalos return (li.tv_sec); 28418de8d7fSPeter Avalos else 28518de8d7fSPeter Avalos return (0); 28618de8d7fSPeter Avalos } 28718de8d7fSPeter Avalos 28818de8d7fSPeter Avalos /* 28918de8d7fSPeter Avalos * login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry 29018de8d7fSPeter Avalos * 29118de8d7fSPeter Avalos * Retrieve a logininfo structure populated (only partially) with 29218de8d7fSPeter Avalos * information from the system lastlog data, or from wtmp/wtmpx if no 29318de8d7fSPeter Avalos * system lastlog information exists. 29418de8d7fSPeter Avalos * 29518de8d7fSPeter Avalos * Note this routine must be given a pre-allocated logininfo. 29618de8d7fSPeter Avalos * 29718de8d7fSPeter Avalos * Returns: 29818de8d7fSPeter Avalos * >0: A pointer to your struct logininfo if successful 29918de8d7fSPeter Avalos * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 30018de8d7fSPeter Avalos */ 30118de8d7fSPeter Avalos struct logininfo * 3029f304aafSPeter Avalos login_get_lastlog(struct logininfo *li, const uid_t uid) 30318de8d7fSPeter Avalos { 30418de8d7fSPeter Avalos struct passwd *pw; 30518de8d7fSPeter Avalos 30618de8d7fSPeter Avalos memset(li, '\0', sizeof(*li)); 30718de8d7fSPeter Avalos li->uid = uid; 30818de8d7fSPeter Avalos 30918de8d7fSPeter Avalos /* 31018de8d7fSPeter Avalos * If we don't have a 'real' lastlog, we need the username to 31118de8d7fSPeter Avalos * reliably search wtmp(x) for the last login (see 31218de8d7fSPeter Avalos * wtmp_get_entry().) 31318de8d7fSPeter Avalos */ 31418de8d7fSPeter Avalos pw = getpwuid(uid); 31518de8d7fSPeter Avalos if (pw == NULL) 3169f304aafSPeter Avalos fatal("%s: Cannot find account for uid %ld", __func__, 3179f304aafSPeter Avalos (long)uid); 31818de8d7fSPeter Avalos 31936e94dc5SPeter Avalos if (strlcpy(li->username, pw->pw_name, sizeof(li->username)) >= 32036e94dc5SPeter Avalos sizeof(li->username)) { 32136e94dc5SPeter Avalos error("%s: username too long (%lu > max %lu)", __func__, 32236e94dc5SPeter Avalos (unsigned long)strlen(pw->pw_name), 32336e94dc5SPeter Avalos (unsigned long)sizeof(li->username) - 1); 32436e94dc5SPeter Avalos return NULL; 32536e94dc5SPeter Avalos } 32618de8d7fSPeter Avalos 32718de8d7fSPeter Avalos if (getlast_entry(li)) 32818de8d7fSPeter Avalos return (li); 32918de8d7fSPeter Avalos else 33018de8d7fSPeter Avalos return (NULL); 33118de8d7fSPeter Avalos } 33218de8d7fSPeter Avalos 33318de8d7fSPeter Avalos /* 33418de8d7fSPeter Avalos * login_alloc_entry(int, char*, char*, char*) - Allocate and initialise 33518de8d7fSPeter Avalos * a logininfo structure 33618de8d7fSPeter Avalos * 33718de8d7fSPeter Avalos * This function creates a new struct logininfo, a data structure 33818de8d7fSPeter Avalos * meant to carry the information required to portably record login info. 33918de8d7fSPeter Avalos * 34018de8d7fSPeter Avalos * Returns a pointer to a newly created struct logininfo. If memory 34118de8d7fSPeter Avalos * allocation fails, the program halts. 34218de8d7fSPeter Avalos */ 34318de8d7fSPeter Avalos struct 3449f304aafSPeter Avalos logininfo *login_alloc_entry(pid_t pid, const char *username, 34518de8d7fSPeter Avalos const char *hostname, const char *line) 34618de8d7fSPeter Avalos { 34718de8d7fSPeter Avalos struct logininfo *newli; 34818de8d7fSPeter Avalos 34918de8d7fSPeter Avalos newli = xmalloc(sizeof(*newli)); 35018de8d7fSPeter Avalos login_init_entry(newli, pid, username, hostname, line); 35118de8d7fSPeter Avalos return (newli); 35218de8d7fSPeter Avalos } 35318de8d7fSPeter Avalos 35418de8d7fSPeter Avalos 35518de8d7fSPeter Avalos /* login_free_entry(struct logininfo *) - free struct memory */ 35618de8d7fSPeter Avalos void 35718de8d7fSPeter Avalos login_free_entry(struct logininfo *li) 35818de8d7fSPeter Avalos { 35936e94dc5SPeter Avalos free(li); 36018de8d7fSPeter Avalos } 36118de8d7fSPeter Avalos 36218de8d7fSPeter Avalos 36318de8d7fSPeter Avalos /* login_init_entry(struct logininfo *, int, char*, char*, char*) 36418de8d7fSPeter Avalos * - initialise a struct logininfo 36518de8d7fSPeter Avalos * 36618de8d7fSPeter Avalos * Populates a new struct logininfo, a data structure meant to carry 36718de8d7fSPeter Avalos * the information required to portably record login info. 36818de8d7fSPeter Avalos * 36918de8d7fSPeter Avalos * Returns: 1 37018de8d7fSPeter Avalos */ 37118de8d7fSPeter Avalos int 3729f304aafSPeter Avalos login_init_entry(struct logininfo *li, pid_t pid, const char *username, 37318de8d7fSPeter Avalos const char *hostname, const char *line) 37418de8d7fSPeter Avalos { 37518de8d7fSPeter Avalos struct passwd *pw; 37618de8d7fSPeter Avalos 37718de8d7fSPeter Avalos memset(li, 0, sizeof(*li)); 37818de8d7fSPeter Avalos 37918de8d7fSPeter Avalos li->pid = pid; 38018de8d7fSPeter Avalos 38118de8d7fSPeter Avalos /* set the line information */ 38218de8d7fSPeter Avalos if (line) 38318de8d7fSPeter Avalos line_fullname(li->line, line, sizeof(li->line)); 38418de8d7fSPeter Avalos 38518de8d7fSPeter Avalos if (username) { 38618de8d7fSPeter Avalos strlcpy(li->username, username, sizeof(li->username)); 38718de8d7fSPeter Avalos pw = getpwnam(li->username); 38818de8d7fSPeter Avalos if (pw == NULL) { 38918de8d7fSPeter Avalos fatal("%s: Cannot find user \"%s\"", __func__, 39018de8d7fSPeter Avalos li->username); 39118de8d7fSPeter Avalos } 39218de8d7fSPeter Avalos li->uid = pw->pw_uid; 39318de8d7fSPeter Avalos } 39418de8d7fSPeter Avalos 39518de8d7fSPeter Avalos if (hostname) 39618de8d7fSPeter Avalos strlcpy(li->hostname, hostname, sizeof(li->hostname)); 39718de8d7fSPeter Avalos 39818de8d7fSPeter Avalos return (1); 39918de8d7fSPeter Avalos } 40018de8d7fSPeter Avalos 40118de8d7fSPeter Avalos /* 40218de8d7fSPeter Avalos * login_set_current_time(struct logininfo *) - set the current time 40318de8d7fSPeter Avalos * 40418de8d7fSPeter Avalos * Set the current time in a logininfo structure. This function is 40518de8d7fSPeter Avalos * meant to eliminate the need to deal with system dependencies for 40618de8d7fSPeter Avalos * time handling. 40718de8d7fSPeter Avalos */ 40818de8d7fSPeter Avalos void 40918de8d7fSPeter Avalos login_set_current_time(struct logininfo *li) 41018de8d7fSPeter Avalos { 41118de8d7fSPeter Avalos struct timeval tv; 41218de8d7fSPeter Avalos 41318de8d7fSPeter Avalos gettimeofday(&tv, NULL); 41418de8d7fSPeter Avalos 41518de8d7fSPeter Avalos li->tv_sec = tv.tv_sec; 41618de8d7fSPeter Avalos li->tv_usec = tv.tv_usec; 41718de8d7fSPeter Avalos } 41818de8d7fSPeter Avalos 41918de8d7fSPeter Avalos /* copy a sockaddr_* into our logininfo */ 42018de8d7fSPeter Avalos void 42118de8d7fSPeter Avalos login_set_addr(struct logininfo *li, const struct sockaddr *sa, 42218de8d7fSPeter Avalos const unsigned int sa_size) 42318de8d7fSPeter Avalos { 42418de8d7fSPeter Avalos unsigned int bufsize = sa_size; 42518de8d7fSPeter Avalos 42618de8d7fSPeter Avalos /* make sure we don't overrun our union */ 42718de8d7fSPeter Avalos if (sizeof(li->hostaddr) < sa_size) 42818de8d7fSPeter Avalos bufsize = sizeof(li->hostaddr); 42918de8d7fSPeter Avalos 43018de8d7fSPeter Avalos memcpy(&li->hostaddr.sa, sa, bufsize); 43118de8d7fSPeter Avalos } 43218de8d7fSPeter Avalos 43318de8d7fSPeter Avalos 43418de8d7fSPeter Avalos /** 43518de8d7fSPeter Avalos ** login_write: Call low-level recording functions based on autoconf 43618de8d7fSPeter Avalos ** results 43718de8d7fSPeter Avalos **/ 43818de8d7fSPeter Avalos int 43918de8d7fSPeter Avalos login_write(struct logininfo *li) 44018de8d7fSPeter Avalos { 44118de8d7fSPeter Avalos #ifndef HAVE_CYGWIN 44218de8d7fSPeter Avalos if (geteuid() != 0) { 44318de8d7fSPeter Avalos logit("Attempt to write login records by non-root user (aborting)"); 44418de8d7fSPeter Avalos return (1); 44518de8d7fSPeter Avalos } 44618de8d7fSPeter Avalos #endif 44718de8d7fSPeter Avalos 44818de8d7fSPeter Avalos /* set the timestamp */ 44918de8d7fSPeter Avalos login_set_current_time(li); 45018de8d7fSPeter Avalos #ifdef USE_LOGIN 45118de8d7fSPeter Avalos syslogin_write_entry(li); 45218de8d7fSPeter Avalos #endif 45318de8d7fSPeter Avalos #ifdef USE_LASTLOG 45418de8d7fSPeter Avalos if (li->type == LTYPE_LOGIN) 45518de8d7fSPeter Avalos lastlog_write_entry(li); 45618de8d7fSPeter Avalos #endif 45718de8d7fSPeter Avalos #ifdef USE_UTMP 45818de8d7fSPeter Avalos utmp_write_entry(li); 45918de8d7fSPeter Avalos #endif 46018de8d7fSPeter Avalos #ifdef USE_WTMP 46118de8d7fSPeter Avalos wtmp_write_entry(li); 46218de8d7fSPeter Avalos #endif 46318de8d7fSPeter Avalos #ifdef USE_UTMPX 46418de8d7fSPeter Avalos utmpx_write_entry(li); 46518de8d7fSPeter Avalos #endif 46618de8d7fSPeter Avalos #ifdef USE_WTMPX 46718de8d7fSPeter Avalos wtmpx_write_entry(li); 46818de8d7fSPeter Avalos #endif 46918de8d7fSPeter Avalos #ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN 47018de8d7fSPeter Avalos if (li->type == LTYPE_LOGIN && 47118de8d7fSPeter Avalos !sys_auth_record_login(li->username,li->hostname,li->line, 472664f4763Szrj loginmsg)) 47318de8d7fSPeter Avalos logit("Writing login record failed for %s", li->username); 47418de8d7fSPeter Avalos #endif 47518de8d7fSPeter Avalos #ifdef SSH_AUDIT_EVENTS 47618de8d7fSPeter Avalos if (li->type == LTYPE_LOGIN) 4779f304aafSPeter Avalos audit_session_open(li); 47818de8d7fSPeter Avalos else if (li->type == LTYPE_LOGOUT) 4799f304aafSPeter Avalos audit_session_close(li); 48018de8d7fSPeter Avalos #endif 48118de8d7fSPeter Avalos return (0); 48218de8d7fSPeter Avalos } 48318de8d7fSPeter Avalos 48418de8d7fSPeter Avalos #ifdef LOGIN_NEEDS_UTMPX 48518de8d7fSPeter Avalos int 48618de8d7fSPeter Avalos login_utmp_only(struct logininfo *li) 48718de8d7fSPeter Avalos { 48818de8d7fSPeter Avalos li->type = LTYPE_LOGIN; 48918de8d7fSPeter Avalos login_set_current_time(li); 49018de8d7fSPeter Avalos # ifdef USE_UTMP 49118de8d7fSPeter Avalos utmp_write_entry(li); 49218de8d7fSPeter Avalos # endif 49318de8d7fSPeter Avalos # ifdef USE_WTMP 49418de8d7fSPeter Avalos wtmp_write_entry(li); 49518de8d7fSPeter Avalos # endif 49618de8d7fSPeter Avalos # ifdef USE_UTMPX 49718de8d7fSPeter Avalos utmpx_write_entry(li); 49818de8d7fSPeter Avalos # endif 49918de8d7fSPeter Avalos # ifdef USE_WTMPX 50018de8d7fSPeter Avalos wtmpx_write_entry(li); 50118de8d7fSPeter Avalos # endif 50218de8d7fSPeter Avalos return (0); 50318de8d7fSPeter Avalos } 50418de8d7fSPeter Avalos #endif 50518de8d7fSPeter Avalos 50618de8d7fSPeter Avalos /** 50718de8d7fSPeter Avalos ** getlast_entry: Call low-level functions to retrieve the last login 50818de8d7fSPeter Avalos ** time. 50918de8d7fSPeter Avalos **/ 51018de8d7fSPeter Avalos 51118de8d7fSPeter Avalos /* take the uid in li and return the last login time */ 51218de8d7fSPeter Avalos int 51318de8d7fSPeter Avalos getlast_entry(struct logininfo *li) 51418de8d7fSPeter Avalos { 51518de8d7fSPeter Avalos #ifdef USE_LASTLOG 51618de8d7fSPeter Avalos return(lastlog_get_entry(li)); 51718de8d7fSPeter Avalos #else /* !USE_LASTLOG */ 518856ea928SPeter Avalos #if defined(USE_UTMPX) && defined(HAVE_SETUTXDB) && \ 519856ea928SPeter Avalos defined(UTXDB_LASTLOGIN) && defined(HAVE_GETUTXUSER) 520856ea928SPeter Avalos return (utmpx_get_entry(li)); 521856ea928SPeter Avalos #endif 52218de8d7fSPeter Avalos 52318de8d7fSPeter Avalos #if defined(DISABLE_LASTLOG) 52418de8d7fSPeter Avalos /* On some systems we shouldn't even try to obtain last login 52518de8d7fSPeter Avalos * time, e.g. AIX */ 52618de8d7fSPeter Avalos return (0); 52718de8d7fSPeter Avalos # elif defined(USE_WTMP) && \ 52818de8d7fSPeter Avalos (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) 52918de8d7fSPeter Avalos /* retrieve last login time from utmp */ 53018de8d7fSPeter Avalos return (wtmp_get_entry(li)); 53118de8d7fSPeter Avalos # elif defined(USE_WTMPX) && \ 53218de8d7fSPeter Avalos (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX)) 53318de8d7fSPeter Avalos /* If wtmp isn't available, try wtmpx */ 53418de8d7fSPeter Avalos return (wtmpx_get_entry(li)); 53518de8d7fSPeter Avalos # else 53618de8d7fSPeter Avalos /* Give up: No means of retrieving last login time */ 53718de8d7fSPeter Avalos return (0); 53818de8d7fSPeter Avalos # endif /* DISABLE_LASTLOG */ 53918de8d7fSPeter Avalos #endif /* USE_LASTLOG */ 54018de8d7fSPeter Avalos } 54118de8d7fSPeter Avalos 54218de8d7fSPeter Avalos 54318de8d7fSPeter Avalos 54418de8d7fSPeter Avalos /* 54518de8d7fSPeter Avalos * 'line' string utility functions 54618de8d7fSPeter Avalos * 54718de8d7fSPeter Avalos * These functions process the 'line' string into one of three forms: 54818de8d7fSPeter Avalos * 54918de8d7fSPeter Avalos * 1. The full filename (including '/dev') 55018de8d7fSPeter Avalos * 2. The stripped name (excluding '/dev') 55118de8d7fSPeter Avalos * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00 55218de8d7fSPeter Avalos * /dev/pts/1 -> ts/1 ) 55318de8d7fSPeter Avalos * 55418de8d7fSPeter Avalos * Form 3 is used on some systems to identify a .tmp.? entry when 55518de8d7fSPeter Avalos * attempting to remove it. Typically both addition and removal is 55618de8d7fSPeter Avalos * performed by one application - say, sshd - so as long as the choice 55718de8d7fSPeter Avalos * uniquely identifies a terminal it's ok. 55818de8d7fSPeter Avalos */ 55918de8d7fSPeter Avalos 56018de8d7fSPeter Avalos 56118de8d7fSPeter Avalos /* 56218de8d7fSPeter Avalos * line_fullname(): add the leading '/dev/' if it doesn't exist make 56318de8d7fSPeter Avalos * sure dst has enough space, if not just copy src (ugh) 56418de8d7fSPeter Avalos */ 56518de8d7fSPeter Avalos char * 56618de8d7fSPeter Avalos line_fullname(char *dst, const char *src, u_int dstsize) 56718de8d7fSPeter Avalos { 56818de8d7fSPeter Avalos memset(dst, '\0', dstsize); 56918de8d7fSPeter Avalos if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) 57018de8d7fSPeter Avalos strlcpy(dst, src, dstsize); 57118de8d7fSPeter Avalos else { 57218de8d7fSPeter Avalos strlcpy(dst, "/dev/", dstsize); 57318de8d7fSPeter Avalos strlcat(dst, src, dstsize); 57418de8d7fSPeter Avalos } 57518de8d7fSPeter Avalos return (dst); 57618de8d7fSPeter Avalos } 57718de8d7fSPeter Avalos 57818de8d7fSPeter Avalos /* line_stripname(): strip the leading '/dev' if it exists, return dst */ 57918de8d7fSPeter Avalos char * 58018de8d7fSPeter Avalos line_stripname(char *dst, const char *src, int dstsize) 58118de8d7fSPeter Avalos { 58218de8d7fSPeter Avalos memset(dst, '\0', dstsize); 58318de8d7fSPeter Avalos if (strncmp(src, "/dev/", 5) == 0) 58418de8d7fSPeter Avalos strlcpy(dst, src + 5, dstsize); 58518de8d7fSPeter Avalos else 58618de8d7fSPeter Avalos strlcpy(dst, src, dstsize); 58718de8d7fSPeter Avalos return (dst); 58818de8d7fSPeter Avalos } 58918de8d7fSPeter Avalos 59018de8d7fSPeter Avalos /* 59118de8d7fSPeter Avalos * line_abbrevname(): Return the abbreviated (usually four-character) 59218de8d7fSPeter Avalos * form of the line (Just use the last <dstsize> characters of the 59318de8d7fSPeter Avalos * full name.) 59418de8d7fSPeter Avalos * 59518de8d7fSPeter Avalos * NOTE: use strncpy because we do NOT necessarily want zero 59618de8d7fSPeter Avalos * termination 59718de8d7fSPeter Avalos */ 59818de8d7fSPeter Avalos char * 59918de8d7fSPeter Avalos line_abbrevname(char *dst, const char *src, int dstsize) 60018de8d7fSPeter Avalos { 60118de8d7fSPeter Avalos size_t len; 60218de8d7fSPeter Avalos 60318de8d7fSPeter Avalos memset(dst, '\0', dstsize); 60418de8d7fSPeter Avalos 60518de8d7fSPeter Avalos /* Always skip prefix if present */ 60618de8d7fSPeter Avalos if (strncmp(src, "/dev/", 5) == 0) 60718de8d7fSPeter Avalos src += 5; 60818de8d7fSPeter Avalos 60918de8d7fSPeter Avalos #ifdef WITH_ABBREV_NO_TTY 61018de8d7fSPeter Avalos if (strncmp(src, "tty", 3) == 0) 61118de8d7fSPeter Avalos src += 3; 61218de8d7fSPeter Avalos #endif 61318de8d7fSPeter Avalos 61418de8d7fSPeter Avalos len = strlen(src); 61518de8d7fSPeter Avalos 61618de8d7fSPeter Avalos if (len > 0) { 61718de8d7fSPeter Avalos if (((int)len - dstsize) > 0) 61818de8d7fSPeter Avalos src += ((int)len - dstsize); 61918de8d7fSPeter Avalos 62018de8d7fSPeter Avalos /* note: _don't_ change this to strlcpy */ 62118de8d7fSPeter Avalos strncpy(dst, src, (size_t)dstsize); 62218de8d7fSPeter Avalos } 62318de8d7fSPeter Avalos 62418de8d7fSPeter Avalos return (dst); 62518de8d7fSPeter Avalos } 62618de8d7fSPeter Avalos 62718de8d7fSPeter Avalos /** 62818de8d7fSPeter Avalos ** utmp utility functions 62918de8d7fSPeter Avalos ** 63018de8d7fSPeter Avalos ** These functions manipulate struct utmp, taking system differences 63118de8d7fSPeter Avalos ** into account. 63218de8d7fSPeter Avalos **/ 63318de8d7fSPeter Avalos 63418de8d7fSPeter Avalos #if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN) 63518de8d7fSPeter Avalos 63618de8d7fSPeter Avalos /* build the utmp structure */ 63718de8d7fSPeter Avalos void 63818de8d7fSPeter Avalos set_utmp_time(struct logininfo *li, struct utmp *ut) 63918de8d7fSPeter Avalos { 64018de8d7fSPeter Avalos # if defined(HAVE_TV_IN_UTMP) 64118de8d7fSPeter Avalos ut->ut_tv.tv_sec = li->tv_sec; 64218de8d7fSPeter Avalos ut->ut_tv.tv_usec = li->tv_usec; 64318de8d7fSPeter Avalos # elif defined(HAVE_TIME_IN_UTMP) 64418de8d7fSPeter Avalos ut->ut_time = li->tv_sec; 64518de8d7fSPeter Avalos # endif 64618de8d7fSPeter Avalos } 64718de8d7fSPeter Avalos 64818de8d7fSPeter Avalos void 64918de8d7fSPeter Avalos construct_utmp(struct logininfo *li, 65018de8d7fSPeter Avalos struct utmp *ut) 65118de8d7fSPeter Avalos { 65218de8d7fSPeter Avalos # ifdef HAVE_ADDR_V6_IN_UTMP 65318de8d7fSPeter Avalos struct sockaddr_in6 *sa6; 65418de8d7fSPeter Avalos # endif 65518de8d7fSPeter Avalos 65618de8d7fSPeter Avalos memset(ut, '\0', sizeof(*ut)); 65718de8d7fSPeter Avalos 65818de8d7fSPeter Avalos /* First fill out fields used for both logins and logouts */ 65918de8d7fSPeter Avalos 66018de8d7fSPeter Avalos # ifdef HAVE_ID_IN_UTMP 66118de8d7fSPeter Avalos line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id)); 66218de8d7fSPeter Avalos # endif 66318de8d7fSPeter Avalos 66418de8d7fSPeter Avalos # ifdef HAVE_TYPE_IN_UTMP 66518de8d7fSPeter Avalos /* This is done here to keep utmp constants out of struct logininfo */ 66618de8d7fSPeter Avalos switch (li->type) { 66718de8d7fSPeter Avalos case LTYPE_LOGIN: 66818de8d7fSPeter Avalos ut->ut_type = USER_PROCESS; 66918de8d7fSPeter Avalos break; 67018de8d7fSPeter Avalos case LTYPE_LOGOUT: 67118de8d7fSPeter Avalos ut->ut_type = DEAD_PROCESS; 67218de8d7fSPeter Avalos break; 67318de8d7fSPeter Avalos } 67418de8d7fSPeter Avalos # endif 67518de8d7fSPeter Avalos set_utmp_time(li, ut); 67618de8d7fSPeter Avalos 67718de8d7fSPeter Avalos line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line)); 67818de8d7fSPeter Avalos 67918de8d7fSPeter Avalos # ifdef HAVE_PID_IN_UTMP 68018de8d7fSPeter Avalos ut->ut_pid = li->pid; 68118de8d7fSPeter Avalos # endif 68218de8d7fSPeter Avalos 68318de8d7fSPeter Avalos /* If we're logging out, leave all other fields blank */ 68418de8d7fSPeter Avalos if (li->type == LTYPE_LOGOUT) 68518de8d7fSPeter Avalos return; 68618de8d7fSPeter Avalos 68718de8d7fSPeter Avalos /* 68818de8d7fSPeter Avalos * These fields are only used when logging in, and are blank 68918de8d7fSPeter Avalos * for logouts. 69018de8d7fSPeter Avalos */ 69118de8d7fSPeter Avalos 69218de8d7fSPeter Avalos /* Use strncpy because we don't necessarily want null termination */ 69318de8d7fSPeter Avalos strncpy(ut->ut_name, li->username, 69418de8d7fSPeter Avalos MIN_SIZEOF(ut->ut_name, li->username)); 69518de8d7fSPeter Avalos # ifdef HAVE_HOST_IN_UTMP 69618de8d7fSPeter Avalos strncpy(ut->ut_host, li->hostname, 69718de8d7fSPeter Avalos MIN_SIZEOF(ut->ut_host, li->hostname)); 69818de8d7fSPeter Avalos # endif 69918de8d7fSPeter Avalos # ifdef HAVE_ADDR_IN_UTMP 70018de8d7fSPeter Avalos /* this is just a 32-bit IP address */ 70118de8d7fSPeter Avalos if (li->hostaddr.sa.sa_family == AF_INET) 70218de8d7fSPeter Avalos ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 70318de8d7fSPeter Avalos # endif 70418de8d7fSPeter Avalos # ifdef HAVE_ADDR_V6_IN_UTMP 70518de8d7fSPeter Avalos /* this is just a 128-bit IPv6 address */ 70618de8d7fSPeter Avalos if (li->hostaddr.sa.sa_family == AF_INET6) { 70718de8d7fSPeter Avalos sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 70818de8d7fSPeter Avalos memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 70918de8d7fSPeter Avalos if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 71018de8d7fSPeter Avalos ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; 71118de8d7fSPeter Avalos ut->ut_addr_v6[1] = 0; 71218de8d7fSPeter Avalos ut->ut_addr_v6[2] = 0; 71318de8d7fSPeter Avalos ut->ut_addr_v6[3] = 0; 71418de8d7fSPeter Avalos } 71518de8d7fSPeter Avalos } 71618de8d7fSPeter Avalos # endif 71718de8d7fSPeter Avalos } 71818de8d7fSPeter Avalos #endif /* USE_UTMP || USE_WTMP || USE_LOGIN */ 71918de8d7fSPeter Avalos 72018de8d7fSPeter Avalos /** 72118de8d7fSPeter Avalos ** utmpx utility functions 72218de8d7fSPeter Avalos ** 72318de8d7fSPeter Avalos ** These functions manipulate struct utmpx, accounting for system 72418de8d7fSPeter Avalos ** variations. 72518de8d7fSPeter Avalos **/ 72618de8d7fSPeter Avalos 72718de8d7fSPeter Avalos #if defined(USE_UTMPX) || defined (USE_WTMPX) 72818de8d7fSPeter Avalos /* build the utmpx structure */ 72918de8d7fSPeter Avalos void 73018de8d7fSPeter Avalos set_utmpx_time(struct logininfo *li, struct utmpx *utx) 73118de8d7fSPeter Avalos { 73218de8d7fSPeter Avalos # if defined(HAVE_TV_IN_UTMPX) 73318de8d7fSPeter Avalos utx->ut_tv.tv_sec = li->tv_sec; 73418de8d7fSPeter Avalos utx->ut_tv.tv_usec = li->tv_usec; 73518de8d7fSPeter Avalos # elif defined(HAVE_TIME_IN_UTMPX) 73618de8d7fSPeter Avalos utx->ut_time = li->tv_sec; 73718de8d7fSPeter Avalos # endif 73818de8d7fSPeter Avalos } 73918de8d7fSPeter Avalos 74018de8d7fSPeter Avalos void 74118de8d7fSPeter Avalos construct_utmpx(struct logininfo *li, struct utmpx *utx) 74218de8d7fSPeter Avalos { 74318de8d7fSPeter Avalos # ifdef HAVE_ADDR_V6_IN_UTMP 74418de8d7fSPeter Avalos struct sockaddr_in6 *sa6; 74518de8d7fSPeter Avalos # endif 74618de8d7fSPeter Avalos memset(utx, '\0', sizeof(*utx)); 74718de8d7fSPeter Avalos 74818de8d7fSPeter Avalos # ifdef HAVE_ID_IN_UTMPX 74918de8d7fSPeter Avalos line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); 75018de8d7fSPeter Avalos # endif 75118de8d7fSPeter Avalos 75218de8d7fSPeter Avalos /* this is done here to keep utmp constants out of loginrec.h */ 75318de8d7fSPeter Avalos switch (li->type) { 75418de8d7fSPeter Avalos case LTYPE_LOGIN: 75518de8d7fSPeter Avalos utx->ut_type = USER_PROCESS; 75618de8d7fSPeter Avalos break; 75718de8d7fSPeter Avalos case LTYPE_LOGOUT: 75818de8d7fSPeter Avalos utx->ut_type = DEAD_PROCESS; 75918de8d7fSPeter Avalos break; 76018de8d7fSPeter Avalos } 76118de8d7fSPeter Avalos line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line)); 76218de8d7fSPeter Avalos set_utmpx_time(li, utx); 76318de8d7fSPeter Avalos utx->ut_pid = li->pid; 76418de8d7fSPeter Avalos 76518de8d7fSPeter Avalos /* strncpy(): Don't necessarily want null termination */ 766856ea928SPeter Avalos strncpy(utx->ut_user, li->username, 767856ea928SPeter Avalos MIN_SIZEOF(utx->ut_user, li->username)); 76818de8d7fSPeter Avalos 76918de8d7fSPeter Avalos if (li->type == LTYPE_LOGOUT) 77018de8d7fSPeter Avalos return; 77118de8d7fSPeter Avalos 77218de8d7fSPeter Avalos /* 77318de8d7fSPeter Avalos * These fields are only used when logging in, and are blank 77418de8d7fSPeter Avalos * for logouts. 77518de8d7fSPeter Avalos */ 77618de8d7fSPeter Avalos 77718de8d7fSPeter Avalos # ifdef HAVE_HOST_IN_UTMPX 77818de8d7fSPeter Avalos strncpy(utx->ut_host, li->hostname, 77918de8d7fSPeter Avalos MIN_SIZEOF(utx->ut_host, li->hostname)); 78018de8d7fSPeter Avalos # endif 78118de8d7fSPeter Avalos # ifdef HAVE_ADDR_IN_UTMPX 78218de8d7fSPeter Avalos /* this is just a 32-bit IP address */ 78318de8d7fSPeter Avalos if (li->hostaddr.sa.sa_family == AF_INET) 78418de8d7fSPeter Avalos utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 78518de8d7fSPeter Avalos # endif 78618de8d7fSPeter Avalos # ifdef HAVE_ADDR_V6_IN_UTMP 78718de8d7fSPeter Avalos /* this is just a 128-bit IPv6 address */ 78818de8d7fSPeter Avalos if (li->hostaddr.sa.sa_family == AF_INET6) { 78918de8d7fSPeter Avalos sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 790e9778795SPeter Avalos memcpy(utx->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 79118de8d7fSPeter Avalos if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 792e9778795SPeter Avalos utx->ut_addr_v6[0] = utx->ut_addr_v6[3]; 793e9778795SPeter Avalos utx->ut_addr_v6[1] = 0; 794e9778795SPeter Avalos utx->ut_addr_v6[2] = 0; 795e9778795SPeter Avalos utx->ut_addr_v6[3] = 0; 79618de8d7fSPeter Avalos } 79718de8d7fSPeter Avalos } 79818de8d7fSPeter Avalos # endif 79918de8d7fSPeter Avalos # ifdef HAVE_SYSLEN_IN_UTMPX 80018de8d7fSPeter Avalos /* ut_syslen is the length of the utx_host string */ 80118de8d7fSPeter Avalos utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host)); 80218de8d7fSPeter Avalos # endif 80318de8d7fSPeter Avalos } 80418de8d7fSPeter Avalos #endif /* USE_UTMPX || USE_WTMPX */ 80518de8d7fSPeter Avalos 80618de8d7fSPeter Avalos /** 80718de8d7fSPeter Avalos ** Low-level utmp functions 80818de8d7fSPeter Avalos **/ 80918de8d7fSPeter Avalos 81018de8d7fSPeter Avalos /* FIXME: (ATL) utmp_write_direct needs testing */ 81118de8d7fSPeter Avalos #ifdef USE_UTMP 81218de8d7fSPeter Avalos 81318de8d7fSPeter Avalos /* if we can, use pututline() etc. */ 81418de8d7fSPeter Avalos # if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \ 81518de8d7fSPeter Avalos defined(HAVE_PUTUTLINE) 81618de8d7fSPeter Avalos # define UTMP_USE_LIBRARY 81718de8d7fSPeter Avalos # endif 81818de8d7fSPeter Avalos 81918de8d7fSPeter Avalos 82018de8d7fSPeter Avalos /* write a utmp entry with the system's help (pututline() and pals) */ 82118de8d7fSPeter Avalos # ifdef UTMP_USE_LIBRARY 82218de8d7fSPeter Avalos static int 82318de8d7fSPeter Avalos utmp_write_library(struct logininfo *li, struct utmp *ut) 82418de8d7fSPeter Avalos { 82518de8d7fSPeter Avalos setutent(); 82618de8d7fSPeter Avalos pututline(ut); 82718de8d7fSPeter Avalos # ifdef HAVE_ENDUTENT 82818de8d7fSPeter Avalos endutent(); 82918de8d7fSPeter Avalos # endif 83018de8d7fSPeter Avalos return (1); 83118de8d7fSPeter Avalos } 83218de8d7fSPeter Avalos # else /* UTMP_USE_LIBRARY */ 83318de8d7fSPeter Avalos 83418de8d7fSPeter Avalos /* 83518de8d7fSPeter Avalos * Write a utmp entry direct to the file 83618de8d7fSPeter Avalos * This is a slightly modification of code in OpenBSD's login.c 83718de8d7fSPeter Avalos */ 83818de8d7fSPeter Avalos static int 83918de8d7fSPeter Avalos utmp_write_direct(struct logininfo *li, struct utmp *ut) 84018de8d7fSPeter Avalos { 84118de8d7fSPeter Avalos struct utmp old_ut; 84218de8d7fSPeter Avalos register int fd; 84318de8d7fSPeter Avalos int tty; 84418de8d7fSPeter Avalos 84518de8d7fSPeter Avalos /* FIXME: (ATL) ttyslot() needs local implementation */ 84618de8d7fSPeter Avalos 84718de8d7fSPeter Avalos #if defined(HAVE_GETTTYENT) 84818de8d7fSPeter Avalos struct ttyent *ty; 84918de8d7fSPeter Avalos 85018de8d7fSPeter Avalos tty=0; 85118de8d7fSPeter Avalos setttyent(); 85218de8d7fSPeter Avalos while (NULL != (ty = getttyent())) { 85318de8d7fSPeter Avalos tty++; 85418de8d7fSPeter Avalos if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line))) 85518de8d7fSPeter Avalos break; 85618de8d7fSPeter Avalos } 85718de8d7fSPeter Avalos endttyent(); 85818de8d7fSPeter Avalos 85918de8d7fSPeter Avalos if (NULL == ty) { 86018de8d7fSPeter Avalos logit("%s: tty not found", __func__); 86118de8d7fSPeter Avalos return (0); 86218de8d7fSPeter Avalos } 86318de8d7fSPeter Avalos #else /* FIXME */ 86418de8d7fSPeter Avalos 86518de8d7fSPeter Avalos tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ 86618de8d7fSPeter Avalos 86718de8d7fSPeter Avalos #endif /* HAVE_GETTTYENT */ 86818de8d7fSPeter Avalos 86918de8d7fSPeter Avalos if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { 87018de8d7fSPeter Avalos off_t pos, ret; 87118de8d7fSPeter Avalos 87218de8d7fSPeter Avalos pos = (off_t)tty * sizeof(struct utmp); 87318de8d7fSPeter Avalos if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { 87418de8d7fSPeter Avalos logit("%s: lseek: %s", __func__, strerror(errno)); 8759f304aafSPeter Avalos close(fd); 87618de8d7fSPeter Avalos return (0); 87718de8d7fSPeter Avalos } 87818de8d7fSPeter Avalos if (ret != pos) { 87918de8d7fSPeter Avalos logit("%s: Couldn't seek to tty %d slot in %s", 88018de8d7fSPeter Avalos __func__, tty, UTMP_FILE); 8819f304aafSPeter Avalos close(fd); 88218de8d7fSPeter Avalos return (0); 88318de8d7fSPeter Avalos } 88418de8d7fSPeter Avalos /* 88518de8d7fSPeter Avalos * Prevent luser from zero'ing out ut_host. 88618de8d7fSPeter Avalos * If the new ut_line is empty but the old one is not 88718de8d7fSPeter Avalos * and ut_line and ut_name match, preserve the old ut_line. 88818de8d7fSPeter Avalos */ 88918de8d7fSPeter Avalos if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && 89018de8d7fSPeter Avalos (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && 89118de8d7fSPeter Avalos (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && 89218de8d7fSPeter Avalos (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) 89318de8d7fSPeter Avalos memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); 89418de8d7fSPeter Avalos 89518de8d7fSPeter Avalos if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { 89618de8d7fSPeter Avalos logit("%s: lseek: %s", __func__, strerror(errno)); 8979f304aafSPeter Avalos close(fd); 89818de8d7fSPeter Avalos return (0); 89918de8d7fSPeter Avalos } 90018de8d7fSPeter Avalos if (ret != pos) { 90118de8d7fSPeter Avalos logit("%s: Couldn't seek to tty %d slot in %s", 90218de8d7fSPeter Avalos __func__, tty, UTMP_FILE); 9039f304aafSPeter Avalos close(fd); 90418de8d7fSPeter Avalos return (0); 90518de8d7fSPeter Avalos } 90618de8d7fSPeter Avalos if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { 90718de8d7fSPeter Avalos logit("%s: error writing %s: %s", __func__, 90818de8d7fSPeter Avalos UTMP_FILE, strerror(errno)); 9099f304aafSPeter Avalos close(fd); 9109f304aafSPeter Avalos return (0); 91118de8d7fSPeter Avalos } 91218de8d7fSPeter Avalos 91318de8d7fSPeter Avalos close(fd); 91418de8d7fSPeter Avalos return (1); 91518de8d7fSPeter Avalos } else { 91618de8d7fSPeter Avalos return (0); 91718de8d7fSPeter Avalos } 91818de8d7fSPeter Avalos } 91918de8d7fSPeter Avalos # endif /* UTMP_USE_LIBRARY */ 92018de8d7fSPeter Avalos 92118de8d7fSPeter Avalos static int 92218de8d7fSPeter Avalos utmp_perform_login(struct logininfo *li) 92318de8d7fSPeter Avalos { 92418de8d7fSPeter Avalos struct utmp ut; 92518de8d7fSPeter Avalos 92618de8d7fSPeter Avalos construct_utmp(li, &ut); 92718de8d7fSPeter Avalos # ifdef UTMP_USE_LIBRARY 92818de8d7fSPeter Avalos if (!utmp_write_library(li, &ut)) { 92918de8d7fSPeter Avalos logit("%s: utmp_write_library() failed", __func__); 93018de8d7fSPeter Avalos return (0); 93118de8d7fSPeter Avalos } 93218de8d7fSPeter Avalos # else 93318de8d7fSPeter Avalos if (!utmp_write_direct(li, &ut)) { 93418de8d7fSPeter Avalos logit("%s: utmp_write_direct() failed", __func__); 93518de8d7fSPeter Avalos return (0); 93618de8d7fSPeter Avalos } 93718de8d7fSPeter Avalos # endif 93818de8d7fSPeter Avalos return (1); 93918de8d7fSPeter Avalos } 94018de8d7fSPeter Avalos 94118de8d7fSPeter Avalos 94218de8d7fSPeter Avalos static int 94318de8d7fSPeter Avalos utmp_perform_logout(struct logininfo *li) 94418de8d7fSPeter Avalos { 94518de8d7fSPeter Avalos struct utmp ut; 94618de8d7fSPeter Avalos 94718de8d7fSPeter Avalos construct_utmp(li, &ut); 94818de8d7fSPeter Avalos # ifdef UTMP_USE_LIBRARY 94918de8d7fSPeter Avalos if (!utmp_write_library(li, &ut)) { 95018de8d7fSPeter Avalos logit("%s: utmp_write_library() failed", __func__); 95118de8d7fSPeter Avalos return (0); 95218de8d7fSPeter Avalos } 95318de8d7fSPeter Avalos # else 95418de8d7fSPeter Avalos if (!utmp_write_direct(li, &ut)) { 95518de8d7fSPeter Avalos logit("%s: utmp_write_direct() failed", __func__); 95618de8d7fSPeter Avalos return (0); 95718de8d7fSPeter Avalos } 95818de8d7fSPeter Avalos # endif 95918de8d7fSPeter Avalos return (1); 96018de8d7fSPeter Avalos } 96118de8d7fSPeter Avalos 96218de8d7fSPeter Avalos 96318de8d7fSPeter Avalos int 96418de8d7fSPeter Avalos utmp_write_entry(struct logininfo *li) 96518de8d7fSPeter Avalos { 96618de8d7fSPeter Avalos switch(li->type) { 96718de8d7fSPeter Avalos case LTYPE_LOGIN: 96818de8d7fSPeter Avalos return (utmp_perform_login(li)); 96918de8d7fSPeter Avalos 97018de8d7fSPeter Avalos case LTYPE_LOGOUT: 97118de8d7fSPeter Avalos return (utmp_perform_logout(li)); 97218de8d7fSPeter Avalos 97318de8d7fSPeter Avalos default: 97418de8d7fSPeter Avalos logit("%s: invalid type field", __func__); 97518de8d7fSPeter Avalos return (0); 97618de8d7fSPeter Avalos } 97718de8d7fSPeter Avalos } 97818de8d7fSPeter Avalos #endif /* USE_UTMP */ 97918de8d7fSPeter Avalos 98018de8d7fSPeter Avalos 98118de8d7fSPeter Avalos /** 98218de8d7fSPeter Avalos ** Low-level utmpx functions 98318de8d7fSPeter Avalos **/ 98418de8d7fSPeter Avalos 98518de8d7fSPeter Avalos /* not much point if we don't want utmpx entries */ 98618de8d7fSPeter Avalos #ifdef USE_UTMPX 98718de8d7fSPeter Avalos 98818de8d7fSPeter Avalos /* if we have the wherewithall, use pututxline etc. */ 98918de8d7fSPeter Avalos # if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \ 99018de8d7fSPeter Avalos defined(HAVE_PUTUTXLINE) 99118de8d7fSPeter Avalos # define UTMPX_USE_LIBRARY 99218de8d7fSPeter Avalos # endif 99318de8d7fSPeter Avalos 99418de8d7fSPeter Avalos 99518de8d7fSPeter Avalos /* write a utmpx entry with the system's help (pututxline() and pals) */ 99618de8d7fSPeter Avalos # ifdef UTMPX_USE_LIBRARY 99718de8d7fSPeter Avalos static int 99818de8d7fSPeter Avalos utmpx_write_library(struct logininfo *li, struct utmpx *utx) 99918de8d7fSPeter Avalos { 100018de8d7fSPeter Avalos setutxent(); 100118de8d7fSPeter Avalos pututxline(utx); 100218de8d7fSPeter Avalos 100318de8d7fSPeter Avalos # ifdef HAVE_ENDUTXENT 100418de8d7fSPeter Avalos endutxent(); 100518de8d7fSPeter Avalos # endif 100618de8d7fSPeter Avalos return (1); 100718de8d7fSPeter Avalos } 100818de8d7fSPeter Avalos 100918de8d7fSPeter Avalos # else /* UTMPX_USE_LIBRARY */ 101018de8d7fSPeter Avalos 101118de8d7fSPeter Avalos /* write a utmp entry direct to the file */ 101218de8d7fSPeter Avalos static int 101318de8d7fSPeter Avalos utmpx_write_direct(struct logininfo *li, struct utmpx *utx) 101418de8d7fSPeter Avalos { 101518de8d7fSPeter Avalos logit("%s: not implemented!", __func__); 101618de8d7fSPeter Avalos return (0); 101718de8d7fSPeter Avalos } 101818de8d7fSPeter Avalos # endif /* UTMPX_USE_LIBRARY */ 101918de8d7fSPeter Avalos 102018de8d7fSPeter Avalos static int 102118de8d7fSPeter Avalos utmpx_perform_login(struct logininfo *li) 102218de8d7fSPeter Avalos { 102318de8d7fSPeter Avalos struct utmpx utx; 102418de8d7fSPeter Avalos 102518de8d7fSPeter Avalos construct_utmpx(li, &utx); 102618de8d7fSPeter Avalos # ifdef UTMPX_USE_LIBRARY 102718de8d7fSPeter Avalos if (!utmpx_write_library(li, &utx)) { 102818de8d7fSPeter Avalos logit("%s: utmp_write_library() failed", __func__); 102918de8d7fSPeter Avalos return (0); 103018de8d7fSPeter Avalos } 103118de8d7fSPeter Avalos # else 103218de8d7fSPeter Avalos if (!utmpx_write_direct(li, &ut)) { 103318de8d7fSPeter Avalos logit("%s: utmp_write_direct() failed", __func__); 103418de8d7fSPeter Avalos return (0); 103518de8d7fSPeter Avalos } 103618de8d7fSPeter Avalos # endif 103718de8d7fSPeter Avalos return (1); 103818de8d7fSPeter Avalos } 103918de8d7fSPeter Avalos 104018de8d7fSPeter Avalos 104118de8d7fSPeter Avalos static int 104218de8d7fSPeter Avalos utmpx_perform_logout(struct logininfo *li) 104318de8d7fSPeter Avalos { 104418de8d7fSPeter Avalos struct utmpx utx; 104518de8d7fSPeter Avalos 104618de8d7fSPeter Avalos construct_utmpx(li, &utx); 104718de8d7fSPeter Avalos # ifdef HAVE_ID_IN_UTMPX 104818de8d7fSPeter Avalos line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); 104918de8d7fSPeter Avalos # endif 105018de8d7fSPeter Avalos # ifdef HAVE_TYPE_IN_UTMPX 105118de8d7fSPeter Avalos utx.ut_type = DEAD_PROCESS; 105218de8d7fSPeter Avalos # endif 105318de8d7fSPeter Avalos 105418de8d7fSPeter Avalos # ifdef UTMPX_USE_LIBRARY 105518de8d7fSPeter Avalos utmpx_write_library(li, &utx); 105618de8d7fSPeter Avalos # else 105718de8d7fSPeter Avalos utmpx_write_direct(li, &utx); 105818de8d7fSPeter Avalos # endif 105918de8d7fSPeter Avalos return (1); 106018de8d7fSPeter Avalos } 106118de8d7fSPeter Avalos 106218de8d7fSPeter Avalos int 106318de8d7fSPeter Avalos utmpx_write_entry(struct logininfo *li) 106418de8d7fSPeter Avalos { 106518de8d7fSPeter Avalos switch(li->type) { 106618de8d7fSPeter Avalos case LTYPE_LOGIN: 106718de8d7fSPeter Avalos return (utmpx_perform_login(li)); 106818de8d7fSPeter Avalos case LTYPE_LOGOUT: 106918de8d7fSPeter Avalos return (utmpx_perform_logout(li)); 107018de8d7fSPeter Avalos default: 107118de8d7fSPeter Avalos logit("%s: invalid type field", __func__); 107218de8d7fSPeter Avalos return (0); 107318de8d7fSPeter Avalos } 107418de8d7fSPeter Avalos } 107518de8d7fSPeter Avalos #endif /* USE_UTMPX */ 107618de8d7fSPeter Avalos 107718de8d7fSPeter Avalos 107818de8d7fSPeter Avalos /** 107918de8d7fSPeter Avalos ** Low-level wtmp functions 108018de8d7fSPeter Avalos **/ 108118de8d7fSPeter Avalos 108218de8d7fSPeter Avalos #ifdef USE_WTMP 108318de8d7fSPeter Avalos 108418de8d7fSPeter Avalos /* 108518de8d7fSPeter Avalos * Write a wtmp entry direct to the end of the file 108618de8d7fSPeter Avalos * This is a slight modification of code in OpenBSD's logwtmp.c 108718de8d7fSPeter Avalos */ 108818de8d7fSPeter Avalos static int 108918de8d7fSPeter Avalos wtmp_write(struct logininfo *li, struct utmp *ut) 109018de8d7fSPeter Avalos { 109118de8d7fSPeter Avalos struct stat buf; 109218de8d7fSPeter Avalos int fd, ret = 1; 109318de8d7fSPeter Avalos 109418de8d7fSPeter Avalos if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 109518de8d7fSPeter Avalos logit("%s: problem writing %s: %s", __func__, 109618de8d7fSPeter Avalos WTMP_FILE, strerror(errno)); 109718de8d7fSPeter Avalos return (0); 109818de8d7fSPeter Avalos } 109918de8d7fSPeter Avalos if (fstat(fd, &buf) == 0) 110018de8d7fSPeter Avalos if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { 110118de8d7fSPeter Avalos ftruncate(fd, buf.st_size); 110218de8d7fSPeter Avalos logit("%s: problem writing %s: %s", __func__, 110318de8d7fSPeter Avalos WTMP_FILE, strerror(errno)); 110418de8d7fSPeter Avalos ret = 0; 110518de8d7fSPeter Avalos } 110618de8d7fSPeter Avalos close(fd); 110718de8d7fSPeter Avalos return (ret); 110818de8d7fSPeter Avalos } 110918de8d7fSPeter Avalos 111018de8d7fSPeter Avalos static int 111118de8d7fSPeter Avalos wtmp_perform_login(struct logininfo *li) 111218de8d7fSPeter Avalos { 111318de8d7fSPeter Avalos struct utmp ut; 111418de8d7fSPeter Avalos 111518de8d7fSPeter Avalos construct_utmp(li, &ut); 111618de8d7fSPeter Avalos return (wtmp_write(li, &ut)); 111718de8d7fSPeter Avalos } 111818de8d7fSPeter Avalos 111918de8d7fSPeter Avalos 112018de8d7fSPeter Avalos static int 112118de8d7fSPeter Avalos wtmp_perform_logout(struct logininfo *li) 112218de8d7fSPeter Avalos { 112318de8d7fSPeter Avalos struct utmp ut; 112418de8d7fSPeter Avalos 112518de8d7fSPeter Avalos construct_utmp(li, &ut); 112618de8d7fSPeter Avalos return (wtmp_write(li, &ut)); 112718de8d7fSPeter Avalos } 112818de8d7fSPeter Avalos 112918de8d7fSPeter Avalos 113018de8d7fSPeter Avalos int 113118de8d7fSPeter Avalos wtmp_write_entry(struct logininfo *li) 113218de8d7fSPeter Avalos { 113318de8d7fSPeter Avalos switch(li->type) { 113418de8d7fSPeter Avalos case LTYPE_LOGIN: 113518de8d7fSPeter Avalos return (wtmp_perform_login(li)); 113618de8d7fSPeter Avalos case LTYPE_LOGOUT: 113718de8d7fSPeter Avalos return (wtmp_perform_logout(li)); 113818de8d7fSPeter Avalos default: 113918de8d7fSPeter Avalos logit("%s: invalid type field", __func__); 114018de8d7fSPeter Avalos return (0); 114118de8d7fSPeter Avalos } 114218de8d7fSPeter Avalos } 114318de8d7fSPeter Avalos 114418de8d7fSPeter Avalos 114518de8d7fSPeter Avalos /* 114618de8d7fSPeter Avalos * Notes on fetching login data from wtmp/wtmpx 114718de8d7fSPeter Avalos * 114818de8d7fSPeter Avalos * Logouts are usually recorded with (amongst other things) a blank 114918de8d7fSPeter Avalos * username on a given tty line. However, some systems (HP-UX is one) 115018de8d7fSPeter Avalos * leave all fields set, but change the ut_type field to DEAD_PROCESS. 115118de8d7fSPeter Avalos * 115218de8d7fSPeter Avalos * Since we're only looking for logins here, we know that the username 115318de8d7fSPeter Avalos * must be set correctly. On systems that leave it in, we check for 115418de8d7fSPeter Avalos * ut_type==USER_PROCESS (indicating a login.) 115518de8d7fSPeter Avalos * 115618de8d7fSPeter Avalos * Portability: Some systems may set something other than USER_PROCESS 115718de8d7fSPeter Avalos * to indicate a login process. I don't know of any as I write. Also, 115818de8d7fSPeter Avalos * it's possible that some systems may both leave the username in 115918de8d7fSPeter Avalos * place and not have ut_type. 116018de8d7fSPeter Avalos */ 116118de8d7fSPeter Avalos 116218de8d7fSPeter Avalos /* return true if this wtmp entry indicates a login */ 116318de8d7fSPeter Avalos static int 116418de8d7fSPeter Avalos wtmp_islogin(struct logininfo *li, struct utmp *ut) 116518de8d7fSPeter Avalos { 116618de8d7fSPeter Avalos if (strncmp(li->username, ut->ut_name, 116718de8d7fSPeter Avalos MIN_SIZEOF(li->username, ut->ut_name)) == 0) { 116818de8d7fSPeter Avalos # ifdef HAVE_TYPE_IN_UTMP 116918de8d7fSPeter Avalos if (ut->ut_type & USER_PROCESS) 117018de8d7fSPeter Avalos return (1); 117118de8d7fSPeter Avalos # else 117218de8d7fSPeter Avalos return (1); 117318de8d7fSPeter Avalos # endif 117418de8d7fSPeter Avalos } 117518de8d7fSPeter Avalos return (0); 117618de8d7fSPeter Avalos } 117718de8d7fSPeter Avalos 117818de8d7fSPeter Avalos int 117918de8d7fSPeter Avalos wtmp_get_entry(struct logininfo *li) 118018de8d7fSPeter Avalos { 118118de8d7fSPeter Avalos struct stat st; 118218de8d7fSPeter Avalos struct utmp ut; 118318de8d7fSPeter Avalos int fd, found = 0; 118418de8d7fSPeter Avalos 118518de8d7fSPeter Avalos /* Clear the time entries in our logininfo */ 118618de8d7fSPeter Avalos li->tv_sec = li->tv_usec = 0; 118718de8d7fSPeter Avalos 118818de8d7fSPeter Avalos if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { 118918de8d7fSPeter Avalos logit("%s: problem opening %s: %s", __func__, 119018de8d7fSPeter Avalos WTMP_FILE, strerror(errno)); 119118de8d7fSPeter Avalos return (0); 119218de8d7fSPeter Avalos } 119318de8d7fSPeter Avalos if (fstat(fd, &st) != 0) { 119418de8d7fSPeter Avalos logit("%s: couldn't stat %s: %s", __func__, 119518de8d7fSPeter Avalos WTMP_FILE, strerror(errno)); 119618de8d7fSPeter Avalos close(fd); 119718de8d7fSPeter Avalos return (0); 119818de8d7fSPeter Avalos } 119918de8d7fSPeter Avalos 120018de8d7fSPeter Avalos /* Seek to the start of the last struct utmp */ 120118de8d7fSPeter Avalos if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { 120218de8d7fSPeter Avalos /* Looks like we've got a fresh wtmp file */ 120318de8d7fSPeter Avalos close(fd); 120418de8d7fSPeter Avalos return (0); 120518de8d7fSPeter Avalos } 120618de8d7fSPeter Avalos 120718de8d7fSPeter Avalos while (!found) { 120818de8d7fSPeter Avalos if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { 120918de8d7fSPeter Avalos logit("%s: read of %s failed: %s", __func__, 121018de8d7fSPeter Avalos WTMP_FILE, strerror(errno)); 121118de8d7fSPeter Avalos close (fd); 121218de8d7fSPeter Avalos return (0); 121318de8d7fSPeter Avalos } 121418de8d7fSPeter Avalos if (wtmp_islogin(li, &ut) ) { 121518de8d7fSPeter Avalos found = 1; 121618de8d7fSPeter Avalos /* 121718de8d7fSPeter Avalos * We've already checked for a time in struct 121818de8d7fSPeter Avalos * utmp, in login_getlast() 121918de8d7fSPeter Avalos */ 122018de8d7fSPeter Avalos # ifdef HAVE_TIME_IN_UTMP 122118de8d7fSPeter Avalos li->tv_sec = ut.ut_time; 122218de8d7fSPeter Avalos # else 122318de8d7fSPeter Avalos # if HAVE_TV_IN_UTMP 122418de8d7fSPeter Avalos li->tv_sec = ut.ut_tv.tv_sec; 122518de8d7fSPeter Avalos # endif 122618de8d7fSPeter Avalos # endif 122718de8d7fSPeter Avalos line_fullname(li->line, ut.ut_line, 122818de8d7fSPeter Avalos MIN_SIZEOF(li->line, ut.ut_line)); 122918de8d7fSPeter Avalos # ifdef HAVE_HOST_IN_UTMP 123018de8d7fSPeter Avalos strlcpy(li->hostname, ut.ut_host, 123118de8d7fSPeter Avalos MIN_SIZEOF(li->hostname, ut.ut_host)); 123218de8d7fSPeter Avalos # endif 123318de8d7fSPeter Avalos continue; 123418de8d7fSPeter Avalos } 123518de8d7fSPeter Avalos /* Seek back 2 x struct utmp */ 123618de8d7fSPeter Avalos if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { 123718de8d7fSPeter Avalos /* We've found the start of the file, so quit */ 123818de8d7fSPeter Avalos close(fd); 123918de8d7fSPeter Avalos return (0); 124018de8d7fSPeter Avalos } 124118de8d7fSPeter Avalos } 124218de8d7fSPeter Avalos 124318de8d7fSPeter Avalos /* We found an entry. Tidy up and return */ 124418de8d7fSPeter Avalos close(fd); 124518de8d7fSPeter Avalos return (1); 124618de8d7fSPeter Avalos } 124718de8d7fSPeter Avalos # endif /* USE_WTMP */ 124818de8d7fSPeter Avalos 124918de8d7fSPeter Avalos 125018de8d7fSPeter Avalos /** 125118de8d7fSPeter Avalos ** Low-level wtmpx functions 125218de8d7fSPeter Avalos **/ 125318de8d7fSPeter Avalos 125418de8d7fSPeter Avalos #ifdef USE_WTMPX 125518de8d7fSPeter Avalos /* 125618de8d7fSPeter Avalos * Write a wtmpx entry direct to the end of the file 125718de8d7fSPeter Avalos * This is a slight modification of code in OpenBSD's logwtmp.c 125818de8d7fSPeter Avalos */ 125918de8d7fSPeter Avalos static int 126018de8d7fSPeter Avalos wtmpx_write(struct logininfo *li, struct utmpx *utx) 126118de8d7fSPeter Avalos { 126218de8d7fSPeter Avalos #ifndef HAVE_UPDWTMPX 126318de8d7fSPeter Avalos struct stat buf; 126418de8d7fSPeter Avalos int fd, ret = 1; 126518de8d7fSPeter Avalos 126618de8d7fSPeter Avalos if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 126718de8d7fSPeter Avalos logit("%s: problem opening %s: %s", __func__, 126818de8d7fSPeter Avalos WTMPX_FILE, strerror(errno)); 126918de8d7fSPeter Avalos return (0); 127018de8d7fSPeter Avalos } 127118de8d7fSPeter Avalos 127218de8d7fSPeter Avalos if (fstat(fd, &buf) == 0) 127318de8d7fSPeter Avalos if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) { 127418de8d7fSPeter Avalos ftruncate(fd, buf.st_size); 127518de8d7fSPeter Avalos logit("%s: problem writing %s: %s", __func__, 127618de8d7fSPeter Avalos WTMPX_FILE, strerror(errno)); 127718de8d7fSPeter Avalos ret = 0; 127818de8d7fSPeter Avalos } 127918de8d7fSPeter Avalos close(fd); 128018de8d7fSPeter Avalos 128118de8d7fSPeter Avalos return (ret); 128218de8d7fSPeter Avalos #else 128318de8d7fSPeter Avalos updwtmpx(WTMPX_FILE, utx); 128418de8d7fSPeter Avalos return (1); 128518de8d7fSPeter Avalos #endif 128618de8d7fSPeter Avalos } 128718de8d7fSPeter Avalos 128818de8d7fSPeter Avalos 128918de8d7fSPeter Avalos static int 129018de8d7fSPeter Avalos wtmpx_perform_login(struct logininfo *li) 129118de8d7fSPeter Avalos { 129218de8d7fSPeter Avalos struct utmpx utx; 129318de8d7fSPeter Avalos 129418de8d7fSPeter Avalos construct_utmpx(li, &utx); 129518de8d7fSPeter Avalos return (wtmpx_write(li, &utx)); 129618de8d7fSPeter Avalos } 129718de8d7fSPeter Avalos 129818de8d7fSPeter Avalos 129918de8d7fSPeter Avalos static int 130018de8d7fSPeter Avalos wtmpx_perform_logout(struct logininfo *li) 130118de8d7fSPeter Avalos { 130218de8d7fSPeter Avalos struct utmpx utx; 130318de8d7fSPeter Avalos 130418de8d7fSPeter Avalos construct_utmpx(li, &utx); 130518de8d7fSPeter Avalos return (wtmpx_write(li, &utx)); 130618de8d7fSPeter Avalos } 130718de8d7fSPeter Avalos 130818de8d7fSPeter Avalos 130918de8d7fSPeter Avalos int 131018de8d7fSPeter Avalos wtmpx_write_entry(struct logininfo *li) 131118de8d7fSPeter Avalos { 131218de8d7fSPeter Avalos switch(li->type) { 131318de8d7fSPeter Avalos case LTYPE_LOGIN: 131418de8d7fSPeter Avalos return (wtmpx_perform_login(li)); 131518de8d7fSPeter Avalos case LTYPE_LOGOUT: 131618de8d7fSPeter Avalos return (wtmpx_perform_logout(li)); 131718de8d7fSPeter Avalos default: 131818de8d7fSPeter Avalos logit("%s: invalid type field", __func__); 131918de8d7fSPeter Avalos return (0); 132018de8d7fSPeter Avalos } 132118de8d7fSPeter Avalos } 132218de8d7fSPeter Avalos 132318de8d7fSPeter Avalos /* Please see the notes above wtmp_islogin() for information about the 132418de8d7fSPeter Avalos next two functions */ 132518de8d7fSPeter Avalos 132618de8d7fSPeter Avalos /* Return true if this wtmpx entry indicates a login */ 132718de8d7fSPeter Avalos static int 132818de8d7fSPeter Avalos wtmpx_islogin(struct logininfo *li, struct utmpx *utx) 132918de8d7fSPeter Avalos { 1330856ea928SPeter Avalos if (strncmp(li->username, utx->ut_user, 1331856ea928SPeter Avalos MIN_SIZEOF(li->username, utx->ut_user)) == 0 ) { 133218de8d7fSPeter Avalos # ifdef HAVE_TYPE_IN_UTMPX 133318de8d7fSPeter Avalos if (utx->ut_type == USER_PROCESS) 133418de8d7fSPeter Avalos return (1); 133518de8d7fSPeter Avalos # else 133618de8d7fSPeter Avalos return (1); 133718de8d7fSPeter Avalos # endif 133818de8d7fSPeter Avalos } 133918de8d7fSPeter Avalos return (0); 134018de8d7fSPeter Avalos } 134118de8d7fSPeter Avalos 134218de8d7fSPeter Avalos 134318de8d7fSPeter Avalos int 134418de8d7fSPeter Avalos wtmpx_get_entry(struct logininfo *li) 134518de8d7fSPeter Avalos { 134618de8d7fSPeter Avalos struct stat st; 134718de8d7fSPeter Avalos struct utmpx utx; 134818de8d7fSPeter Avalos int fd, found=0; 134918de8d7fSPeter Avalos 135018de8d7fSPeter Avalos /* Clear the time entries */ 135118de8d7fSPeter Avalos li->tv_sec = li->tv_usec = 0; 135218de8d7fSPeter Avalos 135318de8d7fSPeter Avalos if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { 135418de8d7fSPeter Avalos logit("%s: problem opening %s: %s", __func__, 135518de8d7fSPeter Avalos WTMPX_FILE, strerror(errno)); 135618de8d7fSPeter Avalos return (0); 135718de8d7fSPeter Avalos } 135818de8d7fSPeter Avalos if (fstat(fd, &st) != 0) { 135918de8d7fSPeter Avalos logit("%s: couldn't stat %s: %s", __func__, 136018de8d7fSPeter Avalos WTMPX_FILE, strerror(errno)); 136118de8d7fSPeter Avalos close(fd); 136218de8d7fSPeter Avalos return (0); 136318de8d7fSPeter Avalos } 136418de8d7fSPeter Avalos 136518de8d7fSPeter Avalos /* Seek to the start of the last struct utmpx */ 136618de8d7fSPeter Avalos if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { 136718de8d7fSPeter Avalos /* probably a newly rotated wtmpx file */ 136818de8d7fSPeter Avalos close(fd); 136918de8d7fSPeter Avalos return (0); 137018de8d7fSPeter Avalos } 137118de8d7fSPeter Avalos 137218de8d7fSPeter Avalos while (!found) { 137318de8d7fSPeter Avalos if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { 137418de8d7fSPeter Avalos logit("%s: read of %s failed: %s", __func__, 137518de8d7fSPeter Avalos WTMPX_FILE, strerror(errno)); 137618de8d7fSPeter Avalos close (fd); 137718de8d7fSPeter Avalos return (0); 137818de8d7fSPeter Avalos } 137918de8d7fSPeter Avalos /* 138018de8d7fSPeter Avalos * Logouts are recorded as a blank username on a particular 138118de8d7fSPeter Avalos * line. So, we just need to find the username in struct utmpx 138218de8d7fSPeter Avalos */ 138318de8d7fSPeter Avalos if (wtmpx_islogin(li, &utx)) { 138418de8d7fSPeter Avalos found = 1; 138518de8d7fSPeter Avalos # if defined(HAVE_TV_IN_UTMPX) 138618de8d7fSPeter Avalos li->tv_sec = utx.ut_tv.tv_sec; 138718de8d7fSPeter Avalos # elif defined(HAVE_TIME_IN_UTMPX) 138818de8d7fSPeter Avalos li->tv_sec = utx.ut_time; 138918de8d7fSPeter Avalos # endif 139018de8d7fSPeter Avalos line_fullname(li->line, utx.ut_line, sizeof(li->line)); 139118de8d7fSPeter Avalos # if defined(HAVE_HOST_IN_UTMPX) 139218de8d7fSPeter Avalos strlcpy(li->hostname, utx.ut_host, 139318de8d7fSPeter Avalos MIN_SIZEOF(li->hostname, utx.ut_host)); 139418de8d7fSPeter Avalos # endif 139518de8d7fSPeter Avalos continue; 139618de8d7fSPeter Avalos } 139718de8d7fSPeter Avalos if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { 139818de8d7fSPeter Avalos close(fd); 139918de8d7fSPeter Avalos return (0); 140018de8d7fSPeter Avalos } 140118de8d7fSPeter Avalos } 140218de8d7fSPeter Avalos 140318de8d7fSPeter Avalos close(fd); 140418de8d7fSPeter Avalos return (1); 140518de8d7fSPeter Avalos } 140618de8d7fSPeter Avalos #endif /* USE_WTMPX */ 140718de8d7fSPeter Avalos 140818de8d7fSPeter Avalos /** 140918de8d7fSPeter Avalos ** Low-level libutil login() functions 141018de8d7fSPeter Avalos **/ 141118de8d7fSPeter Avalos 141218de8d7fSPeter Avalos #ifdef USE_LOGIN 141318de8d7fSPeter Avalos static int 141418de8d7fSPeter Avalos syslogin_perform_login(struct logininfo *li) 141518de8d7fSPeter Avalos { 141618de8d7fSPeter Avalos struct utmp *ut; 141718de8d7fSPeter Avalos 141818de8d7fSPeter Avalos ut = xmalloc(sizeof(*ut)); 141918de8d7fSPeter Avalos construct_utmp(li, ut); 142018de8d7fSPeter Avalos login(ut); 142118de8d7fSPeter Avalos free(ut); 142218de8d7fSPeter Avalos 142318de8d7fSPeter Avalos return (1); 142418de8d7fSPeter Avalos } 142518de8d7fSPeter Avalos 142618de8d7fSPeter Avalos static int 142718de8d7fSPeter Avalos syslogin_perform_logout(struct logininfo *li) 142818de8d7fSPeter Avalos { 142918de8d7fSPeter Avalos # ifdef HAVE_LOGOUT 143018de8d7fSPeter Avalos char line[UT_LINESIZE]; 143118de8d7fSPeter Avalos 143218de8d7fSPeter Avalos (void)line_stripname(line, li->line, sizeof(line)); 143318de8d7fSPeter Avalos 143418de8d7fSPeter Avalos if (!logout(line)) 143518de8d7fSPeter Avalos logit("%s: logout() returned an error", __func__); 143618de8d7fSPeter Avalos # ifdef HAVE_LOGWTMP 143718de8d7fSPeter Avalos else 143818de8d7fSPeter Avalos logwtmp(line, "", ""); 143918de8d7fSPeter Avalos # endif 144018de8d7fSPeter Avalos /* FIXME: (ATL - if the need arises) What to do if we have 144118de8d7fSPeter Avalos * login, but no logout? what if logout but no logwtmp? All 144218de8d7fSPeter Avalos * routines are in libutil so they should all be there, 144318de8d7fSPeter Avalos * but... */ 144418de8d7fSPeter Avalos # endif 144518de8d7fSPeter Avalos return (1); 144618de8d7fSPeter Avalos } 144718de8d7fSPeter Avalos 144818de8d7fSPeter Avalos int 144918de8d7fSPeter Avalos syslogin_write_entry(struct logininfo *li) 145018de8d7fSPeter Avalos { 145118de8d7fSPeter Avalos switch (li->type) { 145218de8d7fSPeter Avalos case LTYPE_LOGIN: 145318de8d7fSPeter Avalos return (syslogin_perform_login(li)); 145418de8d7fSPeter Avalos case LTYPE_LOGOUT: 145518de8d7fSPeter Avalos return (syslogin_perform_logout(li)); 145618de8d7fSPeter Avalos default: 145718de8d7fSPeter Avalos logit("%s: Invalid type field", __func__); 145818de8d7fSPeter Avalos return (0); 145918de8d7fSPeter Avalos } 146018de8d7fSPeter Avalos } 146118de8d7fSPeter Avalos #endif /* USE_LOGIN */ 146218de8d7fSPeter Avalos 146318de8d7fSPeter Avalos /* end of file log-syslogin.c */ 146418de8d7fSPeter Avalos 146518de8d7fSPeter Avalos /** 146618de8d7fSPeter Avalos ** Low-level lastlog functions 146718de8d7fSPeter Avalos **/ 146818de8d7fSPeter Avalos 146918de8d7fSPeter Avalos #ifdef USE_LASTLOG 147018de8d7fSPeter Avalos 1471cb5eb4f1SPeter Avalos #if !defined(LASTLOG_WRITE_PUTUTXLINE) || !defined(HAVE_GETLASTLOGXBYNAME) 1472cb5eb4f1SPeter Avalos /* open the file (using filemode) and seek to the login entry */ 147318de8d7fSPeter Avalos static int 1474cb5eb4f1SPeter Avalos lastlog_openseek(struct logininfo *li, int *fd, int filemode) 147518de8d7fSPeter Avalos { 1476cb5eb4f1SPeter Avalos off_t offset; 1477cb5eb4f1SPeter Avalos char lastlog_file[1024]; 147818de8d7fSPeter Avalos struct stat st; 147918de8d7fSPeter Avalos 148018de8d7fSPeter Avalos if (stat(LASTLOG_FILE, &st) != 0) { 148118de8d7fSPeter Avalos logit("%s: Couldn't stat %s: %s", __func__, 148218de8d7fSPeter Avalos LASTLOG_FILE, strerror(errno)); 148318de8d7fSPeter Avalos return (0); 148418de8d7fSPeter Avalos } 1485cb5eb4f1SPeter Avalos if (S_ISDIR(st.st_mode)) { 148618de8d7fSPeter Avalos snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", 148718de8d7fSPeter Avalos LASTLOG_FILE, li->username); 1488cb5eb4f1SPeter Avalos } else if (S_ISREG(st.st_mode)) { 1489cb5eb4f1SPeter Avalos strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); 1490cb5eb4f1SPeter Avalos } else { 149118de8d7fSPeter Avalos logit("%s: %.100s is not a file or directory!", __func__, 149218de8d7fSPeter Avalos LASTLOG_FILE); 149318de8d7fSPeter Avalos return (0); 149418de8d7fSPeter Avalos } 149518de8d7fSPeter Avalos 149618de8d7fSPeter Avalos *fd = open(lastlog_file, filemode, 0600); 149718de8d7fSPeter Avalos if (*fd < 0) { 149818de8d7fSPeter Avalos debug("%s: Couldn't open %s: %s", __func__, 149918de8d7fSPeter Avalos lastlog_file, strerror(errno)); 150018de8d7fSPeter Avalos return (0); 150118de8d7fSPeter Avalos } 150218de8d7fSPeter Avalos 1503cb5eb4f1SPeter Avalos if (S_ISREG(st.st_mode)) { 150418de8d7fSPeter Avalos /* find this uid's offset in the lastlog file */ 15059f304aafSPeter Avalos offset = (off_t) ((u_long)li->uid * sizeof(struct lastlog)); 150618de8d7fSPeter Avalos 150718de8d7fSPeter Avalos if (lseek(*fd, offset, SEEK_SET) != offset) { 150818de8d7fSPeter Avalos logit("%s: %s->lseek(): %s", __func__, 150918de8d7fSPeter Avalos lastlog_file, strerror(errno)); 15109f304aafSPeter Avalos close(*fd); 151118de8d7fSPeter Avalos return (0); 151218de8d7fSPeter Avalos } 151318de8d7fSPeter Avalos } 151418de8d7fSPeter Avalos 151518de8d7fSPeter Avalos return (1); 151618de8d7fSPeter Avalos } 1517cb5eb4f1SPeter Avalos #endif /* !LASTLOG_WRITE_PUTUTXLINE || !HAVE_GETLASTLOGXBYNAME */ 151818de8d7fSPeter Avalos 1519cb5eb4f1SPeter Avalos #ifdef LASTLOG_WRITE_PUTUTXLINE 1520cb5eb4f1SPeter Avalos int 1521cb5eb4f1SPeter Avalos lastlog_write_entry(struct logininfo *li) 1522cb5eb4f1SPeter Avalos { 1523cb5eb4f1SPeter Avalos switch(li->type) { 1524cb5eb4f1SPeter Avalos case LTYPE_LOGIN: 1525cb5eb4f1SPeter Avalos return 1; /* lastlog written by pututxline */ 1526cb5eb4f1SPeter Avalos default: 1527cb5eb4f1SPeter Avalos logit("lastlog_write_entry: Invalid type field"); 1528cb5eb4f1SPeter Avalos return 0; 1529cb5eb4f1SPeter Avalos } 1530cb5eb4f1SPeter Avalos } 1531cb5eb4f1SPeter Avalos #else /* LASTLOG_WRITE_PUTUTXLINE */ 1532cb5eb4f1SPeter Avalos int 1533cb5eb4f1SPeter Avalos lastlog_write_entry(struct logininfo *li) 153418de8d7fSPeter Avalos { 153518de8d7fSPeter Avalos struct lastlog last; 153618de8d7fSPeter Avalos int fd; 153718de8d7fSPeter Avalos 1538cb5eb4f1SPeter Avalos switch(li->type) { 1539cb5eb4f1SPeter Avalos case LTYPE_LOGIN: 154018de8d7fSPeter Avalos /* create our struct lastlog */ 1541cb5eb4f1SPeter Avalos memset(&last, '\0', sizeof(last)); 1542cb5eb4f1SPeter Avalos line_stripname(last.ll_line, li->line, sizeof(last.ll_line)); 1543cb5eb4f1SPeter Avalos strlcpy(last.ll_host, li->hostname, 1544cb5eb4f1SPeter Avalos MIN_SIZEOF(last.ll_host, li->hostname)); 1545cb5eb4f1SPeter Avalos last.ll_time = li->tv_sec; 154618de8d7fSPeter Avalos 154718de8d7fSPeter Avalos if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) 154818de8d7fSPeter Avalos return (0); 154918de8d7fSPeter Avalos 155018de8d7fSPeter Avalos /* write the entry */ 155118de8d7fSPeter Avalos if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) { 155218de8d7fSPeter Avalos close(fd); 155318de8d7fSPeter Avalos logit("%s: Error writing to %s: %s", __func__, 155418de8d7fSPeter Avalos LASTLOG_FILE, strerror(errno)); 155518de8d7fSPeter Avalos return (0); 155618de8d7fSPeter Avalos } 155718de8d7fSPeter Avalos 155818de8d7fSPeter Avalos close(fd); 155918de8d7fSPeter Avalos return (1); 156018de8d7fSPeter Avalos default: 156118de8d7fSPeter Avalos logit("%s: Invalid type field", __func__); 156218de8d7fSPeter Avalos return (0); 156318de8d7fSPeter Avalos } 156418de8d7fSPeter Avalos } 1565cb5eb4f1SPeter Avalos #endif /* LASTLOG_WRITE_PUTUTXLINE */ 156618de8d7fSPeter Avalos 1567cb5eb4f1SPeter Avalos #ifdef HAVE_GETLASTLOGXBYNAME 1568cb5eb4f1SPeter Avalos int 1569cb5eb4f1SPeter Avalos lastlog_get_entry(struct logininfo *li) 157018de8d7fSPeter Avalos { 1571cb5eb4f1SPeter Avalos struct lastlogx l, *ll; 157218de8d7fSPeter Avalos 1573cb5eb4f1SPeter Avalos if ((ll = getlastlogxbyname(li->username, &l)) == NULL) { 1574cb5eb4f1SPeter Avalos memset(&l, '\0', sizeof(l)); 1575cb5eb4f1SPeter Avalos ll = &l; 1576cb5eb4f1SPeter Avalos } 1577cb5eb4f1SPeter Avalos line_fullname(li->line, ll->ll_line, sizeof(li->line)); 1578cb5eb4f1SPeter Avalos strlcpy(li->hostname, ll->ll_host, 1579cb5eb4f1SPeter Avalos MIN_SIZEOF(li->hostname, ll->ll_host)); 1580cb5eb4f1SPeter Avalos li->tv_sec = ll->ll_tv.tv_sec; 1581cb5eb4f1SPeter Avalos li->tv_usec = ll->ll_tv.tv_usec; 1582cb5eb4f1SPeter Avalos return (1); 1583cb5eb4f1SPeter Avalos } 1584cb5eb4f1SPeter Avalos #else /* HAVE_GETLASTLOGXBYNAME */ 158518de8d7fSPeter Avalos int 158618de8d7fSPeter Avalos lastlog_get_entry(struct logininfo *li) 158718de8d7fSPeter Avalos { 158818de8d7fSPeter Avalos struct lastlog last; 158918de8d7fSPeter Avalos int fd, ret; 159018de8d7fSPeter Avalos 159118de8d7fSPeter Avalos if (!lastlog_openseek(li, &fd, O_RDONLY)) 159218de8d7fSPeter Avalos return (0); 159318de8d7fSPeter Avalos 159418de8d7fSPeter Avalos ret = atomicio(read, fd, &last, sizeof(last)); 159518de8d7fSPeter Avalos close(fd); 159618de8d7fSPeter Avalos 159718de8d7fSPeter Avalos switch (ret) { 159818de8d7fSPeter Avalos case 0: 159918de8d7fSPeter Avalos memset(&last, '\0', sizeof(last)); 160018de8d7fSPeter Avalos /* FALLTHRU */ 160118de8d7fSPeter Avalos case sizeof(last): 1602cb5eb4f1SPeter Avalos line_fullname(li->line, last.ll_line, sizeof(li->line)); 1603cb5eb4f1SPeter Avalos strlcpy(li->hostname, last.ll_host, 1604cb5eb4f1SPeter Avalos MIN_SIZEOF(li->hostname, last.ll_host)); 1605cb5eb4f1SPeter Avalos li->tv_sec = last.ll_time; 160618de8d7fSPeter Avalos return (1); 160718de8d7fSPeter Avalos case -1: 160818de8d7fSPeter Avalos error("%s: Error reading from %s: %s", __func__, 160918de8d7fSPeter Avalos LASTLOG_FILE, strerror(errno)); 161018de8d7fSPeter Avalos return (0); 161118de8d7fSPeter Avalos default: 161218de8d7fSPeter Avalos error("%s: Error reading from %s: Expecting %d, got %d", 161318de8d7fSPeter Avalos __func__, LASTLOG_FILE, (int)sizeof(last), ret); 161418de8d7fSPeter Avalos return (0); 161518de8d7fSPeter Avalos } 161618de8d7fSPeter Avalos 161718de8d7fSPeter Avalos /* NOTREACHED */ 161818de8d7fSPeter Avalos return (0); 161918de8d7fSPeter Avalos } 1620cb5eb4f1SPeter Avalos #endif /* HAVE_GETLASTLOGXBYNAME */ 162118de8d7fSPeter Avalos #endif /* USE_LASTLOG */ 162218de8d7fSPeter Avalos 1623856ea928SPeter Avalos #if defined(USE_UTMPX) && defined(HAVE_SETUTXDB) && \ 1624856ea928SPeter Avalos defined(UTXDB_LASTLOGIN) && defined(HAVE_GETUTXUSER) 1625856ea928SPeter Avalos int 1626856ea928SPeter Avalos utmpx_get_entry(struct logininfo *li) 1627856ea928SPeter Avalos { 1628856ea928SPeter Avalos struct utmpx *utx; 1629856ea928SPeter Avalos 1630856ea928SPeter Avalos if (setutxdb(UTXDB_LASTLOGIN, NULL) != 0) 1631856ea928SPeter Avalos return (0); 1632856ea928SPeter Avalos utx = getutxuser(li->username); 1633856ea928SPeter Avalos if (utx == NULL) { 1634856ea928SPeter Avalos endutxent(); 1635856ea928SPeter Avalos return (0); 1636856ea928SPeter Avalos } 1637856ea928SPeter Avalos 1638856ea928SPeter Avalos line_fullname(li->line, utx->ut_line, 1639856ea928SPeter Avalos MIN_SIZEOF(li->line, utx->ut_line)); 1640856ea928SPeter Avalos strlcpy(li->hostname, utx->ut_host, 1641856ea928SPeter Avalos MIN_SIZEOF(li->hostname, utx->ut_host)); 1642856ea928SPeter Avalos li->tv_sec = utx->ut_tv.tv_sec; 1643856ea928SPeter Avalos li->tv_usec = utx->ut_tv.tv_usec; 1644856ea928SPeter Avalos endutxent(); 1645856ea928SPeter Avalos return (1); 1646856ea928SPeter Avalos } 1647856ea928SPeter Avalos #endif /* USE_UTMPX && HAVE_SETUTXDB && UTXDB_LASTLOGIN && HAVE_GETUTXUSER */ 1648856ea928SPeter Avalos 164918de8d7fSPeter Avalos #ifdef USE_BTMP 165018de8d7fSPeter Avalos /* 165118de8d7fSPeter Avalos * Logs failed login attempts in _PATH_BTMP if that exists. 165218de8d7fSPeter Avalos * The most common login failure is to give password instead of username. 165318de8d7fSPeter Avalos * So the _PATH_BTMP file checked for the correct permission, so that 165418de8d7fSPeter Avalos * only root can read it. 165518de8d7fSPeter Avalos */ 165618de8d7fSPeter Avalos 165718de8d7fSPeter Avalos void 1658664f4763Szrj record_failed_login(struct ssh *ssh, const char *username, const char *hostname, 165918de8d7fSPeter Avalos const char *ttyn) 166018de8d7fSPeter Avalos { 166118de8d7fSPeter Avalos int fd; 166218de8d7fSPeter Avalos struct utmp ut; 166318de8d7fSPeter Avalos struct sockaddr_storage from; 166418de8d7fSPeter Avalos socklen_t fromlen = sizeof(from); 166518de8d7fSPeter Avalos struct sockaddr_in *a4; 166618de8d7fSPeter Avalos struct sockaddr_in6 *a6; 166718de8d7fSPeter Avalos time_t t; 166818de8d7fSPeter Avalos struct stat fst; 166918de8d7fSPeter Avalos 167018de8d7fSPeter Avalos if (geteuid() != 0) 167118de8d7fSPeter Avalos return; 167218de8d7fSPeter Avalos if ((fd = open(_PATH_BTMP, O_WRONLY | O_APPEND)) < 0) { 167318de8d7fSPeter Avalos debug("Unable to open the btmp file %s: %s", _PATH_BTMP, 167418de8d7fSPeter Avalos strerror(errno)); 167518de8d7fSPeter Avalos return; 167618de8d7fSPeter Avalos } 167718de8d7fSPeter Avalos if (fstat(fd, &fst) < 0) { 167818de8d7fSPeter Avalos logit("%s: fstat of %s failed: %s", __func__, _PATH_BTMP, 167918de8d7fSPeter Avalos strerror(errno)); 168018de8d7fSPeter Avalos goto out; 168118de8d7fSPeter Avalos } 16829f304aafSPeter Avalos if((fst.st_mode & (S_IXGRP | S_IRWXO)) || (fst.st_uid != 0)){ 168318de8d7fSPeter Avalos logit("Excess permission or bad ownership on file %s", 168418de8d7fSPeter Avalos _PATH_BTMP); 168518de8d7fSPeter Avalos goto out; 168618de8d7fSPeter Avalos } 168718de8d7fSPeter Avalos 168818de8d7fSPeter Avalos memset(&ut, 0, sizeof(ut)); 168918de8d7fSPeter Avalos /* strncpy because we don't necessarily want nul termination */ 169018de8d7fSPeter Avalos strncpy(ut.ut_user, username, sizeof(ut.ut_user)); 169118de8d7fSPeter Avalos strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line)); 169218de8d7fSPeter Avalos 169318de8d7fSPeter Avalos time(&t); 169418de8d7fSPeter Avalos ut.ut_time = t; /* ut_time is not always a time_t */ 169518de8d7fSPeter Avalos ut.ut_type = LOGIN_PROCESS; 169618de8d7fSPeter Avalos ut.ut_pid = getpid(); 169718de8d7fSPeter Avalos 169818de8d7fSPeter Avalos /* strncpy because we don't necessarily want nul termination */ 169918de8d7fSPeter Avalos strncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); 170018de8d7fSPeter Avalos 1701664f4763Szrj if (ssh_packet_connection_is_on_socket(ssh) && 1702664f4763Szrj getpeername(ssh_packet_get_connection_in(ssh), 170318de8d7fSPeter Avalos (struct sockaddr *)&from, &fromlen) == 0) { 170418de8d7fSPeter Avalos ipv64_normalise_mapped(&from, &fromlen); 170518de8d7fSPeter Avalos if (from.ss_family == AF_INET) { 170618de8d7fSPeter Avalos a4 = (struct sockaddr_in *)&from; 170718de8d7fSPeter Avalos memcpy(&ut.ut_addr, &(a4->sin_addr), 170818de8d7fSPeter Avalos MIN_SIZEOF(ut.ut_addr, a4->sin_addr)); 170918de8d7fSPeter Avalos } 171018de8d7fSPeter Avalos #ifdef HAVE_ADDR_V6_IN_UTMP 171118de8d7fSPeter Avalos if (from.ss_family == AF_INET6) { 171218de8d7fSPeter Avalos a6 = (struct sockaddr_in6 *)&from; 171318de8d7fSPeter Avalos memcpy(&ut.ut_addr_v6, &(a6->sin6_addr), 171418de8d7fSPeter Avalos MIN_SIZEOF(ut.ut_addr_v6, a6->sin6_addr)); 171518de8d7fSPeter Avalos } 171618de8d7fSPeter Avalos #endif 171718de8d7fSPeter Avalos } 171818de8d7fSPeter Avalos 171918de8d7fSPeter Avalos if (atomicio(vwrite, fd, &ut, sizeof(ut)) != sizeof(ut)) 172018de8d7fSPeter Avalos error("Failed to write to %s: %s", _PATH_BTMP, 172118de8d7fSPeter Avalos strerror(errno)); 172218de8d7fSPeter Avalos 172318de8d7fSPeter Avalos out: 172418de8d7fSPeter Avalos close(fd); 172518de8d7fSPeter Avalos } 172618de8d7fSPeter Avalos #endif /* USE_BTMP */ 1727