]> andersk Git - openssh.git/blob - loginrec.c
- (dtucker) [loginrec.c openbsd-compat/port-aix.c openbsd-compat/port-aix.h]
[openssh.git] / 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$");
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("utmp_write_entry: tty not found");
822                 return(1);
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                 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
832                 /*
833                  * Prevent luser from zero'ing out ut_host.
834                  * If the new ut_line is empty but the old one is not
835                  * and ut_line and ut_name match, preserve the old ut_line.
836                  */
837                 if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
838                         (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
839                         (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
840                         (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
841                         (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
842                 }
843
844                 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
845                 if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut))
846                         logit("utmp_write_direct: error writing %s: %s",
847                             UTMP_FILE, strerror(errno));
848
849                 (void)close(fd);
850                 return 1;
851         } else {
852                 return 0;
853         }
854 }
855 # endif /* UTMP_USE_LIBRARY */
856
857 static int
858 utmp_perform_login(struct logininfo *li)
859 {
860         struct utmp ut;
861
862         construct_utmp(li, &ut);
863 # ifdef UTMP_USE_LIBRARY
864         if (!utmp_write_library(li, &ut)) {
865                 logit("utmp_perform_login: utmp_write_library() failed");
866                 return 0;
867         }
868 # else
869         if (!utmp_write_direct(li, &ut)) {
870                 logit("utmp_perform_login: utmp_write_direct() failed");
871                 return 0;
872         }
873 # endif
874         return 1;
875 }
876
877
878 static int
879 utmp_perform_logout(struct logininfo *li)
880 {
881         struct utmp ut;
882
883         construct_utmp(li, &ut);
884 # ifdef UTMP_USE_LIBRARY
885         if (!utmp_write_library(li, &ut)) {
886                 logit("utmp_perform_logout: utmp_write_library() failed");
887                 return 0;
888         }
889 # else
890         if (!utmp_write_direct(li, &ut)) {
891                 logit("utmp_perform_logout: utmp_write_direct() failed");
892                 return 0;
893         }
894 # endif
895         return 1;
896 }
897
898
899 int
900 utmp_write_entry(struct logininfo *li)
901 {
902         switch(li->type) {
903         case LTYPE_LOGIN:
904                 return utmp_perform_login(li);
905
906         case LTYPE_LOGOUT:
907                 return utmp_perform_logout(li);
908
909         default:
910                 logit("utmp_write_entry: invalid type field");
911                 return 0;
912         }
913 }
914 #endif /* USE_UTMP */
915
916
917 /**
918  ** Low-level utmpx functions
919  **/
920
921 /* not much point if we don't want utmpx entries */
922 #ifdef USE_UTMPX
923
924 /* if we have the wherewithall, use pututxline etc. */
925 # if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
926         defined(HAVE_PUTUTXLINE)
927 #  define UTMPX_USE_LIBRARY
928 # endif
929
930
931 /* write a utmpx entry with the system's help (pututxline() and pals) */
932 # ifdef UTMPX_USE_LIBRARY
933 static int
934 utmpx_write_library(struct logininfo *li, struct utmpx *utx)
935 {
936         setutxent();
937         pututxline(utx);
938
939 #  ifdef HAVE_ENDUTXENT
940         endutxent();
941 #  endif
942         return 1;
943 }
944
945 # else /* UTMPX_USE_LIBRARY */
946
947 /* write a utmp entry direct to the file */
948 static int
949 utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
950 {
951         logit("utmpx_write_direct: not implemented!");
952         return 0;
953 }
954 # endif /* UTMPX_USE_LIBRARY */
955
956 static int
957 utmpx_perform_login(struct logininfo *li)
958 {
959         struct utmpx utx;
960
961         construct_utmpx(li, &utx);
962 # ifdef UTMPX_USE_LIBRARY
963         if (!utmpx_write_library(li, &utx)) {
964                 logit("utmpx_perform_login: utmp_write_library() failed");
965                 return 0;
966         }
967 # else
968         if (!utmpx_write_direct(li, &ut)) {
969                 logit("utmpx_perform_login: utmp_write_direct() failed");
970                 return 0;
971         }
972 # endif
973         return 1;
974 }
975
976
977 static int
978 utmpx_perform_logout(struct logininfo *li)
979 {
980         struct utmpx utx;
981
982         construct_utmpx(li, &utx);
983 # ifdef HAVE_ID_IN_UTMPX
984         line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
985 # endif
986 # ifdef HAVE_TYPE_IN_UTMPX
987         utx.ut_type = DEAD_PROCESS;
988 # endif
989
990 # ifdef UTMPX_USE_LIBRARY
991         utmpx_write_library(li, &utx);
992 # else
993         utmpx_write_direct(li, &utx);
994 # endif
995         return 1;
996 }
997
998 int
999 utmpx_write_entry(struct logininfo *li)
1000 {
1001         switch(li->type) {
1002         case LTYPE_LOGIN:
1003                 return utmpx_perform_login(li);
1004         case LTYPE_LOGOUT:
1005                 return utmpx_perform_logout(li);
1006         default:
1007                 logit("utmpx_write_entry: invalid type field");
1008                 return 0;
1009         }
1010 }
1011 #endif /* USE_UTMPX */
1012
1013
1014 /**
1015  ** Low-level wtmp functions
1016  **/
1017
1018 #ifdef USE_WTMP
1019
1020 /* write a wtmp entry direct to the end of the file */
1021 /* This is a slight modification of code in OpenBSD's logwtmp.c */
1022 static int
1023 wtmp_write(struct logininfo *li, struct utmp *ut)
1024 {
1025         struct stat buf;
1026         int fd, ret = 1;
1027
1028         if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1029                 logit("wtmp_write: problem writing %s: %s",
1030                     WTMP_FILE, strerror(errno));
1031                 return 0;
1032         }
1033         if (fstat(fd, &buf) == 0)
1034                 if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
1035                         ftruncate(fd, buf.st_size);
1036                         logit("wtmp_write: problem writing %s: %s",
1037                             WTMP_FILE, strerror(errno));
1038                         ret = 0;
1039                 }
1040         (void)close(fd);
1041         return ret;
1042 }
1043
1044 static int
1045 wtmp_perform_login(struct logininfo *li)
1046 {
1047         struct utmp ut;
1048
1049         construct_utmp(li, &ut);
1050         return wtmp_write(li, &ut);
1051 }
1052
1053
1054 static int
1055 wtmp_perform_logout(struct logininfo *li)
1056 {
1057         struct utmp ut;
1058
1059         construct_utmp(li, &ut);
1060         return wtmp_write(li, &ut);
1061 }
1062
1063
1064 int
1065 wtmp_write_entry(struct logininfo *li)
1066 {
1067         switch(li->type) {
1068         case LTYPE_LOGIN:
1069                 return wtmp_perform_login(li);
1070         case LTYPE_LOGOUT:
1071                 return wtmp_perform_logout(li);
1072         default:
1073                 logit("wtmp_write_entry: invalid type field");
1074                 return 0;
1075         }
1076 }
1077
1078
1079 /* Notes on fetching login data from wtmp/wtmpx
1080  *
1081  * Logouts are usually recorded with (amongst other things) a blank
1082  * username on a given tty line.  However, some systems (HP-UX is one)
1083  * leave all fields set, but change the ut_type field to DEAD_PROCESS.
1084  *
1085  * Since we're only looking for logins here, we know that the username
1086  * must be set correctly. On systems that leave it in, we check for
1087  * ut_type==USER_PROCESS (indicating a login.)
1088  *
1089  * Portability: Some systems may set something other than USER_PROCESS
1090  * to indicate a login process. I don't know of any as I write. Also,
1091  * it's possible that some systems may both leave the username in
1092  * place and not have ut_type.
1093  */
1094
1095 /* return true if this wtmp entry indicates a login */
1096 static int
1097 wtmp_islogin(struct logininfo *li, struct utmp *ut)
1098 {
1099         if (strncmp(li->username, ut->ut_name,
1100                 MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
1101 # ifdef HAVE_TYPE_IN_UTMP
1102                 if (ut->ut_type & USER_PROCESS)
1103                         return 1;
1104 # else
1105                 return 1;
1106 # endif
1107         }
1108         return 0;
1109 }
1110
1111 int
1112 wtmp_get_entry(struct logininfo *li)
1113 {
1114         struct stat st;
1115         struct utmp ut;
1116         int fd, found=0;
1117
1118         /* Clear the time entries in our logininfo */
1119         li->tv_sec = li->tv_usec = 0;
1120
1121         if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1122                 logit("wtmp_get_entry: problem opening %s: %s",
1123                     WTMP_FILE, strerror(errno));
1124                 return 0;
1125         }
1126         if (fstat(fd, &st) != 0) {
1127                 logit("wtmp_get_entry: couldn't stat %s: %s",
1128                     WTMP_FILE, strerror(errno));
1129                 close(fd);
1130                 return 0;
1131         }
1132
1133         /* Seek to the start of the last struct utmp */
1134         if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
1135                 /* Looks like we've got a fresh wtmp file */
1136                 close(fd);
1137                 return 0;
1138         }
1139
1140         while (!found) {
1141                 if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
1142                         logit("wtmp_get_entry: read of %s failed: %s",
1143                             WTMP_FILE, strerror(errno));
1144                         close (fd);
1145                         return 0;
1146                 }
1147                 if ( wtmp_islogin(li, &ut) ) {
1148                         found = 1;
1149                         /* We've already checked for a time in struct
1150                          * utmp, in login_getlast(). */
1151 # ifdef HAVE_TIME_IN_UTMP
1152                         li->tv_sec = ut.ut_time;
1153 # else
1154 #  if HAVE_TV_IN_UTMP
1155                         li->tv_sec = ut.ut_tv.tv_sec;
1156 #  endif
1157 # endif
1158                         line_fullname(li->line, ut.ut_line,
1159                                       MIN_SIZEOF(li->line, ut.ut_line));
1160 # ifdef HAVE_HOST_IN_UTMP
1161                         strlcpy(li->hostname, ut.ut_host,
1162                                 MIN_SIZEOF(li->hostname, ut.ut_host));
1163 # endif
1164                         continue;
1165                 }
1166                 /* Seek back 2 x struct utmp */
1167                 if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
1168                         /* We've found the start of the file, so quit */
1169                         close (fd);
1170                         return 0;
1171                 }
1172         }
1173
1174         /* We found an entry. Tidy up and return */
1175         close(fd);
1176         return 1;
1177 }
1178 # endif /* USE_WTMP */
1179
1180
1181 /**
1182  ** Low-level wtmpx functions
1183  **/
1184
1185 #ifdef USE_WTMPX
1186 /* write a wtmpx entry direct to the end of the file */
1187 /* This is a slight modification of code in OpenBSD's logwtmp.c */
1188 static int
1189 wtmpx_write(struct logininfo *li, struct utmpx *utx)
1190 {
1191 #ifndef HAVE_UPDWTMPX
1192         struct stat buf;
1193         int fd, ret = 1;
1194
1195         if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1196                 logit("wtmpx_write: problem opening %s: %s",
1197                     WTMPX_FILE, strerror(errno));
1198                 return 0;
1199         }
1200
1201         if (fstat(fd, &buf) == 0)
1202                 if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
1203                         ftruncate(fd, buf.st_size);
1204                         logit("wtmpx_write: problem writing %s: %s",
1205                             WTMPX_FILE, strerror(errno));
1206                         ret = 0;
1207                 }
1208         (void)close(fd);
1209
1210         return ret;
1211 #else
1212         updwtmpx(WTMPX_FILE, utx);
1213         return 1;
1214 #endif
1215 }
1216
1217
1218 static int
1219 wtmpx_perform_login(struct logininfo *li)
1220 {
1221         struct utmpx utx;
1222
1223         construct_utmpx(li, &utx);
1224         return wtmpx_write(li, &utx);
1225 }
1226
1227
1228 static int
1229 wtmpx_perform_logout(struct logininfo *li)
1230 {
1231         struct utmpx utx;
1232
1233         construct_utmpx(li, &utx);
1234         return wtmpx_write(li, &utx);
1235 }
1236
1237
1238 int
1239 wtmpx_write_entry(struct logininfo *li)
1240 {
1241         switch(li->type) {
1242         case LTYPE_LOGIN:
1243                 return wtmpx_perform_login(li);
1244         case LTYPE_LOGOUT:
1245                 return wtmpx_perform_logout(li);
1246         default:
1247                 logit("wtmpx_write_entry: invalid type field");
1248                 return 0;
1249         }
1250 }
1251
1252 /* Please see the notes above wtmp_islogin() for information about the
1253    next two functions */
1254
1255 /* Return true if this wtmpx entry indicates a login */
1256 static int
1257 wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1258 {
1259         if ( strncmp(li->username, utx->ut_name,
1260                 MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
1261 # ifdef HAVE_TYPE_IN_UTMPX
1262                 if (utx->ut_type == USER_PROCESS)
1263                         return 1;
1264 # else
1265                 return 1;
1266 # endif
1267         }
1268         return 0;
1269 }
1270
1271
1272 int
1273 wtmpx_get_entry(struct logininfo *li)
1274 {
1275         struct stat st;
1276         struct utmpx utx;
1277         int fd, found=0;
1278
1279         /* Clear the time entries */
1280         li->tv_sec = li->tv_usec = 0;
1281
1282         if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1283                 logit("wtmpx_get_entry: problem opening %s: %s",
1284                     WTMPX_FILE, strerror(errno));
1285                 return 0;
1286         }
1287         if (fstat(fd, &st) != 0) {
1288                 logit("wtmpx_get_entry: couldn't stat %s: %s",
1289                     WTMPX_FILE, strerror(errno));
1290                 close(fd);
1291                 return 0;
1292         }
1293
1294         /* Seek to the start of the last struct utmpx */
1295         if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) {
1296                 /* probably a newly rotated wtmpx file */
1297                 close(fd);
1298                 return 0;
1299         }
1300
1301         while (!found) {
1302                 if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
1303                         logit("wtmpx_get_entry: read of %s failed: %s",
1304                             WTMPX_FILE, strerror(errno));
1305                         close (fd);
1306                         return 0;
1307                 }
1308                 /* Logouts are recorded as a blank username on a particular line.
1309                  * So, we just need to find the username in struct utmpx */
1310                 if ( wtmpx_islogin(li, &utx) ) {
1311                         found = 1;
1312 # ifdef HAVE_TV_IN_UTMPX
1313                         li->tv_sec = utx.ut_tv.tv_sec;
1314 # else
1315 #  ifdef HAVE_TIME_IN_UTMPX
1316                         li->tv_sec = utx.ut_time;
1317 #  endif
1318 # endif
1319                         line_fullname(li->line, utx.ut_line, sizeof(li->line));
1320 # ifdef HAVE_HOST_IN_UTMPX
1321                         strlcpy(li->hostname, utx.ut_host,
1322                                 MIN_SIZEOF(li->hostname, utx.ut_host));
1323 # endif
1324                         continue;
1325                 }
1326                 if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) {
1327                         close (fd);
1328                         return 0;
1329                 }
1330         }
1331
1332         close(fd);
1333         return 1;
1334 }
1335 #endif /* USE_WTMPX */
1336
1337 /**
1338  ** Low-level libutil login() functions
1339  **/
1340
1341 #ifdef USE_LOGIN
1342 static int
1343 syslogin_perform_login(struct logininfo *li)
1344 {
1345         struct utmp *ut;
1346
1347         if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
1348                 logit("syslogin_perform_login: couldn't malloc()");
1349                 return 0;
1350         }
1351         construct_utmp(li, ut);
1352         login(ut);
1353         free(ut);
1354
1355         return 1;
1356 }
1357
1358 static int
1359 syslogin_perform_logout(struct logininfo *li)
1360 {
1361 # ifdef HAVE_LOGOUT
1362         char line[UT_LINESIZE];
1363
1364         (void)line_stripname(line, li->line, sizeof(line));
1365
1366         if (!logout(line)) {
1367                 logit("syslogin_perform_logout: logout() returned an error");
1368 #  ifdef HAVE_LOGWTMP
1369         } else {
1370                 logwtmp(line, "", "");
1371 #  endif
1372         }
1373         /* FIXME: (ATL - if the need arises) What to do if we have
1374          * login, but no logout?  what if logout but no logwtmp? All
1375          * routines are in libutil so they should all be there,
1376          * but... */
1377 # endif
1378         return 1;
1379 }
1380
1381 int
1382 syslogin_write_entry(struct logininfo *li)
1383 {
1384         switch (li->type) {
1385         case LTYPE_LOGIN:
1386                 return syslogin_perform_login(li);
1387         case LTYPE_LOGOUT:
1388                 return syslogin_perform_logout(li);
1389         default:
1390                 logit("syslogin_write_entry: Invalid type field");
1391                 return 0;
1392         }
1393 }
1394 #endif /* USE_LOGIN */
1395
1396 /* end of file log-syslogin.c */
1397
1398 /**
1399  ** Low-level lastlog functions
1400  **/
1401
1402 #ifdef USE_LASTLOG
1403 #define LL_FILE 1
1404 #define LL_DIR 2
1405 #define LL_OTHER 3
1406
1407 static void
1408 lastlog_construct(struct logininfo *li, struct lastlog *last)
1409 {
1410         /* clear the structure */
1411         memset(last, '\0', sizeof(*last));
1412
1413         (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
1414         strlcpy(last->ll_host, li->hostname,
1415                 MIN_SIZEOF(last->ll_host, li->hostname));
1416         last->ll_time = li->tv_sec;
1417 }
1418
1419 static int
1420 lastlog_filetype(char *filename)
1421 {
1422         struct stat st;
1423
1424         if (stat(LASTLOG_FILE, &st) != 0) {
1425                 logit("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
1426                         strerror(errno));
1427                 return 0;
1428         }
1429         if (S_ISDIR(st.st_mode))
1430                 return LL_DIR;
1431         else if (S_ISREG(st.st_mode))
1432                 return LL_FILE;
1433         else
1434                 return LL_OTHER;
1435 }
1436
1437
1438 /* open the file (using filemode) and seek to the login entry */
1439 static int
1440 lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1441 {
1442         off_t offset;
1443         int type;
1444         char lastlog_file[1024];
1445
1446         type = lastlog_filetype(LASTLOG_FILE);
1447         switch (type) {
1448                 case LL_FILE:
1449                         strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1450                         break;
1451                 case LL_DIR:
1452                         snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1453                                  LASTLOG_FILE, li->username);
1454                         break;
1455                 default:
1456                         logit("lastlog_openseek: %.100s is not a file or directory!",
1457                             LASTLOG_FILE);
1458                         return 0;
1459         }
1460
1461         *fd = open(lastlog_file, filemode, 0600);
1462         if ( *fd < 0) {
1463                 debug("lastlog_openseek: Couldn't open %s: %s",
1464                     lastlog_file, strerror(errno));
1465                 return 0;
1466         }
1467
1468         if (type == LL_FILE) {
1469                 /* find this uid's offset in the lastlog file */
1470                 offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
1471
1472                 if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1473                         logit("lastlog_openseek: %s->lseek(): %s",
1474                          lastlog_file, strerror(errno));
1475                         return 0;
1476                 }
1477         }
1478
1479         return 1;
1480 }
1481
1482 static int
1483 lastlog_perform_login(struct logininfo *li)
1484 {
1485         struct lastlog last;
1486         int fd;
1487
1488         /* create our struct lastlog */
1489         lastlog_construct(li, &last);
1490
1491         if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
1492                 return(0);
1493
1494         /* write the entry */
1495         if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) {
1496                 close(fd);
1497                 logit("lastlog_write_filemode: Error writing to %s: %s",
1498                     LASTLOG_FILE, strerror(errno));
1499                 return 0;
1500         }
1501
1502         close(fd);
1503         return 1;
1504 }
1505
1506 int
1507 lastlog_write_entry(struct logininfo *li)
1508 {
1509         switch(li->type) {
1510         case LTYPE_LOGIN:
1511                 return lastlog_perform_login(li);
1512         default:
1513                 logit("lastlog_write_entry: Invalid type field");
1514                 return 0;
1515         }
1516 }
1517
1518 static void
1519 lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1520 {
1521         line_fullname(li->line, last->ll_line, sizeof(li->line));
1522         strlcpy(li->hostname, last->ll_host,
1523                 MIN_SIZEOF(li->hostname, last->ll_host));
1524         li->tv_sec = last->ll_time;
1525 }
1526
1527 int
1528 lastlog_get_entry(struct logininfo *li)
1529 {
1530         struct lastlog last;
1531         int fd, ret;
1532
1533         if (!lastlog_openseek(li, &fd, O_RDONLY))
1534                 return (0);
1535
1536         ret = atomicio(read, fd, &last, sizeof(last));
1537         close(fd);
1538
1539         switch (ret) {
1540         case 0:
1541                 memset(&last, '\0', sizeof(last));
1542                 /* FALLTHRU */
1543         case sizeof(last):
1544                 lastlog_populate_entry(li, &last);
1545                 return (1);
1546         case -1:
1547                 error("%s: Error reading from %s: %s", __func__,
1548                     LASTLOG_FILE, strerror(errno));
1549                 return (0);
1550         default:
1551                 error("%s: Error reading from %s: Expecting %d, got %d",
1552                     __func__, LASTLOG_FILE, sizeof(last), ret);
1553                 return (0);
1554         }
1555
1556         /* NOTREACHED */
1557         return (0);
1558 }
1559 #endif /* USE_LASTLOG */
This page took 3.263033 seconds and 5 git commands to generate.