1 diff -Nur linux-2.6.1-rc1.org/include/linux/netfilter_ipv4/ipt_osf.h linux-2.6.1-rc1/include/linux/netfilter_ipv4/ipt_osf.h
2 --- linux-2.6.1-rc1.org/include/linux/netfilter_ipv4/ipt_osf.h 1970-01-01 01:00:00.000000000 +0100
3 +++ linux-2.6.1-rc1/include/linux/netfilter_ipv4/ipt_osf.h 2004-01-05 12:41:36.000000000 +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-rc1.org/net/ipv4/netfilter/Kconfig linux-2.6.1-rc1/net/ipv4/netfilter/Kconfig
136 --- linux-2.6.1-rc1.org/net/ipv4/netfilter/Kconfig 2004-01-05 12:31:23.000000000 +0100
137 +++ linux-2.6.1-rc1/net/ipv4/netfilter/Kconfig 2004-01-05 12:41:36.000000000 +0100
140 Details and examples are in the kernel module source.
142 +config IP_NF_MATCH_OSF
143 + tristate 'OSF match support'
144 + depends on IP_NF_IPTABLES
146 + The idea of passive OS fingerprint matching exists for quite a long time,
147 + but was created as extension fo OpenBSD pf only some weeks ago.
148 + Original idea was lurked in some OpenBSD mailing list (thanks
149 + grange@open...) and than adopted for Linux netfilter in form of this code.
151 + Original table was created by Michal Zalewski <lcamtuf@coredump.cx> for
152 + his excellent p0f and than changed a bit for more convenience.
154 + This module compares some data(WS, MSS, options and it's order, ttl,
155 + df and others) from first SYN packet (actually from packets with SYN
156 + bit set) with hardcoded in fingers[] table ones.
158 + Example: (Of course this only an example, do not get inspired by this)
160 + # iptables -N LINUX
161 + # iptables -A LINUX -j LOG --log-prefix "Linux"
163 + # iptables -A INPUT -p tcp -m osf --genre Linux -j LINUX
164 + # iptables -A INPUT -p tcp -m osf --genre FreeBSD -j REJECT
166 + NOTE: -p tcp is obviously required as it is a TCP match.
170 + If present, OSF will log determined genres even if they don't match
172 + 0 - log all determined entries,
173 + 1 - only first one.
176 + #iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 1 --smart
178 + In syslog you find something like this:
179 + ipt_osf: Windows [Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139
180 + ipt_osf: Unknown: 16384:106:1:48:020405B401010402 44.33.22.11:1239 -> 11.22.33.44:80
183 + if present, OSF will use some smartness to determine remote OS.
184 + Now only not use TTL( with it far remote machines can be determined).
186 + Fingerprints can be loaded through /proc/sys/net/ipv4/osf file.
187 + Only one fingerprint per open/close.
189 + Fingerprints can be downloaded from http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os
193 diff -Nur linux-2.6.1-rc1.org/net/ipv4/netfilter/Makefile linux-2.6.1-rc1/net/ipv4/netfilter/Makefile
194 --- linux-2.6.1-rc1.org/net/ipv4/netfilter/Makefile 2004-01-05 12:31:23.000000000 +0100
195 +++ linux-2.6.1-rc1/net/ipv4/netfilter/Makefile 2004-01-05 12:41:36.000000000 +0100
197 obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o
198 obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o
200 +obj-$(CONFIG_IP_NF_MATCH_OSF) += ipt_osf.o
203 obj-$(CONFIG_IP_NF_MATCH_TIME) += ipt_time.o
206 diff -Nur linux-2.6.1-rc1.org/net/ipv4/netfilter/ipt_osf.c linux-2.6.1-rc1/net/ipv4/netfilter/ipt_osf.c
207 --- linux-2.6.1-rc1.org/net/ipv4/netfilter/ipt_osf.c 1970-01-01 01:00:00.000000000 +0100
208 +++ linux-2.6.1-rc1/net/ipv4/netfilter/ipt_osf.c 2004-01-05 12:41:36.000000000 +0100
213 + * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
216 + * This program is free software; you can redistribute it and/or modify
217 + * it under the terms of the GNU General Public License as published by
218 + * the Free Software Foundation; either version 2 of the License, or
219 + * (at your option) any later version.
221 + * This program is distributed in the hope that it will be useful,
222 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
223 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
224 + * GNU General Public License for more details.
226 + * You should have received a copy of the GNU General Public License
227 + * along with this program; if not, write to the Free Software
228 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
232 + * OS fingerprint matching module.
233 + * It simply compares various parameters from SYN packet with
234 + * some hardcoded ones.
236 + * Original table was created by Michal Zalewski <lcamtuf@coredump.cx>
240 +#include <linux/config.h>
241 +#include <linux/kernel.h>
242 +#include <linux/smp.h>
243 +#include <linux/module.h>
244 +#include <linux/skbuff.h>
245 +#include <linux/file.h>
246 +#include <linux/ip.h>
247 +#include <linux/proc_fs.h>
248 +#include <linux/fs.h>
249 +#include <linux/slab.h>
250 +#include <linux/spinlock.h>
251 +#include <linux/ctype.h>
252 +#include <linux/list.h>
254 +#include <net/sock.h>
257 +#include <linux/netfilter_ipv4/ip_tables.h>
259 +#include <linux/netfilter_ipv4/ipt_osf.h>
264 +#define log(x...) printk(KERN_INFO "ipt_osf: " x)
265 +#define loga(x...) printk(x)
267 +#define log(x...) do {} while(0)
268 +#define loga(x...) do {} while(0)
271 +#define FMATCH_WRONG 0
273 +#define FMATCH_OPT_WRONG 2
277 +#define MAXOPTSTRLEN 128
278 +#define OSFFLUSH "FLUSH"
280 +static rwlock_t osf_lock = RW_LOCK_UNLOCKED;
281 +static struct list_head finger_list;
283 +static int match(const struct sk_buff *, const struct net_device *, const struct net_device *,
284 + const void *, int, const void *, u_int16_t, int *);
285 +static int checkentry(const char *, const struct ipt_ip *, void *,
286 + unsigned int, unsigned int);
288 +static struct ipt_match osf_match =
298 +static inline int smart_dec(unsigned long flags, unsigned char ip_ttl, unsigned char f_ttl)
300 + if (flags & IPT_OSF_SMART)
303 + return (ip_ttl == f_ttl);
307 +match(const struct sk_buff *skb,
308 + const struct net_device *in,
309 + const struct net_device *out,
310 + const void *matchinfo,
316 + struct ipt_osf_info *info = (struct ipt_osf_info *)matchinfo;
317 + struct iphdr *ip = skb->nh.iph;
318 + struct tcphdr *tcp;
319 + int fmatch = FMATCH_WRONG, fcount = 0;
320 + unsigned long totlen, optsize = 0, window;
321 + unsigned char df, *optp = NULL, *_optp = NULL;
322 + char check_WSS = 0;
323 + struct list_head *ent;
324 + struct osf_finger *f;
329 + tcp = (struct tcphdr *)((u_int32_t *)ip + ip->ihl);
336 + totlen = ntohs(ip->tot_len);
337 + df = ((ntohs(ip->frag_off) & IP_DF)?1:0);
338 + window = ntohs(tcp->window);
340 + if (tcp->doff*4 > sizeof(struct tcphdr))
342 + _optp = optp = (char *)(tcp+1);
343 + optsize = tcp->doff*4 - sizeof(struct tcphdr);
347 + /* Actually we can create hash/table of all genres and search
348 + * only in appropriate part, but here is initial variant,
349 + * so will use slow path.
351 + read_lock(&osf_lock);
352 + list_for_each(ent, &finger_list)
354 + f = list_entry(ent, struct osf_finger, flist);
356 + if (!(info->flags & IPT_OSF_LOG) && strcmp(info->genre, f->genre))
360 + fmatch = FMATCH_WRONG;
362 + if (totlen == f->ss && df == f->df &&
363 + smart_dec(info->flags, ip->ttl, f->ttl))
365 + unsigned long foptsize;
367 + unsigned short mss = 0;
373 + case 0: check_WSS = 0; break;
374 + case 'S': check_WSS = 1; break;
375 + case 'T': check_WSS = 2; break;
376 + case '%': check_WSS = 3; break;
377 + default: log("Wrong fingerprint wss.wc=%d, %s - %s\n",
378 + f->wss.wc, f->genre, f->details);
382 + if (check_WSS == 4)
385 + /* Check options */
388 + for (optnum=0; optnum<f->opt_num; ++optnum)
389 + foptsize += f->opt[optnum].length;
392 + if (foptsize > MAX_IPOPTLEN || optsize > MAX_IPOPTLEN || optsize != foptsize)
397 + fmatch = FMATCH_OK;
398 + loga("\tYEP : matching without options.\n");
399 + if ((info->flags & IPT_OSF_LOG) &&
400 + info->loglevel == IPT_OSF_LOGLEVEL_FIRST)
407 + for (optnum=0; optnum<f->opt_num; ++optnum)
409 + if (f->opt[optnum].kind == (*optp))
411 + unsigned char len = f->opt[optnum].length;
412 + unsigned char *optend = optp + len;
414 + fmatch = FMATCH_OK;
416 + if (*optp == OSFOPT_MSS) /* MSS */
417 + mss = ntohs(*(unsigned short *)(optp+2));
421 + /* Skip kind and length fields*/
424 + if (f->opt[optnum].wc.wc != 0)
426 + unsigned long tmp = 0;
428 + /* Hmmm... It looks a bit ugly. :) */
429 + memcpy(&tmp, &f->opt[optnum].wc.val,
430 + (len > sizeof(unsigned long)?
431 + sizeof(unsigned long):len));
434 + if (tmp != f->opt[optnum].wc.val)
435 + fmatch = FMATCH_OPT_WRONG;
442 + fmatch = FMATCH_OPT_WRONG;
444 + if (fmatch != FMATCH_OK)
448 + if (fmatch != FMATCH_OPT_WRONG)
450 + fmatch = FMATCH_WRONG;
455 + if (window == f->wss.val)
456 + fmatch = FMATCH_OK;
459 + if (window == f->wss.val*mss)
460 + fmatch = FMATCH_OK;
463 + if (window == f->wss.val*(mss+40))
464 + fmatch = FMATCH_OK;
467 + if (window % f->wss.val == 0)
468 + fmatch = FMATCH_OK;
474 + if (fmatch == FMATCH_OK)
477 + log("%s [%s]: %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n",
478 + f->genre, f->details,
479 + NIPQUAD(ip->saddr), ntohs(tcp->source),
480 + NIPQUAD(ip->daddr), ntohs(tcp->dest));
481 + if ((info->flags & IPT_OSF_LOG) &&
482 + info->loglevel == IPT_OSF_LOGLEVEL_FIRST)
487 + if (!fcount && (info->flags & IPT_OSF_LOG))
489 + log("Unknown: %lu:%d:%d:%lu:", window, ip->ttl, df, totlen);
492 + unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
493 + unsigned int i, optsize;
495 + optsize = tcp->doff * 4 - sizeof(struct tcphdr);
496 + if (skb_copy_bits(skb, ip->ihl*4 + sizeof(struct tcphdr),
500 + for (i = 0; i < optsize; i++)
501 + loga("%02X", opt[i]);
504 + loga(" %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n",
505 + NIPQUAD(ip->saddr), ntohs(tcp->source),
506 + NIPQUAD(ip->daddr), ntohs(tcp->dest));
508 + read_unlock(&osf_lock);
510 + return (fmatch == FMATCH_OK)?1:0;
514 +checkentry(const char *tablename,
515 + const struct ipt_ip *ip,
517 + unsigned int matchsize,
518 + unsigned int hook_mask)
520 + if (matchsize != IPT_ALIGN(sizeof(struct ipt_osf_info)))
522 + if (ip->proto != IPPROTO_TCP)
528 +static char * osf_strchr(char *ptr, char c)
532 + tmp = strchr(ptr, c);
534 + while (tmp && tmp+1 && isspace(*(tmp+1)))
540 +static struct osf_finger * finger_alloc(void)
542 + struct osf_finger *f;
544 + f = kmalloc(sizeof(struct osf_finger), GFP_KERNEL);
546 + memset(f, 0, sizeof(struct osf_finger));
551 +static void finger_free(struct osf_finger *f)
553 + memset(f, 0, sizeof(struct osf_finger));
558 +static void osf_parse_opt(struct osf_opt *opt, int *optnum, char *obuf, int olen)
566 + while (ptr != NULL && i < olen)
575 + ptr = osf_strchr(&obuf[i], OPTDEL);
580 + i += (int)(ptr-&obuf[i]);
588 + ptr = osf_strchr(&obuf[i], OPTDEL);
593 + i += (int)(ptr-&obuf[i]);
601 + ptr = osf_strchr(&obuf[i], OPTDEL);
606 + i += (int)(ptr-&obuf[i]);
614 + ptr = osf_strchr(&obuf[i], OPTDEL);
619 + case '%': wc = '%'; break;
620 + case 'S': wc = 'S'; break;
621 + case 'T': wc = 'T'; break;
622 + default: wc = 0; break;
628 + val = simple_strtoul(&obuf[i+2], NULL, 10);
630 + val = simple_strtoul(&obuf[i+1], NULL, 10);
631 + i += (int)(ptr-&obuf[i]);
639 + ptr = osf_strchr(&obuf[i], OPTDEL);
642 + if (obuf[i+1] == '%')
647 + val = simple_strtoul(&obuf[i+2], NULL, 10);
649 + val = simple_strtoul(&obuf[i+1], NULL, 10);
650 + i += (int)(ptr-&obuf[i]);
658 + ptr = osf_strchr(&obuf[i], OPTDEL);
663 + i += (int)(ptr-&obuf[i]);
670 + ptr = osf_strchr(&obuf[i], OPTDEL);
674 + i += (int)(ptr-&obuf[i]);
682 + opt[*optnum].kind = IANA_opts[op].kind;
683 + opt[*optnum].length = IANA_opts[op].length;
684 + opt[*optnum].wc.wc = wc;
685 + opt[*optnum].wc.val = val;
691 +static int osf_proc_read(char *buf, char **start, off_t off, int count, int *eof, void *data)
693 + struct list_head *ent;
694 + struct osf_finger *f = NULL;
700 + read_lock_bh(&osf_lock);
701 + list_for_each(ent, &finger_list)
703 + f = list_entry(ent, struct osf_finger, flist);
705 + log("%s [%s]", f->genre, f->details);
707 + count += sprintf(buf+count, "%s - %s[%s] : %s",
708 + f->genre, f->version,
709 + f->subtype, f->details);
714 + //count += sprintf(buf+count, " OPT: ");
715 + for (i=0; i<f->opt_num; ++i)
717 + //count += sprintf(buf+count, "%d.%c%lu; ",
718 + // f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', f->opt[i].wc.val);
720 + f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', f->opt[i].wc.val);
724 + count += sprintf(buf+count, "\n");
726 + read_unlock_bh(&osf_lock);
731 +static int osf_proc_write(struct file *file, const char *buffer, unsigned long count, void *data)
735 + char obuf[MAXOPTSTRLEN];
736 + struct osf_finger *finger;
737 + struct list_head *ent, *n;
741 + if (count == strlen(OSFFLUSH) && !strncmp(buffer, OSFFLUSH, strlen(OSFFLUSH)))
744 + write_lock_bh(&osf_lock);
745 + list_for_each_safe(ent, n, &finger_list)
748 + finger = list_entry(ent, struct osf_finger, flist);
749 + list_del(&finger->flist);
750 + finger_free(finger);
752 + write_unlock_bh(&osf_lock);
754 + log("Flushed %d entries.\n", i);
761 + for (i=0; i<count && buffer[i] != '\0'; ++i)
762 + if (buffer[i] == ':')
765 + if (cnt != 8 || i != count)
767 + log("Wrong input line cnt=%d[8], len=%lu[%lu]\n",
772 + memset(obuf, 0, sizeof(obuf));
774 + finger = finger_alloc();
777 + log("Failed to allocate new fingerprint entry.\n");
781 + pbeg = (char *)buffer;
782 + pend = osf_strchr(pbeg, OSFPDEL);
786 + if (pbeg[0] == 'S')
788 + finger->wss.wc = 'S';
789 + if (pbeg[1] == '%')
790 + finger->wss.val = simple_strtoul(pbeg+2, NULL, 10);
791 + else if (pbeg[1] == '*')
792 + finger->wss.val = 0;
794 + finger->wss.val = simple_strtoul(pbeg+1, NULL, 10);
796 + else if (pbeg[0] == 'T')
798 + finger->wss.wc = 'T';
799 + if (pbeg[1] == '%')
800 + finger->wss.val = simple_strtoul(pbeg+2, NULL, 10);
801 + else if (pbeg[1] == '*')
802 + finger->wss.val = 0;
804 + finger->wss.val = simple_strtoul(pbeg+1, NULL, 10);
806 + if (isdigit(pbeg[0]))
808 + finger->wss.wc = 0;
809 + finger->wss.val = simple_strtoul(pbeg, NULL, 10);
814 + pend = osf_strchr(pbeg, OSFPDEL);
818 + finger->ttl = simple_strtoul(pbeg, NULL, 10);
821 + pend = osf_strchr(pbeg, OSFPDEL);
825 + finger->df = simple_strtoul(pbeg, NULL, 10);
828 + pend = osf_strchr(pbeg, OSFPDEL);
832 + finger->ss = simple_strtoul(pbeg, NULL, 10);
836 + pend = osf_strchr(pbeg, OSFPDEL);
840 + cnt = snprintf(obuf, sizeof(obuf), "%s", pbeg);
844 + pend = osf_strchr(pbeg, OSFPDEL);
848 + if (pbeg[0] == '@' || pbeg[0] == '*')
849 + cnt = snprintf(finger->genre, sizeof(finger->genre), "%s", pbeg+1);
851 + cnt = snprintf(finger->genre, sizeof(finger->genre), "%s", pbeg);
855 + pend = osf_strchr(pbeg, OSFPDEL);
859 + cnt = snprintf(finger->version, sizeof(finger->version), "%s", pbeg);
863 + pend = osf_strchr(pbeg, OSFPDEL);
867 + cnt = snprintf(finger->subtype, sizeof(finger->subtype), "%s", pbeg);
871 + cnt = snprintf(finger->details,
872 + ((count - (pbeg - buffer)+1) > MAXDETLEN)?MAXDETLEN:(count - (pbeg - buffer)+1),
875 + log("%s - %s[%s] : %s\n",
876 + finger->genre, finger->version,
877 + finger->subtype, finger->details);
879 + osf_parse_opt(finger->opt, &finger->opt_num, obuf, sizeof(obuf));
882 + write_lock_bh(&osf_lock);
883 + list_add_tail(&finger->flist, &finger_list);
884 + write_unlock_bh(&osf_lock);
889 +static int __init osf_init(void)
892 + struct proc_dir_entry *p;
894 + log("Startng OS fingerprint matching module.\n");
896 + INIT_LIST_HEAD(&finger_list);
898 + err = ipt_register_match(&osf_match);
901 + log("Failed to register OS fingerprint matching module.\n");
905 + p = create_proc_entry("sys/net/ipv4/osf", S_IFREG | 0644, NULL);
908 + ipt_unregister_match(&osf_match);
912 + p->write_proc = osf_proc_write;
913 + p->read_proc = osf_proc_read;
918 +static void __exit osf_fini(void)
920 + struct list_head *ent, *n;
921 + struct osf_finger *f;
923 + remove_proc_entry("sys/net/ipv4/osf", NULL);
924 + ipt_unregister_match(&osf_match);
926 + list_for_each_safe(ent, n, &finger_list)
928 + f = list_entry(ent, struct osf_finger, flist);
929 + list_del(&f->flist);
933 + log("OS fingerprint matching module finished.\n");
936 +module_init(osf_init);
937 +module_exit(osf_fini);
939 +MODULE_LICENSE("GPL");
940 +MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
941 +MODULE_DESCRIPTION("Passive OS fingerprint matching.");