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) ...@@ -254,6 +254,8 @@ static inline int tipc_aead_key_size(struct tipc_aead_key *key)
return sizeof(*key) + key->keylen; return sizeof(*key) + key->keylen;
} }
#define TIPC_REKEYING_NOW (~0U)
/* The macros and functions below are deprecated: /* The macros and functions below are deprecated:
*/ */
......
...@@ -165,6 +165,8 @@ enum { ...@@ -165,6 +165,8 @@ enum {
TIPC_NLA_NODE_UP, /* flag */ TIPC_NLA_NODE_UP, /* flag */
TIPC_NLA_NODE_ID, /* data */ TIPC_NLA_NODE_ID, /* data */
TIPC_NLA_NODE_KEY, /* 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 = __TIPC_NLA_NODE_MAX - 1 TIPC_NLA_NODE_MAX = __TIPC_NLA_NODE_MAX - 1
......
...@@ -36,22 +36,28 @@ ...@@ -36,22 +36,28 @@
#include <crypto/aead.h> #include <crypto/aead.h>
#include <crypto/aes.h> #include <crypto/aes.h>
#include <crypto/rng.h>
#include "crypto.h" #include "crypto.h"
#include "msg.h"
#include "bcast.h"
#define TIPC_TX_PROBE_LIM msecs_to_jiffies(1000) /* > 1s */ #define TIPC_TX_GRACE_PERIOD msecs_to_jiffies(5000) /* 5s */
#define TIPC_TX_LASTING_LIM msecs_to_jiffies(120000) /* 2 mins */ #define TIPC_TX_LASTING_TIME msecs_to_jiffies(10000) /* 10s */
#define TIPC_RX_ACTIVE_LIM msecs_to_jiffies(3000) /* 3s */ #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_DEF 10
#define TIPC_MAX_TFMS_LIM 1000 #define TIPC_MAX_TFMS_LIM 1000
#define TIPC_REKEYING_INTV_DEF (60 * 24) /* default: 1 day */
/** /**
* TIPC Key ids * TIPC Key ids
*/ */
enum { enum {
KEY_UNUSED = 0, KEY_MASTER = 0,
KEY_MIN, KEY_MIN = KEY_MASTER,
KEY_1 = KEY_MIN, KEY_1 = 1,
KEY_2, KEY_2,
KEY_3, KEY_3,
KEY_MAX = KEY_3, KEY_MAX = KEY_3,
...@@ -81,6 +87,8 @@ static const char *hstats[MAX_STATS] = {"ok", "nok", "async", "async_ok", ...@@ -81,6 +87,8 @@ static const char *hstats[MAX_STATS] = {"ok", "nok", "async", "async_ok",
/* Max TFMs number per key */ /* Max TFMs number per key */
int sysctl_tipc_max_tfms __read_mostly = TIPC_MAX_TFMS_DEF; 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 * struct tipc_key - TIPC keys' status indicator
...@@ -132,6 +140,8 @@ struct tipc_tfm { ...@@ -132,6 +140,8 @@ struct tipc_tfm {
* @mode: crypto mode is applied to the key * @mode: crypto mode is applied to the key
* @hint[]: a hint for user key * @hint[]: a hint for user key
* @rcu: struct rcu_head * @rcu: struct rcu_head
* @key: the aead key
* @gen: the key's generation
* @seqno: the key seqno (cluster scope) * @seqno: the key seqno (cluster scope)
* @refcnt: the key reference counter * @refcnt: the key reference counter
*/ */
...@@ -144,8 +154,10 @@ struct tipc_aead { ...@@ -144,8 +154,10 @@ struct tipc_aead {
u32 salt; u32 salt;
u8 authsize; u8 authsize;
u8 mode; u8 mode;
char hint[TIPC_AEAD_HINT_LEN + 1]; char hint[2 * TIPC_AEAD_HINT_LEN + 1];
struct rcu_head rcu; struct rcu_head rcu;
struct tipc_aead_key *key;
u16 gen;
atomic64_t seqno ____cacheline_aligned; atomic64_t seqno ____cacheline_aligned;
refcount_t refcnt ____cacheline_aligned; refcount_t refcnt ____cacheline_aligned;
...@@ -165,26 +177,56 @@ struct tipc_crypto_stats { ...@@ -165,26 +177,56 @@ struct tipc_crypto_stats {
* @node: TIPC node (RX) * @node: TIPC node (RX)
* @aead: array of pointers to AEAD keys for encryption/decryption * @aead: array of pointers to AEAD keys for encryption/decryption
* @peer_rx_active: replicated peer RX active key index * @peer_rx_active: replicated peer RX active key index
* @key_gen: TX/RX key generation
* @key: the key states * @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 * @stats: the crypto statistics
* @name: the crypto name
* @sndnxt: the per-peer sndnxt (TX) * @sndnxt: the per-peer sndnxt (TX)
* @timer1: general timer 1 (jiffies) * @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 * @lock: tipc_key lock
*/ */
struct tipc_crypto { struct tipc_crypto {
struct net *net; struct net *net;
struct tipc_node *node; 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; atomic_t peer_rx_active;
u16 key_gen;
struct tipc_key key; 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; struct tipc_crypto_stats __percpu *stats;
char name[48];
atomic64_t sndnxt ____cacheline_aligned; atomic64_t sndnxt ____cacheline_aligned;
unsigned long timer1; unsigned long timer1;
unsigned long timer2; 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 */ spinlock_t lock; /* crypto lock */
} ____cacheline_aligned; } ____cacheline_aligned;
...@@ -234,23 +276,35 @@ static inline void tipc_crypto_key_set_state(struct tipc_crypto *c, ...@@ -234,23 +276,35 @@ static inline void tipc_crypto_key_set_state(struct tipc_crypto *c,
u8 new_active, u8 new_active,
u8 new_pending); u8 new_pending);
static int tipc_crypto_key_attach(struct tipc_crypto *c, 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 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, static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx,
struct tipc_crypto *rx, struct tipc_crypto *rx,
struct sk_buff *skb); struct sk_buff *skb,
static void tipc_crypto_key_synch(struct tipc_crypto *rx, u8 new_rx_active, u8 tx_key);
struct tipc_msg *hdr); 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 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, static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead,
struct tipc_bearer *b, struct tipc_bearer *b,
struct sk_buff **skb, int err); struct sk_buff **skb, int err);
static void tipc_crypto_do_cmd(struct net *net, int cmd); static void tipc_crypto_do_cmd(struct net *net, int cmd);
static char *tipc_crypto_key_dump(struct tipc_crypto *c, char *buf); 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, static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new,
char *buf); 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) #define key_next(cur) ((cur) % KEY_MAX + 1)
...@@ -271,30 +325,55 @@ do { \ ...@@ -271,30 +325,55 @@ do { \
/** /**
* tipc_aead_key_validate - Validate a AEAD user key * 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; int keylen;
/* Check if algorithm exists */ /* Check if algorithm exists */
if (unlikely(!crypto_has_alg(ukey->alg_name, 0, 0))) { 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; return -ENODEV;
} }
/* Currently, we only support the "gcm(aes)" cipher algorithm */ /* 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; return -ENOTSUPP;
}
/* Check if key size is correct */ /* Check if key size is correct */
keylen = ukey->keylen - TIPC_AES_GCM_SALT_SIZE; keylen = ukey->keylen - TIPC_AES_GCM_SALT_SIZE;
if (unlikely(keylen != TIPC_AES_GCM_KEY_SIZE_128 && if (unlikely(keylen != TIPC_AES_GCM_KEY_SIZE_128 &&
keylen != TIPC_AES_GCM_KEY_SIZE_192 && keylen != TIPC_AES_GCM_KEY_SIZE_192 &&
keylen != TIPC_AES_GCM_KEY_SIZE_256)) keylen != TIPC_AES_GCM_KEY_SIZE_256)) {
return -EINVAL; GENL_SET_ERR_MSG(info, "incorrect key length (20, 28 or 36 octets?)");
return -EKEYREJECTED;
}
return 0; 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) static struct tipc_aead *tipc_aead_get(struct tipc_aead __rcu *aead)
{ {
struct tipc_aead *tmp; struct tipc_aead *tmp;
...@@ -339,6 +418,7 @@ static void tipc_aead_free(struct rcu_head *rp) ...@@ -339,6 +418,7 @@ static void tipc_aead_free(struct rcu_head *rp)
kfree(head); kfree(head);
} }
free_percpu(aead->tfm_entry); free_percpu(aead->tfm_entry);
kzfree(aead->key);
kfree(aead); kfree(aead);
} }
...@@ -501,14 +581,15 @@ static int tipc_aead_init(struct tipc_aead **aead, struct tipc_aead_key *ukey, ...@@ -501,14 +581,15 @@ static int tipc_aead_init(struct tipc_aead **aead, struct tipc_aead_key *ukey,
return err; return err;
} }
/* Copy some chars from the user key as a hint */ /* Form a hex string of some last bytes as the key's hint */
memcpy(tmp->hint, ukey->key, TIPC_AEAD_HINT_LEN); bin2hex(tmp->hint, ukey->key + keylen - TIPC_AEAD_HINT_LEN,
tmp->hint[TIPC_AEAD_HINT_LEN] = '\0'; TIPC_AEAD_HINT_LEN);
/* Initialize the other data */ /* Initialize the other data */
tmp->mode = mode; tmp->mode = mode;
tmp->cloned = NULL; tmp->cloned = NULL;
tmp->authsize = TIPC_AES_GCM_TAG_SIZE; 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); memcpy(&tmp->salt, ukey->key + keylen, TIPC_AES_GCM_SALT_SIZE);
atomic_set(&tmp->users, 0); atomic_set(&tmp->users, 0);
atomic64_set(&tmp->seqno, 0); atomic64_set(&tmp->seqno, 0);
...@@ -663,13 +744,11 @@ static int tipc_aead_encrypt(struct tipc_aead *aead, struct sk_buff *skb, ...@@ -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! * but there is no frag_list, it should be still fine!
* Otherwise, we must cow it to be a writable buffer with the tailroom. * Otherwise, we must cow it to be a writable buffer with the tailroom.
*/ */
#ifdef TIPC_CRYPTO_DEBUG
SKB_LINEAR_ASSERT(skb); SKB_LINEAR_ASSERT(skb);
if (tailen > skb_tailroom(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); skb_tailroom(skb), tailen);
} }
#endif
if (unlikely(!skb_cloned(skb) && tailen <= skb_tailroom(skb))) { if (unlikely(!skb_cloned(skb) && tailen <= skb_tailroom(skb))) {
nsg = 1; nsg = 1;
...@@ -940,8 +1019,6 @@ bool tipc_ehdr_validate(struct sk_buff *skb) ...@@ -940,8 +1019,6 @@ bool tipc_ehdr_validate(struct sk_buff *skb)
return false; return false;
if (unlikely(skb->len <= ehsz + TIPC_AES_GCM_TAG_SIZE)) if (unlikely(skb->len <= ehsz + TIPC_AES_GCM_TAG_SIZE))
return false; return false;
if (unlikely(!ehdr->tx_key))
return false;
return true; return true;
} }
...@@ -994,6 +1071,8 @@ static int tipc_ehdr_build(struct net *net, struct tipc_aead *aead, ...@@ -994,6 +1071,8 @@ static int tipc_ehdr_build(struct net *net, struct tipc_aead *aead,
ehdr->tx_key = tx_key; ehdr->tx_key = tx_key;
ehdr->destined = (__rx) ? 1 : 0; ehdr->destined = (__rx) ? 1 : 0;
ehdr->rx_key_active = (__rx) ? __rx->key.active : 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_1 = 0;
ehdr->reserved_2 = 0; ehdr->reserved_2 = 0;
...@@ -1019,23 +1098,16 @@ static inline void tipc_crypto_key_set_state(struct tipc_crypto *c, ...@@ -1019,23 +1098,16 @@ static inline void tipc_crypto_key_set_state(struct tipc_crypto *c,
u8 new_active, u8 new_active,
u8 new_pending) u8 new_pending)
{ {
#ifdef TIPC_CRYPTO_DEBUG
struct tipc_key old = c->key; struct tipc_key old = c->key;
char buf[32]; char buf[32];
#endif
c->key.keys = ((new_passive & KEY_MASK) << (KEY_BITS * 2)) | c->key.keys = ((new_passive & KEY_MASK) << (KEY_BITS * 2)) |
((new_active & KEY_MASK) << (KEY_BITS)) | ((new_active & KEY_MASK) << (KEY_BITS)) |
((new_pending & KEY_MASK)); ((new_pending & KEY_MASK));
#ifdef TIPC_CRYPTO_DEBUG pr_debug("%s: key changing %s ::%pS\n", c->name,
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),
tipc_key_change_dump(old, c->key, buf), tipc_key_change_dump(old, c->key, buf),
__builtin_return_address(0)); __builtin_return_address(0));
#endif
} }
/** /**
...@@ -1043,6 +1115,7 @@ static inline void tipc_crypto_key_set_state(struct tipc_crypto *c, ...@@ -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 * @c: TIPC crypto to which new key is attached
* @ukey: the user key * @ukey: the user key
* @mode: the key mode (CLUSTER_KEY or PER_NODE_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 * A new TIPC AEAD key will be allocated and initiated with the specified user
* key, then attached to the TIPC crypto. * key, then attached to the TIPC crypto.
...@@ -1050,7 +1123,7 @@ static inline void tipc_crypto_key_set_state(struct tipc_crypto *c, ...@@ -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 * Return: new key id in case of success, otherwise: < 0
*/ */
int tipc_crypto_key_init(struct tipc_crypto *c, struct tipc_aead_key *ukey, 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; struct tipc_aead *aead = NULL;
int rc = 0; int rc = 0;
...@@ -1060,17 +1133,11 @@ int tipc_crypto_key_init(struct tipc_crypto *c, struct tipc_aead_key *ukey, ...@@ -1060,17 +1133,11 @@ int tipc_crypto_key_init(struct tipc_crypto *c, struct tipc_aead_key *ukey,
/* Attach it to the crypto */ /* Attach it to the crypto */
if (likely(!rc)) { if (likely(!rc)) {
rc = tipc_crypto_key_attach(c, aead, 0); rc = tipc_crypto_key_attach(c, aead, 0, master_key);
if (rc < 0) if (rc < 0)
tipc_aead_free(&aead->rcu); 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; return rc;
} }
...@@ -1079,58 +1146,58 @@ int tipc_crypto_key_init(struct tipc_crypto *c, struct tipc_aead_key *ukey, ...@@ -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 * @c: TIPC crypto to which the new AEAD key is attached
* @aead: the new AEAD key pointer * @aead: the new AEAD key pointer
* @pos: desired slot in the crypto key array, = 0 if any! * @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 * Return: new key id in case of success, otherwise: -EBUSY
*/ */
static int tipc_crypto_key_attach(struct tipc_crypto *c, 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; struct tipc_key key;
int rc = -EBUSY; int rc = -EBUSY;
u8 new_key;
spin_lock_bh(&c->lock); spin_lock_bh(&c->lock);
key = c->key; key = c->key;
if (master_key) {
new_key = KEY_MASTER;
goto attach;
}
if (key.active && key.passive) if (key.active && key.passive)
goto exit; goto exit;
if (key.passive && !tipc_aead_users(c->aead[key.passive]))
goto exit;
if (key.pending) { if (key.pending) {
if (pos)
goto exit;
if (tipc_aead_users(c->aead[key.pending]) > 0) if (tipc_aead_users(c->aead[key.pending]) > 0)
goto exit; goto exit;
/* if (pos): ok with replacing, will be aligned when needed */
/* Replace it */ /* Replace it */
new_pending = key.pending; new_key = key.pending;
new_passive = key.passive;
new_key = new_pending;
} else { } else {
if (pos) { if (pos) {
if (key.active && pos != key_next(key.active)) { if (key.active && pos != key_next(key.active)) {
new_pending = key.pending; key.passive = pos;
new_passive = pos; new_key = pos;
new_key = new_passive;
goto attach; goto attach;
} else if (!key.active && !key.passive) { } else if (!key.active && !key.passive) {
new_pending = pos; key.pending = pos;
new_passive = key.passive; new_key = pos;
new_key = new_pending;
goto attach; goto attach;
} }
} }
new_pending = key_next(key.active ?: key.passive); key.pending = key_next(key.active ?: key.passive);
new_passive = key.passive; new_key = key.pending;
new_key = new_pending;
} }
attach: attach:
aead->crypto = c; 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); 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->working = 1;
c->timer1 = jiffies; c->nokey = 0;
c->timer2 = jiffies; c->key_master |= master_key;
rc = new_key; rc = new_key;
exit: exit:
...@@ -1140,14 +1207,33 @@ static int tipc_crypto_key_attach(struct tipc_crypto *c, ...@@ -1140,14 +1207,33 @@ static int tipc_crypto_key_attach(struct tipc_crypto *c,
void tipc_crypto_key_flush(struct tipc_crypto *c) void tipc_crypto_key_flush(struct tipc_crypto *c)
{ {
struct tipc_crypto *tx, *rx;
int k; int k;
spin_lock_bh(&c->lock); 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); tipc_crypto_key_set_state(c, 0, 0, 0);
for (k = KEY_MIN; k <= KEY_MAX; k++) for (k = KEY_MIN; k <= KEY_MAX; k++)
tipc_crypto_key_detach(c->aead[k], &c->lock); tipc_crypto_key_detach(c->aead[k], &c->lock);
atomic_set(&c->peer_rx_active, 0);
atomic64_set(&c->sndnxt, 0); atomic64_set(&c->sndnxt, 0);
spin_unlock_bh(&c->lock); spin_unlock_bh(&c->lock);
} }
...@@ -1206,7 +1292,8 @@ static bool tipc_crypto_key_try_align(struct tipc_crypto *rx, u8 new_pending) ...@@ -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); rcu_assign_pointer(rx->aead[new_passive], tmp2);
refcount_set(&tmp1->refcnt, 1); refcount_set(&tmp1->refcnt, 1);
aligned = true; 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: exit:
spin_unlock(&rx->lock); spin_unlock(&rx->lock);
...@@ -1218,6 +1305,7 @@ static bool tipc_crypto_key_try_align(struct tipc_crypto *rx, u8 new_pending) ...@@ -1218,6 +1305,7 @@ static bool tipc_crypto_key_try_align(struct tipc_crypto *rx, u8 new_pending)
* @tx: TX crypto handle * @tx: TX crypto handle
* @rx: RX crypto handle (can be NULL) * @rx: RX crypto handle (can be NULL)
* @skb: the message skb which will be decrypted later * @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 * 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 * 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) ...@@ -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, static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx,
struct tipc_crypto *rx, 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_skb_cb *skb_cb = TIPC_SKB_CB(skb);
struct tipc_aead *aead = NULL; struct tipc_aead *aead = NULL;
...@@ -1246,6 +1335,10 @@ static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx, ...@@ -1246,6 +1335,10 @@ static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx,
/* Pick one TX key */ /* Pick one TX key */
spin_lock(&tx->lock); spin_lock(&tx->lock);
if (tx_key == KEY_MASTER) {
aead = tipc_aead_rcu_ptr(tx->aead[KEY_MASTER], &tx->lock);
goto done;
}
do { do {
k = (i == 0) ? key.pending : k = (i == 0) ? key.pending :
((i == 1) ? key.active : key.passive); ((i == 1) ? key.active : key.passive);
...@@ -1265,9 +1358,12 @@ static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx, ...@@ -1265,9 +1358,12 @@ static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx,
skb->next = skb_clone(skb, GFP_ATOMIC); skb->next = skb_clone(skb, GFP_ATOMIC);
if (unlikely(!skb->next)) if (unlikely(!skb->next))
pr_warn("Failed to clone skb for next round if any\n"); pr_warn("Failed to clone skb for next round if any\n");
WARN_ON(!refcount_inc_not_zero(&aead->refcnt));
break; break;
} while (++i < 3); } while (++i < 3);
done:
if (likely(aead))
WARN_ON(!refcount_inc_not_zero(&aead->refcnt));
spin_unlock(&tx->lock); spin_unlock(&tx->lock);
return aead; return aead;
...@@ -1276,53 +1372,73 @@ static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx, ...@@ -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 * tipc_crypto_key_synch: Synch own key data according to peer key status
* @rx: RX crypto handle * @rx: RX crypto handle
* @new_rx_active: latest RX active key from peer * @skb: TIPCv2 message buffer (incl. the ehdr from peer)
* @hdr: TIPCv2 message
* *
* This function updates the peer node related data as the peer RX active key * 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 * has changed, so the number of TX keys' users on this node are increased and
* decreased correspondingly. * 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. * 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, static void tipc_crypto_key_synch(struct tipc_crypto *rx, struct sk_buff *skb)
struct tipc_msg *hdr)
{ {
struct net *net = rx->net; struct tipc_ehdr *ehdr = (struct tipc_ehdr *)skb_network_header(skb);
struct tipc_crypto *tx = tipc_net(net)->crypto_tx; struct tipc_crypto *tx = tipc_net(rx->net)->crypto_tx;
u8 cur_rx_active; 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 */ /* Update RX 'key_master' flag according to peer, also mark "legacy" if
if (unlikely(!tx->key.active && !tx->key.pending)) * a peer has no master key.
return; */
rx->key_master = ehdr->master_key;
if (!rx->key_master)
tx->legacy_user = 1;
cur_rx_active = atomic_read(&rx->peer_rx_active); /* For later cases, apply only if message is destined to this node */
if (likely(cur_rx_active == new_rx_active)) if (!ehdr->destined || msg_short(hdr) || msg_destnode(hdr) != self)
return; return;
/* Make sure this message destined for this node */ /* Case 1: Peer has no keys, let's make master key take over */
if (unlikely(msg_short(hdr) || if (ehdr->rx_nokey) {
msg_destnode(hdr) != tipc_own_addr(net))) /* Set or extend grace period */
return; 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 */ /* Case 2: Peer RX active key has changed, let's update own TX users */
if (atomic_cmpxchg(&rx->peer_rx_active, cur = atomic_read(&rx->peer_rx_active);
cur_rx_active, new = ehdr->rx_key_active;
new_rx_active) == cur_rx_active) { if (tx->key.keys &&
if (new_rx_active) cur != new &&
tipc_aead_users_inc(tx->aead[new_rx_active], INT_MAX); atomic_cmpxchg(&rx->peer_rx_active, cur, new) == cur) {
if (cur_rx_active) if (new)
tipc_aead_users_dec(tx->aead[cur_rx_active], 0); tipc_aead_users_inc(tx->aead[new], INT_MAX);
if (cur)
tipc_aead_users_dec(tx->aead[cur], 0);
atomic64_set(&rx->sndnxt, 0); atomic64_set(&rx->sndnxt, 0);
/* Mark the point TX key users changed */ /* Mark the point TX key users changed */
tx->timer1 = jiffies; tx->timer1 = jiffies;
#ifdef TIPC_CRYPTO_DEBUG pr_debug("%s: key users changed %d-- %d++, peer %s\n",
pr_info("TX(%s): key users changed %d-- %d++, peer RX(%s)\n", tx->name, cur, new, rx->name);
tipc_own_id_string(net), cur_rx_active,
new_rx_active, tipc_node_get_id_str(rx->node));
#endif
} }
} }
...@@ -1340,7 +1456,7 @@ static int tipc_crypto_key_revoke(struct net *net, u8 tx_key) ...@@ -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); tipc_crypto_key_detach(tx->aead[key.active], &tx->lock);
spin_unlock(&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; return -EKEYREVOKED;
} }
...@@ -1357,6 +1473,15 @@ int tipc_crypto_start(struct tipc_crypto **crypto, struct net *net, ...@@ -1357,6 +1473,15 @@ int tipc_crypto_start(struct tipc_crypto **crypto, struct net *net,
if (!c) if (!c)
return -ENOMEM; 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 */ /* Allocate statistic structure */
c->stats = alloc_percpu_gfp(struct tipc_crypto_stats, GFP_ATOMIC); c->stats = alloc_percpu_gfp(struct tipc_crypto_stats, GFP_ATOMIC);
if (!c->stats) { if (!c->stats) {
...@@ -1364,53 +1489,52 @@ int tipc_crypto_start(struct tipc_crypto **crypto, struct net *net, ...@@ -1364,53 +1489,52 @@ int tipc_crypto_start(struct tipc_crypto **crypto, struct net *net,
return -ENOMEM; return -ENOMEM;
} }
c->working = 0; c->flags = 0;
c->net = net; c->net = net;
c->node = node; c->node = node;
get_random_bytes(&c->key_gen, 2);
tipc_crypto_key_set_state(c, 0, 0, 0); tipc_crypto_key_set_state(c, 0, 0, 0);
atomic_set(&c->key_distr, 0);
atomic_set(&c->peer_rx_active, 0); atomic_set(&c->peer_rx_active, 0);
atomic64_set(&c->sndnxt, 0); atomic64_set(&c->sndnxt, 0);
c->timer1 = jiffies; c->timer1 = jiffies;
c->timer2 = jiffies; c->timer2 = jiffies;
c->rekeying_intv = TIPC_REKEYING_INTV_DEF;
spin_lock_init(&c->lock); 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; return 0;
} }
void tipc_crypto_stop(struct tipc_crypto **crypto) void tipc_crypto_stop(struct tipc_crypto **crypto)
{ {
struct tipc_crypto *c, *tx, *rx; struct tipc_crypto *c = *crypto;
bool is_rx;
u8 k; u8 k;
if (!*crypto) if (!c)
return; return;
rcu_read_lock(); /* Flush any queued works & destroy wq */
/* RX stopping? => decrease TX key users if any */ if (is_tx(c)) {
is_rx = !!((*crypto)->node); c->rekeying_intv = 0;
if (is_rx) { cancel_delayed_work_sync(&c->work);
rx = *crypto; destroy_workqueue(c->wq);
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;
}
} }
/* Release AEAD keys */ /* Release AEAD keys */
c = *crypto; rcu_read_lock();
for (k = KEY_MIN; k <= KEY_MAX; k++) for (k = KEY_MIN; k <= KEY_MAX; k++)
tipc_aead_put(rcu_dereference(c->aead[k])); tipc_aead_put(rcu_dereference(c->aead[k]));
rcu_read_unlock(); rcu_read_unlock();
pr_debug("%s: has been stopped\n", c->name);
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));
/* Free this crypto statistics */ /* Free this crypto statistics */
free_percpu(c->stats); free_percpu(c->stats);
...@@ -1424,106 +1548,91 @@ void tipc_crypto_timeout(struct tipc_crypto *rx) ...@@ -1424,106 +1548,91 @@ void tipc_crypto_timeout(struct tipc_crypto *rx)
struct tipc_net *tn = tipc_net(rx->net); struct tipc_net *tn = tipc_net(rx->net);
struct tipc_crypto *tx = tn->crypto_tx; struct tipc_crypto *tx = tn->crypto_tx;
struct tipc_key key; struct tipc_key key;
u8 new_pending, new_passive;
int cmd; int cmd;
/* TX key activating: /* TX pending: taking all users & stable -> active */
* The pending key (users > 0) -> active
* The active key if any (users == 0) -> free
*/
spin_lock(&tx->lock); spin_lock(&tx->lock);
key = tx->key; key = tx->key;
if (key.active && tipc_aead_users(tx->aead[key.active]) > 0) if (key.active && tipc_aead_users(tx->aead[key.active]) > 0)
goto s1; goto s1;
if (!key.pending || tipc_aead_users(tx->aead[key.pending]) <= 0) if (!key.pending || tipc_aead_users(tx->aead[key.pending]) <= 0)
goto s1; goto s1;
if (time_before(jiffies, tx->timer1 + TIPC_TX_LASTING_LIM)) if (time_before(jiffies, tx->timer1 + TIPC_TX_LASTING_TIME))
goto s1; goto s1;
tipc_crypto_key_set_state(tx, key.passive, key.pending, 0); tipc_crypto_key_set_state(tx, key.passive, key.pending, 0);
if (key.active) if (key.active)
tipc_crypto_key_detach(tx->aead[key.active], &tx->lock); tipc_crypto_key_detach(tx->aead[key.active], &tx->lock);
this_cpu_inc(tx->stats->stat[STAT_SWITCHES]); this_cpu_inc(tx->stats->stat[STAT_SWITCHES]);
pr_info("TX(%s): key %d is activated!\n", tipc_own_id_string(tx->net), pr_info("%s: key[%d] is activated\n", tx->name, key.pending);
key.pending);
s1: s1:
spin_unlock(&tx->lock); spin_unlock(&tx->lock);
/* RX key activating: /* RX pending: having user -> active */
* The pending key (users > 0) -> active
* The active key if any -> passive, freed later
*/
spin_lock(&rx->lock); spin_lock(&rx->lock);
key = rx->key; key = rx->key;
if (!key.pending || tipc_aead_users(rx->aead[key.pending]) <= 0) if (!key.pending || tipc_aead_users(rx->aead[key.pending]) <= 0)
goto s2; goto s2;
new_pending = (key.passive && if (key.active)
!tipc_aead_users(rx->aead[key.passive])) ? key.passive = key.active;
key.passive : 0; key.active = key.pending;
new_passive = (key.active) ?: ((new_pending) ? 0 : key.passive); rx->timer2 = jiffies;
tipc_crypto_key_set_state(rx, new_passive, key.pending, new_pending); tipc_crypto_key_set_state(rx, key.passive, key.active, 0);
this_cpu_inc(rx->stats->stat[STAT_SWITCHES]); this_cpu_inc(rx->stats->stat[STAT_SWITCHES]);
pr_info("RX(%s): key %d is activated!\n", pr_info("%s: key[%d] is activated\n", rx->name, key.pending);
tipc_node_get_id_str(rx->node), key.pending);
goto s5; goto s5;
s2: s2:
/* RX key "faulty" switching: /* RX pending: not working -> remove */
* The faulty pending key (users < -30) -> passive if (!key.pending || tipc_aead_users(rx->aead[key.pending]) > -10)
* 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)
goto s3; goto s3;
new_pending = key.passive; tipc_crypto_key_set_state(rx, key.passive, key.active, 0);
new_passive = key.pending; tipc_crypto_key_detach(rx->aead[key.pending], &rx->lock);
tipc_crypto_key_set_state(rx, new_passive, key.active, new_pending); pr_debug("%s: key[%d] is removed\n", rx->name, key.pending);
goto s5; goto s5;
s3: s3:
/* RX key deactivating: /* RX active: timed out or no user -> pending */
* The passive key if any -> pending
* The active key -> passive (users = 0) / pending
* The pending key if any -> passive (users = 0)
*/
key = rx->key;
if (!key.active) if (!key.active)
goto s4; 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; goto s4;
new_pending = (key.passive) ?: key.active; if (key.pending)
new_passive = (key.passive) ? key.active : key.pending; key.passive = key.active;
tipc_aead_users_set(rx->aead[new_pending], 0); else
if (new_passive) key.pending = key.active;
tipc_aead_users_set(rx->aead[new_passive], 0); rx->timer2 = jiffies;
tipc_crypto_key_set_state(rx, new_passive, 0, new_pending); tipc_crypto_key_set_state(rx, key.passive, 0, key.pending);
pr_info("RX(%s): key %d is deactivated!\n", tipc_aead_users_set(rx->aead[key.pending], 0);
tipc_node_get_id_str(rx->node), key.active); pr_debug("%s: key[%d] is deactivated\n", rx->name, key.active);
goto s5; goto s5;
s4: s4:
/* RX key passive -> freed: */ /* RX passive: outdated or not working -> free */
key = rx->key; if (!key.passive)
if (!key.passive || !tipc_aead_users(rx->aead[key.passive]))
goto s5; 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; goto s5;
tipc_crypto_key_set_state(rx, 0, key.active, key.pending); tipc_crypto_key_set_state(rx, 0, key.active, key.pending);
tipc_crypto_key_detach(rx->aead[key.passive], &rx->lock); 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), pr_debug("%s: key[%d] is freed\n", rx->name, key.passive);
key.passive);
s5: s5:
spin_unlock(&rx->lock); 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 */ /* Limit max_tfms & do debug commands if needed */
if (likely(sysctl_tipc_max_tfms <= TIPC_MAX_TFMS_LIM)) if (likely(sysctl_tipc_max_tfms <= TIPC_MAX_TFMS_LIM))
return; return;
...@@ -1533,6 +1642,22 @@ void tipc_crypto_timeout(struct tipc_crypto *rx) ...@@ -1533,6 +1642,22 @@ void tipc_crypto_timeout(struct tipc_crypto *rx)
tipc_crypto_do_cmd(rx->net, cmd); 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 * tipc_crypto_xmit - Build & encrypt TIPC message for xmit
* @net: struct net * @net: struct net
...@@ -1542,7 +1667,8 @@ void tipc_crypto_timeout(struct tipc_crypto *rx) ...@@ -1542,7 +1667,8 @@ void tipc_crypto_timeout(struct tipc_crypto *rx)
* @__dnode: destination node for reference if any * @__dnode: destination node for reference if any
* *
* First, build an encryption message header on the top of the message, then * 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 * If the encryption is successful, the encrypted skb is returned directly or
* via the callback. * via the callback.
* Otherwise, the skb is freed! * Otherwise, the skb is freed!
...@@ -1562,46 +1688,67 @@ int tipc_crypto_xmit(struct net *net, struct sk_buff **skb, ...@@ -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 *__rx = tipc_node_crypto_rx(__dnode);
struct tipc_crypto *tx = tipc_net(net)->crypto_tx; struct tipc_crypto *tx = tipc_net(net)->crypto_tx;
struct tipc_crypto_stats __percpu *stats = tx->stats; struct tipc_crypto_stats __percpu *stats = tx->stats;
struct tipc_msg *hdr = buf_msg(*skb);
struct tipc_key key = tx->key; struct tipc_key key = tx->key;
struct tipc_aead *aead = NULL; struct tipc_aead *aead = NULL;
struct sk_buff *probe; u32 user = msg_user(hdr);
u32 type = msg_type(hdr);
int rc = -ENOKEY; int rc = -ENOKEY;
u8 tx_key; u8 tx_key = 0;
/* No encryption? */ /* No encryption? */
if (!tx->working) if (!tx->working)
return 0; return 0;
/* Try with the pending key if available and: /* Pending key if peer has active on it or probing time */
* 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;
*/
if (unlikely(key.pending)) { if (unlikely(key.pending)) {
tx_key = key.pending; tx_key = key.pending;
if (!key.active) if (!tx->key_master && !key.active)
goto encrypt; goto encrypt;
if (__rx && atomic_read(&__rx->peer_rx_active) == tx_key) if (__rx && atomic_read(&__rx->peer_rx_active) == tx_key)
goto encrypt; 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; goto encrypt;
if (!__rx && }
time_after(jiffies, tx->timer2 + TIPC_TX_PROBE_LIM)) { if (user == LINK_CONFIG || user == LINK_PROTOCOL)
tx->timer2 = jiffies; tipc_crypto_clone_msg(net, *skb, b, dst, __dnode,
probe = skb_clone(*skb, GFP_ATOMIC); SKB_PROBING);
if (probe) { }
TIPC_SKB_CB(probe)->probe = 1;
tipc_crypto_xmit(net, &probe, b, dst, __dnode); /* Master key if this is a *vital* message or in grace period */
if (probe) if (tx->key_master) {
b->media->send_msg(net, probe, b, dst); 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 */ /* Else, use the active key if any */
if (likely(key.active)) { if (likely(key.active)) {
tx_key = key.active; tx_key = key.active;
goto encrypt; goto encrypt;
} }
goto exit; goto exit;
encrypt: encrypt:
...@@ -1667,30 +1814,21 @@ int tipc_crypto_rcv(struct net *net, struct tipc_crypto *rx, ...@@ -1667,30 +1814,21 @@ int tipc_crypto_rcv(struct net *net, struct tipc_crypto *rx,
struct tipc_aead *aead = NULL; struct tipc_aead *aead = NULL;
struct tipc_key key; struct tipc_key key;
int rc = -ENOKEY; int rc = -ENOKEY;
u8 tx_key = 0; u8 tx_key, n;
tx_key = ((struct tipc_ehdr *)(*skb)->data)->tx_key;
/* New peer? /* New peer?
* Let's try with TX key (i.e. cluster mode) & verify the skb first! * 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; goto pick_tx;
/* Pick RX key according to TX key, three cases are possible: /* Pick RX key according to TX key if any */
* 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;
key = rx->key; key = rx->key;
if (likely(tx_key == key.active)) if (tx_key == key.active || tx_key == key.pending ||
goto decrypt; tx_key == key.passive)
if (tx_key == key.pending)
goto decrypt; 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) */ /* Unknown key, let's try to align RX key(s) */
if (tipc_crypto_key_try_align(rx, tx_key)) if (tipc_crypto_key_try_align(rx, tx_key))
...@@ -1698,7 +1836,7 @@ int tipc_crypto_rcv(struct net *net, struct tipc_crypto *rx, ...@@ -1698,7 +1836,7 @@ int tipc_crypto_rcv(struct net *net, struct tipc_crypto *rx,
pick_tx: pick_tx:
/* No key suitable? Try to pick one from 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) if (aead)
goto decrypt; goto decrypt;
goto exit; goto exit;
...@@ -1726,8 +1864,19 @@ int tipc_crypto_rcv(struct net *net, struct tipc_crypto *rx, ...@@ -1726,8 +1864,19 @@ int tipc_crypto_rcv(struct net *net, struct tipc_crypto *rx,
if (rc == -ENOKEY) { if (rc == -ENOKEY) {
kfree_skb(*skb); kfree_skb(*skb);
*skb = NULL; *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); tipc_node_put(rx->node);
}
this_cpu_inc(stats->stat[STAT_NOKEYS]); this_cpu_inc(stats->stat[STAT_NOKEYS]);
return rc; return rc;
} else if (rc == -EBADMSG) { } else if (rc == -EBADMSG) {
...@@ -1749,21 +1898,17 @@ static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead, ...@@ -1749,21 +1898,17 @@ static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead,
struct tipc_aead *tmp = NULL; struct tipc_aead *tmp = NULL;
struct tipc_ehdr *ehdr; struct tipc_ehdr *ehdr;
struct tipc_node *n; struct tipc_node *n;
u8 rx_key_active;
bool destined;
/* Is this completed by TX? */ /* Is this completed by TX? */
if (unlikely(!rx->node)) { if (unlikely(is_tx(aead->crypto))) {
rx = skb_cb->tx_clone_ctx.rx; rx = skb_cb->tx_clone_ctx.rx;
#ifdef TIPC_CRYPTO_DEBUG pr_debug("TX->RX(%s): err %d, aead %p, skb->next %p, flags %x\n",
pr_info("TX->RX(%s): err %d, aead %p, skb->next %p, flags %x\n",
(rx) ? tipc_node_get_id_str(rx->node) : "-", err, aead, (rx) ? tipc_node_get_id_str(rx->node) : "-", err, aead,
(*skb)->next, skb_cb->flags); (*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, skb_cb->tx_clone_ctx.recurs, skb_cb->tx_clone_ctx.last,
aead->crypto->aead[1], aead->crypto->aead[2], aead->crypto->aead[1], aead->crypto->aead[2],
aead->crypto->aead[3]); aead->crypto->aead[3]);
#endif
if (unlikely(err)) { if (unlikely(err)) {
if (err == -EBADMSG && (*skb)->next) if (err == -EBADMSG && (*skb)->next)
tipc_rcv(net, (*skb)->next, b); tipc_rcv(net, (*skb)->next, b);
...@@ -1784,12 +1929,12 @@ static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead, ...@@ -1784,12 +1929,12 @@ static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead,
goto free_skb; goto free_skb;
} }
/* Skip cloning this time as we had a RX pending key */ /* Ignore cloning if it was TX master key */
if (rx->key.pending) if (ehdr->tx_key == KEY_MASTER)
goto rcv; goto rcv;
if (tipc_aead_clone(&tmp, aead) < 0) if (tipc_aead_clone(&tmp, aead) < 0)
goto rcv; 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); tipc_aead_free(&tmp->rcu);
goto rcv; goto rcv;
} }
...@@ -1805,14 +1950,18 @@ static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead, ...@@ -1805,14 +1950,18 @@ static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead,
/* Set the RX key's user */ /* Set the RX key's user */
tipc_aead_users_set(aead, 1); tipc_aead_users_set(aead, 1);
rcv:
/* Mark this point, RX works */ /* Mark this point, RX works */
rx->timer1 = jiffies; rx->timer1 = jiffies;
rcv:
/* Remove ehdr & auth. tag prior to tipc_rcv() */ /* Remove ehdr & auth. tag prior to tipc_rcv() */
ehdr = (struct tipc_ehdr *)(*skb)->data; 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)); skb_pull(*skb, tipc_ehdr_size(ehdr));
pskb_trim(*skb, (*skb)->len - aead->authsize); pskb_trim(*skb, (*skb)->len - aead->authsize);
...@@ -1822,9 +1971,8 @@ static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead, ...@@ -1822,9 +1971,8 @@ static void tipc_crypto_rcv_complete(struct net *net, struct tipc_aead *aead,
goto free_skb; goto free_skb;
} }
/* Update peer RX active key & TX users */ /* Ok, everything's fine, try to synch own keys according to peers' */
if (destined) tipc_crypto_key_synch(rx, *skb);
tipc_crypto_key_synch(rx, rx_key_active, buf_msg(*skb));
/* Mark skb decrypted */ /* Mark skb decrypted */
skb_cb->decrypted = 1; skb_cb->decrypted = 1;
...@@ -1883,7 +2031,7 @@ static void tipc_crypto_do_cmd(struct net *net, int cmd) ...@@ -1883,7 +2031,7 @@ static void tipc_crypto_do_cmd(struct net *net, int cmd)
/* Print crypto statistics */ /* Print crypto statistics */
for (i = 0, j = 0; i < MAX_STATS; i++) for (i = 0, j = 0; i < MAX_STATS; i++)
j += scnprintf(buf + j, 200 - j, "|%11s ", hstats[i]); j += scnprintf(buf + j, 200 - j, "|%11s ", hstats[i]);
pr_info("\nCounter %s", buf); pr_info("Counter %s", buf);
memset(buf, '-', 115); memset(buf, '-', 115);
buf[115] = '\0'; buf[115] = '\0';
...@@ -1927,6 +2075,15 @@ static char *tipc_crypto_key_dump(struct tipc_crypto *c, char *buf) ...@@ -1927,6 +2075,15 @@ static char *tipc_crypto_key_dump(struct tipc_crypto *c, char *buf)
char *s; char *s;
for (k = KEY_MIN; k <= KEY_MAX; k++) { 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) if (k == key.passive)
s = "PAS"; s = "PAS";
else if (k == key.active) else if (k == key.active)
...@@ -1935,13 +2092,14 @@ static char *tipc_crypto_key_dump(struct tipc_crypto *c, char *buf) ...@@ -1935,13 +2092,14 @@ static char *tipc_crypto_key_dump(struct tipc_crypto *c, char *buf)
s = "PEN"; s = "PEN";
else else
s = "-"; s = "-";
}
i += scnprintf(buf + i, 200 - i, "\tKey%d: %s", k, s); i += scnprintf(buf + i, 200 - i, "\tKey%d: %s", k, s);
rcu_read_lock(); rcu_read_lock();
aead = rcu_dereference(c->aead[k]); aead = rcu_dereference(c->aead[k]);
if (aead) if (aead)
i += scnprintf(buf + i, 200 - i, i += scnprintf(buf + i, 200 - i,
"{\"%s...\", \"%s\"}/%d:%d", "{\"0x...%s\", \"%s\"}/%d:%d",
aead->hint, aead->hint,
(aead->mode == CLUSTER_KEY) ? "c" : "p", (aead->mode == CLUSTER_KEY) ? "c" : "p",
atomic_read(&aead->users), atomic_read(&aead->users),
...@@ -1950,14 +2108,13 @@ static char *tipc_crypto_key_dump(struct tipc_crypto *c, char *buf) ...@@ -1950,14 +2108,13 @@ static char *tipc_crypto_key_dump(struct tipc_crypto *c, char *buf)
i += scnprintf(buf + i, 200 - i, "\n"); 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", i += scnprintf(buf + i, 200 - i, "\tPeer RX active: %d\n",
atomic_read(&c->peer_rx_active)); atomic_read(&c->peer_rx_active));
return buf; return buf;
} }
#ifdef TIPC_CRYPTO_DEBUG
static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new, static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new,
char *buf) char *buf)
{ {
...@@ -1968,7 +2125,7 @@ static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new, ...@@ -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 */ /* Output format: "[%s %s %s] -> [%s %s %s]", max len = 32 */
again: again:
i += scnprintf(buf + i, 32 - i, "["); 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) if (k == key->passive)
s = "pas"; s = "pas";
else if (k == key->active) else if (k == key->active)
...@@ -1978,7 +2135,7 @@ static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new, ...@@ -1978,7 +2135,7 @@ static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new,
else else
s = "-"; s = "-";
i += scnprintf(buf + i, 32 - i, i += scnprintf(buf + i, 32 - i,
(k != KEY_MAX) ? "%s " : "%s", s); (k != KEY_3) ? "%s " : "%s", s);
} }
if (key != &new) { if (key != &new) {
i += scnprintf(buf + i, 32 - i, "] -> "); i += scnprintf(buf + i, 32 - i, "] -> ");
...@@ -1988,4 +2145,320 @@ static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new, ...@@ -1988,4 +2145,320 @@ static char *tipc_key_change_dump(struct tipc_key old, struct tipc_key new,
i += scnprintf(buf + i, 32 - i, "]"); i += scnprintf(buf + i, 32 - i, "]");
return buf; 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 { ...@@ -67,6 +67,7 @@ enum {
}; };
extern int sysctl_tipc_max_tfms __read_mostly; extern int sysctl_tipc_max_tfms __read_mostly;
extern int sysctl_tipc_key_exchange_enabled __read_mostly;
/** /**
* TIPC encryption message format: * TIPC encryption message format:
...@@ -74,7 +75,7 @@ extern int sysctl_tipc_max_tfms __read_mostly; ...@@ -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 * 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 * 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 | * w1:| Seqno |
* w2:| (8 octets) | * w2:| (8 octets) |
...@@ -101,6 +102,9 @@ extern int sysctl_tipc_max_tfms __read_mostly; ...@@ -101,6 +102,9 @@ extern int sysctl_tipc_max_tfms __read_mostly;
* RX : Currently RX active key corresponding to the destination * RX : Currently RX active key corresponding to the destination
* node's TX key (when the "D" bit is set) * node's TX key (when the "D" bit is set)
* K : Keep-alive bit (for RPS, LINK_PROTOCOL/STATE_MSG only) * 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 * Rsvd : Reserved bit, field
* Word1-2: * Word1-2:
* Seqno : The 64-bit sequence number of the encrypted message, also * Seqno : The 64-bit sequence number of the encrypted message, also
...@@ -117,7 +121,9 @@ struct tipc_ehdr { ...@@ -117,7 +121,9 @@ struct tipc_ehdr {
__u8 destined:1, __u8 destined:1,
user:4, user:4,
version:3; version:3;
__u8 reserved_1:3, __u8 reserved_1:1,
rx_nokey:1,
master_key:1,
keepalive:1, keepalive:1,
rx_key_active:2, rx_key_active:2,
tx_key:2; tx_key:2;
...@@ -128,7 +134,9 @@ struct tipc_ehdr { ...@@ -128,7 +134,9 @@ struct tipc_ehdr {
__u8 tx_key:2, __u8 tx_key:2,
rx_key_active:2, rx_key_active:2,
keepalive:1, keepalive:1,
reserved_1:3; master_key:1,
rx_nokey:1,
reserved_1:1;
#else #else
#error "Please fix <asm/byteorder.h>" #error "Please fix <asm/byteorder.h>"
#endif #endif
...@@ -158,10 +166,35 @@ int tipc_crypto_xmit(struct net *net, struct sk_buff **skb, ...@@ -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, int tipc_crypto_rcv(struct net *net, struct tipc_crypto *rx,
struct sk_buff **skb, struct tipc_bearer *b); struct sk_buff **skb, struct tipc_bearer *b);
int tipc_crypto_key_init(struct tipc_crypto *c, struct tipc_aead_key *ukey, 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); 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); 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 /* _TIPC_CRYPTO_H */
#endif #endif
...@@ -1250,6 +1250,11 @@ static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb, ...@@ -1250,6 +1250,11 @@ static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb,
case MSG_FRAGMENTER: case MSG_FRAGMENTER:
case BCAST_PROTOCOL: case BCAST_PROTOCOL:
return false; return false;
#ifdef CONFIG_TIPC_CRYPTO
case MSG_CRYPTO:
tipc_crypto_msg_rcv(l->net, skb);
return true;
#endif
default: default:
pr_warn("Dropping received illegal msg type\n"); pr_warn("Dropping received illegal msg type\n");
kfree_skb(skb); kfree_skb(skb);
......
...@@ -82,6 +82,7 @@ struct plist; ...@@ -82,6 +82,7 @@ struct plist;
#define NAME_DISTRIBUTOR 11 #define NAME_DISTRIBUTOR 11
#define MSG_FRAGMENTER 12 #define MSG_FRAGMENTER 12
#define LINK_CONFIG 13 #define LINK_CONFIG 13
#define MSG_CRYPTO 14
#define SOCK_WAKEUP 14 /* pseudo user */ #define SOCK_WAKEUP 14 /* pseudo user */
#define TOP_SRV 15 /* pseudo user */ #define TOP_SRV 15 /* pseudo user */
...@@ -127,7 +128,9 @@ struct tipc_skb_cb { ...@@ -127,7 +128,9 @@ struct tipc_skb_cb {
#ifdef CONFIG_TIPC_CRYPTO #ifdef CONFIG_TIPC_CRYPTO
u8 encrypted:1; u8 encrypted:1;
u8 decrypted:1; u8 decrypted:1;
u8 probe:1; #define SKB_PROBING 1
#define SKB_GRACING 2
u8 xmit_type:2;
u8 tx_clone_deferred:1; u8 tx_clone_deferred:1;
#endif #endif
}; };
...@@ -747,6 +750,9 @@ static inline void msg_set_nameupper(struct tipc_msg *m, u32 n) ...@@ -747,6 +750,9 @@ static inline void msg_set_nameupper(struct tipc_msg *m, u32 n)
#define GRP_RECLAIM_MSG 4 #define GRP_RECLAIM_MSG 4
#define GRP_REMIT_MSG 5 #define GRP_REMIT_MSG 5
/* Crypto message types */
#define KEY_DISTR_MSG 0
/* /*
* Word 1 * Word 1
*/ */
......
...@@ -108,6 +108,8 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = { ...@@ -108,6 +108,8 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {
.len = TIPC_NODEID_LEN}, .len = TIPC_NODEID_LEN},
[TIPC_NLA_NODE_KEY] = { .type = NLA_BINARY, [TIPC_NLA_NODE_KEY] = { .type = NLA_BINARY,
.len = TIPC_AEAD_KEY_SIZE_MAX}, .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 */ /* Properties valid for media, bearer and link */
......
...@@ -278,6 +278,14 @@ struct tipc_crypto *tipc_node_crypto_rx_by_list(struct list_head *pos) ...@@ -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; 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 #endif
static void tipc_node_free(struct rcu_head *rp) static void tipc_node_free(struct rcu_head *rp)
...@@ -303,7 +311,7 @@ void tipc_node_put(struct tipc_node *node) ...@@ -303,7 +311,7 @@ void tipc_node_put(struct tipc_node *node)
kref_put(&node->kref, tipc_node_kref_release); 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); kref_get(&node->kref);
} }
...@@ -584,6 +592,9 @@ static void tipc_node_calculate_timer(struct tipc_node *n, struct tipc_link *l) ...@@ -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) 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); list_del_rcu(&node->list);
hlist_del_rcu(&node->hash); hlist_del_rcu(&node->hash);
tipc_node_put(node); tipc_node_put(node);
...@@ -2868,15 +2879,27 @@ static int tipc_nl_retrieve_nodeid(struct nlattr **attrs, u8 **node_id) ...@@ -2868,15 +2879,27 @@ static int tipc_nl_retrieve_nodeid(struct nlattr **attrs, u8 **node_id)
return 0; 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) static int __tipc_nl_node_set_key(struct sk_buff *skb, struct genl_info *info)
{ {
struct nlattr *attrs[TIPC_NLA_NODE_MAX + 1]; struct nlattr *attrs[TIPC_NLA_NODE_MAX + 1];
struct net *net = sock_net(skb->sk); 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_node *n = NULL;
struct tipc_aead_key *ukey; struct tipc_aead_key *ukey;
struct tipc_crypto *c; bool rekeying = true, master_key = false;
u8 *id, *own_id; u8 *id, *own_id, mode;
u32 intv = 0;
int rc = 0; int rc = 0;
if (!info->attrs[TIPC_NLA_NODE]) 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) ...@@ -2886,52 +2909,66 @@ static int __tipc_nl_node_set_key(struct sk_buff *skb, struct genl_info *info)
info->attrs[TIPC_NLA_NODE], info->attrs[TIPC_NLA_NODE],
tipc_nl_node_policy, info->extack); tipc_nl_node_policy, info->extack);
if (rc) if (rc)
goto exit; return rc;
own_id = tipc_own_id(net); own_id = tipc_own_id(net);
if (!own_id) { if (!own_id) {
rc = -EPERM; GENL_SET_ERR_MSG(info, "not found own node identity (set id?)");
goto exit; return -EPERM;
} }
rc = tipc_nl_retrieve_rekeying(attrs, &intv);
if (rc == -ENODATA)
rekeying = false;
rc = tipc_nl_retrieve_key(attrs, &ukey); rc = tipc_nl_retrieve_key(attrs, &ukey);
if (rc) if (rc == -ENODATA && rekeying)
goto exit; goto rekeying;
else if (rc)
return rc;
rc = tipc_aead_key_validate(ukey); rc = tipc_aead_key_validate(ukey, info);
if (rc) if (rc)
goto exit; return rc;
rc = tipc_nl_retrieve_nodeid(attrs, &id); rc = tipc_nl_retrieve_nodeid(attrs, &id);
switch (rc) { switch (rc) {
case -ENODATA: case -ENODATA:
/* Cluster key mode */ mode = CLUSTER_KEY;
rc = tipc_crypto_key_init(tn->crypto_tx, ukey, CLUSTER_KEY); master_key = !!(attrs[TIPC_NLA_NODE_KEY_MASTER]);
break; break;
case 0: case 0:
/* Per-node key mode */ mode = PER_NODE_KEY;
if (!memcmp(id, own_id, NODE_ID_LEN)) { if (memcmp(id, own_id, NODE_ID_LEN)) {
c = tn->crypto_tx;
} else {
n = tipc_node_find_by_id(net, id) ?: n = tipc_node_find_by_id(net, id) ?:
tipc_node_create(net, 0, id, 0xffffu, 0, true); tipc_node_create(net, 0, id, 0xffffu, 0, true);
if (unlikely(!n)) { if (unlikely(!n))
rc = -ENOMEM; return -ENOMEM;
break;
}
c = n->crypto_rx; 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) if (n)
tipc_node_put(n); tipc_node_put(n);
break;
default: if (unlikely(rc < 0)) {
break; 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 0;
return (rc < 0) ? rc : 0;
} }
int tipc_nl_node_set_key(struct sk_buff *skb, struct genl_info *info) 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, ...@@ -2958,7 +2995,6 @@ static int __tipc_nl_node_flush_key(struct sk_buff *skb,
tipc_crypto_key_flush(n->crypto_rx); tipc_crypto_key_flush(n->crypto_rx);
rcu_read_unlock(); rcu_read_unlock();
pr_info("All keys are flushed!\n");
return 0; return 0;
} }
......
...@@ -79,12 +79,14 @@ bool tipc_node_get_id(struct net *net, u32 addr, u8 *id); ...@@ -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); u32 tipc_node_get_addr(struct tipc_node *node);
char *tipc_node_get_id_str(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_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, struct tipc_node *tipc_node_create(struct net *net, u32 addr, u8 *peer_id,
u16 capabilities, u32 hash_mixes, u16 capabilities, u32 hash_mixes,
bool preliminary); bool preliminary);
#ifdef CONFIG_TIPC_CRYPTO #ifdef CONFIG_TIPC_CRYPTO
struct tipc_crypto *tipc_node_crypto_rx(struct tipc_node *__n); 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_list(struct list_head *pos);
struct tipc_crypto *tipc_node_crypto_rx_by_addr(struct net *net, u32 addr);
#endif #endif
u32 tipc_node_try_addr(struct net *net, u8 *id, u32 addr); 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, void tipc_node_check_dest(struct net *net, u32 onode, u8 *peer_id128,
......
...@@ -74,6 +74,15 @@ static struct ctl_table tipc_table[] = { ...@@ -74,6 +74,15 @@ static struct ctl_table tipc_table[] = {
.proc_handler = proc_dointvec_minmax, .proc_handler = proc_dointvec_minmax,
.extra1 = SYSCTL_ONE, .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 #endif
{ {
.procname = "bc_retruni", .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