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