diff options
author | Sridhar Samudrala <sri@us.ibm.com> | 2006-08-22 14:50:39 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-08-22 15:52:23 -0400 |
commit | c164a9ba0a8870c5c9d353f63085319931d69f23 (patch) | |
tree | 7e315a50008d0310dd5572a62baef34ddba89988 | |
parent | ac185bdc02c216040f3b83f654d864bd8a29cedc (diff) |
Fix sctp privilege elevation (CVE-2006-3745)
sctp_make_abort_user() now takes the msg_len along with the msg
so that we don't have to recalculate the bytes in iovec.
It also uses memcpy_fromiovec() so that we don't go beyond the
length allocated.
It is good to have this fix even if verify_iovec() is fixed to
return error on overflow.
Signed-off-by: Sridhar Samudrala <sri@us.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | include/net/sctp/sctp.h | 13 | ||||
-rw-r--r-- | include/net/sctp/sm.h | 3 | ||||
-rw-r--r-- | net/sctp/sm_make_chunk.c | 30 | ||||
-rw-r--r-- | net/sctp/sm_statefuns.c | 20 | ||||
-rw-r--r-- | net/sctp/socket.c | 10 |
5 files changed, 23 insertions, 53 deletions
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index a9663b49ea54..92eae0e0f3f1 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h | |||
@@ -404,19 +404,6 @@ static inline int sctp_list_single_entry(struct list_head *head) | |||
404 | return ((head->next != head) && (head->next == head->prev)); | 404 | return ((head->next != head) && (head->next == head->prev)); |
405 | } | 405 | } |
406 | 406 | ||
407 | /* Calculate the size (in bytes) occupied by the data of an iovec. */ | ||
408 | static inline size_t get_user_iov_size(struct iovec *iov, int iovlen) | ||
409 | { | ||
410 | size_t retval = 0; | ||
411 | |||
412 | for (; iovlen > 0; --iovlen) { | ||
413 | retval += iov->iov_len; | ||
414 | iov++; | ||
415 | } | ||
416 | |||
417 | return retval; | ||
418 | } | ||
419 | |||
420 | /* Generate a random jitter in the range of -50% ~ +50% of input RTO. */ | 407 | /* Generate a random jitter in the range of -50% ~ +50% of input RTO. */ |
421 | static inline __s32 sctp_jitter(__u32 rto) | 408 | static inline __s32 sctp_jitter(__u32 rto) |
422 | { | 409 | { |
diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 1eac3d0eb7a9..de313de4fefe 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h | |||
@@ -221,8 +221,7 @@ struct sctp_chunk *sctp_make_abort_no_data(const struct sctp_association *, | |||
221 | const struct sctp_chunk *, | 221 | const struct sctp_chunk *, |
222 | __u32 tsn); | 222 | __u32 tsn); |
223 | struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *, | 223 | struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *, |
224 | const struct sctp_chunk *, | 224 | const struct msghdr *, size_t msg_len); |
225 | const struct msghdr *); | ||
226 | struct sctp_chunk *sctp_make_abort_violation(const struct sctp_association *, | 225 | struct sctp_chunk *sctp_make_abort_violation(const struct sctp_association *, |
227 | const struct sctp_chunk *, | 226 | const struct sctp_chunk *, |
228 | const __u8 *, | 227 | const __u8 *, |
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 4f11f5858209..17b509282cf2 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c | |||
@@ -806,38 +806,26 @@ no_mem: | |||
806 | 806 | ||
807 | /* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error. */ | 807 | /* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error. */ |
808 | struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc, | 808 | struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc, |
809 | const struct sctp_chunk *chunk, | 809 | const struct msghdr *msg, |
810 | const struct msghdr *msg) | 810 | size_t paylen) |
811 | { | 811 | { |
812 | struct sctp_chunk *retval; | 812 | struct sctp_chunk *retval; |
813 | void *payload = NULL, *payoff; | 813 | void *payload = NULL; |
814 | size_t paylen = 0; | 814 | int err; |
815 | struct iovec *iov = NULL; | ||
816 | int iovlen = 0; | ||
817 | |||
818 | if (msg) { | ||
819 | iov = msg->msg_iov; | ||
820 | iovlen = msg->msg_iovlen; | ||
821 | paylen = get_user_iov_size(iov, iovlen); | ||
822 | } | ||
823 | 815 | ||
824 | retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen); | 816 | retval = sctp_make_abort(asoc, NULL, sizeof(sctp_errhdr_t) + paylen); |
825 | if (!retval) | 817 | if (!retval) |
826 | goto err_chunk; | 818 | goto err_chunk; |
827 | 819 | ||
828 | if (paylen) { | 820 | if (paylen) { |
829 | /* Put the msg_iov together into payload. */ | 821 | /* Put the msg_iov together into payload. */ |
830 | payload = kmalloc(paylen, GFP_ATOMIC); | 822 | payload = kmalloc(paylen, GFP_KERNEL); |
831 | if (!payload) | 823 | if (!payload) |
832 | goto err_payload; | 824 | goto err_payload; |
833 | payoff = payload; | ||
834 | 825 | ||
835 | for (; iovlen > 0; --iovlen) { | 826 | err = memcpy_fromiovec(payload, msg->msg_iov, paylen); |
836 | if (copy_from_user(payoff, iov->iov_base,iov->iov_len)) | 827 | if (err < 0) |
837 | goto err_copy; | 828 | goto err_copy; |
838 | payoff += iov->iov_len; | ||
839 | iov++; | ||
840 | } | ||
841 | } | 829 | } |
842 | 830 | ||
843 | sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen); | 831 | sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen); |
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index ead3f1b0ea3d..5b5ae7958322 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c | |||
@@ -4031,18 +4031,12 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( | |||
4031 | * from its upper layer, but retransmits data to the far end | 4031 | * from its upper layer, but retransmits data to the far end |
4032 | * if necessary to fill gaps. | 4032 | * if necessary to fill gaps. |
4033 | */ | 4033 | */ |
4034 | struct msghdr *msg = arg; | 4034 | struct sctp_chunk *abort = arg; |
4035 | struct sctp_chunk *abort; | ||
4036 | sctp_disposition_t retval; | 4035 | sctp_disposition_t retval; |
4037 | 4036 | ||
4038 | retval = SCTP_DISPOSITION_CONSUME; | 4037 | retval = SCTP_DISPOSITION_CONSUME; |
4039 | 4038 | ||
4040 | /* Generate ABORT chunk to send the peer. */ | 4039 | sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); |
4041 | abort = sctp_make_abort_user(asoc, NULL, msg); | ||
4042 | if (!abort) | ||
4043 | retval = SCTP_DISPOSITION_NOMEM; | ||
4044 | else | ||
4045 | sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); | ||
4046 | 4040 | ||
4047 | /* Even if we can't send the ABORT due to low memory delete the | 4041 | /* Even if we can't send the ABORT due to low memory delete the |
4048 | * TCB. This is a departure from our typical NOMEM handling. | 4042 | * TCB. This is a departure from our typical NOMEM handling. |
@@ -4166,8 +4160,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( | |||
4166 | void *arg, | 4160 | void *arg, |
4167 | sctp_cmd_seq_t *commands) | 4161 | sctp_cmd_seq_t *commands) |
4168 | { | 4162 | { |
4169 | struct msghdr *msg = arg; | 4163 | struct sctp_chunk *abort = arg; |
4170 | struct sctp_chunk *abort; | ||
4171 | sctp_disposition_t retval; | 4164 | sctp_disposition_t retval; |
4172 | 4165 | ||
4173 | /* Stop T1-init timer */ | 4166 | /* Stop T1-init timer */ |
@@ -4175,12 +4168,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( | |||
4175 | SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); | 4168 | SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); |
4176 | retval = SCTP_DISPOSITION_CONSUME; | 4169 | retval = SCTP_DISPOSITION_CONSUME; |
4177 | 4170 | ||
4178 | /* Generate ABORT chunk to send the peer */ | 4171 | sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); |
4179 | abort = sctp_make_abort_user(asoc, NULL, msg); | ||
4180 | if (!abort) | ||
4181 | retval = SCTP_DISPOSITION_NOMEM; | ||
4182 | else | ||
4183 | sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); | ||
4184 | 4172 | ||
4185 | sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, | 4173 | sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, |
4186 | SCTP_STATE(SCTP_STATE_CLOSED)); | 4174 | SCTP_STATE(SCTP_STATE_CLOSED)); |
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 54722e622e6d..fde3f55bfd4b 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c | |||
@@ -1520,8 +1520,16 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, | |||
1520 | goto out_unlock; | 1520 | goto out_unlock; |
1521 | } | 1521 | } |
1522 | if (sinfo_flags & SCTP_ABORT) { | 1522 | if (sinfo_flags & SCTP_ABORT) { |
1523 | struct sctp_chunk *chunk; | ||
1524 | |||
1525 | chunk = sctp_make_abort_user(asoc, msg, msg_len); | ||
1526 | if (!chunk) { | ||
1527 | err = -ENOMEM; | ||
1528 | goto out_unlock; | ||
1529 | } | ||
1530 | |||
1523 | SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc); | 1531 | SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc); |
1524 | sctp_primitive_ABORT(asoc, msg); | 1532 | sctp_primitive_ABORT(asoc, chunk); |
1525 | err = 0; | 1533 | err = 0; |
1526 | goto out_unlock; | 1534 | goto out_unlock; |
1527 | } | 1535 | } |