summaryrefslogtreecommitdiff
path: root/readconf.c
diff options
context:
space:
mode:
Diffstat (limited to 'readconf.c')
-rw-r--r--readconf.c416
1 files changed, 266 insertions, 150 deletions
diff --git a/readconf.c b/readconf.c
index 3f9a37f6..842bbcd9 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.355 2021/06/08 07:02:46 dtucker Exp $ */
+/* $OpenBSD: readconf.c,v 1.356 2021/06/08 07:07:15 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -603,25 +603,33 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
debug2("checking match for '%s' host %s originally %s",
cp, host, original_host);
while ((oattrib = attrib = strdelim(&cp)) && *attrib != '\0') {
- criteria = NULL;
+ /* Terminate on comment */
+ if (*attrib == '#') {
+ cp = NULL; /* mark all arguments consumed */
+ break;
+ }
+ arg = criteria = NULL;
this_result = 1;
if ((negate = attrib[0] == '!'))
attrib++;
- /* criteria "all" and "canonical" have no argument */
+ /* Criterion "all" has no argument and must appear alone */
if (strcasecmp(attrib, "all") == 0) {
- if (attributes > 1 ||
- ((arg = strdelim(&cp)) != NULL && *arg != '\0')) {
+ if (attributes > 1 || ((arg = strdelim(&cp)) != NULL &&
+ *arg != '\0' && *arg != '#')) {
error("%.200s line %d: '%s' cannot be combined "
"with other Match attributes",
filename, linenum, oattrib);
result = -1;
goto out;
}
+ if (arg != NULL && *arg == '#')
+ cp = NULL; /* mark all arguments consumed */
if (result)
result = negate ? 0 : 1;
goto out;
}
attributes++;
+ /* criteria "final" and "canonical" have no argument */
if (strcasecmp(attrib, "canonical") == 0 ||
strcasecmp(attrib, "final") == 0) {
/*
@@ -640,7 +648,8 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
continue;
}
/* All other criteria require an argument */
- if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
+ if ((arg = strdelim(&cp)) == NULL ||
+ *arg == '\0' || *arg == '#') {
error("Missing Match criteria for %s", attrib);
result = -1;
goto out;
@@ -915,7 +924,7 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host,
const char *original_host, char *line, const char *filename,
int linenum, int *activep, int flags, int *want_final_pass, int depth)
{
- char *s, **charptr, *endofnumber, *keyword, *arg, *arg2, *p, ch;
+ char *str, **charptr, *endofnumber, *keyword, *arg, *arg2, *p, ch;
char **cpptr, ***cppptr, fwdarg[256];
u_int i, *uintptr, uvalue, max_entries = 0;
int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0;
@@ -929,6 +938,9 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host,
struct allowed_cname *cname;
glob_t gl;
const char *errstr;
+ char **oav = NULL, **av;
+ int oac = 0, ac;
+ int ret = -1;
if (activep == NULL) { /* We are processing a command line directive */
cmdline = 1;
@@ -944,46 +956,62 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host,
line[len] = '\0';
}
- s = line;
+ str = line;
/* Get the keyword. (Each line is supposed to begin with a keyword). */
- if ((keyword = strdelim(&s)) == NULL)
+ if ((keyword = strdelim(&str)) == NULL)
return 0;
/* Ignore leading whitespace. */
if (*keyword == '\0')
- keyword = strdelim(&s);
+ keyword = strdelim(&str);
if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#')
return 0;
/* Match lowercase keyword */
lowercase(keyword);
+ /* Prepare to parse remainder of line */
+ if (str != NULL)
+ str += strspn(str, WHITESPACE);
+ if (str == NULL || *str == '\0') {
+ error("%s line %d: no argument after keyword \"%s\"",
+ filename, linenum, keyword);
+ return -1;
+ }
opcode = parse_token(keyword, filename, linenum,
options->ignored_unknown);
+ if (argv_split(str, &oac, &oav, 1) != 0) {
+ error("%s line %d: invalid quotes", filename, linenum);
+ return -1;
+ }
+ ac = oac;
+ av = oav;
switch (opcode) {
case oBadOption:
/* don't panic, but count bad options */
- return -1;
+ goto out;
case oIgnore:
- return 0;
+ argv_consume(&ac);
+ break;
case oIgnoredUnknownOption:
debug("%s line %d: Ignored unknown option \"%s\"",
filename, linenum, keyword);
- return 0;
+ argv_consume(&ac);
+ break;
case oConnectTimeout:
intptr = &options->connection_timeout;
parse_time:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%s line %d: missing time value.",
filename, linenum);
- return -1;
+ goto out;
}
if (strcmp(arg, "none") == 0)
value = -1;
else if ((value = convtime(arg)) == -1) {
error("%s line %d: invalid time value.",
filename, linenum);
- return -1;
+ goto out;
}
if (*activep && *intptr == -1)
*intptr = value;
@@ -992,11 +1020,11 @@ parse_time:
case oForwardAgent:
intptr = &options->forward_agent;
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%s line %d: missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
value = -1;
@@ -1024,12 +1052,12 @@ parse_time:
parse_flag:
multistate_ptr = multistate_flag;
parse_multistate:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if ((value = parse_multistate_value(arg, filename, linenum,
multistate_ptr)) == -1) {
error("%s line %d: unsupported option \"%s\".",
filename, linenum, arg);
- return -1;
+ goto out;
}
if (*activep && *intptr == -1)
*intptr = value;
@@ -1119,11 +1147,11 @@ parse_time:
goto parse_int;
case oRekeyLimit:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.", filename,
linenum);
- return -1;
+ goto out;
}
if (strcmp(arg, "default") == 0) {
val64 = 0;
@@ -1131,19 +1159,19 @@ parse_time:
if (scan_scaled(arg, &val64) == -1) {
error("%.200s line %d: Bad number '%s': %s",
filename, linenum, arg, strerror(errno));
- return -1;
+ goto out;
}
if (val64 != 0 && val64 < 16) {
error("%.200s line %d: RekeyLimit too small",
filename, linenum);
- return -1;
+ goto out;
}
}
if (*activep && options->rekey_limit == -1)
options->rekey_limit = val64;
- if (s != NULL) { /* optional rekey interval present */
- if (strcmp(s, "none") == 0) {
- (void)strdelim(&s); /* discard */
+ if (ac != 0) { /* optional rekey interval present */
+ if (strcmp(av[0], "none") == 0) {
+ (void)argv_next(&ac, &av); /* discard */
break;
}
intptr = &options->rekey_interval;
@@ -1152,11 +1180,11 @@ parse_time:
break;
case oIdentityFile:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
if (*activep) {
intptr = &options->num_identity_files;
@@ -1164,7 +1192,7 @@ parse_time:
error("%.200s line %d: Too many identity files "
"specified (max %d).", filename, linenum,
SSH_MAX_IDENTITY_FILES);
- return -1;
+ goto out;
}
add_identity_file(options, NULL,
arg, flags & SSHCONF_USERCONF);
@@ -1172,11 +1200,11 @@ parse_time:
break;
case oCertificateFile:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
if (*activep) {
intptr = &options->num_certificate_files;
@@ -1185,7 +1213,7 @@ parse_time:
"files specified (max %d).",
filename, linenum,
SSH_MAX_CERTIFICATE_FILES);
- return -1;
+ goto out;
}
add_certificate_file(options, arg,
flags & SSHCONF_USERCONF);
@@ -1199,11 +1227,11 @@ parse_time:
case oUser:
charptr = &options->user;
parse_string:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
if (*activep && *charptr == NULL)
*charptr = xstrdup(arg);
@@ -1214,17 +1242,34 @@ parse_string:
uintptr = &options->num_system_hostfiles;
max_entries = SSH_MAX_HOSTS_FILES;
parse_char_array:
- if (*activep && *uintptr == 0) {
- while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
+ i = 0;
+ while ((arg = argv_next(&ac, &av)) != NULL) {
+ if (*arg == '\0') {
+ error("%s line %d: keyword %s empty argument",
+ filename, linenum, keyword);
+ goto out;
+ }
+ /* Allow "none" only in first position */
+ if (strcasecmp(arg, "none") == 0) {
+ if (i > 0 || ac > 0) {
+ error("%s line %d: keyword %s \"none\" "
+ "argument must appear alone.",
+ filename, linenum, keyword);
+ goto out;
+ }
+ }
+ i++;
+ if (*activep && *uintptr == 0) {
if ((*uintptr) >= max_entries) {
- error("%s line %d: too many known "
- "hosts files.", filename, linenum);
- return -1;
+ error("%s line %d: too many %s "
+ "entries.", filename, linenum,
+ keyword);
+ goto out;
}
cpptr[(*uintptr)++] = xstrdup(arg);
}
}
- return 0;
+ break;
case oUserKnownHostsFile:
cpptr = (char **)&options->user_hostfiles;
@@ -1270,42 +1315,45 @@ parse_char_array:
if (options->jump_host != NULL)
charptr = &options->jump_host; /* Skip below */
parse_command:
- if (s == NULL) {
+ if (str == NULL) {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
- len = strspn(s, WHITESPACE "=");
+ len = strspn(str, WHITESPACE "=");
if (*activep && *charptr == NULL)
- *charptr = xstrdup(s + len);
- return 0;
+ *charptr = xstrdup(str + len);
+ argv_consume(&ac);
+ break;
case oProxyJump:
- if (s == NULL) {
+ if (str == NULL) {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
- len = strspn(s, WHITESPACE "=");
- if (parse_jump(s + len, options, *activep) == -1) {
+ len = strspn(str, WHITESPACE "=");
+ /* XXX use argv? */
+ if (parse_jump(str + len, options, *activep) == -1) {
error("%.200s line %d: Invalid ProxyJump \"%s\"",
- filename, linenum, s + len);
- return -1;
+ filename, linenum, str + len);
+ goto out;
}
- return 0;
+ argv_consume(&ac);
+ break;
case oPort:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
value = a2port(arg);
if (value <= 0) {
error("%.200s line %d: Bad port '%s'.",
filename, linenum, arg);
- return -1;
+ goto out;
}
if (*activep && options->port == -1)
options->port = value;
@@ -1314,63 +1362,63 @@ parse_command:
case oConnectionAttempts:
intptr = &options->connection_attempts;
parse_int:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if ((errstr = atoi_err(arg, &value)) != NULL) {
error("%s line %d: integer value %s.",
filename, linenum, errstr);
- return -1;
+ goto out;
}
if (*activep && *intptr == -1)
*intptr = value;
break;
case oCiphers:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
if (*arg != '-' &&
!ciphers_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)){
error("%.200s line %d: Bad SSH2 cipher spec '%s'.",
filename, linenum, arg ? arg : "<NONE>");
- return -1;
+ goto out;
}
if (*activep && options->ciphers == NULL)
options->ciphers = xstrdup(arg);
break;
case oMacs:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
if (*arg != '-' &&
!mac_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) {
error("%.200s line %d: Bad SSH2 MAC spec '%s'.",
filename, linenum, arg ? arg : "<NONE>");
- return -1;
+ goto out;
}
if (*activep && options->macs == NULL)
options->macs = xstrdup(arg);
break;
case oKexAlgorithms:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
if (*arg != '-' &&
!kex_names_valid(*arg == '+' || *arg == '^' ?
arg + 1 : arg)) {
error("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.",
filename, linenum, arg ? arg : "<NONE>");
- return -1;
+ goto out;
}
if (*activep && options->kex_algorithms == NULL)
options->kex_algorithms = xstrdup(arg);
@@ -1379,18 +1427,18 @@ parse_int:
case oHostKeyAlgorithms:
charptr = &options->hostkeyalgorithms;
parse_pubkey_algos:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
if (*arg != '-' &&
!sshkey_names_valid2(*arg == '+' || *arg == '^' ?
arg + 1 : arg, 1)) {
error("%s line %d: Bad key types '%s'.",
filename, linenum, arg ? arg : "<NONE>");
- return -1;
+ goto out;
}
if (*activep && *charptr == NULL)
*charptr = xstrdup(arg);
@@ -1402,12 +1450,12 @@ parse_pubkey_algos:
case oLogLevel:
log_level_ptr = &options->log_level;
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
value = log_level_number(arg);
if (value == SYSLOG_LEVEL_NOT_SET) {
error("%.200s line %d: unsupported log level '%s'",
filename, linenum, arg ? arg : "<NONE>");
- return -1;
+ goto out;
}
if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET)
*log_level_ptr = (LogLevel) value;
@@ -1415,12 +1463,12 @@ parse_pubkey_algos:
case oLogFacility:
log_facility_ptr = &options->log_facility;
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
value = log_facility_number(arg);
if (value == SYSLOG_FACILITY_NOT_SET) {
error("%.200s line %d: unsupported log facility '%s'",
filename, linenum, arg ? arg : "<NONE>");
- return -1;
+ goto out;
}
if (*log_facility_ptr == -1)
*log_facility_ptr = (SyslogFacility) value;
@@ -1429,37 +1477,53 @@ parse_pubkey_algos:
case oLogVerbose:
cppptr = &options->log_verbose;
uintptr = &options->num_log_verbose;
- if (*activep && *uintptr == 0) {
- while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
+ i = 0;
+ while ((arg = argv_next(&ac, &av)) != NULL) {
+ if (*arg == '\0') {
+ error("%s line %d: keyword %s empty argument",
+ filename, linenum, keyword);
+ goto out;
+ }
+ /* Allow "none" only in first position */
+ if (strcasecmp(arg, "none") == 0) {
+ if (i > 0 || ac > 0) {
+ error("%s line %d: keyword %s \"none\" "
+ "argument must appear alone.",
+ filename, linenum, keyword);
+ goto out;
+ }
+ }
+ i++;
+ if (*activep && *uintptr == 0) {
*cppptr = xrecallocarray(*cppptr, *uintptr,
*uintptr + 1, sizeof(**cppptr));
(*cppptr)[(*uintptr)++] = xstrdup(arg);
}
}
- return 0;
+ break;
case oLocalForward:
case oRemoteForward:
case oDynamicForward:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
remotefwd = (opcode == oRemoteForward);
dynamicfwd = (opcode == oDynamicForward);
if (!dynamicfwd) {
- arg2 = strdelim(&s);
+ arg2 = argv_next(&ac, &av);
if (arg2 == NULL || *arg2 == '\0') {
if (remotefwd)
dynamicfwd = 1;
else {
error("%.200s line %d: Missing target "
"argument.", filename, linenum);
- return -1;
+ goto out;
}
} else {
/* construct a string for parse_forward */
@@ -1473,7 +1537,7 @@ parse_pubkey_algos:
if (parse_forward(&fwd, fwdarg, dynamicfwd, remotefwd) == 0) {
error("%.200s line %d: Bad forwarding specification.",
filename, linenum);
- return -1;
+ goto out;
}
if (*activep) {
@@ -1488,7 +1552,7 @@ parse_pubkey_algos:
case oPermitRemoteOpen:
uintptr = &options->num_permitted_remote_opens;
cppptr = &options->permitted_remote_opens;
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0')
fatal("%s line %d: missing %s specification",
filename, linenum, lookup_opcode_name(opcode));
@@ -1501,7 +1565,7 @@ parse_pubkey_algos:
}
break;
}
- for (; arg != NULL && *arg != '\0'; arg = strdelim(&s)) {
+ while ((arg = argv_next(&ac, &av)) != NULL) {
arg2 = xstrdup(arg);
ch = '\0';
p = hpdelim2(&arg, &ch);
@@ -1538,13 +1602,20 @@ parse_pubkey_algos:
if (cmdline) {
error("Host directive not supported as a command-line "
"option");
- return -1;
+ goto out;
}
*activep = 0;
arg2 = NULL;
- while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
- if ((flags & SSHCONF_NEVERMATCH) != 0)
+ while ((arg = argv_next(&ac, &av)) != NULL) {
+ if (*arg == '\0') {
+ error("%s line %d: keyword %s empty argument",
+ filename, linenum, keyword);
+ goto out;
+ }
+ if ((flags & SSHCONF_NEVERMATCH) != 0) {
+ argv_consume(&ac);
break;
+ }
negated = *arg == '!';
if (negated)
arg++;
@@ -1555,6 +1626,7 @@ parse_pubkey_algos:
"for %.100s", filename, linenum,
arg);
*activep = 0;
+ argv_consume(&ac);
break;
}
if (!*activep)
@@ -1565,33 +1637,39 @@ parse_pubkey_algos:
if (*activep)
debug("%.200s line %d: Applying options for %.100s",
filename, linenum, arg2);
- /* Avoid garbage check below, as strdelim is done. */
- return 0;
+ break;
case oMatch:
if (cmdline) {
error("Host directive not supported as a command-line "
"option");
- return -1;
+ goto out;
}
- value = match_cfg_line(options, &s, pw, host, original_host,
+ value = match_cfg_line(options, &str, pw, host, original_host,
flags & SSHCONF_FINAL, want_final_pass,
filename, linenum);
if (value < 0) {
error("%.200s line %d: Bad Match condition", filename,
linenum);
- return -1;
+ goto out;
}
*activep = (flags & SSHCONF_NEVERMATCH) ? 0 : value;
+ /*
+ * If match_cfg_line() didn't consume all its arguments then
+ * arrange for the extra arguments check below to fail.
+ */
+
+ if (str == NULL || *str == '\0')
+ argv_consume(&ac);
break;
case oEscapeChar:
intptr = &options->escape_char;
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
if (strcmp(arg, "none") == 0)
value = SSH_ESCAPECHAR_NONE;
@@ -1603,7 +1681,7 @@ parse_pubkey_algos:
else {
error("%.200s line %d: Bad escape character.",
filename, linenum);
- return -1;
+ goto out;
}
if (*activep && *intptr == -1)
*intptr = value;
@@ -1631,11 +1709,11 @@ parse_pubkey_algos:
goto parse_int;
case oSendEnv:
- while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
- if (strchr(arg, '=') != NULL) {
+ while ((arg = argv_next(&ac, &av)) != NULL) {
+ if (*arg == '\0' || strchr(arg, '=') != NULL) {
error("%s line %d: Invalid environment name.",
filename, linenum);
- return -1;
+ goto out;
}
if (!*activep)
continue;
@@ -1648,7 +1726,7 @@ parse_pubkey_algos:
if (options->num_send_env >= INT_MAX) {
error("%s line %d: too many send env.",
filename, linenum);
- return -1;
+ goto out;
}
options->send_env = xrecallocarray(
options->send_env, options->num_send_env,
@@ -1662,11 +1740,11 @@ parse_pubkey_algos:
case oSetEnv:
value = options->num_setenv;
- while ((arg = strdelimw(&s)) != NULL && *arg != '\0') {
+ while ((arg = argv_next(&ac, &av)) != NULL) {
if (strchr(arg, '=') == NULL) {
error("%s line %d: Invalid SetEnv.",
filename, linenum);
- return -1;
+ goto out;
}
if (!*activep || value != 0)
continue;
@@ -1674,7 +1752,7 @@ parse_pubkey_algos:
if (options->num_setenv >= INT_MAX) {
error("%s line %d: too many SetEnv.",
filename, linenum);
- return -1;
+ goto out;
}
options->setenv = xrecallocarray(
options->setenv, options->num_setenv,
@@ -1695,11 +1773,11 @@ parse_pubkey_algos:
case oControlPersist:
/* no/false/yes/true, or a time spec */
intptr = &options->control_persist;
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing ControlPersist"
" argument.", filename, linenum);
- return -1;
+ goto out;
}
value = 0;
value2 = 0; /* timeout */
@@ -1712,7 +1790,7 @@ parse_pubkey_algos:
else {
error("%.200s line %d: Bad ControlPersist argument.",
filename, linenum);
- return -1;
+ goto out;
}
if (*activep && *intptr == -1) {
*intptr = value;
@@ -1730,17 +1808,17 @@ parse_pubkey_algos:
goto parse_multistate;
case oTunnelDevice:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
value = a2tun(arg, &value2);
if (value == SSH_TUNID_ERR) {
error("%.200s line %d: Bad tun device.",
filename, linenum);
- return -1;
+ goto out;
}
if (*activep && options->tun_local == -1) {
options->tun_local = value;
@@ -1768,10 +1846,15 @@ parse_pubkey_algos:
if (cmdline) {
error("Include directive not supported as a "
"command-line option");
- return -1;
+ goto out;
}
value = 0;
- while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
+ while ((arg = argv_next(&ac, &av)) != NULL) {
+ if (*arg == '\0') {
+ error("%s line %d: keyword %s empty argument",
+ filename, linenum, keyword);
+ goto out;
+ }
/*
* Ensure all paths are anchored. User configuration
* files may begin with '~/' but system configurations
@@ -1782,7 +1865,7 @@ parse_pubkey_algos:
if (*arg == '~' && (flags & SSHCONF_USERCONF) == 0) {
error("%.200s line %d: bad include path %s.",
filename, linenum, arg);
- return -1;
+ goto out;
}
if (!path_absolute(arg) && *arg != '~') {
xasprintf(&arg2, "%s/%s",
@@ -1800,7 +1883,7 @@ parse_pubkey_algos:
} else if (r != 0) {
error("%.200s line %d: glob failed for %s.",
filename, linenum, arg2);
- return -1;
+ goto out;
}
free(arg2);
oactive = *activep;
@@ -1819,7 +1902,7 @@ parse_pubkey_algos:
"%.100s: %.100s", gl.gl_pathv[i],
strerror(errno));
globfree(&gl);
- return -1;
+ goto out;
}
/*
* don't let Match in includes clobber the
@@ -1832,23 +1915,23 @@ parse_pubkey_algos:
globfree(&gl);
}
if (value != 0)
- return value;
+ ret = value;
break;
case oIPQoS:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if ((value = parse_ipqos(arg)) == -1) {
error("%s line %d: Bad IPQoS value: %s",
filename, linenum, arg);
- return -1;
+ goto out;
}
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (arg == NULL)
value2 = value;
else if ((value2 = parse_ipqos(arg)) == -1) {
error("%s line %d: Bad IPQoS value: %s",
filename, linenum, arg);
- return -1;
+ goto out;
}
if (*activep && options->ip_qos_interactive == -1) {
options->ip_qos_interactive = value;
@@ -1871,11 +1954,27 @@ parse_pubkey_algos:
case oCanonicalDomains:
value = options->num_canonical_domains != 0;
- while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
+ i = 0;
+ while ((arg = argv_next(&ac, &av)) != NULL) {
+ if (*arg == '\0') {
+ error("%s line %d: keyword %s empty argument",
+ filename, linenum, keyword);
+ goto out;
+ }
+ /* Allow "none" only in first position */
+ if (strcasecmp(arg, "none") == 0) {
+ if (i > 0 || ac > 0) {
+ error("%s line %d: keyword %s \"none\" "
+ "argument must appear alone.",
+ filename, linenum, keyword);
+ goto out;
+ }
+ }
+ i++;
if (!valid_domain(arg, 1, &errstr)) {
error("%s line %d: %s", filename, linenum,
errstr);
- return -1;
+ goto out;
}
if (!*activep || value)
continue;
@@ -1883,7 +1982,7 @@ parse_pubkey_algos:
MAX_CANON_DOMAINS) {
error("%s line %d: too many hostname suffixes.",
filename, linenum);
- return -1;
+ goto out;
}
options->canonical_domains[
options->num_canonical_domains++] = xstrdup(arg);
@@ -1892,7 +1991,7 @@ parse_pubkey_algos:
case oCanonicalizePermittedCNAMEs:
value = options->num_permitted_cnames != 0;
- while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
+ while ((arg = argv_next(&ac, &av)) != NULL) {
/* Either '*' for everything or 'list:list' */
if (strcmp(arg, "*") == 0)
arg2 = arg;
@@ -1903,7 +2002,7 @@ parse_pubkey_algos:
error("%s line %d: "
"Invalid permitted CNAME \"%s\"",
filename, linenum, arg);
- return -1;
+ goto out;
}
*arg2 = '\0';
arg2++;
@@ -1914,7 +2013,7 @@ parse_pubkey_algos:
MAX_CANON_DOMAINS) {
error("%s line %d: too many permitted CNAMEs.",
filename, linenum);
- return -1;
+ goto out;
}
cname = options->permitted_cnames +
options->num_permitted_cnames++;
@@ -1937,17 +2036,17 @@ parse_pubkey_algos:
goto parse_flag;
case oStreamLocalBindMask:
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing StreamLocalBindMask "
"argument.", filename, linenum);
- return -1;
+ goto out;
}
/* Parse mode in octal format */
value = strtol(arg, &endofnumber, 8);
if (arg == endofnumber || value < 0 || value > 0777) {
error("%.200s line %d: Bad mask.", filename, linenum);
- return -1;
+ goto out;
}
options->fwd_opts.streamlocal_bind_mask = (mode_t)value;
break;
@@ -1962,16 +2061,16 @@ parse_pubkey_algos:
case oFingerprintHash:
intptr = &options->fingerprint_hash;
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
if ((value = ssh_digest_alg_by_name(arg)) == -1) {
error("%.200s line %d: Invalid hash algorithm \"%s\".",
filename, linenum, arg);
- return -1;
+ goto out;
}
if (*activep && *intptr == -1)
*intptr = value;
@@ -1991,8 +2090,8 @@ parse_pubkey_algos:
goto parse_pubkey_algos;
case oAddKeysToAgent:
- arg = strdelim(&s);
- arg2 = strdelim(&s);
+ arg = argv_next(&ac, &av);
+ arg2 = argv_next(&ac, &av);
value = parse_multistate_value(arg, filename, linenum,
multistate_yesnoaskconfirm);
value2 = 0; /* unlimited lifespan by default */
@@ -2002,20 +2101,20 @@ parse_pubkey_algos:
value2 > INT_MAX) {
error("%s line %d: invalid time value.",
filename, linenum);
- return -1;
+ goto out;
}
} else if (value == -1 && arg2 == NULL) {
if ((value2 = convtime(arg)) == -1 ||
value2 > INT_MAX) {
error("%s line %d: unsupported option",
filename, linenum);
- return -1;
+ goto out;
}
value = 1; /* yes */
} else if (value == -1 || arg2 != NULL) {
error("%s line %d: unsupported option",
filename, linenum);
- return -1;
+ goto out;
}
if (*activep && options->add_keys_to_agent == -1) {
options->add_keys_to_agent = value;
@@ -2025,18 +2124,18 @@ parse_pubkey_algos:
case oIdentityAgent:
charptr = &options->identity_agent;
- arg = strdelim(&s);
+ arg = argv_next(&ac, &av);
if (!arg || *arg == '\0') {
error("%.200s line %d: Missing argument.",
filename, linenum);
- return -1;
+ goto out;
}
parse_agent_path:
/* Extra validation if the string represents an env var. */
if ((arg2 = dollar_expand(&r, arg)) == NULL || r) {
error("%.200s line %d: Invalid environment expansion "
"%s.", filename, linenum, arg);
- return -1;
+ goto out;
}
free(arg2);
/* check for legacy environment format */
@@ -2044,7 +2143,7 @@ parse_pubkey_algos:
!valid_env_name(arg + 1)) {
error("%.200s line %d: Invalid environment name %s.",
filename, linenum, arg);
- return -1;
+ goto out;
}
if (*activep && *charptr == NULL)
*charptr = xstrdup(arg);
@@ -2053,25 +2152,33 @@ parse_pubkey_algos:
case oDeprecated:
debug("%s line %d: Deprecated option \"%s\"",
filename, linenum, keyword);
- return 0;
+ argv_consume(&ac);
+ break;
case oUnsupported:
error("%s line %d: Unsupported option \"%s\"",
filename, linenum, keyword);
- return 0;
+ argv_consume(&ac);
+ break;
default:
error("%s line %d: Unimplemented opcode %d",
filename, linenum, opcode);
+ goto out;
}
/* Check that there is no garbage at end of line. */
- if ((arg = strdelim(&s)) != NULL && *arg != '\0') {
- error("%.200s line %d: garbage at end of line; \"%.200s\".",
- filename, linenum, arg);
- return -1;
+ if (ac > 0) {
+ error("%.200s line %d: keyword %s extra arguments "
+ "at end of line", filename, linenum, keyword);
+ goto out;
}
- return 0;
+
+ /* success */
+ ret = 0;
+ out:
+ argv_free(oav, oac);
+ return ret;
}
/*
@@ -2097,7 +2204,7 @@ read_config_file_depth(const char *filename, struct passwd *pw,
int flags, int *activep, int *want_final_pass, int depth)
{
FILE *f;
- char *cp, *line = NULL;
+ char *line = NULL;
size_t linesize = 0;
int linenum;
int bad_options = 0;
@@ -2133,8 +2240,6 @@ read_config_file_depth(const char *filename, struct passwd *pw,
* NB - preserve newlines, they are needed to reproduce
* line numbers later for error messages.
*/
- if ((cp = strchr(line, '#')) != NULL)
- *cp = '\0';
if (process_config_line_depth(options, pw, host, original_host,
line, filename, linenum, activep, flags, want_final_pass,
depth) != 0)
@@ -2812,7 +2917,10 @@ parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remo
if (fwd->connect_host != NULL &&
strlen(fwd->connect_host) >= NI_MAXHOST)
goto fail_free;
- /* XXX - if connecting to a remote socket, max sun len may not match this host */
+ /*
+ * XXX - if connecting to a remote socket, max sun len may not
+ * match this host
+ */
if (fwd->connect_path != NULL &&
strlen(fwd->connect_path) >= PATH_MAX_SUN)
goto fail_free;
@@ -2847,6 +2955,12 @@ parse_jump(const char *s, Options *o, int active)
active &= o->proxy_command == NULL && o->jump_host == NULL;
orig = sdup = xstrdup(s);
+
+ /* Remove comment and trailing whitespace */
+ if ((cp = strchr(orig, '#')) != NULL)
+ *cp = '\0';
+ rtrim(orig);
+
first = active;
do {
if (strcasecmp(s, "none") == 0)
@@ -3019,6 +3133,8 @@ dump_cfg_strarray_oneline(OpCodes code, u_int count, char **vals)
u_int i;
printf("%s", lookup_opcode_name(code));
+ if (count == 0)
+ printf(" none");
for (i = 0; i < count; i++)
printf(" %s", vals[i]);
printf("\n");