From: Tomas Mraz Date: Thu, 18 Dec 2025 16:07:44 +0000 (+0100) Subject: Remove BIO_f_reliable() as it is broken X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6f73fe1c688dc3e4c3815e8f33e5b672abeb28d3;p=thirdparty%2Fopenssl.git Remove BIO_f_reliable() as it is broken It was broken since the OpenSSL 3.0 release and nobody complained. Apparently nobody is using it. It would be practically impossible to reimplement it with the provided EVP_MDs in backwards-compatible manner. Fixes #29413 Reviewed-by: Eugene Syromiatnikov Reviewed-by: Shane Lontis Reviewed-by: Neil Horman (Merged from https://github.com/openssl/openssl/pull/29445) --- diff --git a/CHANGES.md b/CHANGES.md index 0e5a3c813f6..3bf218a50a6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -101,6 +101,11 @@ OpenSSL 4.0 *Milan Broz*, *Neil Horman*, *Norbert Pocs* + * BIO_f_reliable() implementation was removed without replacement. + It was broken since 3.0 release without any complaints. + + *Tomáš Mráz* + * Added SNMP KDF (EVP_KDF_SNMPKDF) to EVP_KDF *Barry Fussell and Helen Zhang* diff --git a/crypto/evp/bio_ok.c b/crypto/evp/bio_ok.c deleted file mode 100644 index 9b48c3c7456..00000000000 --- a/crypto/evp/bio_ok.c +++ /dev/null @@ -1,606 +0,0 @@ -/* - * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with the License. You can obtain a copy - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -/*- - From: Arne Ansper - - Why BIO_f_reliable? - - I wrote function which took BIO* as argument, read data from it - and processed it. Then I wanted to store the input file in - encrypted form. OK I pushed BIO_f_cipher to the BIO stack - and everything was OK. BUT if user types wrong password - BIO_f_cipher outputs only garbage and my function crashes. Yes - I can and I should fix my function, but BIO_f_cipher is - easy way to add encryption support to many existing applications - and it's hard to debug and fix them all. - - So I wanted another BIO which would catch the incorrect passwords and - file damages which cause garbage on BIO_f_cipher's output. - - The easy way is to push the BIO_f_md and save the checksum at - the end of the file. However there are several problems with this - approach: - - 1) you must somehow separate checksum from actual data. - 2) you need lot's of memory when reading the file, because you - must read to the end of the file and verify the checksum before - letting the application to read the data. - - BIO_f_reliable tries to solve both problems, so that you can - read and write arbitrary long streams using only fixed amount - of memory. - - BIO_f_reliable splits data stream into blocks. Each block is prefixed - with its length and suffixed with its digest. So you need only - several Kbytes of memory to buffer single block before verifying - its digest. - - BIO_f_reliable goes further and adds several important capabilities: - - 1) the digest of the block is computed over the whole stream - -- so nobody can rearrange the blocks or remove or replace them. - - 2) to detect invalid passwords right at the start BIO_f_reliable - adds special prefix to the stream. In order to avoid known plain-text - attacks this prefix is generated as follows: - - *) digest is initialized with random seed instead of - standardized one. - *) same seed is written to output - *) well-known text is then hashed and the output - of the digest is also written to output. - - reader can now read the seed from stream, hash the same string - and then compare the digest output. - - Bad things: BIO_f_reliable knows what's going on in EVP_Digest. I - initially wrote and tested this code on x86 machine and wrote the - digests out in machine-dependent order :( There are people using - this code and I cannot change this easily without making existing - data files unreadable. - -*/ - -#include -#include -#include -#include "internal/cryptlib.h" -#include -#include "internal/bio.h" -#include -#include -#include "internal/endian.h" -#include "internal/numbers.h" /* includes SIZE_MAX */ -#include "crypto/evp.h" - -static int ok_write(BIO *h, const char *buf, int num); -static int ok_read(BIO *h, char *buf, int size); -static long ok_ctrl(BIO *h, int cmd, long arg1, void *arg2); -static int ok_new(BIO *h); -static int ok_free(BIO *data); -static long ok_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp); - -static __owur int sig_out(BIO *b); -static __owur int sig_in(BIO *b); -static __owur int block_out(BIO *b); -static __owur int block_in(BIO *b); -#define OK_BLOCK_SIZE (1024 * 4) -#define OK_BLOCK_BLOCK 4 -#define IOBS (OK_BLOCK_SIZE + OK_BLOCK_BLOCK + 3 * EVP_MAX_MD_SIZE) -#define WELLKNOWN "The quick brown fox jumped over the lazy dog's back." - -typedef struct ok_struct { - size_t buf_len; - size_t buf_off; - size_t buf_len_save; - size_t buf_off_save; - int cont; /* <= 0 when finished */ - int finished; - EVP_MD_CTX *md; - int blockout; /* output block is ready */ - int sigio; /* must process signature */ - unsigned char buf[IOBS]; -} BIO_OK_CTX; - -static const BIO_METHOD methods_ok = { - BIO_TYPE_CIPHER, - "reliable", - bwrite_conv, - ok_write, - bread_conv, - ok_read, - NULL, /* ok_puts, */ - NULL, /* ok_gets, */ - ok_ctrl, - ok_new, - ok_free, - ok_callback_ctrl, -}; - -const BIO_METHOD *BIO_f_reliable(void) -{ - return &methods_ok; -} - -static int ok_new(BIO *bi) -{ - BIO_OK_CTX *ctx; - - if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) - return 0; - - ctx->cont = 1; - ctx->sigio = 1; - ctx->md = EVP_MD_CTX_new(); - if (ctx->md == NULL) { - OPENSSL_free(ctx); - return 0; - } - BIO_set_init(bi, 0); - BIO_set_data(bi, ctx); - - return 1; -} - -static int ok_free(BIO *a) -{ - BIO_OK_CTX *ctx; - - if (a == NULL) - return 0; - - ctx = BIO_get_data(a); - - EVP_MD_CTX_free(ctx->md); - OPENSSL_clear_free(ctx, sizeof(BIO_OK_CTX)); - BIO_set_data(a, NULL); - BIO_set_init(a, 0); - - return 1; -} - -static int ok_read(BIO *b, char *out, int outl) -{ - int ret = 0, i, n; - BIO_OK_CTX *ctx; - BIO *next; - - if (out == NULL) - return 0; - - ctx = BIO_get_data(b); - next = BIO_next(b); - - if ((ctx == NULL) || (next == NULL) || (BIO_get_init(b) == 0)) - return 0; - - while (outl > 0) { - - /* copy clean bytes to output buffer */ - if (ctx->blockout) { - i = (int)(ctx->buf_len - ctx->buf_off); - if (i > outl) - i = outl; - memcpy(out, &(ctx->buf[ctx->buf_off]), i); - ret += i; - out += i; - outl -= i; - ctx->buf_off += i; - - /* all clean bytes are out */ - if (ctx->buf_len == ctx->buf_off) { - ctx->buf_off = 0; - - /* - * copy start of the next block into proper place - */ - if (ctx->buf_len_save > ctx->buf_off_save) { - ctx->buf_len = ctx->buf_len_save - ctx->buf_off_save; - memmove(ctx->buf, &(ctx->buf[ctx->buf_off_save]), - ctx->buf_len); - } else { - ctx->buf_len = 0; - } - ctx->blockout = 0; - } - } - - /* output buffer full -- cancel */ - if (outl == 0) - break; - - /* no clean bytes in buffer -- fill it */ - n = (int)(IOBS - ctx->buf_len); - i = BIO_read(next, &(ctx->buf[ctx->buf_len]), n); - - if (i <= 0) - break; /* nothing new */ - - ctx->buf_len += i; - - /* no signature yet -- check if we got one */ - if (ctx->sigio == 1) { - if (!sig_in(b)) { - BIO_clear_retry_flags(b); - return 0; - } - } - - /* signature ok -- check if we got block */ - if (ctx->sigio == 0) { - if (!block_in(b)) { - BIO_clear_retry_flags(b); - return 0; - } - } - - /* invalid block -- cancel */ - if (ctx->cont <= 0) - break; - } - - BIO_clear_retry_flags(b); - BIO_copy_next_retry(b); - return ret; -} - -static int ok_write(BIO *b, const char *in, int inl) -{ - int ret = 0, n, i; - BIO_OK_CTX *ctx; - BIO *next; - - if (inl <= 0) - return inl; - - ctx = BIO_get_data(b); - next = BIO_next(b); - ret = inl; - - if ((ctx == NULL) || (next == NULL) || (BIO_get_init(b) == 0)) - return 0; - - if (ctx->sigio && !sig_out(b)) - return 0; - - do { - BIO_clear_retry_flags(b); - n = (int)(ctx->buf_len - ctx->buf_off); - while (ctx->blockout && n > 0) { - i = BIO_write(next, &(ctx->buf[ctx->buf_off]), n); - if (i <= 0) { - BIO_copy_next_retry(b); - if (!BIO_should_retry(b)) - ctx->cont = 0; - return i; - } - ctx->buf_off += i; - n -= i; - } - - /* at this point all pending data has been written */ - ctx->blockout = 0; - if (ctx->buf_len == ctx->buf_off) { - ctx->buf_len = OK_BLOCK_BLOCK; - ctx->buf_off = 0; - } - - if ((in == NULL) || (inl <= 0)) - return 0; - - n = (inl + ctx->buf_len > OK_BLOCK_SIZE + OK_BLOCK_BLOCK) ? (int)(OK_BLOCK_SIZE + OK_BLOCK_BLOCK - ctx->buf_len) : inl; - - memcpy(&ctx->buf[ctx->buf_len], in, n); - ctx->buf_len += n; - inl -= n; - in += n; - - if (ctx->buf_len >= OK_BLOCK_SIZE + OK_BLOCK_BLOCK) { - if (!block_out(b)) { - BIO_clear_retry_flags(b); - return 0; - } - } - } while (inl > 0); - - BIO_clear_retry_flags(b); - BIO_copy_next_retry(b); - return ret; -} - -static long ok_ctrl(BIO *b, int cmd, long num, void *ptr) -{ - BIO_OK_CTX *ctx; - EVP_MD *md; - const EVP_MD **ppmd; - long ret = 1; - int i; - BIO *next; - - ctx = BIO_get_data(b); - next = BIO_next(b); - - switch (cmd) { - case BIO_CTRL_RESET: - ctx->buf_len = 0; - ctx->buf_off = 0; - ctx->buf_len_save = 0; - ctx->buf_off_save = 0; - ctx->cont = 1; - ctx->finished = 0; - ctx->blockout = 0; - ctx->sigio = 1; - ret = BIO_ctrl(next, cmd, num, ptr); - break; - case BIO_CTRL_EOF: /* More to read */ - if (ctx->cont <= 0) - ret = 1; - else - ret = BIO_ctrl(next, cmd, num, ptr); - break; - case BIO_CTRL_PENDING: /* More to read in buffer */ - case BIO_CTRL_WPENDING: /* More to read in buffer */ - ret = ctx->blockout ? (long)(ctx->buf_len - ctx->buf_off) : 0; - if (ret <= 0) - ret = BIO_ctrl(next, cmd, num, ptr); - break; - case BIO_CTRL_FLUSH: - /* do a final write */ - if (ctx->blockout == 0) - if (!block_out(b)) - return 0; - - while (ctx->blockout) { - i = ok_write(b, NULL, 0); - if (i < 0) { - ret = i; - break; - } - } - - ctx->finished = 1; - ctx->buf_off = ctx->buf_len = 0; - ctx->cont = (int)ret; - - /* Finally flush the underlying BIO */ - ret = BIO_ctrl(next, cmd, num, ptr); - BIO_copy_next_retry(b); - break; - case BIO_C_DO_STATE_MACHINE: - BIO_clear_retry_flags(b); - ret = BIO_ctrl(next, cmd, num, ptr); - BIO_copy_next_retry(b); - break; - case BIO_CTRL_INFO: - ret = (long)ctx->cont; - break; - case BIO_C_SET_MD: - md = ptr; - if (!EVP_DigestInit_ex(ctx->md, md, NULL)) - return 0; - BIO_set_init(b, 1); - break; - case BIO_C_GET_MD: - if (BIO_get_init(b)) { - ppmd = ptr; - *ppmd = EVP_MD_CTX_get0_md(ctx->md); - } else - ret = 0; - break; - default: - ret = BIO_ctrl(next, cmd, num, ptr); - break; - } - return ret; -} - -static long ok_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp) -{ - BIO *next; - - next = BIO_next(b); - - if (next == NULL) - return 0; - - return BIO_callback_ctrl(next, cmd, fp); -} - -static void longswap(void *_ptr, size_t len) -{ - DECLARE_IS_ENDIAN; - - if (IS_LITTLE_ENDIAN) { - size_t i; - unsigned char *p = _ptr, c; - - for (i = 0; i < len; i += 4) { - c = p[0], p[0] = p[3], p[3] = c; - c = p[1], p[1] = p[2], p[2] = c; - } - } -} - -static int sig_out(BIO *b) -{ - BIO_OK_CTX *ctx; - EVP_MD_CTX *md; - const EVP_MD *digest; - int md_size; - void *md_data; - - ctx = BIO_get_data(b); - md = ctx->md; - digest = EVP_MD_CTX_get0_md(md); - md_size = EVP_MD_get_size(digest); - md_data = EVP_MD_CTX_get0_md_data(md); - - if (md_size <= 0) - goto berr; - if (ctx->buf_len + 2 * md_size > OK_BLOCK_SIZE) - return 1; - - if (!EVP_DigestInit_ex(md, digest, NULL)) - goto berr; - /* - * FIXME: there's absolutely no guarantee this makes any sense at all, - * particularly now EVP_MD_CTX has been restructured. - */ - if (RAND_bytes(md_data, md_size) <= 0) - goto berr; - memcpy(&(ctx->buf[ctx->buf_len]), md_data, md_size); - longswap(&(ctx->buf[ctx->buf_len]), md_size); - ctx->buf_len += md_size; - - if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN))) - goto berr; - if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL)) - goto berr; - ctx->buf_len += md_size; - ctx->blockout = 1; - ctx->sigio = 0; - return 1; -berr: - BIO_clear_retry_flags(b); - return 0; -} - -static int sig_in(BIO *b) -{ - BIO_OK_CTX *ctx; - EVP_MD_CTX *md; - unsigned char tmp[EVP_MAX_MD_SIZE]; - int ret = 0; - const EVP_MD *digest; - int md_size; - void *md_data; - - ctx = BIO_get_data(b); - if ((md = ctx->md) == NULL) - goto berr; - digest = EVP_MD_CTX_get0_md(md); - if ((md_size = EVP_MD_get_size(digest)) <= 0) - goto berr; - md_data = EVP_MD_CTX_get0_md_data(md); - - if ((int)(ctx->buf_len - ctx->buf_off) < 2 * md_size) - return 1; - - if (!EVP_DigestInit_ex(md, digest, NULL)) - goto berr; - memcpy(md_data, &(ctx->buf[ctx->buf_off]), md_size); - longswap(md_data, md_size); - ctx->buf_off += md_size; - - if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN))) - goto berr; - if (!EVP_DigestFinal_ex(md, tmp, NULL)) - goto berr; - ret = memcmp(&(ctx->buf[ctx->buf_off]), tmp, md_size) == 0; - ctx->buf_off += md_size; - if (ret == 1) { - ctx->sigio = 0; - if (ctx->buf_len != ctx->buf_off) { - memmove(ctx->buf, &(ctx->buf[ctx->buf_off]), - ctx->buf_len - ctx->buf_off); - } - ctx->buf_len -= ctx->buf_off; - ctx->buf_off = 0; - } else { - ctx->cont = 0; - } - return 1; -berr: - BIO_clear_retry_flags(b); - return 0; -} - -static int block_out(BIO *b) -{ - BIO_OK_CTX *ctx; - EVP_MD_CTX *md; - unsigned long tl; - const EVP_MD *digest; - int md_size; - - ctx = BIO_get_data(b); - md = ctx->md; - digest = EVP_MD_CTX_get0_md(md); - md_size = EVP_MD_get_size(digest); - if (md_size <= 0) - goto berr; - - tl = (unsigned long)(ctx->buf_len - OK_BLOCK_BLOCK); - ctx->buf[0] = (unsigned char)(tl >> 24); - ctx->buf[1] = (unsigned char)(tl >> 16); - ctx->buf[2] = (unsigned char)(tl >> 8); - ctx->buf[3] = (unsigned char)(tl); - if (!EVP_DigestUpdate(md, - (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl)) - goto berr; - if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL)) - goto berr; - ctx->buf_len += md_size; - ctx->blockout = 1; - return 1; -berr: - BIO_clear_retry_flags(b); - return 0; -} - -static int block_in(BIO *b) -{ - BIO_OK_CTX *ctx; - EVP_MD_CTX *md; - size_t tl = 0; - unsigned char tmp[EVP_MAX_MD_SIZE]; - int md_size; - - ctx = BIO_get_data(b); - md = ctx->md; - md_size = EVP_MD_get_size(EVP_MD_CTX_get0_md(md)); - if (md_size <= 0) - goto berr; - - assert(sizeof(tl) >= OK_BLOCK_BLOCK); /* always true */ - tl = ((size_t)ctx->buf[0] << 24) - | ((size_t)ctx->buf[1] << 16) - | ((size_t)ctx->buf[2] << 8) - | ((size_t)ctx->buf[3]); - - if (tl > OK_BLOCK_SIZE) - goto berr; - - if (tl > SIZE_MAX - OK_BLOCK_BLOCK - (size_t)md_size) - goto berr; - - if (ctx->buf_len < tl + OK_BLOCK_BLOCK + (size_t)md_size) - return 1; - - if (!EVP_DigestUpdate(md, - (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl)) - goto berr; - if (!EVP_DigestFinal_ex(md, tmp, NULL)) - goto berr; - if (memcmp(&(ctx->buf[tl + OK_BLOCK_BLOCK]), tmp, (size_t)md_size) == 0) { - /* there might be parts from next block lurking around ! */ - ctx->buf_off_save = tl + OK_BLOCK_BLOCK + md_size; - ctx->buf_len_save = ctx->buf_len; - ctx->buf_off = OK_BLOCK_BLOCK; - ctx->buf_len = tl + OK_BLOCK_BLOCK; - ctx->blockout = 1; - } else { - ctx->cont = 0; - } - return 1; -berr: - BIO_clear_retry_flags(b); - return 0; -} diff --git a/crypto/evp/build.info b/crypto/evp/build.info index 1cbf6e89207..45945afcabe 100644 --- a/crypto/evp/build.info +++ b/crypto/evp/build.info @@ -13,7 +13,7 @@ SOURCE[../../libcrypto]=$COMMON\ e_xcbc_d.c e_rc2.c e_cast.c e_rc5.c m_null.c \ p_seal.c p_sign.c p_verify.c p_legacy.c \ bio_md.c bio_b64.c bio_enc.c evp_err.c e_null.c \ - c_allc.c c_alld.c bio_ok.c \ + c_allc.c c_alld.c \ evp_pkey.c evp_pbe.c p5_crpt.c p5_crpt2.c pbe_scrypt.c \ e_aes_cbc_hmac_sha1.c e_aes_cbc_hmac_sha256.c e_rc4_hmac_md5.c \ e_chacha20_poly1305.c \ diff --git a/doc/man7/ossl-removed-api.pod b/doc/man7/ossl-removed-api.pod index d086b680111..3189bdfeec0 100644 --- a/doc/man7/ossl-removed-api.pod +++ b/doc/man7/ossl-removed-api.pod @@ -93,6 +93,7 @@ EVP_PKEY_meth_get_check, EVP_PKEY_meth_get_public_check, EVP_PKEY_meth_get_param_check, EVP_PKEY_meth_get_digest_custom, +BIO_f_reliable, ossl-removed-api - API that has been removed from OpenSSL =head1 SYNOPSIS @@ -304,6 +305,8 @@ This includes consulting the L documentation. =item EVP_PKEY_meth_get_digest_custom (Deprecated in 3.0.0) - consult L +=item BIO_f_reliable (Broken since 3.0.0) - removed without replacement + =back =head1 SEE ALSO diff --git a/include/openssl/evp.h b/include/openssl/evp.h index b00c2044c02..d2a5736ad23 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -848,7 +848,6 @@ int EVP_CIPHER_CTX_get_algor(EVP_CIPHER_CTX *ctx, X509_ALGOR **alg); const BIO_METHOD *BIO_f_md(void); const BIO_METHOD *BIO_f_base64(void); const BIO_METHOD *BIO_f_cipher(void); -const BIO_METHOD *BIO_f_reliable(void); __owur int BIO_set_cipher(BIO *b, const EVP_CIPHER *c, const unsigned char *k, const unsigned char *i, int enc); diff --git a/util/libcrypto.num b/util/libcrypto.num index c1b13670286..78d5fa5114a 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -1001,7 +1001,6 @@ EVP_CIPHER_CTX_get_algor ? 4_0_0 EXIST::FUNCTION: BIO_f_md ? 4_0_0 EXIST::FUNCTION: BIO_f_base64 ? 4_0_0 EXIST::FUNCTION: BIO_f_cipher ? 4_0_0 EXIST::FUNCTION: -BIO_f_reliable ? 4_0_0 EXIST::FUNCTION: BIO_set_cipher ? 4_0_0 EXIST::FUNCTION: EVP_md_null ? 4_0_0 EXIST::FUNCTION: EVP_md2 ? 4_0_0 EXIST::FUNCTION:MD2 diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt index 7c997abb56d..49765d4f7e6 100644 --- a/util/missingcrypto.txt +++ b/util/missingcrypto.txt @@ -186,7 +186,6 @@ BIO_dup_chain(3) BIO_f_asn1(3) BIO_f_linebuffer(3) BIO_f_nbio_test(3) -BIO_f_reliable(3) BIO_fd_non_fatal_error(3) BIO_fd_should_retry(3) BIO_get_accept_socket(3)