Closed Bug 932020 Opened 7 years ago Closed 7 years ago

CERT_CacheOCSPResponseFromSideChannel fails to set error code in some cases when returning SECFailure

Categories

(NSS :: Libraries, defect, P1)

3.15.4
defect

Tracking

(Not tracked)

RESOLVED FIXED
3.15.4

People

(Reporter: briansmith, Assigned: KaiE)

References

Details

(Keywords: regression)

Attachments

(5 files, 4 obsolete files)

When I was testing the NSS head in mozilla-central, I started getting this assertion when visiting https://nmusd-ca.schoolloop.com. The error is almost definitely due to returning SECFailure from some function without calling PR_SetError/PORT_SetError first.

It seems to be a regression caused by the OCSP GET code:

python client.py update_nss NSS_3_15_3_BETA1 -> broken
python client.py update_nss fc6df0051709 -> OK
python client.py update_nss 4be429189df6 -> broken.

In the interests of expediency and reducing risk with the NSS 3.15.3 release, I would like to back out bug 436414 and have land it in NSS 3.15.4 instead. This way we can enable TLS 1.2 in Firefox ASAP by landing NSS 3.15.3 into mozilla-central sooner.

---------------------------
NSGlue_Assertion
---------------------------
ASSERTION: no error set during certificate validation failure: 'Not Reached', file c:/p/firefox/landing/src/security/manager/ssl/src/SSLServerCertVerification.cpp, line 1134

Click Abort to exit the Application.
Click Retry to Debug the Application.
Click Ignore to continue running the Application.
---------------------------
Abort   Retry   Ignore   
---------------------------
Severity: normal → critical
Priority: -- → P1
Target Milestone: --- → 3.15.3
We should give Kai and Bob a chance to investigate this. This reflects
whether we want the OCSP GET code to succeed or not.

I've tracked down the problem. It is a bug introduced in this change to
CERT_CacheOCSPResponseFromSideChannel:

https://hg.mozilla.org/projects/nss/diff/654082cbd702/lib/certhigh/ocsp.c

   1.708 @@ -4953,22 +5165,30 @@ CERT_CacheOCSPResponseFromSideChannel(CE
   1.709      if (rv == SECSuccess && rvOcsp == SECSuccess) {
   1.710          /* The cached value is good. We don't want to waste time validating
   1.711           * this OCSP response. This is the first column in the table above. */
   1.712          CERT_DestroyOCSPCertID(certID);
   1.713          return rv;
   1.714      }
   1.715  
   1.716      /* The logic for caching the more recent response is handled in
   1.717 -     * ocsp_CreateOrUpdateCacheEntry, which is called by this function. */
   1.718 -    rv = ocsp_CacheEncodedOCSPResponse(handle, certID, cert, time,
   1.719 -                                       pwArg, encodedResponse,
   1.720 -                                       PR_FALSE /* don't cache if invalid */,
   1.721 -                                       &certIDWasConsumed,
   1.722 -                                       &rvOcsp);
   1.723 +     * ocsp_CacheSingleResponse. */
   1.724 +
   1.725 +    rv = ocsp_GetDecodedVerifiedSingleResponseForID(handle, certID, cert,
   1.726 +						    time, pwArg,
   1.727 +						    encodedResponse,
   1.728 +						    &decodedResponse,
   1.729 +						    &singleResponse);
   1.730 +    /* Cache any valid singleResponse, regardless of status. */
   1.731 +    if (rv == SECSuccess && singleResponse) {
   1.732 +	ocsp_CacheSingleResponse(certID, singleResponse, &certIDWasConsumed);
   1.733 +    }
   1.734 +    if (decodedResponse) {
   1.735 +	CERT_DestroyOCSPResponse(decodedResponse);
   1.736 +    }
   1.737      if (!certIDWasConsumed) {
   1.738          CERT_DestroyOCSPCertID(certID);
   1.739      }
   1.740      return rv == SECSuccess ? rvOcsp : rv;
   1.741  }
   1.742  
   1.743  /*
   1.744   * Status in *certIDWasConsumed will always be correct, regardless of 

The original code calls ocsp_CacheEncodedOCSPResponse, which returns |rv| and also
sets |rvOcsp| as an output argument.

The new code calls ocsp_GetDecodedVerifiedSingleResponseForID, which returns |rv|.
So |rvOcsp| contains a stale value set by a previous call to
ocsp_GetCachedOCSPResponseStatusIfFresh (not shown in this code snippet).

I think this bug can be fixed by changing the last line
    return rv == SECSuccess ? rvOcsp : rv;
to
    return rv;

This assumes that if the previous ocsp_GetCachedOCSPResponseStatusIfFresh
call returns rv=SECFailure and sets rvOcsp to SECFailure, it is fine to
ignore those rv and rvOcsp values. I think it is fine.
Brian, thanks for reporting the problem.
Wan-Teh, thanks for tracking down the cause.

I failed to set rvOcsp when changing the code.

The fix is to use
   rvOcsp = ocsp_SingleResponseCertHasGoodStatus()

which was done by the old code, 

and which is still being done at the other place that calls ocsp_GetDecodedVerifiedSingleResponseForID().
Attached patch patch v1 (obsolete) — Splinter Review
This patch should restore the previous behaviour.

Wan-Teh, would you be able to review this? If not, let's ask Bob.

Brian, if you're able to easily test this patch, I'd appreciate it.
Attachment #823958 - Flags: review?(wtc)
Attached patch Patch v2Splinter Review
In order to make it obvious that we'll never reuse the former value of rvOcsp, I'd also like to change 
    if (rv == SECSuccess && singleResponse)
to
    if (rv == SECSuccess)

If ocsp_GetDecodedVerifiedSingleResponseForID returns SECSuccess, only, if it returns the singleResponse, too. The other code path already relies on that, without testing singleResponse for non-null.
Attachment #823958 - Attachment is obsolete: true
Attachment #823958 - Flags: review?(wtc)
Attachment #823966 - Flags: review?(wtc)
I've confirmed the fix (attachment 823966 [details] [diff] [review]) using this test patch.

PSM checks the return value of CERT_CacheOCSPResponseFromSideChannel, but the NSS code (default SSL auth and tstclnt) ignores it.

Using this test patch, only, which adds an consistency check that's equivalent to PSM's check, I can reproduce the failure that Brian was reporting (and our test suite aborts).

With both patches applied, I can no longer reproduce the failure (test suite passes).

If we believe this consistency check is reasonable, we could add it to NSS.
I'm adjusting the summary, as I don't think CERT_VerifyCert was responsible for the failure you saw, but rather the culprit is CERT_CacheOCSPResponseFromSideChannel.
Summary: CERT_VerifyCert fails to set error code in some cases when returning SECFailure → CERT_CacheOCSPResponseFromSideChannel fails to set error code in some cases when returning SECFailure
Comment on attachment 823966 [details] [diff] [review]
Patch v2

Review of attachment 823966 [details] [diff] [review]:
-----------------------------------------------------------------

r=wtc.

Side remark: it seems that CERT_CacheOCSPResponseFromSideChannel
should only report the status of the caching operation. Whether
the OCSP response contains a good or revoked status, should not
matter to the caching operation. The good or revoked status will
be used when we verify the certificate.

If CERT_CacheOCSPResponseFromSideChannel already behaved this way
before your OCSP GET change, then it is fine to preserve this
behavior. Otherwise we should not add this behavior.

::: lib/certhigh/ocsp.c
@@ +5169,5 @@
>  						    encodedResponse,
>  						    &decodedResponse,
>  						    &singleResponse);
> +    if (rv == SECSuccess) {
> +	rvOcsp = ocsp_SingleResponseCertHasGoodStatus(singleResponse, time);

The original code checks singleResponse for null. The new code seems
to assume that if rv == SECSuccess, then singleResponse cannot be null.
I verified this assumption is correct by inspecting the code of
ocsp_GetDecodedVerifiedSingleResponseForID. Please confirm that.

Note that if singleResponse is null, we will crash inside
ocsp_SingleResponseCertHasGoodStatus.
Attachment #823966 - Flags: review?(wtc) → review+
(In reply to Kai Engert (:kaie) from comment #4)
>
> If ocsp_GetDecodedVerifiedSingleResponseForID returns SECSuccess, only, if
> it returns the singleResponse, too. The other code path already relies on
> that, without testing singleResponse for non-null.

Ah, you already mentioned this assumption. Thank you.
Comment on attachment 824059 [details] [diff] [review]
testing - optional consistency checking

Review of attachment 824059 [details] [diff] [review]:
-----------------------------------------------------------------

I support checking the return value of
CERT_CacheOCSPResponseFromSideChannel, but I think if
CERT_CacheOCSPResponseFromSideChannel fails, our "authCertificate"
callback/hook should also fail.

::: cmd/tstclnt/tstclnt.c
@@ +527,5 @@
>  
>          csa = SSL_PeerStapledOCSPResponses(fd);
>          if (csa) {
>              for (i = 0; i < csa->len; ++i) {
> +		SECStatus test_rv = 

Note: sslauth.c has the same problems.

1. Nit: the code review tool shows a space at the end of this line.

2. Will this fit under 80 characters?

            SECStatus test_rv = CERT_CacheOCSPResponseFromSideChannel(

If not, we should ideally format the code like this:

            SECStatus test_rv =
                CERT_CacheOCSPResponseFromSideChannel(
                    arguments...

::: lib/ssl/sslauth.c
@@ +272,5 @@
>  					now, &certStatusArray->items[0],
>  					ss->pkcs11PinArg);
> +	if (test_rv != SECSuccess) {
> +	    PRErrorCode error = PR_GetError();
> +	    PORT_Assert(error != 0);

It seems that ownAuthCertificate should fail if
CERT_CacheOCSPResponseFromSideChannel fails. So I
think we need to do more. In that case, we can
just use the existing |rv| variable.
Attachment #824059 - Flags: review+
(In reply to Wan-Teh Chang from comment #7)
> 
> If CERT_CacheOCSPResponseFromSideChannel already behaved this way
> before your OCSP GET change, then it is fine to preserve this
> behavior. Otherwise we should not add this behavior.

Yes, in my understanding it already behaved that way.
Not found in cache -> Response invalid -> Not trying to insert -> return SECFailure

According to the function documentation the status of the OCSP response influences the return value of the function:
 * RETURN:
 *   SECSuccess if the cert was found in the cache, or if the OCSP response was
 *   found to be valid and inserted into the cache. SECFailure otherwise.
(In reply to Wan-Teh Chang from comment #9)
> 
> I support checking the return value of
> CERT_CacheOCSPResponseFromSideChannel, but I think if
> CERT_CacheOCSPResponseFromSideChannel fails, our "authCertificate"
> callback/hook should also fail.

I think we shouldn't do this, because of the behaviour of CERT_CacheOCSPResponseFromSideChannel.

In order to avoid new side effects, an invalid OCSP response retrieved from a side channel shouldn't cause the standard authentication attempts to fail.


> 1. Nit: the code review tool shows a space at the end of this line.
> ...
> If not, we should ideally format the code like this:

The testing patch was made to minimize the visual diff.
I made a new testing patch that reformats correctly.
Attachment #824059 - Attachment is obsolete: true
Attachment #824206 - Flags: review?(wtc)
Attachment #824206 - Attachment description: testing v2 (optional) → consistency checking v2 (optional)
Comment on attachment 824206 [details] [diff] [review]
consistency checking v2 (optional)

Review of attachment 824206 [details] [diff] [review]:
-----------------------------------------------------------------

r=wtc.

Can you change PSM to not fail the certificate verification
if CERT_CacheOCSPResponseFromSideChannel fails? Thanks.
Attachment #824206 - Flags: review?(wtc) → review+
(In reply to Wan-Teh Chang from comment #12)
> Can you change PSM to not fail the certificate verification
> if CERT_CacheOCSPResponseFromSideChannel fails? Thanks.

I've filed bug 932734 for your suggestion.
Checked in:
https://hg.mozilla.org/projects/nss/rev/dcc378735ad8
https://hg.mozilla.org/projects/nss/rev/501ea12af2df
Status: NEW → RESOLVED
Closed: 7 years ago
Resolution: --- → FIXED
Reopening.
The test suite fails in one scenario, because we hit the assertion:

ssl.sh: OCSP stapling, good status, bad signature produced a returncode of 134, expected is 2 - Core file is detected - FAILED

Because Brian had suggested that as a remedy, I have backed out locally all nine patches that were related to OCSP GET, but kept the new assertion check - and I still see the assertion failure in one scenario.

This means, backing out OCSP GET doesn't help, because the scenario "failure without error code" has been possible since we added OCSP stapling.
Status: RESOLVED → REOPENED
Resolution: FIXED → ---
Wan-Teh, you can please review this bustage fix, which I've already checked in?

There was a legacy code path, unchanged since 2007, which didn't set an error code.
Attachment #824683 - Flags: review?(wtc)
Comment on attachment 824683 [details] [diff] [review]
fix legacy code path to set an error code. (backed out by later patch)

https://hg.mozilla.org/projects/nss/rev/d6c53661fe76
Attachment #824683 - Flags: checked-in+
Attachment #823966 - Flags: checked-in+
Attachment #824206 - Flags: checked-in+
Comment on attachment 824683 [details] [diff] [review]
fix legacy code path to set an error code. (backed out by later patch)

Review of attachment 824683 [details] [diff] [review]:
-----------------------------------------------------------------

::: lib/certhigh/ocsp.c
@@ +4120,3 @@
>  	    /* Make the error a little more specific. */
>  	    PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
>  	}

Adding an (err == 0) check here seems wrong. If ocsp_GetSignerCertificate returns NULL on failure then it should call PR_SetError before returning NULL. The problem may not be in ocsp_GetSignerCertificate, but in one of the lower-level functions used by ocsp_GetSignerCertificate. Those lower-level functions are potentially used outside of ocsp_GetSignerCertificate. So, it is important to find out the specific reason that ocsp_GetSignerCertificate returns NULL without calling PR_SetError, to make sure we don't have a broader problem.

Also, the corresponding logic in libpkix is this:

response->signerCert = 
    ocsp_GetSignerCertificate(response->handle, tbsData,
                              signature, issuerCert);
if (response->signerCert == NULL) {
    PORT_SetError(SEC_ERROR_UNKNOWN_SIGNER);
    goto cleanup;
}

It is ungood that libpkix returns a different error code. It also seems ungood that libpkix eats other potential errors. But, that explains why the classic cert verification code exhibits this problem but libpkix does.
Attachment #824683 - Flags: review-
Comment on attachment 824683 [details] [diff] [review]
fix legacy code path to set an error code. (backed out by later patch)

Review of attachment 824683 [details] [diff] [review]:
-----------------------------------------------------------------

::: lib/certhigh/ocsp.c
@@ +4111,5 @@
>  	return signature->status;
>      }
>  
>      signerCert = ocsp_GetSignerCertificate(handle, tbsData,
>                                             signature, issuer);

Please track down where in ocsp_GetSignerCertificate we return NULL without
setting the error code.
Attachment #824683 - Flags: review?(wtc) → review-
Comment on attachment 824206 [details] [diff] [review]
consistency checking v2 (optional)

Review of attachment 824206 [details] [diff] [review]:
-----------------------------------------------------------------

::: cmd/tstclnt/tstclnt.c
@@ +526,5 @@
>          }
>  
>          csa = SSL_PeerStapledOCSPResponses(fd);
>          if (csa) {
>              for (i = 0; i < csa->len; ++i) {

We need to call PORT_SetError(0) here. Otherwise, any previous call to PORT_SetError will cause the PORT_Assert(error != 0); to succeed even when CERT_CacheOCSPResponseFromSideChannel fails to call PR_SetError.
Attachment #824206 - Flags: review-
(In reply to Wan-Teh Chang from comment #19)
> Please track down where in ocsp_GetSignerCertificate we return NULL without
> setting the error code.

(In reply to Brian Smith from comment #18)
> If ocsp_GetSignerCertificate
> returns NULL on failure then it should call PR_SetError before returning
> NULL. The problem may not be in ocsp_GetSignerCertificate, but in one of the
> lower-level functions used by ocsp_GetSignerCertificate.

It happens if ocsp_GetSignerCertificate couldn't find the cert, but no error occurred while searching.

I agree that ocsp_GetSignerCertificate is the better place for the fix, this patch reverts the code that you rejected.

In our test scenario, we perform the lookup by key-id, which involves a couple of comparisons for matches, without finding any matches.

(The code path to search by name calls CERT_FindCertByName - I don't know if that would set an error code on not finding a match, probably not.)

Since the caller of ocsp_GetSignerCertificate already expects a potential error code SEC_ERROR_UNKNOWN_CERT, we could set that code whenever it returns NULL, but no error code has been set yet.


> Also, the corresponding logic in libpkix is this:
> 
> if (response->signerCert == NULL) {
>     PORT_SetError(SEC_ERROR_UNKNOWN_SIGNER);
> 
> It is ungood that libpkix returns a different error code.

Should we adjust what libpkix returns? This is the only place this error code is referenced! I couldn't find any code checking for that error!

We could change libpkix to return the same SEC_ERROR_OCSP_INVALID_SIGNING_CERT error code - if a cert is found but can't be verified, this error code is already being used.
Attachment #824683 - Attachment is obsolete: true
Attachment #824936 - Flags: review?(wtc)
(In reply to Kai Engert (:kaie) from comment #21)
> Since the caller of ocsp_GetSignerCertificate already expects a potential
> error code SEC_ERROR_UNKNOWN_CERT, we could set that code whenever it
> returns NULL, but no error code has been set yet.

... which will cause CERT_VerifyOCSPResponseSignature to set SEC_ERROR_OCSP_INVALID_SIGNING_CERT
Comment on attachment 824936 [details] [diff] [review]
legacy fix, second attempt (and backout rejected bustage fix)

Review of attachment 824936 [details] [diff] [review]:
-----------------------------------------------------------------

Thank you for tracking this down. I suggest two changes.

::: lib/certhigh/ocsp.c
@@ +3998,5 @@
>  	CERT_DestroyCertArray(certs, certCount);
>      }
> +    if (!signerCert && PORT_GetError() == 0) {
> +	PORT_SetError(SEC_ERROR_UNKNOWN_CERT);
> +    }

This is not the right place. Also, PORT_GetError() may return a stale
error code rather than 0.

I think this is a better way to fix the problem:

diff --git a/lib/certhigh/ocsp.c b/lib/certhigh/ocsp.c
--- a/lib/certhigh/ocsp.c
+++ b/lib/certhigh/ocsp.c
@@ -3986,16 +3986,19 @@ ocsp_GetSignerCertificate(CERTCertDBHand
 	} else if (issuer && ocsp_matchcert(certIndex,issuer)) {
 	    signerCert = CERT_DupCertificate(issuer);
 	} 
 	for (i=0; (signerCert == NULL) && (i < certCount); i++) {
 	    if (ocsp_matchcert(certIndex,certs[i])) {
 		signerCert = CERT_DupCertificate(certs[i]);
 	    }
 	}
+	if (signerCert == NULL) {
+	    PORT_SetError(SEC_ERROR_UNKNOWN_CERT);
+	}
     }
 
 finish:
     if (certs != NULL) {
 	CERT_DestroyCertArray(certs, certCount);
     }
 
     return signerCert;

::: lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.c
@@ +853,5 @@
>                  ocsp_GetSignerCertificate(response->handle, tbsData,
>                                            signature, issuerCert);
>              
>              if (response->signerCert == NULL) {
> +                PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);

I believe Brian suggested that we should not call PORT_SetError here.

If you think SEC_ERROR_OCSP_INVALID_SIGNING_CERT is a more accurate
error code, we should set SEC_ERROR_OCSP_INVALID_SIGNING_CERT in
ocsp_GetSignerCertificate.
Attachment #824936 - Flags: review?(wtc) → review-
(In reply to Wan-Teh Chang from comment #23)
> I think this is a better way to fix the problem:

Your suggestion is the minimal fix for the affected code path, only, and if fine with me, too. I'm attaching the updated patch.

> I believe Brian suggested that we should not call PORT_SetError here.

Brian's comment had sounded to me as a statement of unhappyness, but it wasn't clear to me which fix he prefers, and as a result I had suggested a fix that seemed reasonable to me. But never mind, since we apparently understood him differently, and because changing libpkix isn't immediately necessary for the issue we're trying to fix, I'm skipping that part. I'll let Brian clarify which fix he wants.
Attachment #824936 - Attachment is obsolete: true
Attachment #825289 - Flags: review?(wtc)
Comment on attachment 825289 [details] [diff] [review]
legacy fix, third attempt (and backout rejected bustage fix)

Review of attachment 825289 [details] [diff] [review]:
-----------------------------------------------------------------

r=wtc. Thanks.
Attachment #825289 - Flags: review?(wtc) → review+
Comment on attachment 824936 [details] [diff] [review]
legacy fix, second attempt (and backout rejected bustage fix)

Review of attachment 824936 [details] [diff] [review]:
-----------------------------------------------------------------

::: lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.c
@@ +853,5 @@
>                  ocsp_GetSignerCertificate(response->handle, tbsData,
>                                            signature, issuerCert);
>              
>              if (response->signerCert == NULL) {
> +                PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);

I suggest we change this to look like the equivalent code in ocsp.c:

    if (response->signerCert == NULL) {
        if (PORT_GetError() == SEC_ERROR_UNKNOWN_CERT) {
            /* Make the error a little more specific. */
            PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
        }
        goto cleanup;
    }
(In reply to Wan-Teh Chang from comment #26)
> ::: lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.c
> @@ +853,5 @@
> >                  ocsp_GetSignerCertificate(response->handle, tbsData,
> >                                            signature, issuerCert);
> >              
> >              if (response->signerCert == NULL) {
> > +                PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
> 
> I suggest we change this to look like the equivalent code in ocsp.c:
> 
>     if (response->signerCert == NULL) {
>         if (PORT_GetError() == SEC_ERROR_UNKNOWN_CERT) {
>             /* Make the error a little more specific. */
>             PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
>         }
>         goto cleanup;
>     }

I agree.
Attachment #824683 - Attachment description: fix legacy code path to set an error code. → fix legacy code path to set an error code. (backed out by later patch)
Attachment #824683 - Flags: checked-in+
This is Wan-Teh's code from comment 26 turned into a patch.

It has r=bsmith in comment 27.
Attachment #825551 - Flags: review+
Comment on attachment 825289 [details] [diff] [review]
legacy fix, third attempt (and backout rejected bustage fix)

https://hg.mozilla.org/projects/nss/rev/0cbd82e18ea1
Attachment #825289 - Flags: checked-in+
Comment on attachment 825551 [details] [diff] [review]
change libpkix failure handling of ocsp_GetSignerCertificate to match classic NSS code

https://hg.mozilla.org/projects/nss/rev/834ca86eaa63
Attachment #825551 - Flags: checked-in+
Thanks
Status: REOPENED → RESOLVED
Closed: 7 years ago7 years ago
Resolution: --- → FIXED
Kai, I tested your changes in Firefox and verified that we're not hitting the assertion that we were previously hitting. Thanks for fixing this so fast.

Here is a patch that clears the NSPR error code before calling CERT_CacheOCSPResponseFromSideChannel like we do in PSM. This is good because it is highly likely that some function that was called before CERT_CacheOCSPResponseFromSideChannel has called PR_SetError(0).
Attachment #825834 - Flags: review?(kaie)
(In reply to Brian Smith (:briansmith, was :bsmith@mozilla.com; NEEDINFO me if you want a response) from comment #32)
> Here is a patch that clears the NSPR error code before calling
> CERT_CacheOCSPResponseFromSideChannel like we do in PSM. This is good
> because it is highly likely that some function that was called before
> CERT_CacheOCSPResponseFromSideChannel has called PR_SetError(0).

s/called PR_SetError(0)/called PR_SetError(x) with x 1= 0/
Comment on attachment 825834 [details] [diff] [review]
add-PORT_SetError.patch

Thanks, good idea, r=kaie

Checked in.
https://hg.mozilla.org/projects/nss/rev/25745a91e8ae
Attachment #825834 - Flags: review?(kaie)
Attachment #825834 - Flags: review+
Attachment #825834 - Flags: checked-in+
changing target milestone to 3.15.4
Target Milestone: 3.15.3 → 3.15.4
Version: trunk → 3.15.4
You need to log in before you can comment on or make changes to this bug.