blob: 6a6443d2bfbfe108b4f2b4eaac631a86d012c0e3 [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 Miller53c5d462000-06-28 00:50:50 +1000173RCSID("$Id: loginrec.c,v 1.12 2000/06/27 14:50:50 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
538 len = strlen(src);
539
540 if (len <= 0) {
541 src += (len - dstsize);
542 strncpy(dst, src, dstsize); /* note: _don't_ change this to strlcpy */
543 }
544
andre2ff7b5d2000-06-03 14:57:40 +0000545 return dst;
546}
547
andre2ff7b5d2000-06-03 14:57:40 +0000548/**
549 ** utmp utility functions
andre61e67252000-06-04 17:07:49 +0000550 **
551 ** These functions manipulate struct utmp, taking system differences
552 ** into account.
andre2ff7b5d2000-06-03 14:57:40 +0000553 **/
554
555#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
556
andre2ff7b5d2000-06-03 14:57:40 +0000557/* build the utmp structure */
558void
andre61e67252000-06-04 17:07:49 +0000559set_utmp_time(struct logininfo *li, struct utmp *ut)
560{
Damien Millerdd47aa22000-06-27 11:18:27 +1000561# ifdef HAVE_TV_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +0000562 ut->ut_tv.tv_sec = li->tv_sec;
563 ut->ut_tv.tv_usec = li->tv_usec;
Damien Millerdd47aa22000-06-27 11:18:27 +1000564# else
andre2ff7b5d2000-06-03 14:57:40 +0000565# ifdef HAVE_TIME_IN_UTMP
566 ut->ut_time = li->tv_sec;
567# endif
Damien Millerdd47aa22000-06-27 11:18:27 +1000568# endif
andre2ff7b5d2000-06-03 14:57:40 +0000569}
570
571void
572construct_utmp(struct logininfo *li,
andre61e67252000-06-04 17:07:49 +0000573 struct utmp *ut)
574{
andre2ff7b5d2000-06-03 14:57:40 +0000575 memset(ut, '\0', sizeof(struct utmp));
andre6bb92372000-06-19 08:20:03 +0000576
577 /* First fill out fields used for both logins and logouts */
578
Damien Millerdd47aa22000-06-27 11:18:27 +1000579# ifdef HAVE_ID_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +0000580 line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
Damien Millerdd47aa22000-06-27 11:18:27 +1000581# endif
andre2ff7b5d2000-06-03 14:57:40 +0000582
Damien Millerdd47aa22000-06-27 11:18:27 +1000583# ifdef HAVE_TYPE_IN_UTMP
andre6bb92372000-06-19 08:20:03 +0000584 /* This is done here to keep utmp constants out of struct logininfo */
andre2ff7b5d2000-06-03 14:57:40 +0000585 switch (li->type) {
586 case LTYPE_LOGIN:
587 ut->ut_type = USER_PROCESS;
588 break;
589 case LTYPE_LOGOUT:
590 ut->ut_type = DEAD_PROCESS;
591 break;
592 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000593# endif
andre6bb92372000-06-19 08:20:03 +0000594 set_utmp_time(li, ut);
andre2ff7b5d2000-06-03 14:57:40 +0000595
andre6bb92372000-06-19 08:20:03 +0000596 line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
Damien Millerdd47aa22000-06-27 11:18:27 +1000597
598# ifdef HAVE_PID_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +0000599 ut->ut_pid = li->pid;
Damien Millerdd47aa22000-06-27 11:18:27 +1000600# endif
andre6bb92372000-06-19 08:20:03 +0000601
602 /* If we're logging out, leave all other fields blank */
603 if (li->type == LTYPE_LOGOUT)
604 return;
605
Damien Millerdd47aa22000-06-27 11:18:27 +1000606 /*
607 * These fields are only used when logging in, and are blank
608 * for logouts.
609 */
andre6bb92372000-06-19 08:20:03 +0000610
611 /* Use strncpy because we don't necessarily want null termination */
612 strncpy(ut->ut_user, li->username, MIN_SIZEOF(ut->ut_user, li->username));
Damien Millerdd47aa22000-06-27 11:18:27 +1000613# ifdef HAVE_HOST_IN_UTMP
andre6bb92372000-06-19 08:20:03 +0000614 strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
Damien Millerdd47aa22000-06-27 11:18:27 +1000615# endif
616# ifdef HAVE_ADDR_IN_UTMP
andre61e67252000-06-04 17:07:49 +0000617 /* this is just a 32-bit IP address */
618 if (li->hostaddr.sa.sa_family == AF_INET)
619 ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
Damien Millerdd47aa22000-06-27 11:18:27 +1000620# endif
andre61e67252000-06-04 17:07:49 +0000621}
Damien Millerdd47aa22000-06-27 11:18:27 +1000622#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
andre61e67252000-06-04 17:07:49 +0000623
andre2ff7b5d2000-06-03 14:57:40 +0000624/**
625 ** utmpx utility functions
andre61e67252000-06-04 17:07:49 +0000626 **
627 ** These functions manipulate struct utmpx, accounting for system
628 ** variations.
andre2ff7b5d2000-06-03 14:57:40 +0000629 **/
630
631#if defined(USE_UTMPX) || defined (USE_WTMPX)
andre2ff7b5d2000-06-03 14:57:40 +0000632/* build the utmpx structure */
633void
andre61e67252000-06-04 17:07:49 +0000634set_utmpx_time(struct logininfo *li, struct utmpx *utx)
635{
Damien Millerdd47aa22000-06-27 11:18:27 +1000636# ifdef HAVE_TV_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000637 utx->ut_tv.tv_sec = li->tv_sec;
638 utx->ut_tv.tv_usec = li->tv_usec;
Damien Millerdd47aa22000-06-27 11:18:27 +1000639# else /* HAVE_TV_IN_UTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000640# ifdef HAVE_TIME_IN_UTMPX
641 utx->ut_time = li->tv_sec;
Damien Millerdd47aa22000-06-27 11:18:27 +1000642# endif /* HAVE_TIME_IN_UTMPX */
643# endif /* HAVE_TV_IN_UTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000644}
645
andre61e67252000-06-04 17:07:49 +0000646void
647construct_utmpx(struct logininfo *li, struct utmpx *utx)
648{
649 memset(utx, '\0', sizeof(struct utmpx));
andre2ff7b5d2000-06-03 14:57:40 +0000650 line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
651
652 /* this is done here to keep utmp constants out of loginrec.h */
653 switch (li->type) {
654 case LTYPE_LOGIN:
655 utx->ut_type = USER_PROCESS;
656 break;
657 case LTYPE_LOGOUT:
658 utx->ut_type = DEAD_PROCESS;
659 break;
660 }
andre2ff7b5d2000-06-03 14:57:40 +0000661 line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
andre2ff7b5d2000-06-03 14:57:40 +0000662 set_utmpx_time(li, utx);
andre6bb92372000-06-19 08:20:03 +0000663 utx->ut_pid = li->pid;
664
665 if (li->type == LTYPE_LOGOUT)
666 return;
667
Damien Millerdd47aa22000-06-27 11:18:27 +1000668 /*
669 * These fields are only used when logging in, and are blank
670 * for logouts.
671 */
andre6bb92372000-06-19 08:20:03 +0000672
673 /* strncpy(): Don't necessarily want null termination */
674 strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
Damien Millerdd47aa22000-06-27 11:18:27 +1000675# ifdef HAVE_HOST_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +0000676 strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
Damien Millerdd47aa22000-06-27 11:18:27 +1000677# endif
678# ifdef HAVE_ADDR_IN_UTMPX
andre61e67252000-06-04 17:07:49 +0000679 /* FIXME: (ATL) not supported yet */
Damien Millerdd47aa22000-06-27 11:18:27 +1000680# endif
681# ifdef HAVE_SYSLEN_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +0000682 /* ut_syslen is the length of the utx_host string */
683 utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
Damien Millerdd47aa22000-06-27 11:18:27 +1000684# endif
andre61e67252000-06-04 17:07:49 +0000685}
Damien Millerdd47aa22000-06-27 11:18:27 +1000686#endif /* USE_UTMPX || USE_WTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000687
688/**
andre61e67252000-06-04 17:07:49 +0000689 ** Low-level utmp functions
andre2ff7b5d2000-06-03 14:57:40 +0000690 **/
691
692/* FIXME: (ATL) utmp_write_direct needs testing */
andre2ff7b5d2000-06-03 14:57:40 +0000693#ifdef USE_UTMP
694
andre2ff7b5d2000-06-03 14:57:40 +0000695/* if we can, use pututline() etc. */
Damien Millerdd47aa22000-06-27 11:18:27 +1000696# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
697 defined(HAVE_PUTUTLINE)
andre2ff7b5d2000-06-03 14:57:40 +0000698# define UTMP_USE_LIBRARY
Damien Millerdd47aa22000-06-27 11:18:27 +1000699# endif
andre2ff7b5d2000-06-03 14:57:40 +0000700
701
702/* write a utmp entry with the system's help (pututline() and pals) */
Damien Millerdd47aa22000-06-27 11:18:27 +1000703# ifdef UTMP_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000704static int
andre61e67252000-06-04 17:07:49 +0000705utmp_write_library(struct logininfo *li, struct utmp *ut)
706{
andre2ff7b5d2000-06-03 14:57:40 +0000707 setutent();
708 pututline(ut);
709
Damien Millerdd47aa22000-06-27 11:18:27 +1000710# ifdef HAVE_ENDUTENT
andre2ff7b5d2000-06-03 14:57:40 +0000711 endutent();
Damien Millerdd47aa22000-06-27 11:18:27 +1000712# endif
andre2ff7b5d2000-06-03 14:57:40 +0000713 return 1;
andre61e67252000-06-04 17:07:49 +0000714}
Damien Millerdd47aa22000-06-27 11:18:27 +1000715# else /* UTMP_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000716
717/* write a utmp entry direct to the file */
andre61e67252000-06-04 17:07:49 +0000718/* This is a slightly modification of code in OpenBSD's login.c */
andre2ff7b5d2000-06-03 14:57:40 +0000719static int
andre61e67252000-06-04 17:07:49 +0000720utmp_write_direct(struct logininfo *li, struct utmp *ut)
721{
andre2ff7b5d2000-06-03 14:57:40 +0000722 struct utmp old_ut;
723 register int fd;
724 int tty;
725
andre6bb92372000-06-19 08:20:03 +0000726 /* FIXME: (ATL) ttyslot() needs local implementation */
andre2ff7b5d2000-06-03 14:57:40 +0000727 tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
728
729 if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
730 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
731 /*
732 * Prevent luser from zero'ing out ut_host.
733 * If the new ut_line is empty but the old one is not
734 * and ut_line and ut_name match, preserve the old ut_line.
735 */
Damien Miller53c5d462000-06-28 00:50:50 +1000736 if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
737 (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
738 (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
739 (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
andre2ff7b5d2000-06-03 14:57:40 +0000740 (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
Damien Miller53c5d462000-06-28 00:50:50 +1000741 }
742
andre2ff7b5d2000-06-03 14:57:40 +0000743 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
Damien Miller53c5d462000-06-28 00:50:50 +1000744 if (atomicio(write, fd, ut, sizeof(ut)) != sizeof(ut))
andre2ff7b5d2000-06-03 14:57:40 +0000745 log("utmp_write_direct: error writing %s: %s",
andre6bb92372000-06-19 08:20:03 +0000746 UTMP_FILE, strerror(errno));
andre2ff7b5d2000-06-03 14:57:40 +0000747
748 (void)close(fd);
749 return 1;
Damien Miller53c5d462000-06-28 00:50:50 +1000750 } else {
andre2ff7b5d2000-06-03 14:57:40 +0000751 return 0;
Damien Miller53c5d462000-06-28 00:50:50 +1000752 }
andre61e67252000-06-04 17:07:49 +0000753}
Damien Millerdd47aa22000-06-27 11:18:27 +1000754# endif /* UTMP_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000755
756static int
andre61e67252000-06-04 17:07:49 +0000757utmp_perform_login(struct logininfo *li)
758{
andre2ff7b5d2000-06-03 14:57:40 +0000759 struct utmp ut;
760
761 construct_utmp(li, &ut);
Damien Millerdd47aa22000-06-27 11:18:27 +1000762# ifdef UTMP_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000763 if (!utmp_write_library(li, &ut)) {
andre6bb92372000-06-19 08:20:03 +0000764 log("utmp_perform_login: utmp_write_library() failed");
andre2ff7b5d2000-06-03 14:57:40 +0000765 return 0;
766 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000767# else
andre2ff7b5d2000-06-03 14:57:40 +0000768 if (!utmp_write_direct(li, &ut)) {
769 log("utmp_perform_login: utmp_write_direct() failed");
770 return 0;
771 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000772# endif
andre2ff7b5d2000-06-03 14:57:40 +0000773 return 1;
andre61e67252000-06-04 17:07:49 +0000774}
andre2ff7b5d2000-06-03 14:57:40 +0000775
776
777static int
andre61e67252000-06-04 17:07:49 +0000778utmp_perform_logout(struct logininfo *li)
779{
andre2ff7b5d2000-06-03 14:57:40 +0000780 struct utmp ut;
781
andre6bb92372000-06-19 08:20:03 +0000782 construct_utmp(li, &ut);
Damien Millerdd47aa22000-06-27 11:18:27 +1000783# ifdef UTMP_USE_LIBRARY
andre6bb92372000-06-19 08:20:03 +0000784 if (!utmp_write_library(li, &ut)) {
785 log("utmp_perform_logout: utmp_write_library() failed");
786 return 0;
787 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000788# else
andre6bb92372000-06-19 08:20:03 +0000789 if (!utmp_write_direct(li, &ut)) {
790 log("utmp_perform_logout: utmp_write_direct() failed");
791 return 0;
792 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000793# endif
andre2ff7b5d2000-06-03 14:57:40 +0000794 return 1;
andre61e67252000-06-04 17:07:49 +0000795}
andre2ff7b5d2000-06-03 14:57:40 +0000796
797
798int
andre61e67252000-06-04 17:07:49 +0000799utmp_write_entry(struct logininfo *li)
800{
andre2ff7b5d2000-06-03 14:57:40 +0000801 switch(li->type) {
802 case LTYPE_LOGIN:
803 return utmp_perform_login(li);
804
805 case LTYPE_LOGOUT:
806 return utmp_perform_logout(li);
807
808 default:
809 log("utmp_write_entry: invalid type field");
810 return 0;
811 }
andre61e67252000-06-04 17:07:49 +0000812}
Damien Millerdd47aa22000-06-27 11:18:27 +1000813#endif /* USE_UTMP */
andre2ff7b5d2000-06-03 14:57:40 +0000814
815
816/**
andre61e67252000-06-04 17:07:49 +0000817 ** Low-level utmpx functions
andre2ff7b5d2000-06-03 14:57:40 +0000818 **/
819
820/* not much point if we don't want utmpx entries */
821#ifdef USE_UTMPX
822
andre2ff7b5d2000-06-03 14:57:40 +0000823/* if we have the wherewithall, use pututxline etc. */
Damien Millerdd47aa22000-06-27 11:18:27 +1000824# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
825 defined(HAVE_PUTUTXLINE)
andre2ff7b5d2000-06-03 14:57:40 +0000826# define UTMPX_USE_LIBRARY
Damien Millerdd47aa22000-06-27 11:18:27 +1000827# endif
andre2ff7b5d2000-06-03 14:57:40 +0000828
829
830/* write a utmpx entry with the system's help (pututxline() and pals) */
Damien Millerdd47aa22000-06-27 11:18:27 +1000831# ifdef UTMPX_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000832static int
andre61e67252000-06-04 17:07:49 +0000833utmpx_write_library(struct logininfo *li, struct utmpx *utx)
834{
andre2ff7b5d2000-06-03 14:57:40 +0000835 setutxent();
836 pututxline(utx);
837
Damien Millerdd47aa22000-06-27 11:18:27 +1000838# ifdef HAVE_ENDUTXENT
andre2ff7b5d2000-06-03 14:57:40 +0000839 endutxent();
Damien Millerdd47aa22000-06-27 11:18:27 +1000840# endif
andre2ff7b5d2000-06-03 14:57:40 +0000841 return 1;
andre61e67252000-06-04 17:07:49 +0000842}
andre2ff7b5d2000-06-03 14:57:40 +0000843
Damien Millerdd47aa22000-06-27 11:18:27 +1000844# else /* UTMPX_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000845
846/* write a utmp entry direct to the file */
847static int
andre61e67252000-06-04 17:07:49 +0000848utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
849{
andre2ff7b5d2000-06-03 14:57:40 +0000850 log("utmpx_write_direct: not implemented!");
851 return 0;
andre61e67252000-06-04 17:07:49 +0000852}
Damien Millerdd47aa22000-06-27 11:18:27 +1000853# endif /* UTMPX_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000854
855static int
andre61e67252000-06-04 17:07:49 +0000856utmpx_perform_login(struct logininfo *li)
857{
andre2ff7b5d2000-06-03 14:57:40 +0000858 struct utmpx utx;
859
860 construct_utmpx(li, &utx);
Damien Millerdd47aa22000-06-27 11:18:27 +1000861# ifdef UTMPX_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000862 if (!utmpx_write_library(li, &utx)) {
863 log("utmpx_perform_login: utmp_write_library() failed");
864 return 0;
865 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000866# else
andre2ff7b5d2000-06-03 14:57:40 +0000867 if (!utmpx_write_direct(li, &ut)) {
868 log("utmpx_perform_login: utmp_write_direct() failed");
869 return 0;
870 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000871# endif
andre2ff7b5d2000-06-03 14:57:40 +0000872 return 1;
andre61e67252000-06-04 17:07:49 +0000873}
andre2ff7b5d2000-06-03 14:57:40 +0000874
875
876static int
andre61e67252000-06-04 17:07:49 +0000877utmpx_perform_logout(struct logininfo *li)
878{
andre2ff7b5d2000-06-03 14:57:40 +0000879 struct utmpx utx;
880
881 memset(&utx, '\0', sizeof(utx));
882 set_utmpx_time(li, &utx);
883 line_stripname(utx.ut_line, li->line, sizeof(utx.ut_line));
Damien Millerdd47aa22000-06-27 11:18:27 +1000884# ifdef HAVE_ID_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000885 line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
Damien Millerdd47aa22000-06-27 11:18:27 +1000886# endif
887# ifdef HAVE_TYPE_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000888 utx.ut_type = DEAD_PROCESS;
Damien Millerdd47aa22000-06-27 11:18:27 +1000889# endif
andre2ff7b5d2000-06-03 14:57:40 +0000890
Damien Millerdd47aa22000-06-27 11:18:27 +1000891# ifdef UTMPX_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000892 utmpx_write_library(li, &utx);
Damien Millerdd47aa22000-06-27 11:18:27 +1000893# else
andre2ff7b5d2000-06-03 14:57:40 +0000894 utmpx_write_direct(li, &utx);
Damien Millerdd47aa22000-06-27 11:18:27 +1000895# endif
andre2ff7b5d2000-06-03 14:57:40 +0000896 return 1;
andre61e67252000-06-04 17:07:49 +0000897}
andre2ff7b5d2000-06-03 14:57:40 +0000898
andre2ff7b5d2000-06-03 14:57:40 +0000899int
andre61e67252000-06-04 17:07:49 +0000900utmpx_write_entry(struct logininfo *li)
901{
andre2ff7b5d2000-06-03 14:57:40 +0000902 switch(li->type) {
903 case LTYPE_LOGIN:
904 return utmpx_perform_login(li);
905 case LTYPE_LOGOUT:
906 return utmpx_perform_logout(li);
907 default:
908 log("utmpx_write_entry: invalid type field");
909 return 0;
910 }
andre61e67252000-06-04 17:07:49 +0000911}
Damien Millerdd47aa22000-06-27 11:18:27 +1000912#endif /* USE_UTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000913
914
915/**
andre61e67252000-06-04 17:07:49 +0000916 ** Low-level wtmp functions
andre2ff7b5d2000-06-03 14:57:40 +0000917 **/
918
919#ifdef USE_WTMP
920
andre2ff7b5d2000-06-03 14:57:40 +0000921/* write a wtmp entry direct to the end of the file */
andre61e67252000-06-04 17:07:49 +0000922/* This is a slight modification of code in OpenBSD's logwtmp.c */
andre2ff7b5d2000-06-03 14:57:40 +0000923static int
andre61e67252000-06-04 17:07:49 +0000924wtmp_write(struct logininfo *li, struct utmp *ut)
925{
andre2ff7b5d2000-06-03 14:57:40 +0000926 struct stat buf;
927 int fd, ret = 1;
928
929 if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
930 log("wtmp_write: problem writing %s: %s",
931 WTMP_FILE, strerror(errno));
932 return 0;
933 }
andre61e67252000-06-04 17:07:49 +0000934 if (fstat(fd, &buf) == 0)
Damien Miller53c5d462000-06-28 00:50:50 +1000935 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
andre2ff7b5d2000-06-03 14:57:40 +0000936 ftruncate(fd, buf.st_size);
937 log("wtmp_write: problem writing %s: %s",
938 WTMP_FILE, strerror(errno));
939 ret = 0;
940 }
941 (void)close(fd);
andre2ff7b5d2000-06-03 14:57:40 +0000942 return ret;
andre61e67252000-06-04 17:07:49 +0000943}
andre2ff7b5d2000-06-03 14:57:40 +0000944
andre2ff7b5d2000-06-03 14:57:40 +0000945static int
Damien Millerdd47aa22000-06-27 11:18:27 +1000946wtmp_perform_login(struct logininfo *li)
947{
andre2ff7b5d2000-06-03 14:57:40 +0000948 struct utmp ut;
949
950 construct_utmp(li, &ut);
951 return wtmp_write(li, &ut);
andre61e67252000-06-04 17:07:49 +0000952}
andre2ff7b5d2000-06-03 14:57:40 +0000953
954
955static int
andre61e67252000-06-04 17:07:49 +0000956wtmp_perform_logout(struct logininfo *li)
957{
andre2ff7b5d2000-06-03 14:57:40 +0000958 struct utmp ut;
959
960 construct_utmp(li, &ut);
andre2ff7b5d2000-06-03 14:57:40 +0000961 return wtmp_write(li, &ut);
andre61e67252000-06-04 17:07:49 +0000962}
andre2ff7b5d2000-06-03 14:57:40 +0000963
964
965int
andre61e67252000-06-04 17:07:49 +0000966wtmp_write_entry(struct logininfo *li)
967{
andre2ff7b5d2000-06-03 14:57:40 +0000968 switch(li->type) {
969 case LTYPE_LOGIN:
970 return wtmp_perform_login(li);
971 case LTYPE_LOGOUT:
972 return wtmp_perform_logout(li);
973 default:
974 log("wtmp_write_entry: invalid type field");
975 return 0;
976 }
andre61e67252000-06-04 17:07:49 +0000977}
andre2ff7b5d2000-06-03 14:57:40 +0000978
979
andre6bb92372000-06-19 08:20:03 +0000980/* Notes on fetching login data from wtmp/wtmpx
981 *
982 * Logouts are usually recorded with (amongst other things) a blank
983 * username on a given tty line. However, some systems (HP-UX is one)
984 * leave all fields set, but change the ut_type field to DEAD_PROCESS.
985 *
986 * Since we're only looking for logins here, we know that the username
987 * must be set correctly. On systems that leave it in, we check for
988 * ut_type==USER_PROCESS (indicating a login.)
989 *
990 * Portability: Some systems may set something other than USER_PROCESS
991 * to indicate a login process. I don't know of any as I write. Also,
992 * it's possible that some systems may both leave the username in
993 * place and not have ut_type.
994 */
995
andre6bb92372000-06-19 08:20:03 +0000996/* return true if this wtmp entry indicates a login */
997static int
998wtmp_islogin(struct logininfo *li, struct utmp *ut)
999{
Damien Millerdd47aa22000-06-27 11:18:27 +10001000 if (strncmp(li->username, ut->ut_user,
1001 MIN_SIZEOF(li->username, ut->ut_user)) == 0) {
1002# ifdef HAVE_TYPE_IN_UTMP
andre6bb92372000-06-19 08:20:03 +00001003 if (ut->ut_type & USER_PROCESS)
1004 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001005# else
andre6bb92372000-06-19 08:20:03 +00001006 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001007# endif
andre6bb92372000-06-19 08:20:03 +00001008 }
1009 return 0;
1010}
1011
andre2ff7b5d2000-06-03 14:57:40 +00001012int
andre61e67252000-06-04 17:07:49 +00001013wtmp_get_entry(struct logininfo *li)
1014{
andre2ff7b5d2000-06-03 14:57:40 +00001015 struct stat st;
1016 struct utmp ut;
andre6bb92372000-06-19 08:20:03 +00001017 int fd, found=0;
1018
1019 /* Clear the time entries in our logininfo */
1020 li->tv_sec = li->tv_usec = 0;
andre2ff7b5d2000-06-03 14:57:40 +00001021
1022 if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1023 log("wtmp_get_entry: problem opening %s: %s",
1024 WTMP_FILE, strerror(errno));
1025 return 0;
1026 }
andre61e67252000-06-04 17:07:49 +00001027 if (fstat(fd, &st) != 0) {
andre2ff7b5d2000-06-03 14:57:40 +00001028 log("wtmp_get_entry: couldn't stat %s: %s",
1029 WTMP_FILE, strerror(errno));
1030 close(fd);
1031 return 0;
1032 }
andre2ff7b5d2000-06-03 14:57:40 +00001033
andre6bb92372000-06-19 08:20:03 +00001034 /* Seek to the start of the last struct utmp */
1035 if (lseek(fd, (off_t)(0-sizeof(struct utmp)), SEEK_END) == -1) {
1036 /* Looks like we've got a fresh wtmp file */
1037 close(fd);
1038 return 0;
1039 }
1040
1041 while (!found) {
Damien Miller53c5d462000-06-28 00:50:50 +10001042 if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
andre2ff7b5d2000-06-03 14:57:40 +00001043 log("wtmp_get_entry: read of %s failed: %s",
1044 WTMP_FILE, strerror(errno));
1045 close (fd);
1046 return 0;
1047 }
andre6bb92372000-06-19 08:20:03 +00001048 if ( wtmp_islogin(li, &ut) ) {
1049 found = 1;
1050 /* We've already checked for a time in struct
1051 * utmp, in login_getlast(). */
Damien Millerdd47aa22000-06-27 11:18:27 +10001052# ifdef HAVE_TIME_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +00001053 li->tv_sec = ut.ut_time;
Damien Millerdd47aa22000-06-27 11:18:27 +10001054# else
andre2ff7b5d2000-06-03 14:57:40 +00001055# if HAVE_TV_IN_UTMP
1056 li->tv_sec = ut.ut_tv.tv_sec;
1057# endif
Damien Millerdd47aa22000-06-27 11:18:27 +10001058# endif
andre6bb92372000-06-19 08:20:03 +00001059 line_fullname(li->line, ut.ut_line,
1060 MIN_SIZEOF(li->line, ut.ut_line));
Damien Millerdd47aa22000-06-27 11:18:27 +10001061# ifdef HAVE_HOST_IN_UTMP
andre6bb92372000-06-19 08:20:03 +00001062 strlcpy(li->hostname, ut.ut_host,
1063 MIN_SIZEOF(li->hostname, ut.ut_host));
Damien Millerdd47aa22000-06-27 11:18:27 +10001064# endif
andre6bb92372000-06-19 08:20:03 +00001065 continue;
andre2ff7b5d2000-06-03 14:57:40 +00001066 }
andre6bb92372000-06-19 08:20:03 +00001067 /* Seek back 2 x struct utmp */
andre2ff7b5d2000-06-03 14:57:40 +00001068 if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) {
andre6bb92372000-06-19 08:20:03 +00001069 /* We've found the start of the file, so quit */
andre2ff7b5d2000-06-03 14:57:40 +00001070 close (fd);
1071 return 0;
1072 }
andre6bb92372000-06-19 08:20:03 +00001073 }
1074
1075 /* We found an entry. Tidy up and return */
1076 close(fd);
andre2ff7b5d2000-06-03 14:57:40 +00001077 return 1;
andre61e67252000-06-04 17:07:49 +00001078}
Damien Millerdd47aa22000-06-27 11:18:27 +10001079# endif /* USE_WTMP */
andre2ff7b5d2000-06-03 14:57:40 +00001080
1081
1082/**
andre61e67252000-06-04 17:07:49 +00001083 ** Low-level wtmpx functions
andre2ff7b5d2000-06-03 14:57:40 +00001084 **/
1085
1086#ifdef USE_WTMPX
andre2ff7b5d2000-06-03 14:57:40 +00001087/* write a wtmpx entry direct to the end of the file */
andre61e67252000-06-04 17:07:49 +00001088/* This is a slight modification of code in OpenBSD's logwtmp.c */
andre2ff7b5d2000-06-03 14:57:40 +00001089static int
andre61e67252000-06-04 17:07:49 +00001090wtmpx_write(struct logininfo *li, struct utmpx *utx)
1091{
andre2ff7b5d2000-06-03 14:57:40 +00001092 struct stat buf;
1093 int fd, ret = 1;
1094
1095 if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1096 log("wtmpx_write: problem opening %s: %s",
1097 WTMPX_FILE, strerror(errno));
1098 return 0;
1099 }
1100
1101 if (fstat(fd, &buf) == 0)
Damien Miller53c5d462000-06-28 00:50:50 +10001102 if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
andre2ff7b5d2000-06-03 14:57:40 +00001103 ftruncate(fd, buf.st_size);
1104 log("wtmpx_write: problem writing %s: %s",
1105 WTMPX_FILE, strerror(errno));
1106 ret = 0;
1107 }
1108 (void)close(fd);
1109
1110 return ret;
andre61e67252000-06-04 17:07:49 +00001111}
andre2ff7b5d2000-06-03 14:57:40 +00001112
1113
1114static int
andre61e67252000-06-04 17:07:49 +00001115wtmpx_perform_login(struct logininfo *li)
1116{
andre2ff7b5d2000-06-03 14:57:40 +00001117 struct utmpx utx;
1118
1119 construct_utmpx(li, &utx);
1120 return wtmpx_write(li, &utx);
andre61e67252000-06-04 17:07:49 +00001121}
andre2ff7b5d2000-06-03 14:57:40 +00001122
1123
1124static int
andre61e67252000-06-04 17:07:49 +00001125wtmpx_perform_logout(struct logininfo *li)
1126{
andre2ff7b5d2000-06-03 14:57:40 +00001127 struct utmpx utx;
1128
1129 construct_utmpx(li, &utx);
andre2ff7b5d2000-06-03 14:57:40 +00001130 return wtmpx_write(li, &utx);
andre61e67252000-06-04 17:07:49 +00001131}
andre2ff7b5d2000-06-03 14:57:40 +00001132
1133
1134int
andre61e67252000-06-04 17:07:49 +00001135wtmpx_write_entry(struct logininfo *li)
1136{
andre2ff7b5d2000-06-03 14:57:40 +00001137 switch(li->type) {
1138 case LTYPE_LOGIN:
1139 return wtmpx_perform_login(li);
1140 case LTYPE_LOGOUT:
1141 return wtmpx_perform_logout(li);
1142 default:
1143 log("wtmpx_write_entry: invalid type field");
1144 return 0;
1145 }
andre61e67252000-06-04 17:07:49 +00001146}
andre2ff7b5d2000-06-03 14:57:40 +00001147
andre6bb92372000-06-19 08:20:03 +00001148/* Please see the notes above wtmp_islogin() for information about the
1149 next two functions */
1150
1151/* Return true if this wtmpx entry indicates a login */
1152static int
1153wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1154{
1155 if ( strncmp(li->username, utx->ut_user,
Damien Millerdd47aa22000-06-27 11:18:27 +10001156 MIN_SIZEOF(li->username, utx->ut_user)) == 0 ) {
1157# ifdef HAVE_TYPE_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +00001158 if (utx->ut_type == USER_PROCESS)
1159 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001160# else
andre6bb92372000-06-19 08:20:03 +00001161 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001162# endif
andre6bb92372000-06-19 08:20:03 +00001163 }
1164 return 0;
1165}
1166
andre2ff7b5d2000-06-03 14:57:40 +00001167
1168int
andre61e67252000-06-04 17:07:49 +00001169wtmpx_get_entry(struct logininfo *li)
1170{
andre2ff7b5d2000-06-03 14:57:40 +00001171 struct stat st;
1172 struct utmpx utx;
andre6bb92372000-06-19 08:20:03 +00001173 int fd, found=0;
1174
1175 /* Clear the time entries */
1176 li->tv_sec = li->tv_usec = 0;
andre2ff7b5d2000-06-03 14:57:40 +00001177
1178 if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1179 log("wtmpx_get_entry: problem opening %s: %s",
1180 WTMPX_FILE, strerror(errno));
1181 return 0;
1182 }
andre61e67252000-06-04 17:07:49 +00001183 if (fstat(fd, &st) != 0) {
andre2ff7b5d2000-06-03 14:57:40 +00001184 log("wtmpx_get_entry: couldn't stat %s: %s",
1185 WTMP_FILE, strerror(errno));
1186 close(fd);
1187 return 0;
1188 }
andre6bb92372000-06-19 08:20:03 +00001189
1190 /* Seek to the start of the last struct utmpx */
1191 if (lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END) == -1 ) {
1192 /* probably a newly rotated wtmpx file */
1193 close(fd);
1194 return 0;
1195 }
andre2ff7b5d2000-06-03 14:57:40 +00001196
andre6bb92372000-06-19 08:20:03 +00001197 while (!found) {
Damien Miller53c5d462000-06-28 00:50:50 +10001198 if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
andre2ff7b5d2000-06-03 14:57:40 +00001199 log("wtmpx_get_entry: read of %s failed: %s",
1200 WTMPX_FILE, strerror(errno));
1201 close (fd);
1202 return 0;
1203 }
andre2ff7b5d2000-06-03 14:57:40 +00001204 /* Logouts are recorded as a blank username on a particular line.
1205 * So, we just need to find the username in struct utmpx */
andre6bb92372000-06-19 08:20:03 +00001206 if ( wtmpx_islogin(li, &utx) ) {
Damien Millerdd47aa22000-06-27 11:18:27 +10001207# ifdef HAVE_TV_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +00001208 li->tv_sec = utx.ut_tv.tv_sec;
Damien Millerdd47aa22000-06-27 11:18:27 +10001209# else
andre2ff7b5d2000-06-03 14:57:40 +00001210# ifdef HAVE_TIME_IN_UTMPX
1211 li->tv_sec = utx.ut_time;
1212# endif
Damien Millerdd47aa22000-06-27 11:18:27 +10001213# endif
Damien Miller1a132252000-06-13 21:23:17 +10001214 line_fullname(li->line, utx.ut_line, sizeof(li->line));
Damien Millerdd47aa22000-06-27 11:18:27 +10001215# ifdef HAVE_HOST_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +00001216 strlcpy(li->hostname, utx.ut_host,
1217 MIN_SIZEOF(li->hostname, utx.ut_host));
Damien Millerdd47aa22000-06-27 11:18:27 +10001218# endif
andre6bb92372000-06-19 08:20:03 +00001219 continue;
andre2ff7b5d2000-06-03 14:57:40 +00001220 }
1221 if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) {
1222 close (fd);
1223 return 0;
1224 }
andre6bb92372000-06-19 08:20:03 +00001225 }
1226
1227 close(fd);
andre2ff7b5d2000-06-03 14:57:40 +00001228 return 1;
andre61e67252000-06-04 17:07:49 +00001229}
Damien Millerd5bf3072000-06-07 21:32:13 +10001230#endif /* USE_WTMPX */
andre2ff7b5d2000-06-03 14:57:40 +00001231
andre2ff7b5d2000-06-03 14:57:40 +00001232/**
andre61e67252000-06-04 17:07:49 +00001233 ** Low-level libutil login() functions
andre2ff7b5d2000-06-03 14:57:40 +00001234 **/
1235
1236#ifdef USE_LOGIN
andre2ff7b5d2000-06-03 14:57:40 +00001237static int
andre61e67252000-06-04 17:07:49 +00001238syslogin_perform_login(struct logininfo *li)
1239{
andre2ff7b5d2000-06-03 14:57:40 +00001240 struct utmp *ut;
1241
1242 if (! (ut = (struct utmp *)malloc(sizeof(struct utmp)))) {
1243 log("syslogin_perform_login: couldn't malloc()");
1244 return 0;
1245 }
1246 construct_utmp(li, ut);
1247 login(ut);
1248
1249 return 1;
andre61e67252000-06-04 17:07:49 +00001250}
1251
andre2ff7b5d2000-06-03 14:57:40 +00001252static int
andre61e67252000-06-04 17:07:49 +00001253syslogin_perform_logout(struct logininfo *li)
1254{
Damien Millerdd47aa22000-06-27 11:18:27 +10001255# ifdef HAVE_LOGOUT
andre2ff7b5d2000-06-03 14:57:40 +00001256 char line[8];
1257
1258 (void)line_stripname(line, li->line, sizeof(line));
1259
1260 if (!logout(line)) {
1261 log("syslogin_perform_logout: logout() returned an error");
Damien Millerdd47aa22000-06-27 11:18:27 +10001262# ifdef HAVE_LOGWTMP
andre2ff7b5d2000-06-03 14:57:40 +00001263 } else {
1264 logwtmp(line, "", "");
1265 }
Damien Millerdd47aa22000-06-27 11:18:27 +10001266# endif
andre6bb92372000-06-19 08:20:03 +00001267 /* FIXME: (ATL - if the need arises) What to do if we have
1268 * login, but no logout? what if logout but no logwtmp? All
1269 * routines are in libutil so they should all be there,
1270 * but... */
Damien Millerdd47aa22000-06-27 11:18:27 +10001271# endif
andre2ff7b5d2000-06-03 14:57:40 +00001272 return 1;
andre61e67252000-06-04 17:07:49 +00001273}
andre2ff7b5d2000-06-03 14:57:40 +00001274
andre2ff7b5d2000-06-03 14:57:40 +00001275int
andre61e67252000-06-04 17:07:49 +00001276syslogin_write_entry(struct logininfo *li)
1277{
andre2ff7b5d2000-06-03 14:57:40 +00001278 switch (li->type) {
1279 case LTYPE_LOGIN:
1280 return syslogin_perform_login(li);
1281 case LTYPE_LOGOUT:
1282 return syslogin_perform_logout(li);
1283 default:
1284 log("syslogin_write_entry: Invalid type field");
1285 return 0;
1286 }
andre61e67252000-06-04 17:07:49 +00001287}
Damien Millerd5bf3072000-06-07 21:32:13 +10001288#endif /* USE_LOGIN */
andre2ff7b5d2000-06-03 14:57:40 +00001289
1290/* end of file log-syslogin.c */
1291
andre2ff7b5d2000-06-03 14:57:40 +00001292/**
andre61e67252000-06-04 17:07:49 +00001293 ** Low-level lastlog functions
andre2ff7b5d2000-06-03 14:57:40 +00001294 **/
1295
1296#ifdef USE_LASTLOG
Damien Millerdd47aa22000-06-27 11:18:27 +10001297#define LL_FILE 1
1298#define LL_DIR 2
1299#define LL_OTHER 3
andre2ff7b5d2000-06-03 14:57:40 +00001300
andre2ff7b5d2000-06-03 14:57:40 +00001301static void
andre61e67252000-06-04 17:07:49 +00001302lastlog_construct(struct logininfo *li, struct lastlog *last)
1303{
andre2ff7b5d2000-06-03 14:57:40 +00001304 /* clear the structure */
1305 memset(last, '\0', sizeof(struct lastlog));
1306
Damien Millerdd47aa22000-06-27 11:18:27 +10001307 (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
andre6bb92372000-06-19 08:20:03 +00001308 strlcpy(last->ll_host, li->hostname,
1309 MIN_SIZEOF(last->ll_host, li->hostname));
andre2ff7b5d2000-06-03 14:57:40 +00001310 last->ll_time = li->tv_sec;
andre61e67252000-06-04 17:07:49 +00001311}
andre2ff7b5d2000-06-03 14:57:40 +00001312
andre2ff7b5d2000-06-03 14:57:40 +00001313static int
andre61e67252000-06-04 17:07:49 +00001314lastlog_filetype(char *filename)
1315{
andre2ff7b5d2000-06-03 14:57:40 +00001316 struct stat st;
1317
Damien Millerdd47aa22000-06-27 11:18:27 +10001318 if (stat(LASTLOG_FILE, &st) != 0) {
1319 log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
1320 strerror(errno));
andre2ff7b5d2000-06-03 14:57:40 +00001321 return 0;
1322 }
andre2ff7b5d2000-06-03 14:57:40 +00001323 if (S_ISDIR(st.st_mode))
1324 return LL_DIR;
1325 else if (S_ISREG(st.st_mode))
1326 return LL_FILE;
1327 else
1328 return LL_OTHER;
andre61e67252000-06-04 17:07:49 +00001329}
andre2ff7b5d2000-06-03 14:57:40 +00001330
1331
1332/* open the file (using filemode) and seek to the login entry */
1333static int
andre61e67252000-06-04 17:07:49 +00001334lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1335{
andre2ff7b5d2000-06-03 14:57:40 +00001336 off_t offset;
1337 int type;
1338 char lastlog_file[1024];
1339
1340 type = lastlog_filetype(LASTLOG_FILE);
1341 switch (type) {
Damien Millerf8af08d2000-06-27 09:40:06 +10001342 case LL_FILE:
1343 strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1344 break;
1345 case LL_DIR:
1346 snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1347 LASTLOG_FILE, li->username);
1348 break;
1349 default:
1350 log("lastlog_openseek: %.100s is not a file or directory!",
1351 LASTLOG_FILE);
1352 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +10001353 }
andre2ff7b5d2000-06-03 14:57:40 +00001354
1355 *fd = open(lastlog_file, filemode);
1356 if ( *fd < 0) {
Damien Miller53c5d462000-06-28 00:50:50 +10001357 debug("lastlog_openseek: Couldn't open %s: %s",
andre2ff7b5d2000-06-03 14:57:40 +00001358 lastlog_file, strerror(errno));
1359 return 0;
1360 }
Damien Millerdd47aa22000-06-27 11:18:27 +10001361
andre2ff7b5d2000-06-03 14:57:40 +00001362 /* find this uid's offset in the lastlog file */
1363 offset = (off_t) ( (long)li->uid * sizeof(struct lastlog));
1364
1365 if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1366 log("lastlog_openseek: %s->lseek(): %s",
1367 lastlog_file, strerror(errno));
1368 return 0;
1369 }
1370 return 1;
andre61e67252000-06-04 17:07:49 +00001371}
andre2ff7b5d2000-06-03 14:57:40 +00001372
1373static int
andre61e67252000-06-04 17:07:49 +00001374lastlog_perform_login(struct logininfo *li)
1375{
andre2ff7b5d2000-06-03 14:57:40 +00001376 struct lastlog last;
1377 int fd;
1378
1379 /* create our struct lastlog */
1380 lastlog_construct(li, &last);
1381
1382 /* write the entry */
Damien Miller53c5d462000-06-28 00:50:50 +10001383 if (lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) {
1384 if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) {
andre2ff7b5d2000-06-03 14:57:40 +00001385 log("lastlog_write_filemode: Error writing to %s: %s",
1386 LASTLOG_FILE, strerror(errno));
1387 return 0;
1388 }
1389 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001390 } else {
andre2ff7b5d2000-06-03 14:57:40 +00001391 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +10001392 }
andre61e67252000-06-04 17:07:49 +00001393}
andre2ff7b5d2000-06-03 14:57:40 +00001394
andre2ff7b5d2000-06-03 14:57:40 +00001395int
andre61e67252000-06-04 17:07:49 +00001396lastlog_write_entry(struct logininfo *li)
1397{
andre2ff7b5d2000-06-03 14:57:40 +00001398 switch(li->type) {
1399 case LTYPE_LOGIN:
1400 return lastlog_perform_login(li);
1401 default:
1402 log("lastlog_write_entry: Invalid type field");
1403 return 0;
1404 }
andre61e67252000-06-04 17:07:49 +00001405}
andre2ff7b5d2000-06-03 14:57:40 +00001406
andre2ff7b5d2000-06-03 14:57:40 +00001407static void
andre61e67252000-06-04 17:07:49 +00001408lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1409{
andre2ff7b5d2000-06-03 14:57:40 +00001410 line_fullname(li->line, last->ll_line, sizeof(li->line));
Damien Millerdd47aa22000-06-27 11:18:27 +10001411 strlcpy(li->hostname, last->ll_host,
andre6bb92372000-06-19 08:20:03 +00001412 MIN_SIZEOF(li->hostname, last->ll_host));
andre2ff7b5d2000-06-03 14:57:40 +00001413 li->tv_sec = last->ll_time;
andre61e67252000-06-04 17:07:49 +00001414}
andre2ff7b5d2000-06-03 14:57:40 +00001415
andre2ff7b5d2000-06-03 14:57:40 +00001416int
andre61e67252000-06-04 17:07:49 +00001417lastlog_get_entry(struct logininfo *li)
1418{
andre2ff7b5d2000-06-03 14:57:40 +00001419 struct lastlog last;
1420 int fd;
1421
1422 if (lastlog_openseek(li, &fd, O_RDONLY)) {
Damien Miller53c5d462000-06-28 00:50:50 +10001423 if (atomicio(read, fd, &last, sizeof(last)) != sizeof(last)) {
1424 log("lastlog_get_entry: Error reading from %s: %s",
andre2ff7b5d2000-06-03 14:57:40 +00001425 LASTLOG_FILE, strerror(errno));
1426 return 0;
1427 } else {
1428 lastlog_populate_entry(li, &last);
1429 return 1;
1430 }
Damien Millerdd47aa22000-06-27 11:18:27 +10001431 } else {
andre2ff7b5d2000-06-03 14:57:40 +00001432 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +10001433 }
andre61e67252000-06-04 17:07:49 +00001434}
Damien Millerd5bf3072000-06-07 21:32:13 +10001435#endif /* USE_LASTLOG */