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