loginrec.c revision 124211
1204076Spjd/*
2204076Spjd * Copyright (c) 2000 Andre Lucas.  All rights reserved.
3211877Spjd * Portions copyright (c) 1998 Todd C. Miller
4204076Spjd * Portions copyright (c) 1996 Jason Downs
5204076Spjd * Portions copyright (c) 1996 Theo de Raadt
6204076Spjd *
7204076Spjd * Redistribution and use in source and binary forms, with or without
8204076Spjd * modification, are permitted provided that the following conditions
9204076Spjd * are met:
10204076Spjd * 1. Redistributions of source code must retain the above copyright
11204076Spjd *    notice, this list of conditions and the following disclaimer.
12204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
13204076Spjd *    notice, this list of conditions and the following disclaimer in the
14204076Spjd *    documentation and/or other materials provided with the distribution.
15204076Spjd *
16204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17204076Spjd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18204076Spjd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19204076Spjd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20204076Spjd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21204076Spjd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22204076Spjd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23204076Spjd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24204076Spjd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25204076Spjd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26204076Spjd */
27204076Spjd
28204076Spjd/**
29204076Spjd ** loginrec.c:  platform-independent login recording and lastlog retrieval
30204076Spjd **/
31204076Spjd
32204076Spjd/*
33204076Spjd  The new login code explained
34204076Spjd  ============================
35204076Spjd
36204076Spjd  This code attempts to provide a common interface to login recording
37204076Spjd  (utmp and friends) and last login time retrieval.
38204076Spjd
39204076Spjd  Its primary means of achieving this is to use 'struct logininfo', a
40204076Spjd  union of all the useful fields in the various different types of
41204076Spjd  system login record structures one finds on UNIX variants.
42204076Spjd
43204076Spjd  We depend on autoconf to define which recording methods are to be
44204076Spjd  used, and which fields are contained in the relevant data structures
45213009Spjd  on the local system. Many C preprocessor symbols affect which code
46204076Spjd  gets compiled here.
47204076Spjd
48204076Spjd  The code is designed to make it easy to modify a particular
49204076Spjd  recording method, without affecting other methods nor requiring so
50204076Spjd  many nested conditional compilation blocks as were commonplace in
51204076Spjd  the old code.
52204076Spjd
53204076Spjd  For login recording, we try to use the local system's libraries as
54204076Spjd  these are clearly most likely to work correctly. For utmp systems
55204076Spjd  this usually means login() and logout() or setutent() etc., probably
56204076Spjd  in libutil, along with logwtmp() etc. On these systems, we fall back
57212038Spjd  to writing the files directly if we have to, though this method
58204076Spjd  requires very thorough testing so we do not corrupt local auditing
59204076Spjd  information. These files and their access methods are very system
60204076Spjd  specific indeed.
61211977Spjd
62204076Spjd  For utmpx systems, the corresponding library functions are
63204076Spjd  setutxent() etc. To the author's knowledge, all utmpx systems have
64204076Spjd  these library functions and so no direct write is attempted. If such
65204076Spjd  a system exists and needs support, direct analogues of the [uw]tmp
66204076Spjd  code should suffice.
67204076Spjd
68219864Spjd  Retrieving the time of last login ('lastlog') is in some ways even
69219864Spjd  more problemmatic than login recording. Some systems provide a
70204076Spjd  simple table of all users which we seek based on uid and retrieve a
71204076Spjd  relatively standard structure. Others record the same information in
72204076Spjd  a directory with a separate file, and others don't record the
73204076Spjd  information separately at all. For systems in the latter category,
74204076Spjd  we look backwards in the wtmp or wtmpx file for the last login entry
75204076Spjd  for our user. Naturally this is slower and on busy systems could
76204076Spjd  incur a significant performance penalty.
77204076Spjd
78211984Spjd  Calling the new code
79211984Spjd  --------------------
80204076Spjd
81204076Spjd  In OpenSSH all login recording and retrieval is performed in
82204076Spjd  login.c. Here you'll find working examples. Also, in the logintest.c
83204076Spjd  program there are more examples.
84204076Spjd
85204076Spjd  Internal handler calling method
86204076Spjd  -------------------------------
87204076Spjd
88204076Spjd  When a call is made to login_login() or login_logout(), both
89204076Spjd  routines set a struct logininfo flag defining which action (log in,
90204076Spjd  or log out) is to be taken. They both then call login_write(), which
91204076Spjd  calls whichever of the many structure-specific handlers autoconf
92204076Spjd  selects for the local system.
93204076Spjd
94204076Spjd  The handlers themselves handle system data structure specifics. Both
95204076Spjd  struct utmp and struct utmpx have utility functions (see
96204076Spjd  construct_utmp*()) to try to make it simpler to add extra systems
97204076Spjd  that introduce new features to either structure.
98204076Spjd
99204076Spjd  While it may seem terribly wasteful to replicate so much similar
100204076Spjd  code for each method, experience has shown that maintaining code to
101204076Spjd  write both struct utmp and utmpx in one function, whilst maintaining
102204076Spjd  support for all systems whether they have library support or not, is
103204076Spjd  a difficult and time-consuming task.
104204076Spjd
105204076Spjd  Lastlog support proceeds similarly. Functions login_get_lastlog()
106204076Spjd  (and its OpenSSH-tuned friend login_get_lastlog_time()) call
107204076Spjd  getlast_entry(), which tries one of three methods to find the last
108204076Spjd  login time. It uses local system lastlog support if it can,
109204076Spjd  otherwise it tries wtmp or wtmpx before giving up and returning 0,
110211877Spjd  meaning "tilt".
111211877Spjd
112211877Spjd  Maintenance
113211877Spjd  -----------
114211877Spjd
115211877Spjd  In many cases it's possible to tweak autoconf to select the correct
116211877Spjd  methods for a particular platform, either by improving the detection
117211877Spjd  code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE
118211877Spjd  symbols for the platform.
119211877Spjd
120211877Spjd  Use logintest to check which symbols are defined before modifying
121211877Spjd  configure.ac and loginrec.c. (You have to build logintest yourself
122211877Spjd  with 'make logintest' as it's not built by default.)
123211877Spjd
124211877Spjd  Otherwise, patches to the specific method(s) are very helpful!
125211877Spjd
126211877Spjd*/
127211877Spjd
128211877Spjd/**
129211877Spjd ** TODO:
130204076Spjd **   homegrown ttyslot()
131204076Spjd **   test, test, test
132204076Spjd **
133204076Spjd ** Platform status:
134204076Spjd ** ----------------
135204076Spjd **
136204076Spjd ** Known good:
137204076Spjd **   Linux (Redhat 6.2, Debian)
138204076Spjd **   Solaris
139204076Spjd **   HP-UX 10.20 (gcc only)
140204076Spjd **   IRIX
141204076Spjd **   NeXT - M68k/HPPA/Sparc (4.2/3.3)
142204076Spjd **
143204076Spjd ** Testing required: Please send reports!
144204076Spjd **   NetBSD
145204076Spjd **   HP-UX 11
146204076Spjd **   AIX
147204076Spjd **
148204076Spjd ** Platforms with known problems:
149204076Spjd **   Some variants of Slackware Linux
150204076Spjd **
151204076Spjd **/
152204076Spjd
153204076Spjd#include "includes.h"
154204076Spjd
155210879Spjd#include "ssh.h"
156210879Spjd#include "xmalloc.h"
157210879Spjd#include "loginrec.h"
158204076Spjd#include "log.h"
159204076Spjd#include "atomicio.h"
160204076Spjd
161204076SpjdRCSID("$Id: loginrec.c,v 1.52 2003/07/06 05:20:46 dtucker Exp $");
162210879SpjdRCSID("$FreeBSD: head/crypto/openssh/loginrec.c 124211 2004-01-07 11:16:27Z des $");
163210879Spjd
164210879Spjd#ifdef HAVE_UTIL_H
165204076Spjd#  include <util.h>
166204076Spjd#endif
167204076Spjd
168204076Spjd#ifdef HAVE_LIBUTIL_H
169204076Spjd#   include <libutil.h>
170204076Spjd#endif
171204076Spjd
172204076Spjd/**
173204076Spjd ** prototypes for helper functions in this file
174204076Spjd **/
175204076Spjd
176204076Spjd#if HAVE_UTMP_H
177204076Spjdvoid set_utmp_time(struct logininfo *li, struct utmp *ut);
178204076Spjdvoid construct_utmp(struct logininfo *li, struct utmp *ut);
179204076Spjd#endif
180204076Spjd
181204076Spjd#ifdef HAVE_UTMPX_H
182204076Spjdvoid set_utmpx_time(struct logininfo *li, struct utmpx *ut);
183204076Spjdvoid construct_utmpx(struct logininfo *li, struct utmpx *ut);
184204076Spjd#endif
185204076Spjd
186223181Strocinyint utmp_write_entry(struct logininfo *li);
187220271Spjdint utmpx_write_entry(struct logininfo *li);
188220271Spjdint wtmp_write_entry(struct logininfo *li);
189220271Spjdint wtmpx_write_entry(struct logininfo *li);
190223181Strocinyint lastlog_write_entry(struct logininfo *li);
191220271Spjdint syslogin_write_entry(struct logininfo *li);
192204076Spjd
193204076Spjdint getlast_entry(struct logininfo *li);
194204076Spjdint lastlog_get_entry(struct logininfo *li);
195204076Spjdint wtmp_get_entry(struct logininfo *li);
196204076Spjdint wtmpx_get_entry(struct logininfo *li);
197204076Spjd
198204076Spjd/* pick the shortest string */
199204076Spjd#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
200204076Spjd
201204076Spjd/**
202204076Spjd ** platform-independent login functions
203204076Spjd **/
204204076Spjd
205204076Spjd/* login_login(struct logininfo *)     -Record a login
206204076Spjd *
207204076Spjd * Call with a pointer to a struct logininfo initialised with
208204076Spjd * login_init_entry() or login_alloc_entry()
209204076Spjd *
210204076Spjd * Returns:
211204076Spjd *  >0 if successful
212204076Spjd *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
213204076Spjd */
214204076Spjdint
215204076Spjdlogin_login (struct logininfo *li)
216204076Spjd{
217204076Spjd	li->type = LTYPE_LOGIN;
218204076Spjd	return login_write(li);
219204076Spjd}
220204076Spjd
221204076Spjd
222204076Spjd/* login_logout(struct logininfo *)     - Record a logout
223204076Spjd *
224204076Spjd * Call as with login_login()
225204076Spjd *
226204076Spjd * Returns:
227204076Spjd *  >0 if successful
228204076Spjd *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
229204076Spjd */
230204076Spjdint
231204076Spjdlogin_logout(struct logininfo *li)
232204076Spjd{
233204076Spjd	li->type = LTYPE_LOGOUT;
234204076Spjd	return login_write(li);
235204076Spjd}
236204076Spjd
237204076Spjd/* login_get_lastlog_time(int)           - Retrieve the last login time
238204076Spjd *
239204076Spjd * Retrieve the last login time for the given uid. Will try to use the
240204076Spjd * system lastlog facilities if they are available, but will fall back
241204076Spjd * to looking in wtmp/wtmpx if necessary
242204076Spjd *
243204076Spjd * Returns:
244204076Spjd *   0 on failure, or if user has never logged in
245204076Spjd *   Time in seconds from the epoch if successful
246204076Spjd *
247204076Spjd * Useful preprocessor symbols:
248204076Spjd *   DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
249204076Spjd *                    info
250214284Spjd *   USE_LASTLOG: If set, indicates the presence of system lastlog
251214284Spjd *                facilities. If this and DISABLE_LASTLOG are not set,
252214284Spjd *                try to retrieve lastlog information from wtmp/wtmpx.
253214284Spjd */
254204076Spjdunsigned int
255218138Spjdlogin_get_lastlog_time(const int uid)
256204076Spjd{
257204076Spjd	struct logininfo li;
258204076Spjd
259214284Spjd	if (login_get_lastlog(&li, uid))
260214284Spjd		return li.tv_sec;
261214284Spjd	else
262214284Spjd		return 0;
263214284Spjd}
264214284Spjd
265214284Spjd/* login_get_lastlog(struct logininfo *, int)   - Retrieve a lastlog entry
266220865Spjd *
267204076Spjd * Retrieve a logininfo structure populated (only partially) with
268219830Spjd * information from the system lastlog data, or from wtmp/wtmpx if no
269219830Spjd * system lastlog information exists.
270219830Spjd *
271219830Spjd * Note this routine must be given a pre-allocated logininfo.
272219830Spjd *
273219830Spjd * Returns:
274219830Spjd *  >0: A pointer to your struct logininfo if successful
275219830Spjd *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
276219830Spjd *
277219830Spjd */
278219830Spjdstruct logininfo *
279219830Spjdlogin_get_lastlog(struct logininfo *li, const int uid)
280219831Spjd{
281219830Spjd	struct passwd *pw;
282204076Spjd
283204076Spjd	memset(li, '\0', sizeof(*li));
284204076Spjd	li->uid = uid;
285204076Spjd
286219843Spjd	/*
287204076Spjd	 * If we don't have a 'real' lastlog, we need the username to
288204076Spjd	 * reliably search wtmp(x) for the last login (see
289204076Spjd	 * wtmp_get_entry().)
290204076Spjd	 */
291204076Spjd	pw = getpwuid(uid);
292204076Spjd	if (pw == NULL)
293204076Spjd		fatal("login_get_lastlog: Cannot find account for uid %i", uid);
294204076Spjd
295204076Spjd	/* No MIN_SIZEOF here - we absolutely *must not* truncate the
296204076Spjd	 * username */
297204076Spjd	strlcpy(li->username, pw->pw_name, sizeof(li->username));
298204076Spjd
299204076Spjd	if (getlast_entry(li))
300204076Spjd		return li;
301204076Spjd	else
302204076Spjd		return NULL;
303204076Spjd}
304204076Spjd
305204076Spjd
306204076Spjd/* login_alloc_entry(int, char*, char*, char*)    - Allocate and initialise
307204076Spjd *                                                  a logininfo structure
308204076Spjd *
309204076Spjd * This function creates a new struct logininfo, a data structure
310204076Spjd * meant to carry the information required to portably record login info.
311204076Spjd *
312204076Spjd * Returns a pointer to a newly created struct logininfo. If memory
313204076Spjd * allocation fails, the program halts.
314204076Spjd */
315204076Spjdstruct
316204076Spjdlogininfo *login_alloc_entry(int pid, const char *username,
317204076Spjd			     const char *hostname, const char *line)
318204076Spjd{
319204076Spjd	struct logininfo *newli;
320204076Spjd
321204076Spjd	newli = (struct logininfo *) xmalloc (sizeof(*newli));
322204076Spjd	(void)login_init_entry(newli, pid, username, hostname, line);
323204076Spjd	return newli;
324204076Spjd}
325204076Spjd
326204076Spjd
327204076Spjd/* login_free_entry(struct logininfo *)    - free struct memory */
328204076Spjdvoid
329218138Spjdlogin_free_entry(struct logininfo *li)
330204076Spjd{
331204076Spjd	xfree(li);
332204076Spjd}
333204076Spjd
334204076Spjd
335204076Spjd/* login_init_entry(struct logininfo *, int, char*, char*, char*)
336204076Spjd *                                        - initialise a struct logininfo
337204076Spjd *
338204076Spjd * Populates a new struct logininfo, a data structure meant to carry
339204076Spjd * the information required to portably record login info.
340204076Spjd *
341204076Spjd * Returns: 1
342204076Spjd */
343204076Spjdint
344204076Spjdlogin_init_entry(struct logininfo *li, int pid, const char *username,
345204076Spjd		 const char *hostname, const char *line)
346204076Spjd{
347204076Spjd	struct passwd *pw;
348220007Spjd
349204076Spjd	memset(li, 0, sizeof(*li));
350214276Spjd
351204076Spjd	li->pid = pid;
352204076Spjd
353214275Spjd	/* set the line information */
354214275Spjd	if (line)
355209182Spjd		line_fullname(li->line, line, sizeof(li->line));
356223181Strociny
357220271Spjd	if (username) {
358220271Spjd		strlcpy(li->username, username, sizeof(li->username));
359220271Spjd		pw = getpwnam(li->username);
360223181Strociny		if (pw == NULL)
361204076Spjd			fatal("login_init_entry: Cannot find user \"%s\"", li->username);
362204076Spjd		li->uid = pw->pw_uid;
363204076Spjd	}
364212038Spjd
365204076Spjd	if (hostname)
366204076Spjd		strlcpy(li->hostname, hostname, sizeof(li->hostname));
367204076Spjd
368204076Spjd	return 1;
369204076Spjd}
370204076Spjd
371204076Spjd/* login_set_current_time(struct logininfo *)    - set the current time
372213009Spjd *
373204076Spjd * Set the current time in a logininfo structure. This function is
374204076Spjd * meant to eliminate the need to deal with system dependencies for
375219482Strociny * time handling.
376204076Spjd */
377204076Spjdvoid
378204076Spjdlogin_set_current_time(struct logininfo *li)
379204076Spjd{
380219818Spjd	struct timeval tv;
381204076Spjd
382204076Spjd	gettimeofday(&tv, NULL);
383204076Spjd
384204076Spjd	li->tv_sec = tv.tv_sec;
385212038Spjd	li->tv_usec = tv.tv_usec;
386212038Spjd}
387212038Spjd
388219818Spjd/* copy a sockaddr_* into our logininfo */
389212038Spjdvoid
390212038Spjdlogin_set_addr(struct logininfo *li, const struct sockaddr *sa,
391212038Spjd	       const unsigned int sa_size)
392212038Spjd{
393204076Spjd	unsigned int bufsize = sa_size;
394204076Spjd
395204076Spjd	/* make sure we don't overrun our union */
396204076Spjd	if (sizeof(li->hostaddr) < sa_size)
397204076Spjd		bufsize = sizeof(li->hostaddr);
398204076Spjd
399204076Spjd	memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
400204076Spjd}
401204076Spjd
402204076Spjd
403204076Spjd/**
404204076Spjd ** login_write: Call low-level recording functions based on autoconf
405204076Spjd ** results
406212038Spjd **/
407212038Spjdint
408218043Spjdlogin_write (struct logininfo *li)
409218043Spjd{
410204076Spjd#ifndef HAVE_CYGWIN
411204076Spjd	if ((int)geteuid() != 0) {
412204076Spjd	  logit("Attempt to write login records by non-root user (aborting)");
413211977Spjd	  return 1;
414211984Spjd	}
415218043Spjd#endif
416219482Strociny
417211984Spjd	/* set the timestamp */
418218043Spjd	login_set_current_time(li);
419218043Spjd#ifdef USE_LOGIN
420218043Spjd	syslogin_write_entry(li);
421218043Spjd#endif
422218043Spjd#ifdef USE_LASTLOG
423204076Spjd	if (li->type == LTYPE_LOGIN) {
424218045Spjd		lastlog_write_entry(li);
425218045Spjd	}
426218043Spjd#endif
427219482Strociny#ifdef USE_UTMP
428218043Spjd	utmp_write_entry(li);
429220005Spjd#endif
430204076Spjd#ifdef USE_WTMP
431213009Spjd	wtmp_write_entry(li);
432213009Spjd#endif
433210880Spjd#ifdef USE_UTMPX
434207371Spjd	utmpx_write_entry(li);
435219721Strociny#endif
436207371Spjd#ifdef USE_WTMPX
437207371Spjd	wtmpx_write_entry(li);
438207371Spjd#endif
439207371Spjd	return 0;
440204076Spjd}
441213007Spjd
442213007Spjd#ifdef LOGIN_NEEDS_UTMPX
443221899Spjdint
444218049Spjdlogin_utmp_only(struct logininfo *li)
445218214Spjd{
446218049Spjd	li->type = LTYPE_LOGIN;
447213007Spjd	login_set_current_time(li);
448213007Spjd# ifdef USE_UTMP
449213007Spjd	utmp_write_entry(li);
450213007Spjd# endif
451213007Spjd# ifdef USE_WTMP
452213007Spjd	wtmp_write_entry(li);
453213007Spjd# endif
454213007Spjd# ifdef USE_UTMPX
455213007Spjd	utmpx_write_entry(li);
456218138Spjd# endif
457213007Spjd# ifdef USE_WTMPX
458204076Spjd	wtmpx_write_entry(li);
459212038Spjd# endif
460204076Spjd	return 0;
461204076Spjd}
462218138Spjd#endif
463204076Spjd
464218138Spjd/**
465213007Spjd ** getlast_entry: Call low-level functions to retrieve the last login
466204076Spjd **                time.
467204076Spjd **/
468204076Spjd
469204076Spjd/* take the uid in li and return the last login time */
470204076Spjdint
471204076Spjdgetlast_entry(struct logininfo *li)
472204076Spjd{
473204076Spjd#ifdef USE_LASTLOG
474204076Spjd	return(lastlog_get_entry(li));
475204076Spjd#else /* !USE_LASTLOG */
476204076Spjd
477204076Spjd#ifdef DISABLE_LASTLOG
478204076Spjd	/* On some systems we shouldn't even try to obtain last login
479204076Spjd	 * time, e.g. AIX */
480204076Spjd	return 0;
481204076Spjd# else /* DISABLE_LASTLOG */
482204076Spjd	/* Try to retrieve the last login time from wtmp */
483204076Spjd#  if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
484204076Spjd	/* retrieve last login time from utmp */
485204076Spjd	return (wtmp_get_entry(li));
486204076Spjd#  else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */
487204076Spjd	/* If wtmp isn't available, try wtmpx */
488204076Spjd#   if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
489204076Spjd	/* retrieve last login time from utmpx */
490204076Spjd	return (wtmpx_get_entry(li));
491204076Spjd#   else
492204076Spjd	/* Give up: No means of retrieving last login time */
493204076Spjd	return 0;
494204076Spjd#   endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
495204076Spjd#  endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
496204076Spjd# endif /* DISABLE_LASTLOG */
497204076Spjd#endif /* USE_LASTLOG */
498211882Spjd}
499211882Spjd
500211882Spjd
501204076Spjd
502204076Spjd/*
503204076Spjd * 'line' string utility functions
504204076Spjd *
505204076Spjd * These functions process the 'line' string into one of three forms:
506204076Spjd *
507204076Spjd * 1. The full filename (including '/dev')
508204076Spjd * 2. The stripped name (excluding '/dev')
509204076Spjd * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
510204076Spjd *                               /dev/pts/1  -> ts/1 )
511204076Spjd *
512204076Spjd * Form 3 is used on some systems to identify a .tmp.? entry when
513204076Spjd * attempting to remove it. Typically both addition and removal is
514204076Spjd * performed by one application - say, sshd - so as long as the choice
515204076Spjd * uniquely identifies a terminal it's ok.
516204076Spjd */
517204076Spjd
518204076Spjd
519204076Spjd/* line_fullname(): add the leading '/dev/' if it doesn't exist make
520204076Spjd * sure dst has enough space, if not just copy src (ugh) */
521222164Spjdchar *
522211882Spjdline_fullname(char *dst, const char *src, int dstsize)
523211882Spjd{
524204076Spjd	memset(dst, '\0', dstsize);
525204076Spjd	if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) {
526204076Spjd		strlcpy(dst, src, dstsize);
527204076Spjd	} else {
528204076Spjd		strlcpy(dst, "/dev/", dstsize);
529204076Spjd		strlcat(dst, src, dstsize);
530204076Spjd	}
531204076Spjd	return dst;
532204076Spjd}
533204076Spjd
534204076Spjd/* line_stripname(): strip the leading '/dev' if it exists, return dst */
535204076Spjdchar *
536204076Spjdline_stripname(char *dst, const char *src, int dstsize)
537204076Spjd{
538204076Spjd	memset(dst, '\0', dstsize);
539204076Spjd	if (strncmp(src, "/dev/", 5) == 0)
540204076Spjd		strlcpy(dst, src + 5, dstsize);
541204076Spjd	else
542204076Spjd		strlcpy(dst, src, dstsize);
543204076Spjd	return dst;
544204076Spjd}
545204076Spjd
546204076Spjd/* line_abbrevname(): Return the abbreviated (usually four-character)
547204076Spjd * form of the line (Just use the last <dstsize> characters of the
548204076Spjd * full name.)
549204076Spjd *
550204076Spjd * NOTE: use strncpy because we do NOT necessarily want zero
551204076Spjd * termination */
552204076Spjdchar *
553204076Spjdline_abbrevname(char *dst, const char *src, int dstsize)
554204076Spjd{
555204076Spjd	size_t len;
556204076Spjd
557204076Spjd	memset(dst, '\0', dstsize);
558204076Spjd
559204076Spjd	/* Always skip prefix if present */
560204076Spjd	if (strncmp(src, "/dev/", 5) == 0)
561204076Spjd		src += 5;
562204076Spjd
563204076Spjd#ifdef WITH_ABBREV_NO_TTY
564204076Spjd	if (strncmp(src, "tty", 3) == 0)
565204076Spjd		src += 3;
566204076Spjd#endif
567204076Spjd
568204076Spjd	len = strlen(src);
569204076Spjd
570204076Spjd	if (len > 0) {
571204076Spjd		if (((int)len - dstsize) > 0)
572204076Spjd			src +=  ((int)len - dstsize);
573204076Spjd
574204076Spjd		/* note: _don't_ change this to strlcpy */
575204076Spjd		strncpy(dst, src, (size_t)dstsize);
576204076Spjd	}
577204076Spjd
578204076Spjd	return dst;
579204076Spjd}
580204076Spjd
581204076Spjd/**
582212899Spjd ** utmp utility functions
583211984Spjd **
584211984Spjd ** These functions manipulate struct utmp, taking system differences
585211984Spjd ** into account.
586211984Spjd **/
587218138Spjd
588211984Spjd#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
589211984Spjd
590211984Spjd/* build the utmp structure */
591212038Spjdvoid
592211984Spjdset_utmp_time(struct logininfo *li, struct utmp *ut)
593211984Spjd{
594211984Spjd# ifdef HAVE_TV_IN_UTMP
595204076Spjd	ut->ut_tv.tv_sec = li->tv_sec;
596204076Spjd	ut->ut_tv.tv_usec = li->tv_usec;
597204076Spjd# else
598204076Spjd#  ifdef HAVE_TIME_IN_UTMP
599204076Spjd	ut->ut_time = li->tv_sec;
600204076Spjd#  endif
601204076Spjd# endif
602204076Spjd}
603204076Spjd
604204076Spjdvoid
605204076Spjdconstruct_utmp(struct logininfo *li,
606211877Spjd		    struct utmp *ut)
607204076Spjd{
608204076Spjd# ifdef HAVE_ADDR_V6_IN_UTMP
609211984Spjd	struct sockaddr_in6 *sa6;
610204076Spjd#  endif
611204076Spjd	memset(ut, '\0', sizeof(*ut));
612211877Spjd
613211877Spjd	/* First fill out fields used for both logins and logouts */
614211877Spjd
615211877Spjd# ifdef HAVE_ID_IN_UTMP
616211877Spjd	line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
617211877Spjd# endif
618211877Spjd
619222228Spjd# ifdef HAVE_TYPE_IN_UTMP
620222228Spjd	/* This is done here to keep utmp constants out of struct logininfo */
621222228Spjd	switch (li->type) {
622222228Spjd	case LTYPE_LOGIN:
623222228Spjd		ut->ut_type = USER_PROCESS;
624222228Spjd#ifdef _UNICOS
625222228Spjd		cray_set_tmpdir(ut);
626222228Spjd#endif
627222228Spjd		break;
628222228Spjd	case LTYPE_LOGOUT:
629222228Spjd		ut->ut_type = DEAD_PROCESS;
630222228Spjd#ifdef _UNICOS
631222228Spjd		cray_retain_utmp(ut, li->pid);
632222228Spjd#endif
633204076Spjd		break;
634204076Spjd	}
635211882Spjd# endif
636211882Spjd	set_utmp_time(li, ut);
637211882Spjd
638211882Spjd	line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
639211882Spjd
640211882Spjd# ifdef HAVE_PID_IN_UTMP
641211882Spjd	ut->ut_pid = li->pid;
642211882Spjd# endif
643204076Spjd
644204076Spjd	/* If we're logging out, leave all other fields blank */
645211984Spjd	if (li->type == LTYPE_LOGOUT)
646212051Spjd	  return;
647204076Spjd
648204076Spjd	/*
649204076Spjd	 * These fields are only used when logging in, and are blank
650204076Spjd	 * for logouts.
651211877Spjd	 */
652204076Spjd
653204076Spjd	/* Use strncpy because we don't necessarily want null termination */
654204076Spjd	strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
655204076Spjd# ifdef HAVE_HOST_IN_UTMP
656204076Spjd	realhostname_sa(ut->ut_host, sizeof ut->ut_host,
657204076Spjd	    &li->hostaddr.sa, li->hostaddr.sa.sa_len);
658204076Spjd# endif
659204076Spjd# ifdef HAVE_ADDR_IN_UTMP
660204076Spjd	/* this is just a 32-bit IP address */
661204076Spjd	if (li->hostaddr.sa.sa_family == AF_INET)
662204076Spjd		ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
663204076Spjd# endif
664204076Spjd# ifdef HAVE_ADDR_V6_IN_UTMP
665204076Spjd	/* this is just a 128-bit IPv6 address */
666204076Spjd	if (li->hostaddr.sa.sa_family == AF_INET6) {
667211877Spjd		sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
668204076Spjd		memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
669204076Spjd		if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
670204076Spjd			ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
671204076Spjd			ut->ut_addr_v6[1] = 0;
672204076Spjd			ut->ut_addr_v6[2] = 0;
673211877Spjd			ut->ut_addr_v6[3] = 0;
674204076Spjd		}
675204076Spjd	}
676204076Spjd# endif
677204076Spjd}
678204076Spjd#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
679204076Spjd
680204076Spjd/**
681204076Spjd ** utmpx utility functions
682204076Spjd **
683204076Spjd ** These functions manipulate struct utmpx, accounting for system
684204076Spjd ** variations.
685204076Spjd **/
686204076Spjd
687204076Spjd#if defined(USE_UTMPX) || defined (USE_WTMPX)
688204076Spjd/* build the utmpx structure */
689204076Spjdvoid
690204076Spjdset_utmpx_time(struct logininfo *li, struct utmpx *utx)
691204076Spjd{
692204076Spjd# ifdef HAVE_TV_IN_UTMPX
693204076Spjd	utx->ut_tv.tv_sec = li->tv_sec;
694204076Spjd	utx->ut_tv.tv_usec = li->tv_usec;
695204076Spjd# else /* HAVE_TV_IN_UTMPX */
696204076Spjd#  ifdef HAVE_TIME_IN_UTMPX
697204076Spjd	utx->ut_time = li->tv_sec;
698204076Spjd#  endif /* HAVE_TIME_IN_UTMPX */
699204076Spjd# endif /* HAVE_TV_IN_UTMPX */
700204076Spjd}
701204076Spjd
702204076Spjdvoid
703204076Spjdconstruct_utmpx(struct logininfo *li, struct utmpx *utx)
704204076Spjd{
705204076Spjd# ifdef HAVE_ADDR_V6_IN_UTMP
706204076Spjd	struct sockaddr_in6 *sa6;
707204076Spjd#  endif
708204076Spjd	memset(utx, '\0', sizeof(*utx));
709204076Spjd# ifdef HAVE_ID_IN_UTMPX
710204076Spjd	line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
711204076Spjd# endif
712204076Spjd
713204076Spjd	/* this is done here to keep utmp constants out of loginrec.h */
714204076Spjd	switch (li->type) {
715204076Spjd	case LTYPE_LOGIN:
716204076Spjd		utx->ut_type = USER_PROCESS;
717204076Spjd		break;
718204076Spjd	case LTYPE_LOGOUT:
719204076Spjd		utx->ut_type = DEAD_PROCESS;
720204076Spjd		break;
721204076Spjd	}
722204076Spjd	line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
723204076Spjd	set_utmpx_time(li, utx);
724204076Spjd	utx->ut_pid = li->pid;
725204076Spjd	/* strncpy(): Don't necessarily want null termination */
726204076Spjd	strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
727204076Spjd
728204076Spjd	if (li->type == LTYPE_LOGOUT)
729204076Spjd		return;
730204076Spjd
731204076Spjd	/*
732204076Spjd	 * These fields are only used when logging in, and are blank
733204076Spjd	 * for logouts.
734204076Spjd	 */
735204076Spjd
736204076Spjd# ifdef HAVE_HOST_IN_UTMPX
737204076Spjd	strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
738204076Spjd# endif
739204076Spjd# ifdef HAVE_ADDR_IN_UTMPX
740204076Spjd	/* this is just a 32-bit IP address */
741204076Spjd	if (li->hostaddr.sa.sa_family == AF_INET)
742204076Spjd		utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
743204076Spjd# endif
744204076Spjd# ifdef HAVE_ADDR_V6_IN_UTMP
745204076Spjd	/* this is just a 128-bit IPv6 address */
746204076Spjd	if (li->hostaddr.sa.sa_family == AF_INET6) {
747204076Spjd		sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
748204076Spjd		memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
749204076Spjd		if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
750204076Spjd			ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
751211877Spjd			ut->ut_addr_v6[1] = 0;
752204076Spjd			ut->ut_addr_v6[2] = 0;
753204076Spjd			ut->ut_addr_v6[3] = 0;
754204076Spjd		}
755204076Spjd	}
756204076Spjd# endif
757204076Spjd# ifdef HAVE_SYSLEN_IN_UTMPX
758204076Spjd	/* ut_syslen is the length of the utx_host string */
759204076Spjd	utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
760204076Spjd# endif
761204076Spjd}
762204076Spjd#endif /* USE_UTMPX || USE_WTMPX */
763204076Spjd
764204076Spjd/**
765204076Spjd ** Low-level utmp functions
766204076Spjd **/
767204076Spjd
768204076Spjd/* FIXME: (ATL) utmp_write_direct needs testing */
769204076Spjd#ifdef USE_UTMP
770204076Spjd
771211877Spjd/* if we can, use pututline() etc. */
772204076Spjd# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
773204076Spjd	defined(HAVE_PUTUTLINE)
774204076Spjd#  define UTMP_USE_LIBRARY
775204076Spjd# endif
776204076Spjd
777204076Spjd
778204076Spjd/* write a utmp entry with the system's help (pututline() and pals) */
779204076Spjd# ifdef UTMP_USE_LIBRARY
780204076Spjdstatic int
781204076Spjdutmp_write_library(struct logininfo *li, struct utmp *ut)
782204076Spjd{
783204076Spjd	setutent();
784204076Spjd	pututline(ut);
785204076Spjd
786204076Spjd#  ifdef HAVE_ENDUTENT
787204076Spjd	endutent();
788204076Spjd#  endif
789204076Spjd	return 1;
790204076Spjd}
791204076Spjd# else /* UTMP_USE_LIBRARY */
792204076Spjd
793204076Spjd/* write a utmp entry direct to the file */
794204076Spjd/* This is a slightly modification of code in OpenBSD's login.c */
795204076Spjdstatic int
796204076Spjdutmp_write_direct(struct logininfo *li, struct utmp *ut)
797204076Spjd{
798204076Spjd	struct utmp old_ut;
799204076Spjd	register int fd;
800204076Spjd	int tty;
801211984Spjd
802204076Spjd	/* FIXME: (ATL) ttyslot() needs local implementation */
803204076Spjd
804209185Spjd#if defined(HAVE_GETTTYENT)
805204076Spjd	register struct ttyent *ty;
806204076Spjd
807204076Spjd	tty=0;
808211877Spjd
809204076Spjd	setttyent();
810204076Spjd	while ((struct ttyent *)0 != (ty = getttyent())) {
811204076Spjd		tty++;
812204076Spjd		if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
813			break;
814	}
815	endttyent();
816
817	if((struct ttyent *)0 == ty) {
818		logit("utmp_write_entry: tty not found");
819		return(1);
820	}
821#else /* FIXME */
822
823	tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
824
825#endif /* HAVE_GETTTYENT */
826
827	if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
828		(void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
829		/*
830		 * Prevent luser from zero'ing out ut_host.
831		 * If the new ut_line is empty but the old one is not
832		 * and ut_line and ut_name match, preserve the old ut_line.
833		 */
834		if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
835			(ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
836			(strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
837			(strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
838			(void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
839		}
840
841		(void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
842		if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut))
843			logit("utmp_write_direct: error writing %s: %s",
844			    UTMP_FILE, strerror(errno));
845
846		(void)close(fd);
847		return 1;
848	} else {
849		return 0;
850	}
851}
852# endif /* UTMP_USE_LIBRARY */
853
854static int
855utmp_perform_login(struct logininfo *li)
856{
857	struct utmp ut;
858
859	construct_utmp(li, &ut);
860# ifdef UTMP_USE_LIBRARY
861	if (!utmp_write_library(li, &ut)) {
862		logit("utmp_perform_login: utmp_write_library() failed");
863		return 0;
864	}
865# else
866	if (!utmp_write_direct(li, &ut)) {
867		logit("utmp_perform_login: utmp_write_direct() failed");
868		return 0;
869	}
870# endif
871	return 1;
872}
873
874
875static int
876utmp_perform_logout(struct logininfo *li)
877{
878	struct utmp ut;
879
880	construct_utmp(li, &ut);
881# ifdef UTMP_USE_LIBRARY
882	if (!utmp_write_library(li, &ut)) {
883		logit("utmp_perform_logout: utmp_write_library() failed");
884		return 0;
885	}
886# else
887	if (!utmp_write_direct(li, &ut)) {
888		logit("utmp_perform_logout: utmp_write_direct() failed");
889		return 0;
890	}
891# endif
892	return 1;
893}
894
895
896int
897utmp_write_entry(struct logininfo *li)
898{
899	switch(li->type) {
900	case LTYPE_LOGIN:
901		return utmp_perform_login(li);
902
903	case LTYPE_LOGOUT:
904		return utmp_perform_logout(li);
905
906	default:
907		logit("utmp_write_entry: invalid type field");
908		return 0;
909	}
910}
911#endif /* USE_UTMP */
912
913
914/**
915 ** Low-level utmpx functions
916 **/
917
918/* not much point if we don't want utmpx entries */
919#ifdef USE_UTMPX
920
921/* if we have the wherewithall, use pututxline etc. */
922# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
923	defined(HAVE_PUTUTXLINE)
924#  define UTMPX_USE_LIBRARY
925# endif
926
927
928/* write a utmpx entry with the system's help (pututxline() and pals) */
929# ifdef UTMPX_USE_LIBRARY
930static int
931utmpx_write_library(struct logininfo *li, struct utmpx *utx)
932{
933	setutxent();
934	pututxline(utx);
935
936#  ifdef HAVE_ENDUTXENT
937	endutxent();
938#  endif
939	return 1;
940}
941
942# else /* UTMPX_USE_LIBRARY */
943
944/* write a utmp entry direct to the file */
945static int
946utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
947{
948	logit("utmpx_write_direct: not implemented!");
949	return 0;
950}
951# endif /* UTMPX_USE_LIBRARY */
952
953static int
954utmpx_perform_login(struct logininfo *li)
955{
956	struct utmpx utx;
957
958	construct_utmpx(li, &utx);
959# ifdef UTMPX_USE_LIBRARY
960	if (!utmpx_write_library(li, &utx)) {
961		logit("utmpx_perform_login: utmp_write_library() failed");
962		return 0;
963	}
964# else
965	if (!utmpx_write_direct(li, &ut)) {
966		logit("utmpx_perform_login: utmp_write_direct() failed");
967		return 0;
968	}
969# endif
970	return 1;
971}
972
973
974static int
975utmpx_perform_logout(struct logininfo *li)
976{
977	struct utmpx utx;
978
979	construct_utmpx(li, &utx);
980# ifdef HAVE_ID_IN_UTMPX
981	line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
982# endif
983# ifdef HAVE_TYPE_IN_UTMPX
984	utx.ut_type = DEAD_PROCESS;
985# endif
986
987# ifdef UTMPX_USE_LIBRARY
988	utmpx_write_library(li, &utx);
989# else
990	utmpx_write_direct(li, &utx);
991# endif
992	return 1;
993}
994
995int
996utmpx_write_entry(struct logininfo *li)
997{
998	switch(li->type) {
999	case LTYPE_LOGIN:
1000		return utmpx_perform_login(li);
1001	case LTYPE_LOGOUT:
1002		return utmpx_perform_logout(li);
1003	default:
1004		logit("utmpx_write_entry: invalid type field");
1005		return 0;
1006	}
1007}
1008#endif /* USE_UTMPX */
1009
1010
1011/**
1012 ** Low-level wtmp functions
1013 **/
1014
1015#ifdef USE_WTMP
1016
1017/* write a wtmp entry direct to the end of the file */
1018/* This is a slight modification of code in OpenBSD's logwtmp.c */
1019static int
1020wtmp_write(struct logininfo *li, struct utmp *ut)
1021{
1022	struct stat buf;
1023	int fd, ret = 1;
1024
1025	if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1026		logit("wtmp_write: problem writing %s: %s",
1027		    WTMP_FILE, strerror(errno));
1028		return 0;
1029	}
1030	if (fstat(fd, &buf) == 0)
1031		if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
1032			ftruncate(fd, buf.st_size);
1033			logit("wtmp_write: problem writing %s: %s",
1034			    WTMP_FILE, strerror(errno));
1035			ret = 0;
1036		}
1037	(void)close(fd);
1038	return ret;
1039}
1040
1041static int
1042wtmp_perform_login(struct logininfo *li)
1043{
1044	struct utmp ut;
1045
1046	construct_utmp(li, &ut);
1047	return wtmp_write(li, &ut);
1048}
1049
1050
1051static int
1052wtmp_perform_logout(struct logininfo *li)
1053{
1054	struct utmp ut;
1055
1056	construct_utmp(li, &ut);
1057	return wtmp_write(li, &ut);
1058}
1059
1060
1061int
1062wtmp_write_entry(struct logininfo *li)
1063{
1064	switch(li->type) {
1065	case LTYPE_LOGIN:
1066		return wtmp_perform_login(li);
1067	case LTYPE_LOGOUT:
1068		return wtmp_perform_logout(li);
1069	default:
1070		logit("wtmp_write_entry: invalid type field");
1071		return 0;
1072	}
1073}
1074
1075
1076/* Notes on fetching login data from wtmp/wtmpx
1077 *
1078 * Logouts are usually recorded with (amongst other things) a blank
1079 * username on a given tty line.  However, some systems (HP-UX is one)
1080 * leave all fields set, but change the ut_type field to DEAD_PROCESS.
1081 *
1082 * Since we're only looking for logins here, we know that the username
1083 * must be set correctly. On systems that leave it in, we check for
1084 * ut_type==USER_PROCESS (indicating a login.)
1085 *
1086 * Portability: Some systems may set something other than USER_PROCESS
1087 * to indicate a login process. I don't know of any as I write. Also,
1088 * it's possible that some systems may both leave the username in
1089 * place and not have ut_type.
1090 */
1091
1092/* return true if this wtmp entry indicates a login */
1093static int
1094wtmp_islogin(struct logininfo *li, struct utmp *ut)
1095{
1096	if (strncmp(li->username, ut->ut_name,
1097		MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
1098# ifdef HAVE_TYPE_IN_UTMP
1099		if (ut->ut_type & USER_PROCESS)
1100			return 1;
1101# else
1102		return 1;
1103# endif
1104	}
1105	return 0;
1106}
1107
1108int
1109wtmp_get_entry(struct logininfo *li)
1110{
1111	struct stat st;
1112	struct utmp ut;
1113	int fd, found=0;
1114
1115	/* Clear the time entries in our logininfo */
1116	li->tv_sec = li->tv_usec = 0;
1117
1118	if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1119		logit("wtmp_get_entry: problem opening %s: %s",
1120		    WTMP_FILE, strerror(errno));
1121		return 0;
1122	}
1123	if (fstat(fd, &st) != 0) {
1124		logit("wtmp_get_entry: couldn't stat %s: %s",
1125		    WTMP_FILE, strerror(errno));
1126		close(fd);
1127		return 0;
1128	}
1129
1130	/* Seek to the start of the last struct utmp */
1131	if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
1132		/* Looks like we've got a fresh wtmp file */
1133		close(fd);
1134		return 0;
1135	}
1136
1137	while (!found) {
1138		if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
1139			logit("wtmp_get_entry: read of %s failed: %s",
1140			    WTMP_FILE, strerror(errno));
1141			close (fd);
1142			return 0;
1143		}
1144		if ( wtmp_islogin(li, &ut) ) {
1145			found = 1;
1146			/* We've already checked for a time in struct
1147			 * utmp, in login_getlast(). */
1148# ifdef HAVE_TIME_IN_UTMP
1149			li->tv_sec = ut.ut_time;
1150# else
1151#  if HAVE_TV_IN_UTMP
1152			li->tv_sec = ut.ut_tv.tv_sec;
1153#  endif
1154# endif
1155			line_fullname(li->line, ut.ut_line,
1156				      MIN_SIZEOF(li->line, ut.ut_line));
1157# ifdef HAVE_HOST_IN_UTMP
1158			strlcpy(li->hostname, ut.ut_host,
1159				MIN_SIZEOF(li->hostname, ut.ut_host));
1160# endif
1161			continue;
1162		}
1163		/* Seek back 2 x struct utmp */
1164		if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
1165			/* We've found the start of the file, so quit */
1166			close (fd);
1167			return 0;
1168		}
1169	}
1170
1171	/* We found an entry. Tidy up and return */
1172	close(fd);
1173	return 1;
1174}
1175# endif /* USE_WTMP */
1176
1177
1178/**
1179 ** Low-level wtmpx functions
1180 **/
1181
1182#ifdef USE_WTMPX
1183/* write a wtmpx entry direct to the end of the file */
1184/* This is a slight modification of code in OpenBSD's logwtmp.c */
1185static int
1186wtmpx_write(struct logininfo *li, struct utmpx *utx)
1187{
1188	struct stat buf;
1189	int fd, ret = 1;
1190
1191	if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1192		logit("wtmpx_write: problem opening %s: %s",
1193		    WTMPX_FILE, strerror(errno));
1194		return 0;
1195	}
1196
1197	if (fstat(fd, &buf) == 0)
1198		if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
1199			ftruncate(fd, buf.st_size);
1200			logit("wtmpx_write: problem writing %s: %s",
1201			    WTMPX_FILE, strerror(errno));
1202			ret = 0;
1203		}
1204	(void)close(fd);
1205
1206	return ret;
1207}
1208
1209
1210static int
1211wtmpx_perform_login(struct logininfo *li)
1212{
1213	struct utmpx utx;
1214
1215	construct_utmpx(li, &utx);
1216	return wtmpx_write(li, &utx);
1217}
1218
1219
1220static int
1221wtmpx_perform_logout(struct logininfo *li)
1222{
1223	struct utmpx utx;
1224
1225	construct_utmpx(li, &utx);
1226	return wtmpx_write(li, &utx);
1227}
1228
1229
1230int
1231wtmpx_write_entry(struct logininfo *li)
1232{
1233	switch(li->type) {
1234	case LTYPE_LOGIN:
1235		return wtmpx_perform_login(li);
1236	case LTYPE_LOGOUT:
1237		return wtmpx_perform_logout(li);
1238	default:
1239		logit("wtmpx_write_entry: invalid type field");
1240		return 0;
1241	}
1242}
1243
1244/* Please see the notes above wtmp_islogin() for information about the
1245   next two functions */
1246
1247/* Return true if this wtmpx entry indicates a login */
1248static int
1249wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1250{
1251	if ( strncmp(li->username, utx->ut_name,
1252		MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
1253# ifdef HAVE_TYPE_IN_UTMPX
1254		if (utx->ut_type == USER_PROCESS)
1255			return 1;
1256# else
1257		return 1;
1258# endif
1259	}
1260	return 0;
1261}
1262
1263
1264int
1265wtmpx_get_entry(struct logininfo *li)
1266{
1267	struct stat st;
1268	struct utmpx utx;
1269	int fd, found=0;
1270
1271	/* Clear the time entries */
1272	li->tv_sec = li->tv_usec = 0;
1273
1274	if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1275		logit("wtmpx_get_entry: problem opening %s: %s",
1276		    WTMPX_FILE, strerror(errno));
1277		return 0;
1278	}
1279	if (fstat(fd, &st) != 0) {
1280		logit("wtmpx_get_entry: couldn't stat %s: %s",
1281		    WTMPX_FILE, strerror(errno));
1282		close(fd);
1283		return 0;
1284	}
1285
1286	/* Seek to the start of the last struct utmpx */
1287	if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) {
1288		/* probably a newly rotated wtmpx file */
1289		close(fd);
1290		return 0;
1291	}
1292
1293	while (!found) {
1294		if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
1295			logit("wtmpx_get_entry: read of %s failed: %s",
1296			    WTMPX_FILE, strerror(errno));
1297			close (fd);
1298			return 0;
1299		}
1300		/* Logouts are recorded as a blank username on a particular line.
1301		 * So, we just need to find the username in struct utmpx */
1302		if ( wtmpx_islogin(li, &utx) ) {
1303			found = 1;
1304# ifdef HAVE_TV_IN_UTMPX
1305			li->tv_sec = utx.ut_tv.tv_sec;
1306# else
1307#  ifdef HAVE_TIME_IN_UTMPX
1308			li->tv_sec = utx.ut_time;
1309#  endif
1310# endif
1311			line_fullname(li->line, utx.ut_line, sizeof(li->line));
1312# ifdef HAVE_HOST_IN_UTMPX
1313			strlcpy(li->hostname, utx.ut_host,
1314				MIN_SIZEOF(li->hostname, utx.ut_host));
1315# endif
1316			continue;
1317		}
1318		if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) {
1319			close (fd);
1320			return 0;
1321		}
1322	}
1323
1324	close(fd);
1325	return 1;
1326}
1327#endif /* USE_WTMPX */
1328
1329/**
1330 ** Low-level libutil login() functions
1331 **/
1332
1333#ifdef USE_LOGIN
1334static int
1335syslogin_perform_login(struct logininfo *li)
1336{
1337	struct utmp *ut;
1338
1339	if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
1340		logit("syslogin_perform_login: couldn't malloc()");
1341		return 0;
1342	}
1343	construct_utmp(li, ut);
1344	login(ut);
1345	free(ut);
1346
1347	return 1;
1348}
1349
1350static int
1351syslogin_perform_logout(struct logininfo *li)
1352{
1353# ifdef HAVE_LOGOUT
1354	char line[8];
1355
1356	(void)line_stripname(line, li->line, sizeof(line));
1357
1358	if (!logout(line)) {
1359		logit("syslogin_perform_logout: logout() returned an error");
1360#  ifdef HAVE_LOGWTMP
1361	} else {
1362		logwtmp(line, "", "");
1363#  endif
1364	}
1365	/* FIXME: (ATL - if the need arises) What to do if we have
1366	 * login, but no logout?  what if logout but no logwtmp? All
1367	 * routines are in libutil so they should all be there,
1368	 * but... */
1369# endif
1370	return 1;
1371}
1372
1373int
1374syslogin_write_entry(struct logininfo *li)
1375{
1376	switch (li->type) {
1377	case LTYPE_LOGIN:
1378		return syslogin_perform_login(li);
1379	case LTYPE_LOGOUT:
1380		return syslogin_perform_logout(li);
1381	default:
1382		logit("syslogin_write_entry: Invalid type field");
1383		return 0;
1384	}
1385}
1386#endif /* USE_LOGIN */
1387
1388/* end of file log-syslogin.c */
1389
1390/**
1391 ** Low-level lastlog functions
1392 **/
1393
1394#ifdef USE_LASTLOG
1395#define LL_FILE 1
1396#define LL_DIR 2
1397#define LL_OTHER 3
1398
1399static void
1400lastlog_construct(struct logininfo *li, struct lastlog *last)
1401{
1402	/* clear the structure */
1403	memset(last, '\0', sizeof(*last));
1404
1405	(void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
1406	strlcpy(last->ll_host, li->hostname,
1407		MIN_SIZEOF(last->ll_host, li->hostname));
1408	last->ll_time = li->tv_sec;
1409}
1410
1411static int
1412lastlog_filetype(char *filename)
1413{
1414	struct stat st;
1415
1416	if (stat(LASTLOG_FILE, &st) != 0) {
1417		logit("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
1418			strerror(errno));
1419		return 0;
1420	}
1421	if (S_ISDIR(st.st_mode))
1422		return LL_DIR;
1423	else if (S_ISREG(st.st_mode))
1424		return LL_FILE;
1425	else
1426		return LL_OTHER;
1427}
1428
1429
1430/* open the file (using filemode) and seek to the login entry */
1431static int
1432lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1433{
1434	off_t offset;
1435	int type;
1436	char lastlog_file[1024];
1437
1438	type = lastlog_filetype(LASTLOG_FILE);
1439	switch (type) {
1440		case LL_FILE:
1441			strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1442			break;
1443		case LL_DIR:
1444			snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1445				 LASTLOG_FILE, li->username);
1446			break;
1447		default:
1448			logit("lastlog_openseek: %.100s is not a file or directory!",
1449			    LASTLOG_FILE);
1450			return 0;
1451	}
1452
1453	*fd = open(lastlog_file, filemode, 0600);
1454	if ( *fd < 0) {
1455		debug("lastlog_openseek: Couldn't open %s: %s",
1456		    lastlog_file, strerror(errno));
1457		return 0;
1458	}
1459
1460	if (type == LL_FILE) {
1461		/* find this uid's offset in the lastlog file */
1462		offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
1463
1464		if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1465			logit("lastlog_openseek: %s->lseek(): %s",
1466			 lastlog_file, strerror(errno));
1467			return 0;
1468		}
1469	}
1470
1471	return 1;
1472}
1473
1474static int
1475lastlog_perform_login(struct logininfo *li)
1476{
1477	struct lastlog last;
1478	int fd;
1479
1480	/* create our struct lastlog */
1481	lastlog_construct(li, &last);
1482
1483	if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
1484		return(0);
1485
1486	/* write the entry */
1487	if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) {
1488		close(fd);
1489		logit("lastlog_write_filemode: Error writing to %s: %s",
1490		    LASTLOG_FILE, strerror(errno));
1491		return 0;
1492	}
1493
1494	close(fd);
1495	return 1;
1496}
1497
1498int
1499lastlog_write_entry(struct logininfo *li)
1500{
1501	switch(li->type) {
1502	case LTYPE_LOGIN:
1503		return lastlog_perform_login(li);
1504	default:
1505		logit("lastlog_write_entry: Invalid type field");
1506		return 0;
1507	}
1508}
1509
1510static void
1511lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1512{
1513	line_fullname(li->line, last->ll_line, sizeof(li->line));
1514	strlcpy(li->hostname, last->ll_host,
1515		MIN_SIZEOF(li->hostname, last->ll_host));
1516	li->tv_sec = last->ll_time;
1517}
1518
1519int
1520lastlog_get_entry(struct logininfo *li)
1521{
1522	struct lastlog last;
1523	int fd, ret;
1524
1525	if (!lastlog_openseek(li, &fd, O_RDONLY))
1526		return (0);
1527
1528	ret = atomicio(read, fd, &last, sizeof(last));
1529	close(fd);
1530
1531	switch (ret) {
1532	case 0:
1533		memset(&last, '\0', sizeof(last));
1534		/* FALLTHRU */
1535	case sizeof(last):
1536		lastlog_populate_entry(li, &last);
1537		return (1);
1538	case -1:
1539		error("%s: Error reading from %s: %s", __func__,
1540		    LASTLOG_FILE, strerror(errno));
1541		return (0);
1542	default:
1543		error("%s: Error reading from %s: Expecting %d, got %d",
1544		    __func__, LASTLOG_FILE, sizeof(last), ret);
1545		return (0);
1546	}
1547
1548	/* NOTREACHED */
1549	return (0);
1550}
1551#endif /* USE_LASTLOG */
1552