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