From c2ff6de80348ed0a3544eae49a6fb4f81ec30ed0 Mon Sep 17 00:00:00 2001 From: djm Date: Wed, 25 Sep 2024 01:24:04 +0000 Subject: [PATCH] fix regression introduced when I switched the "Match" criteria tokeniser to a more shell-like one. Apparently the old tokeniser (accidentally?) allowed "Match criteria=argument" as well as the "Match criteria argument" syntax that we tested for. People were using this syntax so this adds back support for "Match criteria=argument" bz3739 ok dtucker --- usr.bin/ssh/misc.c | 23 ++++++++++++++++- usr.bin/ssh/misc.h | 3 ++- usr.bin/ssh/readconf.c | 28 +++++++++++++++++---- usr.bin/ssh/servconf.c | 57 +++++++++++++++++++++++++++++++----------- 4 files changed, 89 insertions(+), 22 deletions(-) diff --git a/usr.bin/ssh/misc.c b/usr.bin/ssh/misc.c index 33327ad5c73..a7b5b95d17c 100644 --- a/usr.bin/ssh/misc.c +++ b/usr.bin/ssh/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.196 2024/06/06 17:15:25 djm Exp $ */ +/* $OpenBSD: misc.c,v 1.197 2024/09/25 01:24:04 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2005-2020 Damien Miller. All rights reserved. @@ -87,6 +87,27 @@ rtrim(char *s) } } +/* + * returns pointer to character after 'prefix' in 's' or otherwise NULL + * if the prefix is not present. + */ +const char * +strprefix(const char *s, const char *prefix, int ignorecase) +{ + size_t prefixlen; + + if ((prefixlen = strlen(prefix)) == 0) + return s; + if (ignorecase) { + if (strncasecmp(s, prefix, prefixlen) != 0) + return NULL; + } else { + if (strncmp(s, prefix, prefixlen) != 0) + return NULL; + } + return s + prefixlen; +} + /* set/unset filedescriptor to non-blocking */ int set_nonblock(int fd) diff --git a/usr.bin/ssh/misc.h b/usr.bin/ssh/misc.h index 7589d28e8f3..ee5d51d0ba1 100644 --- a/usr.bin/ssh/misc.h +++ b/usr.bin/ssh/misc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.h,v 1.109 2024/06/06 17:15:25 djm Exp $ */ +/* $OpenBSD: misc.h,v 1.110 2024/09/25 01:24:04 djm Exp $ */ /* * Author: Tatu Ylonen @@ -56,6 +56,7 @@ struct ForwardOptions { char *chop(char *); void rtrim(char *); void skip_space(char **); +const char *strprefix(const char *, const char *, int); char *strdelim(char **); char *strdelimw(char **); int set_nonblock(int); diff --git a/usr.bin/ssh/readconf.c b/usr.bin/ssh/readconf.c index 33bb91adbfe..3ba4355a91c 100644 --- a/usr.bin/ssh/readconf.c +++ b/usr.bin/ssh/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.390 2024/09/15 00:57:36 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.391 2024/09/25 01:24:04 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -687,7 +687,7 @@ match_cfg_line(Options *options, const char *full_line, int *acp, char ***avp, struct passwd *pw, const char *host_arg, const char *original_host, int final_pass, int *want_final_pass, const char *filename, int linenum) { - char *arg, *oattrib, *attrib, *cmd, *host, *criteria; + char *arg, *oattrib, *attrib = NULL, *cmd, *host, *criteria; const char *ruser; int r, this_result, result = 1, attributes = 0, negate; @@ -708,7 +708,8 @@ match_cfg_line(Options *options, const char *full_line, int *acp, char ***avp, debug2("checking match for '%s' host %s originally %s", full_line, host, original_host); - while ((oattrib = attrib = argv_next(acp, avp)) != NULL) { + while ((oattrib = argv_next(acp, avp)) != NULL) { + attrib = xstrdup(oattrib); /* Terminate on comment */ if (*attrib == '#') { argv_consume(acp); @@ -754,9 +755,23 @@ match_cfg_line(Options *options, const char *full_line, int *acp, char ***avp, this_result ? "" : "not ", oattrib); continue; } + + /* Keep this list in sync with below */ + if (strprefix(attrib, "host=", 1) != NULL || + strprefix(attrib, "originalhost=", 1) != NULL || + strprefix(attrib, "user=", 1) != NULL || + strprefix(attrib, "localuser=", 1) != NULL || + strprefix(attrib, "localnetwork=", 1) != NULL || + strprefix(attrib, "tagged=", 1) != NULL || + strprefix(attrib, "exec=", 1) != NULL) { + arg = strchr(attrib, '='); + *(arg++) = '\0'; + } else { + arg = argv_next(acp, avp); + } + /* All other criteria require an argument */ - if ((arg = argv_next(acp, avp)) == NULL || - *arg == '\0' || *arg == '#') { + if (arg == NULL || *arg == '\0' || *arg == '#') { error("Missing Match criteria for %s", attrib); result = -1; goto out; @@ -833,6 +848,8 @@ match_cfg_line(Options *options, const char *full_line, int *acp, char ***avp, criteria == NULL ? "" : criteria, criteria == NULL ? "" : "\""); free(criteria); + free(attrib); + attrib = NULL; } if (attributes == 0) { error("One or more attributes required for Match"); @@ -842,6 +859,7 @@ match_cfg_line(Options *options, const char *full_line, int *acp, char ***avp, out: if (result != -1) debug2("match %sfound", result ? "" : "not "); + free(attrib); free(host); return result; } diff --git a/usr.bin/ssh/servconf.c b/usr.bin/ssh/servconf.c index 347300e1263..cd57ca01112 100644 --- a/usr.bin/ssh/servconf.c +++ b/usr.bin/ssh/servconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.c,v 1.418 2024/09/15 03:09:44 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.419 2024/09/25 01:24:04 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -973,7 +973,7 @@ match_cfg_line(const char *full_line, int *acp, char ***avp, int line, struct connection_info *ci) { int result = 1, attributes = 0, port; - char *arg, *attrib; + char *arg, *attrib = NULL, *oattrib; if (ci == NULL) debug3("checking syntax for 'Match %s'", full_line); @@ -987,7 +987,8 @@ match_cfg_line(const char *full_line, int *acp, char ***avp, ci->laddress ? ci->laddress : "(null)", ci->lport); } - while ((attrib = argv_next(acp, avp)) != NULL) { + while ((oattrib = argv_next(acp, avp)) != NULL) { + attrib = xstrdup(oattrib); /* Terminate on comment */ if (*attrib == '#') { argv_consume(acp); /* mark all arguments consumed */ @@ -1002,11 +1003,13 @@ match_cfg_line(const char *full_line, int *acp, char ***avp, *arg != '\0' && *arg != '#')) { error("'all' cannot be combined with other " "Match attributes"); - return -1; + result = -1; + goto out; } if (arg != NULL && *arg == '#') argv_consume(acp); /* consume remaining args */ - return 1; + result = 1; + goto out; } /* Criterion "invalid-user" also has no argument */ if (strcasecmp(attrib, "invalid-user") == 0) { @@ -1018,11 +1021,26 @@ match_cfg_line(const char *full_line, int *acp, char ***avp, debug("matched invalid-user at line %d", line); continue; } + + /* Keep this list in sync with below */ + if (strprefix(attrib, "user=", 1) != NULL || + strprefix(attrib, "group=", 1) != NULL || + strprefix(attrib, "host=", 1) != NULL || + strprefix(attrib, "address=", 1) != NULL || + strprefix(attrib, "localaddress=", 1) != NULL || + strprefix(attrib, "localport=", 1) != NULL || + strprefix(attrib, "rdomain=", 1) != NULL) { + arg = strchr(attrib, '='); + *(arg++) = '\0'; + } else { + arg = argv_next(acp, avp); + } + /* All other criteria require an argument */ - if ((arg = argv_next(acp, avp)) == NULL || - *arg == '\0' || *arg == '#') { + if (arg == NULL || *arg == '\0' || *arg == '#') { error("Missing Match criteria for %s", attrib); - return -1; + result = -1; + goto out; } if (strcasecmp(attrib, "user") == 0) { if (ci == NULL || (ci->test && ci->user == NULL)) { @@ -1045,7 +1063,8 @@ match_cfg_line(const char *full_line, int *acp, char ***avp, match_test_missing_fatal("Group", "user"); switch (match_cfg_line_group(arg, line, ci->user)) { case -1: - return -1; + result = -1; + goto out; case 0: result = 0; } @@ -1081,7 +1100,8 @@ match_cfg_line(const char *full_line, int *acp, char ***avp, result = 0; break; case -2: - return -1; + result = -1; + goto out; } } else if (strcasecmp(attrib, "localaddress") == 0){ if (ci == NULL || (ci->test && ci->laddress == NULL)) { @@ -1106,13 +1126,15 @@ match_cfg_line(const char *full_line, int *acp, char ***avp, result = 0; break; case -2: - return -1; + result = -1; + goto out; } } else if (strcasecmp(attrib, "localport") == 0) { if ((port = a2port(arg)) == -1) { error("Invalid LocalPort '%s' on Match line", arg); - return -1; + result = -1; + goto out; } if (ci == NULL || (ci->test && ci->lport == -1)) { result = 0; @@ -1140,16 +1162,21 @@ match_cfg_line(const char *full_line, int *acp, char ***avp, debug("user %.100s matched 'RDomain %.100s' at " "line %d", ci->rdomain, arg, line); } else { - error("Unsupported Match attribute %s", attrib); - return -1; + error("Unsupported Match attribute %s", oattrib); + result = -1; + goto out; } + free(attrib); + attrib = NULL; } if (attributes == 0) { error("One or more attributes required for Match"); return -1; } - if (ci != NULL) + out: + if (ci != NULL && result != -1) debug3("match %sfound", result ? "" : "not "); + free(attrib); return result; } -- 2.20.1