Home | History | Annotate | Download | only in in.ftpd
      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 }
    293