Annotation of embedaddon/rsync/clientname.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Functions for looking up the remote name or addr of a socket.
! 3: *
! 4: * Copyright (C) 1992-2001 Andrew Tridgell <tridge@samba.org>
! 5: * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
! 6: * Copyright (C) 2002-2009 Wayne Davison
! 7: *
! 8: * This program is free software; you can redistribute it and/or modify
! 9: * it under the terms of the GNU General Public License as published by
! 10: * the Free Software Foundation; either version 3 of the License, or
! 11: * (at your option) any later version.
! 12: *
! 13: * This program is distributed in the hope that it will be useful,
! 14: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 15: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 16: * GNU General Public License for more details.
! 17: *
! 18: * You should have received a copy of the GNU General Public License along
! 19: * with this program; if not, visit the http://fsf.org website.
! 20: */
! 21:
! 22: /*
! 23: * This file is now converted to use the new-style getaddrinfo()
! 24: * interface, which supports IPv6 but is also supported on recent
! 25: * IPv4-only machines. On systems that don't have that interface, we
! 26: * emulate it using the KAME implementation.
! 27: */
! 28:
! 29: #include "rsync.h"
! 30:
! 31: static const char default_name[] = "UNKNOWN";
! 32: extern int am_server;
! 33:
! 34:
! 35: /**
! 36: * Return the IP addr of the client as a string
! 37: **/
! 38: char *client_addr(int fd)
! 39: {
! 40: static char addr_buf[100];
! 41: static int initialised;
! 42: struct sockaddr_storage ss;
! 43: socklen_t length = sizeof ss;
! 44: char *ssh_info, *p;
! 45:
! 46: if (initialised)
! 47: return addr_buf;
! 48:
! 49: initialised = 1;
! 50:
! 51: if (am_server) { /* daemon over --rsh mode */
! 52: strlcpy(addr_buf, "0.0.0.0", sizeof addr_buf);
! 53: if ((ssh_info = getenv("SSH_CONNECTION")) != NULL
! 54: || (ssh_info = getenv("SSH_CLIENT")) != NULL
! 55: || (ssh_info = getenv("SSH2_CLIENT")) != NULL) {
! 56: strlcpy(addr_buf, ssh_info, sizeof addr_buf);
! 57: /* Truncate the value to just the IP address. */
! 58: if ((p = strchr(addr_buf, ' ')) != NULL)
! 59: *p = '\0';
! 60: }
! 61: } else {
! 62: client_sockaddr(fd, &ss, &length);
! 63: getnameinfo((struct sockaddr *)&ss, length,
! 64: addr_buf, sizeof addr_buf, NULL, 0, NI_NUMERICHOST);
! 65: }
! 66:
! 67: return addr_buf;
! 68: }
! 69:
! 70:
! 71: static int get_sockaddr_family(const struct sockaddr_storage *ss)
! 72: {
! 73: return ((struct sockaddr *) ss)->sa_family;
! 74: }
! 75:
! 76:
! 77: /**
! 78: * Return the DNS name of the client.
! 79: *
! 80: * The name is statically cached so that repeated lookups are quick,
! 81: * so there is a limit of one lookup per customer.
! 82: *
! 83: * If anything goes wrong, including the name->addr->name check, then
! 84: * we just use "UNKNOWN", so you can use that value in hosts allow
! 85: * lines.
! 86: *
! 87: * After translation from sockaddr to name we do a forward lookup to
! 88: * make sure nobody is spoofing PTR records.
! 89: **/
! 90: char *client_name(int fd)
! 91: {
! 92: static char name_buf[100];
! 93: static char port_buf[100];
! 94: static int initialised;
! 95: struct sockaddr_storage ss;
! 96: socklen_t ss_len;
! 97:
! 98: if (initialised)
! 99: return name_buf;
! 100:
! 101: strlcpy(name_buf, default_name, sizeof name_buf);
! 102: initialised = 1;
! 103:
! 104: memset(&ss, 0, sizeof ss);
! 105:
! 106: if (am_server) { /* daemon over --rsh mode */
! 107: char *addr = client_addr(fd);
! 108: struct addrinfo hint, *answer;
! 109: int err;
! 110:
! 111: if (strcmp(addr, "0.0.0.0") == 0)
! 112: return name_buf;
! 113:
! 114: memset(&hint, 0, sizeof hint);
! 115:
! 116: #ifdef AI_NUMERICHOST
! 117: hint.ai_flags = AI_NUMERICHOST;
! 118: #endif
! 119: hint.ai_socktype = SOCK_STREAM;
! 120:
! 121: if ((err = getaddrinfo(addr, NULL, &hint, &answer)) != 0) {
! 122: rprintf(FLOG, "malformed address %s: %s\n",
! 123: addr, gai_strerror(err));
! 124: return name_buf;
! 125: }
! 126:
! 127: switch (answer->ai_family) {
! 128: case AF_INET:
! 129: ss_len = sizeof (struct sockaddr_in);
! 130: memcpy(&ss, answer->ai_addr, ss_len);
! 131: break;
! 132: #ifdef INET6
! 133: case AF_INET6:
! 134: ss_len = sizeof (struct sockaddr_in6);
! 135: memcpy(&ss, answer->ai_addr, ss_len);
! 136: break;
! 137: #endif
! 138: default:
! 139: exit_cleanup(RERR_SOCKETIO);
! 140: }
! 141: freeaddrinfo(answer);
! 142: } else {
! 143: ss_len = sizeof ss;
! 144: client_sockaddr(fd, &ss, &ss_len);
! 145: }
! 146:
! 147: if (lookup_name(fd, &ss, ss_len, name_buf, sizeof name_buf,
! 148: port_buf, sizeof port_buf) == 0)
! 149: check_name(fd, &ss, name_buf, sizeof name_buf);
! 150:
! 151: return name_buf;
! 152: }
! 153:
! 154:
! 155:
! 156: /**
! 157: * Get the sockaddr for the client.
! 158: *
! 159: * If it comes in as an ipv4 address mapped into IPv6 format then we
! 160: * convert it back to a regular IPv4.
! 161: **/
! 162: void client_sockaddr(int fd,
! 163: struct sockaddr_storage *ss,
! 164: socklen_t *ss_len)
! 165: {
! 166: memset(ss, 0, sizeof *ss);
! 167:
! 168: if (getpeername(fd, (struct sockaddr *) ss, ss_len)) {
! 169: /* FIXME: Can we really not continue? */
! 170: rsyserr(FLOG, errno, "getpeername on fd%d failed", fd);
! 171: exit_cleanup(RERR_SOCKETIO);
! 172: }
! 173:
! 174: #ifdef INET6
! 175: if (get_sockaddr_family(ss) == AF_INET6 &&
! 176: IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)ss)->sin6_addr)) {
! 177: /* OK, so ss is in the IPv6 family, but it is really
! 178: * an IPv4 address: something like
! 179: * "::ffff:10.130.1.2". If we use it as-is, then the
! 180: * reverse lookup might fail or perhaps something else
! 181: * bad might happen. So instead we convert it to an
! 182: * equivalent address in the IPv4 address family. */
! 183: struct sockaddr_in6 sin6;
! 184: struct sockaddr_in *sin;
! 185:
! 186: memcpy(&sin6, ss, sizeof sin6);
! 187: sin = (struct sockaddr_in *)ss;
! 188: memset(sin, 0, sizeof *sin);
! 189: sin->sin_family = AF_INET;
! 190: *ss_len = sizeof (struct sockaddr_in);
! 191: #ifdef HAVE_SOCKADDR_IN_LEN
! 192: sin->sin_len = *ss_len;
! 193: #endif
! 194: sin->sin_port = sin6.sin6_port;
! 195:
! 196: /* There is a macro to extract the mapped part
! 197: * (IN6_V4MAPPED_TO_SINADDR ?), but it does not seem
! 198: * to be present in the Linux headers. */
! 199: memcpy(&sin->sin_addr, &sin6.sin6_addr.s6_addr[12],
! 200: sizeof sin->sin_addr);
! 201: }
! 202: #endif
! 203: }
! 204:
! 205:
! 206: /**
! 207: * Look up a name from @p ss into @p name_buf.
! 208: *
! 209: * @param fd file descriptor for client socket.
! 210: **/
! 211: int lookup_name(int fd, const struct sockaddr_storage *ss,
! 212: socklen_t ss_len,
! 213: char *name_buf, size_t name_buf_size,
! 214: char *port_buf, size_t port_buf_size)
! 215: {
! 216: int name_err;
! 217:
! 218: /* reverse lookup */
! 219: name_err = getnameinfo((struct sockaddr *) ss, ss_len,
! 220: name_buf, name_buf_size,
! 221: port_buf, port_buf_size,
! 222: NI_NAMEREQD | NI_NUMERICSERV);
! 223: if (name_err != 0) {
! 224: strlcpy(name_buf, default_name, name_buf_size);
! 225: rprintf(FLOG, "name lookup failed for %s: %s\n",
! 226: client_addr(fd), gai_strerror(name_err));
! 227: return name_err;
! 228: }
! 229:
! 230: return 0;
! 231: }
! 232:
! 233:
! 234:
! 235: /**
! 236: * Compare an addrinfo from the resolver to a sockinfo.
! 237: *
! 238: * Like strcmp, returns 0 for identical.
! 239: **/
! 240: int compare_addrinfo_sockaddr(const struct addrinfo *ai,
! 241: const struct sockaddr_storage *ss)
! 242: {
! 243: int ss_family = get_sockaddr_family(ss);
! 244: const char fn[] = "compare_addrinfo_sockaddr";
! 245:
! 246: if (ai->ai_family != ss_family) {
! 247: rprintf(FLOG, "%s: response family %d != %d\n",
! 248: fn, ai->ai_family, ss_family);
! 249: return 1;
! 250: }
! 251:
! 252: /* The comparison method depends on the particular AF. */
! 253: if (ss_family == AF_INET) {
! 254: const struct sockaddr_in *sin1, *sin2;
! 255:
! 256: sin1 = (const struct sockaddr_in *) ss;
! 257: sin2 = (const struct sockaddr_in *) ai->ai_addr;
! 258:
! 259: return memcmp(&sin1->sin_addr, &sin2->sin_addr,
! 260: sizeof sin1->sin_addr);
! 261: }
! 262:
! 263: #ifdef INET6
! 264: if (ss_family == AF_INET6) {
! 265: const struct sockaddr_in6 *sin1, *sin2;
! 266:
! 267: sin1 = (const struct sockaddr_in6 *) ss;
! 268: sin2 = (const struct sockaddr_in6 *) ai->ai_addr;
! 269:
! 270: if (ai->ai_addrlen < sizeof (struct sockaddr_in6)) {
! 271: rprintf(FLOG, "%s: too short sockaddr_in6; length=%d\n",
! 272: fn, (int)ai->ai_addrlen);
! 273: return 1;
! 274: }
! 275:
! 276: if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
! 277: sizeof sin1->sin6_addr))
! 278: return 1;
! 279:
! 280: #ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
! 281: if (sin1->sin6_scope_id != sin2->sin6_scope_id)
! 282: return 1;
! 283: #endif
! 284: return 0;
! 285: }
! 286: #endif /* INET6 */
! 287:
! 288: /* don't know */
! 289: return 1;
! 290: }
! 291:
! 292:
! 293: /**
! 294: * Do a forward lookup on @p name_buf and make sure it corresponds to
! 295: * @p ss -- otherwise we may be being spoofed. If we suspect we are,
! 296: * then we don't abort the connection but just emit a warning, and
! 297: * change @p name_buf to be "UNKNOWN".
! 298: *
! 299: * We don't do anything with the service when checking the name,
! 300: * because it doesn't seem that it could be spoofed in any way, and
! 301: * getaddrinfo on random service names seems to cause problems on AIX.
! 302: **/
! 303: int check_name(int fd,
! 304: const struct sockaddr_storage *ss,
! 305: char *name_buf, size_t name_buf_size)
! 306: {
! 307: struct addrinfo hints, *res, *res0;
! 308: int error;
! 309: int ss_family = get_sockaddr_family(ss);
! 310:
! 311: memset(&hints, 0, sizeof hints);
! 312: hints.ai_family = ss_family;
! 313: hints.ai_flags = AI_CANONNAME;
! 314: hints.ai_socktype = SOCK_STREAM;
! 315: error = getaddrinfo(name_buf, NULL, &hints, &res0);
! 316: if (error) {
! 317: rprintf(FLOG, "forward name lookup for %s failed: %s\n",
! 318: name_buf, gai_strerror(error));
! 319: strlcpy(name_buf, default_name, name_buf_size);
! 320: return error;
! 321: }
! 322:
! 323: /* Given all these results, we expect that one of them will be
! 324: * the same as ss. The comparison is a bit complicated. */
! 325: for (res = res0; res; res = res->ai_next) {
! 326: if (!compare_addrinfo_sockaddr(res, ss))
! 327: break; /* OK, identical */
! 328: }
! 329:
! 330: if (!res0) {
! 331: /* We hit the end of the list without finding an
! 332: * address that was the same as ss. */
! 333: rprintf(FLOG, "no known address for \"%s\": "
! 334: "spoofed address?\n", name_buf);
! 335: strlcpy(name_buf, default_name, name_buf_size);
! 336: } else if (res == NULL) {
! 337: /* We hit the end of the list without finding an
! 338: * address that was the same as ss. */
! 339: rprintf(FLOG, "%s is not a known address for \"%s\": "
! 340: "spoofed address?\n", client_addr(fd), name_buf);
! 341: strlcpy(name_buf, default_name, name_buf_size);
! 342: }
! 343:
! 344: freeaddrinfo(res0);
! 345: return 0;
! 346: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>