[IPSEC]: Add support for combined mode algorithms
This patch adds support for combined mode algorithms with GCM being the first algorithm supported. Combined mode algorithms can be added through the xfrm_user interface using the new algorithm payload type XFRMA_ALG_AEAD. Each algorithms is identified by its name and the ICV length. For the purposes of matching algorithms in xfrm_tmpl structures, combined mode algorithms occupy the same name space as encryption algorithms. This is in line with how they are negotiated using IKE. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

zatwierdzone przez
David S. Miller

rodzic
6fbf2cb774
commit
1a6509d991
@@ -28,6 +28,105 @@
|
||||
* that instantiated crypto transforms have correct parameters for IPsec
|
||||
* purposes.
|
||||
*/
|
||||
static struct xfrm_algo_desc aead_list[] = {
|
||||
{
|
||||
.name = "rfc4106(gcm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 64,
|
||||
}
|
||||
},
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AES_GCM_ICV8,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "rfc4106(gcm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 96,
|
||||
}
|
||||
},
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AES_GCM_ICV12,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "rfc4106(gcm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AES_GCM_ICV16,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "rfc4309(ccm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 64,
|
||||
}
|
||||
},
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AES_CCM_ICV8,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "rfc4309(ccm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 96,
|
||||
}
|
||||
},
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AES_CCM_ICV12,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "rfc4309(ccm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AES_CCM_ICV16,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static struct xfrm_algo_desc aalg_list[] = {
|
||||
{
|
||||
.name = "hmac(digest_null)",
|
||||
@@ -332,6 +431,11 @@ static struct xfrm_algo_desc calg_list[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static inline int aead_entries(void)
|
||||
{
|
||||
return ARRAY_SIZE(aead_list);
|
||||
}
|
||||
|
||||
static inline int aalg_entries(void)
|
||||
{
|
||||
return ARRAY_SIZE(aalg_list);
|
||||
@@ -354,6 +458,13 @@ struct xfrm_algo_list {
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
static const struct xfrm_algo_list xfrm_aead_list = {
|
||||
.algs = aead_list,
|
||||
.entries = ARRAY_SIZE(aead_list),
|
||||
.type = CRYPTO_ALG_TYPE_AEAD,
|
||||
.mask = CRYPTO_ALG_TYPE_MASK,
|
||||
};
|
||||
|
||||
static const struct xfrm_algo_list xfrm_aalg_list = {
|
||||
.algs = aalg_list,
|
||||
.entries = ARRAY_SIZE(aalg_list),
|
||||
@@ -461,6 +572,33 @@ struct xfrm_algo_desc *xfrm_calg_get_byname(char *name, int probe)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_calg_get_byname);
|
||||
|
||||
struct xfrm_aead_name {
|
||||
const char *name;
|
||||
int icvbits;
|
||||
};
|
||||
|
||||
static int xfrm_aead_name_match(const struct xfrm_algo_desc *entry,
|
||||
const void *data)
|
||||
{
|
||||
const struct xfrm_aead_name *aead = data;
|
||||
const char *name = aead->name;
|
||||
|
||||
return aead->icvbits == entry->uinfo.aead.icv_truncbits && name &&
|
||||
!strcmp(name, entry->name);
|
||||
}
|
||||
|
||||
struct xfrm_algo_desc *xfrm_aead_get_byname(char *name, int icv_len, int probe)
|
||||
{
|
||||
struct xfrm_aead_name data = {
|
||||
.name = name,
|
||||
.icvbits = icv_len,
|
||||
};
|
||||
|
||||
return xfrm_find_algo(&xfrm_aead_list, xfrm_aead_name_match, &data,
|
||||
probe);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_aead_get_byname);
|
||||
|
||||
struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx)
|
||||
{
|
||||
if (idx >= aalg_entries())
|
||||
|
@@ -31,6 +31,11 @@
|
||||
#include <linux/in6.h>
|
||||
#endif
|
||||
|
||||
static inline int aead_len(struct xfrm_algo_aead *alg)
|
||||
{
|
||||
return sizeof(*alg) + ((alg->alg_key_len + 7) / 8);
|
||||
}
|
||||
|
||||
static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
|
||||
{
|
||||
struct nlattr *rt = attrs[type];
|
||||
@@ -68,6 +73,22 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_aead(struct nlattr **attrs)
|
||||
{
|
||||
struct nlattr *rt = attrs[XFRMA_ALG_AEAD];
|
||||
struct xfrm_algo_aead *algp;
|
||||
|
||||
if (!rt)
|
||||
return 0;
|
||||
|
||||
algp = nla_data(rt);
|
||||
if (nla_len(rt) < aead_len(algp))
|
||||
return -EINVAL;
|
||||
|
||||
algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type,
|
||||
xfrm_address_t **addrp)
|
||||
{
|
||||
@@ -119,20 +140,28 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
|
||||
switch (p->id.proto) {
|
||||
case IPPROTO_AH:
|
||||
if (!attrs[XFRMA_ALG_AUTH] ||
|
||||
attrs[XFRMA_ALG_AEAD] ||
|
||||
attrs[XFRMA_ALG_CRYPT] ||
|
||||
attrs[XFRMA_ALG_COMP])
|
||||
goto out;
|
||||
break;
|
||||
|
||||
case IPPROTO_ESP:
|
||||
if ((!attrs[XFRMA_ALG_AUTH] &&
|
||||
!attrs[XFRMA_ALG_CRYPT]) ||
|
||||
attrs[XFRMA_ALG_COMP])
|
||||
if (attrs[XFRMA_ALG_COMP])
|
||||
goto out;
|
||||
if (!attrs[XFRMA_ALG_AUTH] &&
|
||||
!attrs[XFRMA_ALG_CRYPT] &&
|
||||
!attrs[XFRMA_ALG_AEAD])
|
||||
goto out;
|
||||
if ((attrs[XFRMA_ALG_AUTH] ||
|
||||
attrs[XFRMA_ALG_CRYPT]) &&
|
||||
attrs[XFRMA_ALG_AEAD])
|
||||
goto out;
|
||||
break;
|
||||
|
||||
case IPPROTO_COMP:
|
||||
if (!attrs[XFRMA_ALG_COMP] ||
|
||||
attrs[XFRMA_ALG_AEAD] ||
|
||||
attrs[XFRMA_ALG_AUTH] ||
|
||||
attrs[XFRMA_ALG_CRYPT])
|
||||
goto out;
|
||||
@@ -143,6 +172,7 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
|
||||
case IPPROTO_ROUTING:
|
||||
if (attrs[XFRMA_ALG_COMP] ||
|
||||
attrs[XFRMA_ALG_AUTH] ||
|
||||
attrs[XFRMA_ALG_AEAD] ||
|
||||
attrs[XFRMA_ALG_CRYPT] ||
|
||||
attrs[XFRMA_ENCAP] ||
|
||||
attrs[XFRMA_SEC_CTX] ||
|
||||
@@ -155,6 +185,8 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((err = verify_aead(attrs)))
|
||||
goto out;
|
||||
if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH)))
|
||||
goto out;
|
||||
if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT)))
|
||||
@@ -208,6 +240,31 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props,
|
||||
struct nlattr *rta)
|
||||
{
|
||||
struct xfrm_algo_aead *p, *ualg;
|
||||
struct xfrm_algo_desc *algo;
|
||||
|
||||
if (!rta)
|
||||
return 0;
|
||||
|
||||
ualg = nla_data(rta);
|
||||
|
||||
algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1);
|
||||
if (!algo)
|
||||
return -ENOSYS;
|
||||
*props = algo->desc.sadb_alg_id;
|
||||
|
||||
p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
strcpy(p->alg_name, algo->name);
|
||||
*algpp = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx)
|
||||
{
|
||||
int len = 0;
|
||||
@@ -286,6 +343,9 @@ static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
|
||||
|
||||
copy_from_user_state(x, p);
|
||||
|
||||
if ((err = attach_aead(&x->aead, &x->props.ealgo,
|
||||
attrs[XFRMA_ALG_AEAD])))
|
||||
goto error;
|
||||
if ((err = attach_one_algo(&x->aalg, &x->props.aalgo,
|
||||
xfrm_aalg_get_byname,
|
||||
attrs[XFRMA_ALG_AUTH])))
|
||||
@@ -510,6 +570,8 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
|
||||
if (x->lastused)
|
||||
NLA_PUT_U64(skb, XFRMA_LASTUSED, x->lastused);
|
||||
|
||||
if (x->aead)
|
||||
NLA_PUT(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead);
|
||||
if (x->aalg)
|
||||
NLA_PUT(skb, XFRMA_ALG_AUTH, xfrm_alg_len(x->aalg), x->aalg);
|
||||
if (x->ealg)
|
||||
@@ -1808,6 +1870,7 @@ static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
|
||||
#undef XMSGSIZE
|
||||
|
||||
static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
|
||||
[XFRMA_ALG_AEAD] = { .len = sizeof(struct xfrm_algo_aead) },
|
||||
[XFRMA_ALG_AUTH] = { .len = sizeof(struct xfrm_algo) },
|
||||
[XFRMA_ALG_CRYPT] = { .len = sizeof(struct xfrm_algo) },
|
||||
[XFRMA_ALG_COMP] = { .len = sizeof(struct xfrm_algo) },
|
||||
@@ -1972,6 +2035,8 @@ static int xfrm_notify_sa_flush(struct km_event *c)
|
||||
static inline size_t xfrm_sa_len(struct xfrm_state *x)
|
||||
{
|
||||
size_t l = 0;
|
||||
if (x->aead)
|
||||
l += nla_total_size(aead_len(x->aead));
|
||||
if (x->aalg)
|
||||
l += nla_total_size(xfrm_alg_len(x->aalg));
|
||||
if (x->ealg)
|
||||
|
Reference in New Issue
Block a user