[SCTP]: Implete SCTP-AUTH parameter processing
[deliverable/linux.git] / net / sctp / sm_make_chunk.c
index 8d18f570c2e6186598225b330430402b9eab24a1..4c02875786acbb6a4c620fe02a44fb3f11ac17db 100644 (file)
@@ -65,8 +65,6 @@
 #include <net/sctp/sctp.h>
 #include <net/sctp/sm.h>
 
-extern struct kmem_cache *sctp_chunk_cachep;
-
 SCTP_STATIC
 struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc,
                                   __u8 type, __u8 flags, int paylen);
@@ -112,20 +110,16 @@ static const struct sctp_paramhdr prsctp_param = {
  * abort chunk.
  */
 void  sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code,
-                     const void *payload, size_t paylen)
+                     size_t paylen)
 {
        sctp_errhdr_t err;
-       int padlen;
        __u16 len;
 
        /* Cause code constants are now defined in network order.  */
        err.cause = cause_code;
        len = sizeof(sctp_errhdr_t) + paylen;
-       padlen = len % 4;
        err.length  = htons(len);
-       len += padlen;
        chunk->subh.err_hdr = sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err);
-       sctp_addto_chunk(chunk, paylen, payload);
 }
 
 /* 3.3.2 Initiation (INIT) (1)
@@ -185,6 +179,11 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
        sctp_supported_addrs_param_t sat;
        __be16 types[2];
        sctp_adaptation_ind_param_t aiparam;
+       sctp_supported_ext_param_t ext_param;
+       int num_ext = 0;
+       __u8 extensions[3];
+       sctp_paramhdr_t *auth_chunks = NULL,
+                       *auth_hmacs = NULL;
 
        /* RFC 2960 3.3.2 Initiation (INIT) (1)
         *
@@ -208,11 +207,52 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
 
        chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN(num_types);
        chunksize += sizeof(ecap_param);
-       if (sctp_prsctp_enable)
+       if (sctp_prsctp_enable) {
                chunksize += sizeof(prsctp_param);
+               extensions[num_ext] = SCTP_CID_FWD_TSN;
+               num_ext += 1;
+       }
+       /* ADDIP: Section 4.2.7:
+        *  An implementation supporting this extension [ADDIP] MUST list
+        *  the ASCONF,the ASCONF-ACK, and the AUTH  chunks in its INIT and
+        *  INIT-ACK parameters.
+        */
+       if (sctp_addip_enable) {
+               extensions[num_ext] = SCTP_CID_ASCONF;
+               extensions[num_ext+1] = SCTP_CID_ASCONF_ACK;
+               num_ext += 2;
+       }
+
        chunksize += sizeof(aiparam);
        chunksize += vparam_len;
 
+       /* Account for AUTH related parameters */
+       if (sctp_auth_enable) {
+               /* Add random parameter length*/
+               chunksize += sizeof(asoc->c.auth_random);
+
+               /* Add HMACS parameter length if any were defined */
+               auth_hmacs = (sctp_paramhdr_t *)asoc->c.auth_hmacs;
+               if (auth_hmacs->length)
+                       chunksize += ntohs(auth_hmacs->length);
+               else
+                       auth_hmacs = NULL;
+
+               /* Add CHUNKS parameter length */
+               auth_chunks = (sctp_paramhdr_t *)asoc->c.auth_chunks;
+               if (auth_chunks->length)
+                       chunksize += ntohs(auth_chunks->length);
+               else
+                       auth_hmacs = NULL;
+
+               extensions[num_ext] = SCTP_CID_AUTH;
+               num_ext += 1;
+       }
+
+       /* If we have any extensions to report, account for that */
+       if (num_ext)
+               chunksize += sizeof(sctp_supported_ext_param_t) + num_ext;
+
        /* RFC 2960 3.3.2 Initiation (INIT) (1)
         *
         * Note 3: An INIT chunk MUST NOT contain more than one Host
@@ -247,12 +287,38 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
        sctp_addto_chunk(retval, num_types * sizeof(__u16), &types);
 
        sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
+
+       /* Add the supported extensions paramter.  Be nice and add this
+        * fist before addiding the parameters for the extensions themselves
+        */
+       if (num_ext) {
+               ext_param.param_hdr.type = SCTP_PARAM_SUPPORTED_EXT;
+               ext_param.param_hdr.length =
+                           htons(sizeof(sctp_supported_ext_param_t) + num_ext);
+               sctp_addto_chunk(retval, sizeof(sctp_supported_ext_param_t),
+                               &ext_param);
+               sctp_addto_chunk(retval, num_ext, extensions);
+       }
+
        if (sctp_prsctp_enable)
                sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param);
+
        aiparam.param_hdr.type = SCTP_PARAM_ADAPTATION_LAYER_IND;
        aiparam.param_hdr.length = htons(sizeof(aiparam));
        aiparam.adaptation_ind = htonl(sp->adaptation_ind);
        sctp_addto_chunk(retval, sizeof(aiparam), &aiparam);
+
+       /* Add SCTP-AUTH chunks to the parameter list */
+       if (sctp_auth_enable) {
+               sctp_addto_chunk(retval, sizeof(asoc->c.auth_random),
+                                asoc->c.auth_random);
+               if (auth_hmacs)
+                       sctp_addto_chunk(retval, ntohs(auth_hmacs->length),
+                                       auth_hmacs);
+               if (auth_chunks)
+                       sctp_addto_chunk(retval, ntohs(auth_chunks->length),
+                                       auth_chunks);
+       }
 nodata:
        kfree(addrs.v);
        return retval;
@@ -270,6 +336,12 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc,
        int cookie_len;
        size_t chunksize;
        sctp_adaptation_ind_param_t aiparam;
+       sctp_supported_ext_param_t ext_param;
+       int num_ext = 0;
+       __u8 extensions[3];
+       sctp_paramhdr_t *auth_chunks = NULL,
+                       *auth_hmacs = NULL,
+                       *auth_random = NULL;
 
        retval = NULL;
 
@@ -300,11 +372,41 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc,
                chunksize += sizeof(ecap_param);
 
        /* Tell peer that we'll do PR-SCTP only if peer advertised.  */
-       if (asoc->peer.prsctp_capable)
+       if (asoc->peer.prsctp_capable) {
                chunksize += sizeof(prsctp_param);
+               extensions[num_ext] = SCTP_CID_FWD_TSN;
+               num_ext += 1;
+       }
 
+       if (sctp_addip_enable) {
+               extensions[num_ext] = SCTP_CID_ASCONF;
+               extensions[num_ext+1] = SCTP_CID_ASCONF_ACK;
+               num_ext += 2;
+       }
+
+       chunksize += sizeof(ext_param) + num_ext;
        chunksize += sizeof(aiparam);
 
+       if (asoc->peer.auth_capable) {
+               auth_random = (sctp_paramhdr_t *)asoc->c.auth_random;
+               chunksize += ntohs(auth_random->length);
+
+               auth_hmacs = (sctp_paramhdr_t *)asoc->c.auth_hmacs;
+               if (auth_hmacs->length)
+                       chunksize += ntohs(auth_hmacs->length);
+               else
+                       auth_hmacs = NULL;
+
+               auth_chunks = (sctp_paramhdr_t *)asoc->c.auth_chunks;
+               if (auth_chunks->length)
+                       chunksize += ntohs(auth_chunks->length);
+               else
+                       auth_chunks = NULL;
+
+               extensions[num_ext] = SCTP_CID_AUTH;
+               num_ext += 1;
+       }
+
        /* Now allocate and fill out the chunk.  */
        retval = sctp_make_chunk(asoc, SCTP_CID_INIT_ACK, 0, chunksize);
        if (!retval)
@@ -320,6 +422,14 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc,
        sctp_addto_chunk(retval, cookie_len, cookie);
        if (asoc->peer.ecn_capable)
                sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
+       if (num_ext) {
+               ext_param.param_hdr.type = SCTP_PARAM_SUPPORTED_EXT;
+               ext_param.param_hdr.length =
+                           htons(sizeof(sctp_supported_ext_param_t) + num_ext);
+               sctp_addto_chunk(retval, sizeof(sctp_supported_ext_param_t),
+                                &ext_param);
+               sctp_addto_chunk(retval, num_ext, extensions);
+       }
        if (asoc->peer.prsctp_capable)
                sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param);
 
@@ -328,6 +438,17 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc,
        aiparam.adaptation_ind = htonl(sctp_sk(asoc->base.sk)->adaptation_ind);
        sctp_addto_chunk(retval, sizeof(aiparam), &aiparam);
 
+       if (asoc->peer.auth_capable) {
+               sctp_addto_chunk(retval, ntohs(auth_random->length),
+                                auth_random);
+               if (auth_hmacs)
+                       sctp_addto_chunk(retval, ntohs(auth_hmacs->length),
+                                       auth_hmacs);
+               if (auth_chunks)
+                       sctp_addto_chunk(retval, ntohs(auth_chunks->length),
+                                       auth_chunks);
+       }
+
        /* We need to remove the const qualifier at this point.  */
        retval->asoc = (struct sctp_association *) asoc;
 
@@ -785,8 +906,8 @@ struct sctp_chunk *sctp_make_abort_no_data(
 
        /* Put the tsn back into network byte order.  */
        payload = htonl(tsn);
-       sctp_init_cause(retval, SCTP_ERROR_NO_DATA, (const void *)&payload,
-                       sizeof(payload));
+       sctp_init_cause(retval, SCTP_ERROR_NO_DATA, sizeof(payload));
+       sctp_addto_chunk(retval, sizeof(payload), (const void *)&payload);
 
        /* RFC 2960 6.4 Multi-homed SCTP Endpoints
         *
@@ -828,7 +949,8 @@ struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc,
                        goto err_copy;
        }
 
-       sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen);
+       sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, paylen);
+       sctp_addto_chunk(retval, paylen, payload);
 
        if (paylen)
                kfree(payload);
@@ -844,6 +966,26 @@ err_chunk:
        return retval;
 }
 
+/* Append bytes to the end of a parameter.  Will panic if chunk is not big
+ * enough.
+ */
+static void *sctp_addto_param(struct sctp_chunk *chunk, int len,
+                             const void *data)
+{
+       void *target;
+       int chunklen = ntohs(chunk->chunk_hdr->length);
+
+       target = skb_put(chunk->skb, len);
+
+       memcpy(target, data, len);
+
+       /* Adjust the chunk length field.  */
+       chunk->chunk_hdr->length = htons(chunklen + len);
+       chunk->chunk_end = skb_tail_pointer(chunk->skb);
+
+       return target;
+}
+
 /* Make an ABORT chunk with a PROTOCOL VIOLATION cause code. */
 struct sctp_chunk *sctp_make_abort_violation(
        const struct sctp_association *asoc,
@@ -855,15 +997,17 @@ struct sctp_chunk *sctp_make_abort_violation(
        struct sctp_paramhdr phdr;
 
        retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen
-                                       + sizeof(sctp_chunkhdr_t));
+                                       + sizeof(sctp_paramhdr_t));
        if (!retval)
                goto end;
 
-       sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION, payload, paylen);
+       sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION, paylen
+                                       + sizeof(sctp_paramhdr_t));
 
        phdr.type = htons(chunk->chunk_hdr->type);
        phdr.length = chunk->chunk_hdr->length;
-       sctp_addto_chunk(retval, sizeof(sctp_paramhdr_t), &phdr);
+       sctp_addto_chunk(retval, paylen, payload);
+       sctp_addto_param(retval, sizeof(sctp_paramhdr_t), &phdr);
 
 end:
        return retval;
@@ -960,7 +1104,8 @@ struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc,
        if (!retval)
                goto nodata;
 
-       sctp_init_cause(retval, cause_code, payload, paylen);
+       sctp_init_cause(retval, cause_code, paylen);
+       sctp_addto_chunk(retval, paylen, payload);
 
 nodata:
        return retval;
@@ -1133,7 +1278,7 @@ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, const void *data)
        void *target;
        void *padding;
        int chunklen = ntohs(chunk->chunk_hdr->length);
-       int padlen = chunklen % 4;
+       int padlen = WORD_ROUND(chunklen) - chunklen;
 
        padding = skb_put(chunk->skb, padlen);
        target = skb_put(chunk->skb, len);
@@ -1179,25 +1324,36 @@ out:
  */
 void sctp_chunk_assign_ssn(struct sctp_chunk *chunk)
 {
+       struct sctp_datamsg *msg;
+       struct sctp_chunk *lchunk;
+       struct sctp_stream *stream;
        __u16 ssn;
        __u16 sid;
 
        if (chunk->has_ssn)
                return;
 
-       /* This is the last possible instant to assign a SSN. */
-       if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
-               ssn = 0;
-       } else {
-               sid = ntohs(chunk->subh.data_hdr->stream);
-               if (chunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
-                       ssn = sctp_ssn_next(&chunk->asoc->ssnmap->out, sid);
-               else
-                       ssn = sctp_ssn_peek(&chunk->asoc->ssnmap->out, sid);
-       }
+       /* All fragments will be on the same stream */
+       sid = ntohs(chunk->subh.data_hdr->stream);
+       stream = &chunk->asoc->ssnmap->out;
 
-       chunk->subh.data_hdr->ssn = htons(ssn);
-       chunk->has_ssn = 1;
+       /* Now assign the sequence number to the entire message.
+        * All fragments must have the same stream sequence number.
+        */
+       msg = chunk->msg;
+       list_for_each_entry(lchunk, &msg->chunks, frag_list) {
+               if (lchunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
+                       ssn = 0;
+               } else {
+                       if (lchunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
+                               ssn = sctp_ssn_next(stream, sid);
+                       else
+                               ssn = sctp_ssn_peek(stream, sid);
+               }
+
+               lchunk->subh.data_hdr->ssn = htons(ssn);
+               lchunk->has_ssn = 1;
+       }
 }
 
 /* Helper function to assign a TSN if needed.  This assumes that both
@@ -1454,7 +1610,6 @@ no_hmac:
                do_gettimeofday(&tv);
 
        if (!asoc && tv_lt(bear_cookie->expiration, tv)) {
-               __u16 len;
                /*
                 * Section 3.3.10.3 Stale Cookie Error (3)
                 *
@@ -1472,7 +1627,8 @@ no_hmac:
                        __be32 n = htonl(usecs);
 
                        sctp_init_cause(*errp, SCTP_ERROR_STALE_COOKIE,
-                                       &n, sizeof(n));
+                                       sizeof(n));
+                       sctp_addto_chunk(*errp, sizeof(n), &n);
                        *error = -SCTP_IERROR_STALE_COOKIE;
                } else
                        *error = -SCTP_IERROR_NOMEM;
@@ -1503,7 +1659,7 @@ no_hmac:
        /* Also, add the destination address. */
        if (list_empty(&retval->base.bind_addr.address_list)) {
                sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest, 1,
-                                  GFP_ATOMIC);
+                               GFP_ATOMIC);
        }
 
        retval->next_tsn = retval->c.initial_tsn;
@@ -1562,7 +1718,8 @@ static int sctp_process_missing_param(const struct sctp_association *asoc,
                report.num_missing = htonl(1);
                report.type = paramtype;
                sctp_init_cause(*errp, SCTP_ERROR_MISS_PARAM,
-                               &report, sizeof(report));
+                               sizeof(report));
+               sctp_addto_chunk(*errp, sizeof(report), &report);
        }
 
        /* Stop processing this chunk. */
@@ -1580,7 +1737,7 @@ static int sctp_process_inv_mandatory(const struct sctp_association *asoc,
                *errp = sctp_make_op_error_space(asoc, chunk, 0);
 
        if (*errp)
-               sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM, NULL, 0);
+               sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM, 0);
 
        /* Stop processing this chunk. */
        return 0;
@@ -1601,9 +1758,10 @@ static int sctp_process_inv_paramlength(const struct sctp_association *asoc,
                *errp = sctp_make_op_error_space(asoc, chunk, payload_len);
 
        if (*errp) {
-               sctp_init_cause(*errp, SCTP_ERROR_PROTO_VIOLATION, error,
-                               sizeof(error));
-               sctp_addto_chunk(*errp, sizeof(sctp_paramhdr_t), param);
+               sctp_init_cause(*errp, SCTP_ERROR_PROTO_VIOLATION,
+                               sizeof(error) + sizeof(sctp_paramhdr_t));
+               sctp_addto_chunk(*errp, sizeof(error), error);
+               sctp_addto_param(*errp, sizeof(sctp_paramhdr_t), param);
        }
 
        return 0;
@@ -1624,14 +1782,43 @@ static int sctp_process_hn_param(const struct sctp_association *asoc,
        if (!*errp)
                *errp = sctp_make_op_error_space(asoc, chunk, len);
 
-       if (*errp)
-               sctp_init_cause(*errp, SCTP_ERROR_DNS_FAILED,
-                               param.v, len);
+       if (*errp) {
+               sctp_init_cause(*errp, SCTP_ERROR_DNS_FAILED, len);
+               sctp_addto_chunk(*errp, len, param.v);
+       }
 
        /* Stop processing this chunk. */
        return 0;
 }
 
+static void sctp_process_ext_param(struct sctp_association *asoc,
+                                   union sctp_params param)
+{
+       __u16 num_ext = ntohs(param.p->length) - sizeof(sctp_paramhdr_t);
+       int i;
+
+       for (i = 0; i < num_ext; i++) {
+               switch (param.ext->chunks[i]) {
+                   case SCTP_CID_FWD_TSN:
+                           if (sctp_prsctp_enable &&
+                               !asoc->peer.prsctp_capable)
+                                   asoc->peer.prsctp_capable = 1;
+                           break;
+                   case SCTP_CID_AUTH:
+                           /* if the peer reports AUTH, assume that he
+                            * supports AUTH.
+                            */
+                           asoc->peer.auth_capable = 1;
+                           break;
+                   case SCTP_CID_ASCONF:
+                   case SCTP_CID_ASCONF_ACK:
+                           /* don't need to do anything for ASCONF */
+                   default:
+                           break;
+               }
+       }
+}
+
 /* RFC 3.2.1 & the Implementers Guide 2.2.
  *
  * The Parameter Types are encoded such that the
@@ -1678,10 +1865,13 @@ static int sctp_process_unk_param(const struct sctp_association *asoc,
                        *errp = sctp_make_op_error_space(asoc, chunk,
                                        ntohs(chunk->chunk_hdr->length));
 
-               if (*errp)
+               if (*errp) {
                        sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM,
-                                       param.v,
                                        WORD_ROUND(ntohs(param.p->length)));
+                       sctp_addto_chunk(*errp,
+                                       WORD_ROUND(ntohs(param.p->length)),
+                                       param.v);
+               }
 
                break;
        case SCTP_PARAM_ACTION_SKIP:
@@ -1696,8 +1886,10 @@ static int sctp_process_unk_param(const struct sctp_association *asoc,
 
                if (*errp) {
                        sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM,
-                                       param.v,
                                        WORD_ROUND(ntohs(param.p->length)));
+                       sctp_addto_chunk(*errp,
+                                       WORD_ROUND(ntohs(param.p->length)),
+                                       param.v);
                } else {
                        /* If there is no memory for generating the ERROR
                         * report as specified, an ABORT will be triggered
@@ -1743,15 +1935,52 @@ static int sctp_verify_param(const struct sctp_association *asoc,
        case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
        case SCTP_PARAM_ECN_CAPABLE:
        case SCTP_PARAM_ADAPTATION_LAYER_IND:
+       case SCTP_PARAM_SUPPORTED_EXT:
                break;
 
        case SCTP_PARAM_HOST_NAME_ADDRESS:
                /* Tell the peer, we won't support this param.  */
                return sctp_process_hn_param(asoc, param, chunk, err_chunk);
+
        case SCTP_PARAM_FWD_TSN_SUPPORT:
                if (sctp_prsctp_enable)
                        break;
+               goto fallthrough;
+
+       case SCTP_PARAM_RANDOM:
+               if (!sctp_auth_enable)
+                       goto fallthrough;
+
+               /* SCTP-AUTH: Secion 6.1
+                * If the random number is not 32 byte long the association
+                * MUST be aborted.  The ABORT chunk SHOULD contain the error
+                * cause 'Protocol Violation'.
+                */
+               if (SCTP_AUTH_RANDOM_LENGTH !=
+                       ntohs(param.p->length) - sizeof(sctp_paramhdr_t))
+                       return sctp_process_inv_paramlength(asoc, param.p,
+                                                       chunk, err_chunk);
+               break;
+
+       case SCTP_PARAM_CHUNKS:
+               if (!sctp_auth_enable)
+                       goto fallthrough;
+
+               /* SCTP-AUTH: Section 3.2
+                * The CHUNKS parameter MUST be included once in the INIT or
+                *  INIT-ACK chunk if the sender wants to receive authenticated
+                *  chunks.  Its maximum length is 260 bytes.
+                */
+               if (260 < ntohs(param.p->length))
+                       return sctp_process_inv_paramlength(asoc, param.p,
+                                                       chunk, err_chunk);
+               break;
+
+       case SCTP_PARAM_HMAC_ALGO:
+               if (!sctp_auth_enable)
+                       break;
                /* Fall Through */
+fallthrough:
        default:
                SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
                                ntohs(param.p->type), cid);
@@ -1797,7 +2026,7 @@ int sctp_verify_init(const struct sctp_association *asoc,
         * VIOLATION error.  We build the ERROR chunk here and let the normal
         * error handling code build and send the packet.
         */
-       if (param.v < (void*)chunk->chunk_end - sizeof(sctp_paramhdr_t)) {
+       if (param.v != (void*)chunk->chunk_end) {
                sctp_process_inv_paramlength(asoc, param.p, chunk, errp);
                return 0;
        }
@@ -1856,13 +2085,19 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
        }
 
        /* Process the initialization parameters.  */
-
        sctp_walk_params(param, peer_init, init_hdr.params) {
 
                if (!sctp_process_param(asoc, param, peer_addr, gfp))
                        goto clean_up;
        }
 
+       /* AUTH: After processing the parameters, make sure that we
+        * have all the required info to potentially do authentications.
+        */
+       if (asoc->peer.auth_capable && (!asoc->peer.peer_random ||
+                                       !asoc->peer.peer_hmacs))
+               asoc->peer.auth_capable = 0;
+
        /* Walk list of transports, removing transports in the UNKNOWN state. */
        list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
                transport = list_entry(pos, struct sctp_transport, transports);
@@ -2092,12 +2327,57 @@ static int sctp_process_param(struct sctp_association *asoc,
                asoc->peer.adaptation_ind = param.aind->adaptation_ind;
                break;
 
+       case SCTP_PARAM_SUPPORTED_EXT:
+               sctp_process_ext_param(asoc, param);
+               break;
+
        case SCTP_PARAM_FWD_TSN_SUPPORT:
                if (sctp_prsctp_enable) {
                        asoc->peer.prsctp_capable = 1;
                        break;
                }
                /* Fall Through */
+               goto fall_through;
+
+       case SCTP_PARAM_RANDOM:
+               if (!sctp_auth_enable)
+                       goto fall_through;
+
+               /* Save peer's random parameter */
+               asoc->peer.peer_random = kmemdup(param.p,
+                                           ntohs(param.p->length), gfp);
+               if (!asoc->peer.peer_random) {
+                       retval = 0;
+                       break;
+               }
+               break;
+
+       case SCTP_PARAM_HMAC_ALGO:
+               if (!sctp_auth_enable)
+                       goto fall_through;
+
+               /* Save peer's HMAC list */
+               asoc->peer.peer_hmacs = kmemdup(param.p,
+                                           ntohs(param.p->length), gfp);
+               if (!asoc->peer.peer_hmacs) {
+                       retval = 0;
+                       break;
+               }
+
+               /* Set the default HMAC the peer requested*/
+               sctp_auth_asoc_set_default_hmac(asoc, param.hmac_algo);
+               break;
+
+       case SCTP_PARAM_CHUNKS:
+               if (!sctp_auth_enable)
+                       goto fall_through;
+
+               asoc->peer.peer_chunks = kmemdup(param.p,
+                                           ntohs(param.p->length), gfp);
+               if (!asoc->peer.peer_chunks)
+                       retval = 0;
+               break;
+fall_through:
        default:
                /* Any unrecognized parameters should have been caught
                 * and handled by sctp_verify_param() which should be
@@ -2463,6 +2743,52 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
        return SCTP_ERROR_NO_ERROR;
 }
 
+/* Verify the ASCONF packet before we process it.  */
+int sctp_verify_asconf(const struct sctp_association *asoc,
+                      struct sctp_paramhdr *param_hdr, void *chunk_end,
+                      struct sctp_paramhdr **errp) {
+       sctp_addip_param_t *asconf_param;
+       union sctp_params param;
+       int length, plen;
+
+       param.v = (sctp_paramhdr_t *) param_hdr;
+       while (param.v <= chunk_end - sizeof(sctp_paramhdr_t)) {
+               length = ntohs(param.p->length);
+               *errp = param.p;
+
+               if (param.v > chunk_end - length ||
+                   length < sizeof(sctp_paramhdr_t))
+                       return 0;
+
+               switch (param.p->type) {
+               case SCTP_PARAM_ADD_IP:
+               case SCTP_PARAM_DEL_IP:
+               case SCTP_PARAM_SET_PRIMARY:
+                       asconf_param = (sctp_addip_param_t *)param.v;
+                       plen = ntohs(asconf_param->param_hdr.length);
+                       if (plen < sizeof(sctp_addip_param_t) +
+                           sizeof(sctp_paramhdr_t))
+                               return 0;
+                       break;
+               case SCTP_PARAM_SUCCESS_REPORT:
+               case SCTP_PARAM_ADAPTATION_LAYER_IND:
+                       if (length != sizeof(sctp_addip_param_t))
+                               return 0;
+
+                       break;
+               default:
+                       break;
+               }
+
+               param.v += WORD_ROUND(length);
+       }
+
+       if (param.v != chunk_end)
+               return 0;
+
+       return 1;
+}
+
 /* Process an incoming ASCONF chunk with the next expected serial no. and
  * return an ASCONF_ACK chunk to be sent in response.
  */
@@ -2577,22 +2903,16 @@ static int sctp_asconf_param_success(struct sctp_association *asoc,
 
        switch (asconf_param->param_hdr.type) {
        case SCTP_PARAM_ADD_IP:
-               sctp_local_bh_disable();
-               sctp_write_lock(&asoc->base.addr_lock);
-               list_for_each(pos, &bp->address_list) {
-                       saddr = list_entry(pos, struct sctp_sockaddr_entry, list);
+               /* This is always done in BH context with a socket lock
+                * held, so the list can not change.
+                */
+               list_for_each_entry(saddr, &bp->address_list, list) {
                        if (sctp_cmp_addr_exact(&saddr->a, &addr))
                                saddr->use_as_src = 1;
                }
-               sctp_write_unlock(&asoc->base.addr_lock);
-               sctp_local_bh_enable();
                break;
        case SCTP_PARAM_DEL_IP:
-               sctp_local_bh_disable();
-               sctp_write_lock(&asoc->base.addr_lock);
-               retval = sctp_del_bind_addr(bp, &addr);
-               sctp_write_unlock(&asoc->base.addr_lock);
-               sctp_local_bh_enable();
+               retval = sctp_del_bind_addr(bp, &addr, call_rcu_bh);
                list_for_each(pos, &asoc->peer.transport_addr_list) {
                        transport = list_entry(pos, struct sctp_transport,
                                                 transports);
This page took 0.0346379999999999 seconds and 5 git commands to generate.