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 */ 44 static struct list_head xt_osf_fingers[2]; 45 46 static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = { 47 [OSF_ATTR_FINGER] = { .len = sizeof(struct xt_osf_user_finger) }, 48 }; 49 50 static 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 97 static 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 132 static 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 145 static 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 152 static bool 153 xt_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 165 static 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 178 static 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 201 err_out_remove: 202 nfnetlink_subsys_unregister(&xt_osf_nfnetlink); 203 err_out_exit: 204 return err; 205 } 206 207 static 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 228 module_init(xt_osf_init); 229 module_exit(xt_osf_fini); 230 231 MODULE_LICENSE("GPL"); 232 MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>"); 233 MODULE_DESCRIPTION("Passive OS fingerprint matching."); 234 MODULE_ALIAS("ipt_osf"); 235 MODULE_ALIAS("ip6t_osf"); 236 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF); 237