loginrec.c revision 113911
198937Sdes/*
298937Sdes * Copyright (c) 2000 Andre Lucas.  All rights reserved.
398937Sdes * Portions copyright (c) 1998 Todd C. Miller
498937Sdes * Portions copyright (c) 1996 Jason Downs
598937Sdes * Portions copyright (c) 1996 Theo de Raadt
698937Sdes *
798937Sdes * Redistribution and use in source and binary forms, with or without
898937Sdes * modification, are permitted provided that the following conditions
998937Sdes * are met:
1098937Sdes * 1. Redistributions of source code must retain the above copyright
1198937Sdes *    notice, this list of conditions and the following disclaimer.
1298937Sdes * 2. Redistributions in binary form must reproduce the above copyright
1398937Sdes *    notice, this list of conditions and the following disclaimer in the
1498937Sdes *    documentation and/or other materials provided with the distribution.
1598937Sdes * 3. All advertising materials mentioning features or use of this software
1698937Sdes *    must display the following acknowledgement:
1798937Sdes *      This product includes software developed by Markus Friedl.
1898937Sdes * 4. The name of the author may not be used to endorse or promote products
1998937Sdes *    derived from this software without specific prior written permission.
2098937Sdes *
2198937Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2298937Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2398937Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2498937Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2598937Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2698937Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2798937Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2898937Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2998937Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3098937Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3198937Sdes */
3298937Sdes
3398937Sdes/**
3498937Sdes ** loginrec.c:  platform-independent login recording and lastlog retrieval
3598937Sdes **/
3698937Sdes
3798937Sdes/*
3898937Sdes  The new login code explained
3998937Sdes  ============================
4098937Sdes
4198937Sdes  This code attempts to provide a common interface to login recording
4298937Sdes  (utmp and friends) and last login time retrieval.
4398937Sdes
4498937Sdes  Its primary means of achieving this is to use 'struct logininfo', a
4598937Sdes  union of all the useful fields in the various different types of
4698937Sdes  system login record structures one finds on UNIX variants.
4798937Sdes
4898937Sdes  We depend on autoconf to define which recording methods are to be
4998937Sdes  used, and which fields are contained in the relevant data structures
5098937Sdes  on the local system. Many C preprocessor symbols affect which code
5198937Sdes  gets compiled here.
5298937Sdes
5398937Sdes  The code is designed to make it easy to modify a particular
5498937Sdes  recording method, without affecting other methods nor requiring so
5598937Sdes  many nested conditional compilation blocks as were commonplace in
5698937Sdes  the old code.
5798937Sdes
5898937Sdes  For login recording, we try to use the local system's libraries as
5998937Sdes  these are clearly most likely to work correctly. For utmp systems
6098937Sdes  this usually means login() and logout() or setutent() etc., probably
6198937Sdes  in libutil, along with logwtmp() etc. On these systems, we fall back
6298937Sdes  to writing the files directly if we have to, though this method
6398937Sdes  requires very thorough testing so we do not corrupt local auditing
6498937Sdes  information. These files and their access methods are very system
6598937Sdes  specific indeed.
6698937Sdes
6798937Sdes  For utmpx systems, the corresponding library functions are
6898937Sdes  setutxent() etc. To the author's knowledge, all utmpx systems have
6998937Sdes  these library functions and so no direct write is attempted. If such
7098937Sdes  a system exists and needs support, direct analogues of the [uw]tmp
7198937Sdes  code should suffice.
7298937Sdes
7398937Sdes  Retrieving the time of last login ('lastlog') is in some ways even
7498937Sdes  more problemmatic than login recording. Some systems provide a
7598937Sdes  simple table of all users which we seek based on uid and retrieve a
7698937Sdes  relatively standard structure. Others record the same information in
7798937Sdes  a directory with a separate file, and others don't record the
7898937Sdes  information separately at all. For systems in the latter category,
7998937Sdes  we look backwards in the wtmp or wtmpx file for the last login entry
8098937Sdes  for our user. Naturally this is slower and on busy systems could
8198937Sdes  incur a significant performance penalty.
8298937Sdes
8398937Sdes  Calling the new code
8498937Sdes  --------------------
8598937Sdes
8698937Sdes  In OpenSSH all login recording and retrieval is performed in
8798937Sdes  login.c. Here you'll find working examples. Also, in the logintest.c
8898937Sdes  program there are more examples.
8998937Sdes
9098937Sdes  Internal handler calling method
9198937Sdes  -------------------------------
9298937Sdes
9398937Sdes  When a call is made to login_login() or login_logout(), both
9498937Sdes  routines set a struct logininfo flag defining which action (log in,
9598937Sdes  or log out) is to be taken. They both then call login_write(), which
9698937Sdes  calls whichever of the many structure-specific handlers autoconf
9798937Sdes  selects for the local system.
9898937Sdes
9998937Sdes  The handlers themselves handle system data structure specifics. Both
10098937Sdes  struct utmp and struct utmpx have utility functions (see
10198937Sdes  construct_utmp*()) to try to make it simpler to add extra systems
10298937Sdes  that introduce new features to either structure.
10398937Sdes
10498937Sdes  While it may seem terribly wasteful to replicate so much similar
10598937Sdes  code for each method, experience has shown that maintaining code to
10698937Sdes  write both struct utmp and utmpx in one function, whilst maintaining
10798937Sdes  support for all systems whether they have library support or not, is
10898937Sdes  a difficult and time-consuming task.
10998937Sdes
11098937Sdes  Lastlog support proceeds similarly. Functions login_get_lastlog()
11198937Sdes  (and its OpenSSH-tuned friend login_get_lastlog_time()) call
11298937Sdes  getlast_entry(), which tries one of three methods to find the last
11398937Sdes  login time. It uses local system lastlog support if it can,
11498937Sdes  otherwise it tries wtmp or wtmpx before giving up and returning 0,
11598937Sdes  meaning "tilt".
11698937Sdes
11798937Sdes  Maintenance
11898937Sdes  -----------
11998937Sdes
12098937Sdes  In many cases it's possible to tweak autoconf to select the correct
12198937Sdes  methods for a particular platform, either by improving the detection
12298937Sdes  code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE
12398937Sdes  symbols for the platform.
12498937Sdes
12598937Sdes  Use logintest to check which symbols are defined before modifying
12698937Sdes  configure.ac and loginrec.c. (You have to build logintest yourself
12798937Sdes  with 'make logintest' as it's not built by default.)
12898937Sdes
12998937Sdes  Otherwise, patches to the specific method(s) are very helpful!
13098937Sdes
13198937Sdes*/
13298937Sdes
13398937Sdes/**
13498937Sdes ** TODO:
13598937Sdes **   homegrown ttyslot()
13698937Sdes **   test, test, test
13798937Sdes **
13898937Sdes ** Platform status:
13998937Sdes ** ----------------
14098937Sdes **
14198937Sdes ** Known good:
14298937Sdes **   Linux (Redhat 6.2, Debian)
14398937Sdes **   Solaris
14498937Sdes **   HP-UX 10.20 (gcc only)
14598937Sdes **   IRIX
14698937Sdes **   NeXT - M68k/HPPA/Sparc (4.2/3.3)
14798937Sdes **
14898937Sdes ** Testing required: Please send reports!
14998937Sdes **   NetBSD
15098937Sdes **   HP-UX 11
15198937Sdes **   AIX
15298937Sdes **
15398937Sdes ** Platforms with known problems:
15498937Sdes **   Some variants of Slackware Linux
15598937Sdes **
15698937Sdes **/
15798937Sdes
15898937Sdes#include "includes.h"
15998937Sdes
16098937Sdes#include "ssh.h"
16198937Sdes#include "xmalloc.h"
16298937Sdes#include "loginrec.h"
16398937Sdes#include "log.h"
16498937Sdes#include "atomicio.h"
16598937Sdes
166113911SdesRCSID("$Id: loginrec.c,v 1.47 2003/03/10 00:23:07 djm Exp $");
16799768SdesRCSID("$FreeBSD: head/crypto/openssh/loginrec.c 113911 2003-04-23 17:13:13Z des $");
16898937Sdes
16998937Sdes#ifdef HAVE_UTIL_H
17098937Sdes#  include <util.h>
17198937Sdes#endif
17298937Sdes
17398937Sdes#ifdef HAVE_LIBUTIL_H
17498937Sdes#   include <libutil.h>
17598937Sdes#endif
17698937Sdes
17798937Sdes/**
17898937Sdes ** prototypes for helper functions in this file
17998937Sdes **/
18098937Sdes
18198937Sdes#if HAVE_UTMP_H
18298937Sdesvoid set_utmp_time(struct logininfo *li, struct utmp *ut);
18398937Sdesvoid construct_utmp(struct logininfo *li, struct utmp *ut);
18498937Sdes#endif
18598937Sdes
18698937Sdes#ifdef HAVE_UTMPX_H
18798937Sdesvoid set_utmpx_time(struct logininfo *li, struct utmpx *ut);
18898937Sdesvoid construct_utmpx(struct logininfo *li, struct utmpx *ut);
18998937Sdes#endif
19098937Sdes
19198937Sdesint utmp_write_entry(struct logininfo *li);
19298937Sdesint utmpx_write_entry(struct logininfo *li);
19398937Sdesint wtmp_write_entry(struct logininfo *li);
19498937Sdesint wtmpx_write_entry(struct logininfo *li);
19598937Sdesint lastlog_write_entry(struct logininfo *li);
19698937Sdesint syslogin_write_entry(struct logininfo *li);
19798937Sdes
19898937Sdesint getlast_entry(struct logininfo *li);
19998937Sdesint lastlog_get_entry(struct logininfo *li);
20098937Sdesint wtmp_get_entry(struct logininfo *li);
20198937Sdesint wtmpx_get_entry(struct logininfo *li);
20298937Sdes
20398937Sdes/* pick the shortest string */
20498937Sdes#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
20598937Sdes
20698937Sdes/**
20798937Sdes ** platform-independent login functions
20898937Sdes **/
20998937Sdes
21098937Sdes/* login_login(struct logininfo *)     -Record a login
21198937Sdes *
21298937Sdes * Call with a pointer to a struct logininfo initialised with
21398937Sdes * login_init_entry() or login_alloc_entry()
21498937Sdes *
21598937Sdes * Returns:
21698937Sdes *  >0 if successful
21798937Sdes *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
21898937Sdes */
21998937Sdesint
22098937Sdeslogin_login (struct logininfo *li)
22198937Sdes{
22298937Sdes	li->type = LTYPE_LOGIN;
22398937Sdes	return login_write(li);
22498937Sdes}
22598937Sdes
22698937Sdes
22798937Sdes/* login_logout(struct logininfo *)     - Record a logout
22898937Sdes *
22998937Sdes * Call as with login_login()
23098937Sdes *
23198937Sdes * Returns:
23298937Sdes *  >0 if successful
23398937Sdes *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
23498937Sdes */
23598937Sdesint
23698937Sdeslogin_logout(struct logininfo *li)
23798937Sdes{
23898937Sdes	li->type = LTYPE_LOGOUT;
23998937Sdes	return login_write(li);
24098937Sdes}
24198937Sdes
24298937Sdes/* login_get_lastlog_time(int)           - Retrieve the last login time
24398937Sdes *
24498937Sdes * Retrieve the last login time for the given uid. Will try to use the
24598937Sdes * system lastlog facilities if they are available, but will fall back
24698937Sdes * to looking in wtmp/wtmpx if necessary
24798937Sdes *
24898937Sdes * Returns:
24998937Sdes *   0 on failure, or if user has never logged in
25098937Sdes *   Time in seconds from the epoch if successful
25198937Sdes *
25298937Sdes * Useful preprocessor symbols:
25398937Sdes *   DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
25498937Sdes *                    info
25598937Sdes *   USE_LASTLOG: If set, indicates the presence of system lastlog
25698937Sdes *                facilities. If this and DISABLE_LASTLOG are not set,
25798937Sdes *                try to retrieve lastlog information from wtmp/wtmpx.
25898937Sdes */
25998937Sdesunsigned int
26098937Sdeslogin_get_lastlog_time(const int uid)
26198937Sdes{
26298937Sdes	struct logininfo li;
26398937Sdes
26498937Sdes	if (login_get_lastlog(&li, uid))
26598937Sdes		return li.tv_sec;
26698937Sdes	else
26798937Sdes		return 0;
26898937Sdes}
26998937Sdes
27098937Sdes/* login_get_lastlog(struct logininfo *, int)   - Retrieve a lastlog entry
27198937Sdes *
27298937Sdes * Retrieve a logininfo structure populated (only partially) with
27398937Sdes * information from the system lastlog data, or from wtmp/wtmpx if no
27498937Sdes * system lastlog information exists.
27598937Sdes *
27698937Sdes * Note this routine must be given a pre-allocated logininfo.
27798937Sdes *
27898937Sdes * Returns:
27998937Sdes *  >0: A pointer to your struct logininfo if successful
28098937Sdes *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
28198937Sdes *
28298937Sdes */
28398937Sdesstruct logininfo *
28498937Sdeslogin_get_lastlog(struct logininfo *li, const int uid)
28598937Sdes{
28698937Sdes	struct passwd *pw;
28798937Sdes
28898937Sdes	memset(li, '\0', sizeof(*li));
28998937Sdes	li->uid = uid;
29098937Sdes
29198937Sdes	/*
29298937Sdes	 * If we don't have a 'real' lastlog, we need the username to
29398937Sdes	 * reliably search wtmp(x) for the last login (see
29498937Sdes	 * wtmp_get_entry().)
29598937Sdes	 */
29698937Sdes	pw = getpwuid(uid);
29798937Sdes	if (pw == NULL)
29898937Sdes		fatal("login_get_lastlog: Cannot find account for uid %i", uid);
29998937Sdes
30098937Sdes	/* No MIN_SIZEOF here - we absolutely *must not* truncate the
30198937Sdes	 * username */
30298937Sdes	strlcpy(li->username, pw->pw_name, sizeof(li->username));
30398937Sdes
30498937Sdes	if (getlast_entry(li))
30598937Sdes		return li;
30698937Sdes	else
30798937Sdes		return NULL;
30898937Sdes}
30998937Sdes
31098937Sdes
31198937Sdes/* login_alloc_entry(int, char*, char*, char*)    - Allocate and initialise
31298937Sdes *                                                  a logininfo structure
31398937Sdes *
31498937Sdes * This function creates a new struct logininfo, a data structure
31598937Sdes * meant to carry the information required to portably record login info.
31698937Sdes *
31798937Sdes * Returns a pointer to a newly created struct logininfo. If memory
31898937Sdes * allocation fails, the program halts.
31998937Sdes */
32098937Sdesstruct
32198937Sdeslogininfo *login_alloc_entry(int pid, const char *username,
32298937Sdes			     const char *hostname, const char *line)
32398937Sdes{
32498937Sdes	struct logininfo *newli;
32598937Sdes
32698937Sdes	newli = (struct logininfo *) xmalloc (sizeof(*newli));
32798937Sdes	(void)login_init_entry(newli, pid, username, hostname, line);
32898937Sdes	return newli;
32998937Sdes}
33098937Sdes
33198937Sdes
33298937Sdes/* login_free_entry(struct logininfo *)    - free struct memory */
33398937Sdesvoid
33498937Sdeslogin_free_entry(struct logininfo *li)
33598937Sdes{
33698937Sdes	xfree(li);
33798937Sdes}
33898937Sdes
33998937Sdes
34098937Sdes/* login_init_entry(struct logininfo *, int, char*, char*, char*)
34198937Sdes *                                        - initialise a struct logininfo
34298937Sdes *
34398937Sdes * Populates a new struct logininfo, a data structure meant to carry
34498937Sdes * the information required to portably record login info.
34598937Sdes *
34698937Sdes * Returns: 1
34798937Sdes */
34898937Sdesint
34998937Sdeslogin_init_entry(struct logininfo *li, int pid, const char *username,
35098937Sdes		 const char *hostname, const char *line)
35198937Sdes{
35298937Sdes	struct passwd *pw;
35398937Sdes
35498937Sdes	memset(li, 0, sizeof(*li));
35598937Sdes
35698937Sdes	li->pid = pid;
35798937Sdes
35898937Sdes	/* set the line information */
35998937Sdes	if (line)
36098937Sdes		line_fullname(li->line, line, sizeof(li->line));
36198937Sdes
36298937Sdes	if (username) {
36398937Sdes		strlcpy(li->username, username, sizeof(li->username));
36498937Sdes		pw = getpwnam(li->username);
36598937Sdes		if (pw == NULL)
36698937Sdes			fatal("login_init_entry: Cannot find user \"%s\"", li->username);
36798937Sdes		li->uid = pw->pw_uid;
36898937Sdes	}
36998937Sdes
37098937Sdes	if (hostname)
37198937Sdes		strlcpy(li->hostname, hostname, sizeof(li->hostname));
37298937Sdes
37398937Sdes	return 1;
37498937Sdes}
37598937Sdes
37698937Sdes/* login_set_current_time(struct logininfo *)    - set the current time
37798937Sdes *
37898937Sdes * Set the current time in a logininfo structure. This function is
37998937Sdes * meant to eliminate the need to deal with system dependencies for
38098937Sdes * time handling.
38198937Sdes */
38298937Sdesvoid
38398937Sdeslogin_set_current_time(struct logininfo *li)
38498937Sdes{
38598937Sdes	struct timeval tv;
38698937Sdes
38798937Sdes	gettimeofday(&tv, NULL);
38898937Sdes
38998937Sdes	li->tv_sec = tv.tv_sec;
39098937Sdes	li->tv_usec = tv.tv_usec;
39198937Sdes}
39298937Sdes
39398937Sdes/* copy a sockaddr_* into our logininfo */
39498937Sdesvoid
39598937Sdeslogin_set_addr(struct logininfo *li, const struct sockaddr *sa,
39698937Sdes	       const unsigned int sa_size)
39798937Sdes{
39898937Sdes	unsigned int bufsize = sa_size;
39998937Sdes
40098937Sdes	/* make sure we don't overrun our union */
40198937Sdes	if (sizeof(li->hostaddr) < sa_size)
40298937Sdes		bufsize = sizeof(li->hostaddr);
40398937Sdes
40498937Sdes	memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
40598937Sdes}
40698937Sdes
40798937Sdes
40898937Sdes/**
40998937Sdes ** login_write: Call low-level recording functions based on autoconf
41098937Sdes ** results
41198937Sdes **/
41298937Sdesint
41398937Sdeslogin_write (struct logininfo *li)
41498937Sdes{
41598937Sdes#ifndef HAVE_CYGWIN
41698937Sdes	if ((int)geteuid() != 0) {
41798937Sdes	  log("Attempt to write login records by non-root user (aborting)");
41898937Sdes	  return 1;
41998937Sdes	}
42098937Sdes#endif
42198937Sdes
42298937Sdes	/* set the timestamp */
42398937Sdes	login_set_current_time(li);
42498937Sdes#ifdef USE_LOGIN
42598937Sdes	syslogin_write_entry(li);
42698937Sdes#endif
42798937Sdes#ifdef USE_LASTLOG
42898937Sdes	if (li->type == LTYPE_LOGIN) {
42998937Sdes		lastlog_write_entry(li);
43098937Sdes	}
43198937Sdes#endif
43298937Sdes#ifdef USE_UTMP
43398937Sdes	utmp_write_entry(li);
43498937Sdes#endif
43598937Sdes#ifdef USE_WTMP
43698937Sdes	wtmp_write_entry(li);
43798937Sdes#endif
43898937Sdes#ifdef USE_UTMPX
43998937Sdes	utmpx_write_entry(li);
44098937Sdes#endif
44198937Sdes#ifdef USE_WTMPX
44298937Sdes	wtmpx_write_entry(li);
44398937Sdes#endif
44498937Sdes	return 0;
44598937Sdes}
44698937Sdes
44798937Sdes#ifdef LOGIN_NEEDS_UTMPX
44898937Sdesint
44998937Sdeslogin_utmp_only(struct logininfo *li)
45098937Sdes{
45198937Sdes	li->type = LTYPE_LOGIN;
45298937Sdes	login_set_current_time(li);
45398937Sdes# ifdef USE_UTMP
45498937Sdes	utmp_write_entry(li);
45598937Sdes# endif
45698937Sdes# ifdef USE_WTMP
45798937Sdes	wtmp_write_entry(li);
45898937Sdes# endif
45998937Sdes# ifdef USE_UTMPX
46098937Sdes	utmpx_write_entry(li);
46198937Sdes# endif
46298937Sdes# ifdef USE_WTMPX
46398937Sdes	wtmpx_write_entry(li);
46498937Sdes# endif
46598937Sdes	return 0;
46698937Sdes}
46798937Sdes#endif
46898937Sdes
46998937Sdes/**
47098937Sdes ** getlast_entry: Call low-level functions to retrieve the last login
47198937Sdes **                time.
47298937Sdes **/
47398937Sdes
47498937Sdes/* take the uid in li and return the last login time */
47598937Sdesint
47698937Sdesgetlast_entry(struct logininfo *li)
47798937Sdes{
47898937Sdes#ifdef USE_LASTLOG
47998937Sdes	return(lastlog_get_entry(li));
48098937Sdes#else /* !USE_LASTLOG */
48198937Sdes
48298937Sdes#ifdef DISABLE_LASTLOG
48398937Sdes	/* On some systems we shouldn't even try to obtain last login
48498937Sdes	 * time, e.g. AIX */
48598937Sdes	return 0;
48698937Sdes# else /* DISABLE_LASTLOG */
48798937Sdes	/* Try to retrieve the last login time from wtmp */
48898937Sdes#  if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
48998937Sdes	/* retrieve last login time from utmp */
49098937Sdes	return (wtmp_get_entry(li));
49198937Sdes#  else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */
49298937Sdes	/* If wtmp isn't available, try wtmpx */
49398937Sdes#   if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
49498937Sdes	/* retrieve last login time from utmpx */
49598937Sdes	return (wtmpx_get_entry(li));
49698937Sdes#   else
49798937Sdes	/* Give up: No means of retrieving last login time */
49898937Sdes	return 0;
49998937Sdes#   endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
50098937Sdes#  endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
50198937Sdes# endif /* DISABLE_LASTLOG */
50298937Sdes#endif /* USE_LASTLOG */
50398937Sdes}
50498937Sdes
50598937Sdes
50698937Sdes
50798937Sdes/*
50898937Sdes * 'line' string utility functions
50998937Sdes *
51098937Sdes * These functions process the 'line' string into one of three forms:
51198937Sdes *
51298937Sdes * 1. The full filename (including '/dev')
51398937Sdes * 2. The stripped name (excluding '/dev')
51498937Sdes * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
51598937Sdes *                               /dev/pts/1  -> ts/1 )
51698937Sdes *
51798937Sdes * Form 3 is used on some systems to identify a .tmp.? entry when
51898937Sdes * attempting to remove it. Typically both addition and removal is
51998937Sdes * performed by one application - say, sshd - so as long as the choice
52098937Sdes * uniquely identifies a terminal it's ok.
52198937Sdes */
52298937Sdes
52398937Sdes
52498937Sdes/* line_fullname(): add the leading '/dev/' if it doesn't exist make
52598937Sdes * sure dst has enough space, if not just copy src (ugh) */
52698937Sdeschar *
52798937Sdesline_fullname(char *dst, const char *src, int dstsize)
52898937Sdes{
52998937Sdes	memset(dst, '\0', dstsize);
53098937Sdes	if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) {
53198937Sdes		strlcpy(dst, src, dstsize);
53298937Sdes	} else {
53398937Sdes		strlcpy(dst, "/dev/", dstsize);
53498937Sdes		strlcat(dst, src, dstsize);
53598937Sdes	}
53698937Sdes	return dst;
53798937Sdes}
53898937Sdes
53998937Sdes/* line_stripname(): strip the leading '/dev' if it exists, return dst */
54098937Sdeschar *
54198937Sdesline_stripname(char *dst, const char *src, int dstsize)
54298937Sdes{
54398937Sdes	memset(dst, '\0', dstsize);
54498937Sdes	if (strncmp(src, "/dev/", 5) == 0)
54598937Sdes		strlcpy(dst, src + 5, dstsize);
54698937Sdes	else
54798937Sdes		strlcpy(dst, src, dstsize);
54898937Sdes	return dst;
54998937Sdes}
55098937Sdes
55198937Sdes/* line_abbrevname(): Return the abbreviated (usually four-character)
55298937Sdes * form of the line (Just use the last <dstsize> characters of the
55398937Sdes * full name.)
55498937Sdes *
55598937Sdes * NOTE: use strncpy because we do NOT necessarily want zero
55698937Sdes * termination */
55798937Sdeschar *
55898937Sdesline_abbrevname(char *dst, const char *src, int dstsize)
55998937Sdes{
56098937Sdes	size_t len;
56198937Sdes
56298937Sdes	memset(dst, '\0', dstsize);
56398937Sdes
56498937Sdes	/* Always skip prefix if present */
56598937Sdes	if (strncmp(src, "/dev/", 5) == 0)
56698937Sdes		src += 5;
56798937Sdes
56898937Sdes#ifdef WITH_ABBREV_NO_TTY
56998937Sdes	if (strncmp(src, "tty", 3) == 0)
57098937Sdes		src += 3;
57198937Sdes#endif
57298937Sdes
57398937Sdes	len = strlen(src);
57498937Sdes
57598937Sdes	if (len > 0) {
57698937Sdes		if (((int)len - dstsize) > 0)
57798937Sdes			src +=  ((int)len - dstsize);
57898937Sdes
57998937Sdes		/* note: _don't_ change this to strlcpy */
58098937Sdes		strncpy(dst, src, (size_t)dstsize);
58198937Sdes	}
58298937Sdes
58398937Sdes	return dst;
58498937Sdes}
58598937Sdes
58698937Sdes/**
58798937Sdes ** utmp utility functions
58898937Sdes **
58998937Sdes ** These functions manipulate struct utmp, taking system differences
59098937Sdes ** into account.
59198937Sdes **/
59298937Sdes
59398937Sdes#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
59498937Sdes
59598937Sdes/* build the utmp structure */
59698937Sdesvoid
59798937Sdesset_utmp_time(struct logininfo *li, struct utmp *ut)
59898937Sdes{
59998937Sdes# ifdef HAVE_TV_IN_UTMP
60098937Sdes	ut->ut_tv.tv_sec = li->tv_sec;
60198937Sdes	ut->ut_tv.tv_usec = li->tv_usec;
60298937Sdes# else
60398937Sdes#  ifdef HAVE_TIME_IN_UTMP
60498937Sdes	ut->ut_time = li->tv_sec;
60598937Sdes#  endif
60698937Sdes# endif
60798937Sdes}
60898937Sdes
60998937Sdesvoid
61098937Sdesconstruct_utmp(struct logininfo *li,
61198937Sdes		    struct utmp *ut)
61298937Sdes{
613113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP
614113911Sdes	struct sockaddr_in6 *sa6;
615113911Sdes#  endif
61698937Sdes	memset(ut, '\0', sizeof(*ut));
61798937Sdes
61898937Sdes	/* First fill out fields used for both logins and logouts */
61998937Sdes
62098937Sdes# ifdef HAVE_ID_IN_UTMP
62198937Sdes	line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
62298937Sdes# endif
62398937Sdes
62498937Sdes# ifdef HAVE_TYPE_IN_UTMP
62598937Sdes	/* This is done here to keep utmp constants out of struct logininfo */
62698937Sdes	switch (li->type) {
62798937Sdes	case LTYPE_LOGIN:
62898937Sdes		ut->ut_type = USER_PROCESS;
629106130Sdes#ifdef _UNICOS
63098937Sdes		cray_set_tmpdir(ut);
63198937Sdes#endif
63298937Sdes		break;
63398937Sdes	case LTYPE_LOGOUT:
63498937Sdes		ut->ut_type = DEAD_PROCESS;
635106130Sdes#ifdef _UNICOS
63698937Sdes		cray_retain_utmp(ut, li->pid);
63798937Sdes#endif
63898937Sdes		break;
63998937Sdes	}
64098937Sdes# endif
64198937Sdes	set_utmp_time(li, ut);
64298937Sdes
64398937Sdes	line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
64498937Sdes
64598937Sdes# ifdef HAVE_PID_IN_UTMP
64698937Sdes	ut->ut_pid = li->pid;
64798937Sdes# endif
64898937Sdes
64998937Sdes	/* If we're logging out, leave all other fields blank */
65098937Sdes	if (li->type == LTYPE_LOGOUT)
65198937Sdes	  return;
65298937Sdes
65398937Sdes	/*
65498937Sdes	 * These fields are only used when logging in, and are blank
65598937Sdes	 * for logouts.
65698937Sdes	 */
65798937Sdes
65898937Sdes	/* Use strncpy because we don't necessarily want null termination */
65998937Sdes	strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
66098937Sdes# ifdef HAVE_HOST_IN_UTMP
66199768Sdes	realhostname_sa(ut->ut_host, sizeof ut->ut_host,
66299768Sdes	    &li->hostaddr.sa, li->hostaddr.sa.sa_len);
66398937Sdes# endif
66498937Sdes# ifdef HAVE_ADDR_IN_UTMP
66598937Sdes	/* this is just a 32-bit IP address */
66698937Sdes	if (li->hostaddr.sa.sa_family == AF_INET)
66798937Sdes		ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
66898937Sdes# endif
669113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP
670113911Sdes	/* this is just a 128-bit IPv6 address */
671113911Sdes	if (li->hostaddr.sa.sa_family == AF_INET6) {
672113911Sdes		sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
673113911Sdes		memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
674113911Sdes		if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
675113911Sdes			ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
676113911Sdes			ut->ut_addr_v6[1] = 0;
677113911Sdes			ut->ut_addr_v6[2] = 0;
678113911Sdes			ut->ut_addr_v6[3] = 0;
679113911Sdes		}
680113911Sdes	}
681113911Sdes# endif
68298937Sdes}
68398937Sdes#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
68498937Sdes
68598937Sdes/**
68698937Sdes ** utmpx utility functions
68798937Sdes **
68898937Sdes ** These functions manipulate struct utmpx, accounting for system
68998937Sdes ** variations.
69098937Sdes **/
69198937Sdes
69298937Sdes#if defined(USE_UTMPX) || defined (USE_WTMPX)
69398937Sdes/* build the utmpx structure */
69498937Sdesvoid
69598937Sdesset_utmpx_time(struct logininfo *li, struct utmpx *utx)
69698937Sdes{
69798937Sdes# ifdef HAVE_TV_IN_UTMPX
69898937Sdes	utx->ut_tv.tv_sec = li->tv_sec;
69998937Sdes	utx->ut_tv.tv_usec = li->tv_usec;
70098937Sdes# else /* HAVE_TV_IN_UTMPX */
70198937Sdes#  ifdef HAVE_TIME_IN_UTMPX
70298937Sdes	utx->ut_time = li->tv_sec;
70398937Sdes#  endif /* HAVE_TIME_IN_UTMPX */
70498937Sdes# endif /* HAVE_TV_IN_UTMPX */
70598937Sdes}
70698937Sdes
70798937Sdesvoid
70898937Sdesconstruct_utmpx(struct logininfo *li, struct utmpx *utx)
70998937Sdes{
710113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP
711113911Sdes	struct sockaddr_in6 *sa6;
712113911Sdes#  endif
71398937Sdes	memset(utx, '\0', sizeof(*utx));
71498937Sdes# ifdef HAVE_ID_IN_UTMPX
71598937Sdes	line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
71698937Sdes# endif
71798937Sdes
71898937Sdes	/* this is done here to keep utmp constants out of loginrec.h */
71998937Sdes	switch (li->type) {
72098937Sdes	case LTYPE_LOGIN:
72198937Sdes		utx->ut_type = USER_PROCESS;
72298937Sdes		break;
72398937Sdes	case LTYPE_LOGOUT:
72498937Sdes		utx->ut_type = DEAD_PROCESS;
72598937Sdes		break;
72698937Sdes	}
72798937Sdes	line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
72898937Sdes	set_utmpx_time(li, utx);
72998937Sdes	utx->ut_pid = li->pid;
73098937Sdes	/* strncpy(): Don't necessarily want null termination */
73198937Sdes	strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
73298937Sdes
73398937Sdes	if (li->type == LTYPE_LOGOUT)
73498937Sdes		return;
73598937Sdes
73698937Sdes	/*
73798937Sdes	 * These fields are only used when logging in, and are blank
73898937Sdes	 * for logouts.
73998937Sdes	 */
74098937Sdes
74198937Sdes# ifdef HAVE_HOST_IN_UTMPX
74298937Sdes	strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
74398937Sdes# endif
74498937Sdes# ifdef HAVE_ADDR_IN_UTMPX
74598937Sdes	/* this is just a 32-bit IP address */
74698937Sdes	if (li->hostaddr.sa.sa_family == AF_INET)
74798937Sdes		utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
74898937Sdes# endif
749113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP
750113911Sdes	/* this is just a 128-bit IPv6 address */
751113911Sdes	if (li->hostaddr.sa.sa_family == AF_INET6) {
752113911Sdes		sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
753113911Sdes		memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
754113911Sdes		if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
755113911Sdes			ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
756113911Sdes			ut->ut_addr_v6[1] = 0;
757113911Sdes			ut->ut_addr_v6[2] = 0;
758113911Sdes			ut->ut_addr_v6[3] = 0;
759113911Sdes		}
760113911Sdes	}
761113911Sdes# endif
76298937Sdes# ifdef HAVE_SYSLEN_IN_UTMPX
76398937Sdes	/* ut_syslen is the length of the utx_host string */
76498937Sdes	utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
76598937Sdes# endif
76698937Sdes}
76798937Sdes#endif /* USE_UTMPX || USE_WTMPX */
76898937Sdes
76998937Sdes/**
77098937Sdes ** Low-level utmp functions
77198937Sdes **/
77298937Sdes
77398937Sdes/* FIXME: (ATL) utmp_write_direct needs testing */
77498937Sdes#ifdef USE_UTMP
77598937Sdes
77698937Sdes/* if we can, use pututline() etc. */
77798937Sdes# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
77898937Sdes	defined(HAVE_PUTUTLINE)
77998937Sdes#  define UTMP_USE_LIBRARY
78098937Sdes# endif
78198937Sdes
78298937Sdes
78398937Sdes/* write a utmp entry with the system's help (pututline() and pals) */
78498937Sdes# ifdef UTMP_USE_LIBRARY
78598937Sdesstatic int
78698937Sdesutmp_write_library(struct logininfo *li, struct utmp *ut)
78798937Sdes{
78898937Sdes	setutent();
78998937Sdes	pututline(ut);
79098937Sdes
79198937Sdes#  ifdef HAVE_ENDUTENT
79298937Sdes	endutent();
79398937Sdes#  endif
79498937Sdes	return 1;
79598937Sdes}
79698937Sdes# else /* UTMP_USE_LIBRARY */
79798937Sdes
79898937Sdes/* write a utmp entry direct to the file */
79998937Sdes/* This is a slightly modification of code in OpenBSD's login.c */
80098937Sdesstatic int
80198937Sdesutmp_write_direct(struct logininfo *li, struct utmp *ut)
80298937Sdes{
80398937Sdes	struct utmp old_ut;
80498937Sdes	register int fd;
80598937Sdes	int tty;
80698937Sdes
80798937Sdes	/* FIXME: (ATL) ttyslot() needs local implementation */
80898937Sdes
80998937Sdes#if defined(HAVE_GETTTYENT)
81098937Sdes	register struct ttyent *ty;
81198937Sdes
81298937Sdes	tty=0;
81398937Sdes
81498937Sdes	setttyent();
81598937Sdes	while ((struct ttyent *)0 != (ty = getttyent())) {
81698937Sdes		tty++;
81798937Sdes		if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
81898937Sdes			break;
81998937Sdes	}
82098937Sdes	endttyent();
82198937Sdes
82298937Sdes	if((struct ttyent *)0 == ty) {
82398937Sdes		log("utmp_write_entry: tty not found");
82498937Sdes		return(1);
82598937Sdes	}
82698937Sdes#else /* FIXME */
82798937Sdes
82898937Sdes	tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
82998937Sdes
83098937Sdes#endif /* HAVE_GETTTYENT */
83198937Sdes
83298937Sdes	if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
83398937Sdes		(void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
83498937Sdes		/*
83598937Sdes		 * Prevent luser from zero'ing out ut_host.
83698937Sdes		 * If the new ut_line is empty but the old one is not
83798937Sdes		 * and ut_line and ut_name match, preserve the old ut_line.
83898937Sdes		 */
83998937Sdes		if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
84098937Sdes			(ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
84198937Sdes			(strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
84298937Sdes			(strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
84398937Sdes			(void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
84498937Sdes		}
84598937Sdes
84698937Sdes		(void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
84798937Sdes		if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut))
84898937Sdes			log("utmp_write_direct: error writing %s: %s",
84998937Sdes			    UTMP_FILE, strerror(errno));
85098937Sdes
85198937Sdes		(void)close(fd);
85298937Sdes		return 1;
85398937Sdes	} else {
85498937Sdes		return 0;
85598937Sdes	}
85698937Sdes}
85798937Sdes# endif /* UTMP_USE_LIBRARY */
85898937Sdes
85998937Sdesstatic int
86098937Sdesutmp_perform_login(struct logininfo *li)
86198937Sdes{
86298937Sdes	struct utmp ut;
86398937Sdes
86498937Sdes	construct_utmp(li, &ut);
86598937Sdes# ifdef UTMP_USE_LIBRARY
86698937Sdes	if (!utmp_write_library(li, &ut)) {
86798937Sdes		log("utmp_perform_login: utmp_write_library() failed");
86898937Sdes		return 0;
86998937Sdes	}
87098937Sdes# else
87198937Sdes	if (!utmp_write_direct(li, &ut)) {
87298937Sdes		log("utmp_perform_login: utmp_write_direct() failed");
87398937Sdes		return 0;
87498937Sdes	}
87598937Sdes# endif
87698937Sdes	return 1;
87798937Sdes}
87898937Sdes
87998937Sdes
88098937Sdesstatic int
88198937Sdesutmp_perform_logout(struct logininfo *li)
88298937Sdes{
88398937Sdes	struct utmp ut;
88498937Sdes
88598937Sdes	construct_utmp(li, &ut);
88698937Sdes# ifdef UTMP_USE_LIBRARY
88798937Sdes	if (!utmp_write_library(li, &ut)) {
88898937Sdes		log("utmp_perform_logout: utmp_write_library() failed");
88998937Sdes		return 0;
89098937Sdes	}
89198937Sdes# else
89298937Sdes	if (!utmp_write_direct(li, &ut)) {
89398937Sdes		log("utmp_perform_logout: utmp_write_direct() failed");
89498937Sdes		return 0;
89598937Sdes	}
89698937Sdes# endif
89798937Sdes	return 1;
89898937Sdes}
89998937Sdes
90098937Sdes
90198937Sdesint
90298937Sdesutmp_write_entry(struct logininfo *li)
90398937Sdes{
90498937Sdes	switch(li->type) {
90598937Sdes	case LTYPE_LOGIN:
90698937Sdes		return utmp_perform_login(li);
90798937Sdes
90898937Sdes	case LTYPE_LOGOUT:
90998937Sdes		return utmp_perform_logout(li);
91098937Sdes
91198937Sdes	default:
91298937Sdes		log("utmp_write_entry: invalid type field");
91398937Sdes		return 0;
91498937Sdes	}
91598937Sdes}
91698937Sdes#endif /* USE_UTMP */
91798937Sdes
91898937Sdes
91998937Sdes/**
92098937Sdes ** Low-level utmpx functions
92198937Sdes **/
92298937Sdes
92398937Sdes/* not much point if we don't want utmpx entries */
92498937Sdes#ifdef USE_UTMPX
92598937Sdes
92698937Sdes/* if we have the wherewithall, use pututxline etc. */
92798937Sdes# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
92898937Sdes	defined(HAVE_PUTUTXLINE)
92998937Sdes#  define UTMPX_USE_LIBRARY
93098937Sdes# endif
93198937Sdes
93298937Sdes
93398937Sdes/* write a utmpx entry with the system's help (pututxline() and pals) */
93498937Sdes# ifdef UTMPX_USE_LIBRARY
93598937Sdesstatic int
93698937Sdesutmpx_write_library(struct logininfo *li, struct utmpx *utx)
93798937Sdes{
93898937Sdes	setutxent();
93998937Sdes	pututxline(utx);
94098937Sdes
94198937Sdes#  ifdef HAVE_ENDUTXENT
94298937Sdes	endutxent();
94398937Sdes#  endif
94498937Sdes	return 1;
94598937Sdes}
94698937Sdes
94798937Sdes# else /* UTMPX_USE_LIBRARY */
94898937Sdes
94998937Sdes/* write a utmp entry direct to the file */
95098937Sdesstatic int
95198937Sdesutmpx_write_direct(struct logininfo *li, struct utmpx *utx)
95298937Sdes{
95398937Sdes	log("utmpx_write_direct: not implemented!");
95498937Sdes	return 0;
95598937Sdes}
95698937Sdes# endif /* UTMPX_USE_LIBRARY */
95798937Sdes
95898937Sdesstatic int
95998937Sdesutmpx_perform_login(struct logininfo *li)
96098937Sdes{
96198937Sdes	struct utmpx utx;
96298937Sdes
96398937Sdes	construct_utmpx(li, &utx);
96498937Sdes# ifdef UTMPX_USE_LIBRARY
96598937Sdes	if (!utmpx_write_library(li, &utx)) {
96698937Sdes		log("utmpx_perform_login: utmp_write_library() failed");
96798937Sdes		return 0;
96898937Sdes	}
96998937Sdes# else
97098937Sdes	if (!utmpx_write_direct(li, &ut)) {
97198937Sdes		log("utmpx_perform_login: utmp_write_direct() failed");
97298937Sdes		return 0;
97398937Sdes	}
97498937Sdes# endif
97598937Sdes	return 1;
97698937Sdes}
97798937Sdes
97898937Sdes
97998937Sdesstatic int
98098937Sdesutmpx_perform_logout(struct logininfo *li)
98198937Sdes{
98298937Sdes	struct utmpx utx;
98398937Sdes
98498937Sdes	construct_utmpx(li, &utx);
98598937Sdes# ifdef HAVE_ID_IN_UTMPX
98698937Sdes	line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
98798937Sdes# endif
98898937Sdes# ifdef HAVE_TYPE_IN_UTMPX
98998937Sdes	utx.ut_type = DEAD_PROCESS;
99098937Sdes# endif
99198937Sdes
99298937Sdes# ifdef UTMPX_USE_LIBRARY
99398937Sdes	utmpx_write_library(li, &utx);
99498937Sdes# else
99598937Sdes	utmpx_write_direct(li, &utx);
99698937Sdes# endif
99798937Sdes	return 1;
99898937Sdes}
99998937Sdes
100098937Sdesint
100198937Sdesutmpx_write_entry(struct logininfo *li)
100298937Sdes{
100398937Sdes	switch(li->type) {
100498937Sdes	case LTYPE_LOGIN:
100598937Sdes		return utmpx_perform_login(li);
100698937Sdes	case LTYPE_LOGOUT:
100798937Sdes		return utmpx_perform_logout(li);
100898937Sdes	default:
100998937Sdes		log("utmpx_write_entry: invalid type field");
101098937Sdes		return 0;
101198937Sdes	}
101298937Sdes}
101398937Sdes#endif /* USE_UTMPX */
101498937Sdes
101598937Sdes
101698937Sdes/**
101798937Sdes ** Low-level wtmp functions
101898937Sdes **/
101998937Sdes
102098937Sdes#ifdef USE_WTMP
102198937Sdes
102298937Sdes/* write a wtmp entry direct to the end of the file */
102398937Sdes/* This is a slight modification of code in OpenBSD's logwtmp.c */
102498937Sdesstatic int
102598937Sdeswtmp_write(struct logininfo *li, struct utmp *ut)
102698937Sdes{
102798937Sdes	struct stat buf;
102898937Sdes	int fd, ret = 1;
102998937Sdes
103098937Sdes	if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
103198937Sdes		log("wtmp_write: problem writing %s: %s",
103298937Sdes		    WTMP_FILE, strerror(errno));
103398937Sdes		return 0;
103498937Sdes	}
103598937Sdes	if (fstat(fd, &buf) == 0)
103698937Sdes		if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
103798937Sdes			ftruncate(fd, buf.st_size);
103898937Sdes			log("wtmp_write: problem writing %s: %s",
103998937Sdes			    WTMP_FILE, strerror(errno));
104098937Sdes			ret = 0;
104198937Sdes		}
104298937Sdes	(void)close(fd);
104398937Sdes	return ret;
104498937Sdes}
104598937Sdes
104698937Sdesstatic int
104798937Sdeswtmp_perform_login(struct logininfo *li)
104898937Sdes{
104998937Sdes	struct utmp ut;
105098937Sdes
105198937Sdes	construct_utmp(li, &ut);
105298937Sdes	return wtmp_write(li, &ut);
105398937Sdes}
105498937Sdes
105598937Sdes
105698937Sdesstatic int
105798937Sdeswtmp_perform_logout(struct logininfo *li)
105898937Sdes{
105998937Sdes	struct utmp ut;
106098937Sdes
106198937Sdes	construct_utmp(li, &ut);
106298937Sdes	return wtmp_write(li, &ut);
106398937Sdes}
106498937Sdes
106598937Sdes
106698937Sdesint
106798937Sdeswtmp_write_entry(struct logininfo *li)
106898937Sdes{
106998937Sdes	switch(li->type) {
107098937Sdes	case LTYPE_LOGIN:
107198937Sdes		return wtmp_perform_login(li);
107298937Sdes	case LTYPE_LOGOUT:
107398937Sdes		return wtmp_perform_logout(li);
107498937Sdes	default:
107598937Sdes		log("wtmp_write_entry: invalid type field");
107698937Sdes		return 0;
107798937Sdes	}
107898937Sdes}
107998937Sdes
108098937Sdes
108198937Sdes/* Notes on fetching login data from wtmp/wtmpx
108298937Sdes *
108398937Sdes * Logouts are usually recorded with (amongst other things) a blank
108498937Sdes * username on a given tty line.  However, some systems (HP-UX is one)
108598937Sdes * leave all fields set, but change the ut_type field to DEAD_PROCESS.
108698937Sdes *
108798937Sdes * Since we're only looking for logins here, we know that the username
108898937Sdes * must be set correctly. On systems that leave it in, we check for
108998937Sdes * ut_type==USER_PROCESS (indicating a login.)
109098937Sdes *
109198937Sdes * Portability: Some systems may set something other than USER_PROCESS
109298937Sdes * to indicate a login process. I don't know of any as I write. Also,
109398937Sdes * it's possible that some systems may both leave the username in
109498937Sdes * place and not have ut_type.
109598937Sdes */
109698937Sdes
109798937Sdes/* return true if this wtmp entry indicates a login */
109898937Sdesstatic int
109998937Sdeswtmp_islogin(struct logininfo *li, struct utmp *ut)
110098937Sdes{
110198937Sdes	if (strncmp(li->username, ut->ut_name,
110298937Sdes		MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
110398937Sdes# ifdef HAVE_TYPE_IN_UTMP
110498937Sdes		if (ut->ut_type & USER_PROCESS)
110598937Sdes			return 1;
110698937Sdes# else
110798937Sdes		return 1;
110898937Sdes# endif
110998937Sdes	}
111098937Sdes	return 0;
111198937Sdes}
111298937Sdes
111398937Sdesint
111498937Sdeswtmp_get_entry(struct logininfo *li)
111598937Sdes{
111698937Sdes	struct stat st;
111798937Sdes	struct utmp ut;
111898937Sdes	int fd, found=0;
111998937Sdes
112098937Sdes	/* Clear the time entries in our logininfo */
112198937Sdes	li->tv_sec = li->tv_usec = 0;
112298937Sdes
112398937Sdes	if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
112498937Sdes		log("wtmp_get_entry: problem opening %s: %s",
112598937Sdes		    WTMP_FILE, strerror(errno));
112698937Sdes		return 0;
112798937Sdes	}
112898937Sdes	if (fstat(fd, &st) != 0) {
112998937Sdes		log("wtmp_get_entry: couldn't stat %s: %s",
113098937Sdes		    WTMP_FILE, strerror(errno));
113198937Sdes		close(fd);
113298937Sdes		return 0;
113398937Sdes	}
113498937Sdes
113598937Sdes	/* Seek to the start of the last struct utmp */
113698937Sdes	if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
113798937Sdes		/* Looks like we've got a fresh wtmp file */
113898937Sdes		close(fd);
113998937Sdes		return 0;
114098937Sdes	}
114198937Sdes
114298937Sdes	while (!found) {
114398937Sdes		if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
114498937Sdes			log("wtmp_get_entry: read of %s failed: %s",
114598937Sdes			    WTMP_FILE, strerror(errno));
114698937Sdes			close (fd);
114798937Sdes			return 0;
114898937Sdes		}
114998937Sdes		if ( wtmp_islogin(li, &ut) ) {
115098937Sdes			found = 1;
115198937Sdes			/* We've already checked for a time in struct
115298937Sdes			 * utmp, in login_getlast(). */
115398937Sdes# ifdef HAVE_TIME_IN_UTMP
115498937Sdes			li->tv_sec = ut.ut_time;
115598937Sdes# else
115698937Sdes#  if HAVE_TV_IN_UTMP
115798937Sdes			li->tv_sec = ut.ut_tv.tv_sec;
115898937Sdes#  endif
115998937Sdes# endif
116098937Sdes			line_fullname(li->line, ut.ut_line,
116198937Sdes				      MIN_SIZEOF(li->line, ut.ut_line));
116298937Sdes# ifdef HAVE_HOST_IN_UTMP
116398937Sdes			strlcpy(li->hostname, ut.ut_host,
116498937Sdes				MIN_SIZEOF(li->hostname, ut.ut_host));
116598937Sdes# endif
116698937Sdes			continue;
116798937Sdes		}
116898937Sdes		/* Seek back 2 x struct utmp */
116998937Sdes		if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
117098937Sdes			/* We've found the start of the file, so quit */
117198937Sdes			close (fd);
117298937Sdes			return 0;
117398937Sdes		}
117498937Sdes	}
117598937Sdes
117698937Sdes	/* We found an entry. Tidy up and return */
117798937Sdes	close(fd);
117898937Sdes	return 1;
117998937Sdes}
118098937Sdes# endif /* USE_WTMP */
118198937Sdes
118298937Sdes
118398937Sdes/**
118498937Sdes ** Low-level wtmpx functions
118598937Sdes **/
118698937Sdes
118798937Sdes#ifdef USE_WTMPX
118898937Sdes/* write a wtmpx entry direct to the end of the file */
118998937Sdes/* This is a slight modification of code in OpenBSD's logwtmp.c */
119098937Sdesstatic int
119198937Sdeswtmpx_write(struct logininfo *li, struct utmpx *utx)
119298937Sdes{
119398937Sdes	struct stat buf;
119498937Sdes	int fd, ret = 1;
119598937Sdes
119698937Sdes	if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
119798937Sdes		log("wtmpx_write: problem opening %s: %s",
119898937Sdes		    WTMPX_FILE, strerror(errno));
119998937Sdes		return 0;
120098937Sdes	}
120198937Sdes
120298937Sdes	if (fstat(fd, &buf) == 0)
120398937Sdes		if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
120498937Sdes			ftruncate(fd, buf.st_size);
120598937Sdes			log("wtmpx_write: problem writing %s: %s",
120698937Sdes			    WTMPX_FILE, strerror(errno));
120798937Sdes			ret = 0;
120898937Sdes		}
120998937Sdes	(void)close(fd);
121098937Sdes
121198937Sdes	return ret;
121298937Sdes}
121398937Sdes
121498937Sdes
121598937Sdesstatic int
121698937Sdeswtmpx_perform_login(struct logininfo *li)
121798937Sdes{
121898937Sdes	struct utmpx utx;
121998937Sdes
122098937Sdes	construct_utmpx(li, &utx);
122198937Sdes	return wtmpx_write(li, &utx);
122298937Sdes}
122398937Sdes
122498937Sdes
122598937Sdesstatic int
122698937Sdeswtmpx_perform_logout(struct logininfo *li)
122798937Sdes{
122898937Sdes	struct utmpx utx;
122998937Sdes
123098937Sdes	construct_utmpx(li, &utx);
123198937Sdes	return wtmpx_write(li, &utx);
123298937Sdes}
123398937Sdes
123498937Sdes
123598937Sdesint
123698937Sdeswtmpx_write_entry(struct logininfo *li)
123798937Sdes{
123898937Sdes	switch(li->type) {
123998937Sdes	case LTYPE_LOGIN:
124098937Sdes		return wtmpx_perform_login(li);
124198937Sdes	case LTYPE_LOGOUT:
124298937Sdes		return wtmpx_perform_logout(li);
124398937Sdes	default:
124498937Sdes		log("wtmpx_write_entry: invalid type field");
124598937Sdes		return 0;
124698937Sdes	}
124798937Sdes}
124898937Sdes
124998937Sdes/* Please see the notes above wtmp_islogin() for information about the
125098937Sdes   next two functions */
125198937Sdes
125298937Sdes/* Return true if this wtmpx entry indicates a login */
125398937Sdesstatic int
125498937Sdeswtmpx_islogin(struct logininfo *li, struct utmpx *utx)
125598937Sdes{
125698937Sdes	if ( strncmp(li->username, utx->ut_name,
125798937Sdes		MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
125898937Sdes# ifdef HAVE_TYPE_IN_UTMPX
125998937Sdes		if (utx->ut_type == USER_PROCESS)
126098937Sdes			return 1;
126198937Sdes# else
126298937Sdes		return 1;
126398937Sdes# endif
126498937Sdes	}
126598937Sdes	return 0;
126698937Sdes}
126798937Sdes
126898937Sdes
126998937Sdesint
127098937Sdeswtmpx_get_entry(struct logininfo *li)
127198937Sdes{
127298937Sdes	struct stat st;
127398937Sdes	struct utmpx utx;
127498937Sdes	int fd, found=0;
127598937Sdes
127698937Sdes	/* Clear the time entries */
127798937Sdes	li->tv_sec = li->tv_usec = 0;
127898937Sdes
127998937Sdes	if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
128098937Sdes		log("wtmpx_get_entry: problem opening %s: %s",
128198937Sdes		    WTMPX_FILE, strerror(errno));
128298937Sdes		return 0;
128398937Sdes	}
128498937Sdes	if (fstat(fd, &st) != 0) {
128598937Sdes		log("wtmpx_get_entry: couldn't stat %s: %s",
1286106130Sdes		    WTMPX_FILE, strerror(errno));
128798937Sdes		close(fd);
128898937Sdes		return 0;
128998937Sdes	}
129098937Sdes
129198937Sdes	/* Seek to the start of the last struct utmpx */
129298937Sdes	if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) {
129398937Sdes		/* probably a newly rotated wtmpx file */
129498937Sdes		close(fd);
129598937Sdes		return 0;
129698937Sdes	}
129798937Sdes
129898937Sdes	while (!found) {
129998937Sdes		if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
130098937Sdes			log("wtmpx_get_entry: read of %s failed: %s",
130198937Sdes			    WTMPX_FILE, strerror(errno));
130298937Sdes			close (fd);
130398937Sdes			return 0;
130498937Sdes		}
130598937Sdes		/* Logouts are recorded as a blank username on a particular line.
130698937Sdes		 * So, we just need to find the username in struct utmpx */
130798937Sdes		if ( wtmpx_islogin(li, &utx) ) {
1308106130Sdes			found = 1;
130998937Sdes# ifdef HAVE_TV_IN_UTMPX
131098937Sdes			li->tv_sec = utx.ut_tv.tv_sec;
131198937Sdes# else
131298937Sdes#  ifdef HAVE_TIME_IN_UTMPX
131398937Sdes			li->tv_sec = utx.ut_time;
131498937Sdes#  endif
131598937Sdes# endif
131698937Sdes			line_fullname(li->line, utx.ut_line, sizeof(li->line));
131798937Sdes# ifdef HAVE_HOST_IN_UTMPX
131898937Sdes			strlcpy(li->hostname, utx.ut_host,
131998937Sdes				MIN_SIZEOF(li->hostname, utx.ut_host));
132098937Sdes# endif
132198937Sdes			continue;
132298937Sdes		}
132398937Sdes		if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) {
132498937Sdes			close (fd);
132598937Sdes			return 0;
132698937Sdes		}
132798937Sdes	}
132898937Sdes
132998937Sdes	close(fd);
133098937Sdes	return 1;
133198937Sdes}
133298937Sdes#endif /* USE_WTMPX */
133398937Sdes
133498937Sdes/**
133598937Sdes ** Low-level libutil login() functions
133698937Sdes **/
133798937Sdes
133898937Sdes#ifdef USE_LOGIN
133998937Sdesstatic int
134098937Sdessyslogin_perform_login(struct logininfo *li)
134198937Sdes{
134298937Sdes	struct utmp *ut;
134398937Sdes
134498937Sdes	if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
134598937Sdes		log("syslogin_perform_login: couldn't malloc()");
134698937Sdes		return 0;
134798937Sdes	}
134898937Sdes	construct_utmp(li, ut);
134998937Sdes	login(ut);
1350113911Sdes	free(ut);
135198937Sdes
135298937Sdes	return 1;
135398937Sdes}
135498937Sdes
135598937Sdesstatic int
135698937Sdessyslogin_perform_logout(struct logininfo *li)
135798937Sdes{
135898937Sdes# ifdef HAVE_LOGOUT
135998937Sdes	char line[8];
136098937Sdes
136198937Sdes	(void)line_stripname(line, li->line, sizeof(line));
136298937Sdes
136398937Sdes	if (!logout(line)) {
136498937Sdes		log("syslogin_perform_logout: logout() returned an error");
136598937Sdes#  ifdef HAVE_LOGWTMP
136698937Sdes	} else {
136798937Sdes		logwtmp(line, "", "");
136898937Sdes#  endif
136998937Sdes	}
137098937Sdes	/* FIXME: (ATL - if the need arises) What to do if we have
137198937Sdes	 * login, but no logout?  what if logout but no logwtmp? All
137298937Sdes	 * routines are in libutil so they should all be there,
137398937Sdes	 * but... */
137498937Sdes# endif
137598937Sdes	return 1;
137698937Sdes}
137798937Sdes
137898937Sdesint
137998937Sdessyslogin_write_entry(struct logininfo *li)
138098937Sdes{
138198937Sdes	switch (li->type) {
138298937Sdes	case LTYPE_LOGIN:
138398937Sdes		return syslogin_perform_login(li);
138498937Sdes	case LTYPE_LOGOUT:
138598937Sdes		return syslogin_perform_logout(li);
138698937Sdes	default:
138798937Sdes		log("syslogin_write_entry: Invalid type field");
138898937Sdes		return 0;
138998937Sdes	}
139098937Sdes}
139198937Sdes#endif /* USE_LOGIN */
139298937Sdes
139398937Sdes/* end of file log-syslogin.c */
139498937Sdes
139598937Sdes/**
139698937Sdes ** Low-level lastlog functions
139798937Sdes **/
139898937Sdes
139998937Sdes#ifdef USE_LASTLOG
140098937Sdes#define LL_FILE 1
140198937Sdes#define LL_DIR 2
140298937Sdes#define LL_OTHER 3
140398937Sdes
140498937Sdesstatic void
140598937Sdeslastlog_construct(struct logininfo *li, struct lastlog *last)
140698937Sdes{
140798937Sdes	/* clear the structure */
140898937Sdes	memset(last, '\0', sizeof(*last));
140998937Sdes
141098937Sdes	(void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
141198937Sdes	strlcpy(last->ll_host, li->hostname,
141298937Sdes		MIN_SIZEOF(last->ll_host, li->hostname));
141398937Sdes	last->ll_time = li->tv_sec;
141498937Sdes}
141598937Sdes
141698937Sdesstatic int
141798937Sdeslastlog_filetype(char *filename)
141898937Sdes{
141998937Sdes	struct stat st;
142098937Sdes
142198937Sdes	if (stat(LASTLOG_FILE, &st) != 0) {
142298937Sdes		log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
142398937Sdes			strerror(errno));
142498937Sdes		return 0;
142598937Sdes	}
142698937Sdes	if (S_ISDIR(st.st_mode))
142798937Sdes		return LL_DIR;
142898937Sdes	else if (S_ISREG(st.st_mode))
142998937Sdes		return LL_FILE;
143098937Sdes	else
143198937Sdes		return LL_OTHER;
143298937Sdes}
143398937Sdes
143498937Sdes
143598937Sdes/* open the file (using filemode) and seek to the login entry */
143698937Sdesstatic int
143798937Sdeslastlog_openseek(struct logininfo *li, int *fd, int filemode)
143898937Sdes{
143998937Sdes	off_t offset;
144098937Sdes	int type;
144198937Sdes	char lastlog_file[1024];
144298937Sdes
144398937Sdes	type = lastlog_filetype(LASTLOG_FILE);
144498937Sdes	switch (type) {
144598937Sdes		case LL_FILE:
144698937Sdes			strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
144798937Sdes			break;
144898937Sdes		case LL_DIR:
144998937Sdes			snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
145098937Sdes				 LASTLOG_FILE, li->username);
145198937Sdes			break;
145298937Sdes		default:
145398937Sdes			log("lastlog_openseek: %.100s is not a file or directory!",
145498937Sdes			    LASTLOG_FILE);
145598937Sdes			return 0;
145698937Sdes	}
145798937Sdes
145898937Sdes	*fd = open(lastlog_file, filemode);
145998937Sdes	if ( *fd < 0) {
146098937Sdes		debug("lastlog_openseek: Couldn't open %s: %s",
146198937Sdes		    lastlog_file, strerror(errno));
146298937Sdes		return 0;
146398937Sdes	}
146498937Sdes
146598937Sdes	if (type == LL_FILE) {
146698937Sdes		/* find this uid's offset in the lastlog file */
146798937Sdes		offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
146898937Sdes
146998937Sdes		if ( lseek(*fd, offset, SEEK_SET) != offset ) {
147098937Sdes			log("lastlog_openseek: %s->lseek(): %s",
147198937Sdes			 lastlog_file, strerror(errno));
147298937Sdes			return 0;
147398937Sdes		}
147498937Sdes	}
147598937Sdes
147698937Sdes	return 1;
147798937Sdes}
147898937Sdes
147998937Sdesstatic int
148098937Sdeslastlog_perform_login(struct logininfo *li)
148198937Sdes{
148298937Sdes	struct lastlog last;
148398937Sdes	int fd;
148498937Sdes
148598937Sdes	/* create our struct lastlog */
148698937Sdes	lastlog_construct(li, &last);
148798937Sdes
148898937Sdes	if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
148998937Sdes		return(0);
149098937Sdes
149198937Sdes	/* write the entry */
149298937Sdes	if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) {
149398937Sdes		close(fd);
149498937Sdes		log("lastlog_write_filemode: Error writing to %s: %s",
149598937Sdes		    LASTLOG_FILE, strerror(errno));
149698937Sdes		return 0;
149798937Sdes	}
149898937Sdes
149998937Sdes	close(fd);
150098937Sdes	return 1;
150198937Sdes}
150298937Sdes
150398937Sdesint
150498937Sdeslastlog_write_entry(struct logininfo *li)
150598937Sdes{
150698937Sdes	switch(li->type) {
150798937Sdes	case LTYPE_LOGIN:
150898937Sdes		return lastlog_perform_login(li);
150998937Sdes	default:
151098937Sdes		log("lastlog_write_entry: Invalid type field");
151198937Sdes		return 0;
151298937Sdes	}
151398937Sdes}
151498937Sdes
151598937Sdesstatic void
151698937Sdeslastlog_populate_entry(struct logininfo *li, struct lastlog *last)
151798937Sdes{
151898937Sdes	line_fullname(li->line, last->ll_line, sizeof(li->line));
151998937Sdes	strlcpy(li->hostname, last->ll_host,
152098937Sdes		MIN_SIZEOF(li->hostname, last->ll_host));
152198937Sdes	li->tv_sec = last->ll_time;
152298937Sdes}
152398937Sdes
152498937Sdesint
152598937Sdeslastlog_get_entry(struct logininfo *li)
152698937Sdes{
152798937Sdes	struct lastlog last;
1528113911Sdes	int fd, ret;
152998937Sdes
153098937Sdes	if (!lastlog_openseek(li, &fd, O_RDONLY))
1531113911Sdes		return (0);
153298937Sdes
1533113911Sdes	ret = atomicio(read, fd, &last, sizeof(last));
1534113911Sdes	close(fd);
1535113911Sdes
1536113911Sdes	switch (ret) {
1537113911Sdes	case 0:
1538113911Sdes		memset(&last, '\0', sizeof(last));
1539113911Sdes		/* FALLTHRU */
1540113911Sdes	case sizeof(last):
1541113911Sdes		lastlog_populate_entry(li, &last);
1542113911Sdes		return (1);
1543113911Sdes	case -1:
1544113911Sdes		error("%s: Error reading from %s: %s", __func__,
154598937Sdes		    LASTLOG_FILE, strerror(errno));
1546113911Sdes		return (0);
1547113911Sdes	default:
1548113911Sdes		error("%s: Error reading from %s: Expecting %d, got %d",
1549113911Sdes		    __func__, LASTLOG_FILE, sizeof(last), ret);
1550113911Sdes		return (0);
155198937Sdes	}
155298937Sdes
1553113911Sdes	/* NOTREACHED */
1554113911Sdes	return (0);
155598937Sdes}
155698937Sdes#endif /* USE_LASTLOG */
1557