1 /*
2 * Copyright (C) 1997-2003 by Darren Reed
3 *
4 * See the IPFILTER.LICENCE file for details on licencing.
5 *
6 * $Id: ip_ftp_pxy.c,v 2.88.2.15 2005/03/19 19:38:10 darrenr Exp $
7 *
8 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
9 * Use is subject to license terms.
10 *
11 * Simple FTP transparent proxy for in-kernel use. For use with the NAT
12 * code.
13 */
14
15 #define IPF_FTP_PROXY
16
17 #define IPF_MINPORTLEN 18
18 #define IPF_MAXPORTLEN 30
19 #define IPF_MIN227LEN 39
20 #define IPF_MAX227LEN 51
21 #define IPF_MIN229LEN 47
22 #define IPF_MAX229LEN 51
23
24 #define FTPXY_GO 0
25 #define FTPXY_INIT 1
26 #define FTPXY_USER_1 2
27 #define FTPXY_USOK_1 3
28 #define FTPXY_PASS_1 4
29 #define FTPXY_PAOK_1 5
30 #define FTPXY_AUTH_1 6
31 #define FTPXY_AUOK_1 7
32 #define FTPXY_ADAT_1 8
33 #define FTPXY_ADOK_1 9
34 #define FTPXY_ACCT_1 10
35 #define FTPXY_ACOK_1 11
36 #define FTPXY_USER_2 12
37 #define FTPXY_USOK_2 13
38 #define FTPXY_PASS_2 14
39 #define FTPXY_PAOK_2 15
40
41 /*
42 * Values for FTP commands. Numerics cover 0-999
43 */
44 #define FTPXY_C_PASV 1000
45
46 typedef struct ifs_ftppxy {
47 frentry_t ftppxyfr;
48 int ftp_proxy_init;
49 int ippr_ftp_pasvonly;
50 int ippr_ftp_insecure;
51 /* Do not require logins before transfers */
52 int ippr_ftp_pasvrdr;
53 int ippr_ftp_forcepasv;
54 /* PASV must be last command prior to 227 */
55 /*
56 * 1 - security
57 * 2 - errors
58 * 3 - error debugging
59 * 4 - parsing errors
60 * 5 - parsing info
61 * 6 - parsing debug
62 */
63 int ippr_ftp_debug;
64 ipftuneable_t ftptune;
65 } ifs_ftppxy_t;
66
67 int ippr_ftp_client __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int,
68 ifs_ftppxy_t *));
69 int ippr_ftp_complete __P((char *, size_t));
70 int ippr_ftp_in __P((fr_info_t *, ap_session_t *, nat_t *, void *));
71 int ippr_ftp_init __P((void **, ipf_stack_t *));
72 void ippr_ftp_fini __P((void **, ipf_stack_t *));
73 int ippr_ftp_new __P((fr_info_t *, ap_session_t *, nat_t *, void *));
74 int ippr_ftp_out __P((fr_info_t *, ap_session_t *, nat_t *, void *));
75 int ippr_ftp_pasv __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int,
76 ifs_ftppxy_t *));
77 int ippr_ftp_epsv __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int,
78 ifs_ftppxy_t *));
79 int ippr_ftp_port __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *, int,
80 ifs_ftppxy_t *));
81 int ippr_ftp_process __P((fr_info_t *, nat_t *, ftpinfo_t *, int,
82 ifs_ftppxy_t *));
83 int ippr_ftp_server __P((fr_info_t *, ip_t *, nat_t *, ftpinfo_t *, int,
84 ifs_ftppxy_t *));
85 int ippr_ftp_valid __P((ftpinfo_t *, int, char *, size_t, ifs_ftppxy_t *));
86 int ippr_ftp_server_valid __P((ftpside_t *, char *, size_t, ifs_ftppxy_t *));
87 int ippr_ftp_client_valid __P((ftpside_t *, char *, size_t, ifs_ftppxy_t *));
88 u_short ippr_ftp_atoi __P((char **));
89 int ippr_ftp_pasvreply __P((fr_info_t *, ip_t *, nat_t *, ftpside_t *,
90 u_int, char *, char *, u_int, ifs_ftppxy_t *));
91
92 /*
93 * Initialize local structures.
94 */
ippr_ftp_init(private,ifs)95 int ippr_ftp_init(private, ifs)
96 void **private;
97 ipf_stack_t *ifs;
98 {
99 ifs_ftppxy_t *ifsftp;
100
101 KMALLOC(ifsftp, ifs_ftppxy_t *);
102 if (ifsftp == NULL)
103 return -1;
104
105 bzero((char *)&ifsftp->ftppxyfr, sizeof(ifsftp->ftppxyfr));
106 ifsftp->ftppxyfr.fr_ref = 1;
107 ifsftp->ftppxyfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
108 MUTEX_INIT(&ifsftp->ftppxyfr.fr_lock, "FTP Proxy Mutex");
109 ifsftp->ftp_proxy_init = 1;
110 ifsftp->ippr_ftp_pasvonly = 0;
111 ifsftp->ippr_ftp_insecure = 0;
112 ifsftp->ippr_ftp_pasvrdr = 0;
113 ifsftp->ippr_ftp_forcepasv = 0;
114 #if defined(_KERNEL)
115 ifsftp->ippr_ftp_debug = 0;
116 #else
117 ifsftp->ippr_ftp_debug = 2;
118 #endif
119 bzero((char *)&ifsftp->ftptune, sizeof(ifsftp->ftptune));
120 ifsftp->ftptune.ipft_pint = (uint_t *)&ifsftp->ippr_ftp_debug;
121 ifsftp->ftptune.ipft_name = "ippr_ftp_debug";
122 ifsftp->ftptune.ipft_max = 10;
123 ifsftp->ftptune.ipft_sz = sizeof(ifsftp->ippr_ftp_debug);
124 ifsftp->ftptune.ipft_next = NULL;
125
126 (void) fr_addipftune(&ifsftp->ftptune, ifs);
127
128 *private = (void *)ifsftp;
129
130 return 0;
131 }
132
133
ippr_ftp_fini(private,ifs)134 void ippr_ftp_fini(private, ifs)
135 void **private;
136 ipf_stack_t *ifs;
137 {
138 ifs_ftppxy_t *ifsftp = *((ifs_ftppxy_t **)private);
139
140 (void) fr_delipftune(&ifsftp->ftptune, ifs);
141
142 if (ifsftp->ftp_proxy_init == 1) {
143 MUTEX_DESTROY(&ifsftp->ftppxyfr.fr_lock);
144 ifsftp->ftp_proxy_init = 0;
145 }
146
147 KFREE(ifsftp);
148 *private = NULL;
149 }
150
151
152 /*ARGSUSED*/
ippr_ftp_new(fin,aps,nat,private)153 int ippr_ftp_new(fin, aps, nat, private)
154 fr_info_t *fin;
155 ap_session_t *aps;
156 nat_t *nat;
157 void *private;
158 {
159 ftpinfo_t *ftp;
160 ftpside_t *f;
161
162 KMALLOC(ftp, ftpinfo_t *);
163 if (ftp == NULL)
164 return -1;
165
166 fin = fin; /* LINT */
167 nat = nat; /* LINT */
168
169 aps->aps_data = ftp;
170 aps->aps_psiz = sizeof(ftpinfo_t);
171
172 bzero((char *)ftp, sizeof(*ftp));
173 f = &ftp->ftp_side[0];
174 f->ftps_rptr = f->ftps_buf;
175 f->ftps_wptr = f->ftps_buf;
176 f = &ftp->ftp_side[1];
177 f->ftps_rptr = f->ftps_buf;
178 f->ftps_wptr = f->ftps_buf;
179 ftp->ftp_passok = FTPXY_INIT;
180 ftp->ftp_incok = 0;
181 return 0;
182 }
183
184
ippr_ftp_port(fin,ip,nat,f,dlen,ifsftp)185 int ippr_ftp_port(fin, ip, nat, f, dlen, ifsftp)
186 fr_info_t *fin;
187 ip_t *ip;
188 nat_t *nat;
189 ftpside_t *f;
190 int dlen;
191 ifs_ftppxy_t *ifsftp;
192 {
193 tcphdr_t *tcp, tcph, *tcp2 = &tcph;
194 char newbuf[IPF_FTPBUFSZ], *s;
195 struct in_addr swip, swip2;
196 u_int a1, a2, a3, a4;
197 int inc, off, flags;
198 u_short a5, a6, sp;
199 size_t nlen, olen;
200 fr_info_t fi;
201 nat_t *nat2;
202 mb_t *m;
203 ipf_stack_t *ifs = fin->fin_ifs;
204
205 m = fin->fin_m;
206 tcp = (tcphdr_t *)fin->fin_dp;
207 off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
208
209 /*
210 * Check for client sending out PORT message.
211 */
212 if (dlen < IPF_MINPORTLEN) {
213 if (ifsftp->ippr_ftp_debug > 1)
214 printf("ippr_ftp_port:dlen(%d) < IPF_MINPORTLEN\n",
215 dlen);
216 return 0;
217 }
218 /*
219 * Skip the PORT command + space
220 */
221 s = f->ftps_rptr + 5;
222 /*
223 * Pick out the address components, two at a time.
224 */
225 a1 = ippr_ftp_atoi(&s);
226 if (s == NULL) {
227 if (ifsftp->ippr_ftp_debug > 1)
228 printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 1);
229 return 0;
230 }
231 a2 = ippr_ftp_atoi(&s);
232 if (s == NULL) {
233 if (ifsftp->ippr_ftp_debug > 1)
234 printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 2);
235 return 0;
236 }
237
238 /*
239 * Check that IP address in the PORT/PASV reply is the same as the
240 * sender of the command - prevents using PORT for port scanning.
241 */
242 a1 <<= 16;
243 a1 |= a2;
244 if (((nat->nat_dir == NAT_OUTBOUND) &&
245 (a1 != ntohl(nat->nat_inip.s_addr))) ||
246 ((nat->nat_dir == NAT_INBOUND) &&
247 (a1 != ntohl(nat->nat_oip.s_addr)))) {
248 if (ifsftp->ippr_ftp_debug > 0)
249 printf("ippr_ftp_port:%s != nat->nat_inip\n", "a1");
250 return APR_ERR(1);
251 }
252
253 a5 = ippr_ftp_atoi(&s);
254 if (s == NULL) {
255 if (ifsftp->ippr_ftp_debug > 1)
256 printf("ippr_ftp_port:ippr_ftp_atoi(%d) failed\n", 3);
257 return 0;
258 }
259 if (*s == ')')
260 s++;
261
262 /*
263 * check for CR-LF at the end.
264 */
265 if (*s == '\n')
266 s--;
267 if ((*s == '\r') && (*(s + 1) == '\n')) {
268 s += 2;
269 a6 = a5 & 0xff;
270 } else {
271 if (ifsftp->ippr_ftp_debug > 1)
272 printf("ippr_ftp_port:missing %s\n", "cr-lf");
273 return 0;
274 }
275
276 a5 >>= 8;
277 a5 &= 0xff;
278 sp = a5 << 8 | a6;
279 /*
280 * Don't allow the PORT command to specify a port < 1024 due to
281 * security crap.
282 */
283 if (sp < 1024) {
284 if (ifsftp->ippr_ftp_debug > 0)
285 printf("ippr_ftp_port:sp(%d) < 1024\n", sp);
286 return 0;
287 }
288 /*
289 * Calculate new address parts for PORT command
290 */
291 if (nat->nat_dir == NAT_INBOUND)
292 a1 = ntohl(nat->nat_oip.s_addr);
293 else
294 a1 = ntohl(ip->ip_src.s_addr);
295 a2 = (a1 >> 16) & 0xff;
296 a3 = (a1 >> 8) & 0xff;
297 a4 = a1 & 0xff;
298 a1 >>= 24;
299 olen = s - f->ftps_rptr;
300 /* DO NOT change this to snprintf! */
301 #if defined(SNPRINTF) && defined(_KERNEL)
302 (void) SNPRINTF(newbuf, sizeof(newbuf), "%s %u,%u,%u,%u,%u,%u\r\n",
303 "PORT", a1, a2, a3, a4, a5, a6);
304 #else
305 (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
306 "PORT", a1, a2, a3, a4, a5, a6);
307 #endif
308
309 nlen = strlen(newbuf);
310 inc = nlen - olen;
311 if ((inc + ip->ip_len) > 65535) {
312 if (ifsftp->ippr_ftp_debug > 0)
313 printf("ippr_ftp_port:inc(%d) + ip->ip_len > 65535\n",
314 inc);
315 return 0;
316 }
317
318 #if !defined(_KERNEL)
319 bcopy(newbuf, MTOD(m, char *) + off, nlen);
320 #else
321 # if defined(MENTAT)
322 if (inc < 0)
323 (void)adjmsg(m, inc);
324 # else /* defined(MENTAT) */
325 /*
326 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
327 * mean remove -len bytes from the end of the packet.
328 * The mbuf chain will be extended if necessary by m_copyback().
329 */
330 if (inc < 0)
331 m_adj(m, inc);
332 # endif /* defined(MENTAT) */
333 #endif /* !defined(_KERNEL) */
334 COPYBACK(m, off, nlen, newbuf);
335
336 if (inc != 0) {
337 ip->ip_len += inc;
338 fin->fin_dlen += inc;
339 fin->fin_plen += inc;
340 }
341
342 /*
343 * The server may not make the connection back from port 20, but
344 * it is the most likely so use it here to check for a conflicting
345 * mapping.
346 */
347 bcopy((char *)fin, (char *)&fi, sizeof(fi));
348 fi.fin_flx |= FI_IGNORE;
349 fi.fin_data[0] = sp;
350 fi.fin_data[1] = fin->fin_data[1] - 1;
351 /*
352 * Add skeleton NAT entry for connection which will come back the
353 * other way.
354 */
355 if (nat->nat_dir == NAT_OUTBOUND)
356 nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p,
357 nat->nat_inip, nat->nat_oip);
358 else
359 nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p,
360 nat->nat_inip, nat->nat_oip);
361 if (nat2 == NULL) {
362 int slen;
363
364 slen = ip->ip_len;
365 ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
366 bzero((char *)tcp2, sizeof(*tcp2));
367 tcp2->th_win = htons(8192);
368 tcp2->th_sport = htons(sp);
369 TCP_OFF_A(tcp2, 5);
370 tcp2->th_flags = TH_SYN;
371 tcp2->th_dport = 0; /* XXX - don't specify remote port */
372 fi.fin_data[1] = 0;
373 fi.fin_dlen = sizeof(*tcp2);
374 fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
375 fi.fin_dp = (char *)tcp2;
376 fi.fin_fr = &ifsftp->ftppxyfr;
377 fi.fin_out = nat->nat_dir;
378 fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
379 swip = ip->ip_src;
380 swip2 = ip->ip_dst;
381 if (nat->nat_dir == NAT_OUTBOUND) {
382 fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
383 ip->ip_src = nat->nat_inip;
384 } else if (nat->nat_dir == NAT_INBOUND) {
385 fi.fin_fi.fi_saddr = nat->nat_oip.s_addr;
386 ip->ip_src = nat->nat_oip;
387 }
388
389 flags = NAT_SLAVE|IPN_TCP|SI_W_DPORT;
390 if (nat->nat_dir == NAT_INBOUND)
391 flags |= NAT_NOTRULEPORT;
392 nat2 = nat_new(&fi, nat->nat_ptr, NULL, flags, nat->nat_dir);
393
394 if (nat2 != NULL) {
395 (void) nat_proto(&fi, nat2, IPN_TCP);
396 nat_update(&fi, nat2, nat->nat_ptr);
397 fi.fin_ifp = NULL;
398 if (nat->nat_dir == NAT_INBOUND) {
399 fi.fin_fi.fi_daddr = nat->nat_inip.s_addr;
400 ip->ip_dst = nat->nat_inip;
401 }
402 (void) fr_addstate(&fi, &nat2->nat_state, SI_W_DPORT);
403 }
404 ip->ip_len = slen;
405 ip->ip_src = swip;
406 ip->ip_dst = swip2;
407 } else {
408 ipstate_t *is;
409
410 nat_update(&fi, nat2, nat->nat_ptr);
411 READ_ENTER(&ifs->ifs_ipf_state);
412 is = nat2->nat_state;
413 if (is != NULL) {
414 MUTEX_ENTER(&is->is_lock);
415 (void)fr_tcp_age(&is->is_sti, &fi, ifs->ifs_ips_tqtqb,
416 is->is_flags);
417 MUTEX_EXIT(&is->is_lock);
418 }
419 RWLOCK_EXIT(&ifs->ifs_ipf_state);
420 }
421 return APR_INC(inc);
422 }
423
424
ippr_ftp_client(fin,ip,nat,ftp,dlen,ifsftp)425 int ippr_ftp_client(fin, ip, nat, ftp, dlen, ifsftp)
426 fr_info_t *fin;
427 nat_t *nat;
428 ftpinfo_t *ftp;
429 ip_t *ip;
430 int dlen;
431 ifs_ftppxy_t *ifsftp;
432 {
433 char *rptr, *wptr, cmd[6], c;
434 ftpside_t *f;
435 int inc, i;
436
437 inc = 0;
438 f = &ftp->ftp_side[0];
439 rptr = f->ftps_rptr;
440 wptr = f->ftps_wptr;
441
442 for (i = 0; (i < 5) && (i < dlen); i++) {
443 c = rptr[i];
444 if (ISALPHA(c)) {
445 cmd[i] = TOUPPER(c);
446 } else {
447 cmd[i] = c;
448 }
449 }
450 cmd[i] = '\0';
451
452 ftp->ftp_incok = 0;
453 if (!strncmp(cmd, "USER ", 5) || !strncmp(cmd, "XAUT ", 5)) {
454 if (ftp->ftp_passok == FTPXY_ADOK_1 ||
455 ftp->ftp_passok == FTPXY_AUOK_1) {
456 ftp->ftp_passok = FTPXY_USER_2;
457 ftp->ftp_incok = 1;
458 } else {
459 ftp->ftp_passok = FTPXY_USER_1;
460 ftp->ftp_incok = 1;
461 }
462 } else if (!strncmp(cmd, "AUTH ", 5)) {
463 ftp->ftp_passok = FTPXY_AUTH_1;
464 ftp->ftp_incok = 1;
465 } else if (!strncmp(cmd, "PASS ", 5)) {
466 if (ftp->ftp_passok == FTPXY_USOK_1) {
467 ftp->ftp_passok = FTPXY_PASS_1;
468 ftp->ftp_incok = 1;
469 } else if (ftp->ftp_passok == FTPXY_USOK_2) {
470 ftp->ftp_passok = FTPXY_PASS_2;
471 ftp->ftp_incok = 1;
472 }
473 } else if ((ftp->ftp_passok == FTPXY_AUOK_1) &&
474 !strncmp(cmd, "ADAT ", 5)) {
475 ftp->ftp_passok = FTPXY_ADAT_1;
476 ftp->ftp_incok = 1;
477 } else if ((ftp->ftp_passok == FTPXY_PAOK_1 ||
478 ftp->ftp_passok == FTPXY_PAOK_2) &&
479 !strncmp(cmd, "ACCT ", 5)) {
480 ftp->ftp_passok = FTPXY_ACCT_1;
481 ftp->ftp_incok = 1;
482 } else if ((ftp->ftp_passok == FTPXY_GO) &&
483 !ifsftp->ippr_ftp_pasvonly &&
484 !strncmp(cmd, "PORT ", 5)) {
485 inc = ippr_ftp_port(fin, ip, nat, f, dlen, ifsftp);
486 } else if (ifsftp->ippr_ftp_insecure && !ifsftp->ippr_ftp_pasvonly &&
487 !strncmp(cmd, "PORT ", 5)) {
488 inc = ippr_ftp_port(fin, ip, nat, f, dlen, ifsftp);
489 }
490
491 while ((*rptr++ != '\n') && (rptr < wptr))
492 ;
493 f->ftps_rptr = rptr;
494 return inc;
495 }
496
497
ippr_ftp_pasv(fin,ip,nat,ftp,dlen,ifsftp)498 int ippr_ftp_pasv(fin, ip, nat, ftp, dlen, ifsftp)
499 fr_info_t *fin;
500 ip_t *ip;
501 nat_t *nat;
502 ftpinfo_t *ftp;
503 int dlen;
504 ifs_ftppxy_t *ifsftp;
505 {
506 u_int a1, a2, a3, a4, data_ip;
507 char newbuf[IPF_FTPBUFSZ];
508 char *s, *brackets[2];
509 u_short a5, a6;
510 ftpside_t *f;
511
512 if (ifsftp->ippr_ftp_forcepasv != 0 &&
513 ftp->ftp_side[0].ftps_cmds != FTPXY_C_PASV) {
514 if (ifsftp->ippr_ftp_debug > 0)
515 printf("ippr_ftp_pasv:ftps_cmds(%d) != FTPXY_C_PASV\n",
516 ftp->ftp_side[0].ftps_cmds);
517 return 0;
518 }
519
520 f = &ftp->ftp_side[1];
521
522 #define PASV_REPLEN 24
523 /*
524 * Check for PASV reply message.
525 */
526 if (dlen < IPF_MIN227LEN) {
527 if (ifsftp->ippr_ftp_debug > 1)
528 printf("ippr_ftp_pasv:dlen(%d) < IPF_MIN227LEN\n",
529 dlen);
530 return 0;
531 } else if (strncmp(f->ftps_rptr,
532 "227 Entering Passive Mod", PASV_REPLEN)) {
533 if (ifsftp->ippr_ftp_debug > 0)
534 printf("ippr_ftp_pasv:%d reply wrong\n", 227);
535 return 0;
536 }
537
538 brackets[0] = "";
539 brackets[1] = "";
540 /*
541 * Skip the PASV reply + space
542 */
543 s = f->ftps_rptr + PASV_REPLEN;
544 while (*s && !ISDIGIT(*s)) {
545 if (*s == '(') {
546 brackets[0] = "(";
547 brackets[1] = ")";
548 }
549 s++;
550 }
551
552 /*
553 * Pick out the address components, two at a time.
554 */
555 a1 = ippr_ftp_atoi(&s);
556 if (s == NULL) {
557 if (ifsftp->ippr_ftp_debug > 1)
558 printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 1);
559 return 0;
560 }
561 a2 = ippr_ftp_atoi(&s);
562 if (s == NULL) {
563 if (ifsftp->ippr_ftp_debug > 1)
564 printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 2);
565 return 0;
566 }
567
568 /*
569 * check that IP address in the PASV reply is the same as the
570 * sender of the command - prevents using PASV for port scanning.
571 */
572 a1 <<= 16;
573 a1 |= a2;
574
575 if (((nat->nat_dir == NAT_INBOUND) &&
576 (a1 != ntohl(nat->nat_inip.s_addr))) ||
577 ((nat->nat_dir == NAT_OUTBOUND) &&
578 (a1 != ntohl(nat->nat_oip.s_addr)))) {
579 if (ifsftp->ippr_ftp_debug > 0)
580 printf("ippr_ftp_pasv:%s != nat->nat_oip\n", "a1");
581 return 0;
582 }
583
584 a5 = ippr_ftp_atoi(&s);
585 if (s == NULL) {
586 if (ifsftp->ippr_ftp_debug > 1)
587 printf("ippr_ftp_pasv:ippr_ftp_atoi(%d) failed\n", 3);
588 return 0;
589 }
590
591 if (*s == ')')
592 s++;
593 if (*s == '.')
594 s++;
595 if (*s == '\n')
596 s--;
597 /*
598 * check for CR-LF at the end.
599 */
600 if ((*s == '\r') && (*(s + 1) == '\n')) {
601 s += 2;
602 } else {
603 if (ifsftp->ippr_ftp_debug > 1)
604 printf("ippr_ftp_pasv:missing %s", "cr-lf\n");
605 return 0;
606 }
607
608 a6 = a5 & 0xff;
609 a5 >>= 8;
610 /*
611 * Calculate new address parts for 227 reply
612 */
613 if (nat->nat_dir == NAT_INBOUND) {
614 data_ip = nat->nat_outip.s_addr;
615 a1 = ntohl(data_ip);
616 } else
617 data_ip = htonl(a1);
618
619 a2 = (a1 >> 16) & 0xff;
620 a3 = (a1 >> 8) & 0xff;
621 a4 = a1 & 0xff;
622 a1 >>= 24;
623
624 #if defined(SNPRINTF) && defined(_KERNEL)
625 (void) SNPRINTF(newbuf, sizeof(newbuf), "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
626 "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
627 a5, a6, brackets[1]);
628 #else
629 (void) sprintf(newbuf, "%s %s%u,%u,%u,%u,%u,%u%s\r\n",
630 "227 Entering Passive Mode", brackets[0], a1, a2, a3, a4,
631 a5, a6, brackets[1]);
632 #endif
633 return ippr_ftp_pasvreply(fin, ip, nat, f, (a5 << 8 | a6),
634 newbuf, s, data_ip, ifsftp);
635 }
636
ippr_ftp_pasvreply(fin,ip,nat,f,port,newmsg,s,data_ip,ifsftp)637 int ippr_ftp_pasvreply(fin, ip, nat, f, port, newmsg, s, data_ip, ifsftp)
638 fr_info_t *fin;
639 ip_t *ip;
640 nat_t *nat;
641 ftpside_t *f;
642 u_int port;
643 char *newmsg;
644 char *s;
645 u_int data_ip;
646 ifs_ftppxy_t *ifsftp;
647 {
648 int inc, off, nflags, sflags;
649 tcphdr_t *tcp, tcph, *tcp2;
650 struct in_addr swip, swip2;
651 struct in_addr data_addr;
652 size_t nlen, olen;
653 fr_info_t fi;
654 nat_t *nat2;
655 mb_t *m;
656 ipf_stack_t *ifs = fin->fin_ifs;
657
658 m = fin->fin_m;
659 tcp = (tcphdr_t *)fin->fin_dp;
660 off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
661
662 data_addr.s_addr = data_ip;
663 tcp2 = &tcph;
664 inc = 0;
665
666
667 olen = s - f->ftps_rptr;
668 nlen = strlen(newmsg);
669 inc = nlen - olen;
670 if ((inc + ip->ip_len) > 65535) {
671 if (ifsftp->ippr_ftp_debug > 0)
672 printf("ippr_ftp_pasv:inc(%d) + ip->ip_len > 65535\n",
673 inc);
674 return 0;
675 }
676
677 #if !defined(_KERNEL)
678 bcopy(newmsg, MTOD(m, char *) + off, nlen);
679 #else
680 # if defined(MENTAT)
681 if (inc < 0)
682 (void)adjmsg(m, inc);
683 # else /* defined(MENTAT) */
684 /*
685 * m_adj takes care of pkthdr.len, if required and treats inc<0 to
686 * mean remove -len bytes from the end of the packet.
687 * The mbuf chain will be extended if necessary by m_copyback().
688 */
689 if (inc < 0)
690 m_adj(m, inc);
691 # endif /* defined(MENTAT) */
692 #endif /* !defined(_KERNEL) */
693 COPYBACK(m, off, nlen, newmsg);
694
695 if (inc != 0) {
696 ip->ip_len += inc;
697 fin->fin_dlen += inc;
698 fin->fin_plen += inc;
699 }
700
701 /*
702 * Add skeleton NAT entry for connection which will come back the
703 * other way.
704 */
705 bcopy((char *)fin, (char *)&fi, sizeof(fi));
706 fi.fin_flx |= FI_IGNORE;
707 fi.fin_data[0] = 0;
708 fi.fin_data[1] = port;
709 nflags = IPN_TCP|SI_W_SPORT;
710 if (ifsftp->ippr_ftp_pasvrdr && f->ftps_ifp)
711 nflags |= SI_W_DPORT;
712 if (nat->nat_dir == NAT_OUTBOUND)
713 nat2 = nat_outlookup(&fi, nflags|NAT_SEARCH,
714 nat->nat_p, nat->nat_inip, nat->nat_oip);
715 else
716 nat2 = nat_inlookup(&fi, nflags|NAT_SEARCH,
717 nat->nat_p, nat->nat_inip, nat->nat_oip);
718 if (nat2 == NULL) {
719 int slen;
720
721 slen = ip->ip_len;
722 ip->ip_len = fin->fin_hlen + sizeof(*tcp2);
723 bzero((char *)tcp2, sizeof(*tcp2));
724 tcp2->th_win = htons(8192);
725 tcp2->th_sport = 0; /* XXX - fake it for nat_new */
726 TCP_OFF_A(tcp2, 5);
727 tcp2->th_flags = TH_SYN;
728 fi.fin_data[1] = port;
729 fi.fin_dlen = sizeof(*tcp2);
730 tcp2->th_dport = htons(port);
731 fi.fin_data[0] = 0;
732 fi.fin_dp = (char *)tcp2;
733 fi.fin_plen = fi.fin_hlen + sizeof(*tcp);
734 fi.fin_fr = &ifsftp->ftppxyfr;
735 fi.fin_out = nat->nat_dir;
736 fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
737 swip = ip->ip_src;
738 swip2 = ip->ip_dst;
739 if (nat->nat_dir == NAT_OUTBOUND) {
740 fi.fin_fi.fi_daddr = data_addr.s_addr;
741 fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
742 ip->ip_dst = data_addr;
743 ip->ip_src = nat->nat_inip;
744 } else if (nat->nat_dir == NAT_INBOUND) {
745 fi.fin_fi.fi_saddr = nat->nat_oip.s_addr;
746 fi.fin_fi.fi_daddr = nat->nat_outip.s_addr;
747 ip->ip_src = nat->nat_oip;
748 ip->ip_dst = nat->nat_outip;
749 }
750
751 sflags = nflags;
752 nflags |= NAT_SLAVE;
753 if (nat->nat_dir == NAT_INBOUND)
754 nflags |= NAT_NOTRULEPORT;
755 nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir);
756 if (nat2 != NULL) {
757 (void) nat_proto(&fi, nat2, IPN_TCP);
758 nat_update(&fi, nat2, nat->nat_ptr);
759 fi.fin_ifp = NULL;
760 if (nat->nat_dir == NAT_INBOUND) {
761 fi.fin_fi.fi_daddr = nat->nat_inip.s_addr;
762 ip->ip_dst = nat->nat_inip;
763 }
764 (void) fr_addstate(&fi, &nat2->nat_state, sflags);
765 }
766
767 ip->ip_len = slen;
768 ip->ip_src = swip;
769 ip->ip_dst = swip2;
770 } else {
771 ipstate_t *is;
772
773 nat_update(&fi, nat2, nat->nat_ptr);
774 READ_ENTER(&ifs->ifs_ipf_state);
775 is = nat2->nat_state;
776 if (is != NULL) {
777 MUTEX_ENTER(&is->is_lock);
778 (void) fr_tcp_age(&is->is_sti, &fi, ifs->ifs_ips_tqtqb,
779 is->is_flags);
780 MUTEX_EXIT(&is->is_lock);
781 }
782 RWLOCK_EXIT(&ifs->ifs_ipf_state);
783 }
784 return inc;
785 }
786
787
ippr_ftp_server(fin,ip,nat,ftp,dlen,ifsftp)788 int ippr_ftp_server(fin, ip, nat, ftp, dlen, ifsftp)
789 fr_info_t *fin;
790 ip_t *ip;
791 nat_t *nat;
792 ftpinfo_t *ftp;
793 int dlen;
794 ifs_ftppxy_t *ifsftp;
795 {
796 char *rptr, *wptr;
797 ftpside_t *f;
798 int inc;
799
800 inc = 0;
801 f = &ftp->ftp_side[1];
802 rptr = f->ftps_rptr;
803 wptr = f->ftps_wptr;
804
805 if (*rptr == ' ')
806 goto server_cmd_ok;
807 if (!ISDIGIT(*rptr) || !ISDIGIT(*(rptr + 1)) || !ISDIGIT(*(rptr + 2)))
808 return 0;
809 if (ftp->ftp_passok == FTPXY_GO) {
810 if (!strncmp(rptr, "227 ", 4))
811 inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen, ifsftp);
812 else if (!strncmp(rptr, "229 ", 4))
813 inc = ippr_ftp_epsv(fin, ip, nat, f, dlen, ifsftp);
814 } else if (ifsftp->ippr_ftp_insecure && !strncmp(rptr, "227 ", 4)) {
815 inc = ippr_ftp_pasv(fin, ip, nat, ftp, dlen, ifsftp);
816 } else if (ifsftp->ippr_ftp_insecure && !strncmp(rptr, "229 ", 4)) {
817 inc = ippr_ftp_epsv(fin, ip, nat, f, dlen, ifsftp);
818 } else if (*rptr == '5' || *rptr == '4')
819 ftp->ftp_passok = FTPXY_INIT;
820 else if (ftp->ftp_incok) {
821 if (*rptr == '3') {
822 if (ftp->ftp_passok == FTPXY_ACCT_1)
823 ftp->ftp_passok = FTPXY_GO;
824 else
825 ftp->ftp_passok++;
826 } else if (*rptr == '2') {
827 switch (ftp->ftp_passok)
828 {
829 case FTPXY_USER_1 :
830 case FTPXY_USER_2 :
831 case FTPXY_PASS_1 :
832 case FTPXY_PASS_2 :
833 case FTPXY_ACCT_1 :
834 ftp->ftp_passok = FTPXY_GO;
835 break;
836 default :
837 ftp->ftp_passok += 3;
838 break;
839 }
840 }
841 }
842 server_cmd_ok:
843 ftp->ftp_incok = 0;
844
845 while ((*rptr++ != '\n') && (rptr < wptr))
846 ;
847 f->ftps_rptr = rptr;
848 return inc;
849 }
850
851
852 /*
853 * Look to see if the buffer starts with something which we recognise as
854 * being the correct syntax for the FTP protocol.
855 */
ippr_ftp_client_valid(ftps,buf,len,ifsftp)856 int ippr_ftp_client_valid(ftps, buf, len, ifsftp)
857 ftpside_t *ftps;
858 char *buf;
859 size_t len;
860 ifs_ftppxy_t *ifsftp;
861 {
862 register char *s, c, pc;
863 register size_t i = len;
864 char cmd[5];
865
866 s = buf;
867
868 if (ftps->ftps_junk == 1)
869 return 1;
870
871 if (i < 5) {
872 if (ifsftp->ippr_ftp_debug > 3)
873 printf("ippr_ftp_client_valid:i(%d) < 5\n", (int)i);
874 return 2;
875 }
876
877 i--;
878 c = *s++;
879
880 if (ISALPHA(c)) {
881 cmd[0] = TOUPPER(c);
882 c = *s++;
883 i--;
884 if (ISALPHA(c)) {
885 cmd[1] = TOUPPER(c);
886 c = *s++;
887 i--;
888 if (ISALPHA(c)) {
889 cmd[2] = TOUPPER(c);
890 c = *s++;
891 i--;
892 if (ISALPHA(c)) {
893 cmd[3] = TOUPPER(c);
894 c = *s++;
895 i--;
896 if ((c != ' ') && (c != '\r'))
897 goto bad_client_command;
898 } else if ((c != ' ') && (c != '\r'))
899 goto bad_client_command;
900 } else
901 goto bad_client_command;
902 } else
903 goto bad_client_command;
904 } else {
905 bad_client_command:
906 if (ifsftp->ippr_ftp_debug > 3)
907 printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*s]\n",
908 "ippr_ftp_client_valid",
909 ftps->ftps_junk, (int)len, (int)i, c,
910 (int)len, buf);
911 return 1;
912 }
913
914 for (; i; i--) {
915 pc = c;
916 c = *s++;
917 if ((pc == '\r') && (c == '\n')) {
918 cmd[4] = '\0';
919 if (!strcmp(cmd, "PASV"))
920 ftps->ftps_cmds = FTPXY_C_PASV;
921 else
922 ftps->ftps_cmds = 0;
923 return 0;
924 }
925 }
926 #if !defined(_KERNEL)
927 printf("ippr_ftp_client_valid:junk after cmd[%*.*s]\n",
928 (int)len, (int)len, buf);
929 #endif
930 return 2;
931 }
932
933
ippr_ftp_server_valid(ftps,buf,len,ifsftp)934 int ippr_ftp_server_valid(ftps, buf, len, ifsftp)
935 ftpside_t *ftps;
936 char *buf;
937 size_t len;
938 ifs_ftppxy_t *ifsftp;
939 {
940 register char *s, c, pc;
941 register size_t i = len;
942 int cmd;
943
944 s = buf;
945 cmd = 0;
946
947 if (ftps->ftps_junk == 1)
948 return 1;
949
950 if (i < 5) {
951 if (ifsftp->ippr_ftp_debug > 3)
952 printf("ippr_ftp_servert_valid:i(%d) < 5\n", (int)i);
953 return 2;
954 }
955
956 c = *s++;
957 i--;
958 if (c == ' ')
959 goto search_eol;
960
961 if (ISDIGIT(c)) {
962 cmd = (c - '0') * 100;
963 c = *s++;
964 i--;
965 if (ISDIGIT(c)) {
966 cmd += (c - '0') * 10;
967 c = *s++;
968 i--;
969 if (ISDIGIT(c)) {
970 cmd += (c - '0');
971 c = *s++;
972 i--;
973 if ((c != '-') && (c != ' '))
974 goto bad_server_command;
975 } else
976 goto bad_server_command;
977 } else
978 goto bad_server_command;
979 } else {
980 bad_server_command:
981 if (ifsftp->ippr_ftp_debug > 3)
982 printf("%s:bad:junk %d len %d/%d c 0x%x buf [%*s]\n",
983 "ippr_ftp_server_valid",
984 ftps->ftps_junk, (int)len, (int)i,
985 c, (int)len, buf);
986 return 1;
987 }
988 search_eol:
989 for (; i; i--) {
990 pc = c;
991 c = *s++;
992 if ((pc == '\r') && (c == '\n')) {
993 ftps->ftps_cmds = cmd;
994 return 0;
995 }
996 }
997 if (ifsftp->ippr_ftp_debug > 3)
998 printf("ippr_ftp_server_valid:junk after cmd[%*s]\n",
999 (int)len, buf);
1000 return 2;
1001 }
1002
1003
ippr_ftp_valid(ftp,side,buf,len,ifsftp)1004 int ippr_ftp_valid(ftp, side, buf, len, ifsftp)
1005 ftpinfo_t *ftp;
1006 int side;
1007 char *buf;
1008 size_t len;
1009 ifs_ftppxy_t *ifsftp;
1010 {
1011 ftpside_t *ftps;
1012 int ret;
1013
1014 ftps = &ftp->ftp_side[side];
1015
1016 if (side == 0)
1017 ret = ippr_ftp_client_valid(ftps, buf, len, ifsftp);
1018 else
1019 ret = ippr_ftp_server_valid(ftps, buf, len, ifsftp);
1020 return ret;
1021 }
1022
1023
1024 /*
1025 * For map rules, the following applies:
1026 * rv == 0 for outbound processing,
1027 * rv == 1 for inbound processing.
1028 * For rdr rules, the following applies:
1029 * rv == 0 for inbound processing,
1030 * rv == 1 for outbound processing.
1031 */
ippr_ftp_process(fin,nat,ftp,rv,ifsftp)1032 int ippr_ftp_process(fin, nat, ftp, rv, ifsftp)
1033 fr_info_t *fin;
1034 nat_t *nat;
1035 ftpinfo_t *ftp;
1036 int rv;
1037 ifs_ftppxy_t *ifsftp;
1038 {
1039 int mlen, len, off, inc, i, sel, sel2, ok, ackoff, seqoff;
1040 char *rptr, *wptr, *s;
1041 u_32_t thseq, thack;
1042 ap_session_t *aps;
1043 ftpside_t *f, *t;
1044 tcphdr_t *tcp;
1045 ip_t *ip;
1046 mb_t *m;
1047
1048 m = fin->fin_m;
1049 ip = fin->fin_ip;
1050 tcp = (tcphdr_t *)fin->fin_dp;
1051 off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
1052
1053 f = &ftp->ftp_side[rv];
1054 t = &ftp->ftp_side[1 - rv];
1055 thseq = ntohl(tcp->th_seq);
1056 thack = ntohl(tcp->th_ack);
1057
1058 #ifdef __sgi
1059 mlen = fin->fin_plen - off;
1060 #else
1061 mlen = MSGDSIZE(m) - off;
1062 #endif
1063 if (ifsftp->ippr_ftp_debug > 4)
1064 printf("ippr_ftp_process: mlen %d\n", mlen);
1065
1066 if (mlen <= 0) {
1067 if ((tcp->th_flags & TH_OPENING) == TH_OPENING) {
1068 f->ftps_seq[0] = thseq + 1;
1069 t->ftps_seq[0] = thack;
1070 }
1071 return 0;
1072 }
1073 aps = nat->nat_aps;
1074
1075 sel = aps->aps_sel[1 - rv];
1076 sel2 = aps->aps_sel[rv];
1077 if (rv == 0) {
1078 seqoff = aps->aps_seqoff[sel];
1079 if (aps->aps_seqmin[sel] > seqoff + thseq)
1080 seqoff = aps->aps_seqoff[!sel];
1081 ackoff = aps->aps_ackoff[sel2];
1082 if (aps->aps_ackmin[sel2] > ackoff + thack)
1083 ackoff = aps->aps_ackoff[!sel2];
1084 } else {
1085 seqoff = aps->aps_ackoff[sel];
1086 if (ifsftp->ippr_ftp_debug > 2)
1087 printf("seqoff %d thseq %x ackmin %x\n", seqoff, thseq,
1088 aps->aps_ackmin[sel]);
1089 if (aps->aps_ackmin[sel] > seqoff + thseq)
1090 seqoff = aps->aps_ackoff[!sel];
1091
1092 ackoff = aps->aps_seqoff[sel2];
1093 if (ifsftp->ippr_ftp_debug > 2)
1094 printf("ackoff %d thack %x seqmin %x\n", ackoff, thack,
1095 aps->aps_seqmin[sel2]);
1096 if (ackoff > 0) {
1097 if (aps->aps_seqmin[sel2] > ackoff + thack)
1098 ackoff = aps->aps_seqoff[!sel2];
1099 } else {
1100 if (aps->aps_seqmin[sel2] > thack)
1101 ackoff = aps->aps_seqoff[!sel2];
1102 }
1103 }
1104 if (ifsftp->ippr_ftp_debug > 2) {
1105 printf("%s: %x seq %x/%d ack %x/%d len %d/%d off %d\n",
1106 rv ? "IN" : "OUT", tcp->th_flags, thseq, seqoff,
1107 thack, ackoff, mlen, fin->fin_plen, off);
1108 printf("sel %d seqmin %x/%x offset %d/%d\n", sel,
1109 aps->aps_seqmin[sel], aps->aps_seqmin[sel2],
1110 aps->aps_seqoff[sel], aps->aps_seqoff[sel2]);
1111 printf("sel %d ackmin %x/%x offset %d/%d\n", sel2,
1112 aps->aps_ackmin[sel], aps->aps_ackmin[sel2],
1113 aps->aps_ackoff[sel], aps->aps_ackoff[sel2]);
1114 }
1115
1116 /*
1117 * XXX - Ideally, this packet should get dropped because we now know
1118 * that it is out of order (and there is no real danger in doing so
1119 * apart from causing packets to go through here ordered).
1120 */
1121 if (ifsftp->ippr_ftp_debug > 2) {
1122 printf("rv %d t:seq[0] %x seq[1] %x %d/%d\n",
1123 rv, t->ftps_seq[0], t->ftps_seq[1], seqoff, ackoff);
1124 }
1125
1126 ok = 0;
1127 if (t->ftps_seq[0] == 0) {
1128 t->ftps_seq[0] = thack;
1129 ok = 1;
1130 } else {
1131 if (ackoff == 0) {
1132 if (t->ftps_seq[0] == thack)
1133 ok = 1;
1134 else if (t->ftps_seq[1] == thack) {
1135 t->ftps_seq[0] = thack;
1136 ok = 1;
1137 }
1138 } else {
1139 if (t->ftps_seq[0] + ackoff == thack)
1140 ok = 1;
1141 else if (t->ftps_seq[0] == thack + ackoff)
1142 ok = 1;
1143 else if (t->ftps_seq[1] + ackoff == thack) {
1144 t->ftps_seq[0] = thack - ackoff;
1145 ok = 1;
1146 } else if (t->ftps_seq[1] == thack + ackoff) {
1147 t->ftps_seq[0] = thack - ackoff;
1148 ok = 1;
1149 }
1150 }
1151 }
1152
1153 if (ifsftp->ippr_ftp_debug > 2) {
1154 if (!ok)
1155 printf("%s ok\n", "not");
1156 }
1157
1158 if (!mlen) {
1159 if (t->ftps_seq[0] + ackoff != thack) {
1160 if (ifsftp->ippr_ftp_debug > 1) {
1161 printf("%s:seq[0](%x) + (%x) != (%x)\n",
1162 "ippr_ftp_process", t->ftps_seq[0],
1163 ackoff, thack);
1164 }
1165 return APR_ERR(1);
1166 }
1167
1168 if (ifsftp->ippr_ftp_debug > 2) {
1169 printf("ippr_ftp_process:f:seq[0] %x seq[1] %x\n",
1170 f->ftps_seq[0], f->ftps_seq[1]);
1171 }
1172
1173 if (tcp->th_flags & TH_FIN) {
1174 if (thseq == f->ftps_seq[1]) {
1175 f->ftps_seq[0] = f->ftps_seq[1] - seqoff;
1176 f->ftps_seq[1] = thseq + 1 - seqoff;
1177 } else {
1178 if (ifsftp->ippr_ftp_debug > 1) {
1179 printf("FIN: thseq %x seqoff %d ftps_seq %x\n",
1180 thseq, seqoff, f->ftps_seq[0]);
1181 }
1182 return APR_ERR(1);
1183 }
1184 }
1185 f->ftps_len = 0;
1186 return 0;
1187 }
1188
1189 ok = 0;
1190 if ((thseq == f->ftps_seq[0]) || (thseq == f->ftps_seq[1])) {
1191 ok = 1;
1192 /*
1193 * Retransmitted data packet.
1194 */
1195 } else if ((thseq + mlen == f->ftps_seq[0]) ||
1196 (thseq + mlen == f->ftps_seq[1])) {
1197 ok = 1;
1198 }
1199
1200 if (ok == 0) {
1201 inc = thseq - f->ftps_seq[0];
1202 if (ifsftp->ippr_ftp_debug > 1) {
1203 printf("inc %d sel %d rv %d\n", inc, sel, rv);
1204 printf("th_seq %x ftps_seq %x/%x\n",
1205 thseq, f->ftps_seq[0], f->ftps_seq[1]);
1206 printf("ackmin %x ackoff %d\n", aps->aps_ackmin[sel],
1207 aps->aps_ackoff[sel]);
1208 printf("seqmin %x seqoff %d\n", aps->aps_seqmin[sel],
1209 aps->aps_seqoff[sel]);
1210 }
1211
1212 return APR_ERR(1);
1213 }
1214
1215 inc = 0;
1216 rptr = f->ftps_rptr;
1217 wptr = f->ftps_wptr;
1218 f->ftps_seq[0] = thseq;
1219 f->ftps_seq[1] = f->ftps_seq[0] + mlen;
1220 f->ftps_len = mlen;
1221
1222 while (mlen > 0) {
1223 len = MIN(mlen, sizeof(f->ftps_buf) - (wptr - rptr));
1224 COPYDATA(m, off, len, wptr);
1225 mlen -= len;
1226 off += len;
1227 wptr += len;
1228
1229 if (ifsftp->ippr_ftp_debug > 3)
1230 printf("%s:len %d/%d off %d wptr %lx junk %d [%*s]\n",
1231 "ippr_ftp_process",
1232 len, mlen, off, (u_long)wptr, f->ftps_junk,
1233 len, rptr);
1234
1235 f->ftps_wptr = wptr;
1236 if (f->ftps_junk != 0) {
1237 i = f->ftps_junk;
1238 f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr,
1239 wptr - rptr, ifsftp);
1240
1241 if (ifsftp->ippr_ftp_debug > 5)
1242 printf("%s:junk %d -> %d\n",
1243 "ippr_ftp_process", i, f->ftps_junk);
1244
1245 if (f->ftps_junk != 0) {
1246 if (wptr - rptr == sizeof(f->ftps_buf)) {
1247 if (ifsftp->ippr_ftp_debug > 4)
1248 printf("%s:full buffer\n",
1249 "ippr_ftp_process");
1250 f->ftps_rptr = f->ftps_buf;
1251 f->ftps_wptr = f->ftps_buf;
1252 rptr = f->ftps_rptr;
1253 wptr = f->ftps_wptr;
1254 /*
1255 * Because we throw away data here that
1256 * we would otherwise parse, set the
1257 * junk flag to indicate just ignore
1258 * any data upto the next CRLF.
1259 */
1260 f->ftps_junk = 1;
1261 continue;
1262 }
1263 }
1264 }
1265
1266 while ((f->ftps_junk == 0) && (wptr > rptr)) {
1267 len = wptr - rptr;
1268 f->ftps_junk = ippr_ftp_valid(ftp, rv, rptr, len, ifsftp);
1269
1270 if (ifsftp->ippr_ftp_debug > 3) {
1271 printf("%s=%d len %d rv %d ptr %lx/%lx ",
1272 "ippr_ftp_valid",
1273 f->ftps_junk, len, rv, (u_long)rptr,
1274 (u_long)wptr);
1275 printf("buf [%*s]\n", len, rptr);
1276 }
1277
1278 if (f->ftps_junk == 0) {
1279 f->ftps_rptr = rptr;
1280 if (rv)
1281 inc += ippr_ftp_server(fin, ip, nat,
1282 ftp, len, ifsftp);
1283 else
1284 inc += ippr_ftp_client(fin, ip, nat,
1285 ftp, len, ifsftp);
1286 rptr = f->ftps_rptr;
1287 wptr = f->ftps_wptr;
1288 }
1289 }
1290
1291 /*
1292 * Off to a bad start so lets just forget about using the
1293 * ftp proxy for this connection.
1294 */
1295 if ((f->ftps_cmds == 0) && (f->ftps_junk == 1)) {
1296 /* f->ftps_seq[1] += inc; */
1297
1298 if (ifsftp->ippr_ftp_debug > 1)
1299 printf("%s:cmds == 0 junk == 1\n",
1300 "ippr_ftp_process");
1301 return APR_ERR(2);
1302 }
1303
1304 if ((f->ftps_junk != 0) && (rptr < wptr)) {
1305 for (s = rptr; s < wptr; s++) {
1306 if ((*s == '\r') && (s + 1 < wptr) &&
1307 (*(s + 1) == '\n')) {
1308 rptr = s + 2;
1309 f->ftps_junk = 0;
1310 break;
1311 }
1312 }
1313 }
1314
1315 if (rptr == wptr) {
1316 rptr = wptr = f->ftps_buf;
1317 } else {
1318 /*
1319 * Compact the buffer back to the start. The junk
1320 * flag should already be set and because we're not
1321 * throwing away any data, it is preserved from its
1322 * current state.
1323 */
1324 if (rptr > f->ftps_buf) {
1325 bcopy(rptr, f->ftps_buf, len);
1326 wptr -= rptr - f->ftps_buf;
1327 rptr = f->ftps_buf;
1328 }
1329 }
1330 f->ftps_rptr = rptr;
1331 f->ftps_wptr = wptr;
1332 }
1333
1334 /* f->ftps_seq[1] += inc; */
1335 if (tcp->th_flags & TH_FIN)
1336 f->ftps_seq[1]++;
1337 if (ifsftp->ippr_ftp_debug > 3) {
1338 #ifdef __sgi
1339 mlen = fin->fin_plen;
1340 #else
1341 mlen = MSGDSIZE(m);
1342 #endif
1343 mlen -= off;
1344 printf("ftps_seq[1] = %x inc %d len %d\n",
1345 f->ftps_seq[1], inc, mlen);
1346 }
1347
1348 f->ftps_rptr = rptr;
1349 f->ftps_wptr = wptr;
1350 return APR_INC(inc);
1351 }
1352
1353
ippr_ftp_out(fin,aps,nat,private)1354 int ippr_ftp_out(fin, aps, nat, private)
1355 fr_info_t *fin;
1356 ap_session_t *aps;
1357 nat_t *nat;
1358 void *private;
1359 {
1360 ftpinfo_t *ftp;
1361 int rev;
1362
1363 ftp = aps->aps_data;
1364 if (ftp == NULL)
1365 return 0;
1366
1367 rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1368 if (ftp->ftp_side[1 - rev].ftps_ifp == NULL)
1369 ftp->ftp_side[1 - rev].ftps_ifp = fin->fin_ifp;
1370
1371 return ippr_ftp_process(fin, nat, ftp, rev, (ifs_ftppxy_t *)private);
1372 }
1373
1374
ippr_ftp_in(fin,aps,nat,private)1375 int ippr_ftp_in(fin, aps, nat, private)
1376 fr_info_t *fin;
1377 ap_session_t *aps;
1378 nat_t *nat;
1379 void *private;
1380 {
1381 ftpinfo_t *ftp;
1382 int rev;
1383
1384 ftp = aps->aps_data;
1385 if (ftp == NULL)
1386 return 0;
1387
1388 rev = (nat->nat_dir == NAT_OUTBOUND) ? 0 : 1;
1389 if (ftp->ftp_side[rev].ftps_ifp == NULL)
1390 ftp->ftp_side[rev].ftps_ifp = fin->fin_ifp;
1391
1392 return ippr_ftp_process(fin, nat, ftp, 1 - rev, (ifs_ftppxy_t *)private);
1393 }
1394
1395
1396 /*
1397 * ippr_ftp_atoi - implement a version of atoi which processes numbers in
1398 * pairs separated by commas (which are expected to be in the range 0 - 255),
1399 * returning a 16 bit number combining either side of the , as the MSB and
1400 * LSB.
1401 */
ippr_ftp_atoi(ptr)1402 u_short ippr_ftp_atoi(ptr)
1403 char **ptr;
1404 {
1405 register char *s = *ptr, c;
1406 register u_char i = 0, j = 0;
1407
1408 while (((c = *s++) != '\0') && ISDIGIT(c)) {
1409 i *= 10;
1410 i += c - '0';
1411 }
1412 if (c != ',') {
1413 *ptr = NULL;
1414 return 0;
1415 }
1416 while (((c = *s++) != '\0') && ISDIGIT(c)) {
1417 j *= 10;
1418 j += c - '0';
1419 }
1420 *ptr = s;
1421 i &= 0xff;
1422 j &= 0xff;
1423 return (i << 8) | j;
1424 }
1425
1426
ippr_ftp_epsv(fin,ip,nat,f,dlen,ifsftp)1427 int ippr_ftp_epsv(fin, ip, nat, f, dlen, ifsftp)
1428 fr_info_t *fin;
1429 ip_t *ip;
1430 nat_t *nat;
1431 ftpside_t *f;
1432 int dlen;
1433 ifs_ftppxy_t *ifsftp;
1434 {
1435 char newbuf[IPF_FTPBUFSZ];
1436 char *s;
1437 u_short ap = 0;
1438
1439 #define EPSV_REPLEN 33
1440 /*
1441 * Check for EPSV reply message.
1442 */
1443 if (dlen < IPF_MIN229LEN)
1444 return (0);
1445 else if (strncmp(f->ftps_rptr,
1446 "229 Entering Extended Passive Mode", EPSV_REPLEN))
1447 return (0);
1448
1449 /*
1450 * Skip the EPSV command + space
1451 */
1452 s = f->ftps_rptr + 33;
1453 while (*s && !ISDIGIT(*s))
1454 s++;
1455
1456 /*
1457 * As per RFC 2428, there are no addres components in the EPSV
1458 * response. So we'll go straight to getting the port.
1459 */
1460 while (*s && ISDIGIT(*s)) {
1461 ap *= 10;
1462 ap += *s++ - '0';
1463 }
1464
1465 if (!s)
1466 return 0;
1467
1468 if (*s == '|')
1469 s++;
1470 if (*s == ')')
1471 s++;
1472 if (*s == '\n')
1473 s--;
1474 /*
1475 * check for CR-LF at the end.
1476 */
1477 if ((*s == '\r') && (*(s + 1) == '\n')) {
1478 s += 2;
1479 } else
1480 return 0;
1481
1482 #if defined(SNPRINTF) && defined(_KERNEL)
1483 (void) SNPRINTF(newbuf, sizeof(newbuf), "%s (|||%u|)\r\n",
1484 "229 Entering Extended Passive Mode", ap);
1485 #else
1486 (void) sprintf(newbuf, "%s (|||%u|)\r\n",
1487 "229 Entering Extended Passive Mode", ap);
1488 #endif
1489
1490 return ippr_ftp_pasvreply(fin, ip, nat, f, (u_int)ap, newbuf, s,
1491 ip->ip_src.s_addr, ifsftp);
1492 }
1493