1 /*- 2 * Copyright (c) 2001 Charles Mott <cm@linktel.net> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 /* 31 Alias_ftp.c performs special processing for FTP sessions under 32 TCP. Specifically, when a PORT/EPRT command from the client 33 side or 227/229 reply from the server is sent, it is intercepted 34 and modified. The address is changed to the gateway machine 35 and an aliasing port is used. 36 37 For this routine to work, the message must fit entirely into a 38 single TCP packet. This is typically the case, but exceptions 39 can easily be envisioned under the actual specifications. 40 41 Probably the most troubling aspect of the approach taken here is 42 that the new message will typically be a different length, and 43 this causes a certain amount of bookkeeping to keep track of the 44 changes of sequence and acknowledgment numbers, since the client 45 machine is totally unaware of the modification to the TCP stream. 46 47 48 References: RFC 959, RFC 2428. 49 50 Initial version: August, 1996 (cjm) 51 52 Version 1.6 53 Brian Somers and Martin Renters identified an IP checksum 54 error for modified IP packets. 55 56 Version 1.7: January 9, 1996 (cjm) 57 Differential checksum computation for change 58 in IP packet length. 59 60 Version 2.1: May, 1997 (cjm) 61 Very minor changes to conform with 62 local/global/function naming conventions 63 within the packet aliasing module. 64 65 Version 3.1: May, 2000 (eds) 66 Add support for passive mode, alias the 227 replies. 67 68 See HISTORY file for record of revisions. 69 */ 70 71 /* Includes */ 72 #ifdef _KERNEL 73 #include <sys/param.h> 74 #include <sys/ctype.h> 75 #include <sys/systm.h> 76 #include <sys/kernel.h> 77 #include <sys/module.h> 78 #else 79 #include <errno.h> 80 #include <sys/types.h> 81 #include <stdio.h> 82 #endif 83 84 #include <netinet/in_systm.h> 85 #include <netinet/in.h> 86 #include <netinet/ip.h> 87 #include <netinet/tcp.h> 88 89 #ifdef _KERNEL 90 #include <netinet/libalias/alias.h> 91 #include <netinet/libalias/alias_local.h> 92 #include <netinet/libalias/alias_mod.h> 93 #else 94 #include "alias_local.h" 95 #include "alias_mod.h" 96 #endif 97 98 #define FTP_CONTROL_PORT_NUMBER 21 99 100 static void 101 AliasHandleFtpOut(struct libalias *, struct ip *, struct alias_link *, 102 int maxpacketsize); 103 104 static int 105 fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah) 106 { 107 108 if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || 109 ah->maxpktsize == 0) 110 return (-1); 111 if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER 112 || ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER) 113 return (0); 114 return (-1); 115 } 116 117 static int 118 protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) 119 { 120 121 AliasHandleFtpOut(la, pip, ah->lnk, ah->maxpktsize); 122 return (0); 123 } 124 125 struct proto_handler handlers[] = { 126 { 127 .pri = 80, 128 .dir = OUT, 129 .proto = TCP, 130 .fingerprint = &fingerprint, 131 .protohandler = &protohandler 132 }, 133 { EOH } 134 }; 135 136 static int 137 mod_handler(module_t mod, int type, void *data) 138 { 139 int error; 140 141 switch (type) { 142 case MOD_LOAD: 143 error = 0; 144 LibAliasAttachHandlers(handlers); 145 break; 146 case MOD_UNLOAD: 147 error = 0; 148 LibAliasDetachHandlers(handlers); 149 break; 150 default: 151 error = EINVAL; 152 } 153 return (error); 154 } 155 156 #ifdef _KERNEL 157 static 158 #endif 159 moduledata_t alias_mod = { 160 "alias_ftp", mod_handler, NULL 161 }; 162 163 #ifdef _KERNEL 164 DECLARE_MODULE(alias_ftp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); 165 MODULE_VERSION(alias_ftp, 1); 166 MODULE_DEPEND(alias_ftp, libalias, 1, 1, 1); 167 #endif 168 169 #define FTP_CONTROL_PORT_NUMBER 21 170 #define MAX_MESSAGE_SIZE 128 171 172 /* FTP protocol flags. */ 173 #define WAIT_CRLF 0x01 174 175 enum ftp_message_type { 176 FTP_PORT_COMMAND, 177 FTP_EPRT_COMMAND, 178 FTP_227_REPLY, 179 FTP_229_REPLY, 180 FTP_UNKNOWN_MESSAGE 181 }; 182 183 static int ParseFtpPortCommand(struct libalias *la, char *, int); 184 static int ParseFtpEprtCommand(struct libalias *la, char *, int); 185 static int ParseFtp227Reply(struct libalias *la, char *, int); 186 static int ParseFtp229Reply(struct libalias *la, char *, int); 187 static void NewFtpMessage(struct libalias *la, struct ip *, struct alias_link *, int, int); 188 189 static void 190 AliasHandleFtpOut( 191 struct libalias *la, 192 struct ip *pip, /* IP packet to examine/patch */ 193 struct alias_link *lnk, /* The link to go through (aliased port) */ 194 int maxpacketsize /* The maximum size this packet can grow to 195 (including headers) */ ) 196 { 197 int hlen, tlen, dlen, pflags; 198 char *sptr; 199 struct tcphdr *tc; 200 int ftp_message_type; 201 202 /* Calculate data length of TCP packet */ 203 tc = (struct tcphdr *)ip_next(pip); 204 hlen = (pip->ip_hl + tc->th_off) << 2; 205 tlen = ntohs(pip->ip_len); 206 dlen = tlen - hlen; 207 208 /* Place string pointer and beginning of data */ 209 sptr = (char *)pip; 210 sptr += hlen; 211 212 /* 213 * Check that data length is not too long and previous message was 214 * properly terminated with CRLF. 215 */ 216 pflags = GetProtocolFlags(lnk); 217 if (dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) { 218 ftp_message_type = FTP_UNKNOWN_MESSAGE; 219 220 if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) { 221 /* 222 * When aliasing a client, check for the PORT/EPRT command. 223 */ 224 if (ParseFtpPortCommand(la, sptr, dlen)) 225 ftp_message_type = FTP_PORT_COMMAND; 226 else if (ParseFtpEprtCommand(la, sptr, dlen)) 227 ftp_message_type = FTP_EPRT_COMMAND; 228 } else { 229 /* 230 * When aliasing a server, check for the 227/229 reply. 231 */ 232 if (ParseFtp227Reply(la, sptr, dlen)) 233 ftp_message_type = FTP_227_REPLY; 234 else if (ParseFtp229Reply(la, sptr, dlen)) { 235 ftp_message_type = FTP_229_REPLY; 236 la->true_addr.s_addr = pip->ip_src.s_addr; 237 } 238 } 239 240 if (ftp_message_type != FTP_UNKNOWN_MESSAGE) 241 NewFtpMessage(la, pip, lnk, maxpacketsize, ftp_message_type); 242 } 243 /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */ 244 245 if (dlen) { /* only if there's data */ 246 sptr = (char *)pip; /* start over at beginning */ 247 tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may 248 * have grown */ 249 if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n') 250 pflags &= ~WAIT_CRLF; 251 else 252 pflags |= WAIT_CRLF; 253 SetProtocolFlags(lnk, pflags); 254 } 255 } 256 257 static int 258 ParseFtpPortCommand(struct libalias *la, char *sptr, int dlen) 259 { 260 char ch; 261 int i, state; 262 u_int32_t addr; 263 u_short port; 264 u_int8_t octet; 265 266 /* Format: "PORT A,D,D,R,PO,RT". */ 267 268 /* Return if data length is too short. */ 269 if (dlen < 18) 270 return (0); 271 272 addr = port = octet = 0; 273 state = -4; 274 for (i = 0; i < dlen; i++) { 275 ch = sptr[i]; 276 switch (state) { 277 case -4: 278 if (ch == 'P') 279 state++; 280 else 281 return (0); 282 break; 283 case -3: 284 if (ch == 'O') 285 state++; 286 else 287 return (0); 288 break; 289 case -2: 290 if (ch == 'R') 291 state++; 292 else 293 return (0); 294 break; 295 case -1: 296 if (ch == 'T') 297 state++; 298 else 299 return (0); 300 break; 301 302 case 0: 303 if (isspace(ch)) 304 break; 305 else 306 state++; 307 case 1: 308 case 3: 309 case 5: 310 case 7: 311 case 9: 312 case 11: 313 if (isdigit(ch)) { 314 octet = ch - '0'; 315 state++; 316 } else 317 return (0); 318 break; 319 case 2: 320 case 4: 321 case 6: 322 case 8: 323 if (isdigit(ch)) 324 octet = 10 * octet + ch - '0'; 325 else if (ch == ',') { 326 addr = (addr << 8) + octet; 327 state++; 328 } else 329 return (0); 330 break; 331 case 10: 332 case 12: 333 if (isdigit(ch)) 334 octet = 10 * octet + ch - '0'; 335 else if (ch == ',' || state == 12) { 336 port = (port << 8) + octet; 337 state++; 338 } else 339 return (0); 340 break; 341 } 342 } 343 344 if (state == 13) { 345 la->true_addr.s_addr = htonl(addr); 346 la->true_port = port; 347 return (1); 348 } else 349 return (0); 350 } 351 352 static int 353 ParseFtpEprtCommand(struct libalias *la, char *sptr, int dlen) 354 { 355 char ch, delim; 356 int i, state; 357 u_int32_t addr; 358 u_short port; 359 u_int8_t octet; 360 361 /* Format: "EPRT |1|A.D.D.R|PORT|". */ 362 363 /* Return if data length is too short. */ 364 if (dlen < 18) 365 return (0); 366 367 addr = port = octet = 0; 368 delim = '|'; /* XXX gcc -Wuninitialized */ 369 state = -4; 370 for (i = 0; i < dlen; i++) { 371 ch = sptr[i]; 372 switch (state) { 373 case -4: 374 if (ch == 'E') 375 state++; 376 else 377 return (0); 378 break; 379 case -3: 380 if (ch == 'P') 381 state++; 382 else 383 return (0); 384 break; 385 case -2: 386 if (ch == 'R') 387 state++; 388 else 389 return (0); 390 break; 391 case -1: 392 if (ch == 'T') 393 state++; 394 else 395 return (0); 396 break; 397 398 case 0: 399 if (!isspace(ch)) { 400 delim = ch; 401 state++; 402 } 403 break; 404 case 1: 405 if (ch == '1') /* IPv4 address */ 406 state++; 407 else 408 return (0); 409 break; 410 case 2: 411 if (ch == delim) 412 state++; 413 else 414 return (0); 415 break; 416 case 3: 417 case 5: 418 case 7: 419 case 9: 420 if (isdigit(ch)) { 421 octet = ch - '0'; 422 state++; 423 } else 424 return (0); 425 break; 426 case 4: 427 case 6: 428 case 8: 429 case 10: 430 if (isdigit(ch)) 431 octet = 10 * octet + ch - '0'; 432 else if (ch == '.' || state == 10) { 433 addr = (addr << 8) + octet; 434 state++; 435 } else 436 return (0); 437 break; 438 case 11: 439 if (isdigit(ch)) { 440 port = ch - '0'; 441 state++; 442 } else 443 return (0); 444 break; 445 case 12: 446 if (isdigit(ch)) 447 port = 10 * port + ch - '0'; 448 else if (ch == delim) 449 state++; 450 else 451 return (0); 452 break; 453 } 454 } 455 456 if (state == 13) { 457 la->true_addr.s_addr = htonl(addr); 458 la->true_port = port; 459 return (1); 460 } else 461 return (0); 462 } 463 464 static int 465 ParseFtp227Reply(struct libalias *la, char *sptr, int dlen) 466 { 467 char ch; 468 int i, state; 469 u_int32_t addr; 470 u_short port; 471 u_int8_t octet; 472 473 /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */ 474 475 /* Return if data length is too short. */ 476 if (dlen < 17) 477 return (0); 478 479 addr = port = octet = 0; 480 481 state = -3; 482 for (i = 0; i < dlen; i++) { 483 ch = sptr[i]; 484 switch (state) { 485 case -3: 486 if (ch == '2') 487 state++; 488 else 489 return (0); 490 break; 491 case -2: 492 if (ch == '2') 493 state++; 494 else 495 return (0); 496 break; 497 case -1: 498 if (ch == '7') 499 state++; 500 else 501 return (0); 502 break; 503 504 case 0: 505 if (ch == '(') 506 state++; 507 break; 508 case 1: 509 case 3: 510 case 5: 511 case 7: 512 case 9: 513 case 11: 514 if (isdigit(ch)) { 515 octet = ch - '0'; 516 state++; 517 } else 518 return (0); 519 break; 520 case 2: 521 case 4: 522 case 6: 523 case 8: 524 if (isdigit(ch)) 525 octet = 10 * octet + ch - '0'; 526 else if (ch == ',') { 527 addr = (addr << 8) + octet; 528 state++; 529 } else 530 return (0); 531 break; 532 case 10: 533 case 12: 534 if (isdigit(ch)) 535 octet = 10 * octet + ch - '0'; 536 else if (ch == ',' || (state == 12 && ch == ')')) { 537 port = (port << 8) + octet; 538 state++; 539 } else 540 return (0); 541 break; 542 } 543 } 544 545 if (state == 13) { 546 la->true_port = port; 547 la->true_addr.s_addr = htonl(addr); 548 return (1); 549 } else 550 return (0); 551 } 552 553 static int 554 ParseFtp229Reply(struct libalias *la, char *sptr, int dlen) 555 { 556 char ch, delim; 557 int i, state; 558 u_short port; 559 560 /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */ 561 562 /* Return if data length is too short. */ 563 if (dlen < 11) 564 return (0); 565 566 port = 0; 567 delim = '|'; /* XXX gcc -Wuninitialized */ 568 569 state = -3; 570 for (i = 0; i < dlen; i++) { 571 ch = sptr[i]; 572 switch (state) { 573 case -3: 574 if (ch == '2') 575 state++; 576 else 577 return (0); 578 break; 579 case -2: 580 if (ch == '2') 581 state++; 582 else 583 return (0); 584 break; 585 case -1: 586 if (ch == '9') 587 state++; 588 else 589 return (0); 590 break; 591 592 case 0: 593 if (ch == '(') 594 state++; 595 break; 596 case 1: 597 delim = ch; 598 state++; 599 break; 600 case 2: 601 case 3: 602 if (ch == delim) 603 state++; 604 else 605 return (0); 606 break; 607 case 4: 608 if (isdigit(ch)) { 609 port = ch - '0'; 610 state++; 611 } else 612 return (0); 613 break; 614 case 5: 615 if (isdigit(ch)) 616 port = 10 * port + ch - '0'; 617 else if (ch == delim) 618 state++; 619 else 620 return (0); 621 break; 622 case 6: 623 if (ch == ')') 624 state++; 625 else 626 return (0); 627 break; 628 } 629 } 630 631 if (state == 7) { 632 la->true_port = port; 633 return (1); 634 } else 635 return (0); 636 } 637 638 static void 639 NewFtpMessage(struct libalias *la, struct ip *pip, 640 struct alias_link *lnk, 641 int maxpacketsize, 642 int ftp_message_type) 643 { 644 struct alias_link *ftp_lnk; 645 646 /* Security checks. */ 647 if (pip->ip_src.s_addr != la->true_addr.s_addr) 648 return; 649 650 if (la->true_port < IPPORT_RESERVED) 651 return; 652 653 /* Establish link to address and port found in FTP control message. */ 654 ftp_lnk = FindUdpTcpOut(la, la->true_addr, GetDestAddress(lnk), 655 htons(la->true_port), 0, IPPROTO_TCP, 1); 656 657 if (ftp_lnk != NULL) { 658 int slen, hlen, tlen, dlen; 659 struct tcphdr *tc; 660 661 #ifndef NO_FW_PUNCH 662 /* Punch hole in firewall */ 663 PunchFWHole(ftp_lnk); 664 #endif 665 666 /* Calculate data length of TCP packet */ 667 tc = (struct tcphdr *)ip_next(pip); 668 hlen = (pip->ip_hl + tc->th_off) << 2; 669 tlen = ntohs(pip->ip_len); 670 dlen = tlen - hlen; 671 672 /* Create new FTP message. */ 673 { 674 char stemp[MAX_MESSAGE_SIZE + 1]; 675 char *sptr; 676 u_short alias_port; 677 u_char *ptr; 678 int a1, a2, a3, a4, p1, p2; 679 struct in_addr alias_address; 680 681 /* Decompose alias address into quad format */ 682 alias_address = GetAliasAddress(lnk); 683 ptr = (u_char *) & alias_address.s_addr; 684 a1 = *ptr++; 685 a2 = *ptr++; 686 a3 = *ptr++; 687 a4 = *ptr; 688 689 alias_port = GetAliasPort(ftp_lnk); 690 691 switch (ftp_message_type) { 692 case FTP_PORT_COMMAND: 693 case FTP_227_REPLY: 694 /* Decompose alias port into pair format. */ 695 ptr = (char *)&alias_port; 696 p1 = *ptr++; 697 p2 = *ptr; 698 699 if (ftp_message_type == FTP_PORT_COMMAND) { 700 /* Generate PORT command string. */ 701 sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n", 702 a1, a2, a3, a4, p1, p2); 703 } else { 704 /* Generate 227 reply string. */ 705 sprintf(stemp, 706 "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n", 707 a1, a2, a3, a4, p1, p2); 708 } 709 break; 710 case FTP_EPRT_COMMAND: 711 /* Generate EPRT command string. */ 712 sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n", 713 a1, a2, a3, a4, ntohs(alias_port)); 714 break; 715 case FTP_229_REPLY: 716 /* Generate 229 reply string. */ 717 sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n", 718 ntohs(alias_port)); 719 break; 720 } 721 722 /* Save string length for IP header modification */ 723 slen = strlen(stemp); 724 725 /* Copy modified buffer into IP packet. */ 726 sptr = (char *)pip; 727 sptr += hlen; 728 strncpy(sptr, stemp, maxpacketsize - hlen); 729 } 730 731 /* Save information regarding modified seq and ack numbers */ 732 { 733 int delta; 734 735 SetAckModified(lnk); 736 delta = GetDeltaSeqOut(pip, lnk); 737 AddSeq(pip, lnk, delta + slen - dlen); 738 } 739 740 /* Revise IP header */ 741 { 742 u_short new_len; 743 744 new_len = htons(hlen + slen); 745 DifferentialChecksum(&pip->ip_sum, 746 &new_len, 747 &pip->ip_len, 748 1); 749 pip->ip_len = new_len; 750 } 751 752 /* Compute TCP checksum for revised packet */ 753 tc->th_sum = 0; 754 #ifdef _KERNEL 755 tc->th_x2 = 1; 756 #else 757 tc->th_sum = TcpChecksum(pip); 758 #endif 759 } else { 760 #ifdef LIBALIAS_DEBUG 761 fprintf(stderr, 762 "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n"); 763 #endif 764 } 765 } 766