mirror of
git://anongit.mindrot.org/openssh.git
synced 2024-11-23 09:17:32 +08:00
- 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@
This commit is contained in:
parent
d77b81f856
commit
0faf747e2f
10
ChangeLog
10
ChangeLog
@ -3,6 +3,16 @@
|
||||
- jmc@cvs.openbsd.org 2013/10/15 14:10:25
|
||||
[ssh.1 ssh_config.5]
|
||||
tweak previous;
|
||||
- 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@
|
||||
|
||||
20131015
|
||||
- (djm) OpenBSD CVS Sync
|
||||
|
113
readconf.c
113
readconf.c
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: readconf.c,v 1.207 2013/10/14 23:28:23 djm Exp $ */
|
||||
/* $OpenBSD: readconf.c,v 1.208 2013/10/16 02:31:45 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -144,6 +144,8 @@ typedef enum {
|
||||
oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
|
||||
oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
|
||||
oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass,
|
||||
oCanonicalDomains, oCanonicaliseHostname, oCanonicaliseMaxDots,
|
||||
oCanonicaliseFallbackLocal, oCanonicalisePermittedCNAMEs,
|
||||
oIgnoredUnknownOption, oDeprecated, oUnsupported
|
||||
} OpCodes;
|
||||
|
||||
@ -257,6 +259,11 @@ static struct {
|
||||
{ "ipqos", oIPQoS },
|
||||
{ "requesttty", oRequestTTY },
|
||||
{ "proxyusefdpass", oProxyUseFdpass },
|
||||
{ "canonicaldomains", oCanonicalDomains },
|
||||
{ "canonicalisefallbacklocal", oCanonicaliseFallbackLocal },
|
||||
{ "canonicalisehostname", oCanonicaliseHostname },
|
||||
{ "canonicalisemaxdots", oCanonicaliseMaxDots },
|
||||
{ "canonicalisepermittedcnames", oCanonicalisePermittedCNAMEs },
|
||||
{ "ignoreunknown", oIgnoreUnknown },
|
||||
|
||||
{ NULL, oBadOption }
|
||||
@ -535,6 +542,34 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw,
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Check and prepare a domain name: removes trailing '.' and lowercases */
|
||||
static void
|
||||
valid_domain(char *name, const char *filename, int linenum)
|
||||
{
|
||||
size_t i, l = strlen(name);
|
||||
u_char c, last = '\0';
|
||||
|
||||
if (l == 0)
|
||||
fatal("%s line %d: empty hostname suffix", filename, linenum);
|
||||
if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0]))
|
||||
fatal("%s line %d: hostname suffix \"%.100s\" "
|
||||
"starts with invalid character", filename, linenum, name);
|
||||
for (i = 0; i < l; i++) {
|
||||
c = tolower((u_char)name[i]);
|
||||
name[i] = (char)c;
|
||||
if (last == '.' && c == '.')
|
||||
fatal("%s line %d: hostname suffix \"%.100s\" contains "
|
||||
"consecutive separators", filename, linenum, name);
|
||||
if (c != '.' && c != '-' && !isalnum(c) &&
|
||||
c != '_') /* technically invalid, but common */
|
||||
fatal("%s line %d: hostname suffix \"%.100s\" contains "
|
||||
"invalid characters", filename, linenum, name);
|
||||
last = c;
|
||||
}
|
||||
if (name[l - 1] == '.')
|
||||
name[l - 1] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of the token pointed to by cp or oBadOption.
|
||||
*/
|
||||
@ -609,6 +644,14 @@ static const struct multistate multistate_requesttty[] = {
|
||||
{ "auto", REQUEST_TTY_AUTO },
|
||||
{ NULL, -1 }
|
||||
};
|
||||
static const struct multistate multistate_canonicalisehostname[] = {
|
||||
{ "true", SSH_CANONICALISE_YES },
|
||||
{ "false", SSH_CANONICALISE_NO },
|
||||
{ "yes", SSH_CANONICALISE_YES },
|
||||
{ "no", SSH_CANONICALISE_NO },
|
||||
{ "always", SSH_CANONICALISE_ALWAYS },
|
||||
{ NULL, -1 }
|
||||
};
|
||||
|
||||
/*
|
||||
* 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,
|
||||
size_t len;
|
||||
Forward fwd;
|
||||
const struct multistate *multistate_ptr;
|
||||
struct allowed_cname *cname;
|
||||
|
||||
if (activep == NULL) { /* We are processing a command line directive */
|
||||
cmdline = 1;
|
||||
@ -1263,6 +1307,62 @@ parse_int:
|
||||
intptr = &options->proxy_use_fdpass;
|
||||
goto parse_flag;
|
||||
|
||||
case oCanonicalDomains:
|
||||
value = options->num_canonical_domains != 0;
|
||||
while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
|
||||
valid_domain(arg, filename, linenum);
|
||||
if (!*activep || value)
|
||||
continue;
|
||||
if (options->num_canonical_domains >= MAX_CANON_DOMAINS)
|
||||
fatal("%s line %d: too many hostname suffixes.",
|
||||
filename, linenum);
|
||||
options->canonical_domains[
|
||||
options->num_canonical_domains++] = xstrdup(arg);
|
||||
}
|
||||
break;
|
||||
|
||||
case oCanonicalisePermittedCNAMEs:
|
||||
value = options->num_permitted_cnames != 0;
|
||||
while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
|
||||
/* Either '*' for everything or 'list:list' */
|
||||
if (strcmp(arg, "*") == 0)
|
||||
arg2 = arg;
|
||||
else {
|
||||
lowercase(arg);
|
||||
if ((arg2 = strchr(arg, ':')) == NULL ||
|
||||
arg2[1] == '\0') {
|
||||
fatal("%s line %d: "
|
||||
"Invalid permitted CNAME \"%s\"",
|
||||
filename, linenum, arg);
|
||||
}
|
||||
*arg2 = '\0';
|
||||
arg2++;
|
||||
}
|
||||
if (!*activep || value)
|
||||
continue;
|
||||
if (options->num_permitted_cnames >= MAX_CANON_DOMAINS)
|
||||
fatal("%s line %d: too many permitted CNAMEs.",
|
||||
filename, linenum);
|
||||
cname = options->permitted_cnames +
|
||||
options->num_permitted_cnames++;
|
||||
cname->source_list = xstrdup(arg);
|
||||
cname->target_list = xstrdup(arg2);
|
||||
}
|
||||
break;
|
||||
|
||||
case oCanonicaliseHostname:
|
||||
intptr = &options->canonicalise_hostname;
|
||||
multistate_ptr = multistate_canonicalisehostname;
|
||||
goto parse_multistate;
|
||||
|
||||
case oCanonicaliseMaxDots:
|
||||
intptr = &options->canonicalise_max_dots;
|
||||
goto parse_int;
|
||||
|
||||
case oCanonicaliseFallbackLocal:
|
||||
intptr = &options->canonicalise_fallback_local;
|
||||
goto parse_flag;
|
||||
|
||||
case oDeprecated:
|
||||
debug("%s line %d: Deprecated option \"%s\"",
|
||||
filename, linenum, keyword);
|
||||
@ -1426,6 +1526,11 @@ initialize_options(Options * options)
|
||||
options->request_tty = -1;
|
||||
options->proxy_use_fdpass = -1;
|
||||
options->ignored_unknown = NULL;
|
||||
options->num_canonical_domains = 0;
|
||||
options->num_permitted_cnames = 0;
|
||||
options->canonicalise_max_dots = -1;
|
||||
options->canonicalise_fallback_local = -1;
|
||||
options->canonicalise_hostname = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1579,6 +1684,12 @@ fill_default_options(Options * options)
|
||||
options->request_tty = REQUEST_TTY_AUTO;
|
||||
if (options->proxy_use_fdpass == -1)
|
||||
options->proxy_use_fdpass = 0;
|
||||
if (options->canonicalise_max_dots == -1)
|
||||
options->canonicalise_max_dots = 1;
|
||||
if (options->canonicalise_fallback_local == -1)
|
||||
options->canonicalise_fallback_local = 1;
|
||||
if (options->canonicalise_hostname == -1)
|
||||
options->canonicalise_hostname = SSH_CANONICALISE_NO;
|
||||
#define CLEAR_ON_NONE(v) \
|
||||
do { \
|
||||
if (v != NULL && strcasecmp(v, "none") == 0) { \
|
||||
|
22
readconf.h
22
readconf.h
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: readconf.h,v 1.97 2013/10/14 22:22:03 djm Exp $ */
|
||||
/* $OpenBSD: readconf.h,v 1.98 2013/10/16 02:31:46 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
@ -29,7 +29,13 @@ typedef struct {
|
||||
/* Data structure for representing option data. */
|
||||
|
||||
#define MAX_SEND_ENV 256
|
||||
#define SSH_MAX_HOSTS_FILES 256
|
||||
#define SSH_MAX_HOSTS_FILES 32
|
||||
#define MAX_CANON_DOMAINS 32
|
||||
|
||||
struct allowed_cname {
|
||||
char *source_list;
|
||||
char *target_list;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int forward_agent; /* Forward authentication agent. */
|
||||
@ -140,9 +146,21 @@ typedef struct {
|
||||
|
||||
int proxy_use_fdpass;
|
||||
|
||||
int num_canonical_domains;
|
||||
char *canonical_domains[MAX_CANON_DOMAINS];
|
||||
int canonicalise_hostname;
|
||||
int canonicalise_max_dots;
|
||||
int canonicalise_fallback_local;
|
||||
int num_permitted_cnames;
|
||||
struct allowed_cname permitted_cnames[MAX_CANON_DOMAINS];
|
||||
|
||||
char *ignored_unknown; /* Pattern list of unknown tokens to ignore */
|
||||
} Options;
|
||||
|
||||
#define SSH_CANONICALISE_NO 0
|
||||
#define SSH_CANONICALISE_YES 1
|
||||
#define SSH_CANONICALISE_ALWAYS 2
|
||||
|
||||
#define SSHCTL_MASTER_NO 0
|
||||
#define SSHCTL_MASTER_YES 1
|
||||
#define SSHCTL_MASTER_AUTO 2
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: roaming_client.c,v 1.5 2013/05/17 00:13:14 djm Exp $ */
|
||||
/* $OpenBSD: roaming_client.c,v 1.6 2013/10/16 02:31:46 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2004-2009 AppGate Network Security AB
|
||||
*
|
||||
@ -259,10 +259,10 @@ wait_for_roaming_reconnect(void)
|
||||
if (c != '\n' && c != '\r')
|
||||
continue;
|
||||
|
||||
if (ssh_connect(host, &hostaddr, options.port,
|
||||
if (ssh_connect(host, NULL, &hostaddr, options.port,
|
||||
options.address_family, 1, &timeout_ms,
|
||||
options.tcp_keep_alive, options.use_privileged_port,
|
||||
options.proxy_command) == 0 && roaming_resume() == 0) {
|
||||
options.tcp_keep_alive, options.use_privileged_port) == 0 &&
|
||||
roaming_resume() == 0) {
|
||||
packet_restore_state();
|
||||
reenter_guard = 0;
|
||||
fprintf(stderr, "[connection resumed]\n");
|
||||
|
9
ssh.1
9
ssh.1
@ -33,8 +33,8 @@
|
||||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $OpenBSD: ssh.1,v 1.337 2013/10/15 14:10:25 jmc Exp $
|
||||
.Dd $Mdocdate: October 15 2013 $
|
||||
.\" $OpenBSD: ssh.1,v 1.338 2013/10/16 02:31:46 djm Exp $
|
||||
.Dd $Mdocdate: October 16 2013 $
|
||||
.Dt SSH 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -417,6 +417,11 @@ For full details of the options listed below, and their possible values, see
|
||||
.It AddressFamily
|
||||
.It BatchMode
|
||||
.It BindAddress
|
||||
.It CanonicalDomains
|
||||
.It CanonicaliseFallbackLocal
|
||||
.It CanonicaliseHostname
|
||||
.It CanonicaliseMaxDots
|
||||
.It CanonicalisePermittedCNAMEs
|
||||
.It ChallengeResponseAuthentication
|
||||
.It CheckHostIP
|
||||
.It Cipher
|
||||
|
183
ssh.c
183
ssh.c
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ssh.c,v 1.384 2013/10/14 23:31:01 djm Exp $ */
|
||||
/* $OpenBSD: ssh.c,v 1.385 2013/10/16 02:31:46 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -231,6 +231,134 @@ tilde_expand_paths(char **paths, u_int num_paths)
|
||||
}
|
||||
}
|
||||
|
||||
static struct addrinfo *
|
||||
resolve_host(const char *name, u_int port, int logerr, char *cname, size_t clen)
|
||||
{
|
||||
char strport[NI_MAXSERV];
|
||||
struct addrinfo hints, *res;
|
||||
int gaierr, loglevel = SYSLOG_LEVEL_DEBUG1;
|
||||
|
||||
snprintf(strport, sizeof strport, "%u", port);
|
||||
bzero(&hints, sizeof(hints));
|
||||
hints.ai_family = options.address_family;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
if (cname != NULL)
|
||||
hints.ai_flags = AI_CANONNAME;
|
||||
if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) {
|
||||
if (logerr || (gaierr != EAI_NONAME && gaierr != EAI_NODATA))
|
||||
loglevel = SYSLOG_LEVEL_ERROR;
|
||||
do_log2(loglevel, "%s: Could not resolve hostname %.100s: %s",
|
||||
__progname, name, ssh_gai_strerror(gaierr));
|
||||
return NULL;
|
||||
}
|
||||
if (cname != NULL && res->ai_canonname != NULL) {
|
||||
if (strlcpy(cname, res->ai_canonname, clen) >= clen) {
|
||||
error("%s: host \"%s\" cname \"%s\" too long (max %lu)",
|
||||
__func__, name, res->ai_canonname, (u_long)clen);
|
||||
if (clen > 0)
|
||||
*cname = '\0';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the cname is a permitted replacement for the hostname
|
||||
* and perform the replacement if it is.
|
||||
*/
|
||||
static int
|
||||
check_follow_cname(char **namep, const char *cname)
|
||||
{
|
||||
int i;
|
||||
struct allowed_cname *rule;
|
||||
|
||||
if (*cname == '\0' || options.num_permitted_cnames == 0 ||
|
||||
strcmp(*namep, cname) == 0)
|
||||
return 0;
|
||||
if (options.canonicalise_hostname == SSH_CANONICALISE_NO)
|
||||
return 0;
|
||||
/*
|
||||
* Don't attempt to canonicalise names that will be interpreted by
|
||||
* a proxy unless the user specifically requests so.
|
||||
*/
|
||||
if (options.proxy_command != NULL &&
|
||||
options.canonicalise_hostname != SSH_CANONICALISE_ALWAYS)
|
||||
return 0;
|
||||
debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname);
|
||||
for (i = 0; i < options.num_permitted_cnames; i++) {
|
||||
rule = options.permitted_cnames + i;
|
||||
if (match_pattern_list(*namep, rule->source_list,
|
||||
strlen(rule->source_list), 1) != 1 ||
|
||||
match_pattern_list(cname, rule->target_list,
|
||||
strlen(rule->target_list), 1) != 1)
|
||||
continue;
|
||||
verbose("Canonicalised DNS aliased hostname "
|
||||
"\"%s\" => \"%s\"", *namep, cname);
|
||||
free(*namep);
|
||||
*namep = xstrdup(cname);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to resolve the supplied hostname after applying the user's
|
||||
* canonicalisation rules. Returns the address list for the host or NULL
|
||||
* if no name was found after canonicalisation.
|
||||
*/
|
||||
static struct addrinfo *
|
||||
resolve_canonicalise(char **hostp, u_int port)
|
||||
{
|
||||
int i, ndots;
|
||||
char *cp, *fullhost, cname_target[NI_MAXHOST];
|
||||
struct addrinfo *addrs;
|
||||
|
||||
if (options.canonicalise_hostname == SSH_CANONICALISE_NO)
|
||||
return NULL;
|
||||
/*
|
||||
* Don't attempt to canonicalise names that will be interpreted by
|
||||
* a proxy unless the user specifically requests so.
|
||||
*/
|
||||
if (options.proxy_command != NULL &&
|
||||
options.canonicalise_hostname != SSH_CANONICALISE_ALWAYS)
|
||||
return NULL;
|
||||
/* Don't apply canonicalisation to sufficiently-qualified hostnames */
|
||||
ndots = 0;
|
||||
for (cp = *hostp; *cp != '\0'; cp++) {
|
||||
if (*cp == '.')
|
||||
ndots++;
|
||||
}
|
||||
if (ndots > options.canonicalise_max_dots) {
|
||||
debug3("%s: not canonicalising hostname \"%s\" (max dots %d)",
|
||||
__func__, *hostp, options.canonicalise_max_dots);
|
||||
return NULL;
|
||||
}
|
||||
/* Attempt each supplied suffix */
|
||||
for (i = 0; i < options.num_canonical_domains; i++) {
|
||||
*cname_target = '\0';
|
||||
xasprintf(&fullhost, "%s.%s.", *hostp,
|
||||
options.canonical_domains[i]);
|
||||
if ((addrs = resolve_host(fullhost, options.port, 0,
|
||||
cname_target, sizeof(cname_target))) == NULL) {
|
||||
free(fullhost);
|
||||
continue;
|
||||
}
|
||||
/* Remove trailing '.' */
|
||||
fullhost[strlen(fullhost) - 1] = '\0';
|
||||
/* Follow CNAME if requested */
|
||||
if (!check_follow_cname(&fullhost, cname_target)) {
|
||||
debug("Canonicalised hostname \"%s\" => \"%s\"",
|
||||
*hostp, fullhost);
|
||||
}
|
||||
free(*hostp);
|
||||
*hostp = fullhost;
|
||||
return addrs;
|
||||
}
|
||||
if (!options.canonicalise_fallback_local)
|
||||
fatal("%s: Could not resolve host \"%s\"", __progname, host);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main program for the ssh client.
|
||||
*/
|
||||
@ -240,12 +368,14 @@ main(int ac, char **av)
|
||||
int i, r, opt, exit_status, use_syslog;
|
||||
char *p, *cp, *line, *argv0, buf[MAXPATHLEN], *host_arg, *logfile;
|
||||
char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
|
||||
char cname[NI_MAXHOST];
|
||||
struct stat st;
|
||||
struct passwd *pw;
|
||||
int timeout_ms;
|
||||
extern int optind, optreset;
|
||||
extern char *optarg;
|
||||
Forward fwd;
|
||||
struct addrinfo *addrs = NULL;
|
||||
|
||||
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
|
||||
sanitise_stdfd();
|
||||
@ -630,9 +760,9 @@ main(int ac, char **av)
|
||||
usage();
|
||||
options.user = p;
|
||||
*cp = '\0';
|
||||
host = ++cp;
|
||||
host = xstrdup(++cp);
|
||||
} else
|
||||
host = *av;
|
||||
host = xstrdup(*av);
|
||||
if (ac > 1) {
|
||||
optind = optreset = 1;
|
||||
goto again;
|
||||
@ -644,6 +774,9 @@ main(int ac, char **av)
|
||||
if (!host)
|
||||
usage();
|
||||
|
||||
lowercase(host);
|
||||
host_arg = xstrdup(host);
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
ERR_load_crypto_strings();
|
||||
|
||||
@ -728,6 +861,10 @@ main(int ac, char **av)
|
||||
strcmp(options.proxy_command, "-") == 0 &&
|
||||
options.proxy_use_fdpass)
|
||||
fatal("ProxyCommand=- and ProxyUseFDPass are incompatible");
|
||||
#ifndef HAVE_CYGWIN
|
||||
if (original_effective_uid != 0)
|
||||
options.use_privileged_port = 0;
|
||||
#endif
|
||||
|
||||
/* reinit */
|
||||
log_init(argv0, options.log_level, SYSLOG_FACILITY_USER, !use_syslog);
|
||||
@ -762,10 +899,26 @@ main(int ac, char **av)
|
||||
options.port = default_ssh_port();
|
||||
|
||||
/* preserve host name given on command line for %n expansion */
|
||||
host_arg = host;
|
||||
if (options.hostname != NULL) {
|
||||
host = percent_expand(options.hostname,
|
||||
cp = percent_expand(options.hostname,
|
||||
"h", host, (char *)NULL);
|
||||
free(host);
|
||||
host = cp;
|
||||
}
|
||||
|
||||
/* If canonicalisation requested then try to apply it */
|
||||
if (options.canonicalise_hostname != SSH_CANONICALISE_NO)
|
||||
addrs = resolve_canonicalise(&host, options.port);
|
||||
/*
|
||||
* If canonicalisation not requested, or if it failed then try to
|
||||
* resolve the bare hostname name using the system resolver's usual
|
||||
* search rules.
|
||||
*/
|
||||
if (addrs == NULL) {
|
||||
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)
|
||||
@ -803,16 +956,15 @@ main(int ac, char **av)
|
||||
timeout_ms = options.connection_timeout * 1000;
|
||||
|
||||
/* Open a connection to the remote host. */
|
||||
if (ssh_connect(host, &hostaddr, options.port,
|
||||
options.address_family, options.connection_attempts, &timeout_ms,
|
||||
options.tcp_keep_alive,
|
||||
#ifdef HAVE_CYGWIN
|
||||
options.use_privileged_port,
|
||||
#else
|
||||
original_effective_uid == 0 && options.use_privileged_port,
|
||||
#endif
|
||||
options.proxy_command) != 0)
|
||||
exit(255);
|
||||
if (ssh_connect(host, addrs, &hostaddr, options.port,
|
||||
options.address_family, options.connection_attempts,
|
||||
&timeout_ms, options.tcp_keep_alive,
|
||||
options.use_privileged_port) != 0)
|
||||
exit(255);
|
||||
|
||||
freeaddrinfo(addrs);
|
||||
packet_set_timeout(options.server_alive_interval,
|
||||
options.server_alive_count_max);
|
||||
|
||||
if (timeout_ms > 0)
|
||||
debug3("timeout: %d ms remain after connect", timeout_ms);
|
||||
@ -1621,4 +1773,3 @@ main_sigchld_handler(int sig)
|
||||
signal(sig, main_sigchld_handler);
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
|
75
ssh_config.5
75
ssh_config.5
@ -33,8 +33,8 @@
|
||||
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $OpenBSD: ssh_config.5,v 1.170 2013/10/15 14:10:25 jmc Exp $
|
||||
.Dd $Mdocdate: October 15 2013 $
|
||||
.\" $OpenBSD: ssh_config.5,v 1.171 2013/10/16 02:31:46 djm Exp $
|
||||
.Dd $Mdocdate: October 16 2013 $
|
||||
.Dt SSH_CONFIG 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -200,6 +200,77 @@ Note that this option does not work if
|
||||
.Cm UsePrivilegedPort
|
||||
is set to
|
||||
.Dq yes .
|
||||
.It Cm CanonicalDomains
|
||||
when
|
||||
.Cm CanonicaliseHostname
|
||||
is enabled, this option specifies the list of domain suffixes in which to
|
||||
search for the specified destination host.
|
||||
.It Cm CanonicaliseFallbackLocal
|
||||
specified whether to fail with an error when hostname canonicalisation fails.
|
||||
The default of
|
||||
.Dq no
|
||||
will attempt to lookup the unqualified hostname using the system resolver's
|
||||
search rules.
|
||||
A value of
|
||||
.Dq yes
|
||||
will cause
|
||||
.Xr ssh 1
|
||||
to fail instantly if
|
||||
.Cm CanonicaliseHostname
|
||||
is enabled and the target hostname cannot be found in any of the domains
|
||||
specified by
|
||||
.Cm CanonicalDomains .
|
||||
.It Cm CanonicaliseHostname
|
||||
controls whether explicit hostname canonicalisation is performed.
|
||||
The default
|
||||
.Dq no
|
||||
is not to perform any name rewriting and let the system resolver handle all
|
||||
hostname lookups.
|
||||
If set to
|
||||
.Dq yes
|
||||
then, for connections that do not use a
|
||||
.Cm ProxyCommand ,
|
||||
.Xr ssh 1
|
||||
will attempt to canonicalise the hostname specified on the command line
|
||||
using the
|
||||
.Cm CanonicalDomains
|
||||
suffixes and
|
||||
.Cm CanonicalisePermittedCNAMEs
|
||||
rules.
|
||||
If
|
||||
.Cm CanonicaliseHostname
|
||||
is set to
|
||||
.Dq always ,
|
||||
then canonicalisation is applied to proxied connections to.
|
||||
.It Cm CanonicaliseMaxDots
|
||||
specifies the maximum number of dot characters in a hostname name before
|
||||
canonicalisation is disabled.
|
||||
The default of
|
||||
.Dq 1
|
||||
allows a single dot (i.e. hostname.subdomain)
|
||||
.It Cm CanonicalisePermittedCNAMEs
|
||||
specifies rules to determine whether CNAMEs should be followed when
|
||||
canonicalising hostnames.
|
||||
The rules consist of one or more arguments of
|
||||
.Sm off
|
||||
.Ar source_domain_list : Ar target_domain_list
|
||||
.Sm on
|
||||
where
|
||||
.Ar source_domain_list
|
||||
is a pattern-list of domains that are may follow CNAMEs in canonicalisation
|
||||
and
|
||||
.Ar target_domain_list
|
||||
is a pattern-list of domains that they may resove to.
|
||||
.Pp
|
||||
For example,
|
||||
.Dq *.a.example.com:*.b.example.com,*.c.example.com
|
||||
will allow hostnames matching
|
||||
.Dq *.a.example.com
|
||||
to be canonicalised to names in the
|
||||
.Dq *.b.example.com
|
||||
or
|
||||
.Dq *.c.example.com
|
||||
domains.
|
||||
.It Cm ChallengeResponseAuthentication
|
||||
Specifies whether to use challenge-response authentication.
|
||||
The argument to this keyword must be
|
||||
|
74
sshconnect.c
74
sshconnect.c
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshconnect.c,v 1.240 2013/09/19 01:26:29 djm Exp $ */
|
||||
/* $OpenBSD: sshconnect.c,v 1.241 2013/10/16 02:31:46 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* 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,
|
||||
{
|
||||
char *tmp, *ret, strport[NI_MAXSERV];
|
||||
|
||||
snprintf(strport, sizeof strport, "%hu", port);
|
||||
snprintf(strport, sizeof strport, "%d", port);
|
||||
xasprintf(&tmp, "exec %s", proxy_command);
|
||||
ret = percent_expand(tmp, "h", host, "p", strport,
|
||||
"r", options.user, (char *)NULL);
|
||||
@ -170,8 +170,6 @@ ssh_proxy_fdpass_connect(const char *host, u_short port,
|
||||
|
||||
/* Set the connection file descriptors. */
|
||||
packet_set_connection(sock, sock);
|
||||
packet_set_timeout(options.server_alive_interval,
|
||||
options.server_alive_count_max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -187,16 +185,6 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
|
||||
pid_t pid;
|
||||
char *shell;
|
||||
|
||||
if (!strcmp(proxy_command, "-")) {
|
||||
packet_set_connection(STDIN_FILENO, STDOUT_FILENO);
|
||||
packet_set_timeout(options.server_alive_interval,
|
||||
options.server_alive_count_max);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (options.proxy_use_fdpass)
|
||||
return ssh_proxy_fdpass_connect(host, port, proxy_command);
|
||||
|
||||
if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
|
||||
shell = _PATH_BSHELL;
|
||||
|
||||
@ -258,8 +246,6 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
|
||||
|
||||
/* Set the connection file descriptors. */
|
||||
packet_set_connection(pout[0], pin[1]);
|
||||
packet_set_timeout(options.server_alive_interval,
|
||||
options.server_alive_count_max);
|
||||
|
||||
/* Indicate OK return */
|
||||
return 0;
|
||||
@ -429,33 +415,18 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr,
|
||||
* and %p substituted for host and port, respectively) to use to contact
|
||||
* the daemon.
|
||||
*/
|
||||
int
|
||||
ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
|
||||
u_short port, int family, int connection_attempts, int *timeout_ms,
|
||||
int want_keepalive, int needpriv, const char *proxy_command)
|
||||
static int
|
||||
ssh_connect_direct(const char *host, struct addrinfo *aitop,
|
||||
struct sockaddr_storage *hostaddr, u_short port, int family,
|
||||
int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
|
||||
{
|
||||
int gaierr;
|
||||
int on = 1;
|
||||
int sock = -1, attempt;
|
||||
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
|
||||
struct addrinfo hints, *ai, *aitop;
|
||||
struct addrinfo *ai;
|
||||
|
||||
debug2("ssh_connect: needpriv %d", needpriv);
|
||||
|
||||
/* If a proxy command is given, connect using it. */
|
||||
if (proxy_command != NULL)
|
||||
return ssh_proxy_connect(host, port, proxy_command);
|
||||
|
||||
/* No proxy command. */
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = family;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
snprintf(strport, sizeof strport, "%u", port);
|
||||
if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
|
||||
fatal("%s: Could not resolve hostname %.100s: %s", __progname,
|
||||
host, ssh_gai_strerror(gaierr));
|
||||
|
||||
for (attempt = 0; attempt < connection_attempts; attempt++) {
|
||||
if (attempt > 0) {
|
||||
/* Sleep a moment before retrying. */
|
||||
@ -467,7 +438,8 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
|
||||
* sequence until the connection succeeds.
|
||||
*/
|
||||
for (ai = aitop; ai; ai = ai->ai_next) {
|
||||
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
|
||||
if (ai->ai_family != AF_INET &&
|
||||
ai->ai_family != AF_INET6)
|
||||
continue;
|
||||
if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
|
||||
ntop, sizeof(ntop), strport, sizeof(strport),
|
||||
@ -500,8 +472,6 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
|
||||
break; /* Successful connection. */
|
||||
}
|
||||
|
||||
freeaddrinfo(aitop);
|
||||
|
||||
/* Return failure if we didn't get a successful connection. */
|
||||
if (sock == -1) {
|
||||
error("ssh: connect to host %s port %s: %s",
|
||||
@ -519,12 +489,28 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
|
||||
|
||||
/* Set the connection. */
|
||||
packet_set_connection(sock, sock);
|
||||
packet_set_timeout(options.server_alive_interval,
|
||||
options.server_alive_count_max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssh_connect(const char *host, struct addrinfo *addrs,
|
||||
struct sockaddr_storage *hostaddr, u_short port, int family,
|
||||
int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
|
||||
{
|
||||
if (options.proxy_command == NULL) {
|
||||
return ssh_connect_direct(host, addrs, hostaddr, port, family,
|
||||
connection_attempts, timeout_ms, want_keepalive, needpriv);
|
||||
} else if (strcmp(options.proxy_command, "-") == 0) {
|
||||
packet_set_connection(STDIN_FILENO, STDOUT_FILENO);
|
||||
return 0; /* Always succeeds */
|
||||
} else if (options.proxy_use_fdpass) {
|
||||
return ssh_proxy_fdpass_connect(host, port,
|
||||
options.proxy_command);
|
||||
}
|
||||
return ssh_proxy_connect(host, port, options.proxy_command);
|
||||
}
|
||||
|
||||
static void
|
||||
send_client_banner(int connection_out, int minor1)
|
||||
{
|
||||
@ -1265,7 +1251,7 @@ void
|
||||
ssh_login(Sensitive *sensitive, const char *orighost,
|
||||
struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
|
||||
{
|
||||
char *host, *cp;
|
||||
char *host;
|
||||
char *server_user, *local_user;
|
||||
|
||||
local_user = xstrdup(pw->pw_name);
|
||||
@ -1273,9 +1259,7 @@ ssh_login(Sensitive *sensitive, const char *orighost,
|
||||
|
||||
/* Convert the user-supplied hostname into all lowercase. */
|
||||
host = xstrdup(orighost);
|
||||
for (cp = host; *cp; cp++)
|
||||
if (isupper(*cp))
|
||||
*cp = (char)tolower(*cp);
|
||||
lowercase(host);
|
||||
|
||||
/* Exchange protocol version identification strings with the server. */
|
||||
ssh_exchange_identification(timeout_ms);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshconnect.h,v 1.27 2010/11/29 23:45:51 djm Exp $ */
|
||||
/* $OpenBSD: sshconnect.h,v 1.28 2013/10/16 02:31:47 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
@ -31,9 +31,9 @@ struct Sensitive {
|
||||
int external_keysign;
|
||||
};
|
||||
|
||||
int
|
||||
ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int,
|
||||
int *, int, int, const char *);
|
||||
struct addrinfo;
|
||||
int ssh_connect(const char *, struct addrinfo *, struct sockaddr_storage *,
|
||||
u_short, int, int, int *, int, int);
|
||||
void ssh_kill_proxy_command(void);
|
||||
|
||||
void ssh_login(Sensitive *, const char *, struct sockaddr *, u_short,
|
||||
|
Loading…
Reference in New Issue
Block a user