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 }