1 /* $NetBSD: ip_scan.c,v 1.3 2012/07/22 14:27:35 darrenr Exp $ */
2
3 /*
4 * Copyright (C) 2012 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) && !defined(AIX)
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 1.1.1.2 2012/07/22 13:44:24 darrenr Exp";
64 #endif
65
66 #ifdef IPFILTER_SCAN /* endif at bottom of file */
67
68
69 ipscan_t *ipf_scan_list = NULL,
70 *ipf_scan_tail = NULL;
71 ipscanstat_t ipf_scan_stat;
72 # ifdef USE_MUTEXES
73 ipfrwlock_t ipf_scan_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 ipf_scan_add __P((caddr_t));
83 int ipf_scan_remove __P((caddr_t));
84 struct ipscan *ipf_scan_lookup __P((char *));
85 int ipf_scan_matchstr __P((sinfo_t *, char *, int));
86 int ipf_scan_matchisc __P((ipscan_t *, ipstate_t *, int, int, int *));
87 int ipf_scan_match __P((ipstate_t *));
88
89 static int ipf_scan_inited = 0;
90
91
92 int
ipf_scan_init()93 ipf_scan_init()
94 {
95 RWLOCK_INIT(&ipf_scan_rwlock, "ip scan rwlock");
96 ipf_scan_inited = 1;
97 return 0;
98 }
99
100
101 void
ipf_scan_unload(void * arg)102 ipf_scan_unload(void *arg)
103 {
104 if (ipf_scan_inited == 1) {
105 RW_DESTROY(&ipf_scan_rwlock);
106 ipf_scan_inited = 0;
107 }
108 }
109
110
111 int
ipf_scan_add(data)112 ipf_scan_add(data)
113 caddr_t data;
114 {
115 ipscan_t *i, *isc;
116 int err;
117
118 KMALLOC(isc, ipscan_t *);
119 if (!isc) {
120 ipf_interror = 90001;
121 return ENOMEM;
122 }
123
124 err = copyinptr(data, isc, sizeof(*isc));
125 if (err) {
126 KFREE(isc);
127 return err;
128 }
129
130 WRITE_ENTER(&ipf_scan_rwlock);
131
132 i = ipf_scan_lookup(isc->ipsc_tag);
133 if (i != NULL) {
134 RWLOCK_EXIT(&ipf_scan_rwlock);
135 KFREE(isc);
136 ipf_interror = 90002;
137 return EEXIST;
138 }
139
140 if (ipf_scan_tail) {
141 ipf_scan_tail->ipsc_next = isc;
142 isc->ipsc_pnext = &ipf_scan_tail->ipsc_next;
143 ipf_scan_tail = isc;
144 } else {
145 ipf_scan_list = isc;
146 ipf_scan_tail = isc;
147 isc->ipsc_pnext = &ipf_scan_list;
148 }
149 isc->ipsc_next = NULL;
150
151 isc->ipsc_hits = 0;
152 isc->ipsc_fref = 0;
153 isc->ipsc_sref = 0;
154 isc->ipsc_active = 0;
155
156 ipf_scan_stat.iscs_entries++;
157 RWLOCK_EXIT(&ipf_scan_rwlock);
158 return 0;
159 }
160
161
162 int
ipf_scan_remove(data)163 ipf_scan_remove(data)
164 caddr_t data;
165 {
166 ipscan_t isc, *i;
167 int err;
168
169 err = copyinptr(data, &isc, sizeof(isc));
170 if (err)
171 return err;
172
173 WRITE_ENTER(&ipf_scan_rwlock);
174
175 i = ipf_scan_lookup(isc.ipsc_tag);
176 if (i == NULL)
177 err = ENOENT;
178 else {
179 if (i->ipsc_fref) {
180 RWLOCK_EXIT(&ipf_scan_rwlock);
181 ipf_interror = 90003;
182 return EBUSY;
183 }
184
185 *i->ipsc_pnext = i->ipsc_next;
186 if (i->ipsc_next)
187 i->ipsc_next->ipsc_pnext = i->ipsc_pnext;
188 else {
189 if (i->ipsc_pnext == &ipf_scan_list)
190 ipf_scan_tail = NULL;
191 else
192 ipf_scan_tail = *(*i->ipsc_pnext)->ipsc_pnext;
193 }
194
195 ipf_scan_stat.iscs_entries--;
196 KFREE(i);
197 }
198 RWLOCK_EXIT(&ipf_scan_rwlock);
199 return err;
200 }
201
202
203 struct ipscan *
ipf_scan_lookup(tag)204 ipf_scan_lookup(tag)
205 char *tag;
206 {
207 ipscan_t *i;
208
209 for (i = ipf_scan_list; i; i = i->ipsc_next)
210 if (!strcmp(i->ipsc_tag, tag))
211 return i;
212 return NULL;
213 }
214
215
216 int
ipf_scan_attachfr(fr)217 ipf_scan_attachfr(fr)
218 struct frentry *fr;
219 {
220 ipscan_t *i;
221
222 if (fr->fr_isctag != -1) {
223 READ_ENTER(&ipf_scan_rwlock);
224 i = ipf_scan_lookup(fr->fr_isctag + fr->fr_names);
225 if (i != NULL) {
226 ATOMIC_INC32(i->ipsc_fref);
227 }
228 RWLOCK_EXIT(&ipf_scan_rwlock);
229 if (i == NULL) {
230 ipf_interror = 90004;
231 return ENOENT;
232 }
233 fr->fr_isc = i;
234 }
235 return 0;
236 }
237
238
239 int
ipf_scan_attachis(is)240 ipf_scan_attachis(is)
241 struct ipstate *is;
242 {
243 frentry_t *fr;
244 ipscan_t *i;
245
246 READ_ENTER(&ipf_scan_rwlock);
247 fr = is->is_rule;
248 if (fr != NULL) {
249 i = fr->fr_isc;
250 if ((i != NULL) && (i != (ipscan_t *)-1)) {
251 is->is_isc = i;
252 ATOMIC_INC32(i->ipsc_sref);
253 if (i->ipsc_clen)
254 is->is_flags |= IS_SC_CLIENT;
255 else
256 is->is_flags |= IS_SC_MATCHC;
257 if (i->ipsc_slen)
258 is->is_flags |= IS_SC_SERVER;
259 else
260 is->is_flags |= IS_SC_MATCHS;
261 }
262 }
263 RWLOCK_EXIT(&ipf_scan_rwlock);
264 return 0;
265 }
266
267
268 int
ipf_scan_detachfr(fr)269 ipf_scan_detachfr(fr)
270 struct frentry *fr;
271 {
272 ipscan_t *i;
273
274 i = fr->fr_isc;
275 if (i != NULL) {
276 ATOMIC_DEC32(i->ipsc_fref);
277 }
278 return 0;
279 }
280
281
282 int
ipf_scan_detachis(is)283 ipf_scan_detachis(is)
284 struct ipstate *is;
285 {
286 ipscan_t *i;
287
288 READ_ENTER(&ipf_scan_rwlock);
289 if ((i = is->is_isc) && (i != (ipscan_t *)-1)) {
290 ATOMIC_DEC32(i->ipsc_sref);
291 is->is_isc = NULL;
292 is->is_flags &= ~(IS_SC_CLIENT|IS_SC_SERVER);
293 }
294 RWLOCK_EXIT(&ipf_scan_rwlock);
295 return 0;
296 }
297
298
299 /*
300 * 'string' compare for scanning
301 */
302 int
ipf_scan_matchstr(sp,str,n)303 ipf_scan_matchstr(sp, str, n)
304 sinfo_t *sp;
305 char *str;
306 int n;
307 {
308 char *s, *t, *up;
309 int i = n;
310
311 if (i > sp->s_len)
312 i = sp->s_len;
313 up = str;
314
315 for (s = sp->s_txt, t = sp->s_msk; i; i--, s++, t++, up++)
316 switch ((int)*t)
317 {
318 case '.' :
319 if (*s != *up)
320 return 1;
321 break;
322 case '?' :
323 if (!ISALPHA(*up) || ((*s & 0x5f) != (*up & 0x5f)))
324 return 1;
325 break;
326 case '*' :
327 break;
328 }
329 return 0;
330 }
331
332
333 /*
334 * Returns 3 if both server and client match, 2 if just server,
335 * 1 if just client
336 */
337 int
ipf_scan_matchisc(isc,is,cl,sl,maxm)338 ipf_scan_matchisc(isc, is, cl, sl, maxm)
339 ipscan_t *isc;
340 ipstate_t *is;
341 int cl, sl, maxm[2];
342 {
343 int i, j, k, n, ret = 0, flags;
344
345 flags = is->is_flags;
346
347 /*
348 * If we've already matched more than what is on offer, then
349 * assume we have a better match already and forget this one.
350 */
351 if (maxm != NULL) {
352 if (isc->ipsc_clen < maxm[0])
353 return 0;
354 if (isc->ipsc_slen < maxm[1])
355 return 0;
356 j = maxm[0];
357 k = maxm[1];
358 } else {
359 j = 0;
360 k = 0;
361 }
362
363 if (!isc->ipsc_clen)
364 ret = 1;
365 else if (((flags & (IS_SC_MATCHC|IS_SC_CLIENT)) == IS_SC_CLIENT) &&
366 cl && isc->ipsc_clen) {
367 i = 0;
368 n = MIN(cl, isc->ipsc_clen);
369 if ((n > 0) && (!maxm || (n >= maxm[1]))) {
370 if (!ipf_scan_matchstr(&isc->ipsc_cl,
371 is->is_sbuf[0], n)) {
372 i++;
373 ret |= 1;
374 if (n > j)
375 j = n;
376 }
377 }
378 }
379
380 if (!isc->ipsc_slen)
381 ret |= 2;
382 else if (((flags & (IS_SC_MATCHS|IS_SC_SERVER)) == IS_SC_SERVER) &&
383 sl && isc->ipsc_slen) {
384 i = 0;
385 n = MIN(cl, isc->ipsc_slen);
386 if ((n > 0) && (!maxm || (n >= maxm[1]))) {
387 if (!ipf_scan_matchstr(&isc->ipsc_sl,
388 is->is_sbuf[1], n)) {
389 i++;
390 ret |= 2;
391 if (n > k)
392 k = n;
393 }
394 }
395 }
396
397 if (maxm && (ret == 3)) {
398 maxm[0] = j;
399 maxm[1] = k;
400 }
401 return ret;
402 }
403
404
405 int
ipf_scan_match(is)406 ipf_scan_match(is)
407 ipstate_t *is;
408 {
409 int i, j, k, n, cl, sl, maxm[2];
410 ipscan_t *isc, *lm;
411 tcpdata_t *t;
412
413 for (cl = 0, n = is->is_smsk[0]; n & 1; n >>= 1)
414 cl++;
415 for (sl = 0, n = is->is_smsk[1]; n & 1; n >>= 1)
416 sl++;
417
418 j = 0;
419 isc = is->is_isc;
420 if (isc != NULL) {
421 /*
422 * Known object to scan for.
423 */
424 i = ipf_scan_matchisc(isc, is, cl, sl, NULL);
425 if (i & 1) {
426 is->is_flags |= IS_SC_MATCHC;
427 is->is_flags &= ~IS_SC_CLIENT;
428 } else if (cl >= isc->ipsc_clen)
429 is->is_flags &= ~IS_SC_CLIENT;
430 if (i & 2) {
431 is->is_flags |= IS_SC_MATCHS;
432 is->is_flags &= ~IS_SC_SERVER;
433 } else if (sl >= isc->ipsc_slen)
434 is->is_flags &= ~IS_SC_SERVER;
435 } else {
436 i = 0;
437 lm = NULL;
438 maxm[0] = 0;
439 maxm[1] = 0;
440 for (k = 0, isc = ipf_scan_list; isc; isc = isc->ipsc_next) {
441 i = ipf_scan_matchisc(isc, is, cl, sl, maxm);
442 if (i) {
443 /*
444 * We only want to remember the best match
445 * and the number of times we get a best
446 * match.
447 */
448 if ((j == 3) && (i < 3))
449 continue;
450 if ((i == 3) && (j != 3))
451 k = 1;
452 else
453 k++;
454 j = i;
455 lm = isc;
456 }
457 }
458 if (k == 1)
459 isc = lm;
460 if (isc == NULL)
461 return 0;
462
463 /*
464 * No matches or partial matches, so reset the respective
465 * search flag.
466 */
467 if (!(j & 1))
468 is->is_flags &= ~IS_SC_CLIENT;
469
470 if (!(j & 2))
471 is->is_flags &= ~IS_SC_SERVER;
472
473 /*
474 * If we found the best match, then set flags appropriately.
475 */
476 if ((j == 3) && (k == 1)) {
477 is->is_flags &= ~(IS_SC_SERVER|IS_SC_CLIENT);
478 is->is_flags |= (IS_SC_MATCHS|IS_SC_MATCHC);
479 }
480 }
481
482 /*
483 * If the acknowledged side of a connection has moved past the data in
484 * which we are interested, then reset respective flag.
485 */
486 t = &is->is_tcp.ts_data[0];
487 if (t->td_end > is->is_s0[0] + 15)
488 is->is_flags &= ~IS_SC_CLIENT;
489
490 t = &is->is_tcp.ts_data[1];
491 if (t->td_end > is->is_s0[1] + 15)
492 is->is_flags &= ~IS_SC_SERVER;
493
494 /*
495 * Matching complete ?
496 */
497 j = ISC_A_NONE;
498 if ((is->is_flags & IS_SC_MATCHALL) == IS_SC_MATCHALL) {
499 j = isc->ipsc_action;
500 ipf_scan_stat.iscs_acted++;
501 } else if ((is->is_isc != NULL) &&
502 ((is->is_flags & IS_SC_MATCHALL) != IS_SC_MATCHALL) &&
503 !(is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER))) {
504 /*
505 * Matching failed...
506 */
507 j = isc->ipsc_else;
508 ipf_scan_stat.iscs_else++;
509 }
510
511 switch (j)
512 {
513 case ISC_A_CLOSE :
514 /*
515 * If as a result of a successful match we are to
516 * close a connection, change the "keep state" info.
517 * to block packets and generate TCP RST's.
518 */
519 is->is_pass &= ~FR_RETICMP;
520 is->is_pass |= FR_RETRST;
521 break;
522 default :
523 break;
524 }
525
526 return i;
527 }
528
529
530 /*
531 * check if a packet matches what we're scanning for
532 */
533 int
ipf_scan_packet(fin,is)534 ipf_scan_packet(fin, is)
535 fr_info_t *fin;
536 ipstate_t *is;
537 {
538 int i, j, rv, dlen, off, thoff;
539 u_32_t seq, s0;
540 tcphdr_t *tcp;
541
542 rv = !IP6_EQ(&fin->fin_fi.fi_src, &is->is_src);
543 tcp = fin->fin_dp;
544 seq = ntohl(tcp->th_seq);
545
546 if (!is->is_s0[rv])
547 return 1;
548
549 /*
550 * check if this packet has more data that falls within the first
551 * 16 bytes sent in either direction.
552 */
553 s0 = is->is_s0[rv];
554 off = seq - s0;
555 if ((off > 15) || (off < 0))
556 return 1;
557 thoff = TCP_OFF(tcp) << 2;
558 dlen = fin->fin_dlen - thoff;
559 if (dlen <= 0)
560 return 1;
561 if (dlen > 16)
562 dlen = 16;
563 if (off + dlen > 16)
564 dlen = 16 - off;
565
566 j = 0xffff >> (16 - dlen);
567 i = (0xffff & j) << off;
568 #ifdef _KERNEL
569 COPYDATA(*(mb_t **)fin->fin_mp, fin->fin_plen - fin->fin_dlen + thoff,
570 dlen, (caddr_t)is->is_sbuf[rv] + off);
571 #endif
572 is->is_smsk[rv] |= i;
573 for (j = 0, i = is->is_smsk[rv]; i & 1; i >>= 1)
574 j++;
575 if (j == 0)
576 return 1;
577
578 (void) ipf_scan_match(is);
579 #if 0
580 /*
581 * There is the potential here for plain text passwords to get
582 * buffered and stored for some time...
583 */
584 if (!(is->is_flags & IS_SC_CLIENT))
585 bzero(is->is_sbuf[0], sizeof(is->is_sbuf[0]));
586 if (!(is->is_flags & IS_SC_SERVER))
587 bzero(is->is_sbuf[1], sizeof(is->is_sbuf[1]));
588 #endif
589 return 0;
590 }
591
592
593 int
ipf_scan_ioctl(data,cmd,mode,uid,ctx)594 ipf_scan_ioctl(data, cmd, mode, uid, ctx)
595 caddr_t data;
596 ioctlcmd_t cmd;
597 int mode, uid;
598 void *ctx;
599 {
600 ipscanstat_t ipscs;
601 int err;
602
603 switch (cmd)
604 {
605 case SIOCADSCA :
606 err = ipf_scan_add(data);
607 break;
608 case SIOCRMSCA :
609 err = ipf_scan_remove(data);
610 break;
611 case SIOCGSCST :
612 bcopy((char *)&ipf_scan_stat, (char *)&ipscs, sizeof(ipscs));
613 ipscs.iscs_list = ipf_scan_list;
614 err = BCOPYOUT(&ipscs, data, sizeof(ipscs));
615 if (err != 0) {
616 ipf_interror = 90005;
617 err = EFAULT;
618 }
619 break;
620 default :
621 err = EINVAL;
622 break;
623 }
624
625 return err;
626 }
627 #endif /* IPFILTER_SCAN */
628