blob: e51d686bf1d80d40fed1de5ad23d197cdd3a8ae7 [file] [log] [blame]
andre2ff7b5d2000-06-03 14:57:40 +00001/*
2 * Copyright (c) 2000 Andre Lucas. All rights reserved.
andre61e67252000-06-04 17:07:49 +00003 * Portions copyright (c) 1998 Todd C. Miller
4 * Portions copyright (c) 1996 Jason Downs
5 * Portions copyright (c) 1996 Theo de Raadt
andre2ff7b5d2000-06-03 14:57:40 +00006 *
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
andre61e67252000-06-04 17:07:49 +000037/*
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
andre2ff7b5d2000-06-03 14:57:40 +0000133/**
134 ** TODO:
andre61e67252000-06-04 17:07:49 +0000135 ** homegrown ttyslot()q
136 ** test, test, test
andre2ff7b5d2000-06-03 14:57:40 +0000137 **
138 ** Platform status:
139 ** ----------------
140 **
141 ** Known good:
142 ** Linux (Redhat 6.2, need more variants)
143 ** HP-UX 10.20 (gcc only)
andre6bb92372000-06-19 08:20:03 +0000144 ** IRIX
andre2ff7b5d2000-06-03 14:57:40 +0000145 **
146 ** Testing required: Please send reports!
147 ** Solaris
andre2ff7b5d2000-06-03 14:57:40 +0000148 ** NetBSD
149 ** HP-UX 11
andre60f3c982000-06-03 16:18:19 +0000150 ** AIX
andre2ff7b5d2000-06-03 14:57:40 +0000151 **
152 ** Platforms with known problems:
andre2ff7b5d2000-06-03 14:57:40 +0000153 ** NeXT
154 **
155 **/
156
157#include "includes.h"
158
andre61e67252000-06-04 17:07:49 +0000159#if HAVE_UTMP_H
160# include <utmp.h>
161#endif
162#if HAVE_UTMPX_H
163# include <utmpx.h>
164#endif
165#if HAVE_LASTLOG_H
166# include <lastlog.h>
167#endif
andre2ff7b5d2000-06-03 14:57:40 +0000168
169#include "ssh.h"
170#include "xmalloc.h"
171#include "loginrec.h"
172
andre6bb92372000-06-19 08:20:03 +0000173RCSID("$Id: loginrec.c,v 1.7 2000/06/19 08:20:03 andre Exp $");
andre2ff7b5d2000-06-03 14:57:40 +0000174
175/**
176 ** prototypes for helper functions in this file
177 **/
178
179#if HAVE_UTMP_H
andre2ff7b5d2000-06-03 14:57:40 +0000180void set_utmp_time(struct logininfo *li, struct utmp *ut);
181void construct_utmp(struct logininfo *li, struct utmp *ut);
182#endif
183
184#ifdef HAVE_UTMPX_H
andre2ff7b5d2000-06-03 14:57:40 +0000185void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
186void construct_utmpx(struct logininfo *li, struct utmpx *ut);
187#endif
188
189int utmp_write_entry(struct logininfo *li);
190int utmpx_write_entry(struct logininfo *li);
191int wtmp_write_entry(struct logininfo *li);
192int wtmpx_write_entry(struct logininfo *li);
193int lastlog_write_entry(struct logininfo *li);
194int syslogin_write_entry(struct logininfo *li);
195
196int getlast_entry(struct logininfo *li);
197int lastlog_get_entry(struct logininfo *li);
198int wtmp_get_entry(struct logininfo *li);
199int wtmpx_get_entry(struct logininfo *li);
200
201
andre6bb92372000-06-19 08:20:03 +0000202#ifdef MIN
203# undef MIN
204# define MIN(a,b) ( (a)<(b) ? (a) : (b) )
205#endif
206
207/* pick the shortest string */
208#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
209
210
andre2ff7b5d2000-06-03 14:57:40 +0000211/**
212 ** platform-independent login functions
213 **/
214
andre6bb92372000-06-19 08:20:03 +0000215/* login_login(struct logininfo *) -Record a login
216 *
217 * Call with a pointer to a struct logininfo initialised with
218 * login_init_entry() or login_alloc_entry()
219 *
220 * Returns:
221 * >0 if successful
222 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
223 */
andre61e67252000-06-04 17:07:49 +0000224int
225login_login (struct logininfo *li)
226{
227 li->type = LTYPE_LOGIN;
228 return login_write(li);
229}
230
231
andre6bb92372000-06-19 08:20:03 +0000232/* login_logout(struct logininfo *) - Record a logout
233 *
234 * Call as with login_login()
235 *
236 * Returns:
237 * >0 if successful
238 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
239 */
andre61e67252000-06-04 17:07:49 +0000240int
241login_logout(struct logininfo *li)
242{
243 li->type = LTYPE_LOGOUT;
244 return login_write(li);
245}
246
247
andre6bb92372000-06-19 08:20:03 +0000248/* login_get_lastlog_time(int) - Retrieve the last login time
249 *
250 * Retrieve the last login time for the given uid. Will try to use the
251 * system lastlog facilities if they are available, but will fall back
252 * to looking in wtmp/wtmpx if necessary
253 *
254 * Returns:
255 * 0 on failure, or if user has never logged in
256 * Time in seconds from the epoch if successful
257 *
258 * Useful preprocessor symbols:
259 * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
260 * info
261 * USE_LASTLOG: If set, indicates the presence of system lastlog
262 * facilities. If this and DISABLE_LASTLOG are not set,
263 * try to retrieve lastlog information from wtmp/wtmpx.
264 */
andre61e67252000-06-04 17:07:49 +0000265unsigned int
266login_get_lastlog_time(const int uid)
267{
268 struct logininfo li;
269
andre6bb92372000-06-19 08:20:03 +0000270 if (login_get_lastlog(&li, uid))
271 return li.tv_sec;
272 else
273 return 0;
andre61e67252000-06-04 17:07:49 +0000274}
275
andre6bb92372000-06-19 08:20:03 +0000276/* login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry
277 *
278 * Retrieve a logininfo structure populated (only partially) with
279 * information from the system lastlog data, or from wtmp/wtmpx if no
280 * system lastlog information exists.
281 *
282 * Note this routine must be given a pre-allocated logininfo.
283 *
284 * Returns:
285 * >0: A pointer to your struct logininfo if successful
286 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
287 *
288 */
andre61e67252000-06-04 17:07:49 +0000289struct logininfo *
290login_get_lastlog(struct logininfo *li, const int uid)
291{
andre6bb92372000-06-19 08:20:03 +0000292#ifndef USE_LASTLOG
293 struct passwd *pw;
294#endif
295
andre61e67252000-06-04 17:07:49 +0000296 memset(li, '\0', sizeof(struct logininfo));
297 li->uid = uid;
andre6bb92372000-06-19 08:20:03 +0000298
299#ifndef USE_LASTLOG
300 /* If we don't have a 'real' lastlog, we need the username to
301 * reliably search wtmp(x) for the last login (see
302 * wtmp_get_entry().) */
303 pw = getpwuid(uid);
304 strlcpy(li->username, pw->pw_name,
305 MIN_SIZEOF(li->username, pw->pw_name));
306#endif
andre61e67252000-06-04 17:07:49 +0000307 if (getlast_entry(li))
308 return li;
309 else
310 return 0;
311}
312
313
andre6bb92372000-06-19 08:20:03 +0000314/* login_alloc_entry(int, char*, char*, char*) - Allocate and initialise
315 * a logininfo structure
316 *
317 * This function creates a new struct logininfo, a data structure
318 * meant to carry the information required to portably record login info.
319 *
320 * Returns a pointer to a newly created struct logininfo. If memory
321 * allocation fails, the program halts.
322 */
andre61e67252000-06-04 17:07:49 +0000323struct
324logininfo *login_alloc_entry(int pid, const char *username,
325 const char *hostname, const char *line)
326{
andre2ff7b5d2000-06-03 14:57:40 +0000327 struct logininfo *newli;
328
329 newli = (struct logininfo *) xmalloc (sizeof(struct logininfo));
andre61e67252000-06-04 17:07:49 +0000330 (void)login_init_entry(newli, pid, username, hostname, line);
331 return newli;
332}
andre2ff7b5d2000-06-03 14:57:40 +0000333
334
andre6bb92372000-06-19 08:20:03 +0000335/* login_free_entry(struct logininfo *) - free struct memory */
andre61e67252000-06-04 17:07:49 +0000336void
337login_free_entry(struct logininfo *li)
338{
339 xfree(li);
340}
341
andre2ff7b5d2000-06-03 14:57:40 +0000342
andre6bb92372000-06-19 08:20:03 +0000343/* login_init_entry(struct logininfo *, int, char*, char*, char*)
344 * - initialise a struct logininfo
345 *
346 * Populates a new struct logininfo, a data structure meant to carry
347 * the information required to portably record login info.
348 *
349 * Returns: 1
350 */
andre61e67252000-06-04 17:07:49 +0000351int
352login_init_entry(struct logininfo *li, int pid, const char *username,
353 const char *hostname, const char *line)
354{
andre2ff7b5d2000-06-03 14:57:40 +0000355 /* zero the structure */
356 memset(li, 0, sizeof(struct logininfo));
357
andre61e67252000-06-04 17:07:49 +0000358 li->pid = pid;
andre2ff7b5d2000-06-03 14:57:40 +0000359 /* set the line information */
andre61e67252000-06-04 17:07:49 +0000360 if (line)
andre2ff7b5d2000-06-03 14:57:40 +0000361 line_fullname(li->line, line, sizeof(li->line));
andre2ff7b5d2000-06-03 14:57:40 +0000362
andre61e67252000-06-04 17:07:49 +0000363 if (username)
andre2ff7b5d2000-06-03 14:57:40 +0000364 strlcpy(li->username, username, sizeof(li->username));
andre61e67252000-06-04 17:07:49 +0000365 if (hostname)
andre2ff7b5d2000-06-03 14:57:40 +0000366 strlcpy(li->hostname, hostname, sizeof(li->hostname));
andre61e67252000-06-04 17:07:49 +0000367 return 1;
andre2ff7b5d2000-06-03 14:57:40 +0000368}
369
andre6bb92372000-06-19 08:20:03 +0000370/* login_set_current_time(struct logininfo *) - set the current time
371 *
372 * Set the current time in a logininfo structure. This function is
373 * meant to eliminate the need to deal with system dependencies for
374 * time handling.
375 */
andre2ff7b5d2000-06-03 14:57:40 +0000376void
andre61e67252000-06-04 17:07:49 +0000377login_set_current_time(struct logininfo *li)
378{
andre2ff7b5d2000-06-03 14:57:40 +0000379#ifdef HAVE_SYS_TIME_H
380 struct timeval tv;
381
382 gettimeofday(&tv, NULL);
383 li->tv_sec = tv.tv_sec ; li->tv_usec = tv.tv_usec;
384#else
andre61e67252000-06-04 17:07:49 +0000385 time_t tm = time(0);
386
387 li->tv_sec = tm; li->tv_usec = 0;
andre2ff7b5d2000-06-03 14:57:40 +0000388#endif
389}
390
andre61e67252000-06-04 17:07:49 +0000391
392/* copy a sockaddr_* into our logininfo */
andre2ff7b5d2000-06-03 14:57:40 +0000393void
andre61e67252000-06-04 17:07:49 +0000394login_set_addr(struct logininfo *li, const struct sockaddr *sa,
395 const unsigned int sa_size)
396{
397 unsigned int bufsize = sa_size;
398
399 /* make sure we don't overrun our union */
400 if (sizeof(li->hostaddr) < sa_size)
401 bufsize = sizeof(li->hostaddr);
402
403 memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
andre2ff7b5d2000-06-03 14:57:40 +0000404}
405
andre2ff7b5d2000-06-03 14:57:40 +0000406
andre61e67252000-06-04 17:07:49 +0000407/**
408 ** login_write: Call low-level recording functions based on autoconf
409 ** results
410 **/
andre2ff7b5d2000-06-03 14:57:40 +0000411
412int
andre61e67252000-06-04 17:07:49 +0000413login_write (struct logininfo *li)
414{
andre2ff7b5d2000-06-03 14:57:40 +0000415 if ((int)geteuid() != 0) {
416 log("Attempt to write login records by non-root user (aborting)");
417 return 1;
418 }
419 /* set the timestamp */
420 login_set_current_time(li);
421#ifdef USE_LOGIN
422 syslogin_write_entry(li);
423#endif
424#ifdef USE_LASTLOG
425 if (li->type == LTYPE_LOGIN) {
426 lastlog_write_entry(li);
427 }
428#endif
429#ifdef USE_UTMP
430 utmp_write_entry(li);
431#endif
432#ifdef USE_WTMP
433 wtmp_write_entry(li);
434#endif
435#ifdef USE_UTMPX
436 utmpx_write_entry(li);
437#endif
438#ifdef USE_WTMPX
439 wtmpx_write_entry(li);
440#endif
441 return 0;
442}
443
andre2ff7b5d2000-06-03 14:57:40 +0000444
445/**
andre61e67252000-06-04 17:07:49 +0000446 ** getlast_entry: Call low-level functions to retrieve the last login
447 ** time.
andre2ff7b5d2000-06-03 14:57:40 +0000448 **/
449
andre61e67252000-06-04 17:07:49 +0000450/* take the uid in li and return the last login time */
451int
452getlast_entry(struct logininfo *li)
453{
454#ifdef USE_LASTLOG
455 if (lastlog_get_entry(li))
456 return 1;
457 else
458 return 0;
459#else
460 /* !USE_LASTLOG */
461
andreecaabf12000-06-12 22:21:44 +0000462# ifdef DISABLE_LASTLOG
463 /* On some systems we shouldn't even try to obtain last login
464 * time, e.g. AIX */
465 return 0;
466
467# else
andre61e67252000-06-04 17:07:49 +0000468 /* Try to retrieve the last login time from wtmp */
andreecaabf12000-06-12 22:21:44 +0000469# if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
andre61e67252000-06-04 17:07:49 +0000470 /* retrieve last login time from utmp */
471 if (wtmp_get_entry(li))
472 return 1;
473 else
474 return 0;
andreecaabf12000-06-12 22:21:44 +0000475# else
andre61e67252000-06-04 17:07:49 +0000476
477 /* If wtmp isn't available, try wtmpx */
478
andreecaabf12000-06-12 22:21:44 +0000479# if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
andre61e67252000-06-04 17:07:49 +0000480 /* retrieve last login time from utmpx */
481 if (wtmpx_get_entry(li))
482 return 1;
483 else
484 return 0;
andreecaabf12000-06-12 22:21:44 +0000485# else
andre61e67252000-06-04 17:07:49 +0000486
487 /* Give up: No means of retrieving last login time */
488 return 0;
andreecaabf12000-06-12 22:21:44 +0000489# endif
490 /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
491
andre61e67252000-06-04 17:07:49 +0000492# endif
andreecaabf12000-06-12 22:21:44 +0000493 /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
andre61e67252000-06-04 17:07:49 +0000494# endif
andreecaabf12000-06-12 22:21:44 +0000495 /* DISABLE_LASTLOG */
andre61e67252000-06-04 17:07:49 +0000496#endif
497/* USE_LASTLOG */
498}
499
500
501
andre2ff7b5d2000-06-03 14:57:40 +0000502/*
andre61e67252000-06-04 17:07:49 +0000503 * 'line' string utility functions
504 *
505 * These functions process the 'line' string into one of three forms:
506 *
andre2ff7b5d2000-06-03 14:57:40 +0000507 * 1. The full filename (including '/dev')
508 * 2. The stripped name (excluding '/dev')
andre61e67252000-06-04 17:07:49 +0000509 * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
510 * /dev/pts/1 -> ts/1 )
andre2ff7b5d2000-06-03 14:57:40 +0000511 *
512 * Form 3 is used on some systems to identify a .tmp.? entry when
513 * attempting to remove it. Typically both addition and removal is
andre61e67252000-06-04 17:07:49 +0000514 * performed by one application - say, sshd - so as long as the choice
515 * uniquely identifies a terminal it's ok.
andre2ff7b5d2000-06-03 14:57:40 +0000516 */
517
518
andre61e67252000-06-04 17:07:49 +0000519/* line_fullname(): add the leading '/dev/' if it doesn't exist make
520 * sure dst has enough space, if not just copy src (ugh) */
andre2ff7b5d2000-06-03 14:57:40 +0000521char *
andre61e67252000-06-04 17:07:49 +0000522line_fullname(char *dst, const char *src, int dstsize)
523{
andre2ff7b5d2000-06-03 14:57:40 +0000524 memset(dst, '\0', dstsize);
525 if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))
526 strlcpy(dst, src, dstsize);
527 else {
Damien Miller1a132252000-06-13 21:23:17 +1000528 strlcpy(dst, "/dev/", dstsize);
andre2ff7b5d2000-06-03 14:57:40 +0000529 strlcat(dst, src, dstsize);
530 }
531 return dst;
532}
533
andre61e67252000-06-04 17:07:49 +0000534
535/* line_stripname(): strip the leading '/dev' if it exists, return dst */
andre2ff7b5d2000-06-03 14:57:40 +0000536char *
andre61e67252000-06-04 17:07:49 +0000537line_stripname(char *dst, const char *src, int dstsize)
538{
andre2ff7b5d2000-06-03 14:57:40 +0000539 memset(dst, '\0', dstsize);
540 if (strncmp(src, "/dev/", 5) == 0)
541 strlcpy(dst, &src[5], dstsize);
542 else
543 strlcpy(dst, src, dstsize);
544 return dst;
andre61e67252000-06-04 17:07:49 +0000545}
546
andre2ff7b5d2000-06-03 14:57:40 +0000547
andre61e67252000-06-04 17:07:49 +0000548/* line_abbrevname(): Return the abbreviated (usually four-character)
549 * form of the line (Just use the last <dstsize> characters of the
550 * full name.)
551 *
552 * NOTE: use strncpy because we do NOT necessarily want zero
553 * termination */
andre2ff7b5d2000-06-03 14:57:40 +0000554char *
555line_abbrevname(char *dst, const char *src, int dstsize) {
556 memset(dst, '\0', dstsize);
andre61e67252000-06-04 17:07:49 +0000557 src += (strlen(src) - dstsize);
558 strncpy(dst, src, dstsize); /* note: _don't_ change this to strlcpy */
andre2ff7b5d2000-06-03 14:57:40 +0000559 return dst;
560}
561
562
563/**
564 ** utmp utility functions
andre61e67252000-06-04 17:07:49 +0000565 **
566 ** These functions manipulate struct utmp, taking system differences
567 ** into account.
andre2ff7b5d2000-06-03 14:57:40 +0000568 **/
569
570#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
571
andre2ff7b5d2000-06-03 14:57:40 +0000572/* build the utmp structure */
573void
andre61e67252000-06-04 17:07:49 +0000574set_utmp_time(struct logininfo *li, struct utmp *ut)
575{
andre2ff7b5d2000-06-03 14:57:40 +0000576#ifdef HAVE_TV_IN_UTMP
577 ut->ut_tv.tv_sec = li->tv_sec;
578 ut->ut_tv.tv_usec = li->tv_usec;
579#else
580# ifdef HAVE_TIME_IN_UTMP
581 ut->ut_time = li->tv_sec;
582# endif
583#endif
584}
585
andre61e67252000-06-04 17:07:49 +0000586
andre2ff7b5d2000-06-03 14:57:40 +0000587void
588construct_utmp(struct logininfo *li,
andre61e67252000-06-04 17:07:49 +0000589 struct utmp *ut)
590{
andre2ff7b5d2000-06-03 14:57:40 +0000591 memset(ut, '\0', sizeof(struct utmp));
andre6bb92372000-06-19 08:20:03 +0000592
593 /* First fill out fields used for both logins and logouts */
594
andre2ff7b5d2000-06-03 14:57:40 +0000595#ifdef HAVE_ID_IN_UTMP
596 line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
597#endif
598
599#ifdef HAVE_TYPE_IN_UTMP
andre6bb92372000-06-19 08:20:03 +0000600 /* This is done here to keep utmp constants out of struct logininfo */
andre2ff7b5d2000-06-03 14:57:40 +0000601 switch (li->type) {
602 case LTYPE_LOGIN:
603 ut->ut_type = USER_PROCESS;
604 break;
605 case LTYPE_LOGOUT:
606 ut->ut_type = DEAD_PROCESS;
607 break;
608 }
609#endif
andre6bb92372000-06-19 08:20:03 +0000610 set_utmp_time(li, ut);
andre2ff7b5d2000-06-03 14:57:40 +0000611
andre6bb92372000-06-19 08:20:03 +0000612 line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
andre2ff7b5d2000-06-03 14:57:40 +0000613#ifdef HAVE_PID_IN_UTMP
614 ut->ut_pid = li->pid;
615#endif
andre6bb92372000-06-19 08:20:03 +0000616
617 /* If we're logging out, leave all other fields blank */
618 if (li->type == LTYPE_LOGOUT)
619 return;
620
621 /* These fields are only used when logging in, and are blank
622 * for logouts. */
623
624 /* Use strncpy because we don't necessarily want null termination */
625 strncpy(ut->ut_user, li->username, MIN_SIZEOF(ut->ut_user, li->username));
andre2ff7b5d2000-06-03 14:57:40 +0000626#ifdef HAVE_HOST_IN_UTMP
andre6bb92372000-06-19 08:20:03 +0000627 strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
andre2ff7b5d2000-06-03 14:57:40 +0000628#endif
629#ifdef HAVE_ADDR_IN_UTMP
andre61e67252000-06-04 17:07:49 +0000630 /* this is just a 32-bit IP address */
631 if (li->hostaddr.sa.sa_family == AF_INET)
632 ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
633#endif
634}
andre2ff7b5d2000-06-03 14:57:40 +0000635
636#endif
637/* USE_UTMP || USE_WTMP || USE_LOGIN */
638
andre61e67252000-06-04 17:07:49 +0000639
640
andre2ff7b5d2000-06-03 14:57:40 +0000641/**
642 ** utmpx utility functions
andre61e67252000-06-04 17:07:49 +0000643 **
644 ** These functions manipulate struct utmpx, accounting for system
645 ** variations.
andre2ff7b5d2000-06-03 14:57:40 +0000646 **/
647
648#if defined(USE_UTMPX) || defined (USE_WTMPX)
649
andre2ff7b5d2000-06-03 14:57:40 +0000650/* build the utmpx structure */
651void
andre61e67252000-06-04 17:07:49 +0000652set_utmpx_time(struct logininfo *li, struct utmpx *utx)
653{
andre2ff7b5d2000-06-03 14:57:40 +0000654#ifdef HAVE_TV_IN_UTMPX
655 utx->ut_tv.tv_sec = li->tv_sec;
656 utx->ut_tv.tv_usec = li->tv_usec;
657#else
658# ifdef HAVE_TIME_IN_UTMPX
659 utx->ut_time = li->tv_sec;
660# endif
661#endif
662}
663
andre2ff7b5d2000-06-03 14:57:40 +0000664
andre61e67252000-06-04 17:07:49 +0000665void
666construct_utmpx(struct logininfo *li, struct utmpx *utx)
667{
668 memset(utx, '\0', sizeof(struct utmpx));
andre2ff7b5d2000-06-03 14:57:40 +0000669 line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
670
671 /* this is done here to keep utmp constants out of loginrec.h */
672 switch (li->type) {
673 case LTYPE_LOGIN:
674 utx->ut_type = USER_PROCESS;
675 break;
676 case LTYPE_LOGOUT:
677 utx->ut_type = DEAD_PROCESS;
678 break;
679 }
andre2ff7b5d2000-06-03 14:57:40 +0000680 line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
andre2ff7b5d2000-06-03 14:57:40 +0000681 set_utmpx_time(li, utx);
andre6bb92372000-06-19 08:20:03 +0000682 utx->ut_pid = li->pid;
683
684 if (li->type == LTYPE_LOGOUT)
685 return;
686
687 /* These fields are only used when logging in, and are blank
688 * for logouts. */
689
690 /* strncpy(): Don't necessarily want null termination */
691 strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
andre2ff7b5d2000-06-03 14:57:40 +0000692#ifdef HAVE_HOST_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +0000693 strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
andre2ff7b5d2000-06-03 14:57:40 +0000694#endif
695#ifdef HAVE_ADDR_IN_UTMPX
andre61e67252000-06-04 17:07:49 +0000696 /* FIXME: (ATL) not supported yet */
andre2ff7b5d2000-06-03 14:57:40 +0000697#endif
andre6bb92372000-06-19 08:20:03 +0000698#ifdef HAVE_SYSLEN_IN_UTMPX
699 /* ut_syslen is the length of the utx_host string */
700 utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
andre2ff7b5d2000-06-03 14:57:40 +0000701#endif
andre61e67252000-06-04 17:07:49 +0000702}
andre2ff7b5d2000-06-03 14:57:40 +0000703
704#endif
705/* USE_UTMPX || USE_WTMPX */
706
707
708
709/**
andre61e67252000-06-04 17:07:49 +0000710 ** Low-level utmp functions
andre2ff7b5d2000-06-03 14:57:40 +0000711 **/
712
713/* FIXME: (ATL) utmp_write_direct needs testing */
714
715#ifdef USE_UTMP
716
andre2ff7b5d2000-06-03 14:57:40 +0000717/* if we can, use pututline() etc. */
718#if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
719 defined(HAVE_PUTUTLINE)
720# define UTMP_USE_LIBRARY
721#endif
722
723
724/* write a utmp entry with the system's help (pututline() and pals) */
725#ifdef UTMP_USE_LIBRARY
726static int
andre61e67252000-06-04 17:07:49 +0000727utmp_write_library(struct logininfo *li, struct utmp *ut)
728{
andre2ff7b5d2000-06-03 14:57:40 +0000729 setutent();
730 pututline(ut);
731
732#ifdef HAVE_ENDUTENT
733 endutent();
734#endif
735 return 1;
andre61e67252000-06-04 17:07:49 +0000736}
andre2ff7b5d2000-06-03 14:57:40 +0000737
738#else
739
740/* write a utmp entry direct to the file */
andre61e67252000-06-04 17:07:49 +0000741/* This is a slightly modification of code in OpenBSD's login.c */
andre2ff7b5d2000-06-03 14:57:40 +0000742static int
andre61e67252000-06-04 17:07:49 +0000743utmp_write_direct(struct logininfo *li, struct utmp *ut)
744{
andre2ff7b5d2000-06-03 14:57:40 +0000745 struct utmp old_ut;
746 register int fd;
747 int tty;
748
andre6bb92372000-06-19 08:20:03 +0000749 /* FIXME: (ATL) ttyslot() needs local implementation */
andre2ff7b5d2000-06-03 14:57:40 +0000750 tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
751
752 if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
753 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
754 /*
755 * Prevent luser from zero'ing out ut_host.
756 * If the new ut_line is empty but the old one is not
757 * and ut_line and ut_name match, preserve the old ut_line.
758 */
759 if ( read(fd, &old_ut, sizeof(struct utmp)) == sizeof(struct utmp)
760 && ut->ut_host[0] == '\0'
761 && old_ut.ut_host[0] != '\0'
762 && strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0
763 && strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0 )
764 (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
765
766 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
767 if (write(fd, ut, sizeof(struct utmp))==-1)
768 log("utmp_write_direct: error writing %s: %s",
andre6bb92372000-06-19 08:20:03 +0000769 UTMP_FILE, strerror(errno));
andre2ff7b5d2000-06-03 14:57:40 +0000770
771 (void)close(fd);
772 return 1;
773 } else
774 return 0;
andre61e67252000-06-04 17:07:49 +0000775}
andre2ff7b5d2000-06-03 14:57:40 +0000776#endif /* UTMP_USE_LIBRARY */
777
778
779static int
andre61e67252000-06-04 17:07:49 +0000780utmp_perform_login(struct logininfo *li)
781{
andre2ff7b5d2000-06-03 14:57:40 +0000782 struct utmp ut;
783
784 construct_utmp(li, &ut);
andre2ff7b5d2000-06-03 14:57:40 +0000785#ifdef UTMP_USE_LIBRARY
786 if (!utmp_write_library(li, &ut)) {
andre6bb92372000-06-19 08:20:03 +0000787 log("utmp_perform_login: utmp_write_library() failed");
andre2ff7b5d2000-06-03 14:57:40 +0000788 return 0;
789 }
790#else
791 if (!utmp_write_direct(li, &ut)) {
792 log("utmp_perform_login: utmp_write_direct() failed");
793 return 0;
794 }
795#endif
796 return 1;
andre61e67252000-06-04 17:07:49 +0000797}
andre2ff7b5d2000-06-03 14:57:40 +0000798
799
800static int
andre61e67252000-06-04 17:07:49 +0000801utmp_perform_logout(struct logininfo *li)
802{
andre2ff7b5d2000-06-03 14:57:40 +0000803 struct utmp ut;
804
andre6bb92372000-06-19 08:20:03 +0000805 construct_utmp(li, &ut);
806#ifdef UTMP_USE_LIBRARY
807 if (!utmp_write_library(li, &ut)) {
808 log("utmp_perform_logout: utmp_write_library() failed");
809 return 0;
810 }
andre2ff7b5d2000-06-03 14:57:40 +0000811#else
andre6bb92372000-06-19 08:20:03 +0000812 if (!utmp_write_direct(li, &ut)) {
813 log("utmp_perform_logout: utmp_write_direct() failed");
814 return 0;
815 }
andre2ff7b5d2000-06-03 14:57:40 +0000816#endif
andre2ff7b5d2000-06-03 14:57:40 +0000817 return 1;
andre61e67252000-06-04 17:07:49 +0000818}
andre2ff7b5d2000-06-03 14:57:40 +0000819
820
821int
andre61e67252000-06-04 17:07:49 +0000822utmp_write_entry(struct logininfo *li)
823{
andre2ff7b5d2000-06-03 14:57:40 +0000824 switch(li->type) {
825 case LTYPE_LOGIN:
826 return utmp_perform_login(li);
827
828 case LTYPE_LOGOUT:
829 return utmp_perform_logout(li);
830
831 default:
832 log("utmp_write_entry: invalid type field");
833 return 0;
834 }
andre61e67252000-06-04 17:07:49 +0000835}
andre2ff7b5d2000-06-03 14:57:40 +0000836
837
838#endif
839/* USE_UTMP */
840
841
842/**
andre61e67252000-06-04 17:07:49 +0000843 ** Low-level utmpx functions
andre2ff7b5d2000-06-03 14:57:40 +0000844 **/
845
846/* not much point if we don't want utmpx entries */
847#ifdef USE_UTMPX
848
andre2ff7b5d2000-06-03 14:57:40 +0000849/* if we have the wherewithall, use pututxline etc. */
850#if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) \
851 && defined(HAVE_PUTUTXLINE)
852# define UTMPX_USE_LIBRARY
853#endif
854
855
856/* write a utmpx entry with the system's help (pututxline() and pals) */
857#ifdef UTMPX_USE_LIBRARY
858static int
andre61e67252000-06-04 17:07:49 +0000859utmpx_write_library(struct logininfo *li, struct utmpx *utx)
860{
andre2ff7b5d2000-06-03 14:57:40 +0000861 setutxent();
862 pututxline(utx);
863
864#ifdef HAVE_ENDUTXENT
865 endutxent();
866#endif
867 return 1;
andre61e67252000-06-04 17:07:49 +0000868}
andre2ff7b5d2000-06-03 14:57:40 +0000869
870#else
871/* UTMPX_USE_LIBRARY */
872
873
874/* write a utmp entry direct to the file */
875static int
andre61e67252000-06-04 17:07:49 +0000876utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
877{
andre2ff7b5d2000-06-03 14:57:40 +0000878 log("utmpx_write_direct: not implemented!");
879 return 0;
andre61e67252000-06-04 17:07:49 +0000880}
andre2ff7b5d2000-06-03 14:57:40 +0000881
882#endif
883/* UTMPX_USE_LIBRARY */
884
885static int
andre61e67252000-06-04 17:07:49 +0000886utmpx_perform_login(struct logininfo *li)
887{
andre2ff7b5d2000-06-03 14:57:40 +0000888 struct utmpx utx;
889
890 construct_utmpx(li, &utx);
andre2ff7b5d2000-06-03 14:57:40 +0000891#ifdef UTMPX_USE_LIBRARY
892 if (!utmpx_write_library(li, &utx)) {
893 log("utmpx_perform_login: utmp_write_library() failed");
894 return 0;
895 }
896#else
897 if (!utmpx_write_direct(li, &ut)) {
898 log("utmpx_perform_login: utmp_write_direct() failed");
899 return 0;
900 }
901#endif
902 return 1;
andre61e67252000-06-04 17:07:49 +0000903}
andre2ff7b5d2000-06-03 14:57:40 +0000904
905
906static int
andre61e67252000-06-04 17:07:49 +0000907utmpx_perform_logout(struct logininfo *li)
908{
andre2ff7b5d2000-06-03 14:57:40 +0000909 struct utmpx utx;
910
911 memset(&utx, '\0', sizeof(utx));
912 set_utmpx_time(li, &utx);
913 line_stripname(utx.ut_line, li->line, sizeof(utx.ut_line));
914#ifdef HAVE_ID_IN_UTMPX
915 line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
916#endif
917#ifdef HAVE_TYPE_IN_UTMPX
918 utx.ut_type = DEAD_PROCESS;
919#endif
920
921#ifdef UTMPX_USE_LIBRARY
922 utmpx_write_library(li, &utx);
923#else
924 utmpx_write_direct(li, &utx);
925#endif
andre2ff7b5d2000-06-03 14:57:40 +0000926 return 1;
andre61e67252000-06-04 17:07:49 +0000927}
andre2ff7b5d2000-06-03 14:57:40 +0000928
929
930int
andre61e67252000-06-04 17:07:49 +0000931utmpx_write_entry(struct logininfo *li)
932{
andre2ff7b5d2000-06-03 14:57:40 +0000933 switch(li->type) {
934 case LTYPE_LOGIN:
935 return utmpx_perform_login(li);
936 case LTYPE_LOGOUT:
937 return utmpx_perform_logout(li);
938 default:
939 log("utmpx_write_entry: invalid type field");
940 return 0;
941 }
andre61e67252000-06-04 17:07:49 +0000942}
andre2ff7b5d2000-06-03 14:57:40 +0000943
944
945#endif
946/* USE_UTMPX */
947
948
949/**
andre61e67252000-06-04 17:07:49 +0000950 ** Low-level wtmp functions
andre2ff7b5d2000-06-03 14:57:40 +0000951 **/
952
953#ifdef USE_WTMP
954
andre2ff7b5d2000-06-03 14:57:40 +0000955/* write a wtmp entry direct to the end of the file */
andre61e67252000-06-04 17:07:49 +0000956/* This is a slight modification of code in OpenBSD's logwtmp.c */
andre2ff7b5d2000-06-03 14:57:40 +0000957static int
andre61e67252000-06-04 17:07:49 +0000958wtmp_write(struct logininfo *li, struct utmp *ut)
959{
andre2ff7b5d2000-06-03 14:57:40 +0000960 struct stat buf;
961 int fd, ret = 1;
962
963 if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
964 log("wtmp_write: problem writing %s: %s",
965 WTMP_FILE, strerror(errno));
966 return 0;
967 }
andre61e67252000-06-04 17:07:49 +0000968 if (fstat(fd, &buf) == 0)
andre2ff7b5d2000-06-03 14:57:40 +0000969 if (write(fd, (char *)ut, sizeof(struct utmp)) !=
970 sizeof(struct utmp)) {
971 ftruncate(fd, buf.st_size);
972 log("wtmp_write: problem writing %s: %s",
973 WTMP_FILE, strerror(errno));
974 ret = 0;
975 }
976 (void)close(fd);
andre2ff7b5d2000-06-03 14:57:40 +0000977 return ret;
andre61e67252000-06-04 17:07:49 +0000978}
andre2ff7b5d2000-06-03 14:57:40 +0000979
980
981static int
andre61e67252000-06-04 17:07:49 +0000982wtmp_perform_login(struct logininfo *li){
andre2ff7b5d2000-06-03 14:57:40 +0000983 struct utmp ut;
984
985 construct_utmp(li, &ut);
986 return wtmp_write(li, &ut);
andre61e67252000-06-04 17:07:49 +0000987}
andre2ff7b5d2000-06-03 14:57:40 +0000988
989
990static int
andre61e67252000-06-04 17:07:49 +0000991wtmp_perform_logout(struct logininfo *li)
992{
andre2ff7b5d2000-06-03 14:57:40 +0000993 struct utmp ut;
994
995 construct_utmp(li, &ut);
andre2ff7b5d2000-06-03 14:57:40 +0000996 return wtmp_write(li, &ut);
andre61e67252000-06-04 17:07:49 +0000997}
andre2ff7b5d2000-06-03 14:57:40 +0000998
999
1000int
andre61e67252000-06-04 17:07:49 +00001001wtmp_write_entry(struct logininfo *li)
1002{
andre2ff7b5d2000-06-03 14:57:40 +00001003 switch(li->type) {
1004 case LTYPE_LOGIN:
1005 return wtmp_perform_login(li);
1006 case LTYPE_LOGOUT:
1007 return wtmp_perform_logout(li);
1008 default:
1009 log("wtmp_write_entry: invalid type field");
1010 return 0;
1011 }
andre61e67252000-06-04 17:07:49 +00001012}
andre2ff7b5d2000-06-03 14:57:40 +00001013
1014
andre6bb92372000-06-19 08:20:03 +00001015/* Notes on fetching login data from wtmp/wtmpx
1016 *
1017 * Logouts are usually recorded with (amongst other things) a blank
1018 * username on a given tty line. However, some systems (HP-UX is one)
1019 * leave all fields set, but change the ut_type field to DEAD_PROCESS.
1020 *
1021 * Since we're only looking for logins here, we know that the username
1022 * must be set correctly. On systems that leave it in, we check for
1023 * ut_type==USER_PROCESS (indicating a login.)
1024 *
1025 * Portability: Some systems may set something other than USER_PROCESS
1026 * to indicate a login process. I don't know of any as I write. Also,
1027 * it's possible that some systems may both leave the username in
1028 * place and not have ut_type.
1029 */
1030
1031
1032/* return true if this wtmp entry indicates a login */
1033static int
1034wtmp_islogin(struct logininfo *li, struct utmp *ut)
1035{
1036 if ( strncmp(li->username, ut->ut_user,
1037 MIN_SIZEOF(li->username, ut->ut_user)) == 0 ) {
1038#ifdef HAVE_TYPE_IN_UTMP
1039 if (ut->ut_type & USER_PROCESS)
1040 return 1;
1041#else
1042 return 1;
1043#endif
1044 }
1045 return 0;
1046}
1047
1048
andre2ff7b5d2000-06-03 14:57:40 +00001049int
andre61e67252000-06-04 17:07:49 +00001050wtmp_get_entry(struct logininfo *li)
1051{
andre2ff7b5d2000-06-03 14:57:40 +00001052 struct stat st;
1053 struct utmp ut;
andre6bb92372000-06-19 08:20:03 +00001054 int fd, found=0;
1055
1056 /* Clear the time entries in our logininfo */
1057 li->tv_sec = li->tv_usec = 0;
andre2ff7b5d2000-06-03 14:57:40 +00001058
1059 if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1060 log("wtmp_get_entry: problem opening %s: %s",
1061 WTMP_FILE, strerror(errno));
1062 return 0;
1063 }
andre61e67252000-06-04 17:07:49 +00001064 if (fstat(fd, &st) != 0) {
andre2ff7b5d2000-06-03 14:57:40 +00001065 log("wtmp_get_entry: couldn't stat %s: %s",
1066 WTMP_FILE, strerror(errno));
1067 close(fd);
1068 return 0;
1069 }
andre2ff7b5d2000-06-03 14:57:40 +00001070
andre6bb92372000-06-19 08:20:03 +00001071 /* Seek to the start of the last struct utmp */
1072 if (lseek(fd, (off_t)(0-sizeof(struct utmp)), SEEK_END) == -1) {
1073 /* Looks like we've got a fresh wtmp file */
1074 close(fd);
1075 return 0;
1076 }
1077
1078 while (!found) {
andre2ff7b5d2000-06-03 14:57:40 +00001079 if (read(fd, &ut, sizeof(ut)) != sizeof(ut)) {
1080 log("wtmp_get_entry: read of %s failed: %s",
1081 WTMP_FILE, strerror(errno));
1082 close (fd);
1083 return 0;
1084 }
andre6bb92372000-06-19 08:20:03 +00001085 if ( wtmp_islogin(li, &ut) ) {
1086 found = 1;
1087 /* We've already checked for a time in struct
1088 * utmp, in login_getlast(). */
andre2ff7b5d2000-06-03 14:57:40 +00001089#ifdef HAVE_TIME_IN_UTMP
1090 li->tv_sec = ut.ut_time;
1091#else
1092# if HAVE_TV_IN_UTMP
1093 li->tv_sec = ut.ut_tv.tv_sec;
1094# endif
1095#endif
andre6bb92372000-06-19 08:20:03 +00001096 line_fullname(li->line, ut.ut_line,
1097 MIN_SIZEOF(li->line, ut.ut_line));
andre2ff7b5d2000-06-03 14:57:40 +00001098#ifdef HAVE_HOST_IN_UTMP
andre6bb92372000-06-19 08:20:03 +00001099 strlcpy(li->hostname, ut.ut_host,
1100 MIN_SIZEOF(li->hostname, ut.ut_host));
andre2ff7b5d2000-06-03 14:57:40 +00001101#endif
andre6bb92372000-06-19 08:20:03 +00001102 continue;
andre2ff7b5d2000-06-03 14:57:40 +00001103 }
andre6bb92372000-06-19 08:20:03 +00001104 /* Seek back 2 x struct utmp */
andre2ff7b5d2000-06-03 14:57:40 +00001105 if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) {
andre6bb92372000-06-19 08:20:03 +00001106 /* We've found the start of the file, so quit */
andre2ff7b5d2000-06-03 14:57:40 +00001107 close (fd);
1108 return 0;
1109 }
andre6bb92372000-06-19 08:20:03 +00001110 }
1111
1112 /* We found an entry. Tidy up and return */
1113 close(fd);
andre2ff7b5d2000-06-03 14:57:40 +00001114 return 1;
andre61e67252000-06-04 17:07:49 +00001115}
andre2ff7b5d2000-06-03 14:57:40 +00001116
1117
1118#endif
1119/* USE_WTMP */
1120
1121
1122/**
andre61e67252000-06-04 17:07:49 +00001123 ** Low-level wtmpx functions
andre2ff7b5d2000-06-03 14:57:40 +00001124 **/
1125
1126#ifdef USE_WTMPX
1127
andre2ff7b5d2000-06-03 14:57:40 +00001128/* write a wtmpx entry direct to the end of the file */
andre61e67252000-06-04 17:07:49 +00001129/* This is a slight modification of code in OpenBSD's logwtmp.c */
andre2ff7b5d2000-06-03 14:57:40 +00001130static int
andre61e67252000-06-04 17:07:49 +00001131wtmpx_write(struct logininfo *li, struct utmpx *utx)
1132{
andre2ff7b5d2000-06-03 14:57:40 +00001133 struct stat buf;
1134 int fd, ret = 1;
1135
1136 if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1137 log("wtmpx_write: problem opening %s: %s",
1138 WTMPX_FILE, strerror(errno));
1139 return 0;
1140 }
1141
1142 if (fstat(fd, &buf) == 0)
1143 if (write(fd, (char *)utx, sizeof(struct utmpx)) !=
1144 sizeof(struct utmpx)) {
1145 ftruncate(fd, buf.st_size);
1146 log("wtmpx_write: problem writing %s: %s",
1147 WTMPX_FILE, strerror(errno));
1148 ret = 0;
1149 }
1150 (void)close(fd);
1151
1152 return ret;
andre61e67252000-06-04 17:07:49 +00001153}
andre2ff7b5d2000-06-03 14:57:40 +00001154
1155
1156static int
andre61e67252000-06-04 17:07:49 +00001157wtmpx_perform_login(struct logininfo *li)
1158{
andre2ff7b5d2000-06-03 14:57:40 +00001159 struct utmpx utx;
1160
1161 construct_utmpx(li, &utx);
1162 return wtmpx_write(li, &utx);
andre61e67252000-06-04 17:07:49 +00001163}
andre2ff7b5d2000-06-03 14:57:40 +00001164
1165
1166static int
andre61e67252000-06-04 17:07:49 +00001167wtmpx_perform_logout(struct logininfo *li)
1168{
andre2ff7b5d2000-06-03 14:57:40 +00001169 struct utmpx utx;
1170
1171 construct_utmpx(li, &utx);
andre2ff7b5d2000-06-03 14:57:40 +00001172 return wtmpx_write(li, &utx);
andre61e67252000-06-04 17:07:49 +00001173}
andre2ff7b5d2000-06-03 14:57:40 +00001174
1175
1176int
andre61e67252000-06-04 17:07:49 +00001177wtmpx_write_entry(struct logininfo *li)
1178{
andre2ff7b5d2000-06-03 14:57:40 +00001179 switch(li->type) {
1180 case LTYPE_LOGIN:
1181 return wtmpx_perform_login(li);
1182 case LTYPE_LOGOUT:
1183 return wtmpx_perform_logout(li);
1184 default:
1185 log("wtmpx_write_entry: invalid type field");
1186 return 0;
1187 }
andre61e67252000-06-04 17:07:49 +00001188}
andre2ff7b5d2000-06-03 14:57:40 +00001189
andre6bb92372000-06-19 08:20:03 +00001190/* Please see the notes above wtmp_islogin() for information about the
1191 next two functions */
1192
1193/* Return true if this wtmpx entry indicates a login */
1194static int
1195wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1196{
1197 if ( strncmp(li->username, utx->ut_user,
1198 MIN_SIZEOF(li->username, utx->us_user)) == 0 ) {
1199#ifdef HAVE_TYPE_IN_UTMPX
1200 if (utx->ut_type == USER_PROCESS)
1201 return 1;
1202#else
1203 return 1;
1204#endif
1205 }
1206 return 0;
1207}
1208
andre2ff7b5d2000-06-03 14:57:40 +00001209
1210int
andre61e67252000-06-04 17:07:49 +00001211wtmpx_get_entry(struct logininfo *li)
1212{
andre2ff7b5d2000-06-03 14:57:40 +00001213 struct stat st;
1214 struct utmpx utx;
andre6bb92372000-06-19 08:20:03 +00001215 int fd, found=0;
1216
1217 /* Clear the time entries */
1218 li->tv_sec = li->tv_usec = 0;
andre2ff7b5d2000-06-03 14:57:40 +00001219
1220 if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1221 log("wtmpx_get_entry: problem opening %s: %s",
1222 WTMPX_FILE, strerror(errno));
1223 return 0;
1224 }
andre61e67252000-06-04 17:07:49 +00001225 if (fstat(fd, &st) != 0) {
andre2ff7b5d2000-06-03 14:57:40 +00001226 log("wtmpx_get_entry: couldn't stat %s: %s",
1227 WTMP_FILE, strerror(errno));
1228 close(fd);
1229 return 0;
1230 }
andre6bb92372000-06-19 08:20:03 +00001231
1232 /* Seek to the start of the last struct utmpx */
1233 if (lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END) == -1 ) {
1234 /* probably a newly rotated wtmpx file */
1235 close(fd);
1236 return 0;
1237 }
andre2ff7b5d2000-06-03 14:57:40 +00001238
andre6bb92372000-06-19 08:20:03 +00001239 while (!found) {
andre2ff7b5d2000-06-03 14:57:40 +00001240 if (read(fd, &utx, sizeof(utx)) != sizeof(utx)) {
1241 log("wtmpx_get_entry: read of %s failed: %s",
1242 WTMPX_FILE, strerror(errno));
1243 close (fd);
1244 return 0;
1245 }
andre2ff7b5d2000-06-03 14:57:40 +00001246 /* Logouts are recorded as a blank username on a particular line.
1247 * So, we just need to find the username in struct utmpx */
andre6bb92372000-06-19 08:20:03 +00001248 if ( wtmpx_islogin(li, &utx) ) {
andre2ff7b5d2000-06-03 14:57:40 +00001249#ifdef HAVE_TV_IN_UTMPX
1250 li->tv_sec = utx.ut_tv.tv_sec;
1251#else
1252# ifdef HAVE_TIME_IN_UTMPX
1253 li->tv_sec = utx.ut_time;
1254# endif
1255#endif
Damien Miller1a132252000-06-13 21:23:17 +10001256 line_fullname(li->line, utx.ut_line, sizeof(li->line));
andre2ff7b5d2000-06-03 14:57:40 +00001257#ifdef HAVE_HOST_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +00001258 strlcpy(li->hostname, utx.ut_host,
1259 MIN_SIZEOF(li->hostname, utx.ut_host));
andre2ff7b5d2000-06-03 14:57:40 +00001260#endif
andre6bb92372000-06-19 08:20:03 +00001261 continue;
andre2ff7b5d2000-06-03 14:57:40 +00001262 }
1263 if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) {
1264 close (fd);
1265 return 0;
1266 }
andre6bb92372000-06-19 08:20:03 +00001267 }
1268
1269 close(fd);
andre2ff7b5d2000-06-03 14:57:40 +00001270 return 1;
andre61e67252000-06-04 17:07:49 +00001271}
andre2ff7b5d2000-06-03 14:57:40 +00001272
1273
Damien Millerd5bf3072000-06-07 21:32:13 +10001274#endif /* USE_WTMPX */
andre2ff7b5d2000-06-03 14:57:40 +00001275
1276
1277/**
andre61e67252000-06-04 17:07:49 +00001278 ** Low-level libutil login() functions
andre2ff7b5d2000-06-03 14:57:40 +00001279 **/
1280
1281#ifdef USE_LOGIN
1282
andre2ff7b5d2000-06-03 14:57:40 +00001283static int
andre61e67252000-06-04 17:07:49 +00001284syslogin_perform_login(struct logininfo *li)
1285{
andre2ff7b5d2000-06-03 14:57:40 +00001286 struct utmp *ut;
1287
1288 if (! (ut = (struct utmp *)malloc(sizeof(struct utmp)))) {
1289 log("syslogin_perform_login: couldn't malloc()");
1290 return 0;
1291 }
1292 construct_utmp(li, ut);
1293 login(ut);
1294
1295 return 1;
andre61e67252000-06-04 17:07:49 +00001296}
1297
andre2ff7b5d2000-06-03 14:57:40 +00001298
1299static int
andre61e67252000-06-04 17:07:49 +00001300syslogin_perform_logout(struct logininfo *li)
1301{
andre2ff7b5d2000-06-03 14:57:40 +00001302#ifdef HAVE_LOGOUT
1303 char line[8];
1304
1305 (void)line_stripname(line, li->line, sizeof(line));
1306
1307 if (!logout(line)) {
1308 log("syslogin_perform_logout: logout() returned an error");
Damien Millerd5bf3072000-06-07 21:32:13 +10001309# ifdef HAVE_LOGWTMP
andre2ff7b5d2000-06-03 14:57:40 +00001310 } else {
1311 logwtmp(line, "", "");
1312 }
Damien Millerd5bf3072000-06-07 21:32:13 +10001313# endif
andre6bb92372000-06-19 08:20:03 +00001314 /* FIXME: (ATL - if the need arises) What to do if we have
1315 * login, but no logout? what if logout but no logwtmp? All
1316 * routines are in libutil so they should all be there,
1317 * but... */
andre2ff7b5d2000-06-03 14:57:40 +00001318#endif
1319 return 1;
andre61e67252000-06-04 17:07:49 +00001320}
andre2ff7b5d2000-06-03 14:57:40 +00001321
1322
1323int
andre61e67252000-06-04 17:07:49 +00001324syslogin_write_entry(struct logininfo *li)
1325{
andre2ff7b5d2000-06-03 14:57:40 +00001326 switch (li->type) {
1327 case LTYPE_LOGIN:
1328 return syslogin_perform_login(li);
1329 case LTYPE_LOGOUT:
1330 return syslogin_perform_logout(li);
1331 default:
1332 log("syslogin_write_entry: Invalid type field");
1333 return 0;
1334 }
andre61e67252000-06-04 17:07:49 +00001335}
andre2ff7b5d2000-06-03 14:57:40 +00001336
1337
Damien Millerd5bf3072000-06-07 21:32:13 +10001338#endif /* USE_LOGIN */
andre2ff7b5d2000-06-03 14:57:40 +00001339
1340/* end of file log-syslogin.c */
1341
1342
1343/**
andre61e67252000-06-04 17:07:49 +00001344 ** Low-level lastlog functions
andre2ff7b5d2000-06-03 14:57:40 +00001345 **/
1346
1347#ifdef USE_LASTLOG
1348
andre2ff7b5d2000-06-03 14:57:40 +00001349static void
andre61e67252000-06-04 17:07:49 +00001350lastlog_construct(struct logininfo *li, struct lastlog *last)
1351{
andre2ff7b5d2000-06-03 14:57:40 +00001352 /* clear the structure */
1353 memset(last, '\0', sizeof(struct lastlog));
1354
1355 (void)line_stripname(last->ll_line, li->line,
1356 sizeof(last->ll_line));
andre6bb92372000-06-19 08:20:03 +00001357 strlcpy(last->ll_host, li->hostname,
1358 MIN_SIZEOF(last->ll_host, li->hostname));
andre2ff7b5d2000-06-03 14:57:40 +00001359 last->ll_time = li->tv_sec;
andre61e67252000-06-04 17:07:49 +00001360}
andre2ff7b5d2000-06-03 14:57:40 +00001361
1362
1363#define LL_FILE 1
1364#define LL_DIR 2
1365#define LL_OTHER 3
1366
1367static int
andre61e67252000-06-04 17:07:49 +00001368lastlog_filetype(char *filename)
1369{
andre2ff7b5d2000-06-03 14:57:40 +00001370 struct stat st;
1371
1372 if ( stat(LASTLOG_FILE, &st) != 0) {
1373 log("lastlog_perform_login: Couldn't stat %s: %s",
1374 LASTLOG_FILE, strerror(errno));
1375 return 0;
1376 }
andre2ff7b5d2000-06-03 14:57:40 +00001377 if (S_ISDIR(st.st_mode))
1378 return LL_DIR;
1379 else if (S_ISREG(st.st_mode))
1380 return LL_FILE;
1381 else
1382 return LL_OTHER;
andre61e67252000-06-04 17:07:49 +00001383}
andre2ff7b5d2000-06-03 14:57:40 +00001384
1385
1386/* open the file (using filemode) and seek to the login entry */
1387static int
andre61e67252000-06-04 17:07:49 +00001388lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1389{
andre2ff7b5d2000-06-03 14:57:40 +00001390 off_t offset;
1391 int type;
1392 char lastlog_file[1024];
1393
1394 type = lastlog_filetype(LASTLOG_FILE);
1395 switch (type) {
1396 case LL_FILE:
1397 strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1398 break;
1399 case LL_DIR:
1400 snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1401 LASTLOG_FILE, li->username);
1402 break;
1403 default:
1404 log("lastlog_openseek: %.100s is not a file or directory!",
1405 LASTLOG_FILE);
1406 return 0;
1407 } /* switch */
1408
1409 *fd = open(lastlog_file, filemode);
1410 if ( *fd < 0) {
1411 log("lastlog_openseek: Couldn't open %s: %s",
1412 lastlog_file, strerror(errno));
1413 return 0;
1414 }
andre2ff7b5d2000-06-03 14:57:40 +00001415 /* find this uid's offset in the lastlog file */
1416 offset = (off_t) ( (long)li->uid * sizeof(struct lastlog));
1417
1418 if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1419 log("lastlog_openseek: %s->lseek(): %s",
1420 lastlog_file, strerror(errno));
1421 return 0;
1422 }
1423 return 1;
andre61e67252000-06-04 17:07:49 +00001424}
andre2ff7b5d2000-06-03 14:57:40 +00001425
1426static int
andre61e67252000-06-04 17:07:49 +00001427lastlog_perform_login(struct logininfo *li)
1428{
andre2ff7b5d2000-06-03 14:57:40 +00001429 struct lastlog last;
1430 int fd;
1431
1432 /* create our struct lastlog */
1433 lastlog_construct(li, &last);
1434
1435 /* write the entry */
1436 if (lastlog_openseek(li, &fd, O_RDWR)) {
1437 if ( write(fd, &last, sizeof(struct lastlog))
1438 != sizeof(struct lastlog) ) {
1439 log("lastlog_write_filemode: Error writing to %s: %s",
1440 LASTLOG_FILE, strerror(errno));
1441 return 0;
1442 }
1443 return 1;
1444 } else
1445 return 0;
andre61e67252000-06-04 17:07:49 +00001446}
andre2ff7b5d2000-06-03 14:57:40 +00001447
1448
1449int
andre61e67252000-06-04 17:07:49 +00001450lastlog_write_entry(struct logininfo *li)
1451{
andre2ff7b5d2000-06-03 14:57:40 +00001452 switch(li->type) {
1453 case LTYPE_LOGIN:
1454 return lastlog_perform_login(li);
1455 default:
1456 log("lastlog_write_entry: Invalid type field");
1457 return 0;
1458 }
andre61e67252000-06-04 17:07:49 +00001459}
andre2ff7b5d2000-06-03 14:57:40 +00001460
1461
1462static void
andre61e67252000-06-04 17:07:49 +00001463lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1464{
andre2ff7b5d2000-06-03 14:57:40 +00001465 line_fullname(li->line, last->ll_line, sizeof(li->line));
andre6bb92372000-06-19 08:20:03 +00001466 strlcpy(li->hostname, last->ll_host,
1467 MIN_SIZEOF(li->hostname, last->ll_host));
andre2ff7b5d2000-06-03 14:57:40 +00001468 li->tv_sec = last->ll_time;
andre61e67252000-06-04 17:07:49 +00001469}
andre2ff7b5d2000-06-03 14:57:40 +00001470
1471
1472int
andre61e67252000-06-04 17:07:49 +00001473lastlog_get_entry(struct logininfo *li)
1474{
andre2ff7b5d2000-06-03 14:57:40 +00001475 struct lastlog last;
1476 int fd;
1477
1478 if (lastlog_openseek(li, &fd, O_RDONLY)) {
1479 if ( read(fd, &last, sizeof(struct lastlog))
1480 != sizeof(struct lastlog) ) {
1481 log("lastlog_write_filemode: Error reading from %s: %s",
1482 LASTLOG_FILE, strerror(errno));
1483 return 0;
1484 } else {
1485 lastlog_populate_entry(li, &last);
1486 return 1;
1487 }
andre2ff7b5d2000-06-03 14:57:40 +00001488 } else
1489 return 0;
andre61e67252000-06-04 17:07:49 +00001490}
andre2ff7b5d2000-06-03 14:57:40 +00001491
1492
Damien Millerd5bf3072000-06-07 21:32:13 +10001493#endif /* USE_LASTLOG */