352 lines
11 KiB
Diff
352 lines
11 KiB
Diff
http://bugs.gentoo.org/165444
|
|
https://bugzilla.mindrot.org/show_bug.cgi?id=1008
|
|
|
|
--- a/readconf.c
|
|
+++ b/readconf.c
|
|
@@ -148,6 +148,7 @@
|
|
oClearAllForwardings, oNoHostAuthenticationForLocalhost,
|
|
oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
|
|
oAddressFamily, oGssAuthentication, oGssDelegateCreds,
|
|
+ oGssTrustDns,
|
|
oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
|
|
oSendEnv, oControlPath, oControlMaster, oControlPersist,
|
|
oHashKnownHosts,
|
|
@@ -194,9 +195,11 @@
|
|
#if defined(GSSAPI)
|
|
{ "gssapiauthentication", oGssAuthentication },
|
|
{ "gssapidelegatecredentials", oGssDelegateCreds },
|
|
+ { "gssapitrustdns", oGssTrustDns },
|
|
# else
|
|
{ "gssapiauthentication", oUnsupported },
|
|
{ "gssapidelegatecredentials", oUnsupported },
|
|
+ { "gssapitrustdns", oUnsupported },
|
|
#endif
|
|
#ifdef ENABLE_PKCS11
|
|
{ "smartcarddevice", oPKCS11Provider },
|
|
@@ -930,6 +933,10 @@
|
|
intptr = &options->gss_deleg_creds;
|
|
goto parse_flag;
|
|
|
|
+ case oGssTrustDns:
|
|
+ intptr = &options->gss_trust_dns;
|
|
+ goto parse_flag;
|
|
+
|
|
case oBatchMode:
|
|
intptr = &options->batch_mode;
|
|
goto parse_flag;
|
|
@@ -1649,6 +1656,7 @@
|
|
options->challenge_response_authentication = -1;
|
|
options->gss_authentication = -1;
|
|
options->gss_deleg_creds = -1;
|
|
+ options->gss_trust_dns = -1;
|
|
options->password_authentication = -1;
|
|
options->kbd_interactive_authentication = -1;
|
|
options->kbd_interactive_devices = NULL;
|
|
@@ -1779,6 +1787,8 @@
|
|
options->gss_authentication = 0;
|
|
if (options->gss_deleg_creds == -1)
|
|
options->gss_deleg_creds = 0;
|
|
+ if (options->gss_trust_dns == -1)
|
|
+ options->gss_trust_dns = 0;
|
|
if (options->password_authentication == -1)
|
|
options->password_authentication = 1;
|
|
if (options->kbd_interactive_authentication == -1)
|
|
--- a/readconf.h
|
|
+++ b/readconf.h
|
|
@@ -46,6 +46,7 @@
|
|
/* Try S/Key or TIS, authentication. */
|
|
int gss_authentication; /* Try GSS authentication */
|
|
int gss_deleg_creds; /* Delegate GSS credentials */
|
|
+ int gss_trust_dns; /* Trust DNS for GSS canonicalization */
|
|
int password_authentication; /* Try password
|
|
* authentication. */
|
|
int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
|
|
--- a/ssh_config.5
|
|
+++ b/ssh_config.5
|
|
@@ -830,6 +830,16 @@
|
|
Forward (delegate) credentials to the server.
|
|
The default is
|
|
.Cm no .
|
|
+Note that this option applies to protocol version 2 connections using GSSAPI.
|
|
+.It Cm GSSAPITrustDns
|
|
+Set to
|
|
+.Dq yes to indicate that the DNS is trusted to securely canonicalize
|
|
+the name of the host being connected to. If
|
|
+.Dq no, the hostname entered on the
|
|
+command line will be passed untouched to the GSSAPI library.
|
|
+The default is
|
|
+.Dq no .
|
|
+This option only applies to protocol version 2 connections using GSSAPI.
|
|
.It Cm HashKnownHosts
|
|
Indicates that
|
|
.Xr ssh 1
|
|
--- a/sshconnect2.c
|
|
+++ b/sshconnect2.c
|
|
@@ -656,6 +656,13 @@
|
|
static u_int mech = 0;
|
|
OM_uint32 min;
|
|
int ok = 0;
|
|
+ const char *gss_host;
|
|
+
|
|
+ if (options.gss_trust_dns) {
|
|
+ extern const char *auth_get_canonical_hostname(struct ssh *ssh, int use_dns);
|
|
+ gss_host = auth_get_canonical_hostname(active_state, 1);
|
|
+ } else
|
|
+ gss_host = authctxt->host;
|
|
|
|
/* Try one GSSAPI method at a time, rather than sending them all at
|
|
* once. */
|
|
@@ -668,7 +674,7 @@
|
|
/* My DER encoding requires length<128 */
|
|
if (gss_supported->elements[mech].length < 128 &&
|
|
ssh_gssapi_check_mechanism(&gssctxt,
|
|
- &gss_supported->elements[mech], authctxt->host)) {
|
|
+ &gss_supported->elements[mech], gss_host)) {
|
|
ok = 1; /* Mechanism works */
|
|
} else {
|
|
mech++;
|
|
|
|
need to move these two funcs back to canohost so they're available to clients
|
|
and the server. auth.c is only used in the server.
|
|
|
|
--- a/auth.c
|
|
+++ b/auth.c
|
|
@@ -784,117 +784,3 @@ fakepw(void)
|
|
|
|
return (&fake);
|
|
}
|
|
-
|
|
-/*
|
|
- * Returns the remote DNS hostname as a string. The returned string must not
|
|
- * be freed. NB. this will usually trigger a DNS query the first time it is
|
|
- * called.
|
|
- * This function does additional checks on the hostname to mitigate some
|
|
- * attacks on legacy rhosts-style authentication.
|
|
- * XXX is RhostsRSAAuthentication vulnerable to these?
|
|
- * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
|
|
- */
|
|
-
|
|
-static char *
|
|
-remote_hostname(struct ssh *ssh)
|
|
-{
|
|
- struct sockaddr_storage from;
|
|
- socklen_t fromlen;
|
|
- struct addrinfo hints, *ai, *aitop;
|
|
- char name[NI_MAXHOST], ntop2[NI_MAXHOST];
|
|
- const char *ntop = ssh_remote_ipaddr(ssh);
|
|
-
|
|
- /* Get IP address of client. */
|
|
- fromlen = sizeof(from);
|
|
- memset(&from, 0, sizeof(from));
|
|
- if (getpeername(ssh_packet_get_connection_in(ssh),
|
|
- (struct sockaddr *)&from, &fromlen) < 0) {
|
|
- debug("getpeername failed: %.100s", strerror(errno));
|
|
- return strdup(ntop);
|
|
- }
|
|
-
|
|
- ipv64_normalise_mapped(&from, &fromlen);
|
|
- if (from.ss_family == AF_INET6)
|
|
- fromlen = sizeof(struct sockaddr_in6);
|
|
-
|
|
- debug3("Trying to reverse map address %.100s.", ntop);
|
|
- /* Map the IP address to a host name. */
|
|
- if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
|
|
- NULL, 0, NI_NAMEREQD) != 0) {
|
|
- /* Host name not found. Use ip address. */
|
|
- return strdup(ntop);
|
|
- }
|
|
-
|
|
- /*
|
|
- * if reverse lookup result looks like a numeric hostname,
|
|
- * someone is trying to trick us by PTR record like following:
|
|
- * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
|
|
- */
|
|
- memset(&hints, 0, sizeof(hints));
|
|
- hints.ai_socktype = SOCK_DGRAM; /*dummy*/
|
|
- hints.ai_flags = AI_NUMERICHOST;
|
|
- if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
|
|
- logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
|
|
- name, ntop);
|
|
- freeaddrinfo(ai);
|
|
- return strdup(ntop);
|
|
- }
|
|
-
|
|
- /* Names are stored in lowercase. */
|
|
- lowercase(name);
|
|
-
|
|
- /*
|
|
- * Map it back to an IP address and check that the given
|
|
- * address actually is an address of this host. This is
|
|
- * necessary because anyone with access to a name server can
|
|
- * define arbitrary names for an IP address. Mapping from
|
|
- * name to IP address can be trusted better (but can still be
|
|
- * fooled if the intruder has access to the name server of
|
|
- * the domain).
|
|
- */
|
|
- memset(&hints, 0, sizeof(hints));
|
|
- hints.ai_family = from.ss_family;
|
|
- hints.ai_socktype = SOCK_STREAM;
|
|
- if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
|
|
- logit("reverse mapping checking getaddrinfo for %.700s "
|
|
- "[%s] failed.", name, ntop);
|
|
- return strdup(ntop);
|
|
- }
|
|
- /* Look for the address from the list of addresses. */
|
|
- for (ai = aitop; ai; ai = ai->ai_next) {
|
|
- if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
|
|
- sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
|
|
- (strcmp(ntop, ntop2) == 0))
|
|
- break;
|
|
- }
|
|
- freeaddrinfo(aitop);
|
|
- /* If we reached the end of the list, the address was not there. */
|
|
- if (ai == NULL) {
|
|
- /* Address not found for the host name. */
|
|
- logit("Address %.100s maps to %.600s, but this does not "
|
|
- "map back to the address.", ntop, name);
|
|
- return strdup(ntop);
|
|
- }
|
|
- return strdup(name);
|
|
-}
|
|
-
|
|
-/*
|
|
- * Return the canonical name of the host in the other side of the current
|
|
- * connection. The host name is cached, so it is efficient to call this
|
|
- * several times.
|
|
- */
|
|
-
|
|
-const char *
|
|
-auth_get_canonical_hostname(struct ssh *ssh, int use_dns)
|
|
-{
|
|
- static char *dnsname;
|
|
-
|
|
- if (!use_dns)
|
|
- return ssh_remote_ipaddr(ssh);
|
|
- else if (dnsname != NULL)
|
|
- return dnsname;
|
|
- else {
|
|
- dnsname = remote_hostname(ssh);
|
|
- return dnsname;
|
|
- }
|
|
-}
|
|
--- a/canohost.c
|
|
+++ b/canohost.c
|
|
@@ -202,3 +202,117 @@ get_local_port(int sock)
|
|
{
|
|
return get_sock_port(sock, 1);
|
|
}
|
|
+
|
|
+/*
|
|
+ * Returns the remote DNS hostname as a string. The returned string must not
|
|
+ * be freed. NB. this will usually trigger a DNS query the first time it is
|
|
+ * called.
|
|
+ * This function does additional checks on the hostname to mitigate some
|
|
+ * attacks on legacy rhosts-style authentication.
|
|
+ * XXX is RhostsRSAAuthentication vulnerable to these?
|
|
+ * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?)
|
|
+ */
|
|
+
|
|
+static char *
|
|
+remote_hostname(struct ssh *ssh)
|
|
+{
|
|
+ struct sockaddr_storage from;
|
|
+ socklen_t fromlen;
|
|
+ struct addrinfo hints, *ai, *aitop;
|
|
+ char name[NI_MAXHOST], ntop2[NI_MAXHOST];
|
|
+ const char *ntop = ssh_remote_ipaddr(ssh);
|
|
+
|
|
+ /* Get IP address of client. */
|
|
+ fromlen = sizeof(from);
|
|
+ memset(&from, 0, sizeof(from));
|
|
+ if (getpeername(ssh_packet_get_connection_in(ssh),
|
|
+ (struct sockaddr *)&from, &fromlen) < 0) {
|
|
+ debug("getpeername failed: %.100s", strerror(errno));
|
|
+ return strdup(ntop);
|
|
+ }
|
|
+
|
|
+ ipv64_normalise_mapped(&from, &fromlen);
|
|
+ if (from.ss_family == AF_INET6)
|
|
+ fromlen = sizeof(struct sockaddr_in6);
|
|
+
|
|
+ debug3("Trying to reverse map address %.100s.", ntop);
|
|
+ /* Map the IP address to a host name. */
|
|
+ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
|
|
+ NULL, 0, NI_NAMEREQD) != 0) {
|
|
+ /* Host name not found. Use ip address. */
|
|
+ return strdup(ntop);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * if reverse lookup result looks like a numeric hostname,
|
|
+ * someone is trying to trick us by PTR record like following:
|
|
+ * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
|
|
+ */
|
|
+ memset(&hints, 0, sizeof(hints));
|
|
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
|
|
+ hints.ai_flags = AI_NUMERICHOST;
|
|
+ if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
|
|
+ logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
|
|
+ name, ntop);
|
|
+ freeaddrinfo(ai);
|
|
+ return strdup(ntop);
|
|
+ }
|
|
+
|
|
+ /* Names are stored in lowercase. */
|
|
+ lowercase(name);
|
|
+
|
|
+ /*
|
|
+ * Map it back to an IP address and check that the given
|
|
+ * address actually is an address of this host. This is
|
|
+ * necessary because anyone with access to a name server can
|
|
+ * define arbitrary names for an IP address. Mapping from
|
|
+ * name to IP address can be trusted better (but can still be
|
|
+ * fooled if the intruder has access to the name server of
|
|
+ * the domain).
|
|
+ */
|
|
+ memset(&hints, 0, sizeof(hints));
|
|
+ hints.ai_family = from.ss_family;
|
|
+ hints.ai_socktype = SOCK_STREAM;
|
|
+ if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
|
|
+ logit("reverse mapping checking getaddrinfo for %.700s "
|
|
+ "[%s] failed.", name, ntop);
|
|
+ return strdup(ntop);
|
|
+ }
|
|
+ /* Look for the address from the list of addresses. */
|
|
+ for (ai = aitop; ai; ai = ai->ai_next) {
|
|
+ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
|
|
+ sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
|
|
+ (strcmp(ntop, ntop2) == 0))
|
|
+ break;
|
|
+ }
|
|
+ freeaddrinfo(aitop);
|
|
+ /* If we reached the end of the list, the address was not there. */
|
|
+ if (ai == NULL) {
|
|
+ /* Address not found for the host name. */
|
|
+ logit("Address %.100s maps to %.600s, but this does not "
|
|
+ "map back to the address.", ntop, name);
|
|
+ return strdup(ntop);
|
|
+ }
|
|
+ return strdup(name);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return the canonical name of the host in the other side of the current
|
|
+ * connection. The host name is cached, so it is efficient to call this
|
|
+ * several times.
|
|
+ */
|
|
+
|
|
+const char *
|
|
+auth_get_canonical_hostname(struct ssh *ssh, int use_dns)
|
|
+{
|
|
+ static char *dnsname;
|
|
+
|
|
+ if (!use_dns)
|
|
+ return ssh_remote_ipaddr(ssh);
|
|
+ else if (dnsname != NULL)
|
|
+ return dnsname;
|
|
+ else {
|
|
+ dnsname = remote_hostname(ssh);
|
|
+ return dnsname;
|
|
+ }
|
|
+}
|