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