Commit c478df55d5 for openssl.org

commit c478df55d50d5e5b97ae730a54dc324fd3263d90
Author: Alexandr Nedvedicky <sashan@openssl.org>
Date:   Tue Jan 13 18:08:58 2026 +0100

    BIO_FLAGS_BASE64_NO_NL ignored by b64_write() in OpenSSL 4.0.0

    Fixes #29618

    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    Reviewed-by: Neil Horman <nhorman@openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/29629)

diff --git a/crypto/evp/bio_b64.c b/crypto/evp/bio_b64.c
index 7b3decd2bf..2eab67caba 100644
--- a/crypto/evp/bio_b64.c
+++ b/crypto/evp/bio_b64.c
@@ -13,6 +13,7 @@
 #include <openssl/buffer.h>
 #include <openssl/evp.h>
 #include "internal/bio.h"
+#include "crypto/evp.h"

 static int b64_write(BIO *h, const char *buf, int num);
 static int b64_read(BIO *h, char *buf, int size);
@@ -334,6 +335,9 @@ static int b64_write(BIO *b, const char *in, int inl)
     int i;
     BIO_B64_CTX *ctx;
     BIO *next;
+    int encoded_length;
+    unsigned char *encoded;
+    int n_bytes_enc;

     ctx = (BIO_B64_CTX *)BIO_get_data(b);
     next = BIO_next(b);
@@ -348,6 +352,8 @@ static int b64_write(BIO *b, const char *in, int inl)
         ctx->buf_off = 0;
         ctx->tmp_len = 0;
         EVP_EncodeInit(ctx->base64);
+        if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL)
+            evp_encode_ctx_set_flags(ctx->base64, EVP_ENCODE_CTX_NO_NEWLINES);
     }
     if (!ossl_assert(ctx->buf_off < (int)sizeof(ctx->buf))) {
         ERR_raise(ERR_LIB_BIO, ERR_R_INTERNAL_ERROR);
@@ -386,7 +392,7 @@ static int b64_write(BIO *b, const char *in, int inl)
     if (in == NULL || inl <= 0)
         return 0;

-    int encoded_length = EVP_ENCODE_LENGTH(inl);
+    encoded_length = EVP_ENCODE_LENGTH(inl);

     if (ctx->encoded_buf == NULL || (size_t)encoded_length > ctx->encoded_buf_len) {
         OPENSSL_free(ctx->encoded_buf);
@@ -398,13 +404,13 @@ static int b64_write(BIO *b, const char *in, int inl)
         ctx->encoded_buf_len = encoded_length;
     }

-    unsigned char *encoded = ctx->encoded_buf;
+    encoded = ctx->encoded_buf;

     if (encoded == NULL) {
         ERR_raise(ERR_LIB_BIO, ERR_R_MALLOC_FAILURE);
         return -1;
     }
-    int n_bytes_enc = 0;
+    n_bytes_enc = 0;
     if (!EVP_EncodeUpdate(ctx->base64, encoded, &n_bytes_enc,
             (unsigned char *)in, inl)) {
         return -1;
@@ -470,15 +476,7 @@ static long b64_ctrl(BIO *b, int cmd, long num, void *ptr)
             if (i < 0)
                 return i;
         }
-        if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) {
-            if (ctx->tmp_len != 0) {
-                ctx->buf_len = EVP_EncodeBlock(ctx->buf,
-                    ctx->tmp, ctx->tmp_len);
-                ctx->buf_off = 0;
-                ctx->tmp_len = 0;
-                goto again;
-            }
-        } else if (ctx->encode != B64_NONE
+        if (ctx->encode != B64_NONE
             && EVP_ENCODE_CTX_num(ctx->base64) != 0) {
             ctx->buf_off = 0;
             EVP_EncodeFinal(ctx->base64, ctx->buf, &(ctx->buf_len));
diff --git a/test/bio_base64_test.c b/test/bio_base64_test.c
index 733bfa1b7d..dbdce741f4 100644
--- a/test/bio_base64_test.c
+++ b/test/bio_base64_test.c
@@ -428,6 +428,45 @@ static int test_bio_base64_corner_case_bug(int idx)
     return generic_case(&t, 0);
 }

+#define MEM_CHK "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB" \
+                "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB" \
+                "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB"
+
+static int test_bio_base64_no_nl(void)
+{
+    char msg[120];
+    BIO *b64 = NULL;
+    BIO *mem = NULL;
+    BIO *b64_chk;
+    BUF_MEM *bptr = NULL;
+    int ok = 0;
+
+    memset(msg, 'A', sizeof(msg));
+
+    b64 = BIO_new(BIO_f_base64());
+    if (!TEST_ptr(b64))
+        goto done;
+
+    mem = BIO_new(BIO_s_mem());
+    if (!TEST_ptr(mem))
+        goto done;
+
+    b64_chk = BIO_push(b64, mem);
+    if (!TEST_ptr_eq(b64, b64_chk))
+        goto done;
+
+    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+    BIO_write(b64, msg, sizeof(msg));
+    if (!TEST_true(BIO_flush(b64)))
+        goto done;
+    BIO_get_mem_ptr(mem, &bptr);
+    ok = TEST_mem_eq(MEM_CHK, sizeof(MEM_CHK) - 1, bptr->data, bptr->length);
+
+done:
+    BIO_free_all(b64);
+    return ok;
+}
+
 int setup_tests(void)
 {
     int numidx;
@@ -483,5 +522,6 @@ int setup_tests(void)
     numidx = 2 * 2;
     ADD_ALL_TESTS(test_bio_base64_corner_case_bug, numidx);

+    ADD_TEST(test_bio_base64_no_nl);
     return 1;
 }