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