Annotation of embedaddon/rsync/clientname.c, revision 1.1.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>