]> andersk Git - openssh.git/blame - loginrec.c
- (dtucker) [loginrec.c openbsd-compat/xmmap.c openbsd-compat/bindresvport.c
[openssh.git] / loginrec.c
CommitLineData
1d7b9b20 1/*
2 * Copyright (c) 2000 Andre Lucas. All rights reserved.
564dd50a 3 * Portions copyright (c) 1998 Todd C. Miller
4 * Portions copyright (c) 1996 Jason Downs
5 * Portions copyright (c) 1996 Theo de Raadt
1d7b9b20 6 *
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.
1d7b9b20 15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
b6610e8f 28/*
29 * The btmp logging code is derived from login.c from util-linux and is under
30 * the the following license:
31 *
32 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
33 * All rights reserved.
34 *
35 * Redistribution and use in source and binary forms are permitted
36 * provided that the above copyright notice and this paragraph are
37 * duplicated in all such forms and that any documentation,
38 * advertising materials, and other materials related to such
39 * distribution and use acknowledge that the software was developed
40 * by the University of California, Berkeley. The name of the
41 * University may not be used to endorse or promote products derived
42 * from this software without specific prior written permission.
43 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
44 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
45 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
46 */
47
48
2b87da3b 49/**
1d7b9b20 50 ** loginrec.c: platform-independent login recording and lastlog retrieval
51 **/
52
564dd50a 53/*
fa64c868 54 * The new login code explained
55 * ============================
56 *
57 * This code attempts to provide a common interface to login recording
58 * (utmp and friends) and last login time retrieval.
59 *
60 * Its primary means of achieving this is to use 'struct logininfo', a
61 * union of all the useful fields in the various different types of
62 * system login record structures one finds on UNIX variants.
63 *
64 * We depend on autoconf to define which recording methods are to be
65 * used, and which fields are contained in the relevant data structures
66 * on the local system. Many C preprocessor symbols affect which code
67 * gets compiled here.
68 *
69 * The code is designed to make it easy to modify a particular
70 * recording method, without affecting other methods nor requiring so
71 * many nested conditional compilation blocks as were commonplace in
72 * the old code.
73 *
74 * For login recording, we try to use the local system's libraries as
75 * these are clearly most likely to work correctly. For utmp systems
76 * this usually means login() and logout() or setutent() etc., probably
77 * in libutil, along with logwtmp() etc. On these systems, we fall back
78 * to writing the files directly if we have to, though this method
79 * requires very thorough testing so we do not corrupt local auditing
80 * information. These files and their access methods are very system
81 * specific indeed.
82 *
83 * For utmpx systems, the corresponding library functions are
84 * setutxent() etc. To the author's knowledge, all utmpx systems have
85 * these library functions and so no direct write is attempted. If such
86 * a system exists and needs support, direct analogues of the [uw]tmp
87 * code should suffice.
88 *
89 * Retrieving the time of last login ('lastlog') is in some ways even
90 * more problemmatic than login recording. Some systems provide a
91 * simple table of all users which we seek based on uid and retrieve a
92 * relatively standard structure. Others record the same information in
93 * a directory with a separate file, and others don't record the
94 * information separately at all. For systems in the latter category,
95 * we look backwards in the wtmp or wtmpx file for the last login entry
96 * for our user. Naturally this is slower and on busy systems could
97 * incur a significant performance penalty.
98 *
99 * Calling the new code
100 * --------------------
101 *
102 * In OpenSSH all login recording and retrieval is performed in
103 * login.c. Here you'll find working examples. Also, in the logintest.c
104 * program there are more examples.
105 *
106 * Internal handler calling method
107 * -------------------------------
108 *
109 * When a call is made to login_login() or login_logout(), both
110 * routines set a struct logininfo flag defining which action (log in,
111 * or log out) is to be taken. They both then call login_write(), which
112 * calls whichever of the many structure-specific handlers autoconf
113 * selects for the local system.
114 *
115 * The handlers themselves handle system data structure specifics. Both
116 * struct utmp and struct utmpx have utility functions (see
117 * construct_utmp*()) to try to make it simpler to add extra systems
118 * that introduce new features to either structure.
119 *
120 * While it may seem terribly wasteful to replicate so much similar
121 * code for each method, experience has shown that maintaining code to
122 * write both struct utmp and utmpx in one function, whilst maintaining
123 * support for all systems whether they have library support or not, is
124 * a difficult and time-consuming task.
125 *
126 * Lastlog support proceeds similarly. Functions login_get_lastlog()
127 * (and its OpenSSH-tuned friend login_get_lastlog_time()) call
128 * getlast_entry(), which tries one of three methods to find the last
129 * login time. It uses local system lastlog support if it can,
130 * otherwise it tries wtmp or wtmpx before giving up and returning 0,
131 * meaning "tilt".
132 *
133 * Maintenance
134 * -----------
135 *
136 * In many cases it's possible to tweak autoconf to select the correct
137 * methods for a particular platform, either by improving the detection
138 * code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE
139 * symbols for the platform.
140 *
141 * Use logintest to check which symbols are defined before modifying
142 * configure.ac and loginrec.c. (You have to build logintest yourself
143 * with 'make logintest' as it's not built by default.)
144 *
145 * Otherwise, patches to the specific method(s) are very helpful!
146 */
1d7b9b20 147
148#include "includes.h"
149
81e73e57 150#include <sys/types.h>
151#include <sys/stat.h>
9794d008 152#include <sys/socket.h>
153
154#include <netinet/in.h>
81e73e57 155
fec71b2f 156#include <errno.h>
22bbb3e6 157#include <fcntl.h>
b1842393 158#include <pwd.h>
159
1d7b9b20 160#include "ssh.h"
161#include "xmalloc.h"
162#include "loginrec.h"
42f11eb2 163#include "log.h"
164#include "atomicio.h"
b6610e8f 165#include "packet.h"
166#include "canohost.h"
c00e4d75 167#include "auth.h"
c53d8c93 168#include "buffer.h"
1d7b9b20 169
44d5f7f7 170#ifdef HAVE_UTIL_H
fa64c868 171# include <util.h>
44d5f7f7 172#endif
173
8f523d67 174#ifdef HAVE_LIBUTIL_H
fa64c868 175# include <libutil.h>
8f523d67 176#endif
177
1d7b9b20 178/**
179 ** prototypes for helper functions in this file
180 **/
181
182#if HAVE_UTMP_H
1d7b9b20 183void set_utmp_time(struct logininfo *li, struct utmp *ut);
184void construct_utmp(struct logininfo *li, struct utmp *ut);
185#endif
186
187#ifdef HAVE_UTMPX_H
1d7b9b20 188void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
189void construct_utmpx(struct logininfo *li, struct utmpx *ut);
190#endif
191
192int utmp_write_entry(struct logininfo *li);
193int utmpx_write_entry(struct logininfo *li);
194int wtmp_write_entry(struct logininfo *li);
195int wtmpx_write_entry(struct logininfo *li);
196int lastlog_write_entry(struct logininfo *li);
197int syslogin_write_entry(struct logininfo *li);
198
199int getlast_entry(struct logininfo *li);
200int lastlog_get_entry(struct logininfo *li);
201int wtmp_get_entry(struct logininfo *li);
202int wtmpx_get_entry(struct logininfo *li);
203
5ccf88cb 204extern Buffer loginmsg;
205
5abcdf8e 206/* pick the shortest string */
fa64c868 207#define MIN_SIZEOF(s1,s2) (sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2))
5abcdf8e 208
1d7b9b20 209/**
210 ** platform-independent login functions
211 **/
212
fa64c868 213/*
214 * login_login(struct logininfo *) - Record a login
2b87da3b 215 *
5abcdf8e 216 * Call with a pointer to a struct logininfo initialised with
217 * login_init_entry() or login_alloc_entry()
218 *
219 * Returns:
220 * >0 if successful
221 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
222 */
564dd50a 223int
fa64c868 224login_login(struct logininfo *li)
564dd50a 225{
226 li->type = LTYPE_LOGIN;
fa64c868 227 return (login_write(li));
564dd50a 228}
1d7b9b20 229
230
fa64c868 231/*
232 * login_logout(struct logininfo *) - Record a logout
5abcdf8e 233 *
234 * Call as with login_login()
235 *
236 * Returns:
237 * >0 if successful
238 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
239 */
564dd50a 240int
241login_logout(struct logininfo *li)
242{
243 li->type = LTYPE_LOGOUT;
fa64c868 244 return (login_write(li));
1d7b9b20 245}
246
fa64c868 247/*
248 * login_get_lastlog_time(int) - Retrieve the last login time
5abcdf8e 249 *
250 * Retrieve the last login time for the given uid. Will try to use the
251 * system lastlog facilities if they are available, but will fall back
252 * to looking in wtmp/wtmpx if necessary
253 *
254 * Returns:
255 * 0 on failure, or if user has never logged in
256 * Time in seconds from the epoch if successful
257 *
258 * Useful preprocessor symbols:
259 * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
260 * info
261 * USE_LASTLOG: If set, indicates the presence of system lastlog
262 * facilities. If this and DISABLE_LASTLOG are not set,
263 * try to retrieve lastlog information from wtmp/wtmpx.
264 */
564dd50a 265unsigned int
266login_get_lastlog_time(const int uid)
267{
268 struct logininfo li;
1d7b9b20 269
5abcdf8e 270 if (login_get_lastlog(&li, uid))
fa64c868 271 return (li.tv_sec);
5abcdf8e 272 else
fa64c868 273 return (0);
564dd50a 274}
1d7b9b20 275
fa64c868 276/*
277 * login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry
5abcdf8e 278 *
279 * Retrieve a logininfo structure populated (only partially) with
280 * information from the system lastlog data, or from wtmp/wtmpx if no
281 * system lastlog information exists.
282 *
283 * Note this routine must be given a pre-allocated logininfo.
284 *
285 * Returns:
286 * >0: A pointer to your struct logininfo if successful
287 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
5abcdf8e 288 */
564dd50a 289struct logininfo *
290login_get_lastlog(struct logininfo *li, const int uid)
291{
5abcdf8e 292 struct passwd *pw;
5abcdf8e 293
dbaa2e87 294 memset(li, '\0', sizeof(*li));
1d7b9b20 295 li->uid = uid;
5abcdf8e 296
2b87da3b 297 /*
9f32ceb4 298 * If we don't have a 'real' lastlog, we need the username to
5abcdf8e 299 * reliably search wtmp(x) for the last login (see
2b87da3b 300 * wtmp_get_entry().)
9f32ceb4 301 */
5abcdf8e 302 pw = getpwuid(uid);
a05a70ab 303 if (pw == NULL)
a233586b 304 fatal("%s: Cannot find account for uid %i", __func__, uid);
2b87da3b 305
1bfbb762 306 /* No MIN_SIZEOF here - we absolutely *must not* truncate the
fa64c868 307 * username (XXX - so check for trunc!) */
d8caae24 308 strlcpy(li->username, pw->pw_name, sizeof(li->username));
a05a70ab 309
564dd50a 310 if (getlast_entry(li))
fa64c868 311 return (li);
564dd50a 312 else
fa64c868 313 return (NULL);
1d7b9b20 314}
315
1d7b9b20 316
fa64c868 317/*
318 * login_alloc_entry(int, char*, char*, char*) - Allocate and initialise
2b87da3b 319 * a logininfo structure
320 *
5abcdf8e 321 * This function creates a new struct logininfo, a data structure
322 * meant to carry the information required to portably record login info.
323 *
324 * Returns a pointer to a newly created struct logininfo. If memory
325 * allocation fails, the program halts.
326 */
564dd50a 327struct
328logininfo *login_alloc_entry(int pid, const char *username,
fa64c868 329 const char *hostname, const char *line)
564dd50a 330{
331 struct logininfo *newli;
1d7b9b20 332
fa64c868 333 newli = xmalloc(sizeof(*newli));
334 login_init_entry(newli, pid, username, hostname, line);
335 return (newli);
1d7b9b20 336}
337
338
5abcdf8e 339/* login_free_entry(struct logininfo *) - free struct memory */
1d7b9b20 340void
564dd50a 341login_free_entry(struct logininfo *li)
342{
343 xfree(li);
1d7b9b20 344}
345
346
5abcdf8e 347/* login_init_entry(struct logininfo *, int, char*, char*, char*)
348 * - initialise a struct logininfo
2b87da3b 349 *
5abcdf8e 350 * Populates a new struct logininfo, a data structure meant to carry
351 * the information required to portably record login info.
352 *
353 * Returns: 1
354 */
564dd50a 355int
2b87da3b 356login_init_entry(struct logininfo *li, int pid, const char *username,
fa64c868 357 const char *hostname, const char *line)
564dd50a 358{
d8caae24 359 struct passwd *pw;
2b87da3b 360
dbaa2e87 361 memset(li, 0, sizeof(*li));
2b87da3b 362
564dd50a 363 li->pid = pid;
d8caae24 364
564dd50a 365 /* set the line information */
366 if (line)
367 line_fullname(li->line, line, sizeof(li->line));
1d7b9b20 368
d8caae24 369 if (username) {
564dd50a 370 strlcpy(li->username, username, sizeof(li->username));
d8caae24 371 pw = getpwnam(li->username);
fa64c868 372 if (pw == NULL) {
d1cf9a87 373 fatal("%s: Cannot find user \"%s\"", __func__,
fa64c868 374 li->username);
375 }
d8caae24 376 li->uid = pw->pw_uid;
377 }
a05a70ab 378
564dd50a 379 if (hostname)
380 strlcpy(li->hostname, hostname, sizeof(li->hostname));
d8caae24 381
fa64c868 382 return (1);
1d7b9b20 383}
384
d1cf9a87 385/*
fa64c868 386 * login_set_current_time(struct logininfo *) - set the current time
5abcdf8e 387 *
388 * Set the current time in a logininfo structure. This function is
389 * meant to eliminate the need to deal with system dependencies for
390 * time handling.
391 */
1d7b9b20 392void
564dd50a 393login_set_current_time(struct logininfo *li)
394{
1d7b9b20 395 struct timeval tv;
396
397 gettimeofday(&tv, NULL);
2b87da3b 398
d8caae24 399 li->tv_sec = tv.tv_sec;
400 li->tv_usec = tv.tv_usec;
1d7b9b20 401}
402
564dd50a 403/* copy a sockaddr_* into our logininfo */
1d7b9b20 404void
564dd50a 405login_set_addr(struct logininfo *li, const struct sockaddr *sa,
fa64c868 406 const unsigned int sa_size)
564dd50a 407{
408 unsigned int bufsize = sa_size;
409
410 /* make sure we don't overrun our union */
411 if (sizeof(li->hostaddr) < sa_size)
412 bufsize = sizeof(li->hostaddr);
413
fa64c868 414 memcpy(&li->hostaddr.sa, sa, bufsize);
1d7b9b20 415}
1d7b9b20 416
564dd50a 417
418/**
419 ** login_write: Call low-level recording functions based on autoconf
420 ** results
421 **/
1d7b9b20 422int
fa64c868 423login_write(struct logininfo *li)
564dd50a 424{
3c62e7eb 425#ifndef HAVE_CYGWIN
fa64c868 426 if (geteuid() != 0) {
427 logit("Attempt to write login records by non-root user (aborting)");
428 return (1);
1d7b9b20 429 }
3c62e7eb 430#endif
a05a70ab 431
1d7b9b20 432 /* set the timestamp */
433 login_set_current_time(li);
434#ifdef USE_LOGIN
435 syslogin_write_entry(li);
436#endif
437#ifdef USE_LASTLOG
fa64c868 438 if (li->type == LTYPE_LOGIN)
1d7b9b20 439 lastlog_write_entry(li);
1d7b9b20 440#endif
441#ifdef USE_UTMP
442 utmp_write_entry(li);
443#endif
444#ifdef USE_WTMP
445 wtmp_write_entry(li);
446#endif
447#ifdef USE_UTMPX
448 utmpx_write_entry(li);
449#endif
450#ifdef USE_WTMPX
451 wtmpx_write_entry(li);
f5ed3301 452#endif
453#ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN
d1cf9a87 454 if (li->type == LTYPE_LOGIN &&
98c044d0 455 !sys_auth_record_login(li->username,li->hostname,li->line,
456 &loginmsg))
f5ed3301 457 logit("Writing login record failed for %s", li->username);
c00e4d75 458#endif
6039eeef 459#ifdef SSH_AUDIT_EVENTS
c00e4d75 460 if (li->type == LTYPE_LOGIN)
461 audit_session_open(li->line);
462 else if (li->type == LTYPE_LOGOUT)
463 audit_session_close(li->line);
1d7b9b20 464#endif
fa64c868 465 return (0);
1d7b9b20 466}
467
7e2d5fa4 468#ifdef LOGIN_NEEDS_UTMPX
469int
470login_utmp_only(struct logininfo *li)
471{
aff51935 472 li->type = LTYPE_LOGIN;
9e127e27 473 login_set_current_time(li);
7e2d5fa4 474# ifdef USE_UTMP
475 utmp_write_entry(li);
476# endif
477# ifdef USE_WTMP
478 wtmp_write_entry(li);
479# endif
480# ifdef USE_UTMPX
481 utmpx_write_entry(li);
482# endif
483# ifdef USE_WTMPX
484 wtmpx_write_entry(li);
485# endif
fa64c868 486 return (0);
7e2d5fa4 487}
488#endif
489
564dd50a 490/**
491 ** getlast_entry: Call low-level functions to retrieve the last login
492 ** time.
493 **/
1d7b9b20 494
564dd50a 495/* take the uid in li and return the last login time */
1d7b9b20 496int
564dd50a 497getlast_entry(struct logininfo *li)
498{
499#ifdef USE_LASTLOG
9f32ceb4 500 return(lastlog_get_entry(li));
a05a70ab 501#else /* !USE_LASTLOG */
1d7b9b20 502
fa64c868 503#if defined(DISABLE_LASTLOG)
2b87da3b 504 /* On some systems we shouldn't even try to obtain last login
3f45f1c3 505 * time, e.g. AIX */
fa64c868 506 return (0);
507# elif defined(USE_WTMP) && \
508 (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
564dd50a 509 /* retrieve last login time from utmp */
a05a70ab 510 return (wtmp_get_entry(li));
fa64c868 511# elif defined(USE_WTMPX) && \
512 (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
564dd50a 513 /* If wtmp isn't available, try wtmpx */
a05a70ab 514 return (wtmpx_get_entry(li));
fa64c868 515# else
564dd50a 516 /* Give up: No means of retrieving last login time */
fa64c868 517 return (0);
2b87da3b 518# endif /* DISABLE_LASTLOG */
a05a70ab 519#endif /* USE_LASTLOG */
564dd50a 520}
1d7b9b20 521
522
1d7b9b20 523
524/*
564dd50a 525 * 'line' string utility functions
526 *
527 * These functions process the 'line' string into one of three forms:
528 *
1d7b9b20 529 * 1. The full filename (including '/dev')
530 * 2. The stripped name (excluding '/dev')
564dd50a 531 * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
532 * /dev/pts/1 -> ts/1 )
1d7b9b20 533 *
534 * Form 3 is used on some systems to identify a .tmp.? entry when
535 * attempting to remove it. Typically both addition and removal is
564dd50a 536 * performed by one application - say, sshd - so as long as the choice
537 * uniquely identifies a terminal it's ok.
1d7b9b20 538 */
539
540
fa64c868 541/*
542 * line_fullname(): add the leading '/dev/' if it doesn't exist make
543 * sure dst has enough space, if not just copy src (ugh)
544 */
1d7b9b20 545char *
44d71ad5 546line_fullname(char *dst, const char *src, u_int dstsize)
564dd50a 547{
1d7b9b20 548 memset(dst, '\0', dstsize);
fa64c868 549 if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))
1d7b9b20 550 strlcpy(dst, src, dstsize);
fa64c868 551 else {
a4d05724 552 strlcpy(dst, "/dev/", dstsize);
1d7b9b20 553 strlcat(dst, src, dstsize);
554 }
fa64c868 555 return (dst);
1d7b9b20 556}
557
564dd50a 558/* line_stripname(): strip the leading '/dev' if it exists, return dst */
1d7b9b20 559char *
564dd50a 560line_stripname(char *dst, const char *src, int dstsize)
561{
1d7b9b20 562 memset(dst, '\0', dstsize);
563 if (strncmp(src, "/dev/", 5) == 0)
89d7510a 564 strlcpy(dst, src + 5, dstsize);
1d7b9b20 565 else
566 strlcpy(dst, src, dstsize);
fa64c868 567 return (dst);
564dd50a 568}
569
d1cf9a87 570/*
fa64c868 571 * line_abbrevname(): Return the abbreviated (usually four-character)
564dd50a 572 * form of the line (Just use the last <dstsize> characters of the
573 * full name.)
574 *
575 * NOTE: use strncpy because we do NOT necessarily want zero
fa64c868 576 * termination
577 */
1d7b9b20 578char *
2b87da3b 579line_abbrevname(char *dst, const char *src, int dstsize)
a05a70ab 580{
581 size_t len;
2b87da3b 582
1d7b9b20 583 memset(dst, '\0', dstsize);
2b87da3b 584
daaff4d5 585 /* Always skip prefix if present */
586 if (strncmp(src, "/dev/", 5) == 0)
587 src += 5;
2b87da3b 588
0e8f4eba 589#ifdef WITH_ABBREV_NO_TTY
590 if (strncmp(src, "tty", 3) == 0)
591 src += 3;
592#endif
593
a05a70ab 594 len = strlen(src);
595
daaff4d5 596 if (len > 0) {
597 if (((int)len - dstsize) > 0)
598 src += ((int)len - dstsize);
599
600 /* note: _don't_ change this to strlcpy */
2b87da3b 601 strncpy(dst, src, (size_t)dstsize);
a05a70ab 602 }
2b87da3b 603
fa64c868 604 return (dst);
1d7b9b20 605}
606
1d7b9b20 607/**
608 ** utmp utility functions
564dd50a 609 **
610 ** These functions manipulate struct utmp, taking system differences
611 ** into account.
1d7b9b20 612 **/
613
614#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
615
1d7b9b20 616/* build the utmp structure */
617void
564dd50a 618set_utmp_time(struct logininfo *li, struct utmp *ut)
619{
fa64c868 620# if defined(HAVE_TV_IN_UTMP)
1d7b9b20 621 ut->ut_tv.tv_sec = li->tv_sec;
622 ut->ut_tv.tv_usec = li->tv_usec;
fa64c868 623# elif defined(HAVE_TIME_IN_UTMP)
1d7b9b20 624 ut->ut_time = li->tv_sec;
a05a70ab 625# endif
1d7b9b20 626}
627
628void
629construct_utmp(struct logininfo *li,
564dd50a 630 struct utmp *ut)
631{
9746ee4b 632# ifdef HAVE_ADDR_V6_IN_UTMP
633 struct sockaddr_in6 *sa6;
fa64c868 634# endif
635
dbaa2e87 636 memset(ut, '\0', sizeof(*ut));
5abcdf8e 637
638 /* First fill out fields used for both logins and logouts */
639
a05a70ab 640# ifdef HAVE_ID_IN_UTMP
1d7b9b20 641 line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
a05a70ab 642# endif
1d7b9b20 643
a05a70ab 644# ifdef HAVE_TYPE_IN_UTMP
5abcdf8e 645 /* This is done here to keep utmp constants out of struct logininfo */
1d7b9b20 646 switch (li->type) {
647 case LTYPE_LOGIN:
648 ut->ut_type = USER_PROCESS;
ef51930f 649#ifdef _UNICOS
1a23ac2c 650 cray_set_tmpdir(ut);
651#endif
1d7b9b20 652 break;
653 case LTYPE_LOGOUT:
654 ut->ut_type = DEAD_PROCESS;
ef51930f 655#ifdef _UNICOS
1a23ac2c 656 cray_retain_utmp(ut, li->pid);
657#endif
1d7b9b20 658 break;
659 }
a05a70ab 660# endif
5abcdf8e 661 set_utmp_time(li, ut);
1d7b9b20 662
5abcdf8e 663 line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
a05a70ab 664
665# ifdef HAVE_PID_IN_UTMP
1d7b9b20 666 ut->ut_pid = li->pid;
a05a70ab 667# endif
5abcdf8e 668
669 /* If we're logging out, leave all other fields blank */
670 if (li->type == LTYPE_LOGOUT)
fa64c868 671 return;
5abcdf8e 672
a05a70ab 673 /*
674 * These fields are only used when logging in, and are blank
2b87da3b 675 * for logouts.
a05a70ab 676 */
5abcdf8e 677
678 /* Use strncpy because we don't necessarily want null termination */
fa64c868 679 strncpy(ut->ut_name, li->username,
680 MIN_SIZEOF(ut->ut_name, li->username));
a05a70ab 681# ifdef HAVE_HOST_IN_UTMP
fa64c868 682 strncpy(ut->ut_host, li->hostname,
683 MIN_SIZEOF(ut->ut_host, li->hostname));
a05a70ab 684# endif
685# ifdef HAVE_ADDR_IN_UTMP
564dd50a 686 /* this is just a 32-bit IP address */
687 if (li->hostaddr.sa.sa_family == AF_INET)
688 ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
2b87da3b 689# endif
9746ee4b 690# ifdef HAVE_ADDR_V6_IN_UTMP
691 /* this is just a 128-bit IPv6 address */
692 if (li->hostaddr.sa.sa_family == AF_INET6) {
693 sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
694 memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
695 if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
696 ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
697 ut->ut_addr_v6[1] = 0;
698 ut->ut_addr_v6[2] = 0;
699 ut->ut_addr_v6[3] = 0;
700 }
701 }
702# endif
564dd50a 703}
a05a70ab 704#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
564dd50a 705
1d7b9b20 706/**
707 ** utmpx utility functions
564dd50a 708 **
709 ** These functions manipulate struct utmpx, accounting for system
710 ** variations.
1d7b9b20 711 **/
712
713#if defined(USE_UTMPX) || defined (USE_WTMPX)
1d7b9b20 714/* build the utmpx structure */
715void
564dd50a 716set_utmpx_time(struct logininfo *li, struct utmpx *utx)
717{
fa64c868 718# if defined(HAVE_TV_IN_UTMPX)
1d7b9b20 719 utx->ut_tv.tv_sec = li->tv_sec;
720 utx->ut_tv.tv_usec = li->tv_usec;
fa64c868 721# elif defined(HAVE_TIME_IN_UTMPX)
1d7b9b20 722 utx->ut_time = li->tv_sec;
fa64c868 723# endif
1d7b9b20 724}
725
726void
564dd50a 727construct_utmpx(struct logininfo *li, struct utmpx *utx)
728{
9746ee4b 729# ifdef HAVE_ADDR_V6_IN_UTMP
730 struct sockaddr_in6 *sa6;
731# endif
dbaa2e87 732 memset(utx, '\0', sizeof(*utx));
fa64c868 733
daaff4d5 734# ifdef HAVE_ID_IN_UTMPX
1d7b9b20 735 line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
daaff4d5 736# endif
1d7b9b20 737
738 /* this is done here to keep utmp constants out of loginrec.h */
739 switch (li->type) {
740 case LTYPE_LOGIN:
741 utx->ut_type = USER_PROCESS;
742 break;
743 case LTYPE_LOGOUT:
744 utx->ut_type = DEAD_PROCESS;
745 break;
746 }
1d7b9b20 747 line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
1d7b9b20 748 set_utmpx_time(li, utx);
5abcdf8e 749 utx->ut_pid = li->pid;
fa64c868 750
f3837bc6 751 /* strncpy(): Don't necessarily want null termination */
fa64c868 752 strncpy(utx->ut_name, li->username,
753 MIN_SIZEOF(utx->ut_name, li->username));
5abcdf8e 754
755 if (li->type == LTYPE_LOGOUT)
756 return;
757
a05a70ab 758 /*
759 * These fields are only used when logging in, and are blank
2b87da3b 760 * for logouts.
a05a70ab 761 */
5abcdf8e 762
a05a70ab 763# ifdef HAVE_HOST_IN_UTMPX
fa64c868 764 strncpy(utx->ut_host, li->hostname,
765 MIN_SIZEOF(utx->ut_host, li->hostname));
a05a70ab 766# endif
767# ifdef HAVE_ADDR_IN_UTMPX
764d4113 768 /* this is just a 32-bit IP address */
769 if (li->hostaddr.sa.sa_family == AF_INET)
770 utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
a05a70ab 771# endif
9746ee4b 772# ifdef HAVE_ADDR_V6_IN_UTMP
773 /* this is just a 128-bit IPv6 address */
774 if (li->hostaddr.sa.sa_family == AF_INET6) {
775 sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
776 memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
777 if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
778 ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
779 ut->ut_addr_v6[1] = 0;
780 ut->ut_addr_v6[2] = 0;
781 ut->ut_addr_v6[3] = 0;
782 }
783 }
784# endif
a05a70ab 785# ifdef HAVE_SYSLEN_IN_UTMPX
5abcdf8e 786 /* ut_syslen is the length of the utx_host string */
787 utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
a05a70ab 788# endif
564dd50a 789}
a05a70ab 790#endif /* USE_UTMPX || USE_WTMPX */
1d7b9b20 791
792/**
564dd50a 793 ** Low-level utmp functions
1d7b9b20 794 **/
795
796/* FIXME: (ATL) utmp_write_direct needs testing */
1d7b9b20 797#ifdef USE_UTMP
798
1d7b9b20 799/* if we can, use pututline() etc. */
a05a70ab 800# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
801 defined(HAVE_PUTUTLINE)
1d7b9b20 802# define UTMP_USE_LIBRARY
a05a70ab 803# endif
1d7b9b20 804
805
806/* write a utmp entry with the system's help (pututline() and pals) */
a05a70ab 807# ifdef UTMP_USE_LIBRARY
1d7b9b20 808static int
564dd50a 809utmp_write_library(struct logininfo *li, struct utmp *ut)
810{
1d7b9b20 811 setutent();
812 pututline(ut);
a05a70ab 813# ifdef HAVE_ENDUTENT
1d7b9b20 814 endutent();
a05a70ab 815# endif
fa64c868 816 return (1);
564dd50a 817}
a05a70ab 818# else /* UTMP_USE_LIBRARY */
1d7b9b20 819
d1cf9a87 820/*
fa64c868 821 * Write a utmp entry direct to the file
822 * This is a slightly modification of code in OpenBSD's login.c
823 */
1d7b9b20 824static int
564dd50a 825utmp_write_direct(struct logininfo *li, struct utmp *ut)
826{
1d7b9b20 827 struct utmp old_ut;
828 register int fd;
829 int tty;
830
5abcdf8e 831 /* FIXME: (ATL) ttyslot() needs local implementation */
dbaa2e87 832
698d107e 833#if defined(HAVE_GETTTYENT)
fa64c868 834 struct ttyent *ty;
dbaa2e87 835
836 tty=0;
dbaa2e87 837 setttyent();
fa64c868 838 while (NULL != (ty = getttyent())) {
dbaa2e87 839 tty++;
840 if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
841 break;
842 }
843 endttyent();
844
fa64c868 845 if (NULL == ty) {
5f12e050 846 logit("%s: tty not found", __func__);
847 return (0);
dbaa2e87 848 }
849#else /* FIXME */
850
1d7b9b20 851 tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
852
698d107e 853#endif /* HAVE_GETTTYENT */
dbaa2e87 854
1d7b9b20 855 if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
5f12e050 856 off_t pos, ret;
857
858 pos = (off_t)tty * sizeof(struct utmp);
859 if ((ret = lseek(fd, pos, SEEK_SET)) == -1) {
f6d20d59 860 logit("%s: lseek: %s", __func__, strerror(errno));
5f12e050 861 return (0);
862 }
863 if (ret != pos) {
d1cf9a87 864 logit("%s: Couldn't seek to tty %d slot in %s",
f6d20d59 865 __func__, tty, UTMP_FILE);
5f12e050 866 return (0);
867 }
1d7b9b20 868 /*
869 * Prevent luser from zero'ing out ut_host.
870 * If the new ut_line is empty but the old one is not
dc2a6d09 871 * and ut_line and ut_name match, preserve the old ut_line.
1d7b9b20 872 */
2b87da3b 873 if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
fa64c868 874 (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
875 (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
876 (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0))
877 memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
2b87da3b 878
5f12e050 879 if ((ret = lseek(fd, pos, SEEK_SET)) == -1) {
f6d20d59 880 logit("%s: lseek: %s", __func__, strerror(errno));
5f12e050 881 return (0);
882 }
883 if (ret != pos) {
f6d20d59 884 logit("%s: Couldn't seek to tty %d slot in %s",
5f12e050 885 __func__, tty, UTMP_FILE);
886 return (0);
887 }
fa64c868 888 if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
5f12e050 889 logit("%s: error writing %s: %s", __func__,
5abcdf8e 890 UTMP_FILE, strerror(errno));
fa64c868 891 }
2b87da3b 892
fa64c868 893 close(fd);
894 return (1);
9f32ceb4 895 } else {
fa64c868 896 return (0);
9f32ceb4 897 }
564dd50a 898}
a05a70ab 899# endif /* UTMP_USE_LIBRARY */
1d7b9b20 900
901static int
564dd50a 902utmp_perform_login(struct logininfo *li)
903{
1d7b9b20 904 struct utmp ut;
905
906 construct_utmp(li, &ut);
a05a70ab 907# ifdef UTMP_USE_LIBRARY
1d7b9b20 908 if (!utmp_write_library(li, &ut)) {
a233586b 909 logit("%s: utmp_write_library() failed", __func__);
fa64c868 910 return (0);
1d7b9b20 911 }
a05a70ab 912# else
1d7b9b20 913 if (!utmp_write_direct(li, &ut)) {
a233586b 914 logit("%s: utmp_write_direct() failed", __func__);
fa64c868 915 return (0);
1d7b9b20 916 }
a05a70ab 917# endif
fa64c868 918 return (1);
564dd50a 919}
1d7b9b20 920
921
922static int
564dd50a 923utmp_perform_logout(struct logininfo *li)
924{
1d7b9b20 925 struct utmp ut;
926
5abcdf8e 927 construct_utmp(li, &ut);
a05a70ab 928# ifdef UTMP_USE_LIBRARY
5abcdf8e 929 if (!utmp_write_library(li, &ut)) {
a233586b 930 logit("%s: utmp_write_library() failed", __func__);
fa64c868 931 return (0);
5abcdf8e 932 }
a05a70ab 933# else
5abcdf8e 934 if (!utmp_write_direct(li, &ut)) {
a233586b 935 logit("%s: utmp_write_direct() failed", __func__);
fa64c868 936 return (0);
5abcdf8e 937 }
a05a70ab 938# endif
fa64c868 939 return (1);
564dd50a 940}
1d7b9b20 941
942
943int
564dd50a 944utmp_write_entry(struct logininfo *li)
945{
1d7b9b20 946 switch(li->type) {
947 case LTYPE_LOGIN:
fa64c868 948 return (utmp_perform_login(li));
1d7b9b20 949
950 case LTYPE_LOGOUT:
fa64c868 951 return (utmp_perform_logout(li));
1d7b9b20 952
953 default:
a233586b 954 logit("%s: invalid type field", __func__);
fa64c868 955 return (0);
1d7b9b20 956 }
564dd50a 957}
a05a70ab 958#endif /* USE_UTMP */
1d7b9b20 959
960
961/**
564dd50a 962 ** Low-level utmpx functions
1d7b9b20 963 **/
964
965/* not much point if we don't want utmpx entries */
966#ifdef USE_UTMPX
967
1d7b9b20 968/* if we have the wherewithall, use pututxline etc. */
a05a70ab 969# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
970 defined(HAVE_PUTUTXLINE)
1d7b9b20 971# define UTMPX_USE_LIBRARY
a05a70ab 972# endif
1d7b9b20 973
974
975/* write a utmpx entry with the system's help (pututxline() and pals) */
a05a70ab 976# ifdef UTMPX_USE_LIBRARY
1d7b9b20 977static int
564dd50a 978utmpx_write_library(struct logininfo *li, struct utmpx *utx)
979{
1d7b9b20 980 setutxent();
981 pututxline(utx);
982
a05a70ab 983# ifdef HAVE_ENDUTXENT
1d7b9b20 984 endutxent();
a05a70ab 985# endif
fa64c868 986 return (1);
564dd50a 987}
1d7b9b20 988
a05a70ab 989# else /* UTMPX_USE_LIBRARY */
1d7b9b20 990
991/* write a utmp entry direct to the file */
992static int
564dd50a 993utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
2b87da3b 994{
a233586b 995 logit("%s: not implemented!", __func__);
fa64c868 996 return (0);
564dd50a 997}
a05a70ab 998# endif /* UTMPX_USE_LIBRARY */
1d7b9b20 999
1000static int
564dd50a 1001utmpx_perform_login(struct logininfo *li)
1002{
1d7b9b20 1003 struct utmpx utx;
1004
1005 construct_utmpx(li, &utx);
a05a70ab 1006# ifdef UTMPX_USE_LIBRARY
1d7b9b20 1007 if (!utmpx_write_library(li, &utx)) {
a233586b 1008 logit("%s: utmp_write_library() failed", __func__);
fa64c868 1009 return (0);
1d7b9b20 1010 }
a05a70ab 1011# else
1d7b9b20 1012 if (!utmpx_write_direct(li, &ut)) {
a233586b 1013 logit("%s: utmp_write_direct() failed", __func__);
fa64c868 1014 return (0);
1d7b9b20 1015 }
a05a70ab 1016# endif
fa64c868 1017 return (1);
564dd50a 1018}
1d7b9b20 1019
1020
1021static int
564dd50a 1022utmpx_perform_logout(struct logininfo *li)
1023{
1d7b9b20 1024 struct utmpx utx;
1025
f3837bc6 1026 construct_utmpx(li, &utx);
a05a70ab 1027# ifdef HAVE_ID_IN_UTMPX
1d7b9b20 1028 line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
a05a70ab 1029# endif
1030# ifdef HAVE_TYPE_IN_UTMPX
1d7b9b20 1031 utx.ut_type = DEAD_PROCESS;
a05a70ab 1032# endif
1d7b9b20 1033
a05a70ab 1034# ifdef UTMPX_USE_LIBRARY
1d7b9b20 1035 utmpx_write_library(li, &utx);
a05a70ab 1036# else
1d7b9b20 1037 utmpx_write_direct(li, &utx);
a05a70ab 1038# endif
fa64c868 1039 return (1);
564dd50a 1040}
1d7b9b20 1041
1d7b9b20 1042int
564dd50a 1043utmpx_write_entry(struct logininfo *li)
1044{
1d7b9b20 1045 switch(li->type) {
1046 case LTYPE_LOGIN:
fa64c868 1047 return (utmpx_perform_login(li));
1d7b9b20 1048 case LTYPE_LOGOUT:
fa64c868 1049 return (utmpx_perform_logout(li));
1d7b9b20 1050 default:
a233586b 1051 logit("%s: invalid type field", __func__);
fa64c868 1052 return (0);
1d7b9b20 1053 }
564dd50a 1054}
a05a70ab 1055#endif /* USE_UTMPX */
1d7b9b20 1056
1057
1058/**
564dd50a 1059 ** Low-level wtmp functions
1d7b9b20 1060 **/
1061
2b87da3b 1062#ifdef USE_WTMP
1d7b9b20 1063
d1cf9a87 1064/*
fa64c868 1065 * Write a wtmp entry direct to the end of the file
1066 * This is a slight modification of code in OpenBSD's logwtmp.c
1067 */
1d7b9b20 1068static int
564dd50a 1069wtmp_write(struct logininfo *li, struct utmp *ut)
1070{
1d7b9b20 1071 struct stat buf;
1072 int fd, ret = 1;
1073
1074 if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
a233586b 1075 logit("%s: problem writing %s: %s", __func__,
1d7b9b20 1076 WTMP_FILE, strerror(errno));
fa64c868 1077 return (0);
1d7b9b20 1078 }
2b87da3b 1079 if (fstat(fd, &buf) == 0)
d72f7b79 1080 if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
1d7b9b20 1081 ftruncate(fd, buf.st_size);
a233586b 1082 logit("%s: problem writing %s: %s", __func__,
1d7b9b20 1083 WTMP_FILE, strerror(errno));
1084 ret = 0;
1085 }
fa64c868 1086 close(fd);
1087 return (ret);
564dd50a 1088}
1d7b9b20 1089
1d7b9b20 1090static int
a05a70ab 1091wtmp_perform_login(struct logininfo *li)
1092{
1d7b9b20 1093 struct utmp ut;
1094
1095 construct_utmp(li, &ut);
fa64c868 1096 return (wtmp_write(li, &ut));
564dd50a 1097}
1d7b9b20 1098
1099
1100static int
564dd50a 1101wtmp_perform_logout(struct logininfo *li)
1102{
1d7b9b20 1103 struct utmp ut;
1104
1105 construct_utmp(li, &ut);
fa64c868 1106 return (wtmp_write(li, &ut));
564dd50a 1107}
1d7b9b20 1108
1109
1110int
564dd50a 1111wtmp_write_entry(struct logininfo *li)
1112{
1d7b9b20 1113 switch(li->type) {
1114 case LTYPE_LOGIN:
fa64c868 1115 return (wtmp_perform_login(li));
1d7b9b20 1116 case LTYPE_LOGOUT:
fa64c868 1117 return (wtmp_perform_logout(li));
1d7b9b20 1118 default:
a233586b 1119 logit("%s: invalid type field", __func__);
fa64c868 1120 return (0);
1d7b9b20 1121 }
564dd50a 1122}
1d7b9b20 1123
1124
d1cf9a87 1125/*
fa64c868 1126 * Notes on fetching login data from wtmp/wtmpx
2b87da3b 1127 *
5abcdf8e 1128 * Logouts are usually recorded with (amongst other things) a blank
1129 * username on a given tty line. However, some systems (HP-UX is one)
1130 * leave all fields set, but change the ut_type field to DEAD_PROCESS.
1131 *
1132 * Since we're only looking for logins here, we know that the username
1133 * must be set correctly. On systems that leave it in, we check for
1134 * ut_type==USER_PROCESS (indicating a login.)
1135 *
1136 * Portability: Some systems may set something other than USER_PROCESS
1137 * to indicate a login process. I don't know of any as I write. Also,
1138 * it's possible that some systems may both leave the username in
1139 * place and not have ut_type.
1140 */
1141
5abcdf8e 1142/* return true if this wtmp entry indicates a login */
1143static int
1144wtmp_islogin(struct logininfo *li, struct utmp *ut)
1145{
2b87da3b 1146 if (strncmp(li->username, ut->ut_name,
fa64c868 1147 MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
a05a70ab 1148# ifdef HAVE_TYPE_IN_UTMP
5abcdf8e 1149 if (ut->ut_type & USER_PROCESS)
fa64c868 1150 return (1);
a05a70ab 1151# else
fa64c868 1152 return (1);
a05a70ab 1153# endif
5abcdf8e 1154 }
fa64c868 1155 return (0);
5abcdf8e 1156}
1157
1d7b9b20 1158int
564dd50a 1159wtmp_get_entry(struct logininfo *li)
1160{
1d7b9b20 1161 struct stat st;
1162 struct utmp ut;
fa64c868 1163 int fd, found = 0;
5abcdf8e 1164
1165 /* Clear the time entries in our logininfo */
1166 li->tv_sec = li->tv_usec = 0;
1d7b9b20 1167
1168 if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
d1cf9a87 1169 logit("%s: problem opening %s: %s", __func__,
1d7b9b20 1170 WTMP_FILE, strerror(errno));
fa64c868 1171 return (0);
1d7b9b20 1172 }
2b87da3b 1173 if (fstat(fd, &st) != 0) {
d1cf9a87 1174 logit("%s: couldn't stat %s: %s", __func__,
1d7b9b20 1175 WTMP_FILE, strerror(errno));
1176 close(fd);
fa64c868 1177 return (0);
1d7b9b20 1178 }
1d7b9b20 1179
5abcdf8e 1180 /* Seek to the start of the last struct utmp */
d9d47a26 1181 if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
5abcdf8e 1182 /* Looks like we've got a fresh wtmp file */
1183 close(fd);
fa64c868 1184 return (0);
5abcdf8e 1185 }
1186
1187 while (!found) {
9f32ceb4 1188 if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
d1cf9a87 1189 logit("%s: read of %s failed: %s", __func__,
1d7b9b20 1190 WTMP_FILE, strerror(errno));
1191 close (fd);
fa64c868 1192 return (0);
1d7b9b20 1193 }
5abcdf8e 1194 if ( wtmp_islogin(li, &ut) ) {
1195 found = 1;
fa64c868 1196 /*
1197 * We've already checked for a time in struct
1198 * utmp, in login_getlast()
1199 */
a05a70ab 1200# ifdef HAVE_TIME_IN_UTMP
1d7b9b20 1201 li->tv_sec = ut.ut_time;
a05a70ab 1202# else
1d7b9b20 1203# if HAVE_TV_IN_UTMP
1204 li->tv_sec = ut.ut_tv.tv_sec;
1205# endif
a05a70ab 1206# endif
5abcdf8e 1207 line_fullname(li->line, ut.ut_line,
fa64c868 1208 MIN_SIZEOF(li->line, ut.ut_line));
a05a70ab 1209# ifdef HAVE_HOST_IN_UTMP
5abcdf8e 1210 strlcpy(li->hostname, ut.ut_host,
fa64c868 1211 MIN_SIZEOF(li->hostname, ut.ut_host));
a05a70ab 1212# endif
5abcdf8e 1213 continue;
1d7b9b20 1214 }
5abcdf8e 1215 /* Seek back 2 x struct utmp */
d9d47a26 1216 if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
5abcdf8e 1217 /* We've found the start of the file, so quit */
fa64c868 1218 close(fd);
1219 return (0);
1d7b9b20 1220 }
5abcdf8e 1221 }
1222
1223 /* We found an entry. Tidy up and return */
1224 close(fd);
fa64c868 1225 return (1);
564dd50a 1226}
a05a70ab 1227# endif /* USE_WTMP */
1d7b9b20 1228
1229
1230/**
564dd50a 1231 ** Low-level wtmpx functions
1d7b9b20 1232 **/
1233
1234#ifdef USE_WTMPX
fa64c868 1235/*
1236 * Write a wtmpx entry direct to the end of the file
1237 * This is a slight modification of code in OpenBSD's logwtmp.c
1238 */
1d7b9b20 1239static int
564dd50a 1240wtmpx_write(struct logininfo *li, struct utmpx *utx)
1241{
41c64c91 1242#ifndef HAVE_UPDWTMPX
1d7b9b20 1243 struct stat buf;
1244 int fd, ret = 1;
1245
1246 if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
d1cf9a87 1247 logit("%s: problem opening %s: %s", __func__,
1d7b9b20 1248 WTMPX_FILE, strerror(errno));
fa64c868 1249 return (0);
1d7b9b20 1250 }
1251
2b87da3b 1252 if (fstat(fd, &buf) == 0)
d72f7b79 1253 if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
1d7b9b20 1254 ftruncate(fd, buf.st_size);
a233586b 1255 logit("%s: problem writing %s: %s", __func__,
1d7b9b20 1256 WTMPX_FILE, strerror(errno));
1257 ret = 0;
1258 }
fa64c868 1259 close(fd);
1d7b9b20 1260
fa64c868 1261 return (ret);
41c64c91 1262#else
1263 updwtmpx(WTMPX_FILE, utx);
fa64c868 1264 return (1);
41c64c91 1265#endif
564dd50a 1266}
1d7b9b20 1267
1268
1269static int
564dd50a 1270wtmpx_perform_login(struct logininfo *li)
1271{
1d7b9b20 1272 struct utmpx utx;
1273
1274 construct_utmpx(li, &utx);
fa64c868 1275 return (wtmpx_write(li, &utx));
564dd50a 1276}
1d7b9b20 1277
1278
1279static int
564dd50a 1280wtmpx_perform_logout(struct logininfo *li)
1281{
1d7b9b20 1282 struct utmpx utx;
1283
1284 construct_utmpx(li, &utx);
fa64c868 1285 return (wtmpx_write(li, &utx));
564dd50a 1286}
1d7b9b20 1287
1288
1289int
564dd50a 1290wtmpx_write_entry(struct logininfo *li)
1291{
1d7b9b20 1292 switch(li->type) {
1293 case LTYPE_LOGIN:
fa64c868 1294 return (wtmpx_perform_login(li));
1d7b9b20 1295 case LTYPE_LOGOUT:
fa64c868 1296 return (wtmpx_perform_logout(li));
1d7b9b20 1297 default:
a233586b 1298 logit("%s: invalid type field", __func__);
fa64c868 1299 return (0);
1d7b9b20 1300 }
564dd50a 1301}
1d7b9b20 1302
5abcdf8e 1303/* Please see the notes above wtmp_islogin() for information about the
1304 next two functions */
1305
1306/* Return true if this wtmpx entry indicates a login */
1307static int
1308wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1309{
fa64c868 1310 if (strncmp(li->username, utx->ut_name,
1311 MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
a05a70ab 1312# ifdef HAVE_TYPE_IN_UTMPX
5abcdf8e 1313 if (utx->ut_type == USER_PROCESS)
fa64c868 1314 return (1);
a05a70ab 1315# else
fa64c868 1316 return (1);
a05a70ab 1317# endif
5abcdf8e 1318 }
fa64c868 1319 return (0);
5abcdf8e 1320}
1321
1d7b9b20 1322
1323int
564dd50a 1324wtmpx_get_entry(struct logininfo *li)
1325{
1d7b9b20 1326 struct stat st;
1327 struct utmpx utx;
5abcdf8e 1328 int fd, found=0;
1329
1330 /* Clear the time entries */
1331 li->tv_sec = li->tv_usec = 0;
1d7b9b20 1332
1333 if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
d1cf9a87 1334 logit("%s: problem opening %s: %s", __func__,
1d7b9b20 1335 WTMPX_FILE, strerror(errno));
fa64c868 1336 return (0);
1d7b9b20 1337 }
2b87da3b 1338 if (fstat(fd, &st) != 0) {
d1cf9a87 1339 logit("%s: couldn't stat %s: %s", __func__,
fdebdd4f 1340 WTMPX_FILE, strerror(errno));
1d7b9b20 1341 close(fd);
fa64c868 1342 return (0);
1d7b9b20 1343 }
2b87da3b 1344
5abcdf8e 1345 /* Seek to the start of the last struct utmpx */
d9d47a26 1346 if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) {
5abcdf8e 1347 /* probably a newly rotated wtmpx file */
1348 close(fd);
fa64c868 1349 return (0);
5abcdf8e 1350 }
1d7b9b20 1351
5abcdf8e 1352 while (!found) {
9f32ceb4 1353 if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
d1cf9a87 1354 logit("%s: read of %s failed: %s", __func__,
1d7b9b20 1355 WTMPX_FILE, strerror(errno));
1356 close (fd);
fa64c868 1357 return (0);
1d7b9b20 1358 }
fa64c868 1359 /*
d1cf9a87 1360 * Logouts are recorded as a blank username on a particular
fa64c868 1361 * line. So, we just need to find the username in struct utmpx
1362 */
1363 if (wtmpx_islogin(li, &utx)) {
d80063fe 1364 found = 1;
fa64c868 1365# if defined(HAVE_TV_IN_UTMPX)
1d7b9b20 1366 li->tv_sec = utx.ut_tv.tv_sec;
fa64c868 1367# elif defined(HAVE_TIME_IN_UTMPX)
1d7b9b20 1368 li->tv_sec = utx.ut_time;
a05a70ab 1369# endif
a4d05724 1370 line_fullname(li->line, utx.ut_line, sizeof(li->line));
fa64c868 1371# if defined(HAVE_HOST_IN_UTMPX)
5abcdf8e 1372 strlcpy(li->hostname, utx.ut_host,
fa64c868 1373 MIN_SIZEOF(li->hostname, utx.ut_host));
a05a70ab 1374# endif
5abcdf8e 1375 continue;
1d7b9b20 1376 }
d9d47a26 1377 if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) {
fa64c868 1378 close(fd);
1379 return (0);
1d7b9b20 1380 }
5abcdf8e 1381 }
1382
1383 close(fd);
fa64c868 1384 return (1);
564dd50a 1385}
f988dce5 1386#endif /* USE_WTMPX */
1d7b9b20 1387
1d7b9b20 1388/**
564dd50a 1389 ** Low-level libutil login() functions
1d7b9b20 1390 **/
1391
1392#ifdef USE_LOGIN
1d7b9b20 1393static int
564dd50a 1394syslogin_perform_login(struct logininfo *li)
1395{
1d7b9b20 1396 struct utmp *ut;
1397
7a52470e 1398 ut = xmalloc(sizeof(*ut));
1d7b9b20 1399 construct_utmp(li, ut);
1400 login(ut);
6cef88bc 1401 free(ut);
1d7b9b20 1402
fa64c868 1403 return (1);
564dd50a 1404}
1d7b9b20 1405
564dd50a 1406static int
1407syslogin_perform_logout(struct logininfo *li)
1408{
a05a70ab 1409# ifdef HAVE_LOGOUT
f20d4564 1410 char line[UT_LINESIZE];
2b87da3b 1411
1d7b9b20 1412 (void)line_stripname(line, li->line, sizeof(line));
1413
fa64c868 1414 if (!logout(line))
a233586b 1415 logit("%s: logout() returned an error", __func__);
a05a70ab 1416# ifdef HAVE_LOGWTMP
fa64c868 1417 else
1d7b9b20 1418 logwtmp(line, "", "");
a05a70ab 1419# endif
5abcdf8e 1420 /* FIXME: (ATL - if the need arises) What to do if we have
1421 * login, but no logout? what if logout but no logwtmp? All
1422 * routines are in libutil so they should all be there,
1423 * but... */
a05a70ab 1424# endif
fa64c868 1425 return (1);
564dd50a 1426}
1d7b9b20 1427
1d7b9b20 1428int
564dd50a 1429syslogin_write_entry(struct logininfo *li)
1430{
1d7b9b20 1431 switch (li->type) {
1432 case LTYPE_LOGIN:
fa64c868 1433 return (syslogin_perform_login(li));
1d7b9b20 1434 case LTYPE_LOGOUT:
fa64c868 1435 return (syslogin_perform_logout(li));
1d7b9b20 1436 default:
a233586b 1437 logit("%s: Invalid type field", __func__);
fa64c868 1438 return (0);
1d7b9b20 1439 }
564dd50a 1440}
f988dce5 1441#endif /* USE_LOGIN */
1d7b9b20 1442
1443/* end of file log-syslogin.c */
1444
1d7b9b20 1445/**
564dd50a 1446 ** Low-level lastlog functions
1d7b9b20 1447 **/
1448
1449#ifdef USE_LASTLOG
a05a70ab 1450#define LL_FILE 1
1451#define LL_DIR 2
1452#define LL_OTHER 3
1d7b9b20 1453
1d7b9b20 1454static void
564dd50a 1455lastlog_construct(struct logininfo *li, struct lastlog *last)
1456{
1d7b9b20 1457 /* clear the structure */
dbaa2e87 1458 memset(last, '\0', sizeof(*last));
2b87da3b 1459
fa64c868 1460 line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
5abcdf8e 1461 strlcpy(last->ll_host, li->hostname,
1462 MIN_SIZEOF(last->ll_host, li->hostname));
1d7b9b20 1463 last->ll_time = li->tv_sec;
564dd50a 1464}
1d7b9b20 1465
1d7b9b20 1466static int
564dd50a 1467lastlog_filetype(char *filename)
1468{
1d7b9b20 1469 struct stat st;
1470
a05a70ab 1471 if (stat(LASTLOG_FILE, &st) != 0) {
a233586b 1472 logit("%s: Couldn't stat %s: %s", __func__,
fa64c868 1473 LASTLOG_FILE, strerror(errno));
1474 return (0);
1d7b9b20 1475 }
1d7b9b20 1476 if (S_ISDIR(st.st_mode))
fa64c868 1477 return (LL_DIR);
1d7b9b20 1478 else if (S_ISREG(st.st_mode))
fa64c868 1479 return (LL_FILE);
1d7b9b20 1480 else
fa64c868 1481 return (LL_OTHER);
564dd50a 1482}
1d7b9b20 1483
1484
1485/* open the file (using filemode) and seek to the login entry */
1486static int
564dd50a 1487lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1488{
1d7b9b20 1489 off_t offset;
1490 int type;
1491 char lastlog_file[1024];
1492
1493 type = lastlog_filetype(LASTLOG_FILE);
1494 switch (type) {
fa64c868 1495 case LL_FILE:
1496 strlcpy(lastlog_file, LASTLOG_FILE,
1497 sizeof(lastlog_file));
1498 break;
1499 case LL_DIR:
1500 snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1501 LASTLOG_FILE, li->username);
1502 break;
1503 default:
a233586b 1504 logit("%s: %.100s is not a file or directory!", __func__,
fa64c868 1505 LASTLOG_FILE);
1506 return (0);
a05a70ab 1507 }
1d7b9b20 1508
ee476051 1509 *fd = open(lastlog_file, filemode, 0600);
fa64c868 1510 if (*fd < 0) {
a233586b 1511 debug("%s: Couldn't open %s: %s", __func__,
1d7b9b20 1512 lastlog_file, strerror(errno));
fa64c868 1513 return (0);
1d7b9b20 1514 }
2b87da3b 1515
d93a7e5a 1516 if (type == LL_FILE) {
1517 /* find this uid's offset in the lastlog file */
d9d47a26 1518 offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
1d7b9b20 1519
fa64c868 1520 if (lseek(*fd, offset, SEEK_SET) != offset) {
a233586b 1521 logit("%s: %s->lseek(): %s", __func__,
1522 lastlog_file, strerror(errno));
fa64c868 1523 return (0);
d93a7e5a 1524 }
1d7b9b20 1525 }
2b87da3b 1526
fa64c868 1527 return (1);
564dd50a 1528}
1d7b9b20 1529
1530static int
564dd50a 1531lastlog_perform_login(struct logininfo *li)
1532{
1d7b9b20 1533 struct lastlog last;
1534 int fd;
1535
1536 /* create our struct lastlog */
1537 lastlog_construct(li, &last);
1538
ee476051 1539 if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
fa64c868 1540 return (0);
2b87da3b 1541
1d7b9b20 1542 /* write the entry */
d72f7b79 1543 if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) {
1a022229 1544 close(fd);
a233586b 1545 logit("%s: Error writing to %s: %s", __func__,
1a022229 1546 LASTLOG_FILE, strerror(errno));
fa64c868 1547 return (0);
a05a70ab 1548 }
1a022229 1549
1550 close(fd);
fa64c868 1551 return (1);
564dd50a 1552}
1d7b9b20 1553
1d7b9b20 1554int
564dd50a 1555lastlog_write_entry(struct logininfo *li)
1556{
1d7b9b20 1557 switch(li->type) {
1558 case LTYPE_LOGIN:
fa64c868 1559 return (lastlog_perform_login(li));
1d7b9b20 1560 default:
a233586b 1561 logit("%s: Invalid type field", __func__);
fa64c868 1562 return (0);
1d7b9b20 1563 }
564dd50a 1564}
1d7b9b20 1565
1d7b9b20 1566static void
564dd50a 1567lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1568{
1d7b9b20 1569 line_fullname(li->line, last->ll_line, sizeof(li->line));
2b87da3b 1570 strlcpy(li->hostname, last->ll_host,
fa64c868 1571 MIN_SIZEOF(li->hostname, last->ll_host));
1d7b9b20 1572 li->tv_sec = last->ll_time;
564dd50a 1573}
1d7b9b20 1574
1d7b9b20 1575int
564dd50a 1576lastlog_get_entry(struct logininfo *li)
1577{
1d7b9b20 1578 struct lastlog last;
ed05a983 1579 int fd, ret;
1d7b9b20 1580
ce49121d 1581 if (!lastlog_openseek(li, &fd, O_RDONLY))
ed05a983 1582 return (0);
ce49121d 1583
ed05a983 1584 ret = atomicio(read, fd, &last, sizeof(last));
ce49121d 1585 close(fd);
1586
ed05a983 1587 switch (ret) {
1588 case 0:
1589 memset(&last, '\0', sizeof(last));
1590 /* FALLTHRU */
1591 case sizeof(last):
1592 lastlog_populate_entry(li, &last);
1593 return (1);
1594 case -1:
aff51935 1595 error("%s: Error reading from %s: %s", __func__,
ed05a983 1596 LASTLOG_FILE, strerror(errno));
1597 return (0);
1598 default:
1599 error("%s: Error reading from %s: Expecting %d, got %d",
dfde7f6e 1600 __func__, LASTLOG_FILE, (int)sizeof(last), ret);
ed05a983 1601 return (0);
1602 }
ce49121d 1603
ed05a983 1604 /* NOTREACHED */
1605 return (0);
564dd50a 1606}
f988dce5 1607#endif /* USE_LASTLOG */
b6610e8f 1608
1609#ifdef USE_BTMP
1610 /*
1611 * Logs failed login attempts in _PATH_BTMP if that exists.
1612 * The most common login failure is to give password instead of username.
1613 * So the _PATH_BTMP file checked for the correct permission, so that
1614 * only root can read it.
1615 */
1616
1617void
1618record_failed_login(const char *username, const char *hostname,
1619 const char *ttyn)
1620{
1621 int fd;
1622 struct utmp ut;
1623 struct sockaddr_storage from;
dfde7f6e 1624 socklen_t fromlen = sizeof(from);
b6610e8f 1625 struct sockaddr_in *a4;
1626 struct sockaddr_in6 *a6;
1627 time_t t;
1628 struct stat fst;
1629
1630 if (geteuid() != 0)
1631 return;
1632 if ((fd = open(_PATH_BTMP, O_WRONLY | O_APPEND)) < 0) {
1633 debug("Unable to open the btmp file %s: %s", _PATH_BTMP,
1634 strerror(errno));
1635 return;
1636 }
1637 if (fstat(fd, &fst) < 0) {
1638 logit("%s: fstat of %s failed: %s", __func__, _PATH_BTMP,
1639 strerror(errno));
1640 goto out;
1641 }
1642 if((fst.st_mode & (S_IRWXG | S_IRWXO)) || (fst.st_uid != 0)){
1643 logit("Excess permission or bad ownership on file %s",
1644 _PATH_BTMP);
1645 goto out;
1646 }
1647
1648 memset(&ut, 0, sizeof(ut));
1649 /* strncpy because we don't necessarily want nul termination */
1650 strncpy(ut.ut_user, username, sizeof(ut.ut_user));
1651 strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line));
1652
1653 time(&t);
1654 ut.ut_time = t; /* ut_time is not always a time_t */
1655 ut.ut_type = LOGIN_PROCESS;
1656 ut.ut_pid = getpid();
1657
1658 /* strncpy because we don't necessarily want nul termination */
1659 strncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
1660
1661 if (packet_connection_is_on_socket() &&
1662 getpeername(packet_get_connection_in(),
1663 (struct sockaddr *)&from, &fromlen) == 0) {
1664 ipv64_normalise_mapped(&from, &fromlen);
1665 if (from.ss_family == AF_INET) {
1666 a4 = (struct sockaddr_in *)&from;
1667 memcpy(&ut.ut_addr, &(a4->sin_addr),
1668 MIN_SIZEOF(ut.ut_addr, a4->sin_addr));
1669 }
1670#ifdef HAVE_ADDR_V6_IN_UTMP
1671 if (from.ss_family == AF_INET6) {
1672 a6 = (struct sockaddr_in6 *)&from;
1673 memcpy(&ut.ut_addr_v6, &(a6->sin6_addr),
1674 MIN_SIZEOF(ut.ut_addr_v6, a6->sin6_addr));
1675 }
1676#endif
1677 }
1678
1679 if (atomicio(vwrite, fd, &ut, sizeof(ut)) != sizeof(ut))
1680 error("Failed to write to %s: %s", _PATH_BTMP,
1681 strerror(errno));
1682
1683out:
1684 close(fd);
1685}
1686#endif /* USE_BTMP */
This page took 0.532525 seconds and 5 git commands to generate.