Bug 644006: Don't crash on zero-length input to nsNSSCertificateDB::ConstructX509FromBase64. r=honzab

This commit is contained in:
Zack Weinberg 2011-03-27 11:21:08 -07:00
parent da5eb09b05
commit c940b01168
3 changed files with 117 additions and 50 deletions

View File

@ -1485,65 +1485,54 @@ nsNSSCertificateDB::FindCertByEmailAddress(nsISupports *aToken, const char *aEma
/* nsIX509Cert constructX509FromBase64 (in string base64); */
NS_IMETHODIMP
nsNSSCertificateDB::ConstructX509FromBase64(const char * base64, nsIX509Cert **_retval)
nsNSSCertificateDB::ConstructX509FromBase64(const char *base64,
nsIX509Cert **_retval)
{
if (!_retval) {
return NS_ERROR_FAILURE;
NS_ENSURE_ARG_POINTER(_retval);
// sure would be nice to have a smart pointer class for PL_ allocations
// unfortunately, we cannot distinguish out-of-memory from bad-input here
PRUint32 len = PL_strlen(base64);
char *certDER = PL_Base64Decode(base64, len, NULL);
if (!certDER)
return NS_ERROR_ILLEGAL_VALUE;
if (!*certDER) {
PL_strfree(certDER);
return NS_ERROR_ILLEGAL_VALUE;
}
// If we get to this point, we know we had well-formed base64 input;
// therefore the input string cannot have been less than two
// characters long. Compute the unpadded length of the decoded data.
PRUint32 lengthDER = (len * 3) / 4;
if (base64[len-1] == '=') {
lengthDER--;
if (base64[len-2] == '=')
lengthDER--;
}
nsNSSShutDownPreventionLock locker;
PRUint32 len = PL_strlen(base64);
int adjust = 0;
SECItem secitem_cert;
secitem_cert.type = siDERCertBuffer;
secitem_cert.data = (unsigned char*)certDER;
secitem_cert.len = lengthDER;
/* Compute length adjustment */
if (base64[len-1] == '=') {
adjust++;
if (base64[len-2] == '=') adjust++;
}
CERTCertificate *cert =
CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &secitem_cert,
nsnull, PR_FALSE, PR_TRUE);
PL_strfree(certDER);
nsresult rv = NS_OK;
char *certDER = 0;
PRInt32 lengthDER = 0;
if (!cert)
return (PORT_GetError() == SEC_ERROR_NO_MEMORY)
? NS_ERROR_OUT_OF_MEMORY : NS_ERROR_FAILURE;
certDER = PL_Base64Decode(base64, len, NULL);
if (!certDER || !*certDER) {
rv = NS_ERROR_ILLEGAL_VALUE;
}
else {
lengthDER = (len*3)/4 - adjust;
nsNSSCertificate *nsNSS = nsNSSCertificate::Create(cert);
CERT_DestroyCertificate(cert);
SECItem secitem_cert;
secitem_cert.type = siDERCertBuffer;
secitem_cert.data = (unsigned char*)certDER;
secitem_cert.len = lengthDER;
if (!nsNSS)
return NS_ERROR_OUT_OF_MEMORY;
CERTCertificate *cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &secitem_cert, nsnull, PR_FALSE, PR_TRUE);
if (!cert) {
rv = NS_ERROR_FAILURE;
}
else {
nsNSSCertificate *nsNSS = nsNSSCertificate::Create(cert);
if (!nsNSS) {
rv = NS_ERROR_OUT_OF_MEMORY;
}
else {
nsresult rv = nsNSS->QueryInterface(NS_GET_IID(nsIX509Cert), (void**)_retval);
if (NS_SUCCEEDED(rv) && *_retval) {
NS_ADDREF(*_retval);
}
NS_RELEASE(nsNSS);
}
CERT_DestroyCertificate(cert);
}
}
if (certDER) {
nsCRT::free(certDER);
}
return rv;
return CallQueryInterface(nsNSS, _retval);
}
void

View File

@ -54,6 +54,7 @@ _TEST_FILES = \
_CHROME_FILES = \
test_bug413909.html \
test_bug480619.html \
test_bug644006.html \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -0,0 +1,77 @@
<!DOCTYPE HTML>
<html><head>
<title>Test bug 644006</title>
<script type="text/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="text/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
</head><body>
<script type="application/javascript;version=1.7">
const FAILURE = Components.results.NS_ERROR_FAILURE;
const ILLEGAL_VALUE = Components.results.NS_ERROR_ILLEGAL_VALUE;
var certDB = Components.classes["@mozilla.org/security/x509certdb;1"]
.getService(Components.interfaces.nsIX509CertDB);
function excMessage(e)
{
if ("message" in e && e.message !== null) {
let msg = e.message;
if ("data" in e && e.data !== null)
msg = msg + ": " + e.data;
return msg;
} else {
return e.toString();
}
}
function testGood(data)
{
let label = "CN=" + data.cn;
try {
let cert = certDB.constructX509FromBase64(data.cert);
is(cert.commonName, data.cn, label + ": constructX509 succeeded");
} catch (e) {
ok(false, label + ": exception: " + excMessage(e));
}
}
function testBad(data)
{
let label = uneval(data.t)
try {
let cert = certDB.constructX509FromBase64(data.t);
ok(false, label + ": constructX509 succeeded");
} catch (e) {
is(e.result, data.e, label + ": exception: " + excMessage(e));
}
}
const badCases = [
// wrong type or too short
{ t: null, e: ILLEGAL_VALUE },
{ t: "", e: ILLEGAL_VALUE },
{ t: "=", e: ILLEGAL_VALUE },
{ t: "==", e: ILLEGAL_VALUE },
// not base64
{ t: "forty-four dead stone lions", e: ILLEGAL_VALUE },
// not a cert
{ t: "Zm9ydHktZm91ciBkZWFkIHN0b25lIGxpb25z", e: FAILURE }
];
// real certs with all three padding levels
const goodCases = [
{ cn: "A", cert: "MIHhMIGcAgEAMA0GCSqGSIb3DQEBBQUAMAwxCjAIBgNVBAMTAUEwHhcNMTEwMzIzMjMyNTE3WhcNMTEwNDIyMjMyNTE3WjAMMQowCAYDVQQDEwFBMEwwDQYJKoZIhvcNAQEBBQADOwAwOAIxANFm7ZCfYNJViaDWTFuMClX3+9u18VFGiyLfM6xJrxir4QVtQC7VUC/WUGoBUs9COQIDAQABMA0GCSqGSIb3DQEBBQUAAzEAx2+gIwmuYjJO5SyabqIm4lB1MandHH1HQc0y0tUFshBOMESTzQRPSVwPn77a6R9t" },
{ cn: "Bo", cert: "MIHjMIGeAgEAMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNVBAMTAkJvMB4XDTExMDMyMzIzMjYwMloXDTExMDQyMjIzMjYwMlowDTELMAkGA1UEAxMCQm8wTDANBgkqhkiG9w0BAQEFAAM7ADA4AjEA1FoSl9w9HqMqVgk2K0J3OTiRsgHeNsQdPUl6S82ME33gH+E56PcWZA3nse+fpS3NAgMBAAEwDQYJKoZIhvcNAQEFBQADMQAo/e3BvQAmygiATljQ68tWPoWcbMwa1xxAvpWTEc1LOvMqeDBinBUqbAbSmPhGWb4=" },
{ cn: "Cid", cert: "MIHlMIGgAgEAMA0GCSqGSIb3DQEBBQUAMA4xDDAKBgNVBAMTA0NpZDAeFw0xMTAzMjMyMzI2MzJaFw0xMTA0MjIyMzI2MzJaMA4xDDAKBgNVBAMTA0NpZDBMMA0GCSqGSIb3DQEBAQUAAzsAMDgCMQDUUxlF5xKN+8KCSsR83sN+SRwJmZdliXsnBB7PU0OgbmOWN0u8yehRkmu39kN9tzcCAwEAATANBgkqhkiG9w0BAQUFAAMxAJ3UScNqRcjHFrNu4nuwRldZLJlVJvRYXp982V4/kYodQEGN4gJ+Qyj+HTsaXy5x/w==" }
];
var i;
for (i = 0; i < badCases.length; i++)
testBad(badCases[i]);
for (i = 0; i < goodCases.length; i++)
testGood(goodCases[i]);
</script>
</body></html>