diff options
author | Kjell Wooding <kjell@cvs.openbsd.org> | 2000-04-05 05:35:29 +0000 |
---|---|---|
committer | Kjell Wooding <kjell@cvs.openbsd.org> | 2000-04-05 05:35:29 +0000 |
commit | 8ae87ff3f4775b57ca50cbc77079b9fbf25de85a (patch) | |
tree | a3c0b22826bb81f7f5bf66f5e1f7e9092012b2d1 /sys/netinet/ip_ftp_pxy.c | |
parent | bd065e16cb35ebcfbc352e4d0d3c7553d2e84e89 (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.c | 160 |
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; |