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 #include <string.h> 83 #endif 84 85 #include <netinet/in_systm.h> 86 #include <netinet/in.h> 87 #include <netinet/ip.h> 88 #include <netinet/tcp.h> 89 90 #ifdef _KERNEL 91 #include <netinet/libalias/alias.h> 92 #include <netinet/libalias/alias_local.h> 93 #include <netinet/libalias/alias_mod.h> 94 #else 95 #include "alias_local.h" 96 #include "alias_mod.h" 97 #endif 98 99 #define FTP_CONTROL_PORT_NUMBER 21 100 101 static void 102 AliasHandleFtpOut(struct libalias *, struct ip *, struct alias_link *, 103 int maxpacketsize); 104 105 static int 106 fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah) 107 { 108 109 if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || 110 ah->maxpktsize == 0) 111 return (-1); 112 if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER 113 || ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER) 114 return (0); 115 return (-1); 116 } 117 118 static int 119 protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) 120 { 121 122 AliasHandleFtpOut(la, pip, ah->lnk, ah->maxpktsize); 123 return (0); 124 } 125 126 struct proto_handler handlers[] = { 127 { 128 .pri = 80, 129 .dir = OUT, 130 .proto = TCP, 131 .fingerprint = &fingerprint, 132 .protohandler = &protohandler 133 }, 134 { EOH } 135 }; 136 137 static int 138 mod_handler(module_t mod, int type, void *data) 139 { 140 int error; 141 142 switch (type) { 143 case MOD_LOAD: 144 error = 0; 145 LibAliasAttachHandlers(handlers); 146 break; 147 case MOD_UNLOAD: 148 error = 0; 149 LibAliasDetachHandlers(handlers); 150 break; 151 default: 152 error = EINVAL; 153 } 154 return (error); 155 } 156 157 #ifdef _KERNEL 158 static 159 #endif 160 moduledata_t alias_mod = { 161 "alias_ftp", mod_handler, NULL 162 }; 163 164 #ifdef _KERNEL 165 DECLARE_MODULE(alias_ftp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); 166 MODULE_VERSION(alias_ftp, 1); 167 MODULE_DEPEND(alias_ftp, libalias, 1, 1, 1); 168 #endif 169 170 #define FTP_CONTROL_PORT_NUMBER 21 171 #define MAX_MESSAGE_SIZE 128 172 173 /* FTP protocol flags. */ 174 #define WAIT_CRLF 0x01 175 176 enum ftp_message_type { 177 FTP_PORT_COMMAND, 178 FTP_EPRT_COMMAND, 179 FTP_227_REPLY, 180 FTP_229_REPLY, 181 FTP_UNKNOWN_MESSAGE 182 }; 183 184 static int ParseFtpPortCommand(struct libalias *la, char *, int); 185 static int ParseFtpEprtCommand(struct libalias *la, char *, int); 186 static int ParseFtp227Reply(struct libalias *la, char *, int); 187 static int ParseFtp229Reply(struct libalias *la, char *, int); 188 static void NewFtpMessage(struct libalias *la, struct ip *, struct alias_link *, int, int); 189 190 static void 191 AliasHandleFtpOut( 192 struct libalias *la, 193 struct ip *pip, /* IP packet to examine/patch */ 194 struct alias_link *lnk, /* The link to go through (aliased port) */ 195 int maxpacketsize /* The maximum size this packet can grow to 196 (including headers) */ ) 197 { 198 int hlen, tlen, dlen, pflags; 199 char *sptr; 200 struct tcphdr *tc; 201 int ftp_message_type; 202 203 /* Calculate data length of TCP packet */ 204 tc = (struct tcphdr *)ip_next(pip); 205 hlen = (pip->ip_hl + tc->th_off) << 2; 206 tlen = ntohs(pip->ip_len); 207 dlen = tlen - hlen; 208 209 /* Place string pointer and beginning of data */ 210 sptr = (char *)pip; 211 sptr += hlen; 212 213 /* 214 * Check that data length is not too long and previous message was 215 * properly terminated with CRLF. 216 */ 217 pflags = GetProtocolFlags(lnk); 218 if (dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) { 219 ftp_message_type = FTP_UNKNOWN_MESSAGE; 220 221 if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) { 222 /* 223 * When aliasing a client, check for the PORT/EPRT command. 224 */ 225 if (ParseFtpPortCommand(la, sptr, dlen)) 226 ftp_message_type = FTP_PORT_COMMAND; 227 else if (ParseFtpEprtCommand(la, sptr, dlen)) 228 ftp_message_type = FTP_EPRT_COMMAND; 229 } else { 230 /* 231 * When aliasing a server, check for the 227/229 reply. 232 */ 233 if (ParseFtp227Reply(la, sptr, dlen)) 234 ftp_message_type = FTP_227_REPLY; 235 else if (ParseFtp229Reply(la, sptr, dlen)) { 236 ftp_message_type = FTP_229_REPLY; 237 la->true_addr.s_addr = pip->ip_src.s_addr; 238 } 239 } 240 241 if (ftp_message_type != FTP_UNKNOWN_MESSAGE) 242 NewFtpMessage(la, pip, lnk, maxpacketsize, ftp_message_type); 243 } 244 /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */ 245 246 if (dlen) { /* only if there's data */ 247 sptr = (char *)pip; /* start over at beginning */ 248 tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may 249 * have grown */ 250 if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n') 251 pflags &= ~WAIT_CRLF; 252 else 253 pflags |= WAIT_CRLF; 254 SetProtocolFlags(lnk, pflags); 255 } 256 } 257 258 static int 259 ParseFtpPortCommand(struct libalias *la, char *sptr, int dlen) 260 { 261 char ch; 262 int i, state; 263 u_int32_t addr; 264 u_short port; 265 u_int8_t octet; 266 267 /* Format: "PORT A,D,D,R,PO,RT". */ 268 269 /* Return if data length is too short. */ 270 if (dlen < 18) 271 return (0); 272 273 addr = port = octet = 0; 274 state = -4; 275 for (i = 0; i < dlen; i++) { 276 ch = sptr[i]; 277 switch (state) { 278 case -4: 279 if (ch == 'P') 280 state++; 281 else 282 return (0); 283 break; 284 case -3: 285 if (ch == 'O') 286 state++; 287 else 288 return (0); 289 break; 290 case -2: 291 if (ch == 'R') 292 state++; 293 else 294 return (0); 295 break; 296 case -1: 297 if (ch == 'T') 298 state++; 299 else 300 return (0); 301 break; 302 303 case 0: 304 if (isspace(ch)) 305 break; 306 else 307 state++; 308 case 1: 309 case 3: 310 case 5: 311 case 7: 312 case 9: 313 case 11: 314 if (isdigit(ch)) { 315 octet = ch - '0'; 316 state++; 317 } else 318 return (0); 319 break; 320 case 2: 321 case 4: 322 case 6: 323 case 8: 324 if (isdigit(ch)) 325 octet = 10 * octet + ch - '0'; 326 else if (ch == ',') { 327 addr = (addr << 8) + octet; 328 state++; 329 } else 330 return (0); 331 break; 332 case 10: 333 case 12: 334 if (isdigit(ch)) 335 octet = 10 * octet + ch - '0'; 336 else if (ch == ',' || state == 12) { 337 port = (port << 8) + octet; 338 state++; 339 } else 340 return (0); 341 break; 342 } 343 } 344 345 if (state == 13) { 346 la->true_addr.s_addr = htonl(addr); 347 la->true_port = port; 348 return (1); 349 } else 350 return (0); 351 } 352 353 static int 354 ParseFtpEprtCommand(struct libalias *la, char *sptr, int dlen) 355 { 356 char ch, delim; 357 int i, state; 358 u_int32_t addr; 359 u_short port; 360 u_int8_t octet; 361 362 /* Format: "EPRT |1|A.D.D.R|PORT|". */ 363 364 /* Return if data length is too short. */ 365 if (dlen < 18) 366 return (0); 367 368 addr = port = octet = 0; 369 delim = '|'; /* XXX gcc -Wuninitialized */ 370 state = -4; 371 for (i = 0; i < dlen; i++) { 372 ch = sptr[i]; 373 switch (state) { 374 case -4: 375 if (ch == 'E') 376 state++; 377 else 378 return (0); 379 break; 380 case -3: 381 if (ch == 'P') 382 state++; 383 else 384 return (0); 385 break; 386 case -2: 387 if (ch == 'R') 388 state++; 389 else 390 return (0); 391 break; 392 case -1: 393 if (ch == 'T') 394 state++; 395 else 396 return (0); 397 break; 398 399 case 0: 400 if (!isspace(ch)) { 401 delim = ch; 402 state++; 403 } 404 break; 405 case 1: 406 if (ch == '1') /* IPv4 address */ 407 state++; 408 else 409 return (0); 410 break; 411 case 2: 412 if (ch == delim) 413 state++; 414 else 415 return (0); 416 break; 417 case 3: 418 case 5: 419 case 7: 420 case 9: 421 if (isdigit(ch)) { 422 octet = ch - '0'; 423 state++; 424 } else 425 return (0); 426 break; 427 case 4: 428 case 6: 429 case 8: 430 case 10: 431 if (isdigit(ch)) 432 octet = 10 * octet + ch - '0'; 433 else if (ch == '.' || state == 10) { 434 addr = (addr << 8) + octet; 435 state++; 436 } else 437 return (0); 438 break; 439 case 11: 440 if (isdigit(ch)) { 441 port = ch - '0'; 442 state++; 443 } else 444 return (0); 445 break; 446 case 12: 447 if (isdigit(ch)) 448 port = 10 * port + ch - '0'; 449 else if (ch == delim) 450 state++; 451 else 452 return (0); 453 break; 454 } 455 } 456 457 if (state == 13) { 458 la->true_addr.s_addr = htonl(addr); 459 la->true_port = port; 460 return (1); 461 } else 462 return (0); 463 } 464 465 static int 466 ParseFtp227Reply(struct libalias *la, char *sptr, int dlen) 467 { 468 char ch; 469 int i, state; 470 u_int32_t addr; 471 u_short port; 472 u_int8_t octet; 473 474 /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */ 475 476 /* Return if data length is too short. */ 477 if (dlen < 17) 478 return (0); 479 480 addr = port = octet = 0; 481 482 state = -3; 483 for (i = 0; i < dlen; i++) { 484 ch = sptr[i]; 485 switch (state) { 486 case -3: 487 if (ch == '2') 488 state++; 489 else 490 return (0); 491 break; 492 case -2: 493 if (ch == '2') 494 state++; 495 else 496 return (0); 497 break; 498 case -1: 499 if (ch == '7') 500 state++; 501 else 502 return (0); 503 break; 504 505 case 0: 506 if (ch == '(') 507 state++; 508 break; 509 case 1: 510 case 3: 511 case 5: 512 case 7: 513 case 9: 514 case 11: 515 if (isdigit(ch)) { 516 octet = ch - '0'; 517 state++; 518 } else 519 return (0); 520 break; 521 case 2: 522 case 4: 523 case 6: 524 case 8: 525 if (isdigit(ch)) 526 octet = 10 * octet + ch - '0'; 527 else if (ch == ',') { 528 addr = (addr << 8) + octet; 529 state++; 530 } else 531 return (0); 532 break; 533 case 10: 534 case 12: 535 if (isdigit(ch)) 536 octet = 10 * octet + ch - '0'; 537 else if (ch == ',' || (state == 12 && ch == ')')) { 538 port = (port << 8) + octet; 539 state++; 540 } else 541 return (0); 542 break; 543 } 544 } 545 546 if (state == 13) { 547 la->true_port = port; 548 la->true_addr.s_addr = htonl(addr); 549 return (1); 550 } else 551 return (0); 552 } 553 554 static int 555 ParseFtp229Reply(struct libalias *la, char *sptr, int dlen) 556 { 557 char ch, delim; 558 int i, state; 559 u_short port; 560 561 /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */ 562 563 /* Return if data length is too short. */ 564 if (dlen < 11) 565 return (0); 566 567 port = 0; 568 delim = '|'; /* XXX gcc -Wuninitialized */ 569 570 state = -3; 571 for (i = 0; i < dlen; i++) { 572 ch = sptr[i]; 573 switch (state) { 574 case -3: 575 if (ch == '2') 576 state++; 577 else 578 return (0); 579 break; 580 case -2: 581 if (ch == '2') 582 state++; 583 else 584 return (0); 585 break; 586 case -1: 587 if (ch == '9') 588 state++; 589 else 590 return (0); 591 break; 592 593 case 0: 594 if (ch == '(') 595 state++; 596 break; 597 case 1: 598 delim = ch; 599 state++; 600 break; 601 case 2: 602 case 3: 603 if (ch == delim) 604 state++; 605 else 606 return (0); 607 break; 608 case 4: 609 if (isdigit(ch)) { 610 port = ch - '0'; 611 state++; 612 } else 613 return (0); 614 break; 615 case 5: 616 if (isdigit(ch)) 617 port = 10 * port + ch - '0'; 618 else if (ch == delim) 619 state++; 620 else 621 return (0); 622 break; 623 case 6: 624 if (ch == ')') 625 state++; 626 else 627 return (0); 628 break; 629 } 630 } 631 632 if (state == 7) { 633 la->true_port = port; 634 return (1); 635 } else 636 return (0); 637 } 638 639 static void 640 NewFtpMessage(struct libalias *la, struct ip *pip, 641 struct alias_link *lnk, 642 int maxpacketsize, 643 int ftp_message_type) 644 { 645 struct alias_link *ftp_lnk; 646 647 /* Security checks. */ 648 if (pip->ip_src.s_addr != la->true_addr.s_addr) 649 return; 650 651 if (la->true_port < IPPORT_RESERVED) 652 return; 653 654 /* Establish link to address and port found in FTP control message. */ 655 ftp_lnk = FindUdpTcpOut(la, la->true_addr, GetDestAddress(lnk), 656 htons(la->true_port), 0, IPPROTO_TCP, 1); 657 658 if (ftp_lnk != NULL) { 659 int slen, hlen, tlen, dlen; 660 struct tcphdr *tc; 661 662 #ifndef NO_FW_PUNCH 663 /* Punch hole in firewall */ 664 PunchFWHole(ftp_lnk); 665 #endif 666 667 /* Calculate data length of TCP packet */ 668 tc = (struct tcphdr *)ip_next(pip); 669 hlen = (pip->ip_hl + tc->th_off) << 2; 670 tlen = ntohs(pip->ip_len); 671 dlen = tlen - hlen; 672 673 /* Create new FTP message. */ 674 { 675 char stemp[MAX_MESSAGE_SIZE + 1]; 676 char *sptr; 677 u_short alias_port; 678 u_char *ptr; 679 int a1, a2, a3, a4, p1, p2; 680 struct in_addr alias_address; 681 682 /* Decompose alias address into quad format */ 683 alias_address = GetAliasAddress(lnk); 684 ptr = (u_char *) & alias_address.s_addr; 685 a1 = *ptr++; 686 a2 = *ptr++; 687 a3 = *ptr++; 688 a4 = *ptr; 689 690 alias_port = GetAliasPort(ftp_lnk); 691 692 switch (ftp_message_type) { 693 case FTP_PORT_COMMAND: 694 case FTP_227_REPLY: 695 /* Decompose alias port into pair format. */ 696 ptr = (char *)&alias_port; 697 p1 = *ptr++; 698 p2 = *ptr; 699 700 if (ftp_message_type == FTP_PORT_COMMAND) { 701 /* Generate PORT command string. */ 702 sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n", 703 a1, a2, a3, a4, p1, p2); 704 } else { 705 /* Generate 227 reply string. */ 706 sprintf(stemp, 707 "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n", 708 a1, a2, a3, a4, p1, p2); 709 } 710 break; 711 case FTP_EPRT_COMMAND: 712 /* Generate EPRT command string. */ 713 sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n", 714 a1, a2, a3, a4, ntohs(alias_port)); 715 break; 716 case FTP_229_REPLY: 717 /* Generate 229 reply string. */ 718 sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n", 719 ntohs(alias_port)); 720 break; 721 } 722 723 /* Save string length for IP header modification */ 724 slen = strlen(stemp); 725 726 /* Copy modified buffer into IP packet. */ 727 sptr = (char *)pip; 728 sptr += hlen; 729 strncpy(sptr, stemp, maxpacketsize - hlen); 730 } 731 732 /* Save information regarding modified seq and ack numbers */ 733 { 734 int delta; 735 736 SetAckModified(lnk); 737 delta = GetDeltaSeqOut(pip, lnk); 738 AddSeq(pip, lnk, delta + slen - dlen); 739 } 740 741 /* Revise IP header */ 742 { 743 u_short new_len; 744 745 new_len = htons(hlen + slen); 746 DifferentialChecksum(&pip->ip_sum, 747 &new_len, 748 &pip->ip_len, 749 1); 750 pip->ip_len = new_len; 751 } 752 753 /* Compute TCP checksum for revised packet */ 754 tc->th_sum = 0; 755 #ifdef _KERNEL 756 tc->th_x2 = 1; 757 #else 758 tc->th_sum = TcpChecksum(pip); 759 #endif 760 } else { 761 #ifdef LIBALIAS_DEBUG 762 fprintf(stderr, 763 "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n"); 764 #endif 765 } 766 } 767