Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix EVP_CIPHER_CTX_get_block_size() calls to check for -1. #23000

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

slontis
Copy link
Member

@slontis slontis commented Dec 10, 2023

EVP_CIPHER_CTX_get_block_size() returns an int which can be -1.
Fix all the calls that cast it to a size_t.

This is a follow on from reviewing #22995
which makes it more obvious that EVP_CIPHER_CTX_get_block_size() can return -1.

There are a couple of places in the code that already check for this, but the rest do bad things.

Checklist
  • documentation is added or updated
  • tests are added or updated

Fix all the calls that assume it returns a size_t.
@slontis
Copy link
Member Author

slontis commented Dec 10, 2023

Needs tests

@slontis slontis added branch: master Merge to master branch approval: review pending This pull request needs review by a committer approval: otc review pending This pull request needs review by an OTC member hold: needs tests The PR needs tests to be added to it labels Dec 10, 2023
@github-actions github-actions bot added the severity: fips change The pull request changes FIPS provider sources label Dec 10, 2023
…. Fix all the calls that assume it returns a size_t.
@t-j-h
Copy link
Member

t-j-h commented Dec 10, 2023

Nice to see this being tackled - I too saw that we would have some "fun" with this issue where we have had size_t logic get put in a few places incorrectly.

Copy link
Member

@mattcaswell mattcaswell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes it more obvious that EVP_CIPHER_CTX_get_block_size() can return -1.

It's not currently clear to me why #22995 is making that change so that EVP_CIPHER_CTX_get_block_size() can suddenly return -1, where it doesn't seem to have done so before. This seems like a significant change in behaviour.

If we're assuming that the return value of this function can be safely cast to a size_t, then I'd say its a fair bet that applications are making the same assumption.

blocksize = EVP_CIPHER_CTX_get_block_size(rl->enc_ctx);
(EVP_CIPHER_CTX_get_mode(rl->enc_ctx) == EVP_CIPH_CBC_MODE)) {
blksz = EVP_CIPHER_CTX_get_block_size(rl->enc_ctx);
assert(blksz >= 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we instead propagate this error to the caller (there is only one)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also a block size of 0 is really an error. Stream ciphers have block size of 1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated - the propagation required a few more changes

@nhorman
Copy link
Contributor

nhorman commented Dec 11, 2023

@mattcaswell this is sort of an interesting question - The documentation has never been clear on the return value of this call, save to say it returns the block size of the cipher. Given that the function is typed to return an int, it seems that a negative return code is feasible. Whats more, as @slontis has corrected here, we have a myriad of call points that treat the return code differently. Currently, our code base has 24 calls to EVP_CIPHER_CTX_get_block_size:

2 cmac.c       CMAC_CTX_copy                   98 if ((bl = EVP_CIPHER_CTX_get_block_size(in->cctx)) < 0)
3 cmac.c       CMAC_Init                      122 memset(ctx->tbl, 0, EVP_CIPHER_CTX_get_block_size(ctx->cctx));
4 cmac.c       CMAC_Init                      145 if ((bl = EVP_CIPHER_CTX_get_block_size(ctx->cctx)) < 0)
5 cmac.c       CMAC_Update                    173 if ((bl = EVP_CIPHER_CTX_get_block_size(ctx->cctx)) < 0)
6 cmac.c       CMAC_Final                     237 if ((bl = EVP_CIPHER_CTX_get_block_size(ctx->cctx)) < 0)
7 cms_pwri.c   kek_unwrap_key                 204 size_t blocklen = EVP_CIPHER_CTX_get_block_size(ctx);
8 cms_pwri.c   kek_wrap_key                   257 size_t blocklen = EVP_CIPHER_CTX_get_block_size(ctx);
9 bio_enc.c    enc_read                       134 blocksize = EVP_CIPHER_CTX_get_block_size(ctx->cipher);
a e_aes.c      aesni_ecb_cipher               193 size_t bl = EVP_CIPHER_CTX_get_block_size(ctx);
b e_aes.c      aes_ecb_cipher                2530 size_t bl = EVP_CIPHER_CTX_get_block_size(ctx);
c e_camellia.c camellia_ecb_cipher            239 size_t bl = EVP_CIPHER_CTX_get_block_size(ctx);
d e_sm4.c      sm4_ecb_cipher                 172 size_t bl = EVP_CIPHER_CTX_get_block_size(ctx);
e evp_enc.c    EVP_EncryptFinal_ex            747 blocksize = EVP_CIPHER_CTX_get_block_size(ctx);
f evp_enc.c    EVP_DecryptUpdate              834 blocksize = EVP_CIPHER_CTX_get_block_size(ctx);
g evp_enc.c    EVP_DecryptFinal_ex            969 blocksize = EVP_CIPHER_CTX_get_block_size(ctx);
h evp_lib.c    EVP_CIPHER_CTX_get_block_size  393 int EVP_CIPHER_CTX_get_block_size(const EVP_CIPHER_CTX *ctx)
i evp_lib.c    EVP_Cipher                     416 size_t blocksize = EVP_CIPHER_CTX_get_block_size(ctx);
j p12_decr.c   PKCS12_pbe_crypt_ex             46 max_out_len = inlen + EVP_CIPHER_CTX_get_block_size(ctx);
k evp.h        EVP_CIPHER_CTX_block_size      620 #define EVP_CIPHER_CTX_block_size EVP_CIPHER_CTX_get_block_size
 0 krb5kdf.c    KRB5KDF                        417 blocksize = EVP_CIPHER_CTX_get_block_size(ctx);
1 cmac_prov.c  cmac_size                      109 return EVP_CIPHER_CTX_get_block_size(cipherctx);
2 dtls_meth.c  dtls_get_max_record_overhead   749 blocksize = EVP_CIPHER_CTX_get_block_size(rl->enc_ctx);
3 ssl3_meth.c  ssl3_cipher                    116 bs = EVP_CIPHER_CTX_get_block_size(ds);
4 evp.h        EVP_CIPHER_CTX_block_size      615 #define EVP_CIPHER_CTX_block_size EVP_CIPHER_CTX_get_block_size

a) Some, like CMAC_CTX_copy check for a return values expressly less than 0 (treating a 0 return as valid)
b) Some, like CMAC_Init assume a >= 0 return code, and use it in a nested function call
c) some, like kek_unwrap_key assume a >= zero return and automatically cast the return to a size_t, which will lead to all sorts of odd behavior on negative return values

I think in my head, what should be done boils down to

  1. weather or not a block_size of zero is ever valid (based on @t8m statement). If that is true (and can reasonably never be expected to be zero).
  2. weather there is value in differentiating an error that indicates a block size has been (inappropriately) set to zero, from an error that indicates the cipher block size is unknown (i.e. because a cipher hasn't been initalized)

If (1) is true and (2) is false, then we can safely just use 0 as an error return from this function, and fixup (a) and (b) and (c) above to coform to that. If that is the case then we should also consider modifying the EVP_CIPHER_CTX_get_block_size and EVP_CIPHER_get_block size to return a size_t or unsigned int accordingly.

@t8m t8m added triaged: bug The issue/pr is/fixes a bug branch: 3.0 Merge to openssl-3.0 branch branch: 3.1 Merge to openssl-3.1 branch: 3.2 Merge to openssl-3.2 labels Dec 11, 2023
@t8m
Copy link
Member

t8m commented Dec 11, 2023

EVP_CIPHER_CTX_get_block_size() could already return -1 with the pre-existing code so yeah, this is a correct bug fix.

@nhorman nhorman self-requested a review December 12, 2023 13:08
…. Fix all the calls that assume it returns a size_t.
…. Fix all the calls that assume it returns a size_t.
@slontis
Copy link
Member Author

slontis commented Dec 13, 2023

Added some tests for the ones that I was able to..

@slontis slontis added tests: present The PR has suitable tests present and removed hold: needs tests The PR needs tests to be added to it labels Dec 13, 2023
…. Fix all the calls that assume it returns a size_t.
…. Fix all the calls that assume it returns a size_t.
…. Fix all the calls that assume it returns a size_t.
Comment on lines +212 to 215
if (blocksz <= 0 || inlen % blocklen) {
/* Invalid size */
return 0;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also put this if before the previous one. Funnily enough this newly added condition actually will never be evaluated for blocksz being negative as the blocklen value in the previous if will be a huge number and thus the condition inlen < 2 * blocklen will be true.

@@ -132,6 +132,8 @@ static int enc_read(BIO *b, char *out, int outl)
}

blocksize = EVP_CIPHER_CTX_get_block_size(ctx->cipher);
if (blocksize < 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be <= as the 0 return is an error as well.

@@ -413,8 +413,10 @@ int EVP_Cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
*/
int ret = -1;
size_t outl = 0;
size_t blocksize = EVP_CIPHER_CTX_get_block_size(ctx);
int blocksize = EVP_CIPHER_CTX_get_block_size(ctx);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is OK, however below in those ccipher/cupdate/cfinal calls you need to cast blocksize to size_t explicitly before adding inl as otherwise there can be an overflow. Rather theoretical one but...

@@ -37,13 +37,16 @@ unsigned char *PKCS12_pbe_crypt_ex(const X509_ALGOR *algor,
algor->parameter, ctx, en_de, libctx, propq))
goto err;

blksz = EVP_CIPHER_CTX_get_block_size(ctx);
if (blksz < 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<= as 0 is also invalid.

/* Initialize input block */
blocksize = EVP_CIPHER_CTX_get_block_size(ctx);
blksz = EVP_CIPHER_CTX_get_block_size(ctx);
if (blksz < 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<=

@openssl-machine
Copy link
Collaborator

This PR is in a state where it requires action by @openssl/otc but the last update was 30 days ago

@openssl-machine
Copy link
Collaborator

This PR is in a state where it requires action by @openssl/otc but the last update was 61 days ago

@openssl-machine
Copy link
Collaborator

This PR is in a state where it requires action by @openssl/otc but the last update was 92 days ago

@openssl-machine
Copy link
Collaborator

This PR is in a state where it requires action by @openssl/otc but the last update was 123 days ago

@openssl-machine
Copy link
Collaborator

This PR is in a state where it requires action by @openssl/otc but the last update was 154 days ago

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approval: otc review pending This pull request needs review by an OTC member approval: review pending This pull request needs review by a committer branch: master Merge to master branch branch: 3.0 Merge to openssl-3.0 branch branch: 3.1 Merge to openssl-3.1 branch: 3.2 Merge to openssl-3.2 severity: fips change The pull request changes FIPS provider sources tests: present The PR has suitable tests present triaged: bug The issue/pr is/fixes a bug
Projects
Status: New
Development

Successfully merging this pull request may close these issues.

None yet

6 participants