blob: 22c2be1e5e811e709d8f96e3f97d7648e2eea362 [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:
Damien Millere5192fa2000-08-29 14:30:37 +1100135 ** homegrown ttyslot()
andre61e67252000-06-04 17:07:49 +0000136 ** test, test, test
andre2ff7b5d2000-06-03 14:57:40 +0000137 **
138 ** Platform status:
139 ** ----------------
140 **
141 ** Known good:
Damien Millere5192fa2000-08-29 14:30:37 +1100142 ** Linux (Redhat 6.2, Debian)
143 ** Solaris
andre2ff7b5d2000-06-03 14:57:40 +0000144 ** HP-UX 10.20 (gcc only)
andre6bb92372000-06-19 08:20:03 +0000145 ** IRIX
Damien Millere5192fa2000-08-29 14:30:37 +1100146 ** NeXT - M68k/HPPA (4.2/3.3)
andre2ff7b5d2000-06-03 14:57:40 +0000147 **
148 ** Testing required: Please send reports!
andre2ff7b5d2000-06-03 14:57:40 +0000149 ** NetBSD
150 ** HP-UX 11
andre60f3c982000-06-03 16:18:19 +0000151 ** AIX
andre2ff7b5d2000-06-03 14:57:40 +0000152 **
153 ** Platforms with known problems:
Damien Millere5192fa2000-08-29 14:30:37 +1100154 ** Some variants of Slackware Linux
andre2ff7b5d2000-06-03 14:57:40 +0000155 **
156 **/
157
158#include "includes.h"
159
andre2ff7b5d2000-06-03 14:57:40 +0000160#include "ssh.h"
161#include "xmalloc.h"
162#include "loginrec.h"
163
Damien Miller578783e2000-09-23 14:12:24 +1100164RCSID("$Id: loginrec.c,v 1.25 2000/09/23 03:12:25 djm Exp $");
andre2ff7b5d2000-06-03 14:57:40 +0000165
166/**
167 ** prototypes for helper functions in this file
168 **/
169
170#if HAVE_UTMP_H
andre2ff7b5d2000-06-03 14:57:40 +0000171void set_utmp_time(struct logininfo *li, struct utmp *ut);
172void construct_utmp(struct logininfo *li, struct utmp *ut);
173#endif
174
175#ifdef HAVE_UTMPX_H
andre2ff7b5d2000-06-03 14:57:40 +0000176void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
177void construct_utmpx(struct logininfo *li, struct utmpx *ut);
178#endif
179
180int utmp_write_entry(struct logininfo *li);
181int utmpx_write_entry(struct logininfo *li);
182int wtmp_write_entry(struct logininfo *li);
183int wtmpx_write_entry(struct logininfo *li);
184int lastlog_write_entry(struct logininfo *li);
185int syslogin_write_entry(struct logininfo *li);
186
187int getlast_entry(struct logininfo *li);
188int lastlog_get_entry(struct logininfo *li);
189int wtmp_get_entry(struct logininfo *li);
190int wtmpx_get_entry(struct logininfo *li);
191
andre6bb92372000-06-19 08:20:03 +0000192/* pick the shortest string */
193#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
194
andre2ff7b5d2000-06-03 14:57:40 +0000195/**
196 ** platform-independent login functions
197 **/
198
andre6bb92372000-06-19 08:20:03 +0000199/* login_login(struct logininfo *) -Record a login
200 *
201 * Call with a pointer to a struct logininfo initialised with
202 * login_init_entry() or login_alloc_entry()
203 *
204 * Returns:
205 * >0 if successful
206 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
207 */
andre61e67252000-06-04 17:07:49 +0000208int
209login_login (struct logininfo *li)
210{
211 li->type = LTYPE_LOGIN;
212 return login_write(li);
213}
214
215
andre6bb92372000-06-19 08:20:03 +0000216/* login_logout(struct logininfo *) - Record a logout
217 *
218 * Call as with login_login()
219 *
220 * Returns:
221 * >0 if successful
222 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
223 */
andre61e67252000-06-04 17:07:49 +0000224int
225login_logout(struct logininfo *li)
226{
227 li->type = LTYPE_LOGOUT;
228 return login_write(li);
229}
230
andre6bb92372000-06-19 08:20:03 +0000231/* login_get_lastlog_time(int) - Retrieve the last login time
232 *
233 * Retrieve the last login time for the given uid. Will try to use the
234 * system lastlog facilities if they are available, but will fall back
235 * to looking in wtmp/wtmpx if necessary
236 *
237 * Returns:
238 * 0 on failure, or if user has never logged in
239 * Time in seconds from the epoch if successful
240 *
241 * Useful preprocessor symbols:
242 * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
243 * info
244 * USE_LASTLOG: If set, indicates the presence of system lastlog
245 * facilities. If this and DISABLE_LASTLOG are not set,
246 * try to retrieve lastlog information from wtmp/wtmpx.
247 */
andre61e67252000-06-04 17:07:49 +0000248unsigned int
249login_get_lastlog_time(const int uid)
250{
251 struct logininfo li;
252
andre6bb92372000-06-19 08:20:03 +0000253 if (login_get_lastlog(&li, uid))
254 return li.tv_sec;
255 else
256 return 0;
andre61e67252000-06-04 17:07:49 +0000257}
258
andre6bb92372000-06-19 08:20:03 +0000259/* login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry
260 *
261 * Retrieve a logininfo structure populated (only partially) with
262 * information from the system lastlog data, or from wtmp/wtmpx if no
263 * system lastlog information exists.
264 *
265 * Note this routine must be given a pre-allocated logininfo.
266 *
267 * Returns:
268 * >0: A pointer to your struct logininfo if successful
269 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
270 *
271 */
andre61e67252000-06-04 17:07:49 +0000272struct logininfo *
273login_get_lastlog(struct logininfo *li, const int uid)
274{
andre6bb92372000-06-19 08:20:03 +0000275 struct passwd *pw;
andre6bb92372000-06-19 08:20:03 +0000276
Damien Miller348c9b72000-08-15 10:01:22 +1000277 memset(li, '\0', sizeof(*li));
andre61e67252000-06-04 17:07:49 +0000278 li->uid = uid;
andre6bb92372000-06-19 08:20:03 +0000279
Damien Miller53c5d462000-06-28 00:50:50 +1000280 /*
281 * If we don't have a 'real' lastlog, we need the username to
andre6bb92372000-06-19 08:20:03 +0000282 * reliably search wtmp(x) for the last login (see
Damien Miller53c5d462000-06-28 00:50:50 +1000283 * wtmp_get_entry().)
284 */
andre6bb92372000-06-19 08:20:03 +0000285 pw = getpwuid(uid);
Damien Millerdd47aa22000-06-27 11:18:27 +1000286 if (pw == NULL)
287 fatal("login_get_lastlog: Cannot find account for uid %i", uid);
288
andre98cabe02000-06-19 09:11:30 +0000289 /* No MIN_SIZEOF here - we absolutely *must not* truncate the
290 * username */
Damien Millerf8af08d2000-06-27 09:40:06 +1000291 strlcpy(li->username, pw->pw_name, sizeof(li->username));
Damien Millerdd47aa22000-06-27 11:18:27 +1000292
andre61e67252000-06-04 17:07:49 +0000293 if (getlast_entry(li))
294 return li;
295 else
Damien Millerdd47aa22000-06-27 11:18:27 +1000296 return NULL;
andre61e67252000-06-04 17:07:49 +0000297}
298
299
andre6bb92372000-06-19 08:20:03 +0000300/* login_alloc_entry(int, char*, char*, char*) - Allocate and initialise
301 * a logininfo structure
302 *
303 * This function creates a new struct logininfo, a data structure
304 * meant to carry the information required to portably record login info.
305 *
306 * Returns a pointer to a newly created struct logininfo. If memory
307 * allocation fails, the program halts.
308 */
andre61e67252000-06-04 17:07:49 +0000309struct
310logininfo *login_alloc_entry(int pid, const char *username,
311 const char *hostname, const char *line)
312{
andre2ff7b5d2000-06-03 14:57:40 +0000313 struct logininfo *newli;
314
Damien Miller348c9b72000-08-15 10:01:22 +1000315 newli = (struct logininfo *) xmalloc (sizeof(*newli));
andre61e67252000-06-04 17:07:49 +0000316 (void)login_init_entry(newli, pid, username, hostname, line);
317 return newli;
318}
andre2ff7b5d2000-06-03 14:57:40 +0000319
320
andre6bb92372000-06-19 08:20:03 +0000321/* login_free_entry(struct logininfo *) - free struct memory */
andre61e67252000-06-04 17:07:49 +0000322void
323login_free_entry(struct logininfo *li)
324{
325 xfree(li);
326}
327
andre2ff7b5d2000-06-03 14:57:40 +0000328
andre6bb92372000-06-19 08:20:03 +0000329/* login_init_entry(struct logininfo *, int, char*, char*, char*)
330 * - initialise a struct logininfo
331 *
332 * Populates a new struct logininfo, a data structure meant to carry
333 * the information required to portably record login info.
334 *
335 * Returns: 1
336 */
andre61e67252000-06-04 17:07:49 +0000337int
338login_init_entry(struct logininfo *li, int pid, const char *username,
339 const char *hostname, const char *line)
340{
Damien Millerf8af08d2000-06-27 09:40:06 +1000341 struct passwd *pw;
342
Damien Miller348c9b72000-08-15 10:01:22 +1000343 memset(li, 0, sizeof(*li));
andre2ff7b5d2000-06-03 14:57:40 +0000344
andre61e67252000-06-04 17:07:49 +0000345 li->pid = pid;
Damien Millerf8af08d2000-06-27 09:40:06 +1000346
andre2ff7b5d2000-06-03 14:57:40 +0000347 /* set the line information */
andre61e67252000-06-04 17:07:49 +0000348 if (line)
andre2ff7b5d2000-06-03 14:57:40 +0000349 line_fullname(li->line, line, sizeof(li->line));
andre2ff7b5d2000-06-03 14:57:40 +0000350
Damien Millerf8af08d2000-06-27 09:40:06 +1000351 if (username) {
andre2ff7b5d2000-06-03 14:57:40 +0000352 strlcpy(li->username, username, sizeof(li->username));
Damien Millerf8af08d2000-06-27 09:40:06 +1000353 pw = getpwnam(li->username);
354 if (pw == NULL)
355 fatal("login_init_entry: Cannot find user \"%s\"", li->username);
356 li->uid = pw->pw_uid;
357 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000358
andre61e67252000-06-04 17:07:49 +0000359 if (hostname)
andre2ff7b5d2000-06-03 14:57:40 +0000360 strlcpy(li->hostname, hostname, sizeof(li->hostname));
Damien Millerf8af08d2000-06-27 09:40:06 +1000361
andre61e67252000-06-04 17:07:49 +0000362 return 1;
andre2ff7b5d2000-06-03 14:57:40 +0000363}
364
andre6bb92372000-06-19 08:20:03 +0000365/* login_set_current_time(struct logininfo *) - set the current time
366 *
367 * Set the current time in a logininfo structure. This function is
368 * meant to eliminate the need to deal with system dependencies for
369 * time handling.
370 */
andre2ff7b5d2000-06-03 14:57:40 +0000371void
andre61e67252000-06-04 17:07:49 +0000372login_set_current_time(struct logininfo *li)
373{
andre2ff7b5d2000-06-03 14:57:40 +0000374 struct timeval tv;
375
376 gettimeofday(&tv, NULL);
Damien Millerf8af08d2000-06-27 09:40:06 +1000377
378 li->tv_sec = tv.tv_sec;
379 li->tv_usec = tv.tv_usec;
andre2ff7b5d2000-06-03 14:57:40 +0000380}
381
andre61e67252000-06-04 17:07:49 +0000382/* copy a sockaddr_* into our logininfo */
andre2ff7b5d2000-06-03 14:57:40 +0000383void
andre61e67252000-06-04 17:07:49 +0000384login_set_addr(struct logininfo *li, const struct sockaddr *sa,
385 const unsigned int sa_size)
386{
387 unsigned int bufsize = sa_size;
388
389 /* make sure we don't overrun our union */
390 if (sizeof(li->hostaddr) < sa_size)
391 bufsize = sizeof(li->hostaddr);
392
393 memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
andre2ff7b5d2000-06-03 14:57:40 +0000394}
395
andre2ff7b5d2000-06-03 14:57:40 +0000396
andre61e67252000-06-04 17:07:49 +0000397/**
398 ** login_write: Call low-level recording functions based on autoconf
399 ** results
400 **/
andre2ff7b5d2000-06-03 14:57:40 +0000401int
andre61e67252000-06-04 17:07:49 +0000402login_write (struct logininfo *li)
403{
Damien Millerbac2d8a2000-09-05 16:13:06 +1100404#ifndef HAVE_CYGWIN
andre2ff7b5d2000-06-03 14:57:40 +0000405 if ((int)geteuid() != 0) {
406 log("Attempt to write login records by non-root user (aborting)");
407 return 1;
408 }
Damien Millerbac2d8a2000-09-05 16:13:06 +1100409#endif
Damien Millerdd47aa22000-06-27 11:18:27 +1000410
andre2ff7b5d2000-06-03 14:57:40 +0000411 /* set the timestamp */
412 login_set_current_time(li);
413#ifdef USE_LOGIN
414 syslogin_write_entry(li);
415#endif
416#ifdef USE_LASTLOG
417 if (li->type == LTYPE_LOGIN) {
418 lastlog_write_entry(li);
419 }
420#endif
421#ifdef USE_UTMP
422 utmp_write_entry(li);
423#endif
424#ifdef USE_WTMP
425 wtmp_write_entry(li);
426#endif
427#ifdef USE_UTMPX
428 utmpx_write_entry(li);
429#endif
430#ifdef USE_WTMPX
431 wtmpx_write_entry(li);
432#endif
433 return 0;
434}
435
andre2ff7b5d2000-06-03 14:57:40 +0000436/**
andre61e67252000-06-04 17:07:49 +0000437 ** getlast_entry: Call low-level functions to retrieve the last login
438 ** time.
andre2ff7b5d2000-06-03 14:57:40 +0000439 **/
440
andre61e67252000-06-04 17:07:49 +0000441/* take the uid in li and return the last login time */
442int
443getlast_entry(struct logininfo *li)
444{
445#ifdef USE_LASTLOG
Damien Miller53c5d462000-06-28 00:50:50 +1000446 return(lastlog_get_entry(li));
Damien Millerdd47aa22000-06-27 11:18:27 +1000447#else /* !USE_LASTLOG */
andre61e67252000-06-04 17:07:49 +0000448
Damien Millerdd47aa22000-06-27 11:18:27 +1000449#ifdef DISABLE_LASTLOG
andreecaabf12000-06-12 22:21:44 +0000450 /* On some systems we shouldn't even try to obtain last login
451 * time, e.g. AIX */
452 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +1000453# else /* DISABLE_LASTLOG */
andre61e67252000-06-04 17:07:49 +0000454 /* Try to retrieve the last login time from wtmp */
Damien Millerdd47aa22000-06-27 11:18:27 +1000455# if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
andre61e67252000-06-04 17:07:49 +0000456 /* retrieve last login time from utmp */
Damien Millerdd47aa22000-06-27 11:18:27 +1000457 return (wtmp_get_entry(li));
458# else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */
andre61e67252000-06-04 17:07:49 +0000459 /* If wtmp isn't available, try wtmpx */
Damien Millerdd47aa22000-06-27 11:18:27 +1000460# if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
andre61e67252000-06-04 17:07:49 +0000461 /* retrieve last login time from utmpx */
Damien Millerdd47aa22000-06-27 11:18:27 +1000462 return (wtmpx_get_entry(li));
463# else
andre61e67252000-06-04 17:07:49 +0000464 /* Give up: No means of retrieving last login time */
465 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +1000466# endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
467# endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
468# endif /* DISABLE_LASTLOG */
469#endif /* USE_LASTLOG */
andre61e67252000-06-04 17:07:49 +0000470}
471
472
473
andre2ff7b5d2000-06-03 14:57:40 +0000474/*
andre61e67252000-06-04 17:07:49 +0000475 * 'line' string utility functions
476 *
477 * These functions process the 'line' string into one of three forms:
478 *
andre2ff7b5d2000-06-03 14:57:40 +0000479 * 1. The full filename (including '/dev')
480 * 2. The stripped name (excluding '/dev')
andre61e67252000-06-04 17:07:49 +0000481 * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
482 * /dev/pts/1 -> ts/1 )
andre2ff7b5d2000-06-03 14:57:40 +0000483 *
484 * Form 3 is used on some systems to identify a .tmp.? entry when
485 * attempting to remove it. Typically both addition and removal is
andre61e67252000-06-04 17:07:49 +0000486 * performed by one application - say, sshd - so as long as the choice
487 * uniquely identifies a terminal it's ok.
andre2ff7b5d2000-06-03 14:57:40 +0000488 */
489
490
andre61e67252000-06-04 17:07:49 +0000491/* line_fullname(): add the leading '/dev/' if it doesn't exist make
492 * sure dst has enough space, if not just copy src (ugh) */
andre2ff7b5d2000-06-03 14:57:40 +0000493char *
andre61e67252000-06-04 17:07:49 +0000494line_fullname(char *dst, const char *src, int dstsize)
495{
andre2ff7b5d2000-06-03 14:57:40 +0000496 memset(dst, '\0', dstsize);
497 if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))
498 strlcpy(dst, src, dstsize);
499 else {
Damien Miller1a132252000-06-13 21:23:17 +1000500 strlcpy(dst, "/dev/", dstsize);
andre2ff7b5d2000-06-03 14:57:40 +0000501 strlcat(dst, src, dstsize);
502 }
503 return dst;
504}
505
andre61e67252000-06-04 17:07:49 +0000506/* line_stripname(): strip the leading '/dev' if it exists, return dst */
andre2ff7b5d2000-06-03 14:57:40 +0000507char *
andre61e67252000-06-04 17:07:49 +0000508line_stripname(char *dst, const char *src, int dstsize)
509{
andre2ff7b5d2000-06-03 14:57:40 +0000510 memset(dst, '\0', dstsize);
511 if (strncmp(src, "/dev/", 5) == 0)
512 strlcpy(dst, &src[5], dstsize);
513 else
514 strlcpy(dst, src, dstsize);
515 return dst;
andre61e67252000-06-04 17:07:49 +0000516}
517
andre61e67252000-06-04 17:07:49 +0000518/* line_abbrevname(): Return the abbreviated (usually four-character)
519 * form of the line (Just use the last <dstsize> characters of the
520 * full name.)
521 *
522 * NOTE: use strncpy because we do NOT necessarily want zero
523 * termination */
andre2ff7b5d2000-06-03 14:57:40 +0000524char *
Damien Millerdd47aa22000-06-27 11:18:27 +1000525line_abbrevname(char *dst, const char *src, int dstsize)
526{
527 size_t len;
528
andre2ff7b5d2000-06-03 14:57:40 +0000529 memset(dst, '\0', dstsize);
Damien Millerdd47aa22000-06-27 11:18:27 +1000530
Damien Miller8e81ed32000-07-01 13:17:42 +1000531 /* Always skip prefix if present */
532 if (strncmp(src, "/dev/", 5) == 0)
533 src += 5;
534
Damien Millerdd47aa22000-06-27 11:18:27 +1000535 len = strlen(src);
536
Damien Miller8e81ed32000-07-01 13:17:42 +1000537 if (len > 0) {
538 if (((int)len - dstsize) > 0)
539 src += ((int)len - dstsize);
540
541 /* note: _don't_ change this to strlcpy */
542 strncpy(dst, src, (size_t)dstsize);
Damien Millerdd47aa22000-06-27 11:18:27 +1000543 }
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{
Damien Miller348c9b72000-08-15 10:01:22 +1000575 memset(ut, '\0', sizeof(*ut));
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 */
Damien Miller7a0e5dc2000-07-11 12:15:54 +1000612 strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, 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{
Damien Miller348c9b72000-08-15 10:01:22 +1000649 memset(utx, '\0', sizeof(*utx));
Damien Miller8e81ed32000-07-01 13:17:42 +1000650# ifdef HAVE_ID_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000651 line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
Damien Miller8e81ed32000-07-01 13:17:42 +1000652# endif
andre2ff7b5d2000-06-03 14:57:40 +0000653
654 /* this is done here to keep utmp constants out of loginrec.h */
655 switch (li->type) {
656 case LTYPE_LOGIN:
657 utx->ut_type = USER_PROCESS;
658 break;
659 case LTYPE_LOGOUT:
660 utx->ut_type = DEAD_PROCESS;
661 break;
662 }
andre2ff7b5d2000-06-03 14:57:40 +0000663 line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
andre2ff7b5d2000-06-03 14:57:40 +0000664 set_utmpx_time(li, utx);
andre6bb92372000-06-19 08:20:03 +0000665 utx->ut_pid = li->pid;
666
667 if (li->type == LTYPE_LOGOUT)
668 return;
669
Damien Millerdd47aa22000-06-27 11:18:27 +1000670 /*
671 * These fields are only used when logging in, and are blank
672 * for logouts.
673 */
andre6bb92372000-06-19 08:20:03 +0000674
675 /* strncpy(): Don't necessarily want null termination */
Damien Miller7a0e5dc2000-07-11 12:15:54 +1000676 strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
Damien Millerdd47aa22000-06-27 11:18:27 +1000677# ifdef HAVE_HOST_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +0000678 strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
Damien Millerdd47aa22000-06-27 11:18:27 +1000679# endif
680# ifdef HAVE_ADDR_IN_UTMPX
Damien Millerd6f204d2000-09-23 13:57:27 +1100681 /* this is just a 32-bit IP address */
682 if (li->hostaddr.sa.sa_family == AF_INET)
683 utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
Damien Millerdd47aa22000-06-27 11:18:27 +1000684# endif
685# ifdef HAVE_SYSLEN_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +0000686 /* ut_syslen is the length of the utx_host string */
687 utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
Damien Millerdd47aa22000-06-27 11:18:27 +1000688# endif
andre61e67252000-06-04 17:07:49 +0000689}
Damien Millerdd47aa22000-06-27 11:18:27 +1000690#endif /* USE_UTMPX || USE_WTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000691
692/**
andre61e67252000-06-04 17:07:49 +0000693 ** Low-level utmp functions
andre2ff7b5d2000-06-03 14:57:40 +0000694 **/
695
696/* FIXME: (ATL) utmp_write_direct needs testing */
andre2ff7b5d2000-06-03 14:57:40 +0000697#ifdef USE_UTMP
698
andre2ff7b5d2000-06-03 14:57:40 +0000699/* if we can, use pututline() etc. */
Damien Millerdd47aa22000-06-27 11:18:27 +1000700# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
701 defined(HAVE_PUTUTLINE)
andre2ff7b5d2000-06-03 14:57:40 +0000702# define UTMP_USE_LIBRARY
Damien Millerdd47aa22000-06-27 11:18:27 +1000703# endif
andre2ff7b5d2000-06-03 14:57:40 +0000704
705
706/* write a utmp entry with the system's help (pututline() and pals) */
Damien Millerdd47aa22000-06-27 11:18:27 +1000707# ifdef UTMP_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000708static int
andre61e67252000-06-04 17:07:49 +0000709utmp_write_library(struct logininfo *li, struct utmp *ut)
710{
andre2ff7b5d2000-06-03 14:57:40 +0000711 setutent();
712 pututline(ut);
713
Damien Millerdd47aa22000-06-27 11:18:27 +1000714# ifdef HAVE_ENDUTENT
andre2ff7b5d2000-06-03 14:57:40 +0000715 endutent();
Damien Millerdd47aa22000-06-27 11:18:27 +1000716# endif
andre2ff7b5d2000-06-03 14:57:40 +0000717 return 1;
andre61e67252000-06-04 17:07:49 +0000718}
Damien Millerdd47aa22000-06-27 11:18:27 +1000719# else /* UTMP_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000720
721/* write a utmp entry direct to the file */
andre61e67252000-06-04 17:07:49 +0000722/* This is a slightly modification of code in OpenBSD's login.c */
andre2ff7b5d2000-06-03 14:57:40 +0000723static int
andre61e67252000-06-04 17:07:49 +0000724utmp_write_direct(struct logininfo *li, struct utmp *ut)
725{
andre2ff7b5d2000-06-03 14:57:40 +0000726 struct utmp old_ut;
727 register int fd;
728 int tty;
729
andre6bb92372000-06-19 08:20:03 +0000730 /* FIXME: (ATL) ttyslot() needs local implementation */
Damien Miller348c9b72000-08-15 10:01:22 +1000731
Damien Millere5192fa2000-08-29 14:30:37 +1100732#if defined(HAVE_GETTTYENT)
Damien Miller348c9b72000-08-15 10:01:22 +1000733 register struct ttyent *ty;
734
735 tty=0;
736
737 setttyent();
738 while ((struct ttyent *)0 != (ty = getttyent())) {
739 tty++;
740 if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
741 break;
742 }
743 endttyent();
744
745 if((struct ttyent *)0 == ty) {
746 log("utmp_write_entry: tty not found");
747 return(1);
748 }
749#else /* FIXME */
750
andre2ff7b5d2000-06-03 14:57:40 +0000751 tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
752
Damien Millere5192fa2000-08-29 14:30:37 +1100753#endif /* HAVE_GETTTYENT */
Damien Miller348c9b72000-08-15 10:01:22 +1000754
andre2ff7b5d2000-06-03 14:57:40 +0000755 if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
756 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
757 /*
758 * Prevent luser from zero'ing out ut_host.
759 * If the new ut_line is empty but the old one is not
Damien Miller7a0e5dc2000-07-11 12:15:54 +1000760 * and ut_line and ut_name match, preserve the old ut_line.
andre2ff7b5d2000-06-03 14:57:40 +0000761 */
Damien Miller53c5d462000-06-28 00:50:50 +1000762 if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
763 (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
764 (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
Damien Miller7a0e5dc2000-07-11 12:15:54 +1000765 (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
andre2ff7b5d2000-06-03 14:57:40 +0000766 (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
Damien Miller53c5d462000-06-28 00:50:50 +1000767 }
768
andre2ff7b5d2000-06-03 14:57:40 +0000769 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
Damien Miller36ccb5c2000-08-09 16:34:27 +1000770 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut))
andre2ff7b5d2000-06-03 14:57:40 +0000771 log("utmp_write_direct: error writing %s: %s",
andre6bb92372000-06-19 08:20:03 +0000772 UTMP_FILE, strerror(errno));
andre2ff7b5d2000-06-03 14:57:40 +0000773
774 (void)close(fd);
775 return 1;
Damien Miller53c5d462000-06-28 00:50:50 +1000776 } else {
andre2ff7b5d2000-06-03 14:57:40 +0000777 return 0;
Damien Miller53c5d462000-06-28 00:50:50 +1000778 }
andre61e67252000-06-04 17:07:49 +0000779}
Damien Millerdd47aa22000-06-27 11:18:27 +1000780# endif /* UTMP_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000781
782static int
andre61e67252000-06-04 17:07:49 +0000783utmp_perform_login(struct logininfo *li)
784{
andre2ff7b5d2000-06-03 14:57:40 +0000785 struct utmp ut;
786
787 construct_utmp(li, &ut);
Damien Millerdd47aa22000-06-27 11:18:27 +1000788# ifdef UTMP_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000789 if (!utmp_write_library(li, &ut)) {
andre6bb92372000-06-19 08:20:03 +0000790 log("utmp_perform_login: utmp_write_library() failed");
andre2ff7b5d2000-06-03 14:57:40 +0000791 return 0;
792 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000793# else
andre2ff7b5d2000-06-03 14:57:40 +0000794 if (!utmp_write_direct(li, &ut)) {
795 log("utmp_perform_login: utmp_write_direct() failed");
796 return 0;
797 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000798# endif
andre2ff7b5d2000-06-03 14:57:40 +0000799 return 1;
andre61e67252000-06-04 17:07:49 +0000800}
andre2ff7b5d2000-06-03 14:57:40 +0000801
802
803static int
andre61e67252000-06-04 17:07:49 +0000804utmp_perform_logout(struct logininfo *li)
805{
andre2ff7b5d2000-06-03 14:57:40 +0000806 struct utmp ut;
807
andre6bb92372000-06-19 08:20:03 +0000808 construct_utmp(li, &ut);
Damien Millerdd47aa22000-06-27 11:18:27 +1000809# ifdef UTMP_USE_LIBRARY
andre6bb92372000-06-19 08:20:03 +0000810 if (!utmp_write_library(li, &ut)) {
811 log("utmp_perform_logout: utmp_write_library() failed");
812 return 0;
813 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000814# else
andre6bb92372000-06-19 08:20:03 +0000815 if (!utmp_write_direct(li, &ut)) {
816 log("utmp_perform_logout: utmp_write_direct() failed");
817 return 0;
818 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000819# endif
andre2ff7b5d2000-06-03 14:57:40 +0000820 return 1;
andre61e67252000-06-04 17:07:49 +0000821}
andre2ff7b5d2000-06-03 14:57:40 +0000822
823
824int
andre61e67252000-06-04 17:07:49 +0000825utmp_write_entry(struct logininfo *li)
826{
andre2ff7b5d2000-06-03 14:57:40 +0000827 switch(li->type) {
828 case LTYPE_LOGIN:
829 return utmp_perform_login(li);
830
831 case LTYPE_LOGOUT:
832 return utmp_perform_logout(li);
833
834 default:
835 log("utmp_write_entry: invalid type field");
836 return 0;
837 }
andre61e67252000-06-04 17:07:49 +0000838}
Damien Millerdd47aa22000-06-27 11:18:27 +1000839#endif /* USE_UTMP */
andre2ff7b5d2000-06-03 14:57:40 +0000840
841
842/**
andre61e67252000-06-04 17:07:49 +0000843 ** Low-level utmpx functions
andre2ff7b5d2000-06-03 14:57:40 +0000844 **/
845
846/* not much point if we don't want utmpx entries */
847#ifdef USE_UTMPX
848
andre2ff7b5d2000-06-03 14:57:40 +0000849/* if we have the wherewithall, use pututxline etc. */
Damien Millerdd47aa22000-06-27 11:18:27 +1000850# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
851 defined(HAVE_PUTUTXLINE)
andre2ff7b5d2000-06-03 14:57:40 +0000852# define UTMPX_USE_LIBRARY
Damien Millerdd47aa22000-06-27 11:18:27 +1000853# endif
andre2ff7b5d2000-06-03 14:57:40 +0000854
855
856/* write a utmpx entry with the system's help (pututxline() and pals) */
Damien Millerdd47aa22000-06-27 11:18:27 +1000857# ifdef UTMPX_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000858static int
andre61e67252000-06-04 17:07:49 +0000859utmpx_write_library(struct logininfo *li, struct utmpx *utx)
860{
andre2ff7b5d2000-06-03 14:57:40 +0000861 setutxent();
862 pututxline(utx);
863
Damien Millerdd47aa22000-06-27 11:18:27 +1000864# ifdef HAVE_ENDUTXENT
andre2ff7b5d2000-06-03 14:57:40 +0000865 endutxent();
Damien Millerdd47aa22000-06-27 11:18:27 +1000866# endif
andre2ff7b5d2000-06-03 14:57:40 +0000867 return 1;
andre61e67252000-06-04 17:07:49 +0000868}
andre2ff7b5d2000-06-03 14:57:40 +0000869
Damien Millerdd47aa22000-06-27 11:18:27 +1000870# else /* UTMPX_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000871
872/* write a utmp entry direct to the file */
873static int
andre61e67252000-06-04 17:07:49 +0000874utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
875{
andre2ff7b5d2000-06-03 14:57:40 +0000876 log("utmpx_write_direct: not implemented!");
877 return 0;
andre61e67252000-06-04 17:07:49 +0000878}
Damien Millerdd47aa22000-06-27 11:18:27 +1000879# endif /* UTMPX_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000880
881static int
andre61e67252000-06-04 17:07:49 +0000882utmpx_perform_login(struct logininfo *li)
883{
andre2ff7b5d2000-06-03 14:57:40 +0000884 struct utmpx utx;
885
886 construct_utmpx(li, &utx);
Damien Millerdd47aa22000-06-27 11:18:27 +1000887# ifdef UTMPX_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000888 if (!utmpx_write_library(li, &utx)) {
889 log("utmpx_perform_login: utmp_write_library() failed");
890 return 0;
891 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000892# else
andre2ff7b5d2000-06-03 14:57:40 +0000893 if (!utmpx_write_direct(li, &ut)) {
894 log("utmpx_perform_login: utmp_write_direct() failed");
895 return 0;
896 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000897# endif
andre2ff7b5d2000-06-03 14:57:40 +0000898 return 1;
andre61e67252000-06-04 17:07:49 +0000899}
andre2ff7b5d2000-06-03 14:57:40 +0000900
901
902static int
andre61e67252000-06-04 17:07:49 +0000903utmpx_perform_logout(struct logininfo *li)
904{
andre2ff7b5d2000-06-03 14:57:40 +0000905 struct utmpx utx;
906
907 memset(&utx, '\0', sizeof(utx));
908 set_utmpx_time(li, &utx);
909 line_stripname(utx.ut_line, li->line, sizeof(utx.ut_line));
Damien Millerdd47aa22000-06-27 11:18:27 +1000910# ifdef HAVE_ID_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000911 line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
Damien Millerdd47aa22000-06-27 11:18:27 +1000912# endif
913# ifdef HAVE_TYPE_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000914 utx.ut_type = DEAD_PROCESS;
Damien Millerdd47aa22000-06-27 11:18:27 +1000915# endif
andre2ff7b5d2000-06-03 14:57:40 +0000916
Damien Millerdd47aa22000-06-27 11:18:27 +1000917# ifdef UTMPX_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000918 utmpx_write_library(li, &utx);
Damien Millerdd47aa22000-06-27 11:18:27 +1000919# else
andre2ff7b5d2000-06-03 14:57:40 +0000920 utmpx_write_direct(li, &utx);
Damien Millerdd47aa22000-06-27 11:18:27 +1000921# endif
andre2ff7b5d2000-06-03 14:57:40 +0000922 return 1;
andre61e67252000-06-04 17:07:49 +0000923}
andre2ff7b5d2000-06-03 14:57:40 +0000924
andre2ff7b5d2000-06-03 14:57:40 +0000925int
andre61e67252000-06-04 17:07:49 +0000926utmpx_write_entry(struct logininfo *li)
927{
andre2ff7b5d2000-06-03 14:57:40 +0000928 switch(li->type) {
929 case LTYPE_LOGIN:
930 return utmpx_perform_login(li);
931 case LTYPE_LOGOUT:
932 return utmpx_perform_logout(li);
933 default:
934 log("utmpx_write_entry: invalid type field");
935 return 0;
936 }
andre61e67252000-06-04 17:07:49 +0000937}
Damien Millerdd47aa22000-06-27 11:18:27 +1000938#endif /* USE_UTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000939
940
941/**
andre61e67252000-06-04 17:07:49 +0000942 ** Low-level wtmp functions
andre2ff7b5d2000-06-03 14:57:40 +0000943 **/
944
945#ifdef USE_WTMP
946
andre2ff7b5d2000-06-03 14:57:40 +0000947/* write a wtmp entry direct to the end of the file */
andre61e67252000-06-04 17:07:49 +0000948/* This is a slight modification of code in OpenBSD's logwtmp.c */
andre2ff7b5d2000-06-03 14:57:40 +0000949static int
andre61e67252000-06-04 17:07:49 +0000950wtmp_write(struct logininfo *li, struct utmp *ut)
951{
andre2ff7b5d2000-06-03 14:57:40 +0000952 struct stat buf;
953 int fd, ret = 1;
954
955 if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
956 log("wtmp_write: problem writing %s: %s",
957 WTMP_FILE, strerror(errno));
958 return 0;
959 }
andre61e67252000-06-04 17:07:49 +0000960 if (fstat(fd, &buf) == 0)
Damien Miller53c5d462000-06-28 00:50:50 +1000961 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
andre2ff7b5d2000-06-03 14:57:40 +0000962 ftruncate(fd, buf.st_size);
963 log("wtmp_write: problem writing %s: %s",
964 WTMP_FILE, strerror(errno));
965 ret = 0;
966 }
967 (void)close(fd);
andre2ff7b5d2000-06-03 14:57:40 +0000968 return ret;
andre61e67252000-06-04 17:07:49 +0000969}
andre2ff7b5d2000-06-03 14:57:40 +0000970
andre2ff7b5d2000-06-03 14:57:40 +0000971static int
Damien Millerdd47aa22000-06-27 11:18:27 +1000972wtmp_perform_login(struct logininfo *li)
973{
andre2ff7b5d2000-06-03 14:57:40 +0000974 struct utmp ut;
975
976 construct_utmp(li, &ut);
977 return wtmp_write(li, &ut);
andre61e67252000-06-04 17:07:49 +0000978}
andre2ff7b5d2000-06-03 14:57:40 +0000979
980
981static int
andre61e67252000-06-04 17:07:49 +0000982wtmp_perform_logout(struct logininfo *li)
983{
andre2ff7b5d2000-06-03 14:57:40 +0000984 struct utmp ut;
985
986 construct_utmp(li, &ut);
andre2ff7b5d2000-06-03 14:57:40 +0000987 return wtmp_write(li, &ut);
andre61e67252000-06-04 17:07:49 +0000988}
andre2ff7b5d2000-06-03 14:57:40 +0000989
990
991int
andre61e67252000-06-04 17:07:49 +0000992wtmp_write_entry(struct logininfo *li)
993{
andre2ff7b5d2000-06-03 14:57:40 +0000994 switch(li->type) {
995 case LTYPE_LOGIN:
996 return wtmp_perform_login(li);
997 case LTYPE_LOGOUT:
998 return wtmp_perform_logout(li);
999 default:
1000 log("wtmp_write_entry: invalid type field");
1001 return 0;
1002 }
andre61e67252000-06-04 17:07:49 +00001003}
andre2ff7b5d2000-06-03 14:57:40 +00001004
1005
andre6bb92372000-06-19 08:20:03 +00001006/* Notes on fetching login data from wtmp/wtmpx
1007 *
1008 * Logouts are usually recorded with (amongst other things) a blank
1009 * username on a given tty line. However, some systems (HP-UX is one)
1010 * leave all fields set, but change the ut_type field to DEAD_PROCESS.
1011 *
1012 * Since we're only looking for logins here, we know that the username
1013 * must be set correctly. On systems that leave it in, we check for
1014 * ut_type==USER_PROCESS (indicating a login.)
1015 *
1016 * Portability: Some systems may set something other than USER_PROCESS
1017 * to indicate a login process. I don't know of any as I write. Also,
1018 * it's possible that some systems may both leave the username in
1019 * place and not have ut_type.
1020 */
1021
andre6bb92372000-06-19 08:20:03 +00001022/* return true if this wtmp entry indicates a login */
1023static int
1024wtmp_islogin(struct logininfo *li, struct utmp *ut)
1025{
Damien Miller7a0e5dc2000-07-11 12:15:54 +10001026 if (strncmp(li->username, ut->ut_name,
1027 MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
Damien Millerdd47aa22000-06-27 11:18:27 +10001028# ifdef HAVE_TYPE_IN_UTMP
andre6bb92372000-06-19 08:20:03 +00001029 if (ut->ut_type & USER_PROCESS)
1030 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001031# else
andre6bb92372000-06-19 08:20:03 +00001032 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001033# endif
andre6bb92372000-06-19 08:20:03 +00001034 }
1035 return 0;
1036}
1037
andre2ff7b5d2000-06-03 14:57:40 +00001038int
andre61e67252000-06-04 17:07:49 +00001039wtmp_get_entry(struct logininfo *li)
1040{
andre2ff7b5d2000-06-03 14:57:40 +00001041 struct stat st;
1042 struct utmp ut;
andre6bb92372000-06-19 08:20:03 +00001043 int fd, found=0;
1044
1045 /* Clear the time entries in our logininfo */
1046 li->tv_sec = li->tv_usec = 0;
andre2ff7b5d2000-06-03 14:57:40 +00001047
1048 if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1049 log("wtmp_get_entry: problem opening %s: %s",
1050 WTMP_FILE, strerror(errno));
1051 return 0;
1052 }
andre61e67252000-06-04 17:07:49 +00001053 if (fstat(fd, &st) != 0) {
andre2ff7b5d2000-06-03 14:57:40 +00001054 log("wtmp_get_entry: couldn't stat %s: %s",
1055 WTMP_FILE, strerror(errno));
1056 close(fd);
1057 return 0;
1058 }
andre2ff7b5d2000-06-03 14:57:40 +00001059
andre6bb92372000-06-19 08:20:03 +00001060 /* Seek to the start of the last struct utmp */
Damien Miller348c9b72000-08-15 10:01:22 +10001061 if (lseek(fd, (off_t)(0 - sizeof(struct utmp)), SEEK_END) == -1) {
andre6bb92372000-06-19 08:20:03 +00001062 /* Looks like we've got a fresh wtmp file */
1063 close(fd);
1064 return 0;
1065 }
1066
1067 while (!found) {
Damien Miller53c5d462000-06-28 00:50:50 +10001068 if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
andre2ff7b5d2000-06-03 14:57:40 +00001069 log("wtmp_get_entry: read of %s failed: %s",
1070 WTMP_FILE, strerror(errno));
1071 close (fd);
1072 return 0;
1073 }
andre6bb92372000-06-19 08:20:03 +00001074 if ( wtmp_islogin(li, &ut) ) {
1075 found = 1;
1076 /* We've already checked for a time in struct
1077 * utmp, in login_getlast(). */
Damien Millerdd47aa22000-06-27 11:18:27 +10001078# ifdef HAVE_TIME_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +00001079 li->tv_sec = ut.ut_time;
Damien Millerdd47aa22000-06-27 11:18:27 +10001080# else
andre2ff7b5d2000-06-03 14:57:40 +00001081# if HAVE_TV_IN_UTMP
1082 li->tv_sec = ut.ut_tv.tv_sec;
1083# endif
Damien Millerdd47aa22000-06-27 11:18:27 +10001084# endif
andre6bb92372000-06-19 08:20:03 +00001085 line_fullname(li->line, ut.ut_line,
1086 MIN_SIZEOF(li->line, ut.ut_line));
Damien Millerdd47aa22000-06-27 11:18:27 +10001087# ifdef HAVE_HOST_IN_UTMP
andre6bb92372000-06-19 08:20:03 +00001088 strlcpy(li->hostname, ut.ut_host,
1089 MIN_SIZEOF(li->hostname, ut.ut_host));
Damien Millerdd47aa22000-06-27 11:18:27 +10001090# endif
andre6bb92372000-06-19 08:20:03 +00001091 continue;
andre2ff7b5d2000-06-03 14:57:40 +00001092 }
andre6bb92372000-06-19 08:20:03 +00001093 /* Seek back 2 x struct utmp */
andre2ff7b5d2000-06-03 14:57:40 +00001094 if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) {
andre6bb92372000-06-19 08:20:03 +00001095 /* We've found the start of the file, so quit */
andre2ff7b5d2000-06-03 14:57:40 +00001096 close (fd);
1097 return 0;
1098 }
andre6bb92372000-06-19 08:20:03 +00001099 }
1100
1101 /* We found an entry. Tidy up and return */
1102 close(fd);
andre2ff7b5d2000-06-03 14:57:40 +00001103 return 1;
andre61e67252000-06-04 17:07:49 +00001104}
Damien Millerdd47aa22000-06-27 11:18:27 +10001105# endif /* USE_WTMP */
andre2ff7b5d2000-06-03 14:57:40 +00001106
1107
1108/**
andre61e67252000-06-04 17:07:49 +00001109 ** Low-level wtmpx functions
andre2ff7b5d2000-06-03 14:57:40 +00001110 **/
1111
1112#ifdef USE_WTMPX
andre2ff7b5d2000-06-03 14:57:40 +00001113/* write a wtmpx entry direct to the end of the file */
andre61e67252000-06-04 17:07:49 +00001114/* This is a slight modification of code in OpenBSD's logwtmp.c */
andre2ff7b5d2000-06-03 14:57:40 +00001115static int
andre61e67252000-06-04 17:07:49 +00001116wtmpx_write(struct logininfo *li, struct utmpx *utx)
1117{
andre2ff7b5d2000-06-03 14:57:40 +00001118 struct stat buf;
1119 int fd, ret = 1;
1120
1121 if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1122 log("wtmpx_write: problem opening %s: %s",
1123 WTMPX_FILE, strerror(errno));
1124 return 0;
1125 }
1126
1127 if (fstat(fd, &buf) == 0)
Damien Miller53c5d462000-06-28 00:50:50 +10001128 if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
andre2ff7b5d2000-06-03 14:57:40 +00001129 ftruncate(fd, buf.st_size);
1130 log("wtmpx_write: problem writing %s: %s",
1131 WTMPX_FILE, strerror(errno));
1132 ret = 0;
1133 }
1134 (void)close(fd);
1135
1136 return ret;
andre61e67252000-06-04 17:07:49 +00001137}
andre2ff7b5d2000-06-03 14:57:40 +00001138
1139
1140static int
andre61e67252000-06-04 17:07:49 +00001141wtmpx_perform_login(struct logininfo *li)
1142{
andre2ff7b5d2000-06-03 14:57:40 +00001143 struct utmpx utx;
1144
1145 construct_utmpx(li, &utx);
1146 return wtmpx_write(li, &utx);
andre61e67252000-06-04 17:07:49 +00001147}
andre2ff7b5d2000-06-03 14:57:40 +00001148
1149
1150static int
andre61e67252000-06-04 17:07:49 +00001151wtmpx_perform_logout(struct logininfo *li)
1152{
andre2ff7b5d2000-06-03 14:57:40 +00001153 struct utmpx utx;
1154
1155 construct_utmpx(li, &utx);
andre2ff7b5d2000-06-03 14:57:40 +00001156 return wtmpx_write(li, &utx);
andre61e67252000-06-04 17:07:49 +00001157}
andre2ff7b5d2000-06-03 14:57:40 +00001158
1159
1160int
andre61e67252000-06-04 17:07:49 +00001161wtmpx_write_entry(struct logininfo *li)
1162{
andre2ff7b5d2000-06-03 14:57:40 +00001163 switch(li->type) {
1164 case LTYPE_LOGIN:
1165 return wtmpx_perform_login(li);
1166 case LTYPE_LOGOUT:
1167 return wtmpx_perform_logout(li);
1168 default:
1169 log("wtmpx_write_entry: invalid type field");
1170 return 0;
1171 }
andre61e67252000-06-04 17:07:49 +00001172}
andre2ff7b5d2000-06-03 14:57:40 +00001173
andre6bb92372000-06-19 08:20:03 +00001174/* Please see the notes above wtmp_islogin() for information about the
1175 next two functions */
1176
1177/* Return true if this wtmpx entry indicates a login */
1178static int
1179wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1180{
Damien Miller7a0e5dc2000-07-11 12:15:54 +10001181 if ( strncmp(li->username, utx->ut_name,
1182 MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
Damien Millerdd47aa22000-06-27 11:18:27 +10001183# ifdef HAVE_TYPE_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +00001184 if (utx->ut_type == USER_PROCESS)
1185 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001186# else
andre6bb92372000-06-19 08:20:03 +00001187 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001188# endif
andre6bb92372000-06-19 08:20:03 +00001189 }
1190 return 0;
1191}
1192
andre2ff7b5d2000-06-03 14:57:40 +00001193
1194int
andre61e67252000-06-04 17:07:49 +00001195wtmpx_get_entry(struct logininfo *li)
1196{
andre2ff7b5d2000-06-03 14:57:40 +00001197 struct stat st;
1198 struct utmpx utx;
andre6bb92372000-06-19 08:20:03 +00001199 int fd, found=0;
1200
1201 /* Clear the time entries */
1202 li->tv_sec = li->tv_usec = 0;
andre2ff7b5d2000-06-03 14:57:40 +00001203
1204 if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1205 log("wtmpx_get_entry: problem opening %s: %s",
1206 WTMPX_FILE, strerror(errno));
1207 return 0;
1208 }
andre61e67252000-06-04 17:07:49 +00001209 if (fstat(fd, &st) != 0) {
andre2ff7b5d2000-06-03 14:57:40 +00001210 log("wtmpx_get_entry: couldn't stat %s: %s",
1211 WTMP_FILE, strerror(errno));
1212 close(fd);
1213 return 0;
1214 }
andre6bb92372000-06-19 08:20:03 +00001215
1216 /* Seek to the start of the last struct utmpx */
1217 if (lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END) == -1 ) {
1218 /* probably a newly rotated wtmpx file */
1219 close(fd);
1220 return 0;
1221 }
andre2ff7b5d2000-06-03 14:57:40 +00001222
andre6bb92372000-06-19 08:20:03 +00001223 while (!found) {
Damien Miller53c5d462000-06-28 00:50:50 +10001224 if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
andre2ff7b5d2000-06-03 14:57:40 +00001225 log("wtmpx_get_entry: read of %s failed: %s",
1226 WTMPX_FILE, strerror(errno));
1227 close (fd);
1228 return 0;
1229 }
andre2ff7b5d2000-06-03 14:57:40 +00001230 /* Logouts are recorded as a blank username on a particular line.
1231 * So, we just need to find the username in struct utmpx */
andre6bb92372000-06-19 08:20:03 +00001232 if ( wtmpx_islogin(li, &utx) ) {
Damien Millerdd47aa22000-06-27 11:18:27 +10001233# ifdef HAVE_TV_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +00001234 li->tv_sec = utx.ut_tv.tv_sec;
Damien Millerdd47aa22000-06-27 11:18:27 +10001235# else
andre2ff7b5d2000-06-03 14:57:40 +00001236# ifdef HAVE_TIME_IN_UTMPX
1237 li->tv_sec = utx.ut_time;
1238# endif
Damien Millerdd47aa22000-06-27 11:18:27 +10001239# endif
Damien Miller1a132252000-06-13 21:23:17 +10001240 line_fullname(li->line, utx.ut_line, sizeof(li->line));
Damien Millerdd47aa22000-06-27 11:18:27 +10001241# ifdef HAVE_HOST_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +00001242 strlcpy(li->hostname, utx.ut_host,
1243 MIN_SIZEOF(li->hostname, utx.ut_host));
Damien Millerdd47aa22000-06-27 11:18:27 +10001244# endif
andre6bb92372000-06-19 08:20:03 +00001245 continue;
andre2ff7b5d2000-06-03 14:57:40 +00001246 }
1247 if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) {
1248 close (fd);
1249 return 0;
1250 }
andre6bb92372000-06-19 08:20:03 +00001251 }
1252
1253 close(fd);
andre2ff7b5d2000-06-03 14:57:40 +00001254 return 1;
andre61e67252000-06-04 17:07:49 +00001255}
Damien Millerd5bf3072000-06-07 21:32:13 +10001256#endif /* USE_WTMPX */
andre2ff7b5d2000-06-03 14:57:40 +00001257
andre2ff7b5d2000-06-03 14:57:40 +00001258/**
andre61e67252000-06-04 17:07:49 +00001259 ** Low-level libutil login() functions
andre2ff7b5d2000-06-03 14:57:40 +00001260 **/
1261
1262#ifdef USE_LOGIN
andre2ff7b5d2000-06-03 14:57:40 +00001263static int
andre61e67252000-06-04 17:07:49 +00001264syslogin_perform_login(struct logininfo *li)
1265{
andre2ff7b5d2000-06-03 14:57:40 +00001266 struct utmp *ut;
1267
Damien Miller348c9b72000-08-15 10:01:22 +10001268 if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
andre2ff7b5d2000-06-03 14:57:40 +00001269 log("syslogin_perform_login: couldn't malloc()");
1270 return 0;
1271 }
1272 construct_utmp(li, ut);
1273 login(ut);
1274
1275 return 1;
andre61e67252000-06-04 17:07:49 +00001276}
1277
andre2ff7b5d2000-06-03 14:57:40 +00001278static int
andre61e67252000-06-04 17:07:49 +00001279syslogin_perform_logout(struct logininfo *li)
1280{
Damien Millerdd47aa22000-06-27 11:18:27 +10001281# ifdef HAVE_LOGOUT
andre2ff7b5d2000-06-03 14:57:40 +00001282 char line[8];
1283
1284 (void)line_stripname(line, li->line, sizeof(line));
1285
1286 if (!logout(line)) {
1287 log("syslogin_perform_logout: logout() returned an error");
Damien Millerdd47aa22000-06-27 11:18:27 +10001288# ifdef HAVE_LOGWTMP
andre2ff7b5d2000-06-03 14:57:40 +00001289 } else {
1290 logwtmp(line, "", "");
Damien Millerdd47aa22000-06-27 11:18:27 +10001291# endif
Damien Miller9b6d4ab2000-07-02 08:43:18 +10001292 }
andre6bb92372000-06-19 08:20:03 +00001293 /* FIXME: (ATL - if the need arises) What to do if we have
1294 * login, but no logout? what if logout but no logwtmp? All
1295 * routines are in libutil so they should all be there,
1296 * but... */
Damien Millerdd47aa22000-06-27 11:18:27 +10001297# endif
andre2ff7b5d2000-06-03 14:57:40 +00001298 return 1;
andre61e67252000-06-04 17:07:49 +00001299}
andre2ff7b5d2000-06-03 14:57:40 +00001300
andre2ff7b5d2000-06-03 14:57:40 +00001301int
andre61e67252000-06-04 17:07:49 +00001302syslogin_write_entry(struct logininfo *li)
1303{
andre2ff7b5d2000-06-03 14:57:40 +00001304 switch (li->type) {
1305 case LTYPE_LOGIN:
1306 return syslogin_perform_login(li);
1307 case LTYPE_LOGOUT:
1308 return syslogin_perform_logout(li);
1309 default:
1310 log("syslogin_write_entry: Invalid type field");
1311 return 0;
1312 }
andre61e67252000-06-04 17:07:49 +00001313}
Damien Millerd5bf3072000-06-07 21:32:13 +10001314#endif /* USE_LOGIN */
andre2ff7b5d2000-06-03 14:57:40 +00001315
1316/* end of file log-syslogin.c */
1317
andre2ff7b5d2000-06-03 14:57:40 +00001318/**
andre61e67252000-06-04 17:07:49 +00001319 ** Low-level lastlog functions
andre2ff7b5d2000-06-03 14:57:40 +00001320 **/
1321
1322#ifdef USE_LASTLOG
Damien Millerdd47aa22000-06-27 11:18:27 +10001323#define LL_FILE 1
1324#define LL_DIR 2
1325#define LL_OTHER 3
andre2ff7b5d2000-06-03 14:57:40 +00001326
andre2ff7b5d2000-06-03 14:57:40 +00001327static void
andre61e67252000-06-04 17:07:49 +00001328lastlog_construct(struct logininfo *li, struct lastlog *last)
1329{
andre2ff7b5d2000-06-03 14:57:40 +00001330 /* clear the structure */
Damien Miller348c9b72000-08-15 10:01:22 +10001331 memset(last, '\0', sizeof(*last));
andre2ff7b5d2000-06-03 14:57:40 +00001332
Damien Millerdd47aa22000-06-27 11:18:27 +10001333 (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
andre6bb92372000-06-19 08:20:03 +00001334 strlcpy(last->ll_host, li->hostname,
1335 MIN_SIZEOF(last->ll_host, li->hostname));
andre2ff7b5d2000-06-03 14:57:40 +00001336 last->ll_time = li->tv_sec;
andre61e67252000-06-04 17:07:49 +00001337}
andre2ff7b5d2000-06-03 14:57:40 +00001338
andre2ff7b5d2000-06-03 14:57:40 +00001339static int
andre61e67252000-06-04 17:07:49 +00001340lastlog_filetype(char *filename)
1341{
andre2ff7b5d2000-06-03 14:57:40 +00001342 struct stat st;
1343
Damien Millerdd47aa22000-06-27 11:18:27 +10001344 if (stat(LASTLOG_FILE, &st) != 0) {
1345 log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
1346 strerror(errno));
andre2ff7b5d2000-06-03 14:57:40 +00001347 return 0;
1348 }
andre2ff7b5d2000-06-03 14:57:40 +00001349 if (S_ISDIR(st.st_mode))
1350 return LL_DIR;
1351 else if (S_ISREG(st.st_mode))
1352 return LL_FILE;
1353 else
1354 return LL_OTHER;
andre61e67252000-06-04 17:07:49 +00001355}
andre2ff7b5d2000-06-03 14:57:40 +00001356
1357
1358/* open the file (using filemode) and seek to the login entry */
1359static int
andre61e67252000-06-04 17:07:49 +00001360lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1361{
andre2ff7b5d2000-06-03 14:57:40 +00001362 off_t offset;
1363 int type;
1364 char lastlog_file[1024];
1365
1366 type = lastlog_filetype(LASTLOG_FILE);
1367 switch (type) {
Damien Millerf8af08d2000-06-27 09:40:06 +10001368 case LL_FILE:
1369 strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1370 break;
1371 case LL_DIR:
1372 snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1373 LASTLOG_FILE, li->username);
1374 break;
1375 default:
1376 log("lastlog_openseek: %.100s is not a file or directory!",
1377 LASTLOG_FILE);
1378 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +10001379 }
andre2ff7b5d2000-06-03 14:57:40 +00001380
1381 *fd = open(lastlog_file, filemode);
1382 if ( *fd < 0) {
Damien Miller53c5d462000-06-28 00:50:50 +10001383 debug("lastlog_openseek: Couldn't open %s: %s",
andre2ff7b5d2000-06-03 14:57:40 +00001384 lastlog_file, strerror(errno));
1385 return 0;
1386 }
Damien Millerdd47aa22000-06-27 11:18:27 +10001387
Damien Millere477ef62000-08-15 10:21:17 +10001388 if (type == LL_FILE) {
1389 /* find this uid's offset in the lastlog file */
1390 offset = (off_t) ( (long)li->uid * sizeof(struct lastlog));
andre2ff7b5d2000-06-03 14:57:40 +00001391
Damien Millere477ef62000-08-15 10:21:17 +10001392 if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1393 log("lastlog_openseek: %s->lseek(): %s",
1394 lastlog_file, strerror(errno));
1395 return 0;
1396 }
andre2ff7b5d2000-06-03 14:57:40 +00001397 }
Damien Millere477ef62000-08-15 10:21:17 +10001398
andre2ff7b5d2000-06-03 14:57:40 +00001399 return 1;
andre61e67252000-06-04 17:07:49 +00001400}
andre2ff7b5d2000-06-03 14:57:40 +00001401
1402static int
andre61e67252000-06-04 17:07:49 +00001403lastlog_perform_login(struct logininfo *li)
1404{
andre2ff7b5d2000-06-03 14:57:40 +00001405 struct lastlog last;
1406 int fd;
1407
1408 /* create our struct lastlog */
1409 lastlog_construct(li, &last);
1410
Damien Millerc1132e72000-08-18 14:08:38 +10001411 if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
1412 return(0);
1413
andre2ff7b5d2000-06-03 14:57:40 +00001414 /* write the entry */
Damien Millerc1132e72000-08-18 14:08:38 +10001415 if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) {
1416 close(fd);
1417 log("lastlog_write_filemode: Error writing to %s: %s",
1418 LASTLOG_FILE, strerror(errno));
andre2ff7b5d2000-06-03 14:57:40 +00001419 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +10001420 }
Damien Millerc1132e72000-08-18 14:08:38 +10001421
1422 close(fd);
1423 return 1;
andre61e67252000-06-04 17:07:49 +00001424}
andre2ff7b5d2000-06-03 14:57:40 +00001425
andre2ff7b5d2000-06-03 14:57:40 +00001426int
andre61e67252000-06-04 17:07:49 +00001427lastlog_write_entry(struct logininfo *li)
1428{
andre2ff7b5d2000-06-03 14:57:40 +00001429 switch(li->type) {
1430 case LTYPE_LOGIN:
1431 return lastlog_perform_login(li);
1432 default:
1433 log("lastlog_write_entry: Invalid type field");
1434 return 0;
1435 }
andre61e67252000-06-04 17:07:49 +00001436}
andre2ff7b5d2000-06-03 14:57:40 +00001437
andre2ff7b5d2000-06-03 14:57:40 +00001438static void
andre61e67252000-06-04 17:07:49 +00001439lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1440{
andre2ff7b5d2000-06-03 14:57:40 +00001441 line_fullname(li->line, last->ll_line, sizeof(li->line));
Damien Millerdd47aa22000-06-27 11:18:27 +10001442 strlcpy(li->hostname, last->ll_host,
andre6bb92372000-06-19 08:20:03 +00001443 MIN_SIZEOF(li->hostname, last->ll_host));
andre2ff7b5d2000-06-03 14:57:40 +00001444 li->tv_sec = last->ll_time;
andre61e67252000-06-04 17:07:49 +00001445}
andre2ff7b5d2000-06-03 14:57:40 +00001446
andre2ff7b5d2000-06-03 14:57:40 +00001447int
andre61e67252000-06-04 17:07:49 +00001448lastlog_get_entry(struct logininfo *li)
1449{
andre2ff7b5d2000-06-03 14:57:40 +00001450 struct lastlog last;
1451 int fd;
1452
1453 if (lastlog_openseek(li, &fd, O_RDONLY)) {
Damien Miller53c5d462000-06-28 00:50:50 +10001454 if (atomicio(read, fd, &last, sizeof(last)) != sizeof(last)) {
1455 log("lastlog_get_entry: Error reading from %s: %s",
andre2ff7b5d2000-06-03 14:57:40 +00001456 LASTLOG_FILE, strerror(errno));
1457 return 0;
1458 } else {
1459 lastlog_populate_entry(li, &last);
1460 return 1;
1461 }
Damien Millerdd47aa22000-06-27 11:18:27 +10001462 } else {
andre2ff7b5d2000-06-03 14:57:40 +00001463 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +10001464 }
andre61e67252000-06-04 17:07:49 +00001465}
Damien Millerd5bf3072000-06-07 21:32:13 +10001466#endif /* USE_LASTLOG */