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