]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Report truncation in oneshot `openssl dgst -sign`
authorViktor Dukhovni <openssl-users@dukhovni.org>
Tue, 6 Jan 2026 14:21:58 +0000 (01:21 +1100)
committerTomas Mraz <tomas@openssl.org>
Mon, 26 Jan 2026 19:37:35 +0000 (20:37 +0100)
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: Eugene Syromiatnikov <esyr@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
MergeDate: Mon Jan 26 19:37:41 2026

apps/dgst.c
apps/include/apps.h
apps/lib/apps.c
apps/pkeyutl.c

index 8bc8e4c2c54d945b7993983c770a448546c9941e..395d9f82cb8ce64bde009ec44dfb1eaeb0417824 100644 (file)
@@ -736,12 +736,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;
     }
index 504d80c250550b4a97f121cbf7c4bfe1e4d134b6..8f9181db86a9dc772f532ba007440867b9c85c8b 100644 (file)
@@ -267,7 +267,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);
index 85c9ce524fac0dcfe0d1436f4d2b75394d6915ba..69b0d6ecbce1c04446d7bcd30334393ae3dc1afb 100644 (file)
@@ -44,6 +44,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
@@ -2054,45 +2055,45 @@ err:
 }
 
 /*
- * 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)
index bbeed9685677db1c84be5759e361ec9a373dd893..5f6406908f81d560f2978523266085f51ede602b 100644 (file)
@@ -38,7 +38,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)
@@ -148,7 +148,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 ret = 1, rv = -1;
@@ -491,31 +491,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];
@@ -530,7 +530,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;
         }
@@ -541,8 +542,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");
@@ -562,7 +562,7 @@ int pkeyutl_main(int argc, char **argv)
             rv = 1;
         } else {
             rv = do_keyop(ctx, pkey_op, NULL, &buf_outlen,
-                buf_in, (size_t)buf_inlen, NULL, &secretlen);
+                buf_in, buf_inlen, NULL, &secretlen);
         }
         if (rv > 0
             && (secretlen > 0 || (pkey_op != EVP_PKEY_OP_ENCAPSULATE && pkey_op != EVP_PKEY_OP_DECAPSULATE))
@@ -573,7 +573,7 @@ int pkeyutl_main(int argc, char **argv)
                 secret = app_malloc(secretlen, "secret output");
             rv = do_keyop(ctx, pkey_op,
                 buf_out, &buf_outlen,
-                buf_in, (size_t)buf_inlen, secret, &secretlen);
+                buf_in, buf_inlen, secret, &secretlen);
         }
     }
     if (rv <= 0) {
@@ -822,7 +822,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;
@@ -846,7 +846,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);
@@ -880,7 +880,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 (;;) {