diff options
| author | Mark Brown <broonie@kernel.org> | 2020-12-11 17:48:50 +0000 | 
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2020-12-11 17:48:50 +0000 | 
| commit | 58f7553fa424fd0fd74e8b796d50c66014cebebe (patch) | |
| tree | 8aecb1d047b1df2abbfa1ef323d2a724a6a61c77 /net/tipc/crypto.c | |
| parent | dd91c555461261fed220ae29a508f508a0afeb43 (diff) | |
| parent | 9326e4f1e5dd1a4410c429638d3c412b6fc17040 (diff) | |
Merge remote-tracking branch 'spi/for-5.10' into spi-linus
Diffstat (limited to 'net/tipc/crypto.c')
| -rw-r--r-- | net/tipc/crypto.c | 981 | 
1 files changed, 727 insertions, 254 deletions
| diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c index 7c523dc81575..740ab9ae41a6 100644 --- a/net/tipc/crypto.c +++ b/net/tipc/crypto.c @@ -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); +	kfree_sensitive(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", -			skb_tailroom(skb), tailen); +		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), -		tipc_key_change_dump(old, c->key, buf), -		__builtin_return_address(0)); -#endif +	pr_debug("%s: key changing %s ::%pS\n", c->name, +		 tipc_key_change_dump(old, c->key, buf), +		 __builtin_return_address(0));  }  /** @@ -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 @@ exit:  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 @@ exit:   * @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 @@ exit:   */  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 @@ s5:  	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 @@ s5:   * @__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 (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 (!__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 && 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)) +	if (tx_key == key.active || tx_key == key.pending || +	    tx_key == key.passive)  		goto decrypt; -	if (tx_key == key.pending) -		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 @@ exit:  		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", -			(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", -			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 +		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_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]);  		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 @@ rcv:  		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 @@ print_stats:  	/* 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,21 +2075,31 @@ 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.passive) -			s = "PAS"; -		else if (k == key.active) -			s = "ACT"; -		else if (k == key.pending) -			s = "PEN"; -		else -			s = "-"; +		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) +				s = "ACT"; +			else if (k == key.pending) +				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 @@ again:  		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 @@ again:  	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); +		kfree_sensitive(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); +} | 
