Skip to content

Commit

Permalink
Add callback function support to BIO_puts,gets,ctrl
Browse files Browse the repository at this point in the history
  • Loading branch information
kexgaber committed Jul 24, 2024
1 parent 34aceaf commit 045869b
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 16 deletions.
61 changes: 56 additions & 5 deletions crypto/bio/bio.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,17 +212,26 @@ int BIO_gets(BIO *bio, char *buf, int len) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
return -2;
}
int ret = 0;
if (HAS_CALLBACK(bio)) {
ret = (int)bio->callback_ex(bio,BIO_CB_GETS,buf,len,0, 0L, 1L, NULL);
if (ret <= 0) {
return ret;
}
}
if (!bio->init) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
return -2;
}
if (len <= 0) {
return 0;
}
int ret = bio->method->bgets(bio, buf, len);
ret = bio->method->bgets(bio, buf, len);
if (ret > 0) {
bio->num_read += ret;
}
ret = call_bio_callback_with_processed(bio, BIO_CB_GETS | BIO_CB_RETURN, buf,
len, ret);
return ret;
}

Expand Down Expand Up @@ -299,13 +308,43 @@ int BIO_write_all(BIO *bio, const void *data, size_t len) {
}

int BIO_puts(BIO *bio, const char *in) {
size_t len = strlen(in);
const size_t len = strlen(in);
if (len > INT_MAX) {
// |BIO_write| and the return value both assume the string fits in |int|.
OPENSSL_PUT_ERROR(BIO, ERR_R_OVERFLOW);
return -1;
}
return BIO_write(bio, in, (int)len);
// Only checking for bwrite here because we will use that to write to
// the BIO if bputs is NULL
if (bio == NULL || bio->method == NULL || (bio->method->bwrite == NULL &&
bio->method->bputs == NULL)) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
return -2;
}
int ret = 0;
if(HAS_CALLBACK(bio)) {
ret = (int)bio->callback_ex(bio, BIO_CB_PUTS, in, len, 0, 0L, 1L, NULL);
if (ret <= 0) {
return ret;
}
}

if (!bio->init) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
return -2;
}
if (bio->method->bputs != NULL) {
ret = bio->method->bputs(bio, in);
} else {
ret = bio->method->bwrite(bio, in, len);
}
if (ret > 0) {
bio->num_write += ret;
}
ret = call_bio_callback_with_processed(bio, BIO_CB_PUTS | BIO_CB_RETURN,
in, len, ret);

return ret;
}

int BIO_flush(BIO *bio) {
Expand All @@ -321,8 +360,20 @@ long BIO_ctrl(BIO *bio, int cmd, long larg, void *parg) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
return -2;
}
long ret = 0;
if (HAS_CALLBACK(bio)) {
ret = bio->callback_ex(bio, BIO_CB_CTRL, parg, 0, cmd, larg, 1L, NULL);
if (ret <= 0) {
return ret;
}
}

return bio->method->ctrl(bio, cmd, larg, parg);
ret = bio->method->ctrl(bio, cmd, larg, parg);
if (HAS_CALLBACK(bio)) {
ret = bio->callback_ex(bio, BIO_CB_CTRL | BIO_CB_RETURN, parg, 0, cmd, larg,
ret, NULL);
}
return ret;
}

char *BIO_ptr_ctrl(BIO *b, int cmd, long larg) {
Expand Down Expand Up @@ -838,7 +889,7 @@ void BIO_set_shutdown(BIO *bio, int shutdown) { bio->shutdown = shutdown; }
int BIO_get_shutdown(BIO *bio) { return bio->shutdown; }

int BIO_meth_set_puts(BIO_METHOD *method, int (*puts)(BIO *, const char *)) {
// Ignore the parameter. We implement |BIO_puts| using |BIO_write|.
method->bputs = puts;
return 1;
}

Expand Down
130 changes: 130 additions & 0 deletions crypto/bio/bio_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1095,3 +1095,133 @@ TEST(BIOTest, ReadWriteEx) {
EXPECT_FALSE(BIO_read_ex(bio.get(), nullptr, 0, &read));
EXPECT_EQ(read, (size_t)0);
}

TEST(BIOTest, TestPutsAsWrite) {
// By default, |BIO_puts| acts as |BIO_write|.
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
ASSERT_TRUE(bio);

// Test basic puts and read
uint8_t buf[32];
EXPECT_EQ(12, BIO_puts(bio.get(), "hello world\n"));
EXPECT_EQ(12, BIO_read(bio.get(), buf, sizeof(buf)));
}

TEST(BIOTest, TestPutsGetsCtrlCallbacks) {
bio_callback_cleanup();
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
ASSERT_TRUE(bio);

char buf[TEST_BUF_LEN];

BIO_set_callback_ex(bio.get(),bio_cb_ex);

// Test basic putting and reading
EXPECT_EQ(TEST_DATA_WRITTEN, BIO_puts(bio.get(), "12345"));

ASSERT_EQ(param_oper_ex[0], BIO_CB_PUTS);
ASSERT_EQ(param_oper_ex[1], BIO_CB_PUTS | BIO_CB_RETURN);

ASSERT_EQ(param_argp_ex[0], param_argp_ex[1]);
ASSERT_EQ(param_ret_ex[0], 1);
ASSERT_EQ(param_ret_ex[1], TEST_DATA_WRITTEN);

ASSERT_EQ(param_len_ex[0], (size_t)TEST_DATA_WRITTEN);
ASSERT_EQ(param_len_ex[1], (size_t)TEST_DATA_WRITTEN);

ASSERT_EQ(param_argi_ex[0], 0);
ASSERT_EQ(param_argi_ex[1], 0);
ASSERT_EQ(param_argl_ex[0], 0);
ASSERT_EQ(param_argl_ex[1], 0);

ASSERT_EQ(param_processed_ex[0], 0u);
ASSERT_EQ(param_processed_ex[1], 5u);

// The mock should be "full" at this point
ASSERT_EQ(test_count_ex, CB_TEST_COUNT);

// reset the mock then test BIO_gets
bio_callback_cleanup();

ASSERT_EQ(TEST_DATA_WRITTEN, BIO_gets(bio.get(), buf, sizeof(buf)));

ASSERT_EQ(param_oper_ex[0], BIO_CB_GETS);
ASSERT_EQ(param_oper_ex[1], BIO_CB_GETS | BIO_CB_RETURN);

ASSERT_EQ(param_argp_ex[0], param_argp_ex[1]);
ASSERT_EQ(param_ret_ex[0], 1);
ASSERT_EQ(param_ret_ex[1], TEST_DATA_WRITTEN);

ASSERT_EQ(param_len_ex[0], (size_t)TEST_BUF_LEN);
ASSERT_EQ(param_len_ex[1], (size_t)TEST_BUF_LEN);

ASSERT_EQ(param_argi_ex[0], 0);
ASSERT_EQ(param_argi_ex[1], 0);
ASSERT_EQ(param_argl_ex[0], 0);
ASSERT_EQ(param_argl_ex[1], 0);

ASSERT_EQ(param_processed_ex[0], 0u);
ASSERT_EQ(param_processed_ex[1], 5u);

ASSERT_EQ(test_count_ex, CB_TEST_COUNT);

ASSERT_EQ(CALL_BACK_FAILURE, BIO_gets(bio.get(), buf, sizeof(buf)));

// Reset the mock then test BIO_ctrl
bio_callback_cleanup();

// Test BIO_ctrl. This is not normally called directly so we can use one of the macros such as BIO_reset to test it
ASSERT_EQ(BIO_reset(bio.get()), 1);

ASSERT_EQ(param_oper_ex[0], BIO_CB_CTRL);
ASSERT_EQ(param_oper_ex[1], BIO_CB_CTRL | BIO_CB_RETURN);

// argi in this case in the cmd sent to the ctrl method
ASSERT_EQ(param_argi_ex[0], BIO_CTRL_RESET);
ASSERT_EQ(param_argi_ex[1], BIO_CTRL_RESET);

bio_callback_cleanup();
ASSERT_EQ(BIO_gets(bio.get(),buf,sizeof(buf)),0);

}

namespace {
// Define custom BIO and BIO_METHODS to test BIO_puts without write
static int customPuts(BIO *b, const char *in) {
return 0;
}
static int customNew(BIO *b) {
b->init=1;
return 1;
}
static const BIO_METHOD custom_method = {
BIO_TYPE_NONE,
"CustomBioMethod",
NULL,
NULL,
customPuts,
NULL,
NULL,
customNew,
NULL,
NULL
};

static const BIO_METHOD *BIO_cust(void) {return &custom_method; }

TEST(BIOTest, TestCustomPuts) {
bssl::UniquePtr<BIO> bio(BIO_new(BIO_cust()));
ASSERT_TRUE(bio);

ASSERT_EQ(0, BIO_puts(bio.get(), "hello world"));

// Test setting new method
bssl::UniquePtr<BIO_METHOD> method(BIO_meth_new(0, nullptr));
ASSERT_TRUE(method);
ASSERT_TRUE(BIO_meth_set_puts(
method.get(), [](BIO *b, const char *in) -> int {
return 100;
}
));
}
} //namespace
33 changes: 22 additions & 11 deletions include/openssl/bio.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,12 @@ OPENSSL_EXPORT int BIO_read(BIO *bio, void *data, int len);
OPENSSL_EXPORT int BIO_read_ex(BIO *bio, void *data, size_t data_len,
size_t *read_bytes);

// BIO_gets reads a line from |bio| and writes at most |size| bytes into |buf|.
// It returns the number of bytes read or a negative number on error. This
// function's output always includes a trailing NUL byte, so it will read at
// BIO_gets calls the |bio| |callback_ex| if set with |BIO_CB_GETS|, attempts
// to read a line from |bio| and write at most |size| bytes into |buf|, then
// calls |callback_ex| with |BIO_CB_GETS|+|BIO_CB_RETURN|. If |callback_ex| is
// set BIO_gets returns the value from calling the |callback_ex|, otherwise
// |BIO_gets| returns the number of bytes read, or a negative number on error.
// This function's output always includes a trailing NUL byte, so it will read at
// most |size - 1| bytes.
//
// If the function read a complete line, the output will include the newline
Expand All @@ -146,8 +149,13 @@ OPENSSL_EXPORT int BIO_write_ex(BIO *bio, const void *data, size_t data_len,
// It returns one if all bytes were successfully written and zero on error.
OPENSSL_EXPORT int BIO_write_all(BIO *bio, const void *data, size_t len);

// BIO_puts writes a NUL terminated string from |buf| to |bio|. It returns the
// number of bytes written or a negative number on error.
// BIO_puts calls the |bio| |callback_ex| if set with |BIO_CB_PUTS|, attempts
// to write a NUL terminated string from |buf| to |bio|, then calls
// |callback_ex| with |BIO_CB_PUTS|+|BIO_CB_RETURN|. If |callback_ex| is set
// BIO_puts returns the value from calling the |callback_ex|, otherwise
// |BIO_puts| returns the number of bytes written, or a negative number on
// error. Unless the application defines a custom bputs method, this will
// delegate to using bwrite.
OPENSSL_EXPORT int BIO_puts(BIO *bio, const char *buf);

// BIO_flush flushes any buffered output. It returns one on success and zero
Expand All @@ -160,8 +168,9 @@ OPENSSL_EXPORT int BIO_flush(BIO *bio);
// These are generic functions for sending control requests to a BIO. In
// general one should use the wrapper functions like |BIO_get_close|.

// BIO_ctrl sends the control request |cmd| to |bio|. The |cmd| argument should
// be one of the |BIO_C_*| values.
// BIO_ctrl call the |bio| |callback_ex| if set with |BIO_CB_CTRL|, sends the
// control request |cmd| to |bio|, then calls |callback_ex| with |BIO_CB_CTRL|
// + |BIO_CB_RETURN|. The |cmd| argument should be one of the |BIO_C_*| values.
OPENSSL_EXPORT long BIO_ctrl(BIO *bio, int cmd, long larg, void *parg);

// BIO_ptr_ctrl acts like |BIO_ctrl| but passes the address of a |void*|
Expand Down Expand Up @@ -883,8 +892,8 @@ OPENSSL_EXPORT void BIO_set_shutdown(BIO *bio, int shutdown);
// BIO_get_shutdown returns the method-specific "shutdown" bit.
OPENSSL_EXPORT int BIO_get_shutdown(BIO *bio);

// BIO_meth_set_puts returns one. |BIO_puts| is implemented with |BIO_write| in
// BoringSSL.
// BIO_meth_set_puts sets the implementation of |BIO_puts| for |method| and
// returns 1.
OPENSSL_EXPORT int BIO_meth_set_puts(BIO_METHOD *method,
int (*puts)(BIO *, const char *));

Expand Down Expand Up @@ -957,7 +966,6 @@ struct bio_method_st {
const char *name;
int (*bwrite)(BIO *, const char *, int);
int (*bread)(BIO *, char *, int);
// TODO(fork): remove bputs.
int (*bputs)(BIO *, const char *);
int (*bgets)(BIO *, char *, int);
long (*ctrl)(BIO *, int, long, void *);
Expand All @@ -973,7 +981,10 @@ struct bio_st {
// If set, |BIO_read|, |BIO_write|, and |BIO_free| execute |callback_ex|.
// Callbacks are only called with for the following events: |BIO_CB_READ|,
// |BIO_CB_READ|+|BIO_CB_RETURN|, |BIO_CB_WRITE|,
// |BIO_CB_WRITE|+|BIO_CB_RETURN|, and |BIO_CB_FREE|.
// |BIO_CB_WRITE|+|BIO_CB_RETURN|, |BIO_CB_PUTS|,
// |BIO_CB_PUTS|+|BIO_CB_RETURN|, |BIO_CB_GETS|,
// |BIO_CB_GETS|+|BIO_CB_RETURN|, |BIO_CB_CTRL|,
// |BIO_CB_CTRL|+|BIO_CB_RETURN|, and |BIO_CB_FREE|.
BIO_callback_fn_ex callback_ex;
// Optional callback argument, only intended for applications use.
char *cb_arg;
Expand Down

0 comments on commit 045869b

Please sign in to comment.