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