xref: /freebsd/sys/netpfil/ipfilter/netinet/ip_scan.c (revision c03c5b1c)
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 */
599