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