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