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