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