From 43026da035cd266db37df1f723d5575056150744 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Thu, 17 Dec 2020 23:10:27 +0000 Subject: upstream: prepare readconf.c for fuzzing; remove fatal calls and fix some (one-off) memory leaks; ok markus@ OpenBSD-Commit-ID: 91c6aec57b0e7aae9190de188e9fe8933aad5ec5 --- readconf.c | 559 ++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 404 insertions(+), 155 deletions(-) (limited to 'readconf.c') diff --git a/readconf.c b/readconf.c index d60eeacf..c8ba5ddb 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.343 2020/11/30 05:36:39 dtucker Exp $ */ +/* $OpenBSD: readconf.c,v 1.344 2020/12/17 23:10:27 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -315,14 +315,20 @@ static struct { { NULL, oBadOption } }; -static char *kex_default_pk_alg_filtered; const char * kex_default_pk_alg(void) { - if (kex_default_pk_alg_filtered == NULL) - fatal("kex_default_pk_alg not initialized."); - return kex_default_pk_alg_filtered; + static char *pkalgs; + + if (pkalgs == NULL) { + char *all_key; + + all_key = sshkey_alg_list(0, 0, 1, ','); + pkalgs = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key); + free(all_key); + } + return pkalgs; } char * @@ -873,8 +879,10 @@ parse_multistate_value(const char *arg, const char *filename, int linenum, { int i; - if (!arg || *arg == '\0') - fatal("%s line %d: missing argument.", filename, linenum); + if (!arg || *arg == '\0') { + error("%s line %d: missing argument.", filename, linenum); + return -1; + } for (i = 0; multistate_ptr[i].key != NULL; i++) { if (strcasecmp(arg, multistate_ptr[i].key) == 0) return multistate_ptr[i].value; @@ -959,14 +967,18 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host, intptr = &options->connection_timeout; parse_time: arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%s line %d: missing time value.", + if (!arg || *arg == '\0') { + error("%s line %d: missing time value.", filename, linenum); + return -1; + } if (strcmp(arg, "none") == 0) value = -1; - else if ((value = convtime(arg)) == -1) - fatal("%s line %d: invalid time value.", + else if ((value = convtime(arg)) == -1) { + error("%s line %d: invalid time value.", filename, linenum); + return -1; + } if (*activep && *intptr == -1) *intptr = value; break; @@ -975,9 +987,11 @@ parse_time: intptr = &options->forward_agent; arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%s line %d: missing argument.", + if (!arg || *arg == '\0') { + error("%s line %d: missing argument.", filename, linenum); + return -1; + } value = -1; multistate_ptr = multistate_flag; @@ -1007,8 +1021,9 @@ parse_time: arg = strdelim(&s); if ((value = parse_multistate_value(arg, filename, linenum, multistate_ptr)) == -1) { - fatal("%s line %d: unsupported option \"%s\".", + error("%s line %d: unsupported option \"%s\".", filename, linenum, arg); + return -1; } if (*activep && *intptr == -1) *intptr = value; @@ -1099,18 +1114,24 @@ parse_time: case oRekeyLimit: arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", filename, + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", filename, linenum); + return -1; + } if (strcmp(arg, "default") == 0) { val64 = 0; } else { - if (scan_scaled(arg, &val64) == -1) - fatal("%.200s line %d: Bad number '%s': %s", + if (scan_scaled(arg, &val64) == -1) { + error("%.200s line %d: Bad number '%s': %s", filename, linenum, arg, strerror(errno)); - if (val64 != 0 && val64 < 16) - fatal("%.200s line %d: RekeyLimit too small", + return -1; + } + if (val64 != 0 && val64 < 16) { + error("%.200s line %d: RekeyLimit too small", filename, linenum); + return -1; + } } if (*activep && options->rekey_limit == -1) options->rekey_limit = val64; @@ -1126,13 +1147,19 @@ parse_time: case oIdentityFile: arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", filename, linenum); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + return -1; + } if (*activep) { intptr = &options->num_identity_files; - if (*intptr >= SSH_MAX_IDENTITY_FILES) - fatal("%.200s line %d: Too many identity files specified (max %d).", - filename, linenum, SSH_MAX_IDENTITY_FILES); + if (*intptr >= SSH_MAX_IDENTITY_FILES) { + error("%.200s line %d: Too many identity files " + "specified (max %d).", filename, linenum, + SSH_MAX_IDENTITY_FILES); + return -1; + } add_identity_file(options, NULL, arg, flags & SSHCONF_USERCONF); } @@ -1140,16 +1167,19 @@ parse_time: case oCertificateFile: arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", filename, linenum); + return -1; + } if (*activep) { intptr = &options->num_certificate_files; if (*intptr >= SSH_MAX_CERTIFICATE_FILES) { - fatal("%.200s line %d: Too many certificate " + error("%.200s line %d: Too many certificate " "files specified (max %d).", filename, linenum, SSH_MAX_CERTIFICATE_FILES); + return -1; } add_certificate_file(options, arg, flags & SSHCONF_USERCONF); @@ -1164,9 +1194,11 @@ parse_time: charptr = &options->user; parse_string: arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", filename, linenum); + return -1; + } if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; @@ -1178,10 +1210,11 @@ parse_string: parse_char_array: if (*activep && *uintptr == 0) { while ((arg = strdelim(&s)) != NULL && *arg != '\0') { - if ((*uintptr) >= max_entries) - fatal("%s line %d: " - "too many known hosts files.", - filename, linenum); + if ((*uintptr) >= max_entries) { + error("%s line %d: too many known " + "hosts files.", filename, linenum); + return -1; + } cpptr[(*uintptr)++] = xstrdup(arg); } } @@ -1227,8 +1260,11 @@ parse_char_array: if (options->jump_host != NULL) charptr = &options->jump_host; /* Skip below */ parse_command: - if (s == NULL) - fatal("%.200s line %d: Missing argument.", filename, linenum); + if (s == NULL) { + error("%.200s line %d: Missing argument.", + filename, linenum); + return -1; + } len = strspn(s, WHITESPACE "="); if (*activep && *charptr == NULL) *charptr = xstrdup(s + len); @@ -1236,25 +1272,31 @@ parse_command: case oProxyJump: if (s == NULL) { - fatal("%.200s line %d: Missing argument.", + error("%.200s line %d: Missing argument.", filename, linenum); + return -1; } len = strspn(s, WHITESPACE "="); if (parse_jump(s + len, options, *activep) == -1) { - fatal("%.200s line %d: Invalid ProxyJump \"%s\"", + error("%.200s line %d: Invalid ProxyJump \"%s\"", filename, linenum, s + len); + return -1; } return 0; case oPort: arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", filename, linenum); + return -1; + } value = a2port(arg); - if (value <= 0) - fatal("%.200s line %d: Bad port '%s'.", + if (value <= 0) { + error("%.200s line %d: Bad port '%s'.", filename, linenum, arg); + return -1; + } if (*activep && options->port == -1) options->port = value; break; @@ -1263,47 +1305,63 @@ parse_command: intptr = &options->connection_attempts; parse_int: arg = strdelim(&s); - if ((errstr = atoi_err(arg, &value)) != NULL) - fatal("%s line %d: integer value %s.", + if ((errstr = atoi_err(arg, &value)) != NULL) { + error("%s line %d: integer value %s.", filename, linenum, errstr); + return -1; + } if (*activep && *intptr == -1) *intptr = value; break; case oCiphers: arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", filename, linenum); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + return -1; + } if (*arg != '-' && - !ciphers_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) - fatal("%.200s line %d: Bad SSH2 cipher spec '%s'.", + !ciphers_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)){ + error("%.200s line %d: Bad SSH2 cipher spec '%s'.", filename, linenum, arg ? arg : ""); + return -1; + } if (*activep && options->ciphers == NULL) options->ciphers = xstrdup(arg); break; case oMacs: arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", filename, linenum); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + return -1; + } if (*arg != '-' && - !mac_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) - fatal("%.200s line %d: Bad SSH2 MAC spec '%s'.", + !mac_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) { + error("%.200s line %d: Bad SSH2 MAC spec '%s'.", filename, linenum, arg ? arg : ""); + return -1; + } if (*activep && options->macs == NULL) options->macs = xstrdup(arg); break; case oKexAlgorithms: arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", filename, linenum); + return -1; + } if (*arg != '-' && !kex_names_valid(*arg == '+' || *arg == '^' ? - arg + 1 : arg)) - fatal("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.", + arg + 1 : arg)) { + error("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.", filename, linenum, arg ? arg : ""); + return -1; + } if (*activep && options->kex_algorithms == NULL) options->kex_algorithms = xstrdup(arg); break; @@ -1312,14 +1370,18 @@ parse_int: charptr = &options->hostkeyalgorithms; parse_keytypes: arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", filename, linenum); + return -1; + } if (*arg != '-' && !sshkey_names_valid2(*arg == '+' || *arg == '^' ? - arg + 1 : arg, 1)) - fatal("%s line %d: Bad key types '%s'.", - filename, linenum, arg ? arg : ""); + arg + 1 : arg, 1)) { + error("%s line %d: Bad key types '%s'.", + filename, linenum, arg ? arg : ""); + return -1; + } if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; @@ -1332,9 +1394,11 @@ parse_keytypes: log_level_ptr = &options->log_level; arg = strdelim(&s); value = log_level_number(arg); - if (value == SYSLOG_LEVEL_NOT_SET) - fatal("%.200s line %d: unsupported log level '%s'", + if (value == SYSLOG_LEVEL_NOT_SET) { + error("%.200s line %d: unsupported log level '%s'", filename, linenum, arg ? arg : ""); + return -1; + } if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET) *log_level_ptr = (LogLevel) value; break; @@ -1343,9 +1407,11 @@ parse_keytypes: log_facility_ptr = &options->log_facility; arg = strdelim(&s); value = log_facility_number(arg); - if (value == SYSLOG_FACILITY_NOT_SET) - fatal("%.200s line %d: unsupported log facility '%s'", + if (value == SYSLOG_FACILITY_NOT_SET) { + error("%.200s line %d: unsupported log facility '%s'", filename, linenum, arg ? arg : ""); + return -1; + } if (*log_facility_ptr == -1) *log_facility_ptr = (SyslogFacility) value; break; @@ -1366,9 +1432,11 @@ parse_keytypes: case oRemoteForward: case oDynamicForward: arg = strdelim(&s); - if (arg == NULL || *arg == '\0') - fatal("%.200s line %d: Missing port argument.", + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", filename, linenum); + return -1; + } remotefwd = (opcode == oRemoteForward); dynamicfwd = (opcode == oDynamicForward); @@ -1378,9 +1446,11 @@ parse_keytypes: if (arg2 == NULL || *arg2 == '\0') { if (remotefwd) dynamicfwd = 1; - else - fatal("%.200s line %d: Missing target " + else { + error("%.200s line %d: Missing target " "argument.", filename, linenum); + return -1; + } } else { /* construct a string for parse_forward */ snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, @@ -1390,9 +1460,11 @@ parse_keytypes: if (dynamicfwd) strlcpy(fwdarg, arg, sizeof(fwdarg)); - if (parse_forward(&fwd, fwdarg, dynamicfwd, remotefwd) == 0) - fatal("%.200s line %d: Bad forwarding specification.", + if (parse_forward(&fwd, fwdarg, dynamicfwd, remotefwd) == 0) { + error("%.200s line %d: Bad forwarding specification.", filename, linenum); + return -1; + } if (*activep) { if (remotefwd) { @@ -1408,9 +1480,11 @@ parse_keytypes: goto parse_flag; case oHost: - if (cmdline) - fatal("Host directive not supported as a command-line " + if (cmdline) { + error("Host directive not supported as a command-line " "option"); + return -1; + } *activep = 0; arg2 = NULL; while ((arg = strdelim(&s)) != NULL && *arg != '\0') { @@ -1440,23 +1514,30 @@ parse_keytypes: return 0; case oMatch: - if (cmdline) - fatal("Host directive not supported as a command-line " + if (cmdline) { + error("Host directive not supported as a command-line " "option"); + return -1; + } value = match_cfg_line(options, &s, pw, host, original_host, flags & SSHCONF_FINAL, want_final_pass, filename, linenum); - if (value < 0) - fatal("%.200s line %d: Bad Match condition", filename, + if (value < 0) { + error("%.200s line %d: Bad Match condition", filename, linenum); + return -1; + } *activep = (flags & SSHCONF_NEVERMATCH) ? 0 : value; break; case oEscapeChar: intptr = &options->escape_char; arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", filename, linenum); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + return -1; + } if (strcmp(arg, "none") == 0) value = SSH_ESCAPECHAR_NONE; else if (arg[1] == '\0') @@ -1465,9 +1546,9 @@ parse_keytypes: (u_char) arg[1] >= 64 && (u_char) arg[1] < 128) value = (u_char) arg[1] & 31; else { - value = 0; /* Avoid compiler warning. */ - fatal("%.200s line %d: Bad escape character.", + error("%.200s line %d: Bad escape character.", filename, linenum); + return -1; } if (*activep && *intptr == -1) *intptr = value; @@ -1496,9 +1577,11 @@ parse_keytypes: case oSendEnv: while ((arg = strdelim(&s)) != NULL && *arg != '\0') { - if (strchr(arg, '=') != NULL) - fatal("%s line %d: Invalid environment name.", + if (strchr(arg, '=') != NULL) { + error("%s line %d: Invalid environment name.", filename, linenum); + return -1; + } if (!*activep) continue; if (*arg == '-') { @@ -1507,9 +1590,11 @@ parse_keytypes: continue; } else { /* Adding an env var */ - if (options->num_send_env >= INT_MAX) - fatal("%s line %d: too many send env.", + if (options->num_send_env >= INT_MAX) { + error("%s line %d: too many send env.", filename, linenum); + return -1; + } options->send_env = xrecallocarray( options->send_env, options->num_send_env, options->num_send_env + 1, @@ -1523,15 +1608,19 @@ parse_keytypes: case oSetEnv: value = options->num_setenv; while ((arg = strdelimw(&s)) != NULL && *arg != '\0') { - if (strchr(arg, '=') == NULL) - fatal("%s line %d: Invalid SetEnv.", + if (strchr(arg, '=') == NULL) { + error("%s line %d: Invalid SetEnv.", filename, linenum); + return -1; + } if (!*activep || value != 0) continue; /* Adding a setenv var */ - if (options->num_setenv >= INT_MAX) - fatal("%s line %d: too many SetEnv.", + if (options->num_setenv >= INT_MAX) { + error("%s line %d: too many SetEnv.", filename, linenum); + return -1; + } options->setenv = xrecallocarray( options->setenv, options->num_setenv, options->num_setenv + 1, sizeof(*options->setenv)); @@ -1552,9 +1641,11 @@ parse_keytypes: /* no/false/yes/true, or a time spec */ intptr = &options->control_persist; arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing ControlPersist" + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing ControlPersist" " argument.", filename, linenum); + return -1; + } value = 0; value2 = 0; /* timeout */ if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) @@ -1563,9 +1654,11 @@ parse_keytypes: value = 1; else if ((value2 = convtime(arg)) >= 0) value = 1; - else - fatal("%.200s line %d: Bad ControlPersist argument.", + else { + error("%.200s line %d: Bad ControlPersist argument.", filename, linenum); + return -1; + } if (*activep && *intptr == -1) { *intptr = value; options->control_persist_timeout = value2; @@ -1583,11 +1676,17 @@ parse_keytypes: case oTunnelDevice: arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", filename, linenum); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", + filename, linenum); + return -1; + } value = a2tun(arg, &value2); - if (value == SSH_TUNID_ERR) - fatal("%.200s line %d: Bad tun device.", filename, linenum); + if (value == SSH_TUNID_ERR) { + error("%.200s line %d: Bad tun device.", + filename, linenum); + return -1; + } if (*activep) { options->tun_local = value; options->tun_remote = value2; @@ -1611,9 +1710,11 @@ parse_keytypes: goto parse_flag; case oInclude: - if (cmdline) - fatal("Include directive not supported as a " + if (cmdline) { + error("Include directive not supported as a " "command-line option"); + return -1; + } value = 0; while ((arg = strdelim(&s)) != NULL && *arg != '\0') { /* @@ -1623,9 +1724,11 @@ parse_keytypes: * as living in ~/.ssh for user configurations or * /etc/ssh for system ones. */ - if (*arg == '~' && (flags & SSHCONF_USERCONF) == 0) - fatal("%.200s line %d: bad include path %s.", + if (*arg == '~' && (flags & SSHCONF_USERCONF) == 0) { + error("%.200s line %d: bad include path %s.", filename, linenum, arg); + return -1; + } if (!path_absolute(arg) && *arg != '~') { xasprintf(&arg2, "%s/%s", (flags & SSHCONF_USERCONF) ? @@ -1639,9 +1742,11 @@ parse_keytypes: "files",filename, linenum, arg2); free(arg2); continue; - } else if (r != 0) - fatal("%.200s line %d: glob failed for %s.", + } else if (r != 0) { + error("%.200s line %d: glob failed for %s.", filename, linenum, arg2); + return -1; + } free(arg2); oactive = *activep; for (i = 0; i < gl.gl_pathc; i++) { @@ -1655,9 +1760,11 @@ parse_keytypes: (oactive ? 0 : SSHCONF_NEVERMATCH), activep, want_final_pass, depth + 1); if (r != 1 && errno != ENOENT) { - fatal("Can't open user config file " + error("Can't open user config file " "%.100s: %.100s", gl.gl_pathv[i], strerror(errno)); + globfree(&gl); + return -1; } /* * don't let Match in includes clobber the @@ -1675,15 +1782,19 @@ parse_keytypes: case oIPQoS: arg = strdelim(&s); - if ((value = parse_ipqos(arg)) == -1) - fatal("%s line %d: Bad IPQoS value: %s", + if ((value = parse_ipqos(arg)) == -1) { + error("%s line %d: Bad IPQoS value: %s", filename, linenum, arg); + return -1; + } arg = strdelim(&s); if (arg == NULL) value2 = value; - else if ((value2 = parse_ipqos(arg)) == -1) - fatal("%s line %d: Bad IPQoS value: %s", + else if ((value2 = parse_ipqos(arg)) == -1) { + error("%s line %d: Bad IPQoS value: %s", filename, linenum, arg); + return -1; + } if (*activep) { options->ip_qos_interactive = value; options->ip_qos_bulk = value2; @@ -1707,14 +1818,18 @@ parse_keytypes: value = options->num_canonical_domains != 0; while ((arg = strdelim(&s)) != NULL && *arg != '\0') { if (!valid_domain(arg, 1, &errstr)) { - fatal("%s line %d: %s", filename, linenum, + error("%s line %d: %s", filename, linenum, errstr); + return -1; } if (!*activep || value) continue; - if (options->num_canonical_domains >= MAX_CANON_DOMAINS) - fatal("%s line %d: too many hostname suffixes.", + if (options->num_canonical_domains >= + MAX_CANON_DOMAINS) { + error("%s line %d: too many hostname suffixes.", filename, linenum); + return -1; + } options->canonical_domains[ options->num_canonical_domains++] = xstrdup(arg); } @@ -1730,18 +1845,22 @@ parse_keytypes: lowercase(arg); if ((arg2 = strchr(arg, ':')) == NULL || arg2[1] == '\0') { - fatal("%s line %d: " + error("%s line %d: " "Invalid permitted CNAME \"%s\"", filename, linenum, arg); + return -1; } *arg2 = '\0'; arg2++; } if (!*activep || value) continue; - if (options->num_permitted_cnames >= MAX_CANON_DOMAINS) - fatal("%s line %d: too many permitted CNAMEs.", + if (options->num_permitted_cnames >= + MAX_CANON_DOMAINS) { + error("%s line %d: too many permitted CNAMEs.", filename, linenum); + return -1; + } cname = options->permitted_cnames + options->num_permitted_cnames++; cname->source_list = xstrdup(arg); @@ -1764,12 +1883,17 @@ parse_keytypes: case oStreamLocalBindMask: arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing StreamLocalBindMask argument.", filename, linenum); + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing StreamLocalBindMask " + "argument.", filename, linenum); + return -1; + } /* Parse mode in octal format */ value = strtol(arg, &endofnumber, 8); - if (arg == endofnumber || value < 0 || value > 0777) - fatal("%.200s line %d: Bad mask.", filename, linenum); + if (arg == endofnumber || value < 0 || value > 0777) { + error("%.200s line %d: Bad mask.", filename, linenum); + return -1; + } options->fwd_opts.streamlocal_bind_mask = (mode_t)value; break; @@ -1784,12 +1908,16 @@ parse_keytypes: case oFingerprintHash: intptr = &options->fingerprint_hash; arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", filename, linenum); - if ((value = ssh_digest_alg_by_name(arg)) == -1) - fatal("%.200s line %d: Invalid hash algorithm \"%s\".", + return -1; + } + if ((value = ssh_digest_alg_by_name(arg)) == -1) { + error("%.200s line %d: Invalid hash algorithm \"%s\".", filename, linenum, arg); + return -1; + } if (*activep && *intptr == -1) *intptr = value; break; @@ -1815,17 +1943,24 @@ parse_keytypes: value2 = 0; /* unlimited lifespan by default */ if (value == 3 && arg2 != NULL) { /* allow "AddKeysToAgent confirm 5m" */ - if ((value2 = convtime(arg2)) == -1 || value2 > INT_MAX) - fatal("%s line %d: invalid time value.", + if ((value2 = convtime(arg2)) == -1 || + value2 > INT_MAX) { + error("%s line %d: invalid time value.", filename, linenum); + return -1; + } } else if (value == -1 && arg2 == NULL) { - if ((value2 = convtime(arg)) == -1 || value2 > INT_MAX) - fatal("%s line %d: unsupported option", + if ((value2 = convtime(arg)) == -1 || + value2 > INT_MAX) { + error("%s line %d: unsupported option", filename, linenum); + return -1; + } value = 1; /* yes */ } else if (value == -1 || arg2 != NULL) { - fatal("%s line %d: unsupported option", + error("%s line %d: unsupported option", filename, linenum); + return -1; } if (*activep && options->add_keys_to_agent == -1) { options->add_keys_to_agent = value; @@ -1836,19 +1971,25 @@ parse_keytypes: case oIdentityAgent: charptr = &options->identity_agent; arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", + if (!arg || *arg == '\0') { + error("%.200s line %d: Missing argument.", filename, linenum); + return -1; + } parse_agent_path: /* Extra validation if the string represents an env var. */ - if ((arg2 = dollar_expand(&r, arg)) == NULL || r) - fatal("%.200s line %d: Invalid environment expansion " + if ((arg2 = dollar_expand(&r, arg)) == NULL || r) { + error("%.200s line %d: Invalid environment expansion " "%s.", filename, linenum, arg); + return -1; + } free(arg2); /* check for legacy environment format */ - if (arg[0] == '$' && arg[1] != '{' && !valid_env_name(arg + 1)) { - fatal("%.200s line %d: Invalid environment name %s.", + if (arg[0] == '$' && arg[1] != '{' && + !valid_env_name(arg + 1)) { + error("%.200s line %d: Invalid environment name %s.", filename, linenum, arg); + return -1; } if (*activep && *charptr == NULL) *charptr = xstrdup(arg); @@ -1865,13 +2006,15 @@ parse_keytypes: return 0; default: - fatal_f("Unimplemented opcode %d", opcode); + error("%s line %d: Unimplemented opcode %d", + filename, linenum, opcode); } /* Check that there is no garbage at end of line. */ if ((arg = strdelim(&s)) != NULL && *arg != '\0') { - fatal("%.200s line %d: garbage at end of line; \"%.200s\".", + error("%.200s line %d: garbage at end of line; \"%.200s\".", filename, linenum, arg); + return -1; } return 0; } @@ -2005,7 +2148,9 @@ initialize_options(Options * options) options->hostkeyalgorithms = NULL; options->ca_sign_algorithms = NULL; options->num_identity_files = 0; + memset(options->identity_keys, 0, sizeof(options->identity_keys)); options->num_certificate_files = 0; + memset(options->certificates, 0, sizeof(options->certificates)); options->hostname = NULL; options->host_key_alias = NULL; options->proxy_command = NULL; @@ -2093,12 +2238,12 @@ fill_default_options_for_canonicalization(Options *options) * Called after processing other sources of option data, this fills those * options for which no value has been specified with their default values. */ -void +int fill_default_options(Options * options) { char *all_cipher, *all_mac, *all_kex, *all_key, *all_sig; char *def_cipher, *def_mac, *def_kex, *def_key, *def_sig; - int r; + int ret = 0, r; if (options->forward_agent == -1) options->forward_agent = 0; @@ -2122,7 +2267,7 @@ fill_default_options(Options * options) clear_forwardings(options); if (options->xauth_location == NULL) - options->xauth_location = _PATH_XAUTH; + options->xauth_location = xstrdup(_PATH_XAUTH); if (options->fwd_opts.gateway_ports == -1) options->fwd_opts.gateway_ports = 0; if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) @@ -2280,8 +2425,10 @@ fill_default_options(Options * options) #define ASSEMBLE(what, defaults, all) \ do { \ if ((r = kex_assemble_names(&options->what, \ - defaults, all)) != 0) \ - fatal_fr(r, "%s", #what); \ + defaults, all)) != 0) { \ + error_fr(r, "%s", #what); \ + goto fail; \ + } \ } while (0) ASSEMBLE(ciphers, def_cipher, all_cipher); ASSEMBLE(macs, def_mac, all_mac); @@ -2290,16 +2437,6 @@ fill_default_options(Options * options) ASSEMBLE(pubkey_key_types, def_key, all_key); ASSEMBLE(ca_sign_algorithms, def_sig, all_sig); #undef ASSEMBLE - free(all_cipher); - free(all_mac); - free(all_kex); - free(all_key); - free(all_sig); - free(def_cipher); - free(def_mac); - free(def_kex); - kex_default_pk_alg_filtered = def_key; /* save for later use */ - free(def_sig); #define CLEAR_ON_NONE(v) \ do { \ @@ -2326,6 +2463,103 @@ fill_default_options(Options * options) /* options->hostname will be set in the main program if appropriate */ /* options->host_key_alias should not be set by default */ /* options->preferred_authentications will be set in ssh */ + + /* success */ + ret = 0; + fail: + free(all_cipher); + free(all_mac); + free(all_kex); + free(all_key); + free(all_sig); + free(def_cipher); + free(def_mac); + free(def_kex); + free(def_key); + free(def_sig); + return ret; +} + +void +free_options(Options *o) +{ + int i; + + if (o == NULL) + return; + +#define FREE_ARRAY(type, n, a) \ + do { \ + type _i; \ + for (_i = 0; _i < (n); _i++) \ + free((a)[_i]); \ + } while (0) + + free(o->forward_agent_sock_path); + free(o->xauth_location); + FREE_ARRAY(u_int, o->num_log_verbose, o->log_verbose); + free(o->log_verbose); + free(o->ciphers); + free(o->macs); + free(o->hostkeyalgorithms); + free(o->kex_algorithms); + free(o->ca_sign_algorithms); + free(o->hostname); + free(o->host_key_alias); + free(o->proxy_command); + free(o->user); + FREE_ARRAY(u_int, o->num_system_hostfiles, o->system_hostfiles); + FREE_ARRAY(u_int, o->num_user_hostfiles, o->user_hostfiles); + free(o->preferred_authentications); + free(o->bind_address); + free(o->bind_interface); + free(o->pkcs11_provider); + free(o->sk_provider); + for (i = 0; i < o->num_identity_files; i++) { + free(o->identity_files[i]); + sshkey_free(o->identity_keys[i]); + } + for (i = 0; i < o->num_certificate_files; i++) { + free(o->certificate_files[i]); + sshkey_free(o->certificates[i]); + } + free(o->identity_agent); + for (i = 0; i < o->num_local_forwards; i++) { + free(o->local_forwards[i].listen_host); + free(o->local_forwards[i].listen_path); + free(o->local_forwards[i].connect_host); + free(o->local_forwards[i].connect_path); + } + free(o->local_forwards); + for (i = 0; i < o->num_remote_forwards; i++) { + free(o->remote_forwards[i].listen_host); + free(o->remote_forwards[i].listen_path); + free(o->remote_forwards[i].connect_host); + free(o->remote_forwards[i].connect_path); + } + free(o->remote_forwards); + free(o->stdio_forward_host); + FREE_ARRAY(int, o->num_send_env, o->send_env); + free(o->send_env); + FREE_ARRAY(int, o->num_setenv, o->setenv); + free(o->setenv); + free(o->control_path); + free(o->local_command); + free(o->remote_command); + FREE_ARRAY(int, o->num_canonical_domains, o->canonical_domains); + for (i = 0; i < o->num_permitted_cnames; i++) { + free(o->permitted_cnames[i].source_list); + free(o->permitted_cnames[i].target_list); + } + free(o->revoked_host_keys); + free(o->hostbased_key_types); + free(o->pubkey_key_types); + free(o->jump_user); + free(o->jump_host); + free(o->jump_extra); + free(o->ignored_unknown); + explicit_bzero(o, sizeof(*o)); +#undef FREE_ARRAY } struct fwdarg { @@ -2565,12 +2799,12 @@ parse_jump(const char *s, Options *o, int active) if (first) { /* First argument and configuration is active */ - if (parse_ssh_uri(cp, &user, &host, &port) == -1 || + if (parse_ssh_uri(cp, &user, &host, &port) == -1 && parse_user_host_port(cp, &user, &host, &port) != 0) goto out; } else { /* Subsequent argument or inactive configuration */ - if (parse_ssh_uri(cp, NULL, NULL, NULL) == -1 || + if (parse_ssh_uri(cp, NULL, NULL, NULL) == -1 && parse_user_host_port(cp, NULL, NULL, NULL) != 0) goto out; } @@ -2604,12 +2838,27 @@ parse_jump(const char *s, Options *o, int active) int parse_ssh_uri(const char *uri, char **userp, char **hostp, int *portp) { - char *path; - int r; + char *user = NULL, *host = NULL, *path = NULL; + int r, port; - r = parse_uri("ssh", uri, userp, hostp, portp, &path); + r = parse_uri("ssh", uri, &user, &host, &port, &path); if (r == 0 && path != NULL) r = -1; /* path not allowed */ + if (r == 0) { + if (userp != NULL) { + *userp = user; + user = NULL; + } + if (hostp != NULL) { + *hostp = host; + host = NULL; + } + if (portp != NULL) + *portp = port; + } + free(user); + free(host); + free(path); return r; } -- cgit v1.2.1