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