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
55ipscan_t	*ipf_scan_list = NULL,
56		*ipf_scan_tail = NULL;
57ipscanstat_t	ipf_scan_stat;
58# ifdef USE_MUTEXES
59ipfrwlock_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
68int ipf_scan_add(caddr_t);
69int ipf_scan_remove(caddr_t);
70struct ipscan *ipf_scan_lookup(char *);
71int ipf_scan_matchstr(sinfo_t *, char *, int);
72int ipf_scan_matchisc(ipscan_t *, ipstate_t *, int, int, int *);
73int ipf_scan_match(ipstate_t *);
74
75static int	ipf_scan_inited = 0;
76
77
78int
79ipf_scan_init(void)
80{
81	RWLOCK_INIT(&ipf_scan_rwlock, "ip scan rwlock");
82	ipf_scan_inited = 1;
83	return (0);
84}
85
86
87void
88ipf_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
97int
98ipf_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
147int
148ipf_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
187struct ipscan *
188ipf_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
199int
200ipf_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
221int
222ipf_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
249int
250ipf_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
262int
263ipf_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 */
282int
283ipf_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 */
314int
315ipf_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
379int
380ipf_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 */
506int
507ipf_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
564int
565ipf_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