[SCTP]: Enable the sending of the AUTH chunk.

SCTP-AUTH, Section 6.2:

   Endpoints MUST send all requested chunks authenticated where this has
   been requested by the peer.  The other chunks MAY be sent
   authenticated or not.  If endpoint pair shared keys are used, one of
   them MUST be selected for authentication.

   To send chunks in an authenticated way, the sender MUST include these
   chunks after an AUTH chunk.  This means that a sender MUST bundle
   chunks in order to authenticate them.

   If the endpoint has no endpoint pair shared key for the peer, it MUST
   use Shared Key Identifier 0 with an empty endpoint pair shared key.
   If there are multiple endpoint shared keys the sender selects one and
   uses the corresponding Shared Key Identifier

Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Vlad Yasevich 2007-09-16 19:32:45 -07:00 committed by David S. Miller
parent 730fc3d05c
commit 4cd57c8078
5 changed files with 160 additions and 28 deletions

View File

@ -256,6 +256,7 @@ 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);
void sctp_chunk_assign_tsn(struct sctp_chunk *);
void sctp_chunk_assign_ssn(struct sctp_chunk *);

View File

@ -798,6 +798,9 @@ struct sctp_packet {
/* This packet contains an AUTH chunk */
__u8 has_auth;
/* This packet contains at least 1 DATA chunk */
__u8 has_data;
/* SCTP cannot fragment this packet. So let ip fragment it. */
__u8 ipfragok;

View File

@ -194,6 +194,18 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
max = asoc->frag_point;
/* If the the peer requested that we authenticate DATA chunks
* we need to accound for bundling of the AUTH chunks along with
* DATA.
*/
if (sctp_auth_send_cid(SCTP_CID_DATA, asoc)) {
struct sctp_hmac *hmac_desc = sctp_auth_asoc_get_hmac(asoc);
if (hmac_desc)
max -= WORD_ROUND(sizeof(sctp_auth_chunk_t) +
hmac_desc->hmac_len);
}
whole = 0;
first_len = max;

View File

@ -80,6 +80,7 @@ struct sctp_packet *sctp_packet_config(struct sctp_packet *packet,
packet->has_cookie_echo = 0;
packet->has_sack = 0;
packet->has_auth = 0;
packet->has_data = 0;
packet->ipfragok = 0;
packet->auth = NULL;
@ -124,6 +125,7 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet,
packet->has_cookie_echo = 0;
packet->has_sack = 0;
packet->has_auth = 0;
packet->has_data = 0;
packet->ipfragok = 0;
packet->malloced = 0;
packet->auth = NULL;
@ -185,6 +187,39 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,
return retval;
}
/* Try to bundle an auth chunk into the packet. */
static sctp_xmit_t sctp_packet_bundle_auth(struct sctp_packet *pkt,
struct sctp_chunk *chunk)
{
struct sctp_association *asoc = pkt->transport->asoc;
struct sctp_chunk *auth;
sctp_xmit_t retval = SCTP_XMIT_OK;
/* if we don't have an association, we can't do authentication */
if (!asoc)
return retval;
/* See if this is an auth chunk we are bundling or if
* auth is already bundled.
*/
if (chunk->chunk_hdr->type == SCTP_CID_AUTH || pkt->auth)
return retval;
/* if the peer did not request this chunk to be authenticated,
* don't do it
*/
if (!chunk->auth)
return retval;
auth = sctp_make_auth(asoc);
if (!auth)
return retval;
retval = sctp_packet_append_chunk(pkt, auth);
return retval;
}
/* Try to bundle a SACK with the packet. */
static sctp_xmit_t sctp_packet_bundle_sack(struct sctp_packet *pkt,
struct sctp_chunk *chunk)
@ -231,12 +266,17 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __FUNCTION__, packet,
chunk);
retval = sctp_packet_bundle_sack(packet, chunk);
psize = packet->size;
/* Try to bundle AUTH chunk */
retval = sctp_packet_bundle_auth(packet, chunk);
if (retval != SCTP_XMIT_OK)
goto finish;
/* Try to bundle SACK chunk */
retval = sctp_packet_bundle_sack(packet, chunk);
if (retval != SCTP_XMIT_OK)
goto finish;
psize = packet->size;
pmtu = ((packet->transport->asoc) ?
(packet->transport->asoc->pathmtu) :
(packet->transport->pathmtu));
@ -245,10 +285,16 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
/* Decide if we need to fragment or resubmit later. */
if (too_big) {
/* Both control chunks and data chunks with TSNs are
* non-fragmentable.
/* It's OK to fragmet at IP level if any one of the following
* is true:
* 1. The packet is empty (meaning this chunk is greater
* the MTU)
* 2. The chunk we are adding is a control chunk
* 3. The packet doesn't have any data in it yet and data
* requires authentication.
*/
if (sctp_packet_empty(packet) || !sctp_chunk_is_data(chunk)) {
if (sctp_packet_empty(packet) || !sctp_chunk_is_data(chunk) ||
(!packet->has_data && chunk->auth)) {
/* We no longer do re-fragmentation.
* Just fragment at the IP layer, if we
* actually hit this condition
@ -270,16 +316,31 @@ append:
/* DATA is a special case since we must examine both rwnd and cwnd
* before we send DATA.
*/
if (sctp_chunk_is_data(chunk)) {
switch (chunk->chunk_hdr->type) {
case SCTP_CID_DATA:
retval = sctp_packet_append_data(packet, chunk);
/* Disallow SACK bundling after DATA. */
packet->has_sack = 1;
/* Disallow AUTH bundling after DATA */
packet->has_auth = 1;
/* Let it be knows that packet has DATA in it */
packet->has_data = 1;
if (SCTP_XMIT_OK != retval)
goto finish;
} else if (SCTP_CID_COOKIE_ECHO == chunk->chunk_hdr->type)
break;
case SCTP_CID_COOKIE_ECHO:
packet->has_cookie_echo = 1;
else if (SCTP_CID_SACK == chunk->chunk_hdr->type)
break;
case SCTP_CID_SACK:
packet->has_sack = 1;
break;
case SCTP_CID_AUTH:
packet->has_auth = 1;
packet->auth = chunk;
break;
}
/* It is OK to send this chunk. */
list_add_tail(&chunk->list, &packet->chunk_list);
@ -307,6 +368,8 @@ int sctp_packet_transmit(struct sctp_packet *packet)
int padding; /* How much padding do we need? */
__u8 has_data = 0;
struct dst_entry *dst = tp->dst;
unsigned char *auth = NULL; /* pointer to auth in skb data */
__u32 cksum_buf_len = sizeof(struct sctphdr);
SCTP_DEBUG_PRINTK("%s: packet:%p\n", __FUNCTION__, packet);
@ -360,16 +423,6 @@ int sctp_packet_transmit(struct sctp_packet *packet)
sh->vtag = htonl(packet->vtag);
sh->checksum = 0;
/* 2) Calculate the Adler-32 checksum of the whole packet,
* including the SCTP common header and all the
* chunks.
*
* Note: Adler-32 is no longer applicable, as has been replaced
* by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>.
*/
if (!(dst->dev->features & NETIF_F_NO_CSUM))
crc32 = sctp_start_cksum((__u8 *)sh, sizeof(struct sctphdr));
/**
* 6.10 Bundling
*
@ -420,14 +473,16 @@ int sctp_packet_transmit(struct sctp_packet *packet)
if (padding)
memset(skb_put(chunk->skb, padding), 0, padding);
if (dst->dev->features & NETIF_F_NO_CSUM)
memcpy(skb_put(nskb, chunk->skb->len),
/* if this is the auth chunk that we are adding,
* store pointer where it will be added and put
* the auth into the packet.
*/
if (chunk == packet->auth)
auth = skb_tail_pointer(nskb);
cksum_buf_len += chunk->skb->len;
memcpy(skb_put(nskb, chunk->skb->len),
chunk->skb->data, chunk->skb->len);
else
crc32 = sctp_update_copy_cksum(skb_put(nskb,
chunk->skb->len),
chunk->skb->data,
chunk->skb->len, crc32);
SCTP_DEBUG_PRINTK("%s %p[%s] %s 0x%x, %s %d, %s %d, %s %d\n",
"*** Chunk", chunk,
@ -449,9 +504,31 @@ int sctp_packet_transmit(struct sctp_packet *packet)
sctp_chunk_free(chunk);
}
/* Perform final transformation on checksum. */
if (!(dst->dev->features & NETIF_F_NO_CSUM))
/* SCTP-AUTH, Section 6.2
* The sender MUST calculate the MAC as described in RFC2104 [2]
* using the hash function H as described by the MAC Identifier and
* the shared association key K based on the endpoint pair shared key
* described by the shared key identifier. The 'data' used for the
* computation of the AUTH-chunk is given by the AUTH chunk with its
* HMAC field set to zero (as shown in Figure 6) followed by all
* chunks that are placed after the AUTH chunk in the SCTP packet.
*/
if (auth)
sctp_auth_calculate_hmac(asoc, nskb,
(struct sctp_auth_chunk *)auth,
GFP_ATOMIC);
/* 2) Calculate the Adler-32 checksum of the whole packet,
* including the SCTP common header and all the
* chunks.
*
* Note: Adler-32 is no longer applicable, as has been replaced
* by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>.
*/
if (!(dst->dev->features & NETIF_F_NO_CSUM)) {
crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len);
crc32 = sctp_end_cksum(crc32);
}
/* 3) Put the resultant value into the checksum field in the
* common header, and leave the rest of the bits unchanged.

View File

@ -1111,6 +1111,41 @@ nodata:
return retval;
}
struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc)
{
struct sctp_chunk *retval;
struct sctp_hmac *hmac_desc;
struct sctp_authhdr auth_hdr;
__u8 *hmac;
/* Get the first hmac that the peer told us to use */
hmac_desc = sctp_auth_asoc_get_hmac(asoc);
if (unlikely(!hmac_desc))
return NULL;
retval = sctp_make_chunk(asoc, SCTP_CID_AUTH, 0,
hmac_desc->hmac_len + sizeof(sctp_authhdr_t));
if (!retval)
return NULL;
auth_hdr.hmac_id = htons(hmac_desc->hmac_id);
auth_hdr.shkey_id = htons(asoc->active_key_id);
retval->subh.auth_hdr = sctp_addto_chunk(retval, sizeof(sctp_authhdr_t),
&auth_hdr);
hmac = skb_put(retval->skb, hmac_desc->hmac_len);
memset(hmac, 0, hmac_desc->hmac_len);
/* Adjust the chunk header to include the empty MAC */
retval->chunk_hdr->length =
htons(ntohs(retval->chunk_hdr->length) + hmac_desc->hmac_len);
retval->chunk_end = skb_tail_pointer(retval->skb);
return retval;
}
/********************************************************************
* 2nd Level Abstractions
********************************************************************/
@ -1225,6 +1260,10 @@ struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc,
retval->chunk_hdr = chunk_hdr;
retval->chunk_end = ((__u8 *)chunk_hdr) + sizeof(struct sctp_chunkhdr);
/* Determine if the chunk needs to be authenticated */
if (sctp_auth_send_cid(type, asoc))
retval->auth = 1;
/* Set the skb to the belonging sock for accounting. */
skb->sk = sk;