From 0faf747e2f77f0f7083bcd59cbed30c4b5448444 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Thu, 17 Oct 2013 11:47:23 +1100 Subject: [PATCH] - djm@cvs.openbsd.org 2013/10/16 02:31:47 [readconf.c readconf.h roaming_client.c ssh.1 ssh.c ssh_config.5] [sshconnect.c sshconnect.h] Implement client-side hostname canonicalisation to allow an explicit search path of domain suffixes to use to convert unqualified host names to fully-qualified ones for host key matching. This is particularly useful for host certificates, which would otherwise need to list unqualified names alongside fully-qualified ones (and this causes a number of problems). "looks fine" markus@ --- ChangeLog | 10 +++ readconf.c | 113 ++++++++++++++++++++++++++++- readconf.h | 22 +++++- roaming_client.c | 8 +-- ssh.1 | 9 ++- ssh.c | 183 ++++++++++++++++++++++++++++++++++++++++++----- ssh_config.5 | 75 ++++++++++++++++++- sshconnect.c | 74 ++++++++----------- sshconnect.h | 8 +-- 9 files changed, 426 insertions(+), 76 deletions(-) diff --git a/ChangeLog b/ChangeLog index 255a3e021..c765bceab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,16 @@ - jmc@cvs.openbsd.org 2013/10/15 14:10:25 [ssh.1 ssh_config.5] tweak previous; + - djm@cvs.openbsd.org 2013/10/16 02:31:47 + [readconf.c readconf.h roaming_client.c ssh.1 ssh.c ssh_config.5] + [sshconnect.c sshconnect.h] + Implement client-side hostname canonicalisation to allow an explicit + search path of domain suffixes to use to convert unqualified host names + to fully-qualified ones for host key matching. + This is particularly useful for host certificates, which would otherwise + need to list unqualified names alongside fully-qualified ones (and this + causes a number of problems). + "looks fine" markus@ 20131015 - (djm) OpenBSD CVS Sync diff --git a/readconf.c b/readconf.c index 9340effd0..de8eb7cd3 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.207 2013/10/14 23:28:23 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.208 2013/10/16 02:31:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -144,6 +144,8 @@ typedef enum { oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, + oCanonicalDomains, oCanonicaliseHostname, oCanonicaliseMaxDots, + oCanonicaliseFallbackLocal, oCanonicalisePermittedCNAMEs, oIgnoredUnknownOption, oDeprecated, oUnsupported } OpCodes; @@ -257,6 +259,11 @@ static struct { { "ipqos", oIPQoS }, { "requesttty", oRequestTTY }, { "proxyusefdpass", oProxyUseFdpass }, + { "canonicaldomains", oCanonicalDomains }, + { "canonicalisefallbacklocal", oCanonicaliseFallbackLocal }, + { "canonicalisehostname", oCanonicaliseHostname }, + { "canonicalisemaxdots", oCanonicaliseMaxDots }, + { "canonicalisepermittedcnames", oCanonicalisePermittedCNAMEs }, { "ignoreunknown", oIgnoreUnknown }, { NULL, oBadOption } @@ -535,6 +542,34 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw, return result; } +/* Check and prepare a domain name: removes trailing '.' and lowercases */ +static void +valid_domain(char *name, const char *filename, int linenum) +{ + size_t i, l = strlen(name); + u_char c, last = '\0'; + + if (l == 0) + fatal("%s line %d: empty hostname suffix", filename, linenum); + if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) + fatal("%s line %d: hostname suffix \"%.100s\" " + "starts with invalid character", filename, linenum, name); + for (i = 0; i < l; i++) { + c = tolower((u_char)name[i]); + name[i] = (char)c; + if (last == '.' && c == '.') + fatal("%s line %d: hostname suffix \"%.100s\" contains " + "consecutive separators", filename, linenum, name); + if (c != '.' && c != '-' && !isalnum(c) && + c != '_') /* technically invalid, but common */ + fatal("%s line %d: hostname suffix \"%.100s\" contains " + "invalid characters", filename, linenum, name); + last = c; + } + if (name[l - 1] == '.') + name[l - 1] = '\0'; +} + /* * Returns the number of the token pointed to by cp or oBadOption. */ @@ -609,6 +644,14 @@ static const struct multistate multistate_requesttty[] = { { "auto", REQUEST_TTY_AUTO }, { NULL, -1 } }; +static const struct multistate multistate_canonicalisehostname[] = { + { "true", SSH_CANONICALISE_YES }, + { "false", SSH_CANONICALISE_NO }, + { "yes", SSH_CANONICALISE_YES }, + { "no", SSH_CANONICALISE_NO }, + { "always", SSH_CANONICALISE_ALWAYS }, + { NULL, -1 } +}; /* * Processes a single option line as used in the configuration files. This @@ -628,6 +671,7 @@ process_config_line(Options *options, struct passwd *pw, const char *host, size_t len; Forward fwd; const struct multistate *multistate_ptr; + struct allowed_cname *cname; if (activep == NULL) { /* We are processing a command line directive */ cmdline = 1; @@ -1263,6 +1307,62 @@ parse_int: intptr = &options->proxy_use_fdpass; goto parse_flag; + case oCanonicalDomains: + value = options->num_canonical_domains != 0; + while ((arg = strdelim(&s)) != NULL && *arg != '\0') { + valid_domain(arg, filename, linenum); + if (!*activep || value) + continue; + if (options->num_canonical_domains >= MAX_CANON_DOMAINS) + fatal("%s line %d: too many hostname suffixes.", + filename, linenum); + options->canonical_domains[ + options->num_canonical_domains++] = xstrdup(arg); + } + break; + + case oCanonicalisePermittedCNAMEs: + value = options->num_permitted_cnames != 0; + while ((arg = strdelim(&s)) != NULL && *arg != '\0') { + /* Either '*' for everything or 'list:list' */ + if (strcmp(arg, "*") == 0) + arg2 = arg; + else { + lowercase(arg); + if ((arg2 = strchr(arg, ':')) == NULL || + arg2[1] == '\0') { + fatal("%s line %d: " + "Invalid permitted CNAME \"%s\"", + filename, linenum, arg); + } + *arg2 = '\0'; + arg2++; + } + if (!*activep || value) + continue; + if (options->num_permitted_cnames >= MAX_CANON_DOMAINS) + fatal("%s line %d: too many permitted CNAMEs.", + filename, linenum); + cname = options->permitted_cnames + + options->num_permitted_cnames++; + cname->source_list = xstrdup(arg); + cname->target_list = xstrdup(arg2); + } + break; + + case oCanonicaliseHostname: + intptr = &options->canonicalise_hostname; + multistate_ptr = multistate_canonicalisehostname; + goto parse_multistate; + + case oCanonicaliseMaxDots: + intptr = &options->canonicalise_max_dots; + goto parse_int; + + case oCanonicaliseFallbackLocal: + intptr = &options->canonicalise_fallback_local; + goto parse_flag; + case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); @@ -1426,6 +1526,11 @@ initialize_options(Options * options) options->request_tty = -1; options->proxy_use_fdpass = -1; options->ignored_unknown = NULL; + options->num_canonical_domains = 0; + options->num_permitted_cnames = 0; + options->canonicalise_max_dots = -1; + options->canonicalise_fallback_local = -1; + options->canonicalise_hostname = -1; } /* @@ -1579,6 +1684,12 @@ fill_default_options(Options * options) options->request_tty = REQUEST_TTY_AUTO; if (options->proxy_use_fdpass == -1) options->proxy_use_fdpass = 0; + if (options->canonicalise_max_dots == -1) + options->canonicalise_max_dots = 1; + if (options->canonicalise_fallback_local == -1) + options->canonicalise_fallback_local = 1; + if (options->canonicalise_hostname == -1) + options->canonicalise_hostname = SSH_CANONICALISE_NO; #define CLEAR_ON_NONE(v) \ do { \ if (v != NULL && strcasecmp(v, "none") == 0) { \ diff --git a/readconf.h b/readconf.h index cde8b5242..4a210897e 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.97 2013/10/14 22:22:03 djm Exp $ */ +/* $OpenBSD: readconf.h,v 1.98 2013/10/16 02:31:46 djm Exp $ */ /* * Author: Tatu Ylonen @@ -29,7 +29,13 @@ typedef struct { /* Data structure for representing option data. */ #define MAX_SEND_ENV 256 -#define SSH_MAX_HOSTS_FILES 256 +#define SSH_MAX_HOSTS_FILES 32 +#define MAX_CANON_DOMAINS 32 + +struct allowed_cname { + char *source_list; + char *target_list; +}; typedef struct { int forward_agent; /* Forward authentication agent. */ @@ -140,9 +146,21 @@ typedef struct { int proxy_use_fdpass; + int num_canonical_domains; + char *canonical_domains[MAX_CANON_DOMAINS]; + int canonicalise_hostname; + int canonicalise_max_dots; + int canonicalise_fallback_local; + int num_permitted_cnames; + struct allowed_cname permitted_cnames[MAX_CANON_DOMAINS]; + char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ } Options; +#define SSH_CANONICALISE_NO 0 +#define SSH_CANONICALISE_YES 1 +#define SSH_CANONICALISE_ALWAYS 2 + #define SSHCTL_MASTER_NO 0 #define SSHCTL_MASTER_YES 1 #define SSHCTL_MASTER_AUTO 2 diff --git a/roaming_client.c b/roaming_client.c index 81c496827..2fb623121 100644 --- a/roaming_client.c +++ b/roaming_client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: roaming_client.c,v 1.5 2013/05/17 00:13:14 djm Exp $ */ +/* $OpenBSD: roaming_client.c,v 1.6 2013/10/16 02:31:46 djm Exp $ */ /* * Copyright (c) 2004-2009 AppGate Network Security AB * @@ -259,10 +259,10 @@ wait_for_roaming_reconnect(void) if (c != '\n' && c != '\r') continue; - if (ssh_connect(host, &hostaddr, options.port, + if (ssh_connect(host, NULL, &hostaddr, options.port, options.address_family, 1, &timeout_ms, - options.tcp_keep_alive, options.use_privileged_port, - options.proxy_command) == 0 && roaming_resume() == 0) { + options.tcp_keep_alive, options.use_privileged_port) == 0 && + roaming_resume() == 0) { packet_restore_state(); reenter_guard = 0; fprintf(stderr, "[connection resumed]\n"); diff --git a/ssh.1 b/ssh.1 index 8091aecfd..d9e2cb658 100644 --- a/ssh.1 +++ b/ssh.1 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh.1,v 1.337 2013/10/15 14:10:25 jmc Exp $ -.Dd $Mdocdate: October 15 2013 $ +.\" $OpenBSD: ssh.1,v 1.338 2013/10/16 02:31:46 djm Exp $ +.Dd $Mdocdate: October 16 2013 $ .Dt SSH 1 .Os .Sh NAME @@ -417,6 +417,11 @@ For full details of the options listed below, and their possible values, see .It AddressFamily .It BatchMode .It BindAddress +.It CanonicalDomains +.It CanonicaliseFallbackLocal +.It CanonicaliseHostname +.It CanonicaliseMaxDots +.It CanonicalisePermittedCNAMEs .It ChallengeResponseAuthentication .It CheckHostIP .It Cipher diff --git a/ssh.c b/ssh.c index ad6ae0f4f..230591b3a 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.384 2013/10/14 23:31:01 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.385 2013/10/16 02:31:46 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -231,6 +231,134 @@ tilde_expand_paths(char **paths, u_int num_paths) } } +static struct addrinfo * +resolve_host(const char *name, u_int port, int logerr, char *cname, size_t clen) +{ + char strport[NI_MAXSERV]; + struct addrinfo hints, *res; + int gaierr, loglevel = SYSLOG_LEVEL_DEBUG1; + + snprintf(strport, sizeof strport, "%u", port); + bzero(&hints, sizeof(hints)); + hints.ai_family = options.address_family; + hints.ai_socktype = SOCK_STREAM; + if (cname != NULL) + hints.ai_flags = AI_CANONNAME; + if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) { + if (logerr || (gaierr != EAI_NONAME && gaierr != EAI_NODATA)) + loglevel = SYSLOG_LEVEL_ERROR; + do_log2(loglevel, "%s: Could not resolve hostname %.100s: %s", + __progname, name, ssh_gai_strerror(gaierr)); + return NULL; + } + if (cname != NULL && res->ai_canonname != NULL) { + if (strlcpy(cname, res->ai_canonname, clen) >= clen) { + error("%s: host \"%s\" cname \"%s\" too long (max %lu)", + __func__, name, res->ai_canonname, (u_long)clen); + if (clen > 0) + *cname = '\0'; + } + } + return res; +} + +/* + * Check whether the cname is a permitted replacement for the hostname + * and perform the replacement if it is. + */ +static int +check_follow_cname(char **namep, const char *cname) +{ + int i; + struct allowed_cname *rule; + + if (*cname == '\0' || options.num_permitted_cnames == 0 || + strcmp(*namep, cname) == 0) + return 0; + if (options.canonicalise_hostname == SSH_CANONICALISE_NO) + return 0; + /* + * Don't attempt to canonicalise names that will be interpreted by + * a proxy unless the user specifically requests so. + */ + if (options.proxy_command != NULL && + options.canonicalise_hostname != SSH_CANONICALISE_ALWAYS) + return 0; + debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname); + for (i = 0; i < options.num_permitted_cnames; i++) { + rule = options.permitted_cnames + i; + if (match_pattern_list(*namep, rule->source_list, + strlen(rule->source_list), 1) != 1 || + match_pattern_list(cname, rule->target_list, + strlen(rule->target_list), 1) != 1) + continue; + verbose("Canonicalised DNS aliased hostname " + "\"%s\" => \"%s\"", *namep, cname); + free(*namep); + *namep = xstrdup(cname); + return 1; + } + return 0; +} + +/* + * Attempt to resolve the supplied hostname after applying the user's + * canonicalisation rules. Returns the address list for the host or NULL + * if no name was found after canonicalisation. + */ +static struct addrinfo * +resolve_canonicalise(char **hostp, u_int port) +{ + int i, ndots; + char *cp, *fullhost, cname_target[NI_MAXHOST]; + struct addrinfo *addrs; + + if (options.canonicalise_hostname == SSH_CANONICALISE_NO) + return NULL; + /* + * Don't attempt to canonicalise names that will be interpreted by + * a proxy unless the user specifically requests so. + */ + if (options.proxy_command != NULL && + options.canonicalise_hostname != SSH_CANONICALISE_ALWAYS) + return NULL; + /* Don't apply canonicalisation to sufficiently-qualified hostnames */ + ndots = 0; + for (cp = *hostp; *cp != '\0'; cp++) { + if (*cp == '.') + ndots++; + } + if (ndots > options.canonicalise_max_dots) { + debug3("%s: not canonicalising hostname \"%s\" (max dots %d)", + __func__, *hostp, options.canonicalise_max_dots); + return NULL; + } + /* Attempt each supplied suffix */ + for (i = 0; i < options.num_canonical_domains; i++) { + *cname_target = '\0'; + xasprintf(&fullhost, "%s.%s.", *hostp, + options.canonical_domains[i]); + if ((addrs = resolve_host(fullhost, options.port, 0, + cname_target, sizeof(cname_target))) == NULL) { + free(fullhost); + continue; + } + /* Remove trailing '.' */ + fullhost[strlen(fullhost) - 1] = '\0'; + /* Follow CNAME if requested */ + if (!check_follow_cname(&fullhost, cname_target)) { + debug("Canonicalised hostname \"%s\" => \"%s\"", + *hostp, fullhost); + } + free(*hostp); + *hostp = fullhost; + return addrs; + } + if (!options.canonicalise_fallback_local) + fatal("%s: Could not resolve host \"%s\"", __progname, host); + return NULL; +} + /* * Main program for the ssh client. */ @@ -240,12 +368,14 @@ main(int ac, char **av) int i, r, opt, exit_status, use_syslog; char *p, *cp, *line, *argv0, buf[MAXPATHLEN], *host_arg, *logfile; char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; + char cname[NI_MAXHOST]; struct stat st; struct passwd *pw; int timeout_ms; extern int optind, optreset; extern char *optarg; Forward fwd; + struct addrinfo *addrs = NULL; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); @@ -630,9 +760,9 @@ main(int ac, char **av) usage(); options.user = p; *cp = '\0'; - host = ++cp; + host = xstrdup(++cp); } else - host = *av; + host = xstrdup(*av); if (ac > 1) { optind = optreset = 1; goto again; @@ -644,6 +774,9 @@ main(int ac, char **av) if (!host) usage(); + lowercase(host); + host_arg = xstrdup(host); + OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); @@ -728,6 +861,10 @@ main(int ac, char **av) strcmp(options.proxy_command, "-") == 0 && options.proxy_use_fdpass) fatal("ProxyCommand=- and ProxyUseFDPass are incompatible"); +#ifndef HAVE_CYGWIN + if (original_effective_uid != 0) + options.use_privileged_port = 0; +#endif /* reinit */ log_init(argv0, options.log_level, SYSLOG_FACILITY_USER, !use_syslog); @@ -762,10 +899,26 @@ main(int ac, char **av) options.port = default_ssh_port(); /* preserve host name given on command line for %n expansion */ - host_arg = host; if (options.hostname != NULL) { - host = percent_expand(options.hostname, + cp = percent_expand(options.hostname, "h", host, (char *)NULL); + free(host); + host = cp; + } + + /* If canonicalisation requested then try to apply it */ + if (options.canonicalise_hostname != SSH_CANONICALISE_NO) + addrs = resolve_canonicalise(&host, options.port); + /* + * If canonicalisation not requested, or if it failed then try to + * resolve the bare hostname name using the system resolver's usual + * search rules. + */ + if (addrs == NULL) { + if ((addrs = resolve_host(host, options.port, 1, + cname, sizeof(cname))) == NULL) + cleanup_exit(255); /* resolve_host logs the error */ + check_follow_cname(&host, cname); } if (gethostname(thishost, sizeof(thishost)) == -1) @@ -803,16 +956,15 @@ main(int ac, char **av) timeout_ms = options.connection_timeout * 1000; /* Open a connection to the remote host. */ - if (ssh_connect(host, &hostaddr, options.port, - options.address_family, options.connection_attempts, &timeout_ms, - options.tcp_keep_alive, -#ifdef HAVE_CYGWIN - options.use_privileged_port, -#else - original_effective_uid == 0 && options.use_privileged_port, -#endif - options.proxy_command) != 0) - exit(255); + if (ssh_connect(host, addrs, &hostaddr, options.port, + options.address_family, options.connection_attempts, + &timeout_ms, options.tcp_keep_alive, + options.use_privileged_port) != 0) + exit(255); + + freeaddrinfo(addrs); + packet_set_timeout(options.server_alive_interval, + options.server_alive_count_max); if (timeout_ms > 0) debug3("timeout: %d ms remain after connect", timeout_ms); @@ -1621,4 +1773,3 @@ main_sigchld_handler(int sig) signal(sig, main_sigchld_handler); errno = save_errno; } - diff --git a/ssh_config.5 b/ssh_config.5 index 3eaaa536a..3c1f87bef 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.170 2013/10/15 14:10:25 jmc Exp $ -.Dd $Mdocdate: October 15 2013 $ +.\" $OpenBSD: ssh_config.5,v 1.171 2013/10/16 02:31:46 djm Exp $ +.Dd $Mdocdate: October 16 2013 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -200,6 +200,77 @@ Note that this option does not work if .Cm UsePrivilegedPort is set to .Dq yes . +.It Cm CanonicalDomains +when +.Cm CanonicaliseHostname +is enabled, this option specifies the list of domain suffixes in which to +search for the specified destination host. +.It Cm CanonicaliseFallbackLocal +specified whether to fail with an error when hostname canonicalisation fails. +The default of +.Dq no +will attempt to lookup the unqualified hostname using the system resolver's +search rules. +A value of +.Dq yes +will cause +.Xr ssh 1 +to fail instantly if +.Cm CanonicaliseHostname +is enabled and the target hostname cannot be found in any of the domains +specified by +.Cm CanonicalDomains . +.It Cm CanonicaliseHostname +controls whether explicit hostname canonicalisation is performed. +The default +.Dq no +is not to perform any name rewriting and let the system resolver handle all +hostname lookups. +If set to +.Dq yes +then, for connections that do not use a +.Cm ProxyCommand , +.Xr ssh 1 +will attempt to canonicalise the hostname specified on the command line +using the +.Cm CanonicalDomains +suffixes and +.Cm CanonicalisePermittedCNAMEs +rules. +If +.Cm CanonicaliseHostname +is set to +.Dq always , +then canonicalisation is applied to proxied connections to. +.It Cm CanonicaliseMaxDots +specifies the maximum number of dot characters in a hostname name before +canonicalisation is disabled. +The default of +.Dq 1 +allows a single dot (i.e. hostname.subdomain) +.It Cm CanonicalisePermittedCNAMEs +specifies rules to determine whether CNAMEs should be followed when +canonicalising hostnames. +The rules consist of one or more arguments of +.Sm off +.Ar source_domain_list : Ar target_domain_list +.Sm on +where +.Ar source_domain_list +is a pattern-list of domains that are may follow CNAMEs in canonicalisation +and +.Ar target_domain_list +is a pattern-list of domains that they may resove to. +.Pp +For example, +.Dq *.a.example.com:*.b.example.com,*.c.example.com +will allow hostnames matching +.Dq *.a.example.com +to be canonicalised to names in the +.Dq *.b.example.com +or +.Dq *.c.example.com +domains. .It Cm ChallengeResponseAuthentication Specifies whether to use challenge-response authentication. The argument to this keyword must be diff --git a/sshconnect.c b/sshconnect.c index aee38198b..3cdc46149 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.240 2013/09/19 01:26:29 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.241 2013/10/16 02:31:46 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -86,7 +86,7 @@ expand_proxy_command(const char *proxy_command, const char *user, { char *tmp, *ret, strport[NI_MAXSERV]; - snprintf(strport, sizeof strport, "%hu", port); + snprintf(strport, sizeof strport, "%d", port); xasprintf(&tmp, "exec %s", proxy_command); ret = percent_expand(tmp, "h", host, "p", strport, "r", options.user, (char *)NULL); @@ -170,8 +170,6 @@ ssh_proxy_fdpass_connect(const char *host, u_short port, /* Set the connection file descriptors. */ packet_set_connection(sock, sock); - packet_set_timeout(options.server_alive_interval, - options.server_alive_count_max); return 0; } @@ -187,16 +185,6 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) pid_t pid; char *shell; - if (!strcmp(proxy_command, "-")) { - packet_set_connection(STDIN_FILENO, STDOUT_FILENO); - packet_set_timeout(options.server_alive_interval, - options.server_alive_count_max); - return 0; - } - - if (options.proxy_use_fdpass) - return ssh_proxy_fdpass_connect(host, port, proxy_command); - if ((shell = getenv("SHELL")) == NULL || *shell == '\0') shell = _PATH_BSHELL; @@ -258,8 +246,6 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) /* Set the connection file descriptors. */ packet_set_connection(pout[0], pin[1]); - packet_set_timeout(options.server_alive_interval, - options.server_alive_count_max); /* Indicate OK return */ return 0; @@ -429,33 +415,18 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, * and %p substituted for host and port, respectively) to use to contact * the daemon. */ -int -ssh_connect(const char *host, struct sockaddr_storage * hostaddr, - u_short port, int family, int connection_attempts, int *timeout_ms, - int want_keepalive, int needpriv, const char *proxy_command) +static int +ssh_connect_direct(const char *host, struct addrinfo *aitop, + struct sockaddr_storage *hostaddr, u_short port, int family, + int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) { - int gaierr; int on = 1; int sock = -1, attempt; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; - struct addrinfo hints, *ai, *aitop; + struct addrinfo *ai; debug2("ssh_connect: needpriv %d", needpriv); - /* If a proxy command is given, connect using it. */ - if (proxy_command != NULL) - return ssh_proxy_connect(host, port, proxy_command); - - /* No proxy command. */ - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - snprintf(strport, sizeof strport, "%u", port); - if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) - fatal("%s: Could not resolve hostname %.100s: %s", __progname, - host, ssh_gai_strerror(gaierr)); - for (attempt = 0; attempt < connection_attempts; attempt++) { if (attempt > 0) { /* Sleep a moment before retrying. */ @@ -467,7 +438,8 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, * sequence until the connection succeeds. */ for (ai = aitop; ai; ai = ai->ai_next) { - if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + if (ai->ai_family != AF_INET && + ai->ai_family != AF_INET6) continue; if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), @@ -500,8 +472,6 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, break; /* Successful connection. */ } - freeaddrinfo(aitop); - /* Return failure if we didn't get a successful connection. */ if (sock == -1) { error("ssh: connect to host %s port %s: %s", @@ -519,12 +489,28 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, /* Set the connection. */ packet_set_connection(sock, sock); - packet_set_timeout(options.server_alive_interval, - options.server_alive_count_max); return 0; } +int +ssh_connect(const char *host, struct addrinfo *addrs, + struct sockaddr_storage *hostaddr, u_short port, int family, + int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) +{ + if (options.proxy_command == NULL) { + return ssh_connect_direct(host, addrs, hostaddr, port, family, + connection_attempts, timeout_ms, want_keepalive, needpriv); + } else if (strcmp(options.proxy_command, "-") == 0) { + packet_set_connection(STDIN_FILENO, STDOUT_FILENO); + return 0; /* Always succeeds */ + } else if (options.proxy_use_fdpass) { + return ssh_proxy_fdpass_connect(host, port, + options.proxy_command); + } + return ssh_proxy_connect(host, port, options.proxy_command); +} + static void send_client_banner(int connection_out, int minor1) { @@ -1265,7 +1251,7 @@ void ssh_login(Sensitive *sensitive, const char *orighost, struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) { - char *host, *cp; + char *host; char *server_user, *local_user; local_user = xstrdup(pw->pw_name); @@ -1273,9 +1259,7 @@ ssh_login(Sensitive *sensitive, const char *orighost, /* Convert the user-supplied hostname into all lowercase. */ host = xstrdup(orighost); - for (cp = host; *cp; cp++) - if (isupper(*cp)) - *cp = (char)tolower(*cp); + lowercase(host); /* Exchange protocol version identification strings with the server. */ ssh_exchange_identification(timeout_ms); diff --git a/sshconnect.h b/sshconnect.h index fd7f7f7c6..0ea6e99f6 100644 --- a/sshconnect.h +++ b/sshconnect.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.h,v 1.27 2010/11/29 23:45:51 djm Exp $ */ +/* $OpenBSD: sshconnect.h,v 1.28 2013/10/16 02:31:47 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -31,9 +31,9 @@ struct Sensitive { int external_keysign; }; -int -ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int, - int *, int, int, const char *); +struct addrinfo; +int ssh_connect(const char *, struct addrinfo *, struct sockaddr_storage *, + u_short, int, int, int *, int, int); void ssh_kill_proxy_command(void); void ssh_login(Sensitive *, const char *, struct sockaddr *, u_short,