]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sys/netinet/libalias/alias_ftp.c
Clone Kip's Xen on stable/6 tree so that I can work on improving FreeBSD/amd64
[FreeBSD/FreeBSD.git] / 6 / sys / netinet / libalias / alias_ftp.c
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 }