From c8f83979a368d10e8def1796cdadd7f8f3bebf74 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 18 May 2015 13:57:45 +0100 Subject: [PATCH] Log identifying information for the other end of connections. 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. --- Recipe | 4 ++-- configure.ac | 22 ++++++++++++++++++++ errsock.c | 8 +++++++- network.h | 8 ++++++++ pageant.c | 10 +++++++-- portfwd.c | 19 +++++++++++++++-- proxy.c | 3 ++- ssh.c | 9 ++++++-- ssh.h | 3 ++- sshshare.c | 5 ++++- unix/unix.h | 5 +++++ unix/uxnet.c | 51 +++++++++++++++++++++++++++++++++++++++++++++- unix/uxpeer.c | 32 +++++++++++++++++++++++++++++ unix/uxproxy.c | 3 ++- windows/winhsock.c | 8 +++++++- windows/winnet.c | 48 ++++++++++++++++++++++++++++++++++++++++--- windows/winnps.c | 8 +++++++- 17 files changed, 227 insertions(+), 19 deletions(-) create mode 100644 unix/uxpeer.c diff --git a/Recipe b/Recipe index 31d5eb27..81b75704 100644 --- 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 # import.c and dependencies, for PuTTYgen-like utilities that have to # load foreign key files. diff --git a/configure.ac b/configure.ac index 29fbde6d..5818e032 100644 --- a/configure.ac +++ b/configure.ac @@ -132,6 +132,28 @@ AC_CHECK_FUNCS([getaddrinfo posix_openpt ptsname setresuid strsignal updwtmpx]) AC_CHECK_DECLS([CLOCK_MONOTONIC], [], [], [[#include ]]) 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 + #include + ]],[[ + 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']) diff --git a/errsock.c b/errsock.c index 1b3a88a2..854fd8be 100644 --- 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; diff --git a/network.h b/network.h index ec67ba94..7dc49782 100644 --- 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; @@ -182,6 +183,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). diff --git a/pageant.c b/pageant.c index d1db8623..c008f008 100644 --- a/pageant.c +++ b/pageant.c @@ -1100,6 +1100,7 @@ static int pageant_listen_accepting(Plug plug, struct pageant_listen_state *pl = (struct pageant_listen_state *)plug; struct pageant_conn_state *pc; const char *err; + char *peerinfo; pc = snew(struct pageant_conn_state); pc->fn = &connection_fn_table; @@ -1116,8 +1117,13 @@ static int pageant_listen_accepting(Plug plug, sk_set_frozen(pc->connsock, 0); - /* FIXME: can we get any useful peer id info? */ - plog(pl->logctx, pl->logfn, "%p: new connection", pc); + peerinfo = sk_peer_info(pc->connsock); + if (peerinfo) { + plog(pl->logctx, pl->logfn, "%p: new connection from %s", + pc, peerinfo); + } else { + plog(pl->logctx, pl->logfn, "%p: new connection", pc); + } return 0; } diff --git a/portfwd.c b/portfwd.c index e3e63e39..325e8687 100644 --- 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, const 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 423ed8da..3104ef0d 100644 --- a/proxy.c +++ b/proxy.c @@ -388,7 +388,8 @@ Socket new_connection(SockAddr addr, const 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 47f904eb..b56e155e 100644 --- a/ssh.c +++ b/ssh.c @@ -7746,9 +7746,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 0b6b4f8e..1317ca4b 100644 --- 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); diff --git a/sshshare.c b/sshshare.c index 54d58a66..2b2f69a9 100644 --- a/sshshare.c +++ b/sshshare.c @@ -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; } diff --git a/unix/unix.h b/unix/unix.h index aba8a2bd..52394df9 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -186,4 +186,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 diff --git a/unix/uxnet.c b/unix/uxnet.c index e409315a..760d0a57 100644 --- a/unix/uxnet.c +++ b/unix/uxnet.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #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) @@ -1420,6 +1424,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 index 00000000..57bcfb88 --- /dev/null +++ b/unix/uxpeer.c @@ -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 +#endif + +#include + +#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; +} diff --git a/unix/uxproxy.c b/unix/uxproxy.c index f230604a..0399245d 100644 --- a/unix/uxproxy.c +++ b/unix/uxproxy.c @@ -245,7 +245,8 @@ Socket platform_new_connection(SockAddr addr, const 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; diff --git a/windows/winhsock.c b/windows/winhsock.c index 6d7e7da4..6b088bb8 100644 --- a/windows/winhsock.c +++ b/windows/winhsock.c @@ -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; diff --git a/windows/winnet.c b/windows/winnet.c index 5e05cfc2..680de2a7 100644 --- a/windows/winnet.c +++ b/windows/winnet.c @@ -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(const char *srcaddr, int port, 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, }; 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; diff --git a/windows/winnps.c b/windows/winnps.c index 7b4aa0db..2547fd71 100644 --- a/windows/winnps.c +++ b/windows/winnps.c @@ -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; -- 2.45.2