]> andersk Git - openssh.git/blame - loginrec.c
- (bal) UseLogin patch for Solaris/UNICOS. Patch by Wayne Davison
[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;
619 break;
620 case LTYPE_LOGOUT:
621 ut->ut_type = DEAD_PROCESS;
622 break;
623 }
a05a70ab 624# endif
5abcdf8e 625 set_utmp_time(li, ut);
1d7b9b20 626
5abcdf8e 627 line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
a05a70ab 628
629# ifdef HAVE_PID_IN_UTMP
1d7b9b20 630 ut->ut_pid = li->pid;
a05a70ab 631# endif
5abcdf8e 632
633 /* If we're logging out, leave all other fields blank */
634 if (li->type == LTYPE_LOGOUT)
635 return;
636
a05a70ab 637 /*
638 * These fields are only used when logging in, and are blank
2b87da3b 639 * for logouts.
a05a70ab 640 */
5abcdf8e 641
642 /* Use strncpy because we don't necessarily want null termination */
dc2a6d09 643 strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
a05a70ab 644# ifdef HAVE_HOST_IN_UTMP
5abcdf8e 645 strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
a05a70ab 646# endif
647# ifdef HAVE_ADDR_IN_UTMP
564dd50a 648 /* this is just a 32-bit IP address */
649 if (li->hostaddr.sa.sa_family == AF_INET)
650 ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
2b87da3b 651# endif
564dd50a 652}
a05a70ab 653#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
564dd50a 654
1d7b9b20 655/**
656 ** utmpx utility functions
564dd50a 657 **
658 ** These functions manipulate struct utmpx, accounting for system
659 ** variations.
1d7b9b20 660 **/
661
662#if defined(USE_UTMPX) || defined (USE_WTMPX)
1d7b9b20 663/* build the utmpx structure */
664void
564dd50a 665set_utmpx_time(struct logininfo *li, struct utmpx *utx)
666{
a05a70ab 667# ifdef HAVE_TV_IN_UTMPX
1d7b9b20 668 utx->ut_tv.tv_sec = li->tv_sec;
669 utx->ut_tv.tv_usec = li->tv_usec;
a05a70ab 670# else /* HAVE_TV_IN_UTMPX */
1d7b9b20 671# ifdef HAVE_TIME_IN_UTMPX
672 utx->ut_time = li->tv_sec;
a05a70ab 673# endif /* HAVE_TIME_IN_UTMPX */
674# endif /* HAVE_TV_IN_UTMPX */
1d7b9b20 675}
676
677void
564dd50a 678construct_utmpx(struct logininfo *li, struct utmpx *utx)
679{
dbaa2e87 680 memset(utx, '\0', sizeof(*utx));
daaff4d5 681# ifdef HAVE_ID_IN_UTMPX
1d7b9b20 682 line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
daaff4d5 683# endif
1d7b9b20 684
685 /* this is done here to keep utmp constants out of loginrec.h */
686 switch (li->type) {
687 case LTYPE_LOGIN:
688 utx->ut_type = USER_PROCESS;
689 break;
690 case LTYPE_LOGOUT:
691 utx->ut_type = DEAD_PROCESS;
692 break;
693 }
1d7b9b20 694 line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
1d7b9b20 695 set_utmpx_time(li, utx);
5abcdf8e 696 utx->ut_pid = li->pid;
697
698 if (li->type == LTYPE_LOGOUT)
699 return;
700
a05a70ab 701 /*
702 * These fields are only used when logging in, and are blank
2b87da3b 703 * for logouts.
a05a70ab 704 */
5abcdf8e 705
706 /* strncpy(): Don't necessarily want null termination */
dc2a6d09 707 strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
a05a70ab 708# ifdef HAVE_HOST_IN_UTMPX
5abcdf8e 709 strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
a05a70ab 710# endif
711# ifdef HAVE_ADDR_IN_UTMPX
764d4113 712 /* this is just a 32-bit IP address */
713 if (li->hostaddr.sa.sa_family == AF_INET)
714 utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
a05a70ab 715# endif
716# ifdef HAVE_SYSLEN_IN_UTMPX
5abcdf8e 717 /* ut_syslen is the length of the utx_host string */
718 utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
a05a70ab 719# endif
564dd50a 720}
a05a70ab 721#endif /* USE_UTMPX || USE_WTMPX */
1d7b9b20 722
723/**
564dd50a 724 ** Low-level utmp functions
1d7b9b20 725 **/
726
727/* FIXME: (ATL) utmp_write_direct needs testing */
1d7b9b20 728#ifdef USE_UTMP
729
1d7b9b20 730/* if we can, use pututline() etc. */
a05a70ab 731# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
732 defined(HAVE_PUTUTLINE)
1d7b9b20 733# define UTMP_USE_LIBRARY
a05a70ab 734# endif
1d7b9b20 735
736
737/* write a utmp entry with the system's help (pututline() and pals) */
a05a70ab 738# ifdef UTMP_USE_LIBRARY
1d7b9b20 739static int
564dd50a 740utmp_write_library(struct logininfo *li, struct utmp *ut)
741{
1d7b9b20 742 setutent();
743 pututline(ut);
744
a05a70ab 745# ifdef HAVE_ENDUTENT
1d7b9b20 746 endutent();
a05a70ab 747# endif
1d7b9b20 748 return 1;
564dd50a 749}
a05a70ab 750# else /* UTMP_USE_LIBRARY */
1d7b9b20 751
752/* write a utmp entry direct to the file */
564dd50a 753/* This is a slightly modification of code in OpenBSD's login.c */
1d7b9b20 754static int
564dd50a 755utmp_write_direct(struct logininfo *li, struct utmp *ut)
756{
1d7b9b20 757 struct utmp old_ut;
758 register int fd;
759 int tty;
760
5abcdf8e 761 /* FIXME: (ATL) ttyslot() needs local implementation */
dbaa2e87 762
698d107e 763#if defined(HAVE_GETTTYENT)
dbaa2e87 764 register struct ttyent *ty;
765
766 tty=0;
767
768 setttyent();
769 while ((struct ttyent *)0 != (ty = getttyent())) {
770 tty++;
771 if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
772 break;
773 }
774 endttyent();
775
776 if((struct ttyent *)0 == ty) {
777 log("utmp_write_entry: tty not found");
778 return(1);
779 }
780#else /* FIXME */
781
1d7b9b20 782 tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
783
698d107e 784#endif /* HAVE_GETTTYENT */
dbaa2e87 785
1d7b9b20 786 if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
787 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
788 /*
789 * Prevent luser from zero'ing out ut_host.
790 * If the new ut_line is empty but the old one is not
dc2a6d09 791 * and ut_line and ut_name match, preserve the old ut_line.
1d7b9b20 792 */
2b87da3b 793 if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
794 (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
795 (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
dc2a6d09 796 (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
1d7b9b20 797 (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
9f32ceb4 798 }
2b87da3b 799
1d7b9b20 800 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
32eec038 801 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut))
1d7b9b20 802 log("utmp_write_direct: error writing %s: %s",
5abcdf8e 803 UTMP_FILE, strerror(errno));
2b87da3b 804
1d7b9b20 805 (void)close(fd);
806 return 1;
9f32ceb4 807 } else {
1d7b9b20 808 return 0;
9f32ceb4 809 }
564dd50a 810}
a05a70ab 811# endif /* UTMP_USE_LIBRARY */
1d7b9b20 812
813static int
564dd50a 814utmp_perform_login(struct logininfo *li)
815{
1d7b9b20 816 struct utmp ut;
817
818 construct_utmp(li, &ut);
a05a70ab 819# ifdef UTMP_USE_LIBRARY
1d7b9b20 820 if (!utmp_write_library(li, &ut)) {
5abcdf8e 821 log("utmp_perform_login: utmp_write_library() failed");
1d7b9b20 822 return 0;
823 }
a05a70ab 824# else
1d7b9b20 825 if (!utmp_write_direct(li, &ut)) {
826 log("utmp_perform_login: utmp_write_direct() failed");
827 return 0;
828 }
a05a70ab 829# endif
1d7b9b20 830 return 1;
564dd50a 831}
1d7b9b20 832
833
834static int
564dd50a 835utmp_perform_logout(struct logininfo *li)
836{
1d7b9b20 837 struct utmp ut;
838
5abcdf8e 839 construct_utmp(li, &ut);
a05a70ab 840# ifdef UTMP_USE_LIBRARY
5abcdf8e 841 if (!utmp_write_library(li, &ut)) {
842 log("utmp_perform_logout: utmp_write_library() failed");
843 return 0;
844 }
a05a70ab 845# else
5abcdf8e 846 if (!utmp_write_direct(li, &ut)) {
847 log("utmp_perform_logout: utmp_write_direct() failed");
848 return 0;
849 }
a05a70ab 850# endif
1d7b9b20 851 return 1;
564dd50a 852}
1d7b9b20 853
854
855int
564dd50a 856utmp_write_entry(struct logininfo *li)
857{
1d7b9b20 858 switch(li->type) {
859 case LTYPE_LOGIN:
860 return utmp_perform_login(li);
861
862 case LTYPE_LOGOUT:
863 return utmp_perform_logout(li);
864
865 default:
866 log("utmp_write_entry: invalid type field");
867 return 0;
868 }
564dd50a 869}
a05a70ab 870#endif /* USE_UTMP */
1d7b9b20 871
872
873/**
564dd50a 874 ** Low-level utmpx functions
1d7b9b20 875 **/
876
877/* not much point if we don't want utmpx entries */
878#ifdef USE_UTMPX
879
1d7b9b20 880/* if we have the wherewithall, use pututxline etc. */
a05a70ab 881# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
882 defined(HAVE_PUTUTXLINE)
1d7b9b20 883# define UTMPX_USE_LIBRARY
a05a70ab 884# endif
1d7b9b20 885
886
887/* write a utmpx entry with the system's help (pututxline() and pals) */
a05a70ab 888# ifdef UTMPX_USE_LIBRARY
1d7b9b20 889static int
564dd50a 890utmpx_write_library(struct logininfo *li, struct utmpx *utx)
891{
1d7b9b20 892 setutxent();
893 pututxline(utx);
894
a05a70ab 895# ifdef HAVE_ENDUTXENT
1d7b9b20 896 endutxent();
a05a70ab 897# endif
1d7b9b20 898 return 1;
564dd50a 899}
1d7b9b20 900
a05a70ab 901# else /* UTMPX_USE_LIBRARY */
1d7b9b20 902
903/* write a utmp entry direct to the file */
904static int
564dd50a 905utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
2b87da3b 906{
1d7b9b20 907 log("utmpx_write_direct: not implemented!");
908 return 0;
564dd50a 909}
a05a70ab 910# endif /* UTMPX_USE_LIBRARY */
1d7b9b20 911
912static int
564dd50a 913utmpx_perform_login(struct logininfo *li)
914{
1d7b9b20 915 struct utmpx utx;
916
917 construct_utmpx(li, &utx);
a05a70ab 918# ifdef UTMPX_USE_LIBRARY
1d7b9b20 919 if (!utmpx_write_library(li, &utx)) {
920 log("utmpx_perform_login: utmp_write_library() failed");
921 return 0;
922 }
a05a70ab 923# else
1d7b9b20 924 if (!utmpx_write_direct(li, &ut)) {
925 log("utmpx_perform_login: utmp_write_direct() failed");
926 return 0;
927 }
a05a70ab 928# endif
1d7b9b20 929 return 1;
564dd50a 930}
1d7b9b20 931
932
933static int
564dd50a 934utmpx_perform_logout(struct logininfo *li)
935{
1d7b9b20 936 struct utmpx utx;
937
938 memset(&utx, '\0', sizeof(utx));
939 set_utmpx_time(li, &utx);
940 line_stripname(utx.ut_line, li->line, sizeof(utx.ut_line));
a05a70ab 941# ifdef HAVE_ID_IN_UTMPX
1d7b9b20 942 line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
a05a70ab 943# endif
944# ifdef HAVE_TYPE_IN_UTMPX
1d7b9b20 945 utx.ut_type = DEAD_PROCESS;
a05a70ab 946# endif
1d7b9b20 947
a05a70ab 948# ifdef UTMPX_USE_LIBRARY
1d7b9b20 949 utmpx_write_library(li, &utx);
a05a70ab 950# else
1d7b9b20 951 utmpx_write_direct(li, &utx);
a05a70ab 952# endif
1d7b9b20 953 return 1;
564dd50a 954}
1d7b9b20 955
1d7b9b20 956int
564dd50a 957utmpx_write_entry(struct logininfo *li)
958{
1d7b9b20 959 switch(li->type) {
960 case LTYPE_LOGIN:
961 return utmpx_perform_login(li);
962 case LTYPE_LOGOUT:
963 return utmpx_perform_logout(li);
964 default:
965 log("utmpx_write_entry: invalid type field");
966 return 0;
967 }
564dd50a 968}
a05a70ab 969#endif /* USE_UTMPX */
1d7b9b20 970
971
972/**
564dd50a 973 ** Low-level wtmp functions
1d7b9b20 974 **/
975
2b87da3b 976#ifdef USE_WTMP
1d7b9b20 977
1d7b9b20 978/* write a wtmp entry direct to the end of the file */
564dd50a 979/* This is a slight modification of code in OpenBSD's logwtmp.c */
1d7b9b20 980static int
564dd50a 981wtmp_write(struct logininfo *li, struct utmp *ut)
982{
1d7b9b20 983 struct stat buf;
984 int fd, ret = 1;
985
986 if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
987 log("wtmp_write: problem writing %s: %s",
988 WTMP_FILE, strerror(errno));
989 return 0;
990 }
2b87da3b 991 if (fstat(fd, &buf) == 0)
9f32ceb4 992 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
1d7b9b20 993 ftruncate(fd, buf.st_size);
994 log("wtmp_write: problem writing %s: %s",
995 WTMP_FILE, strerror(errno));
996 ret = 0;
997 }
998 (void)close(fd);
1d7b9b20 999 return ret;
564dd50a 1000}
1d7b9b20 1001
1d7b9b20 1002static int
a05a70ab 1003wtmp_perform_login(struct logininfo *li)
1004{
1d7b9b20 1005 struct utmp ut;
1006
1007 construct_utmp(li, &ut);
1008 return wtmp_write(li, &ut);
564dd50a 1009}
1d7b9b20 1010
1011
1012static int
564dd50a 1013wtmp_perform_logout(struct logininfo *li)
1014{
1d7b9b20 1015 struct utmp ut;
1016
1017 construct_utmp(li, &ut);
1d7b9b20 1018 return wtmp_write(li, &ut);
564dd50a 1019}
1d7b9b20 1020
1021
1022int
564dd50a 1023wtmp_write_entry(struct logininfo *li)
1024{
1d7b9b20 1025 switch(li->type) {
1026 case LTYPE_LOGIN:
1027 return wtmp_perform_login(li);
1028 case LTYPE_LOGOUT:
1029 return wtmp_perform_logout(li);
1030 default:
1031 log("wtmp_write_entry: invalid type field");
1032 return 0;
1033 }
564dd50a 1034}
1d7b9b20 1035
1036
5abcdf8e 1037/* Notes on fetching login data from wtmp/wtmpx
2b87da3b 1038 *
5abcdf8e 1039 * Logouts are usually recorded with (amongst other things) a blank
1040 * username on a given tty line. However, some systems (HP-UX is one)
1041 * leave all fields set, but change the ut_type field to DEAD_PROCESS.
1042 *
1043 * Since we're only looking for logins here, we know that the username
1044 * must be set correctly. On systems that leave it in, we check for
1045 * ut_type==USER_PROCESS (indicating a login.)
1046 *
1047 * Portability: Some systems may set something other than USER_PROCESS
1048 * to indicate a login process. I don't know of any as I write. Also,
1049 * it's possible that some systems may both leave the username in
1050 * place and not have ut_type.
1051 */
1052
5abcdf8e 1053/* return true if this wtmp entry indicates a login */
1054static int
1055wtmp_islogin(struct logininfo *li, struct utmp *ut)
1056{
2b87da3b 1057 if (strncmp(li->username, ut->ut_name,
dc2a6d09 1058 MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
a05a70ab 1059# ifdef HAVE_TYPE_IN_UTMP
5abcdf8e 1060 if (ut->ut_type & USER_PROCESS)
1061 return 1;
a05a70ab 1062# else
5abcdf8e 1063 return 1;
a05a70ab 1064# endif
5abcdf8e 1065 }
1066 return 0;
1067}
1068
1d7b9b20 1069int
564dd50a 1070wtmp_get_entry(struct logininfo *li)
1071{
1d7b9b20 1072 struct stat st;
1073 struct utmp ut;
5abcdf8e 1074 int fd, found=0;
1075
1076 /* Clear the time entries in our logininfo */
1077 li->tv_sec = li->tv_usec = 0;
1d7b9b20 1078
1079 if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1080 log("wtmp_get_entry: problem opening %s: %s",
1081 WTMP_FILE, strerror(errno));
1082 return 0;
1083 }
2b87da3b 1084 if (fstat(fd, &st) != 0) {
1d7b9b20 1085 log("wtmp_get_entry: couldn't stat %s: %s",
1086 WTMP_FILE, strerror(errno));
1087 close(fd);
1088 return 0;
1089 }
1d7b9b20 1090
5abcdf8e 1091 /* Seek to the start of the last struct utmp */
dbaa2e87 1092 if (lseek(fd, (off_t)(0 - sizeof(struct utmp)), SEEK_END) == -1) {
5abcdf8e 1093 /* Looks like we've got a fresh wtmp file */
1094 close(fd);
1095 return 0;
1096 }
1097
1098 while (!found) {
9f32ceb4 1099 if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
1d7b9b20 1100 log("wtmp_get_entry: read of %s failed: %s",
1101 WTMP_FILE, strerror(errno));
1102 close (fd);
1103 return 0;
1104 }
5abcdf8e 1105 if ( wtmp_islogin(li, &ut) ) {
1106 found = 1;
1107 /* We've already checked for a time in struct
1108 * utmp, in login_getlast(). */
a05a70ab 1109# ifdef HAVE_TIME_IN_UTMP
1d7b9b20 1110 li->tv_sec = ut.ut_time;
a05a70ab 1111# else
1d7b9b20 1112# if HAVE_TV_IN_UTMP
1113 li->tv_sec = ut.ut_tv.tv_sec;
1114# endif
a05a70ab 1115# endif
5abcdf8e 1116 line_fullname(li->line, ut.ut_line,
1117 MIN_SIZEOF(li->line, ut.ut_line));
a05a70ab 1118# ifdef HAVE_HOST_IN_UTMP
5abcdf8e 1119 strlcpy(li->hostname, ut.ut_host,
1120 MIN_SIZEOF(li->hostname, ut.ut_host));
a05a70ab 1121# endif
5abcdf8e 1122 continue;
1d7b9b20 1123 }
5abcdf8e 1124 /* Seek back 2 x struct utmp */
1d7b9b20 1125 if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) {
5abcdf8e 1126 /* We've found the start of the file, so quit */
1d7b9b20 1127 close (fd);
1128 return 0;
1129 }
5abcdf8e 1130 }
1131
1132 /* We found an entry. Tidy up and return */
1133 close(fd);
1d7b9b20 1134 return 1;
564dd50a 1135}
a05a70ab 1136# endif /* USE_WTMP */
1d7b9b20 1137
1138
1139/**
564dd50a 1140 ** Low-level wtmpx functions
1d7b9b20 1141 **/
1142
1143#ifdef USE_WTMPX
1d7b9b20 1144/* write a wtmpx entry direct to the end of the file */
564dd50a 1145/* This is a slight modification of code in OpenBSD's logwtmp.c */
1d7b9b20 1146static int
564dd50a 1147wtmpx_write(struct logininfo *li, struct utmpx *utx)
1148{
1d7b9b20 1149 struct stat buf;
1150 int fd, ret = 1;
1151
1152 if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1153 log("wtmpx_write: problem opening %s: %s",
1154 WTMPX_FILE, strerror(errno));
1155 return 0;
1156 }
1157
2b87da3b 1158 if (fstat(fd, &buf) == 0)
9f32ceb4 1159 if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
1d7b9b20 1160 ftruncate(fd, buf.st_size);
1161 log("wtmpx_write: problem writing %s: %s",
1162 WTMPX_FILE, strerror(errno));
1163 ret = 0;
1164 }
1165 (void)close(fd);
1166
1167 return ret;
564dd50a 1168}
1d7b9b20 1169
1170
1171static int
564dd50a 1172wtmpx_perform_login(struct logininfo *li)
1173{
1d7b9b20 1174 struct utmpx utx;
1175
1176 construct_utmpx(li, &utx);
1177 return wtmpx_write(li, &utx);
564dd50a 1178}
1d7b9b20 1179
1180
1181static int
564dd50a 1182wtmpx_perform_logout(struct logininfo *li)
1183{
1d7b9b20 1184 struct utmpx utx;
1185
1186 construct_utmpx(li, &utx);
1d7b9b20 1187 return wtmpx_write(li, &utx);
564dd50a 1188}
1d7b9b20 1189
1190
1191int
564dd50a 1192wtmpx_write_entry(struct logininfo *li)
1193{
1d7b9b20 1194 switch(li->type) {
1195 case LTYPE_LOGIN:
1196 return wtmpx_perform_login(li);
1197 case LTYPE_LOGOUT:
1198 return wtmpx_perform_logout(li);
1199 default:
1200 log("wtmpx_write_entry: invalid type field");
1201 return 0;
1202 }
564dd50a 1203}
1d7b9b20 1204
5abcdf8e 1205/* Please see the notes above wtmp_islogin() for information about the
1206 next two functions */
1207
1208/* Return true if this wtmpx entry indicates a login */
1209static int
1210wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1211{
dc2a6d09 1212 if ( strncmp(li->username, utx->ut_name,
1213 MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
a05a70ab 1214# ifdef HAVE_TYPE_IN_UTMPX
5abcdf8e 1215 if (utx->ut_type == USER_PROCESS)
1216 return 1;
a05a70ab 1217# else
5abcdf8e 1218 return 1;
a05a70ab 1219# endif
5abcdf8e 1220 }
1221 return 0;
1222}
1223
1d7b9b20 1224
1225int
564dd50a 1226wtmpx_get_entry(struct logininfo *li)
1227{
1d7b9b20 1228 struct stat st;
1229 struct utmpx utx;
5abcdf8e 1230 int fd, found=0;
1231
1232 /* Clear the time entries */
1233 li->tv_sec = li->tv_usec = 0;
1d7b9b20 1234
1235 if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1236 log("wtmpx_get_entry: problem opening %s: %s",
1237 WTMPX_FILE, strerror(errno));
1238 return 0;
1239 }
2b87da3b 1240 if (fstat(fd, &st) != 0) {
1d7b9b20 1241 log("wtmpx_get_entry: couldn't stat %s: %s",
1242 WTMP_FILE, strerror(errno));
1243 close(fd);
1244 return 0;
1245 }
2b87da3b 1246
5abcdf8e 1247 /* Seek to the start of the last struct utmpx */
1248 if (lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END) == -1 ) {
1249 /* probably a newly rotated wtmpx file */
1250 close(fd);
1251 return 0;
1252 }
1d7b9b20 1253
5abcdf8e 1254 while (!found) {
9f32ceb4 1255 if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
1d7b9b20 1256 log("wtmpx_get_entry: read of %s failed: %s",
1257 WTMPX_FILE, strerror(errno));
1258 close (fd);
1259 return 0;
1260 }
1d7b9b20 1261 /* Logouts are recorded as a blank username on a particular line.
1262 * So, we just need to find the username in struct utmpx */
5abcdf8e 1263 if ( wtmpx_islogin(li, &utx) ) {
a05a70ab 1264# ifdef HAVE_TV_IN_UTMPX
1d7b9b20 1265 li->tv_sec = utx.ut_tv.tv_sec;
a05a70ab 1266# else
1d7b9b20 1267# ifdef HAVE_TIME_IN_UTMPX
1268 li->tv_sec = utx.ut_time;
1269# endif
a05a70ab 1270# endif
a4d05724 1271 line_fullname(li->line, utx.ut_line, sizeof(li->line));
a05a70ab 1272# ifdef HAVE_HOST_IN_UTMPX
5abcdf8e 1273 strlcpy(li->hostname, utx.ut_host,
1274 MIN_SIZEOF(li->hostname, utx.ut_host));
a05a70ab 1275# endif
5abcdf8e 1276 continue;
1d7b9b20 1277 }
1278 if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) {
1279 close (fd);
1280 return 0;
1281 }
5abcdf8e 1282 }
1283
1284 close(fd);
1d7b9b20 1285 return 1;
564dd50a 1286}
f988dce5 1287#endif /* USE_WTMPX */
1d7b9b20 1288
1d7b9b20 1289/**
564dd50a 1290 ** Low-level libutil login() functions
1d7b9b20 1291 **/
1292
1293#ifdef USE_LOGIN
1d7b9b20 1294static int
564dd50a 1295syslogin_perform_login(struct logininfo *li)
1296{
1d7b9b20 1297 struct utmp *ut;
1298
dbaa2e87 1299 if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
1d7b9b20 1300 log("syslogin_perform_login: couldn't malloc()");
1301 return 0;
1302 }
1303 construct_utmp(li, ut);
1304 login(ut);
1305
1306 return 1;
564dd50a 1307}
1d7b9b20 1308
564dd50a 1309static int
1310syslogin_perform_logout(struct logininfo *li)
1311{
a05a70ab 1312# ifdef HAVE_LOGOUT
1d7b9b20 1313 char line[8];
2b87da3b 1314
1d7b9b20 1315 (void)line_stripname(line, li->line, sizeof(line));
1316
1317 if (!logout(line)) {
1318 log("syslogin_perform_logout: logout() returned an error");
a05a70ab 1319# ifdef HAVE_LOGWTMP
1d7b9b20 1320 } else {
1321 logwtmp(line, "", "");
a05a70ab 1322# endif
5637650d 1323 }
5abcdf8e 1324 /* FIXME: (ATL - if the need arises) What to do if we have
1325 * login, but no logout? what if logout but no logwtmp? All
1326 * routines are in libutil so they should all be there,
1327 * but... */
a05a70ab 1328# endif
1d7b9b20 1329 return 1;
564dd50a 1330}
1d7b9b20 1331
1d7b9b20 1332int
564dd50a 1333syslogin_write_entry(struct logininfo *li)
1334{
1d7b9b20 1335 switch (li->type) {
1336 case LTYPE_LOGIN:
1337 return syslogin_perform_login(li);
1338 case LTYPE_LOGOUT:
1339 return syslogin_perform_logout(li);
1340 default:
1341 log("syslogin_write_entry: Invalid type field");
1342 return 0;
1343 }
564dd50a 1344}
f988dce5 1345#endif /* USE_LOGIN */
1d7b9b20 1346
1347/* end of file log-syslogin.c */
1348
1d7b9b20 1349/**
564dd50a 1350 ** Low-level lastlog functions
1d7b9b20 1351 **/
1352
1353#ifdef USE_LASTLOG
a05a70ab 1354#define LL_FILE 1
1355#define LL_DIR 2
1356#define LL_OTHER 3
1d7b9b20 1357
1d7b9b20 1358static void
564dd50a 1359lastlog_construct(struct logininfo *li, struct lastlog *last)
1360{
1d7b9b20 1361 /* clear the structure */
dbaa2e87 1362 memset(last, '\0', sizeof(*last));
2b87da3b 1363
a05a70ab 1364 (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
5abcdf8e 1365 strlcpy(last->ll_host, li->hostname,
1366 MIN_SIZEOF(last->ll_host, li->hostname));
1d7b9b20 1367 last->ll_time = li->tv_sec;
564dd50a 1368}
1d7b9b20 1369
1d7b9b20 1370static int
564dd50a 1371lastlog_filetype(char *filename)
1372{
1d7b9b20 1373 struct stat st;
1374
a05a70ab 1375 if (stat(LASTLOG_FILE, &st) != 0) {
2b87da3b 1376 log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
a05a70ab 1377 strerror(errno));
1d7b9b20 1378 return 0;
1379 }
1d7b9b20 1380 if (S_ISDIR(st.st_mode))
1381 return LL_DIR;
1382 else if (S_ISREG(st.st_mode))
1383 return LL_FILE;
1384 else
1385 return LL_OTHER;
564dd50a 1386}
1d7b9b20 1387
1388
1389/* open the file (using filemode) and seek to the login entry */
1390static int
564dd50a 1391lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1392{
1d7b9b20 1393 off_t offset;
1394 int type;
1395 char lastlog_file[1024];
1396
1397 type = lastlog_filetype(LASTLOG_FILE);
1398 switch (type) {
d8caae24 1399 case LL_FILE:
1400 strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1401 break;
1402 case LL_DIR:
1403 snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1404 LASTLOG_FILE, li->username);
1405 break;
1406 default:
1407 log("lastlog_openseek: %.100s is not a file or directory!",
1408 LASTLOG_FILE);
1409 return 0;
a05a70ab 1410 }
1d7b9b20 1411
1412 *fd = open(lastlog_file, filemode);
1413 if ( *fd < 0) {
9f32ceb4 1414 debug("lastlog_openseek: Couldn't open %s: %s",
1d7b9b20 1415 lastlog_file, strerror(errno));
1416 return 0;
1417 }
2b87da3b 1418
d93a7e5a 1419 if (type == LL_FILE) {
1420 /* find this uid's offset in the lastlog file */
1421 offset = (off_t) ( (long)li->uid * sizeof(struct lastlog));
1d7b9b20 1422
d93a7e5a 1423 if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1424 log("lastlog_openseek: %s->lseek(): %s",
2b87da3b 1425 lastlog_file, strerror(errno));
d93a7e5a 1426 return 0;
1427 }
1d7b9b20 1428 }
2b87da3b 1429
1d7b9b20 1430 return 1;
564dd50a 1431}
1d7b9b20 1432
1433static int
564dd50a 1434lastlog_perform_login(struct logininfo *li)
1435{
1d7b9b20 1436 struct lastlog last;
1437 int fd;
1438
1439 /* create our struct lastlog */
1440 lastlog_construct(li, &last);
1441
1a022229 1442 if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
1443 return(0);
2b87da3b 1444
1d7b9b20 1445 /* write the entry */
1a022229 1446 if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) {
1447 close(fd);
1448 log("lastlog_write_filemode: Error writing to %s: %s",
1449 LASTLOG_FILE, strerror(errno));
1d7b9b20 1450 return 0;
a05a70ab 1451 }
1a022229 1452
1453 close(fd);
1454 return 1;
564dd50a 1455}
1d7b9b20 1456
1d7b9b20 1457int
564dd50a 1458lastlog_write_entry(struct logininfo *li)
1459{
1d7b9b20 1460 switch(li->type) {
1461 case LTYPE_LOGIN:
1462 return lastlog_perform_login(li);
1463 default:
1464 log("lastlog_write_entry: Invalid type field");
1465 return 0;
1466 }
564dd50a 1467}
1d7b9b20 1468
1d7b9b20 1469static void
564dd50a 1470lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1471{
1d7b9b20 1472 line_fullname(li->line, last->ll_line, sizeof(li->line));
2b87da3b 1473 strlcpy(li->hostname, last->ll_host,
5abcdf8e 1474 MIN_SIZEOF(li->hostname, last->ll_host));
1d7b9b20 1475 li->tv_sec = last->ll_time;
564dd50a 1476}
1d7b9b20 1477
1d7b9b20 1478int
564dd50a 1479lastlog_get_entry(struct logininfo *li)
1480{
1d7b9b20 1481 struct lastlog last;
1482 int fd;
1483
1484 if (lastlog_openseek(li, &fd, O_RDONLY)) {
9f32ceb4 1485 if (atomicio(read, fd, &last, sizeof(last)) != sizeof(last)) {
1486 log("lastlog_get_entry: Error reading from %s: %s",
1d7b9b20 1487 LASTLOG_FILE, strerror(errno));
1488 return 0;
1489 } else {
1490 lastlog_populate_entry(li, &last);
1491 return 1;
1492 }
a05a70ab 1493 } else {
2b87da3b 1494 return 0;
a05a70ab 1495 }
564dd50a 1496}
f988dce5 1497#endif /* USE_LASTLOG */
This page took 0.559679 seconds and 5 git commands to generate.