blob: 224866128f5e79b114aaff33c47bd1750b623591 [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
Ben Lindstromdcca9812000-11-10 03:28:31 +0000146 ** NeXT - M68k/HPPA/Sparc (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
Ben Lindstrom75214f92000-12-01 21:19:51 +0000164RCSID("$Id: loginrec.c,v 1.28 2000/12/01 21:19:51 mouring Exp $");
Ben Lindstromdcca9812000-11-10 03:28:31 +0000165
166#ifdef HAVE_UTIL_H
167# include <util.h>
168#endif
andre2ff7b5d2000-06-03 14:57:40 +0000169
170/**
171 ** prototypes for helper functions in this file
172 **/
173
174#if HAVE_UTMP_H
andre2ff7b5d2000-06-03 14:57:40 +0000175void set_utmp_time(struct logininfo *li, struct utmp *ut);
176void construct_utmp(struct logininfo *li, struct utmp *ut);
177#endif
178
179#ifdef HAVE_UTMPX_H
andre2ff7b5d2000-06-03 14:57:40 +0000180void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
181void construct_utmpx(struct logininfo *li, struct utmpx *ut);
182#endif
183
184int utmp_write_entry(struct logininfo *li);
185int utmpx_write_entry(struct logininfo *li);
186int wtmp_write_entry(struct logininfo *li);
187int wtmpx_write_entry(struct logininfo *li);
188int lastlog_write_entry(struct logininfo *li);
189int syslogin_write_entry(struct logininfo *li);
190
191int getlast_entry(struct logininfo *li);
192int lastlog_get_entry(struct logininfo *li);
193int wtmp_get_entry(struct logininfo *li);
194int wtmpx_get_entry(struct logininfo *li);
195
andre6bb92372000-06-19 08:20:03 +0000196/* pick the shortest string */
197#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
198
andre2ff7b5d2000-06-03 14:57:40 +0000199/**
200 ** platform-independent login functions
201 **/
202
andre6bb92372000-06-19 08:20:03 +0000203/* login_login(struct logininfo *) -Record a login
204 *
205 * Call with a pointer to a struct logininfo initialised with
206 * login_init_entry() or login_alloc_entry()
207 *
208 * Returns:
209 * >0 if successful
210 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
211 */
andre61e67252000-06-04 17:07:49 +0000212int
213login_login (struct logininfo *li)
214{
215 li->type = LTYPE_LOGIN;
216 return login_write(li);
217}
218
219
andre6bb92372000-06-19 08:20:03 +0000220/* login_logout(struct logininfo *) - Record a logout
221 *
222 * Call as with login_login()
223 *
224 * Returns:
225 * >0 if successful
226 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
227 */
andre61e67252000-06-04 17:07:49 +0000228int
229login_logout(struct logininfo *li)
230{
231 li->type = LTYPE_LOGOUT;
232 return login_write(li);
233}
234
andre6bb92372000-06-19 08:20:03 +0000235/* login_get_lastlog_time(int) - Retrieve the last login time
236 *
237 * Retrieve the last login time for the given uid. Will try to use the
238 * system lastlog facilities if they are available, but will fall back
239 * to looking in wtmp/wtmpx if necessary
240 *
241 * Returns:
242 * 0 on failure, or if user has never logged in
243 * Time in seconds from the epoch if successful
244 *
245 * Useful preprocessor symbols:
246 * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
247 * info
248 * USE_LASTLOG: If set, indicates the presence of system lastlog
249 * facilities. If this and DISABLE_LASTLOG are not set,
250 * try to retrieve lastlog information from wtmp/wtmpx.
251 */
andre61e67252000-06-04 17:07:49 +0000252unsigned int
253login_get_lastlog_time(const int uid)
254{
255 struct logininfo li;
256
andre6bb92372000-06-19 08:20:03 +0000257 if (login_get_lastlog(&li, uid))
258 return li.tv_sec;
259 else
260 return 0;
andre61e67252000-06-04 17:07:49 +0000261}
262
andre6bb92372000-06-19 08:20:03 +0000263/* login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry
264 *
265 * Retrieve a logininfo structure populated (only partially) with
266 * information from the system lastlog data, or from wtmp/wtmpx if no
267 * system lastlog information exists.
268 *
269 * Note this routine must be given a pre-allocated logininfo.
270 *
271 * Returns:
272 * >0: A pointer to your struct logininfo if successful
273 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
274 *
275 */
andre61e67252000-06-04 17:07:49 +0000276struct logininfo *
277login_get_lastlog(struct logininfo *li, const int uid)
278{
andre6bb92372000-06-19 08:20:03 +0000279 struct passwd *pw;
andre6bb92372000-06-19 08:20:03 +0000280
Damien Miller348c9b72000-08-15 10:01:22 +1000281 memset(li, '\0', sizeof(*li));
andre61e67252000-06-04 17:07:49 +0000282 li->uid = uid;
andre6bb92372000-06-19 08:20:03 +0000283
Damien Miller53c5d462000-06-28 00:50:50 +1000284 /*
285 * If we don't have a 'real' lastlog, we need the username to
andre6bb92372000-06-19 08:20:03 +0000286 * reliably search wtmp(x) for the last login (see
Damien Miller53c5d462000-06-28 00:50:50 +1000287 * wtmp_get_entry().)
288 */
andre6bb92372000-06-19 08:20:03 +0000289 pw = getpwuid(uid);
Damien Millerdd47aa22000-06-27 11:18:27 +1000290 if (pw == NULL)
291 fatal("login_get_lastlog: Cannot find account for uid %i", uid);
292
andre98cabe02000-06-19 09:11:30 +0000293 /* No MIN_SIZEOF here - we absolutely *must not* truncate the
294 * username */
Damien Millerf8af08d2000-06-27 09:40:06 +1000295 strlcpy(li->username, pw->pw_name, sizeof(li->username));
Damien Millerdd47aa22000-06-27 11:18:27 +1000296
andre61e67252000-06-04 17:07:49 +0000297 if (getlast_entry(li))
298 return li;
299 else
Damien Millerdd47aa22000-06-27 11:18:27 +1000300 return NULL;
andre61e67252000-06-04 17:07:49 +0000301}
302
303
andre6bb92372000-06-19 08:20:03 +0000304/* login_alloc_entry(int, char*, char*, char*) - Allocate and initialise
305 * a logininfo structure
306 *
307 * This function creates a new struct logininfo, a data structure
308 * meant to carry the information required to portably record login info.
309 *
310 * Returns a pointer to a newly created struct logininfo. If memory
311 * allocation fails, the program halts.
312 */
andre61e67252000-06-04 17:07:49 +0000313struct
314logininfo *login_alloc_entry(int pid, const char *username,
315 const char *hostname, const char *line)
316{
andre2ff7b5d2000-06-03 14:57:40 +0000317 struct logininfo *newli;
318
Damien Miller348c9b72000-08-15 10:01:22 +1000319 newli = (struct logininfo *) xmalloc (sizeof(*newli));
andre61e67252000-06-04 17:07:49 +0000320 (void)login_init_entry(newli, pid, username, hostname, line);
321 return newli;
322}
andre2ff7b5d2000-06-03 14:57:40 +0000323
324
andre6bb92372000-06-19 08:20:03 +0000325/* login_free_entry(struct logininfo *) - free struct memory */
andre61e67252000-06-04 17:07:49 +0000326void
327login_free_entry(struct logininfo *li)
328{
329 xfree(li);
330}
331
andre2ff7b5d2000-06-03 14:57:40 +0000332
andre6bb92372000-06-19 08:20:03 +0000333/* login_init_entry(struct logininfo *, int, char*, char*, char*)
334 * - initialise a struct logininfo
335 *
336 * Populates a new struct logininfo, a data structure meant to carry
337 * the information required to portably record login info.
338 *
339 * Returns: 1
340 */
andre61e67252000-06-04 17:07:49 +0000341int
342login_init_entry(struct logininfo *li, int pid, const char *username,
343 const char *hostname, const char *line)
344{
Damien Millerf8af08d2000-06-27 09:40:06 +1000345 struct passwd *pw;
346
Damien Miller348c9b72000-08-15 10:01:22 +1000347 memset(li, 0, sizeof(*li));
andre2ff7b5d2000-06-03 14:57:40 +0000348
andre61e67252000-06-04 17:07:49 +0000349 li->pid = pid;
Damien Millerf8af08d2000-06-27 09:40:06 +1000350
andre2ff7b5d2000-06-03 14:57:40 +0000351 /* set the line information */
andre61e67252000-06-04 17:07:49 +0000352 if (line)
andre2ff7b5d2000-06-03 14:57:40 +0000353 line_fullname(li->line, line, sizeof(li->line));
andre2ff7b5d2000-06-03 14:57:40 +0000354
Damien Millerf8af08d2000-06-27 09:40:06 +1000355 if (username) {
andre2ff7b5d2000-06-03 14:57:40 +0000356 strlcpy(li->username, username, sizeof(li->username));
Damien Millerf8af08d2000-06-27 09:40:06 +1000357 pw = getpwnam(li->username);
358 if (pw == NULL)
359 fatal("login_init_entry: Cannot find user \"%s\"", li->username);
360 li->uid = pw->pw_uid;
361 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000362
andre61e67252000-06-04 17:07:49 +0000363 if (hostname)
andre2ff7b5d2000-06-03 14:57:40 +0000364 strlcpy(li->hostname, hostname, sizeof(li->hostname));
Damien Millerf8af08d2000-06-27 09:40:06 +1000365
andre61e67252000-06-04 17:07:49 +0000366 return 1;
andre2ff7b5d2000-06-03 14:57:40 +0000367}
368
andre6bb92372000-06-19 08:20:03 +0000369/* login_set_current_time(struct logininfo *) - set the current time
370 *
371 * Set the current time in a logininfo structure. This function is
372 * meant to eliminate the need to deal with system dependencies for
373 * time handling.
374 */
andre2ff7b5d2000-06-03 14:57:40 +0000375void
andre61e67252000-06-04 17:07:49 +0000376login_set_current_time(struct logininfo *li)
377{
andre2ff7b5d2000-06-03 14:57:40 +0000378 struct timeval tv;
379
380 gettimeofday(&tv, NULL);
Damien Millerf8af08d2000-06-27 09:40:06 +1000381
382 li->tv_sec = tv.tv_sec;
383 li->tv_usec = tv.tv_usec;
andre2ff7b5d2000-06-03 14:57:40 +0000384}
385
andre61e67252000-06-04 17:07:49 +0000386/* copy a sockaddr_* into our logininfo */
andre2ff7b5d2000-06-03 14:57:40 +0000387void
andre61e67252000-06-04 17:07:49 +0000388login_set_addr(struct logininfo *li, const struct sockaddr *sa,
389 const unsigned int sa_size)
390{
391 unsigned int bufsize = sa_size;
392
393 /* make sure we don't overrun our union */
394 if (sizeof(li->hostaddr) < sa_size)
395 bufsize = sizeof(li->hostaddr);
396
397 memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
andre2ff7b5d2000-06-03 14:57:40 +0000398}
399
andre2ff7b5d2000-06-03 14:57:40 +0000400
andre61e67252000-06-04 17:07:49 +0000401/**
402 ** login_write: Call low-level recording functions based on autoconf
403 ** results
404 **/
andre2ff7b5d2000-06-03 14:57:40 +0000405int
andre61e67252000-06-04 17:07:49 +0000406login_write (struct logininfo *li)
407{
Damien Millerbac2d8a2000-09-05 16:13:06 +1100408#ifndef HAVE_CYGWIN
andre2ff7b5d2000-06-03 14:57:40 +0000409 if ((int)geteuid() != 0) {
410 log("Attempt to write login records by non-root user (aborting)");
411 return 1;
412 }
Damien Millerbac2d8a2000-09-05 16:13:06 +1100413#endif
Damien Millerdd47aa22000-06-27 11:18:27 +1000414
andre2ff7b5d2000-06-03 14:57:40 +0000415 /* set the timestamp */
416 login_set_current_time(li);
417#ifdef USE_LOGIN
418 syslogin_write_entry(li);
419#endif
420#ifdef USE_LASTLOG
421 if (li->type == LTYPE_LOGIN) {
422 lastlog_write_entry(li);
423 }
424#endif
425#ifdef USE_UTMP
426 utmp_write_entry(li);
427#endif
428#ifdef USE_WTMP
429 wtmp_write_entry(li);
430#endif
431#ifdef USE_UTMPX
432 utmpx_write_entry(li);
433#endif
434#ifdef USE_WTMPX
435 wtmpx_write_entry(li);
436#endif
437 return 0;
438}
439
andre2ff7b5d2000-06-03 14:57:40 +0000440/**
andre61e67252000-06-04 17:07:49 +0000441 ** getlast_entry: Call low-level functions to retrieve the last login
442 ** time.
andre2ff7b5d2000-06-03 14:57:40 +0000443 **/
444
andre61e67252000-06-04 17:07:49 +0000445/* take the uid in li and return the last login time */
446int
447getlast_entry(struct logininfo *li)
448{
449#ifdef USE_LASTLOG
Damien Miller53c5d462000-06-28 00:50:50 +1000450 return(lastlog_get_entry(li));
Damien Millerdd47aa22000-06-27 11:18:27 +1000451#else /* !USE_LASTLOG */
andre61e67252000-06-04 17:07:49 +0000452
Damien Millerdd47aa22000-06-27 11:18:27 +1000453#ifdef DISABLE_LASTLOG
andreecaabf12000-06-12 22:21:44 +0000454 /* On some systems we shouldn't even try to obtain last login
455 * time, e.g. AIX */
456 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +1000457# else /* DISABLE_LASTLOG */
andre61e67252000-06-04 17:07:49 +0000458 /* Try to retrieve the last login time from wtmp */
Damien Millerdd47aa22000-06-27 11:18:27 +1000459# if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
andre61e67252000-06-04 17:07:49 +0000460 /* retrieve last login time from utmp */
Damien Millerdd47aa22000-06-27 11:18:27 +1000461 return (wtmp_get_entry(li));
462# else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */
andre61e67252000-06-04 17:07:49 +0000463 /* If wtmp isn't available, try wtmpx */
Damien Millerdd47aa22000-06-27 11:18:27 +1000464# if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
andre61e67252000-06-04 17:07:49 +0000465 /* retrieve last login time from utmpx */
Damien Millerdd47aa22000-06-27 11:18:27 +1000466 return (wtmpx_get_entry(li));
467# else
andre61e67252000-06-04 17:07:49 +0000468 /* Give up: No means of retrieving last login time */
469 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +1000470# endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */
471# endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */
472# endif /* DISABLE_LASTLOG */
473#endif /* USE_LASTLOG */
andre61e67252000-06-04 17:07:49 +0000474}
475
476
477
andre2ff7b5d2000-06-03 14:57:40 +0000478/*
andre61e67252000-06-04 17:07:49 +0000479 * 'line' string utility functions
480 *
481 * These functions process the 'line' string into one of three forms:
482 *
andre2ff7b5d2000-06-03 14:57:40 +0000483 * 1. The full filename (including '/dev')
484 * 2. The stripped name (excluding '/dev')
andre61e67252000-06-04 17:07:49 +0000485 * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
486 * /dev/pts/1 -> ts/1 )
andre2ff7b5d2000-06-03 14:57:40 +0000487 *
488 * Form 3 is used on some systems to identify a .tmp.? entry when
489 * attempting to remove it. Typically both addition and removal is
andre61e67252000-06-04 17:07:49 +0000490 * performed by one application - say, sshd - so as long as the choice
491 * uniquely identifies a terminal it's ok.
andre2ff7b5d2000-06-03 14:57:40 +0000492 */
493
494
andre61e67252000-06-04 17:07:49 +0000495/* line_fullname(): add the leading '/dev/' if it doesn't exist make
496 * sure dst has enough space, if not just copy src (ugh) */
andre2ff7b5d2000-06-03 14:57:40 +0000497char *
andre61e67252000-06-04 17:07:49 +0000498line_fullname(char *dst, const char *src, int dstsize)
499{
andre2ff7b5d2000-06-03 14:57:40 +0000500 memset(dst, '\0', dstsize);
Damien Millerf5a81472000-09-30 21:34:44 +1100501 if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) {
andre2ff7b5d2000-06-03 14:57:40 +0000502 strlcpy(dst, src, dstsize);
Damien Millerf5a81472000-09-30 21:34:44 +1100503 } else {
Damien Miller1a132252000-06-13 21:23:17 +1000504 strlcpy(dst, "/dev/", dstsize);
andre2ff7b5d2000-06-03 14:57:40 +0000505 strlcat(dst, src, dstsize);
506 }
507 return dst;
508}
509
andre61e67252000-06-04 17:07:49 +0000510/* line_stripname(): strip the leading '/dev' if it exists, return dst */
andre2ff7b5d2000-06-03 14:57:40 +0000511char *
andre61e67252000-06-04 17:07:49 +0000512line_stripname(char *dst, const char *src, int dstsize)
513{
andre2ff7b5d2000-06-03 14:57:40 +0000514 memset(dst, '\0', dstsize);
515 if (strncmp(src, "/dev/", 5) == 0)
Damien Millerf5a81472000-09-30 21:34:44 +1100516 strlcpy(dst, src + 5, dstsize);
andre2ff7b5d2000-06-03 14:57:40 +0000517 else
518 strlcpy(dst, src, dstsize);
519 return dst;
andre61e67252000-06-04 17:07:49 +0000520}
521
andre61e67252000-06-04 17:07:49 +0000522/* line_abbrevname(): Return the abbreviated (usually four-character)
523 * form of the line (Just use the last <dstsize> characters of the
524 * full name.)
525 *
526 * NOTE: use strncpy because we do NOT necessarily want zero
527 * termination */
andre2ff7b5d2000-06-03 14:57:40 +0000528char *
Damien Millerdd47aa22000-06-27 11:18:27 +1000529line_abbrevname(char *dst, const char *src, int dstsize)
530{
531 size_t len;
532
andre2ff7b5d2000-06-03 14:57:40 +0000533 memset(dst, '\0', dstsize);
Damien Millerdd47aa22000-06-27 11:18:27 +1000534
Damien Miller8e81ed32000-07-01 13:17:42 +1000535 /* Always skip prefix if present */
Damien Millerf5a81472000-09-30 21:34:44 +1100536#ifdef sgi
537 if (strncmp(src, "/dev/tty", 8) == 0)
538 src += 8;
539#else
Damien Miller8e81ed32000-07-01 13:17:42 +1000540 if (strncmp(src, "/dev/", 5) == 0)
541 src += 5;
Damien Millerf5a81472000-09-30 21:34:44 +1100542#endif
Damien Miller8e81ed32000-07-01 13:17:42 +1000543
Damien Millerdd47aa22000-06-27 11:18:27 +1000544 len = strlen(src);
545
Damien Miller8e81ed32000-07-01 13:17:42 +1000546 if (len > 0) {
547 if (((int)len - dstsize) > 0)
548 src += ((int)len - dstsize);
549
550 /* note: _don't_ change this to strlcpy */
551 strncpy(dst, src, (size_t)dstsize);
Damien Millerdd47aa22000-06-27 11:18:27 +1000552 }
553
andre2ff7b5d2000-06-03 14:57:40 +0000554 return dst;
555}
556
andre2ff7b5d2000-06-03 14:57:40 +0000557/**
558 ** utmp utility functions
andre61e67252000-06-04 17:07:49 +0000559 **
560 ** These functions manipulate struct utmp, taking system differences
561 ** into account.
andre2ff7b5d2000-06-03 14:57:40 +0000562 **/
563
564#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
565
andre2ff7b5d2000-06-03 14:57:40 +0000566/* build the utmp structure */
567void
andre61e67252000-06-04 17:07:49 +0000568set_utmp_time(struct logininfo *li, struct utmp *ut)
569{
Damien Millerdd47aa22000-06-27 11:18:27 +1000570# ifdef HAVE_TV_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +0000571 ut->ut_tv.tv_sec = li->tv_sec;
572 ut->ut_tv.tv_usec = li->tv_usec;
Damien Millerdd47aa22000-06-27 11:18:27 +1000573# else
andre2ff7b5d2000-06-03 14:57:40 +0000574# ifdef HAVE_TIME_IN_UTMP
575 ut->ut_time = li->tv_sec;
576# endif
Damien Millerdd47aa22000-06-27 11:18:27 +1000577# endif
andre2ff7b5d2000-06-03 14:57:40 +0000578}
579
580void
581construct_utmp(struct logininfo *li,
andre61e67252000-06-04 17:07:49 +0000582 struct utmp *ut)
583{
Damien Miller348c9b72000-08-15 10:01:22 +1000584 memset(ut, '\0', sizeof(*ut));
andre6bb92372000-06-19 08:20:03 +0000585
586 /* First fill out fields used for both logins and logouts */
587
Damien Millerdd47aa22000-06-27 11:18:27 +1000588# ifdef HAVE_ID_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +0000589 line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
Damien Millerdd47aa22000-06-27 11:18:27 +1000590# endif
andre2ff7b5d2000-06-03 14:57:40 +0000591
Damien Millerdd47aa22000-06-27 11:18:27 +1000592# ifdef HAVE_TYPE_IN_UTMP
andre6bb92372000-06-19 08:20:03 +0000593 /* This is done here to keep utmp constants out of struct logininfo */
andre2ff7b5d2000-06-03 14:57:40 +0000594 switch (li->type) {
595 case LTYPE_LOGIN:
596 ut->ut_type = USER_PROCESS;
597 break;
598 case LTYPE_LOGOUT:
599 ut->ut_type = DEAD_PROCESS;
600 break;
601 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000602# endif
andre6bb92372000-06-19 08:20:03 +0000603 set_utmp_time(li, ut);
andre2ff7b5d2000-06-03 14:57:40 +0000604
andre6bb92372000-06-19 08:20:03 +0000605 line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
Damien Millerdd47aa22000-06-27 11:18:27 +1000606
607# ifdef HAVE_PID_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +0000608 ut->ut_pid = li->pid;
Damien Millerdd47aa22000-06-27 11:18:27 +1000609# endif
andre6bb92372000-06-19 08:20:03 +0000610
611 /* If we're logging out, leave all other fields blank */
612 if (li->type == LTYPE_LOGOUT)
613 return;
614
Damien Millerdd47aa22000-06-27 11:18:27 +1000615 /*
616 * These fields are only used when logging in, and are blank
617 * for logouts.
618 */
andre6bb92372000-06-19 08:20:03 +0000619
620 /* Use strncpy because we don't necessarily want null termination */
Damien Miller7a0e5dc2000-07-11 12:15:54 +1000621 strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
Damien Millerdd47aa22000-06-27 11:18:27 +1000622# ifdef HAVE_HOST_IN_UTMP
andre6bb92372000-06-19 08:20:03 +0000623 strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
Damien Millerdd47aa22000-06-27 11:18:27 +1000624# endif
625# ifdef HAVE_ADDR_IN_UTMP
andre61e67252000-06-04 17:07:49 +0000626 /* this is just a 32-bit IP address */
627 if (li->hostaddr.sa.sa_family == AF_INET)
628 ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
Damien Millerdd47aa22000-06-27 11:18:27 +1000629# endif
andre61e67252000-06-04 17:07:49 +0000630}
Damien Millerdd47aa22000-06-27 11:18:27 +1000631#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
andre61e67252000-06-04 17:07:49 +0000632
andre2ff7b5d2000-06-03 14:57:40 +0000633/**
634 ** utmpx utility functions
andre61e67252000-06-04 17:07:49 +0000635 **
636 ** These functions manipulate struct utmpx, accounting for system
637 ** variations.
andre2ff7b5d2000-06-03 14:57:40 +0000638 **/
639
640#if defined(USE_UTMPX) || defined (USE_WTMPX)
andre2ff7b5d2000-06-03 14:57:40 +0000641/* build the utmpx structure */
642void
andre61e67252000-06-04 17:07:49 +0000643set_utmpx_time(struct logininfo *li, struct utmpx *utx)
644{
Damien Millerdd47aa22000-06-27 11:18:27 +1000645# ifdef HAVE_TV_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000646 utx->ut_tv.tv_sec = li->tv_sec;
647 utx->ut_tv.tv_usec = li->tv_usec;
Damien Millerdd47aa22000-06-27 11:18:27 +1000648# else /* HAVE_TV_IN_UTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000649# ifdef HAVE_TIME_IN_UTMPX
650 utx->ut_time = li->tv_sec;
Damien Millerdd47aa22000-06-27 11:18:27 +1000651# endif /* HAVE_TIME_IN_UTMPX */
652# endif /* HAVE_TV_IN_UTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000653}
654
andre61e67252000-06-04 17:07:49 +0000655void
656construct_utmpx(struct logininfo *li, struct utmpx *utx)
657{
Damien Miller348c9b72000-08-15 10:01:22 +1000658 memset(utx, '\0', sizeof(*utx));
Damien Miller8e81ed32000-07-01 13:17:42 +1000659# ifdef HAVE_ID_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000660 line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
Damien Miller8e81ed32000-07-01 13:17:42 +1000661# endif
andre2ff7b5d2000-06-03 14:57:40 +0000662
663 /* this is done here to keep utmp constants out of loginrec.h */
664 switch (li->type) {
665 case LTYPE_LOGIN:
666 utx->ut_type = USER_PROCESS;
667 break;
668 case LTYPE_LOGOUT:
669 utx->ut_type = DEAD_PROCESS;
670 break;
671 }
andre2ff7b5d2000-06-03 14:57:40 +0000672 line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
andre2ff7b5d2000-06-03 14:57:40 +0000673 set_utmpx_time(li, utx);
andre6bb92372000-06-19 08:20:03 +0000674 utx->ut_pid = li->pid;
675
676 if (li->type == LTYPE_LOGOUT)
677 return;
678
Damien Millerdd47aa22000-06-27 11:18:27 +1000679 /*
680 * These fields are only used when logging in, and are blank
681 * for logouts.
682 */
andre6bb92372000-06-19 08:20:03 +0000683
684 /* strncpy(): Don't necessarily want null termination */
Damien Miller7a0e5dc2000-07-11 12:15:54 +1000685 strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
Damien Millerdd47aa22000-06-27 11:18:27 +1000686# ifdef HAVE_HOST_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +0000687 strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
Damien Millerdd47aa22000-06-27 11:18:27 +1000688# endif
689# ifdef HAVE_ADDR_IN_UTMPX
Damien Millerd6f204d2000-09-23 13:57:27 +1100690 /* this is just a 32-bit IP address */
691 if (li->hostaddr.sa.sa_family == AF_INET)
692 utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
Damien Millerdd47aa22000-06-27 11:18:27 +1000693# endif
694# ifdef HAVE_SYSLEN_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +0000695 /* ut_syslen is the length of the utx_host string */
696 utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
Damien Millerdd47aa22000-06-27 11:18:27 +1000697# endif
andre61e67252000-06-04 17:07:49 +0000698}
Damien Millerdd47aa22000-06-27 11:18:27 +1000699#endif /* USE_UTMPX || USE_WTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000700
701/**
andre61e67252000-06-04 17:07:49 +0000702 ** Low-level utmp functions
andre2ff7b5d2000-06-03 14:57:40 +0000703 **/
704
705/* FIXME: (ATL) utmp_write_direct needs testing */
andre2ff7b5d2000-06-03 14:57:40 +0000706#ifdef USE_UTMP
707
andre2ff7b5d2000-06-03 14:57:40 +0000708/* if we can, use pututline() etc. */
Damien Millerdd47aa22000-06-27 11:18:27 +1000709# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
710 defined(HAVE_PUTUTLINE)
andre2ff7b5d2000-06-03 14:57:40 +0000711# define UTMP_USE_LIBRARY
Damien Millerdd47aa22000-06-27 11:18:27 +1000712# endif
andre2ff7b5d2000-06-03 14:57:40 +0000713
714
715/* write a utmp entry with the system's help (pututline() and pals) */
Damien Millerdd47aa22000-06-27 11:18:27 +1000716# ifdef UTMP_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000717static int
andre61e67252000-06-04 17:07:49 +0000718utmp_write_library(struct logininfo *li, struct utmp *ut)
719{
andre2ff7b5d2000-06-03 14:57:40 +0000720 setutent();
721 pututline(ut);
722
Damien Millerdd47aa22000-06-27 11:18:27 +1000723# ifdef HAVE_ENDUTENT
andre2ff7b5d2000-06-03 14:57:40 +0000724 endutent();
Damien Millerdd47aa22000-06-27 11:18:27 +1000725# endif
andre2ff7b5d2000-06-03 14:57:40 +0000726 return 1;
andre61e67252000-06-04 17:07:49 +0000727}
Damien Millerdd47aa22000-06-27 11:18:27 +1000728# else /* UTMP_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000729
730/* write a utmp entry direct to the file */
andre61e67252000-06-04 17:07:49 +0000731/* This is a slightly modification of code in OpenBSD's login.c */
andre2ff7b5d2000-06-03 14:57:40 +0000732static int
andre61e67252000-06-04 17:07:49 +0000733utmp_write_direct(struct logininfo *li, struct utmp *ut)
734{
andre2ff7b5d2000-06-03 14:57:40 +0000735 struct utmp old_ut;
736 register int fd;
737 int tty;
738
andre6bb92372000-06-19 08:20:03 +0000739 /* FIXME: (ATL) ttyslot() needs local implementation */
Damien Miller348c9b72000-08-15 10:01:22 +1000740
Damien Millere5192fa2000-08-29 14:30:37 +1100741#if defined(HAVE_GETTTYENT)
Damien Miller348c9b72000-08-15 10:01:22 +1000742 register struct ttyent *ty;
743
744 tty=0;
745
746 setttyent();
747 while ((struct ttyent *)0 != (ty = getttyent())) {
748 tty++;
749 if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
750 break;
751 }
752 endttyent();
753
754 if((struct ttyent *)0 == ty) {
755 log("utmp_write_entry: tty not found");
756 return(1);
757 }
758#else /* FIXME */
759
andre2ff7b5d2000-06-03 14:57:40 +0000760 tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
761
Damien Millere5192fa2000-08-29 14:30:37 +1100762#endif /* HAVE_GETTTYENT */
Damien Miller348c9b72000-08-15 10:01:22 +1000763
andre2ff7b5d2000-06-03 14:57:40 +0000764 if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
765 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
766 /*
767 * Prevent luser from zero'ing out ut_host.
768 * If the new ut_line is empty but the old one is not
Damien Miller7a0e5dc2000-07-11 12:15:54 +1000769 * and ut_line and ut_name match, preserve the old ut_line.
andre2ff7b5d2000-06-03 14:57:40 +0000770 */
Damien Miller53c5d462000-06-28 00:50:50 +1000771 if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
772 (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
773 (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
Damien Miller7a0e5dc2000-07-11 12:15:54 +1000774 (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
andre2ff7b5d2000-06-03 14:57:40 +0000775 (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
Damien Miller53c5d462000-06-28 00:50:50 +1000776 }
777
andre2ff7b5d2000-06-03 14:57:40 +0000778 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
Damien Miller36ccb5c2000-08-09 16:34:27 +1000779 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut))
andre2ff7b5d2000-06-03 14:57:40 +0000780 log("utmp_write_direct: error writing %s: %s",
andre6bb92372000-06-19 08:20:03 +0000781 UTMP_FILE, strerror(errno));
andre2ff7b5d2000-06-03 14:57:40 +0000782
783 (void)close(fd);
784 return 1;
Damien Miller53c5d462000-06-28 00:50:50 +1000785 } else {
andre2ff7b5d2000-06-03 14:57:40 +0000786 return 0;
Damien Miller53c5d462000-06-28 00:50:50 +1000787 }
andre61e67252000-06-04 17:07:49 +0000788}
Damien Millerdd47aa22000-06-27 11:18:27 +1000789# endif /* UTMP_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000790
791static int
andre61e67252000-06-04 17:07:49 +0000792utmp_perform_login(struct logininfo *li)
793{
andre2ff7b5d2000-06-03 14:57:40 +0000794 struct utmp ut;
795
796 construct_utmp(li, &ut);
Damien Millerdd47aa22000-06-27 11:18:27 +1000797# ifdef UTMP_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000798 if (!utmp_write_library(li, &ut)) {
andre6bb92372000-06-19 08:20:03 +0000799 log("utmp_perform_login: utmp_write_library() failed");
andre2ff7b5d2000-06-03 14:57:40 +0000800 return 0;
801 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000802# else
andre2ff7b5d2000-06-03 14:57:40 +0000803 if (!utmp_write_direct(li, &ut)) {
804 log("utmp_perform_login: utmp_write_direct() failed");
805 return 0;
806 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000807# endif
andre2ff7b5d2000-06-03 14:57:40 +0000808 return 1;
andre61e67252000-06-04 17:07:49 +0000809}
andre2ff7b5d2000-06-03 14:57:40 +0000810
811
812static int
andre61e67252000-06-04 17:07:49 +0000813utmp_perform_logout(struct logininfo *li)
814{
andre2ff7b5d2000-06-03 14:57:40 +0000815 struct utmp ut;
816
andre6bb92372000-06-19 08:20:03 +0000817 construct_utmp(li, &ut);
Damien Millerdd47aa22000-06-27 11:18:27 +1000818# ifdef UTMP_USE_LIBRARY
andre6bb92372000-06-19 08:20:03 +0000819 if (!utmp_write_library(li, &ut)) {
820 log("utmp_perform_logout: utmp_write_library() failed");
821 return 0;
822 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000823# else
andre6bb92372000-06-19 08:20:03 +0000824 if (!utmp_write_direct(li, &ut)) {
825 log("utmp_perform_logout: utmp_write_direct() failed");
826 return 0;
827 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000828# endif
andre2ff7b5d2000-06-03 14:57:40 +0000829 return 1;
andre61e67252000-06-04 17:07:49 +0000830}
andre2ff7b5d2000-06-03 14:57:40 +0000831
832
833int
andre61e67252000-06-04 17:07:49 +0000834utmp_write_entry(struct logininfo *li)
835{
andre2ff7b5d2000-06-03 14:57:40 +0000836 switch(li->type) {
837 case LTYPE_LOGIN:
838 return utmp_perform_login(li);
839
840 case LTYPE_LOGOUT:
841 return utmp_perform_logout(li);
842
843 default:
844 log("utmp_write_entry: invalid type field");
845 return 0;
846 }
andre61e67252000-06-04 17:07:49 +0000847}
Damien Millerdd47aa22000-06-27 11:18:27 +1000848#endif /* USE_UTMP */
andre2ff7b5d2000-06-03 14:57:40 +0000849
850
851/**
andre61e67252000-06-04 17:07:49 +0000852 ** Low-level utmpx functions
andre2ff7b5d2000-06-03 14:57:40 +0000853 **/
854
855/* not much point if we don't want utmpx entries */
856#ifdef USE_UTMPX
857
andre2ff7b5d2000-06-03 14:57:40 +0000858/* if we have the wherewithall, use pututxline etc. */
Damien Millerdd47aa22000-06-27 11:18:27 +1000859# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
860 defined(HAVE_PUTUTXLINE)
andre2ff7b5d2000-06-03 14:57:40 +0000861# define UTMPX_USE_LIBRARY
Damien Millerdd47aa22000-06-27 11:18:27 +1000862# endif
andre2ff7b5d2000-06-03 14:57:40 +0000863
864
865/* write a utmpx entry with the system's help (pututxline() and pals) */
Damien Millerdd47aa22000-06-27 11:18:27 +1000866# ifdef UTMPX_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000867static int
andre61e67252000-06-04 17:07:49 +0000868utmpx_write_library(struct logininfo *li, struct utmpx *utx)
869{
andre2ff7b5d2000-06-03 14:57:40 +0000870 setutxent();
871 pututxline(utx);
872
Damien Millerdd47aa22000-06-27 11:18:27 +1000873# ifdef HAVE_ENDUTXENT
andre2ff7b5d2000-06-03 14:57:40 +0000874 endutxent();
Damien Millerdd47aa22000-06-27 11:18:27 +1000875# endif
andre2ff7b5d2000-06-03 14:57:40 +0000876 return 1;
andre61e67252000-06-04 17:07:49 +0000877}
andre2ff7b5d2000-06-03 14:57:40 +0000878
Damien Millerdd47aa22000-06-27 11:18:27 +1000879# else /* UTMPX_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000880
881/* write a utmp entry direct to the file */
882static int
andre61e67252000-06-04 17:07:49 +0000883utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
884{
andre2ff7b5d2000-06-03 14:57:40 +0000885 log("utmpx_write_direct: not implemented!");
886 return 0;
andre61e67252000-06-04 17:07:49 +0000887}
Damien Millerdd47aa22000-06-27 11:18:27 +1000888# endif /* UTMPX_USE_LIBRARY */
andre2ff7b5d2000-06-03 14:57:40 +0000889
890static int
andre61e67252000-06-04 17:07:49 +0000891utmpx_perform_login(struct logininfo *li)
892{
andre2ff7b5d2000-06-03 14:57:40 +0000893 struct utmpx utx;
894
895 construct_utmpx(li, &utx);
Damien Millerdd47aa22000-06-27 11:18:27 +1000896# ifdef UTMPX_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000897 if (!utmpx_write_library(li, &utx)) {
898 log("utmpx_perform_login: utmp_write_library() failed");
899 return 0;
900 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000901# else
andre2ff7b5d2000-06-03 14:57:40 +0000902 if (!utmpx_write_direct(li, &ut)) {
903 log("utmpx_perform_login: utmp_write_direct() failed");
904 return 0;
905 }
Damien Millerdd47aa22000-06-27 11:18:27 +1000906# endif
andre2ff7b5d2000-06-03 14:57:40 +0000907 return 1;
andre61e67252000-06-04 17:07:49 +0000908}
andre2ff7b5d2000-06-03 14:57:40 +0000909
910
911static int
andre61e67252000-06-04 17:07:49 +0000912utmpx_perform_logout(struct logininfo *li)
913{
andre2ff7b5d2000-06-03 14:57:40 +0000914 struct utmpx utx;
915
916 memset(&utx, '\0', sizeof(utx));
917 set_utmpx_time(li, &utx);
918 line_stripname(utx.ut_line, li->line, sizeof(utx.ut_line));
Damien Millerdd47aa22000-06-27 11:18:27 +1000919# ifdef HAVE_ID_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000920 line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
Damien Millerdd47aa22000-06-27 11:18:27 +1000921# endif
922# ifdef HAVE_TYPE_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +0000923 utx.ut_type = DEAD_PROCESS;
Damien Millerdd47aa22000-06-27 11:18:27 +1000924# endif
andre2ff7b5d2000-06-03 14:57:40 +0000925
Damien Millerdd47aa22000-06-27 11:18:27 +1000926# ifdef UTMPX_USE_LIBRARY
andre2ff7b5d2000-06-03 14:57:40 +0000927 utmpx_write_library(li, &utx);
Damien Millerdd47aa22000-06-27 11:18:27 +1000928# else
andre2ff7b5d2000-06-03 14:57:40 +0000929 utmpx_write_direct(li, &utx);
Damien Millerdd47aa22000-06-27 11:18:27 +1000930# endif
andre2ff7b5d2000-06-03 14:57:40 +0000931 return 1;
andre61e67252000-06-04 17:07:49 +0000932}
andre2ff7b5d2000-06-03 14:57:40 +0000933
andre2ff7b5d2000-06-03 14:57:40 +0000934int
andre61e67252000-06-04 17:07:49 +0000935utmpx_write_entry(struct logininfo *li)
936{
andre2ff7b5d2000-06-03 14:57:40 +0000937 switch(li->type) {
938 case LTYPE_LOGIN:
939 return utmpx_perform_login(li);
940 case LTYPE_LOGOUT:
941 return utmpx_perform_logout(li);
942 default:
943 log("utmpx_write_entry: invalid type field");
944 return 0;
945 }
andre61e67252000-06-04 17:07:49 +0000946}
Damien Millerdd47aa22000-06-27 11:18:27 +1000947#endif /* USE_UTMPX */
andre2ff7b5d2000-06-03 14:57:40 +0000948
949
950/**
andre61e67252000-06-04 17:07:49 +0000951 ** Low-level wtmp functions
andre2ff7b5d2000-06-03 14:57:40 +0000952 **/
953
954#ifdef USE_WTMP
955
andre2ff7b5d2000-06-03 14:57:40 +0000956/* write a wtmp entry direct to the end of the file */
andre61e67252000-06-04 17:07:49 +0000957/* This is a slight modification of code in OpenBSD's logwtmp.c */
andre2ff7b5d2000-06-03 14:57:40 +0000958static int
andre61e67252000-06-04 17:07:49 +0000959wtmp_write(struct logininfo *li, struct utmp *ut)
960{
andre2ff7b5d2000-06-03 14:57:40 +0000961 struct stat buf;
962 int fd, ret = 1;
963
964 if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
965 log("wtmp_write: problem writing %s: %s",
966 WTMP_FILE, strerror(errno));
967 return 0;
968 }
andre61e67252000-06-04 17:07:49 +0000969 if (fstat(fd, &buf) == 0)
Damien Miller53c5d462000-06-28 00:50:50 +1000970 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
andre2ff7b5d2000-06-03 14:57:40 +0000971 ftruncate(fd, buf.st_size);
972 log("wtmp_write: problem writing %s: %s",
973 WTMP_FILE, strerror(errno));
974 ret = 0;
975 }
976 (void)close(fd);
andre2ff7b5d2000-06-03 14:57:40 +0000977 return ret;
andre61e67252000-06-04 17:07:49 +0000978}
andre2ff7b5d2000-06-03 14:57:40 +0000979
andre2ff7b5d2000-06-03 14:57:40 +0000980static int
Damien Millerdd47aa22000-06-27 11:18:27 +1000981wtmp_perform_login(struct logininfo *li)
982{
andre2ff7b5d2000-06-03 14:57:40 +0000983 struct utmp ut;
984
985 construct_utmp(li, &ut);
986 return wtmp_write(li, &ut);
andre61e67252000-06-04 17:07:49 +0000987}
andre2ff7b5d2000-06-03 14:57:40 +0000988
989
990static int
andre61e67252000-06-04 17:07:49 +0000991wtmp_perform_logout(struct logininfo *li)
992{
andre2ff7b5d2000-06-03 14:57:40 +0000993 struct utmp ut;
994
995 construct_utmp(li, &ut);
andre2ff7b5d2000-06-03 14:57:40 +0000996 return wtmp_write(li, &ut);
andre61e67252000-06-04 17:07:49 +0000997}
andre2ff7b5d2000-06-03 14:57:40 +0000998
999
1000int
andre61e67252000-06-04 17:07:49 +00001001wtmp_write_entry(struct logininfo *li)
1002{
andre2ff7b5d2000-06-03 14:57:40 +00001003 switch(li->type) {
1004 case LTYPE_LOGIN:
1005 return wtmp_perform_login(li);
1006 case LTYPE_LOGOUT:
1007 return wtmp_perform_logout(li);
1008 default:
1009 log("wtmp_write_entry: invalid type field");
1010 return 0;
1011 }
andre61e67252000-06-04 17:07:49 +00001012}
andre2ff7b5d2000-06-03 14:57:40 +00001013
1014
andre6bb92372000-06-19 08:20:03 +00001015/* Notes on fetching login data from wtmp/wtmpx
1016 *
1017 * Logouts are usually recorded with (amongst other things) a blank
1018 * username on a given tty line. However, some systems (HP-UX is one)
1019 * leave all fields set, but change the ut_type field to DEAD_PROCESS.
1020 *
1021 * Since we're only looking for logins here, we know that the username
1022 * must be set correctly. On systems that leave it in, we check for
1023 * ut_type==USER_PROCESS (indicating a login.)
1024 *
1025 * Portability: Some systems may set something other than USER_PROCESS
1026 * to indicate a login process. I don't know of any as I write. Also,
1027 * it's possible that some systems may both leave the username in
1028 * place and not have ut_type.
1029 */
1030
andre6bb92372000-06-19 08:20:03 +00001031/* return true if this wtmp entry indicates a login */
1032static int
1033wtmp_islogin(struct logininfo *li, struct utmp *ut)
1034{
Damien Miller7a0e5dc2000-07-11 12:15:54 +10001035 if (strncmp(li->username, ut->ut_name,
1036 MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
Damien Millerdd47aa22000-06-27 11:18:27 +10001037# ifdef HAVE_TYPE_IN_UTMP
andre6bb92372000-06-19 08:20:03 +00001038 if (ut->ut_type & USER_PROCESS)
1039 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001040# else
andre6bb92372000-06-19 08:20:03 +00001041 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001042# endif
andre6bb92372000-06-19 08:20:03 +00001043 }
1044 return 0;
1045}
1046
andre2ff7b5d2000-06-03 14:57:40 +00001047int
andre61e67252000-06-04 17:07:49 +00001048wtmp_get_entry(struct logininfo *li)
1049{
andre2ff7b5d2000-06-03 14:57:40 +00001050 struct stat st;
1051 struct utmp ut;
andre6bb92372000-06-19 08:20:03 +00001052 int fd, found=0;
1053
1054 /* Clear the time entries in our logininfo */
1055 li->tv_sec = li->tv_usec = 0;
andre2ff7b5d2000-06-03 14:57:40 +00001056
1057 if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1058 log("wtmp_get_entry: problem opening %s: %s",
1059 WTMP_FILE, strerror(errno));
1060 return 0;
1061 }
andre61e67252000-06-04 17:07:49 +00001062 if (fstat(fd, &st) != 0) {
andre2ff7b5d2000-06-03 14:57:40 +00001063 log("wtmp_get_entry: couldn't stat %s: %s",
1064 WTMP_FILE, strerror(errno));
1065 close(fd);
1066 return 0;
1067 }
andre2ff7b5d2000-06-03 14:57:40 +00001068
andre6bb92372000-06-19 08:20:03 +00001069 /* Seek to the start of the last struct utmp */
Damien Miller348c9b72000-08-15 10:01:22 +10001070 if (lseek(fd, (off_t)(0 - sizeof(struct utmp)), SEEK_END) == -1) {
andre6bb92372000-06-19 08:20:03 +00001071 /* Looks like we've got a fresh wtmp file */
1072 close(fd);
1073 return 0;
1074 }
1075
1076 while (!found) {
Damien Miller53c5d462000-06-28 00:50:50 +10001077 if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
andre2ff7b5d2000-06-03 14:57:40 +00001078 log("wtmp_get_entry: read of %s failed: %s",
1079 WTMP_FILE, strerror(errno));
1080 close (fd);
1081 return 0;
1082 }
andre6bb92372000-06-19 08:20:03 +00001083 if ( wtmp_islogin(li, &ut) ) {
1084 found = 1;
1085 /* We've already checked for a time in struct
1086 * utmp, in login_getlast(). */
Damien Millerdd47aa22000-06-27 11:18:27 +10001087# ifdef HAVE_TIME_IN_UTMP
andre2ff7b5d2000-06-03 14:57:40 +00001088 li->tv_sec = ut.ut_time;
Damien Millerdd47aa22000-06-27 11:18:27 +10001089# else
andre2ff7b5d2000-06-03 14:57:40 +00001090# if HAVE_TV_IN_UTMP
1091 li->tv_sec = ut.ut_tv.tv_sec;
1092# endif
Damien Millerdd47aa22000-06-27 11:18:27 +10001093# endif
andre6bb92372000-06-19 08:20:03 +00001094 line_fullname(li->line, ut.ut_line,
1095 MIN_SIZEOF(li->line, ut.ut_line));
Damien Millerdd47aa22000-06-27 11:18:27 +10001096# ifdef HAVE_HOST_IN_UTMP
andre6bb92372000-06-19 08:20:03 +00001097 strlcpy(li->hostname, ut.ut_host,
1098 MIN_SIZEOF(li->hostname, ut.ut_host));
Damien Millerdd47aa22000-06-27 11:18:27 +10001099# endif
andre6bb92372000-06-19 08:20:03 +00001100 continue;
andre2ff7b5d2000-06-03 14:57:40 +00001101 }
andre6bb92372000-06-19 08:20:03 +00001102 /* Seek back 2 x struct utmp */
andre2ff7b5d2000-06-03 14:57:40 +00001103 if (lseek(fd, (off_t)(0-2*sizeof(struct utmp)), SEEK_CUR) == -1) {
andre6bb92372000-06-19 08:20:03 +00001104 /* We've found the start of the file, so quit */
andre2ff7b5d2000-06-03 14:57:40 +00001105 close (fd);
1106 return 0;
1107 }
andre6bb92372000-06-19 08:20:03 +00001108 }
1109
1110 /* We found an entry. Tidy up and return */
1111 close(fd);
andre2ff7b5d2000-06-03 14:57:40 +00001112 return 1;
andre61e67252000-06-04 17:07:49 +00001113}
Damien Millerdd47aa22000-06-27 11:18:27 +10001114# endif /* USE_WTMP */
andre2ff7b5d2000-06-03 14:57:40 +00001115
1116
1117/**
andre61e67252000-06-04 17:07:49 +00001118 ** Low-level wtmpx functions
andre2ff7b5d2000-06-03 14:57:40 +00001119 **/
1120
1121#ifdef USE_WTMPX
andre2ff7b5d2000-06-03 14:57:40 +00001122/* write a wtmpx entry direct to the end of the file */
andre61e67252000-06-04 17:07:49 +00001123/* This is a slight modification of code in OpenBSD's logwtmp.c */
andre2ff7b5d2000-06-03 14:57:40 +00001124static int
andre61e67252000-06-04 17:07:49 +00001125wtmpx_write(struct logininfo *li, struct utmpx *utx)
1126{
andre2ff7b5d2000-06-03 14:57:40 +00001127 struct stat buf;
1128 int fd, ret = 1;
1129
1130 if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1131 log("wtmpx_write: problem opening %s: %s",
1132 WTMPX_FILE, strerror(errno));
1133 return 0;
1134 }
1135
1136 if (fstat(fd, &buf) == 0)
Damien Miller53c5d462000-06-28 00:50:50 +10001137 if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
andre2ff7b5d2000-06-03 14:57:40 +00001138 ftruncate(fd, buf.st_size);
1139 log("wtmpx_write: problem writing %s: %s",
1140 WTMPX_FILE, strerror(errno));
1141 ret = 0;
1142 }
1143 (void)close(fd);
1144
1145 return ret;
andre61e67252000-06-04 17:07:49 +00001146}
andre2ff7b5d2000-06-03 14:57:40 +00001147
1148
1149static int
andre61e67252000-06-04 17:07:49 +00001150wtmpx_perform_login(struct logininfo *li)
1151{
andre2ff7b5d2000-06-03 14:57:40 +00001152 struct utmpx utx;
1153
1154 construct_utmpx(li, &utx);
1155 return wtmpx_write(li, &utx);
andre61e67252000-06-04 17:07:49 +00001156}
andre2ff7b5d2000-06-03 14:57:40 +00001157
1158
1159static int
andre61e67252000-06-04 17:07:49 +00001160wtmpx_perform_logout(struct logininfo *li)
1161{
andre2ff7b5d2000-06-03 14:57:40 +00001162 struct utmpx utx;
1163
1164 construct_utmpx(li, &utx);
andre2ff7b5d2000-06-03 14:57:40 +00001165 return wtmpx_write(li, &utx);
andre61e67252000-06-04 17:07:49 +00001166}
andre2ff7b5d2000-06-03 14:57:40 +00001167
1168
1169int
andre61e67252000-06-04 17:07:49 +00001170wtmpx_write_entry(struct logininfo *li)
1171{
andre2ff7b5d2000-06-03 14:57:40 +00001172 switch(li->type) {
1173 case LTYPE_LOGIN:
1174 return wtmpx_perform_login(li);
1175 case LTYPE_LOGOUT:
1176 return wtmpx_perform_logout(li);
1177 default:
1178 log("wtmpx_write_entry: invalid type field");
1179 return 0;
1180 }
andre61e67252000-06-04 17:07:49 +00001181}
andre2ff7b5d2000-06-03 14:57:40 +00001182
andre6bb92372000-06-19 08:20:03 +00001183/* Please see the notes above wtmp_islogin() for information about the
1184 next two functions */
1185
1186/* Return true if this wtmpx entry indicates a login */
1187static int
1188wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1189{
Damien Miller7a0e5dc2000-07-11 12:15:54 +10001190 if ( strncmp(li->username, utx->ut_name,
1191 MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
Damien Millerdd47aa22000-06-27 11:18:27 +10001192# ifdef HAVE_TYPE_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +00001193 if (utx->ut_type == USER_PROCESS)
1194 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001195# else
andre6bb92372000-06-19 08:20:03 +00001196 return 1;
Damien Millerdd47aa22000-06-27 11:18:27 +10001197# endif
andre6bb92372000-06-19 08:20:03 +00001198 }
1199 return 0;
1200}
1201
andre2ff7b5d2000-06-03 14:57:40 +00001202
1203int
andre61e67252000-06-04 17:07:49 +00001204wtmpx_get_entry(struct logininfo *li)
1205{
andre2ff7b5d2000-06-03 14:57:40 +00001206 struct stat st;
1207 struct utmpx utx;
andre6bb92372000-06-19 08:20:03 +00001208 int fd, found=0;
1209
1210 /* Clear the time entries */
1211 li->tv_sec = li->tv_usec = 0;
andre2ff7b5d2000-06-03 14:57:40 +00001212
1213 if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1214 log("wtmpx_get_entry: problem opening %s: %s",
1215 WTMPX_FILE, strerror(errno));
1216 return 0;
1217 }
andre61e67252000-06-04 17:07:49 +00001218 if (fstat(fd, &st) != 0) {
andre2ff7b5d2000-06-03 14:57:40 +00001219 log("wtmpx_get_entry: couldn't stat %s: %s",
1220 WTMP_FILE, strerror(errno));
1221 close(fd);
1222 return 0;
1223 }
andre6bb92372000-06-19 08:20:03 +00001224
1225 /* Seek to the start of the last struct utmpx */
1226 if (lseek(fd, (off_t)(0-sizeof(struct utmpx)), SEEK_END) == -1 ) {
1227 /* probably a newly rotated wtmpx file */
1228 close(fd);
1229 return 0;
1230 }
andre2ff7b5d2000-06-03 14:57:40 +00001231
andre6bb92372000-06-19 08:20:03 +00001232 while (!found) {
Damien Miller53c5d462000-06-28 00:50:50 +10001233 if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
andre2ff7b5d2000-06-03 14:57:40 +00001234 log("wtmpx_get_entry: read of %s failed: %s",
1235 WTMPX_FILE, strerror(errno));
1236 close (fd);
1237 return 0;
1238 }
andre2ff7b5d2000-06-03 14:57:40 +00001239 /* Logouts are recorded as a blank username on a particular line.
1240 * So, we just need to find the username in struct utmpx */
andre6bb92372000-06-19 08:20:03 +00001241 if ( wtmpx_islogin(li, &utx) ) {
Damien Millerdd47aa22000-06-27 11:18:27 +10001242# ifdef HAVE_TV_IN_UTMPX
andre2ff7b5d2000-06-03 14:57:40 +00001243 li->tv_sec = utx.ut_tv.tv_sec;
Damien Millerdd47aa22000-06-27 11:18:27 +10001244# else
andre2ff7b5d2000-06-03 14:57:40 +00001245# ifdef HAVE_TIME_IN_UTMPX
1246 li->tv_sec = utx.ut_time;
1247# endif
Damien Millerdd47aa22000-06-27 11:18:27 +10001248# endif
Damien Miller1a132252000-06-13 21:23:17 +10001249 line_fullname(li->line, utx.ut_line, sizeof(li->line));
Damien Millerdd47aa22000-06-27 11:18:27 +10001250# ifdef HAVE_HOST_IN_UTMPX
andre6bb92372000-06-19 08:20:03 +00001251 strlcpy(li->hostname, utx.ut_host,
1252 MIN_SIZEOF(li->hostname, utx.ut_host));
Damien Millerdd47aa22000-06-27 11:18:27 +10001253# endif
andre6bb92372000-06-19 08:20:03 +00001254 continue;
andre2ff7b5d2000-06-03 14:57:40 +00001255 }
1256 if (lseek(fd, (off_t)(0-2*sizeof(struct utmpx)), SEEK_CUR) == -1) {
1257 close (fd);
1258 return 0;
1259 }
andre6bb92372000-06-19 08:20:03 +00001260 }
1261
1262 close(fd);
andre2ff7b5d2000-06-03 14:57:40 +00001263 return 1;
andre61e67252000-06-04 17:07:49 +00001264}
Damien Millerd5bf3072000-06-07 21:32:13 +10001265#endif /* USE_WTMPX */
andre2ff7b5d2000-06-03 14:57:40 +00001266
andre2ff7b5d2000-06-03 14:57:40 +00001267/**
andre61e67252000-06-04 17:07:49 +00001268 ** Low-level libutil login() functions
andre2ff7b5d2000-06-03 14:57:40 +00001269 **/
1270
1271#ifdef USE_LOGIN
andre2ff7b5d2000-06-03 14:57:40 +00001272static int
andre61e67252000-06-04 17:07:49 +00001273syslogin_perform_login(struct logininfo *li)
1274{
andre2ff7b5d2000-06-03 14:57:40 +00001275 struct utmp *ut;
1276
Damien Miller348c9b72000-08-15 10:01:22 +10001277 if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
andre2ff7b5d2000-06-03 14:57:40 +00001278 log("syslogin_perform_login: couldn't malloc()");
1279 return 0;
1280 }
1281 construct_utmp(li, ut);
1282 login(ut);
1283
1284 return 1;
andre61e67252000-06-04 17:07:49 +00001285}
1286
andre2ff7b5d2000-06-03 14:57:40 +00001287static int
andre61e67252000-06-04 17:07:49 +00001288syslogin_perform_logout(struct logininfo *li)
1289{
Damien Millerdd47aa22000-06-27 11:18:27 +10001290# ifdef HAVE_LOGOUT
andre2ff7b5d2000-06-03 14:57:40 +00001291 char line[8];
1292
1293 (void)line_stripname(line, li->line, sizeof(line));
1294
1295 if (!logout(line)) {
1296 log("syslogin_perform_logout: logout() returned an error");
Damien Millerdd47aa22000-06-27 11:18:27 +10001297# ifdef HAVE_LOGWTMP
andre2ff7b5d2000-06-03 14:57:40 +00001298 } else {
1299 logwtmp(line, "", "");
Damien Millerdd47aa22000-06-27 11:18:27 +10001300# endif
Damien Miller9b6d4ab2000-07-02 08:43:18 +10001301 }
andre6bb92372000-06-19 08:20:03 +00001302 /* FIXME: (ATL - if the need arises) What to do if we have
1303 * login, but no logout? what if logout but no logwtmp? All
1304 * routines are in libutil so they should all be there,
1305 * but... */
Damien Millerdd47aa22000-06-27 11:18:27 +10001306# endif
andre2ff7b5d2000-06-03 14:57:40 +00001307 return 1;
andre61e67252000-06-04 17:07:49 +00001308}
andre2ff7b5d2000-06-03 14:57:40 +00001309
andre2ff7b5d2000-06-03 14:57:40 +00001310int
andre61e67252000-06-04 17:07:49 +00001311syslogin_write_entry(struct logininfo *li)
1312{
andre2ff7b5d2000-06-03 14:57:40 +00001313 switch (li->type) {
1314 case LTYPE_LOGIN:
1315 return syslogin_perform_login(li);
1316 case LTYPE_LOGOUT:
1317 return syslogin_perform_logout(li);
1318 default:
1319 log("syslogin_write_entry: Invalid type field");
1320 return 0;
1321 }
andre61e67252000-06-04 17:07:49 +00001322}
Damien Millerd5bf3072000-06-07 21:32:13 +10001323#endif /* USE_LOGIN */
andre2ff7b5d2000-06-03 14:57:40 +00001324
1325/* end of file log-syslogin.c */
1326
andre2ff7b5d2000-06-03 14:57:40 +00001327/**
andre61e67252000-06-04 17:07:49 +00001328 ** Low-level lastlog functions
andre2ff7b5d2000-06-03 14:57:40 +00001329 **/
1330
1331#ifdef USE_LASTLOG
Damien Millerdd47aa22000-06-27 11:18:27 +10001332#define LL_FILE 1
1333#define LL_DIR 2
1334#define LL_OTHER 3
andre2ff7b5d2000-06-03 14:57:40 +00001335
andre2ff7b5d2000-06-03 14:57:40 +00001336static void
andre61e67252000-06-04 17:07:49 +00001337lastlog_construct(struct logininfo *li, struct lastlog *last)
1338{
andre2ff7b5d2000-06-03 14:57:40 +00001339 /* clear the structure */
Damien Miller348c9b72000-08-15 10:01:22 +10001340 memset(last, '\0', sizeof(*last));
andre2ff7b5d2000-06-03 14:57:40 +00001341
Damien Millerdd47aa22000-06-27 11:18:27 +10001342 (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
andre6bb92372000-06-19 08:20:03 +00001343 strlcpy(last->ll_host, li->hostname,
1344 MIN_SIZEOF(last->ll_host, li->hostname));
andre2ff7b5d2000-06-03 14:57:40 +00001345 last->ll_time = li->tv_sec;
andre61e67252000-06-04 17:07:49 +00001346}
andre2ff7b5d2000-06-03 14:57:40 +00001347
andre2ff7b5d2000-06-03 14:57:40 +00001348static int
andre61e67252000-06-04 17:07:49 +00001349lastlog_filetype(char *filename)
1350{
andre2ff7b5d2000-06-03 14:57:40 +00001351 struct stat st;
1352
Damien Millerdd47aa22000-06-27 11:18:27 +10001353 if (stat(LASTLOG_FILE, &st) != 0) {
1354 log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
1355 strerror(errno));
andre2ff7b5d2000-06-03 14:57:40 +00001356 return 0;
1357 }
andre2ff7b5d2000-06-03 14:57:40 +00001358 if (S_ISDIR(st.st_mode))
1359 return LL_DIR;
1360 else if (S_ISREG(st.st_mode))
1361 return LL_FILE;
1362 else
1363 return LL_OTHER;
andre61e67252000-06-04 17:07:49 +00001364}
andre2ff7b5d2000-06-03 14:57:40 +00001365
1366
1367/* open the file (using filemode) and seek to the login entry */
1368static int
andre61e67252000-06-04 17:07:49 +00001369lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1370{
andre2ff7b5d2000-06-03 14:57:40 +00001371 off_t offset;
1372 int type;
1373 char lastlog_file[1024];
1374
1375 type = lastlog_filetype(LASTLOG_FILE);
1376 switch (type) {
Damien Millerf8af08d2000-06-27 09:40:06 +10001377 case LL_FILE:
1378 strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1379 break;
1380 case LL_DIR:
1381 snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1382 LASTLOG_FILE, li->username);
1383 break;
1384 default:
1385 log("lastlog_openseek: %.100s is not a file or directory!",
1386 LASTLOG_FILE);
1387 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +10001388 }
andre2ff7b5d2000-06-03 14:57:40 +00001389
1390 *fd = open(lastlog_file, filemode);
1391 if ( *fd < 0) {
Damien Miller53c5d462000-06-28 00:50:50 +10001392 debug("lastlog_openseek: Couldn't open %s: %s",
andre2ff7b5d2000-06-03 14:57:40 +00001393 lastlog_file, strerror(errno));
1394 return 0;
1395 }
Damien Millerdd47aa22000-06-27 11:18:27 +10001396
Damien Millere477ef62000-08-15 10:21:17 +10001397 if (type == LL_FILE) {
1398 /* find this uid's offset in the lastlog file */
1399 offset = (off_t) ( (long)li->uid * sizeof(struct lastlog));
andre2ff7b5d2000-06-03 14:57:40 +00001400
Damien Millere477ef62000-08-15 10:21:17 +10001401 if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1402 log("lastlog_openseek: %s->lseek(): %s",
1403 lastlog_file, strerror(errno));
1404 return 0;
1405 }
andre2ff7b5d2000-06-03 14:57:40 +00001406 }
Damien Millere477ef62000-08-15 10:21:17 +10001407
andre2ff7b5d2000-06-03 14:57:40 +00001408 return 1;
andre61e67252000-06-04 17:07:49 +00001409}
andre2ff7b5d2000-06-03 14:57:40 +00001410
1411static int
andre61e67252000-06-04 17:07:49 +00001412lastlog_perform_login(struct logininfo *li)
1413{
andre2ff7b5d2000-06-03 14:57:40 +00001414 struct lastlog last;
1415 int fd;
1416
1417 /* create our struct lastlog */
1418 lastlog_construct(li, &last);
1419
Damien Millerc1132e72000-08-18 14:08:38 +10001420 if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
1421 return(0);
1422
andre2ff7b5d2000-06-03 14:57:40 +00001423 /* write the entry */
Damien Millerc1132e72000-08-18 14:08:38 +10001424 if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) {
1425 close(fd);
1426 log("lastlog_write_filemode: Error writing to %s: %s",
1427 LASTLOG_FILE, strerror(errno));
andre2ff7b5d2000-06-03 14:57:40 +00001428 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +10001429 }
Damien Millerc1132e72000-08-18 14:08:38 +10001430
1431 close(fd);
1432 return 1;
andre61e67252000-06-04 17:07:49 +00001433}
andre2ff7b5d2000-06-03 14:57:40 +00001434
andre2ff7b5d2000-06-03 14:57:40 +00001435int
andre61e67252000-06-04 17:07:49 +00001436lastlog_write_entry(struct logininfo *li)
1437{
andre2ff7b5d2000-06-03 14:57:40 +00001438 switch(li->type) {
1439 case LTYPE_LOGIN:
1440 return lastlog_perform_login(li);
1441 default:
1442 log("lastlog_write_entry: Invalid type field");
1443 return 0;
1444 }
andre61e67252000-06-04 17:07:49 +00001445}
andre2ff7b5d2000-06-03 14:57:40 +00001446
andre2ff7b5d2000-06-03 14:57:40 +00001447static void
andre61e67252000-06-04 17:07:49 +00001448lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1449{
andre2ff7b5d2000-06-03 14:57:40 +00001450 line_fullname(li->line, last->ll_line, sizeof(li->line));
Damien Millerdd47aa22000-06-27 11:18:27 +10001451 strlcpy(li->hostname, last->ll_host,
andre6bb92372000-06-19 08:20:03 +00001452 MIN_SIZEOF(li->hostname, last->ll_host));
andre2ff7b5d2000-06-03 14:57:40 +00001453 li->tv_sec = last->ll_time;
andre61e67252000-06-04 17:07:49 +00001454}
andre2ff7b5d2000-06-03 14:57:40 +00001455
andre2ff7b5d2000-06-03 14:57:40 +00001456int
andre61e67252000-06-04 17:07:49 +00001457lastlog_get_entry(struct logininfo *li)
1458{
andre2ff7b5d2000-06-03 14:57:40 +00001459 struct lastlog last;
1460 int fd;
1461
1462 if (lastlog_openseek(li, &fd, O_RDONLY)) {
Damien Miller53c5d462000-06-28 00:50:50 +10001463 if (atomicio(read, fd, &last, sizeof(last)) != sizeof(last)) {
1464 log("lastlog_get_entry: Error reading from %s: %s",
andre2ff7b5d2000-06-03 14:57:40 +00001465 LASTLOG_FILE, strerror(errno));
1466 return 0;
1467 } else {
1468 lastlog_populate_entry(li, &last);
1469 return 1;
1470 }
Damien Millerdd47aa22000-06-27 11:18:27 +10001471 } else {
andre2ff7b5d2000-06-03 14:57:40 +00001472 return 0;
Damien Millerdd47aa22000-06-27 11:18:27 +10001473 }
andre61e67252000-06-04 17:07:49 +00001474}
Damien Millerd5bf3072000-06-07 21:32:13 +10001475#endif /* USE_LASTLOG */