Bugzilla – Attachment 3528 Details for
Bug 3288
Ignoring comments at end of config file lines broke ProxyCommand with #-sign in script
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Use a better tokeniser for ssh/sshd_config parsing
bz3288.diff (text/plain), 58.64 KB, created by
Damien Miller
on 2021-06-04 13:52:47 AEST
(
hide
)
Description:
Use a better tokeniser for ssh/sshd_config parsing
Filename:
MIME Type:
Creator:
Damien Miller
Created:
2021-06-04 13:52:47 AEST
Size:
58.64 KB
patch
obsolete
>diff --git a/auth2-pubkey.c b/auth2-pubkey.c >index b77d353..e16dc89 100644 >--- a/auth2-pubkey.c >+++ b/auth2-pubkey.c >@@ -472,7 +472,8 @@ match_principals_command(struct ssh *ssh, struct passwd *user_pw, > } > > /* Turn the command into an argument vector */ >- if (argv_split(options.authorized_principals_command, &ac, &av) != 0) { >+ if (argv_split(options.authorized_principals_command, >+ &ac, &av, 0) != 0) { > error("AuthorizedPrincipalsCommand \"%s\" contains " > "invalid quotes", options.authorized_principals_command); > goto out; >@@ -923,7 +924,7 @@ user_key_command_allowed2(struct ssh *ssh, struct passwd *user_pw, > } > > /* Turn the command into an argument vector */ >- if (argv_split(options.authorized_keys_command, &ac, &av) != 0) { >+ if (argv_split(options.authorized_keys_command, &ac, &av, 0) != 0) { > error("AuthorizedKeysCommand \"%s\" contains invalid quotes", > options.authorized_keys_command); > goto out; >diff --git a/misc.c b/misc.c >index 2f54d63..8e298fe 100644 >--- a/misc.c >+++ b/misc.c >@@ -1823,14 +1823,13 @@ daemonized(void) > return 1; > } > >- > /* > * Splits 's' into an argument vector. Handles quoted string and basic > * escape characters (\\, \", \'). Caller must free the argument vector > * and its members. > */ > int >-argv_split(const char *s, int *argcp, char ***argvp) >+argv_split(const char *s, int *argcp, char ***argvp, int terminate_on_comment) > { > int r = SSH_ERR_INTERNAL_ERROR; > int argc = 0, quote, i, j; >@@ -1843,7 +1842,8 @@ argv_split(const char *s, int *argcp, char ***argvp) > /* Skip leading whitespace */ > if (s[i] == ' ' || s[i] == '\t') > continue; >- >+ if (terminate_on_comment && s[i] == '#') >+ break; > /* Start of a token */ > quote = 0; > >@@ -1856,7 +1856,8 @@ argv_split(const char *s, int *argcp, char ***argvp) > if (s[i] == '\\') { > if (s[i + 1] == '\'' || > s[i + 1] == '\"' || >- s[i + 1] == '\\') { >+ s[i + 1] == '\\' || >+ (quote == 0 && s[i + 1] == ' ')) { > i++; /* Skip '\' */ > arg[j++] = s[i]; > } else { >diff --git a/misc.h b/misc.h >index 70cd778..4a2fedd 100644 >--- a/misc.h >+++ b/misc.h >@@ -174,7 +174,7 @@ void mktemp_proto(char *, size_t); > void child_set_env(char ***envp, u_int *envsizep, const char *name, > const char *value); > >-int argv_split(const char *, int *, char ***); >+int argv_split(const char *, int *, char ***, int); > char *argv_assemble(int, char **argv); > int exited_cleanly(pid_t, const char *, const char *, int); > >diff --git a/readconf.c b/readconf.c >index 448785b..0b51539 100644 >--- a/readconf.c >+++ b/readconf.c >@@ -882,6 +882,24 @@ parse_multistate_value(const char *arg, const char *filename, int linenum, > return -1; > } > >+static char * >+next_arg(int *argcp, char ***argvp) >+{ >+ char *ret = (*argvp)[0]; >+ >+ if (*argcp > 0 && ret != NULL) { >+ (*argcp)--; >+ (*argvp)++; >+ } >+ return ret; >+} >+ >+static void >+consume_args(int *argcp) >+{ >+ *argcp = 0; >+} >+ > /* > * Processes a single option line as used in the configuration files. This > * only sets those values that have not already been set. >@@ -901,7 +919,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; >@@ -915,6 +933,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; >@@ -930,46 +951,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; >+ consume_args(&ac); >+ break; > case oIgnoredUnknownOption: > debug("%s line %d: Ignored unknown option \"%s\"", > filename, linenum, keyword); >- return 0; >+ consume_args(&ac); >+ break; > case oConnectTimeout: > intptr = &options->connection_timeout; > parse_time: >- arg = strdelim(&s); >+ arg = next_arg(&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; >@@ -978,11 +1015,11 @@ parse_time: > case oForwardAgent: > intptr = &options->forward_agent; > >- arg = strdelim(&s); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') { > error("%s line %d: missing argument.", > filename, linenum); >- return -1; >+ goto out; > } > > value = -1; >@@ -1010,12 +1047,12 @@ parse_time: > parse_flag: > multistate_ptr = multistate_flag; > parse_multistate: >- arg = strdelim(&s); >+ arg = next_arg(&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; >@@ -1105,11 +1142,11 @@ parse_time: > goto parse_int; > > case oRekeyLimit: >- arg = strdelim(&s); >+ arg = next_arg(&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; >@@ -1117,19 +1154,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)next_arg(&ac, &av); /* discard */ > break; > } > intptr = &options->rekey_interval; >@@ -1138,11 +1175,11 @@ parse_time: > break; > > case oIdentityFile: >- arg = strdelim(&s); >+ arg = next_arg(&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; >@@ -1150,7 +1187,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); >@@ -1158,11 +1195,11 @@ parse_time: > break; > > case oCertificateFile: >- arg = strdelim(&s); >+ arg = next_arg(&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; >@@ -1171,7 +1208,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); >@@ -1185,11 +1222,11 @@ parse_time: > case oUser: > charptr = &options->user; > parse_string: >- arg = strdelim(&s); >+ arg = next_arg(&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); >@@ -1200,17 +1237,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 = next_arg(&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; >@@ -1256,42 +1310,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); >+ consume_args(&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; >+ consume_args(&ac); >+ break; > > case oPort: >- arg = strdelim(&s); >+ arg = next_arg(&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; >@@ -1300,63 +1357,63 @@ parse_command: > case oConnectionAttempts: > intptr = &options->connection_attempts; > parse_int: >- arg = strdelim(&s); >+ arg = next_arg(&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 = next_arg(&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 = next_arg(&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 = next_arg(&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); >@@ -1365,18 +1422,18 @@ parse_int: > case oHostKeyAlgorithms: > charptr = &options->hostkeyalgorithms; > parse_pubkey_algos: >- arg = strdelim(&s); >+ arg = next_arg(&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); >@@ -1388,12 +1445,12 @@ parse_pubkey_algos: > > case oLogLevel: > log_level_ptr = &options->log_level; >- arg = strdelim(&s); >+ arg = next_arg(&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; >@@ -1401,12 +1458,12 @@ parse_pubkey_algos: > > case oLogFacility: > log_facility_ptr = &options->log_facility; >- arg = strdelim(&s); >+ arg = next_arg(&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; >@@ -1415,37 +1472,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 = next_arg(&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 = next_arg(&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 = next_arg(&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 */ >@@ -1459,7 +1532,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) { >@@ -1474,7 +1547,7 @@ parse_pubkey_algos: > case oPermitRemoteOpen: > uintptr = &options->num_permitted_remote_opens; > cppptr = &options->permitted_remote_opens; >- arg = strdelim(&s); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing %s specification", > filename, linenum, lookup_opcode_name(opcode)); >@@ -1487,7 +1560,7 @@ parse_pubkey_algos: > } > break; > } >- for (; arg != NULL && *arg != '\0'; arg = strdelim(&s)) { >+ while ((arg = next_arg(&ac, &av)) != NULL) { > arg2 = xstrdup(arg); > ch = '\0'; > p = hpdelim2(&arg, &ch); >@@ -1524,11 +1597,16 @@ 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') { >+ while ((arg = next_arg(&ac, &av)) != NULL) { >+ if (*arg == '\0') { >+ error("%s line %d: keyword %s empty argument", >+ filename, linenum, keyword); >+ goto out; >+ } > if ((flags & SSHCONF_NEVERMATCH) != 0) > break; > negated = *arg == '!'; >@@ -1551,33 +1629,33 @@ 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; >+ consume_args(&ac); > break; > > case oEscapeChar: > intptr = &options->escape_char; >- arg = strdelim(&s); >+ arg = next_arg(&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; >@@ -1589,7 +1667,7 @@ parse_pubkey_algos: > else { > error("%.200s line %d: Bad escape character.", > filename, linenum); >- return -1; >+ goto out; > } > if (*activep && *intptr == -1) > *intptr = value; >@@ -1617,11 +1695,11 @@ parse_pubkey_algos: > goto parse_int; > > case oSendEnv: >- while ((arg = strdelim(&s)) != NULL && *arg != '\0') { >- if (strchr(arg, '=') != NULL) { >+ while ((arg = next_arg(&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; >@@ -1634,7 +1712,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, >@@ -1648,11 +1726,11 @@ parse_pubkey_algos: > > case oSetEnv: > value = options->num_setenv; >- while ((arg = strdelimw(&s)) != NULL && *arg != '\0') { >+ while ((arg = next_arg(&ac, &av)) != NULL) { > if (strchr(arg, '=') == NULL) { > error("%s line %d: Invalid SetEnv.", > filename, linenum); >- return -1; >+ goto out; > } > if (!*activep || value != 0) > continue; >@@ -1660,7 +1738,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, >@@ -1681,11 +1759,11 @@ parse_pubkey_algos: > case oControlPersist: > /* no/false/yes/true, or a time spec */ > intptr = &options->control_persist; >- arg = strdelim(&s); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') { > error("%.200s line %d: Missing ControlPersist" > " argument.", filename, linenum); >- return -1; >+ goto out; > } > value = 0; > value2 = 0; /* timeout */ >@@ -1698,7 +1776,7 @@ parse_pubkey_algos: > else { > error("%.200s line %d: Bad ControlPersist argument.", > filename, linenum); >- return -1; >+ goto out; > } > if (*activep && *intptr == -1) { > *intptr = value; >@@ -1716,17 +1794,17 @@ parse_pubkey_algos: > goto parse_multistate; > > case oTunnelDevice: >- arg = strdelim(&s); >+ arg = next_arg(&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 = value; >@@ -1754,10 +1832,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 = next_arg(&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 >@@ -1768,7 +1851,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", >@@ -1786,7 +1869,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; >@@ -1805,7 +1888,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 >@@ -1818,23 +1901,23 @@ parse_pubkey_algos: > globfree(&gl); > } > if (value != 0) >- return value; >+ ret = value; > break; > > case oIPQoS: >- arg = strdelim(&s); >+ arg = next_arg(&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 = next_arg(&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 = value; >@@ -1857,11 +1940,27 @@ parse_pubkey_algos: > > case oCanonicalDomains: > value = options->num_canonical_domains != 0; >- while ((arg = strdelim(&s)) != NULL && *arg != '\0') { >+ i = 0; >+ while ((arg = next_arg(&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; >@@ -1869,7 +1968,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); >@@ -1878,7 +1977,7 @@ parse_pubkey_algos: > > case oCanonicalizePermittedCNAMEs: > value = options->num_permitted_cnames != 0; >- while ((arg = strdelim(&s)) != NULL && *arg != '\0') { >+ while ((arg = next_arg(&ac, &av)) != NULL) { > /* Either '*' for everything or 'list:list' */ > if (strcmp(arg, "*") == 0) > arg2 = arg; >@@ -1889,7 +1988,7 @@ parse_pubkey_algos: > error("%s line %d: " > "Invalid permitted CNAME \"%s\"", > filename, linenum, arg); >- return -1; >+ goto out; > } > *arg2 = '\0'; > arg2++; >@@ -1900,7 +1999,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++; >@@ -1923,17 +2022,17 @@ parse_pubkey_algos: > goto parse_flag; > > case oStreamLocalBindMask: >- arg = strdelim(&s); >+ arg = next_arg(&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; >@@ -1948,16 +2047,16 @@ parse_pubkey_algos: > > case oFingerprintHash: > intptr = &options->fingerprint_hash; >- arg = strdelim(&s); >+ arg = next_arg(&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; >@@ -1977,8 +2076,8 @@ parse_pubkey_algos: > goto parse_pubkey_algos; > > case oAddKeysToAgent: >- arg = strdelim(&s); >- arg2 = strdelim(&s); >+ arg = next_arg(&ac, &av); >+ arg2 = next_arg(&ac, &av); > value = parse_multistate_value(arg, filename, linenum, > multistate_yesnoaskconfirm); > value2 = 0; /* unlimited lifespan by default */ >@@ -1988,20 +2087,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; >@@ -2011,18 +2110,18 @@ parse_pubkey_algos: > > case oIdentityAgent: > charptr = &options->identity_agent; >- arg = strdelim(&s); >+ arg = next_arg(&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 */ >@@ -2030,7 +2129,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); >@@ -2039,25 +2138,35 @@ parse_pubkey_algos: > case oDeprecated: > debug("%s line %d: Deprecated option \"%s\"", > filename, linenum, keyword); >- return 0; >+ consume_args(&ac); >+ break; > > case oUnsupported: > error("%s line %d: Unsupported option \"%s\"", > filename, linenum, keyword); >- return 0; >+ consume_args(&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: >+ for (ac = 0; ac < oac; ac++) >+ free(oav[ac]); >+ free(oav); >+ return ret; > } > > /* >@@ -2083,7 +2192,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; >@@ -2119,8 +2228,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) >@@ -2998,6 +3105,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"); >diff --git a/servconf.c b/servconf.c >index 98c02d7..d8468b3 100644 >--- a/servconf.c >+++ b/servconf.c >@@ -1197,13 +1197,31 @@ static const struct multistate multistate_tcpfwd[] = { > { NULL, -1 } > }; > >+static char * >+next_arg(int *argcp, char ***argvp) >+{ >+ char *ret = (*argvp)[0]; >+ >+ if (*argcp > 0 && ret != NULL) { >+ (*argcp)--; >+ (*argvp)++; >+ } >+ return ret; >+} >+ >+static void >+consume_args(int *argcp) >+{ >+ *argcp = 0; >+} >+ > static int > process_server_config_line_depth(ServerOptions *options, char *line, > const char *filename, int linenum, int *activep, > struct connection_info *connectinfo, int *inc_flags, int depth, > struct include_list *includes) > { >- char ch, *cp, ***chararrayptr, **charptr, *arg, *arg2, *p; >+ char ch, *str, ***chararrayptr, **charptr, *arg, *arg2, *p, *keyword; > int cmdline = 0, *intptr, value, value2, n, port, oactive, r, found; > SyslogFacility *log_facility_ptr; > LogLevel *log_level_ptr; >@@ -1215,6 +1233,9 @@ process_server_config_line_depth(ServerOptions *options, char *line, > const char *errstr; > struct include_item *item; > glob_t gbuf; >+ char **oav = NULL, **av; >+ int oac = 0, ac; >+ int ret = -1; > > /* Strip trailing whitespace. Allow \f (form feed) at EOL only */ > if ((len = strlen(line)) == 0) >@@ -1225,46 +1246,59 @@ process_server_config_line_depth(ServerOptions *options, char *line, > line[len] = '\0'; > } > >- cp = line; >- if ((arg = strdelim(&cp)) == NULL) >+ str = line; >+ if ((keyword = strdelim(&str)) == NULL) > return 0; > /* Ignore leading whitespace */ >- if (*arg == '\0') >- arg = strdelim(&cp); >- if (!arg || !*arg || *arg == '#') >+ if (*keyword == '\0') >+ keyword = strdelim(&str); >+ if (!keyword || !*keyword || *keyword == '#') > return 0; >+ if (str == NULL || *str == '\0') { >+ error("%s line %d: no argument after keyword \"%s\"", >+ filename, linenum, keyword); >+ return -1; >+ } > intptr = NULL; > charptr = NULL; >- opcode = parse_token(arg, filename, linenum, &flags); >+ opcode = parse_token(keyword, filename, linenum, &flags); >+ >+ if (argv_split(str, &oac, &oav, 1) != 0) { >+ error("%s line %d: invalid quotes", filename, linenum); >+ return -1; >+ } >+ ac = oac; >+ av = oav; > > if (activep == NULL) { /* We are processing a command line directive */ > cmdline = 1; > activep = &cmdline; > } > if (*activep && opcode != sMatch && opcode != sInclude) >- debug3("%s:%d setting %s %s", filename, linenum, arg, cp); >+ debug3("%s:%d setting %s %s", filename, linenum, keyword, str); > if (*activep == 0 && !(flags & SSHCFG_MATCH)) { > if (connectinfo == NULL) { > fatal("%s line %d: Directive '%s' is not allowed " >- "within a Match block", filename, linenum, arg); >+ "within a Match block", filename, linenum, keyword); > } else { /* this is a directive we have already processed */ >- while (arg) >- arg = strdelim(&cp); >- return 0; >+ ret = 0; >+ goto out; > } > } > > switch (opcode) { > case sBadOption: >- return -1; >+ goto out; > case sPort: > /* ignore ports from configfile if cmdline specifies ports */ >- if (options->ports_from_cmdline) >- return 0; >+ if (options->ports_from_cmdline) { >+ consume_args(&ac); >+ break; >+ } > if (options->num_ports >= MAX_PORTS) > fatal("%s line %d: too many ports.", > filename, linenum); >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing port number.", > filename, linenum); >@@ -1277,7 +1311,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > case sLoginGraceTime: > intptr = &options->login_grace_time; > parse_time: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing time value.", > filename, linenum); >@@ -1289,7 +1323,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > break; > > case sListenAddress: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (arg == NULL || *arg == '\0') > fatal("%s line %d: missing address", > filename, linenum); >@@ -1314,9 +1348,9 @@ process_server_config_line_depth(ServerOptions *options, char *line, > } > /* Optional routing table */ > arg2 = NULL; >- if ((arg = strdelim(&cp)) != NULL) { >+ if ((arg = next_arg(&ac, &av)) != NULL) { > if (strcmp(arg, "rdomain") != 0 || >- (arg2 = strdelim(&cp)) == NULL) >+ (arg2 = next_arg(&ac, &av)) == NULL) > fatal("%s line %d: bad ListenAddress syntax", > filename, linenum); > if (!valid_rdomain(arg2)) >@@ -1332,7 +1366,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > intptr = &options->address_family; > multistate_ptr = multistate_addressfamily; > parse_multistate: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing argument.", > filename, linenum); >@@ -1351,7 +1385,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > break; > > case sHostKeyFile: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing file name.", > filename, linenum); >@@ -1363,7 +1397,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > > case sHostKeyAgent: > charptr = &options->host_key_agent; >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing socket name.", > filename, linenum); >@@ -1373,7 +1407,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > break; > > case sHostCertificate: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing file name.", > filename, linenum); >@@ -1384,7 +1418,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > case sPidFile: > charptr = &options->pid_file; > parse_filename: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing file name.", > filename, linenum); >@@ -1427,7 +1461,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > case sHostbasedAcceptedAlgorithms: > charptr = &options->hostbased_accepted_algos; > parse_pubkey_algos: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: Missing argument.", > filename, linenum); >@@ -1459,7 +1493,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > case sPubkeyAuthOptions: > intptr = &options->pubkey_auth_options; > value = 0; >- while ((arg = strdelim(&cp)) && *arg != '\0') { >+ while ((arg = next_arg(&ac, &av)) != NULL) { > if (strcasecmp(arg, "none") == 0) > continue; > if (strcasecmp(arg, "touch-required") == 0) >@@ -1467,9 +1501,10 @@ process_server_config_line_depth(ServerOptions *options, char *line, > else if (strcasecmp(arg, "verify-required") == 0) > value |= PUBKEYAUTH_VERIFY_REQUIRED; > else { >- fatal("%s line %d: unsupported " >+ error("%s line %d: unsupported " > "PubkeyAuthOptions option %s", > filename, linenum, arg); >+ goto out; > } > } > if (*activep && *intptr == -1) >@@ -1531,7 +1566,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > case sX11DisplayOffset: > intptr = &options->x11_display_offset; > parse_int: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if ((errstr = atoi_err(arg, &value)) != NULL) > fatal("%s line %d: integer value %s.", > filename, linenum, errstr); >@@ -1570,7 +1605,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > case sPermitUserEnvironment: > intptr = &options->permit_user_env; > charptr = &options->permit_user_env_allowlist; >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing argument.", > filename, linenum); >@@ -1599,7 +1634,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > goto parse_multistate; > > case sRekeyLimit: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%.200s line %d: Missing argument.", filename, > linenum); >@@ -1615,9 +1650,9 @@ process_server_config_line_depth(ServerOptions *options, char *line, > } > if (*activep && options->rekey_limit == -1) > options->rekey_limit = val64; >- if (cp != NULL) { /* optional rekey interval present */ >- if (strcmp(cp, "none") == 0) { >- (void)strdelim(&cp); /* discard */ >+ if (ac != 0) { /* optional rekey interval present */ >+ if (strcmp(av[0], "none") == 0) { >+ (void)next_arg(&ac, &av); /* discard */ > break; > } > intptr = &options->rekey_interval; >@@ -1636,7 +1671,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > > case sLogFacility: > log_facility_ptr = &options->log_facility; >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > value = log_facility_number(arg); > if (value == SYSLOG_FACILITY_NOT_SET) > fatal("%.200s line %d: unsupported log facility '%s'", >@@ -1647,7 +1682,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > > case sLogLevel: > log_level_ptr = &options->log_level; >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > value = log_level_number(arg); > if (value == SYSLOG_LEVEL_NOT_SET) > fatal("%.200s line %d: unsupported log level '%s'", >@@ -1657,7 +1692,23 @@ process_server_config_line_depth(ServerOptions *options, char *line, > break; > > case sLogVerbose: >- while ((arg = strdelim(&cp)) && *arg != '\0') { >+ i = 0; >+ while ((arg = next_arg(&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) > continue; > opt_array_append(filename, linenum, "oLogVerbose", >@@ -1685,53 +1736,48 @@ process_server_config_line_depth(ServerOptions *options, char *line, > goto parse_flag; > > case sAllowUsers: >- while ((arg = strdelim(&cp)) && *arg != '\0') { >- if (match_user(NULL, NULL, NULL, arg) == -1) >- fatal("%s line %d: invalid AllowUsers pattern: " >- "\"%.100s\"", filename, linenum, arg); >+ chararrayptr = &options->allow_users; >+ uintptr = &options->num_allow_users; >+ parse_allowdenyusers: >+ while ((arg = next_arg(&ac, &av)) != NULL) { >+ if (*arg == '\0' || >+ match_user(NULL, NULL, NULL, arg) == -1) >+ fatal("%s line %d: invalid %s pattern: \"%s\"", >+ filename, linenum, keyword, arg); > if (!*activep) > continue; >- opt_array_append(filename, linenum, "AllowUsers", >- &options->allow_users, &options->num_allow_users, >- arg); >+ opt_array_append(filename, linenum, keyword, >+ chararrayptr, uintptr, arg); > } > break; > > case sDenyUsers: >- while ((arg = strdelim(&cp)) && *arg != '\0') { >- if (match_user(NULL, NULL, NULL, arg) == -1) >- fatal("%s line %d: invalid DenyUsers pattern: " >- "\"%.100s\"", filename, linenum, arg); >- if (!*activep) >- continue; >- opt_array_append(filename, linenum, "DenyUsers", >- &options->deny_users, &options->num_deny_users, >- arg); >- } >- break; >+ chararrayptr = &options->deny_users; >+ uintptr = &options->num_deny_users; >+ goto parse_allowdenyusers; > > case sAllowGroups: >- while ((arg = strdelim(&cp)) && *arg != '\0') { >+ chararrayptr = &options->allow_groups; >+ uintptr = &options->num_allow_groups; >+ parse_allowdenygroups: >+ while ((arg = next_arg(&ac, &av)) != NULL) { >+ if (*arg == '\0') >+ fatal("%s line %d: empty %s pattern", >+ filename, linenum, keyword); > if (!*activep) > continue; >- opt_array_append(filename, linenum, "AllowGroups", >- &options->allow_groups, &options->num_allow_groups, >- arg); >+ opt_array_append(filename, linenum, keyword, >+ chararrayptr, uintptr, arg); > } > break; > > case sDenyGroups: >- while ((arg = strdelim(&cp)) && *arg != '\0') { >- if (!*activep) >- continue; >- opt_array_append(filename, linenum, "DenyGroups", >- &options->deny_groups, &options->num_deny_groups, >- arg); >- } >- break; >+ chararrayptr = &options->deny_groups; >+ uintptr = &options->num_deny_groups; >+ goto parse_allowdenygroups; > > case sCiphers: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: Missing argument.", filename, linenum); > if (*arg != '-' && >@@ -1743,7 +1789,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > break; > > case sMacs: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: Missing argument.", filename, linenum); > if (*arg != '-' && >@@ -1755,7 +1801,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > break; > > case sKexAlgorithms: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: Missing argument.", > filename, linenum); >@@ -1773,12 +1819,12 @@ process_server_config_line_depth(ServerOptions *options, char *line, > fatal("%s line %d: too many subsystems defined.", > filename, linenum); > } >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: Missing subsystem name.", > filename, linenum); > if (!*activep) { >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > break; > } > for (i = 0; i < options->num_subsystems; i++) >@@ -1786,7 +1832,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > fatal("%s line %d: Subsystem '%s' already defined.", > filename, linenum, arg); > options->subsystem_name[options->num_subsystems] = xstrdup(arg); >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: Missing subsystem command.", > filename, linenum); >@@ -1795,7 +1841,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > /* Collect arguments (separate to executable) */ > p = xstrdup(arg); > len = strlen(p) + 1; >- while ((arg = strdelim(&cp)) != NULL && *arg != '\0') { >+ while ((arg = next_arg(&ac, &av)) != NULL) { > len += 1 + strlen(arg); > p = xreallocarray(p, 1, len); > strlcat(p, " ", len); >@@ -1806,7 +1852,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > break; > > case sMaxStartups: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: Missing MaxStartups spec.", > filename, linenum); >@@ -1828,7 +1874,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > break; > > case sPerSourceNetBlockSize: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: Missing PerSourceNetBlockSize spec.", > filename, linenum); >@@ -1851,7 +1897,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > break; > > case sPerSourceMaxStartups: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: Missing PerSourceMaxStartups spec.", > filename, linenum); >@@ -1885,21 +1931,27 @@ process_server_config_line_depth(ServerOptions *options, char *line, > * AuthorizedKeysFile /etc/ssh_keys/%u > */ > case sAuthorizedKeysFile: >- if (*activep && options->num_authkeys_files == 0) { >- while ((arg = strdelim(&cp)) && *arg != '\0') { >- arg = tilde_expand_filename(arg, getuid()); >+ uvalue = options->num_authkeys_files; >+ while ((arg = next_arg(&ac, &av)) != NULL) { >+ if (*arg == '\0') { >+ error("%s line %d: keyword %s empty argument", >+ filename, linenum, keyword); >+ goto out; >+ } >+ arg2 = tilde_expand_filename(arg, getuid()); >+ if (*activep && uvalue == 0) { > opt_array_append(filename, linenum, > "AuthorizedKeysFile", > &options->authorized_keys_files, >- &options->num_authkeys_files, arg); >- free(arg); >+ &options->num_authkeys_files, arg2); > } >+ free(arg2); > } >- return 0; >+ break; > > case sAuthorizedPrincipalsFile: > charptr = &options->authorized_principals_file; >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing file name.", > filename, linenum); >@@ -1920,8 +1972,8 @@ process_server_config_line_depth(ServerOptions *options, char *line, > goto parse_int; > > case sAcceptEnv: >- while ((arg = strdelim(&cp)) && *arg != '\0') { >- if (strchr(arg, '=') != NULL) >+ while ((arg = next_arg(&ac, &av)) != NULL) { >+ if (*arg == '\0' || strchr(arg, '=') != NULL) > fatal("%s line %d: Invalid environment name.", > filename, linenum); > if (!*activep) >@@ -1934,8 +1986,8 @@ process_server_config_line_depth(ServerOptions *options, char *line, > > case sSetEnv: > uvalue = options->num_setenv; >- while ((arg = strdelimw(&cp)) && *arg != '\0') { >- if (strchr(arg, '=') == NULL) >+ while ((arg = next_arg(&ac, &av)) != NULL) { >+ if (*arg == '\0' || strchr(arg, '=') == NULL) > fatal("%s line %d: Invalid environment.", > filename, linenum); > if (!*activep || uvalue != 0) >@@ -1947,7 +1999,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > > case sPermitTunnel: > intptr = &options->permit_tun; >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: Missing yes/point-to-point/" > "ethernet/no argument.", filename, linenum); >@@ -1970,7 +2022,12 @@ process_server_config_line_depth(ServerOptions *options, char *line, > "command-line option"); > } > value = 0; >- while ((arg2 = strdelim(&cp)) != NULL && *arg2 != '\0') { >+ while ((arg2 = next_arg(&ac, &av)) != NULL) { >+ if (*arg2 == '\0') { >+ error("%s line %d: keyword %s empty argument", >+ filename, linenum, keyword); >+ goto out; >+ } > value++; > found = 0; > if (*arg2 != '/' && *arg2 != '~') { >@@ -2060,7 +2117,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > if (cmdline) > fatal("Match directive not supported as a command-line " > "option"); >- value = match_cfg_line(&cp, linenum, >+ value = match_cfg_line(&str, linenum, > (*inc_flags & SSHCFG_NEVERMATCH ? NULL : connectinfo)); > if (value < 0) > fatal("%s line %d: Bad Match condition", filename, >@@ -2068,6 +2125,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > *activep = (*inc_flags & SSHCFG_NEVERMATCH) ? 0 : value; > /* The MATCH_ONLY is applicable only until the first match block */ > *inc_flags &= ~SSHCFG_MATCH_ONLY; >+ consume_args(&ac); > break; > > case sPermitListen: >@@ -2079,7 +2137,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > uintptr = &options->num_permitted_opens; > chararrayptr = &options->permitted_opens; > } >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing %s specification", > filename, linenum, lookup_opcode_name(opcode)); >@@ -2093,7 +2151,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > } > break; > } >- for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) { >+ for (; arg != NULL && *arg != '\0'; arg = next_arg(&ac, &av)) { > if (opcode == sPermitListen && > strchr(arg, ':') == NULL) { > /* >@@ -2128,18 +2186,19 @@ process_server_config_line_depth(ServerOptions *options, char *line, > break; > > case sForceCommand: >- if (cp == NULL || *cp == '\0') >+ if (str == NULL || *str == '\0') > fatal("%.200s line %d: Missing argument.", filename, > linenum); >- len = strspn(cp, WHITESPACE); >+ len = strspn(str, WHITESPACE); > if (*activep && options->adm_forced_command == NULL) >- options->adm_forced_command = xstrdup(cp + len); >- return 0; >+ options->adm_forced_command = xstrdup(str + len); >+ consume_args(&ac); >+ break; > > case sChrootDirectory: > charptr = &options->chroot_directory; > >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing file name.", > filename, linenum); >@@ -2157,7 +2216,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > > case sSecurityKeyProvider: > charptr = &options->sk_provider; >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing file name.", > filename, linenum); >@@ -2171,11 +2230,11 @@ process_server_config_line_depth(ServerOptions *options, char *line, > break; > > case sIPQoS: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if ((value = parse_ipqos(arg)) == -1) > fatal("%s line %d: Bad IPQoS value: %s", > filename, linenum, arg); >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (arg == NULL) > value2 = value; > else if ((value2 = parse_ipqos(arg)) == -1) >@@ -2188,79 +2247,61 @@ process_server_config_line_depth(ServerOptions *options, char *line, > break; > > case sVersionAddendum: >- if (cp == NULL || *cp == '\0') >+ if (str == NULL || *str == '\0') > fatal("%.200s line %d: Missing argument.", filename, > linenum); >- len = strspn(cp, WHITESPACE); >+ len = strspn(str, WHITESPACE); >+ if (strchr(str + len, '\r') != NULL) { >+ fatal("%.200s line %d: Invalid %s argument", >+ filename, linenum, keyword); >+ } > if (*activep && options->version_addendum == NULL) { >- if (strcasecmp(cp + len, "none") == 0) >+ if (strcasecmp(str + len, "none") == 0) > options->version_addendum = xstrdup(""); >- else if (strchr(cp + len, '\r') != NULL) >- fatal("%.200s line %d: Invalid argument", >- filename, linenum); > else >- options->version_addendum = xstrdup(cp + len); >+ options->version_addendum = xstrdup(str + len); > } >- return 0; >+ consume_args(&ac); >+ break; > > case sAuthorizedKeysCommand: >- if (cp == NULL) >- fatal("%.200s line %d: Missing argument.", filename, >- linenum); >- len = strspn(cp, WHITESPACE); >- if (*activep && options->authorized_keys_command == NULL) { >- if (cp[len] != '/' && strcasecmp(cp + len, "none") != 0) >- fatal("%.200s line %d: AuthorizedKeysCommand " >- "must be an absolute path", >- filename, linenum); >- options->authorized_keys_command = xstrdup(cp + len); >+ charptr = &options->authorized_keys_command; >+ parse_command: >+ len = strspn(str, WHITESPACE); >+ if (str[len] != '/' && strcasecmp(str + len, "none") != 0) { >+ fatal("%.200s line %d: %s must be an absolute path", >+ filename, linenum, keyword); > } >- return 0; >+ if (*activep && options->authorized_keys_command == NULL) >+ *charptr = xstrdup(str + len); >+ consume_args(&ac); >+ break; > > case sAuthorizedKeysCommandUser: > charptr = &options->authorized_keys_command_user; >- >- arg = strdelim(&cp); >- if (!arg || *arg == '\0') >- fatal("%s line %d: missing AuthorizedKeysCommandUser " >- "argument.", filename, linenum); >+ parse_localuser: >+ arg = next_arg(&ac, &av); >+ if (!arg || *arg == '\0') { >+ fatal("%s line %d: missing %s argument.", >+ filename, linenum, keyword); >+ } > if (*activep && *charptr == NULL) > *charptr = xstrdup(arg); > break; > > case sAuthorizedPrincipalsCommand: >- if (cp == NULL) >- fatal("%.200s line %d: Missing argument.", filename, >- linenum); >- len = strspn(cp, WHITESPACE); >- if (*activep && >- options->authorized_principals_command == NULL) { >- if (cp[len] != '/' && strcasecmp(cp + len, "none") != 0) >- fatal("%.200s line %d: " >- "AuthorizedPrincipalsCommand must be " >- "an absolute path", filename, linenum); >- options->authorized_principals_command = >- xstrdup(cp + len); >- } >- return 0; >+ charptr = &options->authorized_principals_command; >+ goto parse_command; > > case sAuthorizedPrincipalsCommandUser: > charptr = &options->authorized_principals_command_user; >- >- arg = strdelim(&cp); >- if (!arg || *arg == '\0') >- fatal("%s line %d: missing " >- "AuthorizedPrincipalsCommandUser argument.", >- filename, linenum); >- if (*activep && *charptr == NULL) >- *charptr = xstrdup(arg); >- break; >+ goto parse_localuser; > > case sAuthenticationMethods: > if (options->num_auth_methods == 0) { > value = 0; /* seen "any" pseudo-method */ > value2 = 0; /* successfully parsed any method */ >- while ((arg = strdelim(&cp)) && *arg != '\0') { >+ while ((arg = next_arg(&ac, &av)) != NULL) { > if (strcmp(arg, "any") == 0) { > if (options->num_auth_methods > 0) { > fatal("%s line %d: \"any\" " >@@ -2291,10 +2332,10 @@ process_server_config_line_depth(ServerOptions *options, char *line, > "specified", filename, linenum); > } > } >- return 0; >+ break; > > case sStreamLocalBindMask: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%s line %d: missing StreamLocalBindMask " > "argument.", filename, linenum); >@@ -2311,7 +2352,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > goto parse_flag; > > case sFingerprintHash: >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%.200s line %d: Missing argument.", > filename, linenum); >@@ -2328,7 +2369,7 @@ process_server_config_line_depth(ServerOptions *options, char *line, > > case sRDomain: > charptr = &options->routing_domain; >- arg = strdelim(&cp); >+ arg = next_arg(&ac, &av); > if (!arg || *arg == '\0') > fatal("%.200s line %d: Missing argument.", > filename, linenum); >@@ -2346,19 +2387,29 @@ process_server_config_line_depth(ServerOptions *options, char *line, > do_log2(opcode == sIgnore ? > SYSLOG_LEVEL_DEBUG2 : SYSLOG_LEVEL_INFO, > "%s line %d: %s option %s", filename, linenum, >- opcode == sUnsupported ? "Unsupported" : "Deprecated", arg); >- while (arg) >- arg = strdelim(&cp); >+ opcode == sUnsupported ? "Unsupported" : "Deprecated", >+ keyword); >+ consume_args(&ac); > break; > > default: > fatal("%s line %d: Missing handler for opcode %s (%d)", >- filename, linenum, arg, opcode); >+ filename, linenum, keyword, opcode); > } >- if ((arg = strdelim(&cp)) != NULL && *arg != '\0') >- fatal("%s line %d: garbage at end of line; \"%.200s\".", >- filename, linenum, arg); >- return 0; >+ /* Check that there is no garbage at end of line. */ >+ if (ac > 0) { >+ error("%.200s line %d: keyword %s extra arguments " >+ "at end of line", filename, linenum, keyword); >+ goto out; >+ } >+ >+ /* success */ >+ ret = 0; >+ out: >+ for (ac = 0; ac < oac; ac++) >+ free(oav[ac]); >+ free(oav); >+ return ret; > } > > int >@@ -2397,12 +2448,10 @@ load_server_config(const char *filename, struct sshbuf *conf) > while (getline(&line, &linesize, f) != -1) { > lineno++; > /* >- * Trim out comments and strip whitespace >+ * Strip whitespace > * NB - preserve newlines, they are needed to reproduce > * line numbers later for error messages > */ >- if ((cp = strchr(line, '#')) != NULL) >- memcpy(cp, "\n", 2); > cp = line + strspn(line, " \t\r"); > if ((r = sshbuf_put(conf, cp, strlen(cp))) != 0) > fatal_fr(r, "sshbuf_put"); >diff --git a/ssh.c b/ssh.c >index 9cbb93d..ebea075 100644 >--- a/ssh.c >+++ b/ssh.c >@@ -488,6 +488,8 @@ resolve_canonicalize(char **hostp, int port) > } > /* Attempt each supplied suffix */ > for (i = 0; i < options.num_canonical_domains; i++) { >+ if (strcasecmp(options.canonical_domains[i], "none") == 0) >+ break; > xasprintf(&fullhost, "%s.%s.", *hostp, > options.canonical_domains[i]); > debug3_f("attempting \"%s\" => \"%s\"", *hostp, fullhost); >@@ -1314,8 +1316,11 @@ main(int ac, char **av) > > /* reinit */ > log_init(argv0, options.log_level, options.log_facility, !use_syslog); >- for (j = 0; j < options.num_log_verbose; j++) >+ for (j = 0; j < options.num_log_verbose; j++) { >+ if (strcasecmp(options.log_verbose[j], "none") == 0) >+ break; > log_verbose_add(options.log_verbose[j]); >+ } > > if (options.request_tty == REQUEST_TTY_YES || > options.request_tty == REQUEST_TTY_FORCE) >diff --git a/sshconnect.c b/sshconnect.c >index 72fdad1..54f48a3 100644 >--- a/sshconnect.c >+++ b/sshconnect.c >@@ -827,7 +827,7 @@ load_hostkeys_command(struct hostkeys *hostkeys, const char *command_template, > osigchld = ssh_signal(SIGCHLD, SIG_DFL); > > /* Turn the command into an argument vector */ >- if (argv_split(command_template, &ac, &av) != 0) { >+ if (argv_split(command_template, &ac, &av, 0) != 0) { > error("%s \"%s\" contains invalid quotes", tag, > command_template); > goto out;
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 3288
:
3489
| 3528