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