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 */ 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 */ 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 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 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 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 }