[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Updated: nss security wheezy updates ready for testing



On Thu, Apr 07, 2016 at 05:18:21PM -0400, Antoine Beaupré wrote:
> On 2016-04-07 16:44:07, Antoine Beaupré wrote:
> >> The patches by itself look good to me.
> >
> > Alright, I'll rebuild with the tests/ directory, we'll see how that
> > goes. :)
> 
> I rebuild the packages with the tests/ directory:
> 
> https://people.debian.org/~anarcat/debian/wheezy-lts/
> 
> The debdiff is attached for the security team, yet i still feel the
> proper way is to backport the release from stretch...

And here finally goes the jessie version. I've rechecked the CVEs
against mozill's bugzilla to see if there were any regression fixes
necessary but didn't find any.

Cheers and sorry for the delay,
 -- Guido
diff --git a/debian/changelog b/debian/changelog
index cb86f1f..84e06e2 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,22 @@
+nss (2:3.17.2-1.1+deb8u3) jessie-security; urgency=high
+
+  * Non-maintainer upload by the Security Team.
+  * Add CVE-2015-7182.patch:
+    CVE-2015-7182: Heap-based buffer overflow in the ASN.1 decoder
+  * Add CVE-2015-7181.patch:
+    CVE-2015-7181: The sec_asn1d_parse_leaf function improperly restricts
+    access to an unspecified data structure
+  * Add autopkgtest for certificate generation/signing and library linking
+  * Add CVE-2016-1938: The s_mp_div function improperly divides numbers
+  * Add CVE-2016-1978.patch: Use-after-free vulnerability in the
+    ssl3_HandleECDHServerKeyExchange function
+  * Add CVE-2016-1979.patch: Use-after-free vulnerability in the
+    PK11_ImportDERPrivateKeyInfoAndReturnKey
+  * Add CVE-2016-1950: Heap-based buffer allows to execute arbitrary code via
+    crafted ASN.1 data in an X.509 certificate
+
+ -- Guido Günther <agx@sigxcpu.org>  Sat, 28 Nov 2015 14:04:34 +0100
+
 nss (2:3.17.2-1.1+deb8u2) jessie; urgency=medium
 
   [ Andrew Ayer ]
diff --git a/debian/patches/CVE-2015-7181.patch b/debian/patches/CVE-2015-7181.patch
new file mode 100644
index 0000000..6cfd3b0
--- /dev/null
+++ b/debian/patches/CVE-2015-7181.patch
@@ -0,0 +1,145 @@
+From: =?utf-8?q?Guido_G=C3=BCnther?= <agx@sigxcpu.org>
+Date: Mon, 23 Nov 2015 14:46:56 +0100
+Subject: CVE-2015-7181
+
+Consisting of upstream commits
+
+    https://hg.mozilla.org/projects/nss/rev/8ac7f47eecbb
+    https://hg.mozilla.org/projects/nss/rev/25cb033147fd
+---
+ nss/lib/util/secasn1d.c | 101 +++++++++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 86 insertions(+), 15 deletions(-)
+
+diff --git a/nss/lib/util/secasn1d.c b/nss/lib/util/secasn1d.c
+index 4cdae36..43bf3d8 100644
+--- a/nss/lib/util/secasn1d.c
++++ b/nss/lib/util/secasn1d.c
+@@ -951,6 +951,35 @@ sec_asn1d_parse_more_length (sec_asn1d_state *state,
+     return count;
+ }
+ 
++/*
++ * Helper function for sec_asn1d_prepare_for_contents.
++ * Checks that a value representing a number of bytes consumed can be
++ * subtracted from a remaining length. If so, returns PR_TRUE.
++ * Otherwise, sets the error SEC_ERROR_BAD_DER, indicates that there was a
++ * decoding error in the given SEC_ASN1DecoderContext, and returns PR_FALSE.
++ */
++static PRBool
++sec_asn1d_check_and_subtract_length (unsigned long *remaining,
++                                     unsigned long consumed,
++                                     SEC_ASN1DecoderContext *cx)
++{
++    PORT_Assert(remaining);
++    PORT_Assert(cx);
++    if (!remaining || !cx) {
++        PORT_SetError (SEC_ERROR_INVALID_ARGS);
++        cx->status = decodeError;
++        return PR_FALSE;
++    }
++    if (*remaining < consumed) {
++        PORT_SetError (SEC_ERROR_BAD_DER);
++        cx->status = decodeError;
++        return PR_FALSE;
++    }
++    *remaining -= consumed;
++    return PR_TRUE;
++}
++
++
+ 
+ static void
+ sec_asn1d_prepare_for_contents (sec_asn1d_state *state)
+@@ -958,6 +987,7 @@ sec_asn1d_prepare_for_contents (sec_asn1d_state *state)
+     SECItem *item;
+     PLArenaPool *poolp;
+     unsigned long alloc_len;
++    sec_asn1d_state *parent;
+ 
+ #ifdef DEBUG_ASN1D_STATES
+     {
+@@ -965,6 +995,62 @@ sec_asn1d_prepare_for_contents (sec_asn1d_state *state)
+                state->indefinite ? "indefinite" : "");
+     }
+ #endif
++    /**
++     * The maximum length for a child element should be constrained to the
++     * length remaining in the first definite length element in the ancestor
++     * stack. If there is no definite length element in the ancestor stack,
++     * there's nothing to constrain the length of the child, so there's no
++     * further processing necessary.
++     *
++     * It's necessary to walk the ancestor stack, because it's possible to have
++     * definite length children that are part of an indefinite length element,
++     * which is itself part of an indefinite length element, and which is
++     * ultimately part of a definite length element. A simple example of this
++     * would be the handling of constructed OCTET STRINGs in BER encoding.
++     *
++     * This algorithm finds the first definite length element in the ancestor
++     * stack, if any, and if so, ensures that the length of the child element
++     * is consistent with the number of bytes remaining in the constraining
++     * ancestor element (that is, after accounting for any other sibling
++     * elements that may have been read).
++     *
++     * It's slightly complicated by the need to account both for integer
++     * underflow and overflow, as well as ensure that for indefinite length
++     * encodings, there's also enough space for the End-of-Contents (EOC)
++     * octets (Tag = 0x00, Length = 0x00, or two bytes).
++     */
++
++    /* Determine the maximum length available for this element by finding the
++     * first definite length ancestor, if any. */
++    parent = sec_asn1d_get_enclosing_construct(state);
++    while (parent && parent->indefinite) {
++        parent = sec_asn1d_get_enclosing_construct(parent);
++    }
++    /* If parent is null, state is either the outermost state / at the top of
++     * the stack, or the outermost state uses indefinite length encoding. In
++     * these cases, there's nothing external to constrain this element, so
++     * there's nothing to check. */
++    if (parent) {
++        unsigned long remaining = parent->pending;
++        parent = state;
++        do {
++            if (!sec_asn1d_check_and_subtract_length(
++                     &remaining, parent->consumed, state->top) ||
++                /* If parent->indefinite is true, parent->contents_length is
++                 * zero and this is a no-op. */
++                !sec_asn1d_check_and_subtract_length(
++                     &remaining, parent->contents_length, state->top) ||
++                /* If parent->indefinite is true, then ensure there is enough
++                 * space for an EOC tag of 2 bytes. */
++                (parent->indefinite && !sec_asn1d_check_and_subtract_length(
++                                            &remaining, 2, state->top))) {
++                /* This element is larger than its enclosing element, which is
++                 * invalid. */
++                return;
++            }
++        } while ((parent = sec_asn1d_get_enclosing_construct(parent)) &&
++                 parent->indefinite);
++    }
+ 
+     /*
+      * XXX I cannot decide if this allocation should exclude the case
+@@ -1007,21 +1093,6 @@ sec_asn1d_prepare_for_contents (sec_asn1d_state *state)
+      */
+     state->pending = state->contents_length;
+ 
+-    /* If this item has definite length encoding, and 
+-    ** is enclosed by a definite length constructed type,
+-    ** make sure it isn't longer than the remaining space in that 
+-    ** constructed type.  
+-    */
+-    if (state->contents_length > 0) {
+-	sec_asn1d_state *parent = sec_asn1d_get_enclosing_construct(state);
+-	if (parent && !parent->indefinite && 
+-	    state->consumed + state->contents_length > parent->pending) {
+-	    PORT_SetError (SEC_ERROR_BAD_DER);
+-	    state->top->status = decodeError;
+-	    return;
+-	}
+-    }
+-
+     /*
+      * An EXPLICIT is nothing but an outer header, which we have
+      * already parsed and accepted.  Now we need to do the inner
diff --git a/debian/patches/CVE-2015-7182.patch b/debian/patches/CVE-2015-7182.patch
new file mode 100644
index 0000000..5ca35d4
--- /dev/null
+++ b/debian/patches/CVE-2015-7182.patch
@@ -0,0 +1,126 @@
+From: =?utf-8?q?Guido_G=C3=BCnther?= <agx@sigxcpu.org>
+Date: Mon, 23 Nov 2015 11:20:50 +0100
+Subject: CVE-2015-7182
+
+Consisting of upstream commits
+
+    https://hg.mozilla.org/projects/nss/raw-rev/4dc247276e58
+    https://hg.mozilla.org/projects/nss/raw-rev/534aca7a5bca
+    https://hg.mozilla.org/projects/nss/raw-rev/b4feb2cb0ed6
+---
+ nss/lib/util/secasn1d.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 98 insertions(+), 1 deletion(-)
+
+diff --git a/nss/lib/util/secasn1d.c b/nss/lib/util/secasn1d.c
+index d404b72..4cdae36 100644
+--- a/nss/lib/util/secasn1d.c
++++ b/nss/lib/util/secasn1d.c
+@@ -1720,10 +1720,107 @@ sec_asn1d_next_substring (sec_asn1d_state *state)
+ 	if (state->pending == 0)
+ 	    done = PR_TRUE;
+     } else {
++	PRBool preallocatedString;
++	sec_asn1d_state *temp_state;
+ 	PORT_Assert (state->indefinite);
+ 
+ 	item = (SECItem *)(child->dest);
+-	if (item != NULL && item->data != NULL) {
++
++	/**
++	 * At this point, there's three states at play:
++	 *   child: The element that was just parsed
++	 *   state: The currently processed element
++	 *   'parent' (aka state->parent): The enclosing construct
++	 *      of state, or NULL if this is the top-most element.
++	 *
++	 * This state handles both substrings of a constructed string AND
++	 * child elements of items whose template type was that of
++	 * SEC_ASN1_ANY, SEC_ASN1_SAVE, SEC_ASN1_ANY_CONTENTS, SEC_ASN1_SKIP
++	 * template, as described in sec_asn1d_prepare_for_contents. For
++	 * brevity, these will be referred to as 'string' and 'any' types.
++	 *
++	 * This leads to the following possibilities:
++	 *   1: This element is an indefinite length string, part of a
++	 *      definite length string.
++	 *   2: This element is an indefinite length string, part of an
++	 *      indefinite length string.
++	 *   3: This element is an indefinite length any, part of a
++	 *      definite length any.
++	 *   4: This element is an indefinite length any, part of an
++	 *      indefinite length any.
++	 *   5: This element is an indefinite length any and does not
++	 *      meet any of the above criteria. Note that this would include
++	 *      an indefinite length string type matching an indefinite
++	 *      length any template.
++	 *
++	 * In Cases #1 and #3, the definite length 'parent' element will
++	 * have allocated state->dest based on the parent elements definite
++	 * size. During the processing of 'child', sec_asn1d_parse_leaf will
++	 * have copied the (string, any) data directly into the offset of
++	 * dest, as appropriate, so there's no need for this class to still
++	 * store the child - it's already been processed.
++	 *
++	 * In Cases #2 and #4, dest will be set to the parent element's dest,
++	 * but dest->data will not have been allocated yet, due to the
++	 * indefinite length encoding. In this situation, it's necessary to
++	 * hold onto child (and all other children) until the EOC, at which
++	 * point, it becomes possible to compute 'state's overall length. Once
++	 * 'state' has a computed length, this can then be fed to 'parent' (via
++	 * this state), and then 'parent' can similarly compute the length of
++	 * all of its children up to the EOC, which will ultimately transit to
++	 * sec_asn1d_concat_substrings, determine the overall size needed,
++	 * allocate, and copy the contents (of all of parent's children, which
++	 * would include 'state', just as 'state' will have copied all of its
++	 * children via sec_asn1d_concat_substrings)
++	 *
++	 * The final case, Case #5, will manifest in that item->data and
++	 * item->len will be NULL/0, respectively, since this element was
++	 * indefinite-length encoded. In that case, both the tag and length will
++	 * already exist in state's subitems, via sec_asn1d_record_any_header,
++	 * and so the contents (aka 'child') should be added to that list of
++	 * items to concatenate in sec_asn1d_concat_substrings once the EOC
++	 * is encountered.
++	 *
++	 * To distinguish #2/#4 from #1/#3, it's sufficient to walk the ancestor
++	 * tree. If the current type is a string type, then the enclosing
++	 * construct will be that same type (#1/#2). If the current type is an
++	 * any type, then the enclosing construct is either an any type (#3/#4)
++	 * or some other type (#5). Since this is BER, this nesting relationship
++	 * between 'state' and 'parent' may go through several levels of
++	 * constructed encoding, so continue walking the ancestor chain until a
++	 * clear determination can be made.
++	 *
++	 * The variable preallocatedString is used to indicate Case #1/#3,
++	 * indicating an in-place copy has already occurred, and Cases #2, #4,
++	 * and #5 all have the same behaviour of adding a new substring.
++	 */
++	preallocatedString = PR_FALSE;
++	temp_state = state;
++	while (temp_state && item == temp_state->dest && temp_state->indefinite) {
++	    sec_asn1d_state *parent = sec_asn1d_get_enclosing_construct(temp_state);
++	    if (!parent || parent->underlying_kind != temp_state->underlying_kind) {
++	        /* Case #5 - Either this is a top-level construct or it is part
++	         * of some other element (e.g. a SEQUENCE), in which case, a
++	         * new item should be allocated. */
++	        break;
++	    }
++	    if (!parent->indefinite) {
++	        /* Cases #1 / #3 - A definite length ancestor exists, for which
++	         * this is a substring that has already copied into dest. */
++	        preallocatedString = PR_TRUE;
++	        break;
++	    }
++	    if (!parent->substring) {
++	        /* Cases #2 / #4 - If the parent is not a substring, but is
++	         * indefinite, then there's nothing further up that may have
++	         * preallocated dest, thus child will not have already
++		 * been copied in place, therefore it's necessary to save child
++		 * as a subitem. */
++	        break;
++	    }
++	    temp_state = parent;
++	}
++	if (item != NULL && item->data != NULL && !preallocatedString) {
+ 	    /*
+ 	     * Save the string away for later concatenation.
+ 	     */
diff --git a/debian/patches/CVE-2016-1938.patch b/debian/patches/CVE-2016-1938.patch
new file mode 100644
index 0000000..327e7d0
--- /dev/null
+++ b/debian/patches/CVE-2016-1938.patch
@@ -0,0 +1,91 @@
+From: =?utf-8?q?Guido_G=C3=BCnther?= <agx@sigxcpu.org>
+Date: Tue, 23 Feb 2016 20:06:43 +0100
+Subject: CVE-2016-1938
+
+Consisting of upstream commits
+
+    https://hg.mozilla.org/projects/nss/rev/a555bf0fc23a
+    https://hg.mozilla.org/projects/nss/rev/cfd0ad4726cb
+---
+ nss/lib/freebl/mpi/mpi.c | 29 ++++++++++++++++++++---------
+ 1 file changed, 20 insertions(+), 9 deletions(-)
+
+diff --git a/nss/lib/freebl/mpi/mpi.c b/nss/lib/freebl/mpi/mpi.c
+index 2a3719b..a06a2ee 100644
+--- a/nss/lib/freebl/mpi/mpi.c
++++ b/nss/lib/freebl/mpi/mpi.c
+@@ -4192,6 +4192,7 @@ mp_err   s_mp_div(mp_int *rem, 	/* i: dividend, o: remainder */
+ 
+   MP_SIGN(rem) = ZPOS;
+   MP_SIGN(div) = ZPOS;
++  MP_SIGN(&part) = ZPOS;
+ 
+   /* A working temporary for division     */
+   MP_CHECKOK( mp_init_size(&t, MP_ALLOC(rem)));
+@@ -4199,8 +4200,6 @@ mp_err   s_mp_div(mp_int *rem, 	/* i: dividend, o: remainder */
+   /* Normalize to optimize guessing       */
+   MP_CHECKOK( s_mp_norm(rem, div, &d) );
+ 
+-  part = *rem;
+-
+   /* Perform the division itself...woo!   */
+   MP_USED(quot) = MP_ALLOC(quot);
+ 
+@@ -4209,11 +4208,15 @@ mp_err   s_mp_div(mp_int *rem, 	/* i: dividend, o: remainder */
+   while (MP_USED(rem) > MP_USED(div) || s_mp_cmp(rem, div) >= 0) {
+     int i;
+     int unusedRem;
++    int partExtended = 0;  /* set to true if we need to extend part */
+ 
+     unusedRem = MP_USED(rem) - MP_USED(div);
+     MP_DIGITS(&part) = MP_DIGITS(rem) + unusedRem;
+     MP_ALLOC(&part)  = MP_ALLOC(rem)  - unusedRem;
+     MP_USED(&part)   = MP_USED(div);
++
++    /* We have now truncated the part of the remainder to the same length as 
++     * the divisor. If part is smaller than div, extend part by one digit. */
+     if (s_mp_cmp(&part, div) < 0) {
+       -- unusedRem;
+ #if MP_ARGCHK == 2
+@@ -4222,26 +4225,34 @@ mp_err   s_mp_div(mp_int *rem, 	/* i: dividend, o: remainder */
+       -- MP_DIGITS(&part);
+       ++ MP_USED(&part);
+       ++ MP_ALLOC(&part);
++      partExtended = 1;
+     }
+ 
+     /* Compute a guess for the next quotient digit       */
+     q_msd = MP_DIGIT(&part, MP_USED(&part) - 1);
+     div_msd = MP_DIGIT(div, MP_USED(div) - 1);
+-    if (q_msd >= div_msd) {
++    if (!partExtended) {
++      /* In this case, q_msd /= div_msd is always 1. First, since div_msd is
++       * normalized to have the high bit set, 2*div_msd > MP_DIGIT_MAX. Since
++       * we didn't extend part, q_msd >= div_msd. Therefore we know that
++       * div_msd <= q_msd <= MP_DIGIT_MAX < 2*div_msd. Dividing by div_msd we
++       * get 1 <= q_msd/div_msd < 2. So q_msd /= div_msd must be 1. */
+       q_msd = 1;
+-    } else if (MP_USED(&part) > 1) {
++    } else {
+ #if !defined(MP_NO_MP_WORD) && !defined(MP_NO_DIV_WORD)
+       q_msd = (q_msd << MP_DIGIT_BIT) | MP_DIGIT(&part, MP_USED(&part) - 2);
+       q_msd /= div_msd;
+       if (q_msd == RADIX)
+         --q_msd;
+ #else
+-      mp_digit r;
+-      MP_CHECKOK( s_mpv_div_2dx1d(q_msd, MP_DIGIT(&part, MP_USED(&part) - 2), 
+-				  div_msd, &q_msd, &r) );
++      if (q_msd == div_msd) {
++        q_msd = MP_DIGIT_MAX;
++      } else {
++        mp_digit r;
++        MP_CHECKOK( s_mpv_div_2dx1d(q_msd, MP_DIGIT(&part, MP_USED(&part) - 2),
++				    div_msd, &q_msd, &r) );
++      }
+ #endif
+-    } else {
+-      q_msd = 0;
+     }
+ #if MP_ARGCHK == 2
+     assert(q_msd > 0); /* This case should never occur any more. */
diff --git a/debian/patches/CVE-2016-1950.patch b/debian/patches/CVE-2016-1950.patch
new file mode 100644
index 0000000..035c26c
--- /dev/null
+++ b/debian/patches/CVE-2016-1950.patch
@@ -0,0 +1,101 @@
+From: =?utf-8?q?Guido_G=C3=BCnther?= <agx@sigxcpu.org>
+Date: Fri, 1 Apr 2016 16:52:16 +0200
+Subject: CVE-2016-1950
+
+Consisting of upstream commits:
+
+    https://hg.mozilla.org/projects/nss/rev/b125e7dc9e3e
+---
+ nss/lib/util/secasn1d.c | 51 ++++++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 40 insertions(+), 11 deletions(-)
+
+diff --git a/nss/lib/util/secasn1d.c b/nss/lib/util/secasn1d.c
+index 43bf3d8..6ad348b 100644
+--- a/nss/lib/util/secasn1d.c
++++ b/nss/lib/util/secasn1d.c
+@@ -14,6 +14,8 @@
+ #define PR_Assert sec_asn1d_Assert
+ #endif
+ 
++#include <limits.h>
++
+ #include "secasn1.h"
+ #include "secerr.h"
+ 
+@@ -1594,6 +1596,7 @@ sec_asn1d_parse_leaf (sec_asn1d_state *state,
+ 
+     item = (SECItem *)(state->dest);
+     if (item != NULL && item->data != NULL) {
++	unsigned long offset;
+ 	/* Strip leading zeroes when target is unsigned integer */
+ 	if (state->underlying_kind == SEC_ASN1_INTEGER && /* INTEGER   */
+ 	    item->len == 0 &&                             /* MSB       */
+@@ -1604,8 +1607,42 @@ sec_asn1d_parse_leaf (sec_asn1d_state *state,
+ 		len--;
+ 	    }
+ 	}
+-	PORT_Memcpy (item->data + item->len, buf, len);
+-	item->len += len;
++        offset = item->len;
++        if (state->underlying_kind == SEC_ASN1_BIT_STRING) {
++            // The previous bit string must have no unused bits.
++            if (item->len & 0x7) {
++                PORT_SetError (SEC_ERROR_BAD_DER);
++                state->top->status = decodeError;
++                return 0;
++            }
++            // If this is a bit string, the length is bits, not bytes.
++            offset = item->len >> 3;
++        }
++        if (state->underlying_kind == SEC_ASN1_BIT_STRING) {
++            unsigned long len_in_bits;
++            // Protect against overflow during the bytes-to-bits conversion.
++            if (len >= (ULONG_MAX >> 3) + 1) {
++                PORT_SetError (SEC_ERROR_BAD_DER);
++                state->top->status = decodeError;
++                return 0;
++            }
++            len_in_bits = (len << 3) - state->bit_string_unused_bits;
++            // Protect against overflow when computing the total length in bits.
++            if (UINT_MAX - item->len < len_in_bits) {
++                PORT_SetError (SEC_ERROR_BAD_DER);
++                state->top->status = decodeError;
++                return 0;
++            }
++            item->len += len_in_bits;
++        } else {
++            if (UINT_MAX - item->len < len) {
++                PORT_SetError (SEC_ERROR_BAD_DER);
++                state->top->status = decodeError;
++                return 0;
++            }
++            item->len += len;
++        }
++        PORT_Memcpy (item->data + offset, buf, len);
+     }
+     state->pending -= bufLen;
+     if (state->pending == 0)
+@@ -1672,14 +1709,6 @@ sec_asn1d_parse_more_bit_string (sec_asn1d_state *state,
+     }
+ 
+     len = sec_asn1d_parse_leaf (state, buf, len);
+-    if (state->place == beforeEndOfContents && state->dest != NULL) {
+-	SECItem *item;
+-
+-	item = (SECItem *)(state->dest);
+-	if (item->len)
+-	    item->len = (item->len << 3) - state->bit_string_unused_bits;
+-    }
+-
+     return len;
+ }
+ 
+@@ -2209,7 +2238,7 @@ sec_asn1d_concat_substrings (sec_asn1d_state *state)
+ 	     * All bit-string substrings except the last one should be
+ 	     * a clean multiple of 8 bits.
+ 	     */
+-	    if (is_bit_string && (substring->next == NULL)
++	    if (is_bit_string && (substring->next != NULL)
+ 			      && (substring->len & 0x7)) {
+ 		PORT_SetError (SEC_ERROR_BAD_DER);
+ 		state->top->status = decodeError;
diff --git a/debian/patches/CVE-2016-1978.patch b/debian/patches/CVE-2016-1978.patch
new file mode 100644
index 0000000..a151d73
--- /dev/null
+++ b/debian/patches/CVE-2016-1978.patch
@@ -0,0 +1,104 @@
+From: =?utf-8?q?Guido_G=C3=BCnther?= <agx@sigxcpu.org>
+Date: Fri, 1 Apr 2016 16:29:27 +0200
+Subject: CVE-2016-1978
+
+Consisting of upstream commit
+
+    https://hg.mozilla.org/projects/nss/rev/2043304fb048
+---
+ nss/lib/ssl/ssl3con.c | 11 +++++++----
+ nss/lib/ssl/ssl3ecc.c |  9 +++++++--
+ 2 files changed, 14 insertions(+), 6 deletions(-)
+
+diff --git a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c
+index 0e1394e..45bba2b 100644
+--- a/nss/lib/ssl/ssl3con.c
++++ b/nss/lib/ssl/ssl3con.c
+@@ -6716,7 +6716,6 @@ ssl3_HandleServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+ 
+     	peerKey = PORT_ArenaZNew(arena, SECKEYPublicKey);
+     	if (peerKey == NULL) {
+-            PORT_FreeArena(arena, PR_FALSE);
+ 	    goto no_memory;
+ 	}
+ 
+@@ -6727,7 +6726,6 @@ ssl3_HandleServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+ 	if (SECITEM_CopyItem(arena, &peerKey->u.rsa.modulus,        &modulus) ||
+ 	    SECITEM_CopyItem(arena, &peerKey->u.rsa.publicExponent, &exponent))
+ 	{
+-            PORT_FreeArena(arena, PR_FALSE);
+ 	    goto no_memory;
+         }
+     	ss->sec.peerKey = peerKey;
+@@ -6820,7 +6818,7 @@ ssl3_HandleServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+ 	    goto no_memory;
+ 	}
+ 
+-    	ss->sec.peerKey = peerKey = PORT_ArenaZNew(arena, SECKEYPublicKey);
++    	peerKey = PORT_ArenaZNew(arena, SECKEYPublicKey);
+     	if (peerKey == NULL) {
+ 	    goto no_memory;
+ 	}
+@@ -6834,7 +6832,6 @@ ssl3_HandleServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+ 	    SECITEM_CopyItem(arena, &peerKey->u.dh.base,         &dh_g) ||
+ 	    SECITEM_CopyItem(arena, &peerKey->u.dh.publicValue,  &dh_Ys))
+ 	{
+-            PORT_FreeArena(arena, PR_FALSE);
+ 	    goto no_memory;
+         }
+     	ss->sec.peerKey = peerKey;
+@@ -6857,10 +6854,16 @@ ssl3_HandleServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+ alert_loser:
+     (void)SSL3_SendAlert(ss, alert_fatal, desc);
+ loser:
++    if (arena) {
++        PORT_FreeArena(arena, PR_FALSE);
++    }
+     PORT_SetError( errCode );
+     return SECFailure;
+ 
+ no_memory:	/* no-memory error has already been set. */
++    if (arena) {
++        PORT_FreeArena(arena, PR_FALSE);
++    }
+     ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
+     return SECFailure;
+ }
+diff --git a/nss/lib/ssl/ssl3ecc.c b/nss/lib/ssl/ssl3ecc.c
+index 555c89d..79e0839 100644
+--- a/nss/lib/ssl/ssl3ecc.c
++++ b/nss/lib/ssl/ssl3ecc.c
+@@ -703,7 +703,7 @@ ssl3_HandleECDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+         goto no_memory;
+     }
+ 
+-    ss->sec.peerKey = peerKey = PORT_ArenaZNew(arena, SECKEYPublicKey);
++    peerKey = PORT_ArenaZNew(arena, SECKEYPublicKey);
+     if (peerKey == NULL) {
+         goto no_memory;
+     }
+@@ -724,7 +724,6 @@ ssl3_HandleECDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+     /* copy publicValue in peerKey */
+     if (SECITEM_CopyItem(arena, &peerKey->u.ec.publicValue,  &ec_point))
+     {
+-        PORT_FreeArena(arena, PR_FALSE);
+         goto no_memory;
+     }
+     peerKey->pkcs11Slot         = NULL;
+@@ -738,10 +737,16 @@ ssl3_HandleECDHServerKeyExchange(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+ alert_loser:
+     (void)SSL3_SendAlert(ss, alert_fatal, desc);
+ loser:
++    if (arena) {
++        PORT_FreeArena(arena, PR_FALSE);
++    }
+     PORT_SetError( errCode );
+     return SECFailure;
+ 
+ no_memory:      /* no-memory error has already been set. */
++    if (arena) {
++        PORT_FreeArena(arena, PR_FALSE);
++    }
+     ssl_MapLowLevelError(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE);
+     return SECFailure;
+ }
diff --git a/debian/patches/CVE-2016-1979.patch b/debian/patches/CVE-2016-1979.patch
new file mode 100644
index 0000000..ffed19c
--- /dev/null
+++ b/debian/patches/CVE-2016-1979.patch
@@ -0,0 +1,77 @@
+From: =?utf-8?q?Guido_G=C3=BCnther?= <agx@sigxcpu.org>
+Date: Fri, 1 Apr 2016 16:25:44 +0200
+Subject: CVE-2016-1979
+
+Consisting of upstream commit:
+
+    https://hg.mozilla.org/projects/nss/rev/df5c58f4b38d
+    https://hg.mozilla.org/projects/nss/rev/348a65c4f55f
+---
+ nss/lib/cryptohi/seckey.c   | 8 ++++----
+ nss/lib/pk11wrap/pk11pk12.c | 8 ++++++--
+ 2 files changed, 10 insertions(+), 6 deletions(-)
+
+diff --git a/nss/lib/cryptohi/seckey.c b/nss/lib/cryptohi/seckey.c
+index 16d2a49..788d815 100644
+--- a/nss/lib/cryptohi/seckey.c
++++ b/nss/lib/cryptohi/seckey.c
+@@ -1550,7 +1550,7 @@ SECKEY_DestroyPrivateKeyInfo(SECKEYPrivateKeyInfo *pvk,
+ 	     * this yet.
+ 	     */
+ 	    PORT_Memset(pvk->privateKey.data, 0, pvk->privateKey.len);
+-	    PORT_Memset((char *)pvk, 0, sizeof(*pvk));
++	    PORT_Memset(pvk, 0, sizeof(*pvk));
+ 	    if(freeit == PR_TRUE) {
+ 		PORT_FreeArena(poolp, PR_TRUE);
+ 	    } else {
+@@ -1560,7 +1560,7 @@ SECKEY_DestroyPrivateKeyInfo(SECKEYPrivateKeyInfo *pvk,
+ 	    SECITEM_ZfreeItem(&pvk->version, PR_FALSE);
+ 	    SECITEM_ZfreeItem(&pvk->privateKey, PR_FALSE);
+ 	    SECOID_DestroyAlgorithmID(&pvk->algorithm, PR_FALSE);
+-	    PORT_Memset((char *)pvk, 0, sizeof(*pvk));
++	    PORT_Memset(pvk, 0, sizeof(*pvk));
+ 	    if(freeit == PR_TRUE) {
+ 		PORT_Free(pvk);
+ 	    }
+@@ -1581,7 +1581,7 @@ SECKEY_DestroyEncryptedPrivateKeyInfo(SECKEYEncryptedPrivateKeyInfo *epki,
+ 	     * this yet.
+ 	     */
+ 	    PORT_Memset(epki->encryptedData.data, 0, epki->encryptedData.len);
+-	    PORT_Memset((char *)epki, 0, sizeof(*epki));
++	    PORT_Memset(epki, 0, sizeof(*epki));
+ 	    if(freeit == PR_TRUE) {
+ 		PORT_FreeArena(poolp, PR_TRUE);
+ 	    } else {
+@@ -1590,7 +1590,7 @@ SECKEY_DestroyEncryptedPrivateKeyInfo(SECKEYEncryptedPrivateKeyInfo *epki,
+ 	} else {
+ 	    SECITEM_ZfreeItem(&epki->encryptedData, PR_FALSE);
+ 	    SECOID_DestroyAlgorithmID(&epki->algorithm, PR_FALSE);
+-	    PORT_Memset((char *)epki, 0, sizeof(*epki));
++	    PORT_Memset(epki, 0, sizeof(*epki));
+ 	    if(freeit == PR_TRUE) {
+ 		PORT_Free(epki);
+ 	    }
+diff --git a/nss/lib/pk11wrap/pk11pk12.c b/nss/lib/pk11wrap/pk11pk12.c
+index 471e57b..39dbfb2 100644
+--- a/nss/lib/pk11wrap/pk11pk12.c
++++ b/nss/lib/pk11wrap/pk11pk12.c
+@@ -234,13 +234,17 @@ PK11_ImportDERPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot, SECItem *derPKI,
+     rv = SEC_ASN1DecodeItem(pki->arena, pki, SECKEY_PrivateKeyInfoTemplate,
+ 		derPKI);
+     if( rv != SECSuccess ) {
+-	goto finish;
++        /* If SEC_ASN1DecodeItem fails, we cannot assume anything about the
++         * validity of the data in pki. The best we can do is free the arena
++         * and return.
++         */
++        PORT_FreeArena(temparena, PR_TRUE);
++        return rv;
+     }
+ 
+     rv = PK11_ImportPrivateKeyInfoAndReturnKey(slot, pki, nickname,
+ 		publicValue, isPerm, isPrivate, keyUsage, privk, wincx);
+ 
+-finish:
+     /* this zeroes the key and frees the arena */
+     SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE /*freeit*/);
+     return rv;
diff --git a/debian/patches/series b/debian/patches/series
index 7f899ec..55a58d7 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -8,3 +8,9 @@
 99_CVE-2015-2721.patch
 100_CVE-2015-2730.patch
 101_prefer_stronger_cert_chains.patch
+CVE-2015-7182.patch
+CVE-2015-7181.patch
+CVE-2016-1938.patch
+CVE-2016-1950.patch
+CVE-2016-1978.patch
+CVE-2016-1979.patch
diff --git a/debian/tests/control b/debian/tests/control
new file mode 100644
index 0000000..1b3d995
--- /dev/null
+++ b/debian/tests/control
@@ -0,0 +1,5 @@
+Tests: test-cert.sh
+Depends: libnss3-tools
+
+Tests: test-link.make
+Depends: libnss3-dev, pkg-config, g++
diff --git a/debian/tests/test-cert.sh b/debian/tests/test-cert.sh
new file mode 100644
index 0000000..14c0907
--- /dev/null
+++ b/debian/tests/test-cert.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+#
+# Check some basic CA operations
+
+set -e
+
+cleanup() {
+    [ -z "$DIR" ] || rm -rf "$DIR"
+}
+
+
+run_certutil() {
+    CMD="certutil -z $DIR/random -f $DIR/passwd -d sql:$DIR $@"
+    echo "Running: $CMD"
+    $CMD
+}
+
+DIR=`mktemp -p . -d`
+#trap cleanup EXIT ERR
+
+dd  bs=20 count=1 if=/dev/urandom of=$DIR/random 2>/dev/null
+echo "password" > $DIR/passwd
+
+# Create the database
+run_certutil -N
+# Create a self signed certificate
+run_certutil -S -k rsa -n test-ca -s CN=testca -t c -x 2>/dev/null
+# Create a certificate request
+run_certutil -R -k rsa -g 2048 -n test-cert -s "CN=testcert" -o $DIR/cert.req -a 2>/dev/null
+# Sign with ca
+run_certutil -C -m 10000 -c test-ca -i $DIR/cert.req -o $DIR/cert.cer -a
+run_certutil -A -n test-cert -i $DIR/cert.cer -t c -a
+
+echo -n "Checking if ca is present..."
+run_certutil -L -n test-ca >/dev/null
+echo "OK."
+
+echo -n "Checking if cert present..."
+run_certutil -L -n test-cert >/dev/null
+echo "OK."
+
+exit 0
diff --git a/debian/tests/test-link.cpp b/debian/tests/test-link.cpp
new file mode 100644
index 0000000..0880b4e
--- /dev/null
+++ b/debian/tests/test-link.cpp
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <nss.h>
+
+int main()
+{
+  int ret = 0;
+  SECStatus s;
+  char *t = strdup("/tmp/nss.XXXXXX");
+  char *tmpdir = mkdtemp(t);
+
+  if (tmpdir == NULL)
+    fprintf(stderr, "Failed to create temp directory: %s", strerror(errno));
+  
+  s = NSS_InitReadWrite(tmpdir);
+  if (s != SECSuccess)
+    ret = 2;
+  
+  NSS_Shutdown();
+  free(t);
+
+  return ret;
+}
diff --git a/debian/tests/test-link.make b/debian/tests/test-link.make
new file mode 100755
index 0000000..19e4e7c
--- /dev/null
+++ b/debian/tests/test-link.make
@@ -0,0 +1,10 @@
+#!/usr/bin/make -f
+
+all: a.out
+	./a.out
+	rm -f a.out
+
+a.out: debian/tests/test-link.cpp
+	g++ -Wall -Werror $<  $(shell pkg-config --cflags nss) $(shell pkg-config --libs nss)
+
+.PHONY: all

Reply to: