loginrec.c revision 137019
1/*
2 * Copyright (c) 2000 Andre Lucas.  All rights reserved.
3 * Portions copyright (c) 1998 Todd C. Miller
4 * Portions copyright (c) 1996 Jason Downs
5 * Portions copyright (c) 1996 Theo de Raadt
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28/**
29 ** loginrec.c:  platform-independent login recording and lastlog retrieval
30 **/
31
32/*
33  The new login code explained
34  ============================
35
36  This code attempts to provide a common interface to login recording
37  (utmp and friends) and last login time retrieval.
38
39  Its primary means of achieving this is to use 'struct logininfo', a
40  union of all the useful fields in the various different types of
41  system login record structures one finds on UNIX variants.
42
43  We depend on autoconf to define which recording methods are to be
44  used, and which fields are contained in the relevant data structures
45  on the local system. Many C preprocessor symbols affect which code
46  gets compiled here.
47
48  The code is designed to make it easy to modify a particular
49  recording method, without affecting other methods nor requiring so
50  many nested conditional compilation blocks as were commonplace in
51  the old code.
52
53  For login recording, we try to use the local system's libraries as
54  these are clearly most likely to work correctly. For utmp systems
55  this usually means login() and logout() or setutent() etc., probably
56  in libutil, along with logwtmp() etc. On these systems, we fall back
57  to writing the files directly if we have to, though this method
58  requires very thorough testing so we do not corrupt local auditing
59  information. These files and their access methods are very system
60  specific indeed.
61
62  For utmpx systems, the corresponding library functions are
63  setutxent() etc. To the author's knowledge, all utmpx systems have
64  these library functions and so no direct write is attempted. If such
65  a system exists and needs support, direct analogues of the [uw]tmp
66  code should suffice.
67
68  Retrieving the time of last login ('lastlog') is in some ways even
69  more problemmatic than login recording. Some systems provide a
70  simple table of all users which we seek based on uid and retrieve a
71  relatively standard structure. Others record the same information in
72  a directory with a separate file, and others don't record the
73  information separately at all. For systems in the latter category,
74  we look backwards in the wtmp or wtmpx file for the last login entry
75  for our user. Naturally this is slower and on busy systems could
76  incur a significant performance penalty.
77
78  Calling the new code
79  --------------------
80
81  In OpenSSH all login recording and retrieval is performed in
82  login.c. Here you'll find working examples. Also, in the logintest.c
83  program there are more examples.
84
85  Internal handler calling method
86  -------------------------------
87
88  When a call is made to login_login() or login_logout(), both
89  routines set a struct logininfo flag defining which action (log in,
90  or log out) is to be taken. They both then call login_write(), which
91  calls whichever of the many structure-specific handlers autoconf
92  selects for the local system.
93
94  The handlers themselves handle system data structure specifics. Both
95  struct utmp and struct utmpx have utility functions (see
96  construct_utmp*()) to try to make it simpler to add extra systems
97  that introduce new features to either structure.
98
99  While it may seem terribly wasteful to replicate so much similar
100  code for each method, experience has shown that maintaining code to
101  write both struct utmp and utmpx in one function, whilst maintaining
102  support for all systems whether they have library support or not, is
103  a difficult and time-consuming task.
104
105  Lastlog support proceeds similarly. Functions login_get_lastlog()
106  (and its OpenSSH-tuned friend login_get_lastlog_time()) call
107  getlast_entry(), which tries one of three methods to find the last
108  login time. It uses local system lastlog support if it can,
109  otherwise it tries wtmp or wtmpx before giving up and returning 0,
110  meaning "tilt".
111
112  Maintenance
113  -----------
114
115  In many cases it's possible to tweak autoconf to select the correct
116  methods for a particular platform, either by improving the detection
117  code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE
118  symbols for the platform.
119
120  Use logintest to check which symbols are defined before modifying
121  configure.ac and loginrec.c. (You have to build logintest yourself
122  with 'make logintest' as it's not built by default.)
123
124  Otherwise, patches to the specific method(s) are very helpful!
125
126*/
127
128/**
129 ** TODO:
130 **   homegrown ttyslot()
131 **   test, test, test
132 **
133 ** Platform status:
134 ** ----------------
135 **
136 ** Known good:
137 **   Linux (Redhat 6.2, Debian)
138 **   Solaris
139 **   HP-UX 10.20 (gcc only)
140 **   IRIX
141 **   NeXT - M68k/HPPA/Sparc (4.2/3.3)
142 **
143 ** Testing required: Please send reports!
144 **   NetBSD
145 **   HP-UX 11
146 **   AIX
147 **
148 ** Platforms with known problems:
149 **   Some variants of Slackware Linux
150 **
151 **/
152
153#include "includes.h"
154
155#include "ssh.h"
156#include "xmalloc.h"
157#include "loginrec.h"
158#include "log.h"
159#include "atomicio.h"
160
161RCSID("$Id: loginrec.c,v 1.58 2004/08/15 09:12:52 djm Exp $");
162RCSID("$FreeBSD: head/crypto/openssh/loginrec.c 137019 2004-10-28 16:11:31Z des $");
163
164#ifdef HAVE_UTIL_H
165#  include <util.h>
166#endif
167
168#ifdef HAVE_LIBUTIL_H
169#   include <libutil.h>
170#endif
171
172/**
173 ** prototypes for helper functions in this file
174 **/
175
176#if HAVE_UTMP_H
177void set_utmp_time(struct logininfo *li, struct utmp *ut);
178void construct_utmp(struct logininfo *li, struct utmp *ut);
179#endif
180
181#ifdef HAVE_UTMPX_H
182void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
183void construct_utmpx(struct logininfo *li, struct utmpx *ut);
184#endif
185
186int utmp_write_entry(struct logininfo *li);
187int utmpx_write_entry(struct logininfo *li);
188int wtmp_write_entry(struct logininfo *li);
189int wtmpx_write_entry(struct logininfo *li);
190int lastlog_write_entry(struct logininfo *li);
191int syslogin_write_entry(struct logininfo *li);
192
193int getlast_entry(struct logininfo *li);
194int lastlog_get_entry(struct logininfo *li);
195int wtmp_get_entry(struct logininfo *li);
196int wtmpx_get_entry(struct logininfo *li);
197
198/* pick the shortest string */
199#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
200
201/**
202 ** platform-independent login functions
203 **/
204
205/* login_login(struct logininfo *)     -Record a login
206 *
207 * Call with a pointer to a struct logininfo initialised with
208 * login_init_entry() or login_alloc_entry()
209 *
210 * Returns:
211 *  >0 if successful
212 *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
213 */
214int
215login_login (struct logininfo *li)
216{
217	li->type = LTYPE_LOGIN;
218	return login_write(li);
219}
220
221
222/* login_logout(struct logininfo *)     - Record a logout
223 *
224 * Call as with login_login()
225 *
226 * Returns:
227 *  >0 if successful
228 *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
229 */
230int
231login_logout(struct logininfo *li)
232{
233	li->type = LTYPE_LOGOUT;
234	return login_write(li);
235}
236
237/* login_get_lastlog_time(int)           - Retrieve the last login time
238 *
239 * Retrieve the last login time for the given uid. Will try to use the
240 * system lastlog facilities if they are available, but will fall back
241 * to looking in wtmp/wtmpx if necessary
242 *
243 * Returns:
244 *   0 on failure, or if user has never logged in
245 *   Time in seconds from the epoch if successful
246 *
247 * Useful preprocessor symbols:
248 *   DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
249 *                    info
250 *   USE_LASTLOG: If set, indicates the presence of system lastlog
251 *                facilities. If this and DISABLE_LASTLOG are not set,
252 *                try to retrieve lastlog information from wtmp/wtmpx.
253 */
254unsigned int
255login_get_lastlog_time(const int uid)
256{
257	struct logininfo li;
258
259	if (login_get_lastlog(&li, uid))
260		return li.tv_sec;
261	else
262		return 0;
263}
264
265/* login_get_lastlog(struct logininfo *, int)   - Retrieve a lastlog entry
266 *
267 * Retrieve a logininfo structure populated (only partially) with
268 * information from the system lastlog data, or from wtmp/wtmpx if no
269 * system lastlog information exists.
270 *
271 * Note this routine must be given a pre-allocated logininfo.
272 *
273 * Returns:
274 *  >0: A pointer to your struct logininfo if successful
275 *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
276 *
277 */
278struct logininfo *
279login_get_lastlog(struct logininfo *li, const int uid)
280{
281	struct passwd *pw;
282
283	memset(li, '\0', sizeof(*li));
284	li->uid = uid;
285
286	/*
287	 * If we don't have a 'real' lastlog, we need the username to
288	 * reliably search wtmp(x) for the last login (see
289	 * wtmp_get_entry().)
290	 */
291	pw = getpwuid(uid);
292	if (pw == NULL)
293		fatal("login_get_lastlog: Cannot find account for uid %i", uid);
294
295	/* No MIN_SIZEOF here - we absolutely *must not* truncate the
296	 * username */
297	strlcpy(li->username, pw->pw_name, sizeof(li->username));
298
299	if (getlast_entry(li))
300		return li;
301	else
302		return NULL;
303}
304
305
306/* login_alloc_entry(int, char*, char*, char*)    - Allocate and initialise
307 *                                                  a logininfo structure
308 *
309 * This function creates a new struct logininfo, a data structure
310 * meant to carry the information required to portably record login info.
311 *
312 * Returns a pointer to a newly created struct logininfo. If memory
313 * allocation fails, the program halts.
314 */
315struct
316logininfo *login_alloc_entry(int pid, const char *username,
317			     const char *hostname, const char *line)
318{
319	struct logininfo *newli;
320
321	newli = (struct logininfo *) xmalloc (sizeof(*newli));
322	(void)login_init_entry(newli, pid, username, hostname, line);
323	return newli;
324}
325
326
327/* login_free_entry(struct logininfo *)    - free struct memory */
328void
329login_free_entry(struct logininfo *li)
330{
331	xfree(li);
332}
333
334
335/* login_init_entry(struct logininfo *, int, char*, char*, char*)
336 *                                        - initialise a struct logininfo
337 *
338 * Populates a new struct logininfo, a data structure meant to carry
339 * the information required to portably record login info.
340 *
341 * Returns: 1
342 */
343int
344login_init_entry(struct logininfo *li, int pid, const char *username,
345		 const char *hostname, const char *line)
346{
347	struct passwd *pw;
348
349	memset(li, 0, sizeof(*li));
350
351	li->pid = pid;
352
353	/* set the line information */
354	if (line)
355		line_fullname(li->line, line, sizeof(li->line));
356
357	if (username) {
358		strlcpy(li->username, username, sizeof(li->username));
359		pw = getpwnam(li->username);
360		if (pw == NULL)
361			fatal("login_init_entry: Cannot find user \"%s\"", li->username);
362		li->uid = pw->pw_uid;
363	}
364
365	if (hostname)
366		strlcpy(li->hostname, hostname, sizeof(li->hostname));
367
368	return 1;
369}
370
371/* login_set_current_time(struct logininfo *)    - set the current time
372 *
373 * Set the current time in a logininfo structure. This function is
374 * meant to eliminate the need to deal with system dependencies for
375 * time handling.
376 */
377void
378login_set_current_time(struct logininfo *li)
379{
380	struct timeval tv;
381
382	gettimeofday(&tv, NULL);
383
384	li->tv_sec = tv.tv_sec;
385	li->tv_usec = tv.tv_usec;
386}
387
388/* copy a sockaddr_* into our logininfo */
389void
390login_set_addr(struct logininfo *li, const struct sockaddr *sa,
391	       const unsigned int sa_size)
392{
393	unsigned int bufsize = sa_size;
394
395	/* make sure we don't overrun our union */
396	if (sizeof(li->hostaddr) < sa_size)
397		bufsize = sizeof(li->hostaddr);
398
399	memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
400}
401
402
403/**
404 ** login_write: Call low-level recording functions based on autoconf
405 ** results
406 **/
407int
408login_write (struct logininfo *li)
409{
410#ifndef HAVE_CYGWIN
411	if ((int)geteuid() != 0) {
412	  logit("Attempt to write login records by non-root user (aborting)");
413	  return 1;
414	}
415#endif
416
417	/* set the timestamp */
418	login_set_current_time(li);
419#ifdef USE_LOGIN
420	syslogin_write_entry(li);
421#endif
422#ifdef USE_LASTLOG
423	if (li->type == LTYPE_LOGIN) {
424		lastlog_write_entry(li);
425	}
426#endif
427#ifdef USE_UTMP
428	utmp_write_entry(li);
429#endif
430#ifdef USE_WTMP
431	wtmp_write_entry(li);
432#endif
433#ifdef USE_UTMPX
434	utmpx_write_entry(li);
435#endif
436#ifdef USE_WTMPX
437	wtmpx_write_entry(li);
438#endif
439#ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN
440	if (li->type == LTYPE_LOGIN &&
441	   !sys_auth_record_login(li->username,li->hostname,li->line))
442		logit("Writing login record failed for %s", li->username);
443#endif
444	return 0;
445}
446
447#ifdef LOGIN_NEEDS_UTMPX
448int
449login_utmp_only(struct logininfo *li)
450{
451	li->type = LTYPE_LOGIN;
452	login_set_current_time(li);
453# ifdef USE_UTMP
454	utmp_write_entry(li);
455# endif
456# ifdef USE_WTMP
457	wtmp_write_entry(li);
458# endif
459# ifdef USE_UTMPX
460	utmpx_write_entry(li);
461# endif
462# ifdef USE_WTMPX
463	wtmpx_write_entry(li);
464# endif
465	return 0;
466}
467#endif
468
469/**
470 ** getlast_entry: Call low-level functions to retrieve the last login
471 **                time.
472 **/
473
474/* take the uid in li and return the last login time */
475int
476getlast_entry(struct logininfo *li)
477{
478#ifdef USE_LASTLOG
479	return(lastlog_get_entry(li));
480#else /* !USE_LASTLOG */
481
482#ifdef DISABLE_LASTLOG
483	/* On some systems we shouldn't even try to obtain last login
484	 * time, e.g. AIX */
485	return 0;
486# else /* DISABLE_LASTLOG */
487	/* Try to retrieve the last login time from wtmp */
488#  if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
489	/* retrieve last login time from utmp */
490	return (wtmp_get_entry(li));
491#  else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */
492	/* If wtmp isn't available, try wtmpx */
493#   if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
494	/* retrieve last login time from utmpx */
495	return (wtmpx_get_entry(li));
496#   else
497	/* Give up: No means of retrieving last login time */
498	return 0;
499#   endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
500#  endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
501# endif /* DISABLE_LASTLOG */
502#endif /* USE_LASTLOG */
503}
504
505
506
507/*
508 * 'line' string utility functions
509 *
510 * These functions process the 'line' string into one of three forms:
511 *
512 * 1. The full filename (including '/dev')
513 * 2. The stripped name (excluding '/dev')
514 * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
515 *                               /dev/pts/1  -> ts/1 )
516 *
517 * Form 3 is used on some systems to identify a .tmp.? entry when
518 * attempting to remove it. Typically both addition and removal is
519 * performed by one application - say, sshd - so as long as the choice
520 * uniquely identifies a terminal it's ok.
521 */
522
523
524/* line_fullname(): add the leading '/dev/' if it doesn't exist make
525 * sure dst has enough space, if not just copy src (ugh) */
526char *
527line_fullname(char *dst, const char *src, int dstsize)
528{
529	memset(dst, '\0', dstsize);
530	if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) {
531		strlcpy(dst, src, dstsize);
532	} else {
533		strlcpy(dst, "/dev/", dstsize);
534		strlcat(dst, src, dstsize);
535	}
536	return dst;
537}
538
539/* line_stripname(): strip the leading '/dev' if it exists, return dst */
540char *
541line_stripname(char *dst, const char *src, int dstsize)
542{
543	memset(dst, '\0', dstsize);
544	if (strncmp(src, "/dev/", 5) == 0)
545		strlcpy(dst, src + 5, dstsize);
546	else
547		strlcpy(dst, src, dstsize);
548	return dst;
549}
550
551/* line_abbrevname(): Return the abbreviated (usually four-character)
552 * form of the line (Just use the last <dstsize> characters of the
553 * full name.)
554 *
555 * NOTE: use strncpy because we do NOT necessarily want zero
556 * termination */
557char *
558line_abbrevname(char *dst, const char *src, int dstsize)
559{
560	size_t len;
561
562	memset(dst, '\0', dstsize);
563
564	/* Always skip prefix if present */
565	if (strncmp(src, "/dev/", 5) == 0)
566		src += 5;
567
568#ifdef WITH_ABBREV_NO_TTY
569	if (strncmp(src, "tty", 3) == 0)
570		src += 3;
571#endif
572
573	len = strlen(src);
574
575	if (len > 0) {
576		if (((int)len - dstsize) > 0)
577			src +=  ((int)len - dstsize);
578
579		/* note: _don't_ change this to strlcpy */
580		strncpy(dst, src, (size_t)dstsize);
581	}
582
583	return dst;
584}
585
586/**
587 ** utmp utility functions
588 **
589 ** These functions manipulate struct utmp, taking system differences
590 ** into account.
591 **/
592
593#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
594
595/* build the utmp structure */
596void
597set_utmp_time(struct logininfo *li, struct utmp *ut)
598{
599# ifdef HAVE_TV_IN_UTMP
600	ut->ut_tv.tv_sec = li->tv_sec;
601	ut->ut_tv.tv_usec = li->tv_usec;
602# else
603#  ifdef HAVE_TIME_IN_UTMP
604	ut->ut_time = li->tv_sec;
605#  endif
606# endif
607}
608
609void
610construct_utmp(struct logininfo *li,
611		    struct utmp *ut)
612{
613# ifdef HAVE_ADDR_V6_IN_UTMP
614	struct sockaddr_in6 *sa6;
615#  endif
616	memset(ut, '\0', sizeof(*ut));
617
618	/* First fill out fields used for both logins and logouts */
619
620# ifdef HAVE_ID_IN_UTMP
621	line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
622# endif
623
624# ifdef HAVE_TYPE_IN_UTMP
625	/* This is done here to keep utmp constants out of struct logininfo */
626	switch (li->type) {
627	case LTYPE_LOGIN:
628		ut->ut_type = USER_PROCESS;
629#ifdef _UNICOS
630		cray_set_tmpdir(ut);
631#endif
632		break;
633	case LTYPE_LOGOUT:
634		ut->ut_type = DEAD_PROCESS;
635#ifdef _UNICOS
636		cray_retain_utmp(ut, li->pid);
637#endif
638		break;
639	}
640# endif
641	set_utmp_time(li, ut);
642
643	line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
644
645# ifdef HAVE_PID_IN_UTMP
646	ut->ut_pid = li->pid;
647# endif
648
649	/* If we're logging out, leave all other fields blank */
650	if (li->type == LTYPE_LOGOUT)
651	  return;
652
653	/*
654	 * These fields are only used when logging in, and are blank
655	 * for logouts.
656	 */
657
658	/* Use strncpy because we don't necessarily want null termination */
659	strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
660# ifdef HAVE_HOST_IN_UTMP
661	realhostname_sa(ut->ut_host, sizeof ut->ut_host,
662	    &li->hostaddr.sa, li->hostaddr.sa.sa_len);
663# endif
664# ifdef HAVE_ADDR_IN_UTMP
665	/* this is just a 32-bit IP address */
666	if (li->hostaddr.sa.sa_family == AF_INET)
667		ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
668# endif
669# ifdef HAVE_ADDR_V6_IN_UTMP
670	/* this is just a 128-bit IPv6 address */
671	if (li->hostaddr.sa.sa_family == AF_INET6) {
672		sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
673		memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
674		if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
675			ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
676			ut->ut_addr_v6[1] = 0;
677			ut->ut_addr_v6[2] = 0;
678			ut->ut_addr_v6[3] = 0;
679		}
680	}
681# endif
682}
683#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
684
685/**
686 ** utmpx utility functions
687 **
688 ** These functions manipulate struct utmpx, accounting for system
689 ** variations.
690 **/
691
692#if defined(USE_UTMPX) || defined (USE_WTMPX)
693/* build the utmpx structure */
694void
695set_utmpx_time(struct logininfo *li, struct utmpx *utx)
696{
697# ifdef HAVE_TV_IN_UTMPX
698	utx->ut_tv.tv_sec = li->tv_sec;
699	utx->ut_tv.tv_usec = li->tv_usec;
700# else /* HAVE_TV_IN_UTMPX */
701#  ifdef HAVE_TIME_IN_UTMPX
702	utx->ut_time = li->tv_sec;
703#  endif /* HAVE_TIME_IN_UTMPX */
704# endif /* HAVE_TV_IN_UTMPX */
705}
706
707void
708construct_utmpx(struct logininfo *li, struct utmpx *utx)
709{
710# ifdef HAVE_ADDR_V6_IN_UTMP
711	struct sockaddr_in6 *sa6;
712#  endif
713	memset(utx, '\0', sizeof(*utx));
714# ifdef HAVE_ID_IN_UTMPX
715	line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
716# endif
717
718	/* this is done here to keep utmp constants out of loginrec.h */
719	switch (li->type) {
720	case LTYPE_LOGIN:
721		utx->ut_type = USER_PROCESS;
722		break;
723	case LTYPE_LOGOUT:
724		utx->ut_type = DEAD_PROCESS;
725		break;
726	}
727	line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
728	set_utmpx_time(li, utx);
729	utx->ut_pid = li->pid;
730	/* strncpy(): Don't necessarily want null termination */
731	strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
732
733	if (li->type == LTYPE_LOGOUT)
734		return;
735
736	/*
737	 * These fields are only used when logging in, and are blank
738	 * for logouts.
739	 */
740
741# ifdef HAVE_HOST_IN_UTMPX
742	strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
743# endif
744# ifdef HAVE_ADDR_IN_UTMPX
745	/* this is just a 32-bit IP address */
746	if (li->hostaddr.sa.sa_family == AF_INET)
747		utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
748# endif
749# ifdef HAVE_ADDR_V6_IN_UTMP
750	/* this is just a 128-bit IPv6 address */
751	if (li->hostaddr.sa.sa_family == AF_INET6) {
752		sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
753		memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
754		if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
755			ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
756			ut->ut_addr_v6[1] = 0;
757			ut->ut_addr_v6[2] = 0;
758			ut->ut_addr_v6[3] = 0;
759		}
760	}
761# endif
762# ifdef HAVE_SYSLEN_IN_UTMPX
763	/* ut_syslen is the length of the utx_host string */
764	utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
765# endif
766}
767#endif /* USE_UTMPX || USE_WTMPX */
768
769/**
770 ** Low-level utmp functions
771 **/
772
773/* FIXME: (ATL) utmp_write_direct needs testing */
774#ifdef USE_UTMP
775
776/* if we can, use pututline() etc. */
777# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
778	defined(HAVE_PUTUTLINE)
779#  define UTMP_USE_LIBRARY
780# endif
781
782
783/* write a utmp entry with the system's help (pututline() and pals) */
784# ifdef UTMP_USE_LIBRARY
785static int
786utmp_write_library(struct logininfo *li, struct utmp *ut)
787{
788	setutent();
789	pututline(ut);
790
791#  ifdef HAVE_ENDUTENT
792	endutent();
793#  endif
794	return 1;
795}
796# else /* UTMP_USE_LIBRARY */
797
798/* write a utmp entry direct to the file */
799/* This is a slightly modification of code in OpenBSD's login.c */
800static int
801utmp_write_direct(struct logininfo *li, struct utmp *ut)
802{
803	struct utmp old_ut;
804	register int fd;
805	int tty;
806
807	/* FIXME: (ATL) ttyslot() needs local implementation */
808
809#if defined(HAVE_GETTTYENT)
810	register struct ttyent *ty;
811
812	tty=0;
813
814	setttyent();
815	while ((struct ttyent *)0 != (ty = getttyent())) {
816		tty++;
817		if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
818			break;
819	}
820	endttyent();
821
822	if((struct ttyent *)0 == ty) {
823		logit("%s: tty not found", __func__);
824		return (0);
825	}
826#else /* FIXME */
827
828	tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
829
830#endif /* HAVE_GETTTYENT */
831
832	if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
833		off_t pos, ret;
834
835		pos = (off_t)tty * sizeof(struct utmp);
836		if ((ret = lseek(fd, pos, SEEK_SET)) == -1) {
837			logit("%s: llseek: %s", strerror(errno));
838			return (0);
839		}
840		if (ret != pos) {
841			logit("%s: Couldn't seek to tty %s slot in %s", tty,
842			    UTMP_FILE);
843			return (0);
844		}
845		/*
846		 * Prevent luser from zero'ing out ut_host.
847		 * If the new ut_line is empty but the old one is not
848		 * and ut_line and ut_name match, preserve the old ut_line.
849		 */
850		if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
851			(ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
852			(strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
853			(strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
854			(void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
855		}
856
857		if ((ret = lseek(fd, pos, SEEK_SET)) == -1) {
858			logit("%s: llseek: %s", __func__, strerror(errno));
859			return (0);
860		}
861		if (ret != pos) {
862			logit("%s: Couldn't seek to tty %s slot in %s",
863			    __func__, tty, UTMP_FILE);
864			return (0);
865		}
866		if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut))
867			logit("%s: error writing %s: %s", __func__,
868			    UTMP_FILE, strerror(errno));
869
870		(void)close(fd);
871		return 1;
872	} else {
873		return 0;
874	}
875}
876# endif /* UTMP_USE_LIBRARY */
877
878static int
879utmp_perform_login(struct logininfo *li)
880{
881	struct utmp ut;
882
883	construct_utmp(li, &ut);
884# ifdef UTMP_USE_LIBRARY
885	if (!utmp_write_library(li, &ut)) {
886		logit("utmp_perform_login: utmp_write_library() failed");
887		return 0;
888	}
889# else
890	if (!utmp_write_direct(li, &ut)) {
891		logit("utmp_perform_login: utmp_write_direct() failed");
892		return 0;
893	}
894# endif
895	return 1;
896}
897
898
899static int
900utmp_perform_logout(struct logininfo *li)
901{
902	struct utmp ut;
903
904	construct_utmp(li, &ut);
905# ifdef UTMP_USE_LIBRARY
906	if (!utmp_write_library(li, &ut)) {
907		logit("utmp_perform_logout: utmp_write_library() failed");
908		return 0;
909	}
910# else
911	if (!utmp_write_direct(li, &ut)) {
912		logit("utmp_perform_logout: utmp_write_direct() failed");
913		return 0;
914	}
915# endif
916	return 1;
917}
918
919
920int
921utmp_write_entry(struct logininfo *li)
922{
923	switch(li->type) {
924	case LTYPE_LOGIN:
925		return utmp_perform_login(li);
926
927	case LTYPE_LOGOUT:
928		return utmp_perform_logout(li);
929
930	default:
931		logit("utmp_write_entry: invalid type field");
932		return 0;
933	}
934}
935#endif /* USE_UTMP */
936
937
938/**
939 ** Low-level utmpx functions
940 **/
941
942/* not much point if we don't want utmpx entries */
943#ifdef USE_UTMPX
944
945/* if we have the wherewithall, use pututxline etc. */
946# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
947	defined(HAVE_PUTUTXLINE)
948#  define UTMPX_USE_LIBRARY
949# endif
950
951
952/* write a utmpx entry with the system's help (pututxline() and pals) */
953# ifdef UTMPX_USE_LIBRARY
954static int
955utmpx_write_library(struct logininfo *li, struct utmpx *utx)
956{
957	setutxent();
958	pututxline(utx);
959
960#  ifdef HAVE_ENDUTXENT
961	endutxent();
962#  endif
963	return 1;
964}
965
966# else /* UTMPX_USE_LIBRARY */
967
968/* write a utmp entry direct to the file */
969static int
970utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
971{
972	logit("utmpx_write_direct: not implemented!");
973	return 0;
974}
975# endif /* UTMPX_USE_LIBRARY */
976
977static int
978utmpx_perform_login(struct logininfo *li)
979{
980	struct utmpx utx;
981
982	construct_utmpx(li, &utx);
983# ifdef UTMPX_USE_LIBRARY
984	if (!utmpx_write_library(li, &utx)) {
985		logit("utmpx_perform_login: utmp_write_library() failed");
986		return 0;
987	}
988# else
989	if (!utmpx_write_direct(li, &ut)) {
990		logit("utmpx_perform_login: utmp_write_direct() failed");
991		return 0;
992	}
993# endif
994	return 1;
995}
996
997
998static int
999utmpx_perform_logout(struct logininfo *li)
1000{
1001	struct utmpx utx;
1002
1003	construct_utmpx(li, &utx);
1004# ifdef HAVE_ID_IN_UTMPX
1005	line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
1006# endif
1007# ifdef HAVE_TYPE_IN_UTMPX
1008	utx.ut_type = DEAD_PROCESS;
1009# endif
1010
1011# ifdef UTMPX_USE_LIBRARY
1012	utmpx_write_library(li, &utx);
1013# else
1014	utmpx_write_direct(li, &utx);
1015# endif
1016	return 1;
1017}
1018
1019int
1020utmpx_write_entry(struct logininfo *li)
1021{
1022	switch(li->type) {
1023	case LTYPE_LOGIN:
1024		return utmpx_perform_login(li);
1025	case LTYPE_LOGOUT:
1026		return utmpx_perform_logout(li);
1027	default:
1028		logit("utmpx_write_entry: invalid type field");
1029		return 0;
1030	}
1031}
1032#endif /* USE_UTMPX */
1033
1034
1035/**
1036 ** Low-level wtmp functions
1037 **/
1038
1039#ifdef USE_WTMP
1040
1041/* write a wtmp entry direct to the end of the file */
1042/* This is a slight modification of code in OpenBSD's logwtmp.c */
1043static int
1044wtmp_write(struct logininfo *li, struct utmp *ut)
1045{
1046	struct stat buf;
1047	int fd, ret = 1;
1048
1049	if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1050		logit("wtmp_write: problem writing %s: %s",
1051		    WTMP_FILE, strerror(errno));
1052		return 0;
1053	}
1054	if (fstat(fd, &buf) == 0)
1055		if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
1056			ftruncate(fd, buf.st_size);
1057			logit("wtmp_write: problem writing %s: %s",
1058			    WTMP_FILE, strerror(errno));
1059			ret = 0;
1060		}
1061	(void)close(fd);
1062	return ret;
1063}
1064
1065static int
1066wtmp_perform_login(struct logininfo *li)
1067{
1068	struct utmp ut;
1069
1070	construct_utmp(li, &ut);
1071	return wtmp_write(li, &ut);
1072}
1073
1074
1075static int
1076wtmp_perform_logout(struct logininfo *li)
1077{
1078	struct utmp ut;
1079
1080	construct_utmp(li, &ut);
1081	return wtmp_write(li, &ut);
1082}
1083
1084
1085int
1086wtmp_write_entry(struct logininfo *li)
1087{
1088	switch(li->type) {
1089	case LTYPE_LOGIN:
1090		return wtmp_perform_login(li);
1091	case LTYPE_LOGOUT:
1092		return wtmp_perform_logout(li);
1093	default:
1094		logit("wtmp_write_entry: invalid type field");
1095		return 0;
1096	}
1097}
1098
1099
1100/* Notes on fetching login data from wtmp/wtmpx
1101 *
1102 * Logouts are usually recorded with (amongst other things) a blank
1103 * username on a given tty line.  However, some systems (HP-UX is one)
1104 * leave all fields set, but change the ut_type field to DEAD_PROCESS.
1105 *
1106 * Since we're only looking for logins here, we know that the username
1107 * must be set correctly. On systems that leave it in, we check for
1108 * ut_type==USER_PROCESS (indicating a login.)
1109 *
1110 * Portability: Some systems may set something other than USER_PROCESS
1111 * to indicate a login process. I don't know of any as I write. Also,
1112 * it's possible that some systems may both leave the username in
1113 * place and not have ut_type.
1114 */
1115
1116/* return true if this wtmp entry indicates a login */
1117static int
1118wtmp_islogin(struct logininfo *li, struct utmp *ut)
1119{
1120	if (strncmp(li->username, ut->ut_name,
1121		MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
1122# ifdef HAVE_TYPE_IN_UTMP
1123		if (ut->ut_type & USER_PROCESS)
1124			return 1;
1125# else
1126		return 1;
1127# endif
1128	}
1129	return 0;
1130}
1131
1132int
1133wtmp_get_entry(struct logininfo *li)
1134{
1135	struct stat st;
1136	struct utmp ut;
1137	int fd, found=0;
1138
1139	/* Clear the time entries in our logininfo */
1140	li->tv_sec = li->tv_usec = 0;
1141
1142	if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1143		logit("wtmp_get_entry: problem opening %s: %s",
1144		    WTMP_FILE, strerror(errno));
1145		return 0;
1146	}
1147	if (fstat(fd, &st) != 0) {
1148		logit("wtmp_get_entry: couldn't stat %s: %s",
1149		    WTMP_FILE, strerror(errno));
1150		close(fd);
1151		return 0;
1152	}
1153
1154	/* Seek to the start of the last struct utmp */
1155	if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
1156		/* Looks like we've got a fresh wtmp file */
1157		close(fd);
1158		return 0;
1159	}
1160
1161	while (!found) {
1162		if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
1163			logit("wtmp_get_entry: read of %s failed: %s",
1164			    WTMP_FILE, strerror(errno));
1165			close (fd);
1166			return 0;
1167		}
1168		if ( wtmp_islogin(li, &ut) ) {
1169			found = 1;
1170			/* We've already checked for a time in struct
1171			 * utmp, in login_getlast(). */
1172# ifdef HAVE_TIME_IN_UTMP
1173			li->tv_sec = ut.ut_time;
1174# else
1175#  if HAVE_TV_IN_UTMP
1176			li->tv_sec = ut.ut_tv.tv_sec;
1177#  endif
1178# endif
1179			line_fullname(li->line, ut.ut_line,
1180				      MIN_SIZEOF(li->line, ut.ut_line));
1181# ifdef HAVE_HOST_IN_UTMP
1182			strlcpy(li->hostname, ut.ut_host,
1183				MIN_SIZEOF(li->hostname, ut.ut_host));
1184# endif
1185			continue;
1186		}
1187		/* Seek back 2 x struct utmp */
1188		if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
1189			/* We've found the start of the file, so quit */
1190			close (fd);
1191			return 0;
1192		}
1193	}
1194
1195	/* We found an entry. Tidy up and return */
1196	close(fd);
1197	return 1;
1198}
1199# endif /* USE_WTMP */
1200
1201
1202/**
1203 ** Low-level wtmpx functions
1204 **/
1205
1206#ifdef USE_WTMPX
1207/* write a wtmpx entry direct to the end of the file */
1208/* This is a slight modification of code in OpenBSD's logwtmp.c */
1209static int
1210wtmpx_write(struct logininfo *li, struct utmpx *utx)
1211{
1212#ifndef HAVE_UPDWTMPX
1213	struct stat buf;
1214	int fd, ret = 1;
1215
1216	if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1217		logit("wtmpx_write: problem opening %s: %s",
1218		    WTMPX_FILE, strerror(errno));
1219		return 0;
1220	}
1221
1222	if (fstat(fd, &buf) == 0)
1223		if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
1224			ftruncate(fd, buf.st_size);
1225			logit("wtmpx_write: problem writing %s: %s",
1226			    WTMPX_FILE, strerror(errno));
1227			ret = 0;
1228		}
1229	(void)close(fd);
1230
1231	return ret;
1232#else
1233	updwtmpx(WTMPX_FILE, utx);
1234	return 1;
1235#endif
1236}
1237
1238
1239static int
1240wtmpx_perform_login(struct logininfo *li)
1241{
1242	struct utmpx utx;
1243
1244	construct_utmpx(li, &utx);
1245	return wtmpx_write(li, &utx);
1246}
1247
1248
1249static int
1250wtmpx_perform_logout(struct logininfo *li)
1251{
1252	struct utmpx utx;
1253
1254	construct_utmpx(li, &utx);
1255	return wtmpx_write(li, &utx);
1256}
1257
1258
1259int
1260wtmpx_write_entry(struct logininfo *li)
1261{
1262	switch(li->type) {
1263	case LTYPE_LOGIN:
1264		return wtmpx_perform_login(li);
1265	case LTYPE_LOGOUT:
1266		return wtmpx_perform_logout(li);
1267	default:
1268		logit("wtmpx_write_entry: invalid type field");
1269		return 0;
1270	}
1271}
1272
1273/* Please see the notes above wtmp_islogin() for information about the
1274   next two functions */
1275
1276/* Return true if this wtmpx entry indicates a login */
1277static int
1278wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1279{
1280	if ( strncmp(li->username, utx->ut_name,
1281		MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
1282# ifdef HAVE_TYPE_IN_UTMPX
1283		if (utx->ut_type == USER_PROCESS)
1284			return 1;
1285# else
1286		return 1;
1287# endif
1288	}
1289	return 0;
1290}
1291
1292
1293int
1294wtmpx_get_entry(struct logininfo *li)
1295{
1296	struct stat st;
1297	struct utmpx utx;
1298	int fd, found=0;
1299
1300	/* Clear the time entries */
1301	li->tv_sec = li->tv_usec = 0;
1302
1303	if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1304		logit("wtmpx_get_entry: problem opening %s: %s",
1305		    WTMPX_FILE, strerror(errno));
1306		return 0;
1307	}
1308	if (fstat(fd, &st) != 0) {
1309		logit("wtmpx_get_entry: couldn't stat %s: %s",
1310		    WTMPX_FILE, strerror(errno));
1311		close(fd);
1312		return 0;
1313	}
1314
1315	/* Seek to the start of the last struct utmpx */
1316	if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) {
1317		/* probably a newly rotated wtmpx file */
1318		close(fd);
1319		return 0;
1320	}
1321
1322	while (!found) {
1323		if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
1324			logit("wtmpx_get_entry: read of %s failed: %s",
1325			    WTMPX_FILE, strerror(errno));
1326			close (fd);
1327			return 0;
1328		}
1329		/* Logouts are recorded as a blank username on a particular line.
1330		 * So, we just need to find the username in struct utmpx */
1331		if ( wtmpx_islogin(li, &utx) ) {
1332			found = 1;
1333# ifdef HAVE_TV_IN_UTMPX
1334			li->tv_sec = utx.ut_tv.tv_sec;
1335# else
1336#  ifdef HAVE_TIME_IN_UTMPX
1337			li->tv_sec = utx.ut_time;
1338#  endif
1339# endif
1340			line_fullname(li->line, utx.ut_line, sizeof(li->line));
1341# ifdef HAVE_HOST_IN_UTMPX
1342			strlcpy(li->hostname, utx.ut_host,
1343				MIN_SIZEOF(li->hostname, utx.ut_host));
1344# endif
1345			continue;
1346		}
1347		if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) {
1348			close (fd);
1349			return 0;
1350		}
1351	}
1352
1353	close(fd);
1354	return 1;
1355}
1356#endif /* USE_WTMPX */
1357
1358/**
1359 ** Low-level libutil login() functions
1360 **/
1361
1362#ifdef USE_LOGIN
1363static int
1364syslogin_perform_login(struct logininfo *li)
1365{
1366	struct utmp *ut;
1367
1368	if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
1369		logit("syslogin_perform_login: couldn't malloc()");
1370		return 0;
1371	}
1372	construct_utmp(li, ut);
1373	login(ut);
1374	free(ut);
1375
1376	return 1;
1377}
1378
1379static int
1380syslogin_perform_logout(struct logininfo *li)
1381{
1382# ifdef HAVE_LOGOUT
1383	char line[UT_LINESIZE];
1384
1385	(void)line_stripname(line, li->line, sizeof(line));
1386
1387	if (!logout(line)) {
1388		logit("syslogin_perform_logout: logout() returned an error");
1389#  ifdef HAVE_LOGWTMP
1390	} else {
1391		logwtmp(line, "", "");
1392#  endif
1393	}
1394	/* FIXME: (ATL - if the need arises) What to do if we have
1395	 * login, but no logout?  what if logout but no logwtmp? All
1396	 * routines are in libutil so they should all be there,
1397	 * but... */
1398# endif
1399	return 1;
1400}
1401
1402int
1403syslogin_write_entry(struct logininfo *li)
1404{
1405	switch (li->type) {
1406	case LTYPE_LOGIN:
1407		return syslogin_perform_login(li);
1408	case LTYPE_LOGOUT:
1409		return syslogin_perform_logout(li);
1410	default:
1411		logit("syslogin_write_entry: Invalid type field");
1412		return 0;
1413	}
1414}
1415#endif /* USE_LOGIN */
1416
1417/* end of file log-syslogin.c */
1418
1419/**
1420 ** Low-level lastlog functions
1421 **/
1422
1423#ifdef USE_LASTLOG
1424#define LL_FILE 1
1425#define LL_DIR 2
1426#define LL_OTHER 3
1427
1428static void
1429lastlog_construct(struct logininfo *li, struct lastlog *last)
1430{
1431	/* clear the structure */
1432	memset(last, '\0', sizeof(*last));
1433
1434	(void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
1435	strlcpy(last->ll_host, li->hostname,
1436		MIN_SIZEOF(last->ll_host, li->hostname));
1437	last->ll_time = li->tv_sec;
1438}
1439
1440static int
1441lastlog_filetype(char *filename)
1442{
1443	struct stat st;
1444
1445	if (stat(LASTLOG_FILE, &st) != 0) {
1446		logit("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
1447			strerror(errno));
1448		return 0;
1449	}
1450	if (S_ISDIR(st.st_mode))
1451		return LL_DIR;
1452	else if (S_ISREG(st.st_mode))
1453		return LL_FILE;
1454	else
1455		return LL_OTHER;
1456}
1457
1458
1459/* open the file (using filemode) and seek to the login entry */
1460static int
1461lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1462{
1463	off_t offset;
1464	int type;
1465	char lastlog_file[1024];
1466
1467	type = lastlog_filetype(LASTLOG_FILE);
1468	switch (type) {
1469		case LL_FILE:
1470			strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1471			break;
1472		case LL_DIR:
1473			snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1474				 LASTLOG_FILE, li->username);
1475			break;
1476		default:
1477			logit("lastlog_openseek: %.100s is not a file or directory!",
1478			    LASTLOG_FILE);
1479			return 0;
1480	}
1481
1482	*fd = open(lastlog_file, filemode, 0600);
1483	if ( *fd < 0) {
1484		debug("lastlog_openseek: Couldn't open %s: %s",
1485		    lastlog_file, strerror(errno));
1486		return 0;
1487	}
1488
1489	if (type == LL_FILE) {
1490		/* find this uid's offset in the lastlog file */
1491		offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
1492
1493		if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1494			logit("lastlog_openseek: %s->lseek(): %s",
1495			 lastlog_file, strerror(errno));
1496			return 0;
1497		}
1498	}
1499
1500	return 1;
1501}
1502
1503static int
1504lastlog_perform_login(struct logininfo *li)
1505{
1506	struct lastlog last;
1507	int fd;
1508
1509	/* create our struct lastlog */
1510	lastlog_construct(li, &last);
1511
1512	if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
1513		return(0);
1514
1515	/* write the entry */
1516	if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) {
1517		close(fd);
1518		logit("lastlog_write_filemode: Error writing to %s: %s",
1519		    LASTLOG_FILE, strerror(errno));
1520		return 0;
1521	}
1522
1523	close(fd);
1524	return 1;
1525}
1526
1527int
1528lastlog_write_entry(struct logininfo *li)
1529{
1530	switch(li->type) {
1531	case LTYPE_LOGIN:
1532		return lastlog_perform_login(li);
1533	default:
1534		logit("lastlog_write_entry: Invalid type field");
1535		return 0;
1536	}
1537}
1538
1539static void
1540lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1541{
1542	line_fullname(li->line, last->ll_line, sizeof(li->line));
1543	strlcpy(li->hostname, last->ll_host,
1544		MIN_SIZEOF(li->hostname, last->ll_host));
1545	li->tv_sec = last->ll_time;
1546}
1547
1548int
1549lastlog_get_entry(struct logininfo *li)
1550{
1551	struct lastlog last;
1552	int fd, ret;
1553
1554	if (!lastlog_openseek(li, &fd, O_RDONLY))
1555		return (0);
1556
1557	ret = atomicio(read, fd, &last, sizeof(last));
1558	close(fd);
1559
1560	switch (ret) {
1561	case 0:
1562		memset(&last, '\0', sizeof(last));
1563		/* FALLTHRU */
1564	case sizeof(last):
1565		lastlog_populate_entry(li, &last);
1566		return (1);
1567	case -1:
1568		error("%s: Error reading from %s: %s", __func__,
1569		    LASTLOG_FILE, strerror(errno));
1570		return (0);
1571	default:
1572		error("%s: Error reading from %s: Expecting %d, got %d",
1573		    __func__, LASTLOG_FILE, sizeof(last), ret);
1574		return (0);
1575	}
1576
1577	/* NOTREACHED */
1578	return (0);
1579}
1580#endif /* USE_LASTLOG */
1581