diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index ef26878f1282..97390569c4bd 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -725,7 +725,8 @@ static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp) * SCTP. * * Output: - * Return 0 - If the address is a non-unicast or an illegal address. + * Return 0 - If the address is a non-unicast or an illegal address, + * or a non-v4-mapped address on a non-PF_INET6 socket. * Return 1 - If the address is a unicast. */ static int sctp_v6_addr_valid(union sctp_addr *addr, @@ -749,6 +750,9 @@ static int sctp_v6_addr_valid(union sctp_addr *addr, if (!(ret & IPV6_ADDR_UNICAST)) return 0; + if (sp && sctp_opt2sk(sp)->sk_family != PF_INET6) + return 0; + return 1; } diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index ddc31a5dbad3..d64c3ae33298 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1733,7 +1733,9 @@ struct sctp_association *sctp_unpack_cookie( struct sctp_cookie *bear_cookie; struct sctp_chunkhdr *ch; unsigned int len, chlen; + union sctp_addr paddr; enum sctp_scope scope; + struct sctp_af *af; ktime_t kt; /* Header size is static data prior to the actual cookie, including @@ -1842,6 +1844,11 @@ struct sctp_association *sctp_unpack_cookie( goto fail; } + paddr = bear_cookie->peer_addr; + af = sctp_get_af_specific(paddr.sa.sa_family); + if (!af || !af->addr_valid(&paddr, sctp_sk(ep->base.sk), NULL)) + goto malformed; + /* Make a new base association. */ scope = sctp_scope(sctp_source(chunk)); retval = sctp_association_new(ep, ep->base.sk, scope, gfp); @@ -1858,6 +1865,7 @@ struct sctp_association *sctp_unpack_cookie( /* Populate the association from the cookie. */ memcpy(&retval->c, bear_cookie, sizeof(*bear_cookie)); + retval->c.peer_addr = paddr; if (sctp_assoc_set_bind_addr_from_cookie(retval, bear_cookie, GFP_ATOMIC) < 0) { @@ -2382,8 +2390,8 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, (param.p->type == SCTP_PARAM_IPV4_ADDRESS || param.p->type == SCTP_PARAM_IPV6_ADDRESS)) { af = sctp_get_af_specific(param_type2af(param.p->type)); - if (!af->from_addr_param(&addr, param.addr, - chunk->sctp_hdr->source, 0)) + if (!af || !af->from_addr_param(&addr, param.addr, + chunk->sctp_hdr->source, 0)) continue; if (sctp_cmp_addr_exact(sctp_source(chunk), &addr)) src_match = 1; @@ -2555,17 +2563,11 @@ static int sctp_process_param(struct sctp_association *asoc, */ switch (param.p->type) { case SCTP_PARAM_IPV6_ADDRESS: - if (PF_INET6 != asoc->base.sk->sk_family) - break; - goto do_addr_param; - case SCTP_PARAM_IPV4_ADDRESS: - /* v4 addresses are not allowed on v6-only socket */ - if (ipv6_only_sock(asoc->base.sk)) - break; -do_addr_param: af = sctp_get_af_specific(param_type2af(param.p->type)); - if (!af->from_addr_param(&addr, param.addr, htons(asoc->peer.port), 0)) + if (!af || !af->from_addr_param(&addr, param.addr, + htons(asoc->peer.port), 0) || + !af->addr_valid(&addr, sctp_sk(asoc->base.sk), NULL)) break; scope = sctp_scope(peer_addr); if (sctp_in_scope(net, &addr, scope)) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 8dc65498763c..b2f609f177fb 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -814,7 +814,7 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, * effects--it is safe to run them here. */ if (!sctp_process_init(new_asoc, chunk, - &chunk->subh.cookie_hdr->c.peer_addr, + &new_asoc->c.peer_addr, peer_init, GFP_ATOMIC)) goto nomem_init;