X-Git-Url: https://git.saurik.com/apple/network_cmds.git/blobdiff_plain/b7080c8e96625177072137d504eb8e9c9d748e49..ccaf72887e87866a51356d16a08c713bc9273c92:/alias/alias_ftp.c diff --git a/alias/alias_ftp.c b/alias/alias_ftp.c index 73769fd..7985a95 100644 --- a/alias/alias_ftp.c +++ b/alias/alias_ftp.c @@ -1,22 +1,72 @@ +/* + * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/*- + * Copyright (c) 2001 Charles Mott + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Based upon: + * $FreeBSD: src/lib/libalias/alias_ftp.c,v 1.5.2.4 2001/08/21 03:50:25 brian Exp $ + */ + /* Alias_ftp.c performs special processing for FTP sessions under - TCP. Specifically, when a PORT command from the client side - is sent, it is intercepted and modified. The address is changed - to the gateway machine and an aliasing port is used. + TCP. Specifically, when a PORT/EPRT command from the client + side or 227/229 reply from the server is sent, it is intercepted + and modified. The address is changed to the gateway machine + and an aliasing port is used. - For this routine to work, the PORT command must fit entirely - into a single TCP packet. This is typically the case, but exceptions + For this routine to work, the message must fit entirely into a + single TCP packet. This is typically the case, but exceptions can easily be envisioned under the actual specifications. Probably the most troubling aspect of the approach taken here is - that the new PORT command will typically be a different length, and + that the new message will typically be a different length, and this causes a certain amount of bookkeeping to keep track of the changes of sequence and acknowledgment numbers, since the client machine is totally unaware of the modification to the TCP stream. - This software is placed into the public domain with no restrictions - on its distribution. + References: RFC 959, RFC 2428. Initial version: August, 1996 (cjm) @@ -25,20 +75,23 @@ error for modified IP packets. Version 1.7: January 9, 1996 (cjm) - Differental checksum computation for change + Differential checksum computation for change in IP packet length. - + Version 2.1: May, 1997 (cjm) Very minor changes to conform with local/global/function naming conventions - withing the packet alising module. + within the packet aliasing module. + + Version 3.1: May, 2000 (eds) + Add support for passive mode, alias the 227 replies. See HISTORY file for record of revisions. */ /* Includes */ #include -#include +#include #include #include #include @@ -48,9 +101,25 @@ #include "alias_local.h" -static void NewFtpPortCommand(struct ip *, struct alias_link *, struct in_addr, u_short, int); +#define FTP_CONTROL_PORT_NUMBER 21 +#define MAX_MESSAGE_SIZE 128 + +enum ftp_message_type { + FTP_PORT_COMMAND, + FTP_EPRT_COMMAND, + FTP_227_REPLY, + FTP_229_REPLY, + FTP_UNKNOWN_MESSAGE +}; +static int ParseFtpPortCommand(char *, int); +static int ParseFtpEprtCommand(char *, int); +static int ParseFtp227Reply(char *, int); +static int ParseFtp229Reply(char *, int); +static void NewFtpMessage(struct ip *, struct alias_link *, int, int); +static struct in_addr true_addr; /* in network byte order. */ +static u_short true_port; /* in host byte order. */ void AliasHandleFtpOut( @@ -59,93 +128,367 @@ struct alias_link *link, /* The link to go through (aliased port) */ int maxpacketsize /* The maximum size this packet can grow to (including headers) */) { int hlen, tlen, dlen; - struct in_addr true_addr; - u_short true_port; char *sptr; struct tcphdr *tc; - + int ftp_message_type; + /* Calculate data length of TCP packet */ tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); hlen = (pip->ip_hl + tc->th_off) << 2; tlen = ntohs(pip->ip_len); dlen = tlen - hlen; -/* Return is data length is too long or too short */ - if (dlen<10 || dlen>80) - return; - /* Place string pointer and beginning of data */ - sptr = (char *) pip; + sptr = (char *) pip; sptr += hlen; -/* Parse through string using state diagram method */ - { - char ch, zero; - int i, state; - u_long a1, a2, a3, a4; - u_short p1, p2; - - a1=0; a2=0; a3=0; a4=0; p1=0; p2=0; - zero = '0'; - state=-4; - for (i=0; ith_dport) == FTP_CONTROL_PORT_NUMBER) { +/* + * When aliasing a client, check for the PORT/EPRT command. + */ + if (ParseFtpPortCommand(sptr, dlen)) + ftp_message_type = FTP_PORT_COMMAND; + else if (ParseFtpEprtCommand(sptr, dlen)) + ftp_message_type = FTP_EPRT_COMMAND; + } else { +/* + * When aliasing a server, check for the 227/229 reply. + */ + if (ParseFtp227Reply(sptr, dlen)) + ftp_message_type = FTP_227_REPLY; + else if (ParseFtp229Reply(sptr, dlen)) + ftp_message_type = FTP_229_REPLY; + } + + if (ftp_message_type != FTP_UNKNOWN_MESSAGE) + NewFtpMessage(pip, link, maxpacketsize, ftp_message_type); + } + +/* Track the msgs which are CRLF term'd for PORT/PASV FW breach */ + + if (dlen) { /* only if there's data */ + sptr = (char *) pip; /* start over at beginning */ + tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may have grown */ + SetLastLineCrlfTermed(link, + (sptr[tlen-2] == '\r') && (sptr[tlen-1] == '\n')); + } +} + +static int +ParseFtpPortCommand(char *sptr, int dlen) +{ + char ch; + int i, state; + u_int32_t addr; + u_short port; + u_int8_t octet; + + /* Format: "PORT A,D,D,R,PO,RT". */ + + /* Return if data length is too short. */ + if (dlen < 18) + return 0; + + addr = port = octet = 0; + state = -4; + for (i = 0; i < dlen; i++) { + ch = sptr[i]; + switch (state) { + case -4: if (ch == 'P') state++; else return 0; break; + case -3: if (ch == 'O') state++; else return 0; break; + case -2: if (ch == 'R') state++; else return 0; break; + case -1: if (ch == 'T') state++; else return 0; break; + + case 0: + if (isspace(ch)) + break; + else + state++; + case 1: case 3: case 5: case 7: case 9: case 11: + if (isdigit(ch)) { + octet = ch - '0'; + state++; + } else + return 0; + break; + case 2: case 4: case 6: case 8: + if (isdigit(ch)) + octet = 10 * octet + ch - '0'; + else if (ch == ',') { + addr = (addr << 8) + octet; + state++; + } else + return 0; + break; + case 10: case 12: + if (isdigit(ch)) + octet = 10 * octet + ch - '0'; + else if (ch == ',' || state == 12) { + port = (port << 8) + octet; + state++; + } else + return 0; + break; + } + } + + if (state == 13) { + true_addr.s_addr = htonl(addr); + true_port = port; + return 1; + } else + return 0; +} + +static int +ParseFtpEprtCommand(char *sptr, int dlen) +{ + char ch, delim; + int i, state; + u_int32_t addr; + u_short port; + u_int8_t octet; + + /* Format: "EPRT |1|A.D.D.R|PORT|". */ + + /* Return if data length is too short. */ + if (dlen < 18) + return 0; + + addr = port = octet = 0; + delim = '|'; /* XXX gcc -Wuninitialized */ + state = -4; + for (i = 0; i < dlen; i++) { + ch = sptr[i]; + switch (state) + { + case -4: if (ch == 'E') state++; else return 0; break; + case -3: if (ch == 'P') state++; else return 0; break; + case -2: if (ch == 'R') state++; else return 0; break; + case -1: if (ch == 'T') state++; else return 0; break; + + case 0: + if (!isspace(ch)) { + delim = ch; + state++; + } + break; + case 1: + if (ch == '1') /* IPv4 address */ + state++; + else + return 0; + break; + case 2: + if (ch == delim) + state++; + else + return 0; + break; + case 3: case 5: case 7: case 9: + if (isdigit(ch)) { + octet = ch - '0'; + state++; + } else + return 0; + break; + case 4: case 6: case 8: case 10: + if (isdigit(ch)) + octet = 10 * octet + ch - '0'; + else if (ch == '.' || state == 10) { + addr = (addr << 8) + octet; + state++; + } else + return 0; + break; + case 11: + if (isdigit(ch)) { + port = ch - '0'; + state++; + } else + return 0; + break; + case 12: + if (isdigit(ch)) + port = 10 * port + ch - '0'; + else if (ch == delim) + state++; + else + return 0; + break; + } + } + + if (state == 13) { + true_addr.s_addr = htonl(addr); + true_port = port; + return 1; + } else + return 0; +} - if (state == 11) +static int +ParseFtp227Reply(char *sptr, int dlen) +{ + char ch; + int i, state; + u_int32_t addr; + u_short port; + u_int8_t octet; + + /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */ + + /* Return if data length is too short. */ + if (dlen < 17) + return 0; + + addr = port = octet = 0; + + state = -3; + for (i = 0; i < dlen; i++) { + ch = sptr[i]; + switch (state) { - true_port = htons((p1<<8) + p2); - true_addr.s_addr = htonl((a1<<24) + (a2<<16) +(a3<<8) + a4); - NewFtpPortCommand(pip, link, true_addr, true_port, maxpacketsize); - } + case -3: if (ch == '2') state++; else return 0; break; + case -2: if (ch == '2') state++; else return 0; break; + case -1: if (ch == '7') state++; else return 0; break; + + case 0: + if (ch == '(') + state++; + break; + case 1: case 3: case 5: case 7: case 9: case 11: + if (isdigit(ch)) { + octet = ch - '0'; + state++; + } else + return 0; + break; + case 2: case 4: case 6: case 8: + if (isdigit(ch)) + octet = 10 * octet + ch - '0'; + else if (ch == ',') { + addr = (addr << 8) + octet; + state++; + } else + return 0; + break; + case 10: case 12: + if (isdigit(ch)) + octet = 10 * octet + ch - '0'; + else if (ch == ',' || (state == 12 && ch == ')')) { + port = (port << 8) + octet; + state++; + } else + return 0; + break; + } } + + if (state == 13) { + true_port = port; + true_addr.s_addr = htonl(addr); + return 1; + } else + return 0; +} + +static int +ParseFtp229Reply(char *sptr, int dlen) +{ + char ch, delim; + int i, state; + u_short port; + + /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */ + + /* Return if data length is too short. */ + if (dlen < 11) + return 0; + + port = 0; + delim = '|'; /* XXX gcc -Wuninitialized */ + + state = -3; + for (i = 0; i < dlen; i++) { + ch = sptr[i]; + switch (state) + { + case -3: if (ch == '2') state++; else return 0; break; + case -2: if (ch == '2') state++; else return 0; break; + case -1: if (ch == '9') state++; else return 0; break; + + case 0: + if (ch == '(') + state++; + break; + case 1: + delim = ch; + state++; + break; + case 2: case 3: + if (ch == delim) + state++; + else + return 0; + break; + case 4: + if (isdigit(ch)) { + port = ch - '0'; + state++; + } else + return 0; + break; + case 5: + if (isdigit(ch)) + port = 10 * port + ch - '0'; + else if (ch == delim) + state++; + else + return 0; + break; + case 6: + if (ch == ')') + state++; + else + return 0; + break; + } + } + + if (state == 7) { + true_port = port; + return 1; + } else + return 0; } static void -NewFtpPortCommand(struct ip *pip, - struct alias_link *link, - struct in_addr true_addr, - u_short true_port, - int maxpacketsize) -{ +NewFtpMessage(struct ip *pip, + struct alias_link *link, + int maxpacketsize, + int ftp_message_type) +{ struct alias_link *ftp_link; -/* Establish link to address and port found in PORT command */ +/* Security checks. */ + if (ftp_message_type != FTP_229_REPLY && + pip->ip_src.s_addr != true_addr.s_addr) + return; + + if (true_port < IPPORT_RESERVED) + return; + +/* Establish link to address and port found in FTP control message. */ ftp_link = FindUdpTcpOut(true_addr, GetDestAddress(link), - true_port, 0, IPPROTO_TCP); + htons(true_port), 0, IPPROTO_TCP, 1); if (ftp_link != NULL) { @@ -153,8 +496,11 @@ NewFtpPortCommand(struct ip *pip, struct tcphdr *tc; #ifndef NO_FW_PUNCH -/* Punch hole in firewall */ - PunchFWHole(ftp_link); + if (ftp_message_type == FTP_PORT_COMMAND || + ftp_message_type == FTP_EPRT_COMMAND) { + /* Punch hole in firewall */ + PunchFWHole(ftp_link); + } #endif /* Calculate data length of TCP packet */ @@ -163,33 +509,57 @@ NewFtpPortCommand(struct ip *pip, tlen = ntohs(pip->ip_len); dlen = tlen - hlen; -/* Create new PORT command */ +/* Create new FTP message. */ { - char stemp[80]; + char stemp[MAX_MESSAGE_SIZE + 1]; char *sptr; u_short alias_port; u_char *ptr; - int a1, a2, a3, a4, p1, p2; + int a1, a2, a3, a4, p1, p2; struct in_addr alias_address; - + /* Decompose alias address into quad format */ alias_address = GetAliasAddress(link); ptr = (u_char *) &alias_address.s_addr; a1 = *ptr++; a2=*ptr++; a3=*ptr++; a4=*ptr; -/* Decompose alias port into pair format */ - alias_port = GetAliasPort(ftp_link); - ptr = (char *) &alias_port; - p1 = *ptr++; p2=*ptr; - -/* Generate command string */ - sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n", - a1,a2,a3,a4,p1,p2); + alias_port = GetAliasPort(ftp_link); + + switch (ftp_message_type) + { + case FTP_PORT_COMMAND: + case FTP_227_REPLY: + /* Decompose alias port into pair format. */ + ptr = (char *) &alias_port; + p1 = *ptr++; p2=*ptr; + + if (ftp_message_type == FTP_PORT_COMMAND) { + /* Generate PORT command string. */ + sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n", + a1,a2,a3,a4,p1,p2); + } else { + /* Generate 227 reply string. */ + sprintf(stemp, + "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n", + a1,a2,a3,a4,p1,p2); + } + break; + case FTP_EPRT_COMMAND: + /* Generate EPRT command string. */ + sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n", + a1,a2,a3,a4,ntohs(alias_port)); + break; + case FTP_229_REPLY: + /* Generate 229 reply string. */ + sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n", + ntohs(alias_port)); + break; + } /* Save string length for IP header modification */ slen = strlen(stemp); -/* Copy into IP packet */ +/* Copy modified buffer into IP packet. */ sptr = (char *) pip; sptr += hlen; strncpy(sptr, stemp, maxpacketsize-hlen); }