ip_ftp_pxy.c revision 92685
1/* 2 * Simple FTP transparent proxy for in-kernel use. For use with the NAT 3 * code. 4 * 5 * $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_ftp_pxy.c 92685 2002-03-19 11:44:16Z darrenr $ 6 */ 7#if SOLARIS && defined(_KERNEL) 8extern kmutex_t ipf_rw; 9#endif 10 11#define isdigit(x) ((x) >= '0' && (x) <= '9') 12#define isupper(x) (((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z')) 13#define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z')) 14#define isalpha(x) (isupper(x) || islower(x)) 15#define toupper(x) (isupper(x) ? (x) : (x) - 'a' + 'A') 16 17#define IPF_FTP_PROXY 18 19#define IPF_MINPORTLEN 18 20#define IPF_MAXPORTLEN 30 21#define IPF_MIN227LEN 39 22#define IPF_MAX227LEN 51 23#define IPF_FTPBUFSZ 96 /* This *MUST* be >= 53! */ 24 25#define FTPXY_GO 0 26#define FTPXY_INIT 1 27#define FTPXY_USER_1 2 28#define FTPXY_USOK_1 3 29#define FTPXY_PASS_1 4 30#define FTPXY_PAOK_1 5 31#define FTPXY_AUTH_1 6 32#define FTPXY_AUOK_1 7 33#define FTPXY_ADAT_1 8 34#define FTPXY_ADOK_1 9 35#define FTPXY_ACCT_1 10 36#define FTPXY_ACOK_1 11 37#define FTPXY_USER_2 12 38#define FTPXY_USOK_2 13 39#define FTPXY_PASS_2 14 40#define FTPXY_PAOK_2 15 41 42int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); 43int ippr_ftp_complete __P((char *, size_t)); 44int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); 45int ippr_ftp_init __P((void)); 46int ippr_ftp_new __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); 47int ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *)); 48int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); 49int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int)); 50int ippr_ftp_process __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); 51int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int)); 52int ippr_ftp_valid __P((int, char *, size_t)); 53int ippr_ftp_server_valid __P((char *, size_t)); 54int ippr_ftp_client_valid __P((char *, size_t)); 55u_short ippr_ftp_atoi __P((char **)); 56 57static frentry_t ftppxyfr; 58int ippr_ftp_pasvonly = 0; 59int ippr_ftp_insecure = 0; 60 61 62/* 63 * Initialize local structures. 64 */ 65int ippr_ftp_init() 66{ 67 bzero((char *)&ftppxyfr, sizeof(ftppxyfr)); 68 ftppxyfr.fr_ref = 1; 69 ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 70 return 0; 71} 72 73 74int ippr_ftp_new(fin, ip, aps, nat) 75fr_info_t *fin; 76ip_t *ip; 77ap_session_t *aps; 78nat_t *nat; 79{ 80 ftpinfo_t *ftp; 81 ftpside_t *f; 82 83 KMALLOC(ftp, ftpinfo_t *); 84 if (ftp == NULL) 85 return -1; 86 aps->aps_data = ftp; 87 aps->aps_psiz = sizeof(ftpinfo_t); 88 89 bzero((char *)ftp, sizeof(*ftp)); 90 f = &ftp->ftp_side[0]; 91 f->ftps_rptr = f->ftps_buf; 92 f->ftps_wptr = f->ftps_buf; 93 f = &ftp->ftp_side[1]; 94 f->ftps_rptr = f->ftps_buf; 95 f->ftps_wptr = f->ftps_buf; 96 ftp->ftp_passok = FTPXY_INIT; 97 return 0; 98} 99 100 101int ippr_ftp_port(fin, ip, nat, f, dlen) 102fr_info_t *fin; 103ip_t *ip; 104nat_t *nat; 105ftpside_t *f; 106int dlen; 107{ 108 tcphdr_t *tcp, tcph, *tcp2 = &tcph; 109 char newbuf[IPF_FTPBUFSZ], *s; 110 u_int a1, a2, a3, a4; 111 struct in_addr swip; 112 u_short a5, a6, sp; 113 size_t nlen, olen; 114 fr_info_t fi; 115 int inc, off; 116 nat_t *ipn; 117 mb_t *m; 118#if SOLARIS 119 mb_t *m1; 120#endif 121 122 tcp = (tcphdr_t *)fin->fin_dp; 123 /* 124 * Check for client sending out PORT message. 125 */ 126 if (dlen < IPF_MINPORTLEN) 127 return 0; 128 off = fin->fin_hlen + (tcp->th_off << 2); 129 /* 130 * Skip the PORT command + space 131 */ 132 s = f->ftps_rptr + 5; 133 /* 134 * Pick out the address components, two at a time. 135 */ 136 a1 = ippr_ftp_atoi(&s); 137 if (!s) 138 return 0; 139 a2 = ippr_ftp_atoi(&s); 140 if (!s) 141 return 0; 142 /* 143 * check that IP address in the PORT/PASV reply is the same as the 144 * sender of the command - prevents using PORT for port scanning. 145 */ 146 a1 <<= 16; 147 a1 |= a2; 148 if (a1 != ntohl(nat->nat_inip.s_addr)) 149 return 0; 150 151 a5 = ippr_ftp_atoi(&s); 152 if (!s) 153 return 0; 154 if (*s == ')') 155 s++; 156 157 /* 158 * check for CR-LF at the end. 159 */ 160 if (*s == '\n') 161 s--; 162 if ((*s == '\r') && (*(s + 1) == '\n')) { 163 s += 2; 164 a6 = a5 & 0xff; 165 } else 166 return 0; 167 a5 >>= 8; 168 a5 &= 0xff; 169 /* 170 * Calculate new address parts for PORT command 171 */ 172 a1 = ntohl(ip->ip_src.s_addr); 173 a2 = (a1 >> 16) & 0xff; 174 a3 = (a1 >> 8) & 0xff; 175 a4 = a1 & 0xff; 176 a1 >>= 24; 177 olen = s - f->ftps_rptr; 178 /* DO NOT change this to snprintf! */ 179 (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", 180 "PORT", a1, a2, a3, a4, a5, a6); 181 182 nlen = strlen(newbuf); 183 inc = nlen - olen; 184 if ((inc + ip->ip_len) > 65535) 185 return 0; 186 187#if SOLARIS 188 m = fin->fin_qfm; 189 for (m1 = m; m1->b_cont; m1 = m1->b_cont) 190 ; 191 if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { 192 mblk_t *nm; 193 194 /* alloc enough to keep same trailer space for lower driver */ 195 nm = allocb(nlen, BPRI_MED); 196 PANIC((!nm),("ippr_ftp_out: allocb failed")); 197 198 nm->b_band = m1->b_band; 199 nm->b_wptr += nlen; 200 201 m1->b_wptr -= olen; 202 PANIC((m1->b_wptr < m1->b_rptr), 203 ("ippr_ftp_out: cannot handle fragmented data block")); 204 205 linkb(m1, nm); 206 } else { 207 if (m1->b_datap->db_struiolim == m1->b_wptr) 208 m1->b_datap->db_struiolim += inc; 209 m1->b_datap->db_struioflag &= ~STRUIO_IP; 210 m1->b_wptr += inc; 211 } 212 copyin_mblk(m, off, nlen, newbuf); 213#else 214 m = *((mb_t **)fin->fin_mp); 215 if (inc < 0) 216 m_adj(m, inc); 217 /* the mbuf chain will be extended if necessary by m_copyback() */ 218 m_copyback(m, off, nlen, newbuf); 219# ifdef M_PKTHDR 220 if (!(m->m_flags & M_PKTHDR)) 221 m->m_pkthdr.len += inc; 222# endif 223#endif 224 if (inc != 0) { 225#if SOLARIS || defined(__sgi) 226 register u_32_t sum1, sum2; 227 228 sum1 = ip->ip_len; 229 sum2 = ip->ip_len + inc; 230 231 /* Because ~1 == -2, We really need ~1 == -1 */ 232 if (sum1 > sum2) 233 sum2--; 234 sum2 -= sum1; 235 sum2 = (sum2 & 0xffff) + (sum2 >> 16); 236 237 fix_outcksum(fin, &ip->ip_sum, sum2); 238#endif 239 ip->ip_len += inc; 240 } 241 242 /* 243 * Add skeleton NAT entry for connection which will come back the 244 * other way. 245 */ 246 sp = (a5 << 8 | a6); 247 /* 248 * Don't allow the PORT command to specify a port < 1024 due to 249 * security crap. 250 */ 251 if (sp < 1024) 252 return 0; 253 /* 254 * The server may not make the connection back from port 20, but 255 * it is the most likely so use it here to check for a conflicting 256 * mapping. 257 */ 258 bcopy((char *)fin, (char *)&fi, sizeof(fi)); 259 fi.fin_data[0] = sp; 260 fi.fin_data[1] = fin->fin_data[1] - 1; 261 ipn = nat_outlookup(&fi, IPN_TCP, nat->nat_p, nat->nat_inip, 262 ip->ip_dst, 0); 263 if (ipn == NULL) { 264 int slen; 265 266 slen = ip->ip_len; 267 ip->ip_len = fin->fin_hlen + sizeof(*tcp2); 268 bzero((char *)tcp2, sizeof(*tcp2)); 269 tcp2->th_win = htons(8192); 270 tcp2->th_sport = htons(sp); 271 tcp2->th_off = 5; 272 tcp2->th_dport = 0; /* XXX - don't specify remote port */ 273 fi.fin_data[1] = 0; 274 fi.fin_dlen = sizeof(*tcp2); 275 fi.fin_dp = (char *)tcp2; 276 fi.fin_fr = &ftppxyfr; 277 fi.fin_out = 1; 278 swip = ip->ip_src; 279 fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; 280 ip->ip_src = nat->nat_inip; 281 ipn = nat_new(&fi, ip, nat->nat_ptr, NULL, IPN_TCP|FI_W_DPORT, 282 NAT_OUTBOUND); 283 if (ipn != NULL) { 284 ipn->nat_age = fr_defnatage; 285 (void) fr_addstate(ip, &fi, NULL, 286 FI_W_DPORT|FI_IGNOREPKT); 287 } 288 ip->ip_len = slen; 289 ip->ip_src = swip; 290 } 291 return APR_INC(inc); 292} 293 294 295int ippr_ftp_client(fin, ip, nat, ftp, dlen) 296fr_info_t *fin; 297nat_t *nat; 298ftpinfo_t *ftp; 299ip_t *ip; 300int dlen; 301{ 302 char *rptr, *wptr, cmd[6], c; 303 ftpside_t *f; 304 int inc, i; 305 306 inc = 0; 307 f = &ftp->ftp_side[0]; 308 rptr = f->ftps_rptr; 309 wptr = f->ftps_wptr; 310 311 for (i = 0; (i < 5) && (i < dlen); i++) { 312 c = rptr[i]; 313 if (isalpha(c)) { 314 cmd[i] = toupper(c); 315 } else { 316 cmd[i] = c; 317 } 318 } 319 cmd[i] = '\0'; 320 321 ftp->ftp_incok = 0; 322 if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) { 323 if (ftp->ftp_passok == FTPXY_ADOK_1 || 324 ftp->ftp_passok == FTPXY_AUOK_1) { 325 ftp->ftp_passok = FTPXY_USER_2; 326 ftp->ftp_incok = 1; 327 } else { 328 ftp->ftp_passok = FTPXY_USER_1; 329 ftp->ftp_incok = 1; 330 } 331 } else if (!strncmp(cmd, "AUTH ", 5)) { 332 ftp->ftp_passok = FTPXY_AUTH_1; 333 ftp->ftp_incok = 1; 334 } else if (!strncmp(cmd, "PASS ", 5)) { 335 if (ftp->ftp_passok == FTPXY_USOK_1) { 336 ftp->ftp_passok = FTPXY_PASS_1; 337 ftp->ftp_incok = 1; 338 } else if (ftp->ftp_passok == FTPXY_USOK_2) { 339 ftp->ftp_passok = FTPXY_PASS_2; 340 ftp->ftp_incok = 1; 341 } 342 } else if ((ftp->ftp_passok == FTPXY_AUOK_1) && 343 !strncmp(cmd, "ADAT ", 5)) { 344 ftp->ftp_passok = FTPXY_ADAT_1; 345 ftp->ftp_incok = 1; 346 } else if ((ftp->ftp_passok == FTPXY_PAOK_1 || 347 ftp->ftp_passok == FTPXY_PAOK_2) && 348 !strncmp(cmd, "ACCT ", 5)) { 349 ftp->ftp_passok = FTPXY_ACCT_1; 350 ftp->ftp_incok = 1; 351 } else if ((ftp->ftp_passok == FTPXY_GO) && !ippr_ftp_pasvonly && 352 !strncmp(cmd, "PORT ", 5)) { 353 inc = ippr_ftp_port(fin, ip, nat, f, dlen); 354 } else if (ippr_ftp_insecure && !ippr_ftp_pasvonly && 355 !strncmp(cmd, "PORT ", 5)) { 356 inc = ippr_ftp_port(fin, ip, nat, f, dlen); 357 } 358 359 while ((*rptr++ != '\n') && (rptr < wptr)) 360 ; 361 f->ftps_rptr = rptr; 362 return inc; 363} 364 365 366int ippr_ftp_pasv(fin, ip, nat, f, dlen) 367fr_info_t *fin; 368ip_t *ip; 369nat_t *nat; 370ftpside_t *f; 371int dlen; 372{ 373 tcphdr_t *tcp, tcph, *tcp2 = &tcph; 374 struct in_addr swip, swip2; 375 u_int a1, a2, a3, a4; 376 u_short a5, a6, dp; 377 fr_info_t fi; 378 nat_t *ipn; 379 int inc; 380 char *s; 381 382#define PASV_REPLEN 24 383 /* 384 * Check for PASV reply message. 385 */ 386 if (dlen < IPF_MIN227LEN) 387 return 0; 388 else if (strncmp(f->ftps_rptr, "227 Entering Passive Mod", PASV_REPLEN)) 389 return 0; 390 391 tcp = (tcphdr_t *)fin->fin_dp; 392 393 /* 394 * Skip the PORT command + space 395 */ 396 s = f->ftps_rptr + PASV_REPLEN; 397 while (*s && !isdigit(*s)) 398 s++; 399 /* 400 * Pick out the address components, two at a time. 401 */ 402 a1 = ippr_ftp_atoi(&s); 403 if (!s) 404 return 0; 405 a2 = ippr_ftp_atoi(&s); 406 if (!s) 407 return 0; 408 409 /* 410 * check that IP address in the PORT/PASV reply is the same as the 411 * sender of the command - prevents using PORT for port scanning. 412 */ 413 a1 <<= 16; 414 a1 |= a2; 415 if (a1 != ntohl(nat->nat_oip.s_addr)) 416 return 0; 417 418 a5 = ippr_ftp_atoi(&s); 419 if (!s) 420 return 0; 421 422 if (*s == ')') 423 s++; 424 if (*s == '.') 425 s++; 426 if (*s == '\n') 427 s--; 428 /* 429 * check for CR-LF at the end. 430 */ 431 if ((*s == '\r') && (*(s + 1) == '\n')) { 432 s += 2; 433 a6 = a5 & 0xff; 434 } else 435 return 0; 436 a5 >>= 8; 437 /* 438 * Calculate new address parts for 227 reply 439 */ 440 a1 = ntohl(ip->ip_src.s_addr); 441 a2 = (a1 >> 16) & 0xff; 442 a3 = (a1 >> 8) & 0xff; 443 a4 = a1 & 0xff; 444 a1 >>= 24; 445 inc = 0; 446#if 0 447 olen = s - f->ftps_rptr; 448 (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n", 449 "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6); 450 nlen = strlen(newbuf); 451 inc = nlen - olen; 452 if ((inc + ip->ip_len) > 65535) 453 return 0; 454 455#if SOLARIS 456 m = fin->fin_qfm; 457 for (m1 = m; m1->b_cont; m1 = m1->b_cont) 458 ; 459 if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { 460 mblk_t *nm; 461 462 /* alloc enough to keep same trailer space for lower driver */ 463 nm = allocb(nlen, BPRI_MED); 464 PANIC((!nm),("ippr_ftp_out: allocb failed")); 465 466 nm->b_band = m1->b_band; 467 nm->b_wptr += nlen; 468 469 m1->b_wptr -= olen; 470 PANIC((m1->b_wptr < m1->b_rptr), 471 ("ippr_ftp_out: cannot handle fragmented data block")); 472 473 linkb(m1, nm); 474 } else { 475 m1->b_wptr += inc; 476 } 477 /*copyin_mblk(m, off, nlen, newbuf);*/ 478#else /* SOLARIS */ 479 m = *((mb_t **)fin->fin_mp); 480 if (inc < 0) 481 m_adj(m, inc); 482 /* the mbuf chain will be extended if necessary by m_copyback() */ 483 /*m_copyback(m, off, nlen, newbuf);*/ 484#endif /* SOLARIS */ 485 if (inc != 0) { 486#if SOLARIS || defined(__sgi) 487 register u_32_t sum1, sum2; 488 489 sum1 = ip->ip_len; 490 sum2 = ip->ip_len + inc; 491 492 /* Because ~1 == -2, We really need ~1 == -1 */ 493 if (sum1 > sum2) 494 sum2--; 495 sum2 -= sum1; 496 sum2 = (sum2 & 0xffff) + (sum2 >> 16); 497 498 fix_outcksum(fin, &ip->ip_sum, sum2); 499#endif /* SOLARIS || defined(__sgi) */ 500 ip->ip_len += inc; 501 } 502#endif /* 0 */ 503 504 /* 505 * Add skeleton NAT entry for connection which will come back the 506 * other way. 507 */ 508 bcopy((char *)fin, (char *)&fi, sizeof(fi)); 509 fi.fin_data[0] = 0; 510 dp = htons(fin->fin_data[1] - 1); 511 fi.fin_data[1] = ntohs(dp); 512 ipn = nat_outlookup(&fi, IPN_TCP, nat->nat_p, nat->nat_inip, 513 ip->ip_dst, 0); 514 if (ipn == NULL) { 515 int slen; 516 517 slen = ip->ip_len; 518 ip->ip_len = fin->fin_hlen + sizeof(*tcp2); 519 bzero((char *)tcp2, sizeof(*tcp2)); 520 tcp2->th_win = htons(8192); 521 tcp2->th_sport = 0; /* XXX - fake it for nat_new */ 522 tcp2->th_off = 5; 523 fi.fin_data[1] = a5 << 8 | a6; 524 fi.fin_dlen = sizeof(*tcp2); 525 tcp2->th_dport = htons(fi.fin_data[1]); 526 fi.fin_data[0] = 0; 527 fi.fin_dp = (char *)tcp2; 528 fi.fin_fr = &ftppxyfr; 529 fi.fin_out = 1; 530 swip = ip->ip_src; 531 swip2 = ip->ip_dst; 532 fi.fin_fi.fi_daddr = ip->ip_src.s_addr; 533 fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; 534 ip->ip_dst = ip->ip_src; 535 ip->ip_src = nat->nat_inip; 536 ipn = nat_new(&fi, ip, nat->nat_ptr, NULL, IPN_TCP|FI_W_SPORT, 537 NAT_OUTBOUND); 538 if (ipn != NULL) { 539 ipn->nat_age = fr_defnatage; 540 (void) fr_addstate(ip, &fi, NULL, 541 FI_W_SPORT|FI_IGNOREPKT); 542 } 543 ip->ip_len = slen; 544 ip->ip_src = swip; 545 ip->ip_dst = swip2; 546 } 547 return inc; 548} 549 550 551int ippr_ftp_server(fin, ip, nat, ftp, dlen) 552fr_info_t *fin; 553ip_t *ip; 554nat_t *nat; 555ftpinfo_t *ftp; 556int dlen; 557{ 558 char *rptr, *wptr; 559 ftpside_t *f; 560 int inc; 561 562 inc = 0; 563 f = &ftp->ftp_side[1]; 564 rptr = f->ftps_rptr; 565 wptr = f->ftps_wptr; 566 567 if (!isdigit(*rptr) || !isdigit(*(rptr + 1)) || !isdigit(*(rptr + 2))) 568 return inc; 569 if (ftp->ftp_passok == FTPXY_GO) { 570 if (!strncmp(rptr, "227 ", 4)) 571 inc = ippr_ftp_pasv(fin, ip, nat, f, dlen); 572 } else if (ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) { 573 inc = ippr_ftp_pasv(fin, ip, nat, f, dlen); 574 } else if (*rptr == '5' || *rptr == '4') 575 ftp->ftp_passok = FTPXY_INIT; 576 else if (ftp->ftp_incok) { 577 if (*rptr == '3') { 578 if (ftp->ftp_passok == FTPXY_ACCT_1) 579 ftp->ftp_passok = FTPXY_GO; 580 else 581 ftp->ftp_passok++; 582 } else if (*rptr == '2') { 583 switch (ftp->ftp_passok) 584 { 585 case FTPXY_USER_1 : 586 case FTPXY_USER_2 : 587 case FTPXY_PASS_1 : 588 case FTPXY_PASS_2 : 589 case FTPXY_ACCT_1 : 590 ftp->ftp_passok = FTPXY_GO; 591 break; 592 default : 593 ftp->ftp_passok += 3; 594 break; 595 } 596 } 597 } 598 ftp->ftp_incok = 0; 599 while ((*rptr++ != '\n') && (rptr < wptr)) 600 ; 601 f->ftps_rptr = rptr; 602 return inc; 603} 604 605 606/* 607 * Look to see if the buffer starts with something which we recognise as 608 * being the correct syntax for the FTP protocol. 609 */ 610int ippr_ftp_client_valid(buf, len) 611char *buf; 612size_t len; 613{ 614 register char *s, c; 615 register size_t i = len; 616 617 if (i < 5) 618 return 2; 619 s = buf; 620 c = *s++; 621 i--; 622 623 if (isalpha(c)) { 624 c = *s++; 625 i--; 626 if (isalpha(c)) { 627 c = *s++; 628 i--; 629 if (isalpha(c)) { 630 c = *s++; 631 i--; 632 if (isalpha(c)) { 633 c = *s++; 634 i--; 635 if ((c != ' ') && (c != '\r')) 636 return 1; 637 } else if ((c != ' ') && (c != '\r')) 638 return 1; 639 } else 640 return 1; 641 } else 642 return 1; 643 } else 644 return 1; 645 for (; i; i--) { 646 c = *s++; 647 if (c == '\n') 648 return 0; 649 } 650 return 2; 651} 652 653 654int ippr_ftp_server_valid(buf, len) 655char *buf; 656size_t len; 657{ 658 register char *s, c; 659 register size_t i = len; 660 661 if (i < 5) 662 return 2; 663 s = buf; 664 c = *s++; 665 i--; 666 667 if (isdigit(c)) { 668 c = *s++; 669 i--; 670 if (isdigit(c)) { 671 c = *s++; 672 i--; 673 if (isdigit(c)) { 674 c = *s++; 675 i--; 676 if ((c != '-') && (c != ' ')) 677 return 1; 678 } else 679 return 1; 680 } else 681 return 1; 682 } else 683 return 1; 684 for (; i; i--) { 685 c = *s++; 686 if (c == '\n') 687 return 0; 688 } 689 return 2; 690} 691 692 693int ippr_ftp_valid(side, buf, len) 694int side; 695char *buf; 696size_t len; 697{ 698 int ret; 699 700 if (side == 0) 701 ret = ippr_ftp_client_valid(buf, len); 702 else 703 ret = ippr_ftp_server_valid(buf, len); 704 return ret; 705} 706 707 708int ippr_ftp_process(fin, ip, nat, ftp, rv) 709fr_info_t *fin; 710ip_t *ip; 711nat_t *nat; 712ftpinfo_t *ftp; 713int rv; 714{ 715 int mlen, len, off, inc, i, sel; 716 char *rptr, *wptr; 717 ftpside_t *f, *t; 718 tcphdr_t *tcp; 719 mb_t *m; 720 721 tcp = (tcphdr_t *)fin->fin_dp; 722 off = fin->fin_hlen + (tcp->th_off << 2); 723 724#if SOLARIS 725 m = fin->fin_qfm; 726#else 727 m = *((mb_t **)fin->fin_mp); 728#endif 729 730#if SOLARIS 731 mlen = msgdsize(m) - off; 732#else 733 mlen = mbufchainlen(m) - off; 734#endif 735 736 t = &ftp->ftp_side[1 - rv]; 737 f = &ftp->ftp_side[rv]; 738 if (!mlen) { 739 if (!t->ftps_seq || 740 (int)ntohl(tcp->th_ack) - (int)t->ftps_seq > 0) 741 t->ftps_seq = ntohl(tcp->th_ack); 742 f->ftps_len = 0; 743 return 0; 744 } 745 746 inc = 0; 747 rptr = f->ftps_rptr; 748 wptr = f->ftps_wptr; 749 750 sel = nat->nat_aps->aps_sel[1 - rv]; 751 if (rv) 752 i = nat->nat_aps->aps_ackoff[sel]; 753 else 754 i = nat->nat_aps->aps_seqoff[sel]; 755 /* 756 * XXX - Ideally, this packet should get dropped because we now know 757 * that it is out of order (and there is no real danger in doing so 758 * apart from causing packets to go through here ordered). 759 */ 760 if (f->ftps_len + f->ftps_seq == ntohl(tcp->th_seq)) 761 f->ftps_seq = ntohl(tcp->th_seq); 762 else if (ntohl(tcp->th_seq) + i != f->ftps_seq) { 763 return APR_ERR(1); 764 } 765 f->ftps_len = mlen; 766 767 while (mlen > 0) { 768 len = MIN(mlen, FTP_BUFSZ / 2); 769 770#if SOLARIS 771 copyout_mblk(m, off, len, wptr); 772#else 773 m_copydata(m, off, len, wptr); 774#endif 775 mlen -= len; 776 off += len; 777 wptr += len; 778 f->ftps_wptr = wptr; 779 if (f->ftps_junk == 2) 780 f->ftps_junk = ippr_ftp_valid(rv, rptr, wptr - rptr); 781 782 while ((f->ftps_junk == 0) && (wptr > rptr)) { 783 f->ftps_junk = ippr_ftp_valid(rv, rptr, wptr - rptr); 784 if (f->ftps_junk == 0) { 785 f->ftps_cmds++; 786 len = wptr - rptr; 787 f->ftps_rptr = rptr; 788 if (rv) 789 inc += ippr_ftp_server(fin, ip, nat, 790 ftp, len); 791 else 792 inc += ippr_ftp_client(fin, ip, nat, 793 ftp, len); 794 rptr = f->ftps_rptr; 795 wptr = f->ftps_wptr; 796 } 797 } 798 799 /* 800 * Off to a bad start so lets just forget about using the 801 * ftp proxy for this connection. 802 */ 803 if ((f->ftps_cmds == 0) && (f->ftps_junk == 1)) 804 return APR_ERR(2); 805 806 while ((f->ftps_junk == 1) && (rptr < wptr)) { 807 while ((rptr < wptr) && (*rptr != '\r')) 808 rptr++; 809 810 if (*rptr == '\r') { 811 if (rptr + 1 < wptr) { 812 if (*(rptr + 1) == '\n') { 813 rptr += 2; 814 f->ftps_junk = 0; 815 } else 816 rptr++; 817 } else 818 break; 819 } 820 } 821 f->ftps_rptr = rptr; 822 823 if (rptr == wptr) { 824 rptr = wptr = f->ftps_buf; 825 } else { 826 if ((wptr > f->ftps_buf + FTP_BUFSZ / 2)) { 827 i = wptr - rptr; 828 if ((rptr == f->ftps_buf) || 829 (wptr - rptr > FTP_BUFSZ / 2)) { 830 f->ftps_junk = 1; 831 rptr = wptr = f->ftps_buf; 832 } else { 833 bcopy(rptr, f->ftps_buf, i); 834 wptr = f->ftps_buf + i; 835 rptr = f->ftps_buf; 836 } 837 } 838 f->ftps_rptr = rptr; 839 f->ftps_wptr = wptr; 840 } 841 } 842 843 t->ftps_seq = ntohl(tcp->th_ack); 844 f->ftps_rptr = rptr; 845 f->ftps_wptr = wptr; 846 return APR_INC(inc); 847} 848 849 850int ippr_ftp_out(fin, ip, aps, nat) 851fr_info_t *fin; 852ip_t *ip; 853ap_session_t *aps; 854nat_t *nat; 855{ 856 ftpinfo_t *ftp; 857 858 ftp = aps->aps_data; 859 if (ftp == NULL) 860 return 0; 861 return ippr_ftp_process(fin, ip, nat, ftp, 0); 862} 863 864 865int ippr_ftp_in(fin, ip, aps, nat) 866fr_info_t *fin; 867ip_t *ip; 868ap_session_t *aps; 869nat_t *nat; 870{ 871 ftpinfo_t *ftp; 872 873 ftp = aps->aps_data; 874 if (ftp == NULL) 875 return 0; 876 return ippr_ftp_process(fin, ip, nat, ftp, 1); 877} 878 879 880/* 881 * ippr_ftp_atoi - implement a version of atoi which processes numbers in 882 * pairs separated by commas (which are expected to be in the range 0 - 255), 883 * returning a 16 bit number combining either side of the , as the MSB and 884 * LSB. 885 */ 886u_short ippr_ftp_atoi(ptr) 887char **ptr; 888{ 889 register char *s = *ptr, c; 890 register u_char i = 0, j = 0; 891 892 while ((c = *s++) && isdigit(c)) { 893 i *= 10; 894 i += c - '0'; 895 } 896 if (c != ',') { 897 *ptr = NULL; 898 return 0; 899 } 900 while ((c = *s++) && isdigit(c)) { 901 j *= 10; 902 j += c - '0'; 903 } 904 *ptr = s; 905 i &= 0xff; 906 j &= 0xff; 907 return (i << 8) | j; 908} 909