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