canohost.c revision 124211
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" 15124211SdesRCSID("$OpenBSD: canohost.c,v 1.37 2003/06/02 09:17:34 markus 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 *); 2376262Sgreen 2457429Smarkm/* 2557429Smarkm * Return the canonical name of the host at the other end of the socket. The 2657429Smarkm * caller should free the returned string with xfree. 2757429Smarkm */ 2857429Smarkm 2992559Sdesstatic char * 30124211Sdesget_remote_hostname(int socket, int use_dns) 3157429Smarkm{ 3257429Smarkm struct sockaddr_storage from; 3357429Smarkm int i; 3457429Smarkm socklen_t fromlen; 3557429Smarkm struct addrinfo hints, *ai, *aitop; 3676262Sgreen char name[NI_MAXHOST], ntop[NI_MAXHOST], ntop2[NI_MAXHOST]; 3757429Smarkm 3857429Smarkm /* Get IP address of client. */ 3957429Smarkm fromlen = sizeof(from); 4057429Smarkm memset(&from, 0, sizeof(from)); 41113911Sdes if (getpeername(socket, (struct sockaddr *)&from, &fromlen) < 0) { 4257429Smarkm debug("getpeername failed: %.100s", strerror(errno)); 4357429Smarkm fatal_cleanup(); 4457429Smarkm } 4598941Sdes#ifdef IPV4_IN_IPV6 4698941Sdes if (from.ss_family == AF_INET6) { 4798941Sdes struct sockaddr_in6 *from6 = (struct sockaddr_in6 *)&from; 4876262Sgreen 4998941Sdes /* Detect IPv4 in IPv6 mapped address and convert it to */ 5098941Sdes /* plain (AF_INET) IPv4 address */ 5198941Sdes if (IN6_IS_ADDR_V4MAPPED(&from6->sin6_addr)) { 5298941Sdes struct sockaddr_in *from4 = (struct sockaddr_in *)&from; 5398941Sdes struct in_addr addr; 5498941Sdes u_int16_t port; 5598941Sdes 5698941Sdes memcpy(&addr, ((char *)&from6->sin6_addr) + 12, sizeof(addr)); 5798941Sdes port = from6->sin6_port; 5898941Sdes 5998941Sdes memset(&from, 0, sizeof(from)); 6098941Sdes 6198941Sdes from4->sin_family = AF_INET; 62113911Sdes fromlen = sizeof(*from4); 6398941Sdes memcpy(&from4->sin_addr, &addr, sizeof(addr)); 6498941Sdes from4->sin_port = port; 6598941Sdes } 6698941Sdes } 6798941Sdes#endif 68113911Sdes if (from.ss_family == AF_INET6) 69113911Sdes fromlen = sizeof(struct sockaddr_in6); 7098941Sdes 7157429Smarkm if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop), 7292559Sdes NULL, 0, NI_NUMERICHOST) != 0) 7357429Smarkm fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed"); 7457429Smarkm 75124211Sdes if (!use_dns) 76124211Sdes return xstrdup(ntop); 77124211Sdes 7898684Sdes if (from.ss_family == AF_INET) 7998684Sdes check_ip_options(socket, ntop); 8098684Sdes 8176262Sgreen debug3("Trying to reverse map address %.100s.", ntop); 8257429Smarkm /* Map the IP address to a host name. */ 8357429Smarkm if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), 8492559Sdes NULL, 0, NI_NAMEREQD) != 0) { 8576262Sgreen /* Host name not found. Use ip address. */ 8676262Sgreen return xstrdup(ntop); 8757429Smarkm } 8857429Smarkm 8976262Sgreen /* 90124211Sdes * if reverse lookup result looks like a numeric hostname, 91124211Sdes * someone is trying to trick us by PTR record like following: 92124211Sdes * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 93124211Sdes */ 94124211Sdes memset(&hints, 0, sizeof(hints)); 95124211Sdes hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 96124211Sdes hints.ai_flags = AI_NUMERICHOST; 97124211Sdes if (getaddrinfo(name, "0", &hints, &ai) == 0) { 98124211Sdes logit("Nasty PTR record \"%s\" is set up for %s, ignoring", 99124211Sdes name, ntop); 100124211Sdes freeaddrinfo(ai); 101124211Sdes return xstrdup(ntop); 102124211Sdes } 103124211Sdes 104124211Sdes /* 10576262Sgreen * Convert it to all lowercase (which is expected by the rest 10676262Sgreen * of this software). 10776262Sgreen */ 10876262Sgreen for (i = 0; name[i]; i++) 10976262Sgreen if (isupper(name[i])) 11076262Sgreen name[i] = tolower(name[i]); 11157429Smarkm /* 11276262Sgreen * Map it back to an IP address and check that the given 11376262Sgreen * address actually is an address of this host. This is 11476262Sgreen * necessary because anyone with access to a name server can 11576262Sgreen * define arbitrary names for an IP address. Mapping from 11676262Sgreen * name to IP address can be trusted better (but can still be 11776262Sgreen * fooled if the intruder has access to the name server of 11876262Sgreen * the domain). 11957429Smarkm */ 12076262Sgreen memset(&hints, 0, sizeof(hints)); 12176262Sgreen hints.ai_family = from.ss_family; 12276262Sgreen hints.ai_socktype = SOCK_STREAM; 12376262Sgreen if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { 124124211Sdes logit("reverse mapping checking getaddrinfo for %.700s " 12576262Sgreen "failed - POSSIBLE BREAKIN ATTEMPT!", name); 12676262Sgreen return xstrdup(ntop); 12757429Smarkm } 12876262Sgreen /* Look for the address from the list of addresses. */ 12976262Sgreen for (ai = aitop; ai; ai = ai->ai_next) { 13076262Sgreen if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, 13176262Sgreen sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && 13276262Sgreen (strcmp(ntop, ntop2) == 0)) 13376262Sgreen break; 13476262Sgreen } 13576262Sgreen freeaddrinfo(aitop); 13676262Sgreen /* If we reached the end of the list, the address was not there. */ 13776262Sgreen if (!ai) { 13876262Sgreen /* Address not found for the host name. */ 139124211Sdes logit("Address %.100s maps to %.600s, but this does not " 14076262Sgreen "map back to the address - POSSIBLE BREAKIN ATTEMPT!", 14176262Sgreen ntop, name); 14276262Sgreen return xstrdup(ntop); 14376262Sgreen } 14457429Smarkm return xstrdup(name); 14557429Smarkm} 14657429Smarkm 14757429Smarkm/* 14876262Sgreen * If IP options are supported, make sure there are none (log and 14976262Sgreen * disconnect them if any are found). Basically we are worried about 15076262Sgreen * source routing; it can be used to pretend you are somebody 15176262Sgreen * (ip-address) you are not. That itself may be "almost acceptable" 15276262Sgreen * under certain circumstances, but rhosts autentication is useless 15376262Sgreen * if source routing is accepted. Notice also that if we just dropped 15476262Sgreen * source routing here, the other side could use IP spoofing to do 15576262Sgreen * rest of the interaction and could still bypass security. So we 15676262Sgreen * exit here if we detect any IP options. 15776262Sgreen */ 15876262Sgreen/* IPv4 only */ 15992559Sdesstatic void 16076262Sgreencheck_ip_options(int socket, char *ipaddr) 16176262Sgreen{ 162124211Sdes#ifdef IP_OPTIONS 16376262Sgreen u_char options[200]; 16476262Sgreen char text[sizeof(options) * 3 + 1]; 16576262Sgreen socklen_t option_size; 16676262Sgreen int i, ipproto; 16776262Sgreen struct protoent *ip; 16876262Sgreen 16976262Sgreen if ((ip = getprotobyname("ip")) != NULL) 17076262Sgreen ipproto = ip->p_proto; 17176262Sgreen else 17276262Sgreen ipproto = IPPROTO_IP; 17376262Sgreen option_size = sizeof(options); 17492559Sdes if (getsockopt(socket, ipproto, IP_OPTIONS, options, 17576262Sgreen &option_size) >= 0 && option_size != 0) { 17676262Sgreen text[0] = '\0'; 17776262Sgreen for (i = 0; i < option_size; i++) 17876262Sgreen snprintf(text + i*3, sizeof(text) - i*3, 17976262Sgreen " %2.2x", options[i]); 180124211Sdes logit("Connection from %.100s with IP options:%.800s", 18176262Sgreen ipaddr, text); 18276262Sgreen packet_disconnect("Connection from %.100s with IP options:%.800s", 18376262Sgreen ipaddr, text); 18476262Sgreen } 185124211Sdes#endif /* IP_OPTIONS */ 18676262Sgreen} 18776262Sgreen 18876262Sgreen/* 18957429Smarkm * Return the canonical name of the host in the other side of the current 19057429Smarkm * connection. The host name is cached, so it is efficient to call this 19157429Smarkm * several times. 19257429Smarkm */ 19357429Smarkm 19457429Smarkmconst char * 195124211Sdesget_canonical_hostname(int use_dns) 19657429Smarkm{ 19757429Smarkm static char *canonical_host_name = NULL; 198124211Sdes static int use_dns_done = 0; 19957429Smarkm 20076262Sgreen /* Check if we have previously retrieved name with same option. */ 20176262Sgreen if (canonical_host_name != NULL) { 202124211Sdes if (use_dns_done != use_dns) 20376262Sgreen xfree(canonical_host_name); 20476262Sgreen else 20576262Sgreen return canonical_host_name; 20676262Sgreen } 20757429Smarkm 20857429Smarkm /* Get the real hostname if socket; otherwise return UNKNOWN. */ 20957429Smarkm if (packet_connection_is_on_socket()) 21076262Sgreen canonical_host_name = get_remote_hostname( 211124211Sdes packet_get_connection_in(), use_dns); 21257429Smarkm else 21357429Smarkm canonical_host_name = xstrdup("UNKNOWN"); 21457429Smarkm 215124211Sdes use_dns_done = use_dns; 21657429Smarkm return canonical_host_name; 21757429Smarkm} 21857429Smarkm 21957429Smarkm/* 220113911Sdes * Returns the local/remote IP-address/hostname of socket as a string. 221113911Sdes * The returned string must be freed. 22257429Smarkm */ 22392559Sdesstatic char * 22476262Sgreenget_socket_address(int socket, int remote, int flags) 22557429Smarkm{ 22676262Sgreen struct sockaddr_storage addr; 22776262Sgreen socklen_t addrlen; 22857429Smarkm char ntop[NI_MAXHOST]; 22957429Smarkm 23076262Sgreen /* Get IP address of client. */ 23176262Sgreen addrlen = sizeof(addr); 23276262Sgreen memset(&addr, 0, sizeof(addr)); 23357429Smarkm 23476262Sgreen if (remote) { 23576262Sgreen if (getpeername(socket, (struct sockaddr *)&addr, &addrlen) 236106130Sdes < 0) 23776262Sgreen return NULL; 23876262Sgreen } else { 23976262Sgreen if (getsockname(socket, (struct sockaddr *)&addr, &addrlen) 240106130Sdes < 0) 24176262Sgreen return NULL; 24257429Smarkm } 243113911Sdes 244113911Sdes /* Work around Linux IPv6 weirdness */ 245113911Sdes if (addr.ss_family == AF_INET6) 246113911Sdes addrlen = sizeof(struct sockaddr_in6); 247113911Sdes 24876262Sgreen /* Get the address in ascii. */ 24976262Sgreen if (getnameinfo((struct sockaddr *)&addr, addrlen, ntop, sizeof(ntop), 25092559Sdes NULL, 0, flags) != 0) { 251113911Sdes error("get_socket_address: getnameinfo %d failed", flags); 25276262Sgreen return NULL; 25357429Smarkm } 25476262Sgreen return xstrdup(ntop); 25576262Sgreen} 25657429Smarkm 25776262Sgreenchar * 25876262Sgreenget_peer_ipaddr(int socket) 25976262Sgreen{ 260106130Sdes char *p; 261106130Sdes 262106130Sdes if ((p = get_socket_address(socket, 1, NI_NUMERICHOST)) != NULL) 263106130Sdes return p; 264106130Sdes return xstrdup("UNKNOWN"); 26576262Sgreen} 26657429Smarkm 26776262Sgreenchar * 26876262Sgreenget_local_ipaddr(int socket) 26976262Sgreen{ 270106130Sdes char *p; 271106130Sdes 272106130Sdes if ((p = get_socket_address(socket, 0, NI_NUMERICHOST)) != NULL) 273106130Sdes return p; 274106130Sdes return xstrdup("UNKNOWN"); 27557429Smarkm} 27657429Smarkm 27776262Sgreenchar * 27876262Sgreenget_local_name(int socket) 27976262Sgreen{ 28076262Sgreen return get_socket_address(socket, 0, NI_NAMEREQD); 28176262Sgreen} 28276262Sgreen 28362101Sgreen/* 28476262Sgreen * Returns the IP-address of the remote host as a string. The returned 28576262Sgreen * string must not be freed. 28662101Sgreen */ 28762101Sgreen 28862101Sgreenconst char * 28992559Sdesget_remote_ipaddr(void) 29062101Sgreen{ 29162101Sgreen static char *canonical_host_ip = NULL; 29262101Sgreen 29376262Sgreen /* Check whether we have cached the ipaddr. */ 29476262Sgreen if (canonical_host_ip == NULL) { 29576262Sgreen if (packet_connection_is_on_socket()) { 29676262Sgreen canonical_host_ip = 29776262Sgreen get_peer_ipaddr(packet_get_connection_in()); 29876262Sgreen if (canonical_host_ip == NULL) 29976262Sgreen fatal_cleanup(); 30076262Sgreen } else { 30176262Sgreen /* If not on socket, return UNKNOWN. */ 30276262Sgreen canonical_host_ip = xstrdup("UNKNOWN"); 30376262Sgreen } 30462101Sgreen } 30576262Sgreen return canonical_host_ip; 30676262Sgreen} 30762101Sgreen 30876262Sgreenconst char * 309124211Sdesget_remote_name_or_ip(u_int utmp_len, int use_dns) 31076262Sgreen{ 31176262Sgreen static const char *remote = ""; 31276262Sgreen if (utmp_len > 0) 313124211Sdes remote = get_canonical_hostname(use_dns); 31476262Sgreen if (utmp_len == 0 || strlen(remote) > utmp_len) 31576262Sgreen remote = get_remote_ipaddr(); 31676262Sgreen return remote; 31762101Sgreen} 31862101Sgreen 31957429Smarkm/* Returns the local/remote port for the socket. */ 32057429Smarkm 32192559Sdesstatic int 32257429Smarkmget_sock_port(int sock, int local) 32357429Smarkm{ 32457429Smarkm struct sockaddr_storage from; 32557429Smarkm socklen_t fromlen; 32657429Smarkm char strport[NI_MAXSERV]; 32757429Smarkm 32857429Smarkm /* Get IP address of client. */ 32957429Smarkm fromlen = sizeof(from); 33057429Smarkm memset(&from, 0, sizeof(from)); 33157429Smarkm if (local) { 33257429Smarkm if (getsockname(sock, (struct sockaddr *)&from, &fromlen) < 0) { 33357429Smarkm error("getsockname failed: %.100s", strerror(errno)); 33457429Smarkm return 0; 33557429Smarkm } 33657429Smarkm } else { 337113911Sdes if (getpeername(sock, (struct sockaddr *)&from, &fromlen) < 0) { 33857429Smarkm debug("getpeername failed: %.100s", strerror(errno)); 33957429Smarkm fatal_cleanup(); 34057429Smarkm } 34157429Smarkm } 342113911Sdes 343113911Sdes /* Work around Linux IPv6 weirdness */ 344113911Sdes if (from.ss_family == AF_INET6) 345113911Sdes fromlen = sizeof(struct sockaddr_in6); 346113911Sdes 34757429Smarkm /* Return port number. */ 34857429Smarkm if (getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0, 34992559Sdes strport, sizeof(strport), NI_NUMERICSERV) != 0) 35057429Smarkm fatal("get_sock_port: getnameinfo NI_NUMERICSERV failed"); 35157429Smarkm return atoi(strport); 35257429Smarkm} 35357429Smarkm 35457429Smarkm/* Returns remote/local port number for the current connection. */ 35557429Smarkm 35692559Sdesstatic int 35757429Smarkmget_port(int local) 35857429Smarkm{ 35957429Smarkm /* 36057429Smarkm * If the connection is not a socket, return 65535. This is 36157429Smarkm * intentionally chosen to be an unprivileged port number. 36257429Smarkm */ 36357429Smarkm if (!packet_connection_is_on_socket()) 36457429Smarkm return 65535; 36557429Smarkm 36657429Smarkm /* Get socket and return the port number. */ 36757429Smarkm return get_sock_port(packet_get_connection_in(), local); 36857429Smarkm} 36957429Smarkm 37060573Skrisint 37157429Smarkmget_peer_port(int sock) 37257429Smarkm{ 37357429Smarkm return get_sock_port(sock, 0); 37457429Smarkm} 37557429Smarkm 37660573Skrisint 37792559Sdesget_remote_port(void) 37857429Smarkm{ 37957429Smarkm return get_port(0); 38057429Smarkm} 38157429Smarkm 38257429Smarkmint 38392559Sdesget_local_port(void) 38457429Smarkm{ 38557429Smarkm return get_port(1); 38657429Smarkm} 387