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