]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libalias/alias_ftp.c
Add cross-references for the new kldsym(2) man page.
[FreeBSD/FreeBSD.git] / lib / libalias / alias_ftp.c
1 /*-
2  * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
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  * $FreeBSD$
27  */
28
29 /*
30     Alias_ftp.c performs special processing for FTP sessions under
31     TCP.  Specifically, when a PORT/EPRT command from the client
32     side or 227/229 reply from the server is sent, it is intercepted
33     and modified.  The address is changed to the gateway machine
34     and an aliasing port is used.
35
36     For this routine to work, the message must fit entirely into a
37     single TCP packet.  This is typically the case, but exceptions
38     can easily be envisioned under the actual specifications.
39
40     Probably the most troubling aspect of the approach taken here is
41     that the new message will typically be a different length, and
42     this causes a certain amount of bookkeeping to keep track of the
43     changes of sequence and acknowledgment numbers, since the client
44     machine is totally unaware of the modification to the TCP stream.
45
46
47     This software is placed into the public domain with no restrictions
48     on its distribution.
49
50     References: RFC 959, RFC 2428.
51
52     Initial version:  August, 1996  (cjm)
53
54     Version 1.6
55          Brian Somers and Martin Renters identified an IP checksum
56          error for modified IP packets.
57
58     Version 1.7:  January 9, 1996 (cjm)
59          Differential checksum computation for change
60          in IP packet length.
61
62     Version 2.1:  May, 1997 (cjm)
63          Very minor changes to conform with
64          local/global/function naming conventions
65          within the packet aliasing module.
66
67     Version 3.1:  May, 2000 (eds)
68          Add support for passive mode, alias the 227 replies.
69
70     See HISTORY file for record of revisions.
71 */
72
73 /* Includes */
74 #include <ctype.h>
75 #include <stdio.h>
76 #include <string.h>
77 #include <sys/types.h>
78 #include <netinet/in_systm.h>
79 #include <netinet/in.h>
80 #include <netinet/ip.h>
81 #include <netinet/tcp.h>
82
83 #include "alias_local.h"
84
85 #define FTP_CONTROL_PORT_NUMBER 21
86 #define MAX_MESSAGE_SIZE        128
87
88 /* FTP protocol flags. */
89 #define WAIT_CRLF               0x01
90
91 enum ftp_message_type {
92     FTP_PORT_COMMAND,
93     FTP_EPRT_COMMAND,
94     FTP_227_REPLY,
95     FTP_229_REPLY,
96     FTP_UNKNOWN_MESSAGE
97 };
98
99 static int ParseFtpPortCommand(char *, int);
100 static int ParseFtpEprtCommand(char *, int);
101 static int ParseFtp227Reply(char *, int);
102 static int ParseFtp229Reply(char *, int);
103 static void NewFtpMessage(struct ip *, struct alias_link *, int, int);
104
105 static struct in_addr true_addr;        /* in network byte order. */
106 static u_short true_port;               /* in host byte order. */
107
108 void
109 AliasHandleFtpOut(
110 struct ip *pip,   /* IP packet to examine/patch */
111 struct alias_link *link, /* The link to go through (aliased port) */
112 int maxpacketsize  /* The maximum size this packet can grow to (including headers) */)
113 {
114     int hlen, tlen, dlen, pflags;
115     char *sptr;
116     struct tcphdr *tc;
117     int ftp_message_type;
118
119 /* Calculate data length of TCP packet */
120     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
121     hlen = (pip->ip_hl + tc->th_off) << 2;
122     tlen = ntohs(pip->ip_len);
123     dlen = tlen - hlen;
124
125 /* Place string pointer and beginning of data */
126     sptr = (char *) pip;
127     sptr += hlen;
128
129 /*
130  * Check that data length is not too long and previous message was
131  * properly terminated with CRLF.
132  */
133     pflags = GetProtocolFlags(link);
134     if (dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) {
135         ftp_message_type = FTP_UNKNOWN_MESSAGE;
136
137         if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) {
138 /*
139  * When aliasing a client, check for the PORT/EPRT command.
140  */
141             if (ParseFtpPortCommand(sptr, dlen))
142                 ftp_message_type = FTP_PORT_COMMAND;
143             else if (ParseFtpEprtCommand(sptr, dlen))
144                 ftp_message_type = FTP_EPRT_COMMAND;
145         } else {
146 /*
147  * When aliasing a server, check for the 227/229 reply.
148  */
149             if (ParseFtp227Reply(sptr, dlen))
150                 ftp_message_type = FTP_227_REPLY;
151             else if (ParseFtp229Reply(sptr, dlen))
152                 ftp_message_type = FTP_229_REPLY;
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 (ftp_message_type != FTP_229_REPLY &&
471         pip->ip_src.s_addr != true_addr.s_addr)
472         return;
473
474     if (true_port < IPPORT_RESERVED)
475         return;
476
477 /* Establish link to address and port found in FTP control message. */
478     ftp_link = FindUdpTcpOut(true_addr, GetDestAddress(link),
479                              htons(true_port), 0, IPPROTO_TCP, 1);
480
481     if (ftp_link != NULL)
482     {
483         int slen, hlen, tlen, dlen;
484         struct tcphdr *tc;
485
486 #ifndef NO_FW_PUNCH
487         if (ftp_message_type == FTP_PORT_COMMAND ||
488             ftp_message_type == FTP_EPRT_COMMAND) {
489             /* Punch hole in firewall */
490             PunchFWHole(ftp_link);
491         }
492 #endif
493
494 /* Calculate data length of TCP packet */
495         tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
496         hlen = (pip->ip_hl + tc->th_off) << 2;
497         tlen = ntohs(pip->ip_len);
498         dlen = tlen - hlen;
499
500 /* Create new FTP message. */
501         {
502             char stemp[MAX_MESSAGE_SIZE + 1];
503             char *sptr;
504             u_short alias_port;
505             u_char *ptr;
506             int a1, a2, a3, a4, p1, p2;
507             struct in_addr alias_address;
508
509 /* Decompose alias address into quad format */
510             alias_address = GetAliasAddress(link);
511             ptr = (u_char *) &alias_address.s_addr;
512             a1 = *ptr++; a2=*ptr++; a3=*ptr++; a4=*ptr;
513
514             alias_port = GetAliasPort(ftp_link);
515
516             switch (ftp_message_type)
517             {
518             case FTP_PORT_COMMAND:
519             case FTP_227_REPLY:
520                 /* Decompose alias port into pair format. */
521                 ptr = (char *) &alias_port;
522                 p1 = *ptr++; p2=*ptr;
523
524                 if (ftp_message_type == FTP_PORT_COMMAND) {
525                     /* Generate PORT command string. */
526                     sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n",
527                             a1,a2,a3,a4,p1,p2);
528                 } else {
529                     /* Generate 227 reply string. */
530                     sprintf(stemp,
531                             "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
532                             a1,a2,a3,a4,p1,p2);
533                 }
534                 break;
535             case FTP_EPRT_COMMAND:
536                 /* Generate EPRT command string. */
537                 sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
538                         a1,a2,a3,a4,ntohs(alias_port));
539                 break;
540             case FTP_229_REPLY:
541                 /* Generate 229 reply string. */
542                 sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n",
543                         ntohs(alias_port));
544                 break;
545             }
546
547 /* Save string length for IP header modification */
548             slen = strlen(stemp);
549
550 /* Copy modified buffer into IP packet. */
551             sptr = (char *) pip; sptr += hlen;
552             strncpy(sptr, stemp, maxpacketsize-hlen);
553         }
554
555 /* Save information regarding modified seq and ack numbers */
556         {
557             int delta;
558
559             SetAckModified(link);
560             delta = GetDeltaSeqOut(pip, link);
561             AddSeq(pip, link, delta+slen-dlen);
562         }
563
564 /* Revise IP header */
565         {
566             u_short new_len;
567
568             new_len = htons(hlen + slen);
569             DifferentialChecksum(&pip->ip_sum,
570                                  &new_len,
571                                  &pip->ip_len,
572                                  1);
573             pip->ip_len = new_len;
574         }
575
576 /* Compute TCP checksum for revised packet */
577         tc->th_sum = 0;
578         tc->th_sum = TcpChecksum(pip);
579     }
580     else
581     {
582 #ifdef DEBUG
583         fprintf(stderr,
584         "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
585 #endif
586     }
587 }