From 3071d85a47061c1bdaf11a0ac233b501ecba862c Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 17 Jul 2023 04:04:36 +0000 Subject: [PATCH] upstream: add a "match localnetwork" predicate. This allows matching on the addresses of available network interfaces and may be used to vary the effective client configuration based on network location (e.g. to use a ProxyJump when not on a particular network). ok markus@ OpenBSD-Commit-ID: cffb6ff9a3803abfc52b5cad0aa190c5e424c139 --- readconf.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++--- ssh_config.5 | 16 +++++++++-- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/readconf.c b/readconf.c index bb3bf767b..28f6acce3 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.377 2023/06/21 05:10:26 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.378 2023/07/17 04:04:36 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -28,6 +29,9 @@ #include #include #include +#ifdef HAVE_IFADDRS_H +# include +#endif #include #include #ifdef HAVE_PATHS_H @@ -576,6 +580,60 @@ execute_in_shell(const char *cmd) return WEXITSTATUS(status); } +/* + * Check whether a local network interface address appears in CIDR pattern- + * list 'addrlist'. Returns 1 if matched or 0 otherwise. + */ +static int +check_match_ifaddrs(const char *addrlist) +{ + struct ifaddrs *ifa, *ifaddrs = NULL; + int r, found = 0; + char addr[NI_MAXHOST]; + socklen_t salen; + + if (getifaddrs(&ifaddrs) != 0) { + error("match localnetwork: getifaddrs failed: %s", + strerror(errno)); + return 0; + } + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL || + (ifa->ifa_flags & IFF_UP) == 0) + continue; + switch (ifa->ifa_addr->sa_family) { + case AF_INET: + salen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + salen = sizeof(struct sockaddr_in6); + break; + case AF_LINK: + /* ignore */ + continue; + default: + debug2_f("interface %s: unsupported address family %d", + ifa->ifa_name, ifa->ifa_addr->sa_family); + continue; + } + if ((r = getnameinfo(ifa->ifa_addr, salen, addr, sizeof(addr), + NULL, 0, NI_NUMERICHOST)) != 0) { + debug2_f("interface %s getnameinfo failed: %s", + ifa->ifa_name, gai_strerror(r)); + continue; + } + debug3_f("interface %s addr %s", ifa->ifa_name, addr); + if (addr_match_cidr_list(addr, addrlist) == 1) { + debug3_f("matched interface %s: address %s in %s", + ifa->ifa_name, addr, addrlist); + found = 1; + break; + } + } + freeifaddrs(ifaddrs); + return found; +} + /* * Parse and execute a Match directive. */ @@ -680,6 +738,15 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw, r = match_pattern_list(pw->pw_name, arg, 0) == 1; if (r == (negate ? 1 : 0)) this_result = result = 0; + } else if (strcasecmp(attrib, "localnetwork") == 0) { + if (addr_match_cidr_list(NULL, arg) == -1) { + /* Error already printed */ + result = -1; + goto out; + } + r = check_match_ifaddrs(arg) == 1; + if (r == (negate ? 1 : 0)) + this_result = result = 0; } else if (strcasecmp(attrib, "exec") == 0) { char *conn_hash_hex, *keyalias; @@ -733,9 +800,11 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw, result = -1; goto out; } - debug3("%.200s line %d: %smatched '%s \"%.100s\"' ", - filename, linenum, this_result ? "": "not ", - oattrib, criteria); + debug3("%.200s line %d: %smatched '%s%s%.100s%s' ", + filename, linenum, this_result ? "": "not ", oattrib, + criteria == NULL ? "" : " \"", + criteria == NULL ? "" : criteria, + criteria == NULL ? "" : "\""); free(criteria); } if (attributes == 0) { diff --git a/ssh_config.5 b/ssh_config.5 index 0b7d4d192..3d18fb2a2 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.380 2023/03/27 03:56:11 dtucker Exp $ -.Dd $Mdocdate: March 27 2023 $ +.\" $OpenBSD: ssh_config.5,v 1.381 2023/07/17 04:04:36 djm Exp $ +.Dd $Mdocdate: July 17 2023 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -141,6 +141,7 @@ The available criteria keywords are: .Cm canonical , .Cm final , .Cm exec , +.Cm localnetwork , .Cm host , .Cm originalhost , .Cm user , @@ -195,6 +196,17 @@ accept the tokens described in the .Sx TOKENS section. .Pp +The +.Cm localnetwork +keyword matches the addresses of active local network interfaces against the +supplied list of networks in CIDR format. +This may be convenient for varying the effective configuration on devices that +roam between networks. +Note that network address is not a trustworthy criteria in many +situations (e.g. when the network is automatically configured using DHCP) +and so caution should be applied if using it to control security-sensitive +configuration. +.Pp The other keywords' criteria must be single entries or comma-separated lists and may use the wildcard and negation operators described in the .Sx PATTERNS