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