summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--UPDATING8
-rw-r--r--sys/conf/newvers.sh2
-rw-r--r--sys/netinet/sctp_input.c44
-rw-r--r--sys/netinet/sctp_usrreq.c20
4 files changed, 56 insertions, 18 deletions
diff --git a/UPDATING b/UPDATING
index b66ccdcbc144..d35a91ce5820 100644
--- a/UPDATING
+++ b/UPDATING
@@ -16,6 +16,14 @@ from older versions of FreeBSD, try WITHOUT_CLANG to bootstrap to the tip of
stable/10, and then rebuild without this option. The bootstrap process from
older version of current is a bit fragile.
+20150127: p17 FreeBSD-SA-15:02.kmem
+ FreeBSD-SA-15:03.sctp
+
+ Fix SCTP SCTP_SS_VALUE kernel memory corruption and disclosure
+ vulnerability. [SA-15:02]
+
+ Fix SCTP stream reset vulnerability. [SA-15:03]
+
20150114: p16 FreeBSD-SA-15:01.openssl
Fix multiple vulnerabilities in OpenSSL. [SA-15:01]
diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh
index 50ba361b81b8..412b1f91b29d 100644
--- a/sys/conf/newvers.sh
+++ b/sys/conf/newvers.sh
@@ -32,7 +32,7 @@
TYPE="FreeBSD"
REVISION="10.0"
-BRANCH="RELEASE-p16"
+BRANCH="RELEASE-p17"
if [ "X${BRANCH_OVERRIDE}" != "X" ]; then
BRANCH=${BRANCH_OVERRIDE}
fi
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c
index 287c3ede2c3e..06c269a533cc 100644
--- a/sys/netinet/sctp_input.c
+++ b/sys/netinet/sctp_input.c
@@ -3690,6 +3690,9 @@ sctp_handle_stream_reset_response(struct sctp_tcb *stcb,
/* huh ? */
return (0);
}
+ if (ntohs(respin->ph.param_length) < sizeof(struct sctp_stream_reset_response_tsn)) {
+ return (0);
+ }
if (action == SCTP_STREAM_RESET_RESULT_PERFORMED) {
resp = (struct sctp_stream_reset_response_tsn *)respin;
asoc->stream_reset_outstanding--;
@@ -4078,7 +4081,7 @@ __attribute__((noinline))
sctp_handle_stream_reset(struct sctp_tcb *stcb, struct mbuf *m, int offset,
struct sctp_chunkhdr *ch_req)
{
- int chk_length, param_len, ptype;
+ uint16_t remaining_length, param_len, ptype;
struct sctp_paramhdr pstore;
uint8_t cstore[SCTP_CHUNK_BUFFER_SIZE];
uint32_t seq = 0;
@@ -4091,7 +4094,7 @@ __attribute__((noinline))
int num_param = 0;
/* now it may be a reset or a reset-response */
- chk_length = ntohs(ch_req->chunk_length);
+ remaining_length = ntohs(ch_req->chunk_length) - sizeof(struct sctp_chunkhdr);
/* setup for adding the response */
sctp_alloc_a_chunk(stcb, chk);
@@ -4127,20 +4130,27 @@ strres_nochunk:
ch->chunk_length = htons(chk->send_size);
SCTP_BUF_LEN(chk->data) = SCTP_SIZE32(chk->send_size);
offset += sizeof(struct sctp_chunkhdr);
- while ((size_t)chk_length >= sizeof(struct sctp_stream_reset_tsn_request)) {
+ while (remaining_length >= sizeof(struct sctp_paramhdr)) {
ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, sizeof(pstore), (uint8_t *) & pstore);
- if (ph == NULL)
+ if (ph == NULL) {
+ /* TSNH */
break;
+ }
param_len = ntohs(ph->param_length);
- if (param_len < (int)sizeof(struct sctp_stream_reset_tsn_request)) {
- /* bad param */
+ if ((param_len > remaining_length) ||
+ (param_len < (sizeof(struct sctp_paramhdr) + sizeof(uint32_t)))) {
+ /* bad parameter length */
break;
}
- ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, min(param_len, (int)sizeof(cstore)),
+ ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, min(param_len, sizeof(cstore)),
(uint8_t *) & cstore);
+ if (ph == NULL) {
+ /* TSNH */
+ break;
+ }
ptype = ntohs(ph->param_type);
num_param++;
- if (param_len > (int)sizeof(cstore)) {
+ if (param_len > sizeof(cstore)) {
trunc = 1;
} else {
trunc = 0;
@@ -4152,6 +4162,9 @@ strres_nochunk:
if (ptype == SCTP_STR_RESET_OUT_REQUEST) {
struct sctp_stream_reset_out_request *req_out;
+ if (param_len < sizeof(struct sctp_stream_reset_out_request)) {
+ break;
+ }
req_out = (struct sctp_stream_reset_out_request *)ph;
num_req++;
if (stcb->asoc.stream_reset_outstanding) {
@@ -4165,12 +4178,18 @@ strres_nochunk:
} else if (ptype == SCTP_STR_RESET_ADD_OUT_STREAMS) {
struct sctp_stream_reset_add_strm *str_add;
+ if (param_len < sizeof(struct sctp_stream_reset_add_strm)) {
+ break;
+ }
str_add = (struct sctp_stream_reset_add_strm *)ph;
num_req++;
sctp_handle_str_reset_add_strm(stcb, chk, str_add);
} else if (ptype == SCTP_STR_RESET_ADD_IN_STREAMS) {
struct sctp_stream_reset_add_strm *str_add;
+ if (param_len < sizeof(struct sctp_stream_reset_add_strm)) {
+ break;
+ }
str_add = (struct sctp_stream_reset_add_strm *)ph;
num_req++;
sctp_handle_str_reset_add_out_strm(stcb, chk, str_add);
@@ -4195,6 +4214,9 @@ strres_nochunk:
struct sctp_stream_reset_response *resp;
uint32_t result;
+ if (param_len < sizeof(struct sctp_stream_reset_response)) {
+ break;
+ }
resp = (struct sctp_stream_reset_response *)ph;
seq = ntohl(resp->response_seq);
result = ntohl(resp->result);
@@ -4206,7 +4228,11 @@ strres_nochunk:
break;
}
offset += SCTP_SIZE32(param_len);
- chk_length -= SCTP_SIZE32(param_len);
+ if (remaining_length >= SCTP_SIZE32(param_len)) {
+ remaining_length -= SCTP_SIZE32(param_len);
+ } else {
+ remaining_length = 0;
+ }
}
if (num_req == 0) {
/* we have no response free the stuff */
diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c
index 0ff166c5e24b..b0bf1c054de5 100644
--- a/sys/netinet/sctp_usrreq.c
+++ b/sys/netinet/sctp_usrreq.c
@@ -1869,8 +1869,9 @@ flags_out:
SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, *optsize);
SCTP_FIND_STCB(inp, stcb, av->assoc_id);
if (stcb) {
- if (stcb->asoc.ss_functions.sctp_ss_get_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
- &av->stream_value) < 0) {
+ if ((av->stream_id >= stcb->asoc.streamoutcnt) ||
+ (stcb->asoc.ss_functions.sctp_ss_get_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
+ &av->stream_value) < 0)) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
error = EINVAL;
} else {
@@ -3675,8 +3676,9 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, optsize);
SCTP_FIND_STCB(inp, stcb, av->assoc_id);
if (stcb) {
- if (stcb->asoc.ss_functions.sctp_ss_set_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
- av->stream_value) < 0) {
+ if ((av->stream_id >= stcb->asoc.streamoutcnt) ||
+ (stcb->asoc.ss_functions.sctp_ss_set_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id],
+ av->stream_value) < 0)) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
error = EINVAL;
}
@@ -3686,10 +3688,12 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
SCTP_INP_RLOCK(inp);
LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
SCTP_TCB_LOCK(stcb);
- stcb->asoc.ss_functions.sctp_ss_set_value(stcb,
- &stcb->asoc,
- &stcb->asoc.strmout[av->stream_id],
- av->stream_value);
+ if (av->stream_id < stcb->asoc.streamoutcnt) {
+ stcb->asoc.ss_functions.sctp_ss_set_value(stcb,
+ &stcb->asoc,
+ &stcb->asoc.strmout[av->stream_id],
+ av->stream_value);
+ }
SCTP_TCB_UNLOCK(stcb);
}
SCTP_INP_RUNLOCK(inp);