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