diff options
Diffstat (limited to 'ssh.c')
-rw-r--r-- | ssh.c | 150 |
1 files changed, 94 insertions, 56 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh.c,v 1.399 2014/02/04 00:24:29 djm Exp $ */ | 1 | /* $OpenBSD: ssh.c,v 1.400 2014/02/23 20:11:36 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,16 +231,26 @@ tilde_expand_paths(char **paths, u_int num_paths) | |||
231 | } | 231 | } |
232 | } | 232 | } |
233 | 233 | ||
234 | /* | ||
235 | * Attempt to resolve a host name / port to a set of addresses and | ||
236 | * optionally return any CNAMEs encountered along the way. | ||
237 | * Returns NULL on failure. | ||
238 | * NB. this function must operate with a options having undefined members. | ||
239 | */ | ||
234 | static struct addrinfo * | 240 | static struct addrinfo * |
235 | resolve_host(const char *name, u_int port, int logerr, char *cname, size_t clen) | 241 | resolve_host(const char *name, int port, int logerr, char *cname, size_t clen) |
236 | { | 242 | { |
237 | char strport[NI_MAXSERV]; | 243 | char strport[NI_MAXSERV]; |
238 | struct addrinfo hints, *res; | 244 | struct addrinfo hints, *res; |
239 | int gaierr, loglevel = SYSLOG_LEVEL_DEBUG1; | 245 | int gaierr, loglevel = SYSLOG_LEVEL_DEBUG1; |
240 | 246 | ||
247 | if (port <= 0) | ||
248 | port = default_ssh_port(); | ||
249 | |||
241 | snprintf(strport, sizeof strport, "%u", port); | 250 | snprintf(strport, sizeof strport, "%u", port); |
242 | memset(&hints, 0, sizeof(hints)); | 251 | memset(&hints, 0, sizeof(hints)); |
243 | hints.ai_family = options.address_family; | 252 | hints.ai_family = options.address_family == -1 ? |
253 | AF_UNSPEC : options.address_family; | ||
244 | hints.ai_socktype = SOCK_STREAM; | 254 | hints.ai_socktype = SOCK_STREAM; |
245 | if (cname != NULL) | 255 | if (cname != NULL) |
246 | hints.ai_flags = AI_CANONNAME; | 256 | hints.ai_flags = AI_CANONNAME; |
@@ -265,6 +275,7 @@ resolve_host(const char *name, u_int port, int logerr, char *cname, size_t clen) | |||
265 | /* | 275 | /* |
266 | * Check whether the cname is a permitted replacement for the hostname | 276 | * Check whether the cname is a permitted replacement for the hostname |
267 | * and perform the replacement if it is. | 277 | * and perform the replacement if it is. |
278 | * NB. this function must operate with a options having undefined members. | ||
268 | */ | 279 | */ |
269 | static int | 280 | static int |
270 | check_follow_cname(char **namep, const char *cname) | 281 | check_follow_cname(char **namep, const char *cname) |
@@ -281,7 +292,7 @@ check_follow_cname(char **namep, const char *cname) | |||
281 | * Don't attempt to canonicalize names that will be interpreted by | 292 | * Don't attempt to canonicalize names that will be interpreted by |
282 | * a proxy unless the user specifically requests so. | 293 | * a proxy unless the user specifically requests so. |
283 | */ | 294 | */ |
284 | if (options.proxy_command != NULL && | 295 | if (!option_clear_or_none(options.proxy_command) && |
285 | options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) | 296 | options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) |
286 | return 0; | 297 | return 0; |
287 | debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname); | 298 | debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname); |
@@ -305,9 +316,10 @@ check_follow_cname(char **namep, const char *cname) | |||
305 | * Attempt to resolve the supplied hostname after applying the user's | 316 | * Attempt to resolve the supplied hostname after applying the user's |
306 | * canonicalization rules. Returns the address list for the host or NULL | 317 | * canonicalization rules. Returns the address list for the host or NULL |
307 | * if no name was found after canonicalization. | 318 | * if no name was found after canonicalization. |
319 | * NB. this function must operate with a options having undefined members. | ||
308 | */ | 320 | */ |
309 | static struct addrinfo * | 321 | static struct addrinfo * |
310 | resolve_canonicalize(char **hostp, u_int port) | 322 | resolve_canonicalize(char **hostp, int port) |
311 | { | 323 | { |
312 | int i, ndots; | 324 | int i, ndots; |
313 | char *cp, *fullhost, cname_target[NI_MAXHOST]; | 325 | char *cp, *fullhost, cname_target[NI_MAXHOST]; |
@@ -315,13 +327,15 @@ resolve_canonicalize(char **hostp, u_int port) | |||
315 | 327 | ||
316 | if (options.canonicalize_hostname == SSH_CANONICALISE_NO) | 328 | if (options.canonicalize_hostname == SSH_CANONICALISE_NO) |
317 | return NULL; | 329 | return NULL; |
330 | |||
318 | /* | 331 | /* |
319 | * Don't attempt to canonicalize names that will be interpreted by | 332 | * Don't attempt to canonicalize names that will be interpreted by |
320 | * a proxy unless the user specifically requests so. | 333 | * a proxy unless the user specifically requests so. |
321 | */ | 334 | */ |
322 | if (options.proxy_command != NULL && | 335 | if (!option_clear_or_none(options.proxy_command) && |
323 | options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) | 336 | options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) |
324 | return NULL; | 337 | return NULL; |
338 | |||
325 | /* Don't apply canonicalization to sufficiently-qualified hostnames */ | 339 | /* Don't apply canonicalization to sufficiently-qualified hostnames */ |
326 | ndots = 0; | 340 | ndots = 0; |
327 | for (cp = *hostp; *cp != '\0'; cp++) { | 341 | for (cp = *hostp; *cp != '\0'; cp++) { |
@@ -338,7 +352,9 @@ resolve_canonicalize(char **hostp, u_int port) | |||
338 | *cname_target = '\0'; | 352 | *cname_target = '\0'; |
339 | xasprintf(&fullhost, "%s.%s.", *hostp, | 353 | xasprintf(&fullhost, "%s.%s.", *hostp, |
340 | options.canonical_domains[i]); | 354 | options.canonical_domains[i]); |
341 | if ((addrs = resolve_host(fullhost, options.port, 0, | 355 | debug3("%s: attempting \"%s\" => \"%s\"", __func__, |
356 | *hostp, fullhost); | ||
357 | if ((addrs = resolve_host(fullhost, port, 0, | ||
342 | cname_target, sizeof(cname_target))) == NULL) { | 358 | cname_target, sizeof(cname_target))) == NULL) { |
343 | free(fullhost); | 359 | free(fullhost); |
344 | continue; | 360 | continue; |
@@ -355,11 +371,41 @@ resolve_canonicalize(char **hostp, u_int port) | |||
355 | return addrs; | 371 | return addrs; |
356 | } | 372 | } |
357 | if (!options.canonicalize_fallback_local) | 373 | if (!options.canonicalize_fallback_local) |
358 | fatal("%s: Could not resolve host \"%s\"", __progname, host); | 374 | fatal("%s: Could not resolve host \"%s\"", __progname, *hostp); |
375 | debug2("%s: host %s not found in any suffix", __func__, *hostp); | ||
359 | return NULL; | 376 | return NULL; |
360 | } | 377 | } |
361 | 378 | ||
362 | /* | 379 | /* |
380 | * Read per-user configuration file. Ignore the system wide config | ||
381 | * file if the user specifies a config file on the command line. | ||
382 | */ | ||
383 | static void | ||
384 | process_config_files(struct passwd *pw) | ||
385 | { | ||
386 | char buf[MAXPATHLEN]; | ||
387 | int r; | ||
388 | |||
389 | if (config != NULL) { | ||
390 | if (strcasecmp(config, "none") != 0 && | ||
391 | !read_config_file(config, pw, host, &options, | ||
392 | SSHCONF_USERCONF)) | ||
393 | fatal("Can't open user config file %.100s: " | ||
394 | "%.100s", config, strerror(errno)); | ||
395 | } else { | ||
396 | r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, | ||
397 | _PATH_SSH_USER_CONFFILE); | ||
398 | if (r > 0 && (size_t)r < sizeof(buf)) | ||
399 | (void)read_config_file(buf, pw, host, &options, | ||
400 | SSHCONF_CHECKPERM|SSHCONF_USERCONF); | ||
401 | |||
402 | /* Read systemwide configuration file after user config. */ | ||
403 | (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host, | ||
404 | &options, 0); | ||
405 | } | ||
406 | } | ||
407 | |||
408 | /* | ||
363 | * Main program for the ssh client. | 409 | * Main program for the ssh client. |
364 | */ | 410 | */ |
365 | int | 411 | int |
@@ -832,31 +878,54 @@ main(int ac, char **av) | |||
832 | if (debug_flag) | 878 | if (debug_flag) |
833 | logit("%s, %s", SSH_VERSION, SSLeay_version(SSLEAY_VERSION)); | 879 | logit("%s, %s", SSH_VERSION, SSLeay_version(SSLEAY_VERSION)); |
834 | 880 | ||
881 | /* Parse the configuration files */ | ||
882 | process_config_files(pw); | ||
883 | |||
884 | /* Hostname canonicalisation needs a few options filled. */ | ||
885 | fill_default_options_for_canonicalization(&options); | ||
886 | |||
887 | /* If the user has replaced the hostname then take it into use now */ | ||
888 | if (options.hostname != NULL) { | ||
889 | /* NB. Please keep in sync with readconf.c:match_cfg_line() */ | ||
890 | cp = percent_expand(options.hostname, | ||
891 | "h", host, (char *)NULL); | ||
892 | free(host); | ||
893 | host = cp; | ||
894 | } | ||
895 | |||
896 | /* If canonicalization requested then try to apply it */ | ||
897 | lowercase(host); | ||
898 | if (options.canonicalize_hostname != SSH_CANONICALISE_NO) | ||
899 | addrs = resolve_canonicalize(&host, options.port); | ||
900 | |||
835 | /* | 901 | /* |
836 | * Read per-user configuration file. Ignore the system wide config | 902 | * If canonicalization not requested, or if it failed then try to |
837 | * file if the user specifies a config file on the command line. | 903 | * resolve the bare hostname name using the system resolver's usual |
904 | * search rules. Skip the lookup if a ProxyCommand is being used | ||
905 | * unless the user has specifically requested canonicalisation. | ||
838 | */ | 906 | */ |
839 | if (config != NULL) { | 907 | if (addrs == NULL && (option_clear_or_none(options.proxy_command) || |
840 | if (strcasecmp(config, "none") != 0 && | 908 | options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) { |
841 | !read_config_file(config, pw, host, &options, | 909 | if ((addrs = resolve_host(host, options.port, 1, |
842 | SSHCONF_USERCONF)) | 910 | cname, sizeof(cname))) == NULL) |
843 | fatal("Can't open user config file %.100s: " | 911 | cleanup_exit(255); /* resolve_host logs the error */ |
844 | "%.100s", config, strerror(errno)); | 912 | check_follow_cname(&host, cname); |
845 | } else { | 913 | } |
846 | r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, | ||
847 | _PATH_SSH_USER_CONFFILE); | ||
848 | if (r > 0 && (size_t)r < sizeof(buf)) | ||
849 | (void)read_config_file(buf, pw, host, &options, | ||
850 | SSHCONF_CHECKPERM|SSHCONF_USERCONF); | ||
851 | 914 | ||
852 | /* Read systemwide configuration file after user config. */ | 915 | /* |
853 | (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host, | 916 | * If the target hostname has changed as a result of canonicalisation |
854 | &options, 0); | 917 | * then re-parse the configuration files as new stanzas may match. |
918 | */ | ||
919 | if (strcasecmp(host_arg, host) != 0) { | ||
920 | debug("Hostname has changed; re-reading configuration"); | ||
921 | process_config_files(pw); | ||
855 | } | 922 | } |
856 | 923 | ||
857 | /* Fill configuration defaults. */ | 924 | /* Fill configuration defaults. */ |
858 | fill_default_options(&options); | 925 | fill_default_options(&options); |
859 | 926 | ||
927 | if (options.port == 0) | ||
928 | options.port = default_ssh_port(); | ||
860 | channel_set_af(options.address_family); | 929 | channel_set_af(options.address_family); |
861 | 930 | ||
862 | /* Tidy and check options */ | 931 | /* Tidy and check options */ |
@@ -899,37 +968,6 @@ main(int ac, char **av) | |||
899 | if (options.user == NULL) | 968 | if (options.user == NULL) |
900 | options.user = xstrdup(pw->pw_name); | 969 | options.user = xstrdup(pw->pw_name); |
901 | 970 | ||
902 | /* Get default port if port has not been set. */ | ||
903 | if (options.port == 0) | ||
904 | options.port = default_ssh_port(); | ||
905 | |||
906 | /* preserve host name given on command line for %n expansion */ | ||
907 | if (options.hostname != NULL) { | ||
908 | /* NB. Please keep in sync with readconf.c:match_cfg_line() */ | ||
909 | cp = percent_expand(options.hostname, | ||
910 | "h", host, (char *)NULL); | ||
911 | free(host); | ||
912 | host = cp; | ||
913 | } | ||
914 | |||
915 | /* If canonicalization requested then try to apply it */ | ||
916 | lowercase(host); | ||
917 | if (options.canonicalize_hostname != SSH_CANONICALISE_NO) | ||
918 | addrs = resolve_canonicalize(&host, options.port); | ||
919 | /* | ||
920 | * If canonicalization not requested, or if it failed then try to | ||
921 | * resolve the bare hostname name using the system resolver's usual | ||
922 | * search rules. Skip the lookup if a ProxyCommand is being used | ||
923 | * unless the user has specifically requested canonicalisation. | ||
924 | */ | ||
925 | if (addrs == NULL && (options.proxy_command == NULL || | ||
926 | options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) { | ||
927 | if ((addrs = resolve_host(host, options.port, 1, | ||
928 | cname, sizeof(cname))) == NULL) | ||
929 | cleanup_exit(255); /* resolve_host logs the error */ | ||
930 | check_follow_cname(&host, cname); | ||
931 | } | ||
932 | |||
933 | if (gethostname(thishost, sizeof(thishost)) == -1) | 971 | if (gethostname(thishost, sizeof(thishost)) == -1) |
934 | fatal("gethostname: %s", strerror(errno)); | 972 | fatal("gethostname: %s", strerror(errno)); |
935 | strlcpy(shorthost, thishost, sizeof(shorthost)); | 973 | strlcpy(shorthost, thishost, sizeof(shorthost)); |