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