xref: /dflybsd-src/crypto/openssh/loginrec.c (revision 0cbfa66cdb87e23928a110d9b02839f403e32c11)
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