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