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