aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDaniel Borkmann <dborkman@redhat.com>2014-10-09 16:55:31 -0400
committerDavid S. Miller <davem@davemloft.net>2014-10-14 12:46:22 -0400
commit9de7922bc709eee2f609cd01d98aaedc4cf5ea74 (patch)
treeeb9da32f75f21c530beabda5a1014998e6ad41c3 /net
parentb838b4aced99e0d31a272396d43d9ca21cb078cb (diff)
net: sctp: fix skb_over_panic when receiving malformed ASCONF chunks
Commit 6f4c618ddb0 ("SCTP : Add paramters validity check for ASCONF chunk") added basic verification of ASCONF chunks, however, it is still possible to remotely crash a server by sending a special crafted ASCONF chunk, even up to pre 2.6.12 kernels: skb_over_panic: text:ffffffffa01ea1c3 len:31056 put:30768 head:ffff88011bd81800 data:ffff88011bd81800 tail:0x7950 end:0x440 dev:<NULL> ------------[ cut here ]------------ kernel BUG at net/core/skbuff.c:129! [...] Call Trace: <IRQ> [<ffffffff8144fb1c>] skb_put+0x5c/0x70 [<ffffffffa01ea1c3>] sctp_addto_chunk+0x63/0xd0 [sctp] [<ffffffffa01eadaf>] sctp_process_asconf+0x1af/0x540 [sctp] [<ffffffff8152d025>] ? _read_unlock_bh+0x15/0x20 [<ffffffffa01e0038>] sctp_sf_do_asconf+0x168/0x240 [sctp] [<ffffffffa01e3751>] sctp_do_sm+0x71/0x1210 [sctp] [<ffffffff8147645d>] ? fib_rules_lookup+0xad/0xf0 [<ffffffffa01e6b22>] ? sctp_cmp_addr_exact+0x32/0x40 [sctp] [<ffffffffa01e8393>] sctp_assoc_bh_rcv+0xd3/0x180 [sctp] [<ffffffffa01ee986>] sctp_inq_push+0x56/0x80 [sctp] [<ffffffffa01fcc42>] sctp_rcv+0x982/0xa10 [sctp] [<ffffffffa01d5123>] ? ipt_local_in_hook+0x23/0x28 [iptable_filter] [<ffffffff8148bdc9>] ? nf_iterate+0x69/0xb0 [<ffffffff81496d10>] ? ip_local_deliver_finish+0x0/0x2d0 [<ffffffff8148bf86>] ? nf_hook_slow+0x76/0x120 [<ffffffff81496d10>] ? ip_local_deliver_finish+0x0/0x2d0 [<ffffffff81496ded>] ip_local_deliver_finish+0xdd/0x2d0 [<ffffffff81497078>] ip_local_deliver+0x98/0xa0 [<ffffffff8149653d>] ip_rcv_finish+0x12d/0x440 [<ffffffff81496ac5>] ip_rcv+0x275/0x350 [<ffffffff8145c88b>] __netif_receive_skb+0x4ab/0x750 [<ffffffff81460588>] netif_receive_skb+0x58/0x60 This can be triggered e.g., through a simple scripted nmap connection scan injecting the chunk after the handshake, for example, ... -------------- INIT[ASCONF; ASCONF_ACK] -------------> <----------- INIT-ACK[ASCONF; ASCONF_ACK] ------------ -------------------- COOKIE-ECHO --------------------> <-------------------- COOKIE-ACK --------------------- ------------------ ASCONF; UNKNOWN ------------------> ... where ASCONF chunk of length 280 contains 2 parameters ... 1) Add IP address parameter (param length: 16) 2) Add/del IP address parameter (param length: 255) ... followed by an UNKNOWN chunk of e.g. 4 bytes. Here, the Address Parameter in the ASCONF chunk is even missing, too. This is just an example and similarly-crafted ASCONF chunks could be used just as well. The ASCONF chunk passes through sctp_verify_asconf() as all parameters passed sanity checks, and after walking, we ended up successfully at the chunk end boundary, and thus may invoke sctp_process_asconf(). Parameter walking is done with WORD_ROUND() to take padding into account. In sctp_process_asconf()'s TLV processing, we may fail in sctp_process_asconf_param() e.g., due to removal of the IP address that is also the source address of the packet containing the ASCONF chunk, and thus we need to add all TLVs after the failure to our ASCONF response to remote via helper function sctp_add_asconf_response(), which basically invokes a sctp_addto_chunk() adding the error parameters to the given skb. When walking to the next parameter this time, we proceed with ... length = ntohs(asconf_param->param_hdr.length); asconf_param = (void *)asconf_param + length; ... instead of the WORD_ROUND()'ed length, thus resulting here in an off-by-one that leads to reading the follow-up garbage parameter length of 12336, and thus throwing an skb_over_panic for the reply when trying to sctp_addto_chunk() next time, which implicitly calls the skb_put() with that length. Fix it by using sctp_walk_params() [ which is also used in INIT parameter processing ] macro in the verification *and* in ASCONF processing: it will make sure we don't spill over, that we walk parameters WORD_ROUND()'ed. Moreover, we're being more defensive and guard against unknown parameter types and missized addresses. Joint work with Vlad Yasevich. Fixes: b896b82be4ae ("[SCTP] ADDIP: Support for processing incoming ASCONF_ACK chunks.") Signed-off-by: Daniel Borkmann <dborkman@redhat.com> Signed-off-by: Vlad Yasevich <vyasevich@gmail.com> Acked-by: Neil Horman <nhorman@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/sctp/sm_make_chunk.c99
-rw-r--r--net/sctp/sm_statefuns.c18
2 files changed, 57 insertions, 60 deletions
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index ae0e616a7ca5..ab734be8cb20 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3110,50 +3110,63 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
3110 return SCTP_ERROR_NO_ERROR; 3110 return SCTP_ERROR_NO_ERROR;
3111} 3111}
3112 3112
3113/* Verify the ASCONF packet before we process it. */ 3113/* Verify the ASCONF packet before we process it. */
3114int sctp_verify_asconf(const struct sctp_association *asoc, 3114bool sctp_verify_asconf(const struct sctp_association *asoc,
3115 struct sctp_paramhdr *param_hdr, void *chunk_end, 3115 struct sctp_chunk *chunk, bool addr_param_needed,
3116 struct sctp_paramhdr **errp) { 3116 struct sctp_paramhdr **errp)
3117 sctp_addip_param_t *asconf_param; 3117{
3118 sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) chunk->chunk_hdr;
3118 union sctp_params param; 3119 union sctp_params param;
3119 int length, plen; 3120 bool addr_param_seen = false;
3120
3121 param.v = (sctp_paramhdr_t *) param_hdr;
3122 while (param.v <= chunk_end - sizeof(sctp_paramhdr_t)) {
3123 length = ntohs(param.p->length);
3124 *errp = param.p;
3125 3121
3126 if (param.v > chunk_end - length || 3122 sctp_walk_params(param, addip, addip_hdr.params) {
3127 length < sizeof(sctp_paramhdr_t)) 3123 size_t length = ntohs(param.p->length);
3128 return 0;
3129 3124
3125 *errp = param.p;
3130 switch (param.p->type) { 3126 switch (param.p->type) {
3127 case SCTP_PARAM_ERR_CAUSE:
3128 break;
3129 case SCTP_PARAM_IPV4_ADDRESS:
3130 if (length != sizeof(sctp_ipv4addr_param_t))
3131 return false;
3132 addr_param_seen = true;
3133 break;
3134 case SCTP_PARAM_IPV6_ADDRESS:
3135 if (length != sizeof(sctp_ipv6addr_param_t))
3136 return false;
3137 addr_param_seen = true;
3138 break;
3131 case SCTP_PARAM_ADD_IP: 3139 case SCTP_PARAM_ADD_IP:
3132 case SCTP_PARAM_DEL_IP: 3140 case SCTP_PARAM_DEL_IP:
3133 case SCTP_PARAM_SET_PRIMARY: 3141 case SCTP_PARAM_SET_PRIMARY:
3134 asconf_param = (sctp_addip_param_t *)param.v; 3142 /* In ASCONF chunks, these need to be first. */
3135 plen = ntohs(asconf_param->param_hdr.length); 3143 if (addr_param_needed && !addr_param_seen)
3136 if (plen < sizeof(sctp_addip_param_t) + 3144 return false;
3137 sizeof(sctp_paramhdr_t)) 3145 length = ntohs(param.addip->param_hdr.length);
3138 return 0; 3146 if (length < sizeof(sctp_addip_param_t) +
3147 sizeof(sctp_paramhdr_t))
3148 return false;
3139 break; 3149 break;
3140 case SCTP_PARAM_SUCCESS_REPORT: 3150 case SCTP_PARAM_SUCCESS_REPORT:
3141 case SCTP_PARAM_ADAPTATION_LAYER_IND: 3151 case SCTP_PARAM_ADAPTATION_LAYER_IND:
3142 if (length != sizeof(sctp_addip_param_t)) 3152 if (length != sizeof(sctp_addip_param_t))
3143 return 0; 3153 return false;
3144
3145 break; 3154 break;
3146 default: 3155 default:
3147 break; 3156 /* This is unkown to us, reject! */
3157 return false;
3148 } 3158 }
3149
3150 param.v += WORD_ROUND(length);
3151 } 3159 }
3152 3160
3153 if (param.v != chunk_end) 3161 /* Remaining sanity checks. */
3154 return 0; 3162 if (addr_param_needed && !addr_param_seen)
3163 return false;
3164 if (!addr_param_needed && addr_param_seen)
3165 return false;
3166 if (param.v != chunk->chunk_end)
3167 return false;
3155 3168
3156 return 1; 3169 return true;
3157} 3170}
3158 3171
3159/* Process an incoming ASCONF chunk with the next expected serial no. and 3172/* Process an incoming ASCONF chunk with the next expected serial no. and
@@ -3162,16 +3175,17 @@ int sctp_verify_asconf(const struct sctp_association *asoc,
3162struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, 3175struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
3163 struct sctp_chunk *asconf) 3176 struct sctp_chunk *asconf)
3164{ 3177{
3178 sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) asconf->chunk_hdr;
3179 bool all_param_pass = true;
3180 union sctp_params param;
3165 sctp_addiphdr_t *hdr; 3181 sctp_addiphdr_t *hdr;
3166 union sctp_addr_param *addr_param; 3182 union sctp_addr_param *addr_param;
3167 sctp_addip_param_t *asconf_param; 3183 sctp_addip_param_t *asconf_param;
3168 struct sctp_chunk *asconf_ack; 3184 struct sctp_chunk *asconf_ack;
3169
3170 __be16 err_code; 3185 __be16 err_code;
3171 int length = 0; 3186 int length = 0;
3172 int chunk_len; 3187 int chunk_len;
3173 __u32 serial; 3188 __u32 serial;
3174 int all_param_pass = 1;
3175 3189
3176 chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(sctp_chunkhdr_t); 3190 chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(sctp_chunkhdr_t);
3177 hdr = (sctp_addiphdr_t *)asconf->skb->data; 3191 hdr = (sctp_addiphdr_t *)asconf->skb->data;
@@ -3199,9 +3213,14 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
3199 goto done; 3213 goto done;
3200 3214
3201 /* Process the TLVs contained within the ASCONF chunk. */ 3215 /* Process the TLVs contained within the ASCONF chunk. */
3202 while (chunk_len > 0) { 3216 sctp_walk_params(param, addip, addip_hdr.params) {
3217 /* Skip preceeding address parameters. */
3218 if (param.p->type == SCTP_PARAM_IPV4_ADDRESS ||
3219 param.p->type == SCTP_PARAM_IPV6_ADDRESS)
3220 continue;
3221
3203 err_code = sctp_process_asconf_param(asoc, asconf, 3222 err_code = sctp_process_asconf_param(asoc, asconf,
3204 asconf_param); 3223 param.addip);
3205 /* ADDIP 4.1 A7) 3224 /* ADDIP 4.1 A7)
3206 * If an error response is received for a TLV parameter, 3225 * If an error response is received for a TLV parameter,
3207 * all TLVs with no response before the failed TLV are 3226 * all TLVs with no response before the failed TLV are
@@ -3209,28 +3228,20 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
3209 * the failed response are considered unsuccessful unless 3228 * the failed response are considered unsuccessful unless
3210 * a specific success indication is present for the parameter. 3229 * a specific success indication is present for the parameter.
3211 */ 3230 */
3212 if (SCTP_ERROR_NO_ERROR != err_code) 3231 if (err_code != SCTP_ERROR_NO_ERROR)
3213 all_param_pass = 0; 3232 all_param_pass = false;
3214
3215 if (!all_param_pass) 3233 if (!all_param_pass)
3216 sctp_add_asconf_response(asconf_ack, 3234 sctp_add_asconf_response(asconf_ack, param.addip->crr_id,
3217 asconf_param->crr_id, err_code, 3235 err_code, param.addip);
3218 asconf_param);
3219 3236
3220 /* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add 3237 /* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add
3221 * an IP address sends an 'Out of Resource' in its response, it 3238 * an IP address sends an 'Out of Resource' in its response, it
3222 * MUST also fail any subsequent add or delete requests bundled 3239 * MUST also fail any subsequent add or delete requests bundled
3223 * in the ASCONF. 3240 * in the ASCONF.
3224 */ 3241 */
3225 if (SCTP_ERROR_RSRC_LOW == err_code) 3242 if (err_code == SCTP_ERROR_RSRC_LOW)
3226 goto done; 3243 goto done;
3227
3228 /* Move to the next ASCONF param. */
3229 length = ntohs(asconf_param->param_hdr.length);
3230 asconf_param = (void *)asconf_param + length;
3231 chunk_len -= length;
3232 } 3244 }
3233
3234done: 3245done:
3235 asoc->peer.addip_serial++; 3246 asoc->peer.addip_serial++;
3236 3247
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index c8f606324134..bdea3dfbad31 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -3591,9 +3591,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net,
3591 struct sctp_chunk *asconf_ack = NULL; 3591 struct sctp_chunk *asconf_ack = NULL;
3592 struct sctp_paramhdr *err_param = NULL; 3592 struct sctp_paramhdr *err_param = NULL;
3593 sctp_addiphdr_t *hdr; 3593 sctp_addiphdr_t *hdr;
3594 union sctp_addr_param *addr_param;
3595 __u32 serial; 3594 __u32 serial;
3596 int length;
3597 3595
3598 if (!sctp_vtag_verify(chunk, asoc)) { 3596 if (!sctp_vtag_verify(chunk, asoc)) {
3599 sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, 3597 sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
@@ -3618,17 +3616,8 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net,
3618 hdr = (sctp_addiphdr_t *)chunk->skb->data; 3616 hdr = (sctp_addiphdr_t *)chunk->skb->data;
3619 serial = ntohl(hdr->serial); 3617 serial = ntohl(hdr->serial);
3620 3618
3621 addr_param = (union sctp_addr_param *)hdr->params;
3622 length = ntohs(addr_param->p.length);
3623 if (length < sizeof(sctp_paramhdr_t))
3624 return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
3625 (void *)addr_param, commands);
3626
3627 /* Verify the ASCONF chunk before processing it. */ 3619 /* Verify the ASCONF chunk before processing it. */
3628 if (!sctp_verify_asconf(asoc, 3620 if (!sctp_verify_asconf(asoc, chunk, true, &err_param))
3629 (sctp_paramhdr_t *)((void *)addr_param + length),
3630 (void *)chunk->chunk_end,
3631 &err_param))
3632 return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, 3621 return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
3633 (void *)err_param, commands); 3622 (void *)err_param, commands);
3634 3623
@@ -3745,10 +3734,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net,
3745 rcvd_serial = ntohl(addip_hdr->serial); 3734 rcvd_serial = ntohl(addip_hdr->serial);
3746 3735
3747 /* Verify the ASCONF-ACK chunk before processing it. */ 3736 /* Verify the ASCONF-ACK chunk before processing it. */
3748 if (!sctp_verify_asconf(asoc, 3737 if (!sctp_verify_asconf(asoc, asconf_ack, false, &err_param))
3749 (sctp_paramhdr_t *)addip_hdr->params,
3750 (void *)asconf_ack->chunk_end,
3751 &err_param))
3752 return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, 3738 return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
3753 (void *)err_param, commands); 3739 (void *)err_param, commands);
3754 3740