From af0d252a2200310dba06e7d77aa6da4282a245e8 Mon Sep 17 00:00:00 2001 From: djm Date: Mon, 17 Jul 2023 04:04:36 +0000 Subject: [PATCH] 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@ --- usr.bin/ssh/readconf.c | 75 +++++++++++++++++++++++++++++++++++++--- usr.bin/ssh/ssh_config.5 | 16 +++++++-- 2 files changed, 85 insertions(+), 6 deletions(-) diff --git a/usr.bin/ssh/readconf.c b/usr.bin/ssh/readconf.c index de70683e558..9895524b585 100644 --- a/usr.bin/ssh/readconf.c +++ b/usr.bin/ssh/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 @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -562,6 +564,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. */ @@ -666,6 +722,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; @@ -719,9 +784,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/usr.bin/ssh/ssh_config.5 b/usr.bin/ssh/ssh_config.5 index 972cafee625..65ba886ec42 100644 --- a/usr.bin/ssh/ssh_config.5 +++ b/usr.bin/ssh/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 -- 2.20.1