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