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