Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The follow patchset contains Netfilter fixes for your net tree,
they are:

1) Fix compilation warning in x_tables with clang due to useless
   redundant reassignment, from Colin Ian King.

2) Add bugtrap to net_exit to catch uninitialized lists, patch
   from Vasily Averin.

3) Fix out of bounds memory reads in H323 conntrack helper, this
   comes with an initial patch to remove replace the obscure
   CHECK_BOUND macro as a dependency. From Eric Sesterhenn.

4) Reduce retransmission timeout when window is 0 in TCP conntrack,
   from Florian Westphal.

6) ctnetlink clamp timeout to INT_MAX if timeout is too large,
   otherwise timeout wraps around and it results in killing the
   entry that is being added immediately.

7) Missing CAP_NET_ADMIN checks in cthelper and xt_osf, due to
   no netns support. From Kevin Cernekee.

8) Missing maximum number of instructions checks in xt_bpf, patch
   from Jann Horn.

9) With no CONFIG_PROC_FS ipt_CLUSTERIP compilation breaks,
   patch from Arnd Bergmann.

10) Missing netlink attribute policy in nftables exthdr, from
    Florian Westphal.

11) Enable conntrack with IPv6 MASQUERADE rules, as a357b3f80b
    should have done in first place, from Konstantin Khlebnikov.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-12-13 14:12:20 -05:00
commit d6da83813f
16 changed files with 170 additions and 38 deletions

View File

@ -373,7 +373,6 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
if (!xt_find_jump_offset(offsets, newpos,
newinfo->number))
return 0;
e = entry0 + newpos;
} else {
/* ... this is a fallthru */
newpos = pos + e->next_offset;

View File

@ -439,7 +439,6 @@ mark_source_chains(const struct xt_table_info *newinfo,
if (!xt_find_jump_offset(offsets, newpos,
newinfo->number))
return 0;
e = entry0 + newpos;
} else {
/* ... this is a fallthru */
newpos = pos + e->next_offset;

View File

@ -813,12 +813,13 @@ static int clusterip_net_init(struct net *net)
static void clusterip_net_exit(struct net *net)
{
#ifdef CONFIG_PROC_FS
struct clusterip_net *cn = net_generic(net, clusterip_net_id);
#ifdef CONFIG_PROC_FS
proc_remove(cn->procdir);
cn->procdir = NULL;
#endif
nf_unregister_net_hook(net, &cip_arp_ops);
WARN_ON_ONCE(!list_empty(&cn->configs));
}
static struct pernet_operations clusterip_net_ops = {

View File

@ -458,7 +458,6 @@ mark_source_chains(const struct xt_table_info *newinfo,
if (!xt_find_jump_offset(offsets, newpos,
newinfo->number))
return 0;
e = entry0 + newpos;
} else {
/* ... this is a fallthru */
newpos = pos + e->next_offset;

View File

@ -33,13 +33,19 @@ static int masquerade_tg6_checkentry(const struct xt_tgchk_param *par)
if (range->flags & NF_NAT_RANGE_MAP_IPS)
return -EINVAL;
return 0;
return nf_ct_netns_get(par->net, par->family);
}
static void masquerade_tg6_destroy(const struct xt_tgdtor_param *par)
{
nf_ct_netns_put(par->net, par->family);
}
static struct xt_target masquerade_tg6_reg __read_mostly = {
.name = "MASQUERADE",
.family = NFPROTO_IPV6,
.checkentry = masquerade_tg6_checkentry,
.destroy = masquerade_tg6_destroy,
.target = masquerade_tg6,
.targetsize = sizeof(struct nf_nat_range),
.table = "nat",

View File

@ -103,7 +103,6 @@ struct bitstr {
#define INC_BIT(bs) if((++(bs)->bit)>7){(bs)->cur++;(bs)->bit=0;}
#define INC_BITS(bs,b) if(((bs)->bit+=(b))>7){(bs)->cur+=(bs)->bit>>3;(bs)->bit&=7;}
#define BYTE_ALIGN(bs) if((bs)->bit){(bs)->cur++;(bs)->bit=0;}
#define CHECK_BOUND(bs,n) if((bs)->cur+(n)>(bs)->end)return(H323_ERROR_BOUND)
static unsigned int get_len(struct bitstr *bs);
static unsigned int get_bit(struct bitstr *bs);
static unsigned int get_bits(struct bitstr *bs, unsigned int b);
@ -165,6 +164,19 @@ static unsigned int get_len(struct bitstr *bs)
return v;
}
static int nf_h323_error_boundary(struct bitstr *bs, size_t bytes, size_t bits)
{
bits += bs->bit;
bytes += bits / BITS_PER_BYTE;
if (bits % BITS_PER_BYTE > 0)
bytes++;
if (*bs->cur + bytes > *bs->end)
return 1;
return 0;
}
/****************************************************************************/
static unsigned int get_bit(struct bitstr *bs)
{
@ -279,8 +291,8 @@ static int decode_bool(struct bitstr *bs, const struct field_t *f,
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
INC_BIT(bs);
CHECK_BOUND(bs, 0);
if (nf_h323_error_boundary(bs, 0, 0))
return H323_ERROR_BOUND;
return H323_ERROR_NONE;
}
@ -293,11 +305,14 @@ static int decode_oid(struct bitstr *bs, const struct field_t *f,
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
BYTE_ALIGN(bs);
CHECK_BOUND(bs, 1);
if (nf_h323_error_boundary(bs, 1, 0))
return H323_ERROR_BOUND;
len = *bs->cur++;
bs->cur += len;
if (nf_h323_error_boundary(bs, 0, 0))
return H323_ERROR_BOUND;
CHECK_BOUND(bs, 0);
return H323_ERROR_NONE;
}
@ -319,6 +334,8 @@ static int decode_int(struct bitstr *bs, const struct field_t *f,
bs->cur += 2;
break;
case CONS: /* 64K < Range < 4G */
if (nf_h323_error_boundary(bs, 0, 2))
return H323_ERROR_BOUND;
len = get_bits(bs, 2) + 1;
BYTE_ALIGN(bs);
if (base && (f->attr & DECODE)) { /* timeToLive */
@ -330,7 +347,8 @@ static int decode_int(struct bitstr *bs, const struct field_t *f,
break;
case UNCO:
BYTE_ALIGN(bs);
CHECK_BOUND(bs, 2);
if (nf_h323_error_boundary(bs, 2, 0))
return H323_ERROR_BOUND;
len = get_len(bs);
bs->cur += len;
break;
@ -341,7 +359,8 @@ static int decode_int(struct bitstr *bs, const struct field_t *f,
PRINT("\n");
CHECK_BOUND(bs, 0);
if (nf_h323_error_boundary(bs, 0, 0))
return H323_ERROR_BOUND;
return H323_ERROR_NONE;
}
@ -357,7 +376,8 @@ static int decode_enum(struct bitstr *bs, const struct field_t *f,
INC_BITS(bs, f->sz);
}
CHECK_BOUND(bs, 0);
if (nf_h323_error_boundary(bs, 0, 0))
return H323_ERROR_BOUND;
return H323_ERROR_NONE;
}
@ -375,12 +395,14 @@ static int decode_bitstr(struct bitstr *bs, const struct field_t *f,
len = f->lb;
break;
case WORD: /* 2-byte length */
CHECK_BOUND(bs, 2);
if (nf_h323_error_boundary(bs, 2, 0))
return H323_ERROR_BOUND;
len = (*bs->cur++) << 8;
len += (*bs->cur++) + f->lb;
break;
case SEMI:
CHECK_BOUND(bs, 2);
if (nf_h323_error_boundary(bs, 2, 0))
return H323_ERROR_BOUND;
len = get_len(bs);
break;
default:
@ -391,7 +413,8 @@ static int decode_bitstr(struct bitstr *bs, const struct field_t *f,
bs->cur += len >> 3;
bs->bit = len & 7;
CHECK_BOUND(bs, 0);
if (nf_h323_error_boundary(bs, 0, 0))
return H323_ERROR_BOUND;
return H323_ERROR_NONE;
}
@ -404,12 +427,15 @@ static int decode_numstr(struct bitstr *bs, const struct field_t *f,
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
/* 2 <= Range <= 255 */
if (nf_h323_error_boundary(bs, 0, f->sz))
return H323_ERROR_BOUND;
len = get_bits(bs, f->sz) + f->lb;
BYTE_ALIGN(bs);
INC_BITS(bs, (len << 2));
CHECK_BOUND(bs, 0);
if (nf_h323_error_boundary(bs, 0, 0))
return H323_ERROR_BOUND;
return H323_ERROR_NONE;
}
@ -440,15 +466,19 @@ static int decode_octstr(struct bitstr *bs, const struct field_t *f,
break;
case BYTE: /* Range == 256 */
BYTE_ALIGN(bs);
CHECK_BOUND(bs, 1);
if (nf_h323_error_boundary(bs, 1, 0))
return H323_ERROR_BOUND;
len = (*bs->cur++) + f->lb;
break;
case SEMI:
BYTE_ALIGN(bs);
CHECK_BOUND(bs, 2);
if (nf_h323_error_boundary(bs, 2, 0))
return H323_ERROR_BOUND;
len = get_len(bs) + f->lb;
break;
default: /* 2 <= Range <= 255 */
if (nf_h323_error_boundary(bs, 0, f->sz))
return H323_ERROR_BOUND;
len = get_bits(bs, f->sz) + f->lb;
BYTE_ALIGN(bs);
break;
@ -458,7 +488,8 @@ static int decode_octstr(struct bitstr *bs, const struct field_t *f,
PRINT("\n");
CHECK_BOUND(bs, 0);
if (nf_h323_error_boundary(bs, 0, 0))
return H323_ERROR_BOUND;
return H323_ERROR_NONE;
}
@ -473,10 +504,13 @@ static int decode_bmpstr(struct bitstr *bs, const struct field_t *f,
switch (f->sz) {
case BYTE: /* Range == 256 */
BYTE_ALIGN(bs);
CHECK_BOUND(bs, 1);
if (nf_h323_error_boundary(bs, 1, 0))
return H323_ERROR_BOUND;
len = (*bs->cur++) + f->lb;
break;
default: /* 2 <= Range <= 255 */
if (nf_h323_error_boundary(bs, 0, f->sz))
return H323_ERROR_BOUND;
len = get_bits(bs, f->sz) + f->lb;
BYTE_ALIGN(bs);
break;
@ -484,7 +518,8 @@ static int decode_bmpstr(struct bitstr *bs, const struct field_t *f,
bs->cur += len << 1;
CHECK_BOUND(bs, 0);
if (nf_h323_error_boundary(bs, 0, 0))
return H323_ERROR_BOUND;
return H323_ERROR_NONE;
}
@ -503,9 +538,13 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f,
base = (base && (f->attr & DECODE)) ? base + f->offset : NULL;
/* Extensible? */
if (nf_h323_error_boundary(bs, 0, 1))
return H323_ERROR_BOUND;
ext = (f->attr & EXT) ? get_bit(bs) : 0;
/* Get fields bitmap */
if (nf_h323_error_boundary(bs, 0, f->sz))
return H323_ERROR_BOUND;
bmp = get_bitmap(bs, f->sz);
if (base)
*(unsigned int *)base = bmp;
@ -525,9 +564,11 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f,
/* Decode */
if (son->attr & OPEN) { /* Open field */
CHECK_BOUND(bs, 2);
if (nf_h323_error_boundary(bs, 2, 0))
return H323_ERROR_BOUND;
len = get_len(bs);
CHECK_BOUND(bs, len);
if (nf_h323_error_boundary(bs, len, 0))
return H323_ERROR_BOUND;
if (!base || !(son->attr & DECODE)) {
PRINT("%*.s%s\n", (level + 1) * TAB_SIZE,
" ", son->name);
@ -555,8 +596,11 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f,
return H323_ERROR_NONE;
/* Get the extension bitmap */
if (nf_h323_error_boundary(bs, 0, 7))
return H323_ERROR_BOUND;
bmp2_len = get_bits(bs, 7) + 1;
CHECK_BOUND(bs, (bmp2_len + 7) >> 3);
if (nf_h323_error_boundary(bs, 0, bmp2_len))
return H323_ERROR_BOUND;
bmp2 = get_bitmap(bs, bmp2_len);
bmp |= bmp2 >> f->sz;
if (base)
@ -567,9 +611,11 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f,
for (opt = 0; opt < bmp2_len; opt++, i++, son++) {
/* Check Range */
if (i >= f->ub) { /* Newer Version? */
CHECK_BOUND(bs, 2);
if (nf_h323_error_boundary(bs, 2, 0))
return H323_ERROR_BOUND;
len = get_len(bs);
CHECK_BOUND(bs, len);
if (nf_h323_error_boundary(bs, len, 0))
return H323_ERROR_BOUND;
bs->cur += len;
continue;
}
@ -583,9 +629,11 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f,
if (!((0x80000000 >> opt) & bmp2)) /* Not present */
continue;
CHECK_BOUND(bs, 2);
if (nf_h323_error_boundary(bs, 2, 0))
return H323_ERROR_BOUND;
len = get_len(bs);
CHECK_BOUND(bs, len);
if (nf_h323_error_boundary(bs, len, 0))
return H323_ERROR_BOUND;
if (!base || !(son->attr & DECODE)) {
PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ",
son->name);
@ -623,22 +671,27 @@ static int decode_seqof(struct bitstr *bs, const struct field_t *f,
switch (f->sz) {
case BYTE:
BYTE_ALIGN(bs);
CHECK_BOUND(bs, 1);
if (nf_h323_error_boundary(bs, 1, 0))
return H323_ERROR_BOUND;
count = *bs->cur++;
break;
case WORD:
BYTE_ALIGN(bs);
CHECK_BOUND(bs, 2);
if (nf_h323_error_boundary(bs, 2, 0))
return H323_ERROR_BOUND;
count = *bs->cur++;
count <<= 8;
count += *bs->cur++;
break;
case SEMI:
BYTE_ALIGN(bs);
CHECK_BOUND(bs, 2);
if (nf_h323_error_boundary(bs, 2, 0))
return H323_ERROR_BOUND;
count = get_len(bs);
break;
default:
if (nf_h323_error_boundary(bs, 0, f->sz))
return H323_ERROR_BOUND;
count = get_bits(bs, f->sz);
break;
}
@ -658,8 +711,11 @@ static int decode_seqof(struct bitstr *bs, const struct field_t *f,
for (i = 0; i < count; i++) {
if (son->attr & OPEN) {
BYTE_ALIGN(bs);
if (nf_h323_error_boundary(bs, 2, 0))
return H323_ERROR_BOUND;
len = get_len(bs);
CHECK_BOUND(bs, len);
if (nf_h323_error_boundary(bs, len, 0))
return H323_ERROR_BOUND;
if (!base || !(son->attr & DECODE)) {
PRINT("%*.s%s\n", (level + 1) * TAB_SIZE,
" ", son->name);
@ -710,11 +766,17 @@ static int decode_choice(struct bitstr *bs, const struct field_t *f,
base = (base && (f->attr & DECODE)) ? base + f->offset : NULL;
/* Decode the choice index number */
if (nf_h323_error_boundary(bs, 0, 1))
return H323_ERROR_BOUND;
if ((f->attr & EXT) && get_bit(bs)) {
ext = 1;
if (nf_h323_error_boundary(bs, 0, 7))
return H323_ERROR_BOUND;
type = get_bits(bs, 7) + f->lb;
} else {
ext = 0;
if (nf_h323_error_boundary(bs, 0, f->sz))
return H323_ERROR_BOUND;
type = get_bits(bs, f->sz);
if (type >= f->lb)
return H323_ERROR_RANGE;
@ -727,8 +789,11 @@ static int decode_choice(struct bitstr *bs, const struct field_t *f,
/* Check Range */
if (type >= f->ub) { /* Newer version? */
BYTE_ALIGN(bs);
if (nf_h323_error_boundary(bs, 2, 0))
return H323_ERROR_BOUND;
len = get_len(bs);
CHECK_BOUND(bs, len);
if (nf_h323_error_boundary(bs, len, 0))
return H323_ERROR_BOUND;
bs->cur += len;
return H323_ERROR_NONE;
}
@ -742,8 +807,11 @@ static int decode_choice(struct bitstr *bs, const struct field_t *f,
if (ext || (son->attr & OPEN)) {
BYTE_ALIGN(bs);
if (nf_h323_error_boundary(bs, len, 0))
return H323_ERROR_BOUND;
len = get_len(bs);
CHECK_BOUND(bs, len);
if (nf_h323_error_boundary(bs, len, 0))
return H323_ERROR_BOUND;
if (!base || !(son->attr & DECODE)) {
PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ",
son->name);

View File

@ -1565,9 +1565,11 @@ static int ctnetlink_change_helper(struct nf_conn *ct,
static int ctnetlink_change_timeout(struct nf_conn *ct,
const struct nlattr * const cda[])
{
u_int32_t timeout = ntohl(nla_get_be32(cda[CTA_TIMEOUT]));
u64 timeout = (u64)ntohl(nla_get_be32(cda[CTA_TIMEOUT])) * HZ;
ct->timeout = nfct_time_stamp + timeout * HZ;
if (timeout > INT_MAX)
timeout = INT_MAX;
ct->timeout = nfct_time_stamp + (u32)timeout;
if (test_bit(IPS_DYING_BIT, &ct->status))
return -ETIME;
@ -1767,6 +1769,7 @@ ctnetlink_create_conntrack(struct net *net,
int err = -EINVAL;
struct nf_conntrack_helper *helper;
struct nf_conn_tstamp *tstamp;
u64 timeout;
ct = nf_conntrack_alloc(net, zone, otuple, rtuple, GFP_ATOMIC);
if (IS_ERR(ct))
@ -1775,7 +1778,10 @@ ctnetlink_create_conntrack(struct net *net,
if (!cda[CTA_TIMEOUT])
goto err1;
ct->timeout = nfct_time_stamp + ntohl(nla_get_be32(cda[CTA_TIMEOUT])) * HZ;
timeout = (u64)ntohl(nla_get_be32(cda[CTA_TIMEOUT])) * HZ;
if (timeout > INT_MAX)
timeout = INT_MAX;
ct->timeout = (u32)timeout + nfct_time_stamp;
rcu_read_lock();
if (cda[CTA_HELP]) {

View File

@ -1039,6 +1039,9 @@ static int tcp_packet(struct nf_conn *ct,
IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED &&
timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK])
timeout = timeouts[TCP_CONNTRACK_UNACK];
else if (ct->proto.tcp.last_win == 0 &&
timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS])
timeout = timeouts[TCP_CONNTRACK_RETRANS];
else
timeout = timeouts[new_state];
spin_unlock_bh(&ct->lock);

View File

@ -5847,6 +5847,12 @@ static int __net_init nf_tables_init_net(struct net *net)
return 0;
}
static void __net_exit nf_tables_exit_net(struct net *net)
{
WARN_ON_ONCE(!list_empty(&net->nft.af_info));
WARN_ON_ONCE(!list_empty(&net->nft.commit_list));
}
int __nft_release_basechain(struct nft_ctx *ctx)
{
struct nft_rule *rule, *nr;
@ -5917,6 +5923,7 @@ static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)
static struct pernet_operations nf_tables_net_ops = {
.init = nf_tables_init_net,
.exit = nf_tables_exit_net,
};
static int __init nf_tables_module_init(void)

View File

@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/capability.h>
#include <net/netlink.h>
#include <net/sock.h>
@ -407,6 +408,9 @@ static int nfnl_cthelper_new(struct net *net, struct sock *nfnl,
struct nfnl_cthelper *nlcth;
int ret = 0;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE])
return -EINVAL;
@ -611,6 +615,9 @@ static int nfnl_cthelper_get(struct net *net, struct sock *nfnl,
struct nfnl_cthelper *nlcth;
bool tuple_set = false;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
.dump = nfnl_cthelper_dump_table,
@ -678,6 +685,9 @@ static int nfnl_cthelper_del(struct net *net, struct sock *nfnl,
struct nfnl_cthelper *nlcth, *n;
int j = 0, ret;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (tb[NFCTH_NAME])
helper_name = nla_data(tb[NFCTH_NAME]);

View File

@ -1093,10 +1093,15 @@ static int __net_init nfnl_log_net_init(struct net *net)
static void __net_exit nfnl_log_net_exit(struct net *net)
{
struct nfnl_log_net *log = nfnl_log_pernet(net);
unsigned int i;
#ifdef CONFIG_PROC_FS
remove_proc_entry("nfnetlink_log", net->nf.proc_netfilter);
#endif
nf_log_unset(net, &nfulnl_logger);
for (i = 0; i < INSTANCE_BUCKETS; i++)
WARN_ON_ONCE(!hlist_empty(&log->instance_table[i]));
}
static struct pernet_operations nfnl_log_net_ops = {

View File

@ -1512,10 +1512,15 @@ static int __net_init nfnl_queue_net_init(struct net *net)
static void __net_exit nfnl_queue_net_exit(struct net *net)
{
struct nfnl_queue_net *q = nfnl_queue_pernet(net);
unsigned int i;
nf_unregister_queue_handler(net);
#ifdef CONFIG_PROC_FS
remove_proc_entry("nfnetlink_queue", net->nf.proc_netfilter);
#endif
for (i = 0; i < INSTANCE_BUCKETS; i++)
WARN_ON_ONCE(!hlist_empty(&q->instance_table[i]));
}
static void nfnl_queue_net_exit_batch(struct list_head *net_exit_list)

View File

@ -214,6 +214,8 @@ static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
[NFTA_EXTHDR_OFFSET] = { .type = NLA_U32 },
[NFTA_EXTHDR_LEN] = { .type = NLA_U32 },
[NFTA_EXTHDR_FLAGS] = { .type = NLA_U32 },
[NFTA_EXTHDR_OP] = { .type = NLA_U32 },
[NFTA_EXTHDR_SREG] = { .type = NLA_U32 },
};
static int nft_exthdr_init(const struct nft_ctx *ctx,

View File

@ -1729,8 +1729,17 @@ static int __net_init xt_net_init(struct net *net)
return 0;
}
static void __net_exit xt_net_exit(struct net *net)
{
int i;
for (i = 0; i < NFPROTO_NUMPROTO; i++)
WARN_ON_ONCE(!list_empty(&net->xt.tables[i]));
}
static struct pernet_operations xt_net_ops = {
.init = xt_net_init,
.exit = xt_net_exit,
};
static int __init xt_init(void)

View File

@ -27,6 +27,9 @@ static int __bpf_mt_check_bytecode(struct sock_filter *insns, __u16 len,
{
struct sock_fprog_kern program;
if (len > XT_BPF_MAX_NUM_INSTR)
return -EINVAL;
program.len = len;
program.filter = insns;
@ -55,6 +58,9 @@ static int __bpf_mt_check_path(const char *path, struct bpf_prog **ret)
mm_segment_t oldfs = get_fs();
int retval, fd;
if (strnlen(path, XT_BPF_PATH_MAX) == XT_BPF_PATH_MAX)
return -EINVAL;
set_fs(KERNEL_DS);
fd = bpf_obj_get_user(path, 0);
set_fs(oldfs);

View File

@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/capability.h>
#include <linux/if.h>
#include <linux/inetdevice.h>
#include <linux/ip.h>
@ -70,6 +71,9 @@ static int xt_osf_add_callback(struct net *net, struct sock *ctnl,
struct xt_osf_finger *kf = NULL, *sf;
int err = 0;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (!osf_attrs[OSF_ATTR_FINGER])
return -EINVAL;
@ -115,6 +119,9 @@ static int xt_osf_remove_callback(struct net *net, struct sock *ctnl,
struct xt_osf_finger *sf;
int err = -ENOENT;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (!osf_attrs[OSF_ATTR_FINGER])
return -EINVAL;