summaryrefslogtreecommitdiff
path: root/ssh.c
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2013-10-17 11:47:23 +1100
committerDamien Miller <djm@mindrot.org>2013-10-17 11:47:23 +1100
commit0faf747e2f77f0f7083bcd59cbed30c4b5448444 (patch)
tree1f1b80f60be01d61f284070affc314d1b97b6b69 /ssh.c
parentd77b81f856e078714ec6b0f86f61c20249b7ead4 (diff)
- 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@
Diffstat (limited to 'ssh.c')
-rw-r--r--ssh.c183
1 files changed, 167 insertions, 16 deletions
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