Home | History | Annotate | Line # | Download | only in netinet
      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
     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
    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
    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
    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 *
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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