FreeBSD/Linux Kernel Cross Reference
sys/netinet/ip_scan.c
1 /* $NetBSD: ip_scan.c,v 1.1.1.1 2004/03/28 08:56:49 martti Exp $ */
2
3 /*
4 * Copyright (C) 1995-2001 by Darren Reed.
5 *
6 * See the IPFILTER.LICENCE file for details on licencing.
7 */
8 #if defined(KERNEL) || defined(_KERNEL)
9 # undef KERNEL
10 # undef _KERNEL
11 # define KERNEL 1
12 # define _KERNEL 1
13 #endif
14 #include <sys/param.h>
15 #if defined(__hpux) && (HPUXREV >= 1111) && !defined(_KERNEL)
16 # include <sys/kern_svcs.h>
17 #endif
18 #include <sys/types.h>
19 #include <sys/time.h>
20 #include <sys/errno.h>
21 #if !defined(_KERNEL)
22 # include <stdlib.h>
23 # include <string.h>
24 # define _KERNEL
25 # ifdef __OpenBSD__
26 struct file;
27 # endif
28 # include <sys/uio.h>
29 # undef _KERNEL
30 #else
31 # include <sys/systm.h>
32 # if !defined(__svr4__) && !defined(__SVR4)
33 # include <sys/mbuf.h>
34 # endif
35 #endif
36 #include <sys/socket.h>
37 #if !defined(__hpux) && !defined(__osf__) && !defined(linux)
38 # include <sys/ioccom.h>
39 #endif
40 #ifdef __FreeBSD__
41 # include <sys/filio.h>
42 # include <sys/malloc.h>
43 #else
44 # include <sys/ioctl.h>
45 #endif
46
47 #include <netinet/in.h>
48 #include <netinet/in_systm.h>
49 #include <netinet/ip.h>
50 #include <netinet/tcp.h>
51
52 #include <net/if.h>
53
54
55 #include "netinet/ip_compat.h"
56 #include "netinet/ip_fil.h"
57 #include "netinet/ip_state.h"
58 #include "netinet/ip_scan.h"
59 /* END OF INCLUDES */
60
61 #if !defined(lint)
62 static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed";
63 static const char rcsid[] = "@(#)Id: ip_scan.c,v 2.40 2004/01/27 00:24:56 darrenr Exp";
64 #endif
65
66 #ifdef IPFILTER_SCAN /* endif at bottom of file */
67
68
69 ipscan_t *ipsc_list = NULL,
70 *ipsc_tail = NULL;
71 ipscanstat_t ipsc_stat;
72 # ifdef USE_MUTEXES
73 ipfrwlock_t ipsc_rwlock;
74 # endif
75
76 # ifndef isalpha
77 # define isalpha(x) (((x) >= 'A' && 'Z' >= (x)) || \
78 ((x) >= 'a' && 'z' >= (x)))
79 # endif
80
81
82 int ipsc_add __P((caddr_t));
83 int ipsc_delete __P((caddr_t));
84 struct ipscan *ipsc_lookup __P((char *));
85 int ipsc_matchstr __P((sinfo_t *, char *, int));
86 int ipsc_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *));
87 int ipsc_match __P((ipstate_t *));
88
89
90
91 int ipsc_init()
92 {
93 RWLOCK_INIT(&ipsc_rwlock, "ip scan rwlock");
94 return 0;
95 }
96
97
98 void fr_scanunload()
99 {
100 RW_DESTROY(&ipsc_rwlock);
101 }
102
103
104 int ipsc_add(data)
105 caddr_t data;
106 {
107 ipscan_t *i, *isc;
108 int err;
109
110 KMALLOC(isc, ipscan_t *);
111 if (!isc)
112 return ENOMEM;
113
114 err = copyinptr(data, isc, sizeof(*isc));
115 if (err)
116 return err;
117
118 WRITE_ENTER(&ipsc_rwlock);
119
120 i = ipsc_lookup(isc->ipsc_tag);
121 if (i) {
122 RWLOCK_EXIT(&ipsc_rwlock);
123 KFREE(isc);
124 return EEXIST;
125 }
126
127 if (ipsc_tail) {
128 ipsc_tail->ipsc_next = isc;
129 isc->ipsc_pnext = &ipsc_tail->ipsc_next;
130 ipsc_tail = isc;
131 } else {
132 ipsc_list = isc;
133 ipsc_tail = isc;
134 isc->ipsc_pnext = &ipsc_list;
135 }
136 isc->ipsc_next = NULL;
137
138 isc->ipsc_hits = 0;
139 isc->ipsc_fref = 0;
140 isc->ipsc_sref = 0;
141 isc->ipsc_active = 0;
142
143 ipsc_stat.iscs_entries++;
144 RWLOCK_EXIT(&ipsc_rwlock);
145 return 0;
146 }
147
148
149 int ipsc_delete(data)
150 caddr_t data;
151 {
152 ipscan_t isc, *i;
153 int err;
154
155 err = copyinptr(data, &isc, sizeof(isc));
156 if (err)
157 return err;
158
159 WRITE_ENTER(&ipsc_rwlock);
160
161 i = ipsc_lookup(isc.ipsc_tag);
162 if (i == NULL)
163 err = ENOENT;
164 else {
165 if (i->ipsc_fref) {
166 RWLOCK_EXIT(&ipsc_rwlock);
167 return EBUSY;
168 }
169
170 *i->ipsc_pnext = i->ipsc_next;
171 if (i->ipsc_next)
172 i->ipsc_next->ipsc_pnext = i->ipsc_pnext;
173 else {
174 if (i->ipsc_pnext == &ipsc_list)
175 ipsc_tail = NULL;
176 else
177 ipsc_tail = *(*i->ipsc_pnext)->ipsc_pnext;
178 }
179
180 ipsc_stat.iscs_entries--;
181 KFREE(i);
182 }
183 RWLOCK_EXIT(&ipsc_rwlock);
184 return err;
185 }
186
187
188 struct ipscan *ipsc_lookup(tag)
189 char *tag;
190 {
191 ipscan_t *i;
192
193 for (i = ipsc_list; i; i = i->ipsc_next)
194 if (!strcmp(i->ipsc_tag, tag))
195 return i;
196 return NULL;
197 }
198
199
200 int ipsc_attachfr(fr)
201 struct frentry *fr;
202 {
203 ipscan_t *i;
204
205 if (fr->fr_isctag[0]) {
206 READ_ENTER(&ipsc_rwlock);
207 i = ipsc_lookup(fr->fr_isctag);
208 if (i != NULL) {
209 ATOMIC_INC32(i->ipsc_fref);
210 }
211 RWLOCK_EXIT(&ipsc_rwlock);
212 if (i == NULL)
213 return ENOENT;
214 fr->fr_isc = i;
215 }
216 return 0;
217 }
218
219
220 int ipsc_attachis(is)
221 struct ipstate *is;
222 {
223 frentry_t *fr;
224 ipscan_t *i;
225
226 READ_ENTER(&ipsc_rwlock);
227 fr = is->is_rule;
228 if (fr) {
229 i = fr->fr_isc;
230 if (!i || (i != (ipscan_t *)-1)) {
231 is->is_isc = i;
232 if (i) {
233 ATOMIC_INC32(i->ipsc_sref);
234 if (i->ipsc_clen)
235 is->is_flags |= IS_SC_CLIENT;
236 else
237 is->is_flags |= IS_SC_MATCHC;
238 if (i->ipsc_slen)
239 is->is_flags |= IS_SC_SERVER;
240 else
241 is->is_flags |= IS_SC_MATCHS;
242 } else
243 is->is_flags |= (IS_SC_CLIENT|IS_SC_SERVER);
244 }
245 }
246 RWLOCK_EXIT(&ipsc_rwlock);
247 return 0;
248 }
249
250
251 int ipsc_detachfr(fr)
252 struct frentry *fr;
253 {
254 ipscan_t *i;
255
256 i = fr->fr_isc;
257 if (i != NULL) {
258 ATOMIC_DEC32(i->ipsc_fref);
259 }
260 return 0;
261 }
262
263
264 int ipsc_detachis(is)
265 struct ipstate *is;
266 {
267 ipscan_t *i;
268
269 READ_ENTER(&ipsc_rwlock);
270 if ((i = is->is_isc) && (i != (ipscan_t *)-1)) {
271 ATOMIC_DEC32(i->ipsc_sref);
272 is->is_isc = NULL;
273 is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER);
274 }
275 RWLOCK_EXIT(&ipsc_rwlock);
276 return 0;
277 }
278
279
280 /*
281 * 'string' compare for scanning
282 */
283 int ipsc_matchstr(sp, str, n)
284 sinfo_t *sp;
285 char *str;
286 int n;
287 {
288 char *s, *t, *up;
289 int i = n;
290
291 if (i > sp->s_len)
292 i = sp->s_len;
293 up = str;
294
295 for (s = sp->s_txt, t = sp->s_msk; i; i--, s++, t++, up++)
296 switch ((int)*t)
297 {
298 case '.' :
299 if (*s != *up)
300 return 1;
301 break;
302 case '?' :
303 if (!isalpha(*up) || ((*s & 0x5f) != (*up & 0x5f)))
304 return 1;
305 break;
306 case '*' :
307 break;
308 }
309 return 0;
310 }
311
312
313 /*
314 * Returns 3 if both server and client match, 2 if just server,
315 * 1 if just client
316 */
317 int ipsc_matchisc(isc, is, cl, sl, maxm)
318 ipscan_t *isc;
319 ipstate_t *is;
320 int cl, sl, maxm[2];
321 {
322 int i, j, k, n, ret = 0, flags;
323
324 flags = is->is_flags;
325
326 /*
327 * If we've already matched more than what is on offer, then
328 * assume we have a better match already and forget this one.
329 */
330 if (maxm != NULL) {
331 if (isc->ipsc_clen < maxm[0])
332 return 0;
333 if (isc->ipsc_slen < maxm[1])
334 return 0;
335 j = maxm[0];
336 k = maxm[1];
337 } else {
338 j = 0;
339 k = 0;
340 }
341
342 if (!isc->ipsc_clen)
343 ret = 1;
344 else if (((flags & (IS_SC_MATCHC|IS_SC_CLIENT)) == IS_SC_CLIENT) &&
345 cl && isc->ipsc_clen) {
346 i = 0;
347 n = MIN(cl, isc->ipsc_clen);
348 if ((n > 0) && (!maxm || (n >= maxm[1]))) {
349 if (!ipsc_matchstr(&isc->ipsc_cl, is->is_sbuf[0], n)) {
350 i++;
351 ret |= 1;
352 if (n > j)
353 j = n;
354 }
355 }
356 }
357
358 if (!isc->ipsc_slen)
359 ret |= 2;
360 else if (((flags & (IS_SC_MATCHS|IS_SC_SERVER)) == IS_SC_SERVER) &&
361 sl && isc->ipsc_slen) {
362 i = 0;
363 n = MIN(cl, isc->ipsc_slen);
364 if ((n > 0) && (!maxm || (n >= maxm[1]))) {
365 if (!ipsc_matchstr(&isc->ipsc_sl, is->is_sbuf[1], n)) {
366 i++;
367 ret |= 2;
368 if (n > k)
369 k = n;
370 }
371 }
372 }
373
374 if (maxm && (ret == 3)) {
375 maxm[0] = j;
376 maxm[1] = k;
377 }
378 return ret;
379 }
380
381
382 int ipsc_match(is)
383 ipstate_t *is;
384 {
385 int i, j, k, n, cl, sl, maxm[2];
386 ipscan_t *isc, *lm;
387 tcpdata_t *t;
388
389 for (cl = 0, n = is->is_smsk[0]; n & 1; n >>= 1)
390 cl++;
391 for (sl = 0, n = is->is_smsk[1]; n & 1; n >>= 1)
392 sl++;
393
394 j = 0;
395 isc = is->is_isc;
396 if (isc != NULL) {
397 /*
398 * Known object to scan for.
399 */
400 i = ipsc_matchisc(isc, is, cl, sl, NULL);
401 if (i & 1) {
402 is->is_flags |= IS_SC_MATCHC;
403 is->is_flags &= ~IS_SC_CLIENT;
404 } else if (cl >= isc->ipsc_clen)
405 is->is_flags &= ~IS_SC_CLIENT;
406 if (i & 2) {
407 is->is_flags |= IS_SC_MATCHS;
408 is->is_flags &= ~IS_SC_SERVER;
409 } else if (sl >= isc->ipsc_slen)
410 is->is_flags &= ~IS_SC_SERVER;
411 } else {
412 i = 0;
413 lm = NULL;
414 maxm[0] = 0;
415 maxm[1] = 0;
416 for (k = 0, isc = ipsc_list; isc; isc = isc->ipsc_next) {
417 i = ipsc_matchisc(isc, is, cl, sl, maxm);
418 if (i) {
419 /*
420 * We only want to remember the best match
421 * and the number of times we get a best
422 * match.
423 */
424 if ((j == 3) && (i < 3))
425 continue;
426 if ((i == 3) && (j != 3))
427 k = 1;
428 else
429 k++;
430 j = i;
431 lm = isc;
432 }
433 }
434 if (k == 1)
435 isc = lm;
436
437 /*
438 * No matches or partial matches, so reset the respective
439 * search flag.
440 */
441 if (!(j & 1))
442 is->is_flags &= ~IS_SC_CLIENT;
443
444 if (!(j & 2))
445 is->is_flags &= ~IS_SC_SERVER;
446
447 /*
448 * If we found the best match, then set flags appropriately.
449 */
450 if ((j == 3) && (k == 1)) {
451 is->is_flags &= ~(IS_SC_SERVER|IS_SC_CLIENT);
452 is->is_flags |= (IS_SC_MATCHS|IS_SC_MATCHC);
453 }
454 }
455
456 /*
457 * If the acknowledged side of a connection has moved past the data in
458 * which we are interested, then reset respective flag.
459 */
460 t = &is->is_tcp.ts_data[0];
461 if (t->td_end > is->is_s0[0] + 15)
462 is->is_flags &= ~IS_SC_CLIENT;
463
464 t = &is->is_tcp.ts_data[1];
465 if (t->td_end > is->is_s0[1] + 15)
466 is->is_flags &= ~IS_SC_SERVER;
467
468 /*
469 * Matching complete ?
470 */
471 j = ISC_A_NONE;
472 if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) {
473 j = isc->ipsc_action;
474 ipsc_stat.iscs_acted++;
475 } else if ((is->is_isc != NULL) &&
476 ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) &&
477 !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) {
478 /*
479 * Matching failed...
480 */
481 j = isc->ipsc_else;
482 ipsc_stat.iscs_else++;
483 }
484
485 switch (j)
486 {
487 case ISC_A_CLOSE :
488 /*
489 * If as a result of a successful match we are to
490 * close a connection, change the "keep state" info.
491 * to block packets and generate TCP RST's.
492 */
493 is->is_pass &= ~FR_RETICMP;
494 is->is_pass |= FR_RETRST;
495 break;
496 default :
497 break;
498 }
499
500 return i;
501 }
502
503
504 /*
505 * check if a packet matches what we're scanning for
506 */
507 int ipsc_packet(fin, is)
508 fr_info_t *fin;
509 ipstate_t *is;
510 {
511 int i, j, rv, dlen, off, thoff;
512 u_32_t seq, s0;
513 tcphdr_t *tcp;
514
515 rv = !IP6_EQ(&fin->fin_fi.fi_src, &is->is_src);
516 tcp = fin->fin_dp;
517 seq = ntohl(tcp->th_seq);
518
519 if (!is->is_s0[rv])
520 return 1;
521
522 /*
523 * check if this packet has more data that falls within the first
524 * 16 bytes sent in either direction.
525 */
526 s0 = is->is_s0[rv];
527 off = seq - s0;
528 if ((seq > s0 + 15) || (off < 0))
529 return 1;
530 thoff = TCP_OFF(tcp) << 2;
531 dlen = fin->fin_dlen - thoff;
532 if (dlen <= 0)
533 return 1;
534 seq += dlen;
535 if (seq > s0 + 15)
536 dlen -= (seq - (s0 + 15));
537
538 j = 0xffff >> (16 - dlen);
539 i = (0xffff & j) << off;
540 #ifdef _KERNEL
541 COPYDATA(*(mb_t **)fin->fin_mp, fin->fin_hlen + thoff, dlen,
542 (caddr_t)is->is_sbuf[rv] + off);
543 #endif
544 is->is_smsk[rv] |= i;
545 for (j = 0, i = is->is_smsk[rv]; i & 1; i >>= 1)
546 j++;
547 if (j == 0)
548 return 1;
549
550 (void) ipsc_match(is);
551 #if 0
552 /*
553 * There is the potential here for plain text passwords to get
554 * buffered and stored for some time...
555 */
556 if (!(is->is_flags & IS_SC_CLIENT))
557 bzero(is->is_sbuf[0], sizeof(is->is_sbuf[0]));
558 if (!(is->is_flags & IS_SC_SERVER))
559 bzero(is->is_sbuf[1], sizeof(is->is_sbuf[1]));
560 #endif
561 return 0;
562 }
563
564
565 int fr_scan_ioctl(data, cmd, mode)
566 caddr_t data;
567 ioctlcmd_t cmd;
568 int mode;
569 {
570 ipscanstat_t ipscs;
571 int err = 0;
572
573 switch (cmd)
574 {
575 case SIOCADSCA :
576 err = ipsc_add(data);
577 break;
578 case SIOCRMSCA :
579 err = ipsc_delete(data);
580 break;
581 case SIOCGSCST :
582 bcopy((char *)&ipsc_stat, (char *)&ipscs, sizeof(ipscs));
583 ipscs.iscs_list = ipsc_list;
584 BCOPYOUT(&ipscs, data, sizeof(ipscs));
585 break;
586 default :
587 err = EINVAL;
588 break;
589 }
590
591 return err;
592 }
593 #endif /* IPFILTER_SCAN */
Cache object: edb89cdf976583f69ef7910923a89a65
|