]> andersk Git - openssh.git/blob - loginrec.c
Many changes to new login code based on Damien's feedback:
[openssh.git] / loginrec.c
1 /*
2  * Copyright (c) 2000 Andre Lucas.  All rights reserved.
3  * Portions copyright (c) 1998 Todd C. Miller
4  * Portions copyright (c) 1996 Jason Downs
5  * Portions copyright (c) 1996 Theo de Raadt
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Markus Friedl.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 /** 
34  ** loginrec.c:  platform-independent login recording and lastlog retrieval
35  **/
36
37 /*
38   The new login code explained
39   ============================
40
41   This code attempts to provide a common interface to login recording
42   (utmp and friends) and last login time retrieval.
43
44   Its primary means of achieving this is to use 'struct logininfo', a
45   union of all the useful fields in the various different types of
46   system login record structures one finds on UNIX variants.
47
48   We depend on autoconf to define which recording methods are to be
49   used, and which fields are contained in the relevant data structures
50   on the local system. Many C preprocessor symbols affect which code
51   gets compiled here.
52
53   The code is designed to make it easy to modify a particular
54   recording method, without affecting other methods nor requiring so
55   many nested conditional compilation blocks as were commonplace in
56   the old code.
57
58   For login recording, we try to use the local system's libraries as
59   these are clearly most likely to work correctly. For utmp systems
60   this usually means login() and logout() or setutent() etc., probably
61   in libutil, along with logwtmp() etc. On these systems, we fall back
62   to writing the files directly if we have to, though this method
63   requires very thorough testing so we do not corrupt local auditing
64   information. These files and their access methods are very system
65   specific indeed.
66   
67   For utmpx systems, the corresponding library functions are
68   setutxent() etc. To the author's knowledge, all utmpx systems have
69   these library functions and so no direct write is attempted. If such
70   a system exists and needs support, direct analogues of the [uw]tmp
71   code should suffice.
72
73   Retrieving the time of last login ('lastlog') is in some ways even
74   more problemmatic than login recording. Some systems provide a
75   simple table of all users which we seek based on uid and retrieve a
76   relatively standard structure. Others record the same information in
77   a directory with a separate file, and others don't record the
78   information separately at all. For systems in the latter category,
79   we look backwards in the wtmp or wtmpx file for the last login entry
80   for our user. Naturally this is slower and on busy systems could
81   incur a significant performance penalty.
82
83   Calling the new code
84   --------------------
85   
86   In OpenSSH all login recording and retrieval is performed in
87   login.c. Here you'll find working examples. Also, in the logintest.c
88   program there are more examples.
89
90   Internal handler calling method
91   -------------------------------
92   
93   When a call is made to login_login() or login_logout(), both
94   routines set a struct logininfo flag defining which action (log in,
95   or log out) is to be taken. They both then call login_write(), which
96   calls whichever of the many structure-specific handlers autoconf
97   selects for the local system.
98
99   The handlers themselves handle system data structure specifics. Both
100   struct utmp and struct utmpx have utility functions (see
101   construct_utmp*()) to try to make it simpler to add extra systems
102   that introduce new features to either structure.
103
104   While it may seem terribly wasteful to replicate so much similar
105   code for each method, experience has shown that maintaining code to
106   write both struct utmp and utmpx in one function, whilst maintaining
107   support for all systems whether they have library support or not, is
108   a difficult and time-consuming task.
109
110   Lastlog support proceeds similarly. Functions login_get_lastlog()
111   (and its OpenSSH-tuned friend login_get_lastlog_time()) call
112   getlast_entry(), which tries one of three methods to find the last
113   login time. It uses local system lastlog support if it can,
114   otherwise it tries wtmp or wtmpx before giving up and returning 0,
115   meaning "tilt".
116
117   Maintenance
118   -----------
119
120   In many cases it's possible to tweak autoconf to select the correct
121   methods for a particular platform, either by improving the detection
122   code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE
123   symbols for the platform.
124
125   Use logintest to check which symbols are defined before modifying
126   configure.in and loginrec.c. (You have to build logintest yourself
127   with 'make logintest' as it's not built by default.)
128
129   Otherwise, patches to the specific method(s) are very helpful!
130   
131 */
132
133 /**
134  ** TODO:
135  **   homegrown ttyslot()q
136  **   test, test, test
137  **
138  ** Platform status:
139  ** ----------------
140  **
141  ** Known good:
142  **   Linux (Redhat 6.2, need more variants)
143  **   HP-UX 10.20 (gcc only)
144  **
145  ** Testing required: Please send reports!
146  **   Solaris
147  **   IRIX
148  **   NetBSD
149  **   HP-UX 11
150  **   AIX
151  **
152  ** Platforms with known problems:
153  **   NeXT
154  **
155  **/
156
157 #include "includes.h"
158
159 #include <sys/types.h>
160 #include <sys/stat.h>
161 #include <netinet/in.h>
162 #include <unistd.h>
163 #include <fcntl.h>
164 #include <stdlib.h>
165 #include <string.h>
166 #include <stdio.h>
167 #include <errno.h>
168 #ifdef HAVE_PWD_H
169 #  include <pwd.h>
170 #endif
171 #ifdef HAVE_SYS_TIME_H
172 #  include <sys/time.h>
173 #else
174 #  include <time.h>
175 #endif
176 #if HAVE_UTMP_H
177 # include <utmp.h>
178 #endif
179 #if HAVE_UTMPX_H
180 # include <utmpx.h>
181 #endif
182 #if HAVE_LASTLOG_H
183 # include <lastlog.h>
184 #endif
185
186 #include "ssh.h"
187 #include "xmalloc.h"
188 #include "loginrec.h"
189
190 RCSID("$Id$");
191
192
193 /**
194  ** prototypes for helper functions in this file
195  **/
196
197 #if HAVE_UTMP_H
198 void set_utmp_time(struct logininfo *li, struct utmp *ut);
199 void construct_utmp(struct logininfo *li, struct utmp *ut);
200 #endif
201
202 #ifdef HAVE_UTMPX_H
203 void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
204 void construct_utmpx(struct logininfo *li, struct utmpx *ut);
205 #endif
206
207 int utmp_write_entry(struct logininfo *li);
208 int utmpx_write_entry(struct logininfo *li);
209 int wtmp_write_entry(struct logininfo *li);
210 int wtmpx_write_entry(struct logininfo *li);
211 int lastlog_write_entry(struct logininfo *li);
212 int syslogin_write_entry(struct logininfo *li);
213
214 int getlast_entry(struct logininfo *li);
215 int lastlog_get_entry(struct logininfo *li);
216 int wtmp_get_entry(struct logininfo *li);
217 int wtmpx_get_entry(struct logininfo *li);
218
219
220 /**
221  ** platform-independent login functions
222  **/
223
224 /* Record a login */
225 int
226 login_login (struct logininfo *li)
227 {
228         li->type = LTYPE_LOGIN;
229         return login_write(li);
230 }
231
232
233 /* Record a logout */
234 int
235 login_logout(struct logininfo *li)
236 {
237         li->type = LTYPE_LOGOUT;
238         return login_write(li);
239 }
240
241
242 /* Retrieve the last login time for a user (or fake on from wtmp/wtmpx) */
243 unsigned int
244 login_get_lastlog_time(const int uid)
245 {
246         struct logininfo li;
247
248         login_get_lastlog(&li, uid);
249         return li.tv_sec;
250 }
251
252 /* Retrieve a lastlog entry (or fake one from wtmp/wtmpx) */
253 struct logininfo *
254 login_get_lastlog(struct logininfo *li, const int uid)
255 {
256         memset(li, '\0', sizeof(struct logininfo));
257         li->uid = uid;
258         if (getlast_entry(li))
259                 return li;
260         else
261                 return 0;
262 }
263
264
265 /* login_alloc_entry()    - allocate and initialise a logininfo */
266 struct
267 logininfo *login_alloc_entry(int pid, const char *username,
268                              const char *hostname, const char *line)
269 {
270         struct logininfo *newli;
271
272         newli = (struct logininfo *) xmalloc (sizeof(struct logininfo));
273         (void)login_init_entry(newli, pid, username, hostname, line);
274         return newli;
275 }
276
277
278 /* login_free_entry()    - free struct memory (trivial) */
279 void
280 login_free_entry(struct logininfo *li)
281 {
282         xfree(li);
283 }
284
285
286 /* login_init_entry()   - initialise a struct logininfo */
287 int
288 login_init_entry(struct logininfo *li, int pid, const char *username, 
289                  const char *hostname, const char *line)
290 {
291         /* zero the structure */
292         memset(li, 0, sizeof(struct logininfo));
293   
294         li->pid = pid;
295         /* set the line information */
296         if (line)
297                 line_fullname(li->line, line, sizeof(li->line));
298
299         if (username)
300                 strlcpy(li->username, username, sizeof(li->username));
301         if (hostname)
302                 strlcpy(li->hostname, hostname, sizeof(li->hostname));
303         return 1;
304 }
305
306
307 void
308 login_set_current_time(struct logininfo *li)
309 {
310 #ifdef HAVE_SYS_TIME_H
311         struct timeval tv;
312
313         gettimeofday(&tv, NULL);
314         li->tv_sec = tv.tv_sec ; li->tv_usec = tv.tv_usec;
315 #else
316         time_t tm = time(0);
317
318         li->tv_sec = tm; li->tv_usec = 0;
319 #endif
320 }
321
322
323 /* copy a sockaddr_* into our logininfo */
324 void
325 login_set_addr(struct logininfo *li, const struct sockaddr *sa,
326                const unsigned int sa_size)
327 {
328         unsigned int bufsize = sa_size;
329
330         /* make sure we don't overrun our union */
331         if (sizeof(li->hostaddr) < sa_size)
332                 bufsize = sizeof(li->hostaddr);
333
334         memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
335 }
336
337
338 /**
339  ** login_write: Call low-level recording functions based on autoconf
340  ** results
341  **/
342
343 int
344 login_write (struct logininfo *li)
345 {
346         if ((int)geteuid() != 0) {
347           log("Attempt to write login records by non-root user (aborting)");
348           return 1;
349         }
350         /* set the timestamp */
351         login_set_current_time(li);
352 #ifdef USE_LOGIN
353         syslogin_write_entry(li);
354 #endif
355 #ifdef USE_LASTLOG
356         if (li->type == LTYPE_LOGIN) {
357                 lastlog_write_entry(li);
358         }
359 #endif
360 #ifdef USE_UTMP
361         utmp_write_entry(li);
362 #endif
363 #ifdef USE_WTMP
364         wtmp_write_entry(li);
365 #endif
366 #ifdef USE_UTMPX
367         utmpx_write_entry(li);
368 #endif
369 #ifdef USE_WTMPX
370         wtmpx_write_entry(li);
371 #endif
372         return 0;
373 }
374
375
376 /**
377  ** getlast_entry: Call low-level functions to retrieve the last login
378  **                time.
379  **/
380
381 /* take the uid in li and return the last login time */
382 int
383 getlast_entry(struct logininfo *li)
384 {
385 #ifdef USE_LASTLOG
386         if (lastlog_get_entry(li))
387                 return 1;
388         else
389                 return 0;
390 #else
391         /* !USE_LASTLOG */
392
393         /* Try to retrieve the last login time from wtmp */
394 #  if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
395         /* retrieve last login time from utmp */
396         if (wtmp_get_entry(li))
397                 return 1;
398         else
399                 return 0;
400 #  else
401
402         /* If wtmp isn't available, try wtmpx */
403
404 #    if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
405         /* retrieve last login time from utmpx */
406         if (wtmpx_get_entry(li))
407                 return 1;
408         else
409                 return 0;
410 #    else
411
412         /* Give up: No means of retrieving last login time */
413         return 0;
414 #    endif
415 #  endif
416 #endif
417 /* USE_LASTLOG */
418 }
419
420
421
422 /*
423  * 'line' string utility functions
424  *
425  * These functions process the 'line' string into one of three forms:
426  *
427  * 1. The full filename (including '/dev')
428  * 2. The stripped name (excluding '/dev')
429  * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
430  *                               /dev/pts/1  -> ts/1 )
431  *
432  * Form 3 is used on some systems to identify a .tmp.? entry when
433  * attempting to remove it. Typically both addition and removal is
434  * performed by one application - say, sshd - so as long as the choice
435  * uniquely identifies a terminal it's ok.
436  */
437
438
439 /* line_fullname(): add the leading '/dev/' if it doesn't exist make
440  * sure dst has enough space, if not just copy src (ugh) */
441 char *
442 line_fullname(char *dst, const char *src, int dstsize)
443 {
444         memset(dst, '\0', dstsize);
445         if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))
446                 strlcpy(dst, src, dstsize);
447         else {
448                 strlcpy(dst, "/dev/", 5);
449                 strlcat(dst, src, dstsize);
450         }
451         return dst;
452 }
453
454
455 /* line_stripname(): strip the leading '/dev' if it exists, return dst */
456 char *
457 line_stripname(char *dst, const char *src, int dstsize)
458 {
459         memset(dst, '\0', dstsize);
460         if (strncmp(src, "/dev/", 5) == 0)
461                 strlcpy(dst, &src[5], dstsize);
462         else
463                 strlcpy(dst, src, dstsize);
464         return dst;
465 }
466
467   
468 /* line_abbrevname(): Return the abbreviated (usually four-character)
469  * form of the line (Just use the last <dstsize> characters of the
470  * full name.)
471  *
472  * NOTE: use strncpy because we do NOT necessarily want zero
473  * termination */
474 char *
475 line_abbrevname(char *dst, const char *src, int dstsize) {
476         memset(dst, '\0', dstsize);
477         src += (strlen(src) - dstsize);
478         strncpy(dst, src, dstsize); /* note: _don't_ change this to strlcpy */
479         return dst;
480 }
481
482
483 /**
484  ** utmp utility functions
485  **
486  ** These functions manipulate struct utmp, taking system differences
487  ** into account.
488  **/
489
490 #if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
491
492 /* build the utmp structure */
493 void
494 set_utmp_time(struct logininfo *li, struct utmp *ut)
495 {
496 #ifdef HAVE_TV_IN_UTMP
497         ut->ut_tv.tv_sec = li->tv_sec;
498         ut->ut_tv.tv_usec = li->tv_usec;
499 #else
500 #  ifdef HAVE_TIME_IN_UTMP
501         ut->ut_time = li->tv_sec;
502 #  endif
503 #endif
504 }
505
506
507 void
508 construct_utmp(struct logininfo *li,
509                     struct utmp *ut)
510 {
511         memset(ut, '\0', sizeof(struct utmp));
512 #ifdef HAVE_ID_IN_UTMP
513         line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
514 #endif
515
516 #ifdef HAVE_TYPE_IN_UTMP
517         /* This is done here to keep utmp constants out of login.h */
518         switch (li->type) {
519         case LTYPE_LOGIN:
520                 ut->ut_type = USER_PROCESS;
521                 break;
522         case LTYPE_LOGOUT:
523                 ut->ut_type = DEAD_PROCESS;
524                 break;
525         }
526 #endif
527
528 #ifdef HAVE_PID_IN_UTMP
529         ut->ut_pid = li->pid;
530 #endif
531         line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
532         strlcpy(ut->ut_name, li->username, sizeof(ut->ut_name));
533         set_utmp_time(li, ut);
534 #ifdef HAVE_HOST_IN_UTMP
535         strlcpy(ut->ut_host, li->hostname, sizeof(ut->ut_host));
536 #endif
537 #ifdef HAVE_ADDR_IN_UTMP
538         /* this is just a 32-bit IP address */
539         if (li->hostaddr.sa.sa_family == AF_INET)
540                 ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
541 #endif 
542 }
543
544 #endif
545 /* USE_UTMP || USE_WTMP || USE_LOGIN */
546
547
548
549 /**
550  ** utmpx utility functions
551  **
552  ** These functions manipulate struct utmpx, accounting for system
553  ** variations.
554  **/
555
556 #if defined(USE_UTMPX) || defined (USE_WTMPX)
557
558 /* build the utmpx structure */
559 void
560 set_utmpx_time(struct logininfo *li, struct utmpx *utx)
561 {
562 #ifdef HAVE_TV_IN_UTMPX
563         utx->ut_tv.tv_sec = li->tv_sec;
564         utx->ut_tv.tv_usec = li->tv_usec;
565 #else
566 #  ifdef HAVE_TIME_IN_UTMPX
567         utx->ut_time = li->tv_sec;
568 #  endif
569 #endif
570 }
571
572
573 void
574 construct_utmpx(struct logininfo *li, struct utmpx *utx)
575 {
576         memset(utx, '\0', sizeof(struct utmpx));
577         line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
578
579         /* this is done here to keep utmp constants out of loginrec.h */
580         switch (li->type) {
581         case LTYPE_LOGIN:
582                 utx->ut_type = USER_PROCESS;
583                 break;
584         case LTYPE_LOGOUT:
585                 utx->ut_type = DEAD_PROCESS;
586                 break;
587         }
588         utx->ut_pid = li->pid;
589         line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
590         strlcpy(utx->ut_name, li->username, sizeof(utx->ut_name));
591         set_utmpx_time(li, utx);
592 #ifdef HAVE_HOST_IN_UTMPX
593         strlcpy(utx->ut_host, li->hostname, sizeof(utx->ut_host));
594 #endif
595 #ifdef HAVE_ADDR_IN_UTMPX
596         /* FIXME: (ATL) not supported yet */
597 #endif
598 #ifdef HAVE_SYSLEN_IN_UTMPX  
599         /* this is safe because of the extra nulls in logininfo */
600         utx->ut_syslen = strlen(li->hostname);
601 #endif
602 }
603
604 #endif
605 /* USE_UTMPX || USE_WTMPX */
606
607
608
609 /**
610  ** Low-level utmp functions
611  **/
612
613 /* FIXME: (ATL) utmp_write_direct needs testing */
614
615 #ifdef USE_UTMP
616
617 /* if we can, use pututline() etc. */
618 #if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
619     defined(HAVE_PUTUTLINE)
620 #  define UTMP_USE_LIBRARY
621 #endif
622
623
624 /* write a utmp entry with the system's help (pututline() and pals) */
625 #ifdef UTMP_USE_LIBRARY
626 static int
627 utmp_write_library(struct logininfo *li, struct utmp *ut)
628 {
629         setutent();
630         pututline(ut);
631
632 #ifdef HAVE_ENDUTENT
633         endutent();
634 #endif
635         return 1;
636 }
637
638 #else
639
640 /* write a utmp entry direct to the file */
641 /* This is a slightly modification of code in OpenBSD's login.c */
642 static int
643 utmp_write_direct(struct logininfo *li, struct utmp *ut)
644 {
645         struct utmp old_ut;
646         register int fd;
647         int tty;
648
649         tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
650
651         if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
652                 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
653                 /*
654                  * Prevent luser from zero'ing out ut_host.
655                  * If the new ut_line is empty but the old one is not
656                  * and ut_line and ut_name match, preserve the old ut_line.
657                  */
658                 if (   read(fd, &old_ut, sizeof(struct utmp)) == sizeof(struct utmp)
659                        && ut->ut_host[0] == '\0'
660                        && old_ut.ut_host[0] != '\0'
661                        && strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0
662                        && strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0  )
663                         (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
664
665                 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
666                 if (write(fd, ut, sizeof(struct utmp))==-1)
667                         log("utmp_write_direct: error writing %s: %s",
668                                      UTMP_FILE, strerror(errno));
669       
670                 (void)close(fd);
671                 return 1;
672         } else
673                 return 0;
674 }
675 #endif /* UTMP_USE_LIBRARY */
676
677
678 static int
679 utmp_perform_login(struct logininfo *li)
680 {
681         struct utmp ut;
682
683         construct_utmp(li, &ut);
684 #ifdef UTMP_USE_LIBRARY
685         if (!utmp_write_library(li, &ut)) {
686           log("utmp_perform_login: utmp_write_library() failed");
687                 return 0;
688         }
689 #else
690         if (!utmp_write_direct(li, &ut)) {
691                 log("utmp_perform_login: utmp_write_direct() failed");
692                 return 0;
693         }
694 #endif
695         return 1;
696 }
697
698
699 static int
700 utmp_perform_logout(struct logininfo *li)
701 {
702         struct utmp ut;
703
704         memset(&ut, '\0', sizeof(ut));
705         set_utmp_time(li, &ut);
706         line_stripname(ut.ut_line, li->line, sizeof(ut.ut_line));
707 #ifdef HAVE_ID_IN_UTMP
708         line_abbrevname(ut.ut_id, li->line, sizeof(ut.ut_id));
709 #endif
710 #ifdef HAVE_TYPE_IN_UTMP
711         ut.ut_type = DEAD_PROCESS;
712 #endif
713
714 #if   !defined(DISABLE_PUTUTLINE) \
715     && defined(HAVE_SETUTENT) && defined(HAVE_PUTUTLINE)
716         utmp_write_library(li, &ut);
717 #else
718         utmp_write_direct(li, &ut);
719 #endif
720
721         return 1;
722 }
723
724
725 int
726 utmp_write_entry(struct logininfo *li)
727 {
728         switch(li->type) {
729         case LTYPE_LOGIN:
730                 return utmp_perform_login(li);
731
732         case LTYPE_LOGOUT:
733                 return utmp_perform_logout(li);
734
735         default:
736                 log("utmp_write_entry: invalid type field");
737                 return 0;
738         }
739 }
740
741
742 #endif
743 /* USE_UTMP */
744
745
746 /**
747  ** Low-level utmpx functions
748  **/
749
750 /* not much point if we don't want utmpx entries */
751 #ifdef USE_UTMPX
752
753 /* if we have the wherewithall, use pututxline etc. */
754 #if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) \
755     && defined(HAVE_PUTUTXLINE)
756 #  define UTMPX_USE_LIBRARY
757 #endif
758
759
760 /* write a utmpx entry with the system's help (pututxline() and pals) */
761 #ifdef UTMPX_USE_LIBRARY
762 static int
763 utmpx_write_library(struct logininfo *li, struct utmpx *utx)
764 {
765         setutxent();
766         pututxline(utx);
767
768 #ifdef HAVE_ENDUTXENT
769         endutxent();
770 #endif
771         return 1;
772 }
773
774 #else
775 /* UTMPX_USE_LIBRARY */
776
777
778 /* write a utmp entry direct to the file */
779 static int
780 utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
781 {  
782         log("utmpx_write_direct: not implemented!");
783         return 0;
784 }
785
786 #endif
787 /* UTMPX_USE_LIBRARY */
788
789 static int
790 utmpx_perform_login(struct logininfo *li)
791 {
792         struct utmpx utx;
793
794         construct_utmpx(li, &utx);
795 #ifdef UTMPX_USE_LIBRARY
796         if (!utmpx_write_library(li, &utx)) {
797                 log("utmpx_perform_login: utmp_write_library() failed");
798                 return 0;
799         }
800 #else
801         if (!utmpx_write_direct(li, &ut)) {
802                 log("utmpx_perform_login: utmp_write_direct() failed");
803                 return 0;
804         }
805 #endif
806         return 1;
807 }
808
809
810 static int
811 utmpx_perform_logout(struct logininfo *li)
812 {
813         struct utmpx utx;
814
815         memset(&utx, '\0', sizeof(utx));
816         set_utmpx_time(li, &utx);
817         line_stripname(utx.ut_line, li->line, sizeof(utx.ut_line));
818 #ifdef HAVE_ID_IN_UTMPX
819         line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
820 #endif
821 #ifdef HAVE_TYPE_IN_UTMPX
822         utx.ut_type = DEAD_PROCESS;
823 #endif
824
825 #ifdef UTMPX_USE_LIBRARY
826         utmpx_write_library(li, &utx);
827 #else
828         utmpx_write_direct(li, &utx);
829 #endif
830         return 1;
831 }
832
833
834 int
835 utmpx_write_entry(struct logininfo *li)
836 {
837         switch(li->type) {
838         case LTYPE_LOGIN:
839                 return utmpx_perform_login(li);
840         case LTYPE_LOGOUT:
841                 return utmpx_perform_logout(li);
842         default:
843                 log("utmpx_write_entry: invalid type field");
844                 return 0;
845         }
846 }
847
848
849 #endif
850 /* USE_UTMPX */
851
852
853 /**
854  ** Low-level wtmp functions
855  **/
856
857 #ifdef USE_WTMP 
858
859 /* write a wtmp entry direct to the end of the file */
860 /* This is a slight modification of code in OpenBSD's logwtmp.c */
861 static int
862 wtmp_write(struct logininfo *li, struct utmp *ut)
863 {
864         struct stat buf;
865         int fd, ret = 1;
866
867         if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
868                 log("wtmp_write: problem writing %s: %s",
869                     WTMP_FILE, strerror(errno));
870                 return 0;
871         }
872         if (fstat(fd, &buf) == 0) 
873                 if (write(fd, (char *)ut, sizeof(struct utmp)) != 
874                     sizeof(struct utmp)) {
875                         ftruncate(fd, buf.st_size);
876                         log("wtmp_write: problem writing %s: %s",
877                             WTMP_FILE, strerror(errno));
878                         ret = 0;
879                 }
880         (void)close(fd);
881         return ret;
882 }
883
884
885 static int
886 wtmp_perform_login(struct logininfo *li){
887         struct utmp ut;
888
889         construct_utmp(li, &ut);
890         return wtmp_write(li, &ut);
891 }
892
893
894 static int
895 wtmp_perform_logout(struct logininfo *li)
896 {
897         struct utmp ut;
898
899         construct_utmp(li, &ut);
900         /* blank out unnecessary fields */
901         memset(&(ut.ut_name), '\0', sizeof(ut.ut_name));
902 #ifdef HAVE_ID_IN_UTMP
903         memset(&(ut.ut_id), '\0', sizeof(ut.ut_id));
904 #endif
905 #ifdef HAVE_HOST_IN_UTMP
906         memset(&(ut.ut_host), '\0', sizeof(ut.ut_host));
907 #endif
908 #ifdef HAVE_ADDR_IN_UTMP
909         memset(&(ut.ut_addr), '\0', sizeof(ut.ut_addr));
910 #endif
911         return wtmp_write(li, &ut);
912 }
913
914
915 int
916 wtmp_write_entry(struct logininfo *li)
917 {
918         switch(li->type) {
919         case LTYPE_LOGIN:
920                 return wtmp_perform_login(li);
921         case LTYPE_LOGOUT:
922                 return wtmp_perform_logout(li);
923         default:
924                 log("wtmp_write_entry: invalid type field");
925                 return 0;
926         }
927 }
928
929
930 int
931 wtmp_get_entry(struct logininfo *li)
932 {
933         struct stat st;
934         struct utmp ut;
935         int fd;
936
937         if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
938                 log("wtmp_get_entry: problem opening %s: %s",
939                     WTMP_FILE, strerror(errno));
940                 return 0;
941         }
942         if (fstat(fd, &st) != 0) {
943                 log("wtmp_get_entry: couldn't stat %s: %s",
944                     WTMP_FILE, strerror(errno));
945                 close(fd);
946                 return 0;
947         }
948         (void)lseek(fd, (off_t)(0-sizeof(struct utmp)), SEEK_END);
949
950         do {
951                 if (read(fd, &ut, sizeof(ut)) != sizeof(ut)) {
952                         log("wtmp_get_entry: read of %s failed: %s",
953                             WTMP_FILE, strerror(errno));
954                         close (fd);
955                         return 0;
956                 }
957                 /* Logouts are recorded as a blank username on a particular line.
958                  * So, we just need to find the username in struct utmp */
959                 if ( strncmp(li->username, ut.ut_user, 8) == 0 ) {
960                         /* note we've already made sure there's a time in struct utmp */
961 #ifdef HAVE_TIME_IN_UTMP
962                         li->tv_sec = ut.ut_time;
963 #else
964 #  if HAVE_TV_IN_UTMP
965                         li->tv_sec = ut.ut_tv.tv_sec;
966 #  endif
967 #endif
968                         line_fullname(li->line, ut.ut_line, sizeof(ut.ut_line));
969 #ifdef HAVE_HOST_IN_UTMP
970                         strlcpy(li->hostname, ut.ut_host, sizeof(ut.ut_host));
971 #endif
972                 }
973                 if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) {
974                         close (fd);
975                         return 0;
976                 }
977         } while (li->tv_sec == 0);
978       
979         return 1;
980 }
981
982
983 #endif
984 /* USE_WTMP */
985
986
987 /**
988  ** Low-level wtmpx functions
989  **/
990
991 #ifdef USE_WTMPX
992
993 /* write a wtmpx entry direct to the end of the file */
994 /* This is a slight modification of code in OpenBSD's logwtmp.c */
995 static int
996 wtmpx_write(struct logininfo *li, struct utmpx *utx)
997 {
998         struct stat buf;
999         int fd, ret = 1;
1000
1001         if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1002                 log("wtmpx_write: problem opening %s: %s",
1003                     WTMPX_FILE, strerror(errno));
1004                 return 0;
1005         }
1006
1007         if (fstat(fd, &buf) == 0) 
1008                 if (write(fd, (char *)utx, sizeof(struct utmpx)) != 
1009                     sizeof(struct utmpx)) {
1010                         ftruncate(fd, buf.st_size);
1011                         log("wtmpx_write: problem writing %s: %s",
1012                             WTMPX_FILE, strerror(errno));
1013                         ret = 0;
1014                 }
1015         (void)close(fd);
1016
1017         return ret;
1018 }
1019
1020
1021 static int
1022 wtmpx_perform_login(struct logininfo *li)
1023 {
1024         struct utmpx utx;
1025
1026         construct_utmpx(li, &utx);
1027         return wtmpx_write(li, &utx);
1028 }
1029
1030
1031 static int
1032 wtmpx_perform_logout(struct logininfo *li)
1033 {
1034         struct utmpx utx;
1035
1036         construct_utmpx(li, &utx);
1037         /* blank out unnecessary fields */
1038         memset(&(utx.ut_name), '\0', sizeof(utx.ut_name));
1039 #ifdef HAVE_ID_IN_UTMPX
1040         memset(&(utx.ut_id), '\0', sizeof(utx.ut_id));
1041 #endif
1042 #ifdef HAVE_HOST_IN_UTMPX
1043         memset(&(utx.ut_host), '\0', sizeof(utx.ut_host));
1044 #endif
1045 #ifdef HAVE_ADDR_IN_UTMPX
1046         memset(&(utx.ut_addr), '\0', sizeof(utx.ut_addr));
1047 #endif
1048         return wtmpx_write(li, &utx);
1049
1050 }
1051
1052
1053 int
1054 wtmpx_write_entry(struct logininfo *li)
1055 {
1056         switch(li->type) {
1057         case LTYPE_LOGIN:
1058                 return wtmpx_perform_login(li);
1059         case LTYPE_LOGOUT:
1060                 return wtmpx_perform_logout(li);
1061         default:
1062                 log("wtmpx_write_entry: invalid type field");
1063                 return 0;
1064         }
1065 }
1066
1067
1068 int
1069 wtmpx_get_entry(struct logininfo *li)
1070 {
1071         struct stat st;
1072         struct utmpx utx;
1073         int fd;
1074
1075         if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1076                 log("wtmpx_get_entry: problem opening %s: %s",
1077                     WTMPX_FILE, strerror(errno));
1078                 return 0;
1079         }
1080         if (fstat(fd, &st) != 0) {
1081                 log("wtmpx_get_entry: couldn't stat %s: %s",
1082                     WTMP_FILE, strerror(errno));
1083                 close(fd);
1084                 return 0;
1085         }
1086         (void)lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END);
1087
1088         do {
1089                 if (read(fd, &utx, sizeof(utx)) != sizeof(utx)) {
1090                         log("wtmpx_get_entry: read of %s failed: %s",
1091                             WTMPX_FILE, strerror(errno));
1092                         close (fd);
1093                         return 0;
1094                 }
1095                 /* Logouts are recorded as a blank username on a particular line.
1096                  * So, we just need to find the username in struct utmpx */
1097                 if ( strncmp(li->username, utx.ut_user, 8) == 0 ) {
1098                         /* note we've already made sure there's a time in struct utmp */
1099 #ifdef HAVE_TV_IN_UTMPX
1100                         li->tv_sec = utx.ut_tv.tv_sec;
1101 #else
1102 #  ifdef HAVE_TIME_IN_UTMPX
1103                         li->tv_sec = utx.ut_time;
1104 #  endif
1105 #endif
1106                         line_fullname(li->line, utx.ut_line, sizeof(utx.ut_line));
1107 #ifdef HAVE_HOST_IN_UTMPX
1108                         strlcpy(li->hostname, utx.ut_host, sizeof(utx.ut_line));
1109 #endif
1110                 }
1111                 if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) {
1112                         close (fd);
1113                         return 0;
1114                 }
1115         } while (li->tv_sec == 0);
1116         return 1;
1117 }
1118
1119
1120 #endif
1121 /* USE_WTMPX */
1122
1123
1124
1125 /**
1126  ** Low-level libutil login() functions
1127  **/
1128
1129 #ifdef USE_LOGIN
1130
1131 static int
1132 syslogin_perform_login(struct logininfo *li)
1133 {
1134         struct utmp *ut;
1135
1136         if (! (ut = (struct utmp *)malloc(sizeof(struct utmp)))) {
1137                 log("syslogin_perform_login: couldn't malloc()");
1138                 return 0;
1139         }
1140         construct_utmp(li, ut);
1141         login(ut);
1142
1143         return 1;
1144 }
1145
1146
1147 static int
1148 syslogin_perform_logout(struct logininfo *li)
1149 {
1150 #ifdef HAVE_LOGOUT
1151         char line[8];
1152   
1153         (void)line_stripname(line, li->line, sizeof(line));
1154
1155         if (!logout(line)) {
1156                 log("syslogin_perform_logout: logout() returned an error");
1157 #  ifdef HAVE_LOGWTMP
1158         } else {
1159                 logwtmp(line, "", "");
1160         }
1161 #  endif
1162         /* TODO: what to do if we have login, but no logout?
1163          * what if logout but no logwtmp? All routines are in libutil
1164          * so they should all be there, but... */
1165 #endif
1166         return 1;
1167 }
1168
1169
1170 int
1171 syslogin_write_entry(struct logininfo *li)
1172 {
1173         switch (li->type) {
1174         case LTYPE_LOGIN:
1175                 return syslogin_perform_login(li);
1176         case LTYPE_LOGOUT:
1177                 return syslogin_perform_logout(li);
1178         default:
1179                 log("syslogin_write_entry: Invalid type field");
1180                 return 0;
1181         }
1182 }
1183
1184
1185 #endif
1186 /* USE_LOGIN */
1187
1188 /* end of file log-syslogin.c */
1189
1190
1191 /**
1192  ** Low-level lastlog functions
1193  **/
1194
1195 #ifdef USE_LASTLOG
1196
1197 static void
1198 lastlog_construct(struct logininfo *li, struct lastlog *last)
1199 {
1200         /* clear the structure */
1201         memset(last, '\0', sizeof(struct lastlog));
1202   
1203         (void)line_stripname(last->ll_line, li->line,
1204                              sizeof(last->ll_line));
1205         strlcpy(last->ll_host, li->hostname, sizeof(last->ll_host));
1206         last->ll_time = li->tv_sec;
1207 }
1208
1209
1210 #define LL_FILE 1
1211 #define LL_DIR 2
1212 #define LL_OTHER 3
1213
1214 static int
1215 lastlog_filetype(char *filename)
1216 {
1217         struct stat st;
1218
1219         if ( stat(LASTLOG_FILE, &st) != 0) {
1220                 log("lastlog_perform_login: Couldn't stat %s: %s",
1221                     LASTLOG_FILE, strerror(errno));
1222                 return 0;
1223         }
1224         if (S_ISDIR(st.st_mode))
1225                 return LL_DIR;
1226         else if (S_ISREG(st.st_mode))
1227                 return LL_FILE;
1228         else
1229                 return LL_OTHER;
1230 }
1231
1232
1233 /* open the file (using filemode) and seek to the login entry */
1234 static int
1235 lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1236 {
1237         off_t offset;
1238         int type;
1239         char lastlog_file[1024];
1240
1241         type = lastlog_filetype(LASTLOG_FILE);
1242         switch (type) {
1243         case LL_FILE:
1244                 strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1245                 break;
1246         case LL_DIR:
1247                 snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1248                          LASTLOG_FILE, li->username);
1249                 break;
1250         default:
1251                 log("lastlog_openseek: %.100s is not a file or directory!",
1252                     LASTLOG_FILE);
1253                 return 0;
1254         } /* switch */
1255
1256         *fd = open(lastlog_file, filemode);
1257         if ( *fd < 0) {
1258                 log("lastlog_openseek: Couldn't open %s: %s",
1259                     lastlog_file, strerror(errno));
1260                 return 0;
1261         }
1262         /* find this uid's offset in the lastlog file */
1263         offset = (off_t) ( (long)li->uid * sizeof(struct lastlog));
1264
1265         if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1266                 log("lastlog_openseek: %s->lseek(): %s",
1267                     lastlog_file, strerror(errno));
1268                 return 0;
1269         }
1270         return 1;
1271 }
1272
1273 static int
1274 lastlog_perform_login(struct logininfo *li)
1275 {
1276         struct lastlog last;
1277         int fd;
1278
1279         /* create our struct lastlog */
1280         lastlog_construct(li, &last);
1281
1282         /* write the entry */
1283         if (lastlog_openseek(li, &fd, O_RDWR)) {
1284                 if ( write(fd, &last, sizeof(struct lastlog)) 
1285                      != sizeof(struct lastlog) ) {
1286                         log("lastlog_write_filemode: Error writing to %s: %s",
1287                             LASTLOG_FILE, strerror(errno));
1288                         return 0;
1289                 }
1290                 return 1;
1291         } else
1292                 return 0;
1293 }
1294
1295
1296 int
1297 lastlog_write_entry(struct logininfo *li)
1298 {
1299         switch(li->type) {
1300         case LTYPE_LOGIN:
1301                 return lastlog_perform_login(li);
1302         default:
1303                 log("lastlog_write_entry: Invalid type field");
1304                 return 0;
1305         }
1306 }
1307
1308
1309 static void
1310 lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1311 {
1312         line_fullname(li->line, last->ll_line, sizeof(li->line));
1313         strlcpy(li->hostname, last->ll_host, sizeof(li->hostname));
1314         li->tv_sec = last->ll_time;
1315 }
1316
1317
1318 int
1319 lastlog_get_entry(struct logininfo *li)
1320 {
1321         struct lastlog last;
1322         int fd;
1323
1324         if (lastlog_openseek(li, &fd, O_RDONLY)) {
1325                 if ( read(fd, &last, sizeof(struct lastlog)) 
1326                      != sizeof(struct lastlog) ) {
1327                         log("lastlog_write_filemode: Error reading from %s: %s",
1328                             LASTLOG_FILE, strerror(errno));
1329                         return 0;
1330                 } else {
1331                         lastlog_populate_entry(li, &last);
1332                         return 1;
1333                 }
1334         } else
1335                 return 0;    
1336 }
1337
1338
1339 #endif
1340 /* USE_LASTLOG */
This page took 0.145215 seconds and 5 git commands to generate.