canohost.c revision 137019
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"
15137019SdesRCSID("$OpenBSD: canohost.c,v 1.41 2004/07/21 11:51:29 djm 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 *);
23126277Sdesstatic void ipv64_normalise_mapped(struct sockaddr_storage *, socklen_t *);
2476262Sgreen
2557429Smarkm/*
2657429Smarkm * Return the canonical name of the host at the other end of the socket. The
2757429Smarkm * caller should free the returned string with xfree.
2857429Smarkm */
2957429Smarkm
3092559Sdesstatic char *
31137019Sdesget_remote_hostname(int sock, int use_dns)
3257429Smarkm{
3357429Smarkm	struct sockaddr_storage from;
3457429Smarkm	int i;
3557429Smarkm	socklen_t fromlen;
3657429Smarkm	struct addrinfo hints, *ai, *aitop;
3776262Sgreen	char name[NI_MAXHOST], ntop[NI_MAXHOST], ntop2[NI_MAXHOST];
3857429Smarkm
3957429Smarkm	/* Get IP address of client. */
4057429Smarkm	fromlen = sizeof(from);
4157429Smarkm	memset(&from, 0, sizeof(from));
42137019Sdes	if (getpeername(sock, (struct sockaddr *)&from, &fromlen) < 0) {
4357429Smarkm		debug("getpeername failed: %.100s", strerror(errno));
44126277Sdes		cleanup_exit(255);
4557429Smarkm	}
4676262Sgreen
47128460Sdes	if (from.ss_family == AF_INET)
48137019Sdes		check_ip_options(sock, ntop);
49128460Sdes
50126277Sdes	ipv64_normalise_mapped(&from, &fromlen);
5198941Sdes
52113911Sdes	if (from.ss_family == AF_INET6)
53113911Sdes		fromlen = sizeof(struct sockaddr_in6);
5498941Sdes
5557429Smarkm	if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop),
5692559Sdes	    NULL, 0, NI_NUMERICHOST) != 0)
5757429Smarkm		fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed");
5857429Smarkm
59124211Sdes	if (!use_dns)
60124211Sdes		return xstrdup(ntop);
61124211Sdes
6276262Sgreen	debug3("Trying to reverse map address %.100s.", ntop);
6357429Smarkm	/* Map the IP address to a host name. */
6457429Smarkm	if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
6592559Sdes	    NULL, 0, NI_NAMEREQD) != 0) {
6676262Sgreen		/* Host name not found.  Use ip address. */
6776262Sgreen		return xstrdup(ntop);
6857429Smarkm	}
6957429Smarkm
7076262Sgreen	/*
71124211Sdes	 * if reverse lookup result looks like a numeric hostname,
72124211Sdes	 * someone is trying to trick us by PTR record like following:
73124211Sdes	 *	1.1.1.10.in-addr.arpa.	IN PTR	2.3.4.5
74124211Sdes	 */
75124211Sdes	memset(&hints, 0, sizeof(hints));
76124211Sdes	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
77124211Sdes	hints.ai_flags = AI_NUMERICHOST;
78124211Sdes	if (getaddrinfo(name, "0", &hints, &ai) == 0) {
79124211Sdes		logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
80124211Sdes		    name, ntop);
81124211Sdes		freeaddrinfo(ai);
82124211Sdes		return xstrdup(ntop);
83124211Sdes	}
84124211Sdes
85124211Sdes	/*
8676262Sgreen	 * Convert it to all lowercase (which is expected by the rest
8776262Sgreen	 * of this software).
8876262Sgreen	 */
8976262Sgreen	for (i = 0; name[i]; i++)
9076262Sgreen		if (isupper(name[i]))
9176262Sgreen			name[i] = tolower(name[i]);
9257429Smarkm	/*
9376262Sgreen	 * Map it back to an IP address and check that the given
9476262Sgreen	 * address actually is an address of this host.  This is
9576262Sgreen	 * necessary because anyone with access to a name server can
9676262Sgreen	 * define arbitrary names for an IP address. Mapping from
9776262Sgreen	 * name to IP address can be trusted better (but can still be
9876262Sgreen	 * fooled if the intruder has access to the name server of
9976262Sgreen	 * the domain).
10057429Smarkm	 */
10176262Sgreen	memset(&hints, 0, sizeof(hints));
10276262Sgreen	hints.ai_family = from.ss_family;
10376262Sgreen	hints.ai_socktype = SOCK_STREAM;
10476262Sgreen	if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
105124211Sdes		logit("reverse mapping checking getaddrinfo for %.700s "
10676262Sgreen		    "failed - POSSIBLE BREAKIN ATTEMPT!", name);
10776262Sgreen		return xstrdup(ntop);
10857429Smarkm	}
10976262Sgreen	/* Look for the address from the list of addresses. */
11076262Sgreen	for (ai = aitop; ai; ai = ai->ai_next) {
11176262Sgreen		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
11276262Sgreen		    sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
11376262Sgreen		    (strcmp(ntop, ntop2) == 0))
11476262Sgreen				break;
11576262Sgreen	}
11676262Sgreen	freeaddrinfo(aitop);
11776262Sgreen	/* If we reached the end of the list, the address was not there. */
11876262Sgreen	if (!ai) {
11976262Sgreen		/* Address not found for the host name. */
120124211Sdes		logit("Address %.100s maps to %.600s, but this does not "
12176262Sgreen		    "map back to the address - POSSIBLE BREAKIN ATTEMPT!",
12276262Sgreen		    ntop, name);
12376262Sgreen		return xstrdup(ntop);
12476262Sgreen	}
12557429Smarkm	return xstrdup(name);
12657429Smarkm}
12757429Smarkm
12857429Smarkm/*
12976262Sgreen * If IP options are supported, make sure there are none (log and
13076262Sgreen * disconnect them if any are found).  Basically we are worried about
13176262Sgreen * source routing; it can be used to pretend you are somebody
13276262Sgreen * (ip-address) you are not. That itself may be "almost acceptable"
13376262Sgreen * under certain circumstances, but rhosts autentication is useless
13476262Sgreen * if source routing is accepted. Notice also that if we just dropped
13576262Sgreen * source routing here, the other side could use IP spoofing to do
13676262Sgreen * rest of the interaction and could still bypass security.  So we
13776262Sgreen * exit here if we detect any IP options.
13876262Sgreen */
13976262Sgreen/* IPv4 only */
14092559Sdesstatic void
141137019Sdescheck_ip_options(int sock, char *ipaddr)
14276262Sgreen{
143124211Sdes#ifdef IP_OPTIONS
14476262Sgreen	u_char options[200];
14576262Sgreen	char text[sizeof(options) * 3 + 1];
14676262Sgreen	socklen_t option_size;
14776262Sgreen	int i, ipproto;
14876262Sgreen	struct protoent *ip;
14976262Sgreen
15076262Sgreen	if ((ip = getprotobyname("ip")) != NULL)
15176262Sgreen		ipproto = ip->p_proto;
15276262Sgreen	else
15376262Sgreen		ipproto = IPPROTO_IP;
15476262Sgreen	option_size = sizeof(options);
155137019Sdes	if (getsockopt(sock, ipproto, IP_OPTIONS, options,
15676262Sgreen	    &option_size) >= 0 && option_size != 0) {
15776262Sgreen		text[0] = '\0';
15876262Sgreen		for (i = 0; i < option_size; i++)
15976262Sgreen			snprintf(text + i*3, sizeof(text) - i*3,
16076262Sgreen			    " %2.2x", options[i]);
161124211Sdes		logit("Connection from %.100s with IP options:%.800s",
16276262Sgreen		    ipaddr, text);
16376262Sgreen		packet_disconnect("Connection from %.100s with IP options:%.800s",
16476262Sgreen		    ipaddr, text);
16576262Sgreen	}
166124211Sdes#endif /* IP_OPTIONS */
16776262Sgreen}
16876262Sgreen
169126277Sdesstatic void
170126277Sdesipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
171126277Sdes{
172126277Sdes	struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)addr;
173126277Sdes	struct sockaddr_in *a4 = (struct sockaddr_in *)addr;
174126277Sdes	struct in_addr inaddr;
175126277Sdes	u_int16_t port;
176126277Sdes
177126277Sdes	if (addr->ss_family != AF_INET6 ||
178126277Sdes	    !IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr))
179126277Sdes		return;
180126277Sdes
181126277Sdes	debug3("Normalising mapped IPv4 in IPv6 address");
182126277Sdes
183126277Sdes	memcpy(&inaddr, ((char *)&a6->sin6_addr) + 12, sizeof(inaddr));
184126277Sdes	port = a6->sin6_port;
185126277Sdes
186126277Sdes	memset(addr, 0, sizeof(*a4));
187126277Sdes
188126277Sdes	a4->sin_family = AF_INET;
189126277Sdes	*len = sizeof(*a4);
190126277Sdes	memcpy(&a4->sin_addr, &inaddr, sizeof(inaddr));
191126277Sdes	a4->sin_port = port;
192126277Sdes}
193126277Sdes
19476262Sgreen/*
19557429Smarkm * Return the canonical name of the host in the other side of the current
19657429Smarkm * connection.  The host name is cached, so it is efficient to call this
19757429Smarkm * several times.
19857429Smarkm */
19957429Smarkm
20057429Smarkmconst char *
201124211Sdesget_canonical_hostname(int use_dns)
20257429Smarkm{
20357429Smarkm	static char *canonical_host_name = NULL;
204124211Sdes	static int use_dns_done = 0;
20557429Smarkm
20676262Sgreen	/* Check if we have previously retrieved name with same option. */
20776262Sgreen	if (canonical_host_name != NULL) {
208124211Sdes		if (use_dns_done != use_dns)
20976262Sgreen			xfree(canonical_host_name);
21076262Sgreen		else
21176262Sgreen			return canonical_host_name;
21276262Sgreen	}
21357429Smarkm
21457429Smarkm	/* Get the real hostname if socket; otherwise return UNKNOWN. */
21557429Smarkm	if (packet_connection_is_on_socket())
21676262Sgreen		canonical_host_name = get_remote_hostname(
217124211Sdes		    packet_get_connection_in(), use_dns);
21857429Smarkm	else
21957429Smarkm		canonical_host_name = xstrdup("UNKNOWN");
22057429Smarkm
221124211Sdes	use_dns_done = use_dns;
22257429Smarkm	return canonical_host_name;
22357429Smarkm}
22457429Smarkm
22557429Smarkm/*
226113911Sdes * Returns the local/remote IP-address/hostname of socket as a string.
227113911Sdes * The returned string must be freed.
22857429Smarkm */
22992559Sdesstatic char *
230137019Sdesget_socket_address(int sock, int remote, int flags)
23157429Smarkm{
23276262Sgreen	struct sockaddr_storage addr;
23376262Sgreen	socklen_t addrlen;
23457429Smarkm	char ntop[NI_MAXHOST];
23557429Smarkm
23676262Sgreen	/* Get IP address of client. */
23776262Sgreen	addrlen = sizeof(addr);
23876262Sgreen	memset(&addr, 0, sizeof(addr));
23957429Smarkm
24076262Sgreen	if (remote) {
241137019Sdes		if (getpeername(sock, (struct sockaddr *)&addr, &addrlen)
242106130Sdes		    < 0)
24376262Sgreen			return NULL;
24476262Sgreen	} else {
245137019Sdes		if (getsockname(sock, (struct sockaddr *)&addr, &addrlen)
246106130Sdes		    < 0)
24776262Sgreen			return NULL;
24857429Smarkm	}
249113911Sdes
250113911Sdes	/* Work around Linux IPv6 weirdness */
251113911Sdes	if (addr.ss_family == AF_INET6)
252113911Sdes		addrlen = sizeof(struct sockaddr_in6);
253113911Sdes
25476262Sgreen	/* Get the address in ascii. */
25576262Sgreen	if (getnameinfo((struct sockaddr *)&addr, addrlen, ntop, sizeof(ntop),
25692559Sdes	    NULL, 0, flags) != 0) {
257113911Sdes		error("get_socket_address: getnameinfo %d failed", flags);
25876262Sgreen		return NULL;
25957429Smarkm	}
26076262Sgreen	return xstrdup(ntop);
26176262Sgreen}
26257429Smarkm
26376262Sgreenchar *
264137019Sdesget_peer_ipaddr(int sock)
26576262Sgreen{
266106130Sdes	char *p;
267106130Sdes
268137019Sdes	if ((p = get_socket_address(sock, 1, NI_NUMERICHOST)) != NULL)
269106130Sdes		return p;
270106130Sdes	return xstrdup("UNKNOWN");
27176262Sgreen}
27257429Smarkm
27376262Sgreenchar *
274137019Sdesget_local_ipaddr(int sock)
27576262Sgreen{
276106130Sdes	char *p;
277106130Sdes
278137019Sdes	if ((p = get_socket_address(sock, 0, NI_NUMERICHOST)) != NULL)
279106130Sdes		return p;
280106130Sdes	return xstrdup("UNKNOWN");
28157429Smarkm}
28257429Smarkm
28376262Sgreenchar *
284137019Sdesget_local_name(int sock)
28576262Sgreen{
286137019Sdes	return get_socket_address(sock, 0, NI_NAMEREQD);
28776262Sgreen}
28876262Sgreen
28962101Sgreen/*
29076262Sgreen * Returns the IP-address of the remote host as a string.  The returned
29176262Sgreen * string must not be freed.
29262101Sgreen */
29362101Sgreen
29462101Sgreenconst char *
29592559Sdesget_remote_ipaddr(void)
29662101Sgreen{
29762101Sgreen	static char *canonical_host_ip = NULL;
29862101Sgreen
29976262Sgreen	/* Check whether we have cached the ipaddr. */
30076262Sgreen	if (canonical_host_ip == NULL) {
30176262Sgreen		if (packet_connection_is_on_socket()) {
30276262Sgreen			canonical_host_ip =
30376262Sgreen			    get_peer_ipaddr(packet_get_connection_in());
30476262Sgreen			if (canonical_host_ip == NULL)
305126277Sdes				cleanup_exit(255);
30676262Sgreen		} else {
30776262Sgreen			/* If not on socket, return UNKNOWN. */
30876262Sgreen			canonical_host_ip = xstrdup("UNKNOWN");
30976262Sgreen		}
31062101Sgreen	}
31176262Sgreen	return canonical_host_ip;
31276262Sgreen}
31362101Sgreen
31476262Sgreenconst char *
315124211Sdesget_remote_name_or_ip(u_int utmp_len, int use_dns)
31676262Sgreen{
31776262Sgreen	static const char *remote = "";
31876262Sgreen	if (utmp_len > 0)
319124211Sdes		remote = get_canonical_hostname(use_dns);
32076262Sgreen	if (utmp_len == 0 || strlen(remote) > utmp_len)
32176262Sgreen		remote = get_remote_ipaddr();
32276262Sgreen	return remote;
32362101Sgreen}
32462101Sgreen
32557429Smarkm/* Returns the local/remote port for the socket. */
32657429Smarkm
32792559Sdesstatic int
32857429Smarkmget_sock_port(int sock, int local)
32957429Smarkm{
33057429Smarkm	struct sockaddr_storage from;
33157429Smarkm	socklen_t fromlen;
33257429Smarkm	char strport[NI_MAXSERV];
33357429Smarkm
33457429Smarkm	/* Get IP address of client. */
33557429Smarkm	fromlen = sizeof(from);
33657429Smarkm	memset(&from, 0, sizeof(from));
33757429Smarkm	if (local) {
33857429Smarkm		if (getsockname(sock, (struct sockaddr *)&from, &fromlen) < 0) {
33957429Smarkm			error("getsockname failed: %.100s", strerror(errno));
34057429Smarkm			return 0;
34157429Smarkm		}
34257429Smarkm	} else {
343113911Sdes		if (getpeername(sock, (struct sockaddr *)&from, &fromlen) < 0) {
34457429Smarkm			debug("getpeername failed: %.100s", strerror(errno));
345126277Sdes			cleanup_exit(255);
34657429Smarkm		}
34757429Smarkm	}
348113911Sdes
349113911Sdes	/* Work around Linux IPv6 weirdness */
350113911Sdes	if (from.ss_family == AF_INET6)
351113911Sdes		fromlen = sizeof(struct sockaddr_in6);
352113911Sdes
35357429Smarkm	/* Return port number. */
35457429Smarkm	if (getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0,
35592559Sdes	    strport, sizeof(strport), NI_NUMERICSERV) != 0)
35657429Smarkm		fatal("get_sock_port: getnameinfo NI_NUMERICSERV failed");
35757429Smarkm	return atoi(strport);
35857429Smarkm}
35957429Smarkm
36057429Smarkm/* Returns remote/local port number for the current connection. */
36157429Smarkm
36292559Sdesstatic int
36357429Smarkmget_port(int local)
36457429Smarkm{
36557429Smarkm	/*
36657429Smarkm	 * If the connection is not a socket, return 65535.  This is
36757429Smarkm	 * intentionally chosen to be an unprivileged port number.
36857429Smarkm	 */
36957429Smarkm	if (!packet_connection_is_on_socket())
37057429Smarkm		return 65535;
37157429Smarkm
37257429Smarkm	/* Get socket and return the port number. */
37357429Smarkm	return get_sock_port(packet_get_connection_in(), local);
37457429Smarkm}
37557429Smarkm
37660573Skrisint
37757429Smarkmget_peer_port(int sock)
37857429Smarkm{
37957429Smarkm	return get_sock_port(sock, 0);
38057429Smarkm}
38157429Smarkm
38260573Skrisint
38392559Sdesget_remote_port(void)
38457429Smarkm{
385137019Sdes	static int port = -1;
386137019Sdes
387137019Sdes	/* Cache to avoid getpeername() on a dead connection */
388137019Sdes	if (port == -1)
389137019Sdes		port = get_port(0);
390137019Sdes
391137019Sdes	return port;
39257429Smarkm}
39357429Smarkm
39457429Smarkmint
39592559Sdesget_local_port(void)
39657429Smarkm{
39757429Smarkm	return get_port(1);
39857429Smarkm}
399