NetBSD Problem Report #51682
From www@NetBSD.org Fri Dec 2 21:21:07 2016
Return-Path: <www@NetBSD.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
(Client CN "mail.netbsd.org", Issuer "Postmaster NetBSD.org" (verified OK))
by mollari.NetBSD.org (Postfix) with ESMTPS id 0A0107A284
for <gnats-bugs@gnats.NetBSD.org>; Fri, 2 Dec 2016 21:21:07 +0000 (UTC)
Message-Id: <20161202212105.B6CEE7A303@mollari.NetBSD.org>
Date: Fri, 2 Dec 2016 21:21:05 +0000 (UTC)
From: rfoggia@trustwave.com
Reply-To: rfoggia@trustwave.com
To: gnats-bugs@NetBSD.org
Subject: Remote un-authenticated denial of service
X-Send-Pr-Version: www-1.0
>Number: 51682
>Category: security
>Synopsis: Remote un-authenticated denial of service
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: security-officer
>State: open
>Class: support
>Submitter-Id: net
>Arrival-Date: Fri Dec 02 21:25:00 +0000 2016
>Last-Modified: Thu Aug 31 08:55:01 +0000 2017
>Originator: Robert Foggia
>Release:
>Organization:
Trustwave
>Environment:
>Description:
Finding 1: Remote un-authenticated denial of service in ipsec-tools
The ipsec-tools racoon daemon contains a remotely exploitable computational complexity attack when parsing and storing isakmp fragments. The implementation permits a remote attacker to exhaust computational resources on the remote endpoint by repeatedly sending isakmp fragment packets in a particular order such that the worst-case computational complexity is realized in the algorithm utilized to determine if reassembly of the fragments can take place.
We've reached out to IpSec-tools directly. They said to also report it to you as well since you manage your own folk.
>How-To-Repeat:
The algorithm in question is a simple quadratic linked list walk and is in O(n(n+1)) hence O(n^2) for ’n’ fragments received. Since the implementation fails to identify repeated fragment indices, a remote attacker can repeatedly specify the same index. Worst-case complexity is realized if fragments are sent in reverse order, for instance:
253, 252 ... 3, 2, 1, 255 (last fragment)
The absence of fragment index 254 is not an error as this ensures fragment reassembly is not possible.
>Fix:
Only in ipsec-tools-0.8.2-new/: .DS_Store
Only in ipsec-tools-0.8.2-new/src: .DS_Store
diff -ru ipsec-tools-0.8.2/src/racoon/handler.h ipsec-tools-0.8.2-new/src/racoon/handler.h
--- ipsec-tools-0.8.2/src/racoon/handler.h 2010-11-17 10:40:41.000000000 +0000
+++ ipsec-tools-0.8.2-new/src/racoon/handler.h 2016-09-02 17:03:27.000000000 +0100
@@ -141,6 +141,7 @@
#endif
#ifdef ENABLE_FRAG
int frag; /* IKE phase 1 fragmentation */
+ int frag_last_index;
struct isakmp_frag_item *frag_chain; /* Received fragments */
#endif
diff -ru ipsec-tools-0.8.2/src/racoon/isakmp.c ipsec-tools-0.8.2-new/src/racoon/isakmp.c
--- ipsec-tools-0.8.2/src/racoon/isakmp.c 2012-08-29 09:55:26.000000000 +0100
+++ ipsec-tools-0.8.2-new/src/racoon/isakmp.c 2016-09-06 09:14:46.000000000 +0100
@@ -1069,6 +1069,7 @@
iph1->frag = 1;
else
iph1->frag = 0;
+ iph1->frag_last_index = 0;
iph1->frag_chain = NULL;
#endif
iph1->approval = NULL;
@@ -1173,6 +1174,7 @@
#endif
#ifdef ENABLE_FRAG
iph1->frag = 0;
+ iph1->frag_last_index = 0;
iph1->frag_chain = NULL;
#endif
iph1->approval = NULL;
diff -ru ipsec-tools-0.8.2/src/racoon/isakmp_frag.c ipsec-tools-0.8.2-new/src/racoon/isakmp_frag.c
--- ipsec-tools-0.8.2/src/racoon/isakmp_frag.c 2009-04-22 12:24:20.000000000 +0100
+++ ipsec-tools-0.8.2-new/src/racoon/isakmp_frag.c 2016-10-03 12:51:52.000000000 +0100
@@ -224,39 +224,66 @@
item->frag_next = NULL;
item->frag_packet = buf;
- /* Look for the last frag while inserting the new item in the chain */
- if (item->frag_last)
- last_frag = item->frag_num;
-
- if (iph1->frag_chain == NULL) {
- iph1->frag_chain = item;
- } else {
- struct isakmp_frag_item *current;
-
- current = iph1->frag_chain;
- while (current->frag_next) {
- if (current->frag_last)
- last_frag = item->frag_num;
- current = current->frag_next;
+ /* Check for the last frag before inserting the new item in the chain */
+ if (item->frag_last) {
+ /* if we have the last fragment, indices must match */
+ if (iph1->frag_last_index != 0 &&
+ item->frag_last != iph1->frag_last_index) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Repeated last fragment index mismatch\n");
+ racoon_free(item);
+ vfree(buf);
+ return -1;
}
- current->frag_next = item;
+
+ last_frag = iph1->frag_last_index = item->frag_num;
}
- /* If we saw the last frag, check if the chain is complete */
+ /* insert fragment into chain */
+ if (iph1->frag_chain == NULL) {
+ iph1->frag_chain = item;
+ } else {
+ struct isakmp_frag_item *pitem = NULL, *citem = iph1->frag_chain;
+
+ do {
+ if (citem->frag_num == item->frag_num) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Repeated fragment index mismatch\n");
+ racoon_free(item);
+ vfree(buf);
+ return -1;
+ }
+
+ if (citem->frag_num > item->frag_num) {
+ if (pitem)
+ pitem->next = item;
+ item->next = citem;
+ break;
+ }
+
+ pitem = citem;
+ citem = citem->frag_next;
+ } while (citem != NULL);
+
+ /* we reached the end of the list, insert */
+ if (citem == NULL)
+ pitem->next = item;
+ }
+
+ /* If we saw the last frag, check if the chain is complete
+ * we have a sorted list now, so just walk through */
if (last_frag != 0) {
+ item = iph1->frag_chain;
for (i = 1; i <= last_frag; i++) {
- item = iph1->frag_chain;
- do {
- if (item->frag_num == i)
- break;
- item = item->frag_next;
- } while (item != NULL);
+ if (item->frag_num != i)
+ break;
+ item = item->frag_next;
if (item == NULL) /* Not found */
break;
}
- if (item != NULL) /* It is complete */
+ if (i > last_frag) /* It is complete */
return 1;
}
@@ -291,15 +318,9 @@
}
data = buf->v;
+ item = iph1->frag_chain;
for (i = 1; i <= frag_count; i++) {
- item = iph1->frag_chain;
- do {
- if (item->frag_num == i)
- break;
- item = item->frag_next;
- } while (item != NULL);
-
- if (item == NULL) {
+ if (item->frag_num != i) {
plog(LLV_ERROR, LOCATION, NULL,
"Missing fragment #%d\n", i);
vfree(buf);
@@ -308,6 +329,7 @@
}
memcpy(data, item->frag_packet->v, item->frag_packet->l);
data += item->frag_packet->l;
+ item = item->frag_next;
}
out:
diff -ru ipsec-tools-0.8.2/src/racoon/isakmp_inf.c ipsec-tools-0.8.2-new/src/racoon/isakmp_inf.c
--- ipsec-tools-0.8.2/src/racoon/isakmp_inf.c 2013-04-12 10:53:52.000000000 +0100
+++ ipsec-tools-0.8.2-new/src/racoon/isakmp_inf.c 2016-09-05 11:00:40.000000000 +0100
@@ -720,6 +720,7 @@
#endif
#ifdef ENABLE_FRAG
iph1->frag = 0;
+ iph1->frag_last_index = 0;
iph1->frag_chain = NULL;
#endif
>Audit-Trail:
From: christos@zoulas.com (Christos Zoulas)
To: gnats-bugs@NetBSD.org, security-officer@netbsd.org,
gnats-admin@netbsd.org, security-alert@netbsd.org
Cc:
Subject: Re: security/51682: Remote un-authenticated denial of service
Date: Mon, 23 Jan 2017 21:34:33 -0500
On Dec 2, 9:25pm, rfoggia@trustwave.com (rfoggia@trustwave.com) wrote:
-- Subject: security/51682: Remote un-authenticated denial of service
Hi,
Have you tried this patch? It does not compile... Here's a version
that compiles. How did you test it? Do you have a test harness you
can share?
Index: handler.h
===================================================================
RCS file: /cvsroot/src/crypto/dist/ipsec-tools/src/racoon/handler.h,v
retrieving revision 1.25
diff -u -u -r1.25 handler.h
--- handler.h 17 Nov 2010 10:40:41 -0000 1.25
+++ handler.h 24 Jan 2017 02:29:15 -0000
@@ -141,6 +141,7 @@
#endif
#ifdef ENABLE_FRAG
int frag; /* IKE phase 1 fragmentation */
+ int frag_last_index;
struct isakmp_frag_item *frag_chain; /* Received fragments */
#endif
Index: isakmp.c
===================================================================
RCS file: /cvsroot/src/crypto/dist/ipsec-tools/src/racoon/isakmp.c,v
retrieving revision 1.75
diff -u -u -r1.75 isakmp.c
--- isakmp.c 9 Mar 2016 22:27:17 -0000 1.75
+++ isakmp.c 24 Jan 2017 02:29:15 -0000
@@ -1077,6 +1077,7 @@
iph1->frag = 1;
else
iph1->frag = 0;
+ iph1->frag_last_index = 0;
iph1->frag_chain = NULL;
#endif
iph1->approval = NULL;
@@ -1181,6 +1182,7 @@
#endif
#ifdef ENABLE_FRAG
iph1->frag = 0;
+ iph1->frag_last_index = 0;
iph1->frag_chain = NULL;
#endif
iph1->approval = NULL;
Index: isakmp_frag.c
===================================================================
RCS file: /cvsroot/src/crypto/dist/ipsec-tools/src/racoon/isakmp_frag.c,v
retrieving revision 1.5
diff -u -u -r1.5 isakmp_frag.c
--- isakmp_frag.c 22 Apr 2009 11:24:20 -0000 1.5
+++ isakmp_frag.c 24 Jan 2017 02:29:15 -0000
@@ -173,6 +173,38 @@
return ntohl(hp[MD5_DIGEST_LENGTH / sizeof(*hp)]);
}
+static int
+isakmp_frag_insert(struct ph1handle *iph1, struct isakmp_frag_item *item)
+{
+ struct isakmp_frag_item *pitem = NULL;
+ struct isakmp_frag_item *citem = iph1->frag_chain;
+
+ if (iph1->frag_chain == NULL) {
+ iph1->frag_chain = item;
+ return 0;
+ }
+
+ do {
+ if (citem->frag_num == item->frag_num)
+ return -1;
+
+ if (citem->frag_num > item->frag_num) {
+ if (pitem)
+ pitem->frag_next = item;
+ item->frag_next = citem;
+ break;
+ }
+
+ pitem = citem;
+ citem = citem->frag_next;
+ } while (citem != NULL);
+
+ /* we reached the end of the list, insert */
+ if (citem == NULL)
+ pitem->frag_next = item;
+ return 0;
+}
+
int
isakmp_frag_extract(iph1, msg)
struct ph1handle *iph1;
@@ -224,39 +256,43 @@
item->frag_next = NULL;
item->frag_packet = buf;
- /* Look for the last frag while inserting the new item in the chain */
- if (item->frag_last)
- last_frag = item->frag_num;
+ /* Check for the last frag before inserting the new item in the chain */
+ if (item->frag_last) {
+ /* if we have the last fragment, indices must match */
+ if (iph1->frag_last_index != 0 &&
+ item->frag_last != iph1->frag_last_index) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Repeated last fragment index mismatch\n");
+ racoon_free(item);
+ vfree(buf);
+ return -1;
+ }
- if (iph1->frag_chain == NULL) {
- iph1->frag_chain = item;
- } else {
- struct isakmp_frag_item *current;
+ last_frag = iph1->frag_last_index = item->frag_num;
+ }
- current = iph1->frag_chain;
- while (current->frag_next) {
- if (current->frag_last)
- last_frag = item->frag_num;
- current = current->frag_next;
- }
- current->frag_next = item;
+ /* insert fragment into chain */
+ if (isakmp_frag_insert(iph1, item) == -1) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Repeated fragment index mismatch\n");
+ racoon_free(item);
+ vfree(buf);
+ return -1;
}
- /* If we saw the last frag, check if the chain is complete */
+ /* If we saw the last frag, check if the chain is complete
+ * we have a sorted list now, so just walk through */
if (last_frag != 0) {
+ item = iph1->frag_chain;
for (i = 1; i <= last_frag; i++) {
- item = iph1->frag_chain;
- do {
- if (item->frag_num == i)
- break;
- item = item->frag_next;
- } while (item != NULL);
-
+ if (item->frag_num != i)
+ break;
+ item = item->frag_next;
if (item == NULL) /* Not found */
break;
}
- if (item != NULL) /* It is complete */
+ if (i > last_frag) /* It is complete */
return 1;
}
@@ -291,15 +327,9 @@
}
data = buf->v;
+ item = iph1->frag_chain;
for (i = 1; i <= frag_count; i++) {
- item = iph1->frag_chain;
- do {
- if (item->frag_num == i)
- break;
- item = item->frag_next;
- } while (item != NULL);
-
- if (item == NULL) {
+ if (item->frag_num != i) {
plog(LLV_ERROR, LOCATION, NULL,
"Missing fragment #%d\n", i);
vfree(buf);
@@ -308,6 +338,7 @@
}
memcpy(data, item->frag_packet->v, item->frag_packet->l);
data += item->frag_packet->l;
+ item = item->frag_next;
}
out:
Index: isakmp_inf.c
===================================================================
RCS file: /cvsroot/src/crypto/dist/ipsec-tools/src/racoon/isakmp_inf.c,v
retrieving revision 1.50
diff -u -u -r1.50 isakmp_inf.c
--- isakmp_inf.c 12 Apr 2013 09:53:10 -0000 1.50
+++ isakmp_inf.c 24 Jan 2017 02:29:15 -0000
@@ -720,6 +720,7 @@
#endif
#ifdef ENABLE_FRAG
iph1->frag = 0;
+ iph1->frag_last_index = 0;
iph1->frag_chain = NULL;
#endif
From: "Christos Zoulas" <christos@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/51682 CVS commit: src/crypto/dist/ipsec-tools/src/racoon
Date: Tue, 24 Jan 2017 14:23:31 -0500
Module Name: src
Committed By: christos
Date: Tue Jan 24 19:23:31 UTC 2017
Modified Files:
src/crypto/dist/ipsec-tools/src/racoon: isakmp_frag.c
Log Message:
PR/51682: Avoid DoS with fragment out of order insertion; keep fragments
sorted in the list.
To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.6 src/crypto/dist/ipsec-tools/src/racoon/isakmp_frag.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Christos Zoulas" <christos@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/51682 CVS commit: src/crypto/dist/ipsec-tools/src/racoon
Date: Tue, 24 Jan 2017 14:23:56 -0500
Module Name: src
Committed By: christos
Date: Tue Jan 24 19:23:56 UTC 2017
Modified Files:
src/crypto/dist/ipsec-tools/src/racoon: handler.h isakmp.c isakmp_inf.c
Log Message:
PR/51682: Avoid DoS with fragment out of order insertion; keep fragments
sorted in the list.
To generate a diff of this commit:
cvs rdiff -u -r1.25 -r1.26 src/crypto/dist/ipsec-tools/src/racoon/handler.h
cvs rdiff -u -r1.75 -r1.76 src/crypto/dist/ipsec-tools/src/racoon/isakmp.c
cvs rdiff -u -r1.50 -r1.51 \
src/crypto/dist/ipsec-tools/src/racoon/isakmp_inf.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: Robert Foggia <RFoggia@trustwave.com>
To: "gnats-bugs@NetBSD.org" <gnats-bugs@NetBSD.org>,
"security-officer@netbsd.org" <security-officer@netbsd.org>,
"gnats-admin@netbsd.org" <gnats-admin@netbsd.org>,
"security-alert@netbsd.org" <security-alert@netbsd.org>
Cc:
Subject: Re: PR/51682 CVS commit: src/crypto/dist/ipsec-tools/src/racoon
Date: Wed, 28 Jun 2017 21:13:05 +0000
SGVsbG8sDQoNCkp1c3QgY2hlY2tpbmcgaW4gdG8gc2VlIGlmIHRoaXMgd2FzIGZpeGVkLiAgV2Ug
YXJlIGxvb2tpbmcgdG8gcHVibGlzaCBkZXRhaWxzIGFib3V0IHRoaXMuICBUaGFua3MhDQoNCg0K
QmVzdCByZWdhcmRzLA0KDQpSb2JlcnQgRm9nZ2lhDQpTZWN1cml0eSBSZXNlYXJjaGVyLCBJbnRl
bGxpZ2VuY2UgVGVhbSwgU3BpZGVyTGFicw0KdDogKzEgMzEyLjg3My43Njk2DQogDQpUcnVzdHdh
dmUgfCBTTUFSVCBTRUNVUklUWSBPTiBERU1BTkQNCnd3dy50cnVzdHdhdmUuY29tIDxodHRwOi8v
d3d3LnRydXN0d2F2ZS5jb20vPg0KDQoNCg0KDQoNCg0KDQpPbiAxLzI0LzE3LCAxOjI1IFBNLCAi
Q2hyaXN0b3MgWm91bGFzIiA8Y2hyaXN0b3NAbmV0YnNkLm9yZz4gd3JvdGU6DQoNCj5UaGUgZm9s
bG93aW5nIHJlcGx5IHdhcyBtYWRlIHRvIFBSIHNlY3VyaXR5LzUxNjgyOyBpdCBoYXMgYmVlbiBu
b3RlZCBieSBHTkFUUy4NCj4NCj5Gcm9tOiAiQ2hyaXN0b3MgWm91bGFzIiA8Y2hyaXN0b3NAbmV0
YnNkLm9yZz4NCj5UbzogZ25hdHMtYnVnc0BnbmF0cy5OZXRCU0Qub3JnDQo+Q2M6IA0KPlN1Ympl
Y3Q6IFBSLzUxNjgyIENWUyBjb21taXQ6IHNyYy9jcnlwdG8vZGlzdC9pcHNlYy10b29scy9zcmMv
cmFjb29uDQo+RGF0ZTogVHVlLCAyNCBKYW4gMjAxNyAxNDoyMzo1NiAtMDUwMA0KPg0KPiBNb2R1
bGUgTmFtZToJc3JjDQo+IENvbW1pdHRlZCBCeToJY2hyaXN0b3MNCj4gRGF0ZToJCVR1ZSBKYW4g
MjQgMTk6MjM6NTYgVVRDIDIwMTcNCj4gDQo+IE1vZGlmaWVkIEZpbGVzOg0KPiAJc3JjL2NyeXB0
by9kaXN0L2lwc2VjLXRvb2xzL3NyYy9yYWNvb246IGhhbmRsZXIuaCBpc2FrbXAuYyBpc2FrbXBf
aW5mLmMNCj4gDQo+IExvZyBNZXNzYWdlOg0KPiBQUi81MTY4MjogQXZvaWQgRG9TIHdpdGggZnJh
Z21lbnQgb3V0IG9mIG9yZGVyIGluc2VydGlvbjsga2VlcCBmcmFnbWVudHMNCj4gc29ydGVkIGlu
IHRoZSBsaXN0Lg0KPiANCj4gDQo+IFRvIGdlbmVyYXRlIGEgZGlmZiBvZiB0aGlzIGNvbW1pdDoN
Cj4gY3ZzIHJkaWZmIC11IC1yMS4yNSAtcjEuMjYgc3JjL2NyeXB0by9kaXN0L2lwc2VjLXRvb2xz
L3NyYy9yYWNvb24vaGFuZGxlci5oDQo+IGN2cyByZGlmZiAtdSAtcjEuNzUgLXIxLjc2IHNyYy9j
cnlwdG8vZGlzdC9pcHNlYy10b29scy9zcmMvcmFjb29uL2lzYWttcC5jDQo+IGN2cyByZGlmZiAt
dSAtcjEuNTAgLXIxLjUxIFwNCj4gICAgIHNyYy9jcnlwdG8vZGlzdC9pcHNlYy10b29scy9zcmMv
cmFjb29uL2lzYWttcF9pbmYuYw0KPiANCj4gUGxlYXNlIG5vdGUgdGhhdCBkaWZmcyBhcmUgbm90
IHB1YmxpYyBkb21haW47IHRoZXkgYXJlIHN1YmplY3QgdG8gdGhlDQo+IGNvcHlyaWdodCBub3Rp
Y2VzIG9uIHRoZSByZWxldmFudCBmaWxlcy4NCj4gDQo=
From: Taylor R Campbell <riastradh@NetBSD.org>
To: Robert Foggia <RFoggia@trustwave.com>
Cc: "gnats-bugs@NetBSD.org" <gnats-bugs@NetBSD.org>,
"security-officer@netbsd.org" <security-officer@netbsd.org>,
"gnats-admin@netbsd.org" <gnats-admin@netbsd.org>, "security-alert@netbsd.org" <security-alert@netbsd.org>
Subject: Re: PR/51682 CVS commit: src/crypto/dist/ipsec-tools/src/racoon
Date: Thu, 29 Jun 2017 01:01:53 +0000
> Date: Wed, 28 Jun 2017 21:13:05 +0000
> From: Robert Foggia <RFoggia@trustwave.com>
>
> Just checking in to see if this was fixed. We are looking to
> publish details about this. Thanks!
It appears to have been fixed in HEAD, and thus will be in NetBSD 8.0.
The change has not been applied to older branches -- netbsd-7 or
netbsd-6 -- and I have not been paying enough attention to know
whether the bug occurs there too.
From: Jiri Bohac <jbohac@suse.cz>
To: gnats-bugs@NetBSD.org
Cc:
Subject: Re: security/51682
Date: Wed, 12 Jul 2017 22:24:13 +0200
While reviewing the fix for #51682, I found what I believe is a
newly introduced regression / memory leak / DoS.
Bug scenario: the fragments arrive out of order; iph1->frag_chain
already contains some fragments; the newly received fragment has
the lowest frag_num so it should be inserted in the beginning of
the list:
> RCS file: /cvsroot/src/crypto/dist/ipsec-tools/src/racoon/isakmp_frag.c,v
> retrieving revision 1.5
> diff -u -u -r1.5 isakmp_frag.c
> --- isakmp_frag.c 22 Apr 2009 11:24:20 -0000 1.5
> +++ isakmp_frag.c 24 Jan 2017 02:29:15 -0000
> @@ -173,6 +173,38 @@
> return ntohl(hp[MD5_DIGEST_LENGTH / sizeof(*hp)]);
> }
>
> +static int
> +isakmp_frag_insert(struct ph1handle *iph1, struct isakmp_frag_item *item)
> +{
> + struct isakmp_frag_item *pitem = NULL;
> + struct isakmp_frag_item *citem = iph1->frag_chain;
> +
item is not the first to arrive, iph1->frag_chain != NULL,
so the fragment is not inserted here:
> + if (iph1->frag_chain == NULL) {
> + iph1->frag_chain = item;
> + return 0;
> + }
> +
> + do {
> + if (citem->frag_num == item->frag_num)
> + return -1;
> +
item is lowest-numbered fragment so we want to insert it now ...
> + if (citem->frag_num > item->frag_num) {
... but pitem is still NULL, we break doing nothing
> + if (pitem)
> + pitem->frag_next = item;
> + item->frag_next = citem;
> + break;
> + }
> +
> + pitem = citem;
> + citem = citem->frag_next;
> + } while (citem != NULL);
> +
> + /* we reached the end of the list, insert */
> + if (citem == NULL)
> + pitem->frag_next = item;
> + return 0;
We return 0.
The newly received fragment is not inserted in the list -> it's
lost (a regression) and never freed (possible DoS).
I think the correct fix would either need more special cases or
something simpler like: (untested)
static int
isakmp_frag_insert(struct ph1handle *iph1, struct isakmp_frag_item *item)
{
struct isakmp_frag_item **pitem = &iph1->frag_chain
struct isakmp_frag_item *citem = iph1->frag_chain;
while (citem != NULL) {
if (citem->frag_num == item->frag_num)
return -1;
if (citem->frag_num > item->frag_num) {
item->frag_next = citem;
break;
}
pitem = &citem->frag_next;
citem = citem->frag_next;
}
(*pitem)->frag_next = item;
return 0;
}
--
Jiri Bohac <jbohac@suse.cz>
SUSE Labs, Prague, Czechia
From: =?utf-8?Q?Antoine_Beaupr=C3=A9?= <anarcat@orangeseeds.org>
To: gnats-bugs@NetBSD.org
Cc: Jiri Bohac <jbohac@suse.cz>
Subject: Re: security/51682
Date: Wed, 19 Jul 2017 12:39:33 -0400
--=-=-=
Content-Type: text/plain
Hi,
I agree there's something wrong with the code, although I would also
like to have ways of reproducing this. Working on this bug right now is
kind of a shot in the dark, and it seems numerous people here have
worked on PoC or have real world conditions to reproduce those
issues. It would be nice to share those so we can fix those issues
properly.
That said, I believe the change proposed by Jiri Bohac is incorrect: it
will fail if iph1->frag_chain is NULL, as it will try to dereference
that pointer and crash. I also find it confusing that pitem actually
refers to the same item as citem - not sure that works at all.
The original patch handled the empty list explicitely before the loop,
and I'm not sure I see how else to deal with that peculiar case, so I
kept that in my version. I also return explicitly within the loop to
remove any possible confusion.
static int
isakmp_frag_insert(struct ph1handle *iph1, struct isakmp_frag_item *item)
{
struct isakmp_frag_item *pitem = NULL;
struct isakmp_frag_item *citem = iph1->frag_chain;
/* no frag yet, just insert at beginning of list */
if (iph1->frag_chain == NULL) {
iph1->frag_chain = item;
return 0;
}
do {
/* duplicate fragment number, abort (CVE-2016-10396) */
if (citem->frag_num == item->frag_num)
return -1;
/* need to insert before current item */
if (citem->frag_num > item->frag_num) {
if (pitem != NULL)
pitem->frag_next = item
else
/* insert at the beginning of the list */
iph1->frag_chain = item;
item->frag_next = citem;
return 0;
}
pitem = citem;
citem = citem->frag_next;
} while (citem != NULL);
/* we reached the end of the list, insert */
pitem->frag_next = item;
return 0;
}
I think this covers all cases (where are unit tests when you need
them!), described in the comments:
1. no frag yet: just insert at beginning of list
2. duplicate fragment number: abort (CVE-2016-10396)
3. the normal case, where we reach the end of the list and insert and
the end
4. the out of order case, where we insert elsewhere in the list
5. an out of order special case, where insert at the beginning of the
list but correctly linking with the remains of the list
The final patch is attached.
A.
--
Only in the darkness can you see the stars.
- Martin Luther King, Jr.
--=-=-=
Content-Type: text/x-diff
Content-Disposition: inline; filename=CVE-2016-10396.patch
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
--- a/src/racoon/handler.h
+++ b/src/racoon/handler.h
@@ -141,6 +141,7 @@ struct ph1handle {
#endif
#ifdef ENABLE_FRAG
int frag; /* IKE phase 1 fragmentation */
+ int frag_last_index;
struct isakmp_frag_item *frag_chain; /* Received fragments */
#endif
--- a/src/racoon/isakmp.c
+++ b/src/racoon/isakmp.c
@@ -1072,6 +1072,7 @@ isakmp_ph1begin_i(rmconf, remote, local)
iph1->frag = 1;
else
iph1->frag = 0;
+ iph1->frag_last_index = 0;
iph1->frag_chain = NULL;
#endif
iph1->approval = NULL;
@@ -1176,6 +1177,7 @@ isakmp_ph1begin_r(msg, remote, local, et
#endif
#ifdef ENABLE_FRAG
iph1->frag = 0;
+ iph1->frag_last_index = 0;
iph1->frag_chain = NULL;
#endif
iph1->approval = NULL;
--- a/src/racoon/isakmp_frag.c
+++ b/src/racoon/isakmp_frag.c
@@ -1,4 +1,4 @@
-/* $NetBSD: isakmp_frag.c,v 1.5 2009/04/22 11:24:20 tteras Exp $ */
+/* $NetBSD: isakmp_frag.c,v 1.5.36.1 2017/04/21 16:50:42 bouyer Exp $ */
/* Id: isakmp_frag.c,v 1.4 2004/11/13 17:31:36 manubsd Exp */
@@ -173,6 +173,43 @@ vendorid_frag_cap(gen)
return ntohl(hp[MD5_DIGEST_LENGTH / sizeof(*hp)]);
}
+static int
+isakmp_frag_insert(struct ph1handle *iph1, struct isakmp_frag_item *item)
+{
+ struct isakmp_frag_item *pitem = NULL;
+ struct isakmp_frag_item *citem = iph1->frag_chain;
+
+ /* no frag yet, just insert at beginning of list */
+ if (iph1->frag_chain == NULL) {
+ iph1->frag_chain = item;
+ return 0;
+ }
+
+ do {
+ /* duplicate fragment number, abort (CVE-2016-10396) */
+ if (citem->frag_num == item->frag_num)
+ return -1;
+
+ /* need to insert before current item */
+ if (citem->frag_num > item->frag_num) {
+ if (pitem != NULL)
+ pitem->frag_next = item;
+ else
+ /* insert at the beginning of the list */
+ iph1->frag_chain = item;
+ item->frag_next = citem;
+ return 0;
+ }
+
+ pitem = citem;
+ citem = citem->frag_next;
+ } while (citem != NULL);
+
+ /* we reached the end of the list, insert */
+ pitem->frag_next = item;
+ return 0;
+}
+
int
isakmp_frag_extract(iph1, msg)
struct ph1handle *iph1;
@@ -224,39 +261,43 @@ isakmp_frag_extract(iph1, msg)
item->frag_next = NULL;
item->frag_packet = buf;
- /* Look for the last frag while inserting the new item in the chain */
- if (item->frag_last)
- last_frag = item->frag_num;
+ /* Check for the last frag before inserting the new item in the chain */
+ if (item->frag_last) {
+ /* if we have the last fragment, indices must match */
+ if (iph1->frag_last_index != 0 &&
+ item->frag_last != iph1->frag_last_index) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Repeated last fragment index mismatch\n");
+ racoon_free(item);
+ vfree(buf);
+ return -1;
+ }
- if (iph1->frag_chain == NULL) {
- iph1->frag_chain = item;
- } else {
- struct isakmp_frag_item *current;
+ last_frag = iph1->frag_last_index = item->frag_num;
+ }
- current = iph1->frag_chain;
- while (current->frag_next) {
- if (current->frag_last)
- last_frag = item->frag_num;
- current = current->frag_next;
- }
- current->frag_next = item;
+ /* insert fragment into chain */
+ if (isakmp_frag_insert(iph1, item) == -1) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Repeated fragment index mismatch\n");
+ racoon_free(item);
+ vfree(buf);
+ return -1;
}
- /* If we saw the last frag, check if the chain is complete */
+ /* If we saw the last frag, check if the chain is complete
+ * we have a sorted list now, so just walk through */
if (last_frag != 0) {
+ item = iph1->frag_chain;
for (i = 1; i <= last_frag; i++) {
- item = iph1->frag_chain;
- do {
- if (item->frag_num == i)
- break;
- item = item->frag_next;
- } while (item != NULL);
-
+ if (item->frag_num != i)
+ break;
+ item = item->frag_next;
if (item == NULL) /* Not found */
break;
}
- if (item != NULL) /* It is complete */
+ if (i > last_frag) /* It is complete */
return 1;
}
@@ -291,15 +332,9 @@ isakmp_frag_reassembly(iph1)
}
data = buf->v;
+ item = iph1->frag_chain;
for (i = 1; i <= frag_count; i++) {
- item = iph1->frag_chain;
- do {
- if (item->frag_num == i)
- break;
- item = item->frag_next;
- } while (item != NULL);
-
- if (item == NULL) {
+ if (item->frag_num != i) {
plog(LLV_ERROR, LOCATION, NULL,
"Missing fragment #%d\n", i);
vfree(buf);
@@ -308,6 +343,7 @@ isakmp_frag_reassembly(iph1)
}
memcpy(data, item->frag_packet->v, item->frag_packet->l);
data += item->frag_packet->l;
+ item = item->frag_next;
}
out:
--=-=-=--
From: Jiri Bohac <jbohac@suse.cz>
To: Antoine =?iso-8859-1?Q?Beaupr=E9?= <anarcat@orangeseeds.org>
Cc: gnats-bugs@NetBSD.org
Subject: Re: security/51682
Date: Thu, 20 Jul 2017 12:26:14 +0200
On Wed, Jul 19, 2017 at 12:39:33PM -0400, Antoine Beaupré wrote:
> That said, I believe the change proposed by Jiri Bohac is incorrect: it
> will fail if iph1->frag_chain is NULL, as it will try to dereference
> that pointer and crash. I also find it confusing that pitem actually
> refers to the same item as citem - not sure that works at all.
Right. I wanted to write:
*pitem = item;
instead of
(*pitem)->frag_next = item;
Thanks for spotting this!
pitem is a pointer-to-a-pointer; it is initialized with
&iph1->frag_chain, so if iph1->frag_chain is NULL
*pitem = item
will assign to iph1->frag_chain.
Antoine Beaupré's patch looks good to me as well.
--
Jiri Bohac <jbohac@suse.cz>
SUSE Labs, Prague, Czechia
From: "Christos Zoulas" <christos@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/51682 CVS commit: src/crypto/dist/ipsec-tools/src/racoon
Date: Sun, 23 Jul 2017 01:40:28 -0400
Module Name: src
Committed By: christos
Date: Sun Jul 23 05:40:28 UTC 2017
Modified Files:
src/crypto/dist/ipsec-tools/src/racoon: isakmp_frag.c
Log Message:
PR/51682: Antoine Beaupré: Simplify and comment previous patch.
XXX: pullup-8
To generate a diff of this commit:
cvs rdiff -u -r1.6 -r1.7 src/crypto/dist/ipsec-tools/src/racoon/isakmp_frag.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Manuel Bouyer" <bouyer@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/51682 CVS commit: [netbsd-8] src/crypto/dist/ipsec-tools/src/racoon
Date: Thu, 31 Aug 2017 08:50:57 +0000
Module Name: src
Committed By: bouyer
Date: Thu Aug 31 08:50:57 UTC 2017
Modified Files:
src/crypto/dist/ipsec-tools/src/racoon [netbsd-8]: isakmp_frag.c
Log Message:
Pull up following revision(s) (requested by christos in ticket #233):
crypto/dist/ipsec-tools/src/racoon/isakmp_frag.c: revision 1.7
PR/51682: Antoine Beaupr?: Simplify and comment previous patch.
XXX: pullup-8
To generate a diff of this commit:
cvs rdiff -u -r1.6 -r1.6.4.1 \
src/crypto/dist/ipsec-tools/src/racoon/isakmp_frag.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
(Contact us)
$NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2014
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.