On Mon, 23 Jun 2014, Larry Becke wrote: > I was wondering what everyone's thoughts were on a simpler way to exclude > addresses from having listeners on them. > > I know a lot of people have multiple subnets, especially larger > corporations. > > Some networks are non-route-able, and therefor unsuitable for use with SSH, > aside from communication between other servers on the same subnet. I made this patch earlier this year. It adds a ListenFilter sshd_config option to select addresses to listen on by network/mask. It required getifaddrs(), which is available on BSDish/Linux systems but probably not legacy Unix so porting it might be problematic. It also won't cope well with machines with dynamic interfaces: the addresses to listen on will be selected when sshd starts only. Usage: sshd_config => ListenAddress * ListenFilter 127.0.0.0/8 10.0.0.0/8 Will listen only on addresses matching 127.* and 10.* If there is enough interest then it might be worth polishing up and committing. -d Index: servconf.c =================================================================== RCS file: /var/cvs/openssh/servconf.c,v retrieving revision 1.247 diff -u -p -r1.247 servconf.c --- servconf.c 4 Feb 2014 00:12:57 -0000 1.247 +++ servconf.c 24 Mar 2014 07:29:56 -0000 @@ -153,6 +153,7 @@ initialize_server_options(ServerOptions options->ip_qos_interactive = -1; options->ip_qos_bulk = -1; options->version_addendum = NULL; + options->num_listen_filters = 0; } void @@ -348,6 +349,7 @@ typedef enum { sKexAlgorithms, sIPQoS, sVersionAddendum, sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, sAuthenticationMethods, sHostKeyAgent, + sListenFilter, sDeprecated, sUnsupported } ServerOpCodes; @@ -474,6 +476,7 @@ static struct { { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL }, + { "listenfilter", sListenFilter, SSHCFG_GLOBAL }, { NULL, sBadOption, 0 } }; @@ -1620,6 +1623,25 @@ process_server_config_line(ServerOptions } return 0; + case sListenFilter: + if (*activep && options->num_listen_filters == 0) { + while ((arg = strdelim(&cp)) && *arg != '\0') { + if (options->num_listen_filters >= + MAX_LISTEN_FILTERS) + fatal("%s line %d: " + "too many listen address filters.", + filename, linenum); + if (addr_match_list(NULL, arg) != 0) + fatal("%s line %d: " + "invalid ListenFilter address %s", + filename, linenum, arg); + options->listen_filter[ + options->num_listen_filters++] = + xstrdup(arg); + } + } + return 0; + case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); @@ -2056,6 +2078,8 @@ dump_config(ServerOptions *o) dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env); dump_cfg_strarray_oneline(sAuthenticationMethods, o->num_auth_methods, o->auth_methods); + dump_cfg_strarray_oneline(sListenFilter, o->num_listen_filters, + o->listen_filter); /* other arguments */ for (i = 0; i < o->num_subsystems; i++) Index: servconf.h =================================================================== RCS file: /var/cvs/openssh/servconf.h,v retrieving revision 1.104 diff -u -p -r1.104 servconf.h --- servconf.h 4 Feb 2014 00:12:57 -0000 1.104 +++ servconf.h 24 Mar 2014 07:29:56 -0000 @@ -29,6 +29,7 @@ #define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */ #define MAX_AUTHKEYS_FILES 256 /* Max # of authorized_keys files. */ #define MAX_AUTH_METHODS 256 /* Max # of AuthenticationMethods. */ +#define MAX_LISTEN_FILTERS 4 /* Max # of ListenFilters. */ /* permit_root_login */ #define PERMIT_NOT_SET -1 @@ -183,6 +184,9 @@ typedef struct { u_int num_auth_methods; char *auth_methods[MAX_AUTH_METHODS]; + + u_int num_listen_filters; + char *listen_filter[MAX_LISTEN_FILTERS]; } ServerOptions; /* Information about the incoming connection as used by Match */ Index: sshd.c =================================================================== RCS file: /var/cvs/openssh/sshd.c,v retrieving revision 1.448 diff -u -p -r1.448 sshd.c --- sshd.c 26 Feb 2014 23:20:08 -0000 1.448 +++ sshd.c 24 Mar 2014 07:29:56 -0000 @@ -71,6 +71,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <ifaddrs.h> #include <openssl/dh.h> #include <openssl/bn.h> @@ -121,6 +122,7 @@ #include "roaming.h" #include "ssh-sandbox.h" #include "version.h" +#include "match.h" #ifdef LIBWRAP #include <tcpd.h> @@ -1073,6 +1075,114 @@ server_accept_inetd(int *sock_in, int *s debug("inetd sockets after dupping: %d, %d", *sock_in, *sock_out); } +static const char * +render_addr(struct sockaddr *addr) +{ + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + static char ret[NI_MAXHOST+NI_MAXSERV+2+1+1]; + int r; + + if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6) { + snprintf(ret, sizeof(ret), "<UNSUPPORTED FAMILY %d>", + addr->sa_family); + return ret; + } + + if ((r = getnameinfo(addr, + addr->sa_family == AF_INET ? + sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { + snprintf(ret, sizeof(ret), "<GETNAMEINFO ERROR %.40s>", + ssh_gai_strerror(r)); + return ret; + } + snprintf(ret, sizeof(ret), "[%s]:%s", ntop, strport); + return ret; +} + +#define E_S4(a) ((struct sockaddr_in *)((a)->ai_addr)) +#define E_S6(a) ((struct sockaddr_in6 *)((a)->ai_addr)) + +/* Expand wildcard addresses in addrlist to actual interface addresses */ +static struct addrinfo * +expand_local_addrs(struct addrinfo *addrlist) +{ + struct ifaddrs *ifaddrs, *ifa; + struct addrinfo *ai, *ret = NULL, **rl = &ret; + int i, v6, have_wild4 = 0, have_wild6 = 0; + + if (addrlist == NULL) + return NULL; + + if (getifaddrs(&ifaddrs) != 0) + fatal("%s: getifaddrs: %s", __func__, strerror(errno)); + + for (i = 0, ai = addrlist; ai; ai = ai->ai_next, i++) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) { + debug3("%s: addr %d AF %d", __func__, i, ai->ai_family); + continue; + } + v6 = ai->ai_family == AF_INET6; + + debug3("%s: addr %d %s: %s", __func__, + i, v6 ? "IPv6" : "IPv4", render_addr(ai->ai_addr)); + + /* If the address is not all-zero then copy as-is */ + if ((!v6 && E_S4(ai)->sin_addr.s_addr != 0) || + (v6 && IN6_IS_ADDR_UNSPECIFIED(E_S6(ai)))) { + *rl = xcalloc(1, sizeof(*ai)); + **rl = *ai; + (*rl)->ai_addr = xcalloc(1, ai->ai_addrlen); + memcpy((*rl)->ai_addr, ai->ai_addr, ai->ai_addrlen); + (*rl)->ai_canonname = NULL; + (*rl)->ai_next = NULL; + rl = &(*rl)->ai_next; + continue; + } + /* If we've already seen a wildcard of this family then skip */ + if (v6) { + if (have_wild6) + continue; + have_wild6 = 1; + } else { + if (have_wild4) + continue; + have_wild4 = 1; + } + debug3("%s: expanding address", __func__); + /* Append all interface addresses with matching family here */ + for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL || + ifa->ifa_addr->sa_family != ai->ai_family) + continue; + /* Append */ + *rl = xcalloc(1, sizeof(*ai)); + **rl = *ai; + (*rl)->ai_addr = xcalloc(1, ai->ai_addrlen); + memcpy((*rl)->ai_addr, ifa->ifa_addr, + (*rl)->ai_addrlen); + if (v6) + E_S6(*rl)->sin6_port = E_S6(ai)->sin6_port; + else + E_S4(*rl)->sin_port = E_S4(ai)->sin_port; + (*rl)->ai_canonname = NULL; + (*rl)->ai_next = NULL; + debug3("%s: copied address %s from interface %s", + __func__, render_addr((*rl)->ai_addr), + ifa->ifa_name); + rl = &(*rl)->ai_next; + } + } + debug3("%s: result:", __func__); + for (i = 0, ai = ret; ai; ai = ai->ai_next, i++) { + debug3("%s: addr %d: %s", + __func__, i, render_addr(ai->ai_addr)); + } + freeaddrinfo(addrlist); + return ret; +} + /* * Listen for TCP connections */ @@ -1082,6 +1192,10 @@ server_listen(void) int ret, listen_sock, on = 1; struct addrinfo *ai; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + u_int matched, i; + + if (options.num_listen_filters > 0) + options.listen_addrs = expand_local_addrs(options.listen_addrs); for (ai = options.listen_addrs; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) @@ -1094,6 +1208,18 @@ server_listen(void) NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { error("getnameinfo failed: %.100s", ssh_gai_strerror(ret)); + continue; + } + for (i = matched = 0; i < options.num_listen_filters; i++) { + if (addr_match_cidr_list(ntop, + options.listen_filter[i]) == 1) { + matched = 1; + break; + } + } + if (options.num_listen_filters > 0 && !matched) { + debug("%s: listen address [%s]:%s did not match filter", + __func__, ntop, strport); continue; } /* Create socket for listening. */ _______________________________________________ openssh-unix-dev mailing list openssh-unix-dev@xxxxxxxxxxx https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev