summaryrefslogtreecommitdiffstats
path: root/sys/netinet/libalias
diff options
context:
space:
mode:
authorru <ru@FreeBSD.org>2000-06-14 16:09:35 +0000
committerru <ru@FreeBSD.org>2000-06-14 16:09:35 +0000
commit3fa18b8916fe9b25c5597ac51644d51a66e57238 (patch)
treed69af5730e2b0b6f917d67e1eb14b4671f019e7e /sys/netinet/libalias
parent7fdfc2ad2ca6774773c812876c9e0c5b7023d9cb (diff)
downloadFreeBSD-src-3fa18b8916fe9b25c5597ac51644d51a66e57238.zip
FreeBSD-src-3fa18b8916fe9b25c5597ac51644d51a66e57238.tar.gz
- Added support for passive mode FTP by aliasing 227 replies.
It does mean that it is now possible to run passive-mode FTP server behind NAT. - SECURITY: FTP aliasing engine now ensures that: o the segment preceding a PORT/227 segment terminates with a \r\n; o the IP address in the PORT/227 matches the source IP address of the packet; o the port number in the PORT command or 277 reply is greater than or equal to 1024. Submitted by: Erik Salander <erik@whistle.com> Reviewed by: ru
Diffstat (limited to 'sys/netinet/libalias')
-rw-r--r--sys/netinet/libalias/alias_db.c19
-rw-r--r--sys/netinet/libalias/alias_ftp.c222
-rw-r--r--sys/netinet/libalias/alias_local.h2
3 files changed, 228 insertions, 15 deletions
diff --git a/sys/netinet/libalias/alias_db.c b/sys/netinet/libalias/alias_db.c
index e7a8901..211a7c2 100644
--- a/sys/netinet/libalias/alias_db.c
+++ b/sys/netinet/libalias/alias_db.c
@@ -274,6 +274,7 @@ struct alias_link /* Main data structure */
#define LINK_PERMANENT 0x04
#define LINK_PARTIALLY_SPECIFIED 0x03 /* logical-or of first two bits */
#define LINK_UNFIREWALLED 0x08
+#define LINK_LAST_LINE_CRLF_TERMED 0x10
int timestamp; /* Time link was last accessed */
int expire_time; /* Expire time for link */
@@ -1596,6 +1597,7 @@ FindAliasAddress(struct in_addr original_addr)
GetOriginalPort(), GetAliasPort()
SetAckModified(), GetAckModified()
GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq()
+ SetLastLineCrlfTermed(), GetLastLineCrlfTermed()
*/
@@ -1961,6 +1963,23 @@ ClearCheckNewLink(void)
newDefaultLink = 0;
}
+void
+SetLastLineCrlfTermed(struct alias_link *link, int yes)
+{
+
+ if (yes)
+ link->flags |= LINK_LAST_LINE_CRLF_TERMED;
+ else
+ link->flags &= ~LINK_LAST_LINE_CRLF_TERMED;
+}
+
+int
+GetLastLineCrlfTermed(struct alias_link *link)
+{
+
+ return (link->flags & LINK_LAST_LINE_CRLF_TERMED);
+}
+
/* Miscellaneous Functions
diff --git a/sys/netinet/libalias/alias_ftp.c b/sys/netinet/libalias/alias_ftp.c
index 2b4c51e..4976439 100644
--- a/sys/netinet/libalias/alias_ftp.c
+++ b/sys/netinet/libalias/alias_ftp.c
@@ -1,15 +1,16 @@
/*
Alias_ftp.c performs special processing for FTP sessions under
TCP. Specifically, when a PORT command from the client side
- is sent, it is intercepted and modified. The address is changed
- to the gateway machine and an aliasing port is used.
+ or PASV/227 reply from the server is sent, it is intercepted
+ and modified. The address is changed to the gateway machine
+ and an aliasing port is used.
- For this routine to work, the PORT command must fit entirely
+ For this routine to work, the PORT/227 must fit entirely
into a single TCP packet. This is typically the case, but exceptions
can easily be envisioned under the actual specifications.
Probably the most troubling aspect of the approach taken here is
- that the new PORT command will typically be a different length, and
+ that the new PORT/227 will typically be a different length, and
this causes a certain amount of bookkeeping to keep track of the
changes of sequence and acknowledgment numbers, since the client
machine is totally unaware of the modification to the TCP stream.
@@ -37,6 +38,9 @@
local/global/function naming conventions
within the packet aliasing module.
+ Version 3.1: May, 2000 (eds)
+ Add support for passive mode, alias the 227 replies.
+
See HISTORY file for record of revisions.
$FreeBSD$
@@ -54,10 +58,16 @@
#include "alias_local.h"
+#define FTP_CONTROL_PORT_NUMBER 21
+#define MIN_227_REPLY 16
+#define MAX_227_REPLY 128
+
static int ParseFtpPortCommand(char *, int, struct ip *, struct alias_link *, int);
static void ParseFtpEprtCommand(char *, int, struct ip *, struct alias_link *, int);
static void NewFtpPortCommand(struct ip *, struct alias_link *, struct in_addr, u_short, int, int);
+static int ParseFtp227Reply(char *, int, struct ip *, struct alias_link *, int);
+static void NewFtp227Reply(struct ip *, struct alias_link *, struct in_addr, u_short, int);
void
@@ -76,17 +86,27 @@ int maxpacketsize /* The maximum size this packet can grow to (including header
tlen = ntohs(pip->ip_len);
dlen = tlen - hlen;
-/* Return if data length is too long or too short */
- if (dlen<10 || dlen>80)
- return;
-
/* Place string pointer and beginning of data */
sptr = (char *) pip;
sptr += hlen;
+/* When aliasing a client, check for the PORT/EPRT command */
+ if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) {
/* Parse through string using state diagram method */
- if (!ParseFtpPortCommand(sptr, dlen, pip, link, maxpacketsize))
- ParseFtpEprtCommand(sptr, dlen, pip, link, maxpacketsize);
+ if (!ParseFtpPortCommand(sptr, dlen, pip, link, maxpacketsize))
+ ParseFtpEprtCommand(sptr, dlen, pip, link, maxpacketsize);
+ } else {
+ ParseFtp227Reply(sptr, dlen, pip, link, maxpacketsize);
+ }
+
+/* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
+
+ if (dlen) { /* only if there's data */
+ sptr = (char *) pip; /* start over at beginning */
+ tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may have grown */
+ SetLastLineCrlfTermed(link,
+ (sptr[tlen-2] == '\r') && (sptr[tlen-1] == '\n'));
+ }
}
static int
@@ -104,6 +124,10 @@ int maxpacketsize /* The maximum size this packet can grow to (including header
u_long a1, a2, a3, a4;
u_short p1, p2;
+ /* Return if data length is too long or too short */
+ if (dlen<10 || dlen>80)
+ return 0;
+
a1=0; a2=0; a3=0; a4=0; p1=0; p2=0;
state=-4;
for (i=0; i<dlen; i++)
@@ -143,11 +167,13 @@ int maxpacketsize /* The maximum size this packet can grow to (including header
}
}
- if (state == 11)
+ if (state == 11 && GetLastLineCrlfTermed(link))
{
true_port = htons((p1<<8) + p2);
true_addr.s_addr = htonl((a1<<24) + (a2<<16) +(a3<<8) + a4);
- NewFtpPortCommand(pip, link, true_addr, true_port, maxpacketsize, 0);
+ if ((pip->ip_src.s_addr == true_addr.s_addr) &&
+ ((p1<<8) >= IPPORT_RESERVED))
+ NewFtpPortCommand(pip, link, true_addr, true_port, maxpacketsize, 0);
return 1;
}
else
@@ -169,6 +195,10 @@ int maxpacketsize /* The maximum size this packet can grow to (including header
u_long a1, a2, a3, a4;
u_short pt;
+ /* Return if data length is too long or too short */
+ if (dlen<10 || dlen>80)
+ return;
+
a1=0; a2=0; a3=0; a4=0; pt=0;
delim='|'; /* XXX gcc -Wuninitialized */
state=-4;
@@ -226,12 +256,85 @@ int maxpacketsize /* The maximum size this packet can grow to (including header
}
}
- if (state == 13)
+ if (state == 13 && GetLastLineCrlfTermed(link))
{
true_port = htons(pt);
true_addr.s_addr = htonl((a1<<24) + (a2<<16) +(a3<<8) + a4);
- NewFtpPortCommand(pip, link, true_addr, true_port, maxpacketsize, 1);
+ if ((pip->ip_src.s_addr == true_addr.s_addr) &&
+ (pt >= IPPORT_RESERVED))
+ NewFtpPortCommand(pip, link, true_addr, true_port, maxpacketsize, 1);
+ }
+}
+
+static int
+ParseFtp227Reply(char *sptr,
+ int dlen,
+ struct ip *pip, /* IP packet to examine/patch */
+ struct alias_link *link, /* The link to go through (aliased port) */
+ int maxpacketsize) /* The maximum size this packet can grow */
+ /* to (including headers) */
+{
+ struct in_addr true_addr;
+ u_short true_port;
+ char ch;
+ int i, state;
+ u_long a1, a2, a3, a4;
+ u_short p1, p2;
+
+
+ /* Return if wrong size, in case more packet types added later */
+ if (dlen<MIN_227_REPLY || dlen>MAX_227_REPLY)
+ return 0;
+
+ a1=0; a2=0; a3=0; a4=0; p1=0; p2=0;
+ state=-3;
+ for (i=0; i<dlen; i++)
+ {
+ ch = sptr[i];
+ switch (state)
+ {
+ case -3: if (ch == '2') state++; else return 0; break;
+ case -2: if (ch == '2') state++; else return 0; break;
+ case -1: if (ch == '7') state++; else return 0; break;
+
+ case 0 :
+ if (isdigit(ch)) {a1=ch-'0'; state++;} break;
+ case 1 :
+ if (isdigit(ch)) a1=10*a1+ch-'0'; else state++; break;
+ case 2 :
+ if (isdigit(ch)) {a2=ch-'0'; state++;} break;
+ case 3 :
+ if (isdigit(ch)) a2=10*a2+ch-'0'; else state++; break;
+ case 4 :
+ if (isdigit(ch)) {a3=ch-'0'; state++;} break;
+ case 5 :
+ if (isdigit(ch)) a3=10*a3+ch-'0'; else state++; break;
+ case 6 :
+ if (isdigit(ch)) {a4=ch-'0'; state++;} break;
+ case 7 :
+ if (isdigit(ch)) a4=10*a4+ch-'0'; else state++; break;
+ case 8 :
+ if (isdigit(ch)) {p1=ch-'0'; state++;} break;
+ case 9 :
+ if (isdigit(ch)) p1=10*p1+ch-'0'; else state++; break;
+ case 10:
+ if (isdigit(ch)) {p2=ch-'0'; state++;} break;
+ case 11:
+ if (isdigit(ch)) p2=10*p2+ch-'0'; break;
+ }
}
+
+ if (state == 11 && GetLastLineCrlfTermed(link))
+ {
+ true_port = htons((p1<<8) + p2);
+ true_addr.s_addr = htonl((a1<<24) + (a2<<16) +(a3<<8) + a4);
+ if ((pip->ip_src.s_addr == true_addr.s_addr) &&
+ ((p1<<8) >= IPPORT_RESERVED))
+ NewFtp227Reply(pip, link, true_addr, true_port, maxpacketsize);
+ return 1;
+ }
+ else
+ return 0;
}
static void
@@ -331,7 +434,96 @@ NewFtpPortCommand(struct ip *pip,
{
#ifdef DEBUG
fprintf(stderr,
- "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
+ "PacketAlias/HandleFtpOut, PORT: Cannot allocate FTP data port\n");
+#endif
+ }
+}
+
+static void
+NewFtp227Reply(struct ip *pip,
+ struct alias_link *link,
+ struct in_addr true_addr,
+ u_short true_port,
+ int maxpacketsize)
+{
+ struct alias_link *ftp_link;
+
+/* Establish link to address and port found in 227 reply */
+ ftp_link = FindUdpTcpOut(true_addr, GetDestAddress(link),
+ true_port, 0, IPPROTO_TCP);
+
+ if (ftp_link != NULL)
+ {
+ int slen, hlen, tlen, dlen;
+ struct tcphdr *tc;
+
+/* Calculate data length of TCP packet */
+ tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
+ hlen = (pip->ip_hl + tc->th_off) << 2;
+ tlen = ntohs(pip->ip_len);
+ dlen = tlen - hlen;
+
+/* Create new 227 reply */
+ {
+ char stemp[MAX_227_REPLY+1];
+ char *sptr;
+ u_short alias_port;
+ u_char *ptr;
+ int a1, a2, a3, a4, p1, p2;
+ struct in_addr alias_address;
+
+/* Decompose alias address into quad format */
+ alias_address = GetAliasAddress(link);
+ ptr = (u_char *) &alias_address.s_addr;
+ a1 = *ptr++; a2=*ptr++; a3=*ptr++; a4=*ptr;
+
+/* Decompose alias port into pair format */
+ alias_port = GetAliasPort(ftp_link);
+ ptr = (char *) &alias_port;
+ p1 = *ptr++; p2=*ptr;
+
+/* Generate command string */
+ sprintf(stemp, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
+ a1,a2,a3,a4,p1,p2);
+
+/* Save string length for IP header modification */
+ slen = strlen(stemp);
+
+/* Copy modified buffer into IP packet */
+ sptr = (char *) pip; sptr += hlen;
+ strncpy(sptr, stemp, maxpacketsize-hlen);
+ }
+
+/* Save information regarding modified seq and ack numbers */
+ {
+ int delta;
+
+ SetAckModified(link);
+ delta = GetDeltaSeqOut(pip, link);
+ AddSeq(pip, link, delta+slen-dlen);
+ }
+
+/* Revise IP header */
+ {
+ u_short new_len;
+
+ new_len = htons(hlen + slen);
+ DifferentialChecksum(&pip->ip_sum,
+ &new_len,
+ &pip->ip_len,
+ 1);
+ pip->ip_len = new_len;
+ }
+
+/* Compute TCP checksum for revised packet */
+ tc->th_sum = 0;
+ tc->th_sum = TcpChecksum(pip);
+ }
+ else
+ {
+#ifdef DEBUG
+ fprintf(stderr,
+ "PacketAlias/HandleFtpOut, 227: Cannot allocate FTP data port\n");
#endif
}
}
diff --git a/sys/netinet/libalias/alias_local.h b/sys/netinet/libalias/alias_local.h
index ca93151..783f6b2 100644
--- a/sys/netinet/libalias/alias_local.h
+++ b/sys/netinet/libalias/alias_local.h
@@ -140,6 +140,8 @@ int GetDeltaSeqOut(struct ip *, struct alias_link *);
void AddSeq(struct ip *, struct alias_link *, int);
void SetExpire(struct alias_link *, int);
void ClearCheckNewLink(void);
+void SetLastLineCrlfTermed(struct alias_link *, int);
+int GetLastLineCrlfTermed(struct alias_link *);
#ifndef NO_FW_PUNCH
void PunchFWHole(struct alias_link *);
#endif
OpenPOWER on IntegriCloud