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