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 1590cbfa66cSDaniel 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> 1670cbfa66cSDaniel 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" 184*ee116499SAntonio Huete Jimenez #include "misc.h" 18518de8d7fSPeter Avalos 18618de8d7fSPeter Avalos #ifdef HAVE_UTIL_H 18718de8d7fSPeter Avalos # include <util.h> 18818de8d7fSPeter Avalos #endif 18918de8d7fSPeter Avalos 19018de8d7fSPeter Avalos /** 19118de8d7fSPeter Avalos ** prototypes for helper functions in this file 19218de8d7fSPeter Avalos **/ 19318de8d7fSPeter Avalos 19418de8d7fSPeter Avalos #if HAVE_UTMP_H 19518de8d7fSPeter Avalos void set_utmp_time(struct logininfo *li, struct utmp *ut); 19618de8d7fSPeter Avalos void construct_utmp(struct logininfo *li, struct utmp *ut); 19718de8d7fSPeter Avalos #endif 19818de8d7fSPeter Avalos 19918de8d7fSPeter Avalos #ifdef HAVE_UTMPX_H 20018de8d7fSPeter Avalos void set_utmpx_time(struct logininfo *li, struct utmpx *ut); 20118de8d7fSPeter Avalos void construct_utmpx(struct logininfo *li, struct utmpx *ut); 20218de8d7fSPeter Avalos #endif 20318de8d7fSPeter Avalos 20418de8d7fSPeter Avalos int utmp_write_entry(struct logininfo *li); 20518de8d7fSPeter Avalos int utmpx_write_entry(struct logininfo *li); 20618de8d7fSPeter Avalos int wtmp_write_entry(struct logininfo *li); 20718de8d7fSPeter Avalos int wtmpx_write_entry(struct logininfo *li); 20818de8d7fSPeter Avalos int lastlog_write_entry(struct logininfo *li); 20918de8d7fSPeter Avalos int syslogin_write_entry(struct logininfo *li); 21018de8d7fSPeter Avalos 21118de8d7fSPeter Avalos int getlast_entry(struct logininfo *li); 21218de8d7fSPeter Avalos int lastlog_get_entry(struct logininfo *li); 213856ea928SPeter Avalos int utmpx_get_entry(struct logininfo *li); 21418de8d7fSPeter Avalos int wtmp_get_entry(struct logininfo *li); 21518de8d7fSPeter Avalos int wtmpx_get_entry(struct logininfo *li); 21618de8d7fSPeter Avalos 217664f4763Szrj extern struct sshbuf *loginmsg; 21818de8d7fSPeter Avalos 21918de8d7fSPeter Avalos /* pick the shortest string */ 22018de8d7fSPeter Avalos #define MIN_SIZEOF(s1,s2) (sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2)) 22118de8d7fSPeter Avalos 22218de8d7fSPeter Avalos /** 22318de8d7fSPeter Avalos ** platform-independent login functions 22418de8d7fSPeter Avalos **/ 22518de8d7fSPeter Avalos 22618de8d7fSPeter Avalos /* 22718de8d7fSPeter Avalos * login_login(struct logininfo *) - Record a login 22818de8d7fSPeter Avalos * 22918de8d7fSPeter Avalos * Call with a pointer to a struct logininfo initialised with 23018de8d7fSPeter Avalos * login_init_entry() or login_alloc_entry() 23118de8d7fSPeter Avalos * 23218de8d7fSPeter Avalos * Returns: 23318de8d7fSPeter Avalos * >0 if successful 23418de8d7fSPeter Avalos * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 23518de8d7fSPeter Avalos */ 23618de8d7fSPeter Avalos int 23718de8d7fSPeter Avalos login_login(struct logininfo *li) 23818de8d7fSPeter Avalos { 23918de8d7fSPeter Avalos li->type = LTYPE_LOGIN; 24018de8d7fSPeter Avalos return (login_write(li)); 24118de8d7fSPeter Avalos } 24218de8d7fSPeter Avalos 24318de8d7fSPeter Avalos 24418de8d7fSPeter Avalos /* 24518de8d7fSPeter Avalos * login_logout(struct logininfo *) - Record a logout 24618de8d7fSPeter Avalos * 24718de8d7fSPeter Avalos * Call as with login_login() 24818de8d7fSPeter Avalos * 24918de8d7fSPeter Avalos * Returns: 25018de8d7fSPeter Avalos * >0 if successful 25118de8d7fSPeter Avalos * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 25218de8d7fSPeter Avalos */ 25318de8d7fSPeter Avalos int 25418de8d7fSPeter Avalos login_logout(struct logininfo *li) 25518de8d7fSPeter Avalos { 25618de8d7fSPeter Avalos li->type = LTYPE_LOGOUT; 25718de8d7fSPeter Avalos return (login_write(li)); 25818de8d7fSPeter Avalos } 25918de8d7fSPeter Avalos 26018de8d7fSPeter Avalos /* 26118de8d7fSPeter Avalos * login_get_lastlog_time(int) - Retrieve the last login time 26218de8d7fSPeter Avalos * 26318de8d7fSPeter Avalos * Retrieve the last login time for the given uid. Will try to use the 26418de8d7fSPeter Avalos * system lastlog facilities if they are available, but will fall back 26518de8d7fSPeter Avalos * to looking in wtmp/wtmpx if necessary 26618de8d7fSPeter Avalos * 26718de8d7fSPeter Avalos * Returns: 26818de8d7fSPeter Avalos * 0 on failure, or if user has never logged in 26918de8d7fSPeter Avalos * Time in seconds from the epoch if successful 27018de8d7fSPeter Avalos * 27118de8d7fSPeter Avalos * Useful preprocessor symbols: 27218de8d7fSPeter Avalos * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog 27318de8d7fSPeter Avalos * info 27418de8d7fSPeter Avalos * USE_LASTLOG: If set, indicates the presence of system lastlog 27518de8d7fSPeter Avalos * facilities. If this and DISABLE_LASTLOG are not set, 27618de8d7fSPeter Avalos * try to retrieve lastlog information from wtmp/wtmpx. 27718de8d7fSPeter Avalos */ 27818de8d7fSPeter Avalos unsigned int 2799f304aafSPeter Avalos login_get_lastlog_time(const uid_t uid) 28018de8d7fSPeter Avalos { 28118de8d7fSPeter Avalos struct logininfo li; 28218de8d7fSPeter Avalos 28318de8d7fSPeter Avalos if (login_get_lastlog(&li, uid)) 28418de8d7fSPeter Avalos return (li.tv_sec); 28518de8d7fSPeter Avalos else 28618de8d7fSPeter Avalos return (0); 28718de8d7fSPeter Avalos } 28818de8d7fSPeter Avalos 28918de8d7fSPeter Avalos /* 29018de8d7fSPeter Avalos * login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry 29118de8d7fSPeter Avalos * 29218de8d7fSPeter Avalos * Retrieve a logininfo structure populated (only partially) with 29318de8d7fSPeter Avalos * information from the system lastlog data, or from wtmp/wtmpx if no 29418de8d7fSPeter Avalos * system lastlog information exists. 29518de8d7fSPeter Avalos * 29618de8d7fSPeter Avalos * Note this routine must be given a pre-allocated logininfo. 29718de8d7fSPeter Avalos * 29818de8d7fSPeter Avalos * Returns: 29918de8d7fSPeter Avalos * >0: A pointer to your struct logininfo if successful 30018de8d7fSPeter Avalos * 0 on failure (will use OpenSSH's logging facilities for diagnostics) 30118de8d7fSPeter Avalos */ 30218de8d7fSPeter Avalos struct logininfo * 3039f304aafSPeter Avalos login_get_lastlog(struct logininfo *li, const uid_t uid) 30418de8d7fSPeter Avalos { 30518de8d7fSPeter Avalos struct passwd *pw; 30618de8d7fSPeter Avalos 30718de8d7fSPeter Avalos memset(li, '\0', sizeof(*li)); 30818de8d7fSPeter Avalos li->uid = uid; 30918de8d7fSPeter Avalos 31018de8d7fSPeter Avalos /* 31118de8d7fSPeter Avalos * If we don't have a 'real' lastlog, we need the username to 31218de8d7fSPeter Avalos * reliably search wtmp(x) for the last login (see 31318de8d7fSPeter Avalos * wtmp_get_entry().) 31418de8d7fSPeter Avalos */ 31518de8d7fSPeter Avalos pw = getpwuid(uid); 31618de8d7fSPeter Avalos if (pw == NULL) 3179f304aafSPeter Avalos fatal("%s: Cannot find account for uid %ld", __func__, 3189f304aafSPeter Avalos (long)uid); 31918de8d7fSPeter Avalos 32036e94dc5SPeter Avalos if (strlcpy(li->username, pw->pw_name, sizeof(li->username)) >= 32136e94dc5SPeter Avalos sizeof(li->username)) { 32236e94dc5SPeter Avalos error("%s: username too long (%lu > max %lu)", __func__, 32336e94dc5SPeter Avalos (unsigned long)strlen(pw->pw_name), 32436e94dc5SPeter Avalos (unsigned long)sizeof(li->username) - 1); 32536e94dc5SPeter Avalos return NULL; 32636e94dc5SPeter Avalos } 32718de8d7fSPeter Avalos 32818de8d7fSPeter Avalos if (getlast_entry(li)) 32918de8d7fSPeter Avalos return (li); 33018de8d7fSPeter Avalos else 33118de8d7fSPeter Avalos return (NULL); 33218de8d7fSPeter Avalos } 33318de8d7fSPeter Avalos 33418de8d7fSPeter Avalos /* 33518de8d7fSPeter Avalos * login_alloc_entry(int, char*, char*, char*) - Allocate and initialise 33618de8d7fSPeter Avalos * a logininfo structure 33718de8d7fSPeter Avalos * 33818de8d7fSPeter Avalos * This function creates a new struct logininfo, a data structure 33918de8d7fSPeter Avalos * meant to carry the information required to portably record login info. 34018de8d7fSPeter Avalos * 34118de8d7fSPeter Avalos * Returns a pointer to a newly created struct logininfo. If memory 34218de8d7fSPeter Avalos * allocation fails, the program halts. 34318de8d7fSPeter Avalos */ 34418de8d7fSPeter Avalos struct 3459f304aafSPeter Avalos logininfo *login_alloc_entry(pid_t pid, const char *username, 34618de8d7fSPeter Avalos const char *hostname, const char *line) 34718de8d7fSPeter Avalos { 34818de8d7fSPeter Avalos struct logininfo *newli; 34918de8d7fSPeter Avalos 35018de8d7fSPeter Avalos newli = xmalloc(sizeof(*newli)); 35118de8d7fSPeter Avalos login_init_entry(newli, pid, username, hostname, line); 35218de8d7fSPeter Avalos return (newli); 35318de8d7fSPeter Avalos } 35418de8d7fSPeter Avalos 35518de8d7fSPeter Avalos 35618de8d7fSPeter Avalos /* login_free_entry(struct logininfo *) - free struct memory */ 35718de8d7fSPeter Avalos void 35818de8d7fSPeter Avalos login_free_entry(struct logininfo *li) 35918de8d7fSPeter Avalos { 36036e94dc5SPeter Avalos free(li); 36118de8d7fSPeter Avalos } 36218de8d7fSPeter Avalos 36318de8d7fSPeter Avalos 36418de8d7fSPeter Avalos /* login_init_entry(struct logininfo *, int, char*, char*, char*) 36518de8d7fSPeter Avalos * - initialise a struct logininfo 36618de8d7fSPeter Avalos * 36718de8d7fSPeter Avalos * Populates a new struct logininfo, a data structure meant to carry 36818de8d7fSPeter Avalos * the information required to portably record login info. 36918de8d7fSPeter Avalos * 37018de8d7fSPeter Avalos * Returns: 1 37118de8d7fSPeter Avalos */ 37218de8d7fSPeter Avalos int 3739f304aafSPeter Avalos login_init_entry(struct logininfo *li, pid_t pid, const char *username, 37418de8d7fSPeter Avalos const char *hostname, const char *line) 37518de8d7fSPeter Avalos { 37618de8d7fSPeter Avalos struct passwd *pw; 37718de8d7fSPeter Avalos 37818de8d7fSPeter Avalos memset(li, 0, sizeof(*li)); 37918de8d7fSPeter Avalos 38018de8d7fSPeter Avalos li->pid = pid; 38118de8d7fSPeter Avalos 38218de8d7fSPeter Avalos /* set the line information */ 38318de8d7fSPeter Avalos if (line) 38418de8d7fSPeter Avalos line_fullname(li->line, line, sizeof(li->line)); 38518de8d7fSPeter Avalos 38618de8d7fSPeter Avalos if (username) { 38718de8d7fSPeter Avalos strlcpy(li->username, username, sizeof(li->username)); 38818de8d7fSPeter Avalos pw = getpwnam(li->username); 38918de8d7fSPeter Avalos if (pw == NULL) { 39018de8d7fSPeter Avalos fatal("%s: Cannot find user \"%s\"", __func__, 39118de8d7fSPeter Avalos li->username); 39218de8d7fSPeter Avalos } 39318de8d7fSPeter Avalos li->uid = pw->pw_uid; 39418de8d7fSPeter Avalos } 39518de8d7fSPeter Avalos 39618de8d7fSPeter Avalos if (hostname) 39718de8d7fSPeter Avalos strlcpy(li->hostname, hostname, sizeof(li->hostname)); 39818de8d7fSPeter Avalos 39918de8d7fSPeter Avalos return (1); 40018de8d7fSPeter Avalos } 40118de8d7fSPeter Avalos 40218de8d7fSPeter Avalos /* 40318de8d7fSPeter Avalos * login_set_current_time(struct logininfo *) - set the current time 40418de8d7fSPeter Avalos * 40518de8d7fSPeter Avalos * Set the current time in a logininfo structure. This function is 40618de8d7fSPeter Avalos * meant to eliminate the need to deal with system dependencies for 40718de8d7fSPeter Avalos * time handling. 40818de8d7fSPeter Avalos */ 40918de8d7fSPeter Avalos void 41018de8d7fSPeter Avalos login_set_current_time(struct logininfo *li) 41118de8d7fSPeter Avalos { 41218de8d7fSPeter Avalos struct timeval tv; 41318de8d7fSPeter Avalos 41418de8d7fSPeter Avalos gettimeofday(&tv, NULL); 41518de8d7fSPeter Avalos 41618de8d7fSPeter Avalos li->tv_sec = tv.tv_sec; 41718de8d7fSPeter Avalos li->tv_usec = tv.tv_usec; 41818de8d7fSPeter Avalos } 41918de8d7fSPeter Avalos 42018de8d7fSPeter Avalos /* copy a sockaddr_* into our logininfo */ 42118de8d7fSPeter Avalos void 42218de8d7fSPeter Avalos login_set_addr(struct logininfo *li, const struct sockaddr *sa, 42318de8d7fSPeter Avalos const unsigned int sa_size) 42418de8d7fSPeter Avalos { 42518de8d7fSPeter Avalos unsigned int bufsize = sa_size; 42618de8d7fSPeter Avalos 42718de8d7fSPeter Avalos /* make sure we don't overrun our union */ 42818de8d7fSPeter Avalos if (sizeof(li->hostaddr) < sa_size) 42918de8d7fSPeter Avalos bufsize = sizeof(li->hostaddr); 43018de8d7fSPeter Avalos 43118de8d7fSPeter Avalos memcpy(&li->hostaddr.sa, sa, bufsize); 43218de8d7fSPeter Avalos } 43318de8d7fSPeter Avalos 43418de8d7fSPeter Avalos 43518de8d7fSPeter Avalos /** 43618de8d7fSPeter Avalos ** login_write: Call low-level recording functions based on autoconf 43718de8d7fSPeter Avalos ** results 43818de8d7fSPeter Avalos **/ 43918de8d7fSPeter Avalos int 44018de8d7fSPeter Avalos login_write(struct logininfo *li) 44118de8d7fSPeter Avalos { 44218de8d7fSPeter Avalos #ifndef HAVE_CYGWIN 44318de8d7fSPeter Avalos if (geteuid() != 0) { 44418de8d7fSPeter Avalos logit("Attempt to write login records by non-root user (aborting)"); 44518de8d7fSPeter Avalos return (1); 44618de8d7fSPeter Avalos } 44718de8d7fSPeter Avalos #endif 44818de8d7fSPeter Avalos 44918de8d7fSPeter Avalos /* set the timestamp */ 45018de8d7fSPeter Avalos login_set_current_time(li); 45118de8d7fSPeter Avalos #ifdef USE_LOGIN 45218de8d7fSPeter Avalos syslogin_write_entry(li); 45318de8d7fSPeter Avalos #endif 45418de8d7fSPeter Avalos #ifdef USE_LASTLOG 45518de8d7fSPeter Avalos if (li->type == LTYPE_LOGIN) 45618de8d7fSPeter Avalos lastlog_write_entry(li); 45718de8d7fSPeter Avalos #endif 45818de8d7fSPeter Avalos #ifdef USE_UTMP 45918de8d7fSPeter Avalos utmp_write_entry(li); 46018de8d7fSPeter Avalos #endif 46118de8d7fSPeter Avalos #ifdef USE_WTMP 46218de8d7fSPeter Avalos wtmp_write_entry(li); 46318de8d7fSPeter Avalos #endif 46418de8d7fSPeter Avalos #ifdef USE_UTMPX 46518de8d7fSPeter Avalos utmpx_write_entry(li); 46618de8d7fSPeter Avalos #endif 46718de8d7fSPeter Avalos #ifdef USE_WTMPX 46818de8d7fSPeter Avalos wtmpx_write_entry(li); 46918de8d7fSPeter Avalos #endif 47018de8d7fSPeter Avalos #ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN 47118de8d7fSPeter Avalos if (li->type == LTYPE_LOGIN && 47218de8d7fSPeter Avalos !sys_auth_record_login(li->username,li->hostname,li->line, 473664f4763Szrj loginmsg)) 47418de8d7fSPeter Avalos logit("Writing login record failed for %s", li->username); 47518de8d7fSPeter Avalos #endif 47618de8d7fSPeter Avalos #ifdef SSH_AUDIT_EVENTS 47718de8d7fSPeter Avalos if (li->type == LTYPE_LOGIN) 4789f304aafSPeter Avalos audit_session_open(li); 47918de8d7fSPeter Avalos else if (li->type == LTYPE_LOGOUT) 4809f304aafSPeter Avalos audit_session_close(li); 48118de8d7fSPeter Avalos #endif 48218de8d7fSPeter Avalos return (0); 48318de8d7fSPeter Avalos } 48418de8d7fSPeter Avalos 48518de8d7fSPeter Avalos #ifdef LOGIN_NEEDS_UTMPX 48618de8d7fSPeter Avalos int 48718de8d7fSPeter Avalos login_utmp_only(struct logininfo *li) 48818de8d7fSPeter Avalos { 48918de8d7fSPeter Avalos li->type = LTYPE_LOGIN; 49018de8d7fSPeter Avalos login_set_current_time(li); 49118de8d7fSPeter Avalos # ifdef USE_UTMP 49218de8d7fSPeter Avalos utmp_write_entry(li); 49318de8d7fSPeter Avalos # endif 49418de8d7fSPeter Avalos # ifdef USE_WTMP 49518de8d7fSPeter Avalos wtmp_write_entry(li); 49618de8d7fSPeter Avalos # endif 49718de8d7fSPeter Avalos # ifdef USE_UTMPX 49818de8d7fSPeter Avalos utmpx_write_entry(li); 49918de8d7fSPeter Avalos # endif 50018de8d7fSPeter Avalos # ifdef USE_WTMPX 50118de8d7fSPeter Avalos wtmpx_write_entry(li); 50218de8d7fSPeter Avalos # endif 50318de8d7fSPeter Avalos return (0); 50418de8d7fSPeter Avalos } 50518de8d7fSPeter Avalos #endif 50618de8d7fSPeter Avalos 50718de8d7fSPeter Avalos /** 50818de8d7fSPeter Avalos ** getlast_entry: Call low-level functions to retrieve the last login 50918de8d7fSPeter Avalos ** time. 51018de8d7fSPeter Avalos **/ 51118de8d7fSPeter Avalos 51218de8d7fSPeter Avalos /* take the uid in li and return the last login time */ 51318de8d7fSPeter Avalos int 51418de8d7fSPeter Avalos getlast_entry(struct logininfo *li) 51518de8d7fSPeter Avalos { 51618de8d7fSPeter Avalos #ifdef USE_LASTLOG 51718de8d7fSPeter Avalos return(lastlog_get_entry(li)); 51818de8d7fSPeter Avalos #else /* !USE_LASTLOG */ 519856ea928SPeter Avalos #if defined(USE_UTMPX) && defined(HAVE_SETUTXDB) && \ 520856ea928SPeter Avalos defined(UTXDB_LASTLOGIN) && defined(HAVE_GETUTXUSER) 521856ea928SPeter Avalos return (utmpx_get_entry(li)); 522856ea928SPeter Avalos #endif 52318de8d7fSPeter Avalos 52418de8d7fSPeter Avalos #if defined(DISABLE_LASTLOG) 52518de8d7fSPeter Avalos /* On some systems we shouldn't even try to obtain last login 52618de8d7fSPeter Avalos * time, e.g. AIX */ 52718de8d7fSPeter Avalos return (0); 52818de8d7fSPeter Avalos # elif defined(USE_WTMP) && \ 52918de8d7fSPeter Avalos (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) 53018de8d7fSPeter Avalos /* retrieve last login time from utmp */ 53118de8d7fSPeter Avalos return (wtmp_get_entry(li)); 53218de8d7fSPeter Avalos # elif defined(USE_WTMPX) && \ 53318de8d7fSPeter Avalos (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX)) 53418de8d7fSPeter Avalos /* If wtmp isn't available, try wtmpx */ 53518de8d7fSPeter Avalos return (wtmpx_get_entry(li)); 53618de8d7fSPeter Avalos # else 53718de8d7fSPeter Avalos /* Give up: No means of retrieving last login time */ 53818de8d7fSPeter Avalos return (0); 53918de8d7fSPeter Avalos # endif /* DISABLE_LASTLOG */ 54018de8d7fSPeter Avalos #endif /* USE_LASTLOG */ 54118de8d7fSPeter Avalos } 54218de8d7fSPeter Avalos 54318de8d7fSPeter Avalos 54418de8d7fSPeter Avalos 54518de8d7fSPeter Avalos /* 54618de8d7fSPeter Avalos * 'line' string utility functions 54718de8d7fSPeter Avalos * 54818de8d7fSPeter Avalos * These functions process the 'line' string into one of three forms: 54918de8d7fSPeter Avalos * 55018de8d7fSPeter Avalos * 1. The full filename (including '/dev') 55118de8d7fSPeter Avalos * 2. The stripped name (excluding '/dev') 55218de8d7fSPeter Avalos * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00 55318de8d7fSPeter Avalos * /dev/pts/1 -> ts/1 ) 55418de8d7fSPeter Avalos * 55518de8d7fSPeter Avalos * Form 3 is used on some systems to identify a .tmp.? entry when 55618de8d7fSPeter Avalos * attempting to remove it. Typically both addition and removal is 55718de8d7fSPeter Avalos * performed by one application - say, sshd - so as long as the choice 55818de8d7fSPeter Avalos * uniquely identifies a terminal it's ok. 55918de8d7fSPeter Avalos */ 56018de8d7fSPeter Avalos 56118de8d7fSPeter Avalos 56218de8d7fSPeter Avalos /* 56318de8d7fSPeter Avalos * line_fullname(): add the leading '/dev/' if it doesn't exist make 56418de8d7fSPeter Avalos * sure dst has enough space, if not just copy src (ugh) 56518de8d7fSPeter Avalos */ 56618de8d7fSPeter Avalos char * 56718de8d7fSPeter Avalos line_fullname(char *dst, const char *src, u_int dstsize) 56818de8d7fSPeter Avalos { 56918de8d7fSPeter Avalos memset(dst, '\0', dstsize); 57018de8d7fSPeter Avalos if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) 57118de8d7fSPeter Avalos strlcpy(dst, src, dstsize); 57218de8d7fSPeter Avalos else { 57318de8d7fSPeter Avalos strlcpy(dst, "/dev/", dstsize); 57418de8d7fSPeter Avalos strlcat(dst, src, dstsize); 57518de8d7fSPeter Avalos } 57618de8d7fSPeter Avalos return (dst); 57718de8d7fSPeter Avalos } 57818de8d7fSPeter Avalos 57918de8d7fSPeter Avalos /* line_stripname(): strip the leading '/dev' if it exists, return dst */ 58018de8d7fSPeter Avalos char * 58118de8d7fSPeter Avalos line_stripname(char *dst, const char *src, int dstsize) 58218de8d7fSPeter Avalos { 58318de8d7fSPeter Avalos memset(dst, '\0', dstsize); 58418de8d7fSPeter Avalos if (strncmp(src, "/dev/", 5) == 0) 58518de8d7fSPeter Avalos strlcpy(dst, src + 5, dstsize); 58618de8d7fSPeter Avalos else 58718de8d7fSPeter Avalos strlcpy(dst, src, dstsize); 58818de8d7fSPeter Avalos return (dst); 58918de8d7fSPeter Avalos } 59018de8d7fSPeter Avalos 59118de8d7fSPeter Avalos /* 59218de8d7fSPeter Avalos * line_abbrevname(): Return the abbreviated (usually four-character) 59318de8d7fSPeter Avalos * form of the line (Just use the last <dstsize> characters of the 59418de8d7fSPeter Avalos * full name.) 59518de8d7fSPeter Avalos * 59618de8d7fSPeter Avalos * NOTE: use strncpy because we do NOT necessarily want zero 59718de8d7fSPeter Avalos * termination 59818de8d7fSPeter Avalos */ 59918de8d7fSPeter Avalos char * 60018de8d7fSPeter Avalos line_abbrevname(char *dst, const char *src, int dstsize) 60118de8d7fSPeter Avalos { 60218de8d7fSPeter Avalos size_t len; 60318de8d7fSPeter Avalos 60418de8d7fSPeter Avalos memset(dst, '\0', dstsize); 60518de8d7fSPeter Avalos 60618de8d7fSPeter Avalos /* Always skip prefix if present */ 60718de8d7fSPeter Avalos if (strncmp(src, "/dev/", 5) == 0) 60818de8d7fSPeter Avalos src += 5; 60918de8d7fSPeter Avalos 61018de8d7fSPeter Avalos #ifdef WITH_ABBREV_NO_TTY 61118de8d7fSPeter Avalos if (strncmp(src, "tty", 3) == 0) 61218de8d7fSPeter Avalos src += 3; 61318de8d7fSPeter Avalos #endif 61418de8d7fSPeter Avalos 61518de8d7fSPeter Avalos len = strlen(src); 61618de8d7fSPeter Avalos 61718de8d7fSPeter Avalos if (len > 0) { 61818de8d7fSPeter Avalos if (((int)len - dstsize) > 0) 61918de8d7fSPeter Avalos src += ((int)len - dstsize); 62018de8d7fSPeter Avalos 62118de8d7fSPeter Avalos /* note: _don't_ change this to strlcpy */ 62218de8d7fSPeter Avalos strncpy(dst, src, (size_t)dstsize); 62318de8d7fSPeter Avalos } 62418de8d7fSPeter Avalos 62518de8d7fSPeter Avalos return (dst); 62618de8d7fSPeter Avalos } 62718de8d7fSPeter Avalos 62818de8d7fSPeter Avalos /** 62918de8d7fSPeter Avalos ** utmp utility functions 63018de8d7fSPeter Avalos ** 63118de8d7fSPeter Avalos ** These functions manipulate struct utmp, taking system differences 63218de8d7fSPeter Avalos ** into account. 63318de8d7fSPeter Avalos **/ 63418de8d7fSPeter Avalos 63518de8d7fSPeter Avalos #if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN) 63618de8d7fSPeter Avalos 63718de8d7fSPeter Avalos /* build the utmp structure */ 63818de8d7fSPeter Avalos void 63918de8d7fSPeter Avalos set_utmp_time(struct logininfo *li, struct utmp *ut) 64018de8d7fSPeter Avalos { 64118de8d7fSPeter Avalos # if defined(HAVE_TV_IN_UTMP) 64218de8d7fSPeter Avalos ut->ut_tv.tv_sec = li->tv_sec; 64318de8d7fSPeter Avalos ut->ut_tv.tv_usec = li->tv_usec; 64418de8d7fSPeter Avalos # elif defined(HAVE_TIME_IN_UTMP) 64518de8d7fSPeter Avalos ut->ut_time = li->tv_sec; 64618de8d7fSPeter Avalos # endif 64718de8d7fSPeter Avalos } 64818de8d7fSPeter Avalos 64918de8d7fSPeter Avalos void 65018de8d7fSPeter Avalos construct_utmp(struct logininfo *li, 65118de8d7fSPeter Avalos struct utmp *ut) 65218de8d7fSPeter Avalos { 65318de8d7fSPeter Avalos # ifdef HAVE_ADDR_V6_IN_UTMP 65418de8d7fSPeter Avalos struct sockaddr_in6 *sa6; 65518de8d7fSPeter Avalos # endif 65618de8d7fSPeter Avalos 65718de8d7fSPeter Avalos memset(ut, '\0', sizeof(*ut)); 65818de8d7fSPeter Avalos 65918de8d7fSPeter Avalos /* First fill out fields used for both logins and logouts */ 66018de8d7fSPeter Avalos 66118de8d7fSPeter Avalos # ifdef HAVE_ID_IN_UTMP 66218de8d7fSPeter Avalos line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id)); 66318de8d7fSPeter Avalos # endif 66418de8d7fSPeter Avalos 66518de8d7fSPeter Avalos # ifdef HAVE_TYPE_IN_UTMP 66618de8d7fSPeter Avalos /* This is done here to keep utmp constants out of struct logininfo */ 66718de8d7fSPeter Avalos switch (li->type) { 66818de8d7fSPeter Avalos case LTYPE_LOGIN: 66918de8d7fSPeter Avalos ut->ut_type = USER_PROCESS; 67018de8d7fSPeter Avalos break; 67118de8d7fSPeter Avalos case LTYPE_LOGOUT: 67218de8d7fSPeter Avalos ut->ut_type = DEAD_PROCESS; 67318de8d7fSPeter Avalos break; 67418de8d7fSPeter Avalos } 67518de8d7fSPeter Avalos # endif 67618de8d7fSPeter Avalos set_utmp_time(li, ut); 67718de8d7fSPeter Avalos 67818de8d7fSPeter Avalos line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line)); 67918de8d7fSPeter Avalos 68018de8d7fSPeter Avalos # ifdef HAVE_PID_IN_UTMP 68118de8d7fSPeter Avalos ut->ut_pid = li->pid; 68218de8d7fSPeter Avalos # endif 68318de8d7fSPeter Avalos 68418de8d7fSPeter Avalos /* If we're logging out, leave all other fields blank */ 68518de8d7fSPeter Avalos if (li->type == LTYPE_LOGOUT) 68618de8d7fSPeter Avalos return; 68718de8d7fSPeter Avalos 68818de8d7fSPeter Avalos /* 68918de8d7fSPeter Avalos * These fields are only used when logging in, and are blank 69018de8d7fSPeter Avalos * for logouts. 69118de8d7fSPeter Avalos */ 69218de8d7fSPeter Avalos 69318de8d7fSPeter Avalos /* Use strncpy because we don't necessarily want null termination */ 69418de8d7fSPeter Avalos strncpy(ut->ut_name, li->username, 69518de8d7fSPeter Avalos MIN_SIZEOF(ut->ut_name, li->username)); 69618de8d7fSPeter Avalos # ifdef HAVE_HOST_IN_UTMP 69718de8d7fSPeter Avalos strncpy(ut->ut_host, li->hostname, 69818de8d7fSPeter Avalos MIN_SIZEOF(ut->ut_host, li->hostname)); 69918de8d7fSPeter Avalos # endif 70018de8d7fSPeter Avalos # ifdef HAVE_ADDR_IN_UTMP 70118de8d7fSPeter Avalos /* this is just a 32-bit IP address */ 70218de8d7fSPeter Avalos if (li->hostaddr.sa.sa_family == AF_INET) 70318de8d7fSPeter Avalos ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 70418de8d7fSPeter Avalos # endif 70518de8d7fSPeter Avalos # ifdef HAVE_ADDR_V6_IN_UTMP 70618de8d7fSPeter Avalos /* this is just a 128-bit IPv6 address */ 70718de8d7fSPeter Avalos if (li->hostaddr.sa.sa_family == AF_INET6) { 70818de8d7fSPeter Avalos sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 70918de8d7fSPeter Avalos memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 71018de8d7fSPeter Avalos if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 71118de8d7fSPeter Avalos ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; 71218de8d7fSPeter Avalos ut->ut_addr_v6[1] = 0; 71318de8d7fSPeter Avalos ut->ut_addr_v6[2] = 0; 71418de8d7fSPeter Avalos ut->ut_addr_v6[3] = 0; 71518de8d7fSPeter Avalos } 71618de8d7fSPeter Avalos } 71718de8d7fSPeter Avalos # endif 71818de8d7fSPeter Avalos } 71918de8d7fSPeter Avalos #endif /* USE_UTMP || USE_WTMP || USE_LOGIN */ 72018de8d7fSPeter Avalos 72118de8d7fSPeter Avalos /** 72218de8d7fSPeter Avalos ** utmpx utility functions 72318de8d7fSPeter Avalos ** 72418de8d7fSPeter Avalos ** These functions manipulate struct utmpx, accounting for system 72518de8d7fSPeter Avalos ** variations. 72618de8d7fSPeter Avalos **/ 72718de8d7fSPeter Avalos 72818de8d7fSPeter Avalos #if defined(USE_UTMPX) || defined (USE_WTMPX) 72918de8d7fSPeter Avalos /* build the utmpx structure */ 73018de8d7fSPeter Avalos void 73118de8d7fSPeter Avalos set_utmpx_time(struct logininfo *li, struct utmpx *utx) 73218de8d7fSPeter Avalos { 73318de8d7fSPeter Avalos # if defined(HAVE_TV_IN_UTMPX) 73418de8d7fSPeter Avalos utx->ut_tv.tv_sec = li->tv_sec; 73518de8d7fSPeter Avalos utx->ut_tv.tv_usec = li->tv_usec; 73618de8d7fSPeter Avalos # elif defined(HAVE_TIME_IN_UTMPX) 73718de8d7fSPeter Avalos utx->ut_time = li->tv_sec; 73818de8d7fSPeter Avalos # endif 73918de8d7fSPeter Avalos } 74018de8d7fSPeter Avalos 74118de8d7fSPeter Avalos void 74218de8d7fSPeter Avalos construct_utmpx(struct logininfo *li, struct utmpx *utx) 74318de8d7fSPeter Avalos { 74418de8d7fSPeter Avalos # ifdef HAVE_ADDR_V6_IN_UTMP 74518de8d7fSPeter Avalos struct sockaddr_in6 *sa6; 74618de8d7fSPeter Avalos # endif 74718de8d7fSPeter Avalos memset(utx, '\0', sizeof(*utx)); 74818de8d7fSPeter Avalos 74918de8d7fSPeter Avalos # ifdef HAVE_ID_IN_UTMPX 75018de8d7fSPeter Avalos line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); 75118de8d7fSPeter Avalos # endif 75218de8d7fSPeter Avalos 75318de8d7fSPeter Avalos /* this is done here to keep utmp constants out of loginrec.h */ 75418de8d7fSPeter Avalos switch (li->type) { 75518de8d7fSPeter Avalos case LTYPE_LOGIN: 75618de8d7fSPeter Avalos utx->ut_type = USER_PROCESS; 75718de8d7fSPeter Avalos break; 75818de8d7fSPeter Avalos case LTYPE_LOGOUT: 75918de8d7fSPeter Avalos utx->ut_type = DEAD_PROCESS; 76018de8d7fSPeter Avalos break; 76118de8d7fSPeter Avalos } 76218de8d7fSPeter Avalos line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line)); 76318de8d7fSPeter Avalos set_utmpx_time(li, utx); 76418de8d7fSPeter Avalos utx->ut_pid = li->pid; 76518de8d7fSPeter Avalos 76618de8d7fSPeter Avalos /* strncpy(): Don't necessarily want null termination */ 767856ea928SPeter Avalos strncpy(utx->ut_user, li->username, 768856ea928SPeter Avalos MIN_SIZEOF(utx->ut_user, li->username)); 76918de8d7fSPeter Avalos 77018de8d7fSPeter Avalos if (li->type == LTYPE_LOGOUT) 77118de8d7fSPeter Avalos return; 77218de8d7fSPeter Avalos 77318de8d7fSPeter Avalos /* 77418de8d7fSPeter Avalos * These fields are only used when logging in, and are blank 77518de8d7fSPeter Avalos * for logouts. 77618de8d7fSPeter Avalos */ 77718de8d7fSPeter Avalos 77818de8d7fSPeter Avalos # ifdef HAVE_HOST_IN_UTMPX 77918de8d7fSPeter Avalos strncpy(utx->ut_host, li->hostname, 78018de8d7fSPeter Avalos MIN_SIZEOF(utx->ut_host, li->hostname)); 78118de8d7fSPeter Avalos # endif 78250a69bb5SSascha Wildner # ifdef HAVE_SS_IN_UTMPX 78350a69bb5SSascha Wildner utx->ut_ss = li->hostaddr.sa_storage; 78450a69bb5SSascha Wildner # endif 78518de8d7fSPeter Avalos # ifdef HAVE_ADDR_IN_UTMPX 78618de8d7fSPeter Avalos /* this is just a 32-bit IP address */ 78718de8d7fSPeter Avalos if (li->hostaddr.sa.sa_family == AF_INET) 78818de8d7fSPeter Avalos utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; 78918de8d7fSPeter Avalos # endif 79018de8d7fSPeter Avalos # ifdef HAVE_ADDR_V6_IN_UTMP 79118de8d7fSPeter Avalos /* this is just a 128-bit IPv6 address */ 79218de8d7fSPeter Avalos if (li->hostaddr.sa.sa_family == AF_INET6) { 79318de8d7fSPeter Avalos sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); 794e9778795SPeter Avalos memcpy(utx->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); 79518de8d7fSPeter Avalos if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { 796e9778795SPeter Avalos utx->ut_addr_v6[0] = utx->ut_addr_v6[3]; 797e9778795SPeter Avalos utx->ut_addr_v6[1] = 0; 798e9778795SPeter Avalos utx->ut_addr_v6[2] = 0; 799e9778795SPeter Avalos utx->ut_addr_v6[3] = 0; 80018de8d7fSPeter Avalos } 80118de8d7fSPeter Avalos } 80218de8d7fSPeter Avalos # endif 80318de8d7fSPeter Avalos # ifdef HAVE_SYSLEN_IN_UTMPX 80418de8d7fSPeter Avalos /* ut_syslen is the length of the utx_host string */ 805*ee116499SAntonio Huete Jimenez utx->ut_syslen = MINIMUM(strlen(li->hostname), sizeof(utx->ut_host)); 80618de8d7fSPeter Avalos # endif 80718de8d7fSPeter Avalos } 80818de8d7fSPeter Avalos #endif /* USE_UTMPX || USE_WTMPX */ 80918de8d7fSPeter Avalos 81018de8d7fSPeter Avalos /** 81118de8d7fSPeter Avalos ** Low-level utmp functions 81218de8d7fSPeter Avalos **/ 81318de8d7fSPeter Avalos 81418de8d7fSPeter Avalos /* FIXME: (ATL) utmp_write_direct needs testing */ 81518de8d7fSPeter Avalos #ifdef USE_UTMP 81618de8d7fSPeter Avalos 81718de8d7fSPeter Avalos /* if we can, use pututline() etc. */ 81818de8d7fSPeter Avalos # if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \ 81918de8d7fSPeter Avalos defined(HAVE_PUTUTLINE) 82018de8d7fSPeter Avalos # define UTMP_USE_LIBRARY 82118de8d7fSPeter Avalos # endif 82218de8d7fSPeter Avalos 82318de8d7fSPeter Avalos 82418de8d7fSPeter Avalos /* write a utmp entry with the system's help (pututline() and pals) */ 82518de8d7fSPeter Avalos # ifdef UTMP_USE_LIBRARY 82618de8d7fSPeter Avalos static int 82718de8d7fSPeter Avalos utmp_write_library(struct logininfo *li, struct utmp *ut) 82818de8d7fSPeter Avalos { 82918de8d7fSPeter Avalos setutent(); 83018de8d7fSPeter Avalos pututline(ut); 83118de8d7fSPeter Avalos # ifdef HAVE_ENDUTENT 83218de8d7fSPeter Avalos endutent(); 83318de8d7fSPeter Avalos # endif 83418de8d7fSPeter Avalos return (1); 83518de8d7fSPeter Avalos } 83618de8d7fSPeter Avalos # else /* UTMP_USE_LIBRARY */ 83718de8d7fSPeter Avalos 83818de8d7fSPeter Avalos /* 83918de8d7fSPeter Avalos * Write a utmp entry direct to the file 84018de8d7fSPeter Avalos * This is a slightly modification of code in OpenBSD's login.c 84118de8d7fSPeter Avalos */ 84218de8d7fSPeter Avalos static int 84318de8d7fSPeter Avalos utmp_write_direct(struct logininfo *li, struct utmp *ut) 84418de8d7fSPeter Avalos { 84518de8d7fSPeter Avalos struct utmp old_ut; 84618de8d7fSPeter Avalos register int fd; 84718de8d7fSPeter Avalos int tty; 84818de8d7fSPeter Avalos 84918de8d7fSPeter Avalos /* FIXME: (ATL) ttyslot() needs local implementation */ 85018de8d7fSPeter Avalos 85118de8d7fSPeter Avalos #if defined(HAVE_GETTTYENT) 85218de8d7fSPeter Avalos struct ttyent *ty; 85318de8d7fSPeter Avalos 85418de8d7fSPeter Avalos tty=0; 85518de8d7fSPeter Avalos setttyent(); 85618de8d7fSPeter Avalos while (NULL != (ty = getttyent())) { 85718de8d7fSPeter Avalos tty++; 85818de8d7fSPeter Avalos if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line))) 85918de8d7fSPeter Avalos break; 86018de8d7fSPeter Avalos } 86118de8d7fSPeter Avalos endttyent(); 86218de8d7fSPeter Avalos 86318de8d7fSPeter Avalos if (NULL == ty) { 86418de8d7fSPeter Avalos logit("%s: tty not found", __func__); 86518de8d7fSPeter Avalos return (0); 86618de8d7fSPeter Avalos } 86718de8d7fSPeter Avalos #else /* FIXME */ 86818de8d7fSPeter Avalos 86918de8d7fSPeter Avalos tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ 87018de8d7fSPeter Avalos 87118de8d7fSPeter Avalos #endif /* HAVE_GETTTYENT */ 87218de8d7fSPeter Avalos 87318de8d7fSPeter Avalos if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { 87418de8d7fSPeter Avalos off_t pos, ret; 87518de8d7fSPeter Avalos 87618de8d7fSPeter Avalos pos = (off_t)tty * sizeof(struct utmp); 87718de8d7fSPeter Avalos if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { 87818de8d7fSPeter Avalos logit("%s: lseek: %s", __func__, strerror(errno)); 8799f304aafSPeter Avalos close(fd); 88018de8d7fSPeter Avalos return (0); 88118de8d7fSPeter Avalos } 88218de8d7fSPeter Avalos if (ret != pos) { 88318de8d7fSPeter Avalos logit("%s: Couldn't seek to tty %d slot in %s", 88418de8d7fSPeter Avalos __func__, tty, UTMP_FILE); 8859f304aafSPeter Avalos close(fd); 88618de8d7fSPeter Avalos return (0); 88718de8d7fSPeter Avalos } 88818de8d7fSPeter Avalos /* 88918de8d7fSPeter Avalos * Prevent luser from zero'ing out ut_host. 89018de8d7fSPeter Avalos * If the new ut_line is empty but the old one is not 89118de8d7fSPeter Avalos * and ut_line and ut_name match, preserve the old ut_line. 89218de8d7fSPeter Avalos */ 89318de8d7fSPeter Avalos if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && 89418de8d7fSPeter Avalos (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && 89518de8d7fSPeter Avalos (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && 89618de8d7fSPeter Avalos (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) 89718de8d7fSPeter Avalos memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); 89818de8d7fSPeter Avalos 89918de8d7fSPeter Avalos if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { 90018de8d7fSPeter Avalos logit("%s: lseek: %s", __func__, strerror(errno)); 9019f304aafSPeter Avalos close(fd); 90218de8d7fSPeter Avalos return (0); 90318de8d7fSPeter Avalos } 90418de8d7fSPeter Avalos if (ret != pos) { 90518de8d7fSPeter Avalos logit("%s: Couldn't seek to tty %d slot in %s", 90618de8d7fSPeter Avalos __func__, tty, UTMP_FILE); 9079f304aafSPeter Avalos close(fd); 90818de8d7fSPeter Avalos return (0); 90918de8d7fSPeter Avalos } 91018de8d7fSPeter Avalos if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { 91118de8d7fSPeter Avalos logit("%s: error writing %s: %s", __func__, 91218de8d7fSPeter Avalos UTMP_FILE, strerror(errno)); 9139f304aafSPeter Avalos close(fd); 9149f304aafSPeter Avalos return (0); 91518de8d7fSPeter Avalos } 91618de8d7fSPeter Avalos 91718de8d7fSPeter Avalos close(fd); 91818de8d7fSPeter Avalos return (1); 91918de8d7fSPeter Avalos } else { 92018de8d7fSPeter Avalos return (0); 92118de8d7fSPeter Avalos } 92218de8d7fSPeter Avalos } 92318de8d7fSPeter Avalos # endif /* UTMP_USE_LIBRARY */ 92418de8d7fSPeter Avalos 92518de8d7fSPeter Avalos static int 92618de8d7fSPeter Avalos utmp_perform_login(struct logininfo *li) 92718de8d7fSPeter Avalos { 92818de8d7fSPeter Avalos struct utmp ut; 92918de8d7fSPeter Avalos 93018de8d7fSPeter Avalos construct_utmp(li, &ut); 93118de8d7fSPeter Avalos # ifdef UTMP_USE_LIBRARY 93218de8d7fSPeter Avalos if (!utmp_write_library(li, &ut)) { 93318de8d7fSPeter Avalos logit("%s: utmp_write_library() failed", __func__); 93418de8d7fSPeter Avalos return (0); 93518de8d7fSPeter Avalos } 93618de8d7fSPeter Avalos # else 93718de8d7fSPeter Avalos if (!utmp_write_direct(li, &ut)) { 93818de8d7fSPeter Avalos logit("%s: utmp_write_direct() failed", __func__); 93918de8d7fSPeter Avalos return (0); 94018de8d7fSPeter Avalos } 94118de8d7fSPeter Avalos # endif 94218de8d7fSPeter Avalos return (1); 94318de8d7fSPeter Avalos } 94418de8d7fSPeter Avalos 94518de8d7fSPeter Avalos 94618de8d7fSPeter Avalos static int 94718de8d7fSPeter Avalos utmp_perform_logout(struct logininfo *li) 94818de8d7fSPeter Avalos { 94918de8d7fSPeter Avalos struct utmp ut; 95018de8d7fSPeter Avalos 95118de8d7fSPeter Avalos construct_utmp(li, &ut); 95218de8d7fSPeter Avalos # ifdef UTMP_USE_LIBRARY 95318de8d7fSPeter Avalos if (!utmp_write_library(li, &ut)) { 95418de8d7fSPeter Avalos logit("%s: utmp_write_library() failed", __func__); 95518de8d7fSPeter Avalos return (0); 95618de8d7fSPeter Avalos } 95718de8d7fSPeter Avalos # else 95818de8d7fSPeter Avalos if (!utmp_write_direct(li, &ut)) { 95918de8d7fSPeter Avalos logit("%s: utmp_write_direct() failed", __func__); 96018de8d7fSPeter Avalos return (0); 96118de8d7fSPeter Avalos } 96218de8d7fSPeter Avalos # endif 96318de8d7fSPeter Avalos return (1); 96418de8d7fSPeter Avalos } 96518de8d7fSPeter Avalos 96618de8d7fSPeter Avalos 96718de8d7fSPeter Avalos int 96818de8d7fSPeter Avalos utmp_write_entry(struct logininfo *li) 96918de8d7fSPeter Avalos { 97018de8d7fSPeter Avalos switch(li->type) { 97118de8d7fSPeter Avalos case LTYPE_LOGIN: 97218de8d7fSPeter Avalos return (utmp_perform_login(li)); 97318de8d7fSPeter Avalos 97418de8d7fSPeter Avalos case LTYPE_LOGOUT: 97518de8d7fSPeter Avalos return (utmp_perform_logout(li)); 97618de8d7fSPeter Avalos 97718de8d7fSPeter Avalos default: 97818de8d7fSPeter Avalos logit("%s: invalid type field", __func__); 97918de8d7fSPeter Avalos return (0); 98018de8d7fSPeter Avalos } 98118de8d7fSPeter Avalos } 98218de8d7fSPeter Avalos #endif /* USE_UTMP */ 98318de8d7fSPeter Avalos 98418de8d7fSPeter Avalos 98518de8d7fSPeter Avalos /** 98618de8d7fSPeter Avalos ** Low-level utmpx functions 98718de8d7fSPeter Avalos **/ 98818de8d7fSPeter Avalos 98918de8d7fSPeter Avalos /* not much point if we don't want utmpx entries */ 99018de8d7fSPeter Avalos #ifdef USE_UTMPX 99118de8d7fSPeter Avalos 99218de8d7fSPeter Avalos /* if we have the wherewithall, use pututxline etc. */ 99318de8d7fSPeter Avalos # if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \ 99418de8d7fSPeter Avalos defined(HAVE_PUTUTXLINE) 99518de8d7fSPeter Avalos # define UTMPX_USE_LIBRARY 99618de8d7fSPeter Avalos # endif 99718de8d7fSPeter Avalos 99818de8d7fSPeter Avalos 99918de8d7fSPeter Avalos /* write a utmpx entry with the system's help (pututxline() and pals) */ 100018de8d7fSPeter Avalos # ifdef UTMPX_USE_LIBRARY 100118de8d7fSPeter Avalos static int 100218de8d7fSPeter Avalos utmpx_write_library(struct logininfo *li, struct utmpx *utx) 100318de8d7fSPeter Avalos { 100418de8d7fSPeter Avalos setutxent(); 100518de8d7fSPeter Avalos pututxline(utx); 100618de8d7fSPeter Avalos 100718de8d7fSPeter Avalos # ifdef HAVE_ENDUTXENT 100818de8d7fSPeter Avalos endutxent(); 100918de8d7fSPeter Avalos # endif 101018de8d7fSPeter Avalos return (1); 101118de8d7fSPeter Avalos } 101218de8d7fSPeter Avalos 101318de8d7fSPeter Avalos # else /* UTMPX_USE_LIBRARY */ 101418de8d7fSPeter Avalos 101518de8d7fSPeter Avalos /* write a utmp entry direct to the file */ 101618de8d7fSPeter Avalos static int 101718de8d7fSPeter Avalos utmpx_write_direct(struct logininfo *li, struct utmpx *utx) 101818de8d7fSPeter Avalos { 101918de8d7fSPeter Avalos logit("%s: not implemented!", __func__); 102018de8d7fSPeter Avalos return (0); 102118de8d7fSPeter Avalos } 102218de8d7fSPeter Avalos # endif /* UTMPX_USE_LIBRARY */ 102318de8d7fSPeter Avalos 102418de8d7fSPeter Avalos static int 102518de8d7fSPeter Avalos utmpx_perform_login(struct logininfo *li) 102618de8d7fSPeter Avalos { 102718de8d7fSPeter Avalos struct utmpx utx; 102818de8d7fSPeter Avalos 102918de8d7fSPeter Avalos construct_utmpx(li, &utx); 103018de8d7fSPeter Avalos # ifdef UTMPX_USE_LIBRARY 103118de8d7fSPeter Avalos if (!utmpx_write_library(li, &utx)) { 103218de8d7fSPeter Avalos logit("%s: utmp_write_library() failed", __func__); 103318de8d7fSPeter Avalos return (0); 103418de8d7fSPeter Avalos } 103518de8d7fSPeter Avalos # else 103618de8d7fSPeter Avalos if (!utmpx_write_direct(li, &ut)) { 103718de8d7fSPeter Avalos logit("%s: utmp_write_direct() failed", __func__); 103818de8d7fSPeter Avalos return (0); 103918de8d7fSPeter Avalos } 104018de8d7fSPeter Avalos # endif 104118de8d7fSPeter Avalos return (1); 104218de8d7fSPeter Avalos } 104318de8d7fSPeter Avalos 104418de8d7fSPeter Avalos 104518de8d7fSPeter Avalos static int 104618de8d7fSPeter Avalos utmpx_perform_logout(struct logininfo *li) 104718de8d7fSPeter Avalos { 104818de8d7fSPeter Avalos struct utmpx utx; 104918de8d7fSPeter Avalos 105018de8d7fSPeter Avalos construct_utmpx(li, &utx); 105118de8d7fSPeter Avalos # ifdef HAVE_ID_IN_UTMPX 105218de8d7fSPeter Avalos line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); 105318de8d7fSPeter Avalos # endif 105418de8d7fSPeter Avalos # ifdef HAVE_TYPE_IN_UTMPX 105518de8d7fSPeter Avalos utx.ut_type = DEAD_PROCESS; 105618de8d7fSPeter Avalos # endif 105718de8d7fSPeter Avalos 105818de8d7fSPeter Avalos # ifdef UTMPX_USE_LIBRARY 105918de8d7fSPeter Avalos utmpx_write_library(li, &utx); 106018de8d7fSPeter Avalos # else 106118de8d7fSPeter Avalos utmpx_write_direct(li, &utx); 106218de8d7fSPeter Avalos # endif 106318de8d7fSPeter Avalos return (1); 106418de8d7fSPeter Avalos } 106518de8d7fSPeter Avalos 106618de8d7fSPeter Avalos int 106718de8d7fSPeter Avalos utmpx_write_entry(struct logininfo *li) 106818de8d7fSPeter Avalos { 106918de8d7fSPeter Avalos switch(li->type) { 107018de8d7fSPeter Avalos case LTYPE_LOGIN: 107118de8d7fSPeter Avalos return (utmpx_perform_login(li)); 107218de8d7fSPeter Avalos case LTYPE_LOGOUT: 107318de8d7fSPeter Avalos return (utmpx_perform_logout(li)); 107418de8d7fSPeter Avalos default: 107518de8d7fSPeter Avalos logit("%s: invalid type field", __func__); 107618de8d7fSPeter Avalos return (0); 107718de8d7fSPeter Avalos } 107818de8d7fSPeter Avalos } 107918de8d7fSPeter Avalos #endif /* USE_UTMPX */ 108018de8d7fSPeter Avalos 108118de8d7fSPeter Avalos 108218de8d7fSPeter Avalos /** 108318de8d7fSPeter Avalos ** Low-level wtmp functions 108418de8d7fSPeter Avalos **/ 108518de8d7fSPeter Avalos 108618de8d7fSPeter Avalos #ifdef USE_WTMP 108718de8d7fSPeter Avalos 108818de8d7fSPeter Avalos /* 108918de8d7fSPeter Avalos * Write a wtmp entry direct to the end of the file 109018de8d7fSPeter Avalos * This is a slight modification of code in OpenBSD's logwtmp.c 109118de8d7fSPeter Avalos */ 109218de8d7fSPeter Avalos static int 109318de8d7fSPeter Avalos wtmp_write(struct logininfo *li, struct utmp *ut) 109418de8d7fSPeter Avalos { 109518de8d7fSPeter Avalos struct stat buf; 109618de8d7fSPeter Avalos int fd, ret = 1; 109718de8d7fSPeter Avalos 109818de8d7fSPeter Avalos if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 109918de8d7fSPeter Avalos logit("%s: problem writing %s: %s", __func__, 110018de8d7fSPeter Avalos WTMP_FILE, strerror(errno)); 110118de8d7fSPeter Avalos return (0); 110218de8d7fSPeter Avalos } 110318de8d7fSPeter Avalos if (fstat(fd, &buf) == 0) 110418de8d7fSPeter Avalos if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { 110518de8d7fSPeter Avalos ftruncate(fd, buf.st_size); 110618de8d7fSPeter Avalos logit("%s: problem writing %s: %s", __func__, 110718de8d7fSPeter Avalos WTMP_FILE, strerror(errno)); 110818de8d7fSPeter Avalos ret = 0; 110918de8d7fSPeter Avalos } 111018de8d7fSPeter Avalos close(fd); 111118de8d7fSPeter Avalos return (ret); 111218de8d7fSPeter Avalos } 111318de8d7fSPeter Avalos 111418de8d7fSPeter Avalos static int 111518de8d7fSPeter Avalos wtmp_perform_login(struct logininfo *li) 111618de8d7fSPeter Avalos { 111718de8d7fSPeter Avalos struct utmp ut; 111818de8d7fSPeter Avalos 111918de8d7fSPeter Avalos construct_utmp(li, &ut); 112018de8d7fSPeter Avalos return (wtmp_write(li, &ut)); 112118de8d7fSPeter Avalos } 112218de8d7fSPeter Avalos 112318de8d7fSPeter Avalos 112418de8d7fSPeter Avalos static int 112518de8d7fSPeter Avalos wtmp_perform_logout(struct logininfo *li) 112618de8d7fSPeter Avalos { 112718de8d7fSPeter Avalos struct utmp ut; 112818de8d7fSPeter Avalos 112918de8d7fSPeter Avalos construct_utmp(li, &ut); 113018de8d7fSPeter Avalos return (wtmp_write(li, &ut)); 113118de8d7fSPeter Avalos } 113218de8d7fSPeter Avalos 113318de8d7fSPeter Avalos 113418de8d7fSPeter Avalos int 113518de8d7fSPeter Avalos wtmp_write_entry(struct logininfo *li) 113618de8d7fSPeter Avalos { 113718de8d7fSPeter Avalos switch(li->type) { 113818de8d7fSPeter Avalos case LTYPE_LOGIN: 113918de8d7fSPeter Avalos return (wtmp_perform_login(li)); 114018de8d7fSPeter Avalos case LTYPE_LOGOUT: 114118de8d7fSPeter Avalos return (wtmp_perform_logout(li)); 114218de8d7fSPeter Avalos default: 114318de8d7fSPeter Avalos logit("%s: invalid type field", __func__); 114418de8d7fSPeter Avalos return (0); 114518de8d7fSPeter Avalos } 114618de8d7fSPeter Avalos } 114718de8d7fSPeter Avalos 114818de8d7fSPeter Avalos 114918de8d7fSPeter Avalos /* 115018de8d7fSPeter Avalos * Notes on fetching login data from wtmp/wtmpx 115118de8d7fSPeter Avalos * 115218de8d7fSPeter Avalos * Logouts are usually recorded with (amongst other things) a blank 115318de8d7fSPeter Avalos * username on a given tty line. However, some systems (HP-UX is one) 115418de8d7fSPeter Avalos * leave all fields set, but change the ut_type field to DEAD_PROCESS. 115518de8d7fSPeter Avalos * 115618de8d7fSPeter Avalos * Since we're only looking for logins here, we know that the username 115718de8d7fSPeter Avalos * must be set correctly. On systems that leave it in, we check for 115818de8d7fSPeter Avalos * ut_type==USER_PROCESS (indicating a login.) 115918de8d7fSPeter Avalos * 116018de8d7fSPeter Avalos * Portability: Some systems may set something other than USER_PROCESS 116118de8d7fSPeter Avalos * to indicate a login process. I don't know of any as I write. Also, 116218de8d7fSPeter Avalos * it's possible that some systems may both leave the username in 116318de8d7fSPeter Avalos * place and not have ut_type. 116418de8d7fSPeter Avalos */ 116518de8d7fSPeter Avalos 116618de8d7fSPeter Avalos /* return true if this wtmp entry indicates a login */ 116718de8d7fSPeter Avalos static int 116818de8d7fSPeter Avalos wtmp_islogin(struct logininfo *li, struct utmp *ut) 116918de8d7fSPeter Avalos { 117018de8d7fSPeter Avalos if (strncmp(li->username, ut->ut_name, 117118de8d7fSPeter Avalos MIN_SIZEOF(li->username, ut->ut_name)) == 0) { 117218de8d7fSPeter Avalos # ifdef HAVE_TYPE_IN_UTMP 117318de8d7fSPeter Avalos if (ut->ut_type & USER_PROCESS) 117418de8d7fSPeter Avalos return (1); 117518de8d7fSPeter Avalos # else 117618de8d7fSPeter Avalos return (1); 117718de8d7fSPeter Avalos # endif 117818de8d7fSPeter Avalos } 117918de8d7fSPeter Avalos return (0); 118018de8d7fSPeter Avalos } 118118de8d7fSPeter Avalos 118218de8d7fSPeter Avalos int 118318de8d7fSPeter Avalos wtmp_get_entry(struct logininfo *li) 118418de8d7fSPeter Avalos { 118518de8d7fSPeter Avalos struct stat st; 118618de8d7fSPeter Avalos struct utmp ut; 118718de8d7fSPeter Avalos int fd, found = 0; 118818de8d7fSPeter Avalos 118918de8d7fSPeter Avalos /* Clear the time entries in our logininfo */ 119018de8d7fSPeter Avalos li->tv_sec = li->tv_usec = 0; 119118de8d7fSPeter Avalos 119218de8d7fSPeter Avalos if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { 119318de8d7fSPeter Avalos logit("%s: problem opening %s: %s", __func__, 119418de8d7fSPeter Avalos WTMP_FILE, strerror(errno)); 119518de8d7fSPeter Avalos return (0); 119618de8d7fSPeter Avalos } 119718de8d7fSPeter Avalos if (fstat(fd, &st) != 0) { 119818de8d7fSPeter Avalos logit("%s: couldn't stat %s: %s", __func__, 119918de8d7fSPeter Avalos WTMP_FILE, strerror(errno)); 120018de8d7fSPeter Avalos close(fd); 120118de8d7fSPeter Avalos return (0); 120218de8d7fSPeter Avalos } 120318de8d7fSPeter Avalos 120418de8d7fSPeter Avalos /* Seek to the start of the last struct utmp */ 120518de8d7fSPeter Avalos if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { 120618de8d7fSPeter Avalos /* Looks like we've got a fresh wtmp file */ 120718de8d7fSPeter Avalos close(fd); 120818de8d7fSPeter Avalos return (0); 120918de8d7fSPeter Avalos } 121018de8d7fSPeter Avalos 121118de8d7fSPeter Avalos while (!found) { 121218de8d7fSPeter Avalos if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { 121318de8d7fSPeter Avalos logit("%s: read of %s failed: %s", __func__, 121418de8d7fSPeter Avalos WTMP_FILE, strerror(errno)); 121518de8d7fSPeter Avalos close (fd); 121618de8d7fSPeter Avalos return (0); 121718de8d7fSPeter Avalos } 121818de8d7fSPeter Avalos if (wtmp_islogin(li, &ut) ) { 121918de8d7fSPeter Avalos found = 1; 122018de8d7fSPeter Avalos /* 122118de8d7fSPeter Avalos * We've already checked for a time in struct 122218de8d7fSPeter Avalos * utmp, in login_getlast() 122318de8d7fSPeter Avalos */ 122418de8d7fSPeter Avalos # ifdef HAVE_TIME_IN_UTMP 122518de8d7fSPeter Avalos li->tv_sec = ut.ut_time; 122618de8d7fSPeter Avalos # else 122718de8d7fSPeter Avalos # if HAVE_TV_IN_UTMP 122818de8d7fSPeter Avalos li->tv_sec = ut.ut_tv.tv_sec; 122918de8d7fSPeter Avalos # endif 123018de8d7fSPeter Avalos # endif 123118de8d7fSPeter Avalos line_fullname(li->line, ut.ut_line, 123218de8d7fSPeter Avalos MIN_SIZEOF(li->line, ut.ut_line)); 123318de8d7fSPeter Avalos # ifdef HAVE_HOST_IN_UTMP 123418de8d7fSPeter Avalos strlcpy(li->hostname, ut.ut_host, 123518de8d7fSPeter Avalos MIN_SIZEOF(li->hostname, ut.ut_host)); 123618de8d7fSPeter Avalos # endif 123718de8d7fSPeter Avalos continue; 123818de8d7fSPeter Avalos } 123918de8d7fSPeter Avalos /* Seek back 2 x struct utmp */ 124018de8d7fSPeter Avalos if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { 124118de8d7fSPeter Avalos /* We've found the start of the file, so quit */ 124218de8d7fSPeter Avalos close(fd); 124318de8d7fSPeter Avalos return (0); 124418de8d7fSPeter Avalos } 124518de8d7fSPeter Avalos } 124618de8d7fSPeter Avalos 124718de8d7fSPeter Avalos /* We found an entry. Tidy up and return */ 124818de8d7fSPeter Avalos close(fd); 124918de8d7fSPeter Avalos return (1); 125018de8d7fSPeter Avalos } 125118de8d7fSPeter Avalos # endif /* USE_WTMP */ 125218de8d7fSPeter Avalos 125318de8d7fSPeter Avalos 125418de8d7fSPeter Avalos /** 125518de8d7fSPeter Avalos ** Low-level wtmpx functions 125618de8d7fSPeter Avalos **/ 125718de8d7fSPeter Avalos 125818de8d7fSPeter Avalos #ifdef USE_WTMPX 125918de8d7fSPeter Avalos /* 126018de8d7fSPeter Avalos * Write a wtmpx entry direct to the end of the file 126118de8d7fSPeter Avalos * This is a slight modification of code in OpenBSD's logwtmp.c 126218de8d7fSPeter Avalos */ 126318de8d7fSPeter Avalos static int 126418de8d7fSPeter Avalos wtmpx_write(struct logininfo *li, struct utmpx *utx) 126518de8d7fSPeter Avalos { 126618de8d7fSPeter Avalos #ifndef HAVE_UPDWTMPX 126718de8d7fSPeter Avalos struct stat buf; 126818de8d7fSPeter Avalos int fd, ret = 1; 126918de8d7fSPeter Avalos 127018de8d7fSPeter Avalos if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { 127118de8d7fSPeter Avalos logit("%s: problem opening %s: %s", __func__, 127218de8d7fSPeter Avalos WTMPX_FILE, strerror(errno)); 127318de8d7fSPeter Avalos return (0); 127418de8d7fSPeter Avalos } 127518de8d7fSPeter Avalos 127618de8d7fSPeter Avalos if (fstat(fd, &buf) == 0) 127718de8d7fSPeter Avalos if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) { 127818de8d7fSPeter Avalos ftruncate(fd, buf.st_size); 127918de8d7fSPeter Avalos logit("%s: problem writing %s: %s", __func__, 128018de8d7fSPeter Avalos WTMPX_FILE, strerror(errno)); 128118de8d7fSPeter Avalos ret = 0; 128218de8d7fSPeter Avalos } 128318de8d7fSPeter Avalos close(fd); 128418de8d7fSPeter Avalos 128518de8d7fSPeter Avalos return (ret); 128618de8d7fSPeter Avalos #else 128718de8d7fSPeter Avalos updwtmpx(WTMPX_FILE, utx); 128818de8d7fSPeter Avalos return (1); 128918de8d7fSPeter Avalos #endif 129018de8d7fSPeter Avalos } 129118de8d7fSPeter Avalos 129218de8d7fSPeter Avalos 129318de8d7fSPeter Avalos static int 129418de8d7fSPeter Avalos wtmpx_perform_login(struct logininfo *li) 129518de8d7fSPeter Avalos { 129618de8d7fSPeter Avalos struct utmpx utx; 129718de8d7fSPeter Avalos 129818de8d7fSPeter Avalos construct_utmpx(li, &utx); 129918de8d7fSPeter Avalos return (wtmpx_write(li, &utx)); 130018de8d7fSPeter Avalos } 130118de8d7fSPeter Avalos 130218de8d7fSPeter Avalos 130318de8d7fSPeter Avalos static int 130418de8d7fSPeter Avalos wtmpx_perform_logout(struct logininfo *li) 130518de8d7fSPeter Avalos { 130618de8d7fSPeter Avalos struct utmpx utx; 130718de8d7fSPeter Avalos 130818de8d7fSPeter Avalos construct_utmpx(li, &utx); 130918de8d7fSPeter Avalos return (wtmpx_write(li, &utx)); 131018de8d7fSPeter Avalos } 131118de8d7fSPeter Avalos 131218de8d7fSPeter Avalos 131318de8d7fSPeter Avalos int 131418de8d7fSPeter Avalos wtmpx_write_entry(struct logininfo *li) 131518de8d7fSPeter Avalos { 131618de8d7fSPeter Avalos switch(li->type) { 131718de8d7fSPeter Avalos case LTYPE_LOGIN: 131818de8d7fSPeter Avalos return (wtmpx_perform_login(li)); 131918de8d7fSPeter Avalos case LTYPE_LOGOUT: 132018de8d7fSPeter Avalos return (wtmpx_perform_logout(li)); 132118de8d7fSPeter Avalos default: 132218de8d7fSPeter Avalos logit("%s: invalid type field", __func__); 132318de8d7fSPeter Avalos return (0); 132418de8d7fSPeter Avalos } 132518de8d7fSPeter Avalos } 132618de8d7fSPeter Avalos 132718de8d7fSPeter Avalos /* Please see the notes above wtmp_islogin() for information about the 132818de8d7fSPeter Avalos next two functions */ 132918de8d7fSPeter Avalos 133018de8d7fSPeter Avalos /* Return true if this wtmpx entry indicates a login */ 133118de8d7fSPeter Avalos static int 133218de8d7fSPeter Avalos wtmpx_islogin(struct logininfo *li, struct utmpx *utx) 133318de8d7fSPeter Avalos { 1334856ea928SPeter Avalos if (strncmp(li->username, utx->ut_user, 1335856ea928SPeter Avalos MIN_SIZEOF(li->username, utx->ut_user)) == 0 ) { 133618de8d7fSPeter Avalos # ifdef HAVE_TYPE_IN_UTMPX 133718de8d7fSPeter Avalos if (utx->ut_type == USER_PROCESS) 133818de8d7fSPeter Avalos return (1); 133918de8d7fSPeter Avalos # else 134018de8d7fSPeter Avalos return (1); 134118de8d7fSPeter Avalos # endif 134218de8d7fSPeter Avalos } 134318de8d7fSPeter Avalos return (0); 134418de8d7fSPeter Avalos } 134518de8d7fSPeter Avalos 134618de8d7fSPeter Avalos 134718de8d7fSPeter Avalos int 134818de8d7fSPeter Avalos wtmpx_get_entry(struct logininfo *li) 134918de8d7fSPeter Avalos { 135018de8d7fSPeter Avalos struct stat st; 135118de8d7fSPeter Avalos struct utmpx utx; 135218de8d7fSPeter Avalos int fd, found=0; 135318de8d7fSPeter Avalos 135418de8d7fSPeter Avalos /* Clear the time entries */ 135518de8d7fSPeter Avalos li->tv_sec = li->tv_usec = 0; 135618de8d7fSPeter Avalos 135718de8d7fSPeter Avalos if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { 135818de8d7fSPeter Avalos logit("%s: problem opening %s: %s", __func__, 135918de8d7fSPeter Avalos WTMPX_FILE, strerror(errno)); 136018de8d7fSPeter Avalos return (0); 136118de8d7fSPeter Avalos } 136218de8d7fSPeter Avalos if (fstat(fd, &st) != 0) { 136318de8d7fSPeter Avalos logit("%s: couldn't stat %s: %s", __func__, 136418de8d7fSPeter Avalos WTMPX_FILE, strerror(errno)); 136518de8d7fSPeter Avalos close(fd); 136618de8d7fSPeter Avalos return (0); 136718de8d7fSPeter Avalos } 136818de8d7fSPeter Avalos 136918de8d7fSPeter Avalos /* Seek to the start of the last struct utmpx */ 137018de8d7fSPeter Avalos if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { 137118de8d7fSPeter Avalos /* probably a newly rotated wtmpx file */ 137218de8d7fSPeter Avalos close(fd); 137318de8d7fSPeter Avalos return (0); 137418de8d7fSPeter Avalos } 137518de8d7fSPeter Avalos 137618de8d7fSPeter Avalos while (!found) { 137718de8d7fSPeter Avalos if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { 137818de8d7fSPeter Avalos logit("%s: read of %s failed: %s", __func__, 137918de8d7fSPeter Avalos WTMPX_FILE, strerror(errno)); 138018de8d7fSPeter Avalos close (fd); 138118de8d7fSPeter Avalos return (0); 138218de8d7fSPeter Avalos } 138318de8d7fSPeter Avalos /* 138418de8d7fSPeter Avalos * Logouts are recorded as a blank username on a particular 138518de8d7fSPeter Avalos * line. So, we just need to find the username in struct utmpx 138618de8d7fSPeter Avalos */ 138718de8d7fSPeter Avalos if (wtmpx_islogin(li, &utx)) { 138818de8d7fSPeter Avalos found = 1; 138918de8d7fSPeter Avalos # if defined(HAVE_TV_IN_UTMPX) 139018de8d7fSPeter Avalos li->tv_sec = utx.ut_tv.tv_sec; 139118de8d7fSPeter Avalos # elif defined(HAVE_TIME_IN_UTMPX) 139218de8d7fSPeter Avalos li->tv_sec = utx.ut_time; 139318de8d7fSPeter Avalos # endif 139418de8d7fSPeter Avalos line_fullname(li->line, utx.ut_line, sizeof(li->line)); 139518de8d7fSPeter Avalos # if defined(HAVE_HOST_IN_UTMPX) 139618de8d7fSPeter Avalos strlcpy(li->hostname, utx.ut_host, 139718de8d7fSPeter Avalos MIN_SIZEOF(li->hostname, utx.ut_host)); 139818de8d7fSPeter Avalos # endif 139918de8d7fSPeter Avalos continue; 140018de8d7fSPeter Avalos } 140118de8d7fSPeter Avalos if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { 140218de8d7fSPeter Avalos close(fd); 140318de8d7fSPeter Avalos return (0); 140418de8d7fSPeter Avalos } 140518de8d7fSPeter Avalos } 140618de8d7fSPeter Avalos 140718de8d7fSPeter Avalos close(fd); 140818de8d7fSPeter Avalos return (1); 140918de8d7fSPeter Avalos } 141018de8d7fSPeter Avalos #endif /* USE_WTMPX */ 141118de8d7fSPeter Avalos 141218de8d7fSPeter Avalos /** 141318de8d7fSPeter Avalos ** Low-level libutil login() functions 141418de8d7fSPeter Avalos **/ 141518de8d7fSPeter Avalos 141618de8d7fSPeter Avalos #ifdef USE_LOGIN 141718de8d7fSPeter Avalos static int 141818de8d7fSPeter Avalos syslogin_perform_login(struct logininfo *li) 141918de8d7fSPeter Avalos { 142018de8d7fSPeter Avalos struct utmp *ut; 142118de8d7fSPeter Avalos 142218de8d7fSPeter Avalos ut = xmalloc(sizeof(*ut)); 142318de8d7fSPeter Avalos construct_utmp(li, ut); 142418de8d7fSPeter Avalos login(ut); 142518de8d7fSPeter Avalos free(ut); 142618de8d7fSPeter Avalos 142718de8d7fSPeter Avalos return (1); 142818de8d7fSPeter Avalos } 142918de8d7fSPeter Avalos 143018de8d7fSPeter Avalos static int 143118de8d7fSPeter Avalos syslogin_perform_logout(struct logininfo *li) 143218de8d7fSPeter Avalos { 143318de8d7fSPeter Avalos # ifdef HAVE_LOGOUT 143418de8d7fSPeter Avalos char line[UT_LINESIZE]; 143518de8d7fSPeter Avalos 143618de8d7fSPeter Avalos (void)line_stripname(line, li->line, sizeof(line)); 143718de8d7fSPeter Avalos 143818de8d7fSPeter Avalos if (!logout(line)) 143918de8d7fSPeter Avalos logit("%s: logout() returned an error", __func__); 144018de8d7fSPeter Avalos # ifdef HAVE_LOGWTMP 144118de8d7fSPeter Avalos else 144218de8d7fSPeter Avalos logwtmp(line, "", ""); 144318de8d7fSPeter Avalos # endif 144418de8d7fSPeter Avalos /* FIXME: (ATL - if the need arises) What to do if we have 144518de8d7fSPeter Avalos * login, but no logout? what if logout but no logwtmp? All 144618de8d7fSPeter Avalos * routines are in libutil so they should all be there, 144718de8d7fSPeter Avalos * but... */ 144818de8d7fSPeter Avalos # endif 144918de8d7fSPeter Avalos return (1); 145018de8d7fSPeter Avalos } 145118de8d7fSPeter Avalos 145218de8d7fSPeter Avalos int 145318de8d7fSPeter Avalos syslogin_write_entry(struct logininfo *li) 145418de8d7fSPeter Avalos { 145518de8d7fSPeter Avalos switch (li->type) { 145618de8d7fSPeter Avalos case LTYPE_LOGIN: 145718de8d7fSPeter Avalos return (syslogin_perform_login(li)); 145818de8d7fSPeter Avalos case LTYPE_LOGOUT: 145918de8d7fSPeter Avalos return (syslogin_perform_logout(li)); 146018de8d7fSPeter Avalos default: 146118de8d7fSPeter Avalos logit("%s: Invalid type field", __func__); 146218de8d7fSPeter Avalos return (0); 146318de8d7fSPeter Avalos } 146418de8d7fSPeter Avalos } 146518de8d7fSPeter Avalos #endif /* USE_LOGIN */ 146618de8d7fSPeter Avalos 146718de8d7fSPeter Avalos /* end of file log-syslogin.c */ 146818de8d7fSPeter Avalos 146918de8d7fSPeter Avalos /** 147018de8d7fSPeter Avalos ** Low-level lastlog functions 147118de8d7fSPeter Avalos **/ 147218de8d7fSPeter Avalos 147318de8d7fSPeter Avalos #ifdef USE_LASTLOG 147418de8d7fSPeter Avalos 1475cb5eb4f1SPeter Avalos #if !defined(LASTLOG_WRITE_PUTUTXLINE) || !defined(HAVE_GETLASTLOGXBYNAME) 1476cb5eb4f1SPeter Avalos /* open the file (using filemode) and seek to the login entry */ 147718de8d7fSPeter Avalos static int 1478cb5eb4f1SPeter Avalos lastlog_openseek(struct logininfo *li, int *fd, int filemode) 147918de8d7fSPeter Avalos { 1480cb5eb4f1SPeter Avalos off_t offset; 1481cb5eb4f1SPeter Avalos char lastlog_file[1024]; 148218de8d7fSPeter Avalos struct stat st; 148318de8d7fSPeter Avalos 148418de8d7fSPeter Avalos if (stat(LASTLOG_FILE, &st) != 0) { 148518de8d7fSPeter Avalos logit("%s: Couldn't stat %s: %s", __func__, 148618de8d7fSPeter Avalos LASTLOG_FILE, strerror(errno)); 148718de8d7fSPeter Avalos return (0); 148818de8d7fSPeter Avalos } 1489cb5eb4f1SPeter Avalos if (S_ISDIR(st.st_mode)) { 149018de8d7fSPeter Avalos snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", 149118de8d7fSPeter Avalos LASTLOG_FILE, li->username); 1492cb5eb4f1SPeter Avalos } else if (S_ISREG(st.st_mode)) { 1493cb5eb4f1SPeter Avalos strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); 1494cb5eb4f1SPeter Avalos } else { 149518de8d7fSPeter Avalos logit("%s: %.100s is not a file or directory!", __func__, 149618de8d7fSPeter Avalos LASTLOG_FILE); 149718de8d7fSPeter Avalos return (0); 149818de8d7fSPeter Avalos } 149918de8d7fSPeter Avalos 150018de8d7fSPeter Avalos *fd = open(lastlog_file, filemode, 0600); 150118de8d7fSPeter Avalos if (*fd < 0) { 150218de8d7fSPeter Avalos debug("%s: Couldn't open %s: %s", __func__, 150318de8d7fSPeter Avalos lastlog_file, strerror(errno)); 150418de8d7fSPeter Avalos return (0); 150518de8d7fSPeter Avalos } 150618de8d7fSPeter Avalos 1507cb5eb4f1SPeter Avalos if (S_ISREG(st.st_mode)) { 150818de8d7fSPeter Avalos /* find this uid's offset in the lastlog file */ 15099f304aafSPeter Avalos offset = (off_t) ((u_long)li->uid * sizeof(struct lastlog)); 151018de8d7fSPeter Avalos 151118de8d7fSPeter Avalos if (lseek(*fd, offset, SEEK_SET) != offset) { 151218de8d7fSPeter Avalos logit("%s: %s->lseek(): %s", __func__, 151318de8d7fSPeter Avalos lastlog_file, strerror(errno)); 15149f304aafSPeter Avalos close(*fd); 151518de8d7fSPeter Avalos return (0); 151618de8d7fSPeter Avalos } 151718de8d7fSPeter Avalos } 151818de8d7fSPeter Avalos 151918de8d7fSPeter Avalos return (1); 152018de8d7fSPeter Avalos } 1521cb5eb4f1SPeter Avalos #endif /* !LASTLOG_WRITE_PUTUTXLINE || !HAVE_GETLASTLOGXBYNAME */ 152218de8d7fSPeter Avalos 1523cb5eb4f1SPeter Avalos #ifdef LASTLOG_WRITE_PUTUTXLINE 1524cb5eb4f1SPeter Avalos int 1525cb5eb4f1SPeter Avalos lastlog_write_entry(struct logininfo *li) 1526cb5eb4f1SPeter Avalos { 1527cb5eb4f1SPeter Avalos switch(li->type) { 1528cb5eb4f1SPeter Avalos case LTYPE_LOGIN: 1529cb5eb4f1SPeter Avalos return 1; /* lastlog written by pututxline */ 1530cb5eb4f1SPeter Avalos default: 1531cb5eb4f1SPeter Avalos logit("lastlog_write_entry: Invalid type field"); 1532cb5eb4f1SPeter Avalos return 0; 1533cb5eb4f1SPeter Avalos } 1534cb5eb4f1SPeter Avalos } 1535cb5eb4f1SPeter Avalos #else /* LASTLOG_WRITE_PUTUTXLINE */ 1536cb5eb4f1SPeter Avalos int 1537cb5eb4f1SPeter Avalos lastlog_write_entry(struct logininfo *li) 153818de8d7fSPeter Avalos { 153918de8d7fSPeter Avalos struct lastlog last; 154018de8d7fSPeter Avalos int fd; 154118de8d7fSPeter Avalos 1542cb5eb4f1SPeter Avalos switch(li->type) { 1543cb5eb4f1SPeter Avalos case LTYPE_LOGIN: 154418de8d7fSPeter Avalos /* create our struct lastlog */ 1545cb5eb4f1SPeter Avalos memset(&last, '\0', sizeof(last)); 1546cb5eb4f1SPeter Avalos line_stripname(last.ll_line, li->line, sizeof(last.ll_line)); 1547cb5eb4f1SPeter Avalos strlcpy(last.ll_host, li->hostname, 1548cb5eb4f1SPeter Avalos MIN_SIZEOF(last.ll_host, li->hostname)); 1549cb5eb4f1SPeter Avalos last.ll_time = li->tv_sec; 155018de8d7fSPeter Avalos 155118de8d7fSPeter Avalos if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) 155218de8d7fSPeter Avalos return (0); 155318de8d7fSPeter Avalos 155418de8d7fSPeter Avalos /* write the entry */ 155518de8d7fSPeter Avalos if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) { 155618de8d7fSPeter Avalos close(fd); 155718de8d7fSPeter Avalos logit("%s: Error writing to %s: %s", __func__, 155818de8d7fSPeter Avalos LASTLOG_FILE, strerror(errno)); 155918de8d7fSPeter Avalos return (0); 156018de8d7fSPeter Avalos } 156118de8d7fSPeter Avalos 156218de8d7fSPeter Avalos close(fd); 156318de8d7fSPeter Avalos return (1); 156418de8d7fSPeter Avalos default: 156518de8d7fSPeter Avalos logit("%s: Invalid type field", __func__); 156618de8d7fSPeter Avalos return (0); 156718de8d7fSPeter Avalos } 156818de8d7fSPeter Avalos } 1569cb5eb4f1SPeter Avalos #endif /* LASTLOG_WRITE_PUTUTXLINE */ 157018de8d7fSPeter Avalos 1571cb5eb4f1SPeter Avalos #ifdef HAVE_GETLASTLOGXBYNAME 1572cb5eb4f1SPeter Avalos int 1573cb5eb4f1SPeter Avalos lastlog_get_entry(struct logininfo *li) 157418de8d7fSPeter Avalos { 1575cb5eb4f1SPeter Avalos struct lastlogx l, *ll; 157618de8d7fSPeter Avalos 1577cb5eb4f1SPeter Avalos if ((ll = getlastlogxbyname(li->username, &l)) == NULL) { 1578cb5eb4f1SPeter Avalos memset(&l, '\0', sizeof(l)); 1579cb5eb4f1SPeter Avalos ll = &l; 1580cb5eb4f1SPeter Avalos } 1581cb5eb4f1SPeter Avalos line_fullname(li->line, ll->ll_line, sizeof(li->line)); 1582cb5eb4f1SPeter Avalos strlcpy(li->hostname, ll->ll_host, 1583cb5eb4f1SPeter Avalos MIN_SIZEOF(li->hostname, ll->ll_host)); 1584cb5eb4f1SPeter Avalos li->tv_sec = ll->ll_tv.tv_sec; 1585cb5eb4f1SPeter Avalos li->tv_usec = ll->ll_tv.tv_usec; 1586cb5eb4f1SPeter Avalos return (1); 1587cb5eb4f1SPeter Avalos } 1588cb5eb4f1SPeter Avalos #else /* HAVE_GETLASTLOGXBYNAME */ 158918de8d7fSPeter Avalos int 159018de8d7fSPeter Avalos lastlog_get_entry(struct logininfo *li) 159118de8d7fSPeter Avalos { 159218de8d7fSPeter Avalos struct lastlog last; 159318de8d7fSPeter Avalos int fd, ret; 159418de8d7fSPeter Avalos 159518de8d7fSPeter Avalos if (!lastlog_openseek(li, &fd, O_RDONLY)) 159618de8d7fSPeter Avalos return (0); 159718de8d7fSPeter Avalos 159818de8d7fSPeter Avalos ret = atomicio(read, fd, &last, sizeof(last)); 159918de8d7fSPeter Avalos close(fd); 160018de8d7fSPeter Avalos 160118de8d7fSPeter Avalos switch (ret) { 160218de8d7fSPeter Avalos case 0: 160318de8d7fSPeter Avalos memset(&last, '\0', sizeof(last)); 160418de8d7fSPeter Avalos /* FALLTHRU */ 160518de8d7fSPeter Avalos case sizeof(last): 1606cb5eb4f1SPeter Avalos line_fullname(li->line, last.ll_line, sizeof(li->line)); 1607cb5eb4f1SPeter Avalos strlcpy(li->hostname, last.ll_host, 1608cb5eb4f1SPeter Avalos MIN_SIZEOF(li->hostname, last.ll_host)); 1609cb5eb4f1SPeter Avalos li->tv_sec = last.ll_time; 161018de8d7fSPeter Avalos return (1); 161118de8d7fSPeter Avalos case -1: 161218de8d7fSPeter Avalos error("%s: Error reading from %s: %s", __func__, 161318de8d7fSPeter Avalos LASTLOG_FILE, strerror(errno)); 161418de8d7fSPeter Avalos return (0); 161518de8d7fSPeter Avalos default: 161618de8d7fSPeter Avalos error("%s: Error reading from %s: Expecting %d, got %d", 161718de8d7fSPeter Avalos __func__, LASTLOG_FILE, (int)sizeof(last), ret); 161818de8d7fSPeter Avalos return (0); 161918de8d7fSPeter Avalos } 162018de8d7fSPeter Avalos 162118de8d7fSPeter Avalos /* NOTREACHED */ 162218de8d7fSPeter Avalos return (0); 162318de8d7fSPeter Avalos } 1624cb5eb4f1SPeter Avalos #endif /* HAVE_GETLASTLOGXBYNAME */ 162518de8d7fSPeter Avalos #endif /* USE_LASTLOG */ 162618de8d7fSPeter Avalos 1627856ea928SPeter Avalos #if defined(USE_UTMPX) && defined(HAVE_SETUTXDB) && \ 1628856ea928SPeter Avalos defined(UTXDB_LASTLOGIN) && defined(HAVE_GETUTXUSER) 1629856ea928SPeter Avalos int 1630856ea928SPeter Avalos utmpx_get_entry(struct logininfo *li) 1631856ea928SPeter Avalos { 1632856ea928SPeter Avalos struct utmpx *utx; 1633856ea928SPeter Avalos 1634856ea928SPeter Avalos if (setutxdb(UTXDB_LASTLOGIN, NULL) != 0) 1635856ea928SPeter Avalos return (0); 1636856ea928SPeter Avalos utx = getutxuser(li->username); 1637856ea928SPeter Avalos if (utx == NULL) { 1638856ea928SPeter Avalos endutxent(); 1639856ea928SPeter Avalos return (0); 1640856ea928SPeter Avalos } 1641856ea928SPeter Avalos 1642856ea928SPeter Avalos line_fullname(li->line, utx->ut_line, 1643856ea928SPeter Avalos MIN_SIZEOF(li->line, utx->ut_line)); 1644856ea928SPeter Avalos strlcpy(li->hostname, utx->ut_host, 1645856ea928SPeter Avalos MIN_SIZEOF(li->hostname, utx->ut_host)); 1646856ea928SPeter Avalos li->tv_sec = utx->ut_tv.tv_sec; 1647856ea928SPeter Avalos li->tv_usec = utx->ut_tv.tv_usec; 1648856ea928SPeter Avalos endutxent(); 1649856ea928SPeter Avalos return (1); 1650856ea928SPeter Avalos } 1651856ea928SPeter Avalos #endif /* USE_UTMPX && HAVE_SETUTXDB && UTXDB_LASTLOGIN && HAVE_GETUTXUSER */ 1652856ea928SPeter Avalos 165318de8d7fSPeter Avalos #ifdef USE_BTMP 165418de8d7fSPeter Avalos /* 165518de8d7fSPeter Avalos * Logs failed login attempts in _PATH_BTMP if that exists. 165618de8d7fSPeter Avalos * The most common login failure is to give password instead of username. 165718de8d7fSPeter Avalos * So the _PATH_BTMP file checked for the correct permission, so that 165818de8d7fSPeter Avalos * only root can read it. 165918de8d7fSPeter Avalos */ 166018de8d7fSPeter Avalos 166118de8d7fSPeter Avalos void 1662664f4763Szrj record_failed_login(struct ssh *ssh, const char *username, const char *hostname, 166318de8d7fSPeter Avalos const char *ttyn) 166418de8d7fSPeter Avalos { 166518de8d7fSPeter Avalos int fd; 166618de8d7fSPeter Avalos struct utmp ut; 166718de8d7fSPeter Avalos struct sockaddr_storage from; 166818de8d7fSPeter Avalos socklen_t fromlen = sizeof(from); 166918de8d7fSPeter Avalos struct sockaddr_in *a4; 167018de8d7fSPeter Avalos struct sockaddr_in6 *a6; 167118de8d7fSPeter Avalos time_t t; 167218de8d7fSPeter Avalos struct stat fst; 167318de8d7fSPeter Avalos 167418de8d7fSPeter Avalos if (geteuid() != 0) 167518de8d7fSPeter Avalos return; 167618de8d7fSPeter Avalos if ((fd = open(_PATH_BTMP, O_WRONLY | O_APPEND)) < 0) { 167718de8d7fSPeter Avalos debug("Unable to open the btmp file %s: %s", _PATH_BTMP, 167818de8d7fSPeter Avalos strerror(errno)); 167918de8d7fSPeter Avalos return; 168018de8d7fSPeter Avalos } 168118de8d7fSPeter Avalos if (fstat(fd, &fst) < 0) { 168218de8d7fSPeter Avalos logit("%s: fstat of %s failed: %s", __func__, _PATH_BTMP, 168318de8d7fSPeter Avalos strerror(errno)); 168418de8d7fSPeter Avalos goto out; 168518de8d7fSPeter Avalos } 16869f304aafSPeter Avalos if((fst.st_mode & (S_IXGRP | S_IRWXO)) || (fst.st_uid != 0)){ 168718de8d7fSPeter Avalos logit("Excess permission or bad ownership on file %s", 168818de8d7fSPeter Avalos _PATH_BTMP); 168918de8d7fSPeter Avalos goto out; 169018de8d7fSPeter Avalos } 169118de8d7fSPeter Avalos 169218de8d7fSPeter Avalos memset(&ut, 0, sizeof(ut)); 169318de8d7fSPeter Avalos /* strncpy because we don't necessarily want nul termination */ 169418de8d7fSPeter Avalos strncpy(ut.ut_user, username, sizeof(ut.ut_user)); 169518de8d7fSPeter Avalos strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line)); 169618de8d7fSPeter Avalos 169718de8d7fSPeter Avalos time(&t); 169818de8d7fSPeter Avalos ut.ut_time = t; /* ut_time is not always a time_t */ 169918de8d7fSPeter Avalos ut.ut_type = LOGIN_PROCESS; 170018de8d7fSPeter Avalos ut.ut_pid = getpid(); 170118de8d7fSPeter Avalos 170218de8d7fSPeter Avalos /* strncpy because we don't necessarily want nul termination */ 170318de8d7fSPeter Avalos strncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); 170418de8d7fSPeter Avalos 1705664f4763Szrj if (ssh_packet_connection_is_on_socket(ssh) && 1706664f4763Szrj getpeername(ssh_packet_get_connection_in(ssh), 170718de8d7fSPeter Avalos (struct sockaddr *)&from, &fromlen) == 0) { 170818de8d7fSPeter Avalos ipv64_normalise_mapped(&from, &fromlen); 170918de8d7fSPeter Avalos if (from.ss_family == AF_INET) { 171018de8d7fSPeter Avalos a4 = (struct sockaddr_in *)&from; 171118de8d7fSPeter Avalos memcpy(&ut.ut_addr, &(a4->sin_addr), 171218de8d7fSPeter Avalos MIN_SIZEOF(ut.ut_addr, a4->sin_addr)); 171318de8d7fSPeter Avalos } 171418de8d7fSPeter Avalos #ifdef HAVE_ADDR_V6_IN_UTMP 171518de8d7fSPeter Avalos if (from.ss_family == AF_INET6) { 171618de8d7fSPeter Avalos a6 = (struct sockaddr_in6 *)&from; 171718de8d7fSPeter Avalos memcpy(&ut.ut_addr_v6, &(a6->sin6_addr), 171818de8d7fSPeter Avalos MIN_SIZEOF(ut.ut_addr_v6, a6->sin6_addr)); 171918de8d7fSPeter Avalos } 172018de8d7fSPeter Avalos #endif 172118de8d7fSPeter Avalos } 172218de8d7fSPeter Avalos 172318de8d7fSPeter Avalos if (atomicio(vwrite, fd, &ut, sizeof(ut)) != sizeof(ut)) 172418de8d7fSPeter Avalos error("Failed to write to %s: %s", _PATH_BTMP, 172518de8d7fSPeter Avalos strerror(errno)); 172618de8d7fSPeter Avalos 172718de8d7fSPeter Avalos out: 172818de8d7fSPeter Avalos close(fd); 172918de8d7fSPeter Avalos } 173018de8d7fSPeter Avalos #endif /* USE_BTMP */ 1731