canohost.c revision 124211
157429Smarkm/*
257429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
357429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
457429Smarkm *                    All rights reserved
557429Smarkm * Functions for returning the canonical host name of the remote site.
660573Skris *
765674Skris * As far as I am concerned, the code I have written for this software
865674Skris * can be used freely for any purpose.  Any derived versions of this
965674Skris * software must be clearly marked as such, and if the derived work is
1065674Skris * incompatible with the protocol description in the RFC file, it must be
1165674Skris * called by a name other than "ssh" or "Secure Shell".
1257429Smarkm */
1357429Smarkm
1457429Smarkm#include "includes.h"
15124211SdesRCSID("$OpenBSD: canohost.c,v 1.37 2003/06/02 09:17:34 markus Exp $");
1657429Smarkm
1757429Smarkm#include "packet.h"
1857429Smarkm#include "xmalloc.h"
1976262Sgreen#include "log.h"
2076262Sgreen#include "canohost.h"
2157429Smarkm
2292559Sdesstatic void check_ip_options(int, char *);
2376262Sgreen
2457429Smarkm/*
2557429Smarkm * Return the canonical name of the host at the other end of the socket. The
2657429Smarkm * caller should free the returned string with xfree.
2757429Smarkm */
2857429Smarkm
2992559Sdesstatic char *
30124211Sdesget_remote_hostname(int socket, int use_dns)
3157429Smarkm{
3257429Smarkm	struct sockaddr_storage from;
3357429Smarkm	int i;
3457429Smarkm	socklen_t fromlen;
3557429Smarkm	struct addrinfo hints, *ai, *aitop;
3676262Sgreen	char name[NI_MAXHOST], ntop[NI_MAXHOST], ntop2[NI_MAXHOST];
3757429Smarkm
3857429Smarkm	/* Get IP address of client. */
3957429Smarkm	fromlen = sizeof(from);
4057429Smarkm	memset(&from, 0, sizeof(from));
41113911Sdes	if (getpeername(socket, (struct sockaddr *)&from, &fromlen) < 0) {
4257429Smarkm		debug("getpeername failed: %.100s", strerror(errno));
4357429Smarkm		fatal_cleanup();
4457429Smarkm	}
4598941Sdes#ifdef IPV4_IN_IPV6
4698941Sdes	if (from.ss_family == AF_INET6) {
4798941Sdes		struct sockaddr_in6 *from6 = (struct sockaddr_in6 *)&from;
4876262Sgreen
4998941Sdes		/* Detect IPv4 in IPv6 mapped address and convert it to */
5098941Sdes		/* plain (AF_INET) IPv4 address */
5198941Sdes		if (IN6_IS_ADDR_V4MAPPED(&from6->sin6_addr)) {
5298941Sdes			struct sockaddr_in *from4 = (struct sockaddr_in *)&from;
5398941Sdes			struct in_addr addr;
5498941Sdes			u_int16_t port;
5598941Sdes
5698941Sdes			memcpy(&addr, ((char *)&from6->sin6_addr) + 12, sizeof(addr));
5798941Sdes			port = from6->sin6_port;
5898941Sdes
5998941Sdes			memset(&from, 0, sizeof(from));
6098941Sdes
6198941Sdes			from4->sin_family = AF_INET;
62113911Sdes			fromlen = sizeof(*from4);
6398941Sdes			memcpy(&from4->sin_addr, &addr, sizeof(addr));
6498941Sdes			from4->sin_port = port;
6598941Sdes		}
6698941Sdes	}
6798941Sdes#endif
68113911Sdes	if (from.ss_family == AF_INET6)
69113911Sdes		fromlen = sizeof(struct sockaddr_in6);
7098941Sdes
7157429Smarkm	if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop),
7292559Sdes	    NULL, 0, NI_NUMERICHOST) != 0)
7357429Smarkm		fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed");
7457429Smarkm
75124211Sdes	if (!use_dns)
76124211Sdes		return xstrdup(ntop);
77124211Sdes
7898684Sdes	if (from.ss_family == AF_INET)
7998684Sdes		check_ip_options(socket, ntop);
8098684Sdes
8176262Sgreen	debug3("Trying to reverse map address %.100s.", ntop);
8257429Smarkm	/* Map the IP address to a host name. */
8357429Smarkm	if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
8492559Sdes	    NULL, 0, NI_NAMEREQD) != 0) {
8576262Sgreen		/* Host name not found.  Use ip address. */
8676262Sgreen		return xstrdup(ntop);
8757429Smarkm	}
8857429Smarkm
8976262Sgreen	/*
90124211Sdes	 * if reverse lookup result looks like a numeric hostname,
91124211Sdes	 * someone is trying to trick us by PTR record like following:
92124211Sdes	 *	1.1.1.10.in-addr.arpa.	IN PTR	2.3.4.5
93124211Sdes	 */
94124211Sdes	memset(&hints, 0, sizeof(hints));
95124211Sdes	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
96124211Sdes	hints.ai_flags = AI_NUMERICHOST;
97124211Sdes	if (getaddrinfo(name, "0", &hints, &ai) == 0) {
98124211Sdes		logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
99124211Sdes		    name, ntop);
100124211Sdes		freeaddrinfo(ai);
101124211Sdes		return xstrdup(ntop);
102124211Sdes	}
103124211Sdes
104124211Sdes	/*
10576262Sgreen	 * Convert it to all lowercase (which is expected by the rest
10676262Sgreen	 * of this software).
10776262Sgreen	 */
10876262Sgreen	for (i = 0; name[i]; i++)
10976262Sgreen		if (isupper(name[i]))
11076262Sgreen			name[i] = tolower(name[i]);
11157429Smarkm	/*
11276262Sgreen	 * Map it back to an IP address and check that the given
11376262Sgreen	 * address actually is an address of this host.  This is
11476262Sgreen	 * necessary because anyone with access to a name server can
11576262Sgreen	 * define arbitrary names for an IP address. Mapping from
11676262Sgreen	 * name to IP address can be trusted better (but can still be
11776262Sgreen	 * fooled if the intruder has access to the name server of
11876262Sgreen	 * the domain).
11957429Smarkm	 */
12076262Sgreen	memset(&hints, 0, sizeof(hints));
12176262Sgreen	hints.ai_family = from.ss_family;
12276262Sgreen	hints.ai_socktype = SOCK_STREAM;
12376262Sgreen	if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
124124211Sdes		logit("reverse mapping checking getaddrinfo for %.700s "
12576262Sgreen		    "failed - POSSIBLE BREAKIN ATTEMPT!", name);
12676262Sgreen		return xstrdup(ntop);
12757429Smarkm	}
12876262Sgreen	/* Look for the address from the list of addresses. */
12976262Sgreen	for (ai = aitop; ai; ai = ai->ai_next) {
13076262Sgreen		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
13176262Sgreen		    sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
13276262Sgreen		    (strcmp(ntop, ntop2) == 0))
13376262Sgreen				break;
13476262Sgreen	}
13576262Sgreen	freeaddrinfo(aitop);
13676262Sgreen	/* If we reached the end of the list, the address was not there. */
13776262Sgreen	if (!ai) {
13876262Sgreen		/* Address not found for the host name. */
139124211Sdes		logit("Address %.100s maps to %.600s, but this does not "
14076262Sgreen		    "map back to the address - POSSIBLE BREAKIN ATTEMPT!",
14176262Sgreen		    ntop, name);
14276262Sgreen		return xstrdup(ntop);
14376262Sgreen	}
14457429Smarkm	return xstrdup(name);
14557429Smarkm}
14657429Smarkm
14757429Smarkm/*
14876262Sgreen * If IP options are supported, make sure there are none (log and
14976262Sgreen * disconnect them if any are found).  Basically we are worried about
15076262Sgreen * source routing; it can be used to pretend you are somebody
15176262Sgreen * (ip-address) you are not. That itself may be "almost acceptable"
15276262Sgreen * under certain circumstances, but rhosts autentication is useless
15376262Sgreen * if source routing is accepted. Notice also that if we just dropped
15476262Sgreen * source routing here, the other side could use IP spoofing to do
15576262Sgreen * rest of the interaction and could still bypass security.  So we
15676262Sgreen * exit here if we detect any IP options.
15776262Sgreen */
15876262Sgreen/* IPv4 only */
15992559Sdesstatic void
16076262Sgreencheck_ip_options(int socket, char *ipaddr)
16176262Sgreen{
162124211Sdes#ifdef IP_OPTIONS
16376262Sgreen	u_char options[200];
16476262Sgreen	char text[sizeof(options) * 3 + 1];
16576262Sgreen	socklen_t option_size;
16676262Sgreen	int i, ipproto;
16776262Sgreen	struct protoent *ip;
16876262Sgreen
16976262Sgreen	if ((ip = getprotobyname("ip")) != NULL)
17076262Sgreen		ipproto = ip->p_proto;
17176262Sgreen	else
17276262Sgreen		ipproto = IPPROTO_IP;
17376262Sgreen	option_size = sizeof(options);
17492559Sdes	if (getsockopt(socket, ipproto, IP_OPTIONS, options,
17576262Sgreen	    &option_size) >= 0 && option_size != 0) {
17676262Sgreen		text[0] = '\0';
17776262Sgreen		for (i = 0; i < option_size; i++)
17876262Sgreen			snprintf(text + i*3, sizeof(text) - i*3,
17976262Sgreen			    " %2.2x", options[i]);
180124211Sdes		logit("Connection from %.100s with IP options:%.800s",
18176262Sgreen		    ipaddr, text);
18276262Sgreen		packet_disconnect("Connection from %.100s with IP options:%.800s",
18376262Sgreen		    ipaddr, text);
18476262Sgreen	}
185124211Sdes#endif /* IP_OPTIONS */
18676262Sgreen}
18776262Sgreen
18876262Sgreen/*
18957429Smarkm * Return the canonical name of the host in the other side of the current
19057429Smarkm * connection.  The host name is cached, so it is efficient to call this
19157429Smarkm * several times.
19257429Smarkm */
19357429Smarkm
19457429Smarkmconst char *
195124211Sdesget_canonical_hostname(int use_dns)
19657429Smarkm{
19757429Smarkm	static char *canonical_host_name = NULL;
198124211Sdes	static int use_dns_done = 0;
19957429Smarkm
20076262Sgreen	/* Check if we have previously retrieved name with same option. */
20176262Sgreen	if (canonical_host_name != NULL) {
202124211Sdes		if (use_dns_done != use_dns)
20376262Sgreen			xfree(canonical_host_name);
20476262Sgreen		else
20576262Sgreen			return canonical_host_name;
20676262Sgreen	}
20757429Smarkm
20857429Smarkm	/* Get the real hostname if socket; otherwise return UNKNOWN. */
20957429Smarkm	if (packet_connection_is_on_socket())
21076262Sgreen		canonical_host_name = get_remote_hostname(
211124211Sdes		    packet_get_connection_in(), use_dns);
21257429Smarkm	else
21357429Smarkm		canonical_host_name = xstrdup("UNKNOWN");
21457429Smarkm
215124211Sdes	use_dns_done = use_dns;
21657429Smarkm	return canonical_host_name;
21757429Smarkm}
21857429Smarkm
21957429Smarkm/*
220113911Sdes * Returns the local/remote IP-address/hostname of socket as a string.
221113911Sdes * The returned string must be freed.
22257429Smarkm */
22392559Sdesstatic char *
22476262Sgreenget_socket_address(int socket, int remote, int flags)
22557429Smarkm{
22676262Sgreen	struct sockaddr_storage addr;
22776262Sgreen	socklen_t addrlen;
22857429Smarkm	char ntop[NI_MAXHOST];
22957429Smarkm
23076262Sgreen	/* Get IP address of client. */
23176262Sgreen	addrlen = sizeof(addr);
23276262Sgreen	memset(&addr, 0, sizeof(addr));
23357429Smarkm
23476262Sgreen	if (remote) {
23576262Sgreen		if (getpeername(socket, (struct sockaddr *)&addr, &addrlen)
236106130Sdes		    < 0)
23776262Sgreen			return NULL;
23876262Sgreen	} else {
23976262Sgreen		if (getsockname(socket, (struct sockaddr *)&addr, &addrlen)
240106130Sdes		    < 0)
24176262Sgreen			return NULL;
24257429Smarkm	}
243113911Sdes
244113911Sdes	/* Work around Linux IPv6 weirdness */
245113911Sdes	if (addr.ss_family == AF_INET6)
246113911Sdes		addrlen = sizeof(struct sockaddr_in6);
247113911Sdes
24876262Sgreen	/* Get the address in ascii. */
24976262Sgreen	if (getnameinfo((struct sockaddr *)&addr, addrlen, ntop, sizeof(ntop),
25092559Sdes	    NULL, 0, flags) != 0) {
251113911Sdes		error("get_socket_address: getnameinfo %d failed", flags);
25276262Sgreen		return NULL;
25357429Smarkm	}
25476262Sgreen	return xstrdup(ntop);
25576262Sgreen}
25657429Smarkm
25776262Sgreenchar *
25876262Sgreenget_peer_ipaddr(int socket)
25976262Sgreen{
260106130Sdes	char *p;
261106130Sdes
262106130Sdes	if ((p = get_socket_address(socket, 1, NI_NUMERICHOST)) != NULL)
263106130Sdes		return p;
264106130Sdes	return xstrdup("UNKNOWN");
26576262Sgreen}
26657429Smarkm
26776262Sgreenchar *
26876262Sgreenget_local_ipaddr(int socket)
26976262Sgreen{
270106130Sdes	char *p;
271106130Sdes
272106130Sdes	if ((p = get_socket_address(socket, 0, NI_NUMERICHOST)) != NULL)
273106130Sdes		return p;
274106130Sdes	return xstrdup("UNKNOWN");
27557429Smarkm}
27657429Smarkm
27776262Sgreenchar *
27876262Sgreenget_local_name(int socket)
27976262Sgreen{
28076262Sgreen	return get_socket_address(socket, 0, NI_NAMEREQD);
28176262Sgreen}
28276262Sgreen
28362101Sgreen/*
28476262Sgreen * Returns the IP-address of the remote host as a string.  The returned
28576262Sgreen * string must not be freed.
28662101Sgreen */
28762101Sgreen
28862101Sgreenconst char *
28992559Sdesget_remote_ipaddr(void)
29062101Sgreen{
29162101Sgreen	static char *canonical_host_ip = NULL;
29262101Sgreen
29376262Sgreen	/* Check whether we have cached the ipaddr. */
29476262Sgreen	if (canonical_host_ip == NULL) {
29576262Sgreen		if (packet_connection_is_on_socket()) {
29676262Sgreen			canonical_host_ip =
29776262Sgreen			    get_peer_ipaddr(packet_get_connection_in());
29876262Sgreen			if (canonical_host_ip == NULL)
29976262Sgreen				fatal_cleanup();
30076262Sgreen		} else {
30176262Sgreen			/* If not on socket, return UNKNOWN. */
30276262Sgreen			canonical_host_ip = xstrdup("UNKNOWN");
30376262Sgreen		}
30462101Sgreen	}
30576262Sgreen	return canonical_host_ip;
30676262Sgreen}
30762101Sgreen
30876262Sgreenconst char *
309124211Sdesget_remote_name_or_ip(u_int utmp_len, int use_dns)
31076262Sgreen{
31176262Sgreen	static const char *remote = "";
31276262Sgreen	if (utmp_len > 0)
313124211Sdes		remote = get_canonical_hostname(use_dns);
31476262Sgreen	if (utmp_len == 0 || strlen(remote) > utmp_len)
31576262Sgreen		remote = get_remote_ipaddr();
31676262Sgreen	return remote;
31762101Sgreen}
31862101Sgreen
31957429Smarkm/* Returns the local/remote port for the socket. */
32057429Smarkm
32192559Sdesstatic int
32257429Smarkmget_sock_port(int sock, int local)
32357429Smarkm{
32457429Smarkm	struct sockaddr_storage from;
32557429Smarkm	socklen_t fromlen;
32657429Smarkm	char strport[NI_MAXSERV];
32757429Smarkm
32857429Smarkm	/* Get IP address of client. */
32957429Smarkm	fromlen = sizeof(from);
33057429Smarkm	memset(&from, 0, sizeof(from));
33157429Smarkm	if (local) {
33257429Smarkm		if (getsockname(sock, (struct sockaddr *)&from, &fromlen) < 0) {
33357429Smarkm			error("getsockname failed: %.100s", strerror(errno));
33457429Smarkm			return 0;
33557429Smarkm		}
33657429Smarkm	} else {
337113911Sdes		if (getpeername(sock, (struct sockaddr *)&from, &fromlen) < 0) {
33857429Smarkm			debug("getpeername failed: %.100s", strerror(errno));
33957429Smarkm			fatal_cleanup();
34057429Smarkm		}
34157429Smarkm	}
342113911Sdes
343113911Sdes	/* Work around Linux IPv6 weirdness */
344113911Sdes	if (from.ss_family == AF_INET6)
345113911Sdes		fromlen = sizeof(struct sockaddr_in6);
346113911Sdes
34757429Smarkm	/* Return port number. */
34857429Smarkm	if (getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0,
34992559Sdes	    strport, sizeof(strport), NI_NUMERICSERV) != 0)
35057429Smarkm		fatal("get_sock_port: getnameinfo NI_NUMERICSERV failed");
35157429Smarkm	return atoi(strport);
35257429Smarkm}
35357429Smarkm
35457429Smarkm/* Returns remote/local port number for the current connection. */
35557429Smarkm
35692559Sdesstatic int
35757429Smarkmget_port(int local)
35857429Smarkm{
35957429Smarkm	/*
36057429Smarkm	 * If the connection is not a socket, return 65535.  This is
36157429Smarkm	 * intentionally chosen to be an unprivileged port number.
36257429Smarkm	 */
36357429Smarkm	if (!packet_connection_is_on_socket())
36457429Smarkm		return 65535;
36557429Smarkm
36657429Smarkm	/* Get socket and return the port number. */
36757429Smarkm	return get_sock_port(packet_get_connection_in(), local);
36857429Smarkm}
36957429Smarkm
37060573Skrisint
37157429Smarkmget_peer_port(int sock)
37257429Smarkm{
37357429Smarkm	return get_sock_port(sock, 0);
37457429Smarkm}
37557429Smarkm
37660573Skrisint
37792559Sdesget_remote_port(void)
37857429Smarkm{
37957429Smarkm	return get_port(0);
38057429Smarkm}
38157429Smarkm
38257429Smarkmint
38392559Sdesget_local_port(void)
38457429Smarkm{
38557429Smarkm	return get_port(1);
38657429Smarkm}
387