History

netfilter: nf_tables: validate variable length element extension

Update template to validate variable length extensions. This patch adds a new .ext_len[id] field to the template to store the expected extension length. This is used to sanity check the initialization of the variable length extension. Use PTR_ERR() in nft_set_elem_init() to report errors since, after this update, there are two reason why this might fail, either because of ENOMEM or insufficient room in the extension field (EINVAL). Kernels up until 7e6bc1f6cabc ("netfilter: nf_tables: stricter validation of element data") allowed to copy more data to the extension than was allocated. This ext_len field allows to validate if the destination has the correct size as additional check.

Pablo Neira Ayuso committed 4 years ago

Commit 34aae2c1 parent b8c3bf03 files · +73 −17Browse files

Unified · Split

3 files
  1. include/net/netfilter/nf_tables.h +3 −1
  2. net/netfilter/nf_tables_api.c +69 −15
  3. net/netfilter/nft_dynset.c +1 −1
include/net/netfilter/nf_tables.h+3−1
@@ -651,6 +651,7 @@ extern const struct nft_set_ext_type nft_set_ext_types[]; · 650 unmodified lines
651struct nft_set_ext_tmpl {651struct nft_set_ext_tmpl {
652 u16 len;652 u16 len;
653 u8 offset[NFT_SET_EXT_NUM];653 u8 offset[NFT_SET_EXT_NUM];
654 u8 ext_len[NFT_SET_EXT_NUM];
654};655};
655 656
656/**657/**
@@ -680,7 +681,8 @@ static inline int nft_set_ext_add_length(struct nft_set_ext_tmpl *tmpl, u8 id, · 23 unmodified lines
680 return -EINVAL;681 return -EINVAL;
681 682
682 tmpl->offset[id] = tmpl->len;683 tmpl->offset[id] = tmpl->len;
683 tmpl->len += nft_set_ext_types[id].len + len;684 tmpl->ext_len[id] = nft_set_ext_types[id].len + len;
685 tmpl->len += tmpl->ext_len[id];
684 686
685 return 0;687 return 0;
686}688}
net/netfilter/nf_tables_api.c+69−15
@@ -5467,6 +5467,27 @@ err_set_elem_expr: · 5466 unmodified lines
5467 return ERR_PTR(err);5467 return ERR_PTR(err);
5468}5468}
5469 5469
5470static int nft_set_ext_check(const struct nft_set_ext_tmpl *tmpl, u8 id, u32 len)
5471{
5472 len += nft_set_ext_types[id].len;
5473 if (len > tmpl->ext_len[id] ||
5474 len > U8_MAX)
5475 return -1;
5476
5477 return 0;
5478}
5479
5480static int nft_set_ext_memcpy(const struct nft_set_ext_tmpl *tmpl, u8 id,
5481 void *to, const void *from, u32 len)
5482{
5483 if (nft_set_ext_check(tmpl, id, len) < 0)
5484 return -1;
5485
5486 memcpy(to, from, len);
5487
5488 return 0;
5489}
5490
5470void *nft_set_elem_init(const struct nft_set *set,5491void *nft_set_elem_init(const struct nft_set *set,
5471 const struct nft_set_ext_tmpl *tmpl,5492 const struct nft_set_ext_tmpl *tmpl,
5472 const u32 *key, const u32 *key_end,5493 const u32 *key, const u32 *key_end,
@@ -5477,17 +5498,26 @@ void *nft_set_elem_init(const struct nft_set *set, · 4 unmodified lines
5477 5498
5478 elem = kzalloc(set->ops->elemsize + tmpl->len, gfp);5499 elem = kzalloc(set->ops->elemsize + tmpl->len, gfp);
5479 if (elem == NULL)5500 if (elem == NULL)
5480 return NULL;5501 return ERR_PTR(-ENOMEM);
5481 5502
5482 ext = nft_set_elem_ext(set, elem);5503 ext = nft_set_elem_ext(set, elem);
5483 nft_set_ext_init(ext, tmpl);5504 nft_set_ext_init(ext, tmpl);
5484 5505
5485 if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY))5506 if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY) &&
5486 memcpy(nft_set_ext_key(ext), key, set->klen);5507 nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY,
5487 if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END))5508 nft_set_ext_key(ext), key, set->klen) < 0)
5488 memcpy(nft_set_ext_key_end(ext), key_end, set->klen);5509 goto err_ext_check;
5489 if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))5510
5490 memcpy(nft_set_ext_data(ext), data, set->dlen);5511 if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END) &&
5512 nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY_END,
5513 nft_set_ext_key_end(ext), key_end, set->klen) < 0)
5514 goto err_ext_check;
5515
5516 if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
5517 nft_set_ext_memcpy(tmpl, NFT_SET_EXT_DATA,
5518 nft_set_ext_data(ext), data, set->dlen) < 0)
5519 goto err_ext_check;
5520
5491 if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {5521 if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
5492 *nft_set_ext_expiration(ext) = get_jiffies_64() + expiration;5522 *nft_set_ext_expiration(ext) = get_jiffies_64() + expiration;
5493 if (expiration == 0)5523 if (expiration == 0)
@@ -5497,6 +5527,11 @@ void *nft_set_elem_init(const struct nft_set *set, · 3 unmodified lines
5497 *nft_set_ext_timeout(ext) = timeout;5527 *nft_set_ext_timeout(ext) = timeout;
5498 5528
5499 return elem;5529 return elem;
5530
5531err_ext_check:
5532 kfree(elem);
5533
5534 return ERR_PTR(-EINVAL);
5500}5535}
5501 5536
5502static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,5537static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
@@ -5584,14 +5619,25 @@ err_expr: · 81 unmodified lines
5584}5619}
5585 5620
5586static int nft_set_elem_expr_setup(struct nft_ctx *ctx,5621static int nft_set_elem_expr_setup(struct nft_ctx *ctx,
5622 const struct nft_set_ext_tmpl *tmpl,
5587 const struct nft_set_ext *ext,5623 const struct nft_set_ext *ext,
5588 struct nft_expr *expr_array[],5624 struct nft_expr *expr_array[],
5589 u32 num_exprs)5625 u32 num_exprs)
5590{5626{
5591 struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);5627 struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
5628 u32 len = sizeof(struct nft_set_elem_expr);
5592 struct nft_expr *expr;5629 struct nft_expr *expr;
5593 int i, err;5630 int i, err;
5594 5631
5632 if (num_exprs == 0)
5633 return 0;
5634
5635 for (i = 0; i < num_exprs; i++)
5636 len += expr_array[i]->ops->size;
5637
5638 if (nft_set_ext_check(tmpl, NFT_SET_EXT_EXPRESSIONS, len) < 0)
5639 return -EINVAL;
5640
5595 for (i = 0; i < num_exprs; i++) {5641 for (i = 0; i < num_exprs; i++) {
5596 expr = nft_setelem_expr_at(elem_expr, elem_expr->size);5642 expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
5597 err = nft_expr_clone(expr, expr_array[i]);5643 err = nft_expr_clone(expr, expr_array[i]);
@@ -6054,17 +6100,23 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, · 456 unmodified lines
6054 }6100 }
6055 }6101 }
6056 6102
6057 err = -ENOMEM;
6058 elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,6103 elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
6059 elem.key_end.val.data, elem.data.val.data,6104 elem.key_end.val.data, elem.data.val.data,
6060 timeout, expiration, GFP_KERNEL_ACCOUNT);6105 timeout, expiration, GFP_KERNEL_ACCOUNT);
6061 if (elem.priv == NULL)6106 if (IS_ERR(elem.priv)) {
6107 err = PTR_ERR(elem.priv);
6062 goto err_parse_data;6108 goto err_parse_data;
6109 }
6063 6110
6064 ext = nft_set_elem_ext(set, elem.priv);6111 ext = nft_set_elem_ext(set, elem.priv);
6065 if (flags)6112 if (flags)
6066 *nft_set_ext_flags(ext) = flags;6113 *nft_set_ext_flags(ext) = flags;
6114
6067 if (ulen > 0) {6115 if (ulen > 0) {
6116 if (nft_set_ext_check(&tmpl, NFT_SET_EXT_USERDATA, ulen) < 0) {
6117 err = -EINVAL;
6118 goto err_elem_userdata;
6119 }
6068 udata = nft_set_ext_userdata(ext);6120 udata = nft_set_ext_userdata(ext);
6069 udata->len = ulen - 1;6121 udata->len = ulen - 1;
6070 nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);6122 nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
@@ -6073,14 +6125,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, · 2 unmodified lines
6073 *nft_set_ext_obj(ext) = obj;6125 *nft_set_ext_obj(ext) = obj;
6074 obj->use++;6126 obj->use++;
6075 }6127 }
6076 err = nft_set_elem_expr_setup(ctx, ext, expr_array, num_exprs);6128 err = nft_set_elem_expr_setup(ctx, &tmpl, ext, expr_array, num_exprs);
6077 if (err < 0)6129 if (err < 0)
6078 goto err_elem_expr;6130 goto err_elem_free;
6079 6131
6080 trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);6132 trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
6081 if (trans == NULL) {6133 if (trans == NULL) {
6082 err = -ENOMEM;6134 err = -ENOMEM;
6083 goto err_elem_expr;6135 goto err_elem_free;
6084 }6136 }
6085 6137
6086 ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;6138 ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
@@ -6126,10 +6178,10 @@ err_set_full: · 39 unmodified lines
6126 nft_setelem_remove(ctx->net, set, &elem);6178 nft_setelem_remove(ctx->net, set, &elem);
6127err_element_clash:6179err_element_clash:
6128 kfree(trans);6180 kfree(trans);
6129err_elem_expr:6181err_elem_free:
6130 if (obj)6182 if (obj)
6131 obj->use--;6183 obj->use--;
6132 6184err_elem_userdata:
6133 nf_tables_set_elem_destroy(ctx, set, elem.priv);6185 nf_tables_set_elem_destroy(ctx, set, elem.priv);
6134err_parse_data:6186err_parse_data:
6135 if (nla[NFTA_SET_ELEM_DATA] != NULL)6187 if (nla[NFTA_SET_ELEM_DATA] != NULL)
@@ -6311,8 +6363,10 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, · 175 unmodified lines
6311 elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,6363 elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
6312 elem.key_end.val.data, NULL, 0, 0,6364 elem.key_end.val.data, NULL, 0, 0,
6313 GFP_KERNEL_ACCOUNT);6365 GFP_KERNEL_ACCOUNT);
6314 if (elem.priv == NULL)6366 if (IS_ERR(elem.priv)) {
6367 err = PTR_ERR(elem.priv);
6315 goto fail_elem_key_end;6368 goto fail_elem_key_end;
6369 }
6316 6370
6317 ext = nft_set_elem_ext(set, elem.priv);6371 ext = nft_set_elem_ext(set, elem.priv);
6318 if (flags)6372 if (flags)
net/netfilter/nft_dynset.c+1−1
@@ -60,7 +60,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr, · 59 unmodified lines
60 &regs->data[priv->sreg_key], NULL,60 &regs->data[priv->sreg_key], NULL,
61 &regs->data[priv->sreg_data],61 &regs->data[priv->sreg_data],
62 timeout, 0, GFP_ATOMIC);62 timeout, 0, GFP_ATOMIC);
63 if (elem == NULL)63 if (IS_ERR(elem))
64 goto err1;64 goto err1;
65 65
66 ext = nft_set_elem_ext(set, elem);66 ext = nft_set_elem_ext(set, elem);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t