4 * Copyright (C) 1997-2003 by Darren Reed
6 * See the IPFILTER.LICENCE file for details on licencing.
8 * Simple FTP transparent proxy for in-kernel use. For use with the NAT
11 * Id: ip_ftp_pxy.c,v 2.88.2.28 2009/01/01 01:20:54 darrenr Exp
14 #include <sys/cdefs.h>
15 __KERNEL_RCSID(1, "$NetBSD$");
19 #define IPF_MINPORTLEN 18
20 #define IPF_MAXPORTLEN 30
21 #define IPF_MIN227LEN 39
22 #define IPF_MAX227LEN 51
23 #define IPF_MIN229LEN 47
24 #define IPF_MAX229LEN 51
28 #define FTPXY_USER_1 2
29 #define FTPXY_USOK_1 3
30 #define FTPXY_PASS_1 4
31 #define FTPXY_PAOK_1 5
32 #define FTPXY_AUTH_1 6
33 #define FTPXY_AUOK_1 7
34 #define FTPXY_ADAT_1 8
35 #define FTPXY_ADOK_1 9
36 #define FTPXY_ACCT_1 10
37 #define FTPXY_ACOK_1 11
38 #define FTPXY_USER_2 12
39 #define FTPXY_USOK_2 13
40 #define FTPXY_PASS_2 14
41 #define FTPXY_PAOK_2 15
43 #define FTPXY_JUNK_OK 0
44 #define FTPXY_JUNK_BAD 1 /* Ignore all commands for this connection */
45 #define FTPXY_JUNK_EOL 2 /* consume the rest of this line only */
46 #define FTPXY_JUNK_CONT 3 /* Saerching for next numeric */
49 * Values for FTP commands. Numerics cover 0-999
51 #define FTPXY_C_PASV 1000
53 int ippr_ftp_client
__P((fr_info_t
*, ip_t
*, nat_t
*, ftpinfo_t
*, int));
54 int ippr_ftp_complete
__P((char *, size_t));
55 int ippr_ftp_in
__P((fr_info_t
*, ap_session_t
*, nat_t
*));
56 int ippr_ftp_init
__P((void));
57 void ippr_ftp_fini
__P((void));
58 int ippr_ftp_new
__P((fr_info_t
*, ap_session_t
*, nat_t
*));
59 int ippr_ftp_out
__P((fr_info_t
*, ap_session_t
*, nat_t
*));
60 int ippr_ftp_pasv
__P((fr_info_t
*, ip_t
*, nat_t
*, ftpinfo_t
*, int));
61 int ippr_ftp_epsv
__P((fr_info_t
*, ip_t
*, nat_t
*, ftpside_t
*, int));
62 int ippr_ftp_port
__P((fr_info_t
*, ip_t
*, nat_t
*, ftpside_t
*, int));
63 int ippr_ftp_process
__P((fr_info_t
*, nat_t
*, ftpinfo_t
*, int));
64 int ippr_ftp_server
__P((fr_info_t
*, ip_t
*, nat_t
*, ftpinfo_t
*, int));
65 int ippr_ftp_valid
__P((ftpinfo_t
*, int, char *, size_t));
66 int ippr_ftp_server_valid
__P((ftpside_t
*, char *, size_t));
67 int ippr_ftp_client_valid
__P((ftpside_t
*, char *, size_t));
68 u_short ippr_ftp_atoi
__P((char **));
69 int ippr_ftp_pasvreply
__P((fr_info_t
*, ip_t
*, nat_t
*, ftpside_t
*,
70 u_int
, char *, char *, u_int
));
73 int ftp_proxy_init
= 0;
74 int ippr_ftp_pasvonly
= 0;
75 int ippr_ftp_insecure
= 0; /* Do not require logins before transfers */
76 int ippr_ftp_pasvrdr
= 0;
77 int ippr_ftp_forcepasv
= 0; /* PASV must be last command prior to 227 */
79 int ippr_ftp_debug
= 0;
81 int ippr_ftp_debug
= 2;
92 static frentry_t ftppxyfr
;
93 static ipftuneable_t ftptune
= {
98 sizeof(ippr_ftp_debug
),
105 * Initialize local structures.
109 bzero((char *)&ftppxyfr
, sizeof(ftppxyfr
));
111 ftppxyfr
.fr_flags
= FR_INQUE
|FR_PASS
|FR_QUICK
|FR_KEEPSTATE
;
112 MUTEX_INIT(&ftppxyfr
.fr_lock
, "FTP Proxy Mutex");
114 (void) fr_addipftune(&ftptune
);
122 (void) fr_delipftune(&ftptune
);
124 if (ftp_proxy_init
== 1) {
125 MUTEX_DESTROY(&ftppxyfr
.fr_lock
);
131 int ippr_ftp_new(fin
, aps
, nat
)
139 KMALLOC(ftp
, ftpinfo_t
*);
143 fin
= fin
; /* LINT */
144 nat
= nat
; /* LINT */
147 aps
->aps_psiz
= sizeof(ftpinfo_t
);
149 bzero((char *)ftp
, sizeof(*ftp
));
150 f
= &ftp
->ftp_side
[0];
151 f
->ftps_rptr
= f
->ftps_buf
;
152 f
->ftps_wptr
= f
->ftps_buf
;
153 f
= &ftp
->ftp_side
[1];
154 f
->ftps_rptr
= f
->ftps_buf
;
155 f
->ftps_wptr
= f
->ftps_buf
;
156 ftp
->ftp_passok
= FTPXY_INIT
;
162 int ippr_ftp_port(fin
, ip
, nat
, f
, dlen
)
169 tcphdr_t
*tcp
, tcph
, *tcp2
= &tcph
;
170 char newbuf
[IPF_FTPBUFSZ
], *s
;
171 struct in_addr swip
, swip2
;
172 u_int a1
, a2
, a3
, a4
;
181 tcp
= (tcphdr_t
*)fin
->fin_dp
;
182 off
= (char *)tcp
- (char *)ip
+ (TCP_OFF(tcp
) << 2) + fin
->fin_ipoff
;
185 * Check for client sending out PORT message.
187 if (dlen
< IPF_MINPORTLEN
) {
188 if (ippr_ftp_debug
> 1)
189 printf("ippr_ftp_port:dlen(%d) < IPF_MINPORTLEN\n",
194 * Skip the PORT command + space
196 s
= f
->ftps_rptr
+ 5;
198 * Pick out the address components, two at a time.
200 a1
= ippr_ftp_atoi(&s
);
202 if (ippr_ftp_debug
> 1)
203 printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 1);
206 a2
= ippr_ftp_atoi(&s
);
208 if (ippr_ftp_debug
> 1)
209 printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 2);
214 * Check that IP address in the PORT/PASV reply is the same as the
215 * sender of the command - prevents using PORT for port scanning.
219 if (((nat
->nat_dir
== NAT_OUTBOUND
) &&
220 (a1
!= ntohl(nat
->nat_inip
.s_addr
))) ||
221 ((nat
->nat_dir
== NAT_INBOUND
) &&
222 (a1
!= ntohl(nat
->nat_oip
.s_addr
)))) {
223 if (ippr_ftp_debug
> 0)
224 printf("ippr_ftp_port:%s != nat->nat_inip\n", "a1");
228 a5
= ippr_ftp_atoi(&s
);
230 if (ippr_ftp_debug
> 1)
231 printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 3);
238 * check for CR-LF at the end.
242 if ((*s
== '\r') && (*(s
+ 1) == '\n')) {
246 if (ippr_ftp_debug
> 1)
247 printf("ippr_ftp_port:missing %s\n", "cr-lf");
255 * Don't allow the PORT command to specify a port < 1024 due to
259 if (ippr_ftp_debug
> 0)
260 printf("ippr_ftp_port:sp(%d) < 1024\n", sp
);
264 * Calculate new address parts for PORT command
266 if (nat
->nat_dir
== NAT_INBOUND
)
267 a1
= ntohl(nat
->nat_oip
.s_addr
);
269 a1
= ntohl(ip
->ip_src
.s_addr
);
270 a2
= (a1
>> 16) & 0xff;
271 a3
= (a1
>> 8) & 0xff;
274 olen
= s
- f
->ftps_rptr
;
275 /* DO NOT change this to snprintf! */
276 #if defined(SNPRINTF) && defined(_KERNEL)
277 SNPRINTF(newbuf
, sizeof(newbuf
), "%s %u,%u,%u,%u,%u,%u\r\n",
278 "PORT", a1
, a2
, a3
, a4
, a5
, a6
);
280 (void) sprintf(newbuf
, "%s %u,%u,%u,%u,%u,%u\r\n",
281 "PORT", a1
, a2
, a3
, a4
, a5
, a6
);
284 nlen
= strlen(newbuf
);
286 if ((inc
+ ip
->ip_len
) > 65535) {
287 if (ippr_ftp_debug
> 0)
288 printf("ippr_ftp_port:inc(%d) + ip->ip_len > 65535\n",
293 #if !defined(_KERNEL)
294 bcopy(newbuf
, MTOD(m
, char *) + off
, nlen
);
298 (void)adjmsg(m
, inc
);
299 # else /* defined(MENTAT) */
301 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
302 * mean remove -len bytes from the end of the packet.
303 * The mbuf chain will be extended if necessary by m_copyback().
307 # endif /* defined(MENTAT) */
308 #endif /* !defined(_KERNEL) */
309 COPYBACK(m
, off
, nlen
, newbuf
);
313 fin
->fin_dlen
+= inc
;
314 fin
->fin_plen
+= inc
;
318 * The server may not make the connection back from port 20, but
319 * it is the most likely so use it here to check for a conflicting
322 bcopy((char *)fin
, (char *)&fi
, sizeof(fi
));
323 fi
.fin_flx
|= FI_IGNORE
;
325 fi
.fin_data
[1] = fin
->fin_data
[1] - 1;
327 * Add skeleton NAT entry for connection which will come back the
330 if (nat
->nat_dir
== NAT_OUTBOUND
)
331 nat2
= nat_outlookup(&fi
, NAT_SEARCH
|IPN_TCP
, nat
->nat_p
,
332 nat
->nat_inip
, nat
->nat_oip
);
334 nat2
= nat_inlookup(&fi
, NAT_SEARCH
|IPN_TCP
, nat
->nat_p
,
335 nat
->nat_inip
, nat
->nat_oip
);
340 ip
->ip_len
= fin
->fin_hlen
+ sizeof(*tcp2
);
341 bzero((char *)tcp2
, sizeof(*tcp2
));
342 tcp2
->th_win
= htons(8192);
343 tcp2
->th_sport
= htons(sp
);
345 tcp2
->th_flags
= TH_SYN
;
346 tcp2
->th_dport
= 0; /* XXX - don't specify remote port */
348 fi
.fin_dlen
= sizeof(*tcp2
);
349 fi
.fin_plen
= fi
.fin_hlen
+ sizeof(*tcp2
);
350 fi
.fin_dp
= (char *)tcp2
;
351 fi
.fin_fr
= &ftppxyfr
;
352 fi
.fin_out
= nat
->nat_dir
;
353 fi
.fin_flx
&= FI_LOWTTL
|FI_FRAG
|FI_TCPUDP
|FI_OPTIONS
|FI_IGNORE
;
356 if (nat
->nat_dir
== NAT_OUTBOUND
) {
357 fi
.fin_fi
.fi_saddr
= nat
->nat_inip
.s_addr
;
358 ip
->ip_src
= nat
->nat_inip
;
359 } else if (nat
->nat_dir
== NAT_INBOUND
) {
360 fi
.fin_fi
.fi_saddr
= nat
->nat_oip
.s_addr
;
361 ip
->ip_src
= nat
->nat_oip
;
364 flags
= NAT_SLAVE
|IPN_TCP
|SI_W_DPORT
;
365 if (nat
->nat_dir
== NAT_INBOUND
)
366 flags
|= NAT_NOTRULEPORT
;
367 MUTEX_ENTER(&ipf_nat_new
);
368 nat2
= nat_new(&fi
, nat
->nat_ptr
, NULL
, flags
, nat
->nat_dir
);
369 MUTEX_EXIT(&ipf_nat_new
);
372 (void) nat_proto(&fi
, nat2
, IPN_TCP
);
373 MUTEX_ENTER(&nat2
->nat_lock
);
374 nat_update(&fi
, nat2
);
375 MUTEX_EXIT(&nat2
->nat_lock
);
377 if (nat
->nat_dir
== NAT_INBOUND
) {
378 fi
.fin_fi
.fi_daddr
= nat
->nat_inip
.s_addr
;
379 ip
->ip_dst
= nat
->nat_inip
;
381 (void) fr_addstate(&fi
, NULL
, SI_W_DPORT
);
391 int ippr_ftp_client(fin
, ip
, nat
, ftp
, dlen
)
398 char *rptr
, *wptr
, cmd
[6], c
;
403 f
= &ftp
->ftp_side
[0];
407 for (i
= 0; (i
< 5) && (i
< dlen
); i
++) {
418 if (!strncmp(cmd
, "USER ", 5) || !strncmp(cmd
, "XAUT ", 5)) {
419 if (ftp
->ftp_passok
== FTPXY_ADOK_1
||
420 ftp
->ftp_passok
== FTPXY_AUOK_1
) {
421 ftp
->ftp_passok
= FTPXY_USER_2
;
424 ftp
->ftp_passok
= FTPXY_USER_1
;
427 } else if (!strncmp(cmd
, "AUTH ", 5)) {
428 ftp
->ftp_passok
= FTPXY_AUTH_1
;
430 } else if (!strncmp(cmd
, "PASS ", 5)) {
431 if (ftp
->ftp_passok
== FTPXY_USOK_1
) {
432 ftp
->ftp_passok
= FTPXY_PASS_1
;
434 } else if (ftp
->ftp_passok
== FTPXY_USOK_2
) {
435 ftp
->ftp_passok
= FTPXY_PASS_2
;
438 } else if ((ftp
->ftp_passok
== FTPXY_AUOK_1
) &&
439 !strncmp(cmd
, "ADAT ", 5)) {
440 ftp
->ftp_passok
= FTPXY_ADAT_1
;
442 } else if ((ftp
->ftp_passok
== FTPXY_PAOK_1
||
443 ftp
->ftp_passok
== FTPXY_PAOK_2
) &&
444 !strncmp(cmd
, "ACCT ", 5)) {
445 ftp
->ftp_passok
= FTPXY_ACCT_1
;
447 } else if ((ftp
->ftp_passok
== FTPXY_GO
) && !ippr_ftp_pasvonly
&&
448 !strncmp(cmd
, "PORT ", 5)) {
449 inc
= ippr_ftp_port(fin
, ip
, nat
, f
, dlen
);
450 } else if (ippr_ftp_insecure
&& !ippr_ftp_pasvonly
&&
451 !strncmp(cmd
, "PORT ", 5)) {
452 inc
= ippr_ftp_port(fin
, ip
, nat
, f
, dlen
);
455 while ((*rptr
++ != '\n') && (rptr
< wptr
))
462 int ippr_ftp_pasv(fin
, ip
, nat
, ftp
, dlen
)
469 u_int a1
, a2
, a3
, a4
, data_ip
;
470 char newbuf
[IPF_FTPBUFSZ
];
471 const char *brackets
[2];
476 if (ippr_ftp_forcepasv
!= 0 &&
477 ftp
->ftp_side
[0].ftps_cmds
!= FTPXY_C_PASV
) {
478 if (ippr_ftp_debug
> 0)
479 printf("ippr_ftp_pasv:ftps_cmds(%d) != FTPXY_C_PASV\n",
480 ftp
->ftp_side
[0].ftps_cmds
);
484 f
= &ftp
->ftp_side
[1];
486 #define PASV_REPLEN 24
488 * Check for PASV reply message.
490 if (dlen
< IPF_MIN227LEN
) {
491 if (ippr_ftp_debug
> 1)
492 printf("ippr_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n",
495 } else if (strncmp(f
->ftps_rptr
,
496 "227 Entering Passive Mod", PASV_REPLEN
)) {
497 if (ippr_ftp_debug
> 0)
498 printf("ippr_ftp_pasv:%d reply wrong\n", 227);
505 * Skip the PASV reply + space
507 s
= f
->ftps_rptr
+ PASV_REPLEN
;
508 while (*s
&& !ISDIGIT(*s
)) {
517 * Pick out the address components, two at a time.
519 a1
= ippr_ftp_atoi(&s
);
521 if (ippr_ftp_debug
> 1)
522 printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 1);
525 a2
= ippr_ftp_atoi(&s
);
527 if (ippr_ftp_debug
> 1)
528 printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 2);
533 * check that IP address in the PASV reply is the same as the
534 * sender of the command - prevents using PASV for port scanning.
539 if (((nat
->nat_dir
== NAT_INBOUND
) &&
540 (a1
!= ntohl(nat
->nat_inip
.s_addr
))) ||
541 ((nat
->nat_dir
== NAT_OUTBOUND
) &&
542 (a1
!= ntohl(nat
->nat_oip
.s_addr
)))) {
543 if (ippr_ftp_debug
> 0)
544 printf("ippr_ftp_pasv:%s != nat->nat_oip\n", "a1");
548 a5
= ippr_ftp_atoi(&s
);
550 if (ippr_ftp_debug
> 1)
551 printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 3);
562 * check for CR-LF at the end.
564 if ((*s
== '\r') && (*(s
+ 1) == '\n')) {
567 if (ippr_ftp_debug
> 1)
568 printf("ippr_ftp_pasv:missing %s", "cr-lf\n");
575 * Calculate new address parts for 227 reply
577 if (nat
->nat_dir
== NAT_INBOUND
) {
578 data_ip
= nat
->nat_outip
.s_addr
;
583 a2
= (a1
>> 16) & 0xff;
584 a3
= (a1
>> 8) & 0xff;
588 #if defined(SNPRINTF) && defined(_KERNEL)
589 SNPRINTF(newbuf
, sizeof(newbuf
), "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
590 "227 Entering Passive Mode", brackets
[0], a1
, a2
, a3
, a4
,
591 a5
, a6
, brackets
[1]);
593 (void) sprintf(newbuf
, "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
594 "227 Entering Passive Mode", brackets
[0], a1
, a2
, a3
, a4
,
595 a5
, a6
, brackets
[1]);
597 return ippr_ftp_pasvreply(fin
, ip
, nat
, f
, (a5
<< 8 | a6
),
601 int ippr_ftp_pasvreply(fin
, ip
, nat
, f
, port
, newmsg
, s
, data_ip
)
611 int inc
, off
, nflags
, sflags
;
612 tcphdr_t
*tcp
, tcph
, *tcp2
;
613 struct in_addr swip
, swip2
;
614 struct in_addr data_addr
;
621 tcp
= (tcphdr_t
*)fin
->fin_dp
;
622 off
= (char *)tcp
- (char *)ip
+ (TCP_OFF(tcp
) << 2) + fin
->fin_ipoff
;
624 data_addr
.s_addr
= data_ip
;
629 olen
= s
- f
->ftps_rptr
;
630 nlen
= strlen(newmsg
);
632 if ((inc
+ ip
->ip_len
) > 65535) {
633 if (ippr_ftp_debug
> 0)
634 printf("ippr_ftp_pasv:inc(%d) + ip->ip_len > 65535\n",
639 #if !defined(_KERNEL)
640 bcopy(newmsg
, MTOD(m
, char *) + off
, nlen
);
644 (void)adjmsg(m
, inc
);
645 # else /* defined(MENTAT) */
647 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
648 * mean remove -len bytes from the end of the packet.
649 * The mbuf chain will be extended if necessary by m_copyback().
653 # endif /* defined(MENTAT) */
654 #endif /* !defined(_KERNEL) */
655 COPYBACK(m
, off
, nlen
, newmsg
);
659 fin
->fin_dlen
+= inc
;
660 fin
->fin_plen
+= inc
;
664 * Add skeleton NAT entry for connection which will come back the
667 bcopy((char *)fin
, (char *)&fi
, sizeof(fi
));
668 fi
.fin_flx
|= FI_IGNORE
;
670 fi
.fin_data
[1] = port
;
671 nflags
= IPN_TCP
|SI_W_SPORT
;
672 if (ippr_ftp_pasvrdr
&& f
->ftps_ifp
)
673 nflags
|= SI_W_DPORT
;
674 if (nat
->nat_dir
== NAT_OUTBOUND
)
675 nat2
= nat_outlookup(&fi
, nflags
|NAT_SEARCH
,
676 nat
->nat_p
, nat
->nat_inip
, nat
->nat_oip
);
678 nat2
= nat_inlookup(&fi
, nflags
|NAT_SEARCH
,
679 nat
->nat_p
, nat
->nat_inip
, nat
->nat_oip
);
684 ip
->ip_len
= fin
->fin_hlen
+ sizeof(*tcp2
);
685 bzero((char *)tcp2
, sizeof(*tcp2
));
686 tcp2
->th_win
= htons(8192);
687 tcp2
->th_sport
= 0; /* XXX - fake it for nat_new */
689 tcp2
->th_flags
= TH_SYN
;
690 fi
.fin_data
[1] = port
;
691 fi
.fin_dlen
= sizeof(*tcp2
);
692 tcp2
->th_dport
= htons(port
);
694 fi
.fin_dp
= (char *)tcp2
;
695 fi
.fin_plen
= fi
.fin_hlen
+ sizeof(*tcp
);
696 fi
.fin_fr
= &ftppxyfr
;
697 fi
.fin_out
= nat
->nat_dir
;
698 fi
.fin_flx
&= FI_LOWTTL
|FI_FRAG
|FI_TCPUDP
|FI_OPTIONS
|FI_IGNORE
;
701 if (nat
->nat_dir
== NAT_OUTBOUND
) {
702 fi
.fin_fi
.fi_daddr
= data_addr
.s_addr
;
703 fi
.fin_fi
.fi_saddr
= nat
->nat_inip
.s_addr
;
704 ip
->ip_dst
= data_addr
;
705 ip
->ip_src
= nat
->nat_inip
;
706 } else if (nat
->nat_dir
== NAT_INBOUND
) {
707 fi
.fin_fi
.fi_saddr
= nat
->nat_oip
.s_addr
;
708 fi
.fin_fi
.fi_daddr
= nat
->nat_outip
.s_addr
;
709 ip
->ip_src
= nat
->nat_oip
;
710 ip
->ip_dst
= nat
->nat_outip
;
715 if (nat
->nat_dir
== NAT_INBOUND
)
716 nflags
|= NAT_NOTRULEPORT
;
717 MUTEX_ENTER(&ipf_nat_new
);
718 nat2
= nat_new(&fi
, nat
->nat_ptr
, NULL
, nflags
, nat
->nat_dir
);
719 MUTEX_EXIT(&ipf_nat_new
);
721 (void) nat_proto(&fi
, nat2
, IPN_TCP
);
722 MUTEX_ENTER(&nat2
->nat_lock
);
723 nat_update(&fi
, nat2
);
724 MUTEX_EXIT(&nat2
->nat_lock
);
726 if (nat
->nat_dir
== NAT_INBOUND
) {
727 fi
.fin_fi
.fi_daddr
= nat
->nat_inip
.s_addr
;
728 ip
->ip_dst
= nat
->nat_inip
;
730 (void) fr_addstate(&fi
, NULL
, sflags
);
741 int ippr_ftp_server(fin
, ip
, nat
, ftp
, dlen
)
753 f
= &ftp
->ftp_side
[1];
759 if (!ISDIGIT(*rptr
) || !ISDIGIT(*(rptr
+ 1)) || !ISDIGIT(*(rptr
+ 2)))
761 if (ftp
->ftp_passok
== FTPXY_GO
) {
762 if (!strncmp(rptr
, "227 ", 4))
763 inc
= ippr_ftp_pasv(fin
, ip
, nat
, ftp
, dlen
);
764 else if (!strncmp(rptr
, "229 ", 4))
765 inc
= ippr_ftp_epsv(fin
, ip
, nat
, f
, dlen
);
766 } else if (ippr_ftp_insecure
&& !strncmp(rptr
, "227 ", 4)) {
767 inc
= ippr_ftp_pasv(fin
, ip
, nat
, ftp
, dlen
);
768 } else if (ippr_ftp_insecure
&& !strncmp(rptr
, "229 ", 4)) {
769 inc
= ippr_ftp_epsv(fin
, ip
, nat
, f
, dlen
);
770 } else if (*rptr
== '5' || *rptr
== '4')
771 ftp
->ftp_passok
= FTPXY_INIT
;
772 else if (ftp
->ftp_incok
) {
774 if (ftp
->ftp_passok
== FTPXY_ACCT_1
)
775 ftp
->ftp_passok
= FTPXY_GO
;
778 } else if (*rptr
== '2') {
779 switch (ftp
->ftp_passok
)
786 ftp
->ftp_passok
= FTPXY_GO
;
789 ftp
->ftp_passok
+= 3;
797 while ((*rptr
++ != '\n') && (rptr
< wptr
))
805 * Look to see if the buffer starts with something which we recognise as
806 * being the correct syntax for the FTP protocol.
808 int ippr_ftp_client_valid(ftps
, buf
, len
)
813 register char *s
, c
, pc
;
814 register size_t i
= len
;
819 if (ftps
->ftps_junk
== FTPXY_JUNK_BAD
)
820 return FTPXY_JUNK_BAD
;
823 if (ippr_ftp_debug
> 3)
824 printf("ippr_ftp_client_valid:i(%d) < 5\n", (int)i
);
847 if ((c
!= ' ') && (c
!= '\r'))
848 goto bad_client_command
;
849 } else if ((c
!= ' ') && (c
!= '\r'))
850 goto bad_client_command
;
852 goto bad_client_command
;
854 goto bad_client_command
;
857 if (ippr_ftp_debug
> 3)
858 printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
859 "ippr_ftp_client_valid",
860 ftps
->ftps_junk
, (int)len
, (int)i
, c
,
861 (int)len
, (int)len
, buf
);
862 return FTPXY_JUNK_BAD
;
868 if ((pc
== '\r') && (c
== '\n')) {
870 if (!strcmp(cmd
, "PASV"))
871 ftps
->ftps_cmds
= FTPXY_C_PASV
;
877 #if !defined(_KERNEL)
878 printf("ippr_ftp_client_valid:junk after cmd[%*.*s]\n",
879 (int)len
, (int)len
, buf
);
881 return FTPXY_JUNK_EOL
;
885 int ippr_ftp_server_valid(ftps
, buf
, len
)
890 register char *s
, c
, pc
;
891 register size_t i
= len
;
897 if (ftps
->ftps_junk
== FTPXY_JUNK_BAD
)
898 return FTPXY_JUNK_BAD
;
901 if (ippr_ftp_debug
> 3)
902 printf("ippr_ftp_servert_valid:i(%d) < 5\n", (int)i
);
914 cmd
= (c
- '0') * 100;
918 cmd
+= (c
- '0') * 10;
925 if ((c
!= '-') && (c
!= ' '))
926 goto bad_server_command
;
928 return FTPXY_JUNK_CONT
;
930 goto bad_server_command
;
932 goto bad_server_command
;
935 if (ippr_ftp_debug
> 3)
936 printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
937 "ippr_ftp_server_valid",
938 ftps
->ftps_junk
, (int)len
, (int)i
,
939 c
, (int)len
, (int)len
, buf
);
940 if (ftps
->ftps_junk
== FTPXY_JUNK_CONT
)
941 return FTPXY_JUNK_CONT
;
942 return FTPXY_JUNK_BAD
;
948 if ((pc
== '\r') && (c
== '\n')) {
950 if (ftps
->ftps_junk
== FTPXY_JUNK_CONT
)
951 return FTPXY_JUNK_CONT
;
953 ftps
->ftps_cmds
= cmd
;
955 return FTPXY_JUNK_OK
;
958 if (ippr_ftp_debug
> 3)
959 printf("ippr_ftp_server_valid:junk after cmd[%*.*s]\n",
960 (int)len
, (int)len
, buf
);
961 return FTPXY_JUNK_EOL
;
965 int ippr_ftp_valid(ftp
, side
, buf
, len
)
974 ftps
= &ftp
->ftp_side
[side
];
977 ret
= ippr_ftp_client_valid(ftps
, buf
, len
);
979 ret
= ippr_ftp_server_valid(ftps
, buf
, len
);
985 * For map rules, the following applies:
986 * rv == 0 for outbound processing,
987 * rv == 1 for inbound processing.
988 * For rdr rules, the following applies:
989 * rv == 0 for inbound processing,
990 * rv == 1 for outbound processing.
992 int ippr_ftp_process(fin
, nat
, ftp
, rv
)
998 int mlen
, len
, off
, inc
, i
, sel
, sel2
, ok
, ackoff
, seqoff
, retry
;
999 char *rptr
, *wptr
, *s
;
1000 u_32_t thseq
, thack
;
1009 tcp
= (tcphdr_t
*)fin
->fin_dp
;
1010 off
= (char *)tcp
- (char *)ip
+ (TCP_OFF(tcp
) << 2) + fin
->fin_ipoff
;
1012 f
= &ftp
->ftp_side
[rv
];
1013 t
= &ftp
->ftp_side
[1 - rv
];
1014 thseq
= ntohl(tcp
->th_seq
);
1015 thack
= ntohl(tcp
->th_ack
);
1018 mlen
= fin
->fin_plen
- off
;
1020 mlen
= MSGDSIZE(m
) - off
;
1022 if (ippr_ftp_debug
> 4)
1023 printf("ippr_ftp_process: mlen %d\n", mlen
);
1025 if ((mlen
== 0) && ((tcp
->th_flags
& TH_OPENING
) == TH_OPENING
)) {
1026 f
->ftps_seq
[0] = thseq
+ 1;
1027 t
->ftps_seq
[0] = thack
;
1029 } else if (mlen
< 0) {
1035 sel
= aps
->aps_sel
[1 - rv
];
1036 sel2
= aps
->aps_sel
[rv
];
1038 seqoff
= aps
->aps_seqoff
[sel
];
1039 if (aps
->aps_seqmin
[sel
] > seqoff
+ thseq
)
1040 seqoff
= aps
->aps_seqoff
[!sel
];
1041 ackoff
= aps
->aps_ackoff
[sel2
];
1042 if (aps
->aps_ackmin
[sel2
] > ackoff
+ thack
)
1043 ackoff
= aps
->aps_ackoff
[!sel2
];
1045 seqoff
= aps
->aps_ackoff
[sel
];
1046 if (ippr_ftp_debug
> 2)
1047 printf("seqoff %d thseq %x ackmin %x\n", seqoff
, thseq
,
1048 aps
->aps_ackmin
[sel
]);
1049 if (aps
->aps_ackmin
[sel
] > seqoff
+ thseq
)
1050 seqoff
= aps
->aps_ackoff
[!sel
];
1052 ackoff
= aps
->aps_seqoff
[sel2
];
1053 if (ippr_ftp_debug
> 2)
1054 printf("ackoff %d thack %x seqmin %x\n", ackoff
, thack
,
1055 aps
->aps_seqmin
[sel2
]);
1057 if (aps
->aps_seqmin
[sel2
] > ackoff
+ thack
)
1058 ackoff
= aps
->aps_seqoff
[!sel2
];
1060 if (aps
->aps_seqmin
[sel2
] > thack
)
1061 ackoff
= aps
->aps_seqoff
[!sel2
];
1064 if (ippr_ftp_debug
> 2) {
1065 printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n",
1066 rv
? "IN" : "OUT", tcp
->th_flags
, thseq
, seqoff
,
1067 thack
, ackoff
, mlen
, fin
->fin_plen
, off
);
1068 printf("sel %d seqmin %x/%x offset %d/%d\n", sel
,
1069 aps
->aps_seqmin
[sel
], aps
->aps_seqmin
[sel2
],
1070 aps
->aps_seqoff
[sel
], aps
->aps_seqoff
[sel2
]);
1071 printf("sel %d ackmin %x/%x offset %d/%d\n", sel2
,
1072 aps
->aps_ackmin
[sel
], aps
->aps_ackmin
[sel2
],
1073 aps
->aps_ackoff
[sel
], aps
->aps_ackoff
[sel2
]);
1077 * XXX - Ideally, this packet should get dropped because we now know
1078 * that it is out of order (and there is no real danger in doing so
1079 * apart from causing packets to go through here ordered).
1081 if (ippr_ftp_debug
> 2) {
1082 printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
1083 rv
, t
->ftps_seq
[0], t
->ftps_seq
[1], seqoff
, ackoff
);
1087 if (t
->ftps_seq
[0] == 0) {
1088 t
->ftps_seq
[0] = thack
;
1092 if (t
->ftps_seq
[0] == thack
)
1094 else if (t
->ftps_seq
[1] == thack
) {
1095 t
->ftps_seq
[0] = thack
;
1099 if (t
->ftps_seq
[0] + ackoff
== thack
)
1101 else if (t
->ftps_seq
[0] == thack
+ ackoff
)
1103 else if (t
->ftps_seq
[1] + ackoff
== thack
) {
1104 t
->ftps_seq
[0] = thack
- ackoff
;
1106 } else if (t
->ftps_seq
[1] == thack
+ ackoff
) {
1107 t
->ftps_seq
[0] = thack
- ackoff
;
1113 if (ippr_ftp_debug
> 2) {
1115 printf("%s ok\n", "not");
1119 if (t
->ftps_seq
[0] + ackoff
!= thack
) {
1120 if (ippr_ftp_debug
> 1) {
1121 printf("%s:seq[0](%x) + (%x) != (%x)\n",
1122 "ippr_ftp_process", t
->ftps_seq
[0],
1128 if (ippr_ftp_debug
> 2) {
1129 printf("ippr_ftp_process:f:seq[0] %x seq[1] %x\n",
1130 f
->ftps_seq
[0], f
->ftps_seq
[1]);
1133 if (tcp
->th_flags
& TH_FIN
) {
1134 if (thseq
== f
->ftps_seq
[1]) {
1135 f
->ftps_seq
[0] = f
->ftps_seq
[1] - seqoff
;
1136 f
->ftps_seq
[1] = thseq
+ 1 - seqoff
;
1138 if (ippr_ftp_debug
> 1) {
1139 printf("FIN: thseq %x seqoff %d ftps_seq %x %x\n",
1140 thseq
, seqoff
, f
->ftps_seq
[0], f
->ftps_seq
[1]);
1150 if ((thseq
== f
->ftps_seq
[0]) || (thseq
== f
->ftps_seq
[1])) {
1153 * Retransmitted data packet.
1155 } else if ((thseq
+ mlen
== f
->ftps_seq
[0]) ||
1156 (thseq
+ mlen
== f
->ftps_seq
[1])) {
1161 inc
= thseq
- f
->ftps_seq
[0];
1162 if (ippr_ftp_debug
> 1) {
1163 printf("inc %d sel %d rv %d\n", inc
, sel
, rv
);
1164 printf("th_seq %x ftps_seq %x/%x\n",
1165 thseq
, f
->ftps_seq
[0], f
->ftps_seq
[1]);
1166 printf("ackmin %x ackoff %d\n", aps
->aps_ackmin
[sel
],
1167 aps
->aps_ackoff
[sel
]);
1168 printf("seqmin %x seqoff %d\n", aps
->aps_seqmin
[sel
],
1169 aps
->aps_seqoff
[sel
]);
1176 rptr
= f
->ftps_rptr
;
1177 wptr
= f
->ftps_wptr
;
1178 f
->ftps_seq
[0] = thseq
;
1179 f
->ftps_seq
[1] = f
->ftps_seq
[0] + mlen
;
1183 len
= MIN(mlen
, sizeof(f
->ftps_buf
) - (wptr
- rptr
));
1186 COPYDATA(m
, off
, len
, wptr
);
1192 if (ippr_ftp_debug
> 3)
1193 printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n",
1195 len
, mlen
, off
, (u_long
)wptr
, f
->ftps_junk
,
1198 f
->ftps_wptr
= wptr
;
1199 if (f
->ftps_junk
!= FTPXY_JUNK_OK
) {
1201 f
->ftps_junk
= ippr_ftp_valid(ftp
, rv
, rptr
,
1204 if (ippr_ftp_debug
> 5)
1205 printf("%s:junk %d -> %d\n",
1206 "ippr_ftp_process", i
, f
->ftps_junk
);
1208 if (f
->ftps_junk
== FTPXY_JUNK_BAD
) {
1209 if (wptr
- rptr
== sizeof(f
->ftps_buf
)) {
1210 if (ippr_ftp_debug
> 4)
1211 printf("%s:full buffer\n",
1212 "ippr_ftp_process");
1213 f
->ftps_rptr
= f
->ftps_buf
;
1214 f
->ftps_wptr
= f
->ftps_buf
;
1215 rptr
= f
->ftps_rptr
;
1216 wptr
= f
->ftps_wptr
;
1222 while ((f
->ftps_junk
== FTPXY_JUNK_OK
) && (wptr
> rptr
)) {
1224 f
->ftps_junk
= ippr_ftp_valid(ftp
, rv
, rptr
, len
);
1226 if (ippr_ftp_debug
> 3) {
1227 printf("%s=%d len %d rv %d ptr %lx/%lx ",
1229 f
->ftps_junk
, len
, rv
, (u_long
)rptr
,
1231 printf("buf [%*.*s]\n", len
, len
, rptr
);
1234 if (f
->ftps_junk
== FTPXY_JUNK_OK
) {
1235 f
->ftps_rptr
= rptr
;
1237 inc
+= ippr_ftp_server(fin
, ip
, nat
,
1240 inc
+= ippr_ftp_client(fin
, ip
, nat
,
1242 rptr
= f
->ftps_rptr
;
1243 wptr
= f
->ftps_wptr
;
1248 * Off to a bad start so lets just forget about using the
1249 * ftp proxy for this connection.
1251 if ((f
->ftps_cmds
== 0) && (f
->ftps_junk
== FTPXY_JUNK_BAD
)) {
1252 /* f->ftps_seq[1] += inc; */
1254 if (ippr_ftp_debug
> 1)
1255 printf("%s:cmds == 0 junk == 1\n",
1256 "ippr_ftp_process");
1261 if ((f
->ftps_junk
!= FTPXY_JUNK_OK
) && (rptr
< wptr
)) {
1262 for (s
= rptr
; s
< wptr
; s
++) {
1263 if ((*s
== '\r') && (s
+ 1 < wptr
) &&
1264 (*(s
+ 1) == '\n')) {
1267 if (f
->ftps_junk
!= FTPXY_JUNK_CONT
)
1268 f
->ftps_junk
= FTPXY_JUNK_OK
;
1275 rptr
= wptr
= f
->ftps_buf
;
1278 * Compact the buffer back to the start. The junk
1279 * flag should already be set and because we're not
1280 * throwing away any data, it is preserved from its
1283 if (rptr
> f
->ftps_buf
) {
1284 bcopy(rptr
, f
->ftps_buf
, wptr
- rptr
);
1285 wptr
-= rptr
- f
->ftps_buf
;
1289 f
->ftps_rptr
= rptr
;
1290 f
->ftps_wptr
= wptr
;
1295 /* f->ftps_seq[1] += inc; */
1296 if (tcp
->th_flags
& TH_FIN
)
1298 if (ippr_ftp_debug
> 3) {
1300 mlen
= fin
->fin_plen
;
1305 printf("ftps_seq[1] = %x inc %d len %d\n",
1306 f
->ftps_seq
[1], inc
, mlen
);
1309 f
->ftps_rptr
= rptr
;
1310 f
->ftps_wptr
= wptr
;
1311 return APR_INC(inc
);
1315 int ippr_ftp_out(fin
, aps
, nat
)
1323 ftp
= aps
->aps_data
;
1327 rev
= (nat
->nat_dir
== NAT_OUTBOUND
) ? 0 : 1;
1328 if (ftp
->ftp_side
[1 - rev
].ftps_ifp
== NULL
)
1329 ftp
->ftp_side
[1 - rev
].ftps_ifp
= fin
->fin_ifp
;
1331 return ippr_ftp_process(fin
, nat
, ftp
, rev
);
1335 int ippr_ftp_in(fin
, aps
, nat
)
1343 ftp
= aps
->aps_data
;
1347 rev
= (nat
->nat_dir
== NAT_OUTBOUND
) ? 0 : 1;
1348 if (ftp
->ftp_side
[rev
].ftps_ifp
== NULL
)
1349 ftp
->ftp_side
[rev
].ftps_ifp
= fin
->fin_ifp
;
1351 return ippr_ftp_process(fin
, nat
, ftp
, 1 - rev
);
1356 * ippr_ftp_atoi - implement a version of atoi which processes numbers in
1357 * pairs separated by commas (which are expected to be in the range 0 - 255),
1358 * returning a 16 bit number combining either side of the , as the MSB and
1361 u_short
ippr_ftp_atoi(ptr
)
1364 register char *s
= *ptr
, c
;
1365 register u_char i
= 0, j
= 0;
1367 while (((c
= *s
++) != '\0') && ISDIGIT(c
)) {
1375 while (((c
= *s
++) != '\0') && ISDIGIT(c
)) {
1382 return (i
<< 8) | j
;
1386 int ippr_ftp_epsv(fin
, ip
, nat
, f
, dlen
)
1393 char newbuf
[IPF_FTPBUFSZ
];
1397 #define EPSV_REPLEN 33
1399 * Check for EPSV reply message.
1401 if (dlen
< IPF_MIN229LEN
)
1403 else if (strncmp(f
->ftps_rptr
,
1404 "229 Entering Extended Passive Mode", EPSV_REPLEN
))
1408 * Skip the EPSV command + space
1410 s
= f
->ftps_rptr
+ 33;
1411 while (*s
&& !ISDIGIT(*s
))
1415 * As per RFC 2428, there are no addres components in the EPSV
1416 * response. So we'll go straight to getting the port.
1418 while (*s
&& ISDIGIT(*s
)) {
1433 * check for CR-LF at the end.
1435 if ((*s
== '\r') && (*(s
+ 1) == '\n')) {
1440 #if defined(SNPRINTF) && defined(_KERNEL)
1441 SNPRINTF(newbuf
, sizeof(newbuf
), "%s (|||%u|)\r\n",
1442 "229 Entering Extended Passive Mode", ap
);
1444 (void) sprintf(newbuf
, "%s (|||%u|)\r\n",
1445 "229 Entering Extended Passive Mode", ap
);
1448 return ippr_ftp_pasvreply(fin
, ip
, nat
, f
, (u_int
)ap
, newbuf
, s
,