]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ipfilter/ip_ftp_pxy.c
This commit was generated by cvs2svn to compensate for changes in r53568,
[FreeBSD/FreeBSD.git] / contrib / ipfilter / ip_ftp_pxy.c
1 /*
2  * Simple FTP transparent proxy for in-kernel use.  For use with the NAT
3  * code.
4  */
5 #if SOLARIS && defined(_KERNEL)
6 extern  kmutex_t        ipf_rw;
7 #endif
8
9 #define isdigit(x)      ((x) >= '0' && (x) <= '9')
10
11 #define IPF_FTP_PROXY
12
13 #define IPF_MINPORTLEN  18
14 #define IPF_MAXPORTLEN  30
15 #define IPF_MIN227LEN   39
16 #define IPF_MAX227LEN   51
17
18
19 int ippr_ftp_init __P((void));
20 int ippr_ftp_out __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
21 int ippr_ftp_in __P((fr_info_t *, ip_t *, ap_session_t *, nat_t *));
22 int ippr_ftp_portmsg __P((fr_info_t *, ip_t *, nat_t *));
23 int ippr_ftp_pasvmsg __P((fr_info_t *, ip_t *, nat_t *));
24
25 u_short ipf_ftp_atoi __P((char **));
26
27 static  frentry_t       natfr;
28
29
30 /*
31  * Initialize local structures.
32  */
33 int ippr_ftp_init()
34 {
35         bzero((char *)&natfr, sizeof(natfr));
36         natfr.fr_ref = 1;
37         natfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
38         return 0;
39 }
40
41
42 /*
43  * ipf_ftp_atoi - implement a version of atoi which processes numbers in
44  * pairs separated by commas (which are expected to be in the range 0 - 255),
45  * returning a 16 bit number combining either side of the , as the MSB and
46  * LSB.
47  */
48 u_short ipf_ftp_atoi(ptr)
49 char **ptr;
50 {
51         register char *s = *ptr, c;
52         register u_char i = 0, j = 0;
53
54         while ((c = *s++) && isdigit(c)) {
55                 i *= 10;
56                 i += c - '0';
57         }
58         if (c != ',') {
59                 *ptr = NULL;
60                 return 0;
61         }
62         while ((c = *s++) && isdigit(c)) {
63                 j *= 10;
64                 j += c - '0';
65         }
66         *ptr = s;
67         return (i << 8) | j;
68 }
69
70
71 int ippr_ftp_portmsg(fin, ip, nat)
72 fr_info_t *fin;
73 ip_t *ip;
74 nat_t *nat;
75 {
76         char portbuf[IPF_MAXPORTLEN + 1], newbuf[IPF_MAXPORTLEN + 1], *s;
77         tcphdr_t *tcp, tcph, *tcp2 = &tcph;
78         size_t nlen = 0, dlen, olen;
79         u_short a5, a6, sp, dp;
80         u_int a1, a2, a3, a4;
81         struct in_addr swip;
82         int off, inc = 0;
83         fr_info_t fi;
84         nat_t *ipn;
85         mb_t *m;
86 #if     SOLARIS
87         mb_t *m1;
88 #endif
89
90         tcp = (tcphdr_t *)fin->fin_dp;
91         bzero(portbuf, sizeof(portbuf));
92         off = (ip->ip_hl << 2) + (tcp->th_off << 2);
93
94 #if     SOLARIS
95         m = fin->fin_qfm;
96
97         dlen = msgdsize(m) - off;
98         if (dlen > 0)
99                 copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf);
100 #else
101         m = *(mb_t **)fin->fin_mp;
102
103         dlen = mbufchainlen(m) - off;
104         if (dlen > 0)
105                 m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf);
106 #endif
107         if (dlen == 0)
108                 return 0;
109         portbuf[sizeof(portbuf) - 1] = '\0';
110         *newbuf = '\0';
111         if (!strncmp(portbuf, "PORT ", 5)) { 
112                 if (dlen < IPF_MINPORTLEN)
113                         return 0;
114         } else
115                 return 0;
116
117         /*
118          * Skip the PORT command + space
119          */
120         s = portbuf + 5;
121         /*
122          * Pick out the address components, two at a time.
123          */
124         a1 = ipf_ftp_atoi(&s);
125         if (!s)
126                 return 0;
127         a2 = ipf_ftp_atoi(&s);
128         if (!s)
129                 return 0;
130
131         /*
132          * check that IP address in the PORT/PASV reply is the same as the
133          * sender of the command - prevents using PORT for port scanning.
134          */
135         a1 <<= 16;
136         a1 |= a2;
137         if (a1 != ntohl(nat->nat_inip.s_addr))
138                 return 0;
139
140         a5 = ipf_ftp_atoi(&s);
141         if (!s)
142                 return 0;
143         if (*s == ')')
144                 s++;
145
146         /*
147          * check for CR-LF at the end.
148          */
149         if (*s == '\n')
150                 s--;
151         if ((*s == '\r') && (*(s + 1) == '\n')) {
152                 s += 2;
153                 a6 = a5 & 0xff;
154         } else
155                 return 0;
156         a5 >>= 8;
157         /*
158          * Calculate new address parts for PORT command
159          */
160         a1 = ntohl(ip->ip_src.s_addr);
161         a2 = (a1 >> 16) & 0xff;
162         a3 = (a1 >> 8) & 0xff;
163         a4 = a1 & 0xff;
164         a1 >>= 24;
165         olen = s - portbuf;
166         (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
167                        "PORT", a1, a2, a3, a4, a5, a6);
168
169         nlen = strlen(newbuf);
170         inc = nlen - olen;
171 #if SOLARIS
172         for (m1 = m; m1->b_cont; m1 = m1->b_cont)
173                 ;
174         if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
175                 mblk_t *nm;
176
177                 /* alloc enough to keep same trailer space for lower driver */
178                 nm = allocb(nlen, BPRI_MED);
179                 PANIC((!nm),("ippr_ftp_out: allocb failed"));
180
181                 nm->b_band = m1->b_band;
182                 nm->b_wptr += nlen;
183
184                 m1->b_wptr -= olen;
185                 PANIC((m1->b_wptr < m1->b_rptr),
186                       ("ippr_ftp_out: cannot handle fragmented data block"));
187
188                 linkb(m1, nm);
189         } else {
190                 if (m1->b_datap->db_struiolim == m1->b_wptr)
191                         m1->b_datap->db_struiolim += inc;
192                 m1->b_datap->db_struioflag &= ~STRUIO_IP;
193                 m1->b_wptr += inc;
194         }
195         copyin_mblk(m, off, nlen, newbuf);
196 #else
197         if (inc < 0)
198                 m_adj(m, inc);
199         /* the mbuf chain will be extended if necessary by m_copyback() */
200         m_copyback(m, off, nlen, newbuf);
201 #endif
202         if (inc != 0) {
203 #if SOLARIS || defined(__sgi)
204                 register u_32_t sum1, sum2;
205
206                 sum1 = ip->ip_len;
207                 sum2 = ip->ip_len + inc;
208
209                 /* Because ~1 == -2, We really need ~1 == -1 */
210                 if (sum1 > sum2)
211                         sum2--;
212                 sum2 -= sum1;
213                 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
214
215                 fix_outcksum(&ip->ip_sum, sum2);
216 #endif
217                 ip->ip_len += inc;
218         }
219
220         /*
221          * Add skeleton NAT entry for connection which will come back the
222          * other way.
223          */
224         sp = htons(a5 << 8 | a6);
225         /*
226          * The server may not make the connection back from port 20, but
227          * it is the most likely so use it here to check for a conflicting
228          * mapping.
229          */
230         dp = htons(fin->fin_data[1] - 1);
231         ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip,
232                             ip->ip_dst, (dp << 16) | sp);
233         if (ipn == NULL) {
234                 bcopy((char *)fin, (char *)&fi, sizeof(fi));
235                 bzero((char *)tcp2, sizeof(*tcp2));
236                 tcp2->th_win = htons(8192);
237                 tcp2->th_sport = sp;
238                 tcp2->th_dport = 0; /* XXX - don't specify remote port */
239                 fi.fin_data[0] = ntohs(sp);
240                 fi.fin_data[1] = 0;
241                 fi.fin_dp = (char *)tcp2;
242                 swip = ip->ip_src;
243                 ip->ip_src = nat->nat_inip;
244                 ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_DPORT,
245                               NAT_OUTBOUND);
246                 if (ipn != NULL) {
247                         ipn->nat_age = fr_defnatage;
248                         (void) fr_addstate(ip, &fi, FI_W_DPORT);
249                 }
250                 ip->ip_src = swip;
251         }
252         return inc;
253 }
254
255
256 int ippr_ftp_out(fin, ip, aps, nat)
257 fr_info_t *fin;
258 ip_t *ip;
259 ap_session_t *aps;
260 nat_t *nat;
261 {
262         return ippr_ftp_portmsg(fin, ip, nat);
263 }
264
265
266 int ippr_ftp_pasvmsg(fin, ip, nat)
267 fr_info_t *fin;
268 ip_t *ip;
269 nat_t *nat;
270 {
271         char portbuf[IPF_MAX227LEN + 1], newbuf[IPF_MAX227LEN + 1], *s;
272         int off, olen, dlen, nlen = 0, inc = 0;
273         tcphdr_t tcph, *tcp2 = &tcph;
274         struct in_addr swip, swip2;
275         u_short a5, a6, dp, sp;
276         u_int a1, a2, a3, a4;
277         tcphdr_t *tcp;
278         fr_info_t fi;
279         nat_t *ipn;
280         mb_t *m;
281 #if     SOLARIS
282         mb_t *m1;
283 #endif
284
285         tcp = (tcphdr_t *)fin->fin_dp;
286         off = (ip->ip_hl << 2) + (tcp->th_off << 2);
287         m = *(mb_t **)fin->fin_mp;
288         bzero(portbuf, sizeof(portbuf));
289
290 #if     SOLARIS
291         m = fin->fin_qfm;
292
293         dlen = msgdsize(m) - off;
294         if (dlen > 0)
295                 copyout_mblk(m, off, MIN(sizeof(portbuf), dlen), portbuf);
296 #else
297         dlen = mbufchainlen(m) - off;
298         if (dlen > 0)
299                 m_copydata(m, off, MIN(sizeof(portbuf), dlen), portbuf);
300 #endif
301         if (dlen == 0)
302                 return 0;
303         portbuf[sizeof(portbuf) - 1] = '\0';
304         *newbuf = '\0';
305
306         if (!strncmp(portbuf, "227 ", 4)) {
307                 if (dlen < IPF_MIN227LEN)
308                         return 0;
309                 else if (strncmp(portbuf, "227 Entering Passive Mode", 25))
310                         return 0;
311         } else
312                 return 0;
313         /*
314          * Skip the PORT command + space
315          */
316         s = portbuf + 25;
317         while (*s && !isdigit(*s))
318                 s++;
319         /*
320          * Pick out the address components, two at a time.
321          */
322         a1 = ipf_ftp_atoi(&s);
323         if (!s)
324                 return 0;
325         a2 = ipf_ftp_atoi(&s);
326         if (!s)
327                 return 0;
328
329         /*
330          * check that IP address in the PORT/PASV reply is the same as the
331          * sender of the command - prevents using PORT for port scanning.
332          */
333         a1 <<= 16;
334         a1 |= a2;
335         if (a1 != ntohl(nat->nat_oip.s_addr))
336                 return 0;
337
338         a5 = ipf_ftp_atoi(&s);
339         if (!s)
340                 return 0;
341
342         if (*s == ')')
343                 s++;
344         if (*s == '\n')
345                 s--;
346         /*
347          * check for CR-LF at the end.
348          */
349         if ((*s == '\r') && (*(s + 1) == '\n')) {
350                 s += 2;
351                 a6 = a5 & 0xff;
352         } else
353                 return 0;
354         a5 >>= 8;
355         /*
356          * Calculate new address parts for 227 reply
357          */
358         a1 = ntohl(ip->ip_src.s_addr);
359         a2 = (a1 >> 16) & 0xff;
360         a3 = (a1 >> 8) & 0xff;
361         a4 = a1 & 0xff;
362         a1 >>= 24;
363         olen = s - portbuf;
364         (void) sprintf(newbuf, "%s %u,%u,%u,%u,%u,%u\r\n",
365                        "227 Entering Passive Mode", a1, a2, a3, a4, a5, a6);
366
367         nlen = strlen(newbuf);
368         inc = nlen - olen;
369 #if SOLARIS
370         for (m1 = m; m1->b_cont; m1 = m1->b_cont)
371                 ;
372         if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
373                 mblk_t *nm;
374
375                 /* alloc enough to keep same trailer space for lower driver */
376                 nm = allocb(nlen, BPRI_MED);
377                 PANIC((!nm),("ippr_ftp_out: allocb failed"));
378
379                 nm->b_band = m1->b_band;
380                 nm->b_wptr += nlen;
381
382                 m1->b_wptr -= olen;
383                 PANIC((m1->b_wptr < m1->b_rptr),
384                       ("ippr_ftp_out: cannot handle fragmented data block"));
385
386                 linkb(m1, nm);
387         } else {
388                 m1->b_wptr += inc;
389         }
390         copyin_mblk(m, off, nlen, newbuf);
391 #else
392         if (inc < 0)
393                 m_adj(m, inc);
394         /* the mbuf chain will be extended if necessary by m_copyback() */
395         m_copyback(m, off, nlen, newbuf);
396 #endif
397         if (inc != 0) {
398 #if SOLARIS || defined(__sgi)
399                 register u_32_t sum1, sum2;
400
401                 sum1 = ip->ip_len;
402                 sum2 = ip->ip_len + inc;
403
404                 /* Because ~1 == -2, We really need ~1 == -1 */
405                 if (sum1 > sum2)
406                         sum2--;
407                 sum2 -= sum1;
408                 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
409
410                 fix_outcksum(&ip->ip_sum, sum2);
411 #endif
412                 ip->ip_len += inc;
413         }
414
415         /*
416          * Add skeleton NAT entry for connection which will come back the
417          * other way.
418          */
419         sp = 0;
420         dp = htons(fin->fin_data[1] - 1);
421         ipn = nat_outlookup(fin->fin_ifp, IPN_TCP, nat->nat_p, nat->nat_inip,
422                             ip->ip_dst, (dp << 16) | sp);
423         if (ipn == NULL) {
424                 bcopy((char *)fin, (char *)&fi, sizeof(fi));
425                 bzero((char *)tcp2, sizeof(*tcp2));
426                 tcp2->th_win = htons(8192);
427                 tcp2->th_sport = 0;             /* XXX - fake it for nat_new */
428                 fi.fin_data[0] = a5 << 8 | a6;
429                 tcp2->th_dport = htons(fi.fin_data[0]);
430                 fi.fin_data[1] = 0;
431                 fi.fin_dp = (char *)tcp2;
432                 swip = ip->ip_src;
433                 swip2 = ip->ip_dst;
434                 ip->ip_dst = ip->ip_src;
435                 ip->ip_src = nat->nat_inip;
436                 ipn = nat_new(nat->nat_ptr, ip, &fi, IPN_TCP|FI_W_SPORT,
437                               NAT_OUTBOUND);
438                 if (ipn != NULL) {
439                         ipn->nat_age = fr_defnatage;
440                         (void) fr_addstate(ip, &fi, FI_W_SPORT);
441                 }
442                 ip->ip_src = swip;
443                 ip->ip_dst = swip2;
444         }
445         return inc;
446 }
447
448
449 int ippr_ftp_in(fin, ip, aps, nat)
450 fr_info_t *fin;
451 ip_t *ip;
452 ap_session_t *aps;
453 nat_t *nat;
454 {
455
456         return ippr_ftp_pasvmsg(fin, ip, nat);
457 }