summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2014-02-24 15:57:55 +1100
committerDamien Miller <djm@mindrot.org>2014-02-24 15:57:55 +1100
commit13f97b2286142fd0b8eab94e4ce84fe124eeb752 (patch)
treeb332b16a032b4670b60db1261b6dac48e059d916
parentbee3a234f3d1ad4244952bcff1b4b7c525330dc2 (diff)
- djm@cvs.openbsd.org 2014/02/23 20:11:36
[readconf.c readconf.h ssh.c ssh_config.5] reparse ssh_config and ~/.ssh/config if hostname canonicalisation changes the hostname. This allows users to write configurations that always refer to canonical hostnames, e.g. CanonicalizeHostname yes CanonicalDomains int.example.org example.org CanonicalizeFallbackLocal no Host *.int.example.org Compression off Host *.example.org User djm ok markus@
-rw-r--r--ChangeLog16
-rw-r--r--readconf.c27
-rw-r--r--readconf.h4
-rw-r--r--ssh.c150
-rw-r--r--ssh_config.510
5 files changed, 145 insertions, 62 deletions
diff --git a/ChangeLog b/ChangeLog
index e05b8698c..48d49dea1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -16,6 +16,22 @@
16 [ssh-ed25519.c] 16 [ssh-ed25519.c]
17 check for unsigned overflow; not reachable in OpenSSH but others might 17 check for unsigned overflow; not reachable in OpenSSH but others might
18 copy our code... 18 copy our code...
19 - djm@cvs.openbsd.org 2014/02/23 20:11:36
20 [readconf.c readconf.h ssh.c ssh_config.5]
21 reparse ssh_config and ~/.ssh/config if hostname canonicalisation changes
22 the hostname. This allows users to write configurations that always
23 refer to canonical hostnames, e.g.
24
25 CanonicalizeHostname yes
26 CanonicalDomains int.example.org example.org
27 CanonicalizeFallbackLocal no
28
29 Host *.int.example.org
30 Compression off
31 Host *.example.org
32 User djm
33
34 ok markus@
19 35
2020140213 3620140213
21 - (dtucker) [configure.ac openbsd-compat/openssl-compat.{c,h}] Add compat 37 - (dtucker) [configure.ac openbsd-compat/openssl-compat.{c,h}] Add compat
diff --git a/readconf.c b/readconf.c
index 94e645908..dc884c9b1 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.c,v 1.217 2014/02/22 01:32:19 djm Exp $ */ 1/* $OpenBSD: readconf.c,v 1.218 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
@@ -1467,6 +1467,13 @@ read_config_file(const char *filename, struct passwd *pw, const char *host,
1467 return 1; 1467 return 1;
1468} 1468}
1469 1469
1470/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
1471int
1472option_clear_or_none(const char *o)
1473{
1474 return o == NULL || strcasecmp(o, "none") == 0;
1475}
1476
1470/* 1477/*
1471 * Initializes options to special values that indicate that they have not yet 1478 * Initializes options to special values that indicate that they have not yet
1472 * been set. Read_config_file will only set options with this value. Options 1479 * been set. Read_config_file will only set options with this value. Options
@@ -1564,10 +1571,24 @@ initialize_options(Options * options)
1564} 1571}
1565 1572
1566/* 1573/*
1574 * A petite version of fill_default_options() that just fills the options
1575 * needed for hostname canonicalization to proceed.
1576 */
1577void
1578fill_default_options_for_canonicalization(Options *options)
1579{
1580 if (options->canonicalize_max_dots == -1)
1581 options->canonicalize_max_dots = 1;
1582 if (options->canonicalize_fallback_local == -1)
1583 options->canonicalize_fallback_local = 1;
1584 if (options->canonicalize_hostname == -1)
1585 options->canonicalize_hostname = SSH_CANONICALISE_NO;
1586}
1587
1588/*
1567 * Called after processing other sources of option data, this fills those 1589 * Called after processing other sources of option data, this fills those
1568 * options for which no value has been specified with their default values. 1590 * options for which no value has been specified with their default values.
1569 */ 1591 */
1570
1571void 1592void
1572fill_default_options(Options * options) 1593fill_default_options(Options * options)
1573{ 1594{
@@ -1722,7 +1743,7 @@ fill_default_options(Options * options)
1722 options->canonicalize_hostname = SSH_CANONICALISE_NO; 1743 options->canonicalize_hostname = SSH_CANONICALISE_NO;
1723#define CLEAR_ON_NONE(v) \ 1744#define CLEAR_ON_NONE(v) \
1724 do { \ 1745 do { \
1725 if (v != NULL && strcasecmp(v, "none") == 0) { \ 1746 if (option_clear_or_none(v)) { \
1726 free(v); \ 1747 free(v); \
1727 v = NULL; \ 1748 v = NULL; \
1728 } \ 1749 } \
diff --git a/readconf.h b/readconf.h
index 9723da078..75e3f8f7a 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.h,v 1.100 2014/01/29 06:18:35 djm Exp $ */ 1/* $OpenBSD: readconf.h,v 1.101 2014/02/23 20:11:36 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -176,12 +176,14 @@ typedef struct {
176 176
177void initialize_options(Options *); 177void initialize_options(Options *);
178void fill_default_options(Options *); 178void fill_default_options(Options *);
179void fill_default_options_for_canonicalization(Options *);
179int process_config_line(Options *, struct passwd *, const char *, char *, 180int process_config_line(Options *, struct passwd *, const char *, char *,
180 const char *, int, int *, int); 181 const char *, int, int *, int);
181int read_config_file(const char *, struct passwd *, const char *, 182int read_config_file(const char *, struct passwd *, const char *,
182 Options *, int); 183 Options *, int);
183int parse_forward(Forward *, const char *, int, int); 184int parse_forward(Forward *, const char *, int, int);
184int default_ssh_port(void); 185int default_ssh_port(void);
186int option_clear_or_none(const char *);
185 187
186void add_local_forward(Options *, const Forward *); 188void add_local_forward(Options *, const Forward *);
187void add_remote_forward(Options *, const Forward *); 189void add_remote_forward(Options *, const Forward *);
diff --git a/ssh.c b/ssh.c
index add760ca8..b7dbea2b6 100644
--- a/ssh.c
+++ b/ssh.c
@@ -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 */
234static struct addrinfo * 240static struct addrinfo *
235resolve_host(const char *name, u_int port, int logerr, char *cname, size_t clen) 241resolve_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 */
269static int 280static int
270check_follow_cname(char **namep, const char *cname) 281check_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 */
309static struct addrinfo * 321static struct addrinfo *
310resolve_canonicalize(char **hostp, u_int port) 322resolve_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 */
383static void
384process_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 */
365int 411int
@@ -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));
diff --git a/ssh_config.5 b/ssh_config.5
index 3cadcd767..b5803920f 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.184 2014/01/19 04:48:08 djm Exp $ 36.\" $OpenBSD: ssh_config.5,v 1.185 2014/02/23 20:11:36 djm Exp $
37.Dd $Mdocdate: January 19 2014 $ 37.Dd $Mdocdate: February 23 2014 $
38.Dt SSH_CONFIG 5 38.Dt SSH_CONFIG 5
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -263,6 +263,12 @@ If
263is set to 263is set to
264.Dq always , 264.Dq always ,
265then canonicalization is applied to proxied connections too. 265then canonicalization is applied to proxied connections too.
266.Pp
267If this option is enabled and canonicalisation results in the target hostname
268changing, then the configuration files are processed again using the new
269target name to pick up any new configuration in matching
270.Cm Host
271stanzas.
266.It Cm CanonicalizeMaxDots 272.It Cm CanonicalizeMaxDots
267Specifies the maximum number of dot characters in a hostname before 273Specifies the maximum number of dot characters in a hostname before
268canonicalization is disabled. 274canonicalization is disabled.