Closed Bug 287654 Opened 20 years ago Closed 20 years ago

NSC_Encrypt with RSA mechanism crashes if len is greater than modulus len

Categories

(NSS :: Libraries, defect, P1)

3.9.5
defect

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: julien.pierre, Assigned: julien.pierre)

Details

Attachments

(2 files, 2 obsolete files)

I tried to encrypt 140 bytes of data with PK11_PubEncryptRaw . The public key
was 1024 bits. The input and output buffers were both more than large enough
(1024 bytes). This resulted in a crash in the softoken, in rsawrapr.c, in
rsa_FormatBlock .

I used the access checking feature of dbx to identify the problem. See log
below. The issue is that the lower lovel code apparently uses an intermediate
buffer for the encryption, which is only 128 bytes. Then it does a memcpy of the
size of the entire input buffer, which is bigger .

RTC: Enabling Error Checking...
RTC: Running program...
Write to unallocated (wua) on thread 1:
Attempting to write 1 byte at address 0x4aa81c
    which is 12 bytes past end of heap block of size 128 bytes at 0x4aa790
This block was allocated from:
        [1] PR_Malloc() at line 436 in "prmem.c"
        [2] PORT_Alloc() at line 113 in "secport.c"
        [3] SECITEM_ArenaDupItem() at line 210 in "secitem.c"
        [4] SECITEM_DupItem() at line 186 in "secitem.c"
        [5] pk11_addTokenKeyByHandle() at line 2052 in "pkcs11u.c"
        [6] pk11_mkHandle() at line 2930 in "pkcs11u.c"
        [7] pk11_key_collect() at line 4093 in "pkcs11.c"
        [8] nsslowkey_TraverseKeys() at line 439 in "keydb.c"

t@1 (l@1) stopped in rsa_FormatBlock at line 421 in file "rsawrapr.c"
  421          
PORT_Memcpy(result->data+(modulusLen-data->len),data->data,data->len);
(dbx) p data->len
data->len = 140U
(dbx) where     
current thread: t@1
=>[1] rsa_FormatBlock(result = 0xffbfe0f8, modulusLen = 128U, blockType =
RSA_BlockRaw, data = 0xffbfe0ec), line 421 in "rsawrapr.c"
  [2] RSA_EncryptRaw(key = 0x4b2728, output = 0xffbfe3a8 "\xff\xbf\xe4",
output_len = 0xffbfe198, max_output_len = 128U, input = 0xffbfe7a8
"^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A"
..., input_len = 140U), line 827 in "rsawrapr.c"
  [3] NSC_Encrypt(hSession = 4U, pData = 0xffbfe7a8
"^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A"
..., ulDataLen = 140U, pEncryptedData = 0xffbfe3a8 "\xff\xbf\xe4",
pulEncryptedDataLen = 0xffbfe218), line 889 in "pkcs11c.c"
  [4] pk11_PubEncryptRaw(key = 0x4b1ef8, enc = 0xffbfe3a8 "\xff\xbf\xe4", data =
0xffbfe7a8
"^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A"
..., dataLen = 140U, mech = 0xffbfe290, wincx = (nil)), line 824 in "pk11obj.c"
  [5] PK11_PubEncryptRaw(key = 0x4b1ef8, enc = 0xffbfe3a8 "\xff\xbf\xe4", data =
0xffbfe7a8
"^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A"
..., dataLen = 140U, wincx = (nil)), line 840 in "pk11obj.c"
  [6] PK11_PublicKeyOp(key = 0x4b1ef8, output = 0xffbfe3a8 "\xff\xbf\xe4", input
= 0xffbfe7a8
"^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A^A"
...), line 240 in "rsaperf.c"
  [7] main(argc = 8, argv = 0xffbfec64), line 427 in "rsaperf.c"
(dbx)
Comment on attachment 178545 [details] [diff] [review]
don't try to copy data outside output buffer boundaries

This patch fixes the crash (buffer overflow).

However, I'm not sure if it is correct thing to do.

In this case, the caller of PK11_PubEncryptRaw isn't notified that not all the
data was encrypted. That function only returns a SECStatus, and I feel it
should return SECFailure in this case. How that should actually accomplished in
the various layers in between, I'm not certain. Bob ?
Attachment #178545 - Flags: review?(rrelyea)
Priority: -- → P1
Target Milestone: --- → 3.10
PK11_PubEncryptRaw should fail if we try to encrypt more then modulus bytes.
The failure should come from NSC_Encrypt, which should get it from blapi.

This behavior for NSC_Encrypt is defined by PKCS #11. Encrypt PKCS should also
fail if the block is greater than the modulus - the minimum header (there's an
assert in the RSA code for this). It's OK to put the check in RSA OP since
NSC_Decrypt, NSC_Sign, NSC_Verify, and NSC_VerifyRecover should all fail the
same way for RSA operations.

bob
Comment on attachment 178545 [details] [diff] [review]
don't try to copy data outside output buffer boundaries

Marking patch obsolete
Attachment #178545 - Attachment is obsolete: true
Attachment #178545 - Flags: review?(rrelyea)
Attachment #178872 - Flags: review?(rrelyea)
Comment on attachment 178872 [details] [diff] [review]
check modulus length for RSA raw encrypt case

Bob, Nelson, please review this very short patch. Thanks !
Attachment #178872 - Flags: superreview?(nelson)
Comment on attachment 178872 [details] [diff] [review]
check modulus length for RSA raw encrypt case

>+	if (data->len > modulusLen ) {
>+	    return SECFailure;
>+	}

This test suffices to prevent a crash, but still allows SOME invalid data
to be processed.  Specifically, the input data must be numerically less
than the modulus.  This is always true when the input data is shorter 
than the modulus.  When the input data and modulus are of equal length,
memcmp can be used to compare them, and determine if the input is less
than the modulus.  

I guess we have to decide whether the purpose of the test is only to 
avoid crashes, or is to ensure correctness of the operation.  If it is only
to avoid crashes, then this patch is OK.
Comment on attachment 178872 [details] [diff] [review]
check modulus length for RSA raw encrypt case

I was going to say we should set an error code before
returning SECFailure, then I found that none of the
functions in this file sets error code...
Comment on attachment 178872 [details] [diff] [review]
check modulus length for RSA raw encrypt case

Re: Nelson's question in comment 7, I think we should
ensure correctness of the operation.
I agree that we should ensure the correctness of the operation. I believe there
is a subsequent check in freebl for the test that Nelson talked about in comment
7 . I tripped over this in my testing of rsaperf also when I fed an
uninitialized buffer of the correct size, and the operation failed .
I can't remember if it was for encrypt or decrypt.

Looking at the code in freebl/rsa.c, I see the following code in rsa_PrivateKeyOp :

    /* check input out of range (needs to be in range [0..n-1]) */
    modLen = rsa_modulusLen(&key->modulus);
    offset = (key->modulus.data[0] == 0) ? 1 : 0; /* may be leading 0 */
    if (memcmp(input, key->modulus.data + offset, modLen) >= 0) {
	PORT_SetError(SEC_ERROR_INVALID_ARGS);
	return SECFailure;
    }

However, I see no equivalent check in rsa_publicKeyOp, which is the encrypt
case. So, perhaps we have to do the check at the higher PKCS#11 level. But I
think that's inconsistent. Thoughts ?
Attachment #179314 - Flags: review?(nelson)
Summary: NSC_Encrypt with RSA mechanism crashes if output len is greater than modulus len → NSC_Encrypt with RSA mechanism crashes if len is greater than modulus len
Comment on attachment 178872 [details] [diff] [review]
check modulus length for RSA raw encrypt case

Marking r+=nelson
I believe this patch is correct and necessary but not sufficient, as noted
above.
Attachment #178872 - Flags: superreview?(nelson) → superreview+
Comment on attachment 179314 [details] [diff] [review]
also check input value against modulus

Several problems with this patch.

In the private key code, the value comparison was done before the 
numbers were converted from canonical byte strings to mp_ints.
In this case, the numbers have already been converted to mp_ints
so the code cannot simply return here or it will leak the mp_ints.
It needs to "go to cleanup".  Also, since they are already mp_ints,
it is easier and better (and probably faster) to use the mpi 
comparison function to compare their numeric values.  

Also, if the input value is numerically too big, SEC_ERROR_INPUT_LEN 
is probably the right error code.  That goes for the previous 
patch that checked buffer length, too.

Suggested (but untested) alternative patch:

>     /* 2.  Represent message as integer in range [0..n-1] */
>     CHECK_MPI_OK( mp_read_unsigned_octets(&m, input, modLen) );
>+    /* 2 bis. check input out of range (needs to be in range [0..n-1]) */
>+    if (mp_cmp(&m, &n) >= 0)
>+        PORT_SetError(SEC_ERROR_INPUT_LEN);
>+        rv = SECFailure;
>+        goto cleanup;
>+    }
Attachment #179314 - Flags: review?(nelson) → review-
That "alternative patch" is missing a {.  
Nelson,

Thanks for catching the leak. I looked at the code for mp_cmp, and I don't think
way it will execute faster than memcmp in most cases. I would be OK with either
check.
Attached patch correct patchSplinter Review
I think the attached patch will be most efficient. It checks the input value
before converting the message to mp_ints .
Attachment #179314 - Attachment is obsolete: true
Attachment #179555 - Flags: review?(nelson)
Comment on attachment 178872 [details] [diff] [review]
check modulus length for RSA raw encrypt case

I checked in this patch for the softoken.

Checking in rsawrapr.c;
/cvsroot/mozilla/security/nss/lib/softoken/rsawrapr.c,v  <--  rsawrapr.c
new revision: 1.8; previous revision: 1.7
done
Attachment #178872 - Flags: review?(rrelyea)
Comment on attachment 179555 [details] [diff] [review]
correct patch

I agree this patch is correct.	I believe it is less efficient because memcmp
will load and compare each byte of each string, byte-by-byte, whereas mp_cmp
will load and compare the values word-by-word.	But this patch is correct, so I
leave it to you to decide whether to go with memcmp or mp_cmp.
Attachment #179555 - Flags: review?(nelson) → review+
Nelson,

I checked in the patch attached to the tip.

I believe the mp_cmp will be significantly more complex code due to the use of
mp_int structures and pointer indirection, whereas memcmp could be done with
just a couple instruction s(using eg. "rep cmpsb" on x86).

I actually just benchmarked the two methods briefly. I can't tell them apart,
even after running for 30 seconds. The results are within 0.5 % of each other,
one way or the other. I'm not going to spend more time on it because we aren't
concerned about RSA encrypt performance, which is on the client side.

Checking in rsa.c;
/cvsroot/mozilla/security/nss/lib/freebl/rsa.c,v  <--  rsa.c
new revision: 1.35; previous revision: 1.34
Status: NEW → RESOLVED
Closed: 20 years ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: