Commit 5d7d28e5 authored by David S. Miller's avatar David S. Miller

Merge branch 'tipc-add-more-features-to-TIPC-encryption'

Tuong Lien says:

====================
tipc: add more features to TIPC encryption

This series adds some new features to TIPC encryption:

- Patch 1 ("tipc: optimize key switching time and logic") optimizes the
code and logic in preparation for the following commits.

- Patch 2 ("tipc: introduce encryption master key") introduces support
of 'master key' for authentication of new nodes and key exchange. A
master key can be set/changed by user via netlink (eg. using the same
'tipc node set key' command in iproute2/tipc).

- Patch 3 ("tipc: add automatic session key exchange") allows a session
key to be securely exchanged between nodes as needed.

- Patch 4 ("tipc: add automatic rekeying for encryption key") adds
automatic 'rekeying' of session keys a specific interval. The new key
will be distributed automatically to peer nodes, so become active then.
The rekeying interval is configurable via netlink as well.

v2: update the "tipc: add automatic session key exchange" patch to fix
"implicit declaration" issue when built without "CONFIG_TIPC_CRYPTO".

v3: update the patches according to David comments by using the
"genl_info->extack" for messages in response to netlink user config
requests.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents cb589a55 23700da2
......@@ -254,6 +254,8 @@ static inline int tipc_aead_key_size(struct tipc_aead_key *key)
return sizeof(*key) + key->keylen;
}
#define TIPC_REKEYING_NOW (~0U)
/* The macros and functions below are deprecated:
*/
......
......@@ -165,6 +165,8 @@ enum {
TIPC_NLA_NODE_UP, /* flag */
TIPC_NLA_NODE_ID, /* data */
TIPC_NLA_NODE_KEY, /* data */
TIPC_NLA_NODE_KEY_MASTER, /* flag */
TIPC_NLA_NODE_REKEYING, /* u32 */
__TIPC_NLA_NODE_MAX,
TIPC_NLA_NODE_MAX = __TIPC_NLA_NODE_MAX - 1
......
......@@ -36,22 +36,28 @@
#include <crypto/aead.h>
#include <crypto/aes.h>
#include <crypto/rng.h>
#include "crypto.h"
#include "msg.h"
#include "bcast.h"
#define TIPC_TX_PROBE_LIM msecs_to_jiffies(1000) /* > 1s */
#define TIPC_TX_LASTING_LIM msecs_to_jiffies(120000) /* 2 mins */
#define TIPC_TX_GRACE_PERIOD msecs_to_jiffies(5000) /* 5s */
#define TIPC_TX_LASTING_TIME msecs_to_jiffies(10000) /* 10s */
#define TIPC_RX_ACTIVE_LIM msecs_to_jiffies(3000) /* 3s */
#define TIPC_RX_PASSIVE_LIM msecs_to_jiffies(180000) /* 3 mins */
#define TIPC_RX_PASSIVE_LIM msecs_to_jiffies(15000) /* 15s */
#define TIPC_MAX_TFMS_DEF 10
#define TIPC_MAX_TFMS_LIM 1000
#define TIPC_REKEYING_INTV_DEF (60 * 24) /* default: 1 day */
/**
* TIPC Key ids
*/
enum {
KEY_UNUSED = 0,
KEY_MIN,
KEY_1 = KEY_MIN,
KEY_MASTER = 0,
KEY_MIN = KEY_MASTER,
KEY_1 = 1,
KEY_2,
KEY_3,
KEY_MAX = KEY_3,
......@@ -81,6 +87,8 @@ static const char *hstats[MAX_STATS] = {"ok", "nok", "async", "async_ok",
/* Max TFMs number per key */
int sysctl_tipc_max_tfms __read_mostly = TIPC_MAX_TFMS_DEF;
/* Key exchange switch, default: on */
int sysctl_tipc_key_exchange_enabled __read_mostly = 1;
/**
* struct tipc_key - TIPC keys' status indicator
......@@ -132,6 +140,8 @@ struct tipc_tfm {
* @mode: crypto mode is applied to the key
* @hint[]: a hint for user key
* @rcu: struct rcu_head
* @key: the aead key
* @gen: the key's generation
* @seqno: the key seqno (cluster scope)
* @refcnt: the key reference counter
*/
......@@ -144,8 +154,10 @@ struct tipc_aead {
u32 salt;
u8 authsize;
u8 mode;
char hint[TIPC_AEAD_HINT_LEN + 1];
char hint[2 * TIPC_AEAD_HINT_LEN + 1];
struct rcu_head rcu;
struct tipc_aead_key *key;
u16 gen;
atomic64_t seqno ____cacheline_aligned;
refcount_t refcnt ____cacheline_aligned;
......@@ -165,26 +177,56 @@ struct tipc_crypto_stats {
* @node: TIPC node (RX)
* @aead: array of pointers to AEAD keys for encryption/decryption
* @peer_rx_active: replicated peer RX active key index
* @key_gen: TX/RX key generation
* @key: the key states
* @working: the crypto is working or not
* @skey_mode: session key's mode
* @skey: received session key
* @wq: common workqueue on TX crypto
* @work: delayed work sched for TX/RX
* @key_distr: key distributing state
* @rekeying_intv: rekeying interval (in minutes)
* @stats: the crypto statistics
* @name: the crypto name
* @sndnxt: the per-peer sndnxt (TX)
* @timer1: general timer 1 (jiffies)
* @timer2: general timer 1 (jiffies)
* @timer2: general timer 2 (jiffies)
* @working: the crypto is working or not
* @key_master: flag indicates if master key exists
* @legacy_user: flag indicates if a peer joins w/o master key (for bwd comp.)
* @nokey: no key indication
* @lock: tipc_key lock
*/
struct tipc_crypto {
struct net *net;
struct tipc_node *node;
struct tipc_aead __rcu *aead[KEY_MAX + 1]; /* key[0] is UNUSED */
struct tipc_aead __rcu *aead[KEY_MAX + 1];
atomic_t peer_rx_active;
u16 key_gen;
struct tipc_key key;
u8 working:1;
u8 skey_mode;
struct tipc_aead_key *skey;
struct workqueue_struct *wq;
struct delayed_work work;
#define KEY_DISTR_SCHED 1
#define KEY_DISTR_COMPL 2
atomic_t key_distr;
u32 rekeying_intv;
struct tipc_crypto_stats __percpu *stats;
char name[48];
atomic64_t sndnxt ____cacheline_aligned;
unsigned long timer1;
unsigned long timer2;
union {
struct {
u8 working:1;
u8 key_master:1;
u8 legacy_user:1;
u8 nokey: 1;
};
u8 flags;
};
spinlock_t lock; /* crypto lock */
} ____cacheline_aligned;
......@@ -234,23 +276,35 @@ static inline void tipc_crypto_key_set_state(struct tipc_crypto *c,
u8 new_active,
u8 new_pending);
static int tipc_crypto_key_attach(struct tipc_crypto *c,
struct tipc_aead *aead, u8 pos);
struct tipc_aead *aead, u8 pos,
bool master_key);
static bool tipc_crypto_key_try_align(struct tipc_crypto *rx, u8 new_pending);
static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx,
struct tipc_crypto *rx,
struct sk_buff *skb);
static void tipc_crypto_key_synch(struct tipc_crypto *rx, u8 new_rx_active,
struct tipc_msg *hdr);
struct sk_buff *skb,
u8 tx_key);
static void tipc_crypto_key_synch(struct tipc_crypto *rx, struct sk_buff *skb);
static int tipc_crypto_key_revoke(struct net *net, u8 tx_key);
static inline void tipc_crypto_clone_msg(struct net *net, struct sk_buff *_skb,
struct tipc_bearer *b,
struct tipc_media_addr *dst,
struct tipc_node *__dnode, u8 type);
static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead,
struct tipc_bearer *b,
struct sk_buff **skb, int err);
static void tipc_crypto_do_cmd(struct net *net, int cmd);
static char *tipc_crypto_key_dump(struct tipc_crypto *c, char *buf);
#ifdef TIPC_CRYPTO_DEBUG
static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new,
char *buf);
#endif
static int tipc_crypto_key_xmit(struct net *net, struct tipc_aead_key *skey,
u16 gen, u8 mode, u32 dnode);
static bool tipc_crypto_key_rcv(struct tipc_crypto *rx, struct tipc_msg *hdr);
static void tipc_crypto_work_tx(struct work_struct *work);
static void tipc_crypto_work_rx(struct work_struct *work);
static int tipc_aead_key_generate(struct tipc_aead_key *skey);
#define is_tx(crypto) (!(crypto)->node)
#define is_rx(crypto) (!is_tx(crypto))
#define key_next(cur) ((cur) % KEY_MAX + 1)
......@@ -271,30 +325,55 @@ do { \
/**
* tipc_aead_key_validate - Validate a AEAD user key
*/
int tipc_aead_key_validate(struct tipc_aead_key *ukey)
int tipc_aead_key_validate(struct tipc_aead_key *ukey, struct genl_info *info)
{
int keylen;
/* Check if algorithm exists */
if (unlikely(!crypto_has_alg(ukey->alg_name, 0, 0))) {
pr_info("Not found cipher: \"%s\"!\n", ukey->alg_name);
GENL_SET_ERR_MSG(info, "unable to load the algorithm (module existed?)");
return -ENODEV;
}
/* Currently, we only support the "gcm(aes)" cipher algorithm */
if (strcmp(ukey->alg_name, "gcm(aes)"))
if (strcmp(ukey->alg_name, "gcm(aes)")) {
GENL_SET_ERR_MSG(info, "not supported yet the algorithm");
return -ENOTSUPP;
}
/* Check if key size is correct */
keylen = ukey->keylen - TIPC_AES_GCM_SALT_SIZE;
if (unlikely(keylen != TIPC_AES_GCM_KEY_SIZE_128 &&
keylen != TIPC_AES_GCM_KEY_SIZE_192 &&
keylen != TIPC_AES_GCM_KEY_SIZE_256))
return -EINVAL;
keylen != TIPC_AES_GCM_KEY_SIZE_256)) {
GENL_SET_ERR_MSG(info, "incorrect key length (20, 28 or 36 octets?)");
return -EKEYREJECTED;
}
return 0;
}
/**
* tipc_aead_key_generate - Generate new session key
* @skey: input/output key with new content
*
* Return: 0 in case of success, otherwise < 0
*/
static int tipc_aead_key_generate(struct tipc_aead_key *skey)
{
int rc = 0;
/* Fill the key's content with a random value via RNG cipher */
rc = crypto_get_default_rng();
if (likely(!rc)) {
rc = crypto_rng_get_bytes(crypto_default_rng, skey->key,
skey->keylen);
crypto_put_default_rng();
}
return rc;
}
static struct tipc_aead *tipc_aead_get(struct tipc_aead __rcu *aead)
{
struct tipc_aead *tmp;
......@@ -339,6 +418,7 @@ static void tipc_aead_free(struct rcu_head *rp)
kfree(head);
}
free_percpu(aead->tfm_entry);
kzfree(aead->key);
kfree(aead);
}
......@@ -501,14 +581,15 @@ static int tipc_aead_init(struct tipc_aead **aead, struct tipc_aead_key *ukey,
return err;
}
/* Copy some chars from the user key as a hint */
memcpy(tmp->hint, ukey->key, TIPC_AEAD_HINT_LEN);
tmp->hint[TIPC_AEAD_HINT_LEN] = '\0';
/* Form a hex string of some last bytes as the key's hint */
bin2hex(tmp->hint, ukey->key + keylen - TIPC_AEAD_HINT_LEN,
TIPC_AEAD_HINT_LEN);
/* Initialize the other data */
tmp->mode = mode;
tmp->cloned = NULL;
tmp->authsize = TIPC_AES_GCM_TAG_SIZE;
tmp->key = kmemdup(ukey, tipc_aead_key_size(ukey), GFP_KERNEL);
memcpy(&tmp->salt, ukey->key + keylen, TIPC_AES_GCM_SALT_SIZE);
atomic_set(&tmp->users, 0);
atomic64_set(&tmp->seqno, 0);
......@@ -663,13 +744,11 @@ static int tipc_aead_encrypt(struct tipc_aead *aead, struct sk_buff *skb,
* but there is no frag_list, it should be still fine!
* Otherwise, we must cow it to be a writable buffer with the tailroom.
*/
#ifdef TIPC_CRYPTO_DEBUG
SKB_LINEAR_ASSERT(skb);
if (tailen > skb_tailroom(skb)) {
pr_warn("TX: skb tailroom is not enough: %d, requires: %d\n",
pr_debug("TX(): skb tailroom is not enough: %d, requires: %d\n",
skb_tailroom(skb), tailen);
}
#endif
if (unlikely(!skb_cloned(skb) && tailen <= skb_tailroom(skb))) {
nsg = 1;
......@@ -940,8 +1019,6 @@ bool tipc_ehdr_validate(struct sk_buff *skb)
return false;
if (unlikely(skb->len <= ehsz + TIPC_AES_GCM_TAG_SIZE))
return false;
if (unlikely(!ehdr->tx_key))
return false;
return true;
}
......@@ -994,6 +1071,8 @@ static int tipc_ehdr_build(struct net *net, struct tipc_aead *aead,
ehdr->tx_key = tx_key;
ehdr->destined = (__rx) ? 1 : 0;
ehdr->rx_key_active = (__rx) ? __rx->key.active : 0;
ehdr->rx_nokey = (__rx) ? __rx->nokey : 0;
ehdr->master_key = aead->crypto->key_master;
ehdr->reserved_1 = 0;
ehdr->reserved_2 = 0;
......@@ -1019,23 +1098,16 @@ static inline void tipc_crypto_key_set_state(struct tipc_crypto *c,
u8 new_active,
u8 new_pending)
{
#ifdef TIPC_CRYPTO_DEBUG
struct tipc_key old = c->key;
char buf[32];
#endif
c->key.keys = ((new_passive & KEY_MASK) << (KEY_BITS * 2)) |
((new_active & KEY_MASK) << (KEY_BITS)) |
((new_pending & KEY_MASK));
#ifdef TIPC_CRYPTO_DEBUG
pr_info("%s(%s): key changing %s ::%pS\n",
(c->node) ? "RX" : "TX",
(c->node) ? tipc_node_get_id_str(c->node) :
tipc_own_id_string(c->net),
pr_debug("%s: key changing %s ::%pS\n", c->name,
tipc_key_change_dump(old, c->key, buf),
__builtin_return_address(0));
#endif
}
/**
......@@ -1043,6 +1115,7 @@ static inline void tipc_crypto_key_set_state(struct tipc_crypto *c,
* @c: TIPC crypto to which new key is attached
* @ukey: the user key
* @mode: the key mode (CLUSTER_KEY or PER_NODE_KEY)
* @master_key: specify this is a cluster master key
*
* A new TIPC AEAD key will be allocated and initiated with the specified user
* key, then attached to the TIPC crypto.
......@@ -1050,7 +1123,7 @@ static inline void tipc_crypto_key_set_state(struct tipc_crypto *c,
* Return: new key id in case of success, otherwise: < 0
*/
int tipc_crypto_key_init(struct tipc_crypto *c, struct tipc_aead_key *ukey,
u8 mode)
u8 mode, bool master_key)
{
struct tipc_aead *aead = NULL;
int rc = 0;
......@@ -1060,17 +1133,11 @@ int tipc_crypto_key_init(struct tipc_crypto *c, struct tipc_aead_key *ukey,
/* Attach it to the crypto */
if (likely(!rc)) {
rc = tipc_crypto_key_attach(c, aead, 0);
rc = tipc_crypto_key_attach(c, aead, 0, master_key);
if (rc < 0)
tipc_aead_free(&aead->rcu);
}
pr_info("%s(%s): key initiating, rc %d!\n",
(c->node) ? "RX" : "TX",
(c->node) ? tipc_node_get_id_str(c->node) :
tipc_own_id_string(c->net),
rc);
return rc;
}
......@@ -1079,58 +1146,58 @@ int tipc_crypto_key_init(struct tipc_crypto *c, struct tipc_aead_key *ukey,
* @c: TIPC crypto to which the new AEAD key is attached
* @aead: the new AEAD key pointer
* @pos: desired slot in the crypto key array, = 0 if any!
* @master_key: specify this is a cluster master key
*
* Return: new key id in case of success, otherwise: -EBUSY
*/
static int tipc_crypto_key_attach(struct tipc_crypto *c,
struct tipc_aead *aead, u8 pos)
struct tipc_aead *aead, u8 pos,
bool master_key)
{
u8 new_pending, new_passive, new_key;
struct tipc_key key;
int rc = -EBUSY;
u8 new_key;
spin_lock_bh(&c->lock);
key = c->key;
if (master_key) {
new_key = KEY_MASTER;
goto attach;
}
if (key.active && key.passive)
goto exit;
if (key.passive && !tipc_aead_users(c->aead[key.passive]))
goto exit;
if (key.pending) {
if (pos)
goto exit;
if (tipc_aead_users(c->aead[key.pending]) > 0)
goto exit;
/* if (pos): ok with replacing, will be aligned when needed */
/* Replace it */
new_pending = key.pending;
new_passive = key.passive;
new_key = new_pending;
new_key = key.pending;
} else {
if (pos) {
if (key.active && pos != key_next(key.active)) {
new_pending = key.pending;
new_passive = pos;
new_key = new_passive;
key.passive = pos;
new_key = pos;
goto attach;
} else if (!key.active && !key.passive) {
new_pending = pos;
new_passive = key.passive;
new_key = new_pending;
key.pending = pos;
new_key = pos;
goto attach;
}
}
new_pending = key_next(key.active ?: key.passive);
new_passive = key.passive;
new_key = new_pending;
key.pending = key_next(key.active ?: key.passive);
new_key = key.pending;
}
attach:
aead->crypto = c;
tipc_crypto_key_set_state(c, new_passive, key.active, new_pending);
aead->gen = (is_tx(c)) ? ++c->key_gen : c->key_gen;
tipc_aead_rcu_replace(c->aead[new_key], aead, &c->lock);
if (likely(c->key.keys != key.keys))
tipc_crypto_key_set_state(c, key.passive, key.active,
key.pending);
c->working = 1;
c->timer1 = jiffies;
c->timer2 = jiffies;
c->nokey = 0;
c->key_master |= master_key;
rc = new_key;
exit:
......@@ -1140,14 +1207,33 @@ static int tipc_crypto_key_attach(struct tipc_crypto *c,
void tipc_crypto_key_flush(struct tipc_crypto *c)
{
struct tipc_crypto *tx, *rx;
int k;
spin_lock_bh(&c->lock);
c->working = 0;
if (is_rx(c)) {
/* Try to cancel pending work */
rx = c;
tx = tipc_net(rx->net)->crypto_tx;
if (cancel_delayed_work(&rx->work)) {
kfree(rx->skey);
rx->skey = NULL;
atomic_xchg(&rx->key_distr, 0);
tipc_node_put(rx->node);
}
/* RX stopping => decrease TX key users if any */
k = atomic_xchg(&rx->peer_rx_active, 0);
if (k) {
tipc_aead_users_dec(tx->aead[k], 0);
/* Mark the point TX key users changed */
tx->timer1 = jiffies;
}
}
c->flags = 0;
tipc_crypto_key_set_state(c, 0, 0, 0);
for (k = KEY_MIN; k <= KEY_MAX; k++)
tipc_crypto_key_detach(c->aead[k], &c->lock);
atomic_set(&c->peer_rx_active, 0);
atomic64_set(&c->sndnxt, 0);
spin_unlock_bh(&c->lock);
}
......@@ -1206,7 +1292,8 @@ static bool tipc_crypto_key_try_align(struct tipc_crypto *rx, u8 new_pending)
rcu_assign_pointer(rx->aead[new_passive], tmp2);
refcount_set(&tmp1->refcnt, 1);
aligned = true;
pr_info("RX(%s): key is aligned!\n", tipc_node_get_id_str(rx->node));
pr_info_ratelimited("%s: key[%d] -> key[%d]\n", rx->name, key.pending,
new_pending);
exit:
spin_unlock(&rx->lock);
......@@ -1218,6 +1305,7 @@ static bool tipc_crypto_key_try_align(struct tipc_crypto *rx, u8 new_pending)
* @tx: TX crypto handle
* @rx: RX crypto handle (can be NULL)
* @skb: the message skb which will be decrypted later
* @tx_key: peer TX key id
*
* This function looks up the existing TX keys and pick one which is suitable
* for the message decryption, that must be a cluster key and not used before
......@@ -1227,7 +1315,8 @@ static bool tipc_crypto_key_try_align(struct tipc_crypto *rx, u8 new_pending)
*/
static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx,
struct tipc_crypto *rx,
struct sk_buff *skb)
struct sk_buff *skb,
u8 tx_key)
{
struct tipc_skb_cb *skb_cb = TIPC_SKB_CB(skb);
struct tipc_aead *aead = NULL;
......@@ -1246,6 +1335,10 @@ static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx,
/* Pick one TX key */
spin_lock(&tx->lock);
if (tx_key == KEY_MASTER) {
aead = tipc_aead_rcu_ptr(tx->aead[KEY_MASTER], &tx->lock);
goto done;
}
do {
k = (i == 0) ? key.pending :
((i == 1) ? key.active : key.passive);
......@@ -1265,9 +1358,12 @@ static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx,
skb->next = skb_clone(skb, GFP_ATOMIC);
if (unlikely(!skb->next))
pr_warn("Failed to clone skb for next round if any\n");
WARN_ON(!refcount_inc_not_zero(&aead->refcnt));
break;
} while (++i < 3);
done:
if (likely(aead))
WARN_ON(!refcount_inc_not_zero(&aead->refcnt));
spin_unlock(&tx->lock);
return aead;
......@@ -1276,53 +1372,73 @@ static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx,
/**
* tipc_crypto_key_synch: Synch own key data according to peer key status
* @rx: RX crypto handle
* @new_rx_active: latest RX active key from peer
* @hdr: TIPCv2 message
* @skb: TIPCv2 message buffer (incl. the ehdr from peer)
*
* This function updates the peer node related data as the peer RX active key
* has changed, so the number of TX keys' users on this node are increased and
* decreased correspondingly.
*
* It also considers if peer has no key, then we need to make own master key
* (if any) taking over i.e. starting grace period and also trigger key
* distributing process.
*
* The "per-peer" sndnxt is also reset when the peer key has switched.
*/
static void tipc_crypto_key_synch(struct tipc_crypto *rx, u8 new_rx_active,
struct tipc_msg *hdr)
static void tipc_crypto_key_synch(struct tipc_crypto *rx, struct sk_buff *skb)
{
struct net *net = rx->net;
struct tipc_crypto *tx = tipc_net(net)->crypto_tx;
u8 cur_rx_active;
struct tipc_ehdr *ehdr = (struct tipc_ehdr *)skb_network_header(skb);
struct tipc_crypto *tx = tipc_net(rx->net)->crypto_tx;
struct tipc_msg *hdr = buf_msg(skb);
u32 self = tipc_own_addr(rx->net);
u8 cur, new;
unsigned long delay;
/* TX might be even not ready yet */
if (unlikely(!tx->key.active && !tx->key.pending))
return;
/* Update RX 'key_master' flag according to peer, also mark "legacy" if
* a peer has no master key.
*/
rx->key_master = ehdr->master_key;
if (!rx->key_master)
tx->legacy_user = 1;
cur_rx_active = atomic_read(&rx->peer_rx_active);
if (likely(cur_rx_active == new_rx_active))
/* For later cases, apply only if message is destined to this node */
if (!ehdr->destined || msg_short(hdr) || msg_destnode(hdr) != self)
return;
/* Make sure this message destined for this node */
if (unlikely(msg_short(hdr) ||
msg_destnode(hdr) != tipc_own_addr(net)))
return;
/* Case 1: Peer has no keys, let's make master key take over */
if (ehdr->rx_nokey) {
/* Set or extend grace period */
tx->timer2 = jiffies;
/* Schedule key distributing for the peer if not yet */
if (tx->key.keys &&
!atomic_cmpxchg(&rx->key_distr, 0, KEY_DISTR_SCHED)) {
get_random_bytes(&delay, 2);
delay %= 5;
delay = msecs_to_jiffies(500 * ++delay);
if (queue_delayed_work(tx->wq, &rx->work, delay))
tipc_node_get(rx->node);
}
} else {
/* Cancel a pending key distributing if any */
atomic_xchg(&rx->key_distr, 0);
}
/* Peer RX active key has changed, try to update owns' & TX users */
if (atomic_cmpxchg(&rx->peer_rx_active,
cur_rx_active,
new_rx_active) == cur_rx_active) {
if (new_rx_active)
tipc_aead_users_inc(tx->aead[new_rx_active], INT_MAX);
if (cur_rx_active)
tipc_aead_users_dec(tx->aead[cur_rx_active], 0);
/* Case 2: Peer RX active key has changed, let's update own TX users */
cur = atomic_read(&rx->peer_rx_active);
new = ehdr->rx_key_active;
if (tx->key.keys &&
cur != new &&
atomic_cmpxchg(&rx->peer_rx_active, cur, new) == cur) {
if (new)
tipc_aead_users_inc(tx->aead[new], INT_MAX);
if (cur)
tipc_aead_users_dec(tx->aead[cur], 0);
atomic64_set(&rx->sndnxt, 0);
/* Mark the point TX key users changed */
tx->timer1 = jiffies;
#ifdef TIPC_CRYPTO_DEBUG
pr_info("TX(%s): key users changed %d-- %d++, peer RX(%s)\n",
tipc_own_id_string(net), cur_rx_active,
new_rx_active, tipc_node_get_id_str(rx->node));
#endif
pr_debug("%s: key users changed %d-- %d++, peer %s\n",
tx->name, cur, new, rx->name);
}
}
......@@ -1340,7 +1456,7 @@ static int tipc_crypto_key_revoke(struct net *net, u8 tx_key)
tipc_crypto_key_detach(tx->aead[key.active], &tx->lock);
spin_unlock(&tx->lock);
pr_warn("TX(%s): key is revoked!\n", tipc_own_id_string(net));
pr_warn("%s: key is revoked\n", tx->name);
return -EKEYREVOKED;
}
......@@ -1357,6 +1473,15 @@ int tipc_crypto_start(struct tipc_crypto **crypto, struct net *net,
if (!c)
return -ENOMEM;
/* Allocate workqueue on TX */
if (!node) {
c->wq = alloc_ordered_workqueue("tipc_crypto", 0);
if (!c->wq) {
kfree(c);
return -ENOMEM;
}
}
/* Allocate statistic structure */
c->stats = alloc_percpu_gfp(struct tipc_crypto_stats, GFP_ATOMIC);
if (!c->stats) {
......@@ -1364,53 +1489,52 @@ int tipc_crypto_start(struct tipc_crypto **crypto, struct net *net,
return -ENOMEM;
}
c->working = 0;
c->flags = 0;
c->net = net;
c->node = node;
get_random_bytes(&c->key_gen, 2);
tipc_crypto_key_set_state(c, 0, 0, 0);
atomic_set(&c->key_distr, 0);
atomic_set(&c->peer_rx_active, 0);
atomic64_set(&c->sndnxt, 0);
c->timer1 = jiffies;
c->timer2 = jiffies;
c->rekeying_intv = TIPC_REKEYING_INTV_DEF;
spin_lock_init(&c->lock);
*crypto = c;
scnprintf(c->name, 48, "%s(%s)", (is_rx(c)) ? "RX" : "TX",
(is_rx(c)) ? tipc_node_get_id_str(c->node) :
tipc_own_id_string(c->net));
if (is_rx(c))
INIT_DELAYED_WORK(&c->work, tipc_crypto_work_rx);
else
INIT_DELAYED_WORK(&c->work, tipc_crypto_work_tx);
*crypto = c;
return 0;
}
void tipc_crypto_stop(struct tipc_crypto **crypto)
{
struct tipc_crypto *c, *tx, *rx;
bool is_rx;
struct tipc_crypto *c = *crypto;
u8 k;
if (!*crypto)
if (!c)
return;
rcu_read_lock();
/* RX stopping? => decrease TX key users if any */
is_rx = !!((*crypto)->node);
if (is_rx) {
rx = *crypto;
tx = tipc_net(rx->net)->crypto_tx;
k = atomic_read(&rx->peer_rx_active);
if (k) {
tipc_aead_users_dec(tx->aead[k], 0);
/* Mark the point TX key users changed */
tx->timer1 = jiffies;
}
/* Flush any queued works & destroy wq */
if (is_tx(c)) {
c->rekeying_intv = 0;
cancel_delayed_work_sync(&c->work);
destroy_workqueue(c->wq);
}
/* Release AEAD keys */
c = *crypto;
rcu_read_lock();
for (k = KEY_MIN; k <= KEY_MAX; k++)
tipc_aead_put(rcu_dereference(c->aead[k]));
rcu_read_unlock();
pr_warn("%s(%s) has been purged, node left!\n",
(is_rx) ? "RX" : "TX",
(is_rx) ? tipc_node_get_id_str((*crypto)->node) :
tipc_own_id_string((*crypto)->net));
pr_debug("%s: has been stopped\n", c->name);
/* Free this crypto statistics */
free_percpu(c->stats);
......@@ -1424,106 +1548,91 @@ void tipc_crypto_timeout(struct tipc_crypto *rx)
struct tipc_net *tn = tipc_net(rx->net);
struct tipc_crypto *tx = tn->crypto_tx;
struct tipc_key key;
u8 new_pending, new_passive;
int cmd;
/* TX key activating:
* The pending key (users > 0) -> active
* The active key if any (users == 0) -> free
*/
/* TX pending: taking all users & stable -> active */
spin_lock(&tx->lock);
key = tx->key;
if (key.active && tipc_aead_users(tx->aead[key.active]) > 0)
goto s1;
if (!key.pending || tipc_aead_users(tx->aead[key.pending]) <= 0)
goto s1;
if (time_before(jiffies, tx->timer1 + TIPC_TX_LASTING_LIM))
if (time_before(jiffies, tx->timer1 + TIPC_TX_LASTING_TIME))
goto s1;
tipc_crypto_key_set_state(tx, key.passive, key.pending, 0);
if (key.active)
tipc_crypto_key_detach(tx->aead[key.active], &tx->lock);
this_cpu_inc(tx->stats->stat[STAT_SWITCHES]);
pr_info("TX(%s): key %d is activated!\n", tipc_own_id_string(tx->net),
key.pending);
pr_info("%s: key[%d] is activated\n", tx->name, key.pending);
s1:
spin_unlock(&tx->lock);
/* RX key activating:
* The pending key (users > 0) -> active
* The active key if any -> passive, freed later
*/
/* RX pending: having user -> active */
spin_lock(&rx->lock);
key = rx->key;
if (!key.pending || tipc_aead_users(rx->aead[key.pending]) <= 0)
goto s2;
new_pending = (key.passive &&
!tipc_aead_users(rx->aead[key.passive])) ?
key.passive : 0;
new_passive = (key.active) ?: ((new_pending) ? 0 : key.passive);
tipc_crypto_key_set_state(rx, new_passive, key.pending, new_pending);
if (key.active)
key.passive = key.active;
key.active = key.pending;
rx->timer2 = jiffies;
tipc_crypto_key_set_state(rx, key.passive, key.active, 0);
this_cpu_inc(rx->stats->stat[STAT_SWITCHES]);
pr_info("RX(%s): key %d is activated!\n",
tipc_node_get_id_str(rx->node), key.pending);
pr_info("%s: key[%d] is activated\n", rx->name, key.pending);
goto s5;
s2:
/* RX key "faulty" switching:
* The faulty pending key (users < -30) -> passive
* The passive key (users = 0) -> pending
* Note: This only happens after RX deactivated - s3!
*/
key = rx->key;
if (!key.pending || tipc_aead_users(rx->aead[key.pending]) > -30)
goto s3;
if (!key.passive || tipc_aead_users(rx->aead[key.passive]) != 0)
/* RX pending: not working -> remove */
if (!key.pending || tipc_aead_users(rx->aead[key.pending]) > -10)
goto s3;
new_pending = key.passive;
new_passive = key.pending;
tipc_crypto_key_set_state(rx, new_passive, key.active, new_pending);
tipc_crypto_key_set_state(rx, key.passive, key.active, 0);
tipc_crypto_key_detach(rx->aead[key.pending], &rx->lock);
pr_debug("%s: key[%d] is removed\n", rx->name, key.pending);
goto s5;
s3:
/* RX key deactivating:
* The passive key if any -> pending
* The active key -> passive (users = 0) / pending
* The pending key if any -> passive (users = 0)
*/
key = rx->key;
/* RX active: timed out or no user -> pending */
if (!key.active)
goto s4;
if (time_before(jiffies, rx->timer1 + TIPC_RX_ACTIVE_LIM))
if (time_before(jiffies, rx->timer1 + TIPC_RX_ACTIVE_LIM) &&
tipc_aead_users(rx->aead[key.active]) > 0)
goto s4;
new_pending = (key.passive) ?: key.active;
new_passive = (key.passive) ? key.active : key.pending;
tipc_aead_users_set(rx->aead[new_pending], 0);
if (new_passive)
tipc_aead_users_set(rx->aead[new_passive], 0);
tipc_crypto_key_set_state(rx, new_passive, 0, new_pending);
pr_info("RX(%s): key %d is deactivated!\n",
tipc_node_get_id_str(rx->node), key.active);
if (key.pending)
key.passive = key.active;
else
key.pending = key.active;
rx->timer2 = jiffies;
tipc_crypto_key_set_state(rx, key.passive, 0, key.pending);
tipc_aead_users_set(rx->aead[key.pending], 0);
pr_debug("%s: key[%d] is deactivated\n", rx->name, key.active);
goto s5;
s4:
/* RX key passive -> freed: */
key = rx->key;
if (!key.passive || !tipc_aead_users(rx->aead[key.passive]))
/* RX passive: outdated or not working -> free */
if (!key.passive)
goto s5;
if (time_before(jiffies, rx->timer2 + TIPC_RX_PASSIVE_LIM))
if (time_before(jiffies, rx->timer2 + TIPC_RX_PASSIVE_LIM) &&
tipc_aead_users(rx->aead[key.passive]) > -10)
goto s5;
tipc_crypto_key_set_state(rx, 0, key.active, key.pending);
tipc_crypto_key_detach(rx->aead[key.passive], &rx->lock);
pr_info("RX(%s): key %d is freed!\n", tipc_node_get_id_str(rx->node),
key.passive);
pr_debug("%s: key[%d] is freed\n", rx->name, key.passive);
s5:
spin_unlock(&rx->lock);
/* Relax it here, the flag will be set again if it really is, but only
* when we are not in grace period for safety!
*/
if (time_after(jiffies, tx->timer2 + TIPC_TX_GRACE_PERIOD))
tx->legacy_user = 0;
/* Limit max_tfms & do debug commands if needed */
if (likely(sysctl_tipc_max_tfms <= TIPC_MAX_TFMS_LIM))
return;
......@@ -1533,6 +1642,22 @@ void tipc_crypto_timeout(struct tipc_crypto *rx)
tipc_crypto_do_cmd(rx->net, cmd);
}
static inline void tipc_crypto_clone_msg(struct net *net, struct sk_buff *_skb,
struct tipc_bearer *b,
struct tipc_media_addr *dst,
struct tipc_node *__dnode, u8 type)
{
struct sk_buff *skb;
skb = skb_clone(_skb, GFP_ATOMIC);
if (skb) {
TIPC_SKB_CB(skb)->xmit_type = type;
tipc_crypto_xmit(net, &skb, b, dst, __dnode);
if (skb)
b->media->send_msg(net, skb, b, dst);
}
}
/**
* tipc_crypto_xmit - Build & encrypt TIPC message for xmit
* @net: struct net
......@@ -1542,7 +1667,8 @@ void tipc_crypto_timeout(struct tipc_crypto *rx)
* @__dnode: destination node for reference if any
*
* First, build an encryption message header on the top of the message, then
* encrypt the original TIPC message by using the active or pending TX key.
* encrypt the original TIPC message by using the pending, master or active
* key with this preference order.
* If the encryption is successful, the encrypted skb is returned directly or
* via the callback.
* Otherwise, the skb is freed!
......@@ -1562,46 +1688,67 @@ int tipc_crypto_xmit(struct net *net, struct sk_buff **skb,
struct tipc_crypto *__rx = tipc_node_crypto_rx(__dnode);
struct tipc_crypto *tx = tipc_net(net)->crypto_tx;
struct tipc_crypto_stats __percpu *stats = tx->stats;
struct tipc_msg *hdr = buf_msg(*skb);
struct tipc_key key = tx->key;
struct tipc_aead *aead = NULL;
struct sk_buff *probe;
u32 user = msg_user(hdr);
u32 type = msg_type(hdr);
int rc = -ENOKEY;
u8 tx_key;
u8 tx_key = 0;
/* No encryption? */
if (!tx->working)
return 0;
/* Try with the pending key if available and:
* 1) This is the only choice (i.e. no active key) or;
* 2) Peer has switched to this key (unicast only) or;
* 3) It is time to do a pending key probe;
*/
/* Pending key if peer has active on it or probing time */
if (unlikely(key.pending)) {
tx_key = key.pending;
if (!key.active)
if (!tx->key_master && !key.active)
goto encrypt;
if (__rx && atomic_read(&__rx->peer_rx_active) == tx_key)
goto encrypt;
if (TIPC_SKB_CB(*skb)->probe)
if (TIPC_SKB_CB(*skb)->xmit_type == SKB_PROBING) {
pr_debug("%s: probing for key[%d]\n", tx->name,
key.pending);
goto encrypt;
if (!__rx &&
time_after(jiffies, tx->timer2 + TIPC_TX_PROBE_LIM)) {
tx->timer2 = jiffies;
probe = skb_clone(*skb, GFP_ATOMIC);
if (probe) {
TIPC_SKB_CB(probe)->probe = 1;
tipc_crypto_xmit(net, &probe, b, dst, __dnode);
if (probe)
b->media->send_msg(net, probe, b, dst);
}
if (user == LINK_CONFIG || user == LINK_PROTOCOL)
tipc_crypto_clone_msg(net, *skb, b, dst, __dnode,
SKB_PROBING);
}
/* Master key if this is a *vital* message or in grace period */
if (tx->key_master) {
tx_key = KEY_MASTER;
if (!key.active)
goto encrypt;
if (TIPC_SKB_CB(*skb)->xmit_type == SKB_GRACING) {
pr_debug("%s: gracing for msg (%d %d)\n", tx->name,
user, type);
goto encrypt;
}
if (user == LINK_CONFIG ||
(user == LINK_PROTOCOL && type == RESET_MSG) ||
(user == MSG_CRYPTO && type == KEY_DISTR_MSG) ||
time_before(jiffies, tx->timer2 + TIPC_TX_GRACE_PERIOD)) {
if (__rx && __rx->key_master &&
!atomic_read(&__rx->peer_rx_active))
goto encrypt;
if (!__rx) {
if (likely(!tx->legacy_user))
goto encrypt;
tipc_crypto_clone_msg(net, *skb, b, dst,
__dnode, SKB_GRACING);
}
}
}
/* Else, use the active key if any */
if (likely(key.active)) {
tx_key = key.active;
goto encrypt;
}
goto exit;
encrypt:
......@@ -1667,30 +1814,21 @@ int tipc_crypto_rcv(struct net *net, struct tipc_crypto *rx,
struct tipc_aead *aead = NULL;
struct tipc_key key;
int rc = -ENOKEY;
u8 tx_key = 0;
u8 tx_key, n;
tx_key = ((struct tipc_ehdr *)(*skb)->data)->tx_key;
/* New peer?
* Let's try with TX key (i.e. cluster mode) & verify the skb first!
*/
if (unlikely(!rx))
if (unlikely(!rx || tx_key == KEY_MASTER))
goto pick_tx;
/* Pick RX key according to TX key, three cases are possible:
* 1) The current active key (likely) or;
* 2) The pending (new or deactivated) key (if any) or;
* 3) The passive or old active key (i.e. users > 0);
*/
tx_key = ((struct tipc_ehdr *)(*skb)->data)->tx_key;
/* Pick RX key according to TX key if any */
key = rx->key;
if (likely(tx_key == key.active))
goto decrypt;
if (tx_key == key.pending)
if (tx_key == key.active || tx_key == key.pending ||
tx_key == key.passive)
goto decrypt;
if (tx_key == key.passive) {
rx->timer2 = jiffies;
if (tipc_aead_users(rx->aead[key.passive]) > 0)
goto decrypt;
}
/* Unknown key, let's try to align RX key(s) */
if (tipc_crypto_key_try_align(rx, tx_key))
......@@ -1698,7 +1836,7 @@ int tipc_crypto_rcv(struct net *net, struct tipc_crypto *rx,
pick_tx:
/* No key suitable? Try to pick one from TX... */
aead = tipc_crypto_key_pick_tx(tx, rx, *skb);
aead = tipc_crypto_key_pick_tx(tx, rx, *skb, tx_key);
if (aead)
goto decrypt;
goto exit;
......@@ -1726,8 +1864,19 @@ int tipc_crypto_rcv(struct net *net, struct tipc_crypto *rx,
if (rc == -ENOKEY) {
kfree_skb(*skb);
*skb = NULL;
if (rx)
if (rx) {
/* Mark rx->nokey only if we dont have a
* pending received session key, nor a newer
* one i.e. in the next slot.
*/
n = key_next(tx_key);
rx->nokey = !(rx->skey ||
rcu_access_pointer(rx->aead[n]));
pr_debug_ratelimited("%s: nokey %d, key %d/%x\n",
rx->name, rx->nokey,
tx_key, rx->key.keys);
tipc_node_put(rx->node);
}
this_cpu_inc(stats->stat[STAT_NOKEYS]);
return rc;
} else if (rc == -EBADMSG) {
......@@ -1749,21 +1898,17 @@ static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead,
struct tipc_aead *tmp = NULL;
struct tipc_ehdr *ehdr;
struct tipc_node *n;
u8 rx_key_active;
bool destined;
/* Is this completed by TX? */
if (unlikely(!rx->node)) {
if (unlikely(is_tx(aead->crypto))) {
rx = skb_cb->tx_clone_ctx.rx;
#ifdef TIPC_CRYPTO_DEBUG
pr_info("TX->RX(%s): err %d, aead %p, skb->next %p, flags %x\n",
pr_debug("TX->RX(%s): err %d, aead %p, skb->next %p, flags %x\n",
(rx) ? tipc_node_get_id_str(rx->node) : "-", err, aead,
(*skb)->next, skb_cb->flags);
pr_info("skb_cb [recurs %d, last %p], tx->aead [%p %p %p]\n",
pr_debug("skb_cb [recurs %d, last %p], tx->aead [%p %p %p]\n",
skb_cb->tx_clone_ctx.recurs, skb_cb->tx_clone_ctx.last,
aead->crypto->aead[1], aead->crypto->aead[2],
aead->crypto->aead[3]);
#endif
if (unlikely(err)) {
if (err == -EBADMSG && (*skb)->next)
tipc_rcv(net, (*skb)->next, b);
......@@ -1784,12 +1929,12 @@ static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead,
goto free_skb;
}
/* Skip cloning this time as we had a RX pending key */
if (rx->key.pending)
/* Ignore cloning if it was TX master key */
if (ehdr->tx_key == KEY_MASTER)
goto rcv;
if (tipc_aead_clone(&tmp, aead) < 0)
goto rcv;
if (tipc_crypto_key_attach(rx, tmp, ehdr->tx_key) < 0) {
if (tipc_crypto_key_attach(rx, tmp, ehdr->tx_key, false) < 0) {
tipc_aead_free(&tmp->rcu);
goto rcv;
}
......@@ -1805,14 +1950,18 @@ static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead,
/* Set the RX key's user */
tipc_aead_users_set(aead, 1);
rcv:
/* Mark this point, RX works */
rx->timer1 = jiffies;
rcv:
/* Remove ehdr & auth. tag prior to tipc_rcv() */
ehdr = (struct tipc_ehdr *)(*skb)->data;
destined = ehdr->destined;
rx_key_active = ehdr->rx_key_active;
/* Mark this point, RX passive still works */
if (rx->key.passive && ehdr->tx_key == rx->key.passive)
rx->timer2 = jiffies;
skb_reset_network_header(*skb);
skb_pull(*skb, tipc_ehdr_size(ehdr));
pskb_trim(*skb, (*skb)->len - aead->authsize);
......@@ -1822,9 +1971,8 @@ static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead,
goto free_skb;
}
/* Update peer RX active key & TX users */
if (destined)
tipc_crypto_key_synch(rx, rx_key_active, buf_msg(*skb));
/* Ok, everything's fine, try to synch own keys according to peers' */
tipc_crypto_key_synch(rx, *skb);
/* Mark skb decrypted */
skb_cb->decrypted = 1;
......@@ -1883,7 +2031,7 @@ static void tipc_crypto_do_cmd(struct net *net, int cmd)
/* Print crypto statistics */
for (i = 0, j = 0; i < MAX_STATS; i++)
j += scnprintf(buf + j, 200 - j, "|%11s ", hstats[i]);
pr_info("\nCounter %s", buf);
pr_info("Counter %s", buf);
memset(buf, '-', 115);
buf[115] = '\0';
......@@ -1927,6 +2075,15 @@ static char *tipc_crypto_key_dump(struct tipc_crypto *c, char *buf)
char *s;
for (k = KEY_MIN; k <= KEY_MAX; k++) {
if (k == KEY_MASTER) {
if (is_rx(c))
continue;
if (time_before(jiffies,
c->timer2 + TIPC_TX_GRACE_PERIOD))
s = "ACT";
else
s = "PAS";
} else {
if (k == key.passive)
s = "PAS";
else if (k == key.active)
......@@ -1935,13 +2092,14 @@ static char *tipc_crypto_key_dump(struct tipc_crypto *c, char *buf)
s = "PEN";
else
s = "-";
}
i += scnprintf(buf + i, 200 - i, "\tKey%d: %s", k, s);
rcu_read_lock();
aead = rcu_dereference(c->aead[k]);
if (aead)
i += scnprintf(buf + i, 200 - i,
"{\"%s...\", \"%s\"}/%d:%d",
"{\"0x...%s\", \"%s\"}/%d:%d",
aead->hint,
(aead->mode == CLUSTER_KEY) ? "c" : "p",
atomic_read(&aead->users),
......@@ -1950,14 +2108,13 @@ static char *tipc_crypto_key_dump(struct tipc_crypto *c, char *buf)
i += scnprintf(buf + i, 200 - i, "\n");
}
if (c->node)
if (is_rx(c))
i += scnprintf(buf + i, 200 - i, "\tPeer RX active: %d\n",
atomic_read(&c->peer_rx_active));
return buf;
}
#ifdef TIPC_CRYPTO_DEBUG
static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new,
char *buf)
{
......@@ -1968,7 +2125,7 @@ static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new,
/* Output format: "[%s %s %s] -> [%s %s %s]", max len = 32 */
again:
i += scnprintf(buf + i, 32 - i, "[");
for (k = KEY_MIN; k <= KEY_MAX; k++) {
for (k = KEY_1; k <= KEY_3; k++) {
if (k == key->passive)
s = "pas";
else if (k == key->active)
......@@ -1978,7 +2135,7 @@ static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new,
else
s = "-";
i += scnprintf(buf + i, 32 - i,
(k != KEY_MAX) ? "%s " : "%s", s);
(k != KEY_3) ? "%s " : "%s", s);
}
if (key != &new) {
i += scnprintf(buf + i, 32 - i, "] -> ");
......@@ -1988,4 +2145,320 @@ static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new,
i += scnprintf(buf + i, 32 - i, "]");
return buf;
}
#endif
/**
* tipc_crypto_msg_rcv - Common 'MSG_CRYPTO' processing point
* @net: the struct net
* @skb: the receiving message buffer
*/
void tipc_crypto_msg_rcv(struct net *net, struct sk_buff *skb)
{
struct tipc_crypto *rx;
struct tipc_msg *hdr;
if (unlikely(skb_linearize(skb)))
goto exit;
hdr = buf_msg(skb);
rx = tipc_node_crypto_rx_by_addr(net, msg_prevnode(hdr));
if (unlikely(!rx))
goto exit;
switch (msg_type(hdr)) {
case KEY_DISTR_MSG:
if (tipc_crypto_key_rcv(rx, hdr))
goto exit;
break;
default:
break;
}
tipc_node_put(rx->node);
exit:
kfree_skb(skb);
}
/**
* tipc_crypto_key_distr - Distribute a TX key
* @tx: the TX crypto
* @key: the key's index
* @dest: the destination tipc node, = NULL if distributing to all nodes
*
* Return: 0 in case of success, otherwise < 0
*/
int tipc_crypto_key_distr(struct tipc_crypto *tx, u8 key,
struct tipc_node *dest)
{
struct tipc_aead *aead;
u32 dnode = tipc_node_get_addr(dest);
int rc = -ENOKEY;
if (!sysctl_tipc_key_exchange_enabled)
return 0;
if (key) {
rcu_read_lock();
aead = tipc_aead_get(tx->aead[key]);
if (likely(aead)) {
rc = tipc_crypto_key_xmit(tx->net, aead->key,
aead->gen, aead->mode,
dnode);
tipc_aead_put(aead);
}
rcu_read_unlock();
}
return rc;
}
/**
* tipc_crypto_key_xmit - Send a session key
* @net: the struct net
* @skey: the session key to be sent
* @gen: the key's generation
* @mode: the key's mode
* @dnode: the destination node address, = 0 if broadcasting to all nodes
*
* The session key 'skey' is packed in a TIPC v2 'MSG_CRYPTO/KEY_DISTR_MSG'
* as its data section, then xmit-ed through the uc/bc link.
*
* Return: 0 in case of success, otherwise < 0
*/
static int tipc_crypto_key_xmit(struct net *net, struct tipc_aead_key *skey,
u16 gen, u8 mode, u32 dnode)
{
struct sk_buff_head pkts;
struct tipc_msg *hdr;
struct sk_buff *skb;
u16 size, cong_link_cnt;
u8 *data;
int rc;
size = tipc_aead_key_size(skey);
skb = tipc_buf_acquire(INT_H_SIZE + size, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
hdr = buf_msg(skb);
tipc_msg_init(tipc_own_addr(net), hdr, MSG_CRYPTO, KEY_DISTR_MSG,
INT_H_SIZE, dnode);
msg_set_size(hdr, INT_H_SIZE + size);
msg_set_key_gen(hdr, gen);
msg_set_key_mode(hdr, mode);
data = msg_data(hdr);
*((__be32 *)(data + TIPC_AEAD_ALG_NAME)) = htonl(skey->keylen);
memcpy(data, skey->alg_name, TIPC_AEAD_ALG_NAME);
memcpy(data + TIPC_AEAD_ALG_NAME + sizeof(__be32), skey->key,
skey->keylen);
__skb_queue_head_init(&pkts);
__skb_queue_tail(&pkts, skb);
if (dnode)
rc = tipc_node_xmit(net, &pkts, dnode, 0);
else
rc = tipc_bcast_xmit(net, &pkts, &cong_link_cnt);
return rc;
}
/**
* tipc_crypto_key_rcv - Receive a session key
* @rx: the RX crypto
* @hdr: the TIPC v2 message incl. the receiving session key in its data
*
* This function retrieves the session key in the message from peer, then
* schedules a RX work to attach the key to the corresponding RX crypto.
*
* Return: "true" if the key has been scheduled for attaching, otherwise
* "false".
*/
static bool tipc_crypto_key_rcv(struct tipc_crypto *rx, struct tipc_msg *hdr)
{
struct tipc_crypto *tx = tipc_net(rx->net)->crypto_tx;
struct tipc_aead_key *skey = NULL;
u16 key_gen = msg_key_gen(hdr);
u16 size = msg_data_sz(hdr);
u8 *data = msg_data(hdr);
spin_lock(&rx->lock);
if (unlikely(rx->skey || (key_gen == rx->key_gen && rx->key.keys))) {
pr_err("%s: key existed <%p>, gen %d vs %d\n", rx->name,
rx->skey, key_gen, rx->key_gen);
goto exit;
}
/* Allocate memory for the key */
skey = kmalloc(size, GFP_ATOMIC);
if (unlikely(!skey)) {
pr_err("%s: unable to allocate memory for skey\n", rx->name);
goto exit;
}
/* Copy key from msg data */
skey->keylen = ntohl(*((__be32 *)(data + TIPC_AEAD_ALG_NAME)));
memcpy(skey->alg_name, data, TIPC_AEAD_ALG_NAME);
memcpy(skey->key, data + TIPC_AEAD_ALG_NAME + sizeof(__be32),
skey->keylen);
/* Sanity check */
if (unlikely(size != tipc_aead_key_size(skey))) {
kfree(skey);
skey = NULL;
goto exit;
}
rx->key_gen = key_gen;
rx->skey_mode = msg_key_mode(hdr);
rx->skey = skey;
rx->nokey = 0;
mb(); /* for nokey flag */
exit:
spin_unlock(&rx->lock);
/* Schedule the key attaching on this crypto */
if (likely(skey && queue_delayed_work(tx->wq, &rx->work, 0)))
return true;
return false;
}
/**
* tipc_crypto_work_rx - Scheduled RX works handler
* @work: the struct RX work
*
* The function processes the previous scheduled works i.e. distributing TX key
* or attaching a received session key on RX crypto.
*/
static void tipc_crypto_work_rx(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct tipc_crypto *rx = container_of(dwork, struct tipc_crypto, work);
struct tipc_crypto *tx = tipc_net(rx->net)->crypto_tx;
unsigned long delay = msecs_to_jiffies(5000);
bool resched = false;
u8 key;
int rc;
/* Case 1: Distribute TX key to peer if scheduled */
if (atomic_cmpxchg(&rx->key_distr,
KEY_DISTR_SCHED,
KEY_DISTR_COMPL) == KEY_DISTR_SCHED) {
/* Always pick the newest one for distributing */
key = tx->key.pending ?: tx->key.active;
rc = tipc_crypto_key_distr(tx, key, rx->node);
if (unlikely(rc))
pr_warn("%s: unable to distr key[%d] to %s, err %d\n",
tx->name, key, tipc_node_get_id_str(rx->node),
rc);
/* Sched for key_distr releasing */
resched = true;
} else {
atomic_cmpxchg(&rx->key_distr, KEY_DISTR_COMPL, 0);
}
/* Case 2: Attach a pending received session key from peer if any */
if (rx->skey) {
rc = tipc_crypto_key_init(rx, rx->skey, rx->skey_mode, false);
if (unlikely(rc < 0))
pr_warn("%s: unable to attach received skey, err %d\n",
rx->name, rc);
switch (rc) {
case -EBUSY:
case -ENOMEM:
/* Resched the key attaching */
resched = true;
break;
default:
synchronize_rcu();
kfree(rx->skey);
rx->skey = NULL;
break;
}
}
if (resched && queue_delayed_work(tx->wq, &rx->work, delay))
return;
tipc_node_put(rx->node);
}
/**
* tipc_crypto_rekeying_sched - (Re)schedule rekeying w/o new interval
* @tx: TX crypto
* @changed: if the rekeying needs to be rescheduled with new interval
* @new_intv: new rekeying interval (when "changed" = true)
*/
void tipc_crypto_rekeying_sched(struct tipc_crypto *tx, bool changed,
u32 new_intv)
{
unsigned long delay;
bool now = false;
if (changed) {
if (new_intv == TIPC_REKEYING_NOW)
now = true;
else
tx->rekeying_intv = new_intv;
cancel_delayed_work_sync(&tx->work);
}
if (tx->rekeying_intv || now) {
delay = (now) ? 0 : tx->rekeying_intv * 60 * 1000;
queue_delayed_work(tx->wq, &tx->work, msecs_to_jiffies(delay));
}
}
/**
* tipc_crypto_work_tx - Scheduled TX works handler
* @work: the struct TX work
*
* The function processes the previous scheduled work, i.e. key rekeying, by
* generating a new session key based on current one, then attaching it to the
* TX crypto and finally distributing it to peers. It also re-schedules the
* rekeying if needed.
*/
static void tipc_crypto_work_tx(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct tipc_crypto *tx = container_of(dwork, struct tipc_crypto, work);
struct tipc_aead_key *skey = NULL;
struct tipc_key key = tx->key;
struct tipc_aead *aead;
int rc = -ENOMEM;
if (unlikely(key.pending))
goto resched;
/* Take current key as a template */
rcu_read_lock();
aead = rcu_dereference(tx->aead[key.active ?: KEY_MASTER]);
if (unlikely(!aead)) {
rcu_read_unlock();
/* At least one key should exist for securing */
return;
}
/* Lets duplicate it first */
skey = kmemdup(aead->key, tipc_aead_key_size(aead->key), GFP_ATOMIC);
rcu_read_unlock();
/* Now, generate new key, initiate & distribute it */
if (likely(skey)) {
rc = tipc_aead_key_generate(skey) ?:
tipc_crypto_key_init(tx, skey, PER_NODE_KEY, false);
if (likely(rc > 0))
rc = tipc_crypto_key_distr(tx, rc, NULL);
kzfree(skey);
}
if (unlikely(rc))
pr_warn_ratelimited("%s: rekeying returns %d\n", tx->name, rc);
resched:
/* Re-schedule rekeying if any */
tipc_crypto_rekeying_sched(tx, false, 0);
}
......@@ -67,6 +67,7 @@ enum {
};
extern int sysctl_tipc_max_tfms __read_mostly;
extern int sysctl_tipc_key_exchange_enabled __read_mostly;
/**
* TIPC encryption message format:
......@@ -74,7 +75,7 @@ extern int sysctl_tipc_max_tfms __read_mostly;
* 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
* 1 0 9 8 7 6 5 4|3 2 1 0 9 8 7 6|5 4 3 2 1 0 9 8|7 6 5 4 3 2 1 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* w0:|Ver=7| User |D|TX |RX |K| Rsvd |
* w0:|Ver=7| User |D|TX |RX |K|M|N| Rsvd |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* w1:| Seqno |
* w2:| (8 octets) |
......@@ -101,6 +102,9 @@ extern int sysctl_tipc_max_tfms __read_mostly;
* RX : Currently RX active key corresponding to the destination
* node's TX key (when the "D" bit is set)
* K : Keep-alive bit (for RPS, LINK_PROTOCOL/STATE_MSG only)
* M : Bit indicates if sender has master key
* N : Bit indicates if sender has no RX keys corresponding to the
* receiver's TX (when the "D" bit is set)
* Rsvd : Reserved bit, field
* Word1-2:
* Seqno : The 64-bit sequence number of the encrypted message, also
......@@ -117,7 +121,9 @@ struct tipc_ehdr {
__u8 destined:1,
user:4,
version:3;
__u8 reserved_1:3,
__u8 reserved_1:1,
rx_nokey:1,
master_key:1,
keepalive:1,
rx_key_active:2,
tx_key:2;
......@@ -128,7 +134,9 @@ struct tipc_ehdr {
__u8 tx_key:2,
rx_key_active:2,
keepalive:1,
reserved_1:3;
master_key:1,
rx_nokey:1,
reserved_1:1;
#else
#error "Please fix <asm/byteorder.h>"
#endif
......@@ -158,10 +166,35 @@ int tipc_crypto_xmit(struct net *net, struct sk_buff **skb,
int tipc_crypto_rcv(struct net *net, struct tipc_crypto *rx,
struct sk_buff **skb, struct tipc_bearer *b);
int tipc_crypto_key_init(struct tipc_crypto *c, struct tipc_aead_key *ukey,
u8 mode);
u8 mode, bool master_key);
void tipc_crypto_key_flush(struct tipc_crypto *c);
int tipc_aead_key_validate(struct tipc_aead_key *ukey);
int tipc_crypto_key_distr(struct tipc_crypto *tx, u8 key,
struct tipc_node *dest);
void tipc_crypto_msg_rcv(struct net *net, struct sk_buff *skb);
void tipc_crypto_rekeying_sched(struct tipc_crypto *tx, bool changed,
u32 new_intv);
int tipc_aead_key_validate(struct tipc_aead_key *ukey, struct genl_info *info);
bool tipc_ehdr_validate(struct sk_buff *skb);
static inline u32 msg_key_gen(struct tipc_msg *m)
{
return msg_bits(m, 4, 16, 0xffff);
}
static inline void msg_set_key_gen(struct tipc_msg *m, u32 gen)
{
msg_set_bits(m, 4, 16, 0xffff, gen);
}
static inline u32 msg_key_mode(struct tipc_msg *m)
{
return msg_bits(m, 4, 0, 0xf);
}
static inline void msg_set_key_mode(struct tipc_msg *m, u32 mode)
{
msg_set_bits(m, 4, 0, 0xf, mode);
}
#endif /* _TIPC_CRYPTO_H */
#endif
......@@ -1250,6 +1250,11 @@ static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb,
case MSG_FRAGMENTER:
case BCAST_PROTOCOL:
return false;
#ifdef CONFIG_TIPC_CRYPTO
case MSG_CRYPTO:
tipc_crypto_msg_rcv(l->net, skb);
return true;
#endif
default:
pr_warn("Dropping received illegal msg type\n");
kfree_skb(skb);
......
......@@ -82,6 +82,7 @@ struct plist;
#define NAME_DISTRIBUTOR 11
#define MSG_FRAGMENTER 12
#define LINK_CONFIG 13
#define MSG_CRYPTO 14
#define SOCK_WAKEUP 14 /* pseudo user */
#define TOP_SRV 15 /* pseudo user */
......@@ -127,7 +128,9 @@ struct tipc_skb_cb {
#ifdef CONFIG_TIPC_CRYPTO
u8 encrypted:1;
u8 decrypted:1;
u8 probe:1;
#define SKB_PROBING 1
#define SKB_GRACING 2
u8 xmit_type:2;
u8 tx_clone_deferred:1;
#endif
};
......@@ -747,6 +750,9 @@ static inline void msg_set_nameupper(struct tipc_msg *m, u32 n)
#define GRP_RECLAIM_MSG 4
#define GRP_REMIT_MSG 5
/* Crypto message types */
#define KEY_DISTR_MSG 0
/*
* Word 1
*/
......
......@@ -108,6 +108,8 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {
.len = TIPC_NODEID_LEN},
[TIPC_NLA_NODE_KEY] = { .type = NLA_BINARY,
.len = TIPC_AEAD_KEY_SIZE_MAX},
[TIPC_NLA_NODE_KEY_MASTER] = { .type = NLA_FLAG },
[TIPC_NLA_NODE_REKEYING] = { .type = NLA_U32 },
};
/* Properties valid for media, bearer and link */
......
......@@ -278,6 +278,14 @@ struct tipc_crypto *tipc_node_crypto_rx_by_list(struct list_head *pos)
{
return container_of(pos, struct tipc_node, list)->crypto_rx;
}
struct tipc_crypto *tipc_node_crypto_rx_by_addr(struct net *net, u32 addr)
{
struct tipc_node *n;
n = tipc_node_find(net, addr);
return (n) ? n->crypto_rx : NULL;
}
#endif
static void tipc_node_free(struct rcu_head *rp)
......@@ -303,7 +311,7 @@ void tipc_node_put(struct tipc_node *node)
kref_put(&node->kref, tipc_node_kref_release);
}
static void tipc_node_get(struct tipc_node *node)
void tipc_node_get(struct tipc_node *node)
{
kref_get(&node->kref);
}
......@@ -584,6 +592,9 @@ static void tipc_node_calculate_timer(struct tipc_node *n, struct tipc_link *l)
static void tipc_node_delete_from_list(struct tipc_node *node)
{
#ifdef CONFIG_TIPC_CRYPTO
tipc_crypto_key_flush(node->crypto_rx);
#endif
list_del_rcu(&node->list);
hlist_del_rcu(&node->hash);
tipc_node_put(node);
......@@ -2868,15 +2879,27 @@ static int tipc_nl_retrieve_nodeid(struct nlattr **attrs, u8 **node_id)
return 0;
}
static int tipc_nl_retrieve_rekeying(struct nlattr **attrs, u32 *intv)
{
struct nlattr *attr = attrs[TIPC_NLA_NODE_REKEYING];
if (!attr)
return -ENODATA;
*intv = nla_get_u32(attr);
return 0;
}
static int __tipc_nl_node_set_key(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *attrs[TIPC_NLA_NODE_MAX + 1];
struct net *net = sock_net(skb->sk);
struct tipc_net *tn = tipc_net(net);
struct tipc_crypto *tx = tipc_net(net)->crypto_tx, *c = tx;
struct tipc_node *n = NULL;
struct tipc_aead_key *ukey;
struct tipc_crypto *c;
u8 *id, *own_id;
bool rekeying = true, master_key = false;
u8 *id, *own_id, mode;
u32 intv = 0;
int rc = 0;
if (!info->attrs[TIPC_NLA_NODE])
......@@ -2886,52 +2909,66 @@ static int __tipc_nl_node_set_key(struct sk_buff *skb, struct genl_info *info)
info->attrs[TIPC_NLA_NODE],
tipc_nl_node_policy, info->extack);
if (rc)
goto exit;
return rc;
own_id = tipc_own_id(net);
if (!own_id) {
rc = -EPERM;
goto exit;
GENL_SET_ERR_MSG(info, "not found own node identity (set id?)");
return -EPERM;
}
rc = tipc_nl_retrieve_rekeying(attrs, &intv);
if (rc == -ENODATA)
rekeying = false;
rc = tipc_nl_retrieve_key(attrs, &ukey);
if (rc)
goto exit;
if (rc == -ENODATA && rekeying)
goto rekeying;
else if (rc)
return rc;
rc = tipc_aead_key_validate(ukey);
rc = tipc_aead_key_validate(ukey, info);
if (rc)
goto exit;
return rc;
rc = tipc_nl_retrieve_nodeid(attrs, &id);
switch (rc) {
case -ENODATA:
/* Cluster key mode */
rc = tipc_crypto_key_init(tn->crypto_tx, ukey, CLUSTER_KEY);
mode = CLUSTER_KEY;
master_key = !!(attrs[TIPC_NLA_NODE_KEY_MASTER]);
break;
case 0:
/* Per-node key mode */
if (!memcmp(id, own_id, NODE_ID_LEN)) {
c = tn->crypto_tx;
} else {
mode = PER_NODE_KEY;
if (memcmp(id, own_id, NODE_ID_LEN)) {
n = tipc_node_find_by_id(net, id) ?:
tipc_node_create(net, 0, id, 0xffffu, 0, true);
if (unlikely(!n)) {
rc = -ENOMEM;
break;
}
if (unlikely(!n))
return -ENOMEM;
c = n->crypto_rx;
}
break;
default:
return rc;
}
rc = tipc_crypto_key_init(c, ukey, PER_NODE_KEY);
/* Initiate the TX/RX key */
rc = tipc_crypto_key_init(c, ukey, mode, master_key);
if (n)
tipc_node_put(n);
break;
default:
break;
if (unlikely(rc < 0)) {
GENL_SET_ERR_MSG(info, "unable to initiate or attach new key");
return rc;
} else if (c == tx) {
/* Distribute TX key but not master one */
if (!master_key && tipc_crypto_key_distr(tx, rc, NULL))
GENL_SET_ERR_MSG(info, "failed to replicate new key");
rekeying:
/* Schedule TX rekeying if needed */
tipc_crypto_rekeying_sched(tx, rekeying, intv);
}
exit:
return (rc < 0) ? rc : 0;
return 0;
}
int tipc_nl_node_set_key(struct sk_buff *skb, struct genl_info *info)
......@@ -2958,7 +2995,6 @@ static int __tipc_nl_node_flush_key(struct sk_buff *skb,
tipc_crypto_key_flush(n->crypto_rx);
rcu_read_unlock();
pr_info("All keys are flushed!\n");
return 0;
}
......
......@@ -79,12 +79,14 @@ bool tipc_node_get_id(struct net *net, u32 addr, u8 *id);
u32 tipc_node_get_addr(struct tipc_node *node);
char *tipc_node_get_id_str(struct tipc_node *node);
void tipc_node_put(struct tipc_node *node);
void tipc_node_get(struct tipc_node *node);
struct tipc_node *tipc_node_create(struct net *net, u32 addr, u8 *peer_id,
u16 capabilities, u32 hash_mixes,
bool preliminary);
#ifdef CONFIG_TIPC_CRYPTO
struct tipc_crypto *tipc_node_crypto_rx(struct tipc_node *__n);
struct tipc_crypto *tipc_node_crypto_rx_by_list(struct list_head *pos);
struct tipc_crypto *tipc_node_crypto_rx_by_addr(struct net *net, u32 addr);
#endif
u32 tipc_node_try_addr(struct net *net, u8 *id, u32 addr);
void tipc_node_check_dest(struct net *net, u32 onode, u8 *peer_id128,
......
......@@ -74,6 +74,15 @@ static struct ctl_table tipc_table[] = {
.proc_handler = proc_dointvec_minmax,
.extra1 = SYSCTL_ONE,
},
{
.procname = "key_exchange_enabled",
.data = &sysctl_tipc_key_exchange_enabled,
.maxlen = sizeof(sysctl_tipc_key_exchange_enabled),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE,
},
#endif
{
.procname = "bc_retruni",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment