1145522Sdarrenr/* $FreeBSD: stable/11/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 369245 2021-02-09 13:47:46Z git2svn $ */ 2145522Sdarrenr 353642Sguido/* 4255332Scy * Copyright (C) 2012 by Darren Reed. 5145522Sdarrenr * 6145522Sdarrenr * See the IPFILTER.LICENCE file for details on licencing. 7145522Sdarrenr * 853642Sguido * Simple FTP transparent proxy for in-kernel use. For use with the NAT 953642Sguido * code. 1072006Sdarrenr * 1157126Sguido * $FreeBSD: stable/11/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 369245 2021-02-09 13:47:46Z git2svn $ 12161356Sguido * Id: ip_ftp_pxy.c,v 2.88.2.19 2006/04/01 10:14:53 darrenr Exp $ 1353642Sguido */ 1453642Sguido 1553642Sguido#define IPF_FTP_PROXY 1653642Sguido 1753642Sguido#define IPF_MINPORTLEN 18 18255332Scy#define IPF_MINEPRTLEN 20 1953642Sguido#define IPF_MAXPORTLEN 30 2053642Sguido#define IPF_MIN227LEN 39 2153642Sguido#define IPF_MAX227LEN 51 22145522Sdarrenr#define IPF_MIN229LEN 47 23145522Sdarrenr#define IPF_MAX229LEN 51 2453642Sguido 2580482Sdarrenr#define FTPXY_GO 0 2680482Sdarrenr#define FTPXY_INIT 1 2780482Sdarrenr#define FTPXY_USER_1 2 2880482Sdarrenr#define FTPXY_USOK_1 3 2980482Sdarrenr#define FTPXY_PASS_1 4 3080482Sdarrenr#define FTPXY_PAOK_1 5 3180482Sdarrenr#define FTPXY_AUTH_1 6 3280482Sdarrenr#define FTPXY_AUOK_1 7 3380482Sdarrenr#define FTPXY_ADAT_1 8 3480482Sdarrenr#define FTPXY_ADOK_1 9 3580482Sdarrenr#define FTPXY_ACCT_1 10 3680482Sdarrenr#define FTPXY_ACOK_1 11 3780482Sdarrenr#define FTPXY_USER_2 12 3880482Sdarrenr#define FTPXY_USOK_2 13 3980482Sdarrenr#define FTPXY_PASS_2 14 4080482Sdarrenr#define FTPXY_PAOK_2 15 4153642Sguido 42255332Scy#define FTPXY_JUNK_OK 0 43255332Scy#define FTPXY_JUNK_BAD 1 /* Ignore all commands for this connection */ 44255332Scy#define FTPXY_JUNK_EOL 2 /* consume the rest of this line only */ 45255332Scy#define FTPXY_JUNK_CONT 3 /* Saerching for next numeric */ 46255332Scy 47110916Sdarrenr/* 48110916Sdarrenr * Values for FTP commands. Numerics cover 0-999 49110916Sdarrenr */ 50110916Sdarrenr#define FTPXY_C_PASV 1000 51255332Scy#define FTPXY_C_PORT 1001 52255332Scy#define FTPXY_C_EPSV 1002 53255332Scy#define FTPXY_C_EPRT 1003 54110916Sdarrenr 5553642Sguido 56255332Scytypedef struct ipf_ftp_softc_s { 57255332Scy int ipf_p_ftp_pasvonly; 58255332Scy /* Do not require logins before transfers */ 59255332Scy int ipf_p_ftp_insecure; 60255332Scy int ipf_p_ftp_pasvrdr; 61255332Scy /* PASV must be last command prior to 227 */ 62255332Scy int ipf_p_ftp_forcepasv; 63255332Scy int ipf_p_ftp_debug; 64255332Scy int ipf_p_ftp_single_xfer; 65255332Scy void *ipf_p_ftp_tune; 66255332Scy} ipf_ftp_softc_t; 67145522Sdarrenr 68255332Scy 69369245Sgit2svnvoid ipf_p_ftp_main_load(void); 70369245Sgit2svnvoid ipf_p_ftp_main_unload(void); 71369245Sgit2svnvoid *ipf_p_ftp_soft_create(ipf_main_softc_t *); 72369245Sgit2svnvoid ipf_p_ftp_soft_destroy(ipf_main_softc_t *, void *); 73255332Scy 74369245Sgit2svnint ipf_p_ftp_client(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, 75369245Sgit2svn ftpinfo_t *, int); 76369245Sgit2svnint ipf_p_ftp_complete(char *, size_t); 77369245Sgit2svnint ipf_p_ftp_in(void *, fr_info_t *, ap_session_t *, nat_t *); 78369245Sgit2svnint ipf_p_ftp_new(void *, fr_info_t *, ap_session_t *, nat_t *); 79369245Sgit2svnvoid ipf_p_ftp_del(ipf_main_softc_t *, ap_session_t *); 80369245Sgit2svnint ipf_p_ftp_out(void *, fr_info_t *, ap_session_t *, nat_t *); 81369245Sgit2svnint ipf_p_ftp_pasv(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, 82369245Sgit2svn ftpinfo_t *, int); 83369245Sgit2svnint ipf_p_ftp_epsv(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, 84369245Sgit2svn ftpinfo_t *, int); 85369245Sgit2svnint ipf_p_ftp_port(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, 86369245Sgit2svn ftpinfo_t *, int); 87369245Sgit2svnint ipf_p_ftp_process(ipf_ftp_softc_t *, fr_info_t *, nat_t *, 88369245Sgit2svn ftpinfo_t *, int); 89369245Sgit2svnint ipf_p_ftp_server(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, 90369245Sgit2svn ftpinfo_t *, int); 91369245Sgit2svnint ipf_p_ftp_valid(ipf_ftp_softc_t *, ftpinfo_t *, int, char *, size_t); 92369245Sgit2svnint ipf_p_ftp_server_valid(ipf_ftp_softc_t *, ftpside_t *, char *, 93369245Sgit2svn size_t); 94369245Sgit2svnint ipf_p_ftp_client_valid(ipf_ftp_softc_t *, ftpside_t *, char *, 95369245Sgit2svn size_t); 96369245Sgit2svnu_short ipf_p_ftp_atoi(char **); 97369245Sgit2svnint ipf_p_ftp_pasvreply(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, 98369245Sgit2svn ftpinfo_t *, u_int, char *, char *); 99369245Sgit2svnint ipf_p_ftp_eprt(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, 100369245Sgit2svn ftpinfo_t *, int); 101369245Sgit2svnint ipf_p_ftp_eprt4(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, 102369245Sgit2svn ftpinfo_t *, int); 103369245Sgit2svnint ipf_p_ftp_eprt6(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, 104369245Sgit2svn ftpinfo_t *, int); 105369245Sgit2svnint ipf_p_ftp_addport(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *, 106369245Sgit2svn ftpinfo_t *, int, int, int); 107369245Sgit2svnvoid ipf_p_ftp_setpending(ipf_main_softc_t *, ftpinfo_t *); 108255332Scy 109145522Sdarrenr/* 110255332Scy * Debug levels 111145522Sdarrenr */ 112255332Scy#define DEBUG_SECURITY 0x01 113255332Scy#define DEBUG_ERROR 0x02 114255332Scy#define DEBUG_INFO 0x04 115255332Scy#define DEBUG_PARSE_ERR 0x08 116255332Scy#define DEBUG_PARSE_INFO 0x10 117255332Scy#define DEBUG_PARSE 0x20 11853642Sguido 119255332Scystatic int ipf_p_ftp_proxy_init = 0; 120145522Sdarrenrstatic frentry_t ftppxyfr; 121255332Scystatic ipftuneable_t ipf_ftp_tuneables[] = { 122255332Scy { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_debug) }, 123255332Scy "ftp_debug", 0, 0x7f, 124255332Scy stsizeof(ipf_ftp_softc_t, ipf_p_ftp_debug), 125255332Scy 0, NULL, NULL }, 126255332Scy { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly) }, 127255332Scy "ftp_pasvonly", 0, 1, 128255332Scy stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly), 129255332Scy 0, NULL, NULL }, 130255332Scy { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_insecure) }, 131255332Scy "ftp_insecure", 0, 1, 132255332Scy stsizeof(ipf_ftp_softc_t, ipf_p_ftp_insecure), 133255332Scy 0, NULL, NULL }, 134255332Scy { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr) }, 135255332Scy "ftp_pasvrdr", 0, 1, 136255332Scy stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr), 137255332Scy 0, NULL, NULL }, 138255332Scy { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv) }, 139255332Scy "ftp_forcepasv", 0, 1, 140255332Scy stsizeof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv), 141255332Scy 0, NULL, NULL }, 142255332Scy { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer) }, 143255332Scy "ftp_single_xfer", 0, 1, 144255332Scy stsizeof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer), 145255332Scy 0, NULL, NULL }, 146255332Scy { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL } 147145522Sdarrenr}; 14853642Sguido 149145522Sdarrenr 150255332Scyvoid 151255332Scyipf_p_ftp_main_load() 15253642Sguido{ 15392685Sdarrenr bzero((char *)&ftppxyfr, sizeof(ftppxyfr)); 15492685Sdarrenr ftppxyfr.fr_ref = 1; 15592685Sdarrenr ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 156255332Scy 157145522Sdarrenr MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex"); 158255332Scy ipf_p_ftp_proxy_init = 1; 15953642Sguido} 16053642Sguido 16153642Sguido 162255332Scyvoid 163255332Scyipf_p_ftp_main_unload() 164145522Sdarrenr{ 165145522Sdarrenr 166255332Scy if (ipf_p_ftp_proxy_init == 1) { 167145522Sdarrenr MUTEX_DESTROY(&ftppxyfr.fr_lock); 168255332Scy ipf_p_ftp_proxy_init = 0; 169145522Sdarrenr } 170145522Sdarrenr} 171145522Sdarrenr 172145522Sdarrenr 173255332Scy/* 174255332Scy * Initialize local structures. 175255332Scy */ 176255332Scyvoid * 177255332Scyipf_p_ftp_soft_create(softc) 178255332Scy ipf_main_softc_t *softc; 17953642Sguido{ 180255332Scy ipf_ftp_softc_t *softf; 181255332Scy 182255332Scy KMALLOC(softf, ipf_ftp_softc_t *); 183255332Scy if (softf == NULL) 184255332Scy return NULL; 185255332Scy 186255332Scy bzero((char *)softf, sizeof(*softf)); 187255332Scy#if defined(_KERNEL) 188255332Scy softf->ipf_p_ftp_debug = 0; 189255332Scy#else 190255332Scy softf->ipf_p_ftp_debug = DEBUG_PARSE_ERR; 191255332Scy#endif 192255332Scy softf->ipf_p_ftp_forcepasv = 1; 193255332Scy 194255332Scy softf->ipf_p_ftp_tune = ipf_tune_array_copy(softf, 195255332Scy sizeof(ipf_ftp_tuneables), 196255332Scy ipf_ftp_tuneables); 197255332Scy if (softf->ipf_p_ftp_tune == NULL) { 198255332Scy ipf_p_ftp_soft_destroy(softc, softf); 199255332Scy return NULL; 200255332Scy } 201255332Scy if (ipf_tune_array_link(softc, softf->ipf_p_ftp_tune) == -1) { 202255332Scy ipf_p_ftp_soft_destroy(softc, softf); 203255332Scy return NULL; 204255332Scy } 205255332Scy 206255332Scy return softf; 207255332Scy} 208255332Scy 209255332Scy 210255332Scyvoid 211255332Scyipf_p_ftp_soft_destroy(softc, arg) 212255332Scy ipf_main_softc_t *softc; 213255332Scy void *arg; 214255332Scy{ 215255332Scy ipf_ftp_softc_t *softf = arg; 216255332Scy 217255332Scy if (softf->ipf_p_ftp_tune != NULL) { 218255332Scy ipf_tune_array_unlink(softc, softf->ipf_p_ftp_tune); 219255332Scy KFREES(softf->ipf_p_ftp_tune, sizeof(ipf_ftp_tuneables)); 220255332Scy softf->ipf_p_ftp_tune = NULL; 221255332Scy } 222255332Scy 223255332Scy KFREE(softf); 224255332Scy} 225255332Scy 226255332Scy 227255332Scyint 228255332Scyipf_p_ftp_new(arg, fin, aps, nat) 229255332Scy void *arg; 230255332Scy fr_info_t *fin; 231255332Scy ap_session_t *aps; 232255332Scy nat_t *nat; 233255332Scy{ 23460857Sdarrenr ftpinfo_t *ftp; 23560857Sdarrenr ftpside_t *f; 23653642Sguido 23760857Sdarrenr KMALLOC(ftp, ftpinfo_t *); 23860857Sdarrenr if (ftp == NULL) 23960857Sdarrenr return -1; 240145522Sdarrenr 241145522Sdarrenr nat = nat; /* LINT */ 242145522Sdarrenr 24360857Sdarrenr aps->aps_data = ftp; 24460857Sdarrenr aps->aps_psiz = sizeof(ftpinfo_t); 245255332Scy aps->aps_sport = htons(fin->fin_sport); 246255332Scy aps->aps_dport = htons(fin->fin_dport); 24760857Sdarrenr 24860857Sdarrenr bzero((char *)ftp, sizeof(*ftp)); 24960857Sdarrenr f = &ftp->ftp_side[0]; 25060857Sdarrenr f->ftps_rptr = f->ftps_buf; 25160857Sdarrenr f->ftps_wptr = f->ftps_buf; 25260857Sdarrenr f = &ftp->ftp_side[1]; 25360857Sdarrenr f->ftps_rptr = f->ftps_buf; 25460857Sdarrenr f->ftps_wptr = f->ftps_buf; 25580482Sdarrenr ftp->ftp_passok = FTPXY_INIT; 256145522Sdarrenr ftp->ftp_incok = 0; 25760857Sdarrenr return 0; 25853642Sguido} 25953642Sguido 26053642Sguido 261255332Scyvoid 262255332Scyipf_p_ftp_setpending(ipf_main_softc_t *softc, ftpinfo_t *ftp) 26353642Sguido{ 264255332Scy if (ftp->ftp_pendnat != NULL) 265255332Scy ipf_nat_setpending(softc, ftp->ftp_pendnat); 266255332Scy 267255332Scy if (ftp->ftp_pendstate != NULL) { 268255332Scy READ_ENTER(&softc->ipf_state); 269255332Scy ipf_state_setpending(softc, ftp->ftp_pendstate); 270255332Scy RWLOCK_EXIT(&softc->ipf_state); 271255332Scy } 272255332Scy} 273255332Scy 274255332Scy 275255332Scyvoid 276255332Scyipf_p_ftp_del(softc, aps) 277255332Scy ipf_main_softc_t *softc; 278255332Scy ap_session_t *aps; 279255332Scy{ 280255332Scy ftpinfo_t *ftp; 281255332Scy 282255332Scy ftp = aps->aps_data; 283255332Scy if (ftp != NULL) 284255332Scy ipf_p_ftp_setpending(softc, ftp); 285255332Scy} 286255332Scy 287255332Scy 288255332Scyint 289255332Scyipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen) 290255332Scy ipf_ftp_softc_t *softf; 291255332Scy fr_info_t *fin; 292255332Scy ip_t *ip; 293255332Scy nat_t *nat; 294255332Scy ftpinfo_t *ftp; 295255332Scy int dlen; 296255332Scy{ 29760857Sdarrenr char newbuf[IPF_FTPBUFSZ], *s; 29853642Sguido u_int a1, a2, a3, a4; 29992685Sdarrenr u_short a5, a6, sp; 30060857Sdarrenr size_t nlen, olen; 301255332Scy tcphdr_t *tcp; 302255332Scy int inc, off; 303255332Scy ftpside_t *f; 30453642Sguido mb_t *m; 30553642Sguido 306145522Sdarrenr m = fin->fin_m; 307255332Scy f = &ftp->ftp_side[0]; 30853642Sguido tcp = (tcphdr_t *)fin->fin_dp; 309145522Sdarrenr off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 310145522Sdarrenr 31160857Sdarrenr /* 31260857Sdarrenr * Check for client sending out PORT message. 31360857Sdarrenr */ 314110916Sdarrenr if (dlen < IPF_MINPORTLEN) { 315255332Scy DT3(ftp_PORT_error_dlen, nat_t *, nat, ftpside_t *, f, 316255332Scy u_int, dlen); 317255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE) 318255332Scy printf("ipf_p_ftp_port:dlen(%d) < IPF_MINPORTLEN\n", 319145522Sdarrenr dlen); 32053642Sguido return 0; 321110916Sdarrenr } 32260857Sdarrenr /* 32353642Sguido * Skip the PORT command + space 32453642Sguido */ 32560857Sdarrenr s = f->ftps_rptr + 5; 32653642Sguido /* 32753642Sguido * Pick out the address components, two at a time. 32853642Sguido */ 329255332Scy a1 = ipf_p_ftp_atoi(&s); 330110916Sdarrenr if (s == NULL) { 331255332Scy DT2(ftp_PORT_error_atoi_1, nat_t *, nat, ftpside_t *, f); 332255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 333255332Scy printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 1); 33453642Sguido return 0; 335110916Sdarrenr } 336255332Scy a2 = ipf_p_ftp_atoi(&s); 337110916Sdarrenr if (s == NULL) { 338255332Scy DT2(ftp_PORT_error_atoi_2, nat_t *, nat, ftpside_t *, f); 339255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 340255332Scy printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 2); 34153642Sguido return 0; 342110916Sdarrenr } 343145522Sdarrenr 34453642Sguido /* 345145522Sdarrenr * Check that IP address in the PORT/PASV reply is the same as the 34653642Sguido * sender of the command - prevents using PORT for port scanning. 34753642Sguido */ 34853642Sguido a1 <<= 16; 34953642Sguido a1 |= a2; 350145522Sdarrenr if (((nat->nat_dir == NAT_OUTBOUND) && 351255332Scy (a1 != ntohl(nat->nat_osrcaddr))) || 352145522Sdarrenr ((nat->nat_dir == NAT_INBOUND) && 353255332Scy (a1 != ntohl(nat->nat_nsrcaddr)))) { 354255332Scy DT3(ftp_PORT_error_address, nat_t *, nat, ftpside_t *, f, 355255332Scy u_int, a1); 356255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) 357255332Scy printf("ipf_p_ftp_port:%s != nat->nat_inip\n", "a1"); 358145522Sdarrenr return APR_ERR(1); 359110916Sdarrenr } 36053642Sguido 361255332Scy a5 = ipf_p_ftp_atoi(&s); 362110916Sdarrenr if (s == NULL) { 363255332Scy DT2(ftp_PORT_error_atoi_3, nat_t *, nat, ftpside_t *, f); 364255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 365255332Scy printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 3); 36653642Sguido return 0; 367110916Sdarrenr } 36853642Sguido if (*s == ')') 36953642Sguido s++; 37053642Sguido 37153642Sguido /* 37253642Sguido * check for CR-LF at the end. 37353642Sguido */ 37453642Sguido if (*s == '\n') 37553642Sguido s--; 376255332Scy if ((*s != '\r') || (*(s + 1) != '\n')) { 377255332Scy DT2(ftp_PORT_error_no_crlf, nat_t *, nat, ftpside_t *, f); 378255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 379255332Scy printf("ipf_p_ftp_port:missing %s\n", "cr-lf"); 38053642Sguido return 0; 381110916Sdarrenr } 382255332Scy s += 2; 383255332Scy a6 = a5 & 0xff; 384145522Sdarrenr 385255332Scy /* 386255332Scy * Calculate the source port. Verification of > 1024 is in 387255332Scy * ipf_p_ftp_addport. 388255332Scy */ 38953642Sguido a5 >>= 8; 39067614Sdarrenr a5 &= 0xff; 391145522Sdarrenr sp = a5 << 8 | a6; 392255332Scy 39353642Sguido /* 39453642Sguido * Calculate new address parts for PORT command 39553642Sguido */ 396145522Sdarrenr if (nat->nat_dir == NAT_INBOUND) 397255332Scy a1 = ntohl(nat->nat_ndstaddr); 398145522Sdarrenr else 399145522Sdarrenr a1 = ntohl(ip->ip_src.s_addr); 400255332Scy a1 = ntohl(ip->ip_src.s_addr); 40153642Sguido a2 = (a1 >> 16) & 0xff; 40253642Sguido a3 = (a1 >> 8) & 0xff; 40353642Sguido a4 = a1 & 0xff; 40453642Sguido a1 >>= 24; 40560857Sdarrenr olen = s - f->ftps_rptr; 40692685Sdarrenr /* DO NOT change this to snprintf! */ 407145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL) 408145522Sdarrenr SNPRINTF(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n", 409145522Sdarrenr "PORT", a1, a2, a3, a4, a5, a6); 410130886Sdarrenr#else 41155929Sguido (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", 41253642Sguido "PORT", a1, a2, a3, a4, a5, a6); 413130886Sdarrenr#endif 41453642Sguido 41553642Sguido nlen = strlen(newbuf); 41653642Sguido inc = nlen - olen; 417255332Scy if ((inc + fin->fin_plen) > 65535) { 418255332Scy DT3(ftp_PORT_error_inc, nat_t *, nat, ftpside_t *, f, 419255332Scy int, inc); 420255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) 421255332Scy printf("ipf_p_ftp_port:inc(%d) + ip->ip_len > 65535\n", 422145522Sdarrenr inc); 42360857Sdarrenr return 0; 424110916Sdarrenr } 42560857Sdarrenr 42695563Sdarrenr#if !defined(_KERNEL) 427255332Scy M_ADJ(m, inc); 42895563Sdarrenr#else 429145522Sdarrenr /* 430145522Sdarrenr * m_adj takes care of pkthdr.len, if required and treats inc<0 to 431145522Sdarrenr * mean remove -len bytes from the end of the packet. 432145522Sdarrenr * The mbuf chain will be extended if necessary by m_copyback(). 433145522Sdarrenr */ 434145522Sdarrenr if (inc < 0) 435255332Scy M_ADJ(m, inc); 436145522Sdarrenr#endif /* !defined(_KERNEL) */ 437145522Sdarrenr COPYBACK(m, off, nlen, newbuf); 438255332Scy fin->fin_flx |= FI_DOCKSUM; 439145522Sdarrenr 44053642Sguido if (inc != 0) { 441255332Scy fin->fin_plen += inc; 442255332Scy ip->ip_len = htons(fin->fin_plen); 443145522Sdarrenr fin->fin_dlen += inc; 44453642Sguido } 44553642Sguido 446255332Scy f->ftps_cmd = FTPXY_C_PORT; 447255332Scy return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, sp, inc); 448255332Scy} 449255332Scy 450255332Scy 451255332Scyint 452255332Scyipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, nport, inc) 453255332Scy ipf_ftp_softc_t *softf; 454255332Scy fr_info_t *fin; 455255332Scy ip_t *ip; 456255332Scy nat_t *nat; 457255332Scy ftpinfo_t *ftp; 458255332Scy int dlen, nport, inc; 459255332Scy{ 460255332Scy tcphdr_t tcph, *tcp2 = &tcph; 461255332Scy ipf_main_softc_t *softc; 462255332Scy ipf_nat_softc_t *softn; 463255332Scy int direction; 464255332Scy fr_info_t fi; 465255332Scy ipnat_t *ipn; 466255332Scy nat_t *nat2; 467255332Scy u_short sp; 468255332Scy int flags; 469255332Scy 470255332Scy softc = fin->fin_main_soft; 471255332Scy softn = softc->ipf_nat_soft; 472255332Scy 473255332Scy if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL)) { 474255332Scy if (softf->ipf_p_ftp_single_xfer != 0) { 475255332Scy DT2(ftp_PORT_error_add_active, nat_t *, nat, 476255332Scy ftpinfo_t *, ftp); 477255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) 478255332Scy printf("ipf_p_ftp_addport:xfer active %p/%p\n", 479255332Scy ftp->ftp_pendnat, ftp->ftp_pendstate); 480255332Scy return 0; 481255332Scy } 482255332Scy ipf_p_ftp_setpending(softc, ftp); 483255332Scy } 484255332Scy 48553642Sguido /* 486255332Scy * Add skeleton NAT entry for connection which will come back the 487255332Scy * other way. 488255332Scy */ 489255332Scy sp = nport; 490255332Scy /* 491255332Scy * Don't allow the PORT command to specify a port < 1024 due to 492255332Scy * security risks. 493255332Scy */ 494255332Scy if (sp < 1024) { 495255332Scy DT3(ftp_PORT_error_port, nat_t *, nat, ftpinfo_t *, ftp, 496255332Scy u_int, sp); 497255332Scy if (softf->ipf_p_ftp_debug & DEBUG_SECURITY) 498255332Scy printf("ipf_p_ftp_addport:sp(%d) < 1024\n", sp); 499255332Scy return 0; 500255332Scy } 501255332Scy /* 50253642Sguido * The server may not make the connection back from port 20, but 50353642Sguido * it is the most likely so use it here to check for a conflicting 50453642Sguido * mapping. 50553642Sguido */ 50692685Sdarrenr bcopy((char *)fin, (char *)&fi, sizeof(fi)); 507145522Sdarrenr fi.fin_flx |= FI_IGNORE; 50892685Sdarrenr fi.fin_data[0] = sp; 50992685Sdarrenr fi.fin_data[1] = fin->fin_data[1] - 1; 510255332Scy fi.fin_src6 = nat->nat_ndst6; 511255332Scy fi.fin_dst6 = nat->nat_nsrc6; 512255332Scy 513255332Scy if (nat->nat_v[0] == 6) { 514255332Scy#ifndef USE_INET6 515255332Scy return APR_INC(inc); 516255332Scy#endif 517255332Scy } 518255332Scy 519145522Sdarrenr /* 520145522Sdarrenr * Add skeleton NAT entry for connection which will come back the 521145522Sdarrenr * other way. 522145522Sdarrenr */ 523255332Scy#ifdef USE_INET6 524255332Scy if (nat->nat_v[0] == 6) { 525145522Sdarrenr if (nat->nat_dir == NAT_OUTBOUND) { 526255332Scy nat2 = ipf_nat6_outlookup(&fi, IPN_TCP|NAT_SEARCH, 527255332Scy nat->nat_pr[1], 528255332Scy &nat->nat_osrc6.in6, 529255332Scy &nat->nat_odst6.in6); 530255332Scy } else { 531255332Scy nat2 = ipf_nat6_inlookup(&fi, IPN_TCP|NAT_SEARCH, 532255332Scy nat->nat_pr[0], 533255332Scy &nat->nat_odst6.in6, 534255332Scy &nat->nat_osrc6.in6); 53553642Sguido } 536255332Scy } else 537255332Scy#endif 538255332Scy { 539255332Scy if (nat->nat_dir == NAT_OUTBOUND) { 540255332Scy nat2 = ipf_nat_outlookup(&fi, IPN_TCP|NAT_SEARCH, 541255332Scy nat->nat_pr[1], 542255332Scy nat->nat_osrcip, 543255332Scy nat->nat_odstip); 544255332Scy } else { 545255332Scy nat2 = ipf_nat_inlookup(&fi, IPN_TCP|NAT_SEARCH, 546255332Scy nat->nat_pr[0], 547255332Scy nat->nat_odstip, 548255332Scy nat->nat_osrcip); 549255332Scy } 550255332Scy } 551255332Scy if (nat2 != NULL) 552255332Scy return APR_INC(inc); 553145522Sdarrenr 554255332Scy ipn = ipf_proxy_rule_rev(nat); 555255332Scy if (ipn == NULL) 556255332Scy return APR_ERR(1); 557255332Scy ipn->in_use = 0; 558145522Sdarrenr 559255332Scy fi.fin_fr = &ftppxyfr; 560255332Scy fi.fin_dp = (char *)tcp2; 561255332Scy fi.fin_dlen = sizeof(*tcp2); 562255332Scy fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); 563255332Scy fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; 564255332Scy fi.fin_data[1] = sp; 565255332Scy fi.fin_data[0] = 0; 566255332Scy 567255332Scy bzero((char *)tcp2, sizeof(*tcp2)); 568255332Scy tcp2->th_sport = 0; 569255332Scy tcp2->th_dport = htons(sp); 570255332Scy 571255332Scy tcp2->th_win = htons(8192); 572255332Scy TCP_OFF_A(tcp2, 5); 573255332Scy tcp2->th_flags = TH_SYN; 574255332Scy 575255332Scy if (nat->nat_dir == NAT_INBOUND) { 576255332Scy fi.fin_out = 1; 577255332Scy direction = NAT_OUTBOUND; 578255332Scy } else { 579255332Scy fi.fin_out = 0; 580255332Scy direction = NAT_INBOUND; 58153642Sguido } 582255332Scy flags = SI_W_SPORT|NAT_SLAVE|IPN_TCP; 583255332Scy 584255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 585255332Scy if (nat->nat_v[0] == 6) { 586255332Scy#ifdef USE_INET6 587255332Scy nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat, flags, 588255332Scy direction); 589255332Scy#endif 590255332Scy } else { 591255332Scy nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, flags, 592255332Scy direction); 593255332Scy } 594255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 595255332Scy 596255332Scy if (nat2 == NULL) { 597255332Scy KFREES(ipn, ipn->in_size); 598255332Scy return APR_ERR(1); 599255332Scy } 600255332Scy 601255332Scy (void) ipf_nat_proto(&fi, nat2, IPN_TCP); 602255332Scy MUTEX_ENTER(&nat2->nat_lock); 603255332Scy ipf_nat_update(&fi, nat2); 604255332Scy MUTEX_EXIT(&nat2->nat_lock); 605255332Scy fi.fin_ifp = NULL; 606255332Scy if (nat2->nat_dir == NAT_INBOUND) 607255332Scy fi.fin_dst6 = nat->nat_osrc6; 608255332Scy if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate, 609255332Scy SI_W_SPORT) != 0) 610255332Scy ipf_nat_setpending(softc, nat2); 611255332Scy 612145522Sdarrenr return APR_INC(inc); 61353642Sguido} 61453642Sguido 61553642Sguido 616255332Scyint 617255332Scyipf_p_ftp_client(softf, fin, ip, nat, ftp, dlen) 618255332Scy ipf_ftp_softc_t *softf; 619255332Scy fr_info_t *fin; 620255332Scy nat_t *nat; 621255332Scy ftpinfo_t *ftp; 622255332Scy ip_t *ip; 623255332Scy int dlen; 62453642Sguido{ 62563523Sdarrenr char *rptr, *wptr, cmd[6], c; 62660857Sdarrenr ftpside_t *f; 62763523Sdarrenr int inc, i; 62860857Sdarrenr 62960857Sdarrenr inc = 0; 63060857Sdarrenr f = &ftp->ftp_side[0]; 63160857Sdarrenr rptr = f->ftps_rptr; 63260857Sdarrenr wptr = f->ftps_wptr; 63360857Sdarrenr 63463523Sdarrenr for (i = 0; (i < 5) && (i < dlen); i++) { 63563523Sdarrenr c = rptr[i]; 636145522Sdarrenr if (ISALPHA(c)) { 637145522Sdarrenr cmd[i] = TOUPPER(c); 63863523Sdarrenr } else { 63963523Sdarrenr cmd[i] = c; 64063523Sdarrenr } 64163523Sdarrenr } 64263523Sdarrenr cmd[i] = '\0'; 64363523Sdarrenr 64480482Sdarrenr ftp->ftp_incok = 0; 645255332Scy DT2(ftp_client_command, char [], cmd, int, ftp->ftp_passok); 64680482Sdarrenr if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) { 64780482Sdarrenr if (ftp->ftp_passok == FTPXY_ADOK_1 || 64880482Sdarrenr ftp->ftp_passok == FTPXY_AUOK_1) { 64980482Sdarrenr ftp->ftp_passok = FTPXY_USER_2; 65080482Sdarrenr ftp->ftp_incok = 1; 65180482Sdarrenr } else { 65280482Sdarrenr ftp->ftp_passok = FTPXY_USER_1; 65380482Sdarrenr ftp->ftp_incok = 1; 65480482Sdarrenr } 65580482Sdarrenr } else if (!strncmp(cmd, "AUTH ", 5)) { 65680482Sdarrenr ftp->ftp_passok = FTPXY_AUTH_1; 65780482Sdarrenr ftp->ftp_incok = 1; 65880482Sdarrenr } else if (!strncmp(cmd, "PASS ", 5)) { 65980482Sdarrenr if (ftp->ftp_passok == FTPXY_USOK_1) { 66080482Sdarrenr ftp->ftp_passok = FTPXY_PASS_1; 66180482Sdarrenr ftp->ftp_incok = 1; 66280482Sdarrenr } else if (ftp->ftp_passok == FTPXY_USOK_2) { 66380482Sdarrenr ftp->ftp_passok = FTPXY_PASS_2; 66480482Sdarrenr ftp->ftp_incok = 1; 66580482Sdarrenr } 66680482Sdarrenr } else if ((ftp->ftp_passok == FTPXY_AUOK_1) && 66780482Sdarrenr !strncmp(cmd, "ADAT ", 5)) { 66880482Sdarrenr ftp->ftp_passok = FTPXY_ADAT_1; 66980482Sdarrenr ftp->ftp_incok = 1; 67092685Sdarrenr } else if ((ftp->ftp_passok == FTPXY_PAOK_1 || 67192685Sdarrenr ftp->ftp_passok == FTPXY_PAOK_2) && 67280482Sdarrenr !strncmp(cmd, "ACCT ", 5)) { 67380482Sdarrenr ftp->ftp_passok = FTPXY_ACCT_1; 67480482Sdarrenr ftp->ftp_incok = 1; 675255332Scy } else if ((ftp->ftp_passok == FTPXY_GO) && 676255332Scy !softf->ipf_p_ftp_pasvonly && 67763523Sdarrenr !strncmp(cmd, "PORT ", 5)) { 678255332Scy inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen); 679255332Scy } else if ((ftp->ftp_passok == FTPXY_GO) && 680255332Scy !softf->ipf_p_ftp_pasvonly && 681255332Scy !strncmp(cmd, "EPRT ", 5)) { 682255332Scy inc = ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen); 683255332Scy } else if (softf->ipf_p_ftp_insecure && 684255332Scy !softf->ipf_p_ftp_pasvonly && 68563523Sdarrenr !strncmp(cmd, "PORT ", 5)) { 686255332Scy inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen); 68760857Sdarrenr } 688255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE) 689255332Scy printf("ipf_p_ftp_client: cmd[%s] passok %d incok %d inc %d\n", 690255332Scy cmd, ftp->ftp_passok, ftp->ftp_incok, inc); 69160857Sdarrenr 692255332Scy DT2(ftp_client_passok, char *, cmd, int, ftp->ftp_passok); 69360857Sdarrenr while ((*rptr++ != '\n') && (rptr < wptr)) 69460857Sdarrenr ; 69560857Sdarrenr f->ftps_rptr = rptr; 69660857Sdarrenr return inc; 69753642Sguido} 69853642Sguido 69953642Sguido 700255332Scyint 701255332Scyipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen) 702255332Scy ipf_ftp_softc_t *softf; 703255332Scy fr_info_t *fin; 704255332Scy ip_t *ip; 705255332Scy nat_t *nat; 706255332Scy ftpinfo_t *ftp; 707255332Scy int dlen; 70853642Sguido{ 709145522Sdarrenr u_int a1, a2, a3, a4, data_ip; 710145522Sdarrenr char newbuf[IPF_FTPBUFSZ]; 711153876Sguido const char *brackets[2]; 712145522Sdarrenr u_short a5, a6; 713110916Sdarrenr ftpside_t *f; 714153876Sguido char *s; 71553642Sguido 716255332Scy if ((softf->ipf_p_ftp_forcepasv != 0) && 717255332Scy (ftp->ftp_side[0].ftps_cmd != FTPXY_C_PASV)) { 718255332Scy DT2(ftp_PASV_error_state, nat_t *, nat, ftpinfo_t *, ftp); 719255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) 720255332Scy printf("ipf_p_ftp_pasv:ftps_cmd(%d) != FTPXY_C_PASV\n", 721255332Scy ftp->ftp_side[0].ftps_cmd); 722110916Sdarrenr return 0; 723110916Sdarrenr } 724110916Sdarrenr 725110916Sdarrenr f = &ftp->ftp_side[1]; 726110916Sdarrenr 72780482Sdarrenr#define PASV_REPLEN 24 72860857Sdarrenr /* 72960857Sdarrenr * Check for PASV reply message. 73060857Sdarrenr */ 731110916Sdarrenr if (dlen < IPF_MIN227LEN) { 732255332Scy DT3(ftp_PASV_error_short, nat_t *, nat, ftpinfo_t *, ftp, 733255332Scy int, dlen); 734255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) 735255332Scy printf("ipf_p_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n", 736145522Sdarrenr dlen); 73760857Sdarrenr return 0; 738110916Sdarrenr } else if (strncmp(f->ftps_rptr, 739110916Sdarrenr "227 Entering Passive Mod", PASV_REPLEN)) { 740255332Scy DT2(ftp_PASV_error_string, nat_t *, nat, ftpinfo_t *, ftp); 741255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 742255332Scy printf("ipf_p_ftp_pasv:%d reply wrong\n", 227); 74360857Sdarrenr return 0; 744110916Sdarrenr } 74560857Sdarrenr 746145522Sdarrenr brackets[0] = ""; 747145522Sdarrenr brackets[1] = ""; 74853642Sguido /* 749110916Sdarrenr * Skip the PASV reply + space 75053642Sguido */ 75180482Sdarrenr s = f->ftps_rptr + PASV_REPLEN; 752145522Sdarrenr while (*s && !ISDIGIT(*s)) { 753145522Sdarrenr if (*s == '(') { 754145522Sdarrenr brackets[0] = "("; 755145522Sdarrenr brackets[1] = ")"; 756145522Sdarrenr } 75753642Sguido s++; 758145522Sdarrenr } 759145522Sdarrenr 76053642Sguido /* 76153642Sguido * Pick out the address components, two at a time. 76253642Sguido */ 763255332Scy a1 = ipf_p_ftp_atoi(&s); 764110916Sdarrenr if (s == NULL) { 765255332Scy DT2(ftp_PASV_error_atoi_1, nat_t *, nat, ftpside_t *, f); 766255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 767255332Scy printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 1); 76853642Sguido return 0; 769110916Sdarrenr } 770255332Scy a2 = ipf_p_ftp_atoi(&s); 771110916Sdarrenr if (s == NULL) { 772255332Scy DT2(ftp_PASV_error_atoi_2, nat_t *, nat, ftpside_t *, f); 773255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 774255332Scy printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 2); 77553642Sguido return 0; 776110916Sdarrenr } 77753642Sguido 77853642Sguido /* 779145522Sdarrenr * check that IP address in the PASV reply is the same as the 780145522Sdarrenr * sender of the command - prevents using PASV for port scanning. 78153642Sguido */ 78253642Sguido a1 <<= 16; 78353642Sguido a1 |= a2; 784145522Sdarrenr 785145522Sdarrenr if (((nat->nat_dir == NAT_INBOUND) && 786255332Scy (a1 != ntohl(nat->nat_ndstaddr))) || 787145522Sdarrenr ((nat->nat_dir == NAT_OUTBOUND) && 788255332Scy (a1 != ntohl(nat->nat_odstaddr)))) { 789255332Scy DT3(ftp_PASV_error_address, nat_t *, nat, ftpside_t *, f, 790255332Scy u_int, a1); 791255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) 792255332Scy printf("ipf_p_ftp_pasv:%s != nat->nat_oip\n", "a1"); 79353642Sguido return 0; 794110916Sdarrenr } 79553642Sguido 796255332Scy a5 = ipf_p_ftp_atoi(&s); 797110916Sdarrenr if (s == NULL) { 798255332Scy DT2(ftp_PASV_error_atoi_3, nat_t *, nat, ftpside_t *, f); 799255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 800255332Scy printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 3); 80153642Sguido return 0; 802110916Sdarrenr } 80353642Sguido 80453642Sguido if (*s == ')') 80553642Sguido s++; 80680482Sdarrenr if (*s == '.') 80780482Sdarrenr s++; 80853642Sguido if (*s == '\n') 80953642Sguido s--; 81053642Sguido /* 81153642Sguido * check for CR-LF at the end. 81253642Sguido */ 813255332Scy if ((*s != '\r') || (*(s + 1) != '\n')) { 814255332Scy DT(pasv_missing_crlf); 815255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 816255332Scy printf("ipf_p_ftp_pasv:missing %s", "cr-lf\n"); 81753642Sguido return 0; 818110916Sdarrenr } 819255332Scy s += 2; 820145522Sdarrenr 821145522Sdarrenr a6 = a5 & 0xff; 82253642Sguido a5 >>= 8; 82353642Sguido /* 82453642Sguido * Calculate new address parts for 227 reply 82553642Sguido */ 826145522Sdarrenr if (nat->nat_dir == NAT_INBOUND) { 827255332Scy data_ip = nat->nat_odstaddr; 828145522Sdarrenr a1 = ntohl(data_ip); 829145522Sdarrenr } else 830145522Sdarrenr data_ip = htonl(a1); 831145522Sdarrenr 83253642Sguido a2 = (a1 >> 16) & 0xff; 83353642Sguido a3 = (a1 >> 8) & 0xff; 83453642Sguido a4 = a1 & 0xff; 83553642Sguido a1 >>= 24; 836145522Sdarrenr 837145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL) 838145522Sdarrenr SNPRINTF(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n", 839145522Sdarrenr "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4, 840145522Sdarrenr a5, a6, brackets[1]); 841145522Sdarrenr#else 842145522Sdarrenr (void) sprintf(newbuf, "%s %s%u,%u,%u,%u,%u,%u%s\r\n", 843145522Sdarrenr "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4, 844145522Sdarrenr a5, a6, brackets[1]); 845145522Sdarrenr#endif 846255332Scy return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (a5 << 8 | a6), 847255332Scy newbuf, s); 848145522Sdarrenr} 849145522Sdarrenr 850255332Scyint 851255332Scyipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, port, newmsg, s) 852255332Scy ipf_ftp_softc_t *softf; 853255332Scy fr_info_t *fin; 854255332Scy ip_t *ip; 855255332Scy nat_t *nat; 856255332Scy ftpinfo_t *ftp; 857255332Scy u_int port; 858255332Scy char *newmsg; 859255332Scy char *s; 860145522Sdarrenr{ 861255332Scy int inc, off, nflags; 862145522Sdarrenr tcphdr_t *tcp, tcph, *tcp2; 863255332Scy ipf_main_softc_t *softc; 864255332Scy ipf_nat_softc_t *softn; 865145522Sdarrenr size_t nlen, olen; 866255332Scy#ifdef USE_INET6 867255332Scy ip6_t *ip6; 868255332Scy#endif 869255332Scy ipnat_t *ipn; 870145522Sdarrenr fr_info_t fi; 871255332Scy ftpside_t *f; 872145522Sdarrenr nat_t *nat2; 873145522Sdarrenr mb_t *m; 874145522Sdarrenr 875255332Scy softc = fin->fin_main_soft; 876255332Scy softn = softc->ipf_nat_soft; 877255332Scy 878255332Scy if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL)) 879255332Scy ipf_p_ftp_setpending(softc, ftp); 880255332Scy 881145522Sdarrenr m = fin->fin_m; 882145522Sdarrenr tcp = (tcphdr_t *)fin->fin_dp; 883145522Sdarrenr off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 884145522Sdarrenr 885145522Sdarrenr tcp2 = &tcph; 88660857Sdarrenr inc = 0; 887145522Sdarrenr 888255332Scy f = &ftp->ftp_side[1]; 88960857Sdarrenr olen = s - f->ftps_rptr; 890145522Sdarrenr nlen = strlen(newmsg); 89153642Sguido inc = nlen - olen; 892255332Scy if ((inc + fin->fin_plen) > 65535) { 893255332Scy DT3(ftp_PASV_error_inc, nat_t *, nat, ftpside_t *, f, 894255332Scy int, inc); 895255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) 896255332Scy printf("ipf_p_ftp_pasv:inc(%d) + ip->ip_len > 65535\n", 897145522Sdarrenr inc); 89860857Sdarrenr return 0; 899145522Sdarrenr } 90060857Sdarrenr 901255332Scy ipn = ipf_proxy_rule_fwd(nat); 902255332Scy if (ipn == NULL) 903255332Scy return APR_ERR(1); 904255332Scy ipn->in_use = 0; 905145522Sdarrenr 90653642Sguido /* 90753642Sguido * Add skeleton NAT entry for connection which will come back the 90853642Sguido * other way. 90953642Sguido */ 910255332Scy bzero((char *)tcp2, sizeof(*tcp2)); 91192685Sdarrenr bcopy((char *)fin, (char *)&fi, sizeof(fi)); 912145522Sdarrenr fi.fin_flx |= FI_IGNORE; 91392685Sdarrenr fi.fin_data[0] = 0; 914145522Sdarrenr fi.fin_data[1] = port; 915145522Sdarrenr nflags = IPN_TCP|SI_W_SPORT; 916255332Scy 917255332Scy fi.fin_fr = &ftppxyfr; 918255332Scy fi.fin_dp = (char *)tcp2; 919255332Scy fi.fin_out = 1 - fin->fin_out; 920255332Scy fi.fin_dlen = sizeof(*tcp2); 921255332Scy fi.fin_src6 = nat->nat_osrc6; 922255332Scy fi.fin_dst6 = nat->nat_odst6; 923255332Scy fi.fin_plen = fi.fin_hlen + sizeof(*tcp); 924255332Scy fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; 925255332Scy 926255332Scy TCP_OFF_A(tcp2, 5); 927255332Scy tcp2->th_flags = TH_SYN; 928255332Scy tcp2->th_win = htons(8192); 929255332Scy tcp2->th_dport = htons(port); 930255332Scy 931255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 932255332Scy#ifdef USE_INET6 933255332Scy if (nat->nat_v[0] == 6) 934255332Scy nat2 = ipf_nat6_add(&fi, ipn, &ftp->ftp_pendnat, 935255332Scy nflags, nat->nat_dir); 936145522Sdarrenr else 937255332Scy#endif 938255332Scy nat2 = ipf_nat_add(&fi, ipn, &ftp->ftp_pendnat, 939255332Scy nflags, nat->nat_dir); 940255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 941255332Scy 942145522Sdarrenr if (nat2 == NULL) { 943255332Scy KFREES(ipn, ipn->in_size); 944255332Scy return APR_ERR(1); 945255332Scy } 94660857Sdarrenr 947255332Scy (void) ipf_nat_proto(&fi, nat2, IPN_TCP); 948255332Scy MUTEX_ENTER(&nat2->nat_lock); 949255332Scy ipf_nat_update(&fi, nat2); 950255332Scy MUTEX_EXIT(&nat2->nat_lock); 951255332Scy fi.fin_ifp = NULL; 952255332Scy if (nat->nat_dir == NAT_INBOUND) { 953255332Scy if (nat->nat_v[0] == 6) { 954255332Scy#ifdef USE_INET6 955255332Scy fi.fin_dst6 = nat->nat_ndst6; 956255332Scy#endif 957255332Scy } else { 958255332Scy fi.fin_daddr = nat->nat_ndstaddr; 95953642Sguido } 960255332Scy } 961255332Scy if (ipf_state_add(softc, &fi, (ipstate_t **)&ftp->ftp_pendstate, 962255332Scy SI_W_SPORT) != 0) 963255332Scy ipf_nat_setpending(softc, nat2); 964145522Sdarrenr 965255332Scy#if !defined(_KERNEL) 966255332Scy M_ADJ(m, inc); 967255332Scy#else 968255332Scy /* 969255332Scy * m_adj takes care of pkthdr.len, if required and treats inc<0 to 970255332Scy * mean remove -len bytes from the end of the packet. 971255332Scy * The mbuf chain will be extended if necessary by m_copyback(). 972255332Scy */ 973255332Scy if (inc < 0) 974255332Scy M_ADJ(m, inc); 975255332Scy#endif /* !defined(_KERNEL) */ 976255332Scy COPYBACK(m, off, nlen, newmsg); 977255332Scy fin->fin_flx |= FI_DOCKSUM; 978255332Scy 979255332Scy if (inc != 0) { 980255332Scy fin->fin_plen += inc; 981255332Scy fin->fin_dlen += inc; 982255332Scy if (nat->nat_v[0] == 6) { 983255332Scy#ifdef USE_INET6 984255332Scy ip6 = (ip6_t *)fin->fin_ip; 985255332Scy u_short len = ntohs(ip6->ip6_plen) + inc; 986255332Scy ip6->ip6_plen = htons(len); 987255332Scy#endif 988255332Scy } else { 989255332Scy ip->ip_len = htons(fin->fin_plen); 990145522Sdarrenr } 991255332Scy } 992145522Sdarrenr 993255332Scy return APR_INC(inc); 99453642Sguido} 99553642Sguido 99653642Sguido 997255332Scyint 998255332Scyipf_p_ftp_server(softf, fin, ip, nat, ftp, dlen) 999255332Scy ipf_ftp_softc_t *softf; 1000255332Scy fr_info_t *fin; 1001255332Scy ip_t *ip; 1002255332Scy nat_t *nat; 1003255332Scy ftpinfo_t *ftp; 1004255332Scy int dlen; 100560857Sdarrenr{ 100660857Sdarrenr char *rptr, *wptr; 100760857Sdarrenr ftpside_t *f; 100860857Sdarrenr int inc; 100960857Sdarrenr 101060857Sdarrenr inc = 0; 101160857Sdarrenr f = &ftp->ftp_side[1]; 101260857Sdarrenr rptr = f->ftps_rptr; 101360857Sdarrenr wptr = f->ftps_wptr; 101460857Sdarrenr 1015255332Scy DT2(ftp_server_response, char *, rptr, int, ftp->ftp_passok); 1016145522Sdarrenr if (*rptr == ' ') 1017145522Sdarrenr goto server_cmd_ok; 1018145522Sdarrenr if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2))) 1019102520Sdarrenr return 0; 1020255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE) 1021255332Scy printf("ipf_p_ftp_server_1: cmd[%4.4s] passok %d\n", 1022255332Scy rptr, ftp->ftp_passok); 102380482Sdarrenr if (ftp->ftp_passok == FTPXY_GO) { 102480482Sdarrenr if (!strncmp(rptr, "227 ", 4)) 1025255332Scy inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen); 1026145522Sdarrenr else if (!strncmp(rptr, "229 ", 4)) 1027255332Scy inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen); 1028255332Scy else if (strncmp(rptr, "200", 3)) { 1029255332Scy /* 1030255332Scy * 200 is returned for a successful command. 1031255332Scy */ 1032255332Scy ; 1033255332Scy } 1034255332Scy } else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "227 ", 4)) { 1035255332Scy inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen); 1036255332Scy } else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "229 ", 4)) { 1037255332Scy inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen); 103880482Sdarrenr } else if (*rptr == '5' || *rptr == '4') 103980482Sdarrenr ftp->ftp_passok = FTPXY_INIT; 104080482Sdarrenr else if (ftp->ftp_incok) { 104180482Sdarrenr if (*rptr == '3') { 104280482Sdarrenr if (ftp->ftp_passok == FTPXY_ACCT_1) 104380482Sdarrenr ftp->ftp_passok = FTPXY_GO; 104480482Sdarrenr else 104580482Sdarrenr ftp->ftp_passok++; 104680482Sdarrenr } else if (*rptr == '2') { 104780482Sdarrenr switch (ftp->ftp_passok) 104880482Sdarrenr { 104980482Sdarrenr case FTPXY_USER_1 : 105080482Sdarrenr case FTPXY_USER_2 : 105180482Sdarrenr case FTPXY_PASS_1 : 105280482Sdarrenr case FTPXY_PASS_2 : 105380482Sdarrenr case FTPXY_ACCT_1 : 105480482Sdarrenr ftp->ftp_passok = FTPXY_GO; 105580482Sdarrenr break; 105680482Sdarrenr default : 105780482Sdarrenr ftp->ftp_passok += 3; 105880482Sdarrenr break; 105980482Sdarrenr } 106080482Sdarrenr } 106160857Sdarrenr } 1062255332Scy ftp->ftp_incok = 0; 1063145522Sdarrenrserver_cmd_ok: 1064255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE) 1065255332Scy printf("ipf_p_ftp_server_2: cmd[%4.4s] passok %d\n", 1066255332Scy rptr, ftp->ftp_passok); 1067255332Scy DT3(ftp_server_passok, char *,rptr, int, ftp->ftp_incok, 1068255332Scy int, ftp->ftp_passok); 1069110916Sdarrenr 107060857Sdarrenr while ((*rptr++ != '\n') && (rptr < wptr)) 107160857Sdarrenr ; 107260857Sdarrenr f->ftps_rptr = rptr; 107360857Sdarrenr return inc; 107460857Sdarrenr} 107560857Sdarrenr 107660857Sdarrenr 107760857Sdarrenr/* 1078255332Scy * 0 FTPXY_JUNK_OK 1079255332Scy * 1 FTPXY_JUNK_BAD 1080255332Scy * 2 FTPXY_JUNK_EOL 1081255332Scy * 3 FTPXY_JUNK_CONT 1082255332Scy * 108360857Sdarrenr * Look to see if the buffer starts with something which we recognise as 108460857Sdarrenr * being the correct syntax for the FTP protocol. 108560857Sdarrenr */ 1086255332Scyint 1087255332Scyipf_p_ftp_client_valid(softf, ftps, buf, len) 1088255332Scy ipf_ftp_softc_t *softf; 1089255332Scy ftpside_t *ftps; 1090255332Scy char *buf; 1091255332Scy size_t len; 109260857Sdarrenr{ 1093145522Sdarrenr register char *s, c, pc; 109460857Sdarrenr register size_t i = len; 1095110916Sdarrenr char cmd[5]; 109660857Sdarrenr 1097145522Sdarrenr s = buf; 1098145522Sdarrenr 1099255332Scy if (ftps->ftps_junk == FTPXY_JUNK_BAD) 1100255332Scy return FTPXY_JUNK_BAD; 1101145522Sdarrenr 1102110916Sdarrenr if (i < 5) { 1103255332Scy DT1(client_valid, int, i); 1104255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) 1105255332Scy printf("ipf_p_ftp_client_valid:i(%d) < 5\n", (int)i); 110660857Sdarrenr return 2; 1107110916Sdarrenr } 1108145522Sdarrenr 1109145522Sdarrenr i--; 111060857Sdarrenr c = *s++; 111160857Sdarrenr 1112145522Sdarrenr if (ISALPHA(c)) { 1113145522Sdarrenr cmd[0] = TOUPPER(c); 111460857Sdarrenr c = *s++; 111560857Sdarrenr i--; 1116145522Sdarrenr if (ISALPHA(c)) { 1117145522Sdarrenr cmd[1] = TOUPPER(c); 111860857Sdarrenr c = *s++; 111960857Sdarrenr i--; 1120145522Sdarrenr if (ISALPHA(c)) { 1121145522Sdarrenr cmd[2] = TOUPPER(c); 112260857Sdarrenr c = *s++; 112360857Sdarrenr i--; 1124145522Sdarrenr if (ISALPHA(c)) { 1125145522Sdarrenr cmd[3] = TOUPPER(c); 112692685Sdarrenr c = *s++; 112792685Sdarrenr i--; 112892685Sdarrenr if ((c != ' ') && (c != '\r')) 1129110916Sdarrenr goto bad_client_command; 113092685Sdarrenr } else if ((c != ' ') && (c != '\r')) 1131110916Sdarrenr goto bad_client_command; 113260857Sdarrenr } else 1133110916Sdarrenr goto bad_client_command; 113460857Sdarrenr } else 1135110916Sdarrenr goto bad_client_command; 1136110916Sdarrenr } else { 1137110916Sdarrenrbad_client_command: 1138255332Scy DT4(client_junk, int, len, int, i, int, c, char *, buf); 1139255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 1140145522Sdarrenr printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n", 1141255332Scy "ipf_p_ftp_client_valid", 1142145522Sdarrenr ftps->ftps_junk, (int)len, (int)i, c, 1143145522Sdarrenr (int)len, (int)len, buf); 1144255332Scy return FTPXY_JUNK_BAD; 1145110916Sdarrenr } 1146110916Sdarrenr 114792685Sdarrenr for (; i; i--) { 1148145522Sdarrenr pc = c; 114960857Sdarrenr c = *s++; 1150145522Sdarrenr if ((pc == '\r') && (c == '\n')) { 1151110916Sdarrenr cmd[4] = '\0'; 1152255332Scy if (!strcmp(cmd, "PASV")) { 1153255332Scy ftps->ftps_cmd = FTPXY_C_PASV; 1154255332Scy } else if (!strcmp(cmd, "EPSV")) { 1155255332Scy ftps->ftps_cmd = FTPXY_C_EPSV; 1156255332Scy } else { 1157255332Scy ftps->ftps_cmd = 0; 1158255332Scy } 115992685Sdarrenr return 0; 1160110916Sdarrenr } 116192685Sdarrenr } 1162145522Sdarrenr#if !defined(_KERNEL) 1163255332Scy printf("ipf_p_ftp_client_valid:junk after cmd[%*.*s]\n", 1164145522Sdarrenr (int)len, (int)len, buf); 1165110916Sdarrenr#endif 1166255332Scy return FTPXY_JUNK_EOL; 116792685Sdarrenr} 116892685Sdarrenr 116992685Sdarrenr 1170255332Scyint 1171255332Scyipf_p_ftp_server_valid(softf, ftps, buf, len) 1172255332Scy ipf_ftp_softc_t *softf; 1173255332Scy ftpside_t *ftps; 1174255332Scy char *buf; 1175255332Scy size_t len; 117692685Sdarrenr{ 1177145522Sdarrenr register char *s, c, pc; 117892685Sdarrenr register size_t i = len; 1179110916Sdarrenr int cmd; 118092685Sdarrenr 1181145522Sdarrenr s = buf; 1182145522Sdarrenr cmd = 0; 1183145522Sdarrenr 1184255332Scy if (ftps->ftps_junk == FTPXY_JUNK_BAD) 1185255332Scy return FTPXY_JUNK_BAD; 1186145522Sdarrenr 1187145522Sdarrenr if (i < 5) { 1188255332Scy DT1(server_valid, int, i); 1189255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 1190255332Scy printf("ipf_p_ftp_servert_valid:i(%d) < 5\n", (int)i); 119192685Sdarrenr return 2; 1192145522Sdarrenr } 1193145522Sdarrenr 119492685Sdarrenr c = *s++; 119592685Sdarrenr i--; 1196255332Scy if (c == ' ') { 1197255332Scy cmd = -1; 1198145522Sdarrenr goto search_eol; 1199255332Scy } 120092685Sdarrenr 1201145522Sdarrenr if (ISDIGIT(c)) { 1202110916Sdarrenr cmd = (c - '0') * 100; 120392685Sdarrenr c = *s++; 120460857Sdarrenr i--; 1205145522Sdarrenr if (ISDIGIT(c)) { 1206110916Sdarrenr cmd += (c - '0') * 10; 120760857Sdarrenr c = *s++; 120860857Sdarrenr i--; 1209145522Sdarrenr if (ISDIGIT(c)) { 1210110916Sdarrenr cmd += (c - '0'); 121160857Sdarrenr c = *s++; 121260857Sdarrenr i--; 121392685Sdarrenr if ((c != '-') && (c != ' ')) 1214110916Sdarrenr goto bad_server_command; 1215255332Scy if (c == '-') 1216255332Scy return FTPXY_JUNK_CONT; 121760857Sdarrenr } else 1218110916Sdarrenr goto bad_server_command; 121960857Sdarrenr } else 1220110916Sdarrenr goto bad_server_command; 1221110916Sdarrenr } else { 1222110916Sdarrenrbad_server_command: 1223255332Scy DT4(server_junk, int len, buf, int, i, int, c, char *, buf); 1224255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) 1225145522Sdarrenr printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n", 1226255332Scy "ipf_p_ftp_server_valid", 1227145522Sdarrenr ftps->ftps_junk, (int)len, (int)i, 1228145522Sdarrenr c, (int)len, (int)len, buf); 1229255332Scy if (ftps->ftps_junk == FTPXY_JUNK_CONT) 1230255332Scy return FTPXY_JUNK_CONT; 1231255332Scy return FTPXY_JUNK_BAD; 1232110916Sdarrenr } 1233145522Sdarrenrsearch_eol: 123460857Sdarrenr for (; i; i--) { 1235145522Sdarrenr pc = c; 123660857Sdarrenr c = *s++; 1237145522Sdarrenr if ((pc == '\r') && (c == '\n')) { 1238255332Scy if (cmd == -1) { 1239255332Scy if (ftps->ftps_junk == FTPXY_JUNK_CONT) 1240255332Scy return FTPXY_JUNK_CONT; 1241255332Scy } else { 1242255332Scy ftps->ftps_cmd = cmd; 1243255332Scy } 1244255332Scy return FTPXY_JUNK_OK; 1245110916Sdarrenr } 124660857Sdarrenr } 1247255332Scy 1248255332Scy DT2(junk_eol, int, len, char *, buf); 1249255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) 1250255332Scy printf("ipf_p_ftp_server_valid:junk after cmd[%*.*s]\n", 1251145522Sdarrenr (int)len, (int)len, buf); 1252255332Scy return FTPXY_JUNK_EOL; 125360857Sdarrenr} 125460857Sdarrenr 125560857Sdarrenr 1256255332Scyint 1257255332Scyipf_p_ftp_valid(softf, ftp, side, buf, len) 1258255332Scy ipf_ftp_softc_t *softf; 1259255332Scy ftpinfo_t *ftp; 1260255332Scy int side; 1261255332Scy char *buf; 1262255332Scy size_t len; 126392685Sdarrenr{ 1264110916Sdarrenr ftpside_t *ftps; 126592685Sdarrenr int ret; 126692685Sdarrenr 1267110916Sdarrenr ftps = &ftp->ftp_side[side]; 1268110916Sdarrenr 126992685Sdarrenr if (side == 0) 1270255332Scy ret = ipf_p_ftp_client_valid(softf, ftps, buf, len); 127192685Sdarrenr else 1272255332Scy ret = ipf_p_ftp_server_valid(softf, ftps, buf, len); 127392685Sdarrenr return ret; 127492685Sdarrenr} 127592685Sdarrenr 127692685Sdarrenr 1277102520Sdarrenr/* 1278145522Sdarrenr * For map rules, the following applies: 1279102520Sdarrenr * rv == 0 for outbound processing, 1280102520Sdarrenr * rv == 1 for inbound processing. 1281145522Sdarrenr * For rdr rules, the following applies: 1282145522Sdarrenr * rv == 0 for inbound processing, 1283145522Sdarrenr * rv == 1 for outbound processing. 1284102520Sdarrenr */ 1285255332Scyint 1286255332Scyipf_p_ftp_process(softf, fin, nat, ftp, rv) 1287255332Scy ipf_ftp_softc_t *softf; 1288255332Scy fr_info_t *fin; 1289255332Scy nat_t *nat; 1290255332Scy ftpinfo_t *ftp; 1291255332Scy int rv; 129260857Sdarrenr{ 1293255332Scy int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff, retry; 1294145522Sdarrenr char *rptr, *wptr, *s; 1295102520Sdarrenr u_32_t thseq, thack; 1296102520Sdarrenr ap_session_t *aps; 129763523Sdarrenr ftpside_t *f, *t; 129860857Sdarrenr tcphdr_t *tcp; 1299145522Sdarrenr ip_t *ip; 130060857Sdarrenr mb_t *m; 130160857Sdarrenr 1302145522Sdarrenr m = fin->fin_m; 1303145522Sdarrenr ip = fin->fin_ip; 130460857Sdarrenr tcp = (tcphdr_t *)fin->fin_dp; 1305145522Sdarrenr off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 130660857Sdarrenr 1307145522Sdarrenr f = &ftp->ftp_side[rv]; 1308145522Sdarrenr t = &ftp->ftp_side[1 - rv]; 1309145522Sdarrenr thseq = ntohl(tcp->th_seq); 1310145522Sdarrenr thack = ntohl(tcp->th_ack); 1311145522Sdarrenr mlen = MSGDSIZE(m) - off; 131272006Sdarrenr 1313255332Scy DT3(process_debug, tcphdr_t *, tcp, int, off, int, mlen); 1314255332Scy if (softf->ipf_p_ftp_debug & DEBUG_INFO) 1315255332Scy printf("ipf_p_ftp_process: %d:%d,%d, mlen %d flags %x\n", 1316255332Scy fin->fin_out, fin->fin_sport, fin->fin_dport, 1317255332Scy mlen, tcp->th_flags); 1318255332Scy 1319161356Sguido if ((mlen == 0) && ((tcp->th_flags & TH_OPENING) == TH_OPENING)) { 1320161356Sguido f->ftps_seq[0] = thseq + 1; 1321161356Sguido t->ftps_seq[0] = thack; 1322145522Sdarrenr return 0; 1323161356Sguido } else if (mlen < 0) { 1324161356Sguido return 0; 1325145522Sdarrenr } 1326161356Sguido 1327102520Sdarrenr aps = nat->nat_aps; 132860857Sdarrenr 1329102520Sdarrenr sel = aps->aps_sel[1 - rv]; 1330102520Sdarrenr sel2 = aps->aps_sel[rv]; 1331255332Scy if (rv == 1) { 1332102520Sdarrenr seqoff = aps->aps_seqoff[sel]; 1333102520Sdarrenr if (aps->aps_seqmin[sel] > seqoff + thseq) 1334102520Sdarrenr seqoff = aps->aps_seqoff[!sel]; 1335102520Sdarrenr ackoff = aps->aps_ackoff[sel2]; 1336102520Sdarrenr if (aps->aps_ackmin[sel2] > ackoff + thack) 1337102520Sdarrenr ackoff = aps->aps_ackoff[!sel2]; 1338102520Sdarrenr } else { 1339102520Sdarrenr seqoff = aps->aps_ackoff[sel]; 1340255332Scy if (softf->ipf_p_ftp_debug & DEBUG_INFO) 1341145522Sdarrenr printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq, 1342145522Sdarrenr aps->aps_ackmin[sel]); 1343102520Sdarrenr if (aps->aps_ackmin[sel] > seqoff + thseq) 1344102520Sdarrenr seqoff = aps->aps_ackoff[!sel]; 134560857Sdarrenr 1346102520Sdarrenr ackoff = aps->aps_seqoff[sel2]; 1347255332Scy if (softf->ipf_p_ftp_debug & DEBUG_INFO) 1348145522Sdarrenr printf("ackoff %d thack %x seqmin %x\n", ackoff, thack, 1349145522Sdarrenr aps->aps_seqmin[sel2]); 1350102520Sdarrenr if (ackoff > 0) { 1351102520Sdarrenr if (aps->aps_seqmin[sel2] > ackoff + thack) 1352102520Sdarrenr ackoff = aps->aps_seqoff[!sel2]; 1353102520Sdarrenr } else { 1354102520Sdarrenr if (aps->aps_seqmin[sel2] > thack) 1355102520Sdarrenr ackoff = aps->aps_seqoff[!sel2]; 1356102520Sdarrenr } 135795563Sdarrenr } 1358255332Scy if (softf->ipf_p_ftp_debug & DEBUG_INFO) { 1359145522Sdarrenr printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n", 1360145522Sdarrenr rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff, 1361145522Sdarrenr thack, ackoff, mlen, fin->fin_plen, off); 1362145522Sdarrenr printf("sel %d seqmin %x/%x offset %d/%d\n", sel, 1363145522Sdarrenr aps->aps_seqmin[sel], aps->aps_seqmin[sel2], 1364145522Sdarrenr aps->aps_seqoff[sel], aps->aps_seqoff[sel2]); 1365145522Sdarrenr printf("sel %d ackmin %x/%x offset %d/%d\n", sel2, 1366145522Sdarrenr aps->aps_ackmin[sel], aps->aps_ackmin[sel2], 1367145522Sdarrenr aps->aps_ackoff[sel], aps->aps_ackoff[sel2]); 1368145522Sdarrenr } 1369102520Sdarrenr 137060857Sdarrenr /* 137160857Sdarrenr * XXX - Ideally, this packet should get dropped because we now know 137260857Sdarrenr * that it is out of order (and there is no real danger in doing so 137360857Sdarrenr * apart from causing packets to go through here ordered). 137460857Sdarrenr */ 1375255332Scy if (softf->ipf_p_ftp_debug & DEBUG_INFO) { 1376145522Sdarrenr printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n", 1377145522Sdarrenr rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff); 1378145522Sdarrenr } 1379102520Sdarrenr 1380102520Sdarrenr ok = 0; 1381110916Sdarrenr if (t->ftps_seq[0] == 0) { 1382110916Sdarrenr t->ftps_seq[0] = thack; 1383110916Sdarrenr ok = 1; 1384110916Sdarrenr } else { 1385102520Sdarrenr if (ackoff == 0) { 1386102520Sdarrenr if (t->ftps_seq[0] == thack) 1387102520Sdarrenr ok = 1; 1388102520Sdarrenr else if (t->ftps_seq[1] == thack) { 1389102520Sdarrenr t->ftps_seq[0] = thack; 1390102520Sdarrenr ok = 1; 1391102520Sdarrenr } 1392102520Sdarrenr } else { 1393255332Scy if (t->ftps_seq[0] + ackoff == thack) { 1394255332Scy t->ftps_seq[0] = thack; 1395102520Sdarrenr ok = 1; 1396255332Scy } else if (t->ftps_seq[0] == thack + ackoff) { 1397255332Scy t->ftps_seq[0] = thack + ackoff; 1398102520Sdarrenr ok = 1; 1399255332Scy } else if (t->ftps_seq[1] + ackoff == thack) { 1400255332Scy t->ftps_seq[0] = thack; 1401102520Sdarrenr ok = 1; 1402102520Sdarrenr } else if (t->ftps_seq[1] == thack + ackoff) { 1403255332Scy t->ftps_seq[0] = thack + ackoff; 1404102520Sdarrenr ok = 1; 1405102520Sdarrenr } 1406102520Sdarrenr } 1407102520Sdarrenr } 1408102520Sdarrenr 1409255332Scy if (softf->ipf_p_ftp_debug & DEBUG_INFO) { 1410145522Sdarrenr if (!ok) 1411145522Sdarrenr printf("%s ok\n", "not"); 1412145522Sdarrenr } 1413102520Sdarrenr 1414102520Sdarrenr if (!mlen) { 1415255332Scy if (t->ftps_seq[0] + ackoff != thack && 1416255332Scy t->ftps_seq[1] + ackoff != thack) { 1417255332Scy DT3(thack, ftpside_t *t, t, int, ackoff, u_32_t, thack); 1418255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { 1419255332Scy printf("%s:seq[0](%u) + (%d) != (%u)\n", 1420255332Scy "ipf_p_ftp_process", t->ftps_seq[0], 1421145522Sdarrenr ackoff, thack); 1422255332Scy printf("%s:seq[0](%u) + (%d) != (%u)\n", 1423255332Scy "ipf_p_ftp_process", t->ftps_seq[1], 1424255332Scy ackoff, thack); 1425145522Sdarrenr } 142695563Sdarrenr return APR_ERR(1); 1427110916Sdarrenr } 1428102520Sdarrenr 1429255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE) { 1430255332Scy printf("ipf_p_ftp_process:f:seq[0] %x seq[1] %x\n", 1431145522Sdarrenr f->ftps_seq[0], f->ftps_seq[1]); 1432145522Sdarrenr } 1433145522Sdarrenr 1434102520Sdarrenr if (tcp->th_flags & TH_FIN) { 1435110916Sdarrenr if (thseq == f->ftps_seq[1]) { 1436110916Sdarrenr f->ftps_seq[0] = f->ftps_seq[1] - seqoff; 1437110916Sdarrenr f->ftps_seq[1] = thseq + 1 - seqoff; 1438110916Sdarrenr } else { 1439255332Scy DT2(thseq, ftpside_t *t, t, u_32_t, thseq); 1440255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { 1441255332Scy printf("FIN: thseq %x seqoff %d ftps_seq %x\n", 1442255332Scy thseq, seqoff, f->ftps_seq[0]); 1443145522Sdarrenr } 1444102520Sdarrenr return APR_ERR(1); 1445102520Sdarrenr } 144695563Sdarrenr } 1447102520Sdarrenr f->ftps_len = 0; 1448102520Sdarrenr return 0; 144960857Sdarrenr } 1450102520Sdarrenr 1451102520Sdarrenr ok = 0; 1452110916Sdarrenr if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) { 1453102520Sdarrenr ok = 1; 1454102520Sdarrenr /* 1455102520Sdarrenr * Retransmitted data packet. 1456102520Sdarrenr */ 1457110916Sdarrenr } else if ((thseq + mlen == f->ftps_seq[0]) || 1458110916Sdarrenr (thseq + mlen == f->ftps_seq[1])) { 1459102520Sdarrenr ok = 1; 1460110916Sdarrenr } 1461110916Sdarrenr 1462102520Sdarrenr if (ok == 0) { 1463255332Scy DT3(ok_0, ftpside_t *, f, u_32_t, thseq, int, mlen); 1464102520Sdarrenr inc = thseq - f->ftps_seq[0]; 1465255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) { 1466145522Sdarrenr printf("inc %d sel %d rv %d\n", inc, sel, rv); 1467145522Sdarrenr printf("th_seq %x ftps_seq %x/%x\n", 1468145522Sdarrenr thseq, f->ftps_seq[0], f->ftps_seq[1]); 1469145522Sdarrenr printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel], 1470145522Sdarrenr aps->aps_ackoff[sel]); 1471145522Sdarrenr printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel], 1472145522Sdarrenr aps->aps_seqoff[sel]); 1473145522Sdarrenr } 1474102520Sdarrenr 1475102520Sdarrenr return APR_ERR(1); 1476102520Sdarrenr } 1477102520Sdarrenr 147895563Sdarrenr inc = 0; 1479102520Sdarrenr rptr = f->ftps_rptr; 1480102520Sdarrenr wptr = f->ftps_wptr; 1481102520Sdarrenr f->ftps_seq[0] = thseq; 1482102520Sdarrenr f->ftps_seq[1] = f->ftps_seq[0] + mlen; 148372006Sdarrenr f->ftps_len = mlen; 148460857Sdarrenr 148560857Sdarrenr while (mlen > 0) { 1486145522Sdarrenr len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr)); 1487255332Scy if (len == 0) 1488255332Scy break; 1489145522Sdarrenr COPYDATA(m, off, len, wptr); 149060857Sdarrenr mlen -= len; 149160857Sdarrenr off += len; 149260857Sdarrenr wptr += len; 1493145522Sdarrenr 1494255332Scywhilemore: 1495255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE) 1496145522Sdarrenr printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n", 1497255332Scy "ipf_p_ftp_process", 1498145522Sdarrenr len, mlen, off, (u_long)wptr, f->ftps_junk, 1499145522Sdarrenr len, len, rptr); 1500145522Sdarrenr 150160857Sdarrenr f->ftps_wptr = wptr; 1502255332Scy if (f->ftps_junk != FTPXY_JUNK_OK) { 1503145522Sdarrenr i = f->ftps_junk; 1504255332Scy f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr, 1505110916Sdarrenr wptr - rptr); 1506255332Scy DT2(junk_transit, int, i, int, f->ftps_junk); 150760857Sdarrenr 1508255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE) 1509145522Sdarrenr printf("%s:junk %d -> %d\n", 1510255332Scy "ipf_p_ftp_process", i, f->ftps_junk); 1511145522Sdarrenr 1512255332Scy if (f->ftps_junk == FTPXY_JUNK_BAD) { 1513255332Scy DT(buffer_full); 1514145522Sdarrenr if (wptr - rptr == sizeof(f->ftps_buf)) { 1515255332Scy if (softf->ipf_p_ftp_debug & 1516255332Scy DEBUG_PARSE_INFO) 1517145522Sdarrenr printf("%s:full buffer\n", 1518255332Scy "ipf_p_ftp_process"); 1519145522Sdarrenr f->ftps_rptr = f->ftps_buf; 1520145522Sdarrenr f->ftps_wptr = f->ftps_buf; 1521145522Sdarrenr rptr = f->ftps_rptr; 1522145522Sdarrenr wptr = f->ftps_wptr; 1523145522Sdarrenr continue; 1524145522Sdarrenr } 1525145522Sdarrenr } 1526145522Sdarrenr } 1527145522Sdarrenr 1528255332Scy while ((f->ftps_junk == FTPXY_JUNK_OK) && (wptr > rptr)) { 1529145522Sdarrenr len = wptr - rptr; 1530255332Scy f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, 1531255332Scy rptr, len); 1532145522Sdarrenr 1533255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE) { 1534145522Sdarrenr printf("%s=%d len %d rv %d ptr %lx/%lx ", 1535255332Scy "ipf_p_ftp_valid", 1536145522Sdarrenr f->ftps_junk, len, rv, (u_long)rptr, 1537145522Sdarrenr (u_long)wptr); 1538145522Sdarrenr printf("buf [%*.*s]\n", len, len, rptr); 1539145522Sdarrenr } 1540145522Sdarrenr 1541255332Scy if (f->ftps_junk == FTPXY_JUNK_OK) { 1542255332Scy f->ftps_cmds++; 154360857Sdarrenr f->ftps_rptr = rptr; 154460857Sdarrenr if (rv) 1545255332Scy inc += ipf_p_ftp_server(softf, fin, ip, 1546255332Scy nat, ftp, len); 154760857Sdarrenr else 1548255332Scy inc += ipf_p_ftp_client(softf, fin, ip, 1549255332Scy nat, ftp, len); 155060857Sdarrenr rptr = f->ftps_rptr; 155192685Sdarrenr wptr = f->ftps_wptr; 155260857Sdarrenr } 155360857Sdarrenr } 155460857Sdarrenr 155592685Sdarrenr /* 155692685Sdarrenr * Off to a bad start so lets just forget about using the 155792685Sdarrenr * ftp proxy for this connection. 155892685Sdarrenr */ 1559255332Scy if ((f->ftps_cmds == 0) && (f->ftps_junk == FTPXY_JUNK_BAD)) { 1560102520Sdarrenr /* f->ftps_seq[1] += inc; */ 1561145522Sdarrenr 1562255332Scy DT(ftp_junk_cmd); 1563255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) 1564145522Sdarrenr printf("%s:cmds == 0 junk == 1\n", 1565255332Scy "ipf_p_ftp_process"); 156692685Sdarrenr return APR_ERR(2); 156795563Sdarrenr } 156892685Sdarrenr 1569255332Scy retry = 0; 1570255332Scy if ((f->ftps_junk != FTPXY_JUNK_OK) && (rptr < wptr)) { 1571145522Sdarrenr for (s = rptr; s < wptr; s++) { 1572145522Sdarrenr if ((*s == '\r') && (s + 1 < wptr) && 1573145522Sdarrenr (*(s + 1) == '\n')) { 1574145522Sdarrenr rptr = s + 2; 1575255332Scy retry = 1; 1576255332Scy if (f->ftps_junk != FTPXY_JUNK_CONT) 1577255332Scy f->ftps_junk = FTPXY_JUNK_OK; 157867614Sdarrenr break; 1579145522Sdarrenr } 158060857Sdarrenr } 158160857Sdarrenr } 158260857Sdarrenr 158360857Sdarrenr if (rptr == wptr) { 158460857Sdarrenr rptr = wptr = f->ftps_buf; 158560857Sdarrenr } else { 1586145522Sdarrenr /* 1587145522Sdarrenr * Compact the buffer back to the start. The junk 1588145522Sdarrenr * flag should already be set and because we're not 1589145522Sdarrenr * throwing away any data, it is preserved from its 1590145522Sdarrenr * current state. 1591145522Sdarrenr */ 1592145522Sdarrenr if (rptr > f->ftps_buf) { 1593255332Scy bcopy(rptr, f->ftps_buf, wptr - rptr); 1594145522Sdarrenr wptr -= rptr - f->ftps_buf; 1595145522Sdarrenr rptr = f->ftps_buf; 159660857Sdarrenr } 159760857Sdarrenr } 1598145522Sdarrenr f->ftps_rptr = rptr; 1599145522Sdarrenr f->ftps_wptr = wptr; 1600255332Scy if (retry) 1601255332Scy goto whilemore; 160260857Sdarrenr } 160360857Sdarrenr 1604102520Sdarrenr /* f->ftps_seq[1] += inc; */ 1605102520Sdarrenr if (tcp->th_flags & TH_FIN) 1606102520Sdarrenr f->ftps_seq[1]++; 1607255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_INFO) { 1608145522Sdarrenr mlen = MSGDSIZE(m); 1609145522Sdarrenr mlen -= off; 1610145522Sdarrenr printf("ftps_seq[1] = %x inc %d len %d\n", 1611145522Sdarrenr f->ftps_seq[1], inc, mlen); 1612145522Sdarrenr } 1613102520Sdarrenr 161460857Sdarrenr f->ftps_rptr = rptr; 161560857Sdarrenr f->ftps_wptr = wptr; 161664580Sdarrenr return APR_INC(inc); 161760857Sdarrenr} 161860857Sdarrenr 161960857Sdarrenr 1620255332Scyint 1621255332Scyipf_p_ftp_out(arg, fin, aps, nat) 1622255332Scy void *arg; 1623255332Scy fr_info_t *fin; 1624255332Scy ap_session_t *aps; 1625255332Scy nat_t *nat; 162660857Sdarrenr{ 1627255332Scy ipf_ftp_softc_t *softf = arg; 162860857Sdarrenr ftpinfo_t *ftp; 1629145522Sdarrenr int rev; 163060857Sdarrenr 163160857Sdarrenr ftp = aps->aps_data; 163260857Sdarrenr if (ftp == NULL) 163360857Sdarrenr return 0; 1634145522Sdarrenr 1635145522Sdarrenr rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1; 1636145522Sdarrenr if (ftp->ftp_side[1 - rev].ftps_ifp == NULL) 1637145522Sdarrenr ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp; 1638145522Sdarrenr 1639255332Scy return ipf_p_ftp_process(softf, fin, nat, ftp, rev); 164060857Sdarrenr} 164160857Sdarrenr 164260857Sdarrenr 1643255332Scyint 1644255332Scyipf_p_ftp_in(arg, fin, aps, nat) 1645255332Scy void *arg; 1646255332Scy fr_info_t *fin; 1647255332Scy ap_session_t *aps; 1648255332Scy nat_t *nat; 164953642Sguido{ 1650255332Scy ipf_ftp_softc_t *softf = arg; 165160857Sdarrenr ftpinfo_t *ftp; 1652145522Sdarrenr int rev; 165353642Sguido 165460857Sdarrenr ftp = aps->aps_data; 165560857Sdarrenr if (ftp == NULL) 165660857Sdarrenr return 0; 1657145522Sdarrenr 1658145522Sdarrenr rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1; 1659145522Sdarrenr if (ftp->ftp_side[rev].ftps_ifp == NULL) 1660145522Sdarrenr ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp; 1661145522Sdarrenr 1662255332Scy return ipf_p_ftp_process(softf, fin, nat, ftp, 1 - rev); 166353642Sguido} 166460857Sdarrenr 166560857Sdarrenr 166660857Sdarrenr/* 1667255332Scy * ipf_p_ftp_atoi - implement a version of atoi which processes numbers in 166860857Sdarrenr * pairs separated by commas (which are expected to be in the range 0 - 255), 166960857Sdarrenr * returning a 16 bit number combining either side of the , as the MSB and 167060857Sdarrenr * LSB. 167160857Sdarrenr */ 1672255332Scyu_short 1673255332Scyipf_p_ftp_atoi(ptr) 1674255332Scy char **ptr; 167560857Sdarrenr{ 167660857Sdarrenr register char *s = *ptr, c; 167760857Sdarrenr register u_char i = 0, j = 0; 167860857Sdarrenr 1679145522Sdarrenr while (((c = *s++) != '\0') && ISDIGIT(c)) { 168060857Sdarrenr i *= 10; 168160857Sdarrenr i += c - '0'; 168260857Sdarrenr } 168360857Sdarrenr if (c != ',') { 168460857Sdarrenr *ptr = NULL; 168560857Sdarrenr return 0; 168660857Sdarrenr } 1687145522Sdarrenr while (((c = *s++) != '\0') && ISDIGIT(c)) { 168860857Sdarrenr j *= 10; 168960857Sdarrenr j += c - '0'; 169060857Sdarrenr } 169160857Sdarrenr *ptr = s; 169267614Sdarrenr i &= 0xff; 169367614Sdarrenr j &= 0xff; 169460857Sdarrenr return (i << 8) | j; 169560857Sdarrenr} 1696145522Sdarrenr 1697145522Sdarrenr 1698255332Scyint 1699255332Scyipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen) 1700255332Scy ipf_ftp_softc_t *softf; 1701255332Scy fr_info_t *fin; 1702255332Scy ip_t *ip; 1703255332Scy nat_t *nat; 1704255332Scy ftpinfo_t *ftp; 1705255332Scy int dlen; 1706145522Sdarrenr{ 1707255332Scy ftpside_t *f; 1708255332Scy 1709255332Scy /* 1710255332Scy * Check for client sending out EPRT message. 1711255332Scy */ 1712255332Scy if (dlen < IPF_MINEPRTLEN) { 1713255332Scy DT1(epert_dlen, int, dlen); 1714255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 1715255332Scy printf("ipf_p_ftp_eprt:dlen(%d) < IPF_MINEPRTLEN\n", 1716255332Scy dlen); 1717255332Scy return 0; 1718255332Scy } 1719255332Scy 1720255332Scy /* 1721255332Scy * Parse the EPRT command. Format is: 1722255332Scy * "EPRT |1|1.2.3.4|2000|" for IPv4 and 1723255332Scy * "EPRT |2|ef00::1:2|2000|" for IPv6 1724255332Scy */ 1725255332Scy f = &ftp->ftp_side[0]; 1726255332Scy if (f->ftps_rptr[5] != '|') 1727255332Scy return 0; 1728255332Scy if (f->ftps_rptr[5] == f->ftps_rptr[7]) { 1729255332Scy if (f->ftps_rptr[6] == '1' && nat->nat_v[0] == 4) 1730255332Scy return ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen); 1731255332Scy#ifdef USE_INET6 1732255332Scy if (f->ftps_rptr[6] == '2' && nat->nat_v[0] == 6) 1733255332Scy return ipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen); 1734255332Scy#endif 1735255332Scy } 1736255332Scy return 0; 1737255332Scy} 1738255332Scy 1739255332Scy 1740255332Scyint 1741255332Scyipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen) 1742255332Scy ipf_ftp_softc_t *softf; 1743255332Scy fr_info_t *fin; 1744255332Scy ip_t *ip; 1745255332Scy nat_t *nat; 1746255332Scy ftpinfo_t *ftp; 1747255332Scy int dlen; 1748255332Scy{ 1749255332Scy int a1, a2, a3, a4, port, olen, nlen, inc, off; 1750145522Sdarrenr char newbuf[IPF_FTPBUFSZ]; 1751255332Scy char *s, c, delim; 1752255332Scy u_32_t addr, i; 1753255332Scy tcphdr_t *tcp; 1754255332Scy ftpside_t *f; 1755255332Scy mb_t *m; 1756255332Scy 1757255332Scy m = fin->fin_m; 1758255332Scy tcp = (tcphdr_t *)fin->fin_dp; 1759255332Scy off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 1760255332Scy f = &ftp->ftp_side[0]; 1761255332Scy delim = f->ftps_rptr[5]; 1762255332Scy s = f->ftps_rptr + 8; 1763255332Scy 1764255332Scy /* 1765255332Scy * get the IP address. 1766255332Scy */ 1767255332Scy i = 0; 1768255332Scy while (((c = *s++) != '\0') && ISDIGIT(c)) { 1769255332Scy i *= 10; 1770255332Scy i += c - '0'; 1771255332Scy } 1772255332Scy if (i > 255) 1773255332Scy return 0; 1774255332Scy if (c != '.') 1775255332Scy return 0; 1776255332Scy addr = (i << 24); 1777255332Scy 1778255332Scy i = 0; 1779255332Scy while (((c = *s++) != '\0') && ISDIGIT(c)) { 1780255332Scy i *= 10; 1781255332Scy i += c - '0'; 1782255332Scy } 1783255332Scy if (i > 255) 1784255332Scy return 0; 1785255332Scy if (c != '.') 1786255332Scy return 0; 1787255332Scy addr |= (addr << 16); 1788255332Scy 1789255332Scy i = 0; 1790255332Scy while (((c = *s++) != '\0') && ISDIGIT(c)) { 1791255332Scy i *= 10; 1792255332Scy i += c - '0'; 1793255332Scy } 1794255332Scy if (i > 255) 1795255332Scy return 0; 1796255332Scy if (c != '.') 1797255332Scy return 0; 1798255332Scy addr |= (addr << 8); 1799255332Scy 1800255332Scy i = 0; 1801255332Scy while (((c = *s++) != '\0') && ISDIGIT(c)) { 1802255332Scy i *= 10; 1803255332Scy i += c - '0'; 1804255332Scy } 1805255332Scy if (i > 255) 1806255332Scy return 0; 1807255332Scy if (c != delim) 1808255332Scy return 0; 1809255332Scy addr |= addr; 1810255332Scy 1811255332Scy /* 1812255332Scy * Get the port number 1813255332Scy */ 1814255332Scy i = 0; 1815255332Scy while (((c = *s++) != '\0') && ISDIGIT(c)) { 1816255332Scy i *= 10; 1817255332Scy i += c - '0'; 1818255332Scy } 1819255332Scy if (i > 65535) 1820255332Scy return 0; 1821255332Scy if (c != delim) 1822255332Scy return 0; 1823255332Scy port = i; 1824255332Scy 1825255332Scy /* 1826255332Scy * Check for CR-LF at the end of the command string. 1827255332Scy */ 1828255332Scy if ((*s != '\r') || (*(s + 1) != '\n')) { 1829255332Scy DT(eprt4_no_crlf); 1830255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 1831255332Scy printf("ipf_p_ftp_eprt4:missing %s\n", "cr-lf"); 1832255332Scy return 0; 1833255332Scy } 1834255332Scy s += 2; 1835255332Scy 1836255332Scy /* 1837255332Scy * Calculate new address parts for PORT command 1838255332Scy */ 1839255332Scy if (nat->nat_dir == NAT_INBOUND) 1840255332Scy a1 = ntohl(nat->nat_odstaddr); 1841255332Scy else 1842255332Scy a1 = ntohl(ip->ip_src.s_addr); 1843255332Scy a2 = (a1 >> 16) & 0xff; 1844255332Scy a3 = (a1 >> 8) & 0xff; 1845255332Scy a4 = a1 & 0xff; 1846255332Scy a1 >>= 24; 1847255332Scy olen = s - f->ftps_rptr; 1848255332Scy /* DO NOT change this to snprintf! */ 1849255332Scy /* 1850255332Scy * While we could force the use of | as a delimiter here, it makes 1851255332Scy * sense to preserve whatever character is being used by the systems 1852255332Scy * involved in the communication. 1853255332Scy */ 1854255332Scy#if defined(SNPRINTF) && defined(_KERNEL) 1855255332Scy SNPRINTF(newbuf, sizeof(newbuf), "%s %c1%c%u.%u.%u.%u%c%u%c\r\n", 1856255332Scy "EPRT", delim, delim, a1, a2, a3, a4, delim, port, delim); 1857255332Scy#else 1858255332Scy (void) sprintf(newbuf, "%s %c1%c%u.%u.%u.%u%c%u%c\r\n", 1859255332Scy "EPRT", delim, delim, a1, a2, a3, a4, delim, port, 1860255332Scy delim); 1861255332Scy#endif 1862255332Scy 1863255332Scy nlen = strlen(newbuf); 1864255332Scy inc = nlen - olen; 1865255332Scy if ((inc + fin->fin_plen) > 65535) { 1866255332Scy DT2(eprt4_len, int, inc, int, fin->fin_plen); 1867255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) 1868255332Scy printf("ipf_p_ftp_eprt4:inc(%d) + ip->ip_len > 65535\n", 1869255332Scy inc); 1870255332Scy return 0; 1871255332Scy } 1872255332Scy 1873255332Scy off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 1874255332Scy#if !defined(_KERNEL) 1875255332Scy M_ADJ(m, inc); 1876255332Scy#else 1877255332Scy if (inc < 0) 1878255332Scy M_ADJ(m, inc); 1879255332Scy#endif 1880255332Scy /* the mbuf chain will be extended if necessary by m_copyback() */ 1881255332Scy COPYBACK(m, off, nlen, newbuf); 1882255332Scy fin->fin_flx |= FI_DOCKSUM; 1883255332Scy 1884255332Scy if (inc != 0) { 1885255332Scy fin->fin_plen += inc; 1886255332Scy ip->ip_len = htons(fin->fin_plen); 1887255332Scy fin->fin_dlen += inc; 1888255332Scy } 1889255332Scy 1890255332Scy f->ftps_cmd = FTPXY_C_EPRT; 1891255332Scy return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc); 1892255332Scy} 1893255332Scy 1894255332Scy 1895255332Scyint 1896255332Scyipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen) 1897255332Scy ipf_ftp_softc_t *softf; 1898255332Scy fr_info_t *fin; 1899255332Scy ip_t *ip; 1900255332Scy nat_t *nat; 1901255332Scy ftpinfo_t *ftp; 1902255332Scy int dlen; 1903255332Scy{ 1904255332Scy char newbuf[IPF_FTPBUFSZ]; 1905255332Scy u_short ap = 0; 1906255332Scy ftpside_t *f; 1907145522Sdarrenr char *s; 1908145522Sdarrenr 1909255332Scy if ((softf->ipf_p_ftp_forcepasv != 0) && 1910255332Scy (ftp->ftp_side[0].ftps_cmd != FTPXY_C_EPSV)) { 1911255332Scy DT1(epsv_cmd, int, ftp->ftp_side[0].ftps_cmd); 1912255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 1913255332Scy printf("ipf_p_ftp_epsv:ftps_cmd(%d) != FTPXY_C_EPSV\n", 1914255332Scy ftp->ftp_side[0].ftps_cmd); 1915255332Scy return 0; 1916255332Scy } 1917255332Scy f = &ftp->ftp_side[1]; 1918255332Scy 1919145522Sdarrenr#define EPSV_REPLEN 33 1920145522Sdarrenr /* 1921145522Sdarrenr * Check for EPSV reply message. 1922145522Sdarrenr */ 1923255332Scy if (dlen < IPF_MIN229LEN) { 1924145522Sdarrenr return (0); 1925255332Scy } else if (strncmp(f->ftps_rptr, 1926255332Scy "229 Entering Extended Passive Mode", EPSV_REPLEN)) { 1927145522Sdarrenr return (0); 1928255332Scy} 1929145522Sdarrenr 1930145522Sdarrenr /* 1931145522Sdarrenr * Skip the EPSV command + space 1932145522Sdarrenr */ 1933145522Sdarrenr s = f->ftps_rptr + 33; 1934145522Sdarrenr while (*s && !ISDIGIT(*s)) 1935145522Sdarrenr s++; 1936145522Sdarrenr 1937145522Sdarrenr /* 1938145522Sdarrenr * As per RFC 2428, there are no addres components in the EPSV 1939145522Sdarrenr * response. So we'll go straight to getting the port. 1940145522Sdarrenr */ 1941145522Sdarrenr while (*s && ISDIGIT(*s)) { 1942145522Sdarrenr ap *= 10; 1943145522Sdarrenr ap += *s++ - '0'; 1944145522Sdarrenr } 1945145522Sdarrenr 1946145522Sdarrenr if (*s == '|') 1947145522Sdarrenr s++; 1948145522Sdarrenr if (*s == ')') 1949145522Sdarrenr s++; 1950145522Sdarrenr if (*s == '\n') 1951145522Sdarrenr s--; 1952145522Sdarrenr /* 1953145522Sdarrenr * check for CR-LF at the end. 1954145522Sdarrenr */ 1955255332Scy if ((*s != '\r') || (*(s + 1) != '\n')) { 1956145522Sdarrenr return 0; 1957255332Scy } 1958255332Scy s += 2; 1959145522Sdarrenr 1960145522Sdarrenr#if defined(SNPRINTF) && defined(_KERNEL) 1961145522Sdarrenr SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n", 1962145522Sdarrenr "229 Entering Extended Passive Mode", ap); 1963145522Sdarrenr#else 1964145522Sdarrenr (void) sprintf(newbuf, "%s (|||%u|)\r\n", 1965145522Sdarrenr "229 Entering Extended Passive Mode", ap); 1966145522Sdarrenr#endif 1967145522Sdarrenr 1968255332Scy return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (u_int)ap, 1969255332Scy newbuf, s); 1970145522Sdarrenr} 1971255332Scy 1972255332Scy#ifdef USE_INET6 1973255332Scyint 1974255332Scyipf_p_ftp_eprt6(softf, fin, ip, nat, ftp, dlen) 1975255332Scy ipf_ftp_softc_t *softf; 1976255332Scy fr_info_t *fin; 1977255332Scy ip_t *ip; 1978255332Scy nat_t *nat; 1979255332Scy ftpinfo_t *ftp; 1980255332Scy int dlen; 1981255332Scy{ 1982255332Scy int port, olen, nlen, inc, off, left, i; 1983255332Scy char newbuf[IPF_FTPBUFSZ]; 1984255332Scy char *s, c; 1985255332Scy i6addr_t addr, *a6; 1986255332Scy tcphdr_t *tcp; 1987255332Scy ip6_t *ip6; 1988255332Scy char delim; 1989255332Scy u_short whole; 1990255332Scy u_short part; 1991255332Scy ftpside_t *f; 1992255332Scy u_short *t; 1993255332Scy int fwd; 1994255332Scy mb_t *m; 1995255332Scy u_32_t a; 1996255332Scy 1997255332Scy m = fin->fin_m; 1998255332Scy ip6 = (ip6_t *)ip; 1999255332Scy f = &ftp->ftp_side[0]; 2000255332Scy s = f->ftps_rptr + 8; 2001255332Scy f = &ftp->ftp_side[0]; 2002255332Scy delim = f->ftps_rptr[5]; 2003255332Scy tcp = (tcphdr_t *)fin->fin_dp; 2004255332Scy off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 2005255332Scy 2006255332Scy addr.i6[0] = 0; 2007255332Scy addr.i6[1] = 0; 2008255332Scy addr.i6[2] = 0; 2009255332Scy addr.i6[3] = 0; 2010255332Scy /* 2011255332Scy * Parse an IPv6 address. 2012255332Scy * Go forward until either :: or | is found. If :: is found, 2013255332Scy * reverse direction. Direction change is performed to ease 2014255332Scy * parsing an unknown number of 0s in the middle. 2015255332Scy */ 2016255332Scy whole = 0; 2017255332Scy t = (u_short *)&addr; 2018255332Scy fwd = 1; 2019255332Scy for (part = 0; (c = *s) != '\0'; ) { 2020255332Scy if (c == delim) { 2021255332Scy *t = htons((u_short)whole); 2022255332Scy break; 2023255332Scy } 2024255332Scy if (c == ':') { 2025255332Scy *t = part; 2026255332Scy if (fwd) { 2027255332Scy *t = htons((u_short)whole); 2028255332Scy t++; 2029255332Scy } else { 2030255332Scy *t = htons((u_short)(whole >> 16)); 2031255332Scy t--; 2032255332Scy } 2033255332Scy whole = 0; 2034255332Scy if (fwd == 1 && s[1] == ':') { 2035255332Scy while (*s && *s != '|') 2036255332Scy s++; 2037255332Scy if ((c = *s) != delim) 2038255332Scy break; 2039255332Scy t = (u_short *)&addr.i6[3]; 2040255332Scy t++; 2041255332Scy fwd = 0; 2042255332Scy } else if (fwd == 0 && s[-1] == ':') { 2043255332Scy break; 2044255332Scy } 2045255332Scy } else { 2046255332Scy if (c >= '0' && c <= '9') { 2047255332Scy c -= '0'; 2048255332Scy } else if (c >= 'a' && c <= 'f') { 2049255332Scy c -= 'a' + 10; 2050255332Scy } else if (c >= 'A' && c <= 'F') { 2051255332Scy c -= 'A' + 10; 2052255332Scy } 2053255332Scy if (fwd) { 2054255332Scy whole <<= 8; 2055255332Scy whole |= c; 2056255332Scy } else { 2057255332Scy whole >>= 8; 2058255332Scy whole |= ((u_32_t)c) << 24; 2059255332Scy } 2060255332Scy } 2061255332Scy if (fwd) 2062255332Scy s++; 2063255332Scy else 2064255332Scy s--; 2065255332Scy } 2066255332Scy if (c != ':' && c != delim) 2067255332Scy return 0; 2068255332Scy 2069255332Scy while (*s != '|') 2070255332Scy s++; 2071255332Scy s++; 2072255332Scy 2073255332Scy /* 2074255332Scy * Get the port number 2075255332Scy */ 2076255332Scy i = 0; 2077255332Scy while (((c = *s++) != '\0') && ISDIGIT(c)) { 2078255332Scy i *= 10; 2079255332Scy i += c - '0'; 2080255332Scy } 2081255332Scy if (i > 65535) 2082255332Scy return 0; 2083255332Scy if (c != delim) 2084255332Scy return 0; 2085255332Scy port = (u_short)(i & 0xffff); 2086255332Scy 2087255332Scy /* 2088255332Scy * Check for CR-LF at the end of the command string. 2089255332Scy */ 2090255332Scy if ((*s != '\r') || (*(s + 1) != '\n')) { 2091255332Scy DT(eprt6_no_crlf); 2092255332Scy if (softf->ipf_p_ftp_debug & DEBUG_PARSE_ERR) 2093255332Scy printf("ipf_p_ftp_eprt6:missing %s\n", "cr-lf"); 2094255332Scy return 0; 2095255332Scy } 2096255332Scy s += 2; 2097255332Scy 2098255332Scy /* 2099255332Scy * Calculate new address parts for PORT command 2100255332Scy */ 2101255332Scy a6 = (i6addr_t *)&ip6->ip6_src; 2102255332Scy olen = s - f->ftps_rptr; 2103255332Scy /* DO NOT change this to snprintf! */ 2104255332Scy /* 2105255332Scy * While we could force the use of | as a delimiter here, it makes 2106255332Scy * sense to preserve whatever character is being used by the systems 2107255332Scy * involved in the communication. 2108255332Scy */ 2109255332Scy s = newbuf; 2110255332Scy left = sizeof(newbuf); 2111255332Scy#if defined(SNPRINTF) && defined(_KERNEL) 2112255332Scy SNPRINTF(newbuf, left, "EPRT %c2%c", delim, delim); 2113255332Scy left -= strlen(s) + 1; 2114255332Scy s += strlen(s); 2115255332Scy a = ntohl(a6->i6[0]); 2116255332Scy SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff); 2117255332Scy left -= strlen(s); 2118255332Scy s += strlen(s); 2119255332Scy a = ntohl(a6->i6[1]); 2120255332Scy SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff); 2121255332Scy left -= strlen(s); 2122255332Scy s += strlen(s); 2123255332Scy a = ntohl(a6->i6[2]); 2124255332Scy SNPRINTF(s, left, "%x:%x:", a >> 16, a & 0xffff); 2125255332Scy left -= strlen(s); 2126255332Scy s += strlen(s); 2127255332Scy a = ntohl(a6->i6[3]); 2128255332Scy SNPRINTF(s, left, "%x:%x", a >> 16, a & 0xffff); 2129255332Scy left -= strlen(s); 2130255332Scy s += strlen(s); 2131328203Scy SNPRINTF(s, left, "|%d|\r\n", port); 2132255332Scy#else 2133255332Scy (void) sprintf(s, "EPRT %c2%c", delim, delim); 2134255332Scy s += strlen(s); 2135255332Scy a = ntohl(a6->i6[0]); 2136255332Scy sprintf(s, "%x:%x:", a >> 16, a & 0xffff); 2137255332Scy s += strlen(s); 2138255332Scy a = ntohl(a6->i6[1]); 2139255332Scy sprintf(s, "%x:%x:", a >> 16, a & 0xffff); 2140255332Scy left -= strlen(s); 2141255332Scy s += strlen(s); 2142255332Scy a = ntohl(a6->i6[2]); 2143255332Scy sprintf(s, "%x:%x:", a >> 16, a & 0xffff); 2144255332Scy left -= strlen(s); 2145255332Scy s += strlen(s); 2146255332Scy a = ntohl(a6->i6[3]); 2147255332Scy sprintf(s, "%x:%x", a >> 16, a & 0xffff); 2148255332Scy left -= strlen(s); 2149255332Scy s += strlen(s); 2150255332Scy sprintf(s, "|%d|\r\n", port); 2151255332Scy#endif 2152255332Scy nlen = strlen(newbuf); 2153255332Scy inc = nlen - olen; 2154255332Scy if ((inc + fin->fin_plen) > 65535) { 2155255332Scy DT2(eprt6_len, int, inc, int, fin->fin_plen); 2156255332Scy if (softf->ipf_p_ftp_debug & DEBUG_ERROR) 2157255332Scy printf("ipf_p_ftp_eprt6:inc(%d) + ip->ip_len > 65535\n", 2158255332Scy inc); 2159255332Scy return 0; 2160255332Scy } 2161255332Scy 2162255332Scy off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 2163255332Scy#if !defined(_KERNEL) 2164255332Scy M_ADJ(m, inc); 2165255332Scy#else 2166255332Scy if (inc < 0) 2167255332Scy M_ADJ(m, inc); 2168255332Scy#endif 2169255332Scy /* the mbuf chain will be extended if necessary by m_copyback() */ 2170255332Scy COPYBACK(m, off, nlen, newbuf); 2171255332Scy fin->fin_flx |= FI_DOCKSUM; 2172255332Scy 2173255332Scy if (inc != 0) { 2174255332Scy fin->fin_plen += inc; 2175255332Scy ip6->ip6_plen = htons(fin->fin_plen - fin->fin_hlen); 2176255332Scy fin->fin_dlen += inc; 2177255332Scy } 2178255332Scy 2179255332Scy f->ftps_cmd = FTPXY_C_EPRT; 2180255332Scy return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc); 2181255332Scy} 2182255332Scy#endif 2183