]> andersk Git - openssh.git/blobdiff - loginrec.c
- djm@cvs.openbsd.org 2010/01/30 02:54:53
[openssh.git] / loginrec.c
index 0945caaf1475e28fafeb2544e75b1e3cda08cdef..bca95970711e57caa17ee764ff2e2068336a8a40 100644 (file)
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+/*
+ * The btmp logging code is derived from login.c from util-linux and is under
+ * the the following license:
+ *
+ * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+
 /**
  ** loginrec.c:  platform-independent login recording and lastlog retrieval
  **/
 
 #include "includes.h"
 
-#include "ssh.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+#include <pwd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
 #include "xmalloc.h"
+#include "key.h"
+#include "hostfile.h"
+#include "ssh.h"
 #include "loginrec.h"
 #include "log.h"
 #include "atomicio.h"
+#include "packet.h"
+#include "canohost.h"
+#include "auth.h"
+#include "buffer.h"
 
 #ifdef HAVE_UTIL_H
 # include <util.h>
 # include <libutil.h>
 #endif
 
-RCSID("$Id$");
-
 /**
  ** prototypes for helper functions in this file
  **/
@@ -168,6 +210,8 @@ int lastlog_get_entry(struct logininfo *li);
 int wtmp_get_entry(struct logininfo *li);
 int wtmpx_get_entry(struct logininfo *li);
 
+extern Buffer loginmsg;
+
 /* pick the shortest string */
 #define MIN_SIZEOF(s1,s2) (sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2))
 
@@ -335,7 +379,7 @@ login_init_entry(struct logininfo *li, int pid, const char *username,
                strlcpy(li->username, username, sizeof(li->username));
                pw = getpwnam(li->username);
                if (pw == NULL) {
-                       fatal("%s: Cannot find user \"%s\"", __func__, 
+                       fatal("%s: Cannot find user \"%s\"", __func__,
                            li->username);
                }
                li->uid = pw->pw_uid;
@@ -347,7 +391,7 @@ login_init_entry(struct logininfo *li, int pid, const char *username,
        return (1);
 }
 
-/* 
+/*
  * login_set_current_time(struct logininfo *)    - set the current time
  *
  * Set the current time in a logininfo structure. This function is
@@ -416,9 +460,16 @@ login_write(struct logininfo *li)
        wtmpx_write_entry(li);
 #endif
 #ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN
-       if (li->type == LTYPE_LOGIN && 
-          !sys_auth_record_login(li->username,li->hostname,li->line))
+       if (li->type == LTYPE_LOGIN &&
+           !sys_auth_record_login(li->username,li->hostname,li->line,
+           &loginmsg))
                logit("Writing login record failed for %s", li->username);
+#endif
+#ifdef SSH_AUDIT_EVENTS
+       if (li->type == LTYPE_LOGIN)
+               audit_session_open(li->line);
+       else if (li->type == LTYPE_LOGOUT)
+               audit_session_close(li->line);
 #endif
        return (0);
 }
@@ -501,7 +552,7 @@ getlast_entry(struct logininfo *li)
  * sure dst has enough space, if not just copy src (ugh)
  */
 char *
-line_fullname(char *dst, const char *src, int dstsize)
+line_fullname(char *dst, const char *src, u_int dstsize)
 {
        memset(dst, '\0', dstsize);
        if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))
@@ -525,7 +576,7 @@ line_stripname(char *dst, const char *src, int dstsize)
        return (dst);
 }
 
-/* 
+/*
  * line_abbrevname(): Return the abbreviated (usually four-character)
  * form of the line (Just use the last <dstsize> characters of the
  * full name.)
@@ -707,8 +758,8 @@ construct_utmpx(struct logininfo *li, struct utmpx *utx)
        utx->ut_pid = li->pid;
 
        /* strncpy(): Don't necessarily want null termination */
-       strncpy(utx->ut_name, li->username,
-           MIN_SIZEOF(utx->ut_name, li->username));
+       strncpy(utx->ut_user, li->username,
+           MIN_SIZEOF(utx->ut_user, li->username));
 
        if (li->type == LTYPE_LOGOUT)
                return;
@@ -775,7 +826,7 @@ utmp_write_library(struct logininfo *li, struct utmp *ut)
 }
 # else /* UTMP_USE_LIBRARY */
 
-/* 
+/*
  * Write a utmp entry direct to the file
  * This is a slightly modification of code in OpenBSD's login.c
  */
@@ -819,7 +870,7 @@ utmp_write_direct(struct logininfo *li, struct utmp *ut)
                        return (0);
                }
                if (ret != pos) {
-                       logit("%s: Couldn't seek to tty %d slot in %s", 
+                       logit("%s: Couldn't seek to tty %d slot in %s",
                            __func__, tty, UTMP_FILE);
                        return (0);
                }
@@ -1019,7 +1070,7 @@ utmpx_write_entry(struct logininfo *li)
 
 #ifdef USE_WTMP
 
-/* 
+/*
  * Write a wtmp entry direct to the end of the file
  * This is a slight modification of code in OpenBSD's logwtmp.c
  */
@@ -1080,7 +1131,7 @@ wtmp_write_entry(struct logininfo *li)
 }
 
 
-/* 
+/*
  * Notes on fetching login data from wtmp/wtmpx
  *
  * Logouts are usually recorded with (amongst other things) a blank
@@ -1124,12 +1175,12 @@ wtmp_get_entry(struct logininfo *li)
        li->tv_sec = li->tv_usec = 0;
 
        if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
-               logit("%s: problem opening %s: %s", __func__, 
+               logit("%s: problem opening %s: %s", __func__,
                    WTMP_FILE, strerror(errno));
                return (0);
        }
        if (fstat(fd, &st) != 0) {
-               logit("%s: couldn't stat %s: %s", __func__, 
+               logit("%s: couldn't stat %s: %s", __func__,
                    WTMP_FILE, strerror(errno));
                close(fd);
                return (0);
@@ -1144,7 +1195,7 @@ wtmp_get_entry(struct logininfo *li)
 
        while (!found) {
                if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
-                       logit("%s: read of %s failed: %s", __func__, 
+                       logit("%s: read of %s failed: %s", __func__,
                            WTMP_FILE, strerror(errno));
                        close (fd);
                        return (0);
@@ -1202,7 +1253,7 @@ wtmpx_write(struct logininfo *li, struct utmpx *utx)
        int fd, ret = 1;
 
        if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
-               logit("%s: problem opening %s: %s", __func__, 
+               logit("%s: problem opening %s: %s", __func__,
                    WTMPX_FILE, strerror(errno));
                return (0);
        }
@@ -1265,8 +1316,8 @@ wtmpx_write_entry(struct logininfo *li)
 static int
 wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
 {
-       if (strncmp(li->username, utx->ut_name,
-           MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
+       if (strncmp(li->username, utx->ut_user,
+           MIN_SIZEOF(li->username, utx->ut_user)) == 0 ) {
 # ifdef HAVE_TYPE_IN_UTMPX
                if (utx->ut_type == USER_PROCESS)
                        return (1);
@@ -1289,12 +1340,12 @@ wtmpx_get_entry(struct logininfo *li)
        li->tv_sec = li->tv_usec = 0;
 
        if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
-               logit("%s: problem opening %s: %s", __func__, 
+               logit("%s: problem opening %s: %s", __func__,
                    WTMPX_FILE, strerror(errno));
                return (0);
        }
        if (fstat(fd, &st) != 0) {
-               logit("%s: couldn't stat %s: %s", __func__, 
+               logit("%s: couldn't stat %s: %s", __func__,
                    WTMPX_FILE, strerror(errno));
                close(fd);
                return (0);
@@ -1309,13 +1360,13 @@ wtmpx_get_entry(struct logininfo *li)
 
        while (!found) {
                if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
-                       logit("%s: read of %s failed: %s", __func__, 
+                       logit("%s: read of %s failed: %s", __func__,
                            WTMPX_FILE, strerror(errno));
                        close (fd);
                        return (0);
                }
                /*
-                * Logouts are recorded as a blank username on a particular 
+                * Logouts are recorded as a blank username on a particular
                 * line. So, we just need to find the username in struct utmpx
                 */
                if (wtmpx_islogin(li, &utx)) {
@@ -1353,10 +1404,7 @@ syslogin_perform_login(struct logininfo *li)
 {
        struct utmp *ut;
 
-       if ((ut = (struct utmp *)malloc(sizeof(*ut))) == NULL) {
-               logit("%s: couldn't malloc()", __func__);
-               return (0);
-       }
+       ut = xmalloc(sizeof(*ut));
        construct_utmp(li, ut);
        login(ut);
        free(ut);
@@ -1408,25 +1456,14 @@ syslogin_write_entry(struct logininfo *li)
  **/
 
 #ifdef USE_LASTLOG
-#define LL_FILE 1
-#define LL_DIR 2
-#define LL_OTHER 3
-
-static void
-lastlog_construct(struct logininfo *li, struct lastlog *last)
-{
-       /* clear the structure */
-       memset(last, '\0', sizeof(*last));
-
-       line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
-       strlcpy(last->ll_host, li->hostname,
-               MIN_SIZEOF(last->ll_host, li->hostname));
-       last->ll_time = li->tv_sec;
-}
 
+#if !defined(LASTLOG_WRITE_PUTUTXLINE) || !defined(HAVE_GETLASTLOGXBYNAME)
+/* open the file (using filemode) and seek to the login entry */
 static int
-lastlog_filetype(char *filename)
+lastlog_openseek(struct logininfo *li, int *fd, int filemode)
 {
+       off_t offset;
+       char lastlog_file[1024];
        struct stat st;
 
        if (stat(LASTLOG_FILE, &st) != 0) {
@@ -1434,34 +1471,12 @@ lastlog_filetype(char *filename)
                    LASTLOG_FILE, strerror(errno));
                return (0);
        }
-       if (S_ISDIR(st.st_mode))
-               return (LL_DIR);
-       else if (S_ISREG(st.st_mode))
-               return (LL_FILE);
-       else
-               return (LL_OTHER);
-}
-
-
-/* open the file (using filemode) and seek to the login entry */
-static int
-lastlog_openseek(struct logininfo *li, int *fd, int filemode)
-{
-       off_t offset;
-       int type;
-       char lastlog_file[1024];
-
-       type = lastlog_filetype(LASTLOG_FILE);
-       switch (type) {
-       case LL_FILE:
-               strlcpy(lastlog_file, LASTLOG_FILE,
-                   sizeof(lastlog_file));
-               break;
-       case LL_DIR:
+       if (S_ISDIR(st.st_mode)) {
                snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
                    LASTLOG_FILE, li->username);
-               break;
-       default:
+       } else if (S_ISREG(st.st_mode)) {
+               strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
+       } else {
                logit("%s: %.100s is not a file or directory!", __func__,
                    LASTLOG_FILE);
                return (0);
@@ -1474,7 +1489,7 @@ lastlog_openseek(struct logininfo *li, int *fd, int filemode)
                return (0);
        }
 
-       if (type == LL_FILE) {
+       if (S_ISREG(st.st_mode)) {
                /* find this uid's offset in the lastlog file */
                offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
 
@@ -1487,52 +1502,74 @@ lastlog_openseek(struct logininfo *li, int *fd, int filemode)
 
        return (1);
 }
+#endif /* !LASTLOG_WRITE_PUTUTXLINE || !HAVE_GETLASTLOGXBYNAME */
 
-static int
-lastlog_perform_login(struct logininfo *li)
+#ifdef LASTLOG_WRITE_PUTUTXLINE
+int
+lastlog_write_entry(struct logininfo *li)
 {
-       struct lastlog last;
-       int fd;
-
-       /* create our struct lastlog */
-       lastlog_construct(li, &last);
-
-       if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
-               return (0);
-
-       /* write the entry */
-       if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) {
-               close(fd);
-               logit("%s: Error writing to %s: %s", __func__,
-                   LASTLOG_FILE, strerror(errno));
-               return (0);
+       switch(li->type) {
+       case LTYPE_LOGIN:
+               return 1; /* lastlog written by pututxline */
+       default:
+               logit("lastlog_write_entry: Invalid type field");
+               return 0;
        }
-
-       close(fd);
-       return (1);
 }
-
+#else /* LASTLOG_WRITE_PUTUTXLINE */
 int
 lastlog_write_entry(struct logininfo *li)
 {
+       struct lastlog last;
+       int fd;
+
        switch(li->type) {
        case LTYPE_LOGIN:
-               return (lastlog_perform_login(li));
+               /* create our struct lastlog */
+               memset(&last, '\0', sizeof(last));
+               line_stripname(last.ll_line, li->line, sizeof(last.ll_line));
+               strlcpy(last.ll_host, li->hostname,
+                   MIN_SIZEOF(last.ll_host, li->hostname));
+               last.ll_time = li->tv_sec;
+       
+               if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
+                       return (0);
+       
+               /* write the entry */
+               if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) {
+                       close(fd);
+                       logit("%s: Error writing to %s: %s", __func__,
+                           LASTLOG_FILE, strerror(errno));
+                       return (0);
+               }
+       
+               close(fd);
+               return (1);
        default:
                logit("%s: Invalid type field", __func__);
                return (0);
        }
 }
+#endif /* LASTLOG_WRITE_PUTUTXLINE */
 
-static void
-lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
+#ifdef HAVE_GETLASTLOGXBYNAME
+int
+lastlog_get_entry(struct logininfo *li)
 {
-       line_fullname(li->line, last->ll_line, sizeof(li->line));
-       strlcpy(li->hostname, last->ll_host,
-           MIN_SIZEOF(li->hostname, last->ll_host));
-       li->tv_sec = last->ll_time;
-}
+       struct lastlogx l, *ll;
 
+       if ((ll = getlastlogxbyname(li->username, &l)) == NULL) {
+               memset(&l, '\0', sizeof(l));
+               ll = &l;
+       }
+       line_fullname(li->line, ll->ll_line, sizeof(li->line));
+       strlcpy(li->hostname, ll->ll_host,
+               MIN_SIZEOF(li->hostname, ll->ll_host));
+       li->tv_sec = ll->ll_tv.tv_sec;
+       li->tv_usec = ll->ll_tv.tv_usec;
+       return (1);
+}
+#else /* HAVE_GETLASTLOGXBYNAME */
 int
 lastlog_get_entry(struct logininfo *li)
 {
@@ -1550,7 +1587,10 @@ lastlog_get_entry(struct logininfo *li)
                memset(&last, '\0', sizeof(last));
                /* FALLTHRU */
        case sizeof(last):
-               lastlog_populate_entry(li, &last);
+               line_fullname(li->line, last.ll_line, sizeof(li->line));
+               strlcpy(li->hostname, last.ll_host,
+                   MIN_SIZEOF(li->hostname, last.ll_host));
+               li->tv_sec = last.ll_time;
                return (1);
        case -1:
                error("%s: Error reading from %s: %s", __func__,
@@ -1558,11 +1598,91 @@ lastlog_get_entry(struct logininfo *li)
                return (0);
        default:
                error("%s: Error reading from %s: Expecting %d, got %d",
-                   __func__, LASTLOG_FILE, sizeof(last), ret);
+                   __func__, LASTLOG_FILE, (int)sizeof(last), ret);
                return (0);
        }
 
        /* NOTREACHED */
        return (0);
 }
+#endif /* HAVE_GETLASTLOGXBYNAME */
 #endif /* USE_LASTLOG */
+
+#ifdef USE_BTMP
+  /*
+   * Logs failed login attempts in _PATH_BTMP if that exists.
+   * The most common login failure is to give password instead of username.
+   * So the _PATH_BTMP file checked for the correct permission, so that
+   * only root can read it.
+   */
+
+void
+record_failed_login(const char *username, const char *hostname,
+    const char *ttyn)
+{
+       int fd;
+       struct utmp ut;
+       struct sockaddr_storage from;
+       socklen_t fromlen = sizeof(from);
+       struct sockaddr_in *a4;
+       struct sockaddr_in6 *a6;
+       time_t t;
+       struct stat fst;
+
+       if (geteuid() != 0)
+               return;
+       if ((fd = open(_PATH_BTMP, O_WRONLY | O_APPEND)) < 0) {
+               debug("Unable to open the btmp file %s: %s", _PATH_BTMP,
+                   strerror(errno));
+               return;
+       }
+       if (fstat(fd, &fst) < 0) {
+               logit("%s: fstat of %s failed: %s", __func__, _PATH_BTMP,
+                   strerror(errno));
+               goto out;
+       }
+       if((fst.st_mode & (S_IRWXG | S_IRWXO)) || (fst.st_uid != 0)){
+               logit("Excess permission or bad ownership on file %s",
+                   _PATH_BTMP);
+               goto out;
+       }
+
+       memset(&ut, 0, sizeof(ut));
+       /* strncpy because we don't necessarily want nul termination */
+       strncpy(ut.ut_user, username, sizeof(ut.ut_user));
+       strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line));
+
+       time(&t);
+       ut.ut_time = t;     /* ut_time is not always a time_t */
+       ut.ut_type = LOGIN_PROCESS;
+       ut.ut_pid = getpid();
+
+       /* strncpy because we don't necessarily want nul termination */
+       strncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
+
+       if (packet_connection_is_on_socket() &&
+           getpeername(packet_get_connection_in(),
+           (struct sockaddr *)&from, &fromlen) == 0) {
+               ipv64_normalise_mapped(&from, &fromlen);
+               if (from.ss_family == AF_INET) {
+                       a4 = (struct sockaddr_in *)&from;
+                       memcpy(&ut.ut_addr, &(a4->sin_addr),
+                           MIN_SIZEOF(ut.ut_addr, a4->sin_addr));
+               }
+#ifdef HAVE_ADDR_V6_IN_UTMP
+               if (from.ss_family == AF_INET6) {
+                       a6 = (struct sockaddr_in6 *)&from;
+                       memcpy(&ut.ut_addr_v6, &(a6->sin6_addr),
+                           MIN_SIZEOF(ut.ut_addr_v6, a6->sin6_addr));
+               }
+#endif
+       }
+
+       if (atomicio(vwrite, fd, &ut, sizeof(ut)) != sizeof(ut))
+               error("Failed to write to %s: %s", _PATH_BTMP,
+                   strerror(errno));
+
+out:
+       close(fd);
+}
+#endif /* USE_BTMP */
This page took 0.366882 seconds and 4 git commands to generate.