1 diff -Nur linux-2.6.1-rc2.org/include/linux/netfilter_ipv4/ipt_osf.h linux-2.6.1-rc2/include/linux/netfilter_ipv4/ipt_osf.h
2 --- linux-2.6.1-rc2.org/include/linux/netfilter_ipv4/ipt_osf.h 1970-01-01 01:00:00.000000000 +0100
3 +++ linux-2.6.1-rc2/include/linux/netfilter_ipv4/ipt_osf.h 2004-01-07 19:54:12.127377416 +0100
8 + * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
11 + * This program is free software; you can redistribute it and/or modify
12 + * it under the terms of the GNU General Public License as published by
13 + * the Free Software Foundation; either version 2 of the License, or
14 + * (at your option) any later version.
16 + * This program is distributed in the hope that it will be useful,
17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 + * GNU General Public License for more details.
21 + * You should have received a copy of the GNU General Public License
22 + * along with this program; if not, write to the Free Software
23 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 +#define MAXGENRELEN 32
32 +#define IPT_OSF_GENRE 1
33 +#define IPT_OSF_SMART 2
34 +#define IPT_OSF_LOG 4
36 +#define IPT_OSF_LOGLEVEL_ALL 0
37 +#define IPT_OSF_LOGLEVEL_FIRST 1
39 +#include <linux/list.h>
43 + char genre[MAXGENRELEN];
45 + unsigned long flags;
47 + int invert; /* UNSUPPORTED */
56 +/* This struct represents IANA options
57 + * http://www.iana.org/assignments/tcp-parameters
62 + unsigned char length;
70 + struct list_head flist;
75 + char genre[MAXGENRELEN];
76 + char version[MAXGENRELEN], subtype[MAXGENRELEN];
78 + /* Not needed, but for consistency with original table from Michal Zalewski */
79 + char details[MAXDETLEN];
82 + struct osf_opt opt[MAX_IPOPTLEN]; /* In case it is all NOP or EOL */
86 +/* Defines for IANA option kinds */
88 +#define OSFOPT_EOL 0 /* End of options */
89 +#define OSFOPT_NOP 1 /* NOP */
90 +#define OSFOPT_MSS 2 /* Maximum segment size */
91 +#define OSFOPT_WSO 3 /* Window scale option */
92 +#define OSFOPT_SACKP 4 /* SACK permitted */
93 +#define OSFOPT_SACK 5 /* SACK */
94 +#define OSFOPT_ECHO 6
95 +#define OSFOPT_ECHOREPLY 7
96 +#define OSFOPT_TS 8 /* Timestamp option */
97 +#define OSFOPT_POCP 9 /* Partial Order Connection Permitted */
98 +#define OSFOPT_POSP 10 /* Partial Order Service Profile */
99 +/* Others are not used in current OSF */
101 +static struct osf_opt IANA_opts[] =
108 + {5, 1 ,}, /* SACK length is not defined */
114 + {11, 1,}, /* CC: Suppose 1 */
115 + {12, 1,}, /* the same */
116 + {13, 1,}, /* and here too */
118 + {15, 1,}, /* TCP Alternate Checksum Data. Length is not defined */
132 +#endif /* __KERNEL__ */
134 +#endif /* _IPT_OSF_H */
135 diff -Nur linux-2.6.1-rc2.org/net/ipv4/netfilter/ipt_osf.c linux-2.6.1-rc2/net/ipv4/netfilter/ipt_osf.c
136 --- linux-2.6.1-rc2.org/net/ipv4/netfilter/ipt_osf.c 1970-01-01 01:00:00.000000000 +0100
137 +++ linux-2.6.1-rc2/net/ipv4/netfilter/ipt_osf.c 2004-01-07 19:54:12.128377264 +0100
142 + * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
145 + * This program is free software; you can redistribute it and/or modify
146 + * it under the terms of the GNU General Public License as published by
147 + * the Free Software Foundation; either version 2 of the License, or
148 + * (at your option) any later version.
150 + * This program is distributed in the hope that it will be useful,
151 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
152 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
153 + * GNU General Public License for more details.
155 + * You should have received a copy of the GNU General Public License
156 + * along with this program; if not, write to the Free Software
157 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
161 + * OS fingerprint matching module.
162 + * It simply compares various parameters from SYN packet with
163 + * some hardcoded ones.
165 + * Original table was created by Michal Zalewski <lcamtuf@coredump.cx>
169 +#include <linux/config.h>
170 +#include <linux/kernel.h>
171 +#include <linux/smp.h>
172 +#include <linux/module.h>
173 +#include <linux/skbuff.h>
174 +#include <linux/file.h>
175 +#include <linux/ip.h>
176 +#include <linux/proc_fs.h>
177 +#include <linux/fs.h>
178 +#include <linux/slab.h>
179 +#include <linux/spinlock.h>
180 +#include <linux/ctype.h>
181 +#include <linux/list.h>
183 +#include <net/sock.h>
186 +#include <linux/netfilter_ipv4/ip_tables.h>
188 +#include <linux/netfilter_ipv4/ipt_osf.h>
193 +#define log(x...) printk(KERN_INFO "ipt_osf: " x)
194 +#define loga(x...) printk(x)
196 +#define log(x...) do {} while(0)
197 +#define loga(x...) do {} while(0)
200 +#define FMATCH_WRONG 0
202 +#define FMATCH_OPT_WRONG 2
206 +#define MAXOPTSTRLEN 128
207 +#define OSFFLUSH "FLUSH"
209 +static rwlock_t osf_lock = RW_LOCK_UNLOCKED;
210 +static struct list_head finger_list;
212 +static int match(const struct sk_buff *, const struct net_device *, const struct net_device *,
213 + const void *, int, const void *, u_int16_t, int *);
214 +static int checkentry(const char *, const struct ipt_ip *, void *,
215 + unsigned int, unsigned int);
217 +static struct ipt_match osf_match =
227 +static inline int smart_dec(unsigned long flags, unsigned char ip_ttl, unsigned char f_ttl)
229 + if (flags & IPT_OSF_SMART)
232 + return (ip_ttl == f_ttl);
236 +match(const struct sk_buff *skb,
237 + const struct net_device *in,
238 + const struct net_device *out,
239 + const void *matchinfo,
245 + struct ipt_osf_info *info = (struct ipt_osf_info *)matchinfo;
246 + struct iphdr *ip = skb->nh.iph;
247 + struct tcphdr *tcp;
248 + int fmatch = FMATCH_WRONG, fcount = 0;
249 + unsigned long totlen, optsize = 0, window;
250 + unsigned char df, *optp = NULL, *_optp = NULL;
251 + char check_WSS = 0;
252 + struct list_head *ent;
253 + struct osf_finger *f;
258 + tcp = (struct tcphdr *)((u_int32_t *)ip + ip->ihl);
265 + totlen = ntohs(ip->tot_len);
266 + df = ((ntohs(ip->frag_off) & IP_DF)?1:0);
267 + window = ntohs(tcp->window);
269 + if (tcp->doff*4 > sizeof(struct tcphdr))
271 + _optp = optp = (char *)(tcp+1);
272 + optsize = tcp->doff*4 - sizeof(struct tcphdr);
276 + /* Actually we can create hash/table of all genres and search
277 + * only in appropriate part, but here is initial variant,
278 + * so will use slow path.
280 + read_lock(&osf_lock);
281 + list_for_each(ent, &finger_list)
283 + f = list_entry(ent, struct osf_finger, flist);
285 + if (!(info->flags & IPT_OSF_LOG) && strcmp(info->genre, f->genre))
289 + fmatch = FMATCH_WRONG;
291 + if (totlen == f->ss && df == f->df &&
292 + smart_dec(info->flags, ip->ttl, f->ttl))
294 + unsigned long foptsize;
296 + unsigned short mss = 0;
302 + case 0: check_WSS = 0; break;
303 + case 'S': check_WSS = 1; break;
304 + case 'T': check_WSS = 2; break;
305 + case '%': check_WSS = 3; break;
306 + default: log("Wrong fingerprint wss.wc=%d, %s - %s\n",
307 + f->wss.wc, f->genre, f->details);
311 + if (check_WSS == 4)
314 + /* Check options */
317 + for (optnum=0; optnum<f->opt_num; ++optnum)
318 + foptsize += f->opt[optnum].length;
321 + if (foptsize > MAX_IPOPTLEN || optsize > MAX_IPOPTLEN || optsize != foptsize)
326 + fmatch = FMATCH_OK;
327 + loga("\tYEP : matching without options.\n");
328 + if ((info->flags & IPT_OSF_LOG) &&
329 + info->loglevel == IPT_OSF_LOGLEVEL_FIRST)
336 + for (optnum=0; optnum<f->opt_num; ++optnum)
338 + if (f->opt[optnum].kind == (*optp))
340 + unsigned char len = f->opt[optnum].length;
341 + unsigned char *optend = optp + len;
343 + fmatch = FMATCH_OK;
345 + if (*optp == OSFOPT_MSS) /* MSS */
346 + mss = ntohs(*(unsigned short *)(optp+2));
350 + /* Skip kind and length fields*/
353 + if (f->opt[optnum].wc.wc != 0)
355 + unsigned long tmp = 0;
357 + /* Hmmm... It looks a bit ugly. :) */
358 + memcpy(&tmp, &f->opt[optnum].wc.val,
359 + (len > sizeof(unsigned long)?
360 + sizeof(unsigned long):len));
363 + if (tmp != f->opt[optnum].wc.val)
364 + fmatch = FMATCH_OPT_WRONG;
371 + fmatch = FMATCH_OPT_WRONG;
373 + if (fmatch != FMATCH_OK)
377 + if (fmatch != FMATCH_OPT_WRONG)
379 + fmatch = FMATCH_WRONG;
384 + if (window == f->wss.val)
385 + fmatch = FMATCH_OK;
388 + if (window == f->wss.val*mss)
389 + fmatch = FMATCH_OK;
392 + if (window == f->wss.val*(mss+40))
393 + fmatch = FMATCH_OK;
396 + if (window % f->wss.val == 0)
397 + fmatch = FMATCH_OK;
403 + if (fmatch == FMATCH_OK)
406 + log("%s [%s]: %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n",
407 + f->genre, f->details,
408 + NIPQUAD(ip->saddr), ntohs(tcp->source),
409 + NIPQUAD(ip->daddr), ntohs(tcp->dest));
410 + if ((info->flags & IPT_OSF_LOG) &&
411 + info->loglevel == IPT_OSF_LOGLEVEL_FIRST)
416 + if (!fcount && (info->flags & IPT_OSF_LOG))
418 + log("Unknown: %lu:%d:%d:%lu:", window, ip->ttl, df, totlen);
421 + unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
422 + unsigned int i, optsize;
424 + optsize = tcp->doff * 4 - sizeof(struct tcphdr);
425 + if (skb_copy_bits(skb, ip->ihl*4 + sizeof(struct tcphdr),
429 + for (i = 0; i < optsize; i++)
430 + loga("%02X", opt[i]);
433 + loga(" %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n",
434 + NIPQUAD(ip->saddr), ntohs(tcp->source),
435 + NIPQUAD(ip->daddr), ntohs(tcp->dest));
437 + read_unlock(&osf_lock);
439 + return (fmatch == FMATCH_OK)?1:0;
443 +checkentry(const char *tablename,
444 + const struct ipt_ip *ip,
446 + unsigned int matchsize,
447 + unsigned int hook_mask)
449 + if (matchsize != IPT_ALIGN(sizeof(struct ipt_osf_info)))
451 + if (ip->proto != IPPROTO_TCP)
457 +static char * osf_strchr(char *ptr, char c)
461 + tmp = strchr(ptr, c);
463 + while (tmp && tmp+1 && isspace(*(tmp+1)))
469 +static struct osf_finger * finger_alloc(void)
471 + struct osf_finger *f;
473 + f = kmalloc(sizeof(struct osf_finger), GFP_KERNEL);
475 + memset(f, 0, sizeof(struct osf_finger));
480 +static void finger_free(struct osf_finger *f)
482 + memset(f, 0, sizeof(struct osf_finger));
487 +static void osf_parse_opt(struct osf_opt *opt, int *optnum, char *obuf, int olen)
495 + while (ptr != NULL && i < olen)
504 + ptr = osf_strchr(&obuf[i], OPTDEL);
509 + i += (int)(ptr-&obuf[i]);
517 + ptr = osf_strchr(&obuf[i], OPTDEL);
522 + i += (int)(ptr-&obuf[i]);
530 + ptr = osf_strchr(&obuf[i], OPTDEL);
535 + i += (int)(ptr-&obuf[i]);
543 + ptr = osf_strchr(&obuf[i], OPTDEL);
548 + case '%': wc = '%'; break;
549 + case 'S': wc = 'S'; break;
550 + case 'T': wc = 'T'; break;
551 + default: wc = 0; break;
557 + val = simple_strtoul(&obuf[i+2], NULL, 10);
559 + val = simple_strtoul(&obuf[i+1], NULL, 10);
560 + i += (int)(ptr-&obuf[i]);
568 + ptr = osf_strchr(&obuf[i], OPTDEL);
571 + if (obuf[i+1] == '%')
576 + val = simple_strtoul(&obuf[i+2], NULL, 10);
578 + val = simple_strtoul(&obuf[i+1], NULL, 10);
579 + i += (int)(ptr-&obuf[i]);
587 + ptr = osf_strchr(&obuf[i], OPTDEL);
592 + i += (int)(ptr-&obuf[i]);
599 + ptr = osf_strchr(&obuf[i], OPTDEL);
603 + i += (int)(ptr-&obuf[i]);
611 + opt[*optnum].kind = IANA_opts[op].kind;
612 + opt[*optnum].length = IANA_opts[op].length;
613 + opt[*optnum].wc.wc = wc;
614 + opt[*optnum].wc.val = val;
620 +static int osf_proc_read(char *buf, char **start, off_t off, int count, int *eof, void *data)
622 + struct list_head *ent;
623 + struct osf_finger *f = NULL;
629 + read_lock_bh(&osf_lock);
630 + list_for_each(ent, &finger_list)
632 + f = list_entry(ent, struct osf_finger, flist);
634 + log("%s [%s]", f->genre, f->details);
636 + count += sprintf(buf+count, "%s - %s[%s] : %s",
637 + f->genre, f->version,
638 + f->subtype, f->details);
643 + //count += sprintf(buf+count, " OPT: ");
644 + for (i=0; i<f->opt_num; ++i)
646 + //count += sprintf(buf+count, "%d.%c%lu; ",
647 + // f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', f->opt[i].wc.val);
649 + f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', f->opt[i].wc.val);
653 + count += sprintf(buf+count, "\n");
655 + read_unlock_bh(&osf_lock);
660 +static int osf_proc_write(struct file *file, const char *buffer, unsigned long count, void *data)
664 + char obuf[MAXOPTSTRLEN];
665 + struct osf_finger *finger;
666 + struct list_head *ent, *n;
670 + if (count == strlen(OSFFLUSH) && !strncmp(buffer, OSFFLUSH, strlen(OSFFLUSH)))
673 + write_lock_bh(&osf_lock);
674 + list_for_each_safe(ent, n, &finger_list)
677 + finger = list_entry(ent, struct osf_finger, flist);
678 + list_del(&finger->flist);
679 + finger_free(finger);
681 + write_unlock_bh(&osf_lock);
683 + log("Flushed %d entries.\n", i);
690 + for (i=0; i<count && buffer[i] != '\0'; ++i)
691 + if (buffer[i] == ':')
694 + if (cnt != 8 || i != count)
696 + log("Wrong input line cnt=%d[8], len=%lu[%lu]\n",
701 + memset(obuf, 0, sizeof(obuf));
703 + finger = finger_alloc();
706 + log("Failed to allocate new fingerprint entry.\n");
710 + pbeg = (char *)buffer;
711 + pend = osf_strchr(pbeg, OSFPDEL);
715 + if (pbeg[0] == 'S')
717 + finger->wss.wc = 'S';
718 + if (pbeg[1] == '%')
719 + finger->wss.val = simple_strtoul(pbeg+2, NULL, 10);
720 + else if (pbeg[1] == '*')
721 + finger->wss.val = 0;
723 + finger->wss.val = simple_strtoul(pbeg+1, NULL, 10);
725 + else if (pbeg[0] == 'T')
727 + finger->wss.wc = 'T';
728 + if (pbeg[1] == '%')
729 + finger->wss.val = simple_strtoul(pbeg+2, NULL, 10);
730 + else if (pbeg[1] == '*')
731 + finger->wss.val = 0;
733 + finger->wss.val = simple_strtoul(pbeg+1, NULL, 10);
735 + if (isdigit(pbeg[0]))
737 + finger->wss.wc = 0;
738 + finger->wss.val = simple_strtoul(pbeg, NULL, 10);
743 + pend = osf_strchr(pbeg, OSFPDEL);
747 + finger->ttl = simple_strtoul(pbeg, NULL, 10);
750 + pend = osf_strchr(pbeg, OSFPDEL);
754 + finger->df = simple_strtoul(pbeg, NULL, 10);
757 + pend = osf_strchr(pbeg, OSFPDEL);
761 + finger->ss = simple_strtoul(pbeg, NULL, 10);
765 + pend = osf_strchr(pbeg, OSFPDEL);
769 + cnt = snprintf(obuf, sizeof(obuf), "%s", pbeg);
773 + pend = osf_strchr(pbeg, OSFPDEL);
777 + if (pbeg[0] == '@' || pbeg[0] == '*')
778 + cnt = snprintf(finger->genre, sizeof(finger->genre), "%s", pbeg+1);
780 + cnt = snprintf(finger->genre, sizeof(finger->genre), "%s", pbeg);
784 + pend = osf_strchr(pbeg, OSFPDEL);
788 + cnt = snprintf(finger->version, sizeof(finger->version), "%s", pbeg);
792 + pend = osf_strchr(pbeg, OSFPDEL);
796 + cnt = snprintf(finger->subtype, sizeof(finger->subtype), "%s", pbeg);
800 + cnt = snprintf(finger->details,
801 + ((count - (pbeg - buffer)+1) > MAXDETLEN)?MAXDETLEN:(count - (pbeg - buffer)+1),
804 + log("%s - %s[%s] : %s\n",
805 + finger->genre, finger->version,
806 + finger->subtype, finger->details);
808 + osf_parse_opt(finger->opt, &finger->opt_num, obuf, sizeof(obuf));
811 + write_lock_bh(&osf_lock);
812 + list_add_tail(&finger->flist, &finger_list);
813 + write_unlock_bh(&osf_lock);
818 +static int __init osf_init(void)
821 + struct proc_dir_entry *p;
823 + log("Startng OS fingerprint matching module.\n");
825 + INIT_LIST_HEAD(&finger_list);
827 + err = ipt_register_match(&osf_match);
830 + log("Failed to register OS fingerprint matching module.\n");
834 + p = create_proc_entry("sys/net/ipv4/osf", S_IFREG | 0644, NULL);
837 + ipt_unregister_match(&osf_match);
841 + p->write_proc = osf_proc_write;
842 + p->read_proc = osf_proc_read;
847 +static void __exit osf_fini(void)
849 + struct list_head *ent, *n;
850 + struct osf_finger *f;
852 + remove_proc_entry("sys/net/ipv4/osf", NULL);
853 + ipt_unregister_match(&osf_match);
855 + list_for_each_safe(ent, n, &finger_list)
857 + f = list_entry(ent, struct osf_finger, flist);
858 + list_del(&f->flist);
862 + log("OS fingerprint matching module finished.\n");
865 +module_init(osf_init);
866 +module_exit(osf_fini);
868 +MODULE_LICENSE("GPL");
869 +MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
870 +MODULE_DESCRIPTION("Passive OS fingerprint matching.");
872 diff -Nur linux-2.6.1-rc2.org/net/ipv4/netfilter/Kconfig linux-2.6.1-rc2/net/ipv4/netfilter/Kconfig
873 --- linux-2.6.1-rc2.org/net/ipv4/netfilter/Kconfig 2004-01-07 19:52:02.608067344 +0100
874 +++ linux-2.6.1-rc2/net/ipv4/netfilter/Kconfig 2004-01-07 19:54:12.130376960 +0100
877 Details and examples are in the kernel module source.
879 +config IP_NF_MATCH_OSF
880 + tristate 'OSF match support'
881 + depends on IP_NF_IPTABLES
883 + The idea of passive OS fingerprint matching exists for quite a long time,
884 + but was created as extension fo OpenBSD pf only some weeks ago.
885 + Original idea was lurked in some OpenBSD mailing list (thanks
886 + grange@open...) and than adopted for Linux netfilter in form of this code.
888 + Original table was created by Michal Zalewski <lcamtuf@coredump.cx> for
889 + his excellent p0f and than changed a bit for more convenience.
891 + This module compares some data(WS, MSS, options and it's order, ttl,
892 + df and others) from first SYN packet (actually from packets with SYN
893 + bit set) with hardcoded in fingers[] table ones.
895 + Example: (Of course this only an example, do not get inspired by this)
897 + # iptables -N LINUX
898 + # iptables -A LINUX -j LOG --log-prefix "Linux"
900 + # iptables -A INPUT -p tcp -m osf --genre Linux -j LINUX
901 + # iptables -A INPUT -p tcp -m osf --genre FreeBSD -j REJECT
903 + NOTE: -p tcp is obviously required as it is a TCP match.
907 + If present, OSF will log determined genres even if they don't match
909 + 0 - log all determined entries,
910 + 1 - only first one.
913 + #iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 1 --smart
915 + In syslog you find something like this:
916 + ipt_osf: Windows [Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139
917 + ipt_osf: Unknown: 16384:106:1:48:020405B401010402 44.33.22.11:1239 -> 11.22.33.44:80
920 + if present, OSF will use some smartness to determine remote OS.
921 + Now only not use TTL( with it far remote machines can be determined).
923 + Fingerprints can be loaded through /proc/sys/net/ipv4/osf file.
924 + Only one fingerprint per open/close.
926 + Fingerprints can be downloaded from http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os
930 diff -Nur linux-2.6.1-rc2.org/net/ipv4/netfilter/Makefile linux-2.6.1-rc2/net/ipv4/netfilter/Makefile
931 --- linux-2.6.1-rc2.org/net/ipv4/netfilter/Makefile 2004-01-07 19:52:02.608067344 +0100
932 +++ linux-2.6.1-rc2/net/ipv4/netfilter/Makefile 2004-01-07 19:54:12.131376808 +0100
934 obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o
935 obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o
937 +obj-$(CONFIG_IP_NF_MATCH_OSF) += ipt_osf.o
940 obj-$(CONFIG_IP_NF_MATCH_TIME) += ipt_time.o