lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20180314135927.GA28936@hmswarspite.think-freely.org>
Date:   Wed, 14 Mar 2018 09:59:28 -0400
From:   Neil Horman <nhorman@...driver.com>
To:     Xin Long <lucien.xin@...il.com>
Cc:     network dev <netdev@...r.kernel.org>, linux-sctp@...r.kernel.org,
        Marcelo Ricardo Leitner <marcelo.leitner@...il.com>,
        davem@...emloft.net
Subject: Re: [PATCH net-next 1/5] sctp: add refcnt support for sh_key

On Wed, Mar 14, 2018 at 07:05:30PM +0800, Xin Long wrote:
> With refcnt support for sh_key, chunks auth sh_keys can be decided
> before enqueuing it. Changing the active key later will not affect
> the chunks already enqueued.
> 
> Furthermore, this is necessary when adding the support for authinfo
> for sendmsg in next patch.
> 
> Note that struct sctp_chunk can't be grown due to that performance
> drop issue on slow cpu, so it just reuses head_skb memory for shkey
> in sctp_chunk.
> 
> Signed-off-by: Xin Long <lucien.xin@...il.com>
> ---
>  include/net/sctp/auth.h    |  9 +++--
>  include/net/sctp/sm.h      |  3 +-
>  include/net/sctp/structs.h |  9 +++--
>  net/sctp/auth.c            | 86 +++++++++++++++++++++++-----------------------
>  net/sctp/chunk.c           |  5 +++
>  net/sctp/output.c          | 18 ++++++++--
>  net/sctp/sm_make_chunk.c   | 15 ++++++--
>  net/sctp/sm_statefuns.c    | 11 +++---
>  net/sctp/socket.c          |  6 ++++
>  9 files changed, 104 insertions(+), 58 deletions(-)
> 
> diff --git a/include/net/sctp/auth.h b/include/net/sctp/auth.h
> index e5c57d0..017c1aa 100644
> --- a/include/net/sctp/auth.h
> +++ b/include/net/sctp/auth.h
> @@ -62,8 +62,9 @@ struct sctp_auth_bytes {
>  /* Definition for a shared key, weather endpoint or association */
>  struct sctp_shared_key {
>  	struct list_head key_list;
> -	__u16 key_id;
>  	struct sctp_auth_bytes *key;
> +	refcount_t refcnt;
> +	__u16 key_id;
>  };
>  
>  #define key_for_each(__key, __list_head) \
> @@ -103,8 +104,10 @@ int sctp_auth_send_cid(enum sctp_cid chunk,
>  int sctp_auth_recv_cid(enum sctp_cid chunk,
>  		       const struct sctp_association *asoc);
>  void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
> -			    struct sk_buff *skb,
> -			    struct sctp_auth_chunk *auth, gfp_t gfp);
> +			      struct sk_buff *skb, struct sctp_auth_chunk *auth,
> +			      struct sctp_shared_key *ep_key, gfp_t gfp);
> +void sctp_auth_shkey_release(struct sctp_shared_key *sh_key);
> +void sctp_auth_shkey_hold(struct sctp_shared_key *sh_key);
>  
>  /* API Helpers */
>  int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id);
> diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h
> index 2883c43..2d0e782 100644
> --- a/include/net/sctp/sm.h
> +++ b/include/net/sctp/sm.h
> @@ -263,7 +263,8 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
>  struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
>  				    __u32 new_cum_tsn, size_t nstreams,
>  				    struct sctp_fwdtsn_skip *skiplist);
> -struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc);
> +struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc,
> +				  __u16 key_id);
>  struct sctp_chunk *sctp_make_strreset_req(const struct sctp_association *asoc,
>  					  __u16 stream_num, __be16 *stream_list,
>  					  bool out, bool in);
> diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
> index ec6e46b..49ad67b 100644
> --- a/include/net/sctp/structs.h
> +++ b/include/net/sctp/structs.h
> @@ -577,8 +577,12 @@ struct sctp_chunk {
>  	/* This points to the sk_buff containing the actual data.  */
>  	struct sk_buff *skb;
>  
> -	/* In case of GSO packets, this will store the head one */
> -	struct sk_buff *head_skb;
> +	union {
> +		/* In case of GSO packets, this will store the head one */
> +		struct sk_buff *head_skb;
> +		/* In case of auth enabled, this will point to the shkey */
> +		struct sctp_shared_key *shkey;
> +	};
Why do you need to add this at all?  You add the shared key pointer to the
association in this patch, and sctp_chunk already has a pointer to the
association, so you should already be able to find the shared key via
chunk->asoc->shkey, no?


>  
>  	/* These are the SCTP headers by reverse order in a packet.
>  	 * Note that some of these may happen more than once.  In that
> @@ -1995,6 +1999,7 @@ struct sctp_association {
>  	 * The current generated assocaition shared key (secret)
>  	 */
>  	struct sctp_auth_bytes *asoc_shared_key;
> +	struct sctp_shared_key *shkey;
>  
>  	/* SCTP AUTH: hmac id of the first peer requested algorithm
>  	 * that we support.
> diff --git a/net/sctp/auth.c b/net/sctp/auth.c
> index 00667c5..e5214fd 100644
> --- a/net/sctp/auth.c
> +++ b/net/sctp/auth.c
> @@ -101,13 +101,14 @@ struct sctp_shared_key *sctp_auth_shkey_create(__u16 key_id, gfp_t gfp)
>  		return NULL;
>  
>  	INIT_LIST_HEAD(&new->key_list);
> +	refcount_set(&new->refcnt, 1);
>  	new->key_id = key_id;
>  
>  	return new;
>  }
>  
>  /* Free the shared key structure */
> -static void sctp_auth_shkey_free(struct sctp_shared_key *sh_key)
> +static void sctp_auth_shkey_destroy(struct sctp_shared_key *sh_key)
>  {
>  	BUG_ON(!list_empty(&sh_key->key_list));
>  	sctp_auth_key_put(sh_key->key);
> @@ -115,6 +116,17 @@ static void sctp_auth_shkey_free(struct sctp_shared_key *sh_key)
>  	kfree(sh_key);
>  }
>  
> +void sctp_auth_shkey_release(struct sctp_shared_key *sh_key)
> +{
> +	if (refcount_dec_and_test(&sh_key->refcnt))
> +		sctp_auth_shkey_destroy(sh_key);
> +}
> +
> +void sctp_auth_shkey_hold(struct sctp_shared_key *sh_key)
> +{
> +	refcount_inc(&sh_key->refcnt);
> +}
> +
>  /* Destroy the entire key list.  This is done during the
>   * associon and endpoint free process.
>   */
> @@ -128,7 +140,7 @@ void sctp_auth_destroy_keys(struct list_head *keys)
>  
>  	key_for_each_safe(ep_key, tmp, keys) {
>  		list_del_init(&ep_key->key_list);
> -		sctp_auth_shkey_free(ep_key);
> +		sctp_auth_shkey_release(ep_key);
>  	}
>  }
>  
> @@ -409,13 +421,19 @@ int sctp_auth_asoc_init_active_key(struct sctp_association *asoc, gfp_t gfp)
>  
>  	sctp_auth_key_put(asoc->asoc_shared_key);
>  	asoc->asoc_shared_key = secret;
> +	asoc->shkey = ep_key;
>  
>  	/* Update send queue in case any chunk already in there now
>  	 * needs authenticating
>  	 */
>  	list_for_each_entry(chunk, &asoc->outqueue.out_chunk_list, list) {
> -		if (sctp_auth_send_cid(chunk->chunk_hdr->type, asoc))
> +		if (sctp_auth_send_cid(chunk->chunk_hdr->type, asoc)) {
>  			chunk->auth = 1;
> +			if (!chunk->shkey) {
> +				chunk->shkey = asoc->shkey;
> +				sctp_auth_shkey_hold(chunk->shkey);
> +			}
> +		}
Do we really need to take a reference for every chunk we send?  Wouldn't it be
sufficient to just hold the shared key until the association is released?


>  	}
>  
>  	return 0;
> @@ -703,16 +721,15 @@ int sctp_auth_recv_cid(enum sctp_cid chunk, const struct sctp_association *asoc)
>   *    after the AUTH chunk in the SCTP packet.
>   */
>  void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
> -			      struct sk_buff *skb,
> -			      struct sctp_auth_chunk *auth,
> -			      gfp_t gfp)
> +			      struct sk_buff *skb, struct sctp_auth_chunk *auth,
> +			      struct sctp_shared_key *ep_key, gfp_t gfp)
>  {
> -	struct crypto_shash *tfm;
>  	struct sctp_auth_bytes *asoc_key;
> +	struct crypto_shash *tfm;
>  	__u16 key_id, hmac_id;
> -	__u8 *digest;
>  	unsigned char *end;
>  	int free_key = 0;
> +	__u8 *digest;
>  
>  	/* Extract the info we need:
>  	 * - hmac id
> @@ -724,12 +741,7 @@ void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
>  	if (key_id == asoc->active_key_id)
>  		asoc_key = asoc->asoc_shared_key;
>  	else {
> -		struct sctp_shared_key *ep_key;
> -
> -		ep_key = sctp_auth_get_shkey(asoc, key_id);
> -		if (!ep_key)
> -			return;
> -
> +		/* ep_key can't be NULL here */
>  		asoc_key = sctp_auth_asoc_create_secret(asoc, ep_key, gfp);
>  		if (!asoc_key)
>  			return;
> @@ -829,7 +841,7 @@ int sctp_auth_set_key(struct sctp_endpoint *ep,
>  		      struct sctp_association *asoc,
>  		      struct sctp_authkey *auth_key)
>  {
> -	struct sctp_shared_key *cur_key = NULL;
> +	struct sctp_shared_key *cur_key, *shkey;
>  	struct sctp_auth_bytes *key;
>  	struct list_head *sh_keys;
>  	int replace = 0;
> @@ -842,46 +854,34 @@ int sctp_auth_set_key(struct sctp_endpoint *ep,
>  	else
>  		sh_keys = &ep->endpoint_shared_keys;
>  
> -	key_for_each(cur_key, sh_keys) {
> -		if (cur_key->key_id == auth_key->sca_keynumber) {
> +	key_for_each(shkey, sh_keys) {
> +		if (shkey->key_id == auth_key->sca_keynumber) {
>  			replace = 1;
>  			break;
>  		}
>  	}
>  
> -	/* If we are not replacing a key id, we need to allocate
> -	 * a shared key.
> -	 */
> -	if (!replace) {
> -		cur_key = sctp_auth_shkey_create(auth_key->sca_keynumber,
> -						 GFP_KERNEL);
> -		if (!cur_key)
> -			return -ENOMEM;
> -	}
> +	cur_key = sctp_auth_shkey_create(auth_key->sca_keynumber, GFP_KERNEL);
> +	if (!cur_key)
> +		return -ENOMEM;
>  
>  	/* Create a new key data based on the info passed in */
>  	key = sctp_auth_create_key(auth_key->sca_keylength, GFP_KERNEL);
> -	if (!key)
> -		goto nomem;
> +	if (!key) {
> +		kfree(cur_key);
> +		return -ENOMEM;
> +	}
>  
>  	memcpy(key->data, &auth_key->sca_key[0], auth_key->sca_keylength);
> +	cur_key->key = key;
>  
> -	/* If we are replacing, remove the old keys data from the
> -	 * key id.  If we are adding new key id, add it to the
> -	 * list.
> -	 */
> -	if (replace)
> -		sctp_auth_key_put(cur_key->key);
> -	else
> -		list_add(&cur_key->key_list, sh_keys);
> +	if (replace) {
> +		list_del_init(&shkey->key_list);
> +		sctp_auth_shkey_release(shkey);
> +	}
> +	list_add(&cur_key->key_list, sh_keys);
>  
> -	cur_key->key = key;
>  	return 0;
> -nomem:
> -	if (!replace)
> -		sctp_auth_shkey_free(cur_key);
> -
> -	return -ENOMEM;
>  }
>  
>  int sctp_auth_set_active_key(struct sctp_endpoint *ep,
> @@ -952,7 +952,7 @@ int sctp_auth_del_key_id(struct sctp_endpoint *ep,
>  
>  	/* Delete the shared key */
>  	list_del_init(&key->key_list);
> -	sctp_auth_shkey_free(key);
> +	sctp_auth_shkey_release(key);
>  
>  	return 0;
>  }
> diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c
> index 991a530..9f28a9a 100644
> --- a/net/sctp/chunk.c
> +++ b/net/sctp/chunk.c
> @@ -168,6 +168,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
>  {
>  	size_t len, first_len, max_data, remaining;
>  	size_t msg_len = iov_iter_count(from);
> +	struct sctp_shared_key *shkey = NULL;
>  	struct list_head *pos, *temp;
>  	struct sctp_chunk *chunk;
>  	struct sctp_datamsg *msg;
> @@ -204,6 +205,8 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
>  		if (hmac_desc)
>  			max_data -= SCTP_PAD4(sizeof(struct sctp_auth_chunk) +
>  					      hmac_desc->hmac_len);
> +
> +		shkey = asoc->shkey;
>  	}
>  
>  	/* Check what's our max considering the above */
> @@ -275,6 +278,8 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
>  		if (err < 0)
>  			goto errout_chunk_free;
>  
> +		chunk->shkey = shkey;
> +
>  		/* Put the chunk->skb back into the form expected by send.  */
>  		__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr -
>  				       chunk->skb->data);
> diff --git a/net/sctp/output.c b/net/sctp/output.c
> index 01a26ee0..d6e1c90 100644
> --- a/net/sctp/output.c
> +++ b/net/sctp/output.c
> @@ -241,10 +241,13 @@ static enum sctp_xmit sctp_packet_bundle_auth(struct sctp_packet *pkt,
>  	if (!chunk->auth)
>  		return retval;
>  
> -	auth = sctp_make_auth(asoc);
> +	auth = sctp_make_auth(asoc, chunk->shkey->key_id);
>  	if (!auth)
>  		return retval;
>  
> +	auth->shkey = chunk->shkey;
> +	sctp_auth_shkey_hold(auth->shkey);
> +
>  	retval = __sctp_packet_append_chunk(pkt, auth);
>  
>  	if (retval != SCTP_XMIT_OK)
> @@ -490,7 +493,8 @@ static int sctp_packet_pack(struct sctp_packet *packet,
>  		}
>  
>  		if (auth) {
> -			sctp_auth_calculate_hmac(tp->asoc, nskb, auth, gfp);
> +			sctp_auth_calculate_hmac(tp->asoc, nskb, auth,
> +						 packet->auth->shkey, gfp);
>  			/* free auth if no more chunks, or add it back */
>  			if (list_empty(&packet->chunk_list))
>  				sctp_chunk_free(packet->auth);
> @@ -770,6 +774,16 @@ static enum sctp_xmit sctp_packet_will_fit(struct sctp_packet *packet,
>  	enum sctp_xmit retval = SCTP_XMIT_OK;
>  	size_t psize, pmtu, maxsize;
>  
> +	/* Don't bundle in this packet if this chunk's auth key doesn't
> +	 * match other chunks already enqueued on this packet. Also,
> +	 * don't bundle the chunk with auth key if other chunks in this
> +	 * packet don't have auth key.
> +	 */
> +	if ((packet->auth && chunk->shkey != packet->auth->shkey) ||
> +	    (!packet->auth && chunk->shkey &&
> +	     chunk->chunk_hdr->type != SCTP_CID_AUTH))
> +		return SCTP_XMIT_PMTU_FULL;
> +
>  	psize = packet->size;
>  	if (packet->transport->asoc)
>  		pmtu = packet->transport->asoc->pathmtu;
> diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
> index d01475f..10f071c 100644
> --- a/net/sctp/sm_make_chunk.c
> +++ b/net/sctp/sm_make_chunk.c
> @@ -87,7 +87,10 @@ static void  *sctp_addto_chunk_fixed(struct sctp_chunk *, int len,
>  /* Control chunk destructor */
>  static void sctp_control_release_owner(struct sk_buff *skb)
>  {
> -	/*TODO: do memory release */
> +	struct sctp_chunk *chunk = skb_shinfo(skb)->destructor_arg;
> +
> +	if (chunk->shkey)
> +		sctp_auth_shkey_release(chunk->shkey);
>  }
>  
>  static void sctp_control_set_owner_w(struct sctp_chunk *chunk)
> @@ -102,7 +105,12 @@ static void sctp_control_set_owner_w(struct sctp_chunk *chunk)
>  	 *
>  	 *  For now don't do anything for now.
>  	 */
> +	if (chunk->auth) {
> +		chunk->shkey = asoc->shkey;
> +		sctp_auth_shkey_hold(chunk->shkey);
> +	}
>  	skb->sk = asoc ? asoc->base.sk : NULL;
> +	skb_shinfo(skb)->destructor_arg = chunk;
>  	skb->destructor = sctp_control_release_owner;
>  }
>  
> @@ -1271,7 +1279,8 @@ struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc,
>  	return retval;
>  }
>  
> -struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc)
> +struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc,
> +				  __u16 key_id)
>  {
>  	struct sctp_authhdr auth_hdr;
>  	struct sctp_hmac *hmac_desc;
> @@ -1289,7 +1298,7 @@ struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc)
>  		return NULL;
>  
>  	auth_hdr.hmac_id = htons(hmac_desc->hmac_id);
> -	auth_hdr.shkey_id = htons(asoc->active_key_id);
> +	auth_hdr.shkey_id = htons(key_id);
>  
>  	retval->subh.auth_hdr = sctp_addto_chunk(retval, sizeof(auth_hdr),
>  						 &auth_hdr);
> diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
> index eb7905f..792e0e2 100644
> --- a/net/sctp/sm_statefuns.c
> +++ b/net/sctp/sm_statefuns.c
> @@ -4114,6 +4114,7 @@ static enum sctp_ierror sctp_sf_authenticate(
>  					const union sctp_subtype type,
>  					struct sctp_chunk *chunk)
>  {
> +	struct sctp_shared_key *sh_key = NULL;
>  	struct sctp_authhdr *auth_hdr;
>  	__u8 *save_digest, *digest;
>  	struct sctp_hmac *hmac;
> @@ -4135,9 +4136,11 @@ static enum sctp_ierror sctp_sf_authenticate(
>  	 * configured
>  	 */
>  	key_id = ntohs(auth_hdr->shkey_id);
> -	if (key_id != asoc->active_key_id && !sctp_auth_get_shkey(asoc, key_id))
> -		return SCTP_IERROR_AUTH_BAD_KEYID;
> -
> +	if (key_id != asoc->active_key_id) {
> +		sh_key = sctp_auth_get_shkey(asoc, key_id);
> +		if (!sh_key)
> +			return SCTP_IERROR_AUTH_BAD_KEYID;
> +	}
>  
>  	/* Make sure that the length of the signature matches what
>  	 * we expect.
> @@ -4166,7 +4169,7 @@ static enum sctp_ierror sctp_sf_authenticate(
>  
>  	sctp_auth_calculate_hmac(asoc, chunk->skb,
>  				 (struct sctp_auth_chunk *)chunk->chunk_hdr,
> -				 GFP_ATOMIC);
> +				 sh_key, GFP_ATOMIC);
>  
>  	/* Discard the packet if the digests do not match */
>  	if (memcmp(save_digest, digest, sig_len)) {
> diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> index af5cf29..003a4ad 100644
> --- a/net/sctp/socket.c
> +++ b/net/sctp/socket.c
> @@ -156,6 +156,9 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
>  	/* The sndbuf space is tracked per association.  */
>  	sctp_association_hold(asoc);
>  
> +	if (chunk->shkey)
> +		sctp_auth_shkey_hold(chunk->shkey);
> +
>  	skb_set_owner_w(chunk->skb, sk);
>  
>  	chunk->skb->destructor = sctp_wfree;
> @@ -8109,6 +8112,9 @@ static void sctp_wfree(struct sk_buff *skb)
>  	sk->sk_wmem_queued   -= skb->truesize;
>  	sk_mem_uncharge(sk, skb->truesize);
>  
> +	if (chunk->shkey)
> +		sctp_auth_shkey_release(chunk->shkey);
> +
>  	sock_wfree(skb);
>  	sctp_wake_up_waiters(sk, asoc);
>  
> -- 
> 2.1.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
> the body of a message to majordomo@...r.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ