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