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