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