1 /*
2 * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 #pragma ident "%Z%%M% %I% %E% SMI"
7
8 /****************************************************************************
9 Copyright (c) 1999,2000 WU-FTPD Development Group.
10 All rights reserved.
11
12 Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
13 The Regents of the University of California.
14 Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
15 Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
16 Portions Copyright (c) 1989 Massachusetts Institute of Technology.
17 Portions Copyright (c) 1998 Sendmail, Inc.
18 Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
19 Portions Copyright (c) 1997 by Stan Barber.
20 Portions Copyright (c) 1997 by Kent Landfield.
21 Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
22 Free Software Foundation, Inc.
23
24 Use and distribution of this software and its source code are governed
25 by the terms and conditions of the WU-FTPD Software License ("LICENSE").
26
27 If you did not receive a copy of the license, it may be obtained online
28 at http://www.wu-ftpd.org/license.html.
29
30 $Id: routevector.c,v 1.13 2000/07/01 18:17:39 wuftpd Exp $
31
32 ****************************************************************************/
33 /*
34 * Parse the entire ftpaccess file looking for:
35 *
36 * passive address <externalip> <address/CIDR>
37 * passive ports <address/CIDR> <min> <max>
38 *
39 * vect_addr, passive_port_min and passive_port_max store the external IP
40 * address, min and max ports found whose associated address is the most
41 * specific match of the address the client connected from.
42 *
43 * The optional CIDR denotes the number of significant bits in the address,
44 * the higher the CIDR the more specific the address. If no CIDR is specified,
45 * the whole address is significant.
46 *
47 * When a passive data connection is requested the server listens on a port
48 * randomly selected between passive_port_min and passive_port_max
49 * (inclusive), if vect_addr is set its address is reported (if not the
50 * local address of the control connection is reported). Note this does not
51 * change the address the server actually listens on, only the address
52 * reported to the client.
53 *
54 * For example if the ftpaccess file includes:
55 * passive address 194.80.17.14 0.0.0.0/0
56 * passive address 10.0.1.15 10.0.0.0/8
57 *
58 * Clients connecting from the class-A network 10 will be told the passive
59 * connection is listening on IP address 10.0.1.15, while clients connecting
60 * from all other addresses will be told the connection is listening on
61 * 194.80.17.14 (a CIDR of /0 matches all addresses of the same address
62 * family, if IPv6 support is enabled then IPv4 and IPv6 addresses are
63 * supported).
64 */
65
66 #include "config.h"
67 #include <sys/socket.h>
68 #include <netinet/in.h>
69 #include <arpa/inet.h>
70 #include <string.h>
71 #ifdef HAVE_SYS_SYSLOG_H
72 #include <sys/syslog.h>
73 #endif
74 #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
75 #include <syslog.h>
76 #endif
77 #include "extensions.h"
78 #include "proto.h"
79
80 extern struct SOCKSTORAGE his_addr;
81 extern struct SOCKSTORAGE vect_addr; /* best matching external IP address */
82 extern int passive_port_min;
83 extern int passive_port_max;
84
85 /* significance of the external IP address and port entries */
86 static int vect_sig = -1;
87 static int port_sig = -1;
88
89 #ifdef INET6
90 static int his_addr_family = AF_INET;
91 static int his_v4mapped = 0;
92 #endif
93
94 /*
95 * Compares the address the client connected from (in his_addr) with the
96 * supplied address, with the specified number of bits being significant
97 * in the comparison. Returns 0 if the addresses match, non-zero otherwise.
98 */
addr_cmp(void * addr,int sig)99 static int addr_cmp(void *addr, int sig)
100 {
101 uint32_t addr32[4], rem32[4];
102 int bitstozero, i, start = 0, len = sizeof(uint32_t);
103 char *ptr;
104
105 #ifdef INET6
106 if (his_addr_family == AF_INET) {
107 if (his_v4mapped) {
108 ptr = (char *)&((struct sockaddr_in6 *)&his_addr)->sin6_addr;
109 /* move to the IPv4 part of an IPv4-mapped IPv6 address */
110 ptr += 12;
111 }
112 else
113 #endif
114 ptr = (char *)&((struct sockaddr_in *)&his_addr)->sin_addr;
115
116 /* IPv4 addresses are 32-bits long */
117 bitstozero = 32 - sig;
118 memcpy(addr32, addr, sizeof(uint32_t));
119 memcpy(rem32, ptr, sizeof(uint32_t));
120 #ifdef INET6
121 }
122 else {
123 /* IPv6 addresses are 128-bits long */
124 bitstozero = 128 - sig;
125 start = 3;
126 len = sizeof(addr32);
127 memcpy(addr32, addr, sizeof(addr32));
128 memcpy(rem32, &((struct sockaddr_in6 *)&his_addr)->sin6_addr, sizeof(rem32));
129 }
130 #endif
131
132 /* zero bits starting with the least significant */
133 for (i = start; (bitstozero > 0) && (i >= 0); i--, bitstozero -= 32) {
134 if (bitstozero >= 32)
135 addr32[i] = rem32[i] = 0;
136 else {
137 addr32[i] = (ntohl(addr32[i]) >> bitstozero) << bitstozero;
138 rem32[i] = (ntohl(rem32[i]) >> bitstozero) << bitstozero;
139 }
140 }
141
142 /* compare the IP addresses */
143 return memcmp(addr32, rem32, len);
144 }
145
146 /*
147 * Matches a supplied IP address string against the address the client
148 * connected from (in his_addr). Returns 1 and updates sig if the addresses
149 * match and there hasn't already been a more specific match, zero otherwise.
150 */
better_match(char * addrstr,int * sig)151 static int better_match(char *addrstr, int *sig)
152 {
153 int addr_sig, max_sig = 32;
154 char *ptr;
155 void *addr;
156 #ifdef INET6
157 int rval;
158 struct in6_addr in6;
159 #else
160 struct in_addr in;
161 #endif
162
163 /* look for the optional significance (/CIDR) */
164 if ((ptr = strstr(addrstr, "/")))
165 *ptr = '\0';
166
167 #ifdef INET6
168 if (his_addr_family == AF_INET6)
169 max_sig = 128;
170 #endif
171
172 if (ptr) {
173 addr_sig = atoi(++ptr);
174 if (addr_sig < 0)
175 addr_sig = 0;
176 else if (addr_sig > max_sig)
177 addr_sig = max_sig;
178 }
179 else
180 addr_sig = max_sig;
181
182 /* return if we already have a more specific match */
183 if (addr_sig < *sig) {
184 if (ptr)
185 *--ptr = '/';
186 return 0;
187 }
188
189 #ifdef INET6
190 rval = inet_pton6(addrstr, &in6);
191 if (ptr)
192 *--ptr = '/';
193 if (rval != 1)
194 return 0;
195
196 if (his_addr_family == AF_INET) {
197 /* convert IPv4-mapped IPv6 addresses to IPv4 addresses */
198 if (IN6_IS_ADDR_V4MAPPED(&in6))
199 addr = &in6.s6_addr[12];
200 else
201 return 0;
202 }
203 else
204 addr = &in6.s6_addr;
205 #else
206 in.s_addr = inet_addr(addrstr);
207 if (ptr)
208 *--ptr = '/';
209 if ((int)in.s_addr == -1)
210 return 0;
211 addr = &in.s_addr;
212 #endif
213
214 if (addr_cmp(addr, addr_sig) == 0) {
215 *sig = addr_sig;
216 return 1;
217 }
218 return 0;
219 }
220
update_address(char * externalip,char * addrstr)221 static void update_address(char *externalip, char *addrstr)
222 {
223 struct SOCKSTORAGE ext_addr;
224 #ifndef INET6
225 struct in_addr in;
226 #endif
227
228 /* validate the external IP address string */
229 #ifdef INET6
230 SET_SOCK_FAMILY(ext_addr, AF_INET6);
231 if (inet_pton6(externalip, SOCK_ADDR(ext_addr)) != 1)
232 return;
233 if ((his_addr_family == AF_INET) &&
234 !IN6_IS_ADDR_V4MAPPED((struct in6_addr *)SOCK_ADDR(ext_addr)))
235 return;
236 #else
237 if ((int)(in.s_addr = inet_addr(externalip)) == -1)
238 return;
239 SET_SOCK_FAMILY(ext_addr, AF_INET);
240 SET_SOCK_ADDR4(ext_addr, in);
241 #endif
242
243 if (better_match(addrstr, &vect_sig))
244 vect_addr = ext_addr;
245 }
246
update_ports(char * addrstr,char * minport,char * maxport)247 static void update_ports(char *addrstr, char *minport, char *maxport)
248 {
249 int min, max;
250
251 min = atoi(minport);
252 max = atoi(maxport);
253
254 /* validate the ports supplied */
255 if ((min > max) || (min < 0) || (max > 65535) || (min == 0 && max != 0)) {
256 syslog(LOG_WARNING, "ftpaccess passive ports entry invalid: %s %s %s", addrstr, minport, maxport);
257 return;
258 }
259
260 if (better_match(addrstr, &port_sig)) {
261 passive_port_min = min;
262 passive_port_max = max;
263 }
264 }
265
routevector(void)266 int routevector(void)
267 {
268 struct aclmember *entry = NULL;
269
270 #ifdef INET6
271 if (SOCK_FAMILY(his_addr) == AF_INET6) {
272 if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&(his_addr))->sin6_addr))
273 his_v4mapped = 1;
274 else
275 his_addr_family = AF_INET6;
276 }
277 #endif
278
279 while (getaclentry("passive", &entry)) {
280 if (!strcasecmp(ARG0, "address")) {
281 if (!ARG1 || !ARG2)
282 continue;
283 update_address(ARG1, ARG2);
284 }
285 if (!strcasecmp(ARG0, "ports")) {
286 if (!ARG1 || !ARG2 || !ARG3)
287 continue;
288 update_ports(ARG1, ARG2, ARG3);
289 }
290 }
291 return vect_sig != -1;
292 }
293