ip_ftp_pxy.c revision 1.2.4.2 1 /* $NetBSD: ip_ftp_pxy.c,v 1.2.4.2 2012/04/17 00:08:15 yamt Exp $ */
2
3 /*
4 * Copyright (C) 2011 by Darren Reed.
5 *
6 * See the IPFILTER.LICENCE file for details on licencing.
7 *
8 * Simple FTP transparent proxy for in-kernel use. For use with the NAT
9 * code.
10 *
11 * Id: ip_ftp_pxy.c,v 2.129.2.2 2012/01/26 05:29:11 darrenr Exp
12 */
13
14 #include <sys/cdefs.h>
15 __KERNEL_RCSID(1, "$NetBSD: ip_ftp_pxy.c,v 1.2.4.2 2012/04/17 00:08:15 yamt Exp $");
16
17 #define IPF_FTP_PROXY
18
19 #define IPF_MINPORTLEN 18
20 #define IPF_MINEPRTLEN 20
21 #define IPF_MAXPORTLEN 30
22 #define IPF_MIN227LEN 39
23 #define IPF_MAX227LEN 51
24 #define IPF_MIN229LEN 47
25 #define IPF_MAX229LEN 51
26
27 #define FTPXY_GO 0
28 #define FTPXY_INIT 1
29 #define FTPXY_USER_1 2
30 #define FTPXY_USOK_1 3
31 #define FTPXY_PASS_1 4
32 #define FTPXY_PAOK_1 5
33 #define FTPXY_AUTH_1 6
34 #define FTPXY_AUOK_1 7
35 #define FTPXY_ADAT_1 8
36 #define FTPXY_ADOK_1 9
37 #define FTPXY_ACCT_1 10
38 #define FTPXY_ACOK_1 11
39 #define FTPXY_USER_2 12
40 #define FTPXY_USOK_2 13
41 #define FTPXY_PASS_2 14
42 #define FTPXY_PAOK_2 15
43
44 #define FTPXY_JUNK_OK 0
45 #define FTPXY_JUNK_BAD 1 /* Ignore all commands for this connection */
46 #define FTPXY_JUNK_EOL 2 /* consume the rest of this line only */
47 #define FTPXY_JUNK_CONT 3 /* Saerching for next numeric */
48
49 /*
50 * Values for FTP commands. Numerics cover 0-999
51 */
52 #define FTPXY_C_PASV 1000
53 #define FTPXY_C_PORT 1001
54 #define FTPXY_C_EPSV 1002
55 #define FTPXY_C_EPRT 1003
56
57
58 typedef struct ipf_ftp_softc_s {
59 int ipf_p_ftp_pasvonly;
60 /* Do not require logins before transfers */
61 int ipf_p_ftp_insecure;
62 int ipf_p_ftp_pasvrdr;
63 /* PASV must be last command prior to 227 */
64 int ipf_p_ftp_forcepasv;
65 int ipf_p_ftp_debug;
66 int ipf_p_ftp_single_xfer;
67 void *ipf_p_ftp_tune;
68 } ipf_ftp_softc_t;
69
70
71 void ipf_p_ftp_main_load(void);
72 void ipf_p_ftp_main_unload(void);
73 void *ipf_p_ftp_soft_create(ipf_main_softc_t *);
74 void ipf_p_ftp_soft_destroy(ipf_main_softc_t *, void *);
75
76 int ipf_p_ftp_client(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
77 ftpinfo_t *, int);
78 int ipf_p_ftp_complete(char *, size_t);
79 int ipf_p_ftp_in(void *, fr_info_t *, ap_session_t *, nat_t *);
80 int ipf_p_ftp_new(void *, fr_info_t *, ap_session_t *, nat_t *);
81 void ipf_p_ftp_del(ipf_main_softc_t *, ap_session_t *);
82 int ipf_p_ftp_out(void *, fr_info_t *, ap_session_t *, nat_t *);
83 int ipf_p_ftp_pasv(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
84 ftpinfo_t *, int);
85 int ipf_p_ftp_epsv(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
86 ftpinfo_t *, int);
87 int ipf_p_ftp_port(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
88 ftpinfo_t *, int);
89 int ipf_p_ftp_process(ipf_ftp_softc_t *, fr_info_t *, nat_t *,
90 ftpinfo_t *, int);
91 int ipf_p_ftp_server(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
92 ftpinfo_t *, int);
93 int ipf_p_ftp_valid(ipf_ftp_softc_t *, ftpinfo_t *, int, char *, size_t);
94 int ipf_p_ftp_server_valid(ipf_ftp_softc_t *, ftpside_t *, char *,
95 size_t);
96 int ipf_p_ftp_client_valid(ipf_ftp_softc_t *, ftpside_t *, char *,
97 size_t);
98 u_short ipf_p_ftp_atoi(char **);
99 int ipf_p_ftp_pasvreply(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
100 ftpinfo_t *, u_int, char *, char *, u_int);
101 int ipf_p_ftp_eprt(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
102 ftpinfo_t *, int);
103 int ipf_p_ftp_eprt4(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
104 ftpinfo_t *, int);
105 int ipf_p_ftp_addport(ipf_ftp_softc_t *, fr_info_t *, ip_t *, nat_t *,
106 ftpinfo_t *, int, int, int);
107 void ipf_p_ftp_setpending(ipf_main_softc_t *, ftpinfo_t *);
108
109 /*
110 * Debug levels:
111 * 1 - security
112 * 2 - errors
113 * 3 - error debugging
114 * 4 - parsing errors
115 * 5 - parsing info
116 * 6 - parsing debug
117 */
118
119 static int ipf_p_ftp_proxy_init = 0;
120 static frentry_t ftppxyfr;
121 static ipftuneable_t ipf_ftp_tuneables[] = {
122 { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_debug) },
123 "ftp_debug", 0, 10,
124 stsizeof(ipf_ftp_softc_t, ipf_p_ftp_debug),
125 0, NULL, NULL },
126 { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly) },
127 "ftp_pasvonly", 0, 1,
128 stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvonly),
129 0, NULL, NULL },
130 { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_insecure) },
131 "ftp_insecure", 0, 1,
132 stsizeof(ipf_ftp_softc_t, ipf_p_ftp_insecure),
133 0, NULL, NULL },
134 { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr) },
135 "ftp_pasvrdr", 0, 1,
136 stsizeof(ipf_ftp_softc_t, ipf_p_ftp_pasvrdr),
137 0, NULL, NULL },
138 { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv) },
139 "ftp_forcepasv", 0, 1,
140 stsizeof(ipf_ftp_softc_t, ipf_p_ftp_forcepasv),
141 0, NULL, NULL },
142 { { (void *)offsetof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer) },
143 "ftp_single_xfer", 0, 1,
144 stsizeof(ipf_ftp_softc_t, ipf_p_ftp_single_xfer),
145 0, NULL, NULL },
146 { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL }
147 };
148
149
150 void
151 ipf_p_ftp_main_load(void)
152 {
153 bzero((char *)&ftppxyfr, sizeof(ftppxyfr));
154 ftppxyfr.fr_ref = 1;
155 ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
156
157 MUTEX_INIT(&ftppxyfr.fr_lock, "FTP Proxy Mutex");
158 ipf_p_ftp_proxy_init = 1;
159 }
160
161
162 void
163 ipf_p_ftp_main_unload(void)
164 {
165
166 if (ipf_p_ftp_proxy_init == 1) {
167 MUTEX_DESTROY(&ftppxyfr.fr_lock);
168 ipf_p_ftp_proxy_init = 0;
169 }
170 }
171
172
173 /*
174 * Initialize local structures.
175 */
176 void *
177 ipf_p_ftp_soft_create(ipf_main_softc_t *softc)
178 {
179 ipf_ftp_softc_t *softf;
180
181 KMALLOC(softf, ipf_ftp_softc_t *);
182 if (softf == NULL)
183 return NULL;
184
185 bzero((char *)softf, sizeof(*softf));
186 #if defined(_KERNEL)
187 softf->ipf_p_ftp_debug = 0;
188 #else
189 softf->ipf_p_ftp_debug = 2;
190 #endif
191 softf->ipf_p_ftp_forcepasv = 1;
192
193 softf->ipf_p_ftp_tune = ipf_tune_array_copy(softf,
194 sizeof(ipf_ftp_tuneables),
195 ipf_ftp_tuneables);
196 if (softf->ipf_p_ftp_tune == NULL) {
197 ipf_p_ftp_soft_destroy(softc, softf);
198 return NULL;
199 }
200 if (ipf_tune_array_link(softc, softf->ipf_p_ftp_tune) == -1) {
201 ipf_p_ftp_soft_destroy(softc, softf);
202 return NULL;
203 }
204
205 return softf;
206 }
207
208
209 void
210 ipf_p_ftp_soft_destroy(ipf_main_softc_t *softc, void *arg)
211 {
212 ipf_ftp_softc_t *softf = arg;
213
214 if (softf->ipf_p_ftp_tune != NULL) {
215 ipf_tune_array_unlink(softc, softf->ipf_p_ftp_tune);
216 KFREES(softf->ipf_p_ftp_tune, sizeof(ipf_ftp_tuneables));
217 softf->ipf_p_ftp_tune = NULL;
218 }
219
220 KFREE(softf);
221 }
222
223
224 int
225 ipf_p_ftp_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
226 {
227 ftpinfo_t *ftp;
228 ftpside_t *f;
229
230 KMALLOC(ftp, ftpinfo_t *);
231 if (ftp == NULL)
232 return -1;
233
234 fin = fin; /* LINT */
235 nat = nat; /* LINT */
236
237 aps->aps_data = ftp;
238 aps->aps_psiz = sizeof(ftpinfo_t);
239
240 bzero((char *)ftp, sizeof(*ftp));
241 f = &ftp->ftp_side[0];
242 f->ftps_rptr = f->ftps_buf;
243 f->ftps_wptr = f->ftps_buf;
244 f = &ftp->ftp_side[1];
245 f->ftps_rptr = f->ftps_buf;
246 f->ftps_wptr = f->ftps_buf;
247 ftp->ftp_passok = FTPXY_INIT;
248 ftp->ftp_incok = 0;
249 return 0;
250 }
251
252
253 void
254 ipf_p_ftp_setpending(ipf_main_softc_t *softc, ftpinfo_t *ftp)
255 {
256 if (ftp->ftp_pendnat != NULL)
257 ipf_nat_setpending(softc, ftp->ftp_pendnat);
258
259 if (ftp->ftp_pendstate != NULL) {
260 READ_ENTER(&softc->ipf_state);
261 ipf_state_setpending(softc, ftp->ftp_pendstate);
262 RWLOCK_EXIT(&softc->ipf_state);
263 }
264 }
265
266
267 void
268 ipf_p_ftp_del(ipf_main_softc_t *softc, ap_session_t *aps)
269 {
270 ftpinfo_t *ftp;
271
272 ftp = aps->aps_data;
273 if (ftp != NULL)
274 ipf_p_ftp_setpending(softc, ftp);
275 }
276
277
278 int
279 ipf_p_ftp_port(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
280 ftpinfo_t *ftp, int dlen)
281 {
282 char newbuf[IPF_FTPBUFSZ], *s;
283 u_int a1, a2, a3, a4;
284 u_short a5, a6, sp;
285 size_t nlen, olen;
286 tcphdr_t *tcp;
287 int inc, off;
288 ftpside_t *f;
289 mb_t *m;
290
291 m = fin->fin_m;
292 f = &ftp->ftp_side[0];
293 tcp = (tcphdr_t *)fin->fin_dp;
294 off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
295
296 /*
297 * Check for client sending out PORT message.
298 */
299 if (dlen < IPF_MINPORTLEN) {
300 if (softf->ipf_p_ftp_debug > 1)
301 printf("ipf_p_ftp_port:dlen(%d) < IPF_MINPORTLEN\n",
302 dlen);
303 return 0;
304 }
305 /*
306 * Skip the PORT command + space
307 */
308 s = f->ftps_rptr + 5;
309 /*
310 * Pick out the address components, two at a time.
311 */
312 a1 = ipf_p_ftp_atoi(&s);
313 if (s == NULL) {
314 if (softf->ipf_p_ftp_debug > 1)
315 printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 1);
316 return 0;
317 }
318 a2 = ipf_p_ftp_atoi(&s);
319 if (s == NULL) {
320 if (softf->ipf_p_ftp_debug > 1)
321 printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 2);
322 return 0;
323 }
324
325 /*
326 * Check that IP address in the PORT/PASV reply is the same as the
327 * sender of the command - prevents using PORT for port scanning.
328 */
329 a1 <<= 16;
330 a1 |= a2;
331 if (((nat->nat_dir == NAT_OUTBOUND) &&
332 (a1 != ntohl(nat->nat_osrcaddr))) ||
333 ((nat->nat_dir == NAT_INBOUND) &&
334 (a1 != ntohl(nat->nat_odstaddr)))) {
335 if (softf->ipf_p_ftp_debug > 0)
336 printf("ipf_p_ftp_port:%s != nat->nat_inip\n", "a1");
337 return APR_ERR(1);
338 }
339
340 a5 = ipf_p_ftp_atoi(&s);
341 if (s == NULL) {
342 if (softf->ipf_p_ftp_debug > 1)
343 printf("ipf_p_ftp_port:ipf_p_ftp_atoi(%d) failed\n", 3);
344 return 0;
345 }
346 if (*s == ')')
347 s++;
348
349 /*
350 * check for CR-LF at the end.
351 */
352 if (*s == '\n')
353 s--;
354 if ((*s == '\r') && (*(s + 1) == '\n')) {
355 s += 2;
356 a6 = a5 & 0xff;
357 } else {
358 if (softf->ipf_p_ftp_debug > 1)
359 printf("ipf_p_ftp_port:missing %s\n", "cr-lf");
360 return 0;
361 }
362
363 a5 >>= 8;
364 a5 &= 0xff;
365 sp = a5 << 8 | a6;
366 /*
367 * Don't allow the PORT command to specify a port < 1024 due to
368 * security crap.
369 */
370 if (sp < 1024) {
371 if (softf->ipf_p_ftp_debug > 0)
372 printf("ipf_p_ftp_port:sp(%d) < 1024\n", sp);
373 return 0;
374 }
375 /*
376 * Calculate new address parts for PORT command
377 */
378 if (nat->nat_dir == NAT_INBOUND)
379 a1 = ntohl(nat->nat_odstaddr);
380 else
381 a1 = ntohl(ip->ip_src.s_addr);
382 a2 = (a1 >> 16) & 0xff;
383 a3 = (a1 >> 8) & 0xff;
384 a4 = a1 & 0xff;
385 a1 >>= 24;
386 olen = s - f->ftps_rptr;
387 /* DO NOT change this to snprintf! */
388 #if defined(SNPRINTF) && defined(_KERNEL)
389 SNPRINTF(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n",
390 "PORT", a1, a2, a3, a4, a5, a6);
391 #else
392 (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
393 "PORT", a1, a2, a3, a4, a5, a6);
394 #endif
395
396 nlen = strlen(newbuf);
397 inc = nlen - olen;
398 if ((inc + fin->fin_plen) > 65535) {
399 if (softf->ipf_p_ftp_debug > 0)
400 printf("ipf_p_ftp_port:inc(%d) + ip->ip_len > 65535\n",
401 inc);
402 return 0;
403 }
404
405 #if !defined(_KERNEL)
406 bcopy(newbuf, MTOD(m, char *) + off, nlen);
407 m->mb_len += inc;
408 #else
409 /*
410 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
411 * mean remove -len bytes from the end of the packet.
412 * The mbuf chain will be extended if necessary by m_copyback().
413 */
414 if (inc < 0)
415 M_ADJ(m, inc);
416 #endif /* !defined(_KERNEL) */
417 COPYBACK(m, off, nlen, newbuf);
418
419 if (inc != 0) {
420 fin->fin_plen += inc;
421 ip->ip_len = htons(fin->fin_plen);
422 fin->fin_dlen += inc;
423 }
424
425 f->ftps_cmd = FTPXY_C_PORT;
426 return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen,
427 (a5 << 8) | a6, inc);
428 }
429
430
431 int
432 ipf_p_ftp_addport(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
433 ftpinfo_t *ftp, int dlen, int nport, int inc)
434 {
435 tcphdr_t tcph, *tcp2 = &tcph;
436 struct in_addr swip, swip2;
437 ipf_main_softc_t *softc;
438 ipf_nat_softc_t *softn;
439 fr_info_t fi;
440 nat_t *nat2;
441 u_short sp;
442 int flags;
443
444 softc = fin->fin_main_soft;
445 softn = softc->ipf_nat_soft;
446
447 if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL)) {
448 if (softf->ipf_p_ftp_single_xfer != 0) {
449 if (softf->ipf_p_ftp_debug > 0)
450 printf("ipf_p_ftp_addport:xfer active %p/%p\n",
451 ftp->ftp_pendnat, ftp->ftp_pendstate);
452 return 0;
453 }
454 ipf_p_ftp_setpending(softc, ftp);
455 }
456
457 /*
458 * Add skeleton NAT entry for connection which will come back the
459 * other way.
460 */
461 sp = nport;
462 /*
463 * Don't allow the PORT command to specify a port < 1024 due to
464 * security crap.
465 */
466 if (sp < 1024) {
467 if (softf->ipf_p_ftp_debug > 0)
468 printf("ipf_p_ftp_addport:sp(%d) < 1024\n", sp);
469 return 0;
470 }
471 /*
472 * The server may not make the connection back from port 20, but
473 * it is the most likely so use it here to check for a conflicting
474 * mapping.
475 */
476 bcopy((char *)fin, (char *)&fi, sizeof(fi));
477 fi.fin_flx |= FI_IGNORE;
478 fi.fin_data[0] = sp;
479 fi.fin_data[1] = fin->fin_data[1] - 1;
480 /*
481 * Add skeleton NAT entry for connection which will come back the
482 * other way.
483 */
484 if (nat->nat_dir == NAT_OUTBOUND)
485 nat2 = ipf_nat_outlookup(&fi, NAT_SEARCH|IPN_TCP,
486 nat->nat_pr[1], nat->nat_osrcip,
487 nat->nat_odstip);
488 else
489 nat2 = ipf_nat_inlookup(&fi, NAT_SEARCH|IPN_TCP,
490 nat->nat_pr[0], nat->nat_nsrcip,
491 nat->nat_odstip);
492 if (nat2 == NULL) {
493 int slen;
494
495 slen = ip->ip_len;
496 ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp2));
497 bzero((char *)tcp2, sizeof(*tcp2));
498 tcp2->th_win = htons(8192);
499 tcp2->th_sport = htons(sp);
500 TCP_OFF_A(tcp2, 5);
501 tcp2->th_flags = TH_SYN;
502 tcp2->th_dport = 0; /* XXX - don't specify remote port */
503 fi.fin_data[1] = 0;
504 fi.fin_dlen = sizeof(*tcp2);
505 fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
506 fi.fin_dp = (char *)tcp2;
507 fi.fin_fr = &ftppxyfr;
508 fi.fin_out = nat->nat_dir;
509 fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
510 swip = ip->ip_src;
511 swip2 = ip->ip_dst;
512 fi.fin_fi.fi_saddr = nat->nat_osrcaddr;
513 ip->ip_src = nat->nat_osrcip;
514 fi.fin_fi.fi_daddr = nat->nat_odstaddr;
515 ip->ip_dst = nat->nat_odstip;
516
517 flags = NAT_SLAVE|IPN_TCP|SI_W_DPORT;
518 if (nat->nat_dir == NAT_INBOUND)
519 flags |= NAT_NOTRULEPORT;
520 MUTEX_ENTER(&softn->ipf_nat_new);
521 nat2 = ipf_nat_add(&fi, nat->nat_ptr, &ftp->ftp_pendnat,
522 flags, nat->nat_dir);
523 MUTEX_EXIT(&softn->ipf_nat_new);
524
525 if (nat2 != NULL) {
526 (void) ipf_nat_proto(&fi, nat2, IPN_TCP);
527 MUTEX_ENTER(&nat2->nat_lock);
528 ipf_nat_update(&fi, nat2);
529 MUTEX_EXIT(&nat2->nat_lock);
530 fi.fin_ifp = NULL;
531 if (ipf_state_add(softc, &fi,
532 (ipstate_t **)&ftp->ftp_pendstate,
533 SI_W_DPORT) != 0) {
534 ipf_nat_setpending(softc, nat2);
535 }
536 }
537 ip->ip_len = slen;
538 ip->ip_src = swip;
539 ip->ip_dst = swip2;
540 }
541 return APR_INC(inc);
542 }
543
544
545 int
546 ipf_p_ftp_client(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
547 ftpinfo_t *ftp, int dlen)
548 {
549 char *rptr, *wptr, cmd[6], c;
550 ftpside_t *f;
551 int inc, i;
552
553 inc = 0;
554 f = &ftp->ftp_side[0];
555 rptr = f->ftps_rptr;
556 wptr = f->ftps_wptr;
557
558 for (i = 0; (i < 5) && (i < dlen); i++) {
559 c = rptr[i];
560 if (ISALPHA(c)) {
561 cmd[i] = TOUPPER(c);
562 } else {
563 cmd[i] = c;
564 }
565 }
566 cmd[i] = '\0';
567
568 ftp->ftp_incok = 0;
569 if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {
570 if (ftp->ftp_passok == FTPXY_ADOK_1 ||
571 ftp->ftp_passok == FTPXY_AUOK_1) {
572 ftp->ftp_passok = FTPXY_USER_2;
573 ftp->ftp_incok = 1;
574 } else {
575 ftp->ftp_passok = FTPXY_USER_1;
576 ftp->ftp_incok = 1;
577 }
578 } else if (!strncmp(cmd, "AUTH ", 5)) {
579 ftp->ftp_passok = FTPXY_AUTH_1;
580 ftp->ftp_incok = 1;
581 } else if (!strncmp(cmd, "PASS ", 5)) {
582 if (ftp->ftp_passok == FTPXY_USOK_1) {
583 ftp->ftp_passok = FTPXY_PASS_1;
584 ftp->ftp_incok = 1;
585 } else if (ftp->ftp_passok == FTPXY_USOK_2) {
586 ftp->ftp_passok = FTPXY_PASS_2;
587 ftp->ftp_incok = 1;
588 }
589 } else if ((ftp->ftp_passok == FTPXY_AUOK_1) &&
590 !strncmp(cmd, "ADAT ", 5)) {
591 ftp->ftp_passok = FTPXY_ADAT_1;
592 ftp->ftp_incok = 1;
593 } else if ((ftp->ftp_passok == FTPXY_PAOK_1 ||
594 ftp->ftp_passok == FTPXY_PAOK_2) &&
595 !strncmp(cmd, "ACCT ", 5)) {
596 ftp->ftp_passok = FTPXY_ACCT_1;
597 ftp->ftp_incok = 1;
598 } else if ((ftp->ftp_passok == FTPXY_GO) &&
599 !softf->ipf_p_ftp_pasvonly &&
600 !strncmp(cmd, "PORT ", 5)) {
601 inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen);
602 } else if ((ftp->ftp_passok == FTPXY_GO) &&
603 !softf->ipf_p_ftp_pasvonly &&
604 !strncmp(cmd, "EPRT ", 5)) {
605 inc = ipf_p_ftp_eprt(softf, fin, ip, nat, ftp, dlen);
606 } else if (softf->ipf_p_ftp_insecure &&
607 !softf->ipf_p_ftp_pasvonly &&
608 !strncmp(cmd, "PORT ", 5)) {
609 inc = ipf_p_ftp_port(softf, fin, ip, nat, ftp, dlen);
610 }
611 if (softf->ipf_p_ftp_debug > 3)
612 printf("ipf_p_ftp_client: cmd[%s] passok %d incok %d inc %d\n",
613 cmd, ftp->ftp_passok, ftp->ftp_incok, inc);
614
615 while ((*rptr++ != '\n') && (rptr < wptr))
616 ;
617 f->ftps_rptr = rptr;
618 return inc;
619 }
620
621
622 int
623 ipf_p_ftp_pasv(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
624 ftpinfo_t *ftp, int dlen)
625 {
626 u_int a1, a2, a3, a4, data_ip;
627 char newbuf[IPF_FTPBUFSZ];
628 const char *brackets[2];
629 u_short a5, a6;
630 ftpside_t *f;
631 char *s;
632
633 if ((softf->ipf_p_ftp_forcepasv != 0) &&
634 (ftp->ftp_side[0].ftps_cmd != FTPXY_C_PASV)) {
635 if (softf->ipf_p_ftp_debug > 0)
636 printf("ipf_p_ftp_pasv:ftps_cmd(%d) != FTPXY_C_PASV\n",
637 ftp->ftp_side[0].ftps_cmd);
638 return 0;
639 }
640
641 f = &ftp->ftp_side[1];
642
643 #define PASV_REPLEN 24
644 /*
645 * Check for PASV reply message.
646 */
647 if (dlen < IPF_MIN227LEN) {
648 if (softf->ipf_p_ftp_debug > 1)
649 printf("ipf_p_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n",
650 dlen);
651 return 0;
652 } else if (strncmp(f->ftps_rptr,
653 "227 Entering Passive Mod", PASV_REPLEN)) {
654 if (softf->ipf_p_ftp_debug > 0)
655 printf("ipf_p_ftp_pasv:%d reply wrong\n", 227);
656 return 0;
657 }
658
659 brackets[0] = "";
660 brackets[1] = "";
661 /*
662 * Skip the PASV reply + space
663 */
664 s = f->ftps_rptr + PASV_REPLEN;
665 while (*s && !ISDIGIT(*s)) {
666 if (*s == '(') {
667 brackets[0] = "(";
668 brackets[1] = ")";
669 }
670 s++;
671 }
672
673 /*
674 * Pick out the address components, two at a time.
675 */
676 a1 = ipf_p_ftp_atoi(&s);
677 if (s == NULL) {
678 if (softf->ipf_p_ftp_debug > 1)
679 printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 1);
680 return 0;
681 }
682 a2 = ipf_p_ftp_atoi(&s);
683 if (s == NULL) {
684 if (softf->ipf_p_ftp_debug > 1)
685 printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 2);
686 return 0;
687 }
688
689 /*
690 * check that IP address in the PASV reply is the same as the
691 * sender of the command - prevents using PASV for port scanning.
692 */
693 a1 <<= 16;
694 a1 |= a2;
695
696 if (((nat->nat_dir == NAT_INBOUND) &&
697 (a1 != ntohl(nat->nat_osrcaddr))) ||
698 ((nat->nat_dir == NAT_OUTBOUND) &&
699 (a1 != ntohl(nat->nat_odstaddr)))) {
700 if (softf->ipf_p_ftp_debug > 0)
701 printf("ipf_p_ftp_pasv:%s != nat->nat_oip\n", "a1");
702 return 0;
703 }
704
705 a5 = ipf_p_ftp_atoi(&s);
706 if (s == NULL) {
707 if (softf->ipf_p_ftp_debug > 1)
708 printf("ipf_p_ftp_pasv:ipf_p_ftp_atoi(%d) failed\n", 3);
709 return 0;
710 }
711
712 if (*s == ')')
713 s++;
714 if (*s == '.')
715 s++;
716 if (*s == '\n')
717 s--;
718 /*
719 * check for CR-LF at the end.
720 */
721 if ((*s == '\r') && (*(s + 1) == '\n')) {
722 s += 2;
723 } else {
724 if (softf->ipf_p_ftp_debug > 1)
725 printf("ipf_p_ftp_pasv:missing %s", "cr-lf\n");
726 return 0;
727 }
728
729 a6 = a5 & 0xff;
730 a5 >>= 8;
731 /*
732 * Calculate new address parts for 227 reply
733 */
734 if (nat->nat_dir == NAT_INBOUND) {
735 data_ip = nat->nat_ndstaddr;
736 a1 = ntohl(data_ip);
737 } else
738 data_ip = htonl(a1);
739
740 a2 = (a1 >> 16) & 0xff;
741 a3 = (a1 >> 8) & 0xff;
742 a4 = a1 & 0xff;
743 a1 >>= 24;
744
745 #if defined(SNPRINTF) && defined(_KERNEL)
746 SNPRINTF(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
747 "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
748 a5, a6, brackets[1]);
749 #else
750 (void) sprintf(newbuf, "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
751 "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
752 a5, a6, brackets[1]);
753 #endif
754 return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (a5 << 8 | a6),
755 newbuf, s, data_ip);
756 }
757
758 int
759 ipf_p_ftp_pasvreply(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip,
760 nat_t *nat, ftpinfo_t *ftp, u_int port, char *newmsg, char *s,
761 u_int data_ip)
762 {
763 int inc, off, nflags, sflags;
764 tcphdr_t *tcp, tcph, *tcp2;
765 struct in_addr swip, swip2;
766 struct in_addr data_addr;
767 ipf_main_softc_t *softc;
768 ipf_nat_softc_t *softn;
769 size_t nlen, olen;
770 fr_info_t fi;
771 ftpside_t *f;
772 nat_t *nat2;
773 mb_t *m;
774
775 softc = fin->fin_main_soft;
776 softn = softc->ipf_nat_soft;
777
778 if ((ftp->ftp_pendnat != NULL) || (ftp->ftp_pendstate != NULL))
779 ipf_p_ftp_setpending(softc, ftp);
780
781 m = fin->fin_m;
782 tcp = (tcphdr_t *)fin->fin_dp;
783 off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
784
785 data_addr.s_addr = data_ip;
786 tcp2 = &tcph;
787 inc = 0;
788
789 f = &ftp->ftp_side[1];
790 olen = s - f->ftps_rptr;
791 nlen = strlen(newmsg);
792 inc = nlen - olen;
793 if ((inc + fin->fin_plen) > 65535) {
794 if (softf->ipf_p_ftp_debug > 0)
795 printf("ipf_p_ftp_pasv:inc(%d) + ip->ip_len > 65535\n",
796 inc);
797 return 0;
798 }
799
800 #if !defined(_KERNEL)
801 bcopy(newmsg, MTOD(m, char *) + off, nlen);
802 #else
803 /*
804 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
805 * mean remove -len bytes from the end of the packet.
806 * The mbuf chain will be extended if necessary by m_copyback().
807 */
808 if (inc < 0)
809 M_ADJ(m, inc);
810 #endif /* !defined(_KERNEL) */
811 COPYBACK(m, off, nlen, newmsg);
812
813 if (inc != 0) {
814 fin->fin_plen += inc;
815 ip->ip_len = htons(fin->fin_plen);
816 fin->fin_dlen += inc;
817 }
818
819 /*
820 * Add skeleton NAT entry for connection which will come back the
821 * other way.
822 */
823 bcopy((char *)fin, (char *)&fi, sizeof(fi));
824 fi.fin_flx |= FI_IGNORE;
825 fi.fin_data[0] = 0;
826 fi.fin_data[1] = port;
827 nflags = IPN_TCP|SI_W_SPORT;
828 if (softf->ipf_p_ftp_pasvrdr && f->ftps_ifp)
829 nflags |= SI_W_DPORT;
830 if (nat->nat_dir == NAT_OUTBOUND)
831 nat2 = ipf_nat_outlookup(&fi, nflags|NAT_SEARCH,
832 nat->nat_pr[1], nat->nat_osrcip,
833 nat->nat_odstip);
834 else
835 nat2 = ipf_nat_inlookup(&fi, nflags|NAT_SEARCH,
836 nat->nat_pr[0], nat->nat_odstip,
837 nat->nat_osrcip);
838 if (nat2 == NULL) {
839 int slen;
840
841 slen = ip->ip_len;
842 ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp2));
843 bzero((char *)tcp2, sizeof(*tcp2));
844 tcp2->th_win = htons(8192);
845 tcp2->th_sport = 0; /* XXX - fake it for nat_new */
846 TCP_OFF_A(tcp2, 5);
847 tcp2->th_flags = TH_SYN;
848 fi.fin_data[1] = port;
849 fi.fin_dlen = sizeof(*tcp2);
850 tcp2->th_dport = htons(port);
851 fi.fin_data[0] = 0;
852 fi.fin_dp = (char *)tcp2;
853 fi.fin_plen = fi.fin_hlen + sizeof(*tcp);
854 fi.fin_fr = &ftppxyfr;
855 fi.fin_out = nat->nat_dir;
856 fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
857 swip = ip->ip_src;
858 swip2 = ip->ip_dst;
859 if (nat->nat_dir == NAT_OUTBOUND) {
860 fi.fin_fi.fi_daddr = data_addr.s_addr;
861 fi.fin_fi.fi_saddr = nat->nat_nsrcaddr;
862 ip->ip_dst = data_addr;
863 ip->ip_src = nat->nat_nsrcip;
864 } else if (nat->nat_dir == NAT_INBOUND) {
865 fi.fin_fi.fi_saddr = nat->nat_osrcaddr;
866 fi.fin_fi.fi_daddr = nat->nat_ndstaddr;
867 ip->ip_src = nat->nat_osrcip;
868 ip->ip_dst = nat->nat_odstip;
869 }
870
871 sflags = nflags;
872 nflags |= NAT_SLAVE;
873 if (nat->nat_dir == NAT_INBOUND)
874 nflags |= NAT_NOTRULEPORT;
875 MUTEX_ENTER(&softn->ipf_nat_new);
876 nat2 = ipf_nat_add(&fi, nat->nat_ptr, &ftp->ftp_pendnat,
877 nflags, nat->nat_dir);
878 MUTEX_EXIT(&softn->ipf_nat_new);
879
880 if (nat2 != NULL) {
881 (void) ipf_nat_proto(&fi, nat2, IPN_TCP);
882 MUTEX_ENTER(&nat2->nat_lock);
883 ipf_nat_update(&fi, nat2);
884 MUTEX_EXIT(&nat2->nat_lock);
885 fi.fin_ifp = NULL;
886 if (nat->nat_dir == NAT_INBOUND) {
887 fi.fin_fi.fi_daddr = nat->nat_ndstaddr;
888 ip->ip_dst = nat->nat_ndstip;
889 }
890 if (ipf_state_add(softc, &fi,
891 (ipstate_t **)&ftp->ftp_pendstate,
892 sflags) != 0) {
893 ipf_nat_setpending(softc, nat2);
894 }
895 }
896
897 ip->ip_len = slen;
898 ip->ip_src = swip;
899 ip->ip_dst = swip2;
900 }
901 return inc;
902 }
903
904
905 int
906 ipf_p_ftp_server(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
907 ftpinfo_t *ftp, int dlen)
908 {
909 char *rptr, *wptr;
910 ftpside_t *f;
911 int inc;
912
913 inc = 0;
914 f = &ftp->ftp_side[1];
915 rptr = f->ftps_rptr;
916 wptr = f->ftps_wptr;
917
918 if (*rptr == ' ')
919 goto server_cmd_ok;
920 if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2)))
921 return 0;
922 if (ftp->ftp_passok == FTPXY_GO) {
923 if (!strncmp(rptr, "227 ", 4))
924 inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen);
925 else if (!strncmp(rptr, "229 ", 4))
926 inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen);
927 else if (strncmp(rptr, "200", 3)) {
928 /*
929 * 200 is returned for a successful command.
930 */
931 ;
932 }
933 } else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
934 inc = ipf_p_ftp_pasv(softf, fin, ip, nat, ftp, dlen);
935 } else if (softf->ipf_p_ftp_insecure && !strncmp(rptr, "229 ", 4)) {
936 inc = ipf_p_ftp_epsv(softf, fin, ip, nat, ftp, dlen);
937 } else if (*rptr == '5' || *rptr == '4')
938 ftp->ftp_passok = FTPXY_INIT;
939 else if (ftp->ftp_incok) {
940 if (*rptr == '3') {
941 if (ftp->ftp_passok == FTPXY_ACCT_1)
942 ftp->ftp_passok = FTPXY_GO;
943 else
944 ftp->ftp_passok++;
945 } else if (*rptr == '2') {
946 switch (ftp->ftp_passok)
947 {
948 case FTPXY_USER_1 :
949 case FTPXY_USER_2 :
950 case FTPXY_PASS_1 :
951 case FTPXY_PASS_2 :
952 case FTPXY_ACCT_1 :
953 ftp->ftp_passok = FTPXY_GO;
954 break;
955 default :
956 ftp->ftp_passok += 3;
957 break;
958 }
959 }
960 }
961 server_cmd_ok:
962 ftp->ftp_incok = 0;
963
964 while ((*rptr++ != '\n') && (rptr < wptr))
965 ;
966 f->ftps_rptr = rptr;
967 return inc;
968 }
969
970
971 /*
972 * Look to see if the buffer starts with something which we recognise as
973 * being the correct syntax for the FTP protocol.
974 */
975 int
976 ipf_p_ftp_client_valid(ipf_ftp_softc_t *softf, ftpside_t *ftps, char *buf,
977 size_t len)
978 {
979 register char *s, c, pc;
980 register size_t i = len;
981 char cmd[5];
982
983 s = buf;
984
985 if (ftps->ftps_junk == FTPXY_JUNK_BAD)
986 return FTPXY_JUNK_BAD;
987
988 if (i < 5) {
989 if (softf->ipf_p_ftp_debug > 3)
990 printf("ipf_p_ftp_client_valid:i(%d) < 5\n", (int)i);
991 return 2;
992 }
993
994 i--;
995 c = *s++;
996
997 if (ISALPHA(c)) {
998 cmd[0] = TOUPPER(c);
999 c = *s++;
1000 i--;
1001 if (ISALPHA(c)) {
1002 cmd[1] = TOUPPER(c);
1003 c = *s++;
1004 i--;
1005 if (ISALPHA(c)) {
1006 cmd[2] = TOUPPER(c);
1007 c = *s++;
1008 i--;
1009 if (ISALPHA(c)) {
1010 cmd[3] = TOUPPER(c);
1011 c = *s++;
1012 i--;
1013 if ((c != ' ') && (c != '\r'))
1014 goto bad_client_command;
1015 } else if ((c != ' ') && (c != '\r'))
1016 goto bad_client_command;
1017 } else
1018 goto bad_client_command;
1019 } else
1020 goto bad_client_command;
1021 } else {
1022 bad_client_command:
1023 if (softf->ipf_p_ftp_debug > 3)
1024 printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
1025 "ipf_p_ftp_client_valid",
1026 ftps->ftps_junk, (int)len, (int)i, c,
1027 (int)len, (int)len, buf);
1028 return FTPXY_JUNK_BAD;
1029 }
1030
1031 for (; i; i--) {
1032 pc = c;
1033 c = *s++;
1034 if ((pc == '\r') && (c == '\n')) {
1035 cmd[4] = '\0';
1036 if (!strcmp(cmd, "PASV"))
1037 ftps->ftps_cmd = FTPXY_C_PASV;
1038 else
1039 ftps->ftps_cmd = 0;
1040 return 0;
1041 }
1042 }
1043 #if !defined(_KERNEL)
1044 printf("ipf_p_ftp_client_valid:junk after cmd[%*.*s]\n",
1045 (int)len, (int)len, buf);
1046 #endif
1047 return FTPXY_JUNK_EOL;
1048 }
1049
1050
1051 int
1052 ipf_p_ftp_server_valid(ipf_ftp_softc_t *softf, ftpside_t *ftps, char *buf,
1053 size_t len)
1054 {
1055 register char *s, c, pc;
1056 register size_t i = len;
1057 int cmd;
1058
1059 s = buf;
1060 cmd = 0;
1061
1062 if (ftps->ftps_junk == FTPXY_JUNK_BAD)
1063 return FTPXY_JUNK_BAD;
1064
1065 if (i < 5) {
1066 if (softf->ipf_p_ftp_debug > 3)
1067 printf("ipf_p_ftp_servert_valid:i(%d) < 5\n", (int)i);
1068 return 2;
1069 }
1070
1071 c = *s++;
1072 i--;
1073 if (c == ' ') {
1074 cmd = -1;
1075 goto search_eol;
1076 }
1077
1078 if (ISDIGIT(c)) {
1079 cmd = (c - '0') * 100;
1080 c = *s++;
1081 i--;
1082 if (ISDIGIT(c)) {
1083 cmd += (c - '0') * 10;
1084 c = *s++;
1085 i--;
1086 if (ISDIGIT(c)) {
1087 cmd += (c - '0');
1088 c = *s++;
1089 i--;
1090 if ((c != '-') && (c != ' '))
1091 goto bad_server_command;
1092 if (c == '-')
1093 return FTPXY_JUNK_CONT;
1094 } else
1095 goto bad_server_command;
1096 } else
1097 goto bad_server_command;
1098 } else {
1099 bad_server_command:
1100 if (softf->ipf_p_ftp_debug > 3)
1101 printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*.*s]\n",
1102 "ipf_p_ftp_server_valid",
1103 ftps->ftps_junk, (int)len, (int)i,
1104 c, (int)len, (int)len, buf);
1105 if (ftps->ftps_junk == FTPXY_JUNK_CONT)
1106 return FTPXY_JUNK_CONT;
1107 return FTPXY_JUNK_BAD;
1108 }
1109 search_eol:
1110 for (; i; i--) {
1111 pc = c;
1112 c = *s++;
1113 if ((pc == '\r') && (c == '\n')) {
1114 if (cmd == -1) {
1115 if (ftps->ftps_junk == FTPXY_JUNK_CONT)
1116 return FTPXY_JUNK_CONT;
1117 } else {
1118 ftps->ftps_cmd = cmd;
1119 }
1120 return FTPXY_JUNK_OK;
1121 }
1122 }
1123 if (softf->ipf_p_ftp_debug > 3)
1124 printf("ipf_p_ftp_server_valid:junk after cmd[%*.*s]\n",
1125 (int)len, (int)len, buf);
1126 return FTPXY_JUNK_EOL;
1127 }
1128
1129
1130 int
1131 ipf_p_ftp_valid(ipf_ftp_softc_t *softf, ftpinfo_t *ftp, int side, char *buf,
1132 size_t len)
1133 {
1134 ftpside_t *ftps;
1135 int ret;
1136
1137 ftps = &ftp->ftp_side[side];
1138
1139 if (side == 0)
1140 ret = ipf_p_ftp_client_valid(softf, ftps, buf, len);
1141 else
1142 ret = ipf_p_ftp_server_valid(softf, ftps, buf, len);
1143 return ret;
1144 }
1145
1146
1147 /*
1148 * For map rules, the following applies:
1149 * rv == 0 for outbound processing,
1150 * rv == 1 for inbound processing.
1151 * For rdr rules, the following applies:
1152 * rv == 0 for inbound processing,
1153 * rv == 1 for outbound processing.
1154 */
1155 int
1156 ipf_p_ftp_process(ipf_ftp_softc_t *softf, fr_info_t *fin, nat_t *nat,
1157 ftpinfo_t *ftp, int rv)
1158 {
1159 int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff, retry;
1160 char *rptr, *wptr, *s;
1161 u_32_t thseq, thack;
1162 ap_session_t *aps;
1163 ftpside_t *f, *t;
1164 tcphdr_t *tcp;
1165 ip_t *ip;
1166 mb_t *m;
1167
1168 m = fin->fin_m;
1169 ip = fin->fin_ip;
1170 tcp = (tcphdr_t *)fin->fin_dp;
1171 off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1172
1173 f = &ftp->ftp_side[rv];
1174 t = &ftp->ftp_side[1 - rv];
1175 thseq = ntohl(tcp->th_seq);
1176 thack = ntohl(tcp->th_ack);
1177
1178 #ifdef __sgi
1179 mlen = fin->fin_plen - off;
1180 #else
1181 mlen = MSGDSIZE(m) - off;
1182 #endif
1183 if (softf->ipf_p_ftp_debug > 4)
1184 printf("ipf_p_ftp_process: mlen %d\n", mlen);
1185
1186 if ((mlen == 0) && ((tcp->th_flags & TH_OPENING) == TH_OPENING)) {
1187 f->ftps_seq[0] = thseq + 1;
1188 t->ftps_seq[0] = thack;
1189 return 0;
1190 } else if (mlen < 0) {
1191 return 0;
1192 }
1193
1194 aps = nat->nat_aps;
1195
1196 sel = aps->aps_sel[1 - rv];
1197 sel2 = aps->aps_sel[rv];
1198 if (rv == 0) {
1199 seqoff = aps->aps_seqoff[sel];
1200 if (aps->aps_seqmin[sel] > seqoff + thseq)
1201 seqoff = aps->aps_seqoff[!sel];
1202 ackoff = aps->aps_ackoff[sel2];
1203 if (aps->aps_ackmin[sel2] > ackoff + thack)
1204 ackoff = aps->aps_ackoff[!sel2];
1205 } else {
1206 seqoff = aps->aps_ackoff[sel];
1207 if (softf->ipf_p_ftp_debug > 2)
1208 printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq,
1209 aps->aps_ackmin[sel]);
1210 if (aps->aps_ackmin[sel] > seqoff + thseq)
1211 seqoff = aps->aps_ackoff[!sel];
1212
1213 ackoff = aps->aps_seqoff[sel2];
1214 if (softf->ipf_p_ftp_debug > 2)
1215 printf("ackoff %d thack %x seqmin %x\n", ackoff, thack,
1216 aps->aps_seqmin[sel2]);
1217 if (ackoff > 0) {
1218 if (aps->aps_seqmin[sel2] > ackoff + thack)
1219 ackoff = aps->aps_seqoff[!sel2];
1220 } else {
1221 if (aps->aps_seqmin[sel2] > thack)
1222 ackoff = aps->aps_seqoff[!sel2];
1223 }
1224 }
1225 if (softf->ipf_p_ftp_debug > 2) {
1226 printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n",
1227 rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff,
1228 thack, ackoff, mlen, fin->fin_plen, off);
1229 printf("sel %d seqmin %x/%x offset %d/%d\n", sel,
1230 aps->aps_seqmin[sel], aps->aps_seqmin[sel2],
1231 aps->aps_seqoff[sel], aps->aps_seqoff[sel2]);
1232 printf("sel %d ackmin %x/%x offset %d/%d\n", sel2,
1233 aps->aps_ackmin[sel], aps->aps_ackmin[sel2],
1234 aps->aps_ackoff[sel], aps->aps_ackoff[sel2]);
1235 }
1236
1237 /*
1238 * XXX - Ideally, this packet should get dropped because we now know
1239 * that it is out of order (and there is no real danger in doing so
1240 * apart from causing packets to go through here ordered).
1241 */
1242 if (softf->ipf_p_ftp_debug > 2) {
1243 printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
1244 rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff);
1245 }
1246
1247 ok = 0;
1248 if (t->ftps_seq[0] == 0) {
1249 t->ftps_seq[0] = thack;
1250 ok = 1;
1251 } else {
1252 if (ackoff == 0) {
1253 if (t->ftps_seq[0] == thack)
1254 ok = 1;
1255 else if (t->ftps_seq[1] == thack) {
1256 t->ftps_seq[0] = thack;
1257 ok = 1;
1258 }
1259 } else {
1260 if (t->ftps_seq[0] + ackoff == thack)
1261 ok = 1;
1262 else if (t->ftps_seq[0] == thack + ackoff)
1263 ok = 1;
1264 else if (t->ftps_seq[1] + ackoff == thack) {
1265 t->ftps_seq[0] = thack - ackoff;
1266 ok = 1;
1267 } else if (t->ftps_seq[1] == thack + ackoff) {
1268 t->ftps_seq[0] = thack - ackoff;
1269 ok = 1;
1270 }
1271 }
1272 }
1273
1274 if (softf->ipf_p_ftp_debug > 2) {
1275 if (!ok)
1276 printf("%s ok\n", "not");
1277 }
1278
1279 if (!mlen) {
1280 if (t->ftps_seq[0] + ackoff != thack) {
1281 if (softf->ipf_p_ftp_debug > 1) {
1282 printf("%s:seq[0](%x) + (%x) != (%x)\n",
1283 "ipf_p_ftp_process", t->ftps_seq[0],
1284 ackoff, thack);
1285 }
1286 return APR_ERR(1);
1287 }
1288
1289 if (softf->ipf_p_ftp_debug > 2) {
1290 printf("ipf_p_ftp_process:f:seq[0] %x seq[1] %x\n",
1291 f->ftps_seq[0], f->ftps_seq[1]);
1292 }
1293
1294 if (tcp->th_flags & TH_FIN) {
1295 if (thseq == f->ftps_seq[1]) {
1296 f->ftps_seq[0] = f->ftps_seq[1] - seqoff;
1297 f->ftps_seq[1] = thseq + 1 - seqoff;
1298 } else {
1299 if (softf->ipf_p_ftp_debug > 1) {
1300 printf("FIN: thseq %x seqoff %d ftps_seq %x\n",
1301 thseq, seqoff, f->ftps_seq[0]);
1302 }
1303 return APR_ERR(1);
1304 }
1305 }
1306 f->ftps_len = 0;
1307 return 0;
1308 }
1309
1310 ok = 0;
1311 if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) {
1312 ok = 1;
1313 /*
1314 * Retransmitted data packet.
1315 */
1316 } else if ((thseq + mlen == f->ftps_seq[0]) ||
1317 (thseq + mlen == f->ftps_seq[1])) {
1318 ok = 1;
1319 }
1320
1321 if (ok == 0) {
1322 inc = thseq - f->ftps_seq[0];
1323 if (softf->ipf_p_ftp_debug > 1) {
1324 printf("inc %d sel %d rv %d\n", inc, sel, rv);
1325 printf("th_seq %x ftps_seq %x/%x\n",
1326 thseq, f->ftps_seq[0], f->ftps_seq[1]);
1327 printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel],
1328 aps->aps_ackoff[sel]);
1329 printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel],
1330 aps->aps_seqoff[sel]);
1331 }
1332
1333 return APR_ERR(1);
1334 }
1335
1336 inc = 0;
1337 rptr = f->ftps_rptr;
1338 wptr = f->ftps_wptr;
1339 f->ftps_seq[0] = thseq;
1340 f->ftps_seq[1] = f->ftps_seq[0] + mlen;
1341 f->ftps_len = mlen;
1342
1343 while (mlen > 0) {
1344 len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr));
1345 if (len == 0)
1346 break;
1347 COPYDATA(m, off, len, wptr);
1348 mlen -= len;
1349 off += len;
1350 wptr += len;
1351
1352 whilemore:
1353 if (softf->ipf_p_ftp_debug > 3)
1354 printf("%s:len %d/%d off %d wptr %lx junk %d [%*.*s]\n",
1355 "ipf_p_ftp_process",
1356 len, mlen, off, (u_long)wptr, f->ftps_junk,
1357 len, len, rptr);
1358
1359 f->ftps_wptr = wptr;
1360 if (f->ftps_junk != FTPXY_JUNK_OK) {
1361 i = f->ftps_junk;
1362 f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv, rptr,
1363 wptr - rptr);
1364
1365 if (softf->ipf_p_ftp_debug > 5)
1366 printf("%s:junk %d -> %d\n",
1367 "ipf_p_ftp_process", i, f->ftps_junk);
1368
1369 if (f->ftps_junk == FTPXY_JUNK_BAD) {
1370 if (wptr - rptr == sizeof(f->ftps_buf)) {
1371 if (softf->ipf_p_ftp_debug > 4)
1372 printf("%s:full buffer\n",
1373 "ipf_p_ftp_process");
1374 f->ftps_rptr = f->ftps_buf;
1375 f->ftps_wptr = f->ftps_buf;
1376 rptr = f->ftps_rptr;
1377 wptr = f->ftps_wptr;
1378 continue;
1379 }
1380 }
1381 }
1382
1383 while ((f->ftps_junk == FTPXY_JUNK_OK) && (wptr > rptr)) {
1384 len = wptr - rptr;
1385 f->ftps_junk = ipf_p_ftp_valid(softf, ftp, rv,
1386 rptr, len);
1387
1388 if (softf->ipf_p_ftp_debug > 3) {
1389 printf("%s=%d len %d rv %d ptr %lx/%lx ",
1390 "ipf_p_ftp_valid",
1391 f->ftps_junk, len, rv, (u_long)rptr,
1392 (u_long)wptr);
1393 printf("buf [%*.*s]\n", len, len, rptr);
1394 }
1395
1396 if (f->ftps_junk == FTPXY_JUNK_OK) {
1397 f->ftps_cmds++;
1398 f->ftps_rptr = rptr;
1399 if (rv)
1400 inc += ipf_p_ftp_server(softf, fin,
1401 ip, nat,
1402 ftp, len);
1403 else
1404 inc += ipf_p_ftp_client(softf, fin,
1405 ip, nat,
1406 ftp, len);
1407 rptr = f->ftps_rptr;
1408 wptr = f->ftps_wptr;
1409 }
1410 }
1411
1412 /*
1413 * Off to a bad start so lets just forget about using the
1414 * ftp proxy for this connection.
1415 */
1416 if ((f->ftps_cmds == 0) && (f->ftps_junk == FTPXY_JUNK_BAD)) {
1417 /* f->ftps_seq[1] += inc; */
1418
1419 if (softf->ipf_p_ftp_debug > 1)
1420 printf("%s:cmds == 0 junk == 1\n",
1421 "ipf_p_ftp_process");
1422 return APR_ERR(2);
1423 }
1424
1425 retry = 0;
1426 if ((f->ftps_junk != FTPXY_JUNK_OK) && (rptr < wptr)) {
1427 for (s = rptr; s < wptr; s++) {
1428 if ((*s == '\r') && (s + 1 < wptr) &&
1429 (*(s + 1) == '\n')) {
1430 rptr = s + 2;
1431 retry = 1;
1432 if (f->ftps_junk != FTPXY_JUNK_CONT)
1433 f->ftps_junk = FTPXY_JUNK_OK;
1434 break;
1435 }
1436 }
1437 }
1438
1439 if (rptr == wptr) {
1440 rptr = wptr = f->ftps_buf;
1441 } else {
1442 /*
1443 * Compact the buffer back to the start. The junk
1444 * flag should already be set and because we're not
1445 * throwing away any data, it is preserved from its
1446 * current state.
1447 */
1448 if (rptr > f->ftps_buf) {
1449 bcopy(rptr, f->ftps_buf, wptr - rptr);
1450 wptr -= rptr - f->ftps_buf;
1451 rptr = f->ftps_buf;
1452 }
1453 }
1454 f->ftps_rptr = rptr;
1455 f->ftps_wptr = wptr;
1456 if (retry)
1457 goto whilemore;
1458 }
1459
1460 /* f->ftps_seq[1] += inc; */
1461 if (tcp->th_flags & TH_FIN)
1462 f->ftps_seq[1]++;
1463 if (softf->ipf_p_ftp_debug > 3) {
1464 #ifdef __sgi
1465 mlen = fin->fin_plen;
1466 #else
1467 mlen = MSGDSIZE(m);
1468 #endif
1469 mlen -= off;
1470 printf("ftps_seq[1] = %x inc %d len %d\n",
1471 f->ftps_seq[1], inc, mlen);
1472 }
1473
1474 f->ftps_rptr = rptr;
1475 f->ftps_wptr = wptr;
1476 return APR_INC(inc);
1477 }
1478
1479
1480 int
1481 ipf_p_ftp_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
1482 {
1483 ipf_ftp_softc_t *softf = arg;
1484 ftpinfo_t *ftp;
1485 int rev;
1486
1487 ftp = aps->aps_data;
1488 if (ftp == NULL)
1489 return 0;
1490
1491 rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1492 if (ftp->ftp_side[1 - rev].ftps_ifp == NULL)
1493 ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp;
1494
1495 return ipf_p_ftp_process(softf, fin, nat, ftp, rev);
1496 }
1497
1498
1499 int
1500 ipf_p_ftp_in(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
1501 {
1502 ipf_ftp_softc_t *softf = arg;
1503 ftpinfo_t *ftp;
1504 int rev;
1505
1506 ftp = aps->aps_data;
1507 if (ftp == NULL)
1508 return 0;
1509
1510 rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1511 if (ftp->ftp_side[rev].ftps_ifp == NULL)
1512 ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp;
1513
1514 return ipf_p_ftp_process(softf, fin, nat, ftp, 1 - rev);
1515 }
1516
1517
1518 /*
1519 * ipf_p_ftp_atoi - implement a version of atoi which processes numbers in
1520 * pairs separated by commas (which are expected to be in the range 0 - 255),
1521 * returning a 16 bit number combining either side of the , as the MSB and
1522 * LSB.
1523 */
1524 u_short
1525 ipf_p_ftp_atoi(char **ptr)
1526 {
1527 register char *s = *ptr, c;
1528 register u_char i = 0, j = 0;
1529
1530 while (((c = *s++) != '\0') && ISDIGIT(c)) {
1531 i *= 10;
1532 i += c - '0';
1533 }
1534 if (c != ',') {
1535 *ptr = NULL;
1536 return 0;
1537 }
1538 while (((c = *s++) != '\0') && ISDIGIT(c)) {
1539 j *= 10;
1540 j += c - '0';
1541 }
1542 *ptr = s;
1543 i &= 0xff;
1544 j &= 0xff;
1545 return (i << 8) | j;
1546 }
1547
1548
1549 int
1550 ipf_p_ftp_eprt(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
1551 ftpinfo_t *ftp, int dlen)
1552 {
1553 ftpside_t *f;
1554
1555 /*
1556 * Check for client sending out EPRT message.
1557 */
1558 if (dlen < IPF_MINEPRTLEN) {
1559 if (softf->ipf_p_ftp_debug > 1)
1560 printf("ipf_p_ftp_eprt:dlen(%d) < IPF_MINEPRTLEN\n",
1561 dlen);
1562 return 0;
1563 }
1564
1565 /*
1566 * Parse the EPRT command. Format is:
1567 * "EPRT |1|1.2.3.4|2000|" for IPv4 and
1568 * "EPRT |2|ef00::1:2|2000|" for IPv6 (not supported, yet.)
1569 */
1570 f = &ftp->ftp_side[0];
1571 if (f->ftps_rptr[5] == f->ftps_rptr[7]) {
1572 if (f->ftps_rptr[6] == '1')
1573 return ipf_p_ftp_eprt4(softf, fin, ip, nat, ftp, dlen);
1574 }
1575 return 0;
1576 }
1577
1578
1579 int
1580 ipf_p_ftp_eprt4(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
1581 ftpinfo_t *ftp, int dlen)
1582 {
1583 int a1, a2, a3, a4, port, olen, nlen, inc, off;
1584 char newbuf[IPF_FTPBUFSZ];
1585 char *s, c, delim;
1586 u_32_t addr, i;
1587 tcphdr_t *tcp;
1588 ftpside_t *f;
1589 mb_t *m;
1590
1591 m = fin->fin_m;
1592 tcp = (tcphdr_t *)fin->fin_dp;
1593 off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1594 f = &ftp->ftp_side[0];
1595 delim = f->ftps_rptr[5];
1596 s = f->ftps_rptr + 8;
1597
1598 /*
1599 * get the IP address.
1600 */
1601 i = 0;
1602 while (((c = *s++) != '\0') && ISDIGIT(c)) {
1603 i *= 10;
1604 i += c - '0';
1605 }
1606 if (i > 255)
1607 return 0;
1608 if (c != '.')
1609 return 0;
1610 addr = (i << 24);
1611
1612 i = 0;
1613 while (((c = *s++) != '\0') && ISDIGIT(c)) {
1614 i *= 10;
1615 i += c - '0';
1616 }
1617 if (i > 255)
1618 return 0;
1619 if (c != '.')
1620 return 0;
1621 addr |= (addr << 16);
1622
1623 i = 0;
1624 while (((c = *s++) != '\0') && ISDIGIT(c)) {
1625 i *= 10;
1626 i += c - '0';
1627 }
1628 if (i > 255)
1629 return 0;
1630 if (c != '.')
1631 return 0;
1632 addr |= (addr << 8);
1633
1634 i = 0;
1635 while (((c = *s++) != '\0') && ISDIGIT(c)) {
1636 i *= 10;
1637 i += c - '0';
1638 }
1639 if (i > 255)
1640 return 0;
1641 if (c != delim)
1642 return 0;
1643 addr |= addr;
1644
1645 /*
1646 * Get the port number
1647 */
1648 i = 0;
1649 while (((c = *s++) != '\0') && ISDIGIT(c)) {
1650 i *= 10;
1651 i += c - '0';
1652 }
1653 if (i > 65535)
1654 return 0;
1655 if (c != delim)
1656 return 0;
1657 port = i;
1658
1659 /*
1660 * Check for CR-LF at the end of the command string.
1661 */
1662 if ((*s != '\r') || (*(s + 1) != '\n')) {
1663 if (softf->ipf_p_ftp_debug > 1)
1664 printf("ipf_p_ftp_eprt4:missing %s\n", "cr-lf");
1665 return 0;
1666 }
1667
1668 /*
1669 * Calculate new address parts for PORT command
1670 */
1671 if (nat->nat_dir == NAT_INBOUND)
1672 a1 = ntohl(nat->nat_odstaddr);
1673 else
1674 a1 = ntohl(ip->ip_src.s_addr);
1675 a2 = (a1 >> 16) & 0xff;
1676 a3 = (a1 >> 8) & 0xff;
1677 a4 = a1 & 0xff;
1678 a1 >>= 24;
1679 olen = s - f->ftps_rptr;
1680 /* DO NOT change this to snprintf! */
1681 /*
1682 * While we could force the use of | as a delimiter here, it makes
1683 * sense to preserve whatever character is being used by the systems
1684 * involved in the communication.
1685 */
1686 #if defined(SNPRINTF) && defined(_KERNEL)
1687 SNPRINTF(newbuf, sizeof(newbuf), "%s %c1%c%u.%u.%u.%u%c%u%c\r\n",
1688 "EPRT", delim, delim, a1, a2, a3, a4, port, delim);
1689 #else
1690 (void) sprintf(newbuf, "%s %c1%c%u.%u.%u.%u%c%u%c\r\n",
1691 "EPRT", delim, delim, a1, a2, a3, a4, delim, port,
1692 delim);
1693 #endif
1694
1695 nlen = strlen(newbuf);
1696 inc = nlen - olen;
1697 if ((inc + fin->fin_plen) > 65535) {
1698 if (softf->ipf_p_ftp_debug > 0)
1699 printf("ipf_p_ftp_eprt4:inc(%d) + ip->ip_len > 65535\n",
1700 inc);
1701 return 0;
1702 }
1703
1704 off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1705 #if !defined(_KERNEL)
1706 bcopy(newbuf, MTOD(m, char *) + off, nlen);
1707 #else
1708 if (inc < 0)
1709 M_ADJ(m, inc);
1710 #endif
1711 /* the mbuf chain will be extended if necessary by m_copyback() */
1712 COPYBACK(m, off, nlen, newbuf);
1713
1714 if (inc != 0) {
1715 fin->fin_plen += inc;
1716 ip->ip_len = htons(fin->fin_plen);
1717 fin->fin_dlen += inc;
1718 }
1719
1720 f->ftps_cmd = FTPXY_C_EPRT;
1721 return ipf_p_ftp_addport(softf, fin, ip, nat, ftp, dlen, port, inc);
1722 }
1723
1724
1725 int
1726 ipf_p_ftp_epsv(ipf_ftp_softc_t *softf, fr_info_t *fin, ip_t *ip, nat_t *nat,
1727 ftpinfo_t *ftp, int dlen)
1728 {
1729 char newbuf[IPF_FTPBUFSZ];
1730 u_short ap = 0;
1731 ftpside_t *f;
1732 char *s;
1733
1734 if ((softf->ipf_p_ftp_forcepasv != 0) &&
1735 (ftp->ftp_side[0].ftps_cmd != FTPXY_C_EPSV)) {
1736 if (softf->ipf_p_ftp_debug > 0)
1737 printf("ipf_p_ftp_epsv:ftps_cmd(%d) != FTPXY_C_EPSV\n",
1738 ftp->ftp_side[0].ftps_cmd);
1739 return 0;
1740 }
1741 f = &ftp->ftp_side[1];
1742
1743 #define EPSV_REPLEN 33
1744 /*
1745 * Check for EPSV reply message.
1746 */
1747 if (dlen < IPF_MIN229LEN)
1748 return (0);
1749 else if (strncmp(f->ftps_rptr,
1750 "229 Entering Extended Passive Mode", EPSV_REPLEN))
1751 return (0);
1752
1753 /*
1754 * Skip the EPSV command + space
1755 */
1756 s = f->ftps_rptr + 33;
1757 while (*s && !ISDIGIT(*s))
1758 s++;
1759
1760 /*
1761 * As per RFC 2428, there are no addres components in the EPSV
1762 * response. So we'll go straight to getting the port.
1763 */
1764 while (*s && ISDIGIT(*s)) {
1765 ap *= 10;
1766 ap += *s++ - '0';
1767 }
1768
1769 if (!s)
1770 return 0;
1771
1772 if (*s == '|')
1773 s++;
1774 if (*s == ')')
1775 s++;
1776 if (*s == '\n')
1777 s--;
1778 /*
1779 * check for CR-LF at the end.
1780 */
1781 if ((*s == '\r') && (*(s + 1) == '\n')) {
1782 s += 2;
1783 } else
1784 return 0;
1785
1786 #if defined(SNPRINTF) && defined(_KERNEL)
1787 SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n",
1788 "229 Entering Extended Passive Mode", ap);
1789 #else
1790 (void) sprintf(newbuf, "%s (|||%u|)\r\n",
1791 "229 Entering Extended Passive Mode", ap);
1792 #endif
1793
1794 return ipf_p_ftp_pasvreply(softf, fin, ip, nat, ftp, (u_int)ap,
1795 newbuf, s, ip->ip_src.s_addr);
1796 }
1797