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@gmail.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
/* Definition for a shared key, weather endpoint or association */
struct sctp_shared_key {
struct list_head key_list;
/* Definition for a shared key, weather endpoint or association */
struct sctp_shared_key {
struct list_head key_list;
struct sctp_auth_bytes *key;
struct sctp_auth_bytes *key;
+ refcount_t refcnt;
+ __u16 key_id;
};
#define key_for_each(__key, __list_head) \
};
#define key_for_each(__key, __list_head) \
int sctp_auth_recv_cid(enum sctp_cid chunk,
const struct sctp_association *asoc);
void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
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);
/* API Helpers */
int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id);
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_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);
struct sctp_chunk *sctp_make_strreset_req(const struct sctp_association *asoc,
__u16 stream_num, __be16 *stream_list,
bool out, bool in);
/* This points to the sk_buff containing the actual data. */
struct sk_buff *skb;
/* 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;
+ };
/* These are the SCTP headers by reverse order in a packet.
* Note that some of these may happen more than once. In that
/* These are the SCTP headers by reverse order in a packet.
* Note that some of these may happen more than once. In that
* The current generated assocaition shared key (secret)
*/
struct sctp_auth_bytes *asoc_shared_key;
* 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.
/* SCTP AUTH: hmac id of the first peer requested algorithm
* that we support.
return NULL;
INIT_LIST_HEAD(&new->key_list);
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 */
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);
{
BUG_ON(!list_empty(&sh_key->key_list));
sctp_auth_key_put(sh_key->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.
*/
/* Destroy the entire key list. This is done during the
* associon and endpoint free process.
*/
key_for_each_safe(ep_key, tmp, keys) {
list_del_init(&ep_key->key_list);
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);
sctp_auth_key_put(asoc->asoc_shared_key);
asoc->asoc_shared_key = secret;
sctp_auth_key_put(asoc->asoc_shared_key);
asoc->asoc_shared_key = secret;
/* Update send queue in case any chunk already in there now
* needs authenticating
*/
list_for_each_entry(chunk, &asoc->outqueue.out_chunk_list, list) {
/* 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)) {
+ if (!chunk->shkey) {
+ chunk->shkey = asoc->shkey;
+ sctp_auth_shkey_hold(chunk->shkey);
+ }
+ }
* after the AUTH chunk in the SCTP packet.
*/
void sctp_auth_calculate_hmac(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 sctp_auth_bytes *asoc_key;
+ struct crypto_shash *tfm;
unsigned char *end;
int free_key = 0;
unsigned char *end;
int free_key = 0;
/* Extract the info we need:
* - hmac id
/* Extract the info we need:
* - hmac id
if (key_id == asoc->active_key_id)
asoc_key = asoc->asoc_shared_key;
else {
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;
asoc_key = sctp_auth_asoc_create_secret(asoc, ep_key, gfp);
if (!asoc_key)
return;
struct sctp_association *asoc,
struct sctp_authkey *auth_key)
{
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;
struct sctp_auth_bytes *key;
struct list_head *sh_keys;
int replace = 0;
else
sh_keys = &ep->endpoint_shared_keys;
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) {
- /* 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);
/* 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);
memcpy(key->data, &auth_key->sca_key[0], auth_key->sca_keylength);
- /* 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);
-nomem:
- if (!replace)
- sctp_auth_shkey_free(cur_key);
-
- return -ENOMEM;
}
int sctp_auth_set_active_key(struct sctp_endpoint *ep,
}
int sctp_auth_set_active_key(struct sctp_endpoint *ep,
/* Delete the shared key */
list_del_init(&key->key_list);
/* Delete the shared key */
list_del_init(&key->key_list);
- sctp_auth_shkey_free(key);
+ sctp_auth_shkey_release(key);
{
size_t len, first_len, max_data, remaining;
size_t msg_len = iov_iter_count(from);
{
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;
struct list_head *pos, *temp;
struct sctp_chunk *chunk;
struct sctp_datamsg *msg;
if (hmac_desc)
max_data -= SCTP_PAD4(sizeof(struct sctp_auth_chunk) +
hmac_desc->hmac_len);
if (hmac_desc)
max_data -= SCTP_PAD4(sizeof(struct sctp_auth_chunk) +
hmac_desc->hmac_len);
}
/* Check what's our max considering the above */
}
/* Check what's our max considering the above */
if (err < 0)
goto errout_chunk_free;
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);
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr -
chunk->skb->data);
if (!chunk->auth)
return retval;
if (!chunk->auth)
return retval;
- auth = sctp_make_auth(asoc);
+ auth = sctp_make_auth(asoc, chunk->shkey->key_id);
if (!auth)
return retval;
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)
retval = __sctp_packet_append_chunk(pkt, auth);
if (retval != SCTP_XMIT_OK)
- 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);
/* free auth if no more chunks, or add it back */
if (list_empty(&packet->chunk_list))
sctp_chunk_free(packet->auth);
enum sctp_xmit retval = SCTP_XMIT_OK;
size_t psize, pmtu, maxsize;
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;
psize = packet->size;
if (packet->transport->asoc)
pmtu = packet->transport->asoc->pathmtu;
/* Control chunk destructor */
static void sctp_control_release_owner(struct sk_buff *skb)
{
/* 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)
}
static void sctp_control_set_owner_w(struct sctp_chunk *chunk)
*
* For now don't do anything for now.
*/
*
* 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->sk = asoc ? asoc->base.sk : NULL;
+ skb_shinfo(skb)->destructor_arg = chunk;
skb->destructor = sctp_control_release_owner;
}
skb->destructor = sctp_control_release_owner;
}
-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;
{
struct sctp_authhdr auth_hdr;
struct sctp_hmac *hmac_desc;
return NULL;
auth_hdr.hmac_id = htons(hmac_desc->hmac_id);
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);
retval->subh.auth_hdr = sctp_addto_chunk(retval, sizeof(auth_hdr),
&auth_hdr);
const union sctp_subtype type,
struct sctp_chunk *chunk)
{
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;
struct sctp_authhdr *auth_hdr;
__u8 *save_digest, *digest;
struct sctp_hmac *hmac;
* configured
*/
key_id = ntohs(auth_hdr->shkey_id);
* 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.
/* Make sure that the length of the signature matches what
* we expect.
sctp_auth_calculate_hmac(asoc, chunk->skb,
(struct sctp_auth_chunk *)chunk->chunk_hdr,
sctp_auth_calculate_hmac(asoc, chunk->skb,
(struct sctp_auth_chunk *)chunk->chunk_hdr,
/* Discard the packet if the digests do not match */
if (memcmp(save_digest, digest, sig_len)) {
/* Discard the packet if the digests do not match */
if (memcmp(save_digest, digest, sig_len)) {
/* The sndbuf space is tracked per association. */
sctp_association_hold(asoc);
/* 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;
skb_set_owner_w(chunk->skb, sk);
chunk->skb->destructor = sctp_wfree;
sk->sk_wmem_queued -= skb->truesize;
sk_mem_uncharge(sk, skb->truesize);
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);
sock_wfree(skb);
sctp_wake_up_waiters(sk, asoc);