[PATCH net-next v3 20/24] ovpn: implement key add/del/swap via netlink
From: Antonio Quartulli <antonio@openvpn.net>
Date: 2024-05-06 01:15:59
Subsystem:
networking drivers, openvpn data channel offload, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Antonio Quartulli, Linus Torvalds
This change introduces the netlink commands needed to add, delete and swap keys for a specific peer. Userspace is expected to use these commands to create, destroy and rotate session keys for a specific peer. Signed-off-by: Antonio Quartulli <antonio@openvpn.net> --- drivers/net/ovpn/netlink.c | 193 ++++++++++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c
index 914b04631ae8..df14988c1f43 100644
--- a/drivers/net/ovpn/netlink.c
+++ b/drivers/net/ovpn/netlink.c@@ -660,19 +660,206 @@ int ovpn_nl_del_peer_doit(struct sk_buff *skb, struct genl_info *info) return ret; } +static int ovpn_nl_get_key_dir(struct genl_info *info, struct nlattr *key, + enum ovpn_cipher_alg cipher, + struct ovpn_key_direction *dir) +{ + struct nlattr *attr, *attrs[OVPN_A_KEYDIR_MAX + 1]; + int ret; + + ret = nla_parse_nested(attrs, OVPN_A_KEYDIR_MAX, key, + ovpn_keydir_nl_policy, info->extack); + if (ret) + return ret; + + switch (cipher) { + case OVPN_CIPHER_ALG_AES_GCM: + case OVPN_CIPHER_ALG_CHACHA20_POLY1305: + attr = attrs[OVPN_A_KEYDIR_CIPHER_KEY]; + if (!attr) + return -EINVAL; + + dir->cipher_key = nla_data(attr); + dir->cipher_key_size = nla_len(attr); + + attr = attrs[OVPN_A_KEYDIR_NONCE_TAIL]; + /* These algorithms require a 96bit nonce, + * Construct it by combining 4-bytes packet id and + * 8-bytes nonce-tail from userspace + */ + if (!attr) + return -EINVAL; + + dir->nonce_tail = nla_data(attr); + dir->nonce_tail_size = nla_len(attr); + break; + default: + return -EINVAL; + } + + return 0; +} + int ovpn_nl_set_key_doit(struct sk_buff *skb, struct genl_info *info) { - return -ENOTSUPP; + struct nlattr *p_attrs[OVPN_A_PEER_MAX + 1]; + struct nlattr *attrs[OVPN_A_KEYCONF_MAX + 1]; + struct ovpn_struct *ovpn = info->user_ptr[0]; + struct ovpn_peer_key_reset pkr; + struct ovpn_peer *peer; + u32 peer_id; + int ret; + + if (GENL_REQ_ATTR_CHECK(info, OVPN_A_PEER)) + return -EINVAL; + + ret = nla_parse_nested(p_attrs, OVPN_A_PEER_MAX, + info->attrs[OVPN_A_PEER], ovpn_peer_nl_policy, + info->extack); + if (ret) + return ret; + + if (NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_PEER], p_attrs, + OVPN_A_PEER_ID) || + NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_PEER], p_attrs, + OVPN_A_PEER_KEYCONF)) + return -EINVAL; + + ret = nla_parse_nested(attrs, OVPN_A_KEYCONF_MAX, + p_attrs[OVPN_A_PEER_KEYCONF], + ovpn_keyconf_nl_policy, info->extack); + if (ret) + return ret; + + if (NL_REQ_ATTR_CHECK(info->extack, p_attrs[OVPN_A_PEER_KEYCONF], attrs, + OVPN_A_KEYCONF_SLOT) || + NL_REQ_ATTR_CHECK(info->extack, p_attrs[OVPN_A_PEER_KEYCONF], attrs, + OVPN_A_KEYCONF_KEY_ID) || + NL_REQ_ATTR_CHECK(info->extack, p_attrs[OVPN_A_PEER_KEYCONF], attrs, + OVPN_A_KEYCONF_CIPHER_ALG) || + NL_REQ_ATTR_CHECK(info->extack, p_attrs[OVPN_A_PEER_KEYCONF], attrs, + OVPN_A_KEYCONF_ENCRYPT_DIR) || + NL_REQ_ATTR_CHECK(info->extack, p_attrs[OVPN_A_PEER_KEYCONF], attrs, + OVPN_A_KEYCONF_DECRYPT_DIR)) + return -EINVAL; + + peer_id = nla_get_u32(p_attrs[OVPN_A_PEER_ID]); + pkr.slot = nla_get_u8(attrs[OVPN_A_KEYCONF_SLOT]); + pkr.key.key_id = nla_get_u16(attrs[OVPN_A_KEYCONF_KEY_ID]); + pkr.key.cipher_alg = nla_get_u16(attrs[OVPN_A_KEYCONF_CIPHER_ALG]); + + ret = ovpn_nl_get_key_dir(info, attrs[OVPN_A_KEYCONF_ENCRYPT_DIR], + pkr.key.cipher_alg, &pkr.key.encrypt); + if (ret < 0) + return ret; + + ret = ovpn_nl_get_key_dir(info, attrs[OVPN_A_KEYCONF_DECRYPT_DIR], + pkr.key.cipher_alg, &pkr.key.decrypt); + if (ret < 0) + return ret; + + peer = ovpn_peer_get_by_id(ovpn, peer_id); + if (!peer) { + netdev_dbg(ovpn->dev, "%s: no peer with id %u to set key for\n", + __func__, peer_id); + return -ENOENT; + } + + mutex_lock(&peer->crypto.mutex); + ret = ovpn_crypto_state_reset(&peer->crypto, &pkr); + if (ret < 0) { + netdev_dbg(ovpn->dev, + "%s: cannot install new key for peer %u\n", __func__, + peer_id); + goto unlock; + } + + netdev_dbg(ovpn->dev, "%s: new key installed (id=%u) for peer %u\n", + __func__, pkr.key.key_id, peer_id); +unlock: + mutex_unlock(&peer->crypto.mutex); + ovpn_peer_put(peer); + return ret; } int ovpn_nl_swap_keys_doit(struct sk_buff *skb, struct genl_info *info) { - return -ENOTSUPP; + struct ovpn_struct *ovpn = info->user_ptr[0]; + struct nlattr *attrs[OVPN_A_PEER + 1]; + struct ovpn_peer *peer; + u32 peer_id; + int ret; + + if (GENL_REQ_ATTR_CHECK(info, OVPN_A_PEER)) + return -EINVAL; + + ret = nla_parse_nested(attrs, OVPN_A_PEER_MAX, info->attrs[OVPN_A_PEER], + ovpn_peer_nl_policy, info->extack); + if (ret) + return ret; + + if (NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_PEER], attrs, + OVPN_A_PEER_ID)) + return -EINVAL; + + peer_id = nla_get_u32(attrs[OVPN_A_PEER_ID]); + + peer = ovpn_peer_get_by_id(ovpn, peer_id); + if (!peer) + return -ENOENT; + + ovpn_crypto_key_slots_swap(&peer->crypto); + ovpn_peer_put(peer); + + return 0; } int ovpn_nl_del_key_doit(struct sk_buff *skb, struct genl_info *info) { - return -ENOTSUPP; + struct nlattr *p_attrs[OVPN_A_PEER_MAX + 1]; + struct nlattr *attrs[OVPN_A_KEYCONF_MAX + 1]; + struct ovpn_struct *ovpn = info->user_ptr[0]; + enum ovpn_key_slot slot; + struct ovpn_peer *peer; + u32 peer_id; + int ret; + + if (GENL_REQ_ATTR_CHECK(info, OVPN_A_PEER)) + return -EINVAL; + + ret = nla_parse_nested(p_attrs, OVPN_A_PEER_MAX, + info->attrs[OVPN_A_PEER], ovpn_peer_nl_policy, + info->extack); + if (ret) + return ret; + + if (NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_PEER], p_attrs, + OVPN_A_PEER_ID) || + NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_PEER], p_attrs, + OVPN_A_PEER_KEYCONF)) + return -EINVAL; + + ret = nla_parse_nested(attrs, OVPN_A_KEYCONF_MAX, + p_attrs[OVPN_A_PEER_KEYCONF], + ovpn_keyconf_nl_policy, info->extack); + if (ret) + return ret; + + if (NL_REQ_ATTR_CHECK(info->extack, p_attrs[OVPN_A_PEER_KEYCONF], attrs, + OVPN_A_KEYCONF_SLOT)) + return -EINVAL; + + peer_id = nla_get_u32(p_attrs[OVPN_A_PEER_ID]); + slot = nla_get_u8(attrs[OVPN_A_KEYCONF_SLOT]); + + peer = ovpn_peer_get_by_id(ovpn, peer_id); + if (!peer) + return -ENOENT; + + ovpn_crypto_key_slot_delete(&peer->crypto, slot); + ovpn_peer_put(peer); + + return 0; } /**
--
2.43.2