xref: /dflybsd-src/crypto/openssh/loginrec.c (revision ba1276acd1c8c22d225b1bcf370a14c878644f44)
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
45*ba1276acSMatthew Dillon  * 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"
184ee116499SAntonio 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 
194*ba1276acSMatthew Dillon #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
login_login(struct logininfo * li)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
login_logout(struct logininfo * li)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
login_get_lastlog_time(const uid_t uid)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 *
login_get_lastlog(struct logininfo * li,const uid_t uid)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
login_alloc_entry(pid_t pid,const char * username,const char * hostname,const char * line)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
login_free_entry(struct logininfo * li)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
login_init_entry(struct logininfo * li,pid_t pid,const char * username,const char * hostname,const char * line)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
login_set_current_time(struct logininfo * li)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
login_set_addr(struct logininfo * li,const struct sockaddr * sa,const unsigned int sa_size)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
login_write(struct logininfo * li)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
login_utmp_only(struct logininfo * li)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
getlast_entry(struct logininfo * li)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 *
line_fullname(char * dst,const char * src,u_int dstsize)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 *
line_stripname(char * dst,const char * src,int dstsize)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 *
line_abbrevname(char * dst,const char * src,int dstsize)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
set_utmp_time(struct logininfo * li,struct utmp * ut)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
construct_utmp(struct logininfo * li,struct utmp * ut)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
set_utmpx_time(struct logininfo * li,struct utmpx * utx)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
construct_utmpx(struct logininfo * li,struct utmpx * utx)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 */
805ee116499SAntonio 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
utmp_write_library(struct logininfo * li,struct utmp * ut)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
utmp_write_direct(struct logininfo * li,struct utmp * ut)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
utmp_perform_login(struct logininfo * li)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
utmp_perform_logout(struct logininfo * li)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
utmp_write_entry(struct logininfo * li)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
utmpx_write_library(struct logininfo * li,struct utmpx * utx)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
utmpx_write_direct(struct logininfo * li,struct utmpx * utx)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
utmpx_perform_login(struct logininfo * li)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
utmpx_perform_logout(struct logininfo * li)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
utmpx_write_entry(struct logininfo * li)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
wtmp_write(struct logininfo * li,struct utmp * ut)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
wtmp_perform_login(struct logininfo * li)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
wtmp_perform_logout(struct logininfo * li)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
wtmp_write_entry(struct logininfo * li)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
wtmp_islogin(struct logininfo * li,struct utmp * ut)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
wtmp_get_entry(struct logininfo * li)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
wtmpx_write(struct logininfo * li,struct utmpx * utx)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
wtmpx_perform_login(struct logininfo * li)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
wtmpx_perform_logout(struct logininfo * li)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
wtmpx_write_entry(struct logininfo * li)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
wtmpx_islogin(struct logininfo * li,struct utmpx * utx)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
wtmpx_get_entry(struct logininfo * li)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
syslogin_perform_login(struct logininfo * li)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
syslogin_perform_logout(struct logininfo * li)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
syslogin_write_entry(struct logininfo * li)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
lastlog_openseek(struct logininfo * li,int * fd,int filemode)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
lastlog_write_entry(struct logininfo * li)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
lastlog_write_entry(struct logininfo * li)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
lastlog_get_entry(struct logininfo * li)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
lastlog_get_entry(struct logininfo * li)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
utmpx_get_entry(struct logininfo * li)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
record_failed_login(struct ssh * ssh,const char * username,const char * hostname,const char * ttyn)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