Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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, see <http://www.gnu.org/licenses/>.
17 */
18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19#include <linux/module.h>
20#include <linux/kernel.h>
21
22#include <linux/capability.h>
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
40/*
41 * Indexed by dont-fragment bit.
42 * It is the only constant value in the fingerprint.
43 */
44static struct list_head xt_osf_fingers[2];
45
46static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = {
47 [OSF_ATTR_FINGER] = { .len = sizeof(struct xt_osf_user_finger) },
48};
49
50static int xt_osf_add_callback(struct net *net, struct sock *ctnl,
51 struct sk_buff *skb, const struct nlmsghdr *nlh,
52 const struct nlattr * const osf_attrs[],
53 struct netlink_ext_ack *extack)
54{
55 struct xt_osf_user_finger *f;
56 struct xt_osf_finger *kf = NULL, *sf;
57 int err = 0;
58
59 if (!capable(CAP_NET_ADMIN))
60 return -EPERM;
61
62 if (!osf_attrs[OSF_ATTR_FINGER])
63 return -EINVAL;
64
65 if (!(nlh->nlmsg_flags & NLM_F_CREATE))
66 return -EINVAL;
67
68 f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
69
70 kf = kmalloc(sizeof(struct xt_osf_finger), GFP_KERNEL);
71 if (!kf)
72 return -ENOMEM;
73
74 memcpy(&kf->finger, f, sizeof(struct xt_osf_user_finger));
75
76 list_for_each_entry(sf, &xt_osf_fingers[!!f->df], finger_entry) {
77 if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
78 continue;
79
80 kfree(kf);
81 kf = NULL;
82
83 if (nlh->nlmsg_flags & NLM_F_EXCL)
84 err = -EEXIST;
85 break;
86 }
87
88 /*
89 * We are protected by nfnl mutex.
90 */
91 if (kf)
92 list_add_tail_rcu(&kf->finger_entry, &xt_osf_fingers[!!f->df]);
93
94 return err;
95}
96
97static int xt_osf_remove_callback(struct net *net, struct sock *ctnl,
98 struct sk_buff *skb,
99 const struct nlmsghdr *nlh,
100 const struct nlattr * const osf_attrs[],
101 struct netlink_ext_ack *extack)
102{
103 struct xt_osf_user_finger *f;
104 struct xt_osf_finger *sf;
105 int err = -ENOENT;
106
107 if (!capable(CAP_NET_ADMIN))
108 return -EPERM;
109
110 if (!osf_attrs[OSF_ATTR_FINGER])
111 return -EINVAL;
112
113 f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
114
115 list_for_each_entry(sf, &xt_osf_fingers[!!f->df], finger_entry) {
116 if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
117 continue;
118
119 /*
120 * We are protected by nfnl mutex.
121 */
122 list_del_rcu(&sf->finger_entry);
123 kfree_rcu(sf, rcu_head);
124
125 err = 0;
126 break;
127 }
128
129 return err;
130}
131
132static const struct nfnl_callback xt_osf_nfnetlink_callbacks[OSF_MSG_MAX] = {
133 [OSF_MSG_ADD] = {
134 .call = xt_osf_add_callback,
135 .attr_count = OSF_ATTR_MAX,
136 .policy = xt_osf_policy,
137 },
138 [OSF_MSG_REMOVE] = {
139 .call = xt_osf_remove_callback,
140 .attr_count = OSF_ATTR_MAX,
141 .policy = xt_osf_policy,
142 },
143};
144
145static const struct nfnetlink_subsystem xt_osf_nfnetlink = {
146 .name = "osf",
147 .subsys_id = NFNL_SUBSYS_OSF,
148 .cb_count = OSF_MSG_MAX,
149 .cb = xt_osf_nfnetlink_callbacks,
150};
151
152static bool
153xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p)
154{
155 const struct xt_osf_info *info = p->matchinfo;
156 struct net *net = xt_net(p);
157
158 if (!info)
159 return false;
160
161 return nf_osf_match(skb, xt_family(p), xt_hooknum(p), xt_in(p),
162 xt_out(p), info, net, xt_osf_fingers);
163}
164
165static struct xt_match xt_osf_match = {
166 .name = "osf",
167 .revision = 0,
168 .family = NFPROTO_IPV4,
169 .proto = IPPROTO_TCP,
170 .hooks = (1 << NF_INET_LOCAL_IN) |
171 (1 << NF_INET_PRE_ROUTING) |
172 (1 << NF_INET_FORWARD),
173 .match = xt_osf_match_packet,
174 .matchsize = sizeof(struct xt_osf_info),
175 .me = THIS_MODULE,
176};
177
178static int __init xt_osf_init(void)
179{
180 int err = -EINVAL;
181 int i;
182
183 for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i)
184 INIT_LIST_HEAD(&xt_osf_fingers[i]);
185
186 err = nfnetlink_subsys_register(&xt_osf_nfnetlink);
187 if (err < 0) {
188 pr_err("Failed to register OSF nsfnetlink helper (%d)\n", err);
189 goto err_out_exit;
190 }
191
192 err = xt_register_match(&xt_osf_match);
193 if (err) {
194 pr_err("Failed to register OS fingerprint "
195 "matching module (%d)\n", err);
196 goto err_out_remove;
197 }
198
199 return 0;
200
201err_out_remove:
202 nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
203err_out_exit:
204 return err;
205}
206
207static void __exit xt_osf_fini(void)
208{
209 struct xt_osf_finger *f;
210 int i;
211
212 nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
213 xt_unregister_match(&xt_osf_match);
214
215 rcu_read_lock();
216 for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
217
218 list_for_each_entry_rcu(f, &xt_osf_fingers[i], finger_entry) {
219 list_del_rcu(&f->finger_entry);
220 kfree_rcu(f, rcu_head);
221 }
222 }
223 rcu_read_unlock();
224
225 rcu_barrier();
226}
227
228module_init(xt_osf_init);
229module_exit(xt_osf_fini);
230
231MODULE_LICENSE("GPL");
232MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
233MODULE_DESCRIPTION("Passive OS fingerprint matching.");
234MODULE_ALIAS("ipt_osf");
235MODULE_ALIAS("ip6t_osf");
236MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF);