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