summaryrefslogtreecommitdiff
path: root/sys/netinet/ip_ftp_pxy.c
diff options
context:
space:
mode:
authorKjell Wooding <kjell@cvs.openbsd.org>2000-04-05 05:35:29 +0000
committerKjell Wooding <kjell@cvs.openbsd.org>2000-04-05 05:35:29 +0000
commit8ae87ff3f4775b57ca50cbc77079b9fbf25de85a (patch)
treea3c0b22826bb81f7f5bf66f5e1f7e9092012b2d1 /sys/netinet/ip_ftp_pxy.c
parentbd065e16cb35ebcfbc352e4d0d3c7553d2e84e89 (diff)
Update to ipf 3.3.12. Most fixes relate to hardening of
in-kernel ftp proxy. See sbin/ipf/HISTORY for details.
Diffstat (limited to 'sys/netinet/ip_ftp_pxy.c')
-rw-r--r--sys/netinet/ip_ftp_pxy.c160
1 files changed, 156 insertions, 4 deletions
diff --git a/sys/netinet/ip_ftp_pxy.c b/sys/netinet/ip_ftp_pxy.c
index 8080ad9f28f..ab947e72aa8 100644
--- a/sys/netinet/ip_ftp_pxy.c
+++ b/sys/netinet/ip_ftp_pxy.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_ftp_pxy.c,v 1.8 2000/03/13 23:40:18 kjell Exp $ */
+/* $OpenBSD: ip_ftp_pxy.c,v 1.9 2000/04/05 05:35:27 kjell Exp $ */
/*
* Simple FTP transparent proxy for in-kernel use. For use with the NAT
@@ -9,6 +9,7 @@ extern kmutex_t ipf_rw;
#endif
#define isdigit(x) ((x) >= '0' && (x) <= '9')
+#define isupper(x) ((unsigned)((x) - 'A') <= 'Z' - 'A')
#define IPF_FTP_PROXY
@@ -16,17 +17,23 @@ extern kmutex_t ipf_rw;
#define IPF_MAXPORTLEN 30
#define IPF_MIN227LEN 39
#define IPF_MAX227LEN 51
+#define IPF_FTPBUFSZ MAX(68,IPF_MAX227LEN) /* This *MUST* be >= 51! */
+ /* 68 is chosen as the minimum datagram size for */
+ /* an unfragmented packet */
int ippr_ftp_init __P((void));
+int ippr_ftp_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
int ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
int ippr_ftp_portmsg __P((fr_info_t *, ip_t *, nat_t *));
int ippr_ftp_pasvmsg __P((fr_info_t *, ip_t *, nat_t *));
+int ippr_ftp_complete __P((char *, size_t));
u_short ipf_ftp_atoi __P((char **));
static frentry_t natfr;
+int ippr_ftp_pasvonly = 0;
/*
@@ -41,6 +48,90 @@ int ippr_ftp_init()
}
+int ippr_ftp_complete(buf, len)
+char *buf;
+size_t len;
+{
+ register char *s, c;
+ register size_t i;
+
+ if (len < 5)
+ return -1;
+ s = buf;
+ c = *s++;
+ i = len - 1;
+
+ if (isdigit(c)) {
+ c = *s++;
+ i--;
+ if (isdigit(c)) {
+ c = *s++;
+ i--;
+ if (isdigit(c)) {
+ c = *s++;
+ i--;
+ if (c != '-' && c != ' ')
+ return -1;
+ } else
+ return -1;
+ } else
+ return -1;
+ } else if (isupper(c)) {
+ c = *s++;
+ i--;
+ if (isupper(c)) {
+ c = *s++;
+ i--;
+ if (isupper(c)) {
+ c = *s++;
+ i--;
+ if (isupper(c)) {
+ c = *s++;
+ i--;
+ if (c != ' ')
+ return -1;
+ } else
+ return -1;
+ } else
+ return -1;
+ } else
+ return -1;
+ } else
+ return -1;
+
+ for (; i && (c = *s); i--, s++) {
+ if ((c == '\r') && (i != 2))
+ return -1;
+ if ((c == '\n') && (i != 1))
+ return -1;
+ else if ((i == 2) && (c != '\r'))
+ return -1;
+ else if ((i == 1) && (c != '\n'))
+ return -1;
+ }
+ return i;
+}
+
+
+int ippr_ftp_new(fin, ip, aps, nat)
+fr_info_t *fin;
+ip_t *ip;
+ap_session_t *aps;
+nat_t *nat;
+{
+ ftpinfo_t *ftp;
+
+ KMALLOC(ftp, ftpinfo_t *);
+ if (ftp == NULL)
+ return -1;
+ aps->aps_data = ftp;
+ aps->aps_psiz = sizeof(ftpinfo_t);
+
+ ftp->ftp_passok = 0;
+ return 0;
+}
+
+
/*
* ipf_ftp_atoi - implement a version of atoi which processes numbers in
* pairs separated by commas (which are expected to be in the range 0 - 255),
@@ -75,13 +166,14 @@ fr_info_t *fin;
ip_t *ip;
nat_t *nat;
{
- char portbuf[IPF_MAXPORTLEN + 1], newbuf[IPF_MAXPORTLEN + 1], *s;
+ char portbuf[IPF_FTPBUFSZ], newbuf[IPF_FTPBUFSZ], *s;
tcphdr_t *tcp, tcph, *tcp2 = &tcph;
size_t nlen = 0, dlen, olen;
u_short a5, a6, sp, dp;
u_int a1, a2, a3, a4;
struct in_addr swip;
int off, inc = 0;
+ ftpinfo_t *ftp;
fr_info_t fi;
nat_t *ipn;
mb_t *m;
@@ -110,7 +202,30 @@ nat_t *nat;
return 0;
portbuf[sizeof(portbuf) - 1] = '\0';
*newbuf = '\0';
- if (!strncmp(portbuf, "PORT ", 5)) {
+
+ /*
+ * Check that a user is progressing through the login ok.
+ */
+ if (ippr_ftp_complete(portbuf, dlen))
+ return 0;
+ ftp = nat->nat_aps->aps_data;
+ switch (ftp->ftp_passok)
+ {
+ case 0 :
+ if (!strncmp(portbuf, "USER ", 5))
+ ftp->ftp_passok = 1;
+ break;
+ case 2 :
+ if (!strncmp(portbuf, "PASS ", 5))
+ ftp->ftp_passok = 3;
+ break;
+ }
+ if (ftp->ftp_passok != 4)
+ return 0;
+ /*
+ * Check for client sending out PORT message.
+ */
+ if (!ippr_ftp_pasvonly && !strncmp(portbuf, "PORT ", 5)) {
if (dlen < IPF_MINPORTLEN)
return 0;
} else
@@ -165,6 +280,7 @@ nat_t *nat;
a4 = a1 & 0xff;
a1 >>= 24;
olen = s - portbuf;
+ /* DO NOT change this to sprintf! */
(void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
"PORT", a1, a2, a3, a4, a5, a6);
@@ -225,6 +341,12 @@ nat_t *nat;
*/
sp = htons(a5 << 8 | a6);
/*
+ * Don't allow the PORT command to specify a port < 1024 due to
+ * security crap.
+ */
+ if (ntohs(sp) < 1024)
+ return 0;
+ /*
* The server may not make the connection back from port 20, but
* it is the most likely so use it here to check for a conflicting
* mapping.
@@ -270,12 +392,13 @@ fr_info_t *fin;
ip_t *ip;
nat_t *nat;
{
- char portbuf[IPF_MAX227LEN + 1], newbuf[IPF_MAX227LEN + 1], *s;
+ char portbuf[IPF_FTPBUFSZ], newbuf[IPF_FTPBUFSZ], *s;
int off, olen, dlen, nlen = 0, inc = 0;
tcphdr_t tcph, *tcp2 = &tcph;
struct in_addr swip, swip2;
u_short a5, a6, dp, sp;
u_int a1, a2, a3, a4;
+ ftpinfo_t *ftp;
tcphdr_t *tcp;
fr_info_t fi;
nat_t *ipn;
@@ -305,6 +428,35 @@ nat_t *nat;
portbuf[sizeof(portbuf) - 1] = '\0';
*newbuf = '\0';
+ /*
+ * Check that a user is progressing through the login ok.
+ * Don't put the switch in one common function because one side
+ * should only see numeric responses and the other commands.
+ */
+ if (ippr_ftp_complete(portbuf, dlen))
+ return 0;
+ ftp = nat->nat_aps->aps_data;
+ switch (ftp->ftp_passok)
+ {
+ case 1 :
+ if (!strncmp(portbuf, "331", 3))
+ ftp->ftp_passok = 2;
+ else if (!strncmp(portbuf, "520", 3))
+ ftp->ftp_passok = 0;
+ break;
+ case 3 :
+ if (!strncmp(portbuf, "230", 3))
+ ftp->ftp_passok = 4;
+ break;
+ default :
+ break;
+ }
+
+ if (ftp->ftp_passok != 4)
+ return 0;
+ /*
+ * Check for PASV reply message.
+ */
if (!strncmp(portbuf, "227 ", 4)) {
if (dlen < IPF_MIN227LEN)
return 0;