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