]> andersk Git - openssh.git/blob - loginrec.c
- (djm) Fix short copy in loginrec.c (based on patch from Phill Camp
[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  * 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.in 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()q
136  **   test, test, test
137  **
138  ** Platform status:
139  ** ----------------
140  **
141  ** Known good:
142  **   Linux (Redhat 6.2, need more variants)
143  **   HP-UX 10.20 (gcc only)
144  **
145  ** Testing required: Please send reports!
146  **   Solaris
147  **   IRIX
148  **   NetBSD
149  **   HP-UX 11
150  **   AIX
151  **
152  ** Platforms with known problems:
153  **   NeXT
154  **
155  **/
156
157 #include "includes.h"
158
159 #if HAVE_UTMP_H
160 # include <utmp.h>
161 #endif
162 #if HAVE_UTMPX_H
163 # include <utmpx.h>
164 #endif
165 #if HAVE_LASTLOG_H
166 # include <lastlog.h>
167 #endif
168
169 #include "ssh.h"
170 #include "xmalloc.h"
171 #include "loginrec.h"
172
173 RCSID("$Id$");
174
175 /**
176  ** prototypes for helper functions in this file
177  **/
178
179 #if HAVE_UTMP_H
180 void set_utmp_time(struct logininfo *li, struct utmp *ut);
181 void construct_utmp(struct logininfo *li, struct utmp *ut);
182 #endif
183
184 #ifdef HAVE_UTMPX_H
185 void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
186 void construct_utmpx(struct logininfo *li, struct utmpx *ut);
187 #endif
188
189 int utmp_write_entry(struct logininfo *li);
190 int utmpx_write_entry(struct logininfo *li);
191 int wtmp_write_entry(struct logininfo *li);
192 int wtmpx_write_entry(struct logininfo *li);
193 int lastlog_write_entry(struct logininfo *li);
194 int syslogin_write_entry(struct logininfo *li);
195
196 int getlast_entry(struct logininfo *li);
197 int lastlog_get_entry(struct logininfo *li);
198 int wtmp_get_entry(struct logininfo *li);
199 int wtmpx_get_entry(struct logininfo *li);
200
201
202 /**
203  ** platform-independent login functions
204  **/
205
206 /* Record a login */
207 int
208 login_login (struct logininfo *li)
209 {
210         li->type = LTYPE_LOGIN;
211         return login_write(li);
212 }
213
214
215 /* Record a logout */
216 int
217 login_logout(struct logininfo *li)
218 {
219         li->type = LTYPE_LOGOUT;
220         return login_write(li);
221 }
222
223
224 /* Retrieve the last login time for a user (or fake on from wtmp/wtmpx) */
225 unsigned int
226 login_get_lastlog_time(const int uid)
227 {
228         struct logininfo li;
229
230         login_get_lastlog(&li, uid);
231         return li.tv_sec;
232 }
233
234 /* Retrieve a lastlog entry (or fake one from wtmp/wtmpx) */
235 struct logininfo *
236 login_get_lastlog(struct logininfo *li, const int uid)
237 {
238         memset(li, '\0', sizeof(struct logininfo));
239         li->uid = uid;
240         if (getlast_entry(li))
241                 return li;
242         else
243                 return 0;
244 }
245
246
247 /* login_alloc_entry()    - allocate and initialise a logininfo */
248 struct
249 logininfo *login_alloc_entry(int pid, const char *username,
250                              const char *hostname, const char *line)
251 {
252         struct logininfo *newli;
253
254         newli = (struct logininfo *) xmalloc (sizeof(struct logininfo));
255         (void)login_init_entry(newli, pid, username, hostname, line);
256         return newli;
257 }
258
259
260 /* login_free_entry()    - free struct memory (trivial) */
261 void
262 login_free_entry(struct logininfo *li)
263 {
264         xfree(li);
265 }
266
267
268 /* login_init_entry()   - initialise a struct logininfo */
269 int
270 login_init_entry(struct logininfo *li, int pid, const char *username, 
271                  const char *hostname, const char *line)
272 {
273         /* zero the structure */
274         memset(li, 0, sizeof(struct logininfo));
275   
276         li->pid = pid;
277         /* set the line information */
278         if (line)
279                 line_fullname(li->line, line, sizeof(li->line));
280
281         if (username)
282                 strlcpy(li->username, username, sizeof(li->username));
283         if (hostname)
284                 strlcpy(li->hostname, hostname, sizeof(li->hostname));
285         return 1;
286 }
287
288
289 void
290 login_set_current_time(struct logininfo *li)
291 {
292 #ifdef HAVE_SYS_TIME_H
293         struct timeval tv;
294
295         gettimeofday(&tv, NULL);
296         li->tv_sec = tv.tv_sec ; li->tv_usec = tv.tv_usec;
297 #else
298         time_t tm = time(0);
299
300         li->tv_sec = tm; li->tv_usec = 0;
301 #endif
302 }
303
304
305 /* copy a sockaddr_* into our logininfo */
306 void
307 login_set_addr(struct logininfo *li, const struct sockaddr *sa,
308                const unsigned int sa_size)
309 {
310         unsigned int bufsize = sa_size;
311
312         /* make sure we don't overrun our union */
313         if (sizeof(li->hostaddr) < sa_size)
314                 bufsize = sizeof(li->hostaddr);
315
316         memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
317 }
318
319
320 /**
321  ** login_write: Call low-level recording functions based on autoconf
322  ** results
323  **/
324
325 int
326 login_write (struct logininfo *li)
327 {
328         if ((int)geteuid() != 0) {
329           log("Attempt to write login records by non-root user (aborting)");
330           return 1;
331         }
332         /* set the timestamp */
333         login_set_current_time(li);
334 #ifdef USE_LOGIN
335         syslogin_write_entry(li);
336 #endif
337 #ifdef USE_LASTLOG
338         if (li->type == LTYPE_LOGIN) {
339                 lastlog_write_entry(li);
340         }
341 #endif
342 #ifdef USE_UTMP
343         utmp_write_entry(li);
344 #endif
345 #ifdef USE_WTMP
346         wtmp_write_entry(li);
347 #endif
348 #ifdef USE_UTMPX
349         utmpx_write_entry(li);
350 #endif
351 #ifdef USE_WTMPX
352         wtmpx_write_entry(li);
353 #endif
354         return 0;
355 }
356
357
358 /**
359  ** getlast_entry: Call low-level functions to retrieve the last login
360  **                time.
361  **/
362
363 /* take the uid in li and return the last login time */
364 int
365 getlast_entry(struct logininfo *li)
366 {
367 #ifdef USE_LASTLOG
368         if (lastlog_get_entry(li))
369                 return 1;
370         else
371                 return 0;
372 #else
373         /* !USE_LASTLOG */
374
375 #  ifdef DISABLE_LASTLOG
376         /* On some systems we shouldn't even try to obtain last login 
377          * time, e.g. AIX */
378         return 0;
379
380 #  else
381         /* Try to retrieve the last login time from wtmp */
382 #    if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
383         /* retrieve last login time from utmp */
384         if (wtmp_get_entry(li))
385                 return 1;
386         else
387                 return 0;
388 #    else
389
390         /* If wtmp isn't available, try wtmpx */
391
392 #      if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
393         /* retrieve last login time from utmpx */
394         if (wtmpx_get_entry(li))
395                 return 1;
396         else
397                 return 0;
398 #      else
399
400         /* Give up: No means of retrieving last login time */
401         return 0;
402 #      endif
403        /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
404
405 #    endif
406      /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
407 #  endif
408    /* DISABLE_LASTLOG */ 
409 #endif
410 /* USE_LASTLOG */
411 }
412
413
414
415 /*
416  * 'line' string utility functions
417  *
418  * These functions process the 'line' string into one of three forms:
419  *
420  * 1. The full filename (including '/dev')
421  * 2. The stripped name (excluding '/dev')
422  * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
423  *                               /dev/pts/1  -> ts/1 )
424  *
425  * Form 3 is used on some systems to identify a .tmp.? entry when
426  * attempting to remove it. Typically both addition and removal is
427  * performed by one application - say, sshd - so as long as the choice
428  * uniquely identifies a terminal it's ok.
429  */
430
431
432 /* line_fullname(): add the leading '/dev/' if it doesn't exist make
433  * sure dst has enough space, if not just copy src (ugh) */
434 char *
435 line_fullname(char *dst, const char *src, int dstsize)
436 {
437         memset(dst, '\0', dstsize);
438         if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))
439                 strlcpy(dst, src, dstsize);
440         else {
441                 strlcpy(dst, "/dev/", dstsize);
442                 strlcat(dst, src, dstsize);
443         }
444         return dst;
445 }
446
447
448 /* line_stripname(): strip the leading '/dev' if it exists, return dst */
449 char *
450 line_stripname(char *dst, const char *src, int dstsize)
451 {
452         memset(dst, '\0', dstsize);
453         if (strncmp(src, "/dev/", 5) == 0)
454                 strlcpy(dst, &src[5], dstsize);
455         else
456                 strlcpy(dst, src, dstsize);
457         return dst;
458 }
459
460   
461 /* line_abbrevname(): Return the abbreviated (usually four-character)
462  * form of the line (Just use the last <dstsize> characters of the
463  * full name.)
464  *
465  * NOTE: use strncpy because we do NOT necessarily want zero
466  * termination */
467 char *
468 line_abbrevname(char *dst, const char *src, int dstsize) {
469         memset(dst, '\0', dstsize);
470         src += (strlen(src) - dstsize);
471         strncpy(dst, src, dstsize); /* note: _don't_ change this to strlcpy */
472         return dst;
473 }
474
475
476 /**
477  ** utmp utility functions
478  **
479  ** These functions manipulate struct utmp, taking system differences
480  ** into account.
481  **/
482
483 #if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
484
485 /* build the utmp structure */
486 void
487 set_utmp_time(struct logininfo *li, struct utmp *ut)
488 {
489 #ifdef HAVE_TV_IN_UTMP
490         ut->ut_tv.tv_sec = li->tv_sec;
491         ut->ut_tv.tv_usec = li->tv_usec;
492 #else
493 #  ifdef HAVE_TIME_IN_UTMP
494         ut->ut_time = li->tv_sec;
495 #  endif
496 #endif
497 }
498
499
500 void
501 construct_utmp(struct logininfo *li,
502                     struct utmp *ut)
503 {
504         memset(ut, '\0', sizeof(struct utmp));
505 #ifdef HAVE_ID_IN_UTMP
506         line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
507 #endif
508
509 #ifdef HAVE_TYPE_IN_UTMP
510         /* This is done here to keep utmp constants out of login.h */
511         switch (li->type) {
512         case LTYPE_LOGIN:
513                 ut->ut_type = USER_PROCESS;
514                 break;
515         case LTYPE_LOGOUT:
516                 ut->ut_type = DEAD_PROCESS;
517                 break;
518         }
519 #endif
520
521 #ifdef HAVE_PID_IN_UTMP
522         ut->ut_pid = li->pid;
523 #endif
524         line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
525         strlcpy(ut->ut_name, li->username, sizeof(ut->ut_name));
526         set_utmp_time(li, ut);
527 #ifdef HAVE_HOST_IN_UTMP
528         strlcpy(ut->ut_host, li->hostname, sizeof(ut->ut_host));
529 #endif
530 #ifdef HAVE_ADDR_IN_UTMP
531         /* this is just a 32-bit IP address */
532         if (li->hostaddr.sa.sa_family == AF_INET)
533                 ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
534 #endif 
535 }
536
537 #endif
538 /* USE_UTMP || USE_WTMP || USE_LOGIN */
539
540
541
542 /**
543  ** utmpx utility functions
544  **
545  ** These functions manipulate struct utmpx, accounting for system
546  ** variations.
547  **/
548
549 #if defined(USE_UTMPX) || defined (USE_WTMPX)
550
551 /* build the utmpx structure */
552 void
553 set_utmpx_time(struct logininfo *li, struct utmpx *utx)
554 {
555 #ifdef HAVE_TV_IN_UTMPX
556         utx->ut_tv.tv_sec = li->tv_sec;
557         utx->ut_tv.tv_usec = li->tv_usec;
558 #else
559 #  ifdef HAVE_TIME_IN_UTMPX
560         utx->ut_time = li->tv_sec;
561 #  endif
562 #endif
563 }
564
565
566 void
567 construct_utmpx(struct logininfo *li, struct utmpx *utx)
568 {
569         memset(utx, '\0', sizeof(struct utmpx));
570         line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
571
572         /* this is done here to keep utmp constants out of loginrec.h */
573         switch (li->type) {
574         case LTYPE_LOGIN:
575                 utx->ut_type = USER_PROCESS;
576                 break;
577         case LTYPE_LOGOUT:
578                 utx->ut_type = DEAD_PROCESS;
579                 break;
580         }
581         utx->ut_pid = li->pid;
582         line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
583         strlcpy(utx->ut_name, li->username, sizeof(utx->ut_name));
584         set_utmpx_time(li, utx);
585 #ifdef HAVE_HOST_IN_UTMPX
586         strlcpy(utx->ut_host, li->hostname, sizeof(utx->ut_host));
587 #endif
588 #ifdef HAVE_ADDR_IN_UTMPX
589         /* FIXME: (ATL) not supported yet */
590 #endif
591 #ifdef HAVE_SYSLEN_IN_UTMPX  
592         /* this is safe because of the extra nulls in logininfo */
593         utx->ut_syslen = strlen(li->hostname);
594 #endif
595 }
596
597 #endif
598 /* USE_UTMPX || USE_WTMPX */
599
600
601
602 /**
603  ** Low-level utmp functions
604  **/
605
606 /* FIXME: (ATL) utmp_write_direct needs testing */
607
608 #ifdef USE_UTMP
609
610 /* if we can, use pututline() etc. */
611 #if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
612     defined(HAVE_PUTUTLINE)
613 #  define UTMP_USE_LIBRARY
614 #endif
615
616
617 /* write a utmp entry with the system's help (pututline() and pals) */
618 #ifdef UTMP_USE_LIBRARY
619 static int
620 utmp_write_library(struct logininfo *li, struct utmp *ut)
621 {
622         setutent();
623         pututline(ut);
624
625 #ifdef HAVE_ENDUTENT
626         endutent();
627 #endif
628         return 1;
629 }
630
631 #else
632
633 /* write a utmp entry direct to the file */
634 /* This is a slightly modification of code in OpenBSD's login.c */
635 static int
636 utmp_write_direct(struct logininfo *li, struct utmp *ut)
637 {
638         struct utmp old_ut;
639         register int fd;
640         int tty;
641
642         tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
643
644         if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
645                 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
646                 /*
647                  * Prevent luser from zero'ing out ut_host.
648                  * If the new ut_line is empty but the old one is not
649                  * and ut_line and ut_name match, preserve the old ut_line.
650                  */
651                 if (   read(fd, &old_ut, sizeof(struct utmp)) == sizeof(struct utmp)
652                        && ut->ut_host[0] == '\0'
653                        && old_ut.ut_host[0] != '\0'
654                        && strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0
655                        && strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0  )
656                         (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
657
658                 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
659                 if (write(fd, ut, sizeof(struct utmp))==-1)
660                         log("utmp_write_direct: error writing %s: %s",
661                                      UTMP_FILE, strerror(errno));
662       
663                 (void)close(fd);
664                 return 1;
665         } else
666                 return 0;
667 }
668 #endif /* UTMP_USE_LIBRARY */
669
670
671 static int
672 utmp_perform_login(struct logininfo *li)
673 {
674         struct utmp ut;
675
676         construct_utmp(li, &ut);
677 #ifdef UTMP_USE_LIBRARY
678         if (!utmp_write_library(li, &ut)) {
679           log("utmp_perform_login: utmp_write_library() failed");
680                 return 0;
681         }
682 #else
683         if (!utmp_write_direct(li, &ut)) {
684                 log("utmp_perform_login: utmp_write_direct() failed");
685                 return 0;
686         }
687 #endif
688         return 1;
689 }
690
691
692 static int
693 utmp_perform_logout(struct logininfo *li)
694 {
695         struct utmp ut;
696
697         memset(&ut, '\0', sizeof(ut));
698         set_utmp_time(li, &ut);
699         line_stripname(ut.ut_line, li->line, sizeof(ut.ut_line));
700 #ifdef HAVE_ID_IN_UTMP
701         line_abbrevname(ut.ut_id, li->line, sizeof(ut.ut_id));
702 #endif
703 #ifdef HAVE_TYPE_IN_UTMP
704         ut.ut_type = DEAD_PROCESS;
705 #endif
706
707 #if   !defined(DISABLE_PUTUTLINE) \
708     && defined(HAVE_SETUTENT) && defined(HAVE_PUTUTLINE)
709         utmp_write_library(li, &ut);
710 #else
711         utmp_write_direct(li, &ut);
712 #endif
713
714         return 1;
715 }
716
717
718 int
719 utmp_write_entry(struct logininfo *li)
720 {
721         switch(li->type) {
722         case LTYPE_LOGIN:
723                 return utmp_perform_login(li);
724
725         case LTYPE_LOGOUT:
726                 return utmp_perform_logout(li);
727
728         default:
729                 log("utmp_write_entry: invalid type field");
730                 return 0;
731         }
732 }
733
734
735 #endif
736 /* USE_UTMP */
737
738
739 /**
740  ** Low-level utmpx functions
741  **/
742
743 /* not much point if we don't want utmpx entries */
744 #ifdef USE_UTMPX
745
746 /* if we have the wherewithall, use pututxline etc. */
747 #if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) \
748     && defined(HAVE_PUTUTXLINE)
749 #  define UTMPX_USE_LIBRARY
750 #endif
751
752
753 /* write a utmpx entry with the system's help (pututxline() and pals) */
754 #ifdef UTMPX_USE_LIBRARY
755 static int
756 utmpx_write_library(struct logininfo *li, struct utmpx *utx)
757 {
758         setutxent();
759         pututxline(utx);
760
761 #ifdef HAVE_ENDUTXENT
762         endutxent();
763 #endif
764         return 1;
765 }
766
767 #else
768 /* UTMPX_USE_LIBRARY */
769
770
771 /* write a utmp entry direct to the file */
772 static int
773 utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
774 {  
775         log("utmpx_write_direct: not implemented!");
776         return 0;
777 }
778
779 #endif
780 /* UTMPX_USE_LIBRARY */
781
782 static int
783 utmpx_perform_login(struct logininfo *li)
784 {
785         struct utmpx utx;
786
787         construct_utmpx(li, &utx);
788 #ifdef UTMPX_USE_LIBRARY
789         if (!utmpx_write_library(li, &utx)) {
790                 log("utmpx_perform_login: utmp_write_library() failed");
791                 return 0;
792         }
793 #else
794         if (!utmpx_write_direct(li, &ut)) {
795                 log("utmpx_perform_login: utmp_write_direct() failed");
796                 return 0;
797         }
798 #endif
799         return 1;
800 }
801
802
803 static int
804 utmpx_perform_logout(struct logininfo *li)
805 {
806         struct utmpx utx;
807
808         memset(&utx, '\0', sizeof(utx));
809         set_utmpx_time(li, &utx);
810         line_stripname(utx.ut_line, li->line, sizeof(utx.ut_line));
811 #ifdef HAVE_ID_IN_UTMPX
812         line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
813 #endif
814 #ifdef HAVE_TYPE_IN_UTMPX
815         utx.ut_type = DEAD_PROCESS;
816 #endif
817
818 #ifdef UTMPX_USE_LIBRARY
819         utmpx_write_library(li, &utx);
820 #else
821         utmpx_write_direct(li, &utx);
822 #endif
823         return 1;
824 }
825
826
827 int
828 utmpx_write_entry(struct logininfo *li)
829 {
830         switch(li->type) {
831         case LTYPE_LOGIN:
832                 return utmpx_perform_login(li);
833         case LTYPE_LOGOUT:
834                 return utmpx_perform_logout(li);
835         default:
836                 log("utmpx_write_entry: invalid type field");
837                 return 0;
838         }
839 }
840
841
842 #endif
843 /* USE_UTMPX */
844
845
846 /**
847  ** Low-level wtmp functions
848  **/
849
850 #ifdef USE_WTMP 
851
852 /* write a wtmp entry direct to the end of the file */
853 /* This is a slight modification of code in OpenBSD's logwtmp.c */
854 static int
855 wtmp_write(struct logininfo *li, struct utmp *ut)
856 {
857         struct stat buf;
858         int fd, ret = 1;
859
860         if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
861                 log("wtmp_write: problem writing %s: %s",
862                     WTMP_FILE, strerror(errno));
863                 return 0;
864         }
865         if (fstat(fd, &buf) == 0) 
866                 if (write(fd, (char *)ut, sizeof(struct utmp)) != 
867                     sizeof(struct utmp)) {
868                         ftruncate(fd, buf.st_size);
869                         log("wtmp_write: problem writing %s: %s",
870                             WTMP_FILE, strerror(errno));
871                         ret = 0;
872                 }
873         (void)close(fd);
874         return ret;
875 }
876
877
878 static int
879 wtmp_perform_login(struct logininfo *li){
880         struct utmp ut;
881
882         construct_utmp(li, &ut);
883         return wtmp_write(li, &ut);
884 }
885
886
887 static int
888 wtmp_perform_logout(struct logininfo *li)
889 {
890         struct utmp ut;
891
892         construct_utmp(li, &ut);
893         /* blank out unnecessary fields */
894         memset(&(ut.ut_name), '\0', sizeof(ut.ut_name));
895 #ifdef HAVE_ID_IN_UTMP
896         memset(&(ut.ut_id), '\0', sizeof(ut.ut_id));
897 #endif
898 #ifdef HAVE_HOST_IN_UTMP
899         memset(&(ut.ut_host), '\0', sizeof(ut.ut_host));
900 #endif
901 #ifdef HAVE_ADDR_IN_UTMP
902         memset(&(ut.ut_addr), '\0', sizeof(ut.ut_addr));
903 #endif
904         return wtmp_write(li, &ut);
905 }
906
907
908 int
909 wtmp_write_entry(struct logininfo *li)
910 {
911         switch(li->type) {
912         case LTYPE_LOGIN:
913                 return wtmp_perform_login(li);
914         case LTYPE_LOGOUT:
915                 return wtmp_perform_logout(li);
916         default:
917                 log("wtmp_write_entry: invalid type field");
918                 return 0;
919         }
920 }
921
922
923 int
924 wtmp_get_entry(struct logininfo *li)
925 {
926         struct stat st;
927         struct utmp ut;
928         int fd;
929
930         if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
931                 log("wtmp_get_entry: problem opening %s: %s",
932                     WTMP_FILE, strerror(errno));
933                 return 0;
934         }
935         if (fstat(fd, &st) != 0) {
936                 log("wtmp_get_entry: couldn't stat %s: %s",
937                     WTMP_FILE, strerror(errno));
938                 close(fd);
939                 return 0;
940         }
941         (void)lseek(fd, (off_t)(0-sizeof(struct utmp)), SEEK_END);
942
943         do {
944                 if (read(fd, &ut, sizeof(ut)) != sizeof(ut)) {
945                         log("wtmp_get_entry: read of %s failed: %s",
946                             WTMP_FILE, strerror(errno));
947                         close (fd);
948                         return 0;
949                 }
950                 /* Logouts are recorded as a blank username on a particular line.
951                  * So, we just need to find the username in struct utmp */
952                 if ( strncmp(li->username, ut.ut_user, 8) == 0 ) {
953                         /* note we've already made sure there's a time in struct utmp */
954 #ifdef HAVE_TIME_IN_UTMP
955                         li->tv_sec = ut.ut_time;
956 #else
957 #  if HAVE_TV_IN_UTMP
958                         li->tv_sec = ut.ut_tv.tv_sec;
959 #  endif
960 #endif
961                         line_fullname(li->line, ut.ut_line, sizeof(li->line));
962 #ifdef HAVE_HOST_IN_UTMP
963                         strlcpy(li->hostname, ut.ut_host, sizeof(li->hostname));
964 #endif
965                 }
966                 if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) {
967                         close (fd);
968                         return 0;
969                 }
970         } while (li->tv_sec == 0);
971       
972         return 1;
973 }
974
975
976 #endif
977 /* USE_WTMP */
978
979
980 /**
981  ** Low-level wtmpx functions
982  **/
983
984 #ifdef USE_WTMPX
985
986 /* write a wtmpx entry direct to the end of the file */
987 /* This is a slight modification of code in OpenBSD's logwtmp.c */
988 static int
989 wtmpx_write(struct logininfo *li, struct utmpx *utx)
990 {
991         struct stat buf;
992         int fd, ret = 1;
993
994         if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
995                 log("wtmpx_write: problem opening %s: %s",
996                     WTMPX_FILE, strerror(errno));
997                 return 0;
998         }
999
1000         if (fstat(fd, &buf) == 0) 
1001                 if (write(fd, (char *)utx, sizeof(struct utmpx)) != 
1002                     sizeof(struct utmpx)) {
1003                         ftruncate(fd, buf.st_size);
1004                         log("wtmpx_write: problem writing %s: %s",
1005                             WTMPX_FILE, strerror(errno));
1006                         ret = 0;
1007                 }
1008         (void)close(fd);
1009
1010         return ret;
1011 }
1012
1013
1014 static int
1015 wtmpx_perform_login(struct logininfo *li)
1016 {
1017         struct utmpx utx;
1018
1019         construct_utmpx(li, &utx);
1020         return wtmpx_write(li, &utx);
1021 }
1022
1023
1024 static int
1025 wtmpx_perform_logout(struct logininfo *li)
1026 {
1027         struct utmpx utx;
1028
1029         construct_utmpx(li, &utx);
1030         /* blank out unnecessary fields */
1031         memset(&(utx.ut_name), '\0', sizeof(utx.ut_name));
1032 #ifdef HAVE_ID_IN_UTMPX
1033         memset(&(utx.ut_id), '\0', sizeof(utx.ut_id));
1034 #endif
1035 #ifdef HAVE_HOST_IN_UTMPX
1036         memset(&(utx.ut_host), '\0', sizeof(utx.ut_host));
1037 #endif
1038 #ifdef HAVE_ADDR_IN_UTMPX
1039         memset(&(utx.ut_addr), '\0', sizeof(utx.ut_addr));
1040 #endif
1041         return wtmpx_write(li, &utx);
1042
1043 }
1044
1045
1046 int
1047 wtmpx_write_entry(struct logininfo *li)
1048 {
1049         switch(li->type) {
1050         case LTYPE_LOGIN:
1051                 return wtmpx_perform_login(li);
1052         case LTYPE_LOGOUT:
1053                 return wtmpx_perform_logout(li);
1054         default:
1055                 log("wtmpx_write_entry: invalid type field");
1056                 return 0;
1057         }
1058 }
1059
1060
1061 int
1062 wtmpx_get_entry(struct logininfo *li)
1063 {
1064         struct stat st;
1065         struct utmpx utx;
1066         int fd;
1067
1068         if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1069                 log("wtmpx_get_entry: problem opening %s: %s",
1070                     WTMPX_FILE, strerror(errno));
1071                 return 0;
1072         }
1073         if (fstat(fd, &st) != 0) {
1074                 log("wtmpx_get_entry: couldn't stat %s: %s",
1075                     WTMP_FILE, strerror(errno));
1076                 close(fd);
1077                 return 0;
1078         }
1079         (void)lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END);
1080
1081         do {
1082                 if (read(fd, &utx, sizeof(utx)) != sizeof(utx)) {
1083                         log("wtmpx_get_entry: read of %s failed: %s",
1084                             WTMPX_FILE, strerror(errno));
1085                         close (fd);
1086                         return 0;
1087                 }
1088                 /* Logouts are recorded as a blank username on a particular line.
1089                  * So, we just need to find the username in struct utmpx */
1090                 if ( strncmp(li->username, utx.ut_user, 8) == 0 ) {
1091                         /* note we've already made sure there's a time in struct utmp */
1092 #ifdef HAVE_TV_IN_UTMPX
1093                         li->tv_sec = utx.ut_tv.tv_sec;
1094 #else
1095 #  ifdef HAVE_TIME_IN_UTMPX
1096                         li->tv_sec = utx.ut_time;
1097 #  endif
1098 #endif
1099                         line_fullname(li->line, utx.ut_line, sizeof(li->line));
1100 #ifdef HAVE_HOST_IN_UTMPX
1101                         strlcpy(li->hostname, utx.ut_host, sizeof(li->hostname));
1102 #endif
1103                 }
1104                 if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) {
1105                         close (fd);
1106                         return 0;
1107                 }
1108         } while (li->tv_sec == 0);
1109         return 1;
1110 }
1111
1112
1113 #endif /* USE_WTMPX */
1114
1115
1116 /**
1117  ** Low-level libutil login() functions
1118  **/
1119
1120 #ifdef USE_LOGIN
1121
1122 static int
1123 syslogin_perform_login(struct logininfo *li)
1124 {
1125         struct utmp *ut;
1126
1127         if (! (ut = (struct utmp *)malloc(sizeof(struct utmp)))) {
1128                 log("syslogin_perform_login: couldn't malloc()");
1129                 return 0;
1130         }
1131         construct_utmp(li, ut);
1132         login(ut);
1133
1134         return 1;
1135 }
1136
1137
1138 static int
1139 syslogin_perform_logout(struct logininfo *li)
1140 {
1141 #ifdef HAVE_LOGOUT
1142         char line[8];
1143   
1144         (void)line_stripname(line, li->line, sizeof(line));
1145
1146         if (!logout(line)) {
1147                 log("syslogin_perform_logout: logout() returned an error");
1148 # ifdef HAVE_LOGWTMP
1149         } else {
1150                 logwtmp(line, "", "");
1151         }
1152 # endif
1153         /* TODO: what to do if we have login, but no logout?
1154          * what if logout but no logwtmp? All routines are in libutil
1155          * so they should all be there, but... */
1156 #endif
1157         return 1;
1158 }
1159
1160
1161 int
1162 syslogin_write_entry(struct logininfo *li)
1163 {
1164         switch (li->type) {
1165         case LTYPE_LOGIN:
1166                 return syslogin_perform_login(li);
1167         case LTYPE_LOGOUT:
1168                 return syslogin_perform_logout(li);
1169         default:
1170                 log("syslogin_write_entry: Invalid type field");
1171                 return 0;
1172         }
1173 }
1174
1175
1176 #endif /* USE_LOGIN */
1177
1178 /* end of file log-syslogin.c */
1179
1180
1181 /**
1182  ** Low-level lastlog functions
1183  **/
1184
1185 #ifdef USE_LASTLOG
1186
1187 static void
1188 lastlog_construct(struct logininfo *li, struct lastlog *last)
1189 {
1190         /* clear the structure */
1191         memset(last, '\0', sizeof(struct lastlog));
1192   
1193         (void)line_stripname(last->ll_line, li->line,
1194                              sizeof(last->ll_line));
1195         strlcpy(last->ll_host, li->hostname, sizeof(last->ll_host));
1196         last->ll_time = li->tv_sec;
1197 }
1198
1199
1200 #define LL_FILE 1
1201 #define LL_DIR 2
1202 #define LL_OTHER 3
1203
1204 static int
1205 lastlog_filetype(char *filename)
1206 {
1207         struct stat st;
1208
1209         if ( stat(LASTLOG_FILE, &st) != 0) {
1210                 log("lastlog_perform_login: Couldn't stat %s: %s",
1211                     LASTLOG_FILE, strerror(errno));
1212                 return 0;
1213         }
1214         if (S_ISDIR(st.st_mode))
1215                 return LL_DIR;
1216         else if (S_ISREG(st.st_mode))
1217                 return LL_FILE;
1218         else
1219                 return LL_OTHER;
1220 }
1221
1222
1223 /* open the file (using filemode) and seek to the login entry */
1224 static int
1225 lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1226 {
1227         off_t offset;
1228         int type;
1229         char lastlog_file[1024];
1230
1231         type = lastlog_filetype(LASTLOG_FILE);
1232         switch (type) {
1233         case LL_FILE:
1234                 strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1235                 break;
1236         case LL_DIR:
1237                 snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1238                          LASTLOG_FILE, li->username);
1239                 break;
1240         default:
1241                 log("lastlog_openseek: %.100s is not a file or directory!",
1242                     LASTLOG_FILE);
1243                 return 0;
1244         } /* switch */
1245
1246         *fd = open(lastlog_file, filemode);
1247         if ( *fd < 0) {
1248                 log("lastlog_openseek: Couldn't open %s: %s",
1249                     lastlog_file, strerror(errno));
1250                 return 0;
1251         }
1252         /* find this uid's offset in the lastlog file */
1253         offset = (off_t) ( (long)li->uid * sizeof(struct lastlog));
1254
1255         if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1256                 log("lastlog_openseek: %s->lseek(): %s",
1257                     lastlog_file, strerror(errno));
1258                 return 0;
1259         }
1260         return 1;
1261 }
1262
1263 static int
1264 lastlog_perform_login(struct logininfo *li)
1265 {
1266         struct lastlog last;
1267         int fd;
1268
1269         /* create our struct lastlog */
1270         lastlog_construct(li, &last);
1271
1272         /* write the entry */
1273         if (lastlog_openseek(li, &fd, O_RDWR)) {
1274                 if ( write(fd, &last, sizeof(struct lastlog)) 
1275                      != sizeof(struct lastlog) ) {
1276                         log("lastlog_write_filemode: Error writing to %s: %s",
1277                             LASTLOG_FILE, strerror(errno));
1278                         return 0;
1279                 }
1280                 return 1;
1281         } else
1282                 return 0;
1283 }
1284
1285
1286 int
1287 lastlog_write_entry(struct logininfo *li)
1288 {
1289         switch(li->type) {
1290         case LTYPE_LOGIN:
1291                 return lastlog_perform_login(li);
1292         default:
1293                 log("lastlog_write_entry: Invalid type field");
1294                 return 0;
1295         }
1296 }
1297
1298
1299 static void
1300 lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1301 {
1302         line_fullname(li->line, last->ll_line, sizeof(li->line));
1303         strlcpy(li->hostname, last->ll_host, sizeof(li->hostname));
1304         li->tv_sec = last->ll_time;
1305 }
1306
1307
1308 int
1309 lastlog_get_entry(struct logininfo *li)
1310 {
1311         struct lastlog last;
1312         int fd;
1313
1314         if (lastlog_openseek(li, &fd, O_RDONLY)) {
1315                 if ( read(fd, &last, sizeof(struct lastlog)) 
1316                      != sizeof(struct lastlog) ) {
1317                         log("lastlog_write_filemode: Error reading from %s: %s",
1318                             LASTLOG_FILE, strerror(errno));
1319                         return 0;
1320                 } else {
1321                         lastlog_populate_entry(li, &last);
1322                         return 1;
1323                 }
1324         } else
1325                 return 0;    
1326 }
1327
1328
1329 #endif /* USE_LASTLOG */
This page took 0.161302 seconds and 5 git commands to generate.