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