]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Log identifying information for the other end of connections.
authorSimon Tatham <anakin@pobox.com>
Mon, 18 May 2015 12:57:45 +0000 (13:57 +0100)
committerSimon Tatham <anakin@pobox.com>
Sat, 20 Jun 2015 11:47:02 +0000 (12:47 +0100)
When anyone connects to a PuTTY tool's listening socket - whether it's
a user of a local->remote port forwarding, a connection-sharing
downstream or a client of Pageant - we'd like to log as much
information as we can find out about where the connection came from.

To that end, I've implemented a function sk_peer_info() in the socket
abstraction, which returns a freeform text string as best it can (or
NULL, if it can't get anything at all) describing the thing at the
other end of the connection. For TCP connections, this is done using
getpeername() to get an IP address and port in the obvious way; for
Unix-domain sockets, we attempt SO_PEERCRED (conditionalised on some
moderately hairy autoconfery) to get the pid and owner of the peer. I
haven't implemented anything for Windows named pipes, but I will if I
hear of anything useful.

(cherry picked from commit c8f83979a368d10e8def1796cdadd7f8f3bebf74)

Conflicts:
pageant.c

Cherry-picker's notes: the conflict was because the original commit
also added a use of the same feature in the centralised Pageant code,
which doesn't exist on this branch. Also I had to remove 'const' from
the type of the second parameter to wrap_send_port_open(), since this
branch hasn't had the same extensive const-fixing as master.

16 files changed:
Recipe
configure.ac
errsock.c
network.h
portfwd.c
proxy.c
ssh.c
ssh.h
sshshare.c
unix/unix.h
unix/uxnet.c
unix/uxpeer.c [new file with mode: 0644]
unix/uxproxy.c
windows/winhsock.c
windows/winnet.c
windows/winnps.c

diff --git a/Recipe b/Recipe
index bac258f18b8fc5bf5f2164d1255ecfa3482e6a63..ca6369d528c7b5279df562267be49ddf2c4cac0c 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -230,8 +230,8 @@ SFTP     = sftp int64 logging
 MISC     = timing callback misc version settings tree234 proxy conf
 WINMISC  = MISC winstore winnet winhandl cmdline windefs winmisc winproxy
          + wintime winhsock errsock
-UXMISC   = MISC uxstore uxsel uxnet cmdline uxmisc uxproxy time
-OSXMISC  = MISC uxstore uxsel osxsel uxnet uxmisc uxproxy time
+UXMISC   = MISC uxstore uxsel uxnet uxpeer cmdline uxmisc uxproxy time
+OSXMISC  = MISC uxstore uxsel osxsel uxnet uxpeer uxmisc uxproxy time
 
 # Character set library, for use in pterm.
 CHARSET  = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc
index 29fbde6d6bf0114f401d304d1969ec6bce404190..5818e032a1846fd3139d91f52e17cda0f24410d4 100644 (file)
@@ -132,6 +132,28 @@ AC_CHECK_FUNCS([getaddrinfo posix_openpt ptsname setresuid strsignal updwtmpx])
 AC_CHECK_DECLS([CLOCK_MONOTONIC], [], [], [[#include <time.h>]])
 AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME],[],[Define if clock_gettime() is available])])
 
+AC_CACHE_CHECK([for SO_PEERCRED and dependencies], [x_cv_linux_so_peercred], [
+    AC_COMPILE_IFELSE([
+        AC_LANG_PROGRAM([[
+            #define _GNU_SOURCE
+            #include <features.h>
+            #include <sys/socket.h>
+          ]],[[
+            struct ucred cr;
+            socklen_t crlen = sizeof(cr);
+            return getsockopt(0, SOL_SOCKET, SO_PEERCRED, &cr, &crlen) +
+            cr.pid + cr.uid + cr.gid;
+          ]]
+        )],
+        AS_VAR_SET(x_cv_linux_so_peercred, yes),
+        AS_VAR_SET(x_cv_linux_so_peercred, no)
+    )
+])
+AS_IF([test AS_VAR_GET(x_cv_linux_so_peercred) = yes],
+    [AC_DEFINE([HAVE_SO_PEERCRED], [1],
+     [Define if SO_PEERCRED works in the Linux fashion.])]
+)
+
 if test "x$GCC" = "xyes"; then
   :
   AC_SUBST(WARNINGOPTS, ['-Wall -Werror'])
index 1b3a88a2560883a5b8809d08383c26827443ee9e..854fd8be83ef72ae32eb27eb2bf7bea2425b179e 100644 (file)
--- a/errsock.c
+++ b/errsock.c
@@ -43,6 +43,11 @@ static const char *sk_error_socket_error(Socket s)
     return ps->error;
 }
 
+static char *sk_error_peer_info(Socket s)
+{
+    return NULL;
+}
+
 Socket new_error_socket(const char *errmsg, Plug plug)
 {
     static const struct socket_function_table socket_fn_table = {
@@ -53,7 +58,8 @@ Socket new_error_socket(const char *errmsg, Plug plug)
        NULL /* write_eof */,
        NULL /* flush */,
        NULL /* set_frozen */,
-       sk_error_socket_error
+       sk_error_socket_error,
+       sk_error_peer_info,
     };
 
     Error_Socket ret;
index 675d24c7c5575f3e20e52b7822321f5e157af683..319475e7909d6510b7557b3e17bb019ea6e83126 100644 (file)
--- a/network.h
+++ b/network.h
@@ -38,6 +38,7 @@ struct socket_function_table {
     void (*set_frozen) (Socket s, int is_frozen);
     /* ignored by tcp, but vital for ssl */
     const char *(*socket_error) (Socket s);
+    char *(*peer_info) (Socket s);
 };
 
 typedef union { void *p; int i; } accept_ctx_t;
@@ -181,6 +182,13 @@ const char *sk_addr_error(SockAddr addr);
  */
 #define sk_set_frozen(s, is_frozen) (((*s)->set_frozen) (s, is_frozen))
 
+/*
+ * Return a (dynamically allocated) string giving some information
+ * about the other end of the socket, suitable for putting in log
+ * files. May be NULL if nothing is available at all.
+ */
+#define sk_peer_info(s) (((*s)->peer_info) (s))
+
 /*
  * Simple wrapper on getservbyname(), needed by ssh.c. Returns the
  * port number, in host byte order (suitable for printf and so on).
index e3e63e39aafbb3355e0c3c5fbc465bbede9e823f..edbd7bcc3978bc12e6fe5c9ef6306c1ca803d886 100644 (file)
--- a/portfwd.c
+++ b/portfwd.c
@@ -157,6 +157,21 @@ static int pfl_closing(Plug plug, const char *error_msg, int error_code,
     return 1;
 }
 
+static void wrap_send_port_open(void *channel, char *hostname, int port,
+                                Socket s)
+{
+    char *peerinfo, *description;
+    peerinfo = sk_peer_info(s);
+    if (peerinfo) {
+        description = dupprintf("forwarding from %s", peerinfo);
+        sfree(peerinfo);
+    } else {
+        description = dupstr("forwarding");
+    }
+    ssh_send_port_open(channel, hostname, port, description);
+    sfree(description);
+}
+
 static int pfd_receive(Plug plug, int urgent, char *data, int len)
 {
     struct PortForwarding *pf = (struct PortForwarding *) plug;
@@ -371,7 +386,7 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
            return 1;
        } else {
            /* asks to forward to the specified host/port for this */
-           ssh_send_port_open(pf->c, pf->hostname, pf->port, "forwarding");
+           wrap_send_port_open(pf->c, pf->hostname, pf->port, pf->s);
        }
        pf->dynamic = 0;
 
@@ -510,7 +525,7 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
            return 1;
        } else {
            /* asks to forward to the specified host/port for this */
-           ssh_send_port_open(pf->c, pf->hostname, pf->port, "forwarding");
+           wrap_send_port_open(pf->c, pf->hostname, pf->port, s);
        }
     }
 
diff --git a/proxy.c b/proxy.c
index e2201c643f80579e4b5dd3aa52310442ed53f469..51754dc304a2e8e4f8db2c8dfd644c7940337301 100644 (file)
--- a/proxy.c
+++ b/proxy.c
@@ -388,7 +388,8 @@ Socket new_connection(SockAddr addr, char *hostname,
        sk_proxy_write_eof,
        sk_proxy_flush,
        sk_proxy_set_frozen,
-       sk_proxy_socket_error
+       sk_proxy_socket_error,
+        NULL, /* peer_info */
     };
 
     static const struct plug_function_table plug_fn_table = {
diff --git a/ssh.c b/ssh.c
index 368cabef9a5ef1500e6ea7cafa29e5a94bc2cb57..059b6522cec0f022536f510bd77637d62e812a7a 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -7608,9 +7608,14 @@ static void ssh_check_termination(Ssh ssh)
     }
 }
 
-void ssh_sharing_downstream_connected(Ssh ssh, unsigned id)
+void ssh_sharing_downstream_connected(Ssh ssh, unsigned id,
+                                      const char *peerinfo)
 {
-    logeventf(ssh, "Connection sharing downstream #%u connected", id);
+    if (peerinfo)
+        logeventf(ssh, "Connection sharing downstream #%u connected from %s",
+                  id, peerinfo);
+    else
+        logeventf(ssh, "Connection sharing downstream #%u connected", id);
 }
 
 void ssh_sharing_downstream_disconnected(Ssh ssh, unsigned id)
diff --git a/ssh.h b/ssh.h
index a92add2fc8d96468bd43f5d55fa82401cc0e398c..f55136e9aa580a46a4f1d2cf61e7b4968021eb38 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -44,7 +44,8 @@ void ssh_sharing_remove_x11_display(Ssh ssh, struct X11FakeAuth *auth);
 void ssh_send_packet_from_downstream(Ssh ssh, unsigned id, int type,
                                      const void *pkt, int pktlen,
                                      const char *additional_log_text);
-void ssh_sharing_downstream_connected(Ssh ssh, unsigned id);
+void ssh_sharing_downstream_connected(Ssh ssh, unsigned id,
+                                      const char *peerinfo);
 void ssh_sharing_downstream_disconnected(Ssh ssh, unsigned id);
 void ssh_sharing_logf(Ssh ssh, unsigned id, const char *logfmt, ...);
 int ssh_agent_forwarding_permitted(Ssh ssh);
index 54d58a6624bfdb9b19b4d32afb37b2943e9e3751..2b2f69a914f736d3605741f73050d30cdfd4615a 100644 (file)
@@ -1924,6 +1924,7 @@ static int share_listen_accepting(Plug plug,
     struct ssh_sharing_state *sharestate = (struct ssh_sharing_state *)plug;
     struct ssh_sharing_connstate *cs;
     const char *err;
+    char *peerinfo;
 
     /*
      * A new downstream has connected to us.
@@ -1966,7 +1967,9 @@ static int share_listen_accepting(Plug plug,
     cs->forwardings = newtree234(share_forwarding_cmp);
     cs->globreq_head = cs->globreq_tail = NULL;
 
-    ssh_sharing_downstream_connected(sharestate->ssh, cs->id);
+    peerinfo = sk_peer_info(cs->sock);
+    ssh_sharing_downstream_connected(sharestate->ssh, cs->id, peerinfo);
+    sfree(peerinfo);
 
     return 0;
 }
index d43e0ae097a5f273e1b8233eae0b19fdf306bea8..917976d699d704f128a15d65cf4b40f390676ac5 100644 (file)
@@ -184,4 +184,9 @@ void *sk_getxdmdata(void *sock, int *lenp);
  */
 extern Backend serial_backend;
 
+/*
+ * uxpeer.c, wrapping getsockopt(SO_PEERCRED).
+ */
+int so_peercred(int fd, int *pid, int *uid, int *gid);
+
 #endif
index 235d6fd97d039d3ed2f97167e68e4285935981ee..5058d8ab20409a82f92c159979f6f0cd841ded6d 100644 (file)
@@ -16,6 +16,8 @@
 #include <netinet/tcp.h>
 #include <netdb.h>
 #include <sys/un.h>
+#include <pwd.h>
+#include <grp.h>
 
 #define DEFINE_PLUG_METHOD_MACROS
 #include "putty.h"
@@ -485,6 +487,7 @@ static int sk_tcp_write(Socket s, const char *data, int len);
 static int sk_tcp_write_oob(Socket s, const char *data, int len);
 static void sk_tcp_write_eof(Socket s);
 static void sk_tcp_set_frozen(Socket s, int is_frozen);
+static char *sk_tcp_peer_info(Socket s);
 static const char *sk_tcp_socket_error(Socket s);
 
 static struct socket_function_table tcp_fn_table = {
@@ -495,7 +498,8 @@ static struct socket_function_table tcp_fn_table = {
     sk_tcp_write_eof,
     sk_tcp_flush,
     sk_tcp_set_frozen,
-    sk_tcp_socket_error
+    sk_tcp_socket_error,
+    sk_tcp_peer_info,
 };
 
 static Socket sk_tcp_accept(accept_ctx_t ctx, Plug plug)
@@ -1419,6 +1423,51 @@ static void sk_tcp_set_frozen(Socket sock, int is_frozen)
     uxsel_tell(s);
 }
 
+static char *sk_tcp_peer_info(Socket sock)
+{
+    Actual_Socket s = (Actual_Socket) sock;
+    struct sockaddr_storage addr;
+    socklen_t addrlen = sizeof(addr);
+    char buf[INET6_ADDRSTRLEN];
+
+    if (getpeername(s->s, (struct sockaddr *)&addr, &addrlen) < 0)
+        return NULL;
+    if (addr.ss_family == AF_INET) {
+        return dupprintf
+            ("%s:%d",
+             inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr),
+             (int)ntohs(((struct sockaddr_in *)&addr)->sin_port));
+#ifndef NO_IPV6
+    } else if (addr.ss_family == AF_INET6) {
+        return dupprintf
+            ("[%s]:%d",
+             inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr,
+                       buf, sizeof(buf)),
+             (int)ntohs(((struct sockaddr_in6 *)&addr)->sin6_port));
+#endif
+    } else if (addr.ss_family == AF_UNIX) {
+        /*
+         * For Unix sockets, the source address is unlikely to be
+         * helpful. Instead, we try SO_PEERCRED and try to get the
+         * source pid.
+         */
+        int pid, uid, gid;
+        if (so_peercred(s->s, &pid, &uid, &gid)) {
+            char uidbuf[64], gidbuf[64];
+            sprintf(uidbuf, "%d", uid);
+            sprintf(gidbuf, "%d", gid);
+            struct passwd *pw = getpwuid(uid);
+            struct group *gr = getgrgid(gid);
+            return dupprintf("pid %d (%s:%s)", pid,
+                             pw ? pw->pw_name : uidbuf,
+                             gr ? gr->gr_name : gidbuf);
+        }
+        return NULL;
+    } else {
+        return NULL;
+    }
+}
+
 static void uxsel_tell(Actual_Socket s)
 {
     int rwx = 0;
diff --git a/unix/uxpeer.c b/unix/uxpeer.c
new file mode 100644 (file)
index 0000000..57bcfb8
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Unix: wrapper for getsockopt(SO_PEERCRED), conditionalised on
+ * appropriate autoconfery.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "uxconfig.h" /* leading space prevents mkfiles.pl trying to follow */
+#endif
+
+#ifdef HAVE_SO_PEERCRED
+#define _GNU_SOURCE
+#include <features.h>
+#endif
+
+#include <sys/socket.h>
+
+#include "putty.h"
+
+int so_peercred(int fd, int *pid, int *uid, int *gid)
+{
+#ifdef HAVE_SO_PEERCRED
+    struct ucred cr;
+    socklen_t crlen = sizeof(cr);
+    if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &crlen) == 0) {
+        *pid = cr.pid;
+        *uid = cr.uid;
+        *gid = cr.gid;
+        return TRUE;
+    }
+#endif
+    return FALSE;
+}
index dfbcc86a290bdb00ec982fbaffb63fd1cdf3dcc3..fe81d5fd02ac6d3071dd533fa008d2db219f8ba2 100644 (file)
@@ -245,7 +245,8 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
        sk_localproxy_write_eof,
        sk_localproxy_flush,
        sk_localproxy_set_frozen,
-       sk_localproxy_socket_error
+       sk_localproxy_socket_error,
+        NULL, /* peer_info */
     };
 
     Local_Proxy_Socket ret;
index 6d7e7da4f808a7aeab59428881f5a846f25c0b7e..6b088bb8217336d10cb693e311d1ec99be600402 100644 (file)
@@ -234,6 +234,11 @@ static const char *sk_handle_socket_error(Socket s)
     return ps->error;
 }
 
+static char *sk_handle_peer_info(Socket s)
+{
+    return NULL;
+}
+
 Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
                           int overlapped)
 {
@@ -245,7 +250,8 @@ Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
        sk_handle_write_eof,
        sk_handle_flush,
        sk_handle_set_frozen,
-       sk_handle_socket_error
+       sk_handle_socket_error,
+        sk_handle_peer_info,
     };
 
     Handle_Socket ret;
index 039701cf0bd9b9aab05d0e0f89c9f7af3622f6e2..fdb83c806ca8f34df85388a8e5a45b0ec73ba204 100644 (file)
@@ -160,6 +160,8 @@ DECL_WINDOWS_FUNCTION(static, struct servent FAR *, getservbyname,
                      (const char FAR *, const char FAR *));
 DECL_WINDOWS_FUNCTION(static, unsigned long, inet_addr, (const char FAR *));
 DECL_WINDOWS_FUNCTION(static, char FAR *, inet_ntoa, (struct in_addr));
+DECL_WINDOWS_FUNCTION(static, const char FAR *, inet_ntop,
+                      (int, void FAR *, char *, size_t));
 DECL_WINDOWS_FUNCTION(static, int, connect,
                      (SOCKET, const struct sockaddr FAR *, int));
 DECL_WINDOWS_FUNCTION(static, int, bind,
@@ -174,6 +176,8 @@ DECL_WINDOWS_FUNCTION(static, int, ioctlsocket,
                      (SOCKET, long, u_long FAR *));
 DECL_WINDOWS_FUNCTION(static, SOCKET, accept,
                      (SOCKET, struct sockaddr FAR *, int FAR *));
+DECL_WINDOWS_FUNCTION(static, int, getpeername,
+                     (SOCKET, struct sockaddr FAR *, int FAR *));
 DECL_WINDOWS_FUNCTION(static, int, recv, (SOCKET, char FAR *, int, int));
 DECL_WINDOWS_FUNCTION(static, int, WSAIoctl,
                      (SOCKET, DWORD, LPVOID, DWORD, LPVOID, DWORD,
@@ -288,6 +292,7 @@ void sk_init(void)
     GET_WINDOWS_FUNCTION(winsock_module, getservbyname);
     GET_WINDOWS_FUNCTION(winsock_module, inet_addr);
     GET_WINDOWS_FUNCTION(winsock_module, inet_ntoa);
+    GET_WINDOWS_FUNCTION(winsock_module, inet_ntop);
     GET_WINDOWS_FUNCTION(winsock_module, connect);
     GET_WINDOWS_FUNCTION(winsock_module, bind);
     GET_WINDOWS_FUNCTION(winsock_module, setsockopt);
@@ -297,6 +302,7 @@ void sk_init(void)
     GET_WINDOWS_FUNCTION(winsock_module, shutdown);
     GET_WINDOWS_FUNCTION(winsock_module, ioctlsocket);
     GET_WINDOWS_FUNCTION(winsock_module, accept);
+    GET_WINDOWS_FUNCTION(winsock_module, getpeername);
     GET_WINDOWS_FUNCTION(winsock_module, recv);
     GET_WINDOWS_FUNCTION(winsock_module, WSAIoctl);
 
@@ -861,6 +867,7 @@ static int sk_tcp_write_oob(Socket s, const char *data, int len);
 static void sk_tcp_write_eof(Socket s);
 static void sk_tcp_set_frozen(Socket s, int is_frozen);
 static const char *sk_tcp_socket_error(Socket s);
+static char *sk_tcp_peer_info(Socket s);
 
 extern char *do_select(SOCKET skt, int startup);
 
@@ -874,7 +881,8 @@ static Socket sk_tcp_accept(accept_ctx_t ctx, Plug plug)
        sk_tcp_write_eof,
        sk_tcp_flush,
        sk_tcp_set_frozen,
-       sk_tcp_socket_error
+       sk_tcp_socket_error,
+       sk_tcp_peer_info,
     };
 
     DWORD err;
@@ -1122,7 +1130,8 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
        sk_tcp_write_eof,
        sk_tcp_flush,
        sk_tcp_set_frozen,
-       sk_tcp_socket_error
+       sk_tcp_socket_error,
+       sk_tcp_peer_info,
     };
 
     Actual_Socket ret;
@@ -1173,7 +1182,8 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
        sk_tcp_write_eof,
        sk_tcp_flush,
        sk_tcp_set_frozen,
-       sk_tcp_socket_error
+       sk_tcp_socket_error,
+       sk_tcp_peer_info,
     };
 
     SOCKET s;
@@ -1744,6 +1754,38 @@ static const char *sk_tcp_socket_error(Socket sock)
     return s->error;
 }
 
+static char *sk_tcp_peer_info(Socket sock)
+{
+    Actual_Socket s = (Actual_Socket) sock;
+#ifdef NO_IPV6
+    struct sockaddr_in addr;
+#else
+    struct sockaddr_storage addr;
+#endif
+    int addrlen = sizeof(addr);
+    char buf[INET6_ADDRSTRLEN];
+
+    if (p_getpeername(s->s, (struct sockaddr *)&addr, &addrlen) < 0)
+        return NULL;
+
+    if (((struct sockaddr *)&addr)->sa_family == AF_INET) {
+        return dupprintf
+            ("%s:%d",
+             p_inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr),
+             (int)p_ntohs(((struct sockaddr_in *)&addr)->sin_port));
+#ifndef NO_IPV6
+    } else if (((struct sockaddr *)&addr)->sa_family == AF_INET6) {
+        return dupprintf
+            ("[%s]:%d",
+             p_inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr,
+                         buf, sizeof(buf)),
+             (int)p_ntohs(((struct sockaddr_in6 *)&addr)->sin6_port));
+#endif
+    } else {
+        return NULL;
+    }
+}
+
 static void sk_tcp_set_frozen(Socket sock, int is_frozen)
 {
     Actual_Socket s = (Actual_Socket) sock;
index 7b4aa0db7818eba16973cd4451d973b9a5416109..2547fd71c5a3863b8114ab8ca4777a0a86e33d5e 100644 (file)
@@ -71,6 +71,11 @@ static const char *sk_namedpipeserver_socket_error(Socket s)
     return ps->error;
 }
 
+static char *sk_namedpipeserver_peer_info(Socket s)
+{
+    return NULL;
+}
+
 static int create_named_pipe(Named_Pipe_Server_Socket ps, int first_instance)
 {
     SECURITY_ATTRIBUTES sa;
@@ -211,7 +216,8 @@ Socket new_named_pipe_listener(const char *pipename, Plug plug)
         NULL /* write_eof */,
         NULL /* flush */,
         NULL /* set_frozen */,
-       sk_namedpipeserver_socket_error
+       sk_namedpipeserver_socket_error,
+       sk_namedpipeserver_peer_info,
     };
 
     Named_Pipe_Server_Socket ret;