Consistently open files in binary mode on Windows
BIO_*_filename, in upstream OpenSSL, opens in binary mode on Windows,
not text mode. We seem to have lost those ifdefs in the fork. But since
C mandates the 'b' suffix (POSIX just ignores it), apply it consistently
to all OSes for simplicity.
This fixes X509_FILETYPE_ASN1 in X509_STORE's file-based machinery on
Windows.
Also fix the various BIO_new_file calls to all specify binary mode.
Looking through them, I don't think any of them care (they're all
parsing PEM), but let's just apply it across the board so we don't have
to think about this.
Update-Note: BIO_read_filename, etc., now open in binary mode on
Windows. This matches OpenSSL behavior.
Change-Id: I7e555085d5c66ad2f205b476d0317570075bbadb
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66009
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
diff --git a/crypto/bio/bio_test.cc b/crypto/bio/bio_test.cc
index 7115b98..65324d0 100644
--- a/crypto/bio/bio_test.cc
+++ b/crypto/bio/bio_test.cc
@@ -645,19 +645,25 @@
SCOPED_TRACE("file");
// Test |BIO_new_file|.
- bssl::UniquePtr<BIO> bio(BIO_new_file(file.path().c_str(), "r"));
+ bssl::UniquePtr<BIO> bio(BIO_new_file(file.path().c_str(), "rb"));
ASSERT_TRUE(bio);
check_bio_gets(bio.get());
+ // Test |BIO_read_filename|.
+ bio.reset(BIO_new(BIO_s_file()));
+ ASSERT_TRUE(bio);
+ ASSERT_TRUE(BIO_read_filename(bio.get(), file.path().c_str()));
+ check_bio_gets(bio.get());
+
// Test |BIO_NOCLOSE|.
- ScopedFILE file_obj = file.Open("r");
+ ScopedFILE file_obj = file.Open("rb");
ASSERT_TRUE(file_obj);
bio.reset(BIO_new_fp(file_obj.get(), BIO_NOCLOSE));
ASSERT_TRUE(bio);
check_bio_gets(bio.get());
// Test |BIO_CLOSE|.
- file_obj = file.Open("r");
+ file_obj = file.Open("rb");
ASSERT_TRUE(file_obj);
bio.reset(BIO_new_fp(file_obj.get(), BIO_CLOSE));
ASSERT_TRUE(bio);
@@ -700,6 +706,24 @@
EXPECT_EQ(c, 'a');
}
+// Test that, on Windows, |BIO_read_filename| opens files in binary mode.
+TEST(BIOTest, BinaryMode) {
+ if (SkipTempFileTests()) {
+ GTEST_SKIP();
+ }
+
+ TemporaryFile file;
+ ASSERT_TRUE(file.Init("\r\n"));
+
+ // Reading from the file should give back the exact bytes we put in.
+ bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
+ ASSERT_TRUE(bio);
+ ASSERT_TRUE(BIO_read_filename(bio.get(), file.path().c_str()));
+ char buf[2];
+ ASSERT_EQ(2, BIO_read(bio.get(), buf, 2));
+ EXPECT_EQ(Bytes(buf, 2), Bytes("\r\n"));
+}
+
// Run through the tests twice, swapping |bio1| and |bio2|, for symmetry.
class BIOPairTest : public testing::TestWithParam<bool> {};
diff --git a/crypto/bio/file.c b/crypto/bio/file.c
index dd11e50..9b2a6ca 100644
--- a/crypto/bio/file.c
+++ b/crypto/bio/file.c
@@ -206,16 +206,16 @@
const char *mode;
if (num & BIO_FP_APPEND) {
if (num & BIO_FP_READ) {
- mode = "a+";
+ mode = "ab+";
} else {
- mode = "a";
+ mode = "ab";
}
} else if ((num & BIO_FP_READ) && (num & BIO_FP_WRITE)) {
- mode = "r+";
+ mode = "rb+";
} else if (num & BIO_FP_WRITE) {
- mode = "w";
+ mode = "wb";
} else if (num & BIO_FP_READ) {
- mode = "r";
+ mode = "rb";
} else {
OPENSSL_PUT_ERROR(BIO, BIO_R_BAD_FOPEN_MODE);
ret = 0;
diff --git a/crypto/x509/by_file.c b/crypto/x509/by_file.c
index 799e16d..989e179 100644
--- a/crypto/x509/by_file.c
+++ b/crypto/x509/by_file.c
@@ -228,7 +228,7 @@
if (type != X509_FILETYPE_PEM) {
return X509_load_cert_file(ctx, file, type);
}
- in = BIO_new_file(file, "r");
+ in = BIO_new_file(file, "rb");
if (!in) {
OPENSSL_PUT_ERROR(X509, ERR_R_SYS_LIB);
return 0;
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index 61bc54d..b25e8b3 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -7977,14 +7977,6 @@
for (int type : {X509_FILETYPE_PEM, X509_FILETYPE_ASN1}) {
SCOPED_TRACE(type);
-#if defined(OPENSSL_WINDOWS)
- // TODO(davidben): X509_FILETYPE_ASN1 is currently broken on Windows due to
- // some text vs binary mixup.
- if (type == X509_FILETYPE_ASN1) {
- continue;
- }
-#endif
-
// Generate some roots and fill a directory with OpenSSL's directory hash
// format. The hash depends only on the name, so we do not need to
// pre-generate the certificates. Test both DER and PEM.
diff --git a/include/openssl/bio.h b/include/openssl/bio.h
index 33b020b..93f3c0c 100644
--- a/include/openssl/bio.h
+++ b/include/openssl/bio.h
@@ -492,22 +492,26 @@
// BIO_read_filename opens |filename| for reading and sets the result as the
// |FILE| for |bio|. It returns one on success and zero otherwise. The |FILE|
-// will be closed when |bio| is freed.
+// will be closed when |bio| is freed. On Windows, the file is opened in binary
+// mode.
OPENSSL_EXPORT int BIO_read_filename(BIO *bio, const char *filename);
// BIO_write_filename opens |filename| for writing and sets the result as the
// |FILE| for |bio|. It returns one on success and zero otherwise. The |FILE|
-// will be closed when |bio| is freed.
+// will be closed when |bio| is freed. On Windows, the file is opened in binary
+// mode.
OPENSSL_EXPORT int BIO_write_filename(BIO *bio, const char *filename);
// BIO_append_filename opens |filename| for appending and sets the result as
// the |FILE| for |bio|. It returns one on success and zero otherwise. The
-// |FILE| will be closed when |bio| is freed.
+// |FILE| will be closed when |bio| is freed. On Windows, the file is opened in
+// binary mode.
OPENSSL_EXPORT int BIO_append_filename(BIO *bio, const char *filename);
// BIO_rw_filename opens |filename| for reading and writing and sets the result
// as the |FILE| for |bio|. It returns one on success and zero otherwise. The
-// |FILE| will be closed when |bio| is freed.
+// |FILE| will be closed when |bio| is freed. On Windows, the file is opened in
+// binary mode.
OPENSSL_EXPORT int BIO_rw_filename(BIO *bio, const char *filename);
// BIO_tell returns the file offset of |bio|, or a negative number on error or
diff --git a/ssl/ssl_file.cc b/ssl/ssl_file.cc
index 2b596b2..301f8d6 100644
--- a/ssl/ssl_file.cc
+++ b/ssl/ssl_file.cc
@@ -204,7 +204,7 @@
}
STACK_OF(X509_NAME) *SSL_load_client_CA_file(const char *file) {
- bssl::UniquePtr<BIO> in(BIO_new_file(file, "r"));
+ bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
if (in == nullptr) {
return nullptr;
}
@@ -219,7 +219,7 @@
int SSL_add_file_cert_subjects_to_stack(STACK_OF(X509_NAME) *out,
const char *file) {
- bssl::UniquePtr<BIO> in(BIO_new_file(file, "r"));
+ bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
if (in == nullptr) {
return 0;
}