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