xref: /freebsd/crypto/openssh/canohost.c (revision af12a3e74a283727a2aa141f2b4c68ff10fe8dc6)
1511b41d2SMark Murray /*
2511b41d2SMark Murray  * Author: Tatu Ylonen <ylo@cs.hut.fi>
3511b41d2SMark Murray  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4511b41d2SMark Murray  *                    All rights reserved
5511b41d2SMark Murray  * Functions for returning the canonical host name of the remote site.
6511b41d2SMark Murray  *
7c2d3a559SKris Kennaway  * As far as I am concerned, the code I have written for this software
8c2d3a559SKris Kennaway  * can be used freely for any purpose.  Any derived versions of this
9c2d3a559SKris Kennaway  * software must be clearly marked as such, and if the derived work is
10c2d3a559SKris Kennaway  * incompatible with the protocol description in the RFC file, it must be
11c2d3a559SKris Kennaway  * called by a name other than "ssh" or "Secure Shell".
12511b41d2SMark Murray  */
13511b41d2SMark Murray 
14511b41d2SMark Murray #include "includes.h"
15af12a3e7SDag-Erling Smørgrav RCSID("$OpenBSD: canohost.c,v 1.31 2002/02/27 21:23:13 stevesk Exp $");
16c2d3a559SKris Kennaway RCSID("$FreeBSD$");
17511b41d2SMark Murray 
18511b41d2SMark Murray #include "packet.h"
19511b41d2SMark Murray #include "xmalloc.h"
20ca3176e7SBrian Feldman #include "log.h"
21ca3176e7SBrian Feldman #include "canohost.h"
22ca3176e7SBrian Feldman 
23af12a3e7SDag-Erling Smørgrav static void check_ip_options(int, char *);
24511b41d2SMark Murray 
25511b41d2SMark Murray /*
26511b41d2SMark Murray  * Return the canonical name of the host at the other end of the socket. The
27511b41d2SMark Murray  * caller should free the returned string with xfree.
28511b41d2SMark Murray  */
29511b41d2SMark Murray 
30af12a3e7SDag-Erling Smørgrav static char *
31af12a3e7SDag-Erling Smørgrav get_remote_hostname(int socket, int verify_reverse_mapping)
32511b41d2SMark Murray {
33511b41d2SMark Murray 	struct sockaddr_storage from;
34511b41d2SMark Murray 	int i;
35511b41d2SMark Murray 	socklen_t fromlen;
36511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
37ca3176e7SBrian Feldman 	char name[NI_MAXHOST], ntop[NI_MAXHOST], ntop2[NI_MAXHOST];
38511b41d2SMark Murray 
39511b41d2SMark Murray 	/* Get IP address of client. */
40511b41d2SMark Murray 	fromlen = sizeof(from);
41511b41d2SMark Murray 	memset(&from, 0, sizeof(from));
42511b41d2SMark Murray 	if (getpeername(socket, (struct sockaddr *) &from, &fromlen) < 0) {
43511b41d2SMark Murray 		debug("getpeername failed: %.100s", strerror(errno));
44511b41d2SMark Murray 		fatal_cleanup();
45511b41d2SMark Murray 	}
46ca3176e7SBrian Feldman 	if (from.ss_family == AF_INET)
47ca3176e7SBrian Feldman 		check_ip_options(socket, ntop);
48ca3176e7SBrian Feldman 
49511b41d2SMark Murray 	if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop),
50511b41d2SMark Murray 	    NULL, 0, NI_NUMERICHOST) != 0)
51511b41d2SMark Murray 		fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed");
52511b41d2SMark Murray 
53ca3176e7SBrian Feldman 	debug3("Trying to reverse map address %.100s.", ntop);
54511b41d2SMark Murray 	/* Map the IP address to a host name. */
55511b41d2SMark Murray 	if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
56ca3176e7SBrian Feldman 	    NULL, 0, NI_NAMEREQD) != 0) {
57ca3176e7SBrian Feldman 		/* Host name not found.  Use ip address. */
58ca3176e7SBrian Feldman 		log("Could not reverse map address %.100s.", ntop);
59ca3176e7SBrian Feldman 		return xstrdup(ntop);
60ca3176e7SBrian Feldman 	}
61ca3176e7SBrian Feldman 
62511b41d2SMark Murray 	/* Got host name. */
63511b41d2SMark Murray 	name[sizeof(name) - 1] = '\0';
64511b41d2SMark Murray 	/*
65511b41d2SMark Murray 	 * Convert it to all lowercase (which is expected by the rest
66511b41d2SMark Murray 	 * of this software).
67511b41d2SMark Murray 	 */
68511b41d2SMark Murray 	for (i = 0; name[i]; i++)
69511b41d2SMark Murray 		if (isupper(name[i]))
70511b41d2SMark Murray 			name[i] = tolower(name[i]);
71511b41d2SMark Murray 
72af12a3e7SDag-Erling Smørgrav 	if (!verify_reverse_mapping)
73ca3176e7SBrian Feldman 		return xstrdup(name);
74511b41d2SMark Murray 	/*
75511b41d2SMark Murray 	 * Map it back to an IP address and check that the given
76511b41d2SMark Murray 	 * address actually is an address of this host.  This is
77511b41d2SMark Murray 	 * necessary because anyone with access to a name server can
78511b41d2SMark Murray 	 * define arbitrary names for an IP address. Mapping from
79511b41d2SMark Murray 	 * name to IP address can be trusted better (but can still be
80511b41d2SMark Murray 	 * fooled if the intruder has access to the name server of
81511b41d2SMark Murray 	 * the domain).
82511b41d2SMark Murray 	 */
83511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
84511b41d2SMark Murray 	hints.ai_family = from.ss_family;
85511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
86511b41d2SMark Murray 	if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
87ca3176e7SBrian Feldman 		log("reverse mapping checking getaddrinfo for %.700s "
88ca3176e7SBrian Feldman 		    "failed - POSSIBLE BREAKIN ATTEMPT!", name);
89ca3176e7SBrian Feldman 		return xstrdup(ntop);
90511b41d2SMark Murray 	}
91511b41d2SMark Murray 	/* Look for the address from the list of addresses. */
92511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
93511b41d2SMark Murray 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
94511b41d2SMark Murray 		    sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
95511b41d2SMark Murray 		    (strcmp(ntop, ntop2) == 0))
96511b41d2SMark Murray 				break;
97511b41d2SMark Murray 	}
98511b41d2SMark Murray 	freeaddrinfo(aitop);
99511b41d2SMark Murray 	/* If we reached the end of the list, the address was not there. */
100511b41d2SMark Murray 	if (!ai) {
101511b41d2SMark Murray 		/* Address not found for the host name. */
102ca3176e7SBrian Feldman 		log("Address %.100s maps to %.600s, but this does not "
103ca3176e7SBrian Feldman 		    "map back to the address - POSSIBLE BREAKIN ATTEMPT!",
104511b41d2SMark Murray 		    ntop, name);
105ca3176e7SBrian Feldman 		return xstrdup(ntop);
106511b41d2SMark Murray 	}
107ca3176e7SBrian Feldman 	return xstrdup(name);
108511b41d2SMark Murray }
109511b41d2SMark Murray 
110511b41d2SMark Murray /*
111511b41d2SMark Murray  * If IP options are supported, make sure there are none (log and
112511b41d2SMark Murray  * disconnect them if any are found).  Basically we are worried about
113511b41d2SMark Murray  * source routing; it can be used to pretend you are somebody
114511b41d2SMark Murray  * (ip-address) you are not. That itself may be "almost acceptable"
115511b41d2SMark Murray  * under certain circumstances, but rhosts autentication is useless
116511b41d2SMark Murray  * if source routing is accepted. Notice also that if we just dropped
117511b41d2SMark Murray  * source routing here, the other side could use IP spoofing to do
118511b41d2SMark Murray  * rest of the interaction and could still bypass security.  So we
119511b41d2SMark Murray  * exit here if we detect any IP options.
120511b41d2SMark Murray  */
121ca3176e7SBrian Feldman /* IPv4 only */
122af12a3e7SDag-Erling Smørgrav static void
123ca3176e7SBrian Feldman check_ip_options(int socket, char *ipaddr)
124ca3176e7SBrian Feldman {
125ca3176e7SBrian Feldman 	u_char options[200];
126ca3176e7SBrian Feldman 	char text[sizeof(options) * 3 + 1];
127511b41d2SMark Murray 	socklen_t option_size;
128ca3176e7SBrian Feldman 	int i, ipproto;
129511b41d2SMark Murray 	struct protoent *ip;
130511b41d2SMark Murray 
131511b41d2SMark Murray 	if ((ip = getprotobyname("ip")) != NULL)
132511b41d2SMark Murray 		ipproto = ip->p_proto;
133511b41d2SMark Murray 	else
134511b41d2SMark Murray 		ipproto = IPPROTO_IP;
135511b41d2SMark Murray 	option_size = sizeof(options);
136af12a3e7SDag-Erling Smørgrav 	if (getsockopt(socket, ipproto, IP_OPTIONS, options,
137511b41d2SMark Murray 	    &option_size) >= 0 && option_size != 0) {
138ca3176e7SBrian Feldman 		text[0] = '\0';
139ca3176e7SBrian Feldman 		for (i = 0; i < option_size; i++)
140ca3176e7SBrian Feldman 			snprintf(text + i*3, sizeof(text) - i*3,
141ca3176e7SBrian Feldman 			    " %2.2x", options[i]);
142511b41d2SMark Murray 		log("Connection from %.100s with IP options:%.800s",
143ca3176e7SBrian Feldman 		    ipaddr, text);
144511b41d2SMark Murray 		packet_disconnect("Connection from %.100s with IP options:%.800s",
145ca3176e7SBrian Feldman 		    ipaddr, text);
146511b41d2SMark Murray 	}
147511b41d2SMark Murray }
148511b41d2SMark Murray 
149511b41d2SMark Murray /*
150511b41d2SMark Murray  * Return the canonical name of the host in the other side of the current
151511b41d2SMark Murray  * connection.  The host name is cached, so it is efficient to call this
152511b41d2SMark Murray  * several times.
153511b41d2SMark Murray  */
154511b41d2SMark Murray 
155511b41d2SMark Murray const char *
156af12a3e7SDag-Erling Smørgrav get_canonical_hostname(int verify_reverse_mapping)
157511b41d2SMark Murray {
158511b41d2SMark Murray 	static char *canonical_host_name = NULL;
159af12a3e7SDag-Erling Smørgrav 	static int verify_reverse_mapping_done = 0;
160511b41d2SMark Murray 
161ca3176e7SBrian Feldman 	/* Check if we have previously retrieved name with same option. */
162ca3176e7SBrian Feldman 	if (canonical_host_name != NULL) {
163af12a3e7SDag-Erling Smørgrav 		if (verify_reverse_mapping_done != verify_reverse_mapping)
164ca3176e7SBrian Feldman 			xfree(canonical_host_name);
165ca3176e7SBrian Feldman 		else
166511b41d2SMark Murray 			return canonical_host_name;
167ca3176e7SBrian Feldman 	}
168511b41d2SMark Murray 
169511b41d2SMark Murray 	/* Get the real hostname if socket; otherwise return UNKNOWN. */
170511b41d2SMark Murray 	if (packet_connection_is_on_socket())
171ca3176e7SBrian Feldman 		canonical_host_name = get_remote_hostname(
172af12a3e7SDag-Erling Smørgrav 		    packet_get_connection_in(), verify_reverse_mapping);
173511b41d2SMark Murray 	else
174511b41d2SMark Murray 		canonical_host_name = xstrdup("UNKNOWN");
175511b41d2SMark Murray 
176af12a3e7SDag-Erling Smørgrav 	verify_reverse_mapping_done = verify_reverse_mapping;
177511b41d2SMark Murray 	return canonical_host_name;
178511b41d2SMark Murray }
179511b41d2SMark Murray 
180511b41d2SMark Murray /*
181ca3176e7SBrian Feldman  * Returns the remote IP-address of socket as a string.  The returned
182ca3176e7SBrian Feldman  * string must be freed.
183ca3176e7SBrian Feldman  */
184af12a3e7SDag-Erling Smørgrav static char *
185ca3176e7SBrian Feldman get_socket_address(int socket, int remote, int flags)
186ca3176e7SBrian Feldman {
187ca3176e7SBrian Feldman 	struct sockaddr_storage addr;
188ca3176e7SBrian Feldman 	socklen_t addrlen;
189ca3176e7SBrian Feldman 	char ntop[NI_MAXHOST];
190ca3176e7SBrian Feldman 
191ca3176e7SBrian Feldman 	/* Get IP address of client. */
192ca3176e7SBrian Feldman 	addrlen = sizeof(addr);
193ca3176e7SBrian Feldman 	memset(&addr, 0, sizeof(addr));
194ca3176e7SBrian Feldman 
195ca3176e7SBrian Feldman 	if (remote) {
196ca3176e7SBrian Feldman 		if (getpeername(socket, (struct sockaddr *)&addr, &addrlen)
197ca3176e7SBrian Feldman 		    < 0) {
198ca3176e7SBrian Feldman 			debug("get_socket_ipaddr: getpeername failed: %.100s",
199ca3176e7SBrian Feldman 			    strerror(errno));
200ca3176e7SBrian Feldman 			return NULL;
201ca3176e7SBrian Feldman 		}
202ca3176e7SBrian Feldman 	} else {
203ca3176e7SBrian Feldman 		if (getsockname(socket, (struct sockaddr *)&addr, &addrlen)
204ca3176e7SBrian Feldman 		    < 0) {
205ca3176e7SBrian Feldman 			debug("get_socket_ipaddr: getsockname failed: %.100s",
206ca3176e7SBrian Feldman 			    strerror(errno));
207ca3176e7SBrian Feldman 			return NULL;
208ca3176e7SBrian Feldman 		}
209ca3176e7SBrian Feldman 	}
210ca3176e7SBrian Feldman 	/* Get the address in ascii. */
211ca3176e7SBrian Feldman 	if (getnameinfo((struct sockaddr *)&addr, addrlen, ntop, sizeof(ntop),
212ca3176e7SBrian Feldman 	    NULL, 0, flags) != 0) {
213ca3176e7SBrian Feldman 		error("get_socket_ipaddr: getnameinfo %d failed", flags);
214ca3176e7SBrian Feldman 		return NULL;
215ca3176e7SBrian Feldman 	}
216ca3176e7SBrian Feldman 	return xstrdup(ntop);
217ca3176e7SBrian Feldman }
218ca3176e7SBrian Feldman 
219ca3176e7SBrian Feldman char *
220ca3176e7SBrian Feldman get_peer_ipaddr(int socket)
221ca3176e7SBrian Feldman {
222ca3176e7SBrian Feldman 	return get_socket_address(socket, 1, NI_NUMERICHOST);
223ca3176e7SBrian Feldman }
224ca3176e7SBrian Feldman 
225ca3176e7SBrian Feldman char *
226ca3176e7SBrian Feldman get_local_ipaddr(int socket)
227ca3176e7SBrian Feldman {
228ca3176e7SBrian Feldman 	return get_socket_address(socket, 0, NI_NUMERICHOST);
229ca3176e7SBrian Feldman }
230ca3176e7SBrian Feldman 
231ca3176e7SBrian Feldman char *
232ca3176e7SBrian Feldman get_local_name(int socket)
233ca3176e7SBrian Feldman {
234ca3176e7SBrian Feldman 	return get_socket_address(socket, 0, NI_NAMEREQD);
235ca3176e7SBrian Feldman }
236ca3176e7SBrian Feldman 
237ca3176e7SBrian Feldman /*
238511b41d2SMark Murray  * Returns the IP-address of the remote host as a string.  The returned
239511b41d2SMark Murray  * string must not be freed.
240511b41d2SMark Murray  */
241511b41d2SMark Murray 
242511b41d2SMark Murray const char *
243af12a3e7SDag-Erling Smørgrav get_remote_ipaddr(void)
244511b41d2SMark Murray {
245511b41d2SMark Murray 	static char *canonical_host_ip = NULL;
246511b41d2SMark Murray 
247ca3176e7SBrian Feldman 	/* Check whether we have cached the ipaddr. */
248ca3176e7SBrian Feldman 	if (canonical_host_ip == NULL) {
249ca3176e7SBrian Feldman 		if (packet_connection_is_on_socket()) {
250ca3176e7SBrian Feldman 			canonical_host_ip =
251ca3176e7SBrian Feldman 			    get_peer_ipaddr(packet_get_connection_in());
252ca3176e7SBrian Feldman 			if (canonical_host_ip == NULL)
253511b41d2SMark Murray 				fatal_cleanup();
254ca3176e7SBrian Feldman 		} else {
255ca3176e7SBrian Feldman 			/* If not on socket, return UNKNOWN. */
256ca3176e7SBrian Feldman 			canonical_host_ip = xstrdup("UNKNOWN");
257511b41d2SMark Murray 		}
258ca3176e7SBrian Feldman 	}
259511b41d2SMark Murray 	return canonical_host_ip;
260511b41d2SMark Murray }
261511b41d2SMark Murray 
2627e03cf33SBrian Feldman const char *
263af12a3e7SDag-Erling Smørgrav get_remote_name_or_ip(u_int utmp_len, int verify_reverse_mapping)
2647e03cf33SBrian Feldman {
265ca3176e7SBrian Feldman 	static const char *remote = "";
266ca3176e7SBrian Feldman 	if (utmp_len > 0)
267af12a3e7SDag-Erling Smørgrav 		remote = get_canonical_hostname(verify_reverse_mapping);
268ca3176e7SBrian Feldman 	if (utmp_len == 0 || strlen(remote) > utmp_len)
269ca3176e7SBrian Feldman 		remote = get_remote_ipaddr();
270ca3176e7SBrian Feldman 	return remote;
2717e03cf33SBrian Feldman }
2727e03cf33SBrian Feldman 
273511b41d2SMark Murray /* Returns the local/remote port for the socket. */
274511b41d2SMark Murray 
275af12a3e7SDag-Erling Smørgrav static int
276511b41d2SMark Murray get_sock_port(int sock, int local)
277511b41d2SMark Murray {
278511b41d2SMark Murray 	struct sockaddr_storage from;
279511b41d2SMark Murray 	socklen_t fromlen;
280511b41d2SMark Murray 	char strport[NI_MAXSERV];
281511b41d2SMark Murray 
282511b41d2SMark Murray 	/* Get IP address of client. */
283511b41d2SMark Murray 	fromlen = sizeof(from);
284511b41d2SMark Murray 	memset(&from, 0, sizeof(from));
285511b41d2SMark Murray 	if (local) {
286511b41d2SMark Murray 		if (getsockname(sock, (struct sockaddr *)&from, &fromlen) < 0) {
287511b41d2SMark Murray 			error("getsockname failed: %.100s", strerror(errno));
288511b41d2SMark Murray 			return 0;
289511b41d2SMark Murray 		}
290511b41d2SMark Murray 	} else {
291511b41d2SMark Murray 		if (getpeername(sock, (struct sockaddr *) & from, &fromlen) < 0) {
292511b41d2SMark Murray 			debug("getpeername failed: %.100s", strerror(errno));
293511b41d2SMark Murray 			fatal_cleanup();
294511b41d2SMark Murray 		}
295511b41d2SMark Murray 	}
296511b41d2SMark Murray 	/* Return port number. */
297511b41d2SMark Murray 	if (getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0,
298511b41d2SMark Murray 	    strport, sizeof(strport), NI_NUMERICSERV) != 0)
299511b41d2SMark Murray 		fatal("get_sock_port: getnameinfo NI_NUMERICSERV failed");
300511b41d2SMark Murray 	return atoi(strport);
301511b41d2SMark Murray }
302511b41d2SMark Murray 
303511b41d2SMark Murray /* Returns remote/local port number for the current connection. */
304511b41d2SMark Murray 
305af12a3e7SDag-Erling Smørgrav static int
306511b41d2SMark Murray get_port(int local)
307511b41d2SMark Murray {
308511b41d2SMark Murray 	/*
309511b41d2SMark Murray 	 * If the connection is not a socket, return 65535.  This is
310511b41d2SMark Murray 	 * intentionally chosen to be an unprivileged port number.
311511b41d2SMark Murray 	 */
312511b41d2SMark Murray 	if (!packet_connection_is_on_socket())
313511b41d2SMark Murray 		return 65535;
314511b41d2SMark Murray 
315511b41d2SMark Murray 	/* Get socket and return the port number. */
316511b41d2SMark Murray 	return get_sock_port(packet_get_connection_in(), local);
317511b41d2SMark Murray }
318511b41d2SMark Murray 
319511b41d2SMark Murray int
320511b41d2SMark Murray get_peer_port(int sock)
321511b41d2SMark Murray {
322511b41d2SMark Murray 	return get_sock_port(sock, 0);
323511b41d2SMark Murray }
324511b41d2SMark Murray 
325511b41d2SMark Murray int
326af12a3e7SDag-Erling Smørgrav get_remote_port(void)
327511b41d2SMark Murray {
328511b41d2SMark Murray 	return get_port(0);
329511b41d2SMark Murray }
330511b41d2SMark Murray 
331511b41d2SMark Murray int
332af12a3e7SDag-Erling Smørgrav get_local_port(void)
333511b41d2SMark Murray {
334511b41d2SMark Murray 	return get_port(1);
335511b41d2SMark Murray }
336