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