Re: Passive OS fingerprinting.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Jul 01, 2008 at 03:39:28PM +0400, Evgeniy Polyakov wrote:
> Hi.
> 
> Passive OS fingerprinting iptables (xtables) allows to match incoming
> packets by different sets of SYN-packet and determine, which remote
> system is on the remote end, so you can make decisions based on OS
> type and even version at some degreee and perform various netfilter
> actions based on that knowledge.
> 
> This module compares some data (WS, MSS, options and it's order, ttl, df
> and others) from packets with SYN bit set with dynamically loaded OS
> fingerprints.
> 
> This version existed quite for a while in patch-o-matic(-ng), but
> suddenly was dropped and then only was updated on its own repo:
> http://tservice.net.ru/~s0mbre/old/?section=projects&item=osf
> 
> I've updated OSF to match new iptables standards (namely xtables
> support) and present new kernelspace and userspace library files in
> attach.

A few quick questions below.

							Thanx, Paul

> To setup single rule, which will drop and log all Linux incoming
> access one needs to do following steps:
> # insmod ./ipt_osf.ko
> # ./load ./pf.os /proc/sys/net/ipv4/osf
> # iptables -I INPUT -j DROP -p tcp -m osf --genre Linux --log 2 \
> --ttl 2 --connector
> 
> And you will find following lines in dmesg:
> 
> ipt_osf: Linux [2.5-2.6::Linux 2.5/2.6] : aa:aa:aa:aa:32885 -> bb:bb:bb:bb:23 hops=3
> 
> More info can be found on homepage:
> http://tservice.net.ru/~s0mbre/old/?section=projects&item=osf
> 
> Enjoy!
> 
> Signed-off-by: Evgeniy Polyakov <johnpol@xxxxxxxxxxx>
> 
> -- 
> 	Evgeniy Polyakov

> /*
>  * ipt_osf.c
>  *
>  * Copyright (c) 2003-2006 Evgeniy Polyakov <johnpol@xxxxxxxxxxx>
>  *
>  *
>  * This program is free software; you can redistribute it and/or modify
>  * it under the terms of the GNU General Public License as published by
>  * the Free Software Foundation; either version 2 of the License, or
>  * (at your option) any later version.
>  *
>  * This program is distributed in the hope that it will be useful,
>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>  * GNU General Public License for more details.
>  *
>  * You should have received a copy of the GNU General Public License
>  * along with this program; if not, write to the Free Software
>  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>  */
> 
> #include <linux/module.h>
> #include <linux/kernel.h>
> 
> #include <linux/types.h>
> #include <linux/string.h>
> #include <linux/smp.h>
> #include <linux/skbuff.h>
> #include <linux/file.h>
> #include <linux/ip.h>
> #include <linux/proc_fs.h>
> #include <linux/fs.h>
> #include <linux/slab.h>
> #include <linux/spinlock.h>
> #include <linux/ctype.h>
> #include <linux/list.h>
> #include <linux/if.h>
> #include <linux/inetdevice.h>
> #include <net/ip.h>
> #include <linux/tcp.h>
> 
> #include <linux/netfilter/x_tables.h>
> #include <linux/netfilter_ipv4/ip_tables.h>
> #include "ipt_osf.h"
> 
> #define OSF_DEBUG
> 
> #ifdef OSF_DEBUG
> #define log(x...) 		printk(KERN_INFO "ipt_osf: " x)
> #define loga(x...) 		printk(x)
> #else
> #define log(x...) 		do {} while(0)
> #define loga(x...) 		do {} while(0)
> #endif
> 
> #define FMATCH_WRONG		0
> #define FMATCH_OK		1
> #define FMATCH_OPT_WRONG	2
> 
> #define OPTDEL			','
> #define OSFPDEL 		':'
> #define MAXOPTSTRLEN		128
> #define OSFFLUSH		"FLUSH"
> 
> static DEFINE_SPINLOCK(ipt_osf_lock);
> static LIST_HEAD(ipt_finger_list);
> 
> #ifdef CONFIG_CONNECTOR
> #include <linux/connector.h>
> 
> /*
>  * They should live in connector.h.
>  */
> #define CN_IDX_OSF		0x0008
> #define CN_VAL_OSF		0x0000
> 
> static char osf_finger_buf[sizeof(struct ipt_osf_nlmsg) +
> 			   sizeof(struct cn_msg)];
> static struct cb_id osf_id = { CN_IDX_OSF, CN_VAL_OSF };
> static u32 osf_seq;
> 
> static void ipt_osf_send_connector(struct ipt_osf_finger *f,
> 				   const struct sk_buff *sk)
> {
> 	struct cn_msg *m;
> 	struct ipt_osf_nlmsg *data;
> 
> 	m = (struct cn_msg *)osf_finger_buf;
> 	data = (struct ipt_osf_nlmsg *)(m + 1);
> 
> 	memcpy(&m->id, &osf_id, sizeof(m->id));
> 	m->seq = osf_seq++;
> 	m->ack = 0;
> 	m->len = sizeof(*data);
> 
> 	memcpy(&data->f, f, sizeof(struct ipt_osf_finger));
> 	memcpy(&data->ip, sk->nh.iph, sizeof(struct iphdr));
> 	memcpy(&data->tcp,
> 	       (struct tcphdr *)((u32 *)sk->nh.iph + sk->nh.iph->ihl),
> 	       sizeof(struct tcphdr));
> 
> 	cn_netlink_send(m, m->id.idx, GFP_ATOMIC);
> }
> #else
> static void ipt_osf_send_connector(struct ipt_osf_finger *f,
> 				   const struct sk_buff *sk)
> {
> }
> #endif
> 
> static char *ipt_osf_strchr(char *ptr, char c)
> {
> 	char *tmp;
> 
> 	tmp = strchr(ptr, c);
> 
> 	while (tmp && tmp + 1 && isspace(*(tmp + 1)))
> 		tmp++;
> 
> 	return tmp;
> }
> 
> static struct ipt_osf_finger *ipt_osf_finger_alloc(void)
> {
> 	return kzalloc(sizeof(struct ipt_osf_finger), GFP_KERNEL);
> }
> 
> static void ipt_osf_finger_free(struct ipt_osf_finger *f)
> {
> 	memset(f, 0, sizeof(struct ipt_osf_finger));
> 	kfree(f);
> }
> 
> static void ipt_osf_parse_opt(struct ipt_osf_opt *opt, int *optnum, char *obuf,
> 			  int olen)
> {
> 	int i, op;
> 	char *ptr, wc;
> 	unsigned long val;
> 
> 	ptr = &obuf[0];
> 	i = 0;
> 	while (ptr != NULL && i < olen) {
> 		val = 0;
> 		op = 0;
> 		wc = 0;
> 		switch (obuf[i]) {
> 		case 'N':
> 			op = OSFOPT_NOP;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				*ptr = '\0';
> 				ptr++;
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		case 'S':
> 			op = OSFOPT_SACKP;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				*ptr = '\0';
> 				ptr++;
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		case 'T':
> 			op = OSFOPT_TS;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				*ptr = '\0';
> 				ptr++;
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		case 'W':
> 			op = OSFOPT_WSO;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				switch (obuf[i + 1]) {
> 				case '%':
> 					wc = '%';
> 					break;
> 				case 'S':
> 					wc = 'S';
> 					break;
> 				case 'T':
> 					wc = 'T';
> 					break;
> 				default:
> 					wc = 0;
> 					break;
> 				}
> 
> 				*ptr = '\0';
> 				ptr++;
> 				if (wc)
> 					val = simple_strtoul(&obuf[i + 2], 
> 							NULL, 10);
> 				else
> 					val = simple_strtoul(&obuf[i + 1], 
> 							NULL, 10);
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		case 'M':
> 			op = OSFOPT_MSS;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				if (obuf[i + 1] == '%')
> 					wc = '%';
> 				*ptr = '\0';
> 				ptr++;
> 				if (wc)
> 					val = simple_strtoul(&obuf[i + 2], 
> 							NULL, 10);
> 				else
> 					val = simple_strtoul(&obuf[i + 1], 
> 							NULL, 10);
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		case 'E':
> 			op = OSFOPT_EOL;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				*ptr = '\0';
> 				ptr++;
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		default:
> 			op = OSFOPT_EMPTY;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				ptr++;
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		}
> 
> 		if (op != OSFOPT_EMPTY) {
> 			opt[*optnum].kind = IANA_opts[op].kind;
> 			opt[*optnum].length = IANA_opts[op].length;
> 			opt[*optnum].wc.wc = wc;
> 			opt[*optnum].wc.val = val;
> 			(*optnum)++;
> 		}
> 	}
> }
> 
> static int ipt_osf_proc_read(char *buf, char **start, off_t off, int count,
> 			 int *eof, void *data)
> {
> 	struct ipt_osf_finger *f = NULL;
> 	int i, __count, err;
> 
> 	*eof = 1;
> 	__count = count;
> 	count = 0;
> 
> 	rcu_read_lock();
> 	list_for_each_entry(f, &ipt_finger_list, flist) {

Does the above need to be list_for_each_entry_rcu()?

> 		log("%s [%s]", f->genre, f->details);
> 
> 		err = snprintf(buf + count, __count - count, "%s - %s[%s] : %s",
> 			       f->genre, f->version, f->subtype, f->details);
> 		if (err == 0 || __count <= count + err)
> 			break;
> 		else
> 			count += err;
> 		if (f->opt_num) {
> 			loga(" OPT: ");
> 			/* count += sprintf(buf+count, " OPT: "); */
> 			for (i = 0; i < f->opt_num; ++i) {
> 				/*
> 				count += sprintf(buf+count, "%d.%c%u; ", 
> 					f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', 
> 					f->opt[i].wc.val);
> 				*/
> 				loga("%d.%c%u; ",
> 				     f->opt[i].kind,
> 				     (f->opt[i].wc.wc) ? f->opt[i].wc.wc : ' ',
> 				     f->opt[i].wc.val);
> 			}
> 		}
> 		loga("\n");
> 		err = snprintf(buf + count, __count - count, "\n");
> 		if (err == 0 || __count <= count + err)
> 			break;
> 		else
> 			count += err;
> 	}
> 	rcu_read_unlock();
> 
> 	return count;
> }
> 
> static int ipt_osf_proc_write(struct file *file, const char *buffer,
> 			  unsigned long count, void *data)
> {
> 	int cnt, i;
> 	char obuf[MAXOPTSTRLEN];
> 	struct ipt_osf_finger *finger, *n;
> 	char *pbeg, *pend;
> 
> 	if (count > 0xffff)
> 		return -E2BIG;
> 
> 	if (count == strlen(OSFFLUSH) && !strncmp(buffer, OSFFLUSH, strlen(OSFFLUSH))) {
> 		int i = 0;
> 		synchronize_rcu();
> 		spin_lock_bh(&ipt_osf_lock);
> 		list_for_each_entry_safe(finger, n, &ipt_finger_list, flist) {

This is OK -- we hold the update-side lock, so don't need _rcu.

> 			i++;
> 			list_del_rcu(&finger->flist);
> 			ipt_osf_finger_free(finger);

Why is it safe to immediately free the element that we just removed from
an RCU-protected list?  The above synchronize_rcu() won't help us given
that it is still in the list at that point.

This could be fixed by using call_rcu() in ipt_osf_finger_free().

> 		}
> 		spin_unlock_bh(&ipt_osf_lock);
> 
> 		log("Flushed %d entries.\n", i);
> 
> 		return count;
> 	}
> 
> 	cnt = 0;
> 	for (i = 0; i < count && buffer[i] != '\0'; ++i)
> 		if (buffer[i] == ':')
> 			cnt++;
> 
> 	if (cnt != 8 || i != count) {
> 		log("Wrong input line cnt=%d[8], len=%u[%lu]\n",
> 		    cnt, i, count);
> 		return count;
> 	}
> 
> 	memset(obuf, 0, sizeof(obuf));
> 
> 	finger = ipt_osf_finger_alloc();
> 	if (!finger) {
> 		log("Failed to allocate new fingerprint entry.\n");
> 		return -ENOMEM;
> 	}
> 
> 	pbeg = (char *)buffer;
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		if (pbeg[0] == 'S') {
> 			finger->wss.wc = 'S';
> 			if (pbeg[1] == '%')
> 				finger->wss.val = simple_strtoul(&pbeg[2], NULL, 10);
> 			else if (pbeg[1] == '*')
> 				finger->wss.val = 0;
> 			else
> 				finger->wss.val = simple_strtoul(&pbeg[1], NULL, 10);
> 		} else if (pbeg[0] == 'T') {
> 			finger->wss.wc = 'T';
> 			if (pbeg[1] == '%')
> 				finger->wss.val = simple_strtoul(&pbeg[2], NULL, 10);
> 			else if (pbeg[1] == '*')
> 				finger->wss.val = 0;
> 			else
> 				finger->wss.val = simple_strtoul(&pbeg[1], NULL, 10);
> 		} else if (pbeg[0] == '%') {
> 			finger->wss.wc = '%';
> 			finger->wss.val = simple_strtoul(&pbeg[1], NULL, 10);
> 		} else if (isdigit(pbeg[0])) {
> 			finger->wss.wc = 0;
> 			finger->wss.val = simple_strtoul(&pbeg[0], NULL, 10);
> 		}
> 
> 		pbeg = pend + 1;
> 	}
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		finger->ttl = simple_strtoul(pbeg, NULL, 10);
> 		pbeg = pend + 1;
> 	}
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		finger->df = simple_strtoul(pbeg, NULL, 10);
> 		pbeg = pend + 1;
> 	}
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		finger->ss = simple_strtoul(pbeg, NULL, 10);
> 		pbeg = pend + 1;
> 	}
> 
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		cnt = snprintf(obuf, sizeof(obuf), "%s", pbeg);
> 		pbeg = pend + 1;
> 	}
> 
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		if (pbeg[0] == '@' || pbeg[0] == '*')
> 			cnt = snprintf(finger->genre, sizeof(finger->genre), 
> 					"%s", pbeg + 1);
> 		else
> 			cnt = snprintf(finger->genre, sizeof(finger->genre), 
> 					"%s", pbeg);
> 		pbeg = pend + 1;
> 	}
> 
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		cnt =
> 		    snprintf(finger->version, sizeof(finger->version), "%s",
> 			     pbeg);
> 		pbeg = pend + 1;
> 	}
> 
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		cnt =
> 		    snprintf(finger->subtype, sizeof(finger->subtype), "%s",
> 			     pbeg);
> 		pbeg = pend + 1;
> 	}
> 
> 	cnt = snprintf(finger->details,
> 		       ((count - (pbeg - buffer) + 1) >
> 			MAXDETLEN) ? MAXDETLEN : (count - (pbeg - buffer) + 1),
> 		       "%s", pbeg);
> 
> 	log("%s - %s[%s] : %s\n",
> 	    finger->genre, finger->version, finger->subtype, finger->details);
> 
> 	ipt_osf_parse_opt(finger->opt, &finger->opt_num, obuf, sizeof(obuf));
> 
> 	synchronize_rcu();

I don't understand what this synchronize_rcu() is doing for us.

Is the idea to make sure that all RCU readers see the prior deletes as
having happened before we do the below addition?  If so, please add a
comment to this effect.

> 	spin_lock_bh(&ipt_osf_lock);
> 	list_add_tail_rcu(&finger->flist, &ipt_finger_list);
> 	spin_unlock_bh(&ipt_osf_lock);
> 
> 	return count;
> }
> 
> static inline int ipt_osf_ttl(const struct sk_buff *skb, struct ipt_osf_info *info,
> 			    unsigned char f_ttl)
> {
> 	struct iphdr *ip = ip_hdr(skb);
> #if 0
> 	log("f_ttl: %u, ip_ttl: %u, info->ttl: %u, flags_ttl: %u.\n",
> 			f_ttl, ip->ttl, info->ttl, info->flags & IPT_OSF_TTL);
> #endif
> 	if (info->flags & IPT_OSF_TTL) {
> 		if (info->ttl == IPT_OSF_TTL_TRUE)
> 			return (ip->ttl == f_ttl);
> 		if (info->ttl == IPT_OSF_TTL_NOCHECK)
> 			return 1;
> 		else {
> 			struct in_device *in_dev = in_dev_get(skb->dev);
> 
> 			for_ifa(in_dev) {
> 				if (inet_ifa_match(ip->saddr, ifa)) {
> 					in_dev_put(in_dev);
> 					return (ip->ttl == f_ttl);
> 				}
> 			}
> 			endfor_ifa(in_dev);
> 
> 			in_dev_put(in_dev);
> 			return (ip->ttl <= f_ttl);
> 		}
> 	}
> 	
> 	return (ip->ttl == f_ttl);
> }
> 
> static bool
> ipt_osf_match_packet(const struct sk_buff *skb,
> 		const struct net_device *in,
> 		const struct net_device *out,
> 		const struct xt_match *match,
> 		const void *matchinfo,
> 		int offset,
> 		unsigned int unused,
> 		bool *hotdrop)
> {
> 	struct ipt_osf_info *info = (struct ipt_osf_info *)matchinfo;
> 	struct iphdr _iph, *ip;
> 	struct tcphdr _tcph, *tcp;
> 	int fmatch = FMATCH_WRONG, fcount = 0;
> 	unsigned int optsize = 0, check_WSS = 0;
> 	u16 window, totlen, mss = 0;
> 	unsigned char df, *optp = NULL, *_optp = NULL;
> 	unsigned char opts[MAX_IPOPTLEN];
> 	struct ipt_osf_finger *f;
> 
> 	if (!info)
> 		return 0;
> 
> 	ip = skb_header_pointer(skb, 0, sizeof(struct iphdr), &_iph);
> 	if (!ip)
> 		return 0;
> 
> 	tcp = skb_header_pointer(skb, ip->ihl * 4, sizeof(struct tcphdr), &_tcph);
> 	if (!tcp)
> 		return 0;
> 
> 	if (!tcp->syn)
> 		return 0;
> 
> 	totlen = ntohs(ip->tot_len);
> 	df = ((ntohs(ip->frag_off) & IP_DF) ? 1 : 0);
> 	window = ntohs(tcp->window);
> 
> 	if (tcp->doff * 4 > sizeof(struct tcphdr)) {
> 		optsize = tcp->doff * 4 - sizeof(struct tcphdr);
> 
> 		if (optsize > sizeof(opts)) {
> 			log("%s: BUG: too big options size: optsize=%u, max=%zu.\n", 
> 					__func__, optsize, sizeof(opts));
> 			optsize = sizeof(opts);
> 		}
> 
> 		_optp = optp = skb_header_pointer(skb, ip->ihl * 4 + sizeof(struct tcphdr), 
> 				optsize, opts);
> 	}
> 
> 	rcu_read_lock();
> 	list_for_each_entry_rcu(f, &ipt_finger_list, flist) {
> 		if (!(info->flags & IPT_OSF_LOG) && strcmp(info->genre, f->genre))
> 			continue;
> 
> 		optp = _optp;
> 		fmatch = FMATCH_WRONG;
> 
> 		if (totlen == f->ss && df == f->df && ipt_osf_ttl(skb, info, f->ttl)) {
> 			int foptsize, optnum;
> 
> 			check_WSS = 0;
> 
> 			switch (f->wss.wc) {
> 			case 0:
> 				check_WSS = 0;
> 				break;
> 			case 'S':
> 				check_WSS = 1;
> 				break;
> 			case 'T':
> 				check_WSS = 2;
> 				break;
> 			case '%':
> 				check_WSS = 3;
> 				break;
> 			default:
> 				log("Wrong fingerprint wss.wc=%d, %s - %s\n",
> 				    f->wss.wc, f->genre, f->details);
> 				check_WSS = 4;
> 				break;
> 			}
> 			if (check_WSS == 4)
> 				continue;
> 
> 			/* Check options */
> 
> 			foptsize = 0;
> 			for (optnum = 0; optnum < f->opt_num; ++optnum)
> 				foptsize += f->opt[optnum].length;
> #if 0
> 			log("%s.%s.%s: optsize: %u, foptsize: %u, fopt_num: %u, optp: %p, win: %u, mss: %u, totlen: %u, df: %d, ttl: %u.\n", 
> 					f->genre, f->version, f->subtype, optsize, foptsize, f->opt_num, optp,
> 					window, mss, totlen, df, ip->ttl);
> #endif
> 			if (foptsize > MAX_IPOPTLEN || optsize > MAX_IPOPTLEN || optsize != foptsize)
> 				continue;
> 
> 			for (optnum = 0; optnum < f->opt_num; ++optnum) {
> 				if (f->opt[optnum].kind == (*optp)) {
> 					__u32 len = f->opt[optnum].length;
> 					__u8 *optend = optp + len;
> 					int loop_cont = 0;
> 					
> 					fmatch = FMATCH_OK;
> 
> 					switch (*optp) {
> 					case OSFOPT_MSS:
> 						mss = ntohs(*(u16 *)(optp + 2));
> 						break;
> 					case OSFOPT_TS:
> 						loop_cont = 1;
> 						break;
> 					}
> 
> #if 0
> 					if (loop_cont) {
> 						optp = optend;
> 						continue;
> 					}
> 					if (len != 1) {
> 						/* Skip kind and length fields */
> 						optp += 2;
> 
> 						if (f->opt[optnum].wc.val != 0) {
> 							u32 tmp = 0;
> 							u32 copy = len > 4 ? 4 : len;
> 
> 							/* Hmmm... It looks a bit ugly. :) */
> 							memcpy(&tmp, optp, copy);
> 
> 							/* 2 + 2: optlen(2 bytes) + 
> 							 *      kind(1 byte) + length(1 byte) */
> 							if (len == 4)
> 								tmp = ntohs(tmp);
> 							else
> 								tmp = ntohl(tmp);
> 
> 							if (f->opt[optnum].wc.wc == '%') {
> 								if ((tmp % f->opt[optnum].wc.val) != 0)
> 									fmatch = FMATCH_OPT_WRONG;
> 							} else if (tmp != f->opt[optnum].wc.val)
> 								fmatch = FMATCH_OPT_WRONG;
> 						}
> 					}
> #endif
> 					optp = optend;
> 				} else
> 					fmatch = FMATCH_OPT_WRONG;
> 
> 				if (fmatch != FMATCH_OK)
> 					break;
> 			}
> 
> 			if (fmatch != FMATCH_OPT_WRONG) {
> 				fmatch = FMATCH_WRONG;
> 
> 				switch (check_WSS) {
> 				case 0:
> 					if (f->wss.val == 0 || window == f->wss.val)
> 						fmatch = FMATCH_OK;
> 					break;
> 				case 1:	/* MSS */
> #define SMART_MSS_1	1460
> #define SMART_MSS_2	1448
> 					if (window == f->wss.val * mss ||
> 					    window == f->wss.val * SMART_MSS_1 ||
> 					    window == f->wss.val * SMART_MSS_2)
> 						fmatch = FMATCH_OK;
> 					break;
> 				case 2:	/* MTU */
> 					if (window == f->wss.val * (mss + 40) ||
> 					    window == f->wss.val * (SMART_MSS_1 + 40) ||
> 					    window == f->wss.val * (SMART_MSS_2 + 40))
> 						fmatch = FMATCH_OK;
> 					break;
> 				case 3:	/* MOD */
> 					if ((window % f->wss.val) == 0)
> 						fmatch = FMATCH_OK;
> 					break;
> 				}
> 			}
> 
> 			if (fmatch == FMATCH_OK) {
> 				fcount++;
> 				log("%s [%s:%s:%s] : %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n", 
> 						f->genre, f->version, f->subtype, f->details, 
> 						NIPQUAD(ip->saddr), ntohs(tcp->source), 
> 						NIPQUAD(ip->daddr), ntohs(tcp->dest), 
> 						f->ttl - ip->ttl);
> 				if (info->flags & IPT_OSF_CONNECTOR)
> 					ipt_osf_send_connector(f, skb);
> 
> 				if ((info->flags & IPT_OSF_LOG) &&
> 				    info->loglevel == IPT_OSF_LOGLEVEL_FIRST)
> 					break;
> 			}
> 		}
> 	}
> 	rcu_read_unlock();
> 
> 	if (!fcount && (info->flags & (IPT_OSF_LOG | IPT_OSF_NETLINK | IPT_OSF_CONNECTOR))) {
> 		unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
> 		unsigned int i, optsize;
> 		struct ipt_osf_finger fg;
> 
> 		memset(&fg, 0, sizeof(fg));
> #if 1
> 		if ((info->flags & IPT_OSF_LOG) && (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN))
> 			log("Unknown: win: %u, mss: %u, totlen: %u, df: %d, ttl: %u : ", window, mss, totlen, df, ip->ttl);
> 		if (optp) {
> 			optsize = tcp->doff * 4 - sizeof(struct tcphdr);
> 			if (skb_copy_bits(skb, ip->ihl * 4 + sizeof(struct tcphdr),
> 					opt, optsize) < 0) {
> 				if (info->flags & IPT_OSF_LOG)
> 					loga("TRUNCATED");
> 				if (info->flags & IPT_OSF_NETLINK)
> 					strcpy(fg.details, "TRUNCATED");
> 			} else {
> 				for (i = 0; i < optsize; i++) {
> 					if (info->flags & IPT_OSF_LOG)
> 						loga("%02X ", opt[i]);
> 				}
> 				if (info->flags & IPT_OSF_NETLINK)
> 					memcpy(fg.details, opt, min_t(unsigned int, optsize, MAXDETLEN));
> 			}
> 		}
> 		if ((info->flags & IPT_OSF_LOG) && (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN))
> 			loga(" %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n",
> 			     NIPQUAD(ip->saddr), ntohs(tcp->source),
> 			     NIPQUAD(ip->daddr), ntohs(tcp->dest));
> #endif
> 		if (info->flags & (IPT_OSF_NETLINK | IPT_OSF_CONNECTOR)) {
> 			fg.wss.val = window;
> 			fg.ttl = ip->ttl;
> 			fg.df = df;
> 			fg.ss = totlen;
> 			fg.mss = mss;
> 			strncpy(fg.genre, "Unknown", MAXGENRELEN);
> 
> 			if (info->flags & IPT_OSF_CONNECTOR)
> 				ipt_osf_send_connector(&fg, skb);
> 		}
> 	}
> 
> 	if (fcount)
> 		fmatch = FMATCH_OK;
> 
> 	return (fmatch == FMATCH_OK) ? 1 : 0;
> }
> 
> static bool
> ipt_osf_checkentry(const char *tablename,
> 		const void *data,
> 		const struct xt_match *match,
> 		void *matchinfo,
> 		unsigned int hook_mask)
> {
> 	struct ipt_ip *ip = (struct ipt_ip *)data;
> 
> 	if (ip->proto != IPPROTO_TCP)
> 		return false;
> 
> 	return true;
> }
> 
> static struct xt_match ipt_osf_match = {
> 	.name 		= "osf",
> 	.revision	= 0,
> 	.family		= AF_INET,
> 	.hooks      	= (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_PRE_ROUTING),
> 	.match 		= ipt_osf_match_packet,
> 	.checkentry	= ipt_osf_checkentry,
> 	.matchsize	= sizeof(struct ipt_osf_info),
> 	.me		= THIS_MODULE,
> };
> 
> static int __devinit ipt_osf_init(void)
> {
> 	int err = -EINVAL;
> 	struct proc_dir_entry *p;
> 
> 	log("Startng OS fingerprint matching module.\n");
> 
> 	p = create_proc_entry("osf", S_IFREG | 0644, proc_net_netfilter);
> 	if (!p)
> 		goto err_out_exit;
> 
> 	p->write_proc = ipt_osf_proc_write;
> 	p->read_proc = ipt_osf_proc_read;
> 
> 	err = xt_register_match(&ipt_osf_match);
> 	if (err) {
> 		log("Failed to register OS fingerprint matching module.\n");
> 		goto err_out_remove;
> 	}
> 
> 	return 0;
> 
> err_out_remove:
> 	remove_proc_entry("osf", proc_net_netfilter);
> err_out_exit:
> 	return err;
> }
> 
> static void __devexit ipt_osf_fini(void)
> {
> 	struct ipt_osf_finger *f, *n;
> 
> 	remove_proc_entry("osf", proc_net_netfilter);
> 	xt_unregister_match(&ipt_osf_match);
> 
> 	list_for_each_entry_safe(f, n, &ipt_finger_list, flist) {

Why is it safe to do the above without being in an RCU read-side critical
section?  Have all possible RCU readers somehow been banished?  If so,
how have they been banished?

> 		list_del(&f->flist);

Ditto for why the list_del() is safe without the update-side lock and
why it doesn't have to be list_del_rcu().

> 		ipt_osf_finger_free(f);
> 	}
> 
> 	log("OS fingerprint matching module finished.\n");
> }
> 
> module_init(ipt_osf_init);
> module_exit(ipt_osf_fini);
> 
> MODULE_LICENSE("GPL");
> MODULE_AUTHOR("Evgeniy Polyakov <johnpol@xxxxxxxxxxx>");
> MODULE_DESCRIPTION("Passive OS fingerprint matching.");

> /*
>  * ipt_osf.h
>  *
>  * Copyright (c) 2003 Evgeniy Polyakov <johnpol@xxxxxxxxxxx>
>  *
>  *
>  * This program is free software; you can redistribute it and/or modify
>  * it under the terms of the GNU General Public License as published by
>  * the Free Software Foundation; either version 2 of the License, or
>  * (at your option) any later version.
>  *
>  * This program is distributed in the hope that it will be useful,
>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>  * GNU General Public License for more details.
>  *
>  * You should have received a copy of the GNU General Public License
>  * along with this program; if not, write to the Free Software
>  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>  */
> 
> #ifndef _IPT_OSF_H
> #define _IPT_OSF_H
> 
> #define MAXGENRELEN		32
> #define MAXDETLEN		64
> 
> #define IPT_OSF_GENRE		1
> #define	IPT_OSF_TTL		2
> #define IPT_OSF_LOG		4
> #define IPT_OSF_NETLINK		8
> #define IPT_OSF_CONNECTOR	16
> #define IPT_OSF_INVERT		32
> 
> #define IPT_OSF_LOGLEVEL_ALL	0
> #define IPT_OSF_LOGLEVEL_FIRST	1
> #define IPT_OSF_LOGLEVEL_ALL_KNOWN	2
> 
> #define IPT_OSF_TTL_TRUE	0	/* True ip and fingerprint TTL comparison */
> #define IPT_OSF_TTL_LESS	1	/* Check if ip TTL is less than fingerprint one */
> #define IPT_OSF_TTL_NOCHECK	2	/* Do not compare ip and fingerprint TTL at all */
> 
> #ifndef __KERNEL__
> #include <netinet/ip.h>
> #include <netinet/tcp.h>
> 
> struct list_head {
> 	struct list_head *prev, *next;
> };
> #endif
> 
> struct ipt_osf_info {
> 	char genre[MAXGENRELEN];
> 	__u32 len, flags, loglevel, ttl;
> } __attribute__ ((packed));
> 
> struct ipt_osf_wc {
> 	__u8 wc;
> 	__u32 val;
> } __attribute__ ((packed));
> 
> /* This struct represents IANA options
>  * http://www.iana.org/assignments/tcp-parameters
>  */
> struct ipt_osf_opt {
> 	__u8 kind, length;
> 	struct ipt_osf_wc wc;
> } __attribute__ ((packed));
> 
> struct ipt_osf_finger {
> 	struct list_head flist;
> 	struct ipt_osf_wc wss;
> 	__u8 ttl, df;
> 	__u16 ss, mss;
> 	unsigned char genre[MAXGENRELEN];
> 	unsigned char version[MAXGENRELEN], subtype[MAXGENRELEN];
> 
> 	/* Not needed, but for consistency with original table from Michal Zalewski */
> 	unsigned char details[MAXDETLEN];
> 
> 	int opt_num;
> 	struct ipt_osf_opt opt[MAX_IPOPTLEN];	/* In case it is all NOP or EOL */
> 
> } __attribute__ ((packed));
> 
> struct ipt_osf_nlmsg {
> 	struct ipt_osf_finger f;
> 	struct iphdr ip;
> 	struct tcphdr tcp;
> } __attribute__ ((packed));
> 
> #ifdef __KERNEL__
> 
> #include <linux/list.h>
> #include <net/tcp.h>
> 
> /* Defines for IANA option kinds */
> 
> #define OSFOPT_EOL		0	/* End of options */
> #define OSFOPT_NOP		1	/* NOP */
> #define OSFOPT_MSS		2	/* Maximum segment size */
> #define OSFOPT_WSO		3	/* Window scale option */
> #define OSFOPT_SACKP		4	/* SACK permitted */
> #define OSFOPT_SACK		5	/* SACK */
> #define OSFOPT_ECHO		6
> #define OSFOPT_ECHOREPLY	7
> #define OSFOPT_TS		8	/* Timestamp option */
> #define OSFOPT_POCP		9	/* Partial Order Connection Permitted */
> #define OSFOPT_POSP		10	/* Partial Order Service Profile */
> #define OSFOPT_EMPTY		255
> /* Others are not used in current OSF */
> 
> static struct ipt_osf_opt IANA_opts[] = {
> 	{0, 1,},
> 	{1, 1,},
> 	{2, 4,},
> 	{3, 3,},
> 	{4, 2,},
> 	{5, 1,},		/* SACK length is not defined */
> 	{6, 6,},
> 	{7, 6,},
> 	{8, 10,},
> 	{9, 2,},
> 	{10, 3,},
> 	{11, 1,},		/* CC: Suppose 1 */
> 	{12, 1,},		/* the same */
> 	{13, 1,},		/* and here too */
> 	{14, 3,},
> 	{15, 1,},		/* TCP Alternate Checksum Data. Length is not defined */
> 	{16, 1,},
> 	{17, 1,},
> 	{18, 3,},
> 	{19, 18,},
> 	{20, 1,},
> 	{21, 1,},
> 	{22, 1,},
> 	{23, 1,},
> 	{24, 1,},
> 	{25, 1,},
> 	{26, 1,},
> };
> 
> #endif				/* __KERNEL__ */
> 
> #endif				/* _IPT_OSF_H */

> /*
>  * libipt_osf.c
>  *
>  * Copyright (c) 2003-2006 Evgeniy Polyakov <johnpol@xxxxxxxxxxx>
>  *
>  *
>  * This program is free software; you can redistribute it and/or modify
>  * it under the terms of the GNU General Public License as published by
>  * the Free Software Foundation; either version 2 of the License, or
>  * (at your option) any later version.
>  *
>  * This program is distributed in the hope that it will be useful,
>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>  * GNU General Public License for more details.
>  *
>  * You should have received a copy of the GNU General Public License
>  * along with this program; if not, write to the Free Software
>  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>  */
> 
> /*
>  * iptables interface for OS fingerprint matching module.
>  */
> 
> #include <stdio.h>
> #include <netdb.h>
> #include <string.h>
> #include <stdlib.h>
> #include <getopt.h>
> #include <ctype.h>
> 
> #include <iptables.h>
> 
> typedef unsigned int __u32;
> typedef unsigned short __u16;
> typedef unsigned char __u8;
> 
> #include "ipt_osf.h"
> 
> static void osf_help(void)
> {
> 	printf("OS fingerprint match options:\n"
> 		"--genre [!] string	Match a OS genre by passive fingerprinting.\n"
> 		"--ttl			Use some TTL check extensions to determine OS:\n"
> 		"	0			true ip and fingerprint TTL comparison. Works for LAN.\n"
> 		"	1			check if ip TTL is less than fingerprint one. Works for global addresses.\n"
> 		"	2			do not compare TTL at all. Allows to detect NMAP, but can produce false results.\n"
> 		"--log level		Log determined genres into dmesg even if they do not match desired one:\n"
> 		"	0			log all matched or unknown signatures.\n"
> 		"	1			log only first one.\n"
> 		"	2			log all known matched signatures.\n"
> 		"--connector		Log through kernel connector [2.6.14+].\n\n"
> 		);
> }
> 
> 
> static const struct option osf_opts[] = {
> 	{ .name = "genre",	.has_arg = 1, .flag = 0, .val = '1' },
> 	{ .name = "ttl",	.has_arg = 1, .flag = 0, .val = '2' },
> 	{ .name = "log",	.has_arg = 1, .flag = 0, .val = '3' },
> 	{ .name = "netlink",	.has_arg = 0, .flag = 0, .val = '4' },
> 	{ .name = "connector",	.has_arg = 0, .flag = 0, .val = '5' },
> 	{ .name = NULL }
> };
> 
> 
> static void osf_init(struct xt_entry_match *m)
> {
> }
> 
> static void osf_parse_string(const unsigned char *s, struct ipt_osf_info *info)
> {
> 	if (strlen(s) < MAXGENRELEN) 
> 		strcpy(info->genre, s);
> 	else 
> 		exit_error(PARAMETER_PROBLEM, "Genre string too long `%s' [%d], max=%d", 
> 				s, strlen(s), MAXGENRELEN);
> }
> 
> static int osf_parse(int c, char **argv, int invert, unsigned int *flags,
>       			const void *entry,
>       			struct xt_entry_match **match)
> {
> 	struct ipt_osf_info *info = (struct ipt_osf_info *)(*match)->data;
> 	
> 	switch(c) 
> 	{
> 		case '1': /* --genre */
> 			if (*flags & IPT_OSF_GENRE)
> 				exit_error(PARAMETER_PROBLEM, "Can't specify multiple genre parameter");
> 			check_inverse(optarg, &invert, &optind, 0);
> 			osf_parse_string(argv[optind-1], info);
> 			if (invert)
> 				info->flags |= IPT_OSF_INVERT;
> 			info->len=strlen((char *)info->genre);
> 			*flags |= IPT_OSF_GENRE;
> 			break;
> 		case '2': /* --ttl */
> 			if (*flags & IPT_OSF_TTL)
> 				exit_error(PARAMETER_PROBLEM, "Can't specify multiple ttl parameter");
> 			*flags |= IPT_OSF_TTL;
> 			info->flags |= IPT_OSF_TTL;
> 			info->ttl = atoi(argv[optind-1]);
> 			break;
> 		case '3': /* --log */
> 			if (*flags & IPT_OSF_LOG)
> 				exit_error(PARAMETER_PROBLEM, "Can't specify multiple log parameter");
> 			*flags |= IPT_OSF_LOG;
> 			info->loglevel = atoi(argv[optind-1]);
> 			info->flags |= IPT_OSF_LOG;
> 			break;
> 		case '4': /* --netlink */
> 			if (*flags & IPT_OSF_NETLINK)
> 				exit_error(PARAMETER_PROBLEM, "Can't specify multiple netlink parameter");
> 			*flags |= IPT_OSF_NETLINK;
> 			info->flags |= IPT_OSF_NETLINK;
> 			break;
> 		case '5': /* --connector */
> 			if (*flags & IPT_OSF_CONNECTOR)
> 				exit_error(PARAMETER_PROBLEM, "Can't specify multiple connector parameter");
> 			*flags |= IPT_OSF_CONNECTOR;
> 			info->flags |= IPT_OSF_CONNECTOR;
> 			break;
> 		default:
> 			return 0;
> 	}
> 
> 	return 1;
> }
> 
> static void osf_final_check(unsigned int flags)
> {
> 	if (!flags)
> 		exit_error(PARAMETER_PROBLEM, "OS fingerprint match: You must specify `--genre'");
> }
> 
> static void osf_print(const void *ip, const struct xt_entry_match *match, int numeric)
> {
> 	const struct ipt_osf_info *info = (const struct ipt_osf_info*) match->data;
> 
> 	printf("OS fingerprint match %s%s ", (info->flags & IPT_OSF_INVERT) ? "!" : "", info->genre);
> }
> 
> static void osf_save(const void *ip, const struct xt_entry_match *match)
> {
> 	const struct ipt_osf_info *info = (const struct ipt_osf_info*) match->data;
> 
> 	printf("--genre %s%s ", (info->flags & IPT_OSF_INVERT) ? "! ": "", info->genre);
> }
> 
> 
> static struct xtables_match osf_match = {
>     .name		= "osf",
>     .version		= XTABLES_VERSION,
>     .size		= XT_ALIGN(sizeof(struct ipt_osf_info)),
>     .userspacesize	= XT_ALIGN(sizeof(struct ipt_osf_info)),
>     .help		= &osf_help,
>     .init		= &osf_init,
>     .parse		= &osf_parse,
>     .print		= &osf_print,
>     .final_check	= &osf_final_check,
>     .save		= &osf_save,
>     .extra_opts		= osf_opts
> };
> 
> 
> void _init(void)
> {
> 	xtables_register_match(&osf_match);
> }

> obj-m		:= ipt_osf.o
> 
> KDIR	:= /lib/modules/$(shell uname -r)/build
> IPTABLES:= /usr/include
> 
> ifeq ($(IPTABLES),path_to_iptables_sources_or_header_files)
> $(error "Edit IPTABLES variable in Makefile and read README")
> endif
> PWD	:= $(shell pwd)
> 
> iptables_version=$(shell (/sbin/iptables -V | awk {'print $$2'} | cut -c 2-))
> LCFLAGS = -DIPTABLES_VERSION=\"$(iptables_version)\"
> 
> default:
> 	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
> 
> clean:
> 	rm -f *.o *.ko *.mod.* .*.cmd *~
> 	rm -rf .tmp_versions
> 	rm -f *.o *~ *.so *.ko load osfd ucon_osf
> 
> lib:	libipt_osf.c ipt_osf.h
> 	gcc $(LCFLAGS) libipt_osf.c -D_INIT=_init -fPIC -I$(IPTABLES)/include -c -o libipt_osf.o
> 	gcc -shared -nostdlib -rdynamic -Wl,-soname,libipt_osf.so -o libipt_osf.so libipt_osf.o
> 
> bin:	osfd.c load.c ucon_osf.c
> 	gcc -W -Wall osfd.c -o osfd
> 	gcc -W -Wall load.c -o load
> 	gcc -W -Wall ucon_osf.c -o ucon_osf

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux