diff options
Diffstat (limited to 'canohost.c')
-rw-r--r-- | canohost.c | 262 |
1 files changed, 10 insertions, 252 deletions
diff --git a/canohost.c b/canohost.c index 223964ea3..f71a08568 100644 --- a/canohost.c +++ b/canohost.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: canohost.c,v 1.72 2015/03/01 15:44:40 millert Exp $ */ | 1 | /* $OpenBSD: canohost.c,v 1.73 2016/03/07 19:02:43 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -35,147 +35,6 @@ | |||
35 | #include "canohost.h" | 35 | #include "canohost.h" |
36 | #include "misc.h" | 36 | #include "misc.h" |
37 | 37 | ||
38 | static void check_ip_options(int, char *); | ||
39 | static char *canonical_host_ip = NULL; | ||
40 | static int cached_port = -1; | ||
41 | |||
42 | /* | ||
43 | * Return the canonical name of the host at the other end of the socket. The | ||
44 | * caller should free the returned string. | ||
45 | */ | ||
46 | |||
47 | static char * | ||
48 | get_remote_hostname(int sock, int use_dns) | ||
49 | { | ||
50 | struct sockaddr_storage from; | ||
51 | socklen_t fromlen; | ||
52 | struct addrinfo hints, *ai, *aitop; | ||
53 | char name[NI_MAXHOST], ntop[NI_MAXHOST], ntop2[NI_MAXHOST]; | ||
54 | |||
55 | /* Get IP address of client. */ | ||
56 | fromlen = sizeof(from); | ||
57 | memset(&from, 0, sizeof(from)); | ||
58 | if (getpeername(sock, (struct sockaddr *)&from, &fromlen) < 0) { | ||
59 | debug("getpeername failed: %.100s", strerror(errno)); | ||
60 | cleanup_exit(255); | ||
61 | } | ||
62 | |||
63 | if (from.ss_family == AF_INET) | ||
64 | check_ip_options(sock, ntop); | ||
65 | |||
66 | ipv64_normalise_mapped(&from, &fromlen); | ||
67 | |||
68 | if (from.ss_family == AF_INET6) | ||
69 | fromlen = sizeof(struct sockaddr_in6); | ||
70 | |||
71 | if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop), | ||
72 | NULL, 0, NI_NUMERICHOST) != 0) | ||
73 | fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed"); | ||
74 | |||
75 | if (!use_dns) | ||
76 | return xstrdup(ntop); | ||
77 | |||
78 | debug3("Trying to reverse map address %.100s.", ntop); | ||
79 | /* Map the IP address to a host name. */ | ||
80 | if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), | ||
81 | NULL, 0, NI_NAMEREQD) != 0) { | ||
82 | /* Host name not found. Use ip address. */ | ||
83 | return xstrdup(ntop); | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * if reverse lookup result looks like a numeric hostname, | ||
88 | * someone is trying to trick us by PTR record like following: | ||
89 | * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 | ||
90 | */ | ||
91 | memset(&hints, 0, sizeof(hints)); | ||
92 | hints.ai_socktype = SOCK_DGRAM; /*dummy*/ | ||
93 | hints.ai_flags = AI_NUMERICHOST; | ||
94 | if (getaddrinfo(name, NULL, &hints, &ai) == 0) { | ||
95 | logit("Nasty PTR record \"%s\" is set up for %s, ignoring", | ||
96 | name, ntop); | ||
97 | freeaddrinfo(ai); | ||
98 | return xstrdup(ntop); | ||
99 | } | ||
100 | |||
101 | /* Names are stores in lowercase. */ | ||
102 | lowercase(name); | ||
103 | |||
104 | /* | ||
105 | * Map it back to an IP address and check that the given | ||
106 | * address actually is an address of this host. This is | ||
107 | * necessary because anyone with access to a name server can | ||
108 | * define arbitrary names for an IP address. Mapping from | ||
109 | * name to IP address can be trusted better (but can still be | ||
110 | * fooled if the intruder has access to the name server of | ||
111 | * the domain). | ||
112 | */ | ||
113 | memset(&hints, 0, sizeof(hints)); | ||
114 | hints.ai_family = from.ss_family; | ||
115 | hints.ai_socktype = SOCK_STREAM; | ||
116 | if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { | ||
117 | logit("reverse mapping checking getaddrinfo for %.700s " | ||
118 | "[%s] failed - POSSIBLE BREAK-IN ATTEMPT!", name, ntop); | ||
119 | return xstrdup(ntop); | ||
120 | } | ||
121 | /* Look for the address from the list of addresses. */ | ||
122 | for (ai = aitop; ai; ai = ai->ai_next) { | ||
123 | if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, | ||
124 | sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && | ||
125 | (strcmp(ntop, ntop2) == 0)) | ||
126 | break; | ||
127 | } | ||
128 | freeaddrinfo(aitop); | ||
129 | /* If we reached the end of the list, the address was not there. */ | ||
130 | if (!ai) { | ||
131 | /* Address not found for the host name. */ | ||
132 | logit("Address %.100s maps to %.600s, but this does not " | ||
133 | "map back to the address - POSSIBLE BREAK-IN ATTEMPT!", | ||
134 | ntop, name); | ||
135 | return xstrdup(ntop); | ||
136 | } | ||
137 | return xstrdup(name); | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * If IP options are supported, make sure there are none (log and | ||
142 | * disconnect them if any are found). Basically we are worried about | ||
143 | * source routing; it can be used to pretend you are somebody | ||
144 | * (ip-address) you are not. That itself may be "almost acceptable" | ||
145 | * under certain circumstances, but rhosts autentication is useless | ||
146 | * if source routing is accepted. Notice also that if we just dropped | ||
147 | * source routing here, the other side could use IP spoofing to do | ||
148 | * rest of the interaction and could still bypass security. So we | ||
149 | * exit here if we detect any IP options. | ||
150 | */ | ||
151 | /* IPv4 only */ | ||
152 | static void | ||
153 | check_ip_options(int sock, char *ipaddr) | ||
154 | { | ||
155 | #ifdef IP_OPTIONS | ||
156 | u_char options[200]; | ||
157 | char text[sizeof(options) * 3 + 1]; | ||
158 | socklen_t option_size, i; | ||
159 | int ipproto; | ||
160 | struct protoent *ip; | ||
161 | |||
162 | if ((ip = getprotobyname("ip")) != NULL) | ||
163 | ipproto = ip->p_proto; | ||
164 | else | ||
165 | ipproto = IPPROTO_IP; | ||
166 | option_size = sizeof(options); | ||
167 | if (getsockopt(sock, ipproto, IP_OPTIONS, options, | ||
168 | &option_size) >= 0 && option_size != 0) { | ||
169 | text[0] = '\0'; | ||
170 | for (i = 0; i < option_size; i++) | ||
171 | snprintf(text + i*3, sizeof(text) - i*3, | ||
172 | " %2.2x", options[i]); | ||
173 | fatal("Connection from %.100s with IP options:%.800s", | ||
174 | ipaddr, text); | ||
175 | } | ||
176 | #endif /* IP_OPTIONS */ | ||
177 | } | ||
178 | |||
179 | void | 38 | void |
180 | ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len) | 39 | ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len) |
181 | { | 40 | { |
@@ -202,38 +61,6 @@ ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len) | |||
202 | } | 61 | } |
203 | 62 | ||
204 | /* | 63 | /* |
205 | * Return the canonical name of the host in the other side of the current | ||
206 | * connection. The host name is cached, so it is efficient to call this | ||
207 | * several times. | ||
208 | */ | ||
209 | |||
210 | const char * | ||
211 | get_canonical_hostname(int use_dns) | ||
212 | { | ||
213 | char *host; | ||
214 | static char *canonical_host_name = NULL; | ||
215 | static char *remote_ip = NULL; | ||
216 | |||
217 | /* Check if we have previously retrieved name with same option. */ | ||
218 | if (use_dns && canonical_host_name != NULL) | ||
219 | return canonical_host_name; | ||
220 | if (!use_dns && remote_ip != NULL) | ||
221 | return remote_ip; | ||
222 | |||
223 | /* Get the real hostname if socket; otherwise return UNKNOWN. */ | ||
224 | if (packet_connection_is_on_socket()) | ||
225 | host = get_remote_hostname(packet_get_connection_in(), use_dns); | ||
226 | else | ||
227 | host = "UNKNOWN"; | ||
228 | |||
229 | if (use_dns) | ||
230 | canonical_host_name = host; | ||
231 | else | ||
232 | remote_ip = host; | ||
233 | return host; | ||
234 | } | ||
235 | |||
236 | /* | ||
237 | * Returns the local/remote IP-address/hostname of socket as a string. | 64 | * Returns the local/remote IP-address/hostname of socket as a string. |
238 | * The returned string must be freed. | 65 | * The returned string must be freed. |
239 | */ | 66 | */ |
@@ -250,12 +77,10 @@ get_socket_address(int sock, int remote, int flags) | |||
250 | memset(&addr, 0, sizeof(addr)); | 77 | memset(&addr, 0, sizeof(addr)); |
251 | 78 | ||
252 | if (remote) { | 79 | if (remote) { |
253 | if (getpeername(sock, (struct sockaddr *)&addr, &addrlen) | 80 | if (getpeername(sock, (struct sockaddr *)&addr, &addrlen) != 0) |
254 | < 0) | ||
255 | return NULL; | 81 | return NULL; |
256 | } else { | 82 | } else { |
257 | if (getsockname(sock, (struct sockaddr *)&addr, &addrlen) | 83 | if (getsockname(sock, (struct sockaddr *)&addr, &addrlen) != 0) |
258 | < 0) | ||
259 | return NULL; | 84 | return NULL; |
260 | } | 85 | } |
261 | 86 | ||
@@ -271,7 +96,7 @@ get_socket_address(int sock, int remote, int flags) | |||
271 | /* Get the address in ascii. */ | 96 | /* Get the address in ascii. */ |
272 | if ((r = getnameinfo((struct sockaddr *)&addr, addrlen, ntop, | 97 | if ((r = getnameinfo((struct sockaddr *)&addr, addrlen, ntop, |
273 | sizeof(ntop), NULL, 0, flags)) != 0) { | 98 | sizeof(ntop), NULL, 0, flags)) != 0) { |
274 | error("get_socket_address: getnameinfo %d failed: %s", | 99 | error("%s: getnameinfo %d failed: %s", __func__, |
275 | flags, ssh_gai_strerror(r)); | 100 | flags, ssh_gai_strerror(r)); |
276 | return NULL; | 101 | return NULL; |
277 | } | 102 | } |
@@ -316,7 +141,8 @@ get_local_name(int fd) | |||
316 | 141 | ||
317 | /* Handle the case where we were passed a pipe */ | 142 | /* Handle the case where we were passed a pipe */ |
318 | if (gethostname(myname, sizeof(myname)) == -1) { | 143 | if (gethostname(myname, sizeof(myname)) == -1) { |
319 | verbose("get_local_name: gethostname: %s", strerror(errno)); | 144 | verbose("%s: gethostname: %s", __func__, strerror(errno)); |
145 | host = xstrdup("UNKNOWN"); | ||
320 | } else { | 146 | } else { |
321 | host = xstrdup(myname); | 147 | host = xstrdup(myname); |
322 | } | 148 | } |
@@ -324,51 +150,9 @@ get_local_name(int fd) | |||
324 | return host; | 150 | return host; |
325 | } | 151 | } |
326 | 152 | ||
327 | void | ||
328 | clear_cached_addr(void) | ||
329 | { | ||
330 | free(canonical_host_ip); | ||
331 | canonical_host_ip = NULL; | ||
332 | cached_port = -1; | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * Returns the IP-address of the remote host as a string. The returned | ||
337 | * string must not be freed. | ||
338 | */ | ||
339 | |||
340 | const char * | ||
341 | get_remote_ipaddr(void) | ||
342 | { | ||
343 | /* Check whether we have cached the ipaddr. */ | ||
344 | if (canonical_host_ip == NULL) { | ||
345 | if (packet_connection_is_on_socket()) { | ||
346 | canonical_host_ip = | ||
347 | get_peer_ipaddr(packet_get_connection_in()); | ||
348 | if (canonical_host_ip == NULL) | ||
349 | cleanup_exit(255); | ||
350 | } else { | ||
351 | /* If not on socket, return UNKNOWN. */ | ||
352 | canonical_host_ip = xstrdup("UNKNOWN"); | ||
353 | } | ||
354 | } | ||
355 | return canonical_host_ip; | ||
356 | } | ||
357 | |||
358 | const char * | ||
359 | get_remote_name_or_ip(u_int utmp_len, int use_dns) | ||
360 | { | ||
361 | static const char *remote = ""; | ||
362 | if (utmp_len > 0) | ||
363 | remote = get_canonical_hostname(use_dns); | ||
364 | if (utmp_len == 0 || strlen(remote) > utmp_len) | ||
365 | remote = get_remote_ipaddr(); | ||
366 | return remote; | ||
367 | } | ||
368 | |||
369 | /* Returns the local/remote port for the socket. */ | 153 | /* Returns the local/remote port for the socket. */ |
370 | 154 | ||
371 | int | 155 | static int |
372 | get_sock_port(int sock, int local) | 156 | get_sock_port(int sock, int local) |
373 | { | 157 | { |
374 | struct sockaddr_storage from; | 158 | struct sockaddr_storage from; |
@@ -402,27 +186,11 @@ get_sock_port(int sock, int local) | |||
402 | /* Return port number. */ | 186 | /* Return port number. */ |
403 | if ((r = getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0, | 187 | if ((r = getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0, |
404 | strport, sizeof(strport), NI_NUMERICSERV)) != 0) | 188 | strport, sizeof(strport), NI_NUMERICSERV)) != 0) |
405 | fatal("get_sock_port: getnameinfo NI_NUMERICSERV failed: %s", | 189 | fatal("%s: getnameinfo NI_NUMERICSERV failed: %s", __func__, |
406 | ssh_gai_strerror(r)); | 190 | ssh_gai_strerror(r)); |
407 | return atoi(strport); | 191 | return atoi(strport); |
408 | } | 192 | } |
409 | 193 | ||
410 | /* Returns remote/local port number for the current connection. */ | ||
411 | |||
412 | static int | ||
413 | get_port(int local) | ||
414 | { | ||
415 | /* | ||
416 | * If the connection is not a socket, return 65535. This is | ||
417 | * intentionally chosen to be an unprivileged port number. | ||
418 | */ | ||
419 | if (!packet_connection_is_on_socket()) | ||
420 | return 65535; | ||
421 | |||
422 | /* Get socket and return the port number. */ | ||
423 | return get_sock_port(packet_get_connection_in(), local); | ||
424 | } | ||
425 | |||
426 | int | 194 | int |
427 | get_peer_port(int sock) | 195 | get_peer_port(int sock) |
428 | { | 196 | { |
@@ -430,17 +198,7 @@ get_peer_port(int sock) | |||
430 | } | 198 | } |
431 | 199 | ||
432 | int | 200 | int |
433 | get_remote_port(void) | 201 | get_local_port(int sock) |
434 | { | ||
435 | /* Cache to avoid getpeername() on a dead connection */ | ||
436 | if (cached_port == -1) | ||
437 | cached_port = get_port(0); | ||
438 | |||
439 | return cached_port; | ||
440 | } | ||
441 | |||
442 | int | ||
443 | get_local_port(void) | ||
444 | { | 202 | { |
445 | return get_port(1); | 203 | return get_sock_port(sock, 1); |
446 | } | 204 | } |