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;