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