loginrec.c revision 126277
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
161126277SdesRCSID("$Id: loginrec.c,v 1.54 2004/02/10 05:49:35 dtucker Exp $");
16299768SdesRCSID("$FreeBSD: head/crypto/openssh/loginrec.c 126277 2004-02-26 10:52:33Z 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
43998937Sdes	return 0;
44098937Sdes}
44198937Sdes
44298937Sdes#ifdef LOGIN_NEEDS_UTMPX
44398937Sdesint
44498937Sdeslogin_utmp_only(struct logininfo *li)
44598937Sdes{
446126277Sdes	li->type = LTYPE_LOGIN;
44798937Sdes	login_set_current_time(li);
44898937Sdes# ifdef USE_UTMP
44998937Sdes	utmp_write_entry(li);
45098937Sdes# endif
45198937Sdes# ifdef USE_WTMP
45298937Sdes	wtmp_write_entry(li);
45398937Sdes# endif
45498937Sdes# ifdef USE_UTMPX
45598937Sdes	utmpx_write_entry(li);
45698937Sdes# endif
45798937Sdes# ifdef USE_WTMPX
45898937Sdes	wtmpx_write_entry(li);
45998937Sdes# endif
46098937Sdes	return 0;
46198937Sdes}
46298937Sdes#endif
46398937Sdes
46498937Sdes/**
46598937Sdes ** getlast_entry: Call low-level functions to retrieve the last login
46698937Sdes **                time.
46798937Sdes **/
46898937Sdes
46998937Sdes/* take the uid in li and return the last login time */
47098937Sdesint
47198937Sdesgetlast_entry(struct logininfo *li)
47298937Sdes{
47398937Sdes#ifdef USE_LASTLOG
47498937Sdes	return(lastlog_get_entry(li));
47598937Sdes#else /* !USE_LASTLOG */
47698937Sdes
47798937Sdes#ifdef DISABLE_LASTLOG
47898937Sdes	/* On some systems we shouldn't even try to obtain last login
47998937Sdes	 * time, e.g. AIX */
48098937Sdes	return 0;
48198937Sdes# else /* DISABLE_LASTLOG */
48298937Sdes	/* Try to retrieve the last login time from wtmp */
48398937Sdes#  if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
48498937Sdes	/* retrieve last login time from utmp */
48598937Sdes	return (wtmp_get_entry(li));
48698937Sdes#  else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */
48798937Sdes	/* If wtmp isn't available, try wtmpx */
48898937Sdes#   if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
48998937Sdes	/* retrieve last login time from utmpx */
49098937Sdes	return (wtmpx_get_entry(li));
49198937Sdes#   else
49298937Sdes	/* Give up: No means of retrieving last login time */
49398937Sdes	return 0;
49498937Sdes#   endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
49598937Sdes#  endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
49698937Sdes# endif /* DISABLE_LASTLOG */
49798937Sdes#endif /* USE_LASTLOG */
49898937Sdes}
49998937Sdes
50098937Sdes
50198937Sdes
50298937Sdes/*
50398937Sdes * 'line' string utility functions
50498937Sdes *
50598937Sdes * These functions process the 'line' string into one of three forms:
50698937Sdes *
50798937Sdes * 1. The full filename (including '/dev')
50898937Sdes * 2. The stripped name (excluding '/dev')
50998937Sdes * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
51098937Sdes *                               /dev/pts/1  -> ts/1 )
51198937Sdes *
51298937Sdes * Form 3 is used on some systems to identify a .tmp.? entry when
51398937Sdes * attempting to remove it. Typically both addition and removal is
51498937Sdes * performed by one application - say, sshd - so as long as the choice
51598937Sdes * uniquely identifies a terminal it's ok.
51698937Sdes */
51798937Sdes
51898937Sdes
51998937Sdes/* line_fullname(): add the leading '/dev/' if it doesn't exist make
52098937Sdes * sure dst has enough space, if not just copy src (ugh) */
52198937Sdeschar *
52298937Sdesline_fullname(char *dst, const char *src, int dstsize)
52398937Sdes{
52498937Sdes	memset(dst, '\0', dstsize);
52598937Sdes	if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) {
52698937Sdes		strlcpy(dst, src, dstsize);
52798937Sdes	} else {
52898937Sdes		strlcpy(dst, "/dev/", dstsize);
52998937Sdes		strlcat(dst, src, dstsize);
53098937Sdes	}
53198937Sdes	return dst;
53298937Sdes}
53398937Sdes
53498937Sdes/* line_stripname(): strip the leading '/dev' if it exists, return dst */
53598937Sdeschar *
53698937Sdesline_stripname(char *dst, const char *src, int dstsize)
53798937Sdes{
53898937Sdes	memset(dst, '\0', dstsize);
53998937Sdes	if (strncmp(src, "/dev/", 5) == 0)
54098937Sdes		strlcpy(dst, src + 5, dstsize);
54198937Sdes	else
54298937Sdes		strlcpy(dst, src, dstsize);
54398937Sdes	return dst;
54498937Sdes}
54598937Sdes
54698937Sdes/* line_abbrevname(): Return the abbreviated (usually four-character)
54798937Sdes * form of the line (Just use the last <dstsize> characters of the
54898937Sdes * full name.)
54998937Sdes *
55098937Sdes * NOTE: use strncpy because we do NOT necessarily want zero
55198937Sdes * termination */
55298937Sdeschar *
55398937Sdesline_abbrevname(char *dst, const char *src, int dstsize)
55498937Sdes{
55598937Sdes	size_t len;
55698937Sdes
55798937Sdes	memset(dst, '\0', dstsize);
55898937Sdes
55998937Sdes	/* Always skip prefix if present */
56098937Sdes	if (strncmp(src, "/dev/", 5) == 0)
56198937Sdes		src += 5;
56298937Sdes
56398937Sdes#ifdef WITH_ABBREV_NO_TTY
56498937Sdes	if (strncmp(src, "tty", 3) == 0)
56598937Sdes		src += 3;
56698937Sdes#endif
56798937Sdes
56898937Sdes	len = strlen(src);
56998937Sdes
57098937Sdes	if (len > 0) {
57198937Sdes		if (((int)len - dstsize) > 0)
57298937Sdes			src +=  ((int)len - dstsize);
57398937Sdes
57498937Sdes		/* note: _don't_ change this to strlcpy */
57598937Sdes		strncpy(dst, src, (size_t)dstsize);
57698937Sdes	}
57798937Sdes
57898937Sdes	return dst;
57998937Sdes}
58098937Sdes
58198937Sdes/**
58298937Sdes ** utmp utility functions
58398937Sdes **
58498937Sdes ** These functions manipulate struct utmp, taking system differences
58598937Sdes ** into account.
58698937Sdes **/
58798937Sdes
58898937Sdes#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
58998937Sdes
59098937Sdes/* build the utmp structure */
59198937Sdesvoid
59298937Sdesset_utmp_time(struct logininfo *li, struct utmp *ut)
59398937Sdes{
59498937Sdes# ifdef HAVE_TV_IN_UTMP
59598937Sdes	ut->ut_tv.tv_sec = li->tv_sec;
59698937Sdes	ut->ut_tv.tv_usec = li->tv_usec;
59798937Sdes# else
59898937Sdes#  ifdef HAVE_TIME_IN_UTMP
59998937Sdes	ut->ut_time = li->tv_sec;
60098937Sdes#  endif
60198937Sdes# endif
60298937Sdes}
60398937Sdes
60498937Sdesvoid
60598937Sdesconstruct_utmp(struct logininfo *li,
60698937Sdes		    struct utmp *ut)
60798937Sdes{
608113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP
609113911Sdes	struct sockaddr_in6 *sa6;
610113911Sdes#  endif
61198937Sdes	memset(ut, '\0', sizeof(*ut));
61298937Sdes
61398937Sdes	/* First fill out fields used for both logins and logouts */
61498937Sdes
61598937Sdes# ifdef HAVE_ID_IN_UTMP
61698937Sdes	line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
61798937Sdes# endif
61898937Sdes
61998937Sdes# ifdef HAVE_TYPE_IN_UTMP
62098937Sdes	/* This is done here to keep utmp constants out of struct logininfo */
62198937Sdes	switch (li->type) {
62298937Sdes	case LTYPE_LOGIN:
62398937Sdes		ut->ut_type = USER_PROCESS;
624106130Sdes#ifdef _UNICOS
62598937Sdes		cray_set_tmpdir(ut);
62698937Sdes#endif
62798937Sdes		break;
62898937Sdes	case LTYPE_LOGOUT:
62998937Sdes		ut->ut_type = DEAD_PROCESS;
630106130Sdes#ifdef _UNICOS
63198937Sdes		cray_retain_utmp(ut, li->pid);
63298937Sdes#endif
63398937Sdes		break;
63498937Sdes	}
63598937Sdes# endif
63698937Sdes	set_utmp_time(li, ut);
63798937Sdes
63898937Sdes	line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
63998937Sdes
64098937Sdes# ifdef HAVE_PID_IN_UTMP
64198937Sdes	ut->ut_pid = li->pid;
64298937Sdes# endif
64398937Sdes
64498937Sdes	/* If we're logging out, leave all other fields blank */
64598937Sdes	if (li->type == LTYPE_LOGOUT)
64698937Sdes	  return;
64798937Sdes
64898937Sdes	/*
64998937Sdes	 * These fields are only used when logging in, and are blank
65098937Sdes	 * for logouts.
65198937Sdes	 */
65298937Sdes
65398937Sdes	/* Use strncpy because we don't necessarily want null termination */
65498937Sdes	strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
65598937Sdes# ifdef HAVE_HOST_IN_UTMP
65699768Sdes	realhostname_sa(ut->ut_host, sizeof ut->ut_host,
65799768Sdes	    &li->hostaddr.sa, li->hostaddr.sa.sa_len);
65898937Sdes# endif
65998937Sdes# ifdef HAVE_ADDR_IN_UTMP
66098937Sdes	/* this is just a 32-bit IP address */
66198937Sdes	if (li->hostaddr.sa.sa_family == AF_INET)
66298937Sdes		ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
66398937Sdes# endif
664113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP
665113911Sdes	/* this is just a 128-bit IPv6 address */
666113911Sdes	if (li->hostaddr.sa.sa_family == AF_INET6) {
667113911Sdes		sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
668113911Sdes		memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
669113911Sdes		if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
670113911Sdes			ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
671113911Sdes			ut->ut_addr_v6[1] = 0;
672113911Sdes			ut->ut_addr_v6[2] = 0;
673113911Sdes			ut->ut_addr_v6[3] = 0;
674113911Sdes		}
675113911Sdes	}
676113911Sdes# endif
67798937Sdes}
67898937Sdes#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
67998937Sdes
68098937Sdes/**
68198937Sdes ** utmpx utility functions
68298937Sdes **
68398937Sdes ** These functions manipulate struct utmpx, accounting for system
68498937Sdes ** variations.
68598937Sdes **/
68698937Sdes
68798937Sdes#if defined(USE_UTMPX) || defined (USE_WTMPX)
68898937Sdes/* build the utmpx structure */
68998937Sdesvoid
69098937Sdesset_utmpx_time(struct logininfo *li, struct utmpx *utx)
69198937Sdes{
69298937Sdes# ifdef HAVE_TV_IN_UTMPX
69398937Sdes	utx->ut_tv.tv_sec = li->tv_sec;
69498937Sdes	utx->ut_tv.tv_usec = li->tv_usec;
69598937Sdes# else /* HAVE_TV_IN_UTMPX */
69698937Sdes#  ifdef HAVE_TIME_IN_UTMPX
69798937Sdes	utx->ut_time = li->tv_sec;
69898937Sdes#  endif /* HAVE_TIME_IN_UTMPX */
69998937Sdes# endif /* HAVE_TV_IN_UTMPX */
70098937Sdes}
70198937Sdes
70298937Sdesvoid
70398937Sdesconstruct_utmpx(struct logininfo *li, struct utmpx *utx)
70498937Sdes{
705113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP
706113911Sdes	struct sockaddr_in6 *sa6;
707113911Sdes#  endif
70898937Sdes	memset(utx, '\0', sizeof(*utx));
70998937Sdes# ifdef HAVE_ID_IN_UTMPX
71098937Sdes	line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
71198937Sdes# endif
71298937Sdes
71398937Sdes	/* this is done here to keep utmp constants out of loginrec.h */
71498937Sdes	switch (li->type) {
71598937Sdes	case LTYPE_LOGIN:
71698937Sdes		utx->ut_type = USER_PROCESS;
71798937Sdes		break;
71898937Sdes	case LTYPE_LOGOUT:
71998937Sdes		utx->ut_type = DEAD_PROCESS;
72098937Sdes		break;
72198937Sdes	}
72298937Sdes	line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
72398937Sdes	set_utmpx_time(li, utx);
72498937Sdes	utx->ut_pid = li->pid;
72598937Sdes	/* strncpy(): Don't necessarily want null termination */
72698937Sdes	strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
72798937Sdes
72898937Sdes	if (li->type == LTYPE_LOGOUT)
72998937Sdes		return;
73098937Sdes
73198937Sdes	/*
73298937Sdes	 * These fields are only used when logging in, and are blank
73398937Sdes	 * for logouts.
73498937Sdes	 */
73598937Sdes
73698937Sdes# ifdef HAVE_HOST_IN_UTMPX
73798937Sdes	strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
73898937Sdes# endif
73998937Sdes# ifdef HAVE_ADDR_IN_UTMPX
74098937Sdes	/* this is just a 32-bit IP address */
74198937Sdes	if (li->hostaddr.sa.sa_family == AF_INET)
74298937Sdes		utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
74398937Sdes# endif
744113911Sdes# ifdef HAVE_ADDR_V6_IN_UTMP
745113911Sdes	/* this is just a 128-bit IPv6 address */
746113911Sdes	if (li->hostaddr.sa.sa_family == AF_INET6) {
747113911Sdes		sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
748113911Sdes		memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
749113911Sdes		if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
750113911Sdes			ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
751113911Sdes			ut->ut_addr_v6[1] = 0;
752113911Sdes			ut->ut_addr_v6[2] = 0;
753113911Sdes			ut->ut_addr_v6[3] = 0;
754113911Sdes		}
755113911Sdes	}
756113911Sdes# endif
75798937Sdes# ifdef HAVE_SYSLEN_IN_UTMPX
75898937Sdes	/* ut_syslen is the length of the utx_host string */
75998937Sdes	utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
76098937Sdes# endif
76198937Sdes}
76298937Sdes#endif /* USE_UTMPX || USE_WTMPX */
76398937Sdes
76498937Sdes/**
76598937Sdes ** Low-level utmp functions
76698937Sdes **/
76798937Sdes
76898937Sdes/* FIXME: (ATL) utmp_write_direct needs testing */
76998937Sdes#ifdef USE_UTMP
77098937Sdes
77198937Sdes/* if we can, use pututline() etc. */
77298937Sdes# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
77398937Sdes	defined(HAVE_PUTUTLINE)
77498937Sdes#  define UTMP_USE_LIBRARY
77598937Sdes# endif
77698937Sdes
77798937Sdes
77898937Sdes/* write a utmp entry with the system's help (pututline() and pals) */
77998937Sdes# ifdef UTMP_USE_LIBRARY
78098937Sdesstatic int
78198937Sdesutmp_write_library(struct logininfo *li, struct utmp *ut)
78298937Sdes{
78398937Sdes	setutent();
78498937Sdes	pututline(ut);
78598937Sdes
78698937Sdes#  ifdef HAVE_ENDUTENT
78798937Sdes	endutent();
78898937Sdes#  endif
78998937Sdes	return 1;
79098937Sdes}
79198937Sdes# else /* UTMP_USE_LIBRARY */
79298937Sdes
79398937Sdes/* write a utmp entry direct to the file */
79498937Sdes/* This is a slightly modification of code in OpenBSD's login.c */
79598937Sdesstatic int
79698937Sdesutmp_write_direct(struct logininfo *li, struct utmp *ut)
79798937Sdes{
79898937Sdes	struct utmp old_ut;
79998937Sdes	register int fd;
80098937Sdes	int tty;
80198937Sdes
80298937Sdes	/* FIXME: (ATL) ttyslot() needs local implementation */
80398937Sdes
80498937Sdes#if defined(HAVE_GETTTYENT)
80598937Sdes	register struct ttyent *ty;
80698937Sdes
80798937Sdes	tty=0;
80898937Sdes
80998937Sdes	setttyent();
81098937Sdes	while ((struct ttyent *)0 != (ty = getttyent())) {
81198937Sdes		tty++;
81298937Sdes		if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
81398937Sdes			break;
81498937Sdes	}
81598937Sdes	endttyent();
81698937Sdes
81798937Sdes	if((struct ttyent *)0 == ty) {
818124211Sdes		logit("utmp_write_entry: tty not found");
81998937Sdes		return(1);
82098937Sdes	}
82198937Sdes#else /* FIXME */
82298937Sdes
82398937Sdes	tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
82498937Sdes
82598937Sdes#endif /* HAVE_GETTTYENT */
82698937Sdes
82798937Sdes	if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
82898937Sdes		(void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
82998937Sdes		/*
83098937Sdes		 * Prevent luser from zero'ing out ut_host.
83198937Sdes		 * If the new ut_line is empty but the old one is not
83298937Sdes		 * and ut_line and ut_name match, preserve the old ut_line.
83398937Sdes		 */
83498937Sdes		if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
83598937Sdes			(ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
83698937Sdes			(strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
83798937Sdes			(strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
83898937Sdes			(void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
83998937Sdes		}
84098937Sdes
84198937Sdes		(void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
842124211Sdes		if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut))
843124211Sdes			logit("utmp_write_direct: error writing %s: %s",
84498937Sdes			    UTMP_FILE, strerror(errno));
84598937Sdes
84698937Sdes		(void)close(fd);
84798937Sdes		return 1;
84898937Sdes	} else {
84998937Sdes		return 0;
85098937Sdes	}
85198937Sdes}
85298937Sdes# endif /* UTMP_USE_LIBRARY */
85398937Sdes
85498937Sdesstatic int
85598937Sdesutmp_perform_login(struct logininfo *li)
85698937Sdes{
85798937Sdes	struct utmp ut;
85898937Sdes
85998937Sdes	construct_utmp(li, &ut);
86098937Sdes# ifdef UTMP_USE_LIBRARY
86198937Sdes	if (!utmp_write_library(li, &ut)) {
862124211Sdes		logit("utmp_perform_login: utmp_write_library() failed");
86398937Sdes		return 0;
86498937Sdes	}
86598937Sdes# else
86698937Sdes	if (!utmp_write_direct(li, &ut)) {
867124211Sdes		logit("utmp_perform_login: utmp_write_direct() failed");
86898937Sdes		return 0;
86998937Sdes	}
87098937Sdes# endif
87198937Sdes	return 1;
87298937Sdes}
87398937Sdes
87498937Sdes
87598937Sdesstatic int
87698937Sdesutmp_perform_logout(struct logininfo *li)
87798937Sdes{
87898937Sdes	struct utmp ut;
87998937Sdes
88098937Sdes	construct_utmp(li, &ut);
88198937Sdes# ifdef UTMP_USE_LIBRARY
88298937Sdes	if (!utmp_write_library(li, &ut)) {
883124211Sdes		logit("utmp_perform_logout: utmp_write_library() failed");
88498937Sdes		return 0;
88598937Sdes	}
88698937Sdes# else
88798937Sdes	if (!utmp_write_direct(li, &ut)) {
888124211Sdes		logit("utmp_perform_logout: utmp_write_direct() failed");
88998937Sdes		return 0;
89098937Sdes	}
89198937Sdes# endif
89298937Sdes	return 1;
89398937Sdes}
89498937Sdes
89598937Sdes
89698937Sdesint
89798937Sdesutmp_write_entry(struct logininfo *li)
89898937Sdes{
89998937Sdes	switch(li->type) {
90098937Sdes	case LTYPE_LOGIN:
90198937Sdes		return utmp_perform_login(li);
90298937Sdes
90398937Sdes	case LTYPE_LOGOUT:
90498937Sdes		return utmp_perform_logout(li);
90598937Sdes
90698937Sdes	default:
907124211Sdes		logit("utmp_write_entry: invalid type field");
90898937Sdes		return 0;
90998937Sdes	}
91098937Sdes}
91198937Sdes#endif /* USE_UTMP */
91298937Sdes
91398937Sdes
91498937Sdes/**
91598937Sdes ** Low-level utmpx functions
91698937Sdes **/
91798937Sdes
91898937Sdes/* not much point if we don't want utmpx entries */
91998937Sdes#ifdef USE_UTMPX
92098937Sdes
92198937Sdes/* if we have the wherewithall, use pututxline etc. */
92298937Sdes# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
92398937Sdes	defined(HAVE_PUTUTXLINE)
92498937Sdes#  define UTMPX_USE_LIBRARY
92598937Sdes# endif
92698937Sdes
92798937Sdes
92898937Sdes/* write a utmpx entry with the system's help (pututxline() and pals) */
92998937Sdes# ifdef UTMPX_USE_LIBRARY
93098937Sdesstatic int
93198937Sdesutmpx_write_library(struct logininfo *li, struct utmpx *utx)
93298937Sdes{
93398937Sdes	setutxent();
93498937Sdes	pututxline(utx);
93598937Sdes
93698937Sdes#  ifdef HAVE_ENDUTXENT
93798937Sdes	endutxent();
93898937Sdes#  endif
93998937Sdes	return 1;
94098937Sdes}
94198937Sdes
94298937Sdes# else /* UTMPX_USE_LIBRARY */
94398937Sdes
94498937Sdes/* write a utmp entry direct to the file */
94598937Sdesstatic int
94698937Sdesutmpx_write_direct(struct logininfo *li, struct utmpx *utx)
94798937Sdes{
948124211Sdes	logit("utmpx_write_direct: not implemented!");
94998937Sdes	return 0;
95098937Sdes}
95198937Sdes# endif /* UTMPX_USE_LIBRARY */
95298937Sdes
95398937Sdesstatic int
95498937Sdesutmpx_perform_login(struct logininfo *li)
95598937Sdes{
95698937Sdes	struct utmpx utx;
95798937Sdes
95898937Sdes	construct_utmpx(li, &utx);
95998937Sdes# ifdef UTMPX_USE_LIBRARY
96098937Sdes	if (!utmpx_write_library(li, &utx)) {
961124211Sdes		logit("utmpx_perform_login: utmp_write_library() failed");
96298937Sdes		return 0;
96398937Sdes	}
96498937Sdes# else
96598937Sdes	if (!utmpx_write_direct(li, &ut)) {
966124211Sdes		logit("utmpx_perform_login: utmp_write_direct() failed");
96798937Sdes		return 0;
96898937Sdes	}
96998937Sdes# endif
97098937Sdes	return 1;
97198937Sdes}
97298937Sdes
97398937Sdes
97498937Sdesstatic int
97598937Sdesutmpx_perform_logout(struct logininfo *li)
97698937Sdes{
97798937Sdes	struct utmpx utx;
97898937Sdes
97998937Sdes	construct_utmpx(li, &utx);
98098937Sdes# ifdef HAVE_ID_IN_UTMPX
98198937Sdes	line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
98298937Sdes# endif
98398937Sdes# ifdef HAVE_TYPE_IN_UTMPX
98498937Sdes	utx.ut_type = DEAD_PROCESS;
98598937Sdes# endif
98698937Sdes
98798937Sdes# ifdef UTMPX_USE_LIBRARY
98898937Sdes	utmpx_write_library(li, &utx);
98998937Sdes# else
99098937Sdes	utmpx_write_direct(li, &utx);
99198937Sdes# endif
99298937Sdes	return 1;
99398937Sdes}
99498937Sdes
99598937Sdesint
99698937Sdesutmpx_write_entry(struct logininfo *li)
99798937Sdes{
99898937Sdes	switch(li->type) {
99998937Sdes	case LTYPE_LOGIN:
100098937Sdes		return utmpx_perform_login(li);
100198937Sdes	case LTYPE_LOGOUT:
100298937Sdes		return utmpx_perform_logout(li);
100398937Sdes	default:
1004124211Sdes		logit("utmpx_write_entry: invalid type field");
100598937Sdes		return 0;
100698937Sdes	}
100798937Sdes}
100898937Sdes#endif /* USE_UTMPX */
100998937Sdes
101098937Sdes
101198937Sdes/**
101298937Sdes ** Low-level wtmp functions
101398937Sdes **/
101498937Sdes
101598937Sdes#ifdef USE_WTMP
101698937Sdes
101798937Sdes/* write a wtmp entry direct to the end of the file */
101898937Sdes/* This is a slight modification of code in OpenBSD's logwtmp.c */
101998937Sdesstatic int
102098937Sdeswtmp_write(struct logininfo *li, struct utmp *ut)
102198937Sdes{
102298937Sdes	struct stat buf;
102398937Sdes	int fd, ret = 1;
102498937Sdes
102598937Sdes	if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1026124211Sdes		logit("wtmp_write: problem writing %s: %s",
102798937Sdes		    WTMP_FILE, strerror(errno));
102898937Sdes		return 0;
102998937Sdes	}
103098937Sdes	if (fstat(fd, &buf) == 0)
1031124211Sdes		if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
103298937Sdes			ftruncate(fd, buf.st_size);
1033124211Sdes			logit("wtmp_write: problem writing %s: %s",
103498937Sdes			    WTMP_FILE, strerror(errno));
103598937Sdes			ret = 0;
103698937Sdes		}
103798937Sdes	(void)close(fd);
103898937Sdes	return ret;
103998937Sdes}
104098937Sdes
104198937Sdesstatic int
104298937Sdeswtmp_perform_login(struct logininfo *li)
104398937Sdes{
104498937Sdes	struct utmp ut;
104598937Sdes
104698937Sdes	construct_utmp(li, &ut);
104798937Sdes	return wtmp_write(li, &ut);
104898937Sdes}
104998937Sdes
105098937Sdes
105198937Sdesstatic int
105298937Sdeswtmp_perform_logout(struct logininfo *li)
105398937Sdes{
105498937Sdes	struct utmp ut;
105598937Sdes
105698937Sdes	construct_utmp(li, &ut);
105798937Sdes	return wtmp_write(li, &ut);
105898937Sdes}
105998937Sdes
106098937Sdes
106198937Sdesint
106298937Sdeswtmp_write_entry(struct logininfo *li)
106398937Sdes{
106498937Sdes	switch(li->type) {
106598937Sdes	case LTYPE_LOGIN:
106698937Sdes		return wtmp_perform_login(li);
106798937Sdes	case LTYPE_LOGOUT:
106898937Sdes		return wtmp_perform_logout(li);
106998937Sdes	default:
1070124211Sdes		logit("wtmp_write_entry: invalid type field");
107198937Sdes		return 0;
107298937Sdes	}
107398937Sdes}
107498937Sdes
107598937Sdes
107698937Sdes/* Notes on fetching login data from wtmp/wtmpx
107798937Sdes *
107898937Sdes * Logouts are usually recorded with (amongst other things) a blank
107998937Sdes * username on a given tty line.  However, some systems (HP-UX is one)
108098937Sdes * leave all fields set, but change the ut_type field to DEAD_PROCESS.
108198937Sdes *
108298937Sdes * Since we're only looking for logins here, we know that the username
108398937Sdes * must be set correctly. On systems that leave it in, we check for
108498937Sdes * ut_type==USER_PROCESS (indicating a login.)
108598937Sdes *
108698937Sdes * Portability: Some systems may set something other than USER_PROCESS
108798937Sdes * to indicate a login process. I don't know of any as I write. Also,
108898937Sdes * it's possible that some systems may both leave the username in
108998937Sdes * place and not have ut_type.
109098937Sdes */
109198937Sdes
109298937Sdes/* return true if this wtmp entry indicates a login */
109398937Sdesstatic int
109498937Sdeswtmp_islogin(struct logininfo *li, struct utmp *ut)
109598937Sdes{
109698937Sdes	if (strncmp(li->username, ut->ut_name,
109798937Sdes		MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
109898937Sdes# ifdef HAVE_TYPE_IN_UTMP
109998937Sdes		if (ut->ut_type & USER_PROCESS)
110098937Sdes			return 1;
110198937Sdes# else
110298937Sdes		return 1;
110398937Sdes# endif
110498937Sdes	}
110598937Sdes	return 0;
110698937Sdes}
110798937Sdes
110898937Sdesint
110998937Sdeswtmp_get_entry(struct logininfo *li)
111098937Sdes{
111198937Sdes	struct stat st;
111298937Sdes	struct utmp ut;
111398937Sdes	int fd, found=0;
111498937Sdes
111598937Sdes	/* Clear the time entries in our logininfo */
111698937Sdes	li->tv_sec = li->tv_usec = 0;
111798937Sdes
111898937Sdes	if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1119124211Sdes		logit("wtmp_get_entry: problem opening %s: %s",
112098937Sdes		    WTMP_FILE, strerror(errno));
112198937Sdes		return 0;
112298937Sdes	}
112398937Sdes	if (fstat(fd, &st) != 0) {
1124124211Sdes		logit("wtmp_get_entry: couldn't stat %s: %s",
112598937Sdes		    WTMP_FILE, strerror(errno));
112698937Sdes		close(fd);
112798937Sdes		return 0;
112898937Sdes	}
112998937Sdes
113098937Sdes	/* Seek to the start of the last struct utmp */
113198937Sdes	if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
113298937Sdes		/* Looks like we've got a fresh wtmp file */
113398937Sdes		close(fd);
113498937Sdes		return 0;
113598937Sdes	}
113698937Sdes
113798937Sdes	while (!found) {
113898937Sdes		if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
1139124211Sdes			logit("wtmp_get_entry: read of %s failed: %s",
114098937Sdes			    WTMP_FILE, strerror(errno));
114198937Sdes			close (fd);
114298937Sdes			return 0;
114398937Sdes		}
114498937Sdes		if ( wtmp_islogin(li, &ut) ) {
114598937Sdes			found = 1;
114698937Sdes			/* We've already checked for a time in struct
114798937Sdes			 * utmp, in login_getlast(). */
114898937Sdes# ifdef HAVE_TIME_IN_UTMP
114998937Sdes			li->tv_sec = ut.ut_time;
115098937Sdes# else
115198937Sdes#  if HAVE_TV_IN_UTMP
115298937Sdes			li->tv_sec = ut.ut_tv.tv_sec;
115398937Sdes#  endif
115498937Sdes# endif
115598937Sdes			line_fullname(li->line, ut.ut_line,
115698937Sdes				      MIN_SIZEOF(li->line, ut.ut_line));
115798937Sdes# ifdef HAVE_HOST_IN_UTMP
115898937Sdes			strlcpy(li->hostname, ut.ut_host,
115998937Sdes				MIN_SIZEOF(li->hostname, ut.ut_host));
116098937Sdes# endif
116198937Sdes			continue;
116298937Sdes		}
116398937Sdes		/* Seek back 2 x struct utmp */
116498937Sdes		if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
116598937Sdes			/* We've found the start of the file, so quit */
116698937Sdes			close (fd);
116798937Sdes			return 0;
116898937Sdes		}
116998937Sdes	}
117098937Sdes
117198937Sdes	/* We found an entry. Tidy up and return */
117298937Sdes	close(fd);
117398937Sdes	return 1;
117498937Sdes}
117598937Sdes# endif /* USE_WTMP */
117698937Sdes
117798937Sdes
117898937Sdes/**
117998937Sdes ** Low-level wtmpx functions
118098937Sdes **/
118198937Sdes
118298937Sdes#ifdef USE_WTMPX
118398937Sdes/* write a wtmpx entry direct to the end of the file */
118498937Sdes/* This is a slight modification of code in OpenBSD's logwtmp.c */
118598937Sdesstatic int
118698937Sdeswtmpx_write(struct logininfo *li, struct utmpx *utx)
118798937Sdes{
1188126277Sdes#ifndef HAVE_UPDWTMPX
118998937Sdes	struct stat buf;
119098937Sdes	int fd, ret = 1;
119198937Sdes
119298937Sdes	if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1193124211Sdes		logit("wtmpx_write: problem opening %s: %s",
119498937Sdes		    WTMPX_FILE, strerror(errno));
119598937Sdes		return 0;
119698937Sdes	}
119798937Sdes
119898937Sdes	if (fstat(fd, &buf) == 0)
1199124211Sdes		if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
120098937Sdes			ftruncate(fd, buf.st_size);
1201124211Sdes			logit("wtmpx_write: problem writing %s: %s",
120298937Sdes			    WTMPX_FILE, strerror(errno));
120398937Sdes			ret = 0;
120498937Sdes		}
120598937Sdes	(void)close(fd);
120698937Sdes
120798937Sdes	return ret;
1208126277Sdes#else
1209126277Sdes	updwtmpx(WTMPX_FILE, utx);
1210126277Sdes	return 1;
1211126277Sdes#endif
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:
1244124211Sdes		logit("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) {
1280124211Sdes		logit("wtmpx_get_entry: problem opening %s: %s",
128198937Sdes		    WTMPX_FILE, strerror(errno));
128298937Sdes		return 0;
128398937Sdes	}
128498937Sdes	if (fstat(fd, &st) != 0) {
1285124211Sdes		logit("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)) {
1300124211Sdes			logit("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)))) {
1345124211Sdes		logit("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)) {
1364124211Sdes		logit("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:
1387124211Sdes		logit("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) {
1422124211Sdes		logit("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:
1453124211Sdes			logit("lastlog_openseek: %.100s is not a file or directory!",
145498937Sdes			    LASTLOG_FILE);
145598937Sdes			return 0;
145698937Sdes	}
145798937Sdes
1458124211Sdes	*fd = open(lastlog_file, filemode, 0600);
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 ) {
1470124211Sdes			logit("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 */
1492124211Sdes	if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) {
149398937Sdes		close(fd);
1494124211Sdes		logit("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:
1510124211Sdes		logit("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:
1544126277Sdes		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