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