Deleted Added
sdiff udiff text old ( 124621 ) new ( 127094 )
full compact
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

--- 11 unchanged lines hidden (view full) ---

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: head/sys/netinet/libalias/alias_ftp.c 127094 2004-03-16 21:30:41Z des $");
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

--- 45 unchanged lines hidden (view full) ---

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