loginrec.c revision 137019
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 *
1698937Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1798937Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1898937Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1998937Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2098937Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2198937Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2298937Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2398937Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2498937Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2598937Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2698937Sdes */
2798937Sdes
2898937Sdes/**
2998937Sdes ** loginrec.c:  platform-independent login recording and lastlog retrieval
3098937Sdes **/
3198937Sdes
3298937Sdes/*
3398937Sdes  The new login code explained
3498937Sdes  ============================
3598937Sdes
3698937Sdes  This code attempts to provide a common interface to login recording
3798937Sdes  (utmp and friends) and last login time retrieval.
3898937Sdes
3998937Sdes  Its primary means of achieving this is to use 'struct logininfo', a
4098937Sdes  union of all the useful fields in the various different types of
4198937Sdes  system login record structures one finds on UNIX variants.
4298937Sdes
4398937Sdes  We depend on autoconf to define which recording methods are to be
4498937Sdes  used, and which fields are contained in the relevant data structures
4598937Sdes  on the local system. Many C preprocessor symbols affect which code
4698937Sdes  gets compiled here.
4798937Sdes
4898937Sdes  The code is designed to make it easy to modify a particular
4998937Sdes  recording method, without affecting other methods nor requiring so
5098937Sdes  many nested conditional compilation blocks as were commonplace in
5198937Sdes  the old code.
5298937Sdes
5398937Sdes  For login recording, we try to use the local system's libraries as
5498937Sdes  these are clearly most likely to work correctly. For utmp systems
5598937Sdes  this usually means login() and logout() or setutent() etc., probably
5698937Sdes  in libutil, along with logwtmp() etc. On these systems, we fall back
5798937Sdes  to writing the files directly if we have to, though this method
5898937Sdes  requires very thorough testing so we do not corrupt local auditing
5998937Sdes  information. These files and their access methods are very system
6098937Sdes  specific indeed.
6198937Sdes
6298937Sdes  For utmpx systems, the corresponding library functions are
6398937Sdes  setutxent() etc. To the author's knowledge, all utmpx systems have
6498937Sdes  these library functions and so no direct write is attempted. If such
6598937Sdes  a system exists and needs support, direct analogues of the [uw]tmp
6698937Sdes  code should suffice.
6798937Sdes
6898937Sdes  Retrieving the time of last login ('lastlog') is in some ways even
6998937Sdes  more problemmatic than login recording. Some systems provide a
7098937Sdes  simple table of all users which we seek based on uid and retrieve a
7198937Sdes  relatively standard structure. Others record the same information in
7298937Sdes  a directory with a separate file, and others don't record the
7398937Sdes  information separately at all. For systems in the latter category,
7498937Sdes  we look backwards in the wtmp or wtmpx file for the last login entry
7598937Sdes  for our user. Naturally this is slower and on busy systems could
7698937Sdes  incur a significant performance penalty.
7798937Sdes
7898937Sdes  Calling the new code
7998937Sdes  --------------------
8098937Sdes
8198937Sdes  In OpenSSH all login recording and retrieval is performed in
8298937Sdes  login.c. Here you'll find working examples. Also, in the logintest.c
8398937Sdes  program there are more examples.
8498937Sdes
8598937Sdes  Internal handler calling method
8698937Sdes  -------------------------------
8798937Sdes
8898937Sdes  When a call is made to login_login() or login_logout(), both
8998937Sdes  routines set a struct logininfo flag defining which action (log in,
9098937Sdes  or log out) is to be taken. They both then call login_write(), which
9198937Sdes  calls whichever of the many structure-specific handlers autoconf
9298937Sdes  selects for the local system.
9398937Sdes
9498937Sdes  The handlers themselves handle system data structure specifics. Both
9598937Sdes  struct utmp and struct utmpx have utility functions (see
9698937Sdes  construct_utmp*()) to try to make it simpler to add extra systems
9798937Sdes  that introduce new features to either structure.
9898937Sdes
9998937Sdes  While it may seem terribly wasteful to replicate so much similar
10098937Sdes  code for each method, experience has shown that maintaining code to
10198937Sdes  write both struct utmp and utmpx in one function, whilst maintaining
10298937Sdes  support for all systems whether they have library support or not, is
10398937Sdes  a difficult and time-consuming task.
10498937Sdes
10598937Sdes  Lastlog support proceeds similarly. Functions login_get_lastlog()
10698937Sdes  (and its OpenSSH-tuned friend login_get_lastlog_time()) call
10798937Sdes  getlast_entry(), which tries one of three methods to find the last
10898937Sdes  login time. It uses local system lastlog support if it can,
10998937Sdes  otherwise it tries wtmp or wtmpx before giving up and returning 0,
11098937Sdes  meaning "tilt".
11198937Sdes
11298937Sdes  Maintenance
11398937Sdes  -----------
11498937Sdes
11598937Sdes  In many cases it's possible to tweak autoconf to select the correct
11698937Sdes  methods for a particular platform, either by improving the detection
11798937Sdes  code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE
11898937Sdes  symbols for the platform.
11998937Sdes
12098937Sdes  Use logintest to check which symbols are defined before modifying
12198937Sdes  configure.ac and loginrec.c. (You have to build logintest yourself
12298937Sdes  with 'make logintest' as it's not built by default.)
12398937Sdes
12498937Sdes  Otherwise, patches to the specific method(s) are very helpful!
12598937Sdes
12698937Sdes*/
12798937Sdes
12898937Sdes/**
12998937Sdes ** TODO:
13098937Sdes **   homegrown ttyslot()
13198937Sdes **   test, test, test
13298937Sdes **
13398937Sdes ** Platform status:
13498937Sdes ** ----------------
13598937Sdes **
13698937Sdes ** Known good:
13798937Sdes **   Linux (Redhat 6.2, Debian)
13898937Sdes **   Solaris
13998937Sdes **   HP-UX 10.20 (gcc only)
14098937Sdes **   IRIX
14198937Sdes **   NeXT - M68k/HPPA/Sparc (4.2/3.3)
14298937Sdes **
14398937Sdes ** Testing required: Please send reports!
14498937Sdes **   NetBSD
14598937Sdes **   HP-UX 11
14698937Sdes **   AIX
14798937Sdes **
14898937Sdes ** Platforms with known problems:
14998937Sdes **   Some variants of Slackware Linux
15098937Sdes **
15198937Sdes **/
15298937Sdes
15398937Sdes#include "includes.h"
15498937Sdes
15598937Sdes#include "ssh.h"
15698937Sdes#include "xmalloc.h"
15798937Sdes#include "loginrec.h"
15898937Sdes#include "log.h"
15998937Sdes#include "atomicio.h"
16098937Sdes
161137019SdesRCSID("$Id: loginrec.c,v 1.58 2004/08/15 09:12:52 djm Exp $");
162128460SdesRCSID("$FreeBSD: head/crypto/openssh/loginrec.c 137019 2004-10-28 16:11:31Z des $");
16398937Sdes
16498937Sdes#ifdef HAVE_UTIL_H
16598937Sdes#  include <util.h>
16698937Sdes#endif
16798937Sdes
16898937Sdes#ifdef HAVE_LIBUTIL_H
16998937Sdes#   include <libutil.h>
17098937Sdes#endif
17198937Sdes
17298937Sdes/**
17398937Sdes ** prototypes for helper functions in this file
17498937Sdes **/
17598937Sdes
17698937Sdes#if HAVE_UTMP_H
17798937Sdesvoid set_utmp_time(struct logininfo *li, struct utmp *ut);
17898937Sdesvoid construct_utmp(struct logininfo *li, struct utmp *ut);
17998937Sdes#endif
18098937Sdes
18198937Sdes#ifdef HAVE_UTMPX_H
18298937Sdesvoid set_utmpx_time(struct logininfo *li, struct utmpx *ut);
18398937Sdesvoid construct_utmpx(struct logininfo *li, struct utmpx *ut);
18498937Sdes#endif
18598937Sdes
18698937Sdesint utmp_write_entry(struct logininfo *li);
18798937Sdesint utmpx_write_entry(struct logininfo *li);
18898937Sdesint wtmp_write_entry(struct logininfo *li);
18998937Sdesint wtmpx_write_entry(struct logininfo *li);
19098937Sdesint lastlog_write_entry(struct logininfo *li);
19198937Sdesint syslogin_write_entry(struct logininfo *li);
19298937Sdes
19398937Sdesint getlast_entry(struct logininfo *li);
19498937Sdesint lastlog_get_entry(struct logininfo *li);
19598937Sdesint wtmp_get_entry(struct logininfo *li);
19698937Sdesint wtmpx_get_entry(struct logininfo *li);
19798937Sdes
19898937Sdes/* pick the shortest string */
19998937Sdes#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
20098937Sdes
20198937Sdes/**
20298937Sdes ** platform-independent login functions
20398937Sdes **/
20498937Sdes
20598937Sdes/* login_login(struct logininfo *)     -Record a login
20698937Sdes *
20798937Sdes * Call with a pointer to a struct logininfo initialised with
20898937Sdes * login_init_entry() or login_alloc_entry()
20998937Sdes *
21098937Sdes * Returns:
21198937Sdes *  >0 if successful
21298937Sdes *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
21398937Sdes */
21498937Sdesint
21598937Sdeslogin_login (struct logininfo *li)
21698937Sdes{
21798937Sdes	li->type = LTYPE_LOGIN;
21898937Sdes	return login_write(li);
21998937Sdes}
22098937Sdes
22198937Sdes
22298937Sdes/* login_logout(struct logininfo *)     - Record a logout
22398937Sdes *
22498937Sdes * Call as with login_login()
22598937Sdes *
22698937Sdes * Returns:
22798937Sdes *  >0 if successful
22898937Sdes *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
22998937Sdes */
23098937Sdesint
23198937Sdeslogin_logout(struct logininfo *li)
23298937Sdes{
23398937Sdes	li->type = LTYPE_LOGOUT;
23498937Sdes	return login_write(li);
23598937Sdes}
23698937Sdes
23798937Sdes/* login_get_lastlog_time(int)           - Retrieve the last login time
23898937Sdes *
23998937Sdes * Retrieve the last login time for the given uid. Will try to use the
24098937Sdes * system lastlog facilities if they are available, but will fall back
24198937Sdes * to looking in wtmp/wtmpx if necessary
24298937Sdes *
24398937Sdes * Returns:
24498937Sdes *   0 on failure, or if user has never logged in
24598937Sdes *   Time in seconds from the epoch if successful
24698937Sdes *
24798937Sdes * Useful preprocessor symbols:
24898937Sdes *   DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
24998937Sdes *                    info
25098937Sdes *   USE_LASTLOG: If set, indicates the presence of system lastlog
25198937Sdes *                facilities. If this and DISABLE_LASTLOG are not set,
25298937Sdes *                try to retrieve lastlog information from wtmp/wtmpx.
25398937Sdes */
25498937Sdesunsigned int
25598937Sdeslogin_get_lastlog_time(const int uid)
25698937Sdes{
25798937Sdes	struct logininfo li;
25898937Sdes
25998937Sdes	if (login_get_lastlog(&li, uid))
26098937Sdes		return li.tv_sec;
26198937Sdes	else
26298937Sdes		return 0;
26398937Sdes}
26498937Sdes
26598937Sdes/* login_get_lastlog(struct logininfo *, int)   - Retrieve a lastlog entry
26698937Sdes *
26798937Sdes * Retrieve a logininfo structure populated (only partially) with
26898937Sdes * information from the system lastlog data, or from wtmp/wtmpx if no
26998937Sdes * system lastlog information exists.
27098937Sdes *
27198937Sdes * Note this routine must be given a pre-allocated logininfo.
27298937Sdes *
27398937Sdes * Returns:
27498937Sdes *  >0: A pointer to your struct logininfo if successful
27598937Sdes *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
27698937Sdes *
27798937Sdes */
27898937Sdesstruct logininfo *
27998937Sdeslogin_get_lastlog(struct logininfo *li, const int uid)
28098937Sdes{
28198937Sdes	struct passwd *pw;
28298937Sdes
28398937Sdes	memset(li, '\0', sizeof(*li));
28498937Sdes	li->uid = uid;
28598937Sdes
28698937Sdes	/*
28798937Sdes	 * If we don't have a 'real' lastlog, we need the username to
28898937Sdes	 * reliably search wtmp(x) for the last login (see
28998937Sdes	 * wtmp_get_entry().)
29098937Sdes	 */
29198937Sdes	pw = getpwuid(uid);
29298937Sdes	if (pw == NULL)
29398937Sdes		fatal("login_get_lastlog: Cannot find account for uid %i", uid);
29498937Sdes
29598937Sdes	/* No MIN_SIZEOF here - we absolutely *must not* truncate the
29698937Sdes	 * username */
29798937Sdes	strlcpy(li->username, pw->pw_name, sizeof(li->username));
29898937Sdes
29998937Sdes	if (getlast_entry(li))
30098937Sdes		return li;
30198937Sdes	else
30298937Sdes		return NULL;
30398937Sdes}
30498937Sdes
30598937Sdes
30698937Sdes/* login_alloc_entry(int, char*, char*, char*)    - Allocate and initialise
30798937Sdes *                                                  a logininfo structure
30898937Sdes *
30998937Sdes * This function creates a new struct logininfo, a data structure
31098937Sdes * meant to carry the information required to portably record login info.
31198937Sdes *
31298937Sdes * Returns a pointer to a newly created struct logininfo. If memory
31398937Sdes * allocation fails, the program halts.
31498937Sdes */
31598937Sdesstruct
31698937Sdeslogininfo *login_alloc_entry(int pid, const char *username,
31798937Sdes			     const char *hostname, const char *line)
31898937Sdes{
31998937Sdes	struct logininfo *newli;
32098937Sdes
32198937Sdes	newli = (struct logininfo *) xmalloc (sizeof(*newli));
32298937Sdes	(void)login_init_entry(newli, pid, username, hostname, line);
32398937Sdes	return newli;
32498937Sdes}
32598937Sdes
32698937Sdes
32798937Sdes/* login_free_entry(struct logininfo *)    - free struct memory */
32898937Sdesvoid
32998937Sdeslogin_free_entry(struct logininfo *li)
33098937Sdes{
33198937Sdes	xfree(li);
33298937Sdes}
33398937Sdes
33498937Sdes
33598937Sdes/* login_init_entry(struct logininfo *, int, char*, char*, char*)
33698937Sdes *                                        - initialise a struct logininfo
33798937Sdes *
33898937Sdes * Populates a new struct logininfo, a data structure meant to carry
33998937Sdes * the information required to portably record login info.
34098937Sdes *
34198937Sdes * Returns: 1
34298937Sdes */
34398937Sdesint
34498937Sdeslogin_init_entry(struct logininfo *li, int pid, const char *username,
34598937Sdes		 const char *hostname, const char *line)
34698937Sdes{
34798937Sdes	struct passwd *pw;
34898937Sdes
34998937Sdes	memset(li, 0, sizeof(*li));
35098937Sdes
35198937Sdes	li->pid = pid;
35298937Sdes
35398937Sdes	/* set the line information */
35498937Sdes	if (line)
35598937Sdes		line_fullname(li->line, line, sizeof(li->line));
35698937Sdes
35798937Sdes	if (username) {
35898937Sdes		strlcpy(li->username, username, sizeof(li->username));
35998937Sdes		pw = getpwnam(li->username);
36098937Sdes		if (pw == NULL)
36198937Sdes			fatal("login_init_entry: Cannot find user \"%s\"", li->username);
36298937Sdes		li->uid = pw->pw_uid;
36398937Sdes	}
36498937Sdes
36598937Sdes	if (hostname)
36698937Sdes		strlcpy(li->hostname, hostname, sizeof(li->hostname));
36798937Sdes
36898937Sdes	return 1;
36998937Sdes}
37098937Sdes
37198937Sdes/* login_set_current_time(struct logininfo *)    - set the current time
37298937Sdes *
37398937Sdes * Set the current time in a logininfo structure. This function is
37498937Sdes * meant to eliminate the need to deal with system dependencies for
37598937Sdes * time handling.
37698937Sdes */
37798937Sdesvoid
37898937Sdeslogin_set_current_time(struct logininfo *li)
37998937Sdes{
38098937Sdes	struct timeval tv;
38198937Sdes
38298937Sdes	gettimeofday(&tv, NULL);
38398937Sdes
38498937Sdes	li->tv_sec = tv.tv_sec;
38598937Sdes	li->tv_usec = tv.tv_usec;
38698937Sdes}
38798937Sdes
38898937Sdes/* copy a sockaddr_* into our logininfo */
38998937Sdesvoid
39098937Sdeslogin_set_addr(struct logininfo *li, const struct sockaddr *sa,
39198937Sdes	       const unsigned int sa_size)
39298937Sdes{
39398937Sdes	unsigned int bufsize = sa_size;
39498937Sdes
39598937Sdes	/* make sure we don't overrun our union */
39698937Sdes	if (sizeof(li->hostaddr) < sa_size)
39798937Sdes		bufsize = sizeof(li->hostaddr);
39898937Sdes
39998937Sdes	memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
40098937Sdes}
40198937Sdes
40298937Sdes
40398937Sdes/**
40498937Sdes ** login_write: Call low-level recording functions based on autoconf
40598937Sdes ** results
40698937Sdes **/
40798937Sdesint
40898937Sdeslogin_write (struct logininfo *li)
40998937Sdes{
41098937Sdes#ifndef HAVE_CYGWIN
41198937Sdes	if ((int)geteuid() != 0) {
412124211Sdes	  logit("Attempt to write login records by non-root user (aborting)");
41398937Sdes	  return 1;
41498937Sdes	}
41598937Sdes#endif
41698937Sdes
41798937Sdes	/* set the timestamp */
41898937Sdes	login_set_current_time(li);
41998937Sdes#ifdef USE_LOGIN
42098937Sdes	syslogin_write_entry(li);
42198937Sdes#endif
42298937Sdes#ifdef USE_LASTLOG
42398937Sdes	if (li->type == LTYPE_LOGIN) {
42498937Sdes		lastlog_write_entry(li);
42598937Sdes	}
42698937Sdes#endif
42798937Sdes#ifdef USE_UTMP
42898937Sdes	utmp_write_entry(li);
42998937Sdes#endif
43098937Sdes#ifdef USE_WTMP
43198937Sdes	wtmp_write_entry(li);
43298937Sdes#endif
43398937Sdes#ifdef USE_UTMPX
43498937Sdes	utmpx_write_entry(li);
43598937Sdes#endif
43698937Sdes#ifdef USE_WTMPX
43798937Sdes	wtmpx_write_entry(li);
43898937Sdes#endif
439137019Sdes#ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN
440137019Sdes	if (li->type == LTYPE_LOGIN &&
441137019Sdes	   !sys_auth_record_login(li->username,li->hostname,li->line))
442137019Sdes		logit("Writing login record failed for %s", li->username);
443137019Sdes#endif
44498937Sdes	return 0;
44598937Sdes}
44698937Sdes
44798937Sdes#ifdef LOGIN_NEEDS_UTMPX
44898937Sdesint
44998937Sdeslogin_utmp_only(struct logininfo *li)
45098937Sdes{
451126277Sdes	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) {
823137019Sdes		logit("%s: tty not found", __func__);
824137019Sdes		return (0);
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) {
833137019Sdes		off_t pos, ret;
834137019Sdes
835137019Sdes		pos = (off_t)tty * sizeof(struct utmp);
836137019Sdes		if ((ret = lseek(fd, pos, SEEK_SET)) == -1) {
837137019Sdes			logit("%s: llseek: %s", strerror(errno));
838137019Sdes			return (0);
839137019Sdes		}
840137019Sdes		if (ret != pos) {
841137019Sdes			logit("%s: Couldn't seek to tty %s slot in %s", tty,
842137019Sdes			    UTMP_FILE);
843137019Sdes			return (0);
844137019Sdes		}
84598937Sdes		/*
84698937Sdes		 * Prevent luser from zero'ing out ut_host.
84798937Sdes		 * If the new ut_line is empty but the old one is not
84898937Sdes		 * and ut_line and ut_name match, preserve the old ut_line.
84998937Sdes		 */
85098937Sdes		if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
85198937Sdes			(ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
85298937Sdes			(strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
85398937Sdes			(strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
85498937Sdes			(void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
85598937Sdes		}
85698937Sdes
857137019Sdes		if ((ret = lseek(fd, pos, SEEK_SET)) == -1) {
858137019Sdes			logit("%s: llseek: %s", __func__, strerror(errno));
859137019Sdes			return (0);
860137019Sdes		}
861137019Sdes		if (ret != pos) {
862137019Sdes			logit("%s: Couldn't seek to tty %s slot in %s",
863137019Sdes			    __func__, tty, UTMP_FILE);
864137019Sdes			return (0);
865137019Sdes		}
866124211Sdes		if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut))
867137019Sdes			logit("%s: error writing %s: %s", __func__,
86898937Sdes			    UTMP_FILE, strerror(errno));
86998937Sdes
87098937Sdes		(void)close(fd);
87198937Sdes		return 1;
87298937Sdes	} else {
87398937Sdes		return 0;
87498937Sdes	}
87598937Sdes}
87698937Sdes# endif /* UTMP_USE_LIBRARY */
87798937Sdes
87898937Sdesstatic int
87998937Sdesutmp_perform_login(struct logininfo *li)
88098937Sdes{
88198937Sdes	struct utmp ut;
88298937Sdes
88398937Sdes	construct_utmp(li, &ut);
88498937Sdes# ifdef UTMP_USE_LIBRARY
88598937Sdes	if (!utmp_write_library(li, &ut)) {
886124211Sdes		logit("utmp_perform_login: utmp_write_library() failed");
88798937Sdes		return 0;
88898937Sdes	}
88998937Sdes# else
89098937Sdes	if (!utmp_write_direct(li, &ut)) {
891124211Sdes		logit("utmp_perform_login: utmp_write_direct() failed");
89298937Sdes		return 0;
89398937Sdes	}
89498937Sdes# endif
89598937Sdes	return 1;
89698937Sdes}
89798937Sdes
89898937Sdes
89998937Sdesstatic int
90098937Sdesutmp_perform_logout(struct logininfo *li)
90198937Sdes{
90298937Sdes	struct utmp ut;
90398937Sdes
90498937Sdes	construct_utmp(li, &ut);
90598937Sdes# ifdef UTMP_USE_LIBRARY
90698937Sdes	if (!utmp_write_library(li, &ut)) {
907124211Sdes		logit("utmp_perform_logout: utmp_write_library() failed");
90898937Sdes		return 0;
90998937Sdes	}
91098937Sdes# else
91198937Sdes	if (!utmp_write_direct(li, &ut)) {
912124211Sdes		logit("utmp_perform_logout: utmp_write_direct() failed");
91398937Sdes		return 0;
91498937Sdes	}
91598937Sdes# endif
91698937Sdes	return 1;
91798937Sdes}
91898937Sdes
91998937Sdes
92098937Sdesint
92198937Sdesutmp_write_entry(struct logininfo *li)
92298937Sdes{
92398937Sdes	switch(li->type) {
92498937Sdes	case LTYPE_LOGIN:
92598937Sdes		return utmp_perform_login(li);
92698937Sdes
92798937Sdes	case LTYPE_LOGOUT:
92898937Sdes		return utmp_perform_logout(li);
92998937Sdes
93098937Sdes	default:
931124211Sdes		logit("utmp_write_entry: invalid type field");
93298937Sdes		return 0;
93398937Sdes	}
93498937Sdes}
93598937Sdes#endif /* USE_UTMP */
93698937Sdes
93798937Sdes
93898937Sdes/**
93998937Sdes ** Low-level utmpx functions
94098937Sdes **/
94198937Sdes
94298937Sdes/* not much point if we don't want utmpx entries */
94398937Sdes#ifdef USE_UTMPX
94498937Sdes
94598937Sdes/* if we have the wherewithall, use pututxline etc. */
94698937Sdes# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
94798937Sdes	defined(HAVE_PUTUTXLINE)
94898937Sdes#  define UTMPX_USE_LIBRARY
94998937Sdes# endif
95098937Sdes
95198937Sdes
95298937Sdes/* write a utmpx entry with the system's help (pututxline() and pals) */
95398937Sdes# ifdef UTMPX_USE_LIBRARY
95498937Sdesstatic int
95598937Sdesutmpx_write_library(struct logininfo *li, struct utmpx *utx)
95698937Sdes{
95798937Sdes	setutxent();
95898937Sdes	pututxline(utx);
95998937Sdes
96098937Sdes#  ifdef HAVE_ENDUTXENT
96198937Sdes	endutxent();
96298937Sdes#  endif
96398937Sdes	return 1;
96498937Sdes}
96598937Sdes
96698937Sdes# else /* UTMPX_USE_LIBRARY */
96798937Sdes
96898937Sdes/* write a utmp entry direct to the file */
96998937Sdesstatic int
97098937Sdesutmpx_write_direct(struct logininfo *li, struct utmpx *utx)
97198937Sdes{
972124211Sdes	logit("utmpx_write_direct: not implemented!");
97398937Sdes	return 0;
97498937Sdes}
97598937Sdes# endif /* UTMPX_USE_LIBRARY */
97698937Sdes
97798937Sdesstatic int
97898937Sdesutmpx_perform_login(struct logininfo *li)
97998937Sdes{
98098937Sdes	struct utmpx utx;
98198937Sdes
98298937Sdes	construct_utmpx(li, &utx);
98398937Sdes# ifdef UTMPX_USE_LIBRARY
98498937Sdes	if (!utmpx_write_library(li, &utx)) {
985124211Sdes		logit("utmpx_perform_login: utmp_write_library() failed");
98698937Sdes		return 0;
98798937Sdes	}
98898937Sdes# else
98998937Sdes	if (!utmpx_write_direct(li, &ut)) {
990124211Sdes		logit("utmpx_perform_login: utmp_write_direct() failed");
99198937Sdes		return 0;
99298937Sdes	}
99398937Sdes# endif
99498937Sdes	return 1;
99598937Sdes}
99698937Sdes
99798937Sdes
99898937Sdesstatic int
99998937Sdesutmpx_perform_logout(struct logininfo *li)
100098937Sdes{
100198937Sdes	struct utmpx utx;
100298937Sdes
100398937Sdes	construct_utmpx(li, &utx);
100498937Sdes# ifdef HAVE_ID_IN_UTMPX
100598937Sdes	line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
100698937Sdes# endif
100798937Sdes# ifdef HAVE_TYPE_IN_UTMPX
100898937Sdes	utx.ut_type = DEAD_PROCESS;
100998937Sdes# endif
101098937Sdes
101198937Sdes# ifdef UTMPX_USE_LIBRARY
101298937Sdes	utmpx_write_library(li, &utx);
101398937Sdes# else
101498937Sdes	utmpx_write_direct(li, &utx);
101598937Sdes# endif
101698937Sdes	return 1;
101798937Sdes}
101898937Sdes
101998937Sdesint
102098937Sdesutmpx_write_entry(struct logininfo *li)
102198937Sdes{
102298937Sdes	switch(li->type) {
102398937Sdes	case LTYPE_LOGIN:
102498937Sdes		return utmpx_perform_login(li);
102598937Sdes	case LTYPE_LOGOUT:
102698937Sdes		return utmpx_perform_logout(li);
102798937Sdes	default:
1028124211Sdes		logit("utmpx_write_entry: invalid type field");
102998937Sdes		return 0;
103098937Sdes	}
103198937Sdes}
103298937Sdes#endif /* USE_UTMPX */
103398937Sdes
103498937Sdes
103598937Sdes/**
103698937Sdes ** Low-level wtmp functions
103798937Sdes **/
103898937Sdes
103998937Sdes#ifdef USE_WTMP
104098937Sdes
104198937Sdes/* write a wtmp entry direct to the end of the file */
104298937Sdes/* This is a slight modification of code in OpenBSD's logwtmp.c */
104398937Sdesstatic int
104498937Sdeswtmp_write(struct logininfo *li, struct utmp *ut)
104598937Sdes{
104698937Sdes	struct stat buf;
104798937Sdes	int fd, ret = 1;
104898937Sdes
104998937Sdes	if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1050124211Sdes		logit("wtmp_write: problem writing %s: %s",
105198937Sdes		    WTMP_FILE, strerror(errno));
105298937Sdes		return 0;
105398937Sdes	}
105498937Sdes	if (fstat(fd, &buf) == 0)
1055124211Sdes		if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
105698937Sdes			ftruncate(fd, buf.st_size);
1057124211Sdes			logit("wtmp_write: problem writing %s: %s",
105898937Sdes			    WTMP_FILE, strerror(errno));
105998937Sdes			ret = 0;
106098937Sdes		}
106198937Sdes	(void)close(fd);
106298937Sdes	return ret;
106398937Sdes}
106498937Sdes
106598937Sdesstatic int
106698937Sdeswtmp_perform_login(struct logininfo *li)
106798937Sdes{
106898937Sdes	struct utmp ut;
106998937Sdes
107098937Sdes	construct_utmp(li, &ut);
107198937Sdes	return wtmp_write(li, &ut);
107298937Sdes}
107398937Sdes
107498937Sdes
107598937Sdesstatic int
107698937Sdeswtmp_perform_logout(struct logininfo *li)
107798937Sdes{
107898937Sdes	struct utmp ut;
107998937Sdes
108098937Sdes	construct_utmp(li, &ut);
108198937Sdes	return wtmp_write(li, &ut);
108298937Sdes}
108398937Sdes
108498937Sdes
108598937Sdesint
108698937Sdeswtmp_write_entry(struct logininfo *li)
108798937Sdes{
108898937Sdes	switch(li->type) {
108998937Sdes	case LTYPE_LOGIN:
109098937Sdes		return wtmp_perform_login(li);
109198937Sdes	case LTYPE_LOGOUT:
109298937Sdes		return wtmp_perform_logout(li);
109398937Sdes	default:
1094124211Sdes		logit("wtmp_write_entry: invalid type field");
109598937Sdes		return 0;
109698937Sdes	}
109798937Sdes}
109898937Sdes
109998937Sdes
110098937Sdes/* Notes on fetching login data from wtmp/wtmpx
110198937Sdes *
110298937Sdes * Logouts are usually recorded with (amongst other things) a blank
110398937Sdes * username on a given tty line.  However, some systems (HP-UX is one)
110498937Sdes * leave all fields set, but change the ut_type field to DEAD_PROCESS.
110598937Sdes *
110698937Sdes * Since we're only looking for logins here, we know that the username
110798937Sdes * must be set correctly. On systems that leave it in, we check for
110898937Sdes * ut_type==USER_PROCESS (indicating a login.)
110998937Sdes *
111098937Sdes * Portability: Some systems may set something other than USER_PROCESS
111198937Sdes * to indicate a login process. I don't know of any as I write. Also,
111298937Sdes * it's possible that some systems may both leave the username in
111398937Sdes * place and not have ut_type.
111498937Sdes */
111598937Sdes
111698937Sdes/* return true if this wtmp entry indicates a login */
111798937Sdesstatic int
111898937Sdeswtmp_islogin(struct logininfo *li, struct utmp *ut)
111998937Sdes{
112098937Sdes	if (strncmp(li->username, ut->ut_name,
112198937Sdes		MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
112298937Sdes# ifdef HAVE_TYPE_IN_UTMP
112398937Sdes		if (ut->ut_type & USER_PROCESS)
112498937Sdes			return 1;
112598937Sdes# else
112698937Sdes		return 1;
112798937Sdes# endif
112898937Sdes	}
112998937Sdes	return 0;
113098937Sdes}
113198937Sdes
113298937Sdesint
113398937Sdeswtmp_get_entry(struct logininfo *li)
113498937Sdes{
113598937Sdes	struct stat st;
113698937Sdes	struct utmp ut;
113798937Sdes	int fd, found=0;
113898937Sdes
113998937Sdes	/* Clear the time entries in our logininfo */
114098937Sdes	li->tv_sec = li->tv_usec = 0;
114198937Sdes
114298937Sdes	if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1143124211Sdes		logit("wtmp_get_entry: problem opening %s: %s",
114498937Sdes		    WTMP_FILE, strerror(errno));
114598937Sdes		return 0;
114698937Sdes	}
114798937Sdes	if (fstat(fd, &st) != 0) {
1148124211Sdes		logit("wtmp_get_entry: couldn't stat %s: %s",
114998937Sdes		    WTMP_FILE, strerror(errno));
115098937Sdes		close(fd);
115198937Sdes		return 0;
115298937Sdes	}
115398937Sdes
115498937Sdes	/* Seek to the start of the last struct utmp */
115598937Sdes	if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
115698937Sdes		/* Looks like we've got a fresh wtmp file */
115798937Sdes		close(fd);
115898937Sdes		return 0;
115998937Sdes	}
116098937Sdes
116198937Sdes	while (!found) {
116298937Sdes		if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
1163124211Sdes			logit("wtmp_get_entry: read of %s failed: %s",
116498937Sdes			    WTMP_FILE, strerror(errno));
116598937Sdes			close (fd);
116698937Sdes			return 0;
116798937Sdes		}
116898937Sdes		if ( wtmp_islogin(li, &ut) ) {
116998937Sdes			found = 1;
117098937Sdes			/* We've already checked for a time in struct
117198937Sdes			 * utmp, in login_getlast(). */
117298937Sdes# ifdef HAVE_TIME_IN_UTMP
117398937Sdes			li->tv_sec = ut.ut_time;
117498937Sdes# else
117598937Sdes#  if HAVE_TV_IN_UTMP
117698937Sdes			li->tv_sec = ut.ut_tv.tv_sec;
117798937Sdes#  endif
117898937Sdes# endif
117998937Sdes			line_fullname(li->line, ut.ut_line,
118098937Sdes				      MIN_SIZEOF(li->line, ut.ut_line));
118198937Sdes# ifdef HAVE_HOST_IN_UTMP
118298937Sdes			strlcpy(li->hostname, ut.ut_host,
118398937Sdes				MIN_SIZEOF(li->hostname, ut.ut_host));
118498937Sdes# endif
118598937Sdes			continue;
118698937Sdes		}
118798937Sdes		/* Seek back 2 x struct utmp */
118898937Sdes		if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
118998937Sdes			/* We've found the start of the file, so quit */
119098937Sdes			close (fd);
119198937Sdes			return 0;
119298937Sdes		}
119398937Sdes	}
119498937Sdes
119598937Sdes	/* We found an entry. Tidy up and return */
119698937Sdes	close(fd);
119798937Sdes	return 1;
119898937Sdes}
119998937Sdes# endif /* USE_WTMP */
120098937Sdes
120198937Sdes
120298937Sdes/**
120398937Sdes ** Low-level wtmpx functions
120498937Sdes **/
120598937Sdes
120698937Sdes#ifdef USE_WTMPX
120798937Sdes/* write a wtmpx entry direct to the end of the file */
120898937Sdes/* This is a slight modification of code in OpenBSD's logwtmp.c */
120998937Sdesstatic int
121098937Sdeswtmpx_write(struct logininfo *li, struct utmpx *utx)
121198937Sdes{
1212126277Sdes#ifndef HAVE_UPDWTMPX
121398937Sdes	struct stat buf;
121498937Sdes	int fd, ret = 1;
121598937Sdes
121698937Sdes	if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1217124211Sdes		logit("wtmpx_write: problem opening %s: %s",
121898937Sdes		    WTMPX_FILE, strerror(errno));
121998937Sdes		return 0;
122098937Sdes	}
122198937Sdes
122298937Sdes	if (fstat(fd, &buf) == 0)
1223124211Sdes		if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
122498937Sdes			ftruncate(fd, buf.st_size);
1225124211Sdes			logit("wtmpx_write: problem writing %s: %s",
122698937Sdes			    WTMPX_FILE, strerror(errno));
122798937Sdes			ret = 0;
122898937Sdes		}
122998937Sdes	(void)close(fd);
123098937Sdes
123198937Sdes	return ret;
1232126277Sdes#else
1233126277Sdes	updwtmpx(WTMPX_FILE, utx);
1234126277Sdes	return 1;
1235126277Sdes#endif
123698937Sdes}
123798937Sdes
123898937Sdes
123998937Sdesstatic int
124098937Sdeswtmpx_perform_login(struct logininfo *li)
124198937Sdes{
124298937Sdes	struct utmpx utx;
124398937Sdes
124498937Sdes	construct_utmpx(li, &utx);
124598937Sdes	return wtmpx_write(li, &utx);
124698937Sdes}
124798937Sdes
124898937Sdes
124998937Sdesstatic int
125098937Sdeswtmpx_perform_logout(struct logininfo *li)
125198937Sdes{
125298937Sdes	struct utmpx utx;
125398937Sdes
125498937Sdes	construct_utmpx(li, &utx);
125598937Sdes	return wtmpx_write(li, &utx);
125698937Sdes}
125798937Sdes
125898937Sdes
125998937Sdesint
126098937Sdeswtmpx_write_entry(struct logininfo *li)
126198937Sdes{
126298937Sdes	switch(li->type) {
126398937Sdes	case LTYPE_LOGIN:
126498937Sdes		return wtmpx_perform_login(li);
126598937Sdes	case LTYPE_LOGOUT:
126698937Sdes		return wtmpx_perform_logout(li);
126798937Sdes	default:
1268124211Sdes		logit("wtmpx_write_entry: invalid type field");
126998937Sdes		return 0;
127098937Sdes	}
127198937Sdes}
127298937Sdes
127398937Sdes/* Please see the notes above wtmp_islogin() for information about the
127498937Sdes   next two functions */
127598937Sdes
127698937Sdes/* Return true if this wtmpx entry indicates a login */
127798937Sdesstatic int
127898937Sdeswtmpx_islogin(struct logininfo *li, struct utmpx *utx)
127998937Sdes{
128098937Sdes	if ( strncmp(li->username, utx->ut_name,
128198937Sdes		MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
128298937Sdes# ifdef HAVE_TYPE_IN_UTMPX
128398937Sdes		if (utx->ut_type == USER_PROCESS)
128498937Sdes			return 1;
128598937Sdes# else
128698937Sdes		return 1;
128798937Sdes# endif
128898937Sdes	}
128998937Sdes	return 0;
129098937Sdes}
129198937Sdes
129298937Sdes
129398937Sdesint
129498937Sdeswtmpx_get_entry(struct logininfo *li)
129598937Sdes{
129698937Sdes	struct stat st;
129798937Sdes	struct utmpx utx;
129898937Sdes	int fd, found=0;
129998937Sdes
130098937Sdes	/* Clear the time entries */
130198937Sdes	li->tv_sec = li->tv_usec = 0;
130298937Sdes
130398937Sdes	if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1304124211Sdes		logit("wtmpx_get_entry: problem opening %s: %s",
130598937Sdes		    WTMPX_FILE, strerror(errno));
130698937Sdes		return 0;
130798937Sdes	}
130898937Sdes	if (fstat(fd, &st) != 0) {
1309124211Sdes		logit("wtmpx_get_entry: couldn't stat %s: %s",
1310106130Sdes		    WTMPX_FILE, strerror(errno));
131198937Sdes		close(fd);
131298937Sdes		return 0;
131398937Sdes	}
131498937Sdes
131598937Sdes	/* Seek to the start of the last struct utmpx */
131698937Sdes	if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) {
131798937Sdes		/* probably a newly rotated wtmpx file */
131898937Sdes		close(fd);
131998937Sdes		return 0;
132098937Sdes	}
132198937Sdes
132298937Sdes	while (!found) {
132398937Sdes		if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
1324124211Sdes			logit("wtmpx_get_entry: read of %s failed: %s",
132598937Sdes			    WTMPX_FILE, strerror(errno));
132698937Sdes			close (fd);
132798937Sdes			return 0;
132898937Sdes		}
132998937Sdes		/* Logouts are recorded as a blank username on a particular line.
133098937Sdes		 * So, we just need to find the username in struct utmpx */
133198937Sdes		if ( wtmpx_islogin(li, &utx) ) {
1332106130Sdes			found = 1;
133398937Sdes# ifdef HAVE_TV_IN_UTMPX
133498937Sdes			li->tv_sec = utx.ut_tv.tv_sec;
133598937Sdes# else
133698937Sdes#  ifdef HAVE_TIME_IN_UTMPX
133798937Sdes			li->tv_sec = utx.ut_time;
133898937Sdes#  endif
133998937Sdes# endif
134098937Sdes			line_fullname(li->line, utx.ut_line, sizeof(li->line));
134198937Sdes# ifdef HAVE_HOST_IN_UTMPX
134298937Sdes			strlcpy(li->hostname, utx.ut_host,
134398937Sdes				MIN_SIZEOF(li->hostname, utx.ut_host));
134498937Sdes# endif
134598937Sdes			continue;
134698937Sdes		}
134798937Sdes		if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) {
134898937Sdes			close (fd);
134998937Sdes			return 0;
135098937Sdes		}
135198937Sdes	}
135298937Sdes
135398937Sdes	close(fd);
135498937Sdes	return 1;
135598937Sdes}
135698937Sdes#endif /* USE_WTMPX */
135798937Sdes
135898937Sdes/**
135998937Sdes ** Low-level libutil login() functions
136098937Sdes **/
136198937Sdes
136298937Sdes#ifdef USE_LOGIN
136398937Sdesstatic int
136498937Sdessyslogin_perform_login(struct logininfo *li)
136598937Sdes{
136698937Sdes	struct utmp *ut;
136798937Sdes
136898937Sdes	if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
1369124211Sdes		logit("syslogin_perform_login: couldn't malloc()");
137098937Sdes		return 0;
137198937Sdes	}
137298937Sdes	construct_utmp(li, ut);
137398937Sdes	login(ut);
1374113911Sdes	free(ut);
137598937Sdes
137698937Sdes	return 1;
137798937Sdes}
137898937Sdes
137998937Sdesstatic int
138098937Sdessyslogin_perform_logout(struct logininfo *li)
138198937Sdes{
138298937Sdes# ifdef HAVE_LOGOUT
1383128460Sdes	char line[UT_LINESIZE];
138498937Sdes
138598937Sdes	(void)line_stripname(line, li->line, sizeof(line));
138698937Sdes
138798937Sdes	if (!logout(line)) {
1388124211Sdes		logit("syslogin_perform_logout: logout() returned an error");
138998937Sdes#  ifdef HAVE_LOGWTMP
139098937Sdes	} else {
139198937Sdes		logwtmp(line, "", "");
139298937Sdes#  endif
139398937Sdes	}
139498937Sdes	/* FIXME: (ATL - if the need arises) What to do if we have
139598937Sdes	 * login, but no logout?  what if logout but no logwtmp? All
139698937Sdes	 * routines are in libutil so they should all be there,
139798937Sdes	 * but... */
139898937Sdes# endif
139998937Sdes	return 1;
140098937Sdes}
140198937Sdes
140298937Sdesint
140398937Sdessyslogin_write_entry(struct logininfo *li)
140498937Sdes{
140598937Sdes	switch (li->type) {
140698937Sdes	case LTYPE_LOGIN:
140798937Sdes		return syslogin_perform_login(li);
140898937Sdes	case LTYPE_LOGOUT:
140998937Sdes		return syslogin_perform_logout(li);
141098937Sdes	default:
1411124211Sdes		logit("syslogin_write_entry: Invalid type field");
141298937Sdes		return 0;
141398937Sdes	}
141498937Sdes}
141598937Sdes#endif /* USE_LOGIN */
141698937Sdes
141798937Sdes/* end of file log-syslogin.c */
141898937Sdes
141998937Sdes/**
142098937Sdes ** Low-level lastlog functions
142198937Sdes **/
142298937Sdes
142398937Sdes#ifdef USE_LASTLOG
142498937Sdes#define LL_FILE 1
142598937Sdes#define LL_DIR 2
142698937Sdes#define LL_OTHER 3
142798937Sdes
142898937Sdesstatic void
142998937Sdeslastlog_construct(struct logininfo *li, struct lastlog *last)
143098937Sdes{
143198937Sdes	/* clear the structure */
143298937Sdes	memset(last, '\0', sizeof(*last));
143398937Sdes
143498937Sdes	(void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
143598937Sdes	strlcpy(last->ll_host, li->hostname,
143698937Sdes		MIN_SIZEOF(last->ll_host, li->hostname));
143798937Sdes	last->ll_time = li->tv_sec;
143898937Sdes}
143998937Sdes
144098937Sdesstatic int
144198937Sdeslastlog_filetype(char *filename)
144298937Sdes{
144398937Sdes	struct stat st;
144498937Sdes
144598937Sdes	if (stat(LASTLOG_FILE, &st) != 0) {
1446124211Sdes		logit("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
144798937Sdes			strerror(errno));
144898937Sdes		return 0;
144998937Sdes	}
145098937Sdes	if (S_ISDIR(st.st_mode))
145198937Sdes		return LL_DIR;
145298937Sdes	else if (S_ISREG(st.st_mode))
145398937Sdes		return LL_FILE;
145498937Sdes	else
145598937Sdes		return LL_OTHER;
145698937Sdes}
145798937Sdes
145898937Sdes
145998937Sdes/* open the file (using filemode) and seek to the login entry */
146098937Sdesstatic int
146198937Sdeslastlog_openseek(struct logininfo *li, int *fd, int filemode)
146298937Sdes{
146398937Sdes	off_t offset;
146498937Sdes	int type;
146598937Sdes	char lastlog_file[1024];
146698937Sdes
146798937Sdes	type = lastlog_filetype(LASTLOG_FILE);
146898937Sdes	switch (type) {
146998937Sdes		case LL_FILE:
147098937Sdes			strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
147198937Sdes			break;
147298937Sdes		case LL_DIR:
147398937Sdes			snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
147498937Sdes				 LASTLOG_FILE, li->username);
147598937Sdes			break;
147698937Sdes		default:
1477124211Sdes			logit("lastlog_openseek: %.100s is not a file or directory!",
147898937Sdes			    LASTLOG_FILE);
147998937Sdes			return 0;
148098937Sdes	}
148198937Sdes
1482124211Sdes	*fd = open(lastlog_file, filemode, 0600);
148398937Sdes	if ( *fd < 0) {
148498937Sdes		debug("lastlog_openseek: Couldn't open %s: %s",
148598937Sdes		    lastlog_file, strerror(errno));
148698937Sdes		return 0;
148798937Sdes	}
148898937Sdes
148998937Sdes	if (type == LL_FILE) {
149098937Sdes		/* find this uid's offset in the lastlog file */
149198937Sdes		offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
149298937Sdes
149398937Sdes		if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1494124211Sdes			logit("lastlog_openseek: %s->lseek(): %s",
149598937Sdes			 lastlog_file, strerror(errno));
149698937Sdes			return 0;
149798937Sdes		}
149898937Sdes	}
149998937Sdes
150098937Sdes	return 1;
150198937Sdes}
150298937Sdes
150398937Sdesstatic int
150498937Sdeslastlog_perform_login(struct logininfo *li)
150598937Sdes{
150698937Sdes	struct lastlog last;
150798937Sdes	int fd;
150898937Sdes
150998937Sdes	/* create our struct lastlog */
151098937Sdes	lastlog_construct(li, &last);
151198937Sdes
151298937Sdes	if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
151398937Sdes		return(0);
151498937Sdes
151598937Sdes	/* write the entry */
1516124211Sdes	if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) {
151798937Sdes		close(fd);
1518124211Sdes		logit("lastlog_write_filemode: Error writing to %s: %s",
151998937Sdes		    LASTLOG_FILE, strerror(errno));
152098937Sdes		return 0;
152198937Sdes	}
152298937Sdes
152398937Sdes	close(fd);
152498937Sdes	return 1;
152598937Sdes}
152698937Sdes
152798937Sdesint
152898937Sdeslastlog_write_entry(struct logininfo *li)
152998937Sdes{
153098937Sdes	switch(li->type) {
153198937Sdes	case LTYPE_LOGIN:
153298937Sdes		return lastlog_perform_login(li);
153398937Sdes	default:
1534124211Sdes		logit("lastlog_write_entry: Invalid type field");
153598937Sdes		return 0;
153698937Sdes	}
153798937Sdes}
153898937Sdes
153998937Sdesstatic void
154098937Sdeslastlog_populate_entry(struct logininfo *li, struct lastlog *last)
154198937Sdes{
154298937Sdes	line_fullname(li->line, last->ll_line, sizeof(li->line));
154398937Sdes	strlcpy(li->hostname, last->ll_host,
154498937Sdes		MIN_SIZEOF(li->hostname, last->ll_host));
154598937Sdes	li->tv_sec = last->ll_time;
154698937Sdes}
154798937Sdes
154898937Sdesint
154998937Sdeslastlog_get_entry(struct logininfo *li)
155098937Sdes{
155198937Sdes	struct lastlog last;
1552113911Sdes	int fd, ret;
155398937Sdes
155498937Sdes	if (!lastlog_openseek(li, &fd, O_RDONLY))
1555113911Sdes		return (0);
155698937Sdes
1557113911Sdes	ret = atomicio(read, fd, &last, sizeof(last));
1558113911Sdes	close(fd);
1559113911Sdes
1560113911Sdes	switch (ret) {
1561113911Sdes	case 0:
1562113911Sdes		memset(&last, '\0', sizeof(last));
1563113911Sdes		/* FALLTHRU */
1564113911Sdes	case sizeof(last):
1565113911Sdes		lastlog_populate_entry(li, &last);
1566113911Sdes		return (1);
1567113911Sdes	case -1:
1568126277Sdes		error("%s: Error reading from %s: %s", __func__,
156998937Sdes		    LASTLOG_FILE, strerror(errno));
1570113911Sdes		return (0);
1571113911Sdes	default:
1572113911Sdes		error("%s: Error reading from %s: Expecting %d, got %d",
1573113911Sdes		    __func__, LASTLOG_FILE, sizeof(last), ret);
1574113911Sdes		return (0);
157598937Sdes	}
157698937Sdes
1577113911Sdes	/* NOTREACHED */
1578113911Sdes	return (0);
157998937Sdes}
158098937Sdes#endif /* USE_LASTLOG */
1581