version 1.399, 2014/02/04 00:24:29 |
version 1.400, 2014/02/23 20:11:36 |
|
|
} |
} |
} |
} |
|
|
|
/* |
|
* Attempt to resolve a host name / port to a set of addresses and |
|
* optionally return any CNAMEs encountered along the way. |
|
* Returns NULL on failure. |
|
* NB. this function must operate with a options having undefined members. |
|
*/ |
static struct addrinfo * |
static struct addrinfo * |
resolve_host(const char *name, u_int port, int logerr, char *cname, size_t clen) |
resolve_host(const char *name, int port, int logerr, char *cname, size_t clen) |
{ |
{ |
char strport[NI_MAXSERV]; |
char strport[NI_MAXSERV]; |
struct addrinfo hints, *res; |
struct addrinfo hints, *res; |
int gaierr, loglevel = SYSLOG_LEVEL_DEBUG1; |
int gaierr, loglevel = SYSLOG_LEVEL_DEBUG1; |
|
|
|
if (port <= 0) |
|
port = default_ssh_port(); |
|
|
snprintf(strport, sizeof strport, "%u", port); |
snprintf(strport, sizeof strport, "%u", port); |
memset(&hints, 0, sizeof(hints)); |
memset(&hints, 0, sizeof(hints)); |
hints.ai_family = options.address_family; |
hints.ai_family = options.address_family == -1 ? |
|
AF_UNSPEC : options.address_family; |
hints.ai_socktype = SOCK_STREAM; |
hints.ai_socktype = SOCK_STREAM; |
if (cname != NULL) |
if (cname != NULL) |
hints.ai_flags = AI_CANONNAME; |
hints.ai_flags = AI_CANONNAME; |
|
|
/* |
/* |
* Check whether the cname is a permitted replacement for the hostname |
* Check whether the cname is a permitted replacement for the hostname |
* and perform the replacement if it is. |
* and perform the replacement if it is. |
|
* NB. this function must operate with a options having undefined members. |
*/ |
*/ |
static int |
static int |
check_follow_cname(char **namep, const char *cname) |
check_follow_cname(char **namep, const char *cname) |
|
|
* Don't attempt to canonicalize names that will be interpreted by |
* Don't attempt to canonicalize names that will be interpreted by |
* a proxy unless the user specifically requests so. |
* a proxy unless the user specifically requests so. |
*/ |
*/ |
if (options.proxy_command != NULL && |
if (!option_clear_or_none(options.proxy_command) && |
options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) |
options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) |
return 0; |
return 0; |
debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname); |
debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname); |
|
|
* Attempt to resolve the supplied hostname after applying the user's |
* Attempt to resolve the supplied hostname after applying the user's |
* canonicalization rules. Returns the address list for the host or NULL |
* canonicalization rules. Returns the address list for the host or NULL |
* if no name was found after canonicalization. |
* if no name was found after canonicalization. |
|
* NB. this function must operate with a options having undefined members. |
*/ |
*/ |
static struct addrinfo * |
static struct addrinfo * |
resolve_canonicalize(char **hostp, u_int port) |
resolve_canonicalize(char **hostp, int port) |
{ |
{ |
int i, ndots; |
int i, ndots; |
char *cp, *fullhost, cname_target[NI_MAXHOST]; |
char *cp, *fullhost, cname_target[NI_MAXHOST]; |
|
|
|
|
if (options.canonicalize_hostname == SSH_CANONICALISE_NO) |
if (options.canonicalize_hostname == SSH_CANONICALISE_NO) |
return NULL; |
return NULL; |
|
|
/* |
/* |
* Don't attempt to canonicalize names that will be interpreted by |
* Don't attempt to canonicalize names that will be interpreted by |
* a proxy unless the user specifically requests so. |
* a proxy unless the user specifically requests so. |
*/ |
*/ |
if (options.proxy_command != NULL && |
if (!option_clear_or_none(options.proxy_command) && |
options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) |
options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) |
return NULL; |
return NULL; |
|
|
/* Don't apply canonicalization to sufficiently-qualified hostnames */ |
/* Don't apply canonicalization to sufficiently-qualified hostnames */ |
ndots = 0; |
ndots = 0; |
for (cp = *hostp; *cp != '\0'; cp++) { |
for (cp = *hostp; *cp != '\0'; cp++) { |
|
|
*cname_target = '\0'; |
*cname_target = '\0'; |
xasprintf(&fullhost, "%s.%s.", *hostp, |
xasprintf(&fullhost, "%s.%s.", *hostp, |
options.canonical_domains[i]); |
options.canonical_domains[i]); |
if ((addrs = resolve_host(fullhost, options.port, 0, |
debug3("%s: attempting \"%s\" => \"%s\"", __func__, |
|
*hostp, fullhost); |
|
if ((addrs = resolve_host(fullhost, port, 0, |
cname_target, sizeof(cname_target))) == NULL) { |
cname_target, sizeof(cname_target))) == NULL) { |
free(fullhost); |
free(fullhost); |
continue; |
continue; |
|
|
return addrs; |
return addrs; |
} |
} |
if (!options.canonicalize_fallback_local) |
if (!options.canonicalize_fallback_local) |
fatal("%s: Could not resolve host \"%s\"", __progname, host); |
fatal("%s: Could not resolve host \"%s\"", __progname, *hostp); |
|
debug2("%s: host %s not found in any suffix", __func__, *hostp); |
return NULL; |
return NULL; |
} |
} |
|
|
/* |
/* |
|
* Read per-user configuration file. Ignore the system wide config |
|
* file if the user specifies a config file on the command line. |
|
*/ |
|
static void |
|
process_config_files(struct passwd *pw) |
|
{ |
|
char buf[MAXPATHLEN]; |
|
int r; |
|
|
|
if (config != NULL) { |
|
if (strcasecmp(config, "none") != 0 && |
|
!read_config_file(config, pw, host, &options, |
|
SSHCONF_USERCONF)) |
|
fatal("Can't open user config file %.100s: " |
|
"%.100s", config, strerror(errno)); |
|
} else { |
|
r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, |
|
_PATH_SSH_USER_CONFFILE); |
|
if (r > 0 && (size_t)r < sizeof(buf)) |
|
(void)read_config_file(buf, pw, host, &options, |
|
SSHCONF_CHECKPERM|SSHCONF_USERCONF); |
|
|
|
/* Read systemwide configuration file after user config. */ |
|
(void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host, |
|
&options, 0); |
|
} |
|
} |
|
|
|
/* |
* Main program for the ssh client. |
* Main program for the ssh client. |
*/ |
*/ |
int |
int |
|
|
if (debug_flag) |
if (debug_flag) |
logit("%s, %s", SSH_VERSION, SSLeay_version(SSLEAY_VERSION)); |
logit("%s, %s", SSH_VERSION, SSLeay_version(SSLEAY_VERSION)); |
|
|
|
/* Parse the configuration files */ |
|
process_config_files(pw); |
|
|
|
/* Hostname canonicalisation needs a few options filled. */ |
|
fill_default_options_for_canonicalization(&options); |
|
|
|
/* If the user has replaced the hostname then take it into use now */ |
|
if (options.hostname != NULL) { |
|
/* NB. Please keep in sync with readconf.c:match_cfg_line() */ |
|
cp = percent_expand(options.hostname, |
|
"h", host, (char *)NULL); |
|
free(host); |
|
host = cp; |
|
} |
|
|
|
/* If canonicalization requested then try to apply it */ |
|
lowercase(host); |
|
if (options.canonicalize_hostname != SSH_CANONICALISE_NO) |
|
addrs = resolve_canonicalize(&host, options.port); |
|
|
/* |
/* |
* Read per-user configuration file. Ignore the system wide config |
* If canonicalization not requested, or if it failed then try to |
* file if the user specifies a config file on the command line. |
* resolve the bare hostname name using the system resolver's usual |
|
* search rules. Skip the lookup if a ProxyCommand is being used |
|
* unless the user has specifically requested canonicalisation. |
*/ |
*/ |
if (config != NULL) { |
if (addrs == NULL && (option_clear_or_none(options.proxy_command) || |
if (strcasecmp(config, "none") != 0 && |
options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) { |
!read_config_file(config, pw, host, &options, |
if ((addrs = resolve_host(host, options.port, 1, |
SSHCONF_USERCONF)) |
cname, sizeof(cname))) == NULL) |
fatal("Can't open user config file %.100s: " |
cleanup_exit(255); /* resolve_host logs the error */ |
"%.100s", config, strerror(errno)); |
check_follow_cname(&host, cname); |
} else { |
} |
r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, |
|
_PATH_SSH_USER_CONFFILE); |
|
if (r > 0 && (size_t)r < sizeof(buf)) |
|
(void)read_config_file(buf, pw, host, &options, |
|
SSHCONF_CHECKPERM|SSHCONF_USERCONF); |
|
|
|
/* Read systemwide configuration file after user config. */ |
/* |
(void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host, |
* If the target hostname has changed as a result of canonicalisation |
&options, 0); |
* then re-parse the configuration files as new stanzas may match. |
|
*/ |
|
if (strcasecmp(host_arg, host) != 0) { |
|
debug("Hostname has changed; re-reading configuration"); |
|
process_config_files(pw); |
} |
} |
|
|
/* Fill configuration defaults. */ |
/* Fill configuration defaults. */ |
fill_default_options(&options); |
fill_default_options(&options); |
|
|
|
if (options.port == 0) |
|
options.port = default_ssh_port(); |
channel_set_af(options.address_family); |
channel_set_af(options.address_family); |
|
|
/* Tidy and check options */ |
/* Tidy and check options */ |
|
|
|
|
if (options.user == NULL) |
if (options.user == NULL) |
options.user = xstrdup(pw->pw_name); |
options.user = xstrdup(pw->pw_name); |
|
|
/* Get default port if port has not been set. */ |
|
if (options.port == 0) |
|
options.port = default_ssh_port(); |
|
|
|
/* preserve host name given on command line for %n expansion */ |
|
if (options.hostname != NULL) { |
|
/* NB. Please keep in sync with readconf.c:match_cfg_line() */ |
|
cp = percent_expand(options.hostname, |
|
"h", host, (char *)NULL); |
|
free(host); |
|
host = cp; |
|
} |
|
|
|
/* If canonicalization requested then try to apply it */ |
|
lowercase(host); |
|
if (options.canonicalize_hostname != SSH_CANONICALISE_NO) |
|
addrs = resolve_canonicalize(&host, options.port); |
|
/* |
|
* If canonicalization not requested, or if it failed then try to |
|
* resolve the bare hostname name using the system resolver's usual |
|
* search rules. Skip the lookup if a ProxyCommand is being used |
|
* unless the user has specifically requested canonicalisation. |
|
*/ |
|
if (addrs == NULL && (options.proxy_command == NULL || |
|
options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) { |
|
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) |
if (gethostname(thishost, sizeof(thishost)) == -1) |
fatal("gethostname: %s", strerror(errno)); |
fatal("gethostname: %s", strerror(errno)); |