summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2013-10-17 02:47:23 +0200
committerDamien Miller <djm@mindrot.org>2013-10-17 02:47:23 +0200
commit0faf747e2f77f0f7083bcd59cbed30c4b5448444 (patch)
tree1f1b80f60be01d61f284070affc314d1b97b6b69
parent - jmc@cvs.openbsd.org 2013/10/15 14:10:25 (diff)
downloadopenssh-0faf747e2f77f0f7083bcd59cbed30c4b5448444.tar.xz
openssh-0faf747e2f77f0f7083bcd59cbed30c4b5448444.zip
- 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@
-rw-r--r--ChangeLog10
-rw-r--r--readconf.c113
-rw-r--r--readconf.h22
-rw-r--r--roaming_client.c8
-rw-r--r--ssh.19
-rw-r--r--ssh.c183
-rw-r--r--ssh_config.575
-rw-r--r--sshconnect.c74
-rw-r--r--sshconnect.h8
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 <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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 <ylo@cs.hut.fi>
@@ -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 <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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 <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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,