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