aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorEvgeniy Polyakov <zbr@ioremap.net>2009-06-08 11:01:51 -0400
committerPatrick McHardy <kaber@trash.net>2009-06-08 11:01:51 -0400
commit11eeef41d5f63c7d2f7fdfcc733eb7fb137cc384 (patch)
tree6fe29550776dc668b73b81bb2407064049ccd0ef /net
parentf87fb666bb00a7afcbd7992d236e42ac544996f9 (diff)
netfilter: passive OS fingerprint xtables match
Passive OS fingerprinting netfilter module allows to passively detect remote OS 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. Fingerprint matching rules can be downloaded from OpenBSD source tree or found in archive and loaded via netfilter netlink subsystem into the kernel via special util found in archive. Archive contains library file (also attached), which was shipped with iptables extensions some time ago (at least when ipt_osf existed in patch-o-matic). Following changes were made in this release: * added NLM_F_CREATE/NLM_F_EXCL checks * dropped _rcu list traversing helpers in the protected add/remove calls * dropped unneded structures, debug prints, obscure comment and check Fingerprints can be downloaded from http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os or can be found in archive Example usage: -d switch removes fingerprints Please consider for inclusion. Thank you. Passive OS fingerprint homepage (archives, examples): http://www.ioremap.net/projects/osf Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net> Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/Kconfig13
-rw-r--r--net/netfilter/Makefile1
-rw-r--r--net/netfilter/xt_osf.c428
3 files changed, 442 insertions, 0 deletions
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index cb3ad741ebf8..79ba47f042c0 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -917,6 +917,19 @@ config NETFILTER_XT_MATCH_U32
917 917
918 Details and examples are in the kernel module source. 918 Details and examples are in the kernel module source.
919 919
920config NETFILTER_XT_MATCH_OSF
921 tristate '"osf" Passive OS fingerprint match'
922 depends on NETFILTER_ADVANCED && NETFILTER_NETLINK
923 help
924 This option selects the Passive OS Fingerprinting match module
925 that allows to passively match the remote operating system by
926 analyzing incoming TCP SYN packets.
927
928 Rules and loading software can be downloaded from
929 http://www.ioremap.net/projects/osf
930
931 To compile it as a module, choose M here. If unsure, say N.
932
920endif # NETFILTER_XTABLES 933endif # NETFILTER_XTABLES
921 934
922endmenu 935endmenu
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 6282060fbda9..49f62ee4e9ff 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o
77obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o 77obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o
78obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o 78obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o
79obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o 79obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o
80obj-$(CONFIG_NETFILTER_XT_MATCH_OSF) += xt_osf.o
80obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o 81obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o
81obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o 82obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o
82obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o 83obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
new file mode 100644
index 000000000000..863e40977a4d
--- /dev/null
+++ b/net/netfilter/xt_osf.c
@@ -0,0 +1,428 @@
1/*
2 * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
3 *
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#include <linux/module.h>
21#include <linux/kernel.h>
22
23#include <linux/if.h>
24#include <linux/inetdevice.h>
25#include <linux/ip.h>
26#include <linux/list.h>
27#include <linux/rculist.h>
28#include <linux/skbuff.h>
29#include <linux/slab.h>
30#include <linux/tcp.h>
31
32#include <net/ip.h>
33#include <net/tcp.h>
34
35#include <linux/netfilter/nfnetlink.h>
36#include <linux/netfilter/x_tables.h>
37#include <net/netfilter/nf_log.h>
38#include <linux/netfilter/xt_osf.h>
39
40struct xt_osf_finger {
41 struct rcu_head rcu_head;
42 struct list_head finger_entry;
43 struct xt_osf_user_finger finger;
44};
45
46enum osf_fmatch_states {
47 /* Packet does not match the fingerprint */
48 FMATCH_WRONG = 0,
49 /* Packet matches the fingerprint */
50 FMATCH_OK,
51 /* Options do not match the fingerprint, but header does */
52 FMATCH_OPT_WRONG,
53};
54
55/*
56 * Indexed by dont-fragment bit.
57 * It is the only constant value in the fingerprint.
58 */
59static struct list_head xt_osf_fingers[2];
60
61static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = {
62 [OSF_ATTR_FINGER] = { .len = sizeof(struct xt_osf_user_finger) },
63};
64
65static void xt_osf_finger_free_rcu(struct rcu_head *rcu_head)
66{
67 struct xt_osf_finger *f = container_of(rcu_head, struct xt_osf_finger, rcu_head);
68
69 kfree(f);
70}
71
72static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb,
73 struct nlmsghdr *nlh, struct nlattr *osf_attrs[])
74{
75 struct xt_osf_user_finger *f;
76 struct xt_osf_finger *kf = NULL, *sf;
77 int err = 0;
78
79 if (!osf_attrs[OSF_ATTR_FINGER])
80 return -EINVAL;
81
82 if (!(nlh->nlmsg_flags & NLM_F_CREATE))
83 return -EINVAL;
84
85 f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
86
87 kf = kmalloc(sizeof(struct xt_osf_finger), GFP_KERNEL);
88 if (!kf)
89 return -ENOMEM;
90
91 memcpy(&kf->finger, f, sizeof(struct xt_osf_user_finger));
92
93 list_for_each_entry(sf, &xt_osf_fingers[!!f->df], finger_entry) {
94 if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
95 continue;
96
97 kfree(kf);
98 kf = NULL;
99
100 if (nlh->nlmsg_flags & NLM_F_EXCL)
101 err = -EEXIST;
102 break;
103 }
104
105 /*
106 * We are protected by nfnl mutex.
107 */
108 if (kf)
109 list_add_tail_rcu(&kf->finger_entry, &xt_osf_fingers[!!f->df]);
110
111 return err;
112}
113
114static int xt_osf_remove_callback(struct sock *ctnl, struct sk_buff *skb,
115 struct nlmsghdr *nlh, struct nlattr *osf_attrs[])
116{
117 struct xt_osf_user_finger *f;
118 struct xt_osf_finger *sf;
119 int err = ENOENT;
120
121 if (!osf_attrs[OSF_ATTR_FINGER])
122 return -EINVAL;
123
124 f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
125
126 list_for_each_entry(sf, &xt_osf_fingers[!!f->df], finger_entry) {
127 if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
128 continue;
129
130 /*
131 * We are protected by nfnl mutex.
132 */
133 list_del_rcu(&sf->finger_entry);
134 call_rcu(&sf->rcu_head, xt_osf_finger_free_rcu);
135
136 err = 0;
137 break;
138 }
139
140 return err;
141}
142
143static const struct nfnl_callback xt_osf_nfnetlink_callbacks[OSF_MSG_MAX] = {
144 [OSF_MSG_ADD] = {
145 .call = xt_osf_add_callback,
146 .attr_count = OSF_ATTR_MAX,
147 .policy = xt_osf_policy,
148 },
149 [OSF_MSG_REMOVE] = {
150 .call = xt_osf_remove_callback,
151 .attr_count = OSF_ATTR_MAX,
152 .policy = xt_osf_policy,
153 },
154};
155
156static const struct nfnetlink_subsystem xt_osf_nfnetlink = {
157 .name = "osf",
158 .subsys_id = NFNL_SUBSYS_OSF,
159 .cb_count = OSF_MSG_MAX,
160 .cb = xt_osf_nfnetlink_callbacks,
161};
162
163static inline int xt_osf_ttl(const struct sk_buff *skb, const struct xt_osf_info *info,
164 unsigned char f_ttl)
165{
166 const struct iphdr *ip = ip_hdr(skb);
167
168 if (info->flags & XT_OSF_TTL) {
169 if (info->ttl == XT_OSF_TTL_TRUE)
170 return ip->ttl == f_ttl;
171 if (info->ttl == XT_OSF_TTL_NOCHECK)
172 return 1;
173 else if (ip->ttl <= f_ttl)
174 return 1;
175 else {
176 struct in_device *in_dev = __in_dev_get_rcu(skb->dev);
177 int ret = 0;
178
179 for_ifa(in_dev) {
180 if (inet_ifa_match(ip->saddr, ifa)) {
181 ret = (ip->ttl == f_ttl);
182 break;
183 }
184 }
185 endfor_ifa(in_dev);
186
187 return ret;
188 }
189 }
190
191 return ip->ttl == f_ttl;
192}
193
194static bool xt_osf_match_packet(const struct sk_buff *skb,
195 const struct xt_match_param *p)
196{
197 const struct xt_osf_info *info = p->matchinfo;
198 const struct iphdr *ip = ip_hdr(skb);
199 const struct tcphdr *tcp;
200 struct tcphdr _tcph;
201 int fmatch = FMATCH_WRONG, fcount = 0;
202 unsigned int optsize = 0, check_WSS = 0;
203 u16 window, totlen, mss = 0;
204 bool df;
205 const unsigned char *optp = NULL, *_optp = NULL;
206 unsigned char opts[MAX_IPOPTLEN];
207 const struct xt_osf_finger *kf;
208 const struct xt_osf_user_finger *f;
209
210 if (!info)
211 return false;
212
213 tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph);
214 if (!tcp)
215 return false;
216
217 if (!tcp->syn)
218 return false;
219
220 totlen = ntohs(ip->tot_len);
221 df = ntohs(ip->frag_off) & IP_DF;
222 window = ntohs(tcp->window);
223
224 if (tcp->doff * 4 > sizeof(struct tcphdr)) {
225 optsize = tcp->doff * 4 - sizeof(struct tcphdr);
226
227 _optp = optp = skb_header_pointer(skb, ip_hdrlen(skb) +
228 sizeof(struct tcphdr), optsize, opts);
229 }
230
231 rcu_read_lock();
232 list_for_each_entry_rcu(kf, &xt_osf_fingers[df], finger_entry) {
233 f = &kf->finger;
234
235 if (!(info->flags & XT_OSF_LOG) && strcmp(info->genre, f->genre))
236 continue;
237
238 optp = _optp;
239 fmatch = FMATCH_WRONG;
240
241 if (totlen == f->ss && xt_osf_ttl(skb, info, f->ttl)) {
242 int foptsize, optnum;
243
244 /*
245 * Should not happen if userspace parser was written correctly.
246 */
247 if (f->wss.wc >= OSF_WSS_MAX)
248 continue;
249
250 /* Check options */
251
252 foptsize = 0;
253 for (optnum = 0; optnum < f->opt_num; ++optnum)
254 foptsize += f->opt[optnum].length;
255
256 if (foptsize > MAX_IPOPTLEN ||
257 optsize > MAX_IPOPTLEN ||
258 optsize != foptsize)
259 continue;
260
261 check_WSS = f->wss.wc;
262
263 for (optnum = 0; optnum < f->opt_num; ++optnum) {
264 if (f->opt[optnum].kind == (*optp)) {
265 __u32 len = f->opt[optnum].length;
266 const __u8 *optend = optp + len;
267 int loop_cont = 0;
268
269 fmatch = FMATCH_OK;
270
271 switch (*optp) {
272 case OSFOPT_MSS:
273 mss = optp[3];
274 mss <<= 8;
275 mss |= optp[2];
276
277 mss = ntohs(mss);
278 break;
279 case OSFOPT_TS:
280 loop_cont = 1;
281 break;
282 }
283
284 optp = optend;
285 } else
286 fmatch = FMATCH_OPT_WRONG;
287
288 if (fmatch != FMATCH_OK)
289 break;
290 }
291
292 if (fmatch != FMATCH_OPT_WRONG) {
293 fmatch = FMATCH_WRONG;
294
295 switch (check_WSS) {
296 case OSF_WSS_PLAIN:
297 if (f->wss.val == 0 || window == f->wss.val)
298 fmatch = FMATCH_OK;
299 break;
300 case OSF_WSS_MSS:
301 /*
302 * Some smart modems decrease mangle MSS to
303 * SMART_MSS_2, so we check standard, decreased
304 * and the one provided in the fingerprint MSS
305 * values.
306 */
307#define SMART_MSS_1 1460
308#define SMART_MSS_2 1448
309 if (window == f->wss.val * mss ||
310 window == f->wss.val * SMART_MSS_1 ||
311 window == f->wss.val * SMART_MSS_2)
312 fmatch = FMATCH_OK;
313 break;
314 case OSF_WSS_MTU:
315 if (window == f->wss.val * (mss + 40) ||
316 window == f->wss.val * (SMART_MSS_1 + 40) ||
317 window == f->wss.val * (SMART_MSS_2 + 40))
318 fmatch = FMATCH_OK;
319 break;
320 case OSF_WSS_MODULO:
321 if ((window % f->wss.val) == 0)
322 fmatch = FMATCH_OK;
323 break;
324 }
325 }
326
327 if (fmatch != FMATCH_OK)
328 continue;
329
330 fcount++;
331
332 if (info->flags & XT_OSF_LOG)
333 nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
334 "%s [%s:%s] : %pi4:%d -> %pi4:%d hops=%d\n",
335 f->genre, f->version, f->subtype,
336 &ip->saddr, ntohs(tcp->source),
337 &ip->daddr, ntohs(tcp->dest),
338 f->ttl - ip->ttl);
339
340 if ((info->flags & XT_OSF_LOG) &&
341 info->loglevel == XT_OSF_LOGLEVEL_FIRST)
342 break;
343 }
344 }
345 rcu_read_unlock();
346
347 if (!fcount && (info->flags & XT_OSF_LOG))
348 nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
349 "Remote OS is not known: %pi4:%u -> %pi4:%u\n",
350 &ip->saddr, ntohs(tcp->source),
351 &ip->daddr, ntohs(tcp->dest));
352
353 if (fcount)
354 fmatch = FMATCH_OK;
355
356 return fmatch == FMATCH_OK;
357}
358
359static struct xt_match xt_osf_match = {
360 .name = "osf",
361 .revision = 0,
362 .family = NFPROTO_IPV4,
363 .proto = IPPROTO_TCP,
364 .hooks = (1 << NF_INET_LOCAL_IN) |
365 (1 << NF_INET_PRE_ROUTING) |
366 (1 << NF_INET_FORWARD),
367 .match = xt_osf_match_packet,
368 .matchsize = sizeof(struct xt_osf_info),
369 .me = THIS_MODULE,
370};
371
372static int __init xt_osf_init(void)
373{
374 int err = -EINVAL;
375 int i;
376
377 for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i)
378 INIT_LIST_HEAD(&xt_osf_fingers[i]);
379
380 err = nfnetlink_subsys_register(&xt_osf_nfnetlink);
381 if (err < 0) {
382 printk(KERN_ERR "Failed (%d) to register OSF nsfnetlink helper.\n", err);
383 goto err_out_exit;
384 }
385
386 err = xt_register_match(&xt_osf_match);
387 if (err) {
388 printk(KERN_ERR "Failed (%d) to register OS fingerprint "
389 "matching module.\n", err);
390 goto err_out_remove;
391 }
392
393 return 0;
394
395err_out_remove:
396 nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
397err_out_exit:
398 return err;
399}
400
401static void __exit xt_osf_fini(void)
402{
403 struct xt_osf_finger *f;
404 int i;
405
406 nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
407 xt_unregister_match(&xt_osf_match);
408
409 rcu_read_lock();
410 for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
411
412 list_for_each_entry_rcu(f, &xt_osf_fingers[i], finger_entry) {
413 list_del_rcu(&f->finger_entry);
414 call_rcu(&f->rcu_head, xt_osf_finger_free_rcu);
415 }
416 }
417 rcu_read_unlock();
418
419 rcu_barrier();
420}
421
422module_init(xt_osf_init);
423module_exit(xt_osf_fini);
424
425MODULE_LICENSE("GPL");
426MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
427MODULE_DESCRIPTION("Passive OS fingerprint matching.");
428MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF);