From 5a18868b2d506b9e2254047d8d9fb9df5b2a8ae0 Mon Sep 17 00:00:00 2001 From: des Date: Sun, 7 Feb 2016 11:38:54 +0000 Subject: MFH (r265214, r294333, r294407, r294467): misc prop fixes MFH (r285975, r287143): register mergeinfo for security fixes MFH (r294497, r294498, r295139): internal documentation MFH (r294328): upgrade to openssh 6.7p1, re-add libwrap MFH (r294332): upgrade to openssh 6.8p1 MFH (r294367): update pam_ssh for api changes MFH (r294909): switch usedns back on MFH (r294336): upgrade to openssh 6.9p1 MFH (r294495): re-enable dsa keys MFH (r294464): upgrade to openssh 7.0p1 MFH (r294496): upgrade to openssh 7.1p2 Approved by: re (gjb) Relnotes: yes --- crypto/openssh/readconf.c | 806 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 674 insertions(+), 132 deletions(-) (limited to 'crypto/openssh/readconf.c') diff --git a/crypto/openssh/readconf.c b/crypto/openssh/readconf.c index df8387c..326a59c 100644 --- a/crypto/openssh/readconf.c +++ b/crypto/openssh/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.218 2014/02/23 20:11:36 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.239 2015/07/30 00:01:34 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -20,6 +20,7 @@ __RCSID("$FreeBSD$"); #include #include #include +#include #include #include @@ -29,6 +30,7 @@ __RCSID("$FreeBSD$"); #include #include #include +#include #include #ifdef HAVE_PATHS_H # include @@ -42,6 +44,9 @@ __RCSID("$FreeBSD$"); #ifdef HAVE_UTIL_H #include #endif +#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) +# include +#endif #include "xmalloc.h" #include "ssh.h" @@ -49,14 +54,15 @@ __RCSID("$FreeBSD$"); #include "cipher.h" #include "pathnames.h" #include "log.h" -#include "key.h" +#include "sshkey.h" +#include "misc.h" #include "readconf.h" #include "match.h" -#include "misc.h" -#include "buffer.h" #include "kex.h" #include "mac.h" #include "uidswap.h" +#include "myproposal.h" +#include "digest.h" #include "version.h" /* Format of the configuration file: @@ -126,6 +132,7 @@ __RCSID("$FreeBSD$"); typedef enum { oBadOption, + oVersionAddendum, oHost, oMatch, oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, oGatewayPorts, oExitOnForwardFailure, @@ -137,7 +144,7 @@ typedef enum { oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs, - oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication, + oPubkeyAuthentication, oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, @@ -152,7 +159,9 @@ typedef enum { oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, - oVersionAddendum, + oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, + oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes, + oPubkeyAcceptedKeyTypes, oIgnoredUnknownOption, oDeprecated, oUnsupported } OpCodes; @@ -214,7 +223,7 @@ static struct { { "globalknownhostsfile", oGlobalKnownHostsFile }, { "globalknownhostsfile2", oDeprecated }, { "userknownhostsfile", oUserKnownHostsFile }, - { "userknownhostsfile2", oDeprecated }, + { "userknownhostsfile2", oDeprecated }, { "connectionattempts", oConnectionAttempts }, { "batchmode", oBatchMode }, { "checkhostip", oCheckHostIP }, @@ -265,11 +274,20 @@ static struct { { "canonicalizehostname", oCanonicalizeHostname }, { "canonicalizemaxdots", oCanonicalizeMaxDots }, { "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs }, + { "streamlocalbindmask", oStreamLocalBindMask }, + { "streamlocalbindunlink", oStreamLocalBindUnlink }, + { "revokedhostkeys", oRevokedHostKeys }, + { "fingerprinthash", oFingerprintHash }, + { "updatehostkeys", oUpdateHostkeys }, + { "hostbasedkeytypes", oHostbasedKeyTypes }, + { "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes }, { "ignoreunknown", oIgnoreUnknown }, { "hpndisabled", oDeprecated }, { "hpnbuffersize", oDeprecated }, { "tcprcvbufpoll", oDeprecated }, { "tcprcvbuf", oDeprecated }, + { "noneenabled", oUnsupported }, + { "noneswitch", oUnsupported }, { "versionaddendum", oVersionAddendum }, { NULL, oBadOption } @@ -281,9 +299,9 @@ static struct { */ void -add_local_forward(Options *options, const Forward *newfwd) +add_local_forward(Options *options, const struct Forward *newfwd) { - Forward *fwd; + struct Forward *fwd; #ifndef NO_IPPORT_RESERVED_CONCEPT extern uid_t original_real_uid; int ipport_reserved; @@ -299,17 +317,21 @@ add_local_forward(Options *options, const Forward *newfwd) ipport_reserved = IPPORT_RESERVED; #endif if (newfwd->listen_port < ipport_reserved && original_real_uid != 0) + if (newfwd->listen_port < ipport_reserved && original_real_uid != 0 && + newfwd->listen_path == NULL) fatal("Privileged ports can only be forwarded by root."); #endif - options->local_forwards = xrealloc(options->local_forwards, + options->local_forwards = xreallocarray(options->local_forwards, options->num_local_forwards + 1, sizeof(*options->local_forwards)); fwd = &options->local_forwards[options->num_local_forwards++]; fwd->listen_host = newfwd->listen_host; fwd->listen_port = newfwd->listen_port; + fwd->listen_path = newfwd->listen_path; fwd->connect_host = newfwd->connect_host; fwd->connect_port = newfwd->connect_port; + fwd->connect_path = newfwd->connect_path; } /* @@ -318,19 +340,21 @@ add_local_forward(Options *options, const Forward *newfwd) */ void -add_remote_forward(Options *options, const Forward *newfwd) +add_remote_forward(Options *options, const struct Forward *newfwd) { - Forward *fwd; + struct Forward *fwd; - options->remote_forwards = xrealloc(options->remote_forwards, + options->remote_forwards = xreallocarray(options->remote_forwards, options->num_remote_forwards + 1, sizeof(*options->remote_forwards)); fwd = &options->remote_forwards[options->num_remote_forwards++]; fwd->listen_host = newfwd->listen_host; fwd->listen_port = newfwd->listen_port; + fwd->listen_path = newfwd->listen_path; fwd->connect_host = newfwd->connect_host; fwd->connect_port = newfwd->connect_port; + fwd->connect_path = newfwd->connect_path; fwd->handle = newfwd->handle; fwd->allocated_port = 0; } @@ -342,7 +366,9 @@ clear_forwardings(Options *options) for (i = 0; i < options->num_local_forwards; i++) { free(options->local_forwards[i].listen_host); + free(options->local_forwards[i].listen_path); free(options->local_forwards[i].connect_host); + free(options->local_forwards[i].connect_path); } if (options->num_local_forwards > 0) { free(options->local_forwards); @@ -351,7 +377,9 @@ clear_forwardings(Options *options) options->num_local_forwards = 0; for (i = 0; i < options->num_remote_forwards; i++) { free(options->remote_forwards[i].listen_host); + free(options->remote_forwards[i].listen_path); free(options->remote_forwards[i].connect_host); + free(options->remote_forwards[i].connect_path); } if (options->num_remote_forwards > 0) { free(options->remote_forwards); @@ -366,6 +394,7 @@ add_identity_file(Options *options, const char *dir, const char *filename, int userprovided) { char *path; + int i; if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) fatal("Too many identity files specified (max %d)", @@ -376,6 +405,16 @@ add_identity_file(Options *options, const char *dir, const char *filename, else (void)xasprintf(&path, "%.100s%.100s", dir, filename); + /* Avoid registering duplicates */ + for (i = 0; i < options->num_identity_files; i++) { + if (options->identity_file_userprovided[i] == userprovided && + strcmp(options->identity_files[i], path) == 0) { + debug2("%s: ignoring duplicate key %s", __func__, path); + free(path); + return; + } + } + options->identity_file_userprovided[options->num_identity_files] = userprovided; options->identity_files[options->num_identity_files++] = path; @@ -463,7 +502,7 @@ execute_in_shell(const char *cmd) if (!WIFEXITED(status)) { error("command '%.100s' exited abnormally", cmd); return -1; - } + } debug3("command returned status %d", WEXITSTATUS(status)); return WEXITSTATUS(status); } @@ -473,12 +512,12 @@ execute_in_shell(const char *cmd) */ static int match_cfg_line(Options *options, char **condition, struct passwd *pw, - const char *host_arg, const char *filename, int linenum) + const char *host_arg, const char *original_host, int post_canon, + const char *filename, int linenum) { - char *arg, *attrib, *cmd, *cp = *condition, *host; + char *arg, *oattrib, *attrib, *cmd, *cp = *condition, *host, *criteria; const char *ruser; - int r, port, result = 1, attributes = 0; - size_t len; + int r, port, this_result, result = 1, attributes = 0, negate; char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; /* @@ -494,53 +533,63 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw, } else host = xstrdup(host_arg); - debug3("checking match for '%s' host %s", cp, host); - while ((attrib = strdelim(&cp)) && *attrib != '\0') { - attributes++; + debug2("checking match for '%s' host %s originally %s", + cp, host, original_host); + while ((oattrib = attrib = strdelim(&cp)) && *attrib != '\0') { + criteria = NULL; + this_result = 1; + if ((negate = attrib[0] == '!')) + attrib++; + /* criteria "all" and "canonical" have no argument */ if (strcasecmp(attrib, "all") == 0) { - if (attributes != 1 || + if (attributes > 1 || ((arg = strdelim(&cp)) != NULL && *arg != '\0')) { - error("'all' cannot be combined with other " - "Match attributes"); + error("%.200s line %d: '%s' cannot be combined " + "with other Match attributes", + filename, linenum, oattrib); result = -1; goto out; } - *condition = cp; - result = 1; + if (result) + result = negate ? 0 : 1; goto out; } + attributes++; + if (strcasecmp(attrib, "canonical") == 0) { + r = !!post_canon; /* force bitmask member to boolean */ + if (r == (negate ? 1 : 0)) + this_result = result = 0; + debug3("%.200s line %d: %smatched '%s'", + filename, linenum, + this_result ? "" : "not ", oattrib); + continue; + } + /* All other criteria require an argument */ if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { error("Missing Match criteria for %s", attrib); result = -1; goto out; } - len = strlen(arg); if (strcasecmp(attrib, "host") == 0) { - if (match_hostname(host, arg, len) != 1) - result = 0; - else - debug("%.200s line %d: matched 'Host %.100s' ", - filename, linenum, host); + criteria = xstrdup(host); + r = match_hostname(host, arg) == 1; + if (r == (negate ? 1 : 0)) + this_result = result = 0; } else if (strcasecmp(attrib, "originalhost") == 0) { - if (match_hostname(host_arg, arg, len) != 1) - result = 0; - else - debug("%.200s line %d: matched " - "'OriginalHost %.100s' ", - filename, linenum, host_arg); + criteria = xstrdup(original_host); + r = match_hostname(original_host, arg) == 1; + if (r == (negate ? 1 : 0)) + this_result = result = 0; } else if (strcasecmp(attrib, "user") == 0) { - if (match_pattern_list(ruser, arg, len, 0) != 1) - result = 0; - else - debug("%.200s line %d: matched 'User %.100s' ", - filename, linenum, ruser); + criteria = xstrdup(ruser); + r = match_pattern_list(ruser, arg, 0) == 1; + if (r == (negate ? 1 : 0)) + this_result = result = 0; } else if (strcasecmp(attrib, "localuser") == 0) { - if (match_pattern_list(pw->pw_name, arg, len, 0) != 1) - result = 0; - else - debug("%.200s line %d: matched " - "'LocalUser %.100s' ", - filename, linenum, pw->pw_name); + criteria = xstrdup(pw->pw_name); + r = match_pattern_list(pw->pw_name, arg, 0) == 1; + if (r == (negate ? 1 : 0)) + this_result = result = 0; } else if (strcasecmp(attrib, "exec") == 0) { if (gethostname(thishost, sizeof(thishost)) == -1) fatal("gethostname: %s", strerror(errno)); @@ -553,47 +602,49 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw, "d", pw->pw_dir, "h", host, "l", thishost, - "n", host_arg, + "n", original_host, "p", portstr, "r", ruser, "u", pw->pw_name, (char *)NULL); if (result != 1) { /* skip execution if prior predicate failed */ - debug("%.200s line %d: skipped exec \"%.100s\"", - filename, linenum, cmd); - } else { - r = execute_in_shell(cmd); - if (r == -1) { - fatal("%.200s line %d: match exec " - "'%.100s' error", filename, - linenum, cmd); - } else if (r == 0) { - debug("%.200s line %d: matched " - "'exec \"%.100s\"'", filename, - linenum, cmd); - } else { - debug("%.200s line %d: no match " - "'exec \"%.100s\"'", filename, - linenum, cmd); - result = 0; - } + debug3("%.200s line %d: skipped exec " + "\"%.100s\"", filename, linenum, cmd); + free(cmd); + continue; } + r = execute_in_shell(cmd); + if (r == -1) { + fatal("%.200s line %d: match exec " + "'%.100s' error", filename, + linenum, cmd); + } + criteria = xstrdup(cmd); free(cmd); + /* Force exit status to boolean */ + r = r == 0; + if (r == (negate ? 1 : 0)) + this_result = result = 0; } else { error("Unsupported Match attribute %s", attrib); result = -1; goto out; } + debug3("%.200s line %d: %smatched '%s \"%.100s\"' ", + filename, linenum, this_result ? "": "not ", + oattrib, criteria); + free(criteria); } if (attributes == 0) { error("One or more attributes required for Match"); result = -1; goto out; } - debug3("match %sfound", result ? "" : "not "); - *condition = cp; out: + if (result != -1) + debug2("match %sfound", result ? "" : "not "); + *condition = cp; free(host); return result; } @@ -638,8 +689,8 @@ parse_token(const char *cp, const char *filename, int linenum, for (i = 0; keywords[i].name; i++) if (strcmp(cp, keywords[i].name) == 0) return keywords[i].opcode; - if (ignored_unknown != NULL && match_pattern_list(cp, ignored_unknown, - strlen(ignored_unknown), 1) == 1) + if (ignored_unknown != NULL && + match_pattern_list(cp, ignored_unknown, 1) == 1) return oIgnoredUnknownOption; error("%s: line %d: Bad configuration option: %s", filename, linenum, cp); @@ -716,7 +767,8 @@ static const struct multistate multistate_canonicalizehostname[] = { #define WHITESPACE " \t\r\n" int process_config_line(Options *options, struct passwd *pw, const char *host, - char *line, const char *filename, int linenum, int *activep, int userconfig) + const char *original_host, char *line, const char *filename, + int linenum, int *activep, int flags) { char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; char **cpptr, fwdarg[256]; @@ -725,7 +777,7 @@ process_config_line(Options *options, struct passwd *pw, const char *host, LogLevel *log_level_ptr; long long val64; size_t len; - Forward fwd; + struct Forward fwd; const struct multistate *multistate_ptr; struct allowed_cname *cname; @@ -735,7 +787,9 @@ process_config_line(Options *options, struct passwd *pw, const char *host, } /* Strip trailing whitespace */ - for (len = strlen(line) - 1; len > 0; len--) { + if ((len = strlen(line)) == 0) + return 0; + for (len--; len > 0; len--) { if (strchr(WHITESPACE, line[len]) == NULL) break; line[len] = '\0'; @@ -772,7 +826,9 @@ parse_time: if (!arg || *arg == '\0') fatal("%s line %d: missing time value.", filename, linenum); - if ((value = convtime(arg)) == -1) + if (strcmp(arg, "none") == 0) + value = -1; + else if ((value = convtime(arg)) == -1) fatal("%s line %d: invalid time value.", filename, linenum); if (*activep && *intptr == -1) @@ -809,13 +865,13 @@ parse_time: case oForwardX11Trusted: intptr = &options->forward_x11_trusted; goto parse_flag; - + case oForwardX11Timeout: intptr = &options->forward_x11_timeout; goto parse_time; case oGatewayPorts: - intptr = &options->gateway_ports; + intptr = &options->fwd_opts.gateway_ports; goto parse_flag; case oExitOnForwardFailure: @@ -944,7 +1000,8 @@ parse_time: if (*intptr >= SSH_MAX_IDENTITY_FILES) fatal("%.200s line %d: Too many identity files specified (max %d).", filename, linenum, SSH_MAX_IDENTITY_FILES); - add_identity_file(options, NULL, arg, userconfig); + add_identity_file(options, NULL, + arg, flags & SSHCONF_USERCONF); } break; @@ -1053,7 +1110,7 @@ parse_int: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!ciphers_valid(arg)) + if (!ciphers_valid(*arg == '+' ? arg + 1 : arg)) fatal("%.200s line %d: Bad SSH2 cipher spec '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->ciphers == NULL) @@ -1064,7 +1121,7 @@ parse_int: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!mac_valid(arg)) + if (!mac_valid(*arg == '+' ? arg + 1 : arg)) fatal("%.200s line %d: Bad SSH2 Mac spec '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->macs == NULL) @@ -1076,7 +1133,7 @@ parse_int: if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!kex_names_valid(arg)) + if (!kex_names_valid(*arg == '+' ? arg + 1 : arg)) fatal("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->kex_algorithms == NULL) @@ -1084,14 +1141,17 @@ parse_int: break; case oHostKeyAlgorithms: + charptr = &options->hostkeyalgorithms; +parse_keytypes: arg = strdelim(&s); if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!key_names_valid2(arg)) - fatal("%.200s line %d: Bad protocol 2 host key algorithms '%s'.", - filename, linenum, arg ? arg : ""); - if (*activep && options->hostkeyalgorithms == NULL) - options->hostkeyalgorithms = xstrdup(arg); + fatal("%.200s line %d: Missing argument.", + filename, linenum); + if (!sshkey_names_valid2(*arg == '+' ? arg + 1 : arg, 1)) + fatal("%s line %d: Bad key types '%s'.", + filename, linenum, arg ? arg : ""); + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); break; case oProtocol: @@ -1192,8 +1252,8 @@ parse_int: if (cmdline) fatal("Host directive not supported as a command-line " "option"); - value = match_cfg_line(options, &s, pw, host, - filename, linenum); + value = match_cfg_line(options, &s, pw, host, original_host, + flags & SSHCONF_POSTCANON, filename, linenum); if (value < 0) fatal("%.200s line %d: Bad Match condition", filename, linenum); @@ -1205,13 +1265,13 @@ parse_int: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (arg[0] == '^' && arg[2] == 0 && + if (strcmp(arg, "none") == 0) + value = SSH_ESCAPECHAR_NONE; + else if (arg[1] == '\0') + value = (u_char) arg[0]; + else if (arg[0] == '^' && arg[2] == 0 && (u_char) arg[1] >= 64 && (u_char) arg[1] < 128) value = (u_char) arg[1] & 31; - else if (strlen(arg) == 1) - value = (u_char) arg[0]; - else if (strcmp(arg, "none") == 0) - value = SSH_ESCAPECHAR_NONE; else { fatal("%.200s line %d: Bad escape character.", filename, linenum); @@ -1431,6 +1491,51 @@ parse_int: intptr = &options->canonicalize_fallback_local; goto parse_flag; + case oStreamLocalBindMask: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing StreamLocalBindMask argument.", filename, linenum); + /* 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); + options->fwd_opts.streamlocal_bind_mask = (mode_t)value; + break; + + case oStreamLocalBindUnlink: + intptr = &options->fwd_opts.streamlocal_bind_unlink; + goto parse_flag; + + case oRevokedHostKeys: + charptr = &options->revoked_host_keys; + goto parse_string; + + case oFingerprintHash: + intptr = &options->fingerprint_hash; + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", + filename, linenum); + if ((value = ssh_digest_alg_by_name(arg)) == -1) + fatal("%.200s line %d: Invalid hash algorithm \"%s\".", + filename, linenum, arg); + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oUpdateHostkeys: + intptr = &options->update_hostkeys; + multistate_ptr = multistate_yesnoask; + goto parse_multistate; + + case oHostbasedKeyTypes: + charptr = &options->hostbased_key_types; + goto parse_keytypes; + + case oPubkeyAcceptedKeyTypes: + charptr = &options->pubkey_key_types; + goto parse_keytypes; + case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); @@ -1442,7 +1547,7 @@ parse_int: return 0; default: - fatal("process_config_line: Unimplemented opcode %d", opcode); + fatal("%s: Unimplemented opcode %d", __func__, opcode); } /* Check that there is no garbage at end of line. */ @@ -1462,7 +1567,7 @@ parse_int: int read_config_file(const char *filename, struct passwd *pw, const char *host, - Options *options, int flags) + const char *original_host, Options *options, int flags) { FILE *f; char line[1024]; @@ -1493,8 +1598,8 @@ read_config_file(const char *filename, struct passwd *pw, const char *host, while (fgets(line, sizeof(line), f)) { /* Update line number counter. */ linenum++; - if (process_config_line(options, pw, host, line, filename, - linenum, &active, flags & SSHCONF_USERCONF) != 0) + if (process_config_line(options, pw, host, original_host, + line, filename, linenum, &active, flags) != 0) bad_options++; } fclose(f); @@ -1522,13 +1627,16 @@ void initialize_options(Options * options) { memset(options, 'X', sizeof(*options)); + options->version_addendum = NULL; options->forward_agent = -1; options->forward_x11 = -1; options->forward_x11_trusted = -1; options->forward_x11_timeout = -1; options->exit_on_forward_failure = -1; options->xauth_location = NULL; - options->gateway_ports = -1; + options->fwd_opts.gateway_ports = -1; + options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; + options->fwd_opts.streamlocal_bind_unlink = -1; options->use_privileged_port = -1; options->rsa_authentication = -1; options->pubkey_authentication = -1; @@ -1605,7 +1713,11 @@ initialize_options(Options * options) options->canonicalize_max_dots = -1; options->canonicalize_fallback_local = -1; options->canonicalize_hostname = -1; - options->version_addendum = NULL; + options->revoked_host_keys = NULL; + options->fingerprint_hash = -1; + options->update_hostkeys = -1; + options->hostbased_key_types = NULL; + options->pubkey_key_types = NULL; } /* @@ -1642,8 +1754,12 @@ fill_default_options(Options * options) options->exit_on_forward_failure = 0; if (options->xauth_location == NULL) options->xauth_location = _PATH_XAUTH; - if (options->gateway_ports == -1) - options->gateway_ports = 0; + if (options->fwd_opts.gateway_ports == -1) + options->fwd_opts.gateway_ports = 0; + if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) + options->fwd_opts.streamlocal_bind_mask = 0177; + if (options->fwd_opts.streamlocal_bind_unlink == -1) + options->fwd_opts.streamlocal_bind_unlink = 0; if (options->use_privileged_port == -1) options->use_privileged_port = 0; if (options->rsa_authentication == -1) @@ -1687,9 +1803,6 @@ fill_default_options(Options * options) /* Selected in ssh_login(). */ if (options->cipher == -1) options->cipher = SSH_CIPHER_NOT_SET; - /* options->ciphers, default set in myproposals.h */ - /* options->macs, default set in myproposals.h */ - /* options->kex_algorithms, default set in myproposals.h */ /* options->hostkeyalgorithms, default set in myproposals.h */ if (options->protocol == SSH_PROTO_UNKNOWN) options->protocol = SSH_PROTO_2; @@ -1784,6 +1897,19 @@ fill_default_options(Options * options) options->canonicalize_fallback_local = 1; if (options->canonicalize_hostname == -1) options->canonicalize_hostname = SSH_CANONICALISE_NO; + if (options->fingerprint_hash == -1) + options->fingerprint_hash = SSH_FP_HASH_DEFAULT; + if (options->update_hostkeys == -1) + options->update_hostkeys = 0; + if (kex_assemble_names(KEX_CLIENT_ENCRYPT, &options->ciphers) != 0 || + kex_assemble_names(KEX_CLIENT_MAC, &options->macs) != 0 || + kex_assemble_names(KEX_CLIENT_KEX, &options->kex_algorithms) != 0 || + kex_assemble_names(KEX_DEFAULT_PK_ALG, + &options->hostbased_key_types) != 0 || + kex_assemble_names(KEX_DEFAULT_PK_ALG, + &options->pubkey_key_types) != 0) + fatal("%s: kex_assemble_names failed", __func__); + #define CLEAR_ON_NONE(v) \ do { \ if (option_clear_or_none(v)) { \ @@ -1794,6 +1920,7 @@ fill_default_options(Options * options) CLEAR_ON_NONE(options->local_command); CLEAR_ON_NONE(options->proxy_command); CLEAR_ON_NONE(options->control_path); + CLEAR_ON_NONE(options->revoked_host_keys); /* options->user will be set in the main program if appropriate */ /* options->hostname will be set in the main program if appropriate */ /* options->host_key_alias should not be set by default */ @@ -1802,22 +1929,93 @@ fill_default_options(Options * options) options->version_addendum = xstrdup(SSH_VERSION_FREEBSD); } +struct fwdarg { + char *arg; + int ispath; +}; + +/* + * parse_fwd_field + * parses the next field in a port forwarding specification. + * sets fwd to the parsed field and advances p past the colon + * or sets it to NULL at end of string. + * returns 0 on success, else non-zero. + */ +static int +parse_fwd_field(char **p, struct fwdarg *fwd) +{ + char *ep, *cp = *p; + int ispath = 0; + + if (*cp == '\0') { + *p = NULL; + return -1; /* end of string */ + } + + /* + * A field escaped with square brackets is used literally. + * XXX - allow ']' to be escaped via backslash? + */ + if (*cp == '[') { + /* find matching ']' */ + for (ep = cp + 1; *ep != ']' && *ep != '\0'; ep++) { + if (*ep == '/') + ispath = 1; + } + /* no matching ']' or not at end of field. */ + if (ep[0] != ']' || (ep[1] != ':' && ep[1] != '\0')) + return -1; + /* NUL terminate the field and advance p past the colon */ + *ep++ = '\0'; + if (*ep != '\0') + *ep++ = '\0'; + fwd->arg = cp + 1; + fwd->ispath = ispath; + *p = ep; + return 0; + } + + for (cp = *p; *cp != '\0'; cp++) { + switch (*cp) { + case '\\': + memmove(cp, cp + 1, strlen(cp + 1) + 1); + if (*cp == '\0') + return -1; + break; + case '/': + ispath = 1; + break; + case ':': + *cp++ = '\0'; + goto done; + } + } +done: + fwd->arg = *p; + fwd->ispath = ispath; + *p = cp; + return 0; +} + /* * parse_forward * parses a string containing a port forwarding specification of the form: * dynamicfwd == 0 - * [listenhost:]listenport:connecthost:connectport + * [listenhost:]listenport|listenpath:connecthost:connectport|connectpath + * listenpath:connectpath * dynamicfwd == 1 * [listenhost:]listenport * returns number of arguments parsed or zero on error */ int -parse_forward(Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) +parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) { + struct fwdarg fwdargs[4]; + char *p, *cp; int i; - char *p, *cp, *fwdarg[4]; - memset(fwd, '\0', sizeof(*fwd)); + memset(fwd, 0, sizeof(*fwd)); + memset(fwdargs, 0, sizeof(fwdargs)); cp = p = xstrdup(fwdspec); @@ -1825,39 +2023,70 @@ parse_forward(Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) while (isspace((u_char)*cp)) cp++; - for (i = 0; i < 4; ++i) - if ((fwdarg[i] = hpdelim(&cp)) == NULL) + for (i = 0; i < 4; ++i) { + if (parse_fwd_field(&cp, &fwdargs[i]) != 0) break; + } /* Check for trailing garbage */ - if (cp != NULL) + if (cp != NULL && *cp != '\0') { i = 0; /* failure */ + } switch (i) { case 1: - fwd->listen_host = NULL; - fwd->listen_port = a2port(fwdarg[0]); + if (fwdargs[0].ispath) { + fwd->listen_path = xstrdup(fwdargs[0].arg); + fwd->listen_port = PORT_STREAMLOCAL; + } else { + fwd->listen_host = NULL; + fwd->listen_port = a2port(fwdargs[0].arg); + } fwd->connect_host = xstrdup("socks"); break; case 2: - fwd->listen_host = xstrdup(cleanhostname(fwdarg[0])); - fwd->listen_port = a2port(fwdarg[1]); - fwd->connect_host = xstrdup("socks"); + if (fwdargs[0].ispath && fwdargs[1].ispath) { + fwd->listen_path = xstrdup(fwdargs[0].arg); + fwd->listen_port = PORT_STREAMLOCAL; + fwd->connect_path = xstrdup(fwdargs[1].arg); + fwd->connect_port = PORT_STREAMLOCAL; + } else if (fwdargs[1].ispath) { + fwd->listen_host = NULL; + fwd->listen_port = a2port(fwdargs[0].arg); + fwd->connect_path = xstrdup(fwdargs[1].arg); + fwd->connect_port = PORT_STREAMLOCAL; + } else { + fwd->listen_host = xstrdup(fwdargs[0].arg); + fwd->listen_port = a2port(fwdargs[1].arg); + fwd->connect_host = xstrdup("socks"); + } break; case 3: - fwd->listen_host = NULL; - fwd->listen_port = a2port(fwdarg[0]); - fwd->connect_host = xstrdup(cleanhostname(fwdarg[1])); - fwd->connect_port = a2port(fwdarg[2]); + if (fwdargs[0].ispath) { + fwd->listen_path = xstrdup(fwdargs[0].arg); + fwd->listen_port = PORT_STREAMLOCAL; + fwd->connect_host = xstrdup(fwdargs[1].arg); + fwd->connect_port = a2port(fwdargs[2].arg); + } else if (fwdargs[2].ispath) { + fwd->listen_host = xstrdup(fwdargs[0].arg); + fwd->listen_port = a2port(fwdargs[1].arg); + fwd->connect_path = xstrdup(fwdargs[2].arg); + fwd->connect_port = PORT_STREAMLOCAL; + } else { + fwd->listen_host = NULL; + fwd->listen_port = a2port(fwdargs[0].arg); + fwd->connect_host = xstrdup(fwdargs[1].arg); + fwd->connect_port = a2port(fwdargs[2].arg); + } break; case 4: - fwd->listen_host = xstrdup(cleanhostname(fwdarg[0])); - fwd->listen_port = a2port(fwdarg[1]); - fwd->connect_host = xstrdup(cleanhostname(fwdarg[2])); - fwd->connect_port = a2port(fwdarg[3]); + fwd->listen_host = xstrdup(fwdargs[0].arg); + fwd->listen_port = a2port(fwdargs[1].arg); + fwd->connect_host = xstrdup(fwdargs[2].arg); + fwd->connect_port = a2port(fwdargs[3].arg); break; default: i = 0; /* failure */ @@ -1869,29 +2098,342 @@ parse_forward(Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) if (!(i == 1 || i == 2)) goto fail_free; } else { - if (!(i == 3 || i == 4)) - goto fail_free; - if (fwd->connect_port <= 0) + if (!(i == 3 || i == 4)) { + if (fwd->connect_path == NULL && + fwd->listen_path == NULL) + goto fail_free; + } + if (fwd->connect_port <= 0 && fwd->connect_path == NULL) goto fail_free; } - if (fwd->listen_port < 0 || (!remotefwd && fwd->listen_port == 0)) + if ((fwd->listen_port < 0 && fwd->listen_path == NULL) || + (!remotefwd && fwd->listen_port == 0)) goto fail_free; - if (fwd->connect_host != NULL && strlen(fwd->connect_host) >= NI_MAXHOST) goto fail_free; + /* XXX - if connecting to a remote socket, max sun len may not match this host */ + if (fwd->connect_path != NULL && + strlen(fwd->connect_path) >= PATH_MAX_SUN) + goto fail_free; if (fwd->listen_host != NULL && strlen(fwd->listen_host) >= NI_MAXHOST) goto fail_free; - + if (fwd->listen_path != NULL && + strlen(fwd->listen_path) >= PATH_MAX_SUN) + goto fail_free; return (i); fail_free: free(fwd->connect_host); fwd->connect_host = NULL; + free(fwd->connect_path); + fwd->connect_path = NULL; free(fwd->listen_host); fwd->listen_host = NULL; + free(fwd->listen_path); + fwd->listen_path = NULL; return (0); } + +/* XXX the following is a near-vebatim copy from servconf.c; refactor */ +static const char * +fmt_multistate_int(int val, const struct multistate *m) +{ + u_int i; + + for (i = 0; m[i].key != NULL; i++) { + if (m[i].value == val) + return m[i].key; + } + return "UNKNOWN"; +} + +static const char * +fmt_intarg(OpCodes code, int val) +{ + if (val == -1) + return "unset"; + switch (code) { + case oAddressFamily: + return fmt_multistate_int(val, multistate_addressfamily); + case oVerifyHostKeyDNS: + case oStrictHostKeyChecking: + case oUpdateHostkeys: + return fmt_multistate_int(val, multistate_yesnoask); + case oControlMaster: + return fmt_multistate_int(val, multistate_controlmaster); + case oTunnel: + return fmt_multistate_int(val, multistate_tunnel); + case oRequestTTY: + return fmt_multistate_int(val, multistate_requesttty); + case oCanonicalizeHostname: + return fmt_multistate_int(val, multistate_canonicalizehostname); + case oFingerprintHash: + return ssh_digest_alg_name(val); + case oProtocol: + switch (val) { + case SSH_PROTO_1: + return "1"; + case SSH_PROTO_2: + return "2"; + case (SSH_PROTO_1|SSH_PROTO_2): + return "2,1"; + default: + return "UNKNOWN"; + } + default: + switch (val) { + case 0: + return "no"; + case 1: + return "yes"; + default: + return "UNKNOWN"; + } + } +} + +static const char * +lookup_opcode_name(OpCodes code) +{ + u_int i; + + for (i = 0; keywords[i].name != NULL; i++) + if (keywords[i].opcode == code) + return(keywords[i].name); + return "UNKNOWN"; +} + +static void +dump_cfg_int(OpCodes code, int val) +{ + printf("%s %d\n", lookup_opcode_name(code), val); +} + +static void +dump_cfg_fmtint(OpCodes code, int val) +{ + printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val)); +} + +static void +dump_cfg_string(OpCodes code, const char *val) +{ + if (val == NULL) + return; + printf("%s %s\n", lookup_opcode_name(code), val); +} + +static void +dump_cfg_strarray(OpCodes code, u_int count, char **vals) +{ + u_int i; + + for (i = 0; i < count; i++) + printf("%s %s\n", lookup_opcode_name(code), vals[i]); +} + +static void +dump_cfg_strarray_oneline(OpCodes code, u_int count, char **vals) +{ + u_int i; + + printf("%s", lookup_opcode_name(code)); + for (i = 0; i < count; i++) + printf(" %s", vals[i]); + printf("\n"); +} + +static void +dump_cfg_forwards(OpCodes code, u_int count, const struct Forward *fwds) +{ + const struct Forward *fwd; + u_int i; + + /* oDynamicForward */ + for (i = 0; i < count; i++) { + fwd = &fwds[i]; + if (code == oDynamicForward && + strcmp(fwd->connect_host, "socks") != 0) + continue; + if (code == oLocalForward && + strcmp(fwd->connect_host, "socks") == 0) + continue; + printf("%s", lookup_opcode_name(code)); + if (fwd->listen_port == PORT_STREAMLOCAL) + printf(" %s", fwd->listen_path); + else if (fwd->listen_host == NULL) + printf(" %d", fwd->listen_port); + else { + printf(" [%s]:%d", + fwd->listen_host, fwd->listen_port); + } + if (code != oDynamicForward) { + if (fwd->connect_port == PORT_STREAMLOCAL) + printf(" %s", fwd->connect_path); + else if (fwd->connect_host == NULL) + printf(" %d", fwd->connect_port); + else { + printf(" [%s]:%d", + fwd->connect_host, fwd->connect_port); + } + } + printf("\n"); + } +} + +void +dump_client_config(Options *o, const char *host) +{ + int i; + char vbuf[5]; + + /* Most interesting options first: user, host, port */ + dump_cfg_string(oUser, o->user); + dump_cfg_string(oHostName, host); + dump_cfg_int(oPort, o->port); + + /* Flag options */ + dump_cfg_fmtint(oAddressFamily, o->address_family); + dump_cfg_fmtint(oBatchMode, o->batch_mode); + dump_cfg_fmtint(oCanonicalizeFallbackLocal, o->canonicalize_fallback_local); + dump_cfg_fmtint(oCanonicalizeHostname, o->canonicalize_hostname); + dump_cfg_fmtint(oChallengeResponseAuthentication, o->challenge_response_authentication); + dump_cfg_fmtint(oCheckHostIP, o->check_host_ip); + dump_cfg_fmtint(oCompression, o->compression); + dump_cfg_fmtint(oControlMaster, o->control_master); + dump_cfg_fmtint(oEnableSSHKeysign, o->enable_ssh_keysign); + dump_cfg_fmtint(oExitOnForwardFailure, o->exit_on_forward_failure); + dump_cfg_fmtint(oFingerprintHash, o->fingerprint_hash); + dump_cfg_fmtint(oForwardAgent, o->forward_agent); + dump_cfg_fmtint(oForwardX11, o->forward_x11); + dump_cfg_fmtint(oForwardX11Trusted, o->forward_x11_trusted); + dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); +#ifdef GSSAPI + dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); + dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); +#endif /* GSSAPI */ + dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); + dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); + dump_cfg_fmtint(oIdentitiesOnly, o->identities_only); + dump_cfg_fmtint(oKbdInteractiveAuthentication, o->kbd_interactive_authentication); + dump_cfg_fmtint(oNoHostAuthenticationForLocalhost, o->no_host_authentication_for_localhost); + dump_cfg_fmtint(oPasswordAuthentication, o->password_authentication); + dump_cfg_fmtint(oPermitLocalCommand, o->permit_local_command); + dump_cfg_fmtint(oProtocol, o->protocol); + dump_cfg_fmtint(oProxyUseFdpass, o->proxy_use_fdpass); + dump_cfg_fmtint(oPubkeyAuthentication, o->pubkey_authentication); + dump_cfg_fmtint(oRequestTTY, o->request_tty); + dump_cfg_fmtint(oRhostsRSAAuthentication, o->rhosts_rsa_authentication); + dump_cfg_fmtint(oRSAAuthentication, o->rsa_authentication); + dump_cfg_fmtint(oStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink); + dump_cfg_fmtint(oStrictHostKeyChecking, o->strict_host_key_checking); + dump_cfg_fmtint(oTCPKeepAlive, o->tcp_keep_alive); + dump_cfg_fmtint(oTunnel, o->tun_open); + dump_cfg_fmtint(oUsePrivilegedPort, o->use_privileged_port); + dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns); + dump_cfg_fmtint(oVisualHostKey, o->visual_host_key); + dump_cfg_fmtint(oUpdateHostkeys, o->update_hostkeys); + + /* Integer options */ + dump_cfg_int(oCanonicalizeMaxDots, o->canonicalize_max_dots); + dump_cfg_int(oCompressionLevel, o->compression_level); + dump_cfg_int(oConnectionAttempts, o->connection_attempts); + dump_cfg_int(oForwardX11Timeout, o->forward_x11_timeout); + dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts); + dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max); + dump_cfg_int(oServerAliveInterval, o->server_alive_interval); + + /* String options */ + dump_cfg_string(oBindAddress, o->bind_address); + dump_cfg_string(oCiphers, o->ciphers ? o->ciphers : KEX_CLIENT_ENCRYPT); + dump_cfg_string(oControlPath, o->control_path); + dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms ? o->hostkeyalgorithms : KEX_DEFAULT_PK_ALG); + dump_cfg_string(oHostKeyAlias, o->host_key_alias); + dump_cfg_string(oHostbasedKeyTypes, o->hostbased_key_types); + dump_cfg_string(oKbdInteractiveDevices, o->kbd_interactive_devices); + dump_cfg_string(oKexAlgorithms, o->kex_algorithms ? o->kex_algorithms : KEX_CLIENT_KEX); + dump_cfg_string(oLocalCommand, o->local_command); + dump_cfg_string(oLogLevel, log_level_name(o->log_level)); + dump_cfg_string(oMacs, o->macs ? o->macs : KEX_CLIENT_MAC); + dump_cfg_string(oPKCS11Provider, o->pkcs11_provider); + dump_cfg_string(oPreferredAuthentications, o->preferred_authentications); + dump_cfg_string(oProxyCommand, o->proxy_command); + dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys); + dump_cfg_string(oXAuthLocation, o->xauth_location); + + /* Forwards */ + dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards); + dump_cfg_forwards(oLocalForward, o->num_local_forwards, o->local_forwards); + dump_cfg_forwards(oRemoteForward, o->num_remote_forwards, o->remote_forwards); + + /* String array options */ + dump_cfg_strarray(oIdentityFile, o->num_identity_files, o->identity_files); + dump_cfg_strarray_oneline(oCanonicalDomains, o->num_canonical_domains, o->canonical_domains); + dump_cfg_strarray_oneline(oGlobalKnownHostsFile, o->num_system_hostfiles, o->system_hostfiles); + dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles); + dump_cfg_strarray(oSendEnv, o->num_send_env, o->send_env); + + /* Special cases */ + + /* oConnectTimeout */ + if (o->connection_timeout == -1) + printf("connecttimeout none\n"); + else + dump_cfg_int(oConnectTimeout, o->connection_timeout); + + /* oTunnelDevice */ + printf("tunneldevice"); + if (o->tun_local == SSH_TUNID_ANY) + printf(" any"); + else + printf(" %d", o->tun_local); + if (o->tun_remote == SSH_TUNID_ANY) + printf(":any"); + else + printf(":%d", o->tun_remote); + printf("\n"); + + /* oCanonicalizePermittedCNAMEs */ + if ( o->num_permitted_cnames > 0) { + printf("canonicalizePermittedcnames"); + for (i = 0; i < o->num_permitted_cnames; i++) { + printf(" %s:%s", o->permitted_cnames[i].source_list, + o->permitted_cnames[i].target_list); + } + printf("\n"); + } + + /* oCipher */ + if (o->cipher != SSH_CIPHER_NOT_SET) + printf("Cipher %s\n", cipher_name(o->cipher)); + + /* oControlPersist */ + if (o->control_persist == 0 || o->control_persist_timeout == 0) + dump_cfg_fmtint(oControlPersist, o->control_persist); + else + dump_cfg_int(oControlPersist, o->control_persist_timeout); + + /* oEscapeChar */ + if (o->escape_char == SSH_ESCAPECHAR_NONE) + printf("escapechar none\n"); + else { + vis(vbuf, o->escape_char, VIS_WHITE, 0); + printf("escapechar %s\n", vbuf); + } + + /* oIPQoS */ + printf("ipqos %s ", iptos2str(o->ip_qos_interactive)); + printf("%s\n", iptos2str(o->ip_qos_bulk)); + + /* oRekeyLimit */ + printf("rekeylimit %lld %d\n", + (long long)o->rekey_limit, o->rekey_interval); + + /* oStreamLocalBindMask */ + printf("streamlocalbindmask 0%o\n", + o->fwd_opts.streamlocal_bind_mask); +} -- cgit v1.1