diff options
Diffstat (limited to 'sys/contrib/ipfilter/netinet/ip_ftp_pxy.c')
-rw-r--r-- | sys/contrib/ipfilter/netinet/ip_ftp_pxy.c | 1685 |
1 files changed, 1224 insertions, 461 deletions
diff --git a/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c b/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c index f56a690..ff83976 100644 --- a/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c +++ b/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c @@ -1,7 +1,7 @@ /* $FreeBSD$ */ /* - * Copyright (C) 1997-2003 by Darren Reed + * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * @@ -15,6 +15,7 @@ #define IPF_FTP_PROXY #define IPF_MINPORTLEN 18 +#define IPF_MINEPRTLEN 20 #define IPF_MAXPORTLEN 30 #define IPF_MIN227LEN 39 #define IPF_MAX227LEN 51 @@ -38,93 +39,197 @@ #define FTPXY_PASS_2 14 #define FTPXY_PAOK_2 15 +#define FTPXY_JUNK_OK 0 +#define FTPXY_JUNK_BAD 1 /* Ignore all commands for this connection */ +#define FTPXY_JUNK_EOL 2 /* consume the rest of this line only */ +#define FTPXY_JUNK_CONT 3 /* Saerching for next numeric */ + /* * Values for FTP commands. Numerics cover 0-999 */ #define FTPXY_C_PASV 1000 +#define FTPXY_C_PORT 1001 +#define FTPXY_C_EPSV 1002 +#define FTPXY_C_EPRT 1003 + + +typedef struct ipf_ftp_softc_s { + int ipf_p_ftp_pasvonly; + /* Do not require logins before transfers */ + int ipf_p_ftp_insecure; + int ipf_p_ftp_pasvrdr; + /* PASV must be last command prior to 227 */ + int ipf_p_ftp_forcepasv; + int ipf_p_ftp_debug; + int ipf_p_ftp_single_xfer; + void *ipf_p_ftp_tune; +} ipf_ftp_softc_t; + + +void ipf_p_ftp_main_load __P((void)); +void ipf_p_ftp_main_unload __P((void)); +void *ipf_p_ftp_soft_create __P((ipf_main_softc_t *)); +void ipf_p_ftp_soft_destroy __P((ipf_main_softc_t *, void *)); + +int ipf_p_ftp_client __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_complete __P((char *, size_t)); +int ipf_p_ftp_in __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_ftp_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +void ipf_p_ftp_del __P((ipf_main_softc_t *, ap_session_t *)); +int ipf_p_ftp_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); +int ipf_p_ftp_pasv __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_epsv __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_port __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_process __P((ipf_ftp_softc_t *, fr_info_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_server __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_valid __P((ipf_ftp_softc_t *, ftpinfo_t *, int, char *, size_t)); +int ipf_p_ftp_server_valid __P((ipf_ftp_softc_t *, ftpside_t *, char *, + size_t)); +int ipf_p_ftp_client_valid __P((ipf_ftp_softc_t *, ftpside_t *, char *, + size_t)); +u_short ipf_p_ftp_atoi __P((char **)); +int ipf_p_ftp_pasvreply __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, u_int, char *, char *)); +int ipf_p_ftp_eprt __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_eprt4 __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_eprt6 __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int)); +int ipf_p_ftp_addport __P((ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, + ftpinfo_t *, int, int, int)); +void ipf_p_ftp_setpending __P((ipf_main_softc_t *, ftpinfo_t *)); -int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); -int ippr_ftp_complete __P((char *, size_t)); -int ippr_ftp_in __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_ftp_init __P((void)); -void ippr_ftp_fini __P((void)); -int ippr_ftp_new __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_ftp_out __P((fr_info_t *, ap_session_t *, nat_t *)); -int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); -int ippr_ftp_epsv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); -int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); -int ippr_ftp_process __P((fr_info_t *, nat_t *, ftpinfo_t *, int)); -int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); -int ippr_ftp_valid __P((ftpinfo_t *, int, char *, size_t)); -int ippr_ftp_server_valid __P((ftpside_t *, char *, size_t)); -int ippr_ftp_client_valid __P((ftpside_t *, char *, size_t)); -u_short ippr_ftp_atoi __P((char **)); -int ippr_ftp_pasvreply __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, - u_int, char *, char *, u_int)); - - -int ftp_proxy_init = 0; -int ippr_ftp_pasvonly = 0; -int ippr_ftp_insecure = 0; /* Do not require logins before transfers */ -int ippr_ftp_pasvrdr = 0; -int ippr_ftp_forcepasv = 0; /* PASV must be last command prior to 227 */ -#if defined(_KERNEL) -int ippr_ftp_debug = 0; -#else -int ippr_ftp_debug = 2; -#endif /* - * 1 - security - * 2 - errors - * 3 - error debugging - * 4 - parsing errors - * 5 - parsing info - * 6 - parsing debug + * Debug levels */ - +#define DEBUG_SECURITY 0x01 +#define DEBUG_ERROR 0x02 +#define DEBUG_INFO 0x04 +#define DEBUG_PARSE_ERR 0x08 +#define DEBUG_PARSE_INFO 0x10 +#define DEBUG_PARSE 0x20 + +static int ipf_p_ftp_proxy_init = 0; static frentry_t ftppxyfr; -static ipftuneable_t ftptune = { - { &ippr_ftp_debug }, - "ippr_ftp_debug", - 0, - 10, - sizeof(ippr_ftp_debug), - 0, - NULL +static ipftuneable_t ipf_ftp_tuneables[] = { + { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_debug) }, + "ftp_debug", 0, 0x7f, + stsizeof(ipf_ftp_softc_t, ipf_p_ftp_debug), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly) }, + "ftp_pasvonly", 0, 1, + stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_insecure) }, + "ftp_insecure", 0, 1, + stsizeof(ipf_ftp_softc_t, ipf_p_ftp_insecure), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr) }, + "ftp_pasvrdr", 0, 1, + stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv) }, + "ftp_forcepasv", 0, 1, + stsizeof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv), + 0, NULL, NULL }, + { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer) }, + "ftp_single_xfer", 0, 1, + stsizeof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer), + 0, NULL, NULL }, + { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } }; -/* - * Initialize local structures. - */ -int ippr_ftp_init() +void +ipf_p_ftp_main_load() { bzero((char *)&ftppxyfr, sizeof(ftppxyfr)); ftppxyfr.fr_ref = 1; ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; - MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex"); - ftp_proxy_init = 1; - (void) fr_addipftune(&ftptune); - return 0; + MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex"); + ipf_p_ftp_proxy_init = 1; } -void ippr_ftp_fini() +void +ipf_p_ftp_main_unload() { - (void) fr_delipftune(&ftptune); - if (ftp_proxy_init == 1) { + if (ipf_p_ftp_proxy_init == 1) { MUTEX_DESTROY(&ftppxyfr.fr_lock); - ftp_proxy_init = 0; + ipf_p_ftp_proxy_init = 0; } } -int ippr_ftp_new(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +/* + * Initialize local structures. + */ +void * +ipf_p_ftp_soft_create(softc) + ipf_main_softc_t *softc; +{ + ipf_ftp_softc_t *softf; + + KMALLOC(softf, ipf_ftp_softc_t *); + if (softf == NULL) + return NULL; + + bzero((char *)softf, sizeof(*softf)); +#if defined(_KERNEL) + softf->ipf_p_ftp_debug = 0; +#else + softf->ipf_p_ftp_debug = DEBUG_PARSE_ERR; +#endif + softf->ipf_p_ftp_forcepasv = 1; + + softf->ipf_p_ftp_tune = ipf_tune_array_copy(softf, + sizeof(ipf_ftp_tuneables), + ipf_ftp_tuneables); + if (softf->ipf_p_ftp_tune == NULL) { + ipf_p_ftp_soft_destroy(softc, softf); + return NULL; + } + if (ipf_tune_array_link(softc, softf->ipf_p_ftp_tune) == -1) { + ipf_p_ftp_soft_destroy(softc, softf); + return NULL; + } + + return softf; +} + + +void +ipf_p_ftp_soft_destroy(softc, arg) + ipf_main_softc_t *softc; + void *arg; +{ + ipf_ftp_softc_t *softf = arg; + + if (softf->ipf_p_ftp_tune != NULL) { + ipf_tune_array_unlink(softc, softf->ipf_p_ftp_tune); + KFREES(softf->ipf_p_ftp_tune, sizeof(ipf_ftp_tuneables)); + softf->ipf_p_ftp_tune = NULL; + } + + KFREE(softf); +} + + +int +ipf_p_ftp_new(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { ftpinfo_t *ftp; ftpside_t *f; @@ -133,11 +238,12 @@ nat_t *nat; if (ftp == NULL) return -1; - fin = fin; /* LINT */ nat = nat; /* LINT */ aps->aps_data = ftp; aps->aps_psiz = sizeof(ftpinfo_t); + aps->aps_sport = htons(fin->fin_sport); + aps->aps_dport = htons(fin->fin_dport); bzero((char *)ftp, sizeof(*ftp)); f = &ftp->ftp_side[0]; @@ -152,25 +258,53 @@ nat_t *nat; } -int ippr_ftp_port(fin, ip, nat, f, dlen) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpside_t *f; -int dlen; +void +ipf_p_ftp_setpending(ipf_main_softc_t *softc, ftpinfo_t *ftp) +{ + if (ftp->ftp_pendnat != NULL) + ipf_nat_setpending(softc, ftp->ftp_pendnat); + + if (ftp->ftp_pendstate != NULL) { + READ_ENTER(&softc->ipf_state); + ipf_state_setpending(softc, ftp->ftp_pendstate); + RWLOCK_EXIT(&softc->ipf_state); + } +} + + +void +ipf_p_ftp_del(softc, aps) + ipf_main_softc_t *softc; + ap_session_t *aps; +{ + ftpinfo_t *ftp; + + ftp = aps->aps_data; + if (ftp != NULL) + ipf_p_ftp_setpending(softc, ftp); +} + + +int +ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; { - tcphdr_t *tcp, tcph, *tcp2 = &tcph; char newbuf[IPF_FTPBUFSZ], *s; - struct in_addr swip, swip2; u_int a1, a2, a3, a4; - int inc, off, flags; u_short a5, a6, sp; size_t nlen, olen; - fr_info_t fi; - nat_t *nat2; + tcphdr_t *tcp; + int inc, off; + ftpside_t *f; mb_t *m; m = fin->fin_m; + f = &ftp->ftp_side[0]; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; @@ -178,8 +312,10 @@ int dlen; * Check for client sending out PORT message. */ if (dlen < IPF_MINPORTLEN) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:dlen(%d) < IPF_MINPORTLEN\n", + DT3(ftp_PORT_error_dlen, nat_t *, nat, ftpside_t *, f, + u_int, dlen); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) + printf("ipf_p_ftp_port:dlen(%d) < IPF_MINPORTLEN\n", dlen); return 0; } @@ -190,16 +326,18 @@ int dlen; /* * Pick out the address components, two at a time. */ - a1 = ippr_ftp_atoi(&s); + a1 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 1); + DT2(ftp_PORT_error_atoi_1, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 1); return 0; } - a2 = ippr_ftp_atoi(&s); + a2 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 2); + DT2(ftp_PORT_error_atoi_2, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 2); return 0; } @@ -210,18 +348,21 @@ int dlen; a1 <<= 16; a1 |= a2; if (((nat->nat_dir == NAT_OUTBOUND) && - (a1 != ntohl(nat->nat_inip.s_addr))) || + (a1 != ntohl(nat->nat_osrcaddr))) || ((nat->nat_dir == NAT_INBOUND) && - (a1 != ntohl(nat->nat_oip.s_addr)))) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_port:%s != nat->nat_inip\n", "a1"); + (a1 != ntohl(nat->nat_nsrcaddr)))) { + DT3(ftp_PORT_error_address, nat_t *, nat, ftpside_t *, f, + u_int, a1); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_port:%s != nat->nat_inip\n", "a1"); return APR_ERR(1); } - a5 = ippr_ftp_atoi(&s); + a5 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 3); + DT2(ftp_PORT_error_atoi_3, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 3); return 0; } if (*s == ')') @@ -232,34 +373,31 @@ int dlen; */ if (*s == '\n') s--; - if ((*s == '\r') && (*(s + 1) == '\n')) { - s += 2; - a6 = a5 & 0xff; - } else { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_port:missing %s\n", "cr-lf"); + if ((*s != '\r') || (*(s + 1) != '\n')) { + DT2(ftp_PORT_error_no_crlf, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_port:missing %s\n", "cr-lf"); return 0; } + s += 2; + a6 = a5 & 0xff; + /* + * Calculate the source port. Verification of > 1024 is in + * ipf_p_ftp_addport. + */ a5 >>= 8; a5 &= 0xff; sp = a5 << 8 | a6; - /* - * Don't allow the PORT command to specify a port < 1024 due to - * security crap. - */ - if (sp < 1024) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_port:sp(%d) < 1024\n", sp); - return 0; - } + /* * Calculate new address parts for PORT command */ if (nat->nat_dir == NAT_INBOUND) - a1 = ntohl(nat->nat_oip.s_addr); + a1 = ntohl(nat->nat_ndstaddr); else a1 = ntohl(ip->ip_src.s_addr); + a1 = ntohl(ip->ip_src.s_addr); a2 = (a1 >> 16) & 0xff; a3 = (a1 >> 8) & 0xff; a4 = a1 & 0xff; @@ -276,117 +414,213 @@ int dlen; nlen = strlen(newbuf); inc = nlen - olen; - if ((inc + ip->ip_len) > 65535) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_port:inc(%d) + ip->ip_len > 65535\n", + if ((inc + fin->fin_plen) > 65535) { + DT3(ftp_PORT_error_inc, nat_t *, nat, ftpside_t *, f, + int, inc); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_port:inc(%d) + ip->ip_len > 65535\n", inc); return 0; } #if !defined(_KERNEL) - bcopy(newbuf, MTOD(m, char *) + off, nlen); + M_ADJ(m, inc); #else -# if defined(MENTAT) - if (inc < 0) - (void)adjmsg(m, inc); -# else /* defined(MENTAT) */ /* * m_adj takes care of pkthdr.len, if required and treats inc<0 to * mean remove -len bytes from the end of the packet. * The mbuf chain will be extended if necessary by m_copyback(). */ if (inc < 0) - m_adj(m, inc); -# endif /* defined(MENTAT) */ + M_ADJ(m, inc); #endif /* !defined(_KERNEL) */ COPYBACK(m, off, nlen, newbuf); + fin->fin_flx |= FI_DOCKSUM; if (inc != 0) { - ip->ip_len += inc; - fin->fin_dlen += inc; fin->fin_plen += inc; + ip->ip_len = htons(fin->fin_plen); + fin->fin_dlen += inc; + } + + f->ftps_cmd = FTPXY_C_PORT; + return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, sp, inc); +} + + +int +ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, nport, inc) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen, nport, inc; +{ + tcphdr_t tcph, *tcp2 = &tcph; + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; + int direction; + fr_info_t fi; + ipnat_t *ipn; + nat_t *nat2; + u_short sp; + int flags; + + softc = fin->fin_main_soft; + softn = softc->ipf_nat_soft; + + if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL)) { + if (softf->ipf_p_ftp_single_xfer != 0) { + DT2(ftp_PORT_error_add_active, nat_t *, nat, + ftpinfo_t *, ftp); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_addport:xfer active %p/%p\n", + ftp->ftp_pendnat, ftp->ftp_pendstate); + return 0; + } + ipf_p_ftp_setpending(softc, ftp); } /* + * Add skeleton NAT entry for connection which will come back the + * other way. + */ + sp = nport; + /* + * Don't allow the PORT command to specify a port < 1024 due to + * security risks. + */ + if (sp < 1024) { + DT3(ftp_PORT_error_port, nat_t *, nat, ftpinfo_t *, ftp, + u_int, sp); + if (softf->ipf_p_ftp_debug & DEBUG_SECURITY) + printf("ipf_p_ftp_addport:sp(%d) < 1024\n", sp); + 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. */ bcopy((char *)fin, (char *)&fi, sizeof(fi)); - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_flx |= FI_IGNORE; fi.fin_data[0] = sp; fi.fin_data[1] = fin->fin_data[1] - 1; + fi.fin_src6 = nat->nat_ndst6; + fi.fin_dst6 = nat->nat_nsrc6; + + if (nat->nat_v[0] == 6) { +#ifndef USE_INET6 + return APR_INC(inc); +#endif + } + /* * Add skeleton NAT entry for connection which will come back the * other way. */ - if (nat->nat_dir == NAT_OUTBOUND) - nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, - nat->nat_inip, nat->nat_oip); - else - nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, - nat->nat_inip, nat->nat_oip); - if (nat2 == NULL) { - int slen; - - slen = ip->ip_len; - ip->ip_len = fin->fin_hlen + sizeof(*tcp2); - bzero((char *)tcp2, sizeof(*tcp2)); - tcp2->th_win = htons(8192); - tcp2->th_sport = htons(sp); - TCP_OFF_A(tcp2, 5); - tcp2->th_flags = TH_SYN; - tcp2->th_dport = 0; /* XXX - don't specify remote port */ - fi.fin_data[1] = 0; - fi.fin_dlen = sizeof(*tcp2); - fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); - fi.fin_dp = (char *)tcp2; - fi.fin_fr = &ftppxyfr; - fi.fin_out = nat->nat_dir; - fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; - swip = ip->ip_src; - swip2 = ip->ip_dst; +#ifdef USE_INET6 + if (nat->nat_v[0] == 6) { if (nat->nat_dir == NAT_OUTBOUND) { - fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; - ip->ip_src = nat->nat_inip; - } else if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_saddr = nat->nat_oip.s_addr; - ip->ip_src = nat->nat_oip; + nat2 = ipf_nat6_outlookup(&fi, IPN_TCP|NAT_SEARCH, + nat->nat_pr[1], + &nat->nat_osrc6.in6, + &nat->nat_odst6.in6); + } else { + nat2 = ipf_nat6_inlookup(&fi, IPN_TCP|NAT_SEARCH, + nat->nat_pr[0], + &nat->nat_odst6.in6, + &nat->nat_osrc6.in6); } - - flags = NAT_SLAVE|IPN_TCP|SI_W_DPORT; - if (nat->nat_dir == NAT_INBOUND) - flags |= NAT_NOTRULEPORT; - nat2 = nat_new(&fi, nat->nat_ptr, NULL, flags, nat->nat_dir); - - if (nat2 != NULL) { - (void) nat_proto(&fi, nat2, IPN_TCP); - nat_update(&fi, nat2, nat->nat_ptr); - fi.fin_ifp = NULL; - if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_daddr = nat->nat_inip.s_addr; - ip->ip_dst = nat->nat_inip; - } - (void) fr_addstate(&fi, NULL, SI_W_DPORT); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + } else +#endif + { + if (nat->nat_dir == NAT_OUTBOUND) { + nat2 = ipf_nat_outlookup(&fi, IPN_TCP|NAT_SEARCH, + nat->nat_pr[1], + nat->nat_osrcip, + nat->nat_odstip); + } else { + nat2 = ipf_nat_inlookup(&fi, IPN_TCP|NAT_SEARCH, + nat->nat_pr[0], + nat->nat_odstip, + nat->nat_osrcip); } - ip->ip_len = slen; - ip->ip_src = swip; - ip->ip_dst = swip2; } + if (nat2 != NULL) + return APR_INC(inc); + + ipn = ipf_proxy_rule_rev(nat); + if (ipn == NULL) + return APR_ERR(1); + ipn->in_use = 0; + + fi.fin_fr = &ftppxyfr; + fi.fin_dp = (char *)tcp2; + fi.fin_dlen = sizeof(*tcp2); + fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); + fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; + fi.fin_data[1] = sp; + fi.fin_data[0] = 0; + + bzero((char *)tcp2, sizeof(*tcp2)); + tcp2->th_sport = 0; + tcp2->th_dport = htons(sp); + + tcp2->th_win = htons(8192); + TCP_OFF_A(tcp2, 5); + tcp2->th_flags = TH_SYN; + + if (nat->nat_dir == NAT_INBOUND) { + fi.fin_out = 1; + direction = NAT_OUTBOUND; + } else { + fi.fin_out = 0; + direction = NAT_INBOUND; + } + flags = SI_W_SPORT|NAT_SLAVE|IPN_TCP; + + MUTEX_ENTER(&softn->ipf_nat_new); + if (nat->nat_v[0] == 6) { +#ifdef USE_INET6 + nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat, flags, + direction); +#endif + } else { + nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, flags, + direction); + } + MUTEX_EXIT(&softn->ipf_nat_new); + + if (nat2 == NULL) { + KFREES(ipn, ipn->in_size); + return APR_ERR(1); + } + + (void) ipf_nat_proto(&fi, nat2, IPN_TCP); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); + fi.fin_ifp = NULL; + if (nat2->nat_dir == NAT_INBOUND) + fi.fin_dst6 = nat->nat_osrc6; + if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate, + SI_W_SPORT) != 0) + ipf_nat_setpending(softc, nat2); + return APR_INC(inc); } -int ippr_ftp_client(fin, ip, nat, ftp, dlen) -fr_info_t *fin; -nat_t *nat; -ftpinfo_t *ftp; -ip_t *ip; -int dlen; +int +ipf_p_ftp_client(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + nat_t *nat; + ftpinfo_t *ftp; + ip_t *ip; + int dlen; { char *rptr, *wptr, cmd[6], c; ftpside_t *f; @@ -408,6 +642,7 @@ int dlen; cmd[i] = '\0'; ftp->ftp_incok = 0; + DT2(ftp_client_command, char [], cmd, int, ftp->ftp_passok); if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) { if (ftp->ftp_passok == FTPXY_ADOK_1 || ftp->ftp_passok == FTPXY_AUOK_1) { @@ -437,14 +672,24 @@ int dlen; !strncmp(cmd, "ACCT ", 5)) { ftp->ftp_passok = FTPXY_ACCT_1; ftp->ftp_incok = 1; - } else if ((ftp->ftp_passok == FTPXY_GO) && !ippr_ftp_pasvonly && + } else if ((ftp->ftp_passok == FTPXY_GO) && + !softf->ipf_p_ftp_pasvonly && !strncmp(cmd, "PORT ", 5)) { - inc = ippr_ftp_port(fin, ip, nat, f, dlen); - } else if (ippr_ftp_insecure && !ippr_ftp_pasvonly && + inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen); + } else if ((ftp->ftp_passok == FTPXY_GO) && + !softf->ipf_p_ftp_pasvonly && + !strncmp(cmd, "EPRT ", 5)) { + inc = ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen); + } else if (softf->ipf_p_ftp_insecure && + !softf->ipf_p_ftp_pasvonly && !strncmp(cmd, "PORT ", 5)) { - inc = ippr_ftp_port(fin, ip, nat, f, dlen); + inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen); } + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) + printf("ipf_p_ftp_client: cmd[%s] passok %d incok %d inc %d\n", + cmd, ftp->ftp_passok, ftp->ftp_incok, inc); + DT2(ftp_client_passok, char *, cmd, int, ftp->ftp_passok); while ((*rptr++ != '\n') && (rptr < wptr)) ; f->ftps_rptr = rptr; @@ -452,12 +697,14 @@ int dlen; } -int ippr_ftp_pasv(fin, ip, nat, ftp, dlen) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpinfo_t *ftp; -int dlen; +int +ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; { u_int a1, a2, a3, a4, data_ip; char newbuf[IPF_FTPBUFSZ]; @@ -466,11 +713,12 @@ int dlen; ftpside_t *f; char *s; - if (ippr_ftp_forcepasv != 0 && - ftp->ftp_side[0].ftps_cmds != FTPXY_C_PASV) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_pasv:ftps_cmds(%d) != FTPXY_C_PASV\n", - ftp->ftp_side[0].ftps_cmds); + if ((softf->ipf_p_ftp_forcepasv != 0) && + (ftp->ftp_side[0].ftps_cmd != FTPXY_C_PASV)) { + DT2(ftp_PASV_error_state, nat_t *, nat, ftpinfo_t *, ftp); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_pasv:ftps_cmd(%d) != FTPXY_C_PASV\n", + ftp->ftp_side[0].ftps_cmd); return 0; } @@ -481,14 +729,17 @@ int dlen; * Check for PASV reply message. */ if (dlen < IPF_MIN227LEN) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n", + DT3(ftp_PASV_error_short, nat_t *, nat, ftpinfo_t *, ftp, + int, dlen); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n", dlen); return 0; } else if (strncmp(f->ftps_rptr, "227 Entering Passive Mod", PASV_REPLEN)) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_pasv:%d reply wrong\n", 227); + DT2(ftp_PASV_error_string, nat_t *, nat, ftpinfo_t *, ftp); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_pasv:%d reply wrong\n", 227); return 0; } @@ -509,16 +760,18 @@ int dlen; /* * Pick out the address components, two at a time. */ - a1 = ippr_ftp_atoi(&s); + a1 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 1); + DT2(ftp_PASV_error_atoi_1, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 1); return 0; } - a2 = ippr_ftp_atoi(&s); + a2 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 2); + DT2(ftp_PASV_error_atoi_2, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 2); return 0; } @@ -530,18 +783,21 @@ int dlen; a1 |= a2; if (((nat->nat_dir == NAT_INBOUND) && - (a1 != ntohl(nat->nat_inip.s_addr))) || + (a1 != ntohl(nat->nat_ndstaddr))) || ((nat->nat_dir == NAT_OUTBOUND) && - (a1 != ntohl(nat->nat_oip.s_addr)))) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_pasv:%s != nat->nat_oip\n", "a1"); + (a1 != ntohl(nat->nat_odstaddr)))) { + DT3(ftp_PASV_error_address, nat_t *, nat, ftpside_t *, f, + u_int, a1); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_pasv:%s != nat->nat_oip\n", "a1"); return 0; } - a5 = ippr_ftp_atoi(&s); + a5 = ipf_p_ftp_atoi(&s); if (s == NULL) { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 3); + DT2(ftp_PASV_error_atoi_3, nat_t *, nat, ftpside_t *, f); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 3); return 0; } @@ -554,13 +810,13 @@ int dlen; /* * check for CR-LF at the end. */ - if ((*s == '\r') && (*(s + 1) == '\n')) { - s += 2; - } else { - if (ippr_ftp_debug > 1) - printf("ippr_ftp_pasv:missing %s", "cr-lf\n"); + if ((*s != '\r') || (*(s + 1) != '\n')) { + DT(pasv_missing_crlf); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_pasv:missing %s", "cr-lf\n"); return 0; } + s += 2; a6 = a5 & 0xff; a5 >>= 8; @@ -568,7 +824,7 @@ int dlen; * Calculate new address parts for 227 reply */ if (nat->nat_dir == NAT_INBOUND) { - data_ip = nat->nat_outip.s_addr; + data_ip = nat->nat_odstaddr; a1 = ntohl(data_ip); } else data_ip = htonl(a1); @@ -587,156 +843,165 @@ int dlen; "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4, a5, a6, brackets[1]); #endif - return ippr_ftp_pasvreply(fin, ip, nat, f, (a5 << 8 | a6), - newbuf, s, data_ip); + return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (a5 << 8 | a6), + newbuf, s); } -int ippr_ftp_pasvreply(fin, ip, nat, f, port, newmsg, s, data_ip) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpside_t *f; -u_int port; -char *newmsg; -char *s; -u_int data_ip; +int +ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, port, newmsg, s) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + u_int port; + char *newmsg; + char *s; { - int inc, off, nflags, sflags; + int inc, off, nflags; tcphdr_t *tcp, tcph, *tcp2; - struct in_addr swip, swip2; - struct in_addr data_addr; + ipf_main_softc_t *softc; + ipf_nat_softc_t *softn; size_t nlen, olen; +#ifdef USE_INET6 + ip6_t *ip6; +#endif + ipnat_t *ipn; fr_info_t fi; + ftpside_t *f; nat_t *nat2; mb_t *m; + softc = fin->fin_main_soft; + softn = softc->ipf_nat_soft; + + if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL)) + ipf_p_ftp_setpending(softc, ftp); + m = fin->fin_m; tcp = (tcphdr_t *)fin->fin_dp; off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; - data_addr.s_addr = data_ip; tcp2 = &tcph; inc = 0; - + f = &ftp->ftp_side[1]; olen = s - f->ftps_rptr; nlen = strlen(newmsg); inc = nlen - olen; - if ((inc + ip->ip_len) > 65535) { - if (ippr_ftp_debug > 0) - printf("ippr_ftp_pasv:inc(%d) + ip->ip_len > 65535\n", + if ((inc + fin->fin_plen) > 65535) { + DT3(ftp_PASV_error_inc, nat_t *, nat, ftpside_t *, f, + int, inc); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_pasv:inc(%d) + ip->ip_len > 65535\n", inc); return 0; } -#if !defined(_KERNEL) - bcopy(newmsg, MTOD(m, char *) + off, nlen); -#else -# if defined(MENTAT) - if (inc < 0) - (void)adjmsg(m, inc); -# else /* defined(MENTAT) */ - /* - * m_adj takes care of pkthdr.len, if required and treats inc<0 to - * mean remove -len bytes from the end of the packet. - * The mbuf chain will be extended if necessary by m_copyback(). - */ - if (inc < 0) - m_adj(m, inc); -# endif /* defined(MENTAT) */ -#endif /* !defined(_KERNEL) */ - COPYBACK(m, off, nlen, newmsg); - - if (inc != 0) { - ip->ip_len += inc; - fin->fin_dlen += inc; - fin->fin_plen += inc; - } + ipn = ipf_proxy_rule_fwd(nat); + if (ipn == NULL) + return APR_ERR(1); + ipn->in_use = 0; /* * Add skeleton NAT entry for connection which will come back the * other way. */ + bzero((char *)tcp2, sizeof(*tcp2)); bcopy((char *)fin, (char *)&fi, sizeof(fi)); - fi.fin_state = NULL; - fi.fin_nat = NULL; fi.fin_flx |= FI_IGNORE; fi.fin_data[0] = 0; fi.fin_data[1] = port; nflags = IPN_TCP|SI_W_SPORT; - if (ippr_ftp_pasvrdr && f->ftps_ifp) - nflags |= SI_W_DPORT; - if (nat->nat_dir == NAT_OUTBOUND) - nat2 = nat_outlookup(&fi, nflags|NAT_SEARCH, - nat->nat_p, nat->nat_inip, nat->nat_oip); + + fi.fin_fr = &ftppxyfr; + fi.fin_dp = (char *)tcp2; + fi.fin_out = 1 - fin->fin_out; + fi.fin_dlen = sizeof(*tcp2); + fi.fin_src6 = nat->nat_osrc6; + fi.fin_dst6 = nat->nat_odst6; + fi.fin_plen = fi.fin_hlen + sizeof(*tcp); + fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; + + TCP_OFF_A(tcp2, 5); + tcp2->th_flags = TH_SYN; + tcp2->th_win = htons(8192); + tcp2->th_dport = htons(port); + + MUTEX_ENTER(&softn->ipf_nat_new); +#ifdef USE_INET6 + if (nat->nat_v[0] == 6) + nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat, + nflags, nat->nat_dir); else - nat2 = nat_inlookup(&fi, nflags|NAT_SEARCH, - nat->nat_p, nat->nat_inip, nat->nat_oip); +#endif + nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, + nflags, nat->nat_dir); + MUTEX_EXIT(&softn->ipf_nat_new); + if (nat2 == NULL) { - int slen; - - slen = ip->ip_len; - ip->ip_len = fin->fin_hlen + sizeof(*tcp2); - bzero((char *)tcp2, sizeof(*tcp2)); - tcp2->th_win = htons(8192); - tcp2->th_sport = 0; /* XXX - fake it for nat_new */ - TCP_OFF_A(tcp2, 5); - tcp2->th_flags = TH_SYN; - fi.fin_data[1] = port; - fi.fin_dlen = sizeof(*tcp2); - tcp2->th_dport = htons(port); - fi.fin_data[0] = 0; - fi.fin_dp = (char *)tcp2; - fi.fin_plen = fi.fin_hlen + sizeof(*tcp); - fi.fin_fr = &ftppxyfr; - fi.fin_out = nat->nat_dir; - fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; - swip = ip->ip_src; - swip2 = ip->ip_dst; - if (nat->nat_dir == NAT_OUTBOUND) { - fi.fin_fi.fi_daddr = data_addr.s_addr; - fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; - ip->ip_dst = data_addr; - ip->ip_src = nat->nat_inip; - } else if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_saddr = nat->nat_oip.s_addr; - fi.fin_fi.fi_daddr = nat->nat_outip.s_addr; - ip->ip_src = nat->nat_oip; - ip->ip_dst = nat->nat_outip; - } + KFREES(ipn, ipn->in_size); + return APR_ERR(1); + } - sflags = nflags; - nflags |= NAT_SLAVE; - if (nat->nat_dir == NAT_INBOUND) - nflags |= NAT_NOTRULEPORT; - nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir); - if (nat2 != NULL) { - (void) nat_proto(&fi, nat2, IPN_TCP); - nat_update(&fi, nat2, nat->nat_ptr); - fi.fin_ifp = NULL; - if (nat->nat_dir == NAT_INBOUND) { - fi.fin_fi.fi_daddr = nat->nat_inip.s_addr; - ip->ip_dst = nat->nat_inip; - } - (void) fr_addstate(&fi, NULL, sflags); - if (fi.fin_state != NULL) - fr_statederef((ipstate_t **)&fi.fin_state); + (void) ipf_nat_proto(&fi, nat2, IPN_TCP); + MUTEX_ENTER(&nat2->nat_lock); + ipf_nat_update(&fi, nat2); + MUTEX_EXIT(&nat2->nat_lock); + fi.fin_ifp = NULL; + if (nat->nat_dir == NAT_INBOUND) { + if (nat->nat_v[0] == 6) { +#ifdef USE_INET6 + fi.fin_dst6 = nat->nat_ndst6; +#endif + } else { + fi.fin_daddr = nat->nat_ndstaddr; } + } + if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate, + SI_W_SPORT) != 0) + ipf_nat_setpending(softc, nat2); + +#if !defined(_KERNEL) + M_ADJ(m, inc); +#else + /* + * m_adj takes care of pkthdr.len, if required and treats inc<0 to + * mean remove -len bytes from the end of the packet. + * The mbuf chain will be extended if necessary by m_copyback(). + */ + if (inc < 0) + M_ADJ(m, inc); +#endif /* !defined(_KERNEL) */ + COPYBACK(m, off, nlen, newmsg); + fin->fin_flx |= FI_DOCKSUM; - ip->ip_len = slen; - ip->ip_src = swip; - ip->ip_dst = swip2; + if (inc != 0) { + fin->fin_plen += inc; + fin->fin_dlen += inc; + if (nat->nat_v[0] == 6) { +#ifdef USE_INET6 + ip6 = (ip6_t *)fin->fin_ip; + u_short len = ntohs(ip6->ip6_plen) + inc; + ip6->ip6_plen = htons(len); +#endif + } else { + ip->ip_len = htons(fin->fin_plen); + } } - return inc; + + return APR_INC(inc); } -int ippr_ftp_server(fin, ip, nat, ftp, dlen) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpinfo_t *ftp; -int dlen; +int +ipf_p_ftp_server(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; { char *rptr, *wptr; ftpside_t *f; @@ -747,19 +1012,29 @@ int dlen; rptr = f->ftps_rptr; wptr = f->ftps_wptr; + DT2(ftp_server_response, char *, rptr, int, ftp->ftp_passok); if (*rptr == ' ') goto server_cmd_ok; if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2))) return 0; + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) + printf("ipf_p_ftp_server_1: cmd[%4.4s] passok %d\n", + rptr, ftp->ftp_passok); if (ftp->ftp_passok == FTPXY_GO) { if (!strncmp(rptr, "227 ", 4)) - inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen); + inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen); else if (!strncmp(rptr, "229 ", 4)) - inc = ippr_ftp_epsv(fin, ip, nat, f, dlen); - } else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) { - inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen); - } else if (ippr_ftp_insecure && !strncmp(rptr, "229 ", 4)) { - inc = ippr_ftp_epsv(fin, ip, nat, f, dlen); + inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen); + else if (strncmp(rptr, "200", 3)) { + /* + * 200 is returned for a successful command. + */ + ; + } + } else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "227 ", 4)) { + inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen); + } else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "229 ", 4)) { + inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen); } else if (*rptr == '5' || *rptr == '4') ftp->ftp_passok = FTPXY_INIT; else if (ftp->ftp_incok) { @@ -784,8 +1059,13 @@ int dlen; } } } -server_cmd_ok: ftp->ftp_incok = 0; +server_cmd_ok: + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) + printf("ipf_p_ftp_server_2: cmd[%4.4s] passok %d\n", + rptr, ftp->ftp_passok); + DT3(ftp_server_passok, char *,rptr, int, ftp->ftp_incok, + int, ftp->ftp_passok); while ((*rptr++ != '\n') && (rptr < wptr)) ; @@ -795,13 +1075,20 @@ server_cmd_ok: /* + * 0 FTPXY_JUNK_OK + * 1 FTPXY_JUNK_BAD + * 2 FTPXY_JUNK_EOL + * 3 FTPXY_JUNK_CONT + * * Look to see if the buffer starts with something which we recognise as * being the correct syntax for the FTP protocol. */ -int ippr_ftp_client_valid(ftps, buf, len) -ftpside_t *ftps; -char *buf; -size_t len; +int +ipf_p_ftp_client_valid(softf, ftps, buf, len) + ipf_ftp_softc_t *softf; + ftpside_t *ftps; + char *buf; + size_t len; { register char *s, c, pc; register size_t i = len; @@ -809,12 +1096,13 @@ size_t len; s = buf; - if (ftps->ftps_junk == 1) - return 1; + if (ftps->ftps_junk == FTPXY_JUNK_BAD) + return FTPXY_JUNK_BAD; if (i < 5) { - if (ippr_ftp_debug > 3) - printf("ippr_ftp_client_valid:i(%d) < 5\n", (int)i); + DT1(client_valid, int, i); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_client_valid:i(%d) < 5\n", (int)i); return 2; } @@ -847,12 +1135,13 @@ size_t len; goto bad_client_command; } else { bad_client_command: - if (ippr_ftp_debug > 3) + DT4(client_junk, int, len, int, i, int, c, char *, buf); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n", - "ippr_ftp_client_valid", + "ipf_p_ftp_client_valid", ftps->ftps_junk, (int)len, (int)i, c, (int)len, (int)len, buf); - return 1; + return FTPXY_JUNK_BAD; } for (; i; i--) { @@ -860,25 +1149,30 @@ bad_client_command: c = *s++; if ((pc == '\r') && (c == '\n')) { cmd[4] = '\0'; - if (!strcmp(cmd, "PASV")) - ftps->ftps_cmds = FTPXY_C_PASV; - else - ftps->ftps_cmds = 0; + if (!strcmp(cmd, "PASV")) { + ftps->ftps_cmd = FTPXY_C_PASV; + } else if (!strcmp(cmd, "EPSV")) { + ftps->ftps_cmd = FTPXY_C_EPSV; + } else { + ftps->ftps_cmd = 0; + } return 0; } } #if !defined(_KERNEL) - printf("ippr_ftp_client_valid:junk after cmd[%*.*s]\n", + printf("ipf_p_ftp_client_valid:junk after cmd[%*.*s]\n", (int)len, (int)len, buf); #endif - return 2; + return FTPXY_JUNK_EOL; } -int ippr_ftp_server_valid(ftps, buf, len) -ftpside_t *ftps; -char *buf; -size_t len; +int +ipf_p_ftp_server_valid(softf, ftps, buf, len) + ipf_ftp_softc_t *softf; + ftpside_t *ftps; + char *buf; + size_t len; { register char *s, c, pc; register size_t i = len; @@ -887,19 +1181,22 @@ size_t len; s = buf; cmd = 0; - if (ftps->ftps_junk == 1) - return 1; + if (ftps->ftps_junk == FTPXY_JUNK_BAD) + return FTPXY_JUNK_BAD; if (i < 5) { - if (ippr_ftp_debug > 3) - printf("ippr_ftp_servert_valid:i(%d) < 5\n", (int)i); + DT1(server_valid, int, i); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_servert_valid:i(%d) < 5\n", (int)i); return 2; } c = *s++; i--; - if (c == ' ') + if (c == ' ') { + cmd = -1; goto search_eol; + } if (ISDIGIT(c)) { cmd = (c - '0') * 100; @@ -915,40 +1212,54 @@ size_t len; i--; if ((c != '-') && (c != ' ')) goto bad_server_command; + if (c == '-') + return FTPXY_JUNK_CONT; } else goto bad_server_command; } else goto bad_server_command; } else { bad_server_command: - if (ippr_ftp_debug > 3) + DT4(server_junk, int len, buf, int, i, int, c, char *, buf); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n", - "ippr_ftp_server_valid", + "ipf_p_ftp_server_valid", ftps->ftps_junk, (int)len, (int)i, c, (int)len, (int)len, buf); - return 1; + if (ftps->ftps_junk == FTPXY_JUNK_CONT) + return FTPXY_JUNK_CONT; + return FTPXY_JUNK_BAD; } search_eol: for (; i; i--) { pc = c; c = *s++; if ((pc == '\r') && (c == '\n')) { - ftps->ftps_cmds = cmd; - return 0; + if (cmd == -1) { + if (ftps->ftps_junk == FTPXY_JUNK_CONT) + return FTPXY_JUNK_CONT; + } else { + ftps->ftps_cmd = cmd; + } + return FTPXY_JUNK_OK; } } - if (ippr_ftp_debug > 3) - printf("ippr_ftp_server_valid:junk after cmd[%*.*s]\n", + + DT2(junk_eol, int, len, char *, buf); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) + printf("ipf_p_ftp_server_valid:junk after cmd[%*.*s]\n", (int)len, (int)len, buf); - return 2; + return FTPXY_JUNK_EOL; } -int ippr_ftp_valid(ftp, side, buf, len) -ftpinfo_t *ftp; -int side; -char *buf; -size_t len; +int +ipf_p_ftp_valid(softf, ftp, side, buf, len) + ipf_ftp_softc_t *softf; + ftpinfo_t *ftp; + int side; + char *buf; + size_t len; { ftpside_t *ftps; int ret; @@ -956,9 +1267,9 @@ size_t len; ftps = &ftp->ftp_side[side]; if (side == 0) - ret = ippr_ftp_client_valid(ftps, buf, len); + ret = ipf_p_ftp_client_valid(softf, ftps, buf, len); else - ret = ippr_ftp_server_valid(ftps, buf, len); + ret = ipf_p_ftp_server_valid(softf, ftps, buf, len); return ret; } @@ -971,13 +1282,15 @@ size_t len; * rv == 0 for inbound processing, * rv == 1 for outbound processing. */ -int ippr_ftp_process(fin, nat, ftp, rv) -fr_info_t *fin; -nat_t *nat; -ftpinfo_t *ftp; -int rv; +int +ipf_p_ftp_process(softf, fin, nat, ftp, rv) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + nat_t *nat; + ftpinfo_t *ftp; + int rv; { - int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff; + int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff, retry; char *rptr, *wptr, *s; u_32_t thseq, thack; ap_session_t *aps; @@ -995,14 +1308,17 @@ int rv; t = &ftp->ftp_side[1 - rv]; thseq = ntohl(tcp->th_seq); thack = ntohl(tcp->th_ack); - #ifdef __sgi mlen = fin->fin_plen - off; #else mlen = MSGDSIZE(m) - off; #endif - if (ippr_ftp_debug > 4) - printf("ippr_ftp_process: mlen %d\n", mlen); + + DT3(process_debug, tcphdr_t *, tcp, int, off, int, mlen); + if (softf->ipf_p_ftp_debug & DEBUG_INFO) + printf("ipf_p_ftp_process: %d:%d,%d, mlen %d flags %x\n", + fin->fin_out, fin->fin_sport, fin->fin_dport, + mlen, tcp->th_flags); if ((mlen == 0) && ((tcp->th_flags & TH_OPENING) == TH_OPENING)) { f->ftps_seq[0] = thseq + 1; @@ -1016,7 +1332,7 @@ int rv; sel = aps->aps_sel[1 - rv]; sel2 = aps->aps_sel[rv]; - if (rv == 0) { + if (rv == 1) { seqoff = aps->aps_seqoff[sel]; if (aps->aps_seqmin[sel] > seqoff + thseq) seqoff = aps->aps_seqoff[!sel]; @@ -1025,14 +1341,14 @@ int rv; ackoff = aps->aps_ackoff[!sel2]; } else { seqoff = aps->aps_ackoff[sel]; - if (ippr_ftp_debug > 2) + if (softf->ipf_p_ftp_debug & DEBUG_INFO) printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq, aps->aps_ackmin[sel]); if (aps->aps_ackmin[sel] > seqoff + thseq) seqoff = aps->aps_ackoff[!sel]; ackoff = aps->aps_seqoff[sel2]; - if (ippr_ftp_debug > 2) + if (softf->ipf_p_ftp_debug & DEBUG_INFO) printf("ackoff %d thack %x seqmin %x\n", ackoff, thack, aps->aps_seqmin[sel2]); if (ackoff > 0) { @@ -1043,7 +1359,7 @@ int rv; ackoff = aps->aps_seqoff[!sel2]; } } - if (ippr_ftp_debug > 2) { + if (softf->ipf_p_ftp_debug & DEBUG_INFO) { printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n", rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff, thack, ackoff, mlen, fin->fin_plen, off); @@ -1060,7 +1376,7 @@ int rv; * that it is out of order (and there is no real danger in doing so * apart from causing packets to go through here ordered). */ - if (ippr_ftp_debug > 2) { + if (softf->ipf_p_ftp_debug & DEBUG_INFO) { printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n", rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff); } @@ -1078,37 +1394,44 @@ int rv; ok = 1; } } else { - if (t->ftps_seq[0] + ackoff == thack) + if (t->ftps_seq[0] + ackoff == thack) { + t->ftps_seq[0] = thack; ok = 1; - else if (t->ftps_seq[0] == thack + ackoff) + } else if (t->ftps_seq[0] == thack + ackoff) { + t->ftps_seq[0] = thack + ackoff; ok = 1; - else if (t->ftps_seq[1] + ackoff == thack) { - t->ftps_seq[0] = thack - ackoff; + } else if (t->ftps_seq[1] + ackoff == thack) { + t->ftps_seq[0] = thack; ok = 1; } else if (t->ftps_seq[1] == thack + ackoff) { - t->ftps_seq[0] = thack - ackoff; + t->ftps_seq[0] = thack + ackoff; ok = 1; } } } - if (ippr_ftp_debug > 2) { + if (softf->ipf_p_ftp_debug & DEBUG_INFO) { if (!ok) printf("%s ok\n", "not"); } if (!mlen) { - if (t->ftps_seq[0] + ackoff != thack) { - if (ippr_ftp_debug > 1) { - printf("%s:seq[0](%x) + (%x) != (%x)\n", - "ippr_ftp_process", t->ftps_seq[0], + if (t->ftps_seq[0] + ackoff != thack && + t->ftps_seq[1] + ackoff != thack) { + DT3(thack, ftpside_t *t, t, int, ackoff, u_32_t, thack); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { + printf("%s:seq[0](%u) + (%d) != (%u)\n", + "ipf_p_ftp_process", t->ftps_seq[0], + ackoff, thack); + printf("%s:seq[0](%u) + (%d) != (%u)\n", + "ipf_p_ftp_process", t->ftps_seq[1], ackoff, thack); } return APR_ERR(1); } - if (ippr_ftp_debug > 2) { - printf("ippr_ftp_process:f:seq[0] %x seq[1] %x\n", + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) { + printf("ipf_p_ftp_process:f:seq[0] %x seq[1] %x\n", f->ftps_seq[0], f->ftps_seq[1]); } @@ -1117,9 +1440,10 @@ int rv; f->ftps_seq[0] = f->ftps_seq[1] - seqoff; f->ftps_seq[1] = thseq + 1 - seqoff; } else { - if (ippr_ftp_debug > 1) { - printf("FIN: thseq %x seqoff %d ftps_seq %x %x\n", - thseq, seqoff, f->ftps_seq[0], f->ftps_seq[1]); + DT2(thseq, ftpside_t *t, t, u_32_t, thseq); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { + printf("FIN: thseq %x seqoff %d ftps_seq %x\n", + thseq, seqoff, f->ftps_seq[0]); } return APR_ERR(1); } @@ -1140,8 +1464,9 @@ int rv; } if (ok == 0) { + DT3(ok_0, ftpside_t *, f, u_32_t, thseq, int, mlen); inc = thseq - f->ftps_seq[0]; - if (ippr_ftp_debug > 1) { + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { printf("inc %d sel %d rv %d\n", inc, sel, rv); printf("th_seq %x ftps_seq %x/%x\n", thseq, f->ftps_seq[0], f->ftps_seq[1]); @@ -1163,68 +1488,69 @@ int rv; while (mlen > 0) { len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr)); + if (len == 0) + break; COPYDATA(m, off, len, wptr); mlen -= len; off += len; wptr += len; - if (ippr_ftp_debug > 3) +whilemore: + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n", - "ippr_ftp_process", + "ipf_p_ftp_process", len, mlen, off, (u_long)wptr, f->ftps_junk, len, len, rptr); f->ftps_wptr = wptr; - if (f->ftps_junk != 0) { + if (f->ftps_junk != FTPXY_JUNK_OK) { i = f->ftps_junk; - f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, + f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr, wptr - rptr); + DT2(junk_transit, int, i, int, f->ftps_junk); - if (ippr_ftp_debug > 5) + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) printf("%s:junk %d -> %d\n", - "ippr_ftp_process", i, f->ftps_junk); + "ipf_p_ftp_process", i, f->ftps_junk); - if (f->ftps_junk != 0) { + if (f->ftps_junk == FTPXY_JUNK_BAD) { + DT(buffer_full); if (wptr - rptr == sizeof(f->ftps_buf)) { - if (ippr_ftp_debug > 4) + if (softf->ipf_p_ftp_debug & + DEBUG_PARSE_INFO) printf("%s:full buffer\n", - "ippr_ftp_process"); + "ipf_p_ftp_process"); f->ftps_rptr = f->ftps_buf; f->ftps_wptr = f->ftps_buf; rptr = f->ftps_rptr; wptr = f->ftps_wptr; - /* - * Because we throw away data here that - * we would otherwise parse, set the - * junk flag to indicate just ignore - * any data upto the next CRLF. - */ - f->ftps_junk = 1; continue; } } } - while ((f->ftps_junk == 0) && (wptr > rptr)) { + while ((f->ftps_junk == FTPXY_JUNK_OK) && (wptr > rptr)) { len = wptr - rptr; - f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, len); + f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, + rptr, len); - if (ippr_ftp_debug > 3) { + if (softf->ipf_p_ftp_debug & DEBUG_PARSE) { printf("%s=%d len %d rv %d ptr %lx/%lx ", - "ippr_ftp_valid", + "ipf_p_ftp_valid", f->ftps_junk, len, rv, (u_long)rptr, (u_long)wptr); printf("buf [%*.*s]\n", len, len, rptr); } - if (f->ftps_junk == 0) { + if (f->ftps_junk == FTPXY_JUNK_OK) { + f->ftps_cmds++; f->ftps_rptr = rptr; if (rv) - inc += ippr_ftp_server(fin, ip, nat, - ftp, len); + inc += ipf_p_ftp_server(softf, fin, ip, + nat, ftp, len); else - inc += ippr_ftp_client(fin, ip, nat, - ftp, len); + inc += ipf_p_ftp_client(softf, fin, ip, + nat, ftp, len); rptr = f->ftps_rptr; wptr = f->ftps_wptr; } @@ -1234,21 +1560,25 @@ int rv; * Off to a bad start so lets just forget about using the * ftp proxy for this connection. */ - if ((f->ftps_cmds == 0) && (f->ftps_junk == 1)) { + if ((f->ftps_cmds == 0) && (f->ftps_junk == FTPXY_JUNK_BAD)) { /* f->ftps_seq[1] += inc; */ - if (ippr_ftp_debug > 1) + DT(ftp_junk_cmd); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) printf("%s:cmds == 0 junk == 1\n", - "ippr_ftp_process"); + "ipf_p_ftp_process"); return APR_ERR(2); } - if ((f->ftps_junk != 0) && (rptr < wptr)) { + retry = 0; + if ((f->ftps_junk != FTPXY_JUNK_OK) && (rptr < wptr)) { for (s = rptr; s < wptr; s++) { if ((*s == '\r') && (s + 1 < wptr) && (*(s + 1) == '\n')) { rptr = s + 2; - f->ftps_junk = 0; + retry = 1; + if (f->ftps_junk != FTPXY_JUNK_CONT) + f->ftps_junk = FTPXY_JUNK_OK; break; } } @@ -1264,19 +1594,21 @@ int rv; * current state. */ if (rptr > f->ftps_buf) { - bcopy(rptr, f->ftps_buf, len); + bcopy(rptr, f->ftps_buf, wptr - rptr); wptr -= rptr - f->ftps_buf; rptr = f->ftps_buf; } } f->ftps_rptr = rptr; f->ftps_wptr = wptr; + if (retry) + goto whilemore; } /* f->ftps_seq[1] += inc; */ if (tcp->th_flags & TH_FIN) f->ftps_seq[1]++; - if (ippr_ftp_debug > 3) { + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) { #ifdef __sgi mlen = fin->fin_plen; #else @@ -1293,11 +1625,14 @@ int rv; } -int ippr_ftp_out(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_ftp_out(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { + ipf_ftp_softc_t *softf = arg; ftpinfo_t *ftp; int rev; @@ -1309,15 +1644,18 @@ nat_t *nat; if (ftp->ftp_side[1 - rev].ftps_ifp == NULL) ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp; - return ippr_ftp_process(fin, nat, ftp, rev); + return ipf_p_ftp_process(softf, fin, nat, ftp, rev); } -int ippr_ftp_in(fin, aps, nat) -fr_info_t *fin; -ap_session_t *aps; -nat_t *nat; +int +ipf_p_ftp_in(arg, fin, aps, nat) + void *arg; + fr_info_t *fin; + ap_session_t *aps; + nat_t *nat; { + ipf_ftp_softc_t *softf = arg; ftpinfo_t *ftp; int rev; @@ -1329,18 +1667,19 @@ nat_t *nat; if (ftp->ftp_side[rev].ftps_ifp == NULL) ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp; - return ippr_ftp_process(fin, nat, ftp, 1 - rev); + return ipf_p_ftp_process(softf, fin, nat, ftp, 1 - rev); } /* - * ippr_ftp_atoi - implement a version of atoi which processes numbers in + * ipf_p_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), * returning a 16 bit number combining either side of the , as the MSB and * LSB. */ -u_short ippr_ftp_atoi(ptr) -char **ptr; +u_short +ipf_p_ftp_atoi(ptr) + char **ptr; { register char *s = *ptr, c; register u_char i = 0, j = 0; @@ -1364,26 +1703,237 @@ char **ptr; } -int ippr_ftp_epsv(fin, ip, nat, f, dlen) -fr_info_t *fin; -ip_t *ip; -nat_t *nat; -ftpside_t *f; -int dlen; +int +ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; +{ + ftpside_t *f; + + /* + * Check for client sending out EPRT message. + */ + if (dlen < IPF_MINEPRTLEN) { + DT1(epert_dlen, int, dlen); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_eprt:dlen(%d) < IPF_MINEPRTLEN\n", + dlen); + return 0; + } + + /* + * Parse the EPRT command. Format is: + * "EPRT |1|1.2.3.4|2000|" for IPv4 and + * "EPRT |2|ef00::1:2|2000|" for IPv6 + */ + f = &ftp->ftp_side[0]; + if (f->ftps_rptr[5] != '|') + return 0; + if (f->ftps_rptr[5] == f->ftps_rptr[7]) { + if (f->ftps_rptr[6] == '1' && nat->nat_v[0] == 4) + return ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen); +#ifdef USE_INET6 + if (f->ftps_rptr[6] == '2' && nat->nat_v[0] == 6) + return ipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen); +#endif + } + return 0; +} + + +int +ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; +{ + int a1, a2, a3, a4, port, olen, nlen, inc, off; + char newbuf[IPF_FTPBUFSZ]; + char *s, c, delim; + u_32_t addr, i; + tcphdr_t *tcp; + ftpside_t *f; + mb_t *m; + + m = fin->fin_m; + tcp = (tcphdr_t *)fin->fin_dp; + off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; + f = &ftp->ftp_side[0]; + delim = f->ftps_rptr[5]; + s = f->ftps_rptr + 8; + + /* + * get the IP address. + */ + i = 0; + while (((c = *s++) != '\0') && ISDIGIT(c)) { + i *= 10; + i += c - '0'; + } + if (i > 255) + return 0; + if (c != '.') + return 0; + addr = (i << 24); + + i = 0; + while (((c = *s++) != '\0') && ISDIGIT(c)) { + i *= 10; + i += c - '0'; + } + if (i > 255) + return 0; + if (c != '.') + return 0; + addr |= (addr << 16); + + i = 0; + while (((c = *s++) != '\0') && ISDIGIT(c)) { + i *= 10; + i += c - '0'; + } + if (i > 255) + return 0; + if (c != '.') + return 0; + addr |= (addr << 8); + + i = 0; + while (((c = *s++) != '\0') && ISDIGIT(c)) { + i *= 10; + i += c - '0'; + } + if (i > 255) + return 0; + if (c != delim) + return 0; + addr |= addr; + + /* + * Get the port number + */ + i = 0; + while (((c = *s++) != '\0') && ISDIGIT(c)) { + i *= 10; + i += c - '0'; + } + if (i > 65535) + return 0; + if (c != delim) + return 0; + port = i; + + /* + * Check for CR-LF at the end of the command string. + */ + if ((*s != '\r') || (*(s + 1) != '\n')) { + DT(eprt4_no_crlf); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_eprt4:missing %s\n", "cr-lf"); + return 0; + } + s += 2; + + /* + * Calculate new address parts for PORT command + */ + if (nat->nat_dir == NAT_INBOUND) + a1 = ntohl(nat->nat_odstaddr); + else + a1 = ntohl(ip->ip_src.s_addr); + a2 = (a1 >> 16) & 0xff; + a3 = (a1 >> 8) & 0xff; + a4 = a1 & 0xff; + a1 >>= 24; + olen = s - f->ftps_rptr; + /* DO NOT change this to snprintf! */ + /* + * While we could force the use of | as a delimiter here, it makes + * sense to preserve whatever character is being used by the systems + * involved in the communication. + */ +#if defined(SNPRINTF) && defined(_KERNEL) + SNPRINTF(newbuf, sizeof(newbuf), "%s %c1%c%u.%u.%u.%u%c%u%c\r\n", + "EPRT", delim, delim, a1, a2, a3, a4, delim, port, delim); +#else + (void) sprintf(newbuf, "%s %c1%c%u.%u.%u.%u%c%u%c\r\n", + "EPRT", delim, delim, a1, a2, a3, a4, delim, port, + delim); +#endif + + nlen = strlen(newbuf); + inc = nlen - olen; + if ((inc + fin->fin_plen) > 65535) { + DT2(eprt4_len, int, inc, int, fin->fin_plen); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_eprt4:inc(%d) + ip->ip_len > 65535\n", + inc); + return 0; + } + + off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; +#if !defined(_KERNEL) + M_ADJ(m, inc); +#else + if (inc < 0) + M_ADJ(m, inc); +#endif + /* the mbuf chain will be extended if necessary by m_copyback() */ + COPYBACK(m, off, nlen, newbuf); + fin->fin_flx |= FI_DOCKSUM; + + if (inc != 0) { + fin->fin_plen += inc; + ip->ip_len = htons(fin->fin_plen); + fin->fin_dlen += inc; + } + + f->ftps_cmd = FTPXY_C_EPRT; + return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc); +} + + +int +ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; { char newbuf[IPF_FTPBUFSZ]; - char *s; u_short ap = 0; + ftpside_t *f; + char *s; + + if ((softf->ipf_p_ftp_forcepasv != 0) && + (ftp->ftp_side[0].ftps_cmd != FTPXY_C_EPSV)) { + DT1(epsv_cmd, int, ftp->ftp_side[0].ftps_cmd); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_epsv:ftps_cmd(%d) != FTPXY_C_EPSV\n", + ftp->ftp_side[0].ftps_cmd); + return 0; + } + f = &ftp->ftp_side[1]; #define EPSV_REPLEN 33 /* * Check for EPSV reply message. */ - if (dlen < IPF_MIN229LEN) + if (dlen < IPF_MIN229LEN) { return (0); - else if (strncmp(f->ftps_rptr, - "229 Entering Extended Passive Mode", EPSV_REPLEN)) + } else if (strncmp(f->ftps_rptr, + "229 Entering Extended Passive Mode", EPSV_REPLEN)) { return (0); +} /* * Skip the EPSV command + space @@ -1401,8 +1951,9 @@ int dlen; ap += *s++ - '0'; } - if (!*s) + if (!s) { return 0; +} if (*s == '|') s++; @@ -1413,10 +1964,10 @@ int dlen; /* * check for CR-LF at the end. */ - if ((*s == '\r') && (*(s + 1) == '\n')) { - s += 2; - } else + if ((*s != '\r') || (*(s + 1) != '\n')) { return 0; + } + s += 2; #if defined(SNPRINTF) && defined(_KERNEL) SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n", @@ -1426,6 +1977,218 @@ int dlen; "229 Entering Extended Passive Mode", ap); #endif - return ippr_ftp_pasvreply(fin, ip, nat, f, (u_int)ap, newbuf, s, - ip->ip_src.s_addr); + return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (u_int)ap, + newbuf, s); +} + +#ifdef USE_INET6 +int +ipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen) + ipf_ftp_softc_t *softf; + fr_info_t *fin; + ip_t *ip; + nat_t *nat; + ftpinfo_t *ftp; + int dlen; +{ + int port, olen, nlen, inc, off, left, i; + char newbuf[IPF_FTPBUFSZ]; + char *s, c; + i6addr_t addr, *a6; + tcphdr_t *tcp; + ip6_t *ip6; + char delim; + u_short whole; + u_short part; + ftpside_t *f; + u_short *t; + int fwd; + mb_t *m; + u_32_t a; + + m = fin->fin_m; + ip6 = (ip6_t *)ip; + f = &ftp->ftp_side[0]; + s = f->ftps_rptr + 8; + f = &ftp->ftp_side[0]; + delim = f->ftps_rptr[5]; + tcp = (tcphdr_t *)fin->fin_dp; + off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; + + addr.i6[0] = 0; + addr.i6[1] = 0; + addr.i6[2] = 0; + addr.i6[3] = 0; + /* + * Parse an IPv6 address. + * Go forward until either :: or | is found. If :: is found, + * reverse direction. Direction change is performed to ease + * parsing an unknown number of 0s in the middle. + */ + whole = 0; + t = (u_short *)&addr; + fwd = 1; + for (part = 0; (c = *s) != '\0'; ) { + if (c == delim) { + *t = htons((u_short)whole); + break; + } + if (c == ':') { + *t = part; + if (fwd) { + *t = htons((u_short)whole); + t++; + } else { + *t = htons((u_short)(whole >> 16)); + t--; + } + whole = 0; + if (fwd == 1 && s[1] == ':') { + while (*s && *s != '|') + s++; + if ((c = *s) != delim) + break; + t = (u_short *)&addr.i6[3]; + t++; + fwd = 0; + } else if (fwd == 0 && s[-1] == ':') { + break; + } + } else { + if (c >= '0' && c <= '9') { + c -= '0'; + } else if (c >= 'a' && c <= 'f') { + c -= 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + c -= 'A' + 10; + } + if (fwd) { + whole <<= 8; + whole |= c; + } else { + whole >>= 8; + whole |= ((u_32_t)c) << 24; + } + } + if (fwd) + s++; + else + s--; + } + if (c != ':' && c != delim) + return 0; + + while (*s != '|') + s++; + s++; + + /* + * Get the port number + */ + i = 0; + while (((c = *s++) != '\0') && ISDIGIT(c)) { + i *= 10; + i += c - '0'; + } + if (i > 65535) + return 0; + if (c != delim) + return 0; + port = (u_short)(i & 0xffff); + + /* + * Check for CR-LF at the end of the command string. + */ + if ((*s != '\r') || (*(s + 1) != '\n')) { + DT(eprt6_no_crlf); + if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) + printf("ipf_p_ftp_eprt6:missing %s\n", "cr-lf"); + return 0; + } + s += 2; + + /* + * Calculate new address parts for PORT command + */ + a6 = (i6addr_t *)&ip6->ip6_src; + olen = s - f->ftps_rptr; + /* DO NOT change this to snprintf! */ + /* + * While we could force the use of | as a delimiter here, it makes + * sense to preserve whatever character is being used by the systems + * involved in the communication. + */ + s = newbuf; + left = sizeof(newbuf); +#if defined(SNPRINTF) && defined(_KERNEL) + SNPRINTF(newbuf, left, "EPRT %c2%c", delim, delim); + left -= strlen(s) + 1; + s += strlen(s); + a = ntohl(a6->i6[0]); + SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + a = ntohl(a6->i6[1]); + SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + a = ntohl(a6->i6[2]); + SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + a = ntohl(a6->i6[3]); + SNPRINTF(s, left, "%x:%x", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + sprintf(s, "|%d|\r\n", port); +#else + (void) sprintf(s, "EPRT %c2%c", delim, delim); + s += strlen(s); + a = ntohl(a6->i6[0]); + sprintf(s, "%x:%x:", a >> 16, a & 0xffff); + s += strlen(s); + a = ntohl(a6->i6[1]); + sprintf(s, "%x:%x:", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + a = ntohl(a6->i6[2]); + sprintf(s, "%x:%x:", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + a = ntohl(a6->i6[3]); + sprintf(s, "%x:%x", a >> 16, a & 0xffff); + left -= strlen(s); + s += strlen(s); + sprintf(s, "|%d|\r\n", port); +#endif + nlen = strlen(newbuf); + inc = nlen - olen; + if ((inc + fin->fin_plen) > 65535) { + DT2(eprt6_len, int, inc, int, fin->fin_plen); + if (softf->ipf_p_ftp_debug & DEBUG_ERROR) + printf("ipf_p_ftp_eprt6:inc(%d) + ip->ip_len > 65535\n", + inc); + return 0; + } + + off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; +#if !defined(_KERNEL) + M_ADJ(m, inc); +#else + if (inc < 0) + M_ADJ(m, inc); +#endif + /* the mbuf chain will be extended if necessary by m_copyback() */ + COPYBACK(m, off, nlen, newbuf); + fin->fin_flx |= FI_DOCKSUM; + + if (inc != 0) { + fin->fin_plen += inc; + ip6->ip6_plen = htons(fin->fin_plen - fin->fin_hlen); + fin->fin_dlen += inc; + } + + f->ftps_cmd = FTPXY_C_EPRT; + return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc); } +#endif |