summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/routevector.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/routevector.c')
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/routevector.c292
1 files changed, 292 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/routevector.c b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/routevector.c
new file mode 100644
index 0000000000..c0c7fc38f2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/routevector.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/****************************************************************************
+ Copyright (c) 1999,2000 WU-FTPD Development Group.
+ All rights reserved.
+
+ Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
+ The Regents of the University of California.
+ Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
+ Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
+ Portions Copyright (c) 1989 Massachusetts Institute of Technology.
+ Portions Copyright (c) 1998 Sendmail, Inc.
+ Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
+ Portions Copyright (c) 1997 by Stan Barber.
+ Portions Copyright (c) 1997 by Kent Landfield.
+ Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ Free Software Foundation, Inc.
+
+ Use and distribution of this software and its source code are governed
+ by the terms and conditions of the WU-FTPD Software License ("LICENSE").
+
+ If you did not receive a copy of the license, it may be obtained online
+ at http://www.wu-ftpd.org/license.html.
+
+ $Id: routevector.c,v 1.13 2000/07/01 18:17:39 wuftpd Exp $
+
+****************************************************************************/
+/*
+ * Parse the entire ftpaccess file looking for:
+ *
+ * passive address <externalip> <address/CIDR>
+ * passive ports <address/CIDR> <min> <max>
+ *
+ * vect_addr, passive_port_min and passive_port_max store the external IP
+ * address, min and max ports found whose associated address is the most
+ * specific match of the address the client connected from.
+ *
+ * The optional CIDR denotes the number of significant bits in the address,
+ * the higher the CIDR the more specific the address. If no CIDR is specified,
+ * the whole address is significant.
+ *
+ * When a passive data connection is requested the server listens on a port
+ * randomly selected between passive_port_min and passive_port_max
+ * (inclusive), if vect_addr is set its address is reported (if not the
+ * local address of the control connection is reported). Note this does not
+ * change the address the server actually listens on, only the address
+ * reported to the client.
+ *
+ * For example if the ftpaccess file includes:
+ * passive address 194.80.17.14 0.0.0.0/0
+ * passive address 10.0.1.15 10.0.0.0/8
+ *
+ * Clients connecting from the class-A network 10 will be told the passive
+ * connection is listening on IP address 10.0.1.15, while clients connecting
+ * from all other addresses will be told the connection is listening on
+ * 194.80.17.14 (a CIDR of /0 matches all addresses of the same address
+ * family, if IPv6 support is enabled then IPv4 and IPv6 addresses are
+ * supported).
+ */
+
+#include "config.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#ifdef HAVE_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif
+#if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
+#include <syslog.h>
+#endif
+#include "extensions.h"
+#include "proto.h"
+
+extern struct SOCKSTORAGE his_addr;
+extern struct SOCKSTORAGE vect_addr; /* best matching external IP address */
+extern int passive_port_min;
+extern int passive_port_max;
+
+/* significance of the external IP address and port entries */
+static int vect_sig = -1;
+static int port_sig = -1;
+
+#ifdef INET6
+static int his_addr_family = AF_INET;
+static int his_v4mapped = 0;
+#endif
+
+/*
+ * Compares the address the client connected from (in his_addr) with the
+ * supplied address, with the specified number of bits being significant
+ * in the comparison. Returns 0 if the addresses match, non-zero otherwise.
+ */
+static int addr_cmp(void *addr, int sig)
+{
+ uint32_t addr32[4], rem32[4];
+ int bitstozero, i, start = 0, len = sizeof(uint32_t);
+ char *ptr;
+
+#ifdef INET6
+ if (his_addr_family == AF_INET) {
+ if (his_v4mapped) {
+ ptr = (char *)&((struct sockaddr_in6 *)&his_addr)->sin6_addr;
+ /* move to the IPv4 part of an IPv4-mapped IPv6 address */
+ ptr += 12;
+ }
+ else
+#endif
+ ptr = (char *)&((struct sockaddr_in *)&his_addr)->sin_addr;
+
+ /* IPv4 addresses are 32-bits long */
+ bitstozero = 32 - sig;
+ memcpy(addr32, addr, sizeof(uint32_t));
+ memcpy(rem32, ptr, sizeof(uint32_t));
+#ifdef INET6
+ }
+ else {
+ /* IPv6 addresses are 128-bits long */
+ bitstozero = 128 - sig;
+ start = 3;
+ len = sizeof(addr32);
+ memcpy(addr32, addr, sizeof(addr32));
+ memcpy(rem32, &((struct sockaddr_in6 *)&his_addr)->sin6_addr, sizeof(rem32));
+ }
+#endif
+
+ /* zero bits starting with the least significant */
+ for (i = start; (bitstozero > 0) && (i >= 0); i--, bitstozero -= 32) {
+ if (bitstozero >= 32)
+ addr32[i] = rem32[i] = 0;
+ else {
+ addr32[i] = (ntohl(addr32[i]) >> bitstozero) << bitstozero;
+ rem32[i] = (ntohl(rem32[i]) >> bitstozero) << bitstozero;
+ }
+ }
+
+ /* compare the IP addresses */
+ return memcmp(addr32, rem32, len);
+}
+
+/*
+ * Matches a supplied IP address string against the address the client
+ * connected from (in his_addr). Returns 1 and updates sig if the addresses
+ * match and there hasn't already been a more specific match, zero otherwise.
+ */
+static int better_match(char *addrstr, int *sig)
+{
+ int addr_sig, max_sig = 32;
+ char *ptr;
+ void *addr;
+#ifdef INET6
+ int rval;
+ struct in6_addr in6;
+#else
+ struct in_addr in;
+#endif
+
+ /* look for the optional significance (/CIDR) */
+ if ((ptr = strstr(addrstr, "/")))
+ *ptr = '\0';
+
+#ifdef INET6
+ if (his_addr_family == AF_INET6)
+ max_sig = 128;
+#endif
+
+ if (ptr) {
+ addr_sig = atoi(++ptr);
+ if (addr_sig < 0)
+ addr_sig = 0;
+ else if (addr_sig > max_sig)
+ addr_sig = max_sig;
+ }
+ else
+ addr_sig = max_sig;
+
+ /* return if we already have a more specific match */
+ if (addr_sig < *sig) {
+ if (ptr)
+ *--ptr = '/';
+ return 0;
+ }
+
+#ifdef INET6
+ rval = inet_pton6(addrstr, &in6);
+ if (ptr)
+ *--ptr = '/';
+ if (rval != 1)
+ return 0;
+
+ if (his_addr_family == AF_INET) {
+ /* convert IPv4-mapped IPv6 addresses to IPv4 addresses */
+ if (IN6_IS_ADDR_V4MAPPED(&in6))
+ addr = &in6.s6_addr[12];
+ else
+ return 0;
+ }
+ else
+ addr = &in6.s6_addr;
+#else
+ in.s_addr = inet_addr(addrstr);
+ if (ptr)
+ *--ptr = '/';
+ if ((int)in.s_addr == -1)
+ return 0;
+ addr = &in.s_addr;
+#endif
+
+ if (addr_cmp(addr, addr_sig) == 0) {
+ *sig = addr_sig;
+ return 1;
+ }
+ return 0;
+}
+
+static void update_address(char *externalip, char *addrstr)
+{
+ struct SOCKSTORAGE ext_addr;
+#ifndef INET6
+ struct in_addr in;
+#endif
+
+ /* validate the external IP address string */
+#ifdef INET6
+ SET_SOCK_FAMILY(ext_addr, AF_INET6);
+ if (inet_pton6(externalip, SOCK_ADDR(ext_addr)) != 1)
+ return;
+ if ((his_addr_family == AF_INET) &&
+ !IN6_IS_ADDR_V4MAPPED((struct in6_addr *)SOCK_ADDR(ext_addr)))
+ return;
+#else
+ if ((int)(in.s_addr = inet_addr(externalip)) == -1)
+ return;
+ SET_SOCK_FAMILY(ext_addr, AF_INET);
+ SET_SOCK_ADDR4(ext_addr, in);
+#endif
+
+ if (better_match(addrstr, &vect_sig))
+ vect_addr = ext_addr;
+}
+
+static void update_ports(char *addrstr, char *minport, char *maxport)
+{
+ int min, max;
+
+ min = atoi(minport);
+ max = atoi(maxport);
+
+ /* validate the ports supplied */
+ if ((min > max) || (min < 0) || (max > 65535) || (min == 0 && max != 0)) {
+ syslog(LOG_WARNING, "ftpaccess passive ports entry invalid: %s %s %s", addrstr, minport, maxport);
+ return;
+ }
+
+ if (better_match(addrstr, &port_sig)) {
+ passive_port_min = min;
+ passive_port_max = max;
+ }
+}
+
+int routevector(void)
+{
+ struct aclmember *entry = NULL;
+
+#ifdef INET6
+ if (SOCK_FAMILY(his_addr) == AF_INET6) {
+ if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&(his_addr))->sin6_addr))
+ his_v4mapped = 1;
+ else
+ his_addr_family = AF_INET6;
+ }
+#endif
+
+ while (getaclentry("passive", &entry)) {
+ if (!strcasecmp(ARG0, "address")) {
+ if (!ARG1 || !ARG2)
+ continue;
+ update_address(ARG1, ARG2);
+ }
+ if (!strcasecmp(ARG0, "ports")) {
+ if (!ARG1 || !ARG2 || !ARG3)
+ continue;
+ update_ports(ARG1, ARG2, ARG3);
+ }
+ }
+ return vect_sig != -1;
+}