1 /* $NetBSD: socket.c,v 1.19 2012/03/21 10:10:37 matt Exp $ */ 2 3 /* 4 * This module determines the type of socket (datagram, stream), the client 5 * socket address and port, the server socket address and port. In addition, 6 * it provides methods to map a transport address to a printable host name 7 * or address. Socket address information results are in static memory. 8 * 9 * The result from the hostname lookup method is STRING_PARANOID when a host 10 * pretends to have someone elses name, or when a host name is available but 11 * could not be verified. 12 * 13 * When lookup or conversion fails the result is set to STRING_UNKNOWN. 14 * 15 * Diagnostics are reported through syslog(3). 16 * 17 * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 18 */ 19 20 #include <sys/cdefs.h> 21 #ifndef lint 22 #if 0 23 static char sccsid[] = "@(#) socket.c 1.15 97/03/21 19:27:24"; 24 #else 25 __RCSID("$NetBSD: socket.c,v 1.19 2012/03/21 10:10:37 matt Exp $"); 26 #endif 27 #endif 28 29 /* System libraries. */ 30 31 #include <sys/types.h> 32 #include <sys/param.h> 33 #include <sys/socket.h> 34 #include <netinet/in.h> 35 #include <netdb.h> 36 #include <stdio.h> 37 #include <syslog.h> 38 #include <string.h> 39 #include <arpa/inet.h> 40 41 /* Local stuff. */ 42 43 #include "tcpd.h" 44 45 /* Forward declarations. */ 46 47 #ifdef APPEND_DOT 48 static const char *append_dot __P((const char *)); 49 #endif 50 static void sock_sink __P((int)); 51 52 #ifdef APPEND_DOT 53 /* 54 * Speed up DNS lookups by terminating the host name with a dot. Should be 55 * done with care. The speedup can give problems with lookups from sources 56 * that lack DNS-style trailing dot magic, such as local files or NIS maps. 57 */ 58 59 static const char * 60 append_dot(name) 61 const char *name; 62 { 63 static char hbuf[MAXHOSTNAMELEN + 1]; 64 65 /* 66 * Don't append dots to unqualified names. Such names are likely to come 67 * from local hosts files or from NIS. 68 */ 69 70 if (strchr(name, '.') == 0 || strlen(name) + 2 > sizeof(hbuf)) 71 strlcpy(hbuf, name, sizeof(hbuf)); 72 else 73 (void)snprintf(hbuf, sizeof(hbuf), "%s.", name); 74 return hbuf; 75 } 76 #endif 77 78 /* sock_host - look up endpoint addresses and install conversion methods */ 79 80 void 81 sock_host(struct request_info *request) 82 { 83 static struct sockaddr_storage client; 84 static struct sockaddr_storage server; 85 socklen_t len; 86 char buf[BUFSIZ]; 87 int fd = request->fd; 88 89 sock_methods(request); 90 91 /* 92 * Look up the client host address. Hal R. Brand <BRAND@addvax.llnl.gov> 93 * suggested how to get the client host info in case of UDP connections: 94 * peek at the first message without actually looking at its contents. We 95 * really should verify that client.sin_family gets the value AF_INET, 96 * but this program has already caused too much grief on systems with 97 * broken library code. 98 * 99 * XXX the last sentence is untrue as we support AF_INET6 as well :-) 100 */ 101 102 if (request->client->sin == NULL) { 103 len = sizeof(client); 104 if (getpeername(fd, (struct sockaddr *)(void *)& client, &len) < 0) { 105 request->sink = sock_sink; 106 len = sizeof(client); 107 if (recvfrom(fd, buf, sizeof(buf), MSG_PEEK, 108 (struct sockaddr *) & client, &len) < 0) { 109 tcpd_warn("can't get client address: %m"); 110 return; /* give up */ 111 } 112 #ifdef really_paranoid 113 memset(buf, 0, sizeof(buf)); 114 #endif 115 } 116 request->client->sin = (struct sockaddr *)&client; 117 } 118 119 /* 120 * Determine the server binding. This is used for client username 121 * lookups, and for access control rules that trigger on the server 122 * address or name. 123 */ 124 125 if (request->server->sin == NULL) { 126 len = sizeof(server); 127 if (getsockname(fd, (struct sockaddr *) & server, &len) < 0) { 128 tcpd_warn("getsockname: %m"); 129 return; 130 } 131 request->server->sin = (struct sockaddr *)&server; 132 } 133 } 134 135 /* sock_hostaddr - map endpoint address to printable form */ 136 137 void 138 sock_hostaddr(struct host_info *host) 139 { 140 struct sockaddr *sa = host->sin; 141 142 if (!sa) 143 return; 144 host->addr[0] = '\0'; 145 getnameinfo(sa, sa->sa_len, host->addr, sizeof(host->addr), 146 NULL, 0, NI_NUMERICHOST); 147 } 148 149 /* sock_hostname - map endpoint address to host name */ 150 151 void 152 sock_hostname(struct host_info *host) 153 { 154 struct sockaddr *sa = host->sin; 155 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 156 struct addrinfo hints, *res, *res0; 157 #ifdef INET6 158 struct sockaddr_in tmp; 159 #endif 160 161 if (!sa) 162 return; 163 #ifdef INET6 164 /* special case on reverse lookup: mapped addr. I hate it */ 165 if (sa->sa_family == AF_INET6 && 166 IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sa)->sin6_addr)) { 167 memset(&tmp, 0, sizeof(tmp)); 168 tmp.sin_family = AF_INET; 169 tmp.sin_len = sizeof(struct sockaddr_in); 170 memcpy(&tmp.sin_addr, 171 &((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[12], 4); 172 sa = (struct sockaddr *)&tmp; 173 } 174 #endif 175 if (getnameinfo(sa, sa->sa_len, h1, sizeof(h1), NULL, 0, 176 NI_NUMERICHOST) != 0) { 177 return; 178 } 179 if (getnameinfo(sa, sa->sa_len, host->name, sizeof(host->name), NULL, 0, 180 NI_NAMEREQD) == 0) { 181 /* 182 * if reverse lookup result looks like a numeric hostname, 183 * someone is trying to trick us by PTR record like following: 184 * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 185 */ 186 memset(&hints, 0, sizeof(hints)); 187 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 188 hints.ai_flags = AI_NUMERICHOST; 189 #ifdef APPEND_DOT 190 if (getaddrinfo(append_dot(host->name), "0", &hints, &res0) == 0) 191 #else 192 if (getaddrinfo(host->name, "0", &hints, &res0) == 0) 193 #endif 194 { 195 tcpd_warn("Nasty PTR record is configured"); 196 freeaddrinfo(res0); 197 /* name is bad, clobber it */ 198 (void)strlcpy(host->name, paranoid, sizeof(host->name)); 199 return; 200 } 201 202 /* 203 * Verify that the address is a member of the address list returned 204 * by getaddrinfo(hostname). 205 * 206 * Verify also that getnameinfo() and getaddrinfo() return the same 207 * hostname, or rshd and rlogind may still end up being spoofed. 208 * 209 * On some sites, getaddrinfo("localhost") returns "localhost.domain". 210 * This is a DNS artefact. We treat it as a special case. When we 211 * can't believe the address list from getaddrinfo("localhost") 212 * we're in big trouble anyway. 213 */ 214 memset(&hints, 0, sizeof(hints)); 215 hints.ai_family = sa->sa_family; 216 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 217 hints.ai_flags = AI_CANONNAME; 218 #ifdef APPEND_DOT 219 if (getaddrinfo(append_dot(host->name), "0", &hints, &res0) != 0) 220 #else 221 if (getaddrinfo(host->name, "0", &hints, &res0) != 0) 222 #endif 223 { 224 /* 225 * Unable to verify that the host name matches the address. This 226 * may be a transient problem or a botched name server setup. 227 */ 228 229 tcpd_warn("can't verify hostname: getaddrinfo(%s, %d) failed", 230 host->name, hints.ai_family); 231 } else if (res0->ai_canonname && 232 STR_NE(host->name, res0->ai_canonname) && 233 STR_NE(host->name, "localhost")) { 234 /* 235 * The getnameinfo() and getaddrinfo() calls did not return 236 * the same hostname. This could be a nameserver configuration 237 * problem. It could also be that someone is trying to spoof us. 238 */ 239 240 tcpd_warn("host name/name mismatch: %s != %s", 241 host->name, res0->ai_canonname); 242 freeaddrinfo(res0); 243 } else { 244 /* 245 * The address should be a member of the address list returned by 246 * getaddrinfo(). 247 */ 248 249 for (res = res0; res; res = res->ai_next) { 250 if (getnameinfo(res->ai_addr, res->ai_addrlen, h2, sizeof(h2), 251 NULL, 0, NI_NUMERICHOST) != 0) { 252 continue; 253 } 254 if (STR_EQ(h1, h2)) { 255 freeaddrinfo(res0); 256 return; 257 } 258 } 259 260 /* 261 * The host name does not map to the initial address. Perhaps 262 * someone has messed up. Perhaps someone compromised a name 263 * server. 264 */ 265 266 tcpd_warn("host name/address mismatch: %s != %s", h1, 267 res0->ai_canonname ? res0->ai_canonname : "?"); 268 269 freeaddrinfo(res0); 270 } 271 /* name is bad, clobber it */ 272 (void)strlcpy(host->name, paranoid, sizeof(host->name)); 273 } 274 } 275 276 /* sock_sink - absorb unreceived IP datagram */ 277 278 static void 279 sock_sink(int fd) 280 { 281 char buf[BUFSIZ]; 282 struct sockaddr_storage ss; 283 socklen_t size = sizeof(ss); 284 285 /* 286 * Eat up the not-yet received datagram. Some systems insist on a 287 * non-zero source address argument in the recvfrom() call below. 288 */ 289 290 (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) & ss, &size); 291 } 292