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