]> andersk Git - openssh.git/blame - loginrec.c
Disable lastlog for AIX, as it's handled by the OS as part of the login
[openssh.git] / loginrec.c
CommitLineData
1d7b9b20 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
a3cef3ca 50 ** AIX
1d7b9b20 51 **
52 ** Platforms with known problems:
1d7b9b20 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
81RCSID("$Id$");
82
83
84/**
85 ** prototypes for helper functions in this file
86 **/
87
88#if HAVE_UTMP_H
89# include <utmp.h>
90void set_utmp_time(struct logininfo *li, struct utmp *ut);
91void construct_utmp(struct logininfo *li, struct utmp *ut);
92#endif
93
94#ifdef HAVE_UTMPX_H
95# include <utmpx.h>
96void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
97void construct_utmpx(struct logininfo *li, struct utmpx *ut);
98#endif
99
100int utmp_write_entry(struct logininfo *li);
101int utmpx_write_entry(struct logininfo *li);
102int wtmp_write_entry(struct logininfo *li);
103int wtmpx_write_entry(struct logininfo *li);
104int lastlog_write_entry(struct logininfo *li);
105int syslogin_write_entry(struct logininfo *li);
106
107int getlast_entry(struct logininfo *li);
108int lastlog_get_entry(struct logininfo *li);
109int wtmp_get_entry(struct logininfo *li);
110int 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 */
118struct 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) */
133void 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 */
141int 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
162void
163login_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
171void
172login_set_type(struct logininfo *li, int type) {
173 li->type = type;
174}
175
176void
177login_set_pid(struct logininfo *li, int pid) {
178 if (!pid)
179 li->pid = (int)getpid();
180 else
181 li->pid = pid;
182}
183
184void
185login_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
194void
195login_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
203void
204login_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
219void
220login_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
227void
228login_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 */
235void
236login_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
243void
244login_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
257void
258login_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
265void
266login_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
277int
278login_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
309int
310login_login (struct logininfo *li) {
311 li->type = LTYPE_LOGIN;
312 return login_write(li);
313}
314
315int
316login_logout(struct logininfo *li) {
317 li->type = LTYPE_LOGOUT;
318 return login_write(li);
319}
320
321int
322login_log_entry(struct logininfo *li) {
323 return login_write(li);
324}
325
326
327unsigned int
328login_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
340unsigned int
341login_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
353struct logininfo *
354login_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
363struct logininfo *
364login_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) */
398char *
399line_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 */
411char *
412line_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 */
427char *
428line_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 */
452void
453set_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
464void
465construct_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 */
519void
520set_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
531void
532construct_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
588static int
589utmp_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. */
605static int
606utmp_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
641static int
642utmp_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
662static int
663utmp_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
687int
688utmp_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
726static int
727utmpx_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 */
743static int
744utmpx_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
753static int
754utmpx_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
774static int
775utmpx_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
798int
799utmpx_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 */
828static int
829wtmp_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
854static int
855wtmp_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
863static int
864wtmp_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
883int
884wtmp_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
899int
900wtmp_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 */
969static int
970wtmpx_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
995static int
996wtmpx_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
1004static int
1005wtmpx_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
1025int
1026wtmpx_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
1041int
1042wtmpx_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
1119static int
1120syslogin_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
1133static int
1134syslogin_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
1157int
1158syslogin_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
1193static void
1194lastlog_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
1210static int
1211lastlog_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 */
1230static int
1231lastlog_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
1270static int
1271lastlog_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
1292int
1293lastlog_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
1306static void
1307lastlog_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
1316int
1317lastlog_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 */
1346int
1347getlast_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.719627 seconds and 5 git commands to generate.