267 lines
9.8 KiB
Diff
267 lines
9.8 KiB
Diff
From ef48810aafdc3b8c6c4a85e52314caeec0cb596c Mon Sep 17 00:00:00 2001
|
|
From: Viktor Dukhovni <openssl-users@dukhovni.org>
|
|
Date: Wed, 7 Jan 2026 01:21:58 +1100
|
|
Subject: [PATCH] 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
|
|
---
|
|
apps/dgst.c | 7 +++---
|
|
apps/include/apps.h | 2 +-
|
|
apps/lib/apps.c | 55 +++++++++++++++++++++++----------------------
|
|
apps/pkeyutl.c | 36 ++++++++++++++---------------
|
|
4 files changed, 50 insertions(+), 50 deletions(-)
|
|
|
|
diff --git a/apps/dgst.c b/apps/dgst.c
|
|
index 94415128d7f..7168b5f8b84 100644
|
|
--- a/apps/dgst.c
|
|
+++ b/apps/dgst.c
|
|
@@ -721,12 +721,11 @@ static int do_fp_oneshot_sign(BIO *out, EVP_MD_CTX *ctx, BIO *in, int sep, int b
|
|
{
|
|
int res, ret = EXIT_FAILURE;
|
|
size_t len = 0;
|
|
- int buflen = 0;
|
|
- int maxlen = 16 * 1024 * 1024;
|
|
+ size_t buflen = 0;
|
|
+ size_t maxlen = 16 * 1024 * 1024;
|
|
uint8_t *buf = NULL, *sig = NULL;
|
|
|
|
- buflen = bio_to_mem(&buf, maxlen, in);
|
|
- if (buflen <= 0) {
|
|
+ if (!bio_to_mem(&buf, &buflen, maxlen, in)) {
|
|
BIO_printf(bio_err, "Read error in %s\n", file);
|
|
return ret;
|
|
}
|
|
diff --git a/apps/include/apps.h b/apps/include/apps.h
|
|
index 6a23dbbb131..c9471ddc4ed 100644
|
|
--- a/apps/include/apps.h
|
|
+++ b/apps/include/apps.h
|
|
@@ -253,7 +253,7 @@ int parse_yesno(const char *str, int def);
|
|
X509_NAME *parse_name(const char *str, int chtype, int multirdn,
|
|
const char *desc);
|
|
void policies_print(X509_STORE_CTX *ctx);
|
|
-int bio_to_mem(unsigned char **out, int maxlen, BIO *in);
|
|
+int bio_to_mem(unsigned char **out, size_t *outlen, size_t maxlen, BIO *in);
|
|
int pkey_ctrl_string(EVP_PKEY_CTX *ctx, const char *value);
|
|
int x509_ctrl_string(X509 *x, const char *value);
|
|
int x509_req_ctrl_string(X509_REQ *x, const char *value);
|
|
diff --git a/apps/lib/apps.c b/apps/lib/apps.c
|
|
index 0e436582030..76f3c1683b2 100644
|
|
--- a/apps/lib/apps.c
|
|
+++ b/apps/lib/apps.c
|
|
@@ -49,6 +49,7 @@
|
|
#include "apps.h"
|
|
|
|
#include "internal/sockets.h" /* for openssl_fdset() */
|
|
+#include "internal/numbers.h" /* for LONG_MAX */
|
|
#include "internal/e_os.h"
|
|
|
|
#ifdef _WIN32
|
|
@@ -2010,45 +2011,45 @@ X509_NAME *parse_name(const char *cp, int chtype, int canmulti,
|
|
}
|
|
|
|
/*
|
|
- * Read whole contents of a BIO into an allocated memory buffer and return
|
|
- * it.
|
|
+ * Read whole contents of a BIO into an allocated memory buffer.
|
|
+ * The return value is one on success, zero on error.
|
|
+ * If `maxlen` is non-zero, at most `maxlen` bytes are returned, or else, if
|
|
+ * the input is longer than `maxlen`, an error is returned.
|
|
+ * If `maxlen` is zero, the limit is effectively `SIZE_MAX`.
|
|
*/
|
|
-
|
|
-int bio_to_mem(unsigned char **out, int maxlen, BIO *in)
|
|
+int bio_to_mem(unsigned char **out, size_t *outlen, size_t maxlen, BIO *in)
|
|
{
|
|
+ unsigned char tbuf[4096];
|
|
BIO *mem;
|
|
- int len, ret;
|
|
- unsigned char tbuf[1024];
|
|
+ BUF_MEM *bufm;
|
|
+ size_t sz = 0;
|
|
+ int len;
|
|
|
|
mem = BIO_new(BIO_s_mem());
|
|
if (mem == NULL)
|
|
- return -1;
|
|
+ return 0;
|
|
for (;;) {
|
|
- if ((maxlen != -1) && maxlen < 1024)
|
|
- len = maxlen;
|
|
- else
|
|
- len = 1024;
|
|
- len = BIO_read(in, tbuf, len);
|
|
- if (len < 0) {
|
|
- BIO_free(mem);
|
|
- return -1;
|
|
- }
|
|
- if (len == 0)
|
|
+ if ((len = BIO_read(in, tbuf, 4096)) == 0)
|
|
break;
|
|
- if (BIO_write(mem, tbuf, len) != len) {
|
|
+ if (len < 0
|
|
+ || BIO_write(mem, tbuf, len) != len
|
|
+ || sz > SIZE_MAX - len
|
|
+ || ((sz += len) > maxlen && maxlen != 0)) {
|
|
BIO_free(mem);
|
|
- return -1;
|
|
+ return 0;
|
|
}
|
|
- if (maxlen != -1)
|
|
- maxlen -= len;
|
|
-
|
|
- if (maxlen == 0)
|
|
- break;
|
|
}
|
|
- ret = BIO_get_mem_data(mem, (char **)out);
|
|
- BIO_set_flags(mem, BIO_FLAGS_MEM_RDONLY);
|
|
+
|
|
+ /* So BIO_free orphans BUF_MEM */
|
|
+ (void)BIO_set_close(mem, BIO_NOCLOSE);
|
|
+ BIO_get_mem_ptr(mem, &bufm);
|
|
BIO_free(mem);
|
|
- return ret;
|
|
+ *out = (unsigned char *)bufm->data;
|
|
+ *outlen = bufm->length;
|
|
+ /* Tell BUF_MEM to orphan data */
|
|
+ bufm->data = NULL;
|
|
+ BUF_MEM_free(bufm);
|
|
+ return 1;
|
|
}
|
|
|
|
int pkey_ctrl_string(EVP_PKEY_CTX *ctx, const char *value)
|
|
diff --git a/apps/pkeyutl.c b/apps/pkeyutl.c
|
|
index deecec6bcd7..2681114fba1 100644
|
|
--- a/apps/pkeyutl.c
|
|
+++ b/apps/pkeyutl.c
|
|
@@ -40,7 +40,7 @@ static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op,
|
|
|
|
static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx,
|
|
EVP_PKEY *pkey, BIO *in,
|
|
- int filesize, unsigned char *sig, int siglen,
|
|
+ int filesize, unsigned char *sig, size_t siglen,
|
|
unsigned char **out, size_t *poutlen);
|
|
|
|
static int only_nomd(EVP_PKEY *pkey)
|
|
@@ -158,7 +158,7 @@ int pkeyutl_main(int argc, char **argv)
|
|
char hexdump = 0, asn1parse = 0, rev = 0, *prog;
|
|
unsigned char *buf_in = NULL, *buf_out = NULL, *sig = NULL, *secret = NULL;
|
|
OPTION_CHOICE o;
|
|
- int buf_inlen = 0, siglen = -1;
|
|
+ size_t buf_inlen = 0, siglen = 0;
|
|
int keyform = FORMAT_UNDEF, peerform = FORMAT_UNDEF;
|
|
int keysize = -1, pkey_op = EVP_PKEY_OP_SIGN, key_type = KEY_PRIVKEY;
|
|
int engine_impl = 0;
|
|
@@ -508,31 +508,31 @@ int pkeyutl_main(int argc, char **argv)
|
|
|
|
if (sigfile != NULL) {
|
|
BIO *sigbio = BIO_new_file(sigfile, "rb");
|
|
+ size_t maxsiglen = 16 * 1024 * 1024;
|
|
|
|
if (sigbio == NULL) {
|
|
BIO_printf(bio_err, "Can't open signature file %s\n", sigfile);
|
|
goto end;
|
|
}
|
|
- siglen = bio_to_mem(&sig, keysize * 10, sigbio);
|
|
- BIO_free(sigbio);
|
|
- if (siglen < 0) {
|
|
+ if (!bio_to_mem(&sig, &siglen, maxsiglen, sigbio)) {
|
|
+ BIO_free(sigbio);
|
|
BIO_printf(bio_err, "Error reading signature data\n");
|
|
goto end;
|
|
}
|
|
+ BIO_free(sigbio);
|
|
}
|
|
|
|
/* Raw input data is handled elsewhere */
|
|
if (in != NULL && !rawin) {
|
|
/* Read the input data */
|
|
- buf_inlen = bio_to_mem(&buf_in, -1, in);
|
|
- if (buf_inlen < 0) {
|
|
+ if (!bio_to_mem(&buf_in, &buf_inlen, 0, in)) {
|
|
BIO_printf(bio_err, "Error reading input Data\n");
|
|
goto end;
|
|
}
|
|
if (rev) {
|
|
size_t i;
|
|
unsigned char ctmp;
|
|
- size_t l = (size_t)buf_inlen;
|
|
+ size_t l = buf_inlen;
|
|
|
|
for (i = 0; i < l / 2; i++) {
|
|
ctmp = buf_in[i];
|
|
@@ -547,7 +547,8 @@ int pkeyutl_main(int argc, char **argv)
|
|
&& (pkey_op == EVP_PKEY_OP_SIGN || pkey_op == EVP_PKEY_OP_VERIFY)) {
|
|
if (buf_inlen > EVP_MAX_MD_SIZE) {
|
|
BIO_printf(bio_err,
|
|
- "Error: The non-raw input data length %d is too long - max supported hashed size is %d\n",
|
|
+ "Error: The non-raw input data length %zd is too long - "
|
|
+ "max supported hashed size is %d\n",
|
|
buf_inlen, EVP_MAX_MD_SIZE);
|
|
goto end;
|
|
}
|
|
@@ -558,8 +559,7 @@ int pkeyutl_main(int argc, char **argv)
|
|
rv = do_raw_keyop(pkey_op, mctx, pkey, in, filesize, sig, siglen,
|
|
NULL, 0);
|
|
} else {
|
|
- rv = EVP_PKEY_verify(ctx, sig, (size_t)siglen,
|
|
- buf_in, (size_t)buf_inlen);
|
|
+ rv = EVP_PKEY_verify(ctx, sig, siglen, buf_in, buf_inlen);
|
|
}
|
|
if (rv == 1) {
|
|
BIO_puts(out, "Signature Verified Successfully\n");
|
|
@@ -578,8 +578,8 @@ int pkeyutl_main(int argc, char **argv)
|
|
buf_outlen = kdflen;
|
|
rv = 1;
|
|
} else {
|
|
- rv = do_keyop(ctx, pkey_op, NULL, (size_t *)&buf_outlen,
|
|
- buf_in, (size_t)buf_inlen, NULL, (size_t *)&secretlen);
|
|
+ rv = do_keyop(ctx, pkey_op, NULL, &buf_outlen,
|
|
+ buf_in, buf_inlen, NULL, &secretlen);
|
|
}
|
|
if (rv > 0
|
|
&& (secretlen > 0 || (pkey_op != EVP_PKEY_OP_ENCAPSULATE
|
|
@@ -589,8 +589,8 @@ int pkeyutl_main(int argc, char **argv)
|
|
if (secretlen > 0)
|
|
secret = app_malloc(secretlen, "secret output");
|
|
rv = do_keyop(ctx, pkey_op,
|
|
- buf_out, (size_t *)&buf_outlen,
|
|
- buf_in, (size_t)buf_inlen, secret, (size_t *)&secretlen);
|
|
+ buf_out, &buf_outlen,
|
|
+ buf_in, buf_inlen, secret, &secretlen);
|
|
}
|
|
}
|
|
if (rv <= 0) {
|
|
@@ -857,7 +857,7 @@ static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op,
|
|
|
|
static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx,
|
|
EVP_PKEY *pkey, BIO *in,
|
|
- int filesize, unsigned char *sig, int siglen,
|
|
+ int filesize, unsigned char *sig, size_t siglen,
|
|
unsigned char **out, size_t *poutlen)
|
|
{
|
|
int rv = 0;
|
|
@@ -880,7 +880,7 @@ static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx,
|
|
BIO_printf(bio_err, "Error reading raw input data\n");
|
|
goto end;
|
|
}
|
|
- rv = EVP_DigestVerify(mctx, sig, (size_t)siglen, mbuf, buf_len);
|
|
+ rv = EVP_DigestVerify(mctx, sig, siglen, mbuf, buf_len);
|
|
break;
|
|
case EVP_PKEY_OP_SIGN:
|
|
buf_len = BIO_read(in, mbuf, filesize);
|
|
@@ -914,7 +914,7 @@ static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx,
|
|
goto end;
|
|
}
|
|
}
|
|
- rv = EVP_DigestVerifyFinal(mctx, sig, (size_t)siglen);
|
|
+ rv = EVP_DigestVerifyFinal(mctx, sig, siglen);
|
|
break;
|
|
case EVP_PKEY_OP_SIGN:
|
|
for (;;) {
|