-/* $OpenBSD: readconf.c,v 1.384 2024/01/11 01:45:36 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.385 2024/03/04 02:16:11 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
{
char *str, **charptr, *endofnumber, *keyword, *arg, *arg2, *p;
char **cpptr, ***cppptr, fwdarg[256];
- u_int i, *uintptr, uvalue, max_entries = 0;
+ u_int i, *uintptr, max_entries = 0;
int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0;
- int remotefwd, dynamicfwd, ca_only = 0;
+ int remotefwd, dynamicfwd, ca_only = 0, found = 0;
LogLevel *log_level_ptr;
SyslogFacility *log_facility_ptr;
long long val64;
size_t len;
struct Forward fwd;
const struct multistate *multistate_ptr;
- struct allowed_cname *cname;
glob_t gl;
const char *errstr;
char **oav = NULL, **av;
int oac = 0, ac;
int ret = -1;
+ struct allowed_cname *cnames = NULL;
+ u_int ncnames = 0;
+ char **strs = NULL; /* string array arguments; freed implicitly */
+ u_int nstrs = 0;
if (activep == NULL) { /* We are processing a command line directive */
cmdline = 1;
case oPermitRemoteOpen:
uintptr = &options->num_permitted_remote_opens;
cppptr = &options->permitted_remote_opens;
- uvalue = *uintptr; /* modified later */
- i = 0;
+ found = *uintptr == 0;
while ((arg = argv_next(&ac, &av)) != NULL) {
arg2 = xstrdup(arg);
/* Allow any/none only in first position */
if (strcasecmp(arg, "none") == 0 ||
strcasecmp(arg, "any") == 0) {
- if (i > 0 || ac > 0) {
+ if (nstrs > 0 || ac > 0) {
error("%s line %d: keyword %s \"%s\" "
"argument must appear alone.",
filename, linenum, keyword, arg);
lookup_opcode_name(opcode));
}
}
- if (*activep && uvalue == 0) {
- opt_array_append(filename, linenum,
- lookup_opcode_name(opcode),
- cppptr, uintptr, arg2);
- }
+ opt_array_append(filename, linenum,
+ lookup_opcode_name(opcode),
+ &strs, &nstrs, arg2);
free(arg2);
- i++;
}
- if (i == 0)
+ if (nstrs == 0)
fatal("%s line %d: missing %s specification",
filename, linenum, lookup_opcode_name(opcode));
+ if (found && *activep) {
+ *cppptr = strs;
+ *uintptr = nstrs;
+ strs = NULL; /* transferred */
+ nstrs = 0;
+ }
break;
case oClearAllForwardings:
goto parse_int;
case oSendEnv:
+ /* XXX appends to list; doesn't respect first-match-wins */
while ((arg = argv_next(&ac, &av)) != NULL) {
if (*arg == '\0' || strchr(arg, '=') != NULL) {
error("%s line %d: Invalid environment name.",
filename, linenum);
goto out;
}
+ found = 1;
if (!*activep)
continue;
if (*arg == '-') {
lookup_opcode_name(opcode),
&options->send_env, &options->num_send_env, arg);
}
+ if (!found) {
+ fatal("%s line %d: no %s specified",
+ filename, linenum, keyword);
+ }
break;
case oSetEnv:
- value = options->num_setenv;
+ found = options->num_setenv == 0;
while ((arg = argv_next(&ac, &av)) != NULL) {
if (strchr(arg, '=') == NULL) {
error("%s line %d: Invalid SetEnv.",
filename, linenum);
goto out;
}
- if (!*activep || value != 0)
- continue;
- if (lookup_setenv_in_list(arg, options->setenv,
- options->num_setenv) != NULL) {
+ if (lookup_setenv_in_list(arg, strs, nstrs) != NULL) {
debug2("%s line %d: ignoring duplicate env "
"name \"%.64s\"", filename, linenum, arg);
continue;
}
opt_array_append(filename, linenum,
lookup_opcode_name(opcode),
- &options->setenv, &options->num_setenv, arg);
+ &strs, &nstrs, arg);
+ }
+ if (nstrs == 0) {
+ fatal("%s line %d: no %s specified",
+ filename, linenum, keyword);
+ }
+ if (found && *activep) {
+ options->setenv = strs;
+ options->num_setenv = nstrs;
+ strs = NULL; /* transferred */
+ nstrs = 0;
}
break;
goto parse_flag;
case oCanonicalDomains:
- value = options->num_canonical_domains != 0;
- i = 0;
+ found = options->num_canonical_domains == 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) {
+ if (nstrs > 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);
goto out;
}
- if (!*activep || value)
- continue;
- if (options->num_canonical_domains >=
- MAX_CANON_DOMAINS) {
- error("%s line %d: too many hostname suffixes.",
- filename, linenum);
- goto out;
- }
- options->canonical_domains[
- options->num_canonical_domains++] = xstrdup(arg);
+ opt_array_append(filename, linenum, keyword,
+ &strs, &nstrs, arg);
+ }
+ if (nstrs == 0) {
+ fatal("%s line %d: no %s specified",
+ filename, linenum, keyword);
+ }
+ if (found && *activep) {
+ options->canonical_domains = strs;
+ options->num_canonical_domains = nstrs;
+ strs = NULL; /* transferred */
+ nstrs = 0;
}
break;
case oCanonicalizePermittedCNAMEs:
- value = options->num_permitted_cnames != 0;
- i = 0;
+ found = options->num_permitted_cnames == 0;
while ((arg = argv_next(&ac, &av)) != NULL) {
/*
* Either 'none' (only in first position), '*' for
* everything or 'list:list'
*/
if (strcasecmp(arg, "none") == 0) {
- if (i > 0 || ac > 0) {
+ if (ncnames > 0 || ac > 0) {
error("%s line %d: keyword %s \"none\" "
"argument must appear alone.",
filename, linenum, keyword);
*arg2 = '\0';
arg2++;
}
- i++;
- if (!*activep || value)
- continue;
- if (options->num_permitted_cnames >=
- MAX_CANON_DOMAINS) {
- error("%s line %d: too many permitted CNAMEs.",
- filename, linenum);
- goto out;
+ cnames = xrecallocarray(cnames, ncnames, ncnames + 1,
+ sizeof(*cnames));
+ cnames[ncnames].source_list = xstrdup(arg);
+ cnames[ncnames].target_list = xstrdup(arg2);
+ ncnames++;
+ }
+ if (ncnames == 0) {
+ fatal("%s line %d: no %s specified",
+ filename, linenum, keyword);
+ }
+ if (found && *activep) {
+ options->permitted_cnames = cnames;
+ options->num_permitted_cnames = ncnames;
+ } else {
+ for (i = 0; i < ncnames; i++) {
+ free(cnames[i].source_list);
+ free(cnames[i].target_list);
}
- cname = options->permitted_cnames +
- options->num_permitted_cnames++;
- cname->source_list = xstrdup(arg);
- cname->target_list = xstrdup(arg2);
+ free(cnames);
}
break;
break;
case oChannelTimeout:
- uvalue = options->num_channel_timeouts;
- i = 0;
+ found = options->num_channel_timeouts == 0;
while ((arg = argv_next(&ac, &av)) != NULL) {
/* Allow "none" only in first position */
if (strcasecmp(arg, "none") == 0) {
- if (i > 0 || ac > 0) {
+ if (nstrs > 0 || ac > 0) {
error("%s line %d: keyword %s \"none\" "
"argument must appear alone.",
filename, linenum, keyword);
fatal("%s line %d: invalid channel timeout %s",
filename, linenum, arg);
}
- if (!*activep || uvalue != 0)
- continue;
opt_array_append(filename, linenum, keyword,
- &options->channel_timeouts,
- &options->num_channel_timeouts, arg);
+ &strs, &nstrs, arg);
+ }
+ if (nstrs == 0) {
+ fatal("%s line %d: no %s specified",
+ filename, linenum, keyword);
+ }
+ if (found && *activep) {
+ options->channel_timeouts = strs;
+ options->num_channel_timeouts = nstrs;
+ strs = NULL; /* transferred */
+ nstrs = 0;
}
break;
/* success */
ret = 0;
out:
+ opt_array_free2(strs, NULL, nstrs);
argv_free(oav, oac);
return ret;
}
-/* $OpenBSD: servconf.c,v 1.404 2024/02/20 04:10:03 djm Exp $ */
+/* $OpenBSD: servconf.c,v 1.405 2024/03/04 02:16:11 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
struct include_list *includes)
{
char *str, ***chararrayptr, **charptr, *arg, *arg2, *p, *keyword;
- int cmdline = 0, *intptr, value, value2, n, port, oactive, r, found;
- int ca_only = 0;
+ int cmdline = 0, *intptr, value, value2, n, port, oactive, r;
+ int ca_only = 0, found = 0;
SyslogFacility *log_facility_ptr;
LogLevel *log_level_ptr;
ServerOpCodes opcode;
- u_int i, *uintptr, uvalue, flags = 0;
+ u_int i, *uintptr, flags = 0;
size_t len;
long long val64;
const struct multistate *multistate_ptr;
char **oav = NULL, **av;
int oac = 0, ac;
int ret = -1;
+ char **strs = NULL; /* string array arguments; freed implicitly */
+ u_int nstrs = 0;
/* Strip trailing whitespace. Allow \f (form feed) at EOL only */
if ((len = strlen(line)) == 0)
case sLogVerbose:
found = options->num_log_verbose == 0;
- i = 0;
while ((arg = argv_next(&ac, &av)) != NULL) {
if (*arg == '\0') {
error("%s line %d: keyword %s empty argument",
}
/* Allow "none" only in first position */
if (strcasecmp(arg, "none") == 0) {
- if (i > 0 || ac > 0) {
+ if (nstrs > 0 || ac > 0) {
error("%s line %d: keyword %s \"none\" "
"argument must appear alone.",
filename, linenum, keyword);
goto out;
}
}
- i++;
- if (!found || !*activep)
- continue;
opt_array_append(filename, linenum, keyword,
- &options->log_verbose, &options->num_log_verbose,
- arg);
+ &strs, &nstrs, arg);
+ }
+ if (nstrs == 0) {
+ fatal("%s line %d: no %s specified",
+ filename, linenum, keyword);
+ }
+ if (found && *activep) {
+ options->log_verbose = strs;
+ options->num_log_verbose = nstrs;
+ strs = NULL; /* transferred */
+ nstrs = 0;
}
break;
chararrayptr = &options->allow_users;
uintptr = &options->num_allow_users;
parse_allowdenyusers:
+ /* XXX appends to list; doesn't respect first-match-wins */
while ((arg = argv_next(&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);
+ found = 1;
if (!*activep)
continue;
opt_array_append(filename, linenum, keyword,
chararrayptr, uintptr, arg);
}
+ if (!found) {
+ fatal("%s line %d: no %s specified",
+ filename, linenum, keyword);
+ }
break;
case sDenyUsers:
case sAllowGroups:
chararrayptr = &options->allow_groups;
uintptr = &options->num_allow_groups;
+ /* XXX appends to list; doesn't respect first-match-wins */
parse_allowdenygroups:
while ((arg = argv_next(&ac, &av)) != NULL) {
if (*arg == '\0')
fatal("%s line %d: empty %s pattern",
filename, linenum, keyword);
+ found = 1;
if (!*activep)
continue;
opt_array_append(filename, linenum, keyword,
chararrayptr, uintptr, arg);
}
+ if (!found) {
+ fatal("%s line %d: no %s specified",
+ filename, linenum, keyword);
+ }
break;
case sDenyGroups:
* AuthorizedKeysFile /etc/ssh_keys/%u
*/
case sAuthorizedKeysFile:
- uvalue = options->num_authkeys_files;
+ found = options->num_authkeys_files == 0;
while ((arg = argv_next(&ac, &av)) != NULL) {
if (*arg == '\0') {
error("%s line %d: keyword %s empty argument",
goto out;
}
arg2 = tilde_expand_filename(arg, getuid());
- if (*activep && uvalue == 0) {
- opt_array_append(filename, linenum, keyword,
- &options->authorized_keys_files,
- &options->num_authkeys_files, arg2);
- }
+ opt_array_append(filename, linenum, keyword,
+ &strs, &nstrs, arg2);
free(arg2);
}
+ if (nstrs == 0) {
+ fatal("%s line %d: no %s specified",
+ filename, linenum, keyword);
+ }
+ if (found && *activep) {
+ options->authorized_keys_files = strs;
+ options->num_authkeys_files = nstrs;
+ strs = NULL; /* transferred */
+ nstrs = 0;
+ }
break;
case sAuthorizedPrincipalsFile:
goto parse_int;
case sAcceptEnv:
+ /* XXX appends to list; doesn't respect first-match-wins */
while ((arg = argv_next(&ac, &av)) != NULL) {
if (*arg == '\0' || strchr(arg, '=') != NULL)
fatal("%s line %d: Invalid environment name.",
filename, linenum);
+ found = 1;
if (!*activep)
continue;
opt_array_append(filename, linenum, keyword,
&options->accept_env, &options->num_accept_env,
arg);
}
+ if (!found) {
+ fatal("%s line %d: no %s specified",
+ filename, linenum, keyword);
+ }
break;
case sSetEnv:
- uvalue = options->num_setenv;
+ found = options->num_setenv == 0;
while ((arg = argv_next(&ac, &av)) != NULL) {
if (*arg == '\0' || strchr(arg, '=') == NULL)
fatal("%s line %d: Invalid environment.",
filename, linenum);
- if (!*activep || uvalue != 0)
- continue;
- if (lookup_setenv_in_list(arg, options->setenv,
- options->num_setenv) != NULL) {
+ if (lookup_setenv_in_list(arg, strs, nstrs) != NULL) {
debug2("%s line %d: ignoring duplicate env "
"name \"%.64s\"", filename, linenum, arg);
continue;
}
opt_array_append(filename, linenum, keyword,
- &options->setenv, &options->num_setenv, arg);
+ &strs, &nstrs, arg);
+ }
+ if (nstrs == 0) {
+ fatal("%s line %d: no %s specified",
+ filename, linenum, keyword);
+ }
+ if (found && *activep) {
+ options->setenv = strs;
+ options->num_setenv = nstrs;
+ strs = NULL; /* transferred */
+ nstrs = 0;
}
break;
uintptr = &options->num_permitted_opens;
chararrayptr = &options->permitted_opens;
}
- arg = argv_next(&ac, &av);
- if (!arg || *arg == '\0')
- fatal("%s line %d: %s missing argument.",
- filename, linenum, keyword);
- uvalue = *uintptr; /* modified later */
- if (strcmp(arg, "any") == 0 || strcmp(arg, "none") == 0) {
- if (*activep && uvalue == 0) {
- *uintptr = 1;
- *chararrayptr = xcalloc(1,
- sizeof(**chararrayptr));
- (*chararrayptr)[0] = xstrdup(arg);
+ found = *uintptr == 0;
+ while ((arg = argv_next(&ac, &av)) != NULL) {
+ if (strcmp(arg, "any") == 0 ||
+ strcmp(arg, "none") == 0) {
+ if (nstrs != 0) {
+ fatal("%s line %d: %s must appear "
+ "alone on a %s line.",
+ filename, linenum, arg, keyword);
+ }
+ opt_array_append(filename, linenum, keyword,
+ &strs, &nstrs, arg);
+ continue;
}
- break;
- }
- for (; arg != NULL && *arg != '\0'; arg = argv_next(&ac, &av)) {
+
if (opcode == sPermitListen &&
strchr(arg, ':') == NULL) {
/*
fatal("%s line %d: %s bad port number",
filename, linenum, keyword);
}
- if (*activep && uvalue == 0) {
- opt_array_append(filename, linenum, keyword,
- chararrayptr, uintptr, arg2);
- }
+ opt_array_append(filename, linenum, keyword,
+ &strs, &nstrs, arg2);
free(arg2);
}
+ if (nstrs == 0) {
+ fatal("%s line %d: %s missing argument.",
+ filename, linenum, keyword);
+ }
+ if (found && *activep) {
+ *chararrayptr = strs;
+ *uintptr = nstrs;
+ strs = NULL; /* transferred */
+ nstrs = 0;
+ }
break;
case sForceCommand:
case sAuthenticationMethods:
found = options->num_auth_methods == 0;
value = 0; /* seen "any" pseudo-method */
- value2 = 0; /* successfully parsed any method */
while ((arg = argv_next(&ac, &av)) != NULL) {
if (strcmp(arg, "any") == 0) {
- if (options->num_auth_methods > 0) {
+ if (nstrs > 0) {
fatal("%s line %d: \"any\" must "
"appear alone in %s",
filename, linenum, keyword);
fatal("%s line %d: invalid %s method list.",
filename, linenum, keyword);
}
- value2 = 1;
- if (!found || !*activep)
- continue;
opt_array_append(filename, linenum, keyword,
- &options->auth_methods,
- &options->num_auth_methods, arg);
+ &strs, &nstrs, arg);
}
- if (value2 == 0) {
+ if (nstrs == 0) {
fatal("%s line %d: no %s specified",
filename, linenum, keyword);
}
+ if (found && *activep) {
+ options->auth_methods = strs;
+ options->num_auth_methods = nstrs;
+ strs = NULL; /* transferred */
+ nstrs = 0;
+ }
break;
case sStreamLocalBindMask:
goto parse_int;
case sChannelTimeout:
- uvalue = options->num_channel_timeouts;
- i = 0;
+ found = options->num_channel_timeouts == 0;
while ((arg = argv_next(&ac, &av)) != NULL) {
/* Allow "none" only in first position */
if (strcasecmp(arg, "none") == 0) {
- if (i > 0 || ac > 0) {
+ if (nstrs > 0 || ac > 0) {
error("%s line %d: keyword %s \"none\" "
"argument must appear alone.",
filename, linenum, keyword);
fatal("%s line %d: invalid channel timeout %s",
filename, linenum, arg);
}
- if (!*activep || uvalue != 0)
- continue;
opt_array_append(filename, linenum, keyword,
- &options->channel_timeouts,
- &options->num_channel_timeouts, arg);
+ &strs, &nstrs, arg);
+ }
+ if (nstrs == 0) {
+ fatal("%s line %d: no %s specified",
+ filename, linenum, keyword);
+ }
+ if (found && *activep) {
+ options->channel_timeouts = strs;
+ options->num_channel_timeouts = nstrs;
+ strs = NULL; /* transferred */
+ nstrs = 0;
}
break;
/* success */
ret = 0;
out:
+ opt_array_free2(strs, NULL, nstrs);
argv_free(oav, oac);
return ret;
}