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