Skip to content

Commit a7936fa

Browse files
Viktor Dukhovnit8m
authored andcommitted
Report truncation in oneshot openssl dgst -sign
Previously input was silently truncated at 16MB, now if the input is longer than limit, an error is reported. The bio_to_mem() apps helper function was changed to return 0 or 1, and return the size of the result via an output size_t pointer. Fixes CVE-2025-15469 Reviewed-by: Saša Nedvědický <sashan@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> MergeDate: Mon Jan 26 19:38:42 2026
1 parent 1f08e54 commit a7936fa

4 files changed

Lines changed: 50 additions & 50 deletions

File tree

apps/dgst.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -721,12 +721,11 @@ static int do_fp_oneshot_sign(BIO *out, EVP_MD_CTX *ctx, BIO *in, int sep, int b
721721
{
722722
int res, ret = EXIT_FAILURE;
723723
size_t len = 0;
724-
int buflen = 0;
725-
int maxlen = 16 * 1024 * 1024;
724+
size_t buflen = 0;
725+
size_t maxlen = 16 * 1024 * 1024;
726726
uint8_t *buf = NULL, *sig = NULL;
727727

728-
buflen = bio_to_mem(&buf, maxlen, in);
729-
if (buflen <= 0) {
728+
if (!bio_to_mem(&buf, &buflen, maxlen, in)) {
730729
BIO_printf(bio_err, "Read error in %s\n", file);
731730
return ret;
732731
}

apps/include/apps.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ int parse_yesno(const char *str, int def);
253253
X509_NAME *parse_name(const char *str, int chtype, int multirdn,
254254
const char *desc);
255255
void policies_print(X509_STORE_CTX *ctx);
256-
int bio_to_mem(unsigned char **out, int maxlen, BIO *in);
256+
int bio_to_mem(unsigned char **out, size_t *outlen, size_t maxlen, BIO *in);
257257
int pkey_ctrl_string(EVP_PKEY_CTX *ctx, const char *value);
258258
int x509_ctrl_string(X509 *x, const char *value);
259259
int x509_req_ctrl_string(X509_REQ *x, const char *value);

apps/lib/apps.c

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include "apps.h"
5050

5151
#include "internal/sockets.h" /* for openssl_fdset() */
52+
#include "internal/numbers.h" /* for LONG_MAX */
5253
#include "internal/e_os.h"
5354

5455
#ifdef _WIN32
@@ -2010,45 +2011,45 @@ X509_NAME *parse_name(const char *cp, int chtype, int canmulti,
20102011
}
20112012

20122013
/*
2013-
* Read whole contents of a BIO into an allocated memory buffer and return
2014-
* it.
2014+
* Read whole contents of a BIO into an allocated memory buffer.
2015+
* The return value is one on success, zero on error.
2016+
* If `maxlen` is non-zero, at most `maxlen` bytes are returned, or else, if
2017+
* the input is longer than `maxlen`, an error is returned.
2018+
* If `maxlen` is zero, the limit is effectively `SIZE_MAX`.
20152019
*/
2016-
2017-
int bio_to_mem(unsigned char **out, int maxlen, BIO *in)
2020+
int bio_to_mem(unsigned char **out, size_t *outlen, size_t maxlen, BIO *in)
20182021
{
2022+
unsigned char tbuf[4096];
20192023
BIO *mem;
2020-
int len, ret;
2021-
unsigned char tbuf[1024];
2024+
BUF_MEM *bufm;
2025+
size_t sz = 0;
2026+
int len;
20222027

20232028
mem = BIO_new(BIO_s_mem());
20242029
if (mem == NULL)
2025-
return -1;
2030+
return 0;
20262031
for (;;) {
2027-
if ((maxlen != -1) && maxlen < 1024)
2028-
len = maxlen;
2029-
else
2030-
len = 1024;
2031-
len = BIO_read(in, tbuf, len);
2032-
if (len < 0) {
2033-
BIO_free(mem);
2034-
return -1;
2035-
}
2036-
if (len == 0)
2032+
if ((len = BIO_read(in, tbuf, 4096)) == 0)
20372033
break;
2038-
if (BIO_write(mem, tbuf, len) != len) {
2034+
if (len < 0
2035+
|| BIO_write(mem, tbuf, len) != len
2036+
|| sz > SIZE_MAX - len
2037+
|| ((sz += len) > maxlen && maxlen != 0)) {
20392038
BIO_free(mem);
2040-
return -1;
2039+
return 0;
20412040
}
2042-
if (maxlen != -1)
2043-
maxlen -= len;
2044-
2045-
if (maxlen == 0)
2046-
break;
20472041
}
2048-
ret = BIO_get_mem_data(mem, (char **)out);
2049-
BIO_set_flags(mem, BIO_FLAGS_MEM_RDONLY);
2042+
2043+
/* So BIO_free orphans BUF_MEM */
2044+
(void)BIO_set_close(mem, BIO_NOCLOSE);
2045+
BIO_get_mem_ptr(mem, &bufm);
20502046
BIO_free(mem);
2051-
return ret;
2047+
*out = (unsigned char *)bufm->data;
2048+
*outlen = bufm->length;
2049+
/* Tell BUF_MEM to orphan data */
2050+
bufm->data = NULL;
2051+
BUF_MEM_free(bufm);
2052+
return 1;
20522053
}
20532054

20542055
int pkey_ctrl_string(EVP_PKEY_CTX *ctx, const char *value)

apps/pkeyutl.c

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op,
4040

4141
static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx,
4242
EVP_PKEY *pkey, BIO *in,
43-
int filesize, unsigned char *sig, int siglen,
43+
int filesize, unsigned char *sig, size_t siglen,
4444
unsigned char **out, size_t *poutlen);
4545

4646
static int only_nomd(EVP_PKEY *pkey)
@@ -158,7 +158,7 @@ int pkeyutl_main(int argc, char **argv)
158158
char hexdump = 0, asn1parse = 0, rev = 0, *prog;
159159
unsigned char *buf_in = NULL, *buf_out = NULL, *sig = NULL, *secret = NULL;
160160
OPTION_CHOICE o;
161-
int buf_inlen = 0, siglen = -1;
161+
size_t buf_inlen = 0, siglen = 0;
162162
int keyform = FORMAT_UNDEF, peerform = FORMAT_UNDEF;
163163
int keysize = -1, pkey_op = EVP_PKEY_OP_SIGN, key_type = KEY_PRIVKEY;
164164
int engine_impl = 0;
@@ -508,31 +508,31 @@ int pkeyutl_main(int argc, char **argv)
508508

509509
if (sigfile != NULL) {
510510
BIO *sigbio = BIO_new_file(sigfile, "rb");
511+
size_t maxsiglen = 16 * 1024 * 1024;
511512

512513
if (sigbio == NULL) {
513514
BIO_printf(bio_err, "Can't open signature file %s\n", sigfile);
514515
goto end;
515516
}
516-
siglen = bio_to_mem(&sig, keysize * 10, sigbio);
517-
BIO_free(sigbio);
518-
if (siglen < 0) {
517+
if (!bio_to_mem(&sig, &siglen, maxsiglen, sigbio)) {
518+
BIO_free(sigbio);
519519
BIO_printf(bio_err, "Error reading signature data\n");
520520
goto end;
521521
}
522+
BIO_free(sigbio);
522523
}
523524

524525
/* Raw input data is handled elsewhere */
525526
if (in != NULL && !rawin) {
526527
/* Read the input data */
527-
buf_inlen = bio_to_mem(&buf_in, -1, in);
528-
if (buf_inlen < 0) {
528+
if (!bio_to_mem(&buf_in, &buf_inlen, 0, in)) {
529529
BIO_printf(bio_err, "Error reading input Data\n");
530530
goto end;
531531
}
532532
if (rev) {
533533
size_t i;
534534
unsigned char ctmp;
535-
size_t l = (size_t)buf_inlen;
535+
size_t l = buf_inlen;
536536

537537
for (i = 0; i < l / 2; i++) {
538538
ctmp = buf_in[i];
@@ -547,7 +547,8 @@ int pkeyutl_main(int argc, char **argv)
547547
&& (pkey_op == EVP_PKEY_OP_SIGN || pkey_op == EVP_PKEY_OP_VERIFY)) {
548548
if (buf_inlen > EVP_MAX_MD_SIZE) {
549549
BIO_printf(bio_err,
550-
"Error: The non-raw input data length %d is too long - max supported hashed size is %d\n",
550+
"Error: The non-raw input data length %zd is too long - "
551+
"max supported hashed size is %d\n",
551552
buf_inlen, EVP_MAX_MD_SIZE);
552553
goto end;
553554
}
@@ -558,8 +559,7 @@ int pkeyutl_main(int argc, char **argv)
558559
rv = do_raw_keyop(pkey_op, mctx, pkey, in, filesize, sig, siglen,
559560
NULL, 0);
560561
} else {
561-
rv = EVP_PKEY_verify(ctx, sig, (size_t)siglen,
562-
buf_in, (size_t)buf_inlen);
562+
rv = EVP_PKEY_verify(ctx, sig, siglen, buf_in, buf_inlen);
563563
}
564564
if (rv == 1) {
565565
BIO_puts(out, "Signature Verified Successfully\n");
@@ -578,8 +578,8 @@ int pkeyutl_main(int argc, char **argv)
578578
buf_outlen = kdflen;
579579
rv = 1;
580580
} else {
581-
rv = do_keyop(ctx, pkey_op, NULL, (size_t *)&buf_outlen,
582-
buf_in, (size_t)buf_inlen, NULL, (size_t *)&secretlen);
581+
rv = do_keyop(ctx, pkey_op, NULL, &buf_outlen,
582+
buf_in, buf_inlen, NULL, &secretlen);
583583
}
584584
if (rv > 0
585585
&& (secretlen > 0 || (pkey_op != EVP_PKEY_OP_ENCAPSULATE && pkey_op != EVP_PKEY_OP_DECAPSULATE))
@@ -589,8 +589,8 @@ int pkeyutl_main(int argc, char **argv)
589589
if (secretlen > 0)
590590
secret = app_malloc(secretlen, "secret output");
591591
rv = do_keyop(ctx, pkey_op,
592-
buf_out, (size_t *)&buf_outlen,
593-
buf_in, (size_t)buf_inlen, secret, (size_t *)&secretlen);
592+
buf_out, &buf_outlen,
593+
buf_in, buf_inlen, secret, &secretlen);
594594
}
595595
}
596596
if (rv <= 0) {
@@ -857,7 +857,7 @@ static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op,
857857

858858
static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx,
859859
EVP_PKEY *pkey, BIO *in,
860-
int filesize, unsigned char *sig, int siglen,
860+
int filesize, unsigned char *sig, size_t siglen,
861861
unsigned char **out, size_t *poutlen)
862862
{
863863
int rv = 0;
@@ -880,7 +880,7 @@ static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx,
880880
BIO_printf(bio_err, "Error reading raw input data\n");
881881
goto end;
882882
}
883-
rv = EVP_DigestVerify(mctx, sig, (size_t)siglen, mbuf, buf_len);
883+
rv = EVP_DigestVerify(mctx, sig, siglen, mbuf, buf_len);
884884
break;
885885
case EVP_PKEY_OP_SIGN:
886886
buf_len = BIO_read(in, mbuf, filesize);
@@ -914,7 +914,7 @@ static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx,
914914
goto end;
915915
}
916916
}
917-
rv = EVP_DigestVerifyFinal(mctx, sig, (size_t)siglen);
917+
rv = EVP_DigestVerifyFinal(mctx, sig, siglen);
918918
break;
919919
case EVP_PKEY_OP_SIGN:
920920
for (;;) {

0 commit comments

Comments
 (0)