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