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