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