summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 @@
3 - jmc@cvs.openbsd.org 2013/10/15 14:10:25 3 - jmc@cvs.openbsd.org 2013/10/15 14:10:25
4 [ssh.1 ssh_config.5] 4 [ssh.1 ssh_config.5]
5 tweak previous; 5 tweak previous;
6 - djm@cvs.openbsd.org 2013/10/16 02:31:47
7 [readconf.c readconf.h roaming_client.c ssh.1 ssh.c ssh_config.5]
8 [sshconnect.c sshconnect.h]
9 Implement client-side hostname canonicalisation to allow an explicit
10 search path of domain suffixes to use to convert unqualified host names
11 to fully-qualified ones for host key matching.
12 This is particularly useful for host certificates, which would otherwise
13 need to list unqualified names alongside fully-qualified ones (and this
14 causes a number of problems).
15 "looks fine" markus@
6 16
720131015 1720131015
8 - (djm) OpenBSD CVS Sync 18 - (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 @@
1/* $OpenBSD: readconf.c,v 1.207 2013/10/14 23:28:23 djm Exp $ */ 1/* $OpenBSD: readconf.c,v 1.208 2013/10/16 02:31:45 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -144,6 +144,8 @@ typedef enum {
144 oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, 144 oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
145 oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, 145 oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
146 oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, 146 oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass,
147 oCanonicalDomains, oCanonicaliseHostname, oCanonicaliseMaxDots,
148 oCanonicaliseFallbackLocal, oCanonicalisePermittedCNAMEs,
147 oIgnoredUnknownOption, oDeprecated, oUnsupported 149 oIgnoredUnknownOption, oDeprecated, oUnsupported
148} OpCodes; 150} OpCodes;
149 151
@@ -257,6 +259,11 @@ static struct {
257 { "ipqos", oIPQoS }, 259 { "ipqos", oIPQoS },
258 { "requesttty", oRequestTTY }, 260 { "requesttty", oRequestTTY },
259 { "proxyusefdpass", oProxyUseFdpass }, 261 { "proxyusefdpass", oProxyUseFdpass },
262 { "canonicaldomains", oCanonicalDomains },
263 { "canonicalisefallbacklocal", oCanonicaliseFallbackLocal },
264 { "canonicalisehostname", oCanonicaliseHostname },
265 { "canonicalisemaxdots", oCanonicaliseMaxDots },
266 { "canonicalisepermittedcnames", oCanonicalisePermittedCNAMEs },
260 { "ignoreunknown", oIgnoreUnknown }, 267 { "ignoreunknown", oIgnoreUnknown },
261 268
262 { NULL, oBadOption } 269 { NULL, oBadOption }
@@ -535,6 +542,34 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
535 return result; 542 return result;
536} 543}
537 544
545/* Check and prepare a domain name: removes trailing '.' and lowercases */
546static void
547valid_domain(char *name, const char *filename, int linenum)
548{
549 size_t i, l = strlen(name);
550 u_char c, last = '\0';
551
552 if (l == 0)
553 fatal("%s line %d: empty hostname suffix", filename, linenum);
554 if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0]))
555 fatal("%s line %d: hostname suffix \"%.100s\" "
556 "starts with invalid character", filename, linenum, name);
557 for (i = 0; i < l; i++) {
558 c = tolower((u_char)name[i]);
559 name[i] = (char)c;
560 if (last == '.' && c == '.')
561 fatal("%s line %d: hostname suffix \"%.100s\" contains "
562 "consecutive separators", filename, linenum, name);
563 if (c != '.' && c != '-' && !isalnum(c) &&
564 c != '_') /* technically invalid, but common */
565 fatal("%s line %d: hostname suffix \"%.100s\" contains "
566 "invalid characters", filename, linenum, name);
567 last = c;
568 }
569 if (name[l - 1] == '.')
570 name[l - 1] = '\0';
571}
572
538/* 573/*
539 * Returns the number of the token pointed to by cp or oBadOption. 574 * Returns the number of the token pointed to by cp or oBadOption.
540 */ 575 */
@@ -609,6 +644,14 @@ static const struct multistate multistate_requesttty[] = {
609 { "auto", REQUEST_TTY_AUTO }, 644 { "auto", REQUEST_TTY_AUTO },
610 { NULL, -1 } 645 { NULL, -1 }
611}; 646};
647static const struct multistate multistate_canonicalisehostname[] = {
648 { "true", SSH_CANONICALISE_YES },
649 { "false", SSH_CANONICALISE_NO },
650 { "yes", SSH_CANONICALISE_YES },
651 { "no", SSH_CANONICALISE_NO },
652 { "always", SSH_CANONICALISE_ALWAYS },
653 { NULL, -1 }
654};
612 655
613/* 656/*
614 * Processes a single option line as used in the configuration files. This 657 * 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,
628 size_t len; 671 size_t len;
629 Forward fwd; 672 Forward fwd;
630 const struct multistate *multistate_ptr; 673 const struct multistate *multistate_ptr;
674 struct allowed_cname *cname;
631 675
632 if (activep == NULL) { /* We are processing a command line directive */ 676 if (activep == NULL) { /* We are processing a command line directive */
633 cmdline = 1; 677 cmdline = 1;
@@ -1263,6 +1307,62 @@ parse_int:
1263 intptr = &options->proxy_use_fdpass; 1307 intptr = &options->proxy_use_fdpass;
1264 goto parse_flag; 1308 goto parse_flag;
1265 1309
1310 case oCanonicalDomains:
1311 value = options->num_canonical_domains != 0;
1312 while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
1313 valid_domain(arg, filename, linenum);
1314 if (!*activep || value)
1315 continue;
1316 if (options->num_canonical_domains >= MAX_CANON_DOMAINS)
1317 fatal("%s line %d: too many hostname suffixes.",
1318 filename, linenum);
1319 options->canonical_domains[
1320 options->num_canonical_domains++] = xstrdup(arg);
1321 }
1322 break;
1323
1324 case oCanonicalisePermittedCNAMEs:
1325 value = options->num_permitted_cnames != 0;
1326 while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
1327 /* Either '*' for everything or 'list:list' */
1328 if (strcmp(arg, "*") == 0)
1329 arg2 = arg;
1330 else {
1331 lowercase(arg);
1332 if ((arg2 = strchr(arg, ':')) == NULL ||
1333 arg2[1] == '\0') {
1334 fatal("%s line %d: "
1335 "Invalid permitted CNAME \"%s\"",
1336 filename, linenum, arg);
1337 }
1338 *arg2 = '\0';
1339 arg2++;
1340 }
1341 if (!*activep || value)
1342 continue;
1343 if (options->num_permitted_cnames >= MAX_CANON_DOMAINS)
1344 fatal("%s line %d: too many permitted CNAMEs.",
1345 filename, linenum);
1346 cname = options->permitted_cnames +
1347 options->num_permitted_cnames++;
1348 cname->source_list = xstrdup(arg);
1349 cname->target_list = xstrdup(arg2);
1350 }
1351 break;
1352
1353 case oCanonicaliseHostname:
1354 intptr = &options->canonicalise_hostname;
1355 multistate_ptr = multistate_canonicalisehostname;
1356 goto parse_multistate;
1357
1358 case oCanonicaliseMaxDots:
1359 intptr = &options->canonicalise_max_dots;
1360 goto parse_int;
1361
1362 case oCanonicaliseFallbackLocal:
1363 intptr = &options->canonicalise_fallback_local;
1364 goto parse_flag;
1365
1266 case oDeprecated: 1366 case oDeprecated:
1267 debug("%s line %d: Deprecated option \"%s\"", 1367 debug("%s line %d: Deprecated option \"%s\"",
1268 filename, linenum, keyword); 1368 filename, linenum, keyword);
@@ -1426,6 +1526,11 @@ initialize_options(Options * options)
1426 options->request_tty = -1; 1526 options->request_tty = -1;
1427 options->proxy_use_fdpass = -1; 1527 options->proxy_use_fdpass = -1;
1428 options->ignored_unknown = NULL; 1528 options->ignored_unknown = NULL;
1529 options->num_canonical_domains = 0;
1530 options->num_permitted_cnames = 0;
1531 options->canonicalise_max_dots = -1;
1532 options->canonicalise_fallback_local = -1;
1533 options->canonicalise_hostname = -1;
1429} 1534}
1430 1535
1431/* 1536/*
@@ -1579,6 +1684,12 @@ fill_default_options(Options * options)
1579 options->request_tty = REQUEST_TTY_AUTO; 1684 options->request_tty = REQUEST_TTY_AUTO;
1580 if (options->proxy_use_fdpass == -1) 1685 if (options->proxy_use_fdpass == -1)
1581 options->proxy_use_fdpass = 0; 1686 options->proxy_use_fdpass = 0;
1687 if (options->canonicalise_max_dots == -1)
1688 options->canonicalise_max_dots = 1;
1689 if (options->canonicalise_fallback_local == -1)
1690 options->canonicalise_fallback_local = 1;
1691 if (options->canonicalise_hostname == -1)
1692 options->canonicalise_hostname = SSH_CANONICALISE_NO;
1582#define CLEAR_ON_NONE(v) \ 1693#define CLEAR_ON_NONE(v) \
1583 do { \ 1694 do { \
1584 if (v != NULL && strcasecmp(v, "none") == 0) { \ 1695 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 @@
1/* $OpenBSD: readconf.h,v 1.97 2013/10/14 22:22:03 djm Exp $ */ 1/* $OpenBSD: readconf.h,v 1.98 2013/10/16 02:31:46 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -29,7 +29,13 @@ typedef struct {
29/* Data structure for representing option data. */ 29/* Data structure for representing option data. */
30 30
31#define MAX_SEND_ENV 256 31#define MAX_SEND_ENV 256
32#define SSH_MAX_HOSTS_FILES 256 32#define SSH_MAX_HOSTS_FILES 32
33#define MAX_CANON_DOMAINS 32
34
35struct allowed_cname {
36 char *source_list;
37 char *target_list;
38};
33 39
34typedef struct { 40typedef struct {
35 int forward_agent; /* Forward authentication agent. */ 41 int forward_agent; /* Forward authentication agent. */
@@ -140,9 +146,21 @@ typedef struct {
140 146
141 int proxy_use_fdpass; 147 int proxy_use_fdpass;
142 148
149 int num_canonical_domains;
150 char *canonical_domains[MAX_CANON_DOMAINS];
151 int canonicalise_hostname;
152 int canonicalise_max_dots;
153 int canonicalise_fallback_local;
154 int num_permitted_cnames;
155 struct allowed_cname permitted_cnames[MAX_CANON_DOMAINS];
156
143 char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ 157 char *ignored_unknown; /* Pattern list of unknown tokens to ignore */
144} Options; 158} Options;
145 159
160#define SSH_CANONICALISE_NO 0
161#define SSH_CANONICALISE_YES 1
162#define SSH_CANONICALISE_ALWAYS 2
163
146#define SSHCTL_MASTER_NO 0 164#define SSHCTL_MASTER_NO 0
147#define SSHCTL_MASTER_YES 1 165#define SSHCTL_MASTER_YES 1
148#define SSHCTL_MASTER_AUTO 2 166#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 @@
1/* $OpenBSD: roaming_client.c,v 1.5 2013/05/17 00:13:14 djm Exp $ */ 1/* $OpenBSD: roaming_client.c,v 1.6 2013/10/16 02:31:46 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2004-2009 AppGate Network Security AB 3 * Copyright (c) 2004-2009 AppGate Network Security AB
4 * 4 *
@@ -259,10 +259,10 @@ wait_for_roaming_reconnect(void)
259 if (c != '\n' && c != '\r') 259 if (c != '\n' && c != '\r')
260 continue; 260 continue;
261 261
262 if (ssh_connect(host, &hostaddr, options.port, 262 if (ssh_connect(host, NULL, &hostaddr, options.port,
263 options.address_family, 1, &timeout_ms, 263 options.address_family, 1, &timeout_ms,
264 options.tcp_keep_alive, options.use_privileged_port, 264 options.tcp_keep_alive, options.use_privileged_port) == 0 &&
265 options.proxy_command) == 0 && roaming_resume() == 0) { 265 roaming_resume() == 0) {
266 packet_restore_state(); 266 packet_restore_state();
267 reenter_guard = 0; 267 reenter_guard = 0;
268 fprintf(stderr, "[connection resumed]\n"); 268 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 @@
33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35.\" 35.\"
36.\" $OpenBSD: ssh.1,v 1.337 2013/10/15 14:10:25 jmc Exp $ 36.\" $OpenBSD: ssh.1,v 1.338 2013/10/16 02:31:46 djm Exp $
37.Dd $Mdocdate: October 15 2013 $ 37.Dd $Mdocdate: October 16 2013 $
38.Dt SSH 1 38.Dt SSH 1
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -417,6 +417,11 @@ For full details of the options listed below, and their possible values, see
417.It AddressFamily 417.It AddressFamily
418.It BatchMode 418.It BatchMode
419.It BindAddress 419.It BindAddress
420.It CanonicalDomains
421.It CanonicaliseFallbackLocal
422.It CanonicaliseHostname
423.It CanonicaliseMaxDots
424.It CanonicalisePermittedCNAMEs
420.It ChallengeResponseAuthentication 425.It ChallengeResponseAuthentication
421.It CheckHostIP 426.It CheckHostIP
422.It Cipher 427.It Cipher
diff --git a/ssh.c b/ssh.c
index ad6ae0f4f..230591b3a 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh.c,v 1.384 2013/10/14 23:31:01 djm Exp $ */ 1/* $OpenBSD: ssh.c,v 1.385 2013/10/16 02:31:46 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -231,6 +231,134 @@ tilde_expand_paths(char **paths, u_int num_paths)
231 } 231 }
232} 232}
233 233
234static struct addrinfo *
235resolve_host(const char *name, u_int port, int logerr, char *cname, size_t clen)
236{
237 char strport[NI_MAXSERV];
238 struct addrinfo hints, *res;
239 int gaierr, loglevel = SYSLOG_LEVEL_DEBUG1;
240
241 snprintf(strport, sizeof strport, "%u", port);
242 bzero(&hints, sizeof(hints));
243 hints.ai_family = options.address_family;
244 hints.ai_socktype = SOCK_STREAM;
245 if (cname != NULL)
246 hints.ai_flags = AI_CANONNAME;
247 if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) {
248 if (logerr || (gaierr != EAI_NONAME && gaierr != EAI_NODATA))
249 loglevel = SYSLOG_LEVEL_ERROR;
250 do_log2(loglevel, "%s: Could not resolve hostname %.100s: %s",
251 __progname, name, ssh_gai_strerror(gaierr));
252 return NULL;
253 }
254 if (cname != NULL && res->ai_canonname != NULL) {
255 if (strlcpy(cname, res->ai_canonname, clen) >= clen) {
256 error("%s: host \"%s\" cname \"%s\" too long (max %lu)",
257 __func__, name, res->ai_canonname, (u_long)clen);
258 if (clen > 0)
259 *cname = '\0';
260 }
261 }
262 return res;
263}
264
265/*
266 * Check whether the cname is a permitted replacement for the hostname
267 * and perform the replacement if it is.
268 */
269static int
270check_follow_cname(char **namep, const char *cname)
271{
272 int i;
273 struct allowed_cname *rule;
274
275 if (*cname == '\0' || options.num_permitted_cnames == 0 ||
276 strcmp(*namep, cname) == 0)
277 return 0;
278 if (options.canonicalise_hostname == SSH_CANONICALISE_NO)
279 return 0;
280 /*
281 * Don't attempt to canonicalise names that will be interpreted by
282 * a proxy unless the user specifically requests so.
283 */
284 if (options.proxy_command != NULL &&
285 options.canonicalise_hostname != SSH_CANONICALISE_ALWAYS)
286 return 0;
287 debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname);
288 for (i = 0; i < options.num_permitted_cnames; i++) {
289 rule = options.permitted_cnames + i;
290 if (match_pattern_list(*namep, rule->source_list,
291 strlen(rule->source_list), 1) != 1 ||
292 match_pattern_list(cname, rule->target_list,
293 strlen(rule->target_list), 1) != 1)
294 continue;
295 verbose("Canonicalised DNS aliased hostname "
296 "\"%s\" => \"%s\"", *namep, cname);
297 free(*namep);
298 *namep = xstrdup(cname);
299 return 1;
300 }
301 return 0;
302}
303
304/*
305 * Attempt to resolve the supplied hostname after applying the user's
306 * canonicalisation rules. Returns the address list for the host or NULL
307 * if no name was found after canonicalisation.
308 */
309static struct addrinfo *
310resolve_canonicalise(char **hostp, u_int port)
311{
312 int i, ndots;
313 char *cp, *fullhost, cname_target[NI_MAXHOST];
314 struct addrinfo *addrs;
315
316 if (options.canonicalise_hostname == SSH_CANONICALISE_NO)
317 return NULL;
318 /*
319 * Don't attempt to canonicalise names that will be interpreted by
320 * a proxy unless the user specifically requests so.
321 */
322 if (options.proxy_command != NULL &&
323 options.canonicalise_hostname != SSH_CANONICALISE_ALWAYS)
324 return NULL;
325 /* Don't apply canonicalisation to sufficiently-qualified hostnames */
326 ndots = 0;
327 for (cp = *hostp; *cp != '\0'; cp++) {
328 if (*cp == '.')
329 ndots++;
330 }
331 if (ndots > options.canonicalise_max_dots) {
332 debug3("%s: not canonicalising hostname \"%s\" (max dots %d)",
333 __func__, *hostp, options.canonicalise_max_dots);
334 return NULL;
335 }
336 /* Attempt each supplied suffix */
337 for (i = 0; i < options.num_canonical_domains; i++) {
338 *cname_target = '\0';
339 xasprintf(&fullhost, "%s.%s.", *hostp,
340 options.canonical_domains[i]);
341 if ((addrs = resolve_host(fullhost, options.port, 0,
342 cname_target, sizeof(cname_target))) == NULL) {
343 free(fullhost);
344 continue;
345 }
346 /* Remove trailing '.' */
347 fullhost[strlen(fullhost) - 1] = '\0';
348 /* Follow CNAME if requested */
349 if (!check_follow_cname(&fullhost, cname_target)) {
350 debug("Canonicalised hostname \"%s\" => \"%s\"",
351 *hostp, fullhost);
352 }
353 free(*hostp);
354 *hostp = fullhost;
355 return addrs;
356 }
357 if (!options.canonicalise_fallback_local)
358 fatal("%s: Could not resolve host \"%s\"", __progname, host);
359 return NULL;
360}
361
234/* 362/*
235 * Main program for the ssh client. 363 * Main program for the ssh client.
236 */ 364 */
@@ -240,12 +368,14 @@ main(int ac, char **av)
240 int i, r, opt, exit_status, use_syslog; 368 int i, r, opt, exit_status, use_syslog;
241 char *p, *cp, *line, *argv0, buf[MAXPATHLEN], *host_arg, *logfile; 369 char *p, *cp, *line, *argv0, buf[MAXPATHLEN], *host_arg, *logfile;
242 char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; 370 char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
371 char cname[NI_MAXHOST];
243 struct stat st; 372 struct stat st;
244 struct passwd *pw; 373 struct passwd *pw;
245 int timeout_ms; 374 int timeout_ms;
246 extern int optind, optreset; 375 extern int optind, optreset;
247 extern char *optarg; 376 extern char *optarg;
248 Forward fwd; 377 Forward fwd;
378 struct addrinfo *addrs = NULL;
249 379
250 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 380 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
251 sanitise_stdfd(); 381 sanitise_stdfd();
@@ -630,9 +760,9 @@ main(int ac, char **av)
630 usage(); 760 usage();
631 options.user = p; 761 options.user = p;
632 *cp = '\0'; 762 *cp = '\0';
633 host = ++cp; 763 host = xstrdup(++cp);
634 } else 764 } else
635 host = *av; 765 host = xstrdup(*av);
636 if (ac > 1) { 766 if (ac > 1) {
637 optind = optreset = 1; 767 optind = optreset = 1;
638 goto again; 768 goto again;
@@ -644,6 +774,9 @@ main(int ac, char **av)
644 if (!host) 774 if (!host)
645 usage(); 775 usage();
646 776
777 lowercase(host);
778 host_arg = xstrdup(host);
779
647 OpenSSL_add_all_algorithms(); 780 OpenSSL_add_all_algorithms();
648 ERR_load_crypto_strings(); 781 ERR_load_crypto_strings();
649 782
@@ -728,6 +861,10 @@ main(int ac, char **av)
728 strcmp(options.proxy_command, "-") == 0 && 861 strcmp(options.proxy_command, "-") == 0 &&
729 options.proxy_use_fdpass) 862 options.proxy_use_fdpass)
730 fatal("ProxyCommand=- and ProxyUseFDPass are incompatible"); 863 fatal("ProxyCommand=- and ProxyUseFDPass are incompatible");
864#ifndef HAVE_CYGWIN
865 if (original_effective_uid != 0)
866 options.use_privileged_port = 0;
867#endif
731 868
732 /* reinit */ 869 /* reinit */
733 log_init(argv0, options.log_level, SYSLOG_FACILITY_USER, !use_syslog); 870 log_init(argv0, options.log_level, SYSLOG_FACILITY_USER, !use_syslog);
@@ -762,10 +899,26 @@ main(int ac, char **av)
762 options.port = default_ssh_port(); 899 options.port = default_ssh_port();
763 900
764 /* preserve host name given on command line for %n expansion */ 901 /* preserve host name given on command line for %n expansion */
765 host_arg = host;
766 if (options.hostname != NULL) { 902 if (options.hostname != NULL) {
767 host = percent_expand(options.hostname, 903 cp = percent_expand(options.hostname,
768 "h", host, (char *)NULL); 904 "h", host, (char *)NULL);
905 free(host);
906 host = cp;
907 }
908
909 /* If canonicalisation requested then try to apply it */
910 if (options.canonicalise_hostname != SSH_CANONICALISE_NO)
911 addrs = resolve_canonicalise(&host, options.port);
912 /*
913 * If canonicalisation not requested, or if it failed then try to
914 * resolve the bare hostname name using the system resolver's usual
915 * search rules.
916 */
917 if (addrs == NULL) {
918 if ((addrs = resolve_host(host, options.port, 1,
919 cname, sizeof(cname))) == NULL)
920 cleanup_exit(255); /* resolve_host logs the error */
921 check_follow_cname(&host, cname);
769 } 922 }
770 923
771 if (gethostname(thishost, sizeof(thishost)) == -1) 924 if (gethostname(thishost, sizeof(thishost)) == -1)
@@ -803,16 +956,15 @@ main(int ac, char **av)
803 timeout_ms = options.connection_timeout * 1000; 956 timeout_ms = options.connection_timeout * 1000;
804 957
805 /* Open a connection to the remote host. */ 958 /* Open a connection to the remote host. */
806 if (ssh_connect(host, &hostaddr, options.port, 959 if (ssh_connect(host, addrs, &hostaddr, options.port,
807 options.address_family, options.connection_attempts, &timeout_ms, 960 options.address_family, options.connection_attempts,
808 options.tcp_keep_alive, 961 &timeout_ms, options.tcp_keep_alive,
809#ifdef HAVE_CYGWIN 962 options.use_privileged_port) != 0)
810 options.use_privileged_port, 963 exit(255);
811#else 964
812 original_effective_uid == 0 && options.use_privileged_port, 965 freeaddrinfo(addrs);
813#endif 966 packet_set_timeout(options.server_alive_interval,
814 options.proxy_command) != 0) 967 options.server_alive_count_max);
815 exit(255);
816 968
817 if (timeout_ms > 0) 969 if (timeout_ms > 0)
818 debug3("timeout: %d ms remain after connect", timeout_ms); 970 debug3("timeout: %d ms remain after connect", timeout_ms);
@@ -1621,4 +1773,3 @@ main_sigchld_handler(int sig)
1621 signal(sig, main_sigchld_handler); 1773 signal(sig, main_sigchld_handler);
1622 errno = save_errno; 1774 errno = save_errno;
1623} 1775}
1624
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 @@
33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35.\" 35.\"
36.\" $OpenBSD: ssh_config.5,v 1.170 2013/10/15 14:10:25 jmc Exp $ 36.\" $OpenBSD: ssh_config.5,v 1.171 2013/10/16 02:31:46 djm Exp $
37.Dd $Mdocdate: October 15 2013 $ 37.Dd $Mdocdate: October 16 2013 $
38.Dt SSH_CONFIG 5 38.Dt SSH_CONFIG 5
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -200,6 +200,77 @@ Note that this option does not work if
200.Cm UsePrivilegedPort 200.Cm UsePrivilegedPort
201is set to 201is set to
202.Dq yes . 202.Dq yes .
203.It Cm CanonicalDomains
204when
205.Cm CanonicaliseHostname
206is enabled, this option specifies the list of domain suffixes in which to
207search for the specified destination host.
208.It Cm CanonicaliseFallbackLocal
209specified whether to fail with an error when hostname canonicalisation fails.
210The default of
211.Dq no
212will attempt to lookup the unqualified hostname using the system resolver's
213search rules.
214A value of
215.Dq yes
216will cause
217.Xr ssh 1
218to fail instantly if
219.Cm CanonicaliseHostname
220is enabled and the target hostname cannot be found in any of the domains
221specified by
222.Cm CanonicalDomains .
223.It Cm CanonicaliseHostname
224controls whether explicit hostname canonicalisation is performed.
225The default
226.Dq no
227is not to perform any name rewriting and let the system resolver handle all
228hostname lookups.
229If set to
230.Dq yes
231then, for connections that do not use a
232.Cm ProxyCommand ,
233.Xr ssh 1
234will attempt to canonicalise the hostname specified on the command line
235using the
236.Cm CanonicalDomains
237suffixes and
238.Cm CanonicalisePermittedCNAMEs
239rules.
240If
241.Cm CanonicaliseHostname
242is set to
243.Dq always ,
244then canonicalisation is applied to proxied connections to.
245.It Cm CanonicaliseMaxDots
246specifies the maximum number of dot characters in a hostname name before
247canonicalisation is disabled.
248The default of
249.Dq 1
250allows a single dot (i.e. hostname.subdomain)
251.It Cm CanonicalisePermittedCNAMEs
252specifies rules to determine whether CNAMEs should be followed when
253canonicalising hostnames.
254The rules consist of one or more arguments of
255.Sm off
256.Ar source_domain_list : Ar target_domain_list
257.Sm on
258where
259.Ar source_domain_list
260is a pattern-list of domains that are may follow CNAMEs in canonicalisation
261and
262.Ar target_domain_list
263is a pattern-list of domains that they may resove to.
264.Pp
265For example,
266.Dq *.a.example.com:*.b.example.com,*.c.example.com
267will allow hostnames matching
268.Dq *.a.example.com
269to be canonicalised to names in the
270.Dq *.b.example.com
271or
272.Dq *.c.example.com
273domains.
203.It Cm ChallengeResponseAuthentication 274.It Cm ChallengeResponseAuthentication
204Specifies whether to use challenge-response authentication. 275Specifies whether to use challenge-response authentication.
205The argument to this keyword must be 276The 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 @@
1/* $OpenBSD: sshconnect.c,v 1.240 2013/09/19 01:26:29 djm Exp $ */ 1/* $OpenBSD: sshconnect.c,v 1.241 2013/10/16 02:31:46 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * 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,
86{ 86{
87 char *tmp, *ret, strport[NI_MAXSERV]; 87 char *tmp, *ret, strport[NI_MAXSERV];
88 88
89 snprintf(strport, sizeof strport, "%hu", port); 89 snprintf(strport, sizeof strport, "%d", port);
90 xasprintf(&tmp, "exec %s", proxy_command); 90 xasprintf(&tmp, "exec %s", proxy_command);
91 ret = percent_expand(tmp, "h", host, "p", strport, 91 ret = percent_expand(tmp, "h", host, "p", strport,
92 "r", options.user, (char *)NULL); 92 "r", options.user, (char *)NULL);
@@ -170,8 +170,6 @@ ssh_proxy_fdpass_connect(const char *host, u_short port,
170 170
171 /* Set the connection file descriptors. */ 171 /* Set the connection file descriptors. */
172 packet_set_connection(sock, sock); 172 packet_set_connection(sock, sock);
173 packet_set_timeout(options.server_alive_interval,
174 options.server_alive_count_max);
175 173
176 return 0; 174 return 0;
177} 175}
@@ -187,16 +185,6 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
187 pid_t pid; 185 pid_t pid;
188 char *shell; 186 char *shell;
189 187
190 if (!strcmp(proxy_command, "-")) {
191 packet_set_connection(STDIN_FILENO, STDOUT_FILENO);
192 packet_set_timeout(options.server_alive_interval,
193 options.server_alive_count_max);
194 return 0;
195 }
196
197 if (options.proxy_use_fdpass)
198 return ssh_proxy_fdpass_connect(host, port, proxy_command);
199
200 if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 188 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
201 shell = _PATH_BSHELL; 189 shell = _PATH_BSHELL;
202 190
@@ -258,8 +246,6 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
258 246
259 /* Set the connection file descriptors. */ 247 /* Set the connection file descriptors. */
260 packet_set_connection(pout[0], pin[1]); 248 packet_set_connection(pout[0], pin[1]);
261 packet_set_timeout(options.server_alive_interval,
262 options.server_alive_count_max);
263 249
264 /* Indicate OK return */ 250 /* Indicate OK return */
265 return 0; 251 return 0;
@@ -429,33 +415,18 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr,
429 * and %p substituted for host and port, respectively) to use to contact 415 * and %p substituted for host and port, respectively) to use to contact
430 * the daemon. 416 * the daemon.
431 */ 417 */
432int 418static int
433ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 419ssh_connect_direct(const char *host, struct addrinfo *aitop,
434 u_short port, int family, int connection_attempts, int *timeout_ms, 420 struct sockaddr_storage *hostaddr, u_short port, int family,
435 int want_keepalive, int needpriv, const char *proxy_command) 421 int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
436{ 422{
437 int gaierr;
438 int on = 1; 423 int on = 1;
439 int sock = -1, attempt; 424 int sock = -1, attempt;
440 char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 425 char ntop[NI_MAXHOST], strport[NI_MAXSERV];
441 struct addrinfo hints, *ai, *aitop; 426 struct addrinfo *ai;
442 427
443 debug2("ssh_connect: needpriv %d", needpriv); 428 debug2("ssh_connect: needpriv %d", needpriv);
444 429
445 /* If a proxy command is given, connect using it. */
446 if (proxy_command != NULL)
447 return ssh_proxy_connect(host, port, proxy_command);
448
449 /* No proxy command. */
450
451 memset(&hints, 0, sizeof(hints));
452 hints.ai_family = family;
453 hints.ai_socktype = SOCK_STREAM;
454 snprintf(strport, sizeof strport, "%u", port);
455 if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
456 fatal("%s: Could not resolve hostname %.100s: %s", __progname,
457 host, ssh_gai_strerror(gaierr));
458
459 for (attempt = 0; attempt < connection_attempts; attempt++) { 430 for (attempt = 0; attempt < connection_attempts; attempt++) {
460 if (attempt > 0) { 431 if (attempt > 0) {
461 /* Sleep a moment before retrying. */ 432 /* Sleep a moment before retrying. */
@@ -467,7 +438,8 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
467 * sequence until the connection succeeds. 438 * sequence until the connection succeeds.
468 */ 439 */
469 for (ai = aitop; ai; ai = ai->ai_next) { 440 for (ai = aitop; ai; ai = ai->ai_next) {
470 if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 441 if (ai->ai_family != AF_INET &&
442 ai->ai_family != AF_INET6)
471 continue; 443 continue;
472 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 444 if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
473 ntop, sizeof(ntop), strport, sizeof(strport), 445 ntop, sizeof(ntop), strport, sizeof(strport),
@@ -500,8 +472,6 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
500 break; /* Successful connection. */ 472 break; /* Successful connection. */
501 } 473 }
502 474
503 freeaddrinfo(aitop);
504
505 /* Return failure if we didn't get a successful connection. */ 475 /* Return failure if we didn't get a successful connection. */
506 if (sock == -1) { 476 if (sock == -1) {
507 error("ssh: connect to host %s port %s: %s", 477 error("ssh: connect to host %s port %s: %s",
@@ -519,12 +489,28 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
519 489
520 /* Set the connection. */ 490 /* Set the connection. */
521 packet_set_connection(sock, sock); 491 packet_set_connection(sock, sock);
522 packet_set_timeout(options.server_alive_interval,
523 options.server_alive_count_max);
524 492
525 return 0; 493 return 0;
526} 494}
527 495
496int
497ssh_connect(const char *host, struct addrinfo *addrs,
498 struct sockaddr_storage *hostaddr, u_short port, int family,
499 int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
500{
501 if (options.proxy_command == NULL) {
502 return ssh_connect_direct(host, addrs, hostaddr, port, family,
503 connection_attempts, timeout_ms, want_keepalive, needpriv);
504 } else if (strcmp(options.proxy_command, "-") == 0) {
505 packet_set_connection(STDIN_FILENO, STDOUT_FILENO);
506 return 0; /* Always succeeds */
507 } else if (options.proxy_use_fdpass) {
508 return ssh_proxy_fdpass_connect(host, port,
509 options.proxy_command);
510 }
511 return ssh_proxy_connect(host, port, options.proxy_command);
512}
513
528static void 514static void
529send_client_banner(int connection_out, int minor1) 515send_client_banner(int connection_out, int minor1)
530{ 516{
@@ -1265,7 +1251,7 @@ void
1265ssh_login(Sensitive *sensitive, const char *orighost, 1251ssh_login(Sensitive *sensitive, const char *orighost,
1266 struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) 1252 struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
1267{ 1253{
1268 char *host, *cp; 1254 char *host;
1269 char *server_user, *local_user; 1255 char *server_user, *local_user;
1270 1256
1271 local_user = xstrdup(pw->pw_name); 1257 local_user = xstrdup(pw->pw_name);
@@ -1273,9 +1259,7 @@ ssh_login(Sensitive *sensitive, const char *orighost,
1273 1259
1274 /* Convert the user-supplied hostname into all lowercase. */ 1260 /* Convert the user-supplied hostname into all lowercase. */
1275 host = xstrdup(orighost); 1261 host = xstrdup(orighost);
1276 for (cp = host; *cp; cp++) 1262 lowercase(host);
1277 if (isupper(*cp))
1278 *cp = (char)tolower(*cp);
1279 1263
1280 /* Exchange protocol version identification strings with the server. */ 1264 /* Exchange protocol version identification strings with the server. */
1281 ssh_exchange_identification(timeout_ms); 1265 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 @@
1/* $OpenBSD: sshconnect.h,v 1.27 2010/11/29 23:45:51 djm Exp $ */ 1/* $OpenBSD: sshconnect.h,v 1.28 2013/10/16 02:31:47 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -31,9 +31,9 @@ struct Sensitive {
31 int external_keysign; 31 int external_keysign;
32}; 32};
33 33
34int 34struct addrinfo;
35ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int, 35int ssh_connect(const char *, struct addrinfo *, struct sockaddr_storage *,
36 int *, int, int, const char *); 36 u_short, int, int, int *, int, int);
37void ssh_kill_proxy_command(void); 37void ssh_kill_proxy_command(void);
38 38
39void ssh_login(Sensitive *, const char *, struct sockaddr *, u_short, 39void ssh_login(Sensitive *, const char *, struct sockaddr *, u_short,