blob: 5338847aa6e162e7940897952a821aea30a7fe04 [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
Damien Miller8e81ed32000-07-01 13:17:42 +1000173RCSID("$Id: loginrec.c,v 1.13 2000/07/01 03:17:42 djm 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
andre6bb92372000-06-19 08:20:03 +0000201/* pick the shortest string */
202#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
203
andre2ff7b5d2000-06-03 14:57:40 +0000204/**
205 ** platform-independent login functions
206 **/
207
andre6bb92372000-06-19 08:20:03 +0000208/* login_login(struct logininfo *) -Record a login
209 *
210 * Call with a pointer to a struct logininfo initialised with
211 * login_init_entry() or login_alloc_entry()
212 *
213 * Returns:
214 * >0 if successful
215 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
216 */
andre61e67252000-06-04 17:07:49 +0000217int
218login_login (struct logininfo *li)
219{
220 li->type = LTYPE_LOGIN;
221 return login_write(li);
222}
223
224
andre6bb92372000-06-19 08:20:03 +0000225/* login_logout(struct logininfo *) - Record a logout
226 *
227 * Call as with login_login()
228 *
229 * Returns:
230 * >0 if successful
231 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
232 */
andre61e67252000-06-04 17:07:49 +0000233int
234login_logout(struct logininfo *li)
235{
236 li->type = LTYPE_LOGOUT;
237 return login_write(li);
238}
239
andre6bb92372000-06-19 08:20:03 +0000240/* login_get_lastlog_time(int) - Retrieve the last login time
241 *
242 * Retrieve the last login time for the given uid. Will try to use the
243 * system lastlog facilities if they are available, but will fall back
244 * to looking in wtmp/wtmpx if necessary
245 *
246 * Returns:
247 * 0 on failure, or if user has never logged in
248 * Time in seconds from the epoch if successful
249 *
250 * Useful preprocessor symbols:
251 * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
252 * info
253 * USE_LASTLOG: If set, indicates the presence of system lastlog
254 * facilities. If this and DISABLE_LASTLOG are not set,
255 * try to retrieve lastlog information from wtmp/wtmpx.
256 */
andre61e67252000-06-04 17:07:49 +0000257unsigned int
258login_get_lastlog_time(const int uid)
259{
260 struct logininfo li;
261
andre6bb92372000-06-19 08:20:03 +0000262 if (login_get_lastlog(&li, uid))
263 return li.tv_sec;
264 else
265 return 0;
andre61e67252000-06-04 17:07:49 +0000266}
267
andre6bb92372000-06-19 08:20:03 +0000268/* login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry
269 *
270 * Retrieve a logininfo structure populated (only partially) with
271 * information from the system lastlog data, or from wtmp/wtmpx if no
272 * system lastlog information exists.
273 *
274 * Note this routine must be given a pre-allocated logininfo.
275 *
276 * Returns:
277 * >0: A pointer to your struct logininfo if successful
278 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
279 *
280 */
andre61e67252000-06-04 17:07:49 +0000281struct logininfo *
282login_get_lastlog(struct logininfo *li, const int uid)
283{
andre6bb92372000-06-19 08:20:03 +0000284 struct passwd *pw;
andre6bb92372000-06-19 08:20:03 +0000285
andre61e67252000-06-04 17:07:49 +0000286 memset(li, '\0', sizeof(struct logininfo));
287 li->uid = uid;
andre6bb92372000-06-19 08:20:03 +0000288
Damien Miller53c5d462000-06-28 00:50:50 +1000289 /*
290 * If we don't have a 'real' lastlog, we need the username to
andre6bb92372000-06-19 08:20:03 +0000291 * reliably search wtmp(x) for the last login (see
Damien Miller53c5d462000-06-28 00:50:50 +1000292 * wtmp_get_entry().)
293 */
andre6bb92372000-06-19 08:20:03 +0000294 pw = getpwuid(uid);
Damien Millerdd47aa22000-06-27 11:18:27 +1000295 if (pw == NULL)
296 fatal("login_get_lastlog: Cannot find account for uid %i", uid);
297
andre98cabe02000-06-19 09:11:30 +0000298 /* No MIN_SIZEOF here - we absolutely *must not* truncate the
299 * username */
Damien Millerf8af08d2000-06-27 09:40:06 +1000300 strlcpy(li->username, pw->pw_name, sizeof(li->username));
Damien Millerdd47aa22000-06-27 11:18:27 +1000301
andre61e67252000-06-04 17:07:49 +0000302 if (getlast_entry(li))
303 return li;
304 else
Damien Millerdd47aa22000-06-27 11:18:27 +1000305 return NULL;
andre61e67252000-06-04 17:07:49 +0000306}
307
308
andre6bb92372000-06-19 08:20:03 +0000309/* login_alloc_entry(int, char*, char*, char*) - Allocate and initialise
310 * a logininfo structure
311 *
312 * This function creates a new struct logininfo, a data structure
313 * meant to carry the information required to portably record login info.
314 *
315 * Returns a pointer to a newly created struct logininfo. If memory
316 * allocation fails, the program halts.
317 */
andre61e67252000-06-04 17:07:49 +0000318struct
319logininfo *login_alloc_entry(int pid, const char *username,
320 const char *hostname, const char *line)
321{
andre2ff7b5d2000-06-03 14:57:40 +0000322 struct logininfo *newli;
323
324 newli = (struct logininfo *) xmalloc (sizeof(struct logininfo));
andre61e67252000-06-04 17:07:49 +0000325 (void)login_init_entry(newli, pid, username, hostname, line);
326 return newli;
327}
andre2ff7b5d2000-06-03 14:57:40 +0000328
329
andre6bb92372000-06-19 08:20:03 +0000330/* login_free_entry(struct logininfo *) - free struct memory */
andre61e67252000-06-04 17:07:49 +0000331void
332login_free_entry(struct logininfo *li)
333{
334 xfree(li);
335}
336
andre2ff7b5d2000-06-03 14:57:40 +0000337
andre6bb92372000-06-19 08:20:03 +0000338/* login_init_entry(struct logininfo *, int, char*, char*, char*)
339 * - initialise a struct logininfo
340 *
341 * Populates a new struct logininfo, a data structure meant to carry
342 * the information required to portably record login info.
343 *
344 * Returns: 1
345 */
andre61e67252000-06-04 17:07:49 +0000346int
347login_init_entry(struct logininfo *li, int pid, const char *username,
348 const char *hostname, const char *line)
349{
Damien Millerf8af08d2000-06-27 09:40:06 +1000350 struct passwd *pw;
351
andre2ff7b5d2000-06-03 14:57:40 +0000352 memset(li, 0, sizeof(struct logininfo));
353
andre61e67252000-06-04 17:07:49 +0000354 li->pid = pid;
Damien Millerf8af08d2000-06-27 09:40:06 +1000355
andre2ff7b5d2000-06-03 14:57:40 +0000356 /* set the line information */
andre61e67252000-06-04 17:07:49 +0000357 if (line)
andre2ff7b5d2000-06-03 14:57:40 +0000358 line_fullname(li->line, line, sizeof(li->line));
andre2ff7b5d2000-06-03 14:57:40 +0000359
Damien Millerf8af08d2000-06-27 09:40:06 +1000360 if (username) {
andre2ff7b5d2000-06-03 14:57:40 +0000361 strlcpy(li->username, username, sizeof(li->username));
Damien Millerf8af08d2000-06-27 09:40:06 +1000362 pw = getpwnam(li->username);
363 if (pw == NULL)
364 fatal("login_init_entry: Cannot find user \"%s\"", li->username);
365 li->uid = pw->pw_uid;
366 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000367
andre61e67252000-06-04 17:07:49 +0000368 if (hostname)
andre2ff7b5d2000-06-03 14:57:40 +0000369 strlcpy(li->hostname, hostname, sizeof(li->hostname));
Damien Millerf8af08d2000-06-27 09:40:06 +1000370
andre61e67252000-06-04 17:07:49 +0000371 return 1;
andre2ff7b5d2000-06-03 14:57:40 +0000372}
373
andre6bb92372000-06-19 08:20:03 +0000374/* login_set_current_time(struct logininfo *) - set the current time
375 *
376 * Set the current time in a logininfo structure. This function is
377 * meant to eliminate the need to deal with system dependencies for
378 * time handling.
379 */
andre2ff7b5d2000-06-03 14:57:40 +0000380void
andre61e67252000-06-04 17:07:49 +0000381login_set_current_time(struct logininfo *li)
382{
andre2ff7b5d2000-06-03 14:57:40 +0000383 struct timeval tv;
384
385 gettimeofday(&tv, NULL);
Damien Millerf8af08d2000-06-27 09:40:06 +1000386
387 li->tv_sec = tv.tv_sec;
388 li->tv_usec = tv.tv_usec;
andre2ff7b5d2000-06-03 14:57:40 +0000389}
390
andre61e67252000-06-04 17:07:49 +0000391/* copy a sockaddr_* into our logininfo */
andre2ff7b5d2000-06-03 14:57:40 +0000392void
andre61e67252000-06-04 17:07:49 +0000393login_set_addr(struct logininfo *li, const struct sockaddr *sa,
394 const unsigned int sa_size)
395{
396 unsigned int bufsize = sa_size;
397
398 /* make sure we don't overrun our union */
399 if (sizeof(li->hostaddr) < sa_size)
400 bufsize = sizeof(li->hostaddr);
401
402 memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
andre2ff7b5d2000-06-03 14:57:40 +0000403}
404
andre2ff7b5d2000-06-03 14:57:40 +0000405
andre61e67252000-06-04 17:07:49 +0000406/**
407 ** login_write: Call low-level recording functions based on autoconf
408 ** results
409 **/
andre2ff7b5d2000-06-03 14:57:40 +0000410int
andre61e67252000-06-04 17:07:49 +0000411login_write (struct logininfo *li)
412{
andre2ff7b5d2000-06-03 14:57:40 +0000413 if ((int)geteuid() != 0) {
414 log("Attempt to write login records by non-root user (aborting)");
415 return 1;
416 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000417
andre2ff7b5d2000-06-03 14:57:40 +0000418 /* set the timestamp */
419 login_set_current_time(li);
420#ifdef USE_LOGIN
421 syslogin_write_entry(li);
422#endif
423#ifdef USE_LASTLOG
424 if (li->type == LTYPE_LOGIN) {
425 lastlog_write_entry(li);
426 }
427#endif
428#ifdef USE_UTMP
429 utmp_write_entry(li);
430#endif
431#ifdef USE_WTMP
432 wtmp_write_entry(li);
433#endif
434#ifdef USE_UTMPX
435 utmpx_write_entry(li);
436#endif
437#ifdef USE_WTMPX
438 wtmpx_write_entry(li);
439#endif
440 return 0;
441}
442
andre2ff7b5d2000-06-03 14:57:40 +0000443/**
andre61e67252000-06-04 17:07:49 +0000444 ** getlast_entry: Call low-level functions to retrieve the last login
445 ** time.
andre2ff7b5d2000-06-03 14:57:40 +0000446 **/
447
andre61e67252000-06-04 17:07:49 +0000448/* take the uid in li and return the last login time */
449int
450getlast_entry(struct logininfo *li)
451{
452#ifdef USE_LASTLOG
Damien Miller53c5d462000-06-28 00:50:50 +1000453 return(lastlog_get_entry(li));
Damien Millerdd47aa22000-06-27 11:18:27 +1000454#else /* !USE_LASTLOG */
andre61e67252000-06-04 17:07:49 +0000455
Damien Millerdd47aa22000-06-27 11:18:27 +1000456#ifdef DISABLE_LASTLOG
andreecaabf12000-06-12 22:21:44 +0000457 /* On some systems we shouldn't even try to obtain last login
458 * time, e.g. AIX */
459 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +1000460# else /* DISABLE_LASTLOG */
andre61e67252000-06-04 17:07:49 +0000461 /* Try to retrieve the last login time from wtmp */
Damien Millerdd47aa22000-06-27 11:18:27 +1000462# if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
andre61e67252000-06-04 17:07:49 +0000463 /* retrieve last login time from utmp */
Damien Millerdd47aa22000-06-27 11:18:27 +1000464 return (wtmp_get_entry(li));
465# else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */
andre61e67252000-06-04 17:07:49 +0000466 /* If wtmp isn't available, try wtmpx */
Damien Millerdd47aa22000-06-27 11:18:27 +1000467# if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
andre61e67252000-06-04 17:07:49 +0000468 /* retrieve last login time from utmpx */
Damien Millerdd47aa22000-06-27 11:18:27 +1000469 return (wtmpx_get_entry(li));
470# else
andre61e67252000-06-04 17:07:49 +0000471 /* Give up: No means of retrieving last login time */
472 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +1000473# endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
474# endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
475# endif /* DISABLE_LASTLOG */
476#endif /* USE_LASTLOG */
andre61e67252000-06-04 17:07:49 +0000477}
478
479
480
andre2ff7b5d2000-06-03 14:57:40 +0000481/*
andre61e67252000-06-04 17:07:49 +0000482 * 'line' string utility functions
483 *
484 * These functions process the 'line' string into one of three forms:
485 *
andre2ff7b5d2000-06-03 14:57:40 +0000486 * 1. The full filename (including '/dev')
487 * 2. The stripped name (excluding '/dev')
andre61e67252000-06-04 17:07:49 +0000488 * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
489 * /dev/pts/1 -> ts/1 )
andre2ff7b5d2000-06-03 14:57:40 +0000490 *
491 * Form 3 is used on some systems to identify a .tmp.? entry when
492 * attempting to remove it. Typically both addition and removal is
andre61e67252000-06-04 17:07:49 +0000493 * performed by one application - say, sshd - so as long as the choice
494 * uniquely identifies a terminal it's ok.
andre2ff7b5d2000-06-03 14:57:40 +0000495 */
496
497
andre61e67252000-06-04 17:07:49 +0000498/* line_fullname(): add the leading '/dev/' if it doesn't exist make
499 * sure dst has enough space, if not just copy src (ugh) */
andre2ff7b5d2000-06-03 14:57:40 +0000500char *
andre61e67252000-06-04 17:07:49 +0000501line_fullname(char *dst, const char *src, int dstsize)
502{
andre2ff7b5d2000-06-03 14:57:40 +0000503 memset(dst, '\0', dstsize);
504 if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))
505 strlcpy(dst, src, dstsize);
506 else {
Damien Miller1a132252000-06-13 21:23:17 +1000507 strlcpy(dst, "/dev/", dstsize);
andre2ff7b5d2000-06-03 14:57:40 +0000508 strlcat(dst, src, dstsize);
509 }
510 return dst;
511}
512
andre61e67252000-06-04 17:07:49 +0000513/* line_stripname(): strip the leading '/dev' if it exists, return dst */
andre2ff7b5d2000-06-03 14:57:40 +0000514char *
andre61e67252000-06-04 17:07:49 +0000515line_stripname(char *dst, const char *src, int dstsize)
516{
andre2ff7b5d2000-06-03 14:57:40 +0000517 memset(dst, '\0', dstsize);
518 if (strncmp(src, "/dev/", 5) == 0)
519 strlcpy(dst, &src[5], dstsize);
520 else
521 strlcpy(dst, src, dstsize);
522 return dst;
andre61e67252000-06-04 17:07:49 +0000523}
524
andre61e67252000-06-04 17:07:49 +0000525/* line_abbrevname(): Return the abbreviated (usually four-character)
526 * form of the line (Just use the last <dstsize> characters of the
527 * full name.)
528 *
529 * NOTE: use strncpy because we do NOT necessarily want zero
530 * termination */
andre2ff7b5d2000-06-03 14:57:40 +0000531char *
Damien Millerdd47aa22000-06-27 11:18:27 +1000532line_abbrevname(char *dst, const char *src, int dstsize)
533{
534 size_t len;
535
andre2ff7b5d2000-06-03 14:57:40 +0000536 memset(dst, '\0', dstsize);
Damien Millerdd47aa22000-06-27 11:18:27 +1000537
Damien Miller8e81ed32000-07-01 13:17:42 +1000538 /* Always skip prefix if present */
539 if (strncmp(src, "/dev/", 5) == 0)
540 src += 5;
541
Damien Millerdd47aa22000-06-27 11:18:27 +1000542 len = strlen(src);
543
Damien Miller8e81ed32000-07-01 13:17:42 +1000544 if (len > 0) {
545 if (((int)len - dstsize) > 0)
546 src += ((int)len - dstsize);
547
548 /* note: _don't_ change this to strlcpy */
549 strncpy(dst, src, (size_t)dstsize);
Damien Millerdd47aa22000-06-27 11:18:27 +1000550 }
551
andre2ff7b5d2000-06-03 14:57:40 +0000552 return dst;
553}
554
andre2ff7b5d2000-06-03 14:57:40 +0000555/**
556 ** utmp utility functions
andre61e67252000-06-04 17:07:49 +0000557 **
558 ** These functions manipulate struct utmp, taking system differences
559 ** into account.
andre2ff7b5d2000-06-03 14:57:40 +0000560 **/
561
562#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
563
andre2ff7b5d2000-06-03 14:57:40 +0000564/* build the utmp structure */
565void
andre61e67252000-06-04 17:07:49 +0000566set_utmp_time(struct logininfo *li, struct utmp *ut)
567{
Damien Millerdd47aa22000-06-27 11:18:27 +1000568# ifdef HAVE_TV_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +0000569 ut->ut_tv.tv_sec = li->tv_sec;
570 ut->ut_tv.tv_usec = li->tv_usec;
Damien Millerdd47aa22000-06-27 11:18:27 +1000571# else
andre2ff7b5d2000-06-03 14:57:40 +0000572# ifdef HAVE_TIME_IN_UTMP
573 ut->ut_time = li->tv_sec;
574# endif
Damien Millerdd47aa22000-06-27 11:18:27 +1000575# endif
andre2ff7b5d2000-06-03 14:57:40 +0000576}
577
578void
579construct_utmp(struct logininfo *li,
andre61e67252000-06-04 17:07:49 +0000580 struct utmp *ut)
581{
andre2ff7b5d2000-06-03 14:57:40 +0000582 memset(ut, '\0', sizeof(struct utmp));
andre6bb92372000-06-19 08:20:03 +0000583
584 /* First fill out fields used for both logins and logouts */
585
Damien Millerdd47aa22000-06-27 11:18:27 +1000586# ifdef HAVE_ID_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +0000587 line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
Damien Millerdd47aa22000-06-27 11:18:27 +1000588# endif
andre2ff7b5d2000-06-03 14:57:40 +0000589
Damien Millerdd47aa22000-06-27 11:18:27 +1000590# ifdef HAVE_TYPE_IN_UTMP
andre6bb92372000-06-19 08:20:03 +0000591 /* This is done here to keep utmp constants out of struct logininfo */
andre2ff7b5d2000-06-03 14:57:40 +0000592 switch (li->type) {
593 case LTYPE_LOGIN:
594 ut->ut_type = USER_PROCESS;
595 break;
596 case LTYPE_LOGOUT:
597 ut->ut_type = DEAD_PROCESS;
598 break;
599 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000600# endif
andre6bb92372000-06-19 08:20:03 +0000601 set_utmp_time(li, ut);
andre2ff7b5d2000-06-03 14:57:40 +0000602
andre6bb92372000-06-19 08:20:03 +0000603 line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
Damien Millerdd47aa22000-06-27 11:18:27 +1000604
605# ifdef HAVE_PID_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +0000606 ut->ut_pid = li->pid;
Damien Millerdd47aa22000-06-27 11:18:27 +1000607# endif
andre6bb92372000-06-19 08:20:03 +0000608
609 /* If we're logging out, leave all other fields blank */
610 if (li->type == LTYPE_LOGOUT)
611 return;
612
Damien Millerdd47aa22000-06-27 11:18:27 +1000613 /*
614 * These fields are only used when logging in, and are blank
615 * for logouts.
616 */
andre6bb92372000-06-19 08:20:03 +0000617
618 /* Use strncpy because we don't necessarily want null termination */
619 strncpy(ut->ut_user, li->username, MIN_SIZEOF(ut->ut_user, li->username));
Damien Millerdd47aa22000-06-27 11:18:27 +1000620# ifdef HAVE_HOST_IN_UTMP
andre6bb92372000-06-19 08:20:03 +0000621 strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
Damien Millerdd47aa22000-06-27 11:18:27 +1000622# endif
623# ifdef HAVE_ADDR_IN_UTMP
andre61e67252000-06-04 17:07:49 +0000624 /* this is just a 32-bit IP address */
625 if (li->hostaddr.sa.sa_family == AF_INET)
626 ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
Damien Millerdd47aa22000-06-27 11:18:27 +1000627# endif
andre61e67252000-06-04 17:07:49 +0000628}
Damien Millerdd47aa22000-06-27 11:18:27 +1000629#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
andre61e67252000-06-04 17:07:49 +0000630
andre2ff7b5d2000-06-03 14:57:40 +0000631/**
632 ** utmpx utility functions
andre61e67252000-06-04 17:07:49 +0000633 **
634 ** These functions manipulate struct utmpx, accounting for system
635 ** variations.
andre2ff7b5d2000-06-03 14:57:40 +0000636 **/
637
638#if defined(USE_UTMPX) || defined (USE_WTMPX)
andre2ff7b5d2000-06-03 14:57:40 +0000639/* build the utmpx structure */
640void
andre61e67252000-06-04 17:07:49 +0000641set_utmpx_time(struct logininfo *li, struct utmpx *utx)
642{
Damien Millerdd47aa22000-06-27 11:18:27 +1000643# ifdef HAVE_TV_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000644 utx->ut_tv.tv_sec = li->tv_sec;
645 utx->ut_tv.tv_usec = li->tv_usec;
Damien Millerdd47aa22000-06-27 11:18:27 +1000646# else /* HAVE_TV_IN_UTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000647# ifdef HAVE_TIME_IN_UTMPX
648 utx->ut_time = li->tv_sec;
Damien Millerdd47aa22000-06-27 11:18:27 +1000649# endif /* HAVE_TIME_IN_UTMPX */
650# endif /* HAVE_TV_IN_UTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000651}
652
andre61e67252000-06-04 17:07:49 +0000653void
654construct_utmpx(struct logininfo *li, struct utmpx *utx)
655{
656 memset(utx, '\0', sizeof(struct utmpx));
Damien Miller8e81ed32000-07-01 13:17:42 +1000657# ifdef HAVE_ID_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000658 line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
Damien Miller8e81ed32000-07-01 13:17:42 +1000659# endif
andre2ff7b5d2000-06-03 14:57:40 +0000660
661 /* this is done here to keep utmp constants out of loginrec.h */
662 switch (li->type) {
663 case LTYPE_LOGIN:
664 utx->ut_type = USER_PROCESS;
665 break;
666 case LTYPE_LOGOUT:
667 utx->ut_type = DEAD_PROCESS;
668 break;
669 }
andre2ff7b5d2000-06-03 14:57:40 +0000670 line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
andre2ff7b5d2000-06-03 14:57:40 +0000671 set_utmpx_time(li, utx);
andre6bb92372000-06-19 08:20:03 +0000672 utx->ut_pid = li->pid;
673
674 if (li->type == LTYPE_LOGOUT)
675 return;
676
Damien Millerdd47aa22000-06-27 11:18:27 +1000677 /*
678 * These fields are only used when logging in, and are blank
679 * for logouts.
680 */
andre6bb92372000-06-19 08:20:03 +0000681
682 /* strncpy(): Don't necessarily want null termination */
683 strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
Damien Millerdd47aa22000-06-27 11:18:27 +1000684# ifdef HAVE_HOST_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +0000685 strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
Damien Millerdd47aa22000-06-27 11:18:27 +1000686# endif
687# ifdef HAVE_ADDR_IN_UTMPX
andre61e67252000-06-04 17:07:49 +0000688 /* FIXME: (ATL) not supported yet */
Damien Millerdd47aa22000-06-27 11:18:27 +1000689# endif
690# ifdef HAVE_SYSLEN_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +0000691 /* ut_syslen is the length of the utx_host string */
692 utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
Damien Millerdd47aa22000-06-27 11:18:27 +1000693# endif
andre61e67252000-06-04 17:07:49 +0000694}
Damien Millerdd47aa22000-06-27 11:18:27 +1000695#endif /* USE_UTMPX || USE_WTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000696
697/**
andre61e67252000-06-04 17:07:49 +0000698 ** Low-level utmp functions
andre2ff7b5d2000-06-03 14:57:40 +0000699 **/
700
701/* FIXME: (ATL) utmp_write_direct needs testing */
andre2ff7b5d2000-06-03 14:57:40 +0000702#ifdef USE_UTMP
703
andre2ff7b5d2000-06-03 14:57:40 +0000704/* if we can, use pututline() etc. */
Damien Millerdd47aa22000-06-27 11:18:27 +1000705# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
706 defined(HAVE_PUTUTLINE)
andre2ff7b5d2000-06-03 14:57:40 +0000707# define UTMP_USE_LIBRARY
Damien Millerdd47aa22000-06-27 11:18:27 +1000708# endif
andre2ff7b5d2000-06-03 14:57:40 +0000709
710
711/* write a utmp entry with the system's help (pututline() and pals) */
Damien Millerdd47aa22000-06-27 11:18:27 +1000712# ifdef UTMP_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000713static int
andre61e67252000-06-04 17:07:49 +0000714utmp_write_library(struct logininfo *li, struct utmp *ut)
715{
andre2ff7b5d2000-06-03 14:57:40 +0000716 setutent();
717 pututline(ut);
718
Damien Millerdd47aa22000-06-27 11:18:27 +1000719# ifdef HAVE_ENDUTENT
andre2ff7b5d2000-06-03 14:57:40 +0000720 endutent();
Damien Millerdd47aa22000-06-27 11:18:27 +1000721# endif
andre2ff7b5d2000-06-03 14:57:40 +0000722 return 1;
andre61e67252000-06-04 17:07:49 +0000723}
Damien Millerdd47aa22000-06-27 11:18:27 +1000724# else /* UTMP_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000725
726/* write a utmp entry direct to the file */
andre61e67252000-06-04 17:07:49 +0000727/* This is a slightly modification of code in OpenBSD's login.c */
andre2ff7b5d2000-06-03 14:57:40 +0000728static int
andre61e67252000-06-04 17:07:49 +0000729utmp_write_direct(struct logininfo *li, struct utmp *ut)
730{
andre2ff7b5d2000-06-03 14:57:40 +0000731 struct utmp old_ut;
732 register int fd;
733 int tty;
734
andre6bb92372000-06-19 08:20:03 +0000735 /* FIXME: (ATL) ttyslot() needs local implementation */
andre2ff7b5d2000-06-03 14:57:40 +0000736 tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
737
738 if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
739 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
740 /*
741 * Prevent luser from zero'ing out ut_host.
742 * If the new ut_line is empty but the old one is not
743 * and ut_line and ut_name match, preserve the old ut_line.
744 */
Damien Miller53c5d462000-06-28 00:50:50 +1000745 if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
746 (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
747 (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
748 (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
andre2ff7b5d2000-06-03 14:57:40 +0000749 (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
Damien Miller53c5d462000-06-28 00:50:50 +1000750 }
751
andre2ff7b5d2000-06-03 14:57:40 +0000752 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
Damien Miller53c5d462000-06-28 00:50:50 +1000753 if (atomicio(write, fd, ut, sizeof(ut)) != sizeof(ut))
andre2ff7b5d2000-06-03 14:57:40 +0000754 log("utmp_write_direct: error writing %s: %s",
andre6bb92372000-06-19 08:20:03 +0000755 UTMP_FILE, strerror(errno));
andre2ff7b5d2000-06-03 14:57:40 +0000756
757 (void)close(fd);
758 return 1;
Damien Miller53c5d462000-06-28 00:50:50 +1000759 } else {
andre2ff7b5d2000-06-03 14:57:40 +0000760 return 0;
Damien Miller53c5d462000-06-28 00:50:50 +1000761 }
andre61e67252000-06-04 17:07:49 +0000762}
Damien Millerdd47aa22000-06-27 11:18:27 +1000763# endif /* UTMP_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000764
765static int
andre61e67252000-06-04 17:07:49 +0000766utmp_perform_login(struct logininfo *li)
767{
andre2ff7b5d2000-06-03 14:57:40 +0000768 struct utmp ut;
769
770 construct_utmp(li, &ut);
Damien Millerdd47aa22000-06-27 11:18:27 +1000771# ifdef UTMP_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000772 if (!utmp_write_library(li, &ut)) {
andre6bb92372000-06-19 08:20:03 +0000773 log("utmp_perform_login: utmp_write_library() failed");
andre2ff7b5d2000-06-03 14:57:40 +0000774 return 0;
775 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000776# else
andre2ff7b5d2000-06-03 14:57:40 +0000777 if (!utmp_write_direct(li, &ut)) {
778 log("utmp_perform_login: utmp_write_direct() failed");
779 return 0;
780 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000781# endif
andre2ff7b5d2000-06-03 14:57:40 +0000782 return 1;
andre61e67252000-06-04 17:07:49 +0000783}
andre2ff7b5d2000-06-03 14:57:40 +0000784
785
786static int
andre61e67252000-06-04 17:07:49 +0000787utmp_perform_logout(struct logininfo *li)
788{
andre2ff7b5d2000-06-03 14:57:40 +0000789 struct utmp ut;
790
andre6bb92372000-06-19 08:20:03 +0000791 construct_utmp(li, &ut);
Damien Millerdd47aa22000-06-27 11:18:27 +1000792# ifdef UTMP_USE_LIBRARY
andre6bb92372000-06-19 08:20:03 +0000793 if (!utmp_write_library(li, &ut)) {
794 log("utmp_perform_logout: utmp_write_library() failed");
795 return 0;
796 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000797# else
andre6bb92372000-06-19 08:20:03 +0000798 if (!utmp_write_direct(li, &ut)) {
799 log("utmp_perform_logout: utmp_write_direct() failed");
800 return 0;
801 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000802# endif
andre2ff7b5d2000-06-03 14:57:40 +0000803 return 1;
andre61e67252000-06-04 17:07:49 +0000804}
andre2ff7b5d2000-06-03 14:57:40 +0000805
806
807int
andre61e67252000-06-04 17:07:49 +0000808utmp_write_entry(struct logininfo *li)
809{
andre2ff7b5d2000-06-03 14:57:40 +0000810 switch(li->type) {
811 case LTYPE_LOGIN:
812 return utmp_perform_login(li);
813
814 case LTYPE_LOGOUT:
815 return utmp_perform_logout(li);
816
817 default:
818 log("utmp_write_entry: invalid type field");
819 return 0;
820 }
andre61e67252000-06-04 17:07:49 +0000821}
Damien Millerdd47aa22000-06-27 11:18:27 +1000822#endif /* USE_UTMP */
andre2ff7b5d2000-06-03 14:57:40 +0000823
824
825/**
andre61e67252000-06-04 17:07:49 +0000826 ** Low-level utmpx functions
andre2ff7b5d2000-06-03 14:57:40 +0000827 **/
828
829/* not much point if we don't want utmpx entries */
830#ifdef USE_UTMPX
831
andre2ff7b5d2000-06-03 14:57:40 +0000832/* if we have the wherewithall, use pututxline etc. */
Damien Millerdd47aa22000-06-27 11:18:27 +1000833# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
834 defined(HAVE_PUTUTXLINE)
andre2ff7b5d2000-06-03 14:57:40 +0000835# define UTMPX_USE_LIBRARY
Damien Millerdd47aa22000-06-27 11:18:27 +1000836# endif
andre2ff7b5d2000-06-03 14:57:40 +0000837
838
839/* write a utmpx entry with the system's help (pututxline() and pals) */
Damien Millerdd47aa22000-06-27 11:18:27 +1000840# ifdef UTMPX_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000841static int
andre61e67252000-06-04 17:07:49 +0000842utmpx_write_library(struct logininfo *li, struct utmpx *utx)
843{
andre2ff7b5d2000-06-03 14:57:40 +0000844 setutxent();
845 pututxline(utx);
846
Damien Millerdd47aa22000-06-27 11:18:27 +1000847# ifdef HAVE_ENDUTXENT
andre2ff7b5d2000-06-03 14:57:40 +0000848 endutxent();
Damien Millerdd47aa22000-06-27 11:18:27 +1000849# endif
andre2ff7b5d2000-06-03 14:57:40 +0000850 return 1;
andre61e67252000-06-04 17:07:49 +0000851}
andre2ff7b5d2000-06-03 14:57:40 +0000852
Damien Millerdd47aa22000-06-27 11:18:27 +1000853# else /* UTMPX_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000854
855/* write a utmp entry direct to the file */
856static int
andre61e67252000-06-04 17:07:49 +0000857utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
858{
andre2ff7b5d2000-06-03 14:57:40 +0000859 log("utmpx_write_direct: not implemented!");
860 return 0;
andre61e67252000-06-04 17:07:49 +0000861}
Damien Millerdd47aa22000-06-27 11:18:27 +1000862# endif /* UTMPX_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000863
864static int
andre61e67252000-06-04 17:07:49 +0000865utmpx_perform_login(struct logininfo *li)
866{
andre2ff7b5d2000-06-03 14:57:40 +0000867 struct utmpx utx;
868
869 construct_utmpx(li, &utx);
Damien Millerdd47aa22000-06-27 11:18:27 +1000870# ifdef UTMPX_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000871 if (!utmpx_write_library(li, &utx)) {
872 log("utmpx_perform_login: utmp_write_library() failed");
873 return 0;
874 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000875# else
andre2ff7b5d2000-06-03 14:57:40 +0000876 if (!utmpx_write_direct(li, &ut)) {
877 log("utmpx_perform_login: utmp_write_direct() failed");
878 return 0;
879 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000880# endif
andre2ff7b5d2000-06-03 14:57:40 +0000881 return 1;
andre61e67252000-06-04 17:07:49 +0000882}
andre2ff7b5d2000-06-03 14:57:40 +0000883
884
885static int
andre61e67252000-06-04 17:07:49 +0000886utmpx_perform_logout(struct logininfo *li)
887{
andre2ff7b5d2000-06-03 14:57:40 +0000888 struct utmpx utx;
889
890 memset(&utx, '\0', sizeof(utx));
891 set_utmpx_time(li, &utx);
892 line_stripname(utx.ut_line, li->line, sizeof(utx.ut_line));
Damien Millerdd47aa22000-06-27 11:18:27 +1000893# ifdef HAVE_ID_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000894 line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
Damien Millerdd47aa22000-06-27 11:18:27 +1000895# endif
896# ifdef HAVE_TYPE_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000897 utx.ut_type = DEAD_PROCESS;
Damien Millerdd47aa22000-06-27 11:18:27 +1000898# endif
andre2ff7b5d2000-06-03 14:57:40 +0000899
Damien Millerdd47aa22000-06-27 11:18:27 +1000900# ifdef UTMPX_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000901 utmpx_write_library(li, &utx);
Damien Millerdd47aa22000-06-27 11:18:27 +1000902# else
andre2ff7b5d2000-06-03 14:57:40 +0000903 utmpx_write_direct(li, &utx);
Damien Millerdd47aa22000-06-27 11:18:27 +1000904# endif
andre2ff7b5d2000-06-03 14:57:40 +0000905 return 1;
andre61e67252000-06-04 17:07:49 +0000906}
andre2ff7b5d2000-06-03 14:57:40 +0000907
andre2ff7b5d2000-06-03 14:57:40 +0000908int
andre61e67252000-06-04 17:07:49 +0000909utmpx_write_entry(struct logininfo *li)
910{
andre2ff7b5d2000-06-03 14:57:40 +0000911 switch(li->type) {
912 case LTYPE_LOGIN:
913 return utmpx_perform_login(li);
914 case LTYPE_LOGOUT:
915 return utmpx_perform_logout(li);
916 default:
917 log("utmpx_write_entry: invalid type field");
918 return 0;
919 }
andre61e67252000-06-04 17:07:49 +0000920}
Damien Millerdd47aa22000-06-27 11:18:27 +1000921#endif /* USE_UTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000922
923
924/**
andre61e67252000-06-04 17:07:49 +0000925 ** Low-level wtmp functions
andre2ff7b5d2000-06-03 14:57:40 +0000926 **/
927
928#ifdef USE_WTMP
929
andre2ff7b5d2000-06-03 14:57:40 +0000930/* write a wtmp entry direct to the end of the file */
andre61e67252000-06-04 17:07:49 +0000931/* This is a slight modification of code in OpenBSD's logwtmp.c */
andre2ff7b5d2000-06-03 14:57:40 +0000932static int
andre61e67252000-06-04 17:07:49 +0000933wtmp_write(struct logininfo *li, struct utmp *ut)
934{
andre2ff7b5d2000-06-03 14:57:40 +0000935 struct stat buf;
936 int fd, ret = 1;
937
938 if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
939 log("wtmp_write: problem writing %s: %s",
940 WTMP_FILE, strerror(errno));
941 return 0;
942 }
andre61e67252000-06-04 17:07:49 +0000943 if (fstat(fd, &buf) == 0)
Damien Miller53c5d462000-06-28 00:50:50 +1000944 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
andre2ff7b5d2000-06-03 14:57:40 +0000945 ftruncate(fd, buf.st_size);
946 log("wtmp_write: problem writing %s: %s",
947 WTMP_FILE, strerror(errno));
948 ret = 0;
949 }
950 (void)close(fd);
andre2ff7b5d2000-06-03 14:57:40 +0000951 return ret;
andre61e67252000-06-04 17:07:49 +0000952}
andre2ff7b5d2000-06-03 14:57:40 +0000953
andre2ff7b5d2000-06-03 14:57:40 +0000954static int
Damien Millerdd47aa22000-06-27 11:18:27 +1000955wtmp_perform_login(struct logininfo *li)
956{
andre2ff7b5d2000-06-03 14:57:40 +0000957 struct utmp ut;
958
959 construct_utmp(li, &ut);
960 return wtmp_write(li, &ut);
andre61e67252000-06-04 17:07:49 +0000961}
andre2ff7b5d2000-06-03 14:57:40 +0000962
963
964static int
andre61e67252000-06-04 17:07:49 +0000965wtmp_perform_logout(struct logininfo *li)
966{
andre2ff7b5d2000-06-03 14:57:40 +0000967 struct utmp ut;
968
969 construct_utmp(li, &ut);
andre2ff7b5d2000-06-03 14:57:40 +0000970 return wtmp_write(li, &ut);
andre61e67252000-06-04 17:07:49 +0000971}
andre2ff7b5d2000-06-03 14:57:40 +0000972
973
974int
andre61e67252000-06-04 17:07:49 +0000975wtmp_write_entry(struct logininfo *li)
976{
andre2ff7b5d2000-06-03 14:57:40 +0000977 switch(li->type) {
978 case LTYPE_LOGIN:
979 return wtmp_perform_login(li);
980 case LTYPE_LOGOUT:
981 return wtmp_perform_logout(li);
982 default:
983 log("wtmp_write_entry: invalid type field");
984 return 0;
985 }
andre61e67252000-06-04 17:07:49 +0000986}
andre2ff7b5d2000-06-03 14:57:40 +0000987
988
andre6bb92372000-06-19 08:20:03 +0000989/* Notes on fetching login data from wtmp/wtmpx
990 *
991 * Logouts are usually recorded with (amongst other things) a blank
992 * username on a given tty line. However, some systems (HP-UX is one)
993 * leave all fields set, but change the ut_type field to DEAD_PROCESS.
994 *
995 * Since we're only looking for logins here, we know that the username
996 * must be set correctly. On systems that leave it in, we check for
997 * ut_type==USER_PROCESS (indicating a login.)
998 *
999 * Portability: Some systems may set something other than USER_PROCESS
1000 * to indicate a login process. I don't know of any as I write. Also,
1001 * it's possible that some systems may both leave the username in
1002 * place and not have ut_type.
1003 */
1004
andre6bb92372000-06-19 08:20:03 +00001005/* return true if this wtmp entry indicates a login */
1006static int
1007wtmp_islogin(struct logininfo *li, struct utmp *ut)
1008{
Damien Millerdd47aa22000-06-27 11:18:27 +10001009 if (strncmp(li->username, ut->ut_user,
1010 MIN_SIZEOF(li->username, ut->ut_user)) == 0) {
1011# ifdef HAVE_TYPE_IN_UTMP
andre6bb92372000-06-19 08:20:03 +00001012 if (ut->ut_type & USER_PROCESS)
1013 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001014# else
andre6bb92372000-06-19 08:20:03 +00001015 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001016# endif
andre6bb92372000-06-19 08:20:03 +00001017 }
1018 return 0;
1019}
1020
andre2ff7b5d2000-06-03 14:57:40 +00001021int
andre61e67252000-06-04 17:07:49 +00001022wtmp_get_entry(struct logininfo *li)
1023{
andre2ff7b5d2000-06-03 14:57:40 +00001024 struct stat st;
1025 struct utmp ut;
andre6bb92372000-06-19 08:20:03 +00001026 int fd, found=0;
1027
1028 /* Clear the time entries in our logininfo */
1029 li->tv_sec = li->tv_usec = 0;
andre2ff7b5d2000-06-03 14:57:40 +00001030
1031 if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1032 log("wtmp_get_entry: problem opening %s: %s",
1033 WTMP_FILE, strerror(errno));
1034 return 0;
1035 }
andre61e67252000-06-04 17:07:49 +00001036 if (fstat(fd, &st) != 0) {
andre2ff7b5d2000-06-03 14:57:40 +00001037 log("wtmp_get_entry: couldn't stat %s: %s",
1038 WTMP_FILE, strerror(errno));
1039 close(fd);
1040 return 0;
1041 }
andre2ff7b5d2000-06-03 14:57:40 +00001042
andre6bb92372000-06-19 08:20:03 +00001043 /* Seek to the start of the last struct utmp */
1044 if (lseek(fd, (off_t)(0-sizeof(struct utmp)), SEEK_END) == -1) {
1045 /* Looks like we've got a fresh wtmp file */
1046 close(fd);
1047 return 0;
1048 }
1049
1050 while (!found) {
Damien Miller53c5d462000-06-28 00:50:50 +10001051 if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
andre2ff7b5d2000-06-03 14:57:40 +00001052 log("wtmp_get_entry: read of %s failed: %s",
1053 WTMP_FILE, strerror(errno));
1054 close (fd);
1055 return 0;
1056 }
andre6bb92372000-06-19 08:20:03 +00001057 if ( wtmp_islogin(li, &ut) ) {
1058 found = 1;
1059 /* We've already checked for a time in struct
1060 * utmp, in login_getlast(). */
Damien Millerdd47aa22000-06-27 11:18:27 +10001061# ifdef HAVE_TIME_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +00001062 li->tv_sec = ut.ut_time;
Damien Millerdd47aa22000-06-27 11:18:27 +10001063# else
andre2ff7b5d2000-06-03 14:57:40 +00001064# if HAVE_TV_IN_UTMP
1065 li->tv_sec = ut.ut_tv.tv_sec;
1066# endif
Damien Millerdd47aa22000-06-27 11:18:27 +10001067# endif
andre6bb92372000-06-19 08:20:03 +00001068 line_fullname(li->line, ut.ut_line,
1069 MIN_SIZEOF(li->line, ut.ut_line));
Damien Millerdd47aa22000-06-27 11:18:27 +10001070# ifdef HAVE_HOST_IN_UTMP
andre6bb92372000-06-19 08:20:03 +00001071 strlcpy(li->hostname, ut.ut_host,
1072 MIN_SIZEOF(li->hostname, ut.ut_host));
Damien Millerdd47aa22000-06-27 11:18:27 +10001073# endif
andre6bb92372000-06-19 08:20:03 +00001074 continue;
andre2ff7b5d2000-06-03 14:57:40 +00001075 }
andre6bb92372000-06-19 08:20:03 +00001076 /* Seek back 2 x struct utmp */
andre2ff7b5d2000-06-03 14:57:40 +00001077 if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) {
andre6bb92372000-06-19 08:20:03 +00001078 /* We've found the start of the file, so quit */
andre2ff7b5d2000-06-03 14:57:40 +00001079 close (fd);
1080 return 0;
1081 }
andre6bb92372000-06-19 08:20:03 +00001082 }
1083
1084 /* We found an entry. Tidy up and return */
1085 close(fd);
andre2ff7b5d2000-06-03 14:57:40 +00001086 return 1;
andre61e67252000-06-04 17:07:49 +00001087}
Damien Millerdd47aa22000-06-27 11:18:27 +10001088# endif /* USE_WTMP */
andre2ff7b5d2000-06-03 14:57:40 +00001089
1090
1091/**
andre61e67252000-06-04 17:07:49 +00001092 ** Low-level wtmpx functions
andre2ff7b5d2000-06-03 14:57:40 +00001093 **/
1094
1095#ifdef USE_WTMPX
andre2ff7b5d2000-06-03 14:57:40 +00001096/* write a wtmpx entry direct to the end of the file */
andre61e67252000-06-04 17:07:49 +00001097/* This is a slight modification of code in OpenBSD's logwtmp.c */
andre2ff7b5d2000-06-03 14:57:40 +00001098static int
andre61e67252000-06-04 17:07:49 +00001099wtmpx_write(struct logininfo *li, struct utmpx *utx)
1100{
andre2ff7b5d2000-06-03 14:57:40 +00001101 struct stat buf;
1102 int fd, ret = 1;
1103
1104 if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1105 log("wtmpx_write: problem opening %s: %s",
1106 WTMPX_FILE, strerror(errno));
1107 return 0;
1108 }
1109
1110 if (fstat(fd, &buf) == 0)
Damien Miller53c5d462000-06-28 00:50:50 +10001111 if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
andre2ff7b5d2000-06-03 14:57:40 +00001112 ftruncate(fd, buf.st_size);
1113 log("wtmpx_write: problem writing %s: %s",
1114 WTMPX_FILE, strerror(errno));
1115 ret = 0;
1116 }
1117 (void)close(fd);
1118
1119 return ret;
andre61e67252000-06-04 17:07:49 +00001120}
andre2ff7b5d2000-06-03 14:57:40 +00001121
1122
1123static int
andre61e67252000-06-04 17:07:49 +00001124wtmpx_perform_login(struct logininfo *li)
1125{
andre2ff7b5d2000-06-03 14:57:40 +00001126 struct utmpx utx;
1127
1128 construct_utmpx(li, &utx);
1129 return wtmpx_write(li, &utx);
andre61e67252000-06-04 17:07:49 +00001130}
andre2ff7b5d2000-06-03 14:57:40 +00001131
1132
1133static int
andre61e67252000-06-04 17:07:49 +00001134wtmpx_perform_logout(struct logininfo *li)
1135{
andre2ff7b5d2000-06-03 14:57:40 +00001136 struct utmpx utx;
1137
1138 construct_utmpx(li, &utx);
andre2ff7b5d2000-06-03 14:57:40 +00001139 return wtmpx_write(li, &utx);
andre61e67252000-06-04 17:07:49 +00001140}
andre2ff7b5d2000-06-03 14:57:40 +00001141
1142
1143int
andre61e67252000-06-04 17:07:49 +00001144wtmpx_write_entry(struct logininfo *li)
1145{
andre2ff7b5d2000-06-03 14:57:40 +00001146 switch(li->type) {
1147 case LTYPE_LOGIN:
1148 return wtmpx_perform_login(li);
1149 case LTYPE_LOGOUT:
1150 return wtmpx_perform_logout(li);
1151 default:
1152 log("wtmpx_write_entry: invalid type field");
1153 return 0;
1154 }
andre61e67252000-06-04 17:07:49 +00001155}
andre2ff7b5d2000-06-03 14:57:40 +00001156
andre6bb92372000-06-19 08:20:03 +00001157/* Please see the notes above wtmp_islogin() for information about the
1158 next two functions */
1159
1160/* Return true if this wtmpx entry indicates a login */
1161static int
1162wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1163{
1164 if ( strncmp(li->username, utx->ut_user,
Damien Millerdd47aa22000-06-27 11:18:27 +10001165 MIN_SIZEOF(li->username, utx->ut_user)) == 0 ) {
1166# ifdef HAVE_TYPE_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +00001167 if (utx->ut_type == USER_PROCESS)
1168 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001169# else
andre6bb92372000-06-19 08:20:03 +00001170 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001171# endif
andre6bb92372000-06-19 08:20:03 +00001172 }
1173 return 0;
1174}
1175
andre2ff7b5d2000-06-03 14:57:40 +00001176
1177int
andre61e67252000-06-04 17:07:49 +00001178wtmpx_get_entry(struct logininfo *li)
1179{
andre2ff7b5d2000-06-03 14:57:40 +00001180 struct stat st;
1181 struct utmpx utx;
andre6bb92372000-06-19 08:20:03 +00001182 int fd, found=0;
1183
1184 /* Clear the time entries */
1185 li->tv_sec = li->tv_usec = 0;
andre2ff7b5d2000-06-03 14:57:40 +00001186
1187 if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1188 log("wtmpx_get_entry: problem opening %s: %s",
1189 WTMPX_FILE, strerror(errno));
1190 return 0;
1191 }
andre61e67252000-06-04 17:07:49 +00001192 if (fstat(fd, &st) != 0) {
andre2ff7b5d2000-06-03 14:57:40 +00001193 log("wtmpx_get_entry: couldn't stat %s: %s",
1194 WTMP_FILE, strerror(errno));
1195 close(fd);
1196 return 0;
1197 }
andre6bb92372000-06-19 08:20:03 +00001198
1199 /* Seek to the start of the last struct utmpx */
1200 if (lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END) == -1 ) {
1201 /* probably a newly rotated wtmpx file */
1202 close(fd);
1203 return 0;
1204 }
andre2ff7b5d2000-06-03 14:57:40 +00001205
andre6bb92372000-06-19 08:20:03 +00001206 while (!found) {
Damien Miller53c5d462000-06-28 00:50:50 +10001207 if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
andre2ff7b5d2000-06-03 14:57:40 +00001208 log("wtmpx_get_entry: read of %s failed: %s",
1209 WTMPX_FILE, strerror(errno));
1210 close (fd);
1211 return 0;
1212 }
andre2ff7b5d2000-06-03 14:57:40 +00001213 /* Logouts are recorded as a blank username on a particular line.
1214 * So, we just need to find the username in struct utmpx */
andre6bb92372000-06-19 08:20:03 +00001215 if ( wtmpx_islogin(li, &utx) ) {
Damien Millerdd47aa22000-06-27 11:18:27 +10001216# ifdef HAVE_TV_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +00001217 li->tv_sec = utx.ut_tv.tv_sec;
Damien Millerdd47aa22000-06-27 11:18:27 +10001218# else
andre2ff7b5d2000-06-03 14:57:40 +00001219# ifdef HAVE_TIME_IN_UTMPX
1220 li->tv_sec = utx.ut_time;
1221# endif
Damien Millerdd47aa22000-06-27 11:18:27 +10001222# endif
Damien Miller1a132252000-06-13 21:23:17 +10001223 line_fullname(li->line, utx.ut_line, sizeof(li->line));
Damien Millerdd47aa22000-06-27 11:18:27 +10001224# ifdef HAVE_HOST_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +00001225 strlcpy(li->hostname, utx.ut_host,
1226 MIN_SIZEOF(li->hostname, utx.ut_host));
Damien Millerdd47aa22000-06-27 11:18:27 +10001227# endif
andre6bb92372000-06-19 08:20:03 +00001228 continue;
andre2ff7b5d2000-06-03 14:57:40 +00001229 }
1230 if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) {
1231 close (fd);
1232 return 0;
1233 }
andre6bb92372000-06-19 08:20:03 +00001234 }
1235
1236 close(fd);
andre2ff7b5d2000-06-03 14:57:40 +00001237 return 1;
andre61e67252000-06-04 17:07:49 +00001238}
Damien Millerd5bf3072000-06-07 21:32:13 +10001239#endif /* USE_WTMPX */
andre2ff7b5d2000-06-03 14:57:40 +00001240
andre2ff7b5d2000-06-03 14:57:40 +00001241/**
andre61e67252000-06-04 17:07:49 +00001242 ** Low-level libutil login() functions
andre2ff7b5d2000-06-03 14:57:40 +00001243 **/
1244
1245#ifdef USE_LOGIN
andre2ff7b5d2000-06-03 14:57:40 +00001246static int
andre61e67252000-06-04 17:07:49 +00001247syslogin_perform_login(struct logininfo *li)
1248{
andre2ff7b5d2000-06-03 14:57:40 +00001249 struct utmp *ut;
1250
1251 if (! (ut = (struct utmp *)malloc(sizeof(struct utmp)))) {
1252 log("syslogin_perform_login: couldn't malloc()");
1253 return 0;
1254 }
1255 construct_utmp(li, ut);
1256 login(ut);
1257
1258 return 1;
andre61e67252000-06-04 17:07:49 +00001259}
1260
andre2ff7b5d2000-06-03 14:57:40 +00001261static int
andre61e67252000-06-04 17:07:49 +00001262syslogin_perform_logout(struct logininfo *li)
1263{
Damien Millerdd47aa22000-06-27 11:18:27 +10001264# ifdef HAVE_LOGOUT
andre2ff7b5d2000-06-03 14:57:40 +00001265 char line[8];
1266
1267 (void)line_stripname(line, li->line, sizeof(line));
1268
1269 if (!logout(line)) {
1270 log("syslogin_perform_logout: logout() returned an error");
Damien Millerdd47aa22000-06-27 11:18:27 +10001271# ifdef HAVE_LOGWTMP
andre2ff7b5d2000-06-03 14:57:40 +00001272 } else {
1273 logwtmp(line, "", "");
1274 }
Damien Millerdd47aa22000-06-27 11:18:27 +10001275# endif
andre6bb92372000-06-19 08:20:03 +00001276 /* FIXME: (ATL - if the need arises) What to do if we have
1277 * login, but no logout? what if logout but no logwtmp? All
1278 * routines are in libutil so they should all be there,
1279 * but... */
Damien Millerdd47aa22000-06-27 11:18:27 +10001280# endif
andre2ff7b5d2000-06-03 14:57:40 +00001281 return 1;
andre61e67252000-06-04 17:07:49 +00001282}
andre2ff7b5d2000-06-03 14:57:40 +00001283
andre2ff7b5d2000-06-03 14:57:40 +00001284int
andre61e67252000-06-04 17:07:49 +00001285syslogin_write_entry(struct logininfo *li)
1286{
andre2ff7b5d2000-06-03 14:57:40 +00001287 switch (li->type) {
1288 case LTYPE_LOGIN:
1289 return syslogin_perform_login(li);
1290 case LTYPE_LOGOUT:
1291 return syslogin_perform_logout(li);
1292 default:
1293 log("syslogin_write_entry: Invalid type field");
1294 return 0;
1295 }
andre61e67252000-06-04 17:07:49 +00001296}
Damien Millerd5bf3072000-06-07 21:32:13 +10001297#endif /* USE_LOGIN */
andre2ff7b5d2000-06-03 14:57:40 +00001298
1299/* end of file log-syslogin.c */
1300
andre2ff7b5d2000-06-03 14:57:40 +00001301/**
andre61e67252000-06-04 17:07:49 +00001302 ** Low-level lastlog functions
andre2ff7b5d2000-06-03 14:57:40 +00001303 **/
1304
1305#ifdef USE_LASTLOG
Damien Millerdd47aa22000-06-27 11:18:27 +10001306#define LL_FILE 1
1307#define LL_DIR 2
1308#define LL_OTHER 3
andre2ff7b5d2000-06-03 14:57:40 +00001309
andre2ff7b5d2000-06-03 14:57:40 +00001310static void
andre61e67252000-06-04 17:07:49 +00001311lastlog_construct(struct logininfo *li, struct lastlog *last)
1312{
andre2ff7b5d2000-06-03 14:57:40 +00001313 /* clear the structure */
1314 memset(last, '\0', sizeof(struct lastlog));
1315
Damien Millerdd47aa22000-06-27 11:18:27 +10001316 (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
andre6bb92372000-06-19 08:20:03 +00001317 strlcpy(last->ll_host, li->hostname,
1318 MIN_SIZEOF(last->ll_host, li->hostname));
andre2ff7b5d2000-06-03 14:57:40 +00001319 last->ll_time = li->tv_sec;
andre61e67252000-06-04 17:07:49 +00001320}
andre2ff7b5d2000-06-03 14:57:40 +00001321
andre2ff7b5d2000-06-03 14:57:40 +00001322static int
andre61e67252000-06-04 17:07:49 +00001323lastlog_filetype(char *filename)
1324{
andre2ff7b5d2000-06-03 14:57:40 +00001325 struct stat st;
1326
Damien Millerdd47aa22000-06-27 11:18:27 +10001327 if (stat(LASTLOG_FILE, &st) != 0) {
1328 log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
1329 strerror(errno));
andre2ff7b5d2000-06-03 14:57:40 +00001330 return 0;
1331 }
andre2ff7b5d2000-06-03 14:57:40 +00001332 if (S_ISDIR(st.st_mode))
1333 return LL_DIR;
1334 else if (S_ISREG(st.st_mode))
1335 return LL_FILE;
1336 else
1337 return LL_OTHER;
andre61e67252000-06-04 17:07:49 +00001338}
andre2ff7b5d2000-06-03 14:57:40 +00001339
1340
1341/* open the file (using filemode) and seek to the login entry */
1342static int
andre61e67252000-06-04 17:07:49 +00001343lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1344{
andre2ff7b5d2000-06-03 14:57:40 +00001345 off_t offset;
1346 int type;
1347 char lastlog_file[1024];
1348
1349 type = lastlog_filetype(LASTLOG_FILE);
1350 switch (type) {
Damien Millerf8af08d2000-06-27 09:40:06 +10001351 case LL_FILE:
1352 strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1353 break;
1354 case LL_DIR:
1355 snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1356 LASTLOG_FILE, li->username);
1357 break;
1358 default:
1359 log("lastlog_openseek: %.100s is not a file or directory!",
1360 LASTLOG_FILE);
1361 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +10001362 }
andre2ff7b5d2000-06-03 14:57:40 +00001363
1364 *fd = open(lastlog_file, filemode);
1365 if ( *fd < 0) {
Damien Miller53c5d462000-06-28 00:50:50 +10001366 debug("lastlog_openseek: Couldn't open %s: %s",
andre2ff7b5d2000-06-03 14:57:40 +00001367 lastlog_file, strerror(errno));
1368 return 0;
1369 }
Damien Millerdd47aa22000-06-27 11:18:27 +10001370
andre2ff7b5d2000-06-03 14:57:40 +00001371 /* find this uid's offset in the lastlog file */
1372 offset = (off_t) ( (long)li->uid * sizeof(struct lastlog));
1373
1374 if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1375 log("lastlog_openseek: %s->lseek(): %s",
1376 lastlog_file, strerror(errno));
1377 return 0;
1378 }
1379 return 1;
andre61e67252000-06-04 17:07:49 +00001380}
andre2ff7b5d2000-06-03 14:57:40 +00001381
1382static int
andre61e67252000-06-04 17:07:49 +00001383lastlog_perform_login(struct logininfo *li)
1384{
andre2ff7b5d2000-06-03 14:57:40 +00001385 struct lastlog last;
1386 int fd;
1387
1388 /* create our struct lastlog */
1389 lastlog_construct(li, &last);
1390
1391 /* write the entry */
Damien Miller53c5d462000-06-28 00:50:50 +10001392 if (lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) {
1393 if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) {
andre2ff7b5d2000-06-03 14:57:40 +00001394 log("lastlog_write_filemode: Error writing to %s: %s",
1395 LASTLOG_FILE, strerror(errno));
1396 return 0;
1397 }
1398 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001399 } else {
andre2ff7b5d2000-06-03 14:57:40 +00001400 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +10001401 }
andre61e67252000-06-04 17:07:49 +00001402}
andre2ff7b5d2000-06-03 14:57:40 +00001403
andre2ff7b5d2000-06-03 14:57:40 +00001404int
andre61e67252000-06-04 17:07:49 +00001405lastlog_write_entry(struct logininfo *li)
1406{
andre2ff7b5d2000-06-03 14:57:40 +00001407 switch(li->type) {
1408 case LTYPE_LOGIN:
1409 return lastlog_perform_login(li);
1410 default:
1411 log("lastlog_write_entry: Invalid type field");
1412 return 0;
1413 }
andre61e67252000-06-04 17:07:49 +00001414}
andre2ff7b5d2000-06-03 14:57:40 +00001415
andre2ff7b5d2000-06-03 14:57:40 +00001416static void
andre61e67252000-06-04 17:07:49 +00001417lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1418{
andre2ff7b5d2000-06-03 14:57:40 +00001419 line_fullname(li->line, last->ll_line, sizeof(li->line));
Damien Millerdd47aa22000-06-27 11:18:27 +10001420 strlcpy(li->hostname, last->ll_host,
andre6bb92372000-06-19 08:20:03 +00001421 MIN_SIZEOF(li->hostname, last->ll_host));
andre2ff7b5d2000-06-03 14:57:40 +00001422 li->tv_sec = last->ll_time;
andre61e67252000-06-04 17:07:49 +00001423}
andre2ff7b5d2000-06-03 14:57:40 +00001424
andre2ff7b5d2000-06-03 14:57:40 +00001425int
andre61e67252000-06-04 17:07:49 +00001426lastlog_get_entry(struct logininfo *li)
1427{
andre2ff7b5d2000-06-03 14:57:40 +00001428 struct lastlog last;
1429 int fd;
1430
1431 if (lastlog_openseek(li, &fd, O_RDONLY)) {
Damien Miller53c5d462000-06-28 00:50:50 +10001432 if (atomicio(read, fd, &last, sizeof(last)) != sizeof(last)) {
1433 log("lastlog_get_entry: Error reading from %s: %s",
andre2ff7b5d2000-06-03 14:57:40 +00001434 LASTLOG_FILE, strerror(errno));
1435 return 0;
1436 } else {
1437 lastlog_populate_entry(li, &last);
1438 return 1;
1439 }
Damien Millerdd47aa22000-06-27 11:18:27 +10001440 } else {
andre2ff7b5d2000-06-03 14:57:40 +00001441 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +10001442 }
andre61e67252000-06-04 17:07:49 +00001443}
Damien Millerd5bf3072000-06-07 21:32:13 +10001444#endif /* USE_LASTLOG */