diff options
author | Shane Lontis <shane.lontis@oracle.com> | 2021-03-09 17:25:26 +1000 |
---|---|---|
committer | Shane Lontis <shane.lontis@oracle.com> | 2021-03-11 07:57:31 +1000 |
commit | a30823c80f8c1f4ac22fb358cab65ce4e81a5046 (patch) | |
tree | 4932e6285f6fc31b31097ce448c510d5f45530e8 /crypto/bio | |
parent | c8511e89804749e82d1212fba1dc06c86a266ee4 (diff) |
Add new filter BIO BIO_f_readbuffer()
This allows BIO_tell() and BIO_seek() to work for BIO's that do
not support these methods. The main use case for this is file/fd BIO's
that use stdin.
This works for stdin taken from input redirection (command < file),
and stdin via pipe (cat file | command).
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14407)
Diffstat (limited to 'crypto/bio')
-rw-r--r-- | crypto/bio/bf_readbuff.c | 268 | ||||
-rw-r--r-- | crypto/bio/build.info | 2 |
2 files changed, 269 insertions, 1 deletions
diff --git a/crypto/bio/bf_readbuff.c b/crypto/bio/bf_readbuff.c new file mode 100644 index 0000000000..673d592ec0 --- /dev/null +++ b/crypto/bio/bf_readbuff.c @@ -0,0 +1,268 @@ +/* + * Copyright 2021 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 + */ + +/* + * This is a read only BIO filter that can be used to add BIO_tell() and + * BIO_seek() support to source/sink BIO's (such as a file BIO that uses stdin). + * It does this by caching ALL data read from the BIO source/sink into a + * resizable memory buffer. + */ + +#include <stdio.h> +#include <errno.h> +#include "bio_local.h" +#include "internal/cryptlib.h" + +#define DEFAULT_BUFFER_SIZE 4096 + +static int readbuffer_write(BIO *h, const char *buf, int num); +static int readbuffer_read(BIO *h, char *buf, int size); +static int readbuffer_puts(BIO *h, const char *str); +static int readbuffer_gets(BIO *h, char *str, int size); +static long readbuffer_ctrl(BIO *h, int cmd, long arg1, void *arg2); +static int readbuffer_new(BIO *h); +static int readbuffer_free(BIO *data); +static long readbuffer_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp); + +static const BIO_METHOD methods_readbuffer = { + BIO_TYPE_BUFFER, + "readbuffer", + bwrite_conv, + readbuffer_write, + bread_conv, + readbuffer_read, + readbuffer_puts, + readbuffer_gets, + readbuffer_ctrl, + readbuffer_new, + readbuffer_free, + readbuffer_callback_ctrl, +}; + +const BIO_METHOD *BIO_f_readbuffer(void) +{ + return &methods_readbuffer; +} + +static int readbuffer_new(BIO *bi) +{ + BIO_F_BUFFER_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx == NULL) + return 0; + ctx->ibuf_size = DEFAULT_BUFFER_SIZE; + ctx->ibuf = OPENSSL_malloc(DEFAULT_BUFFER_SIZE); + if (ctx->ibuf == NULL) { + OPENSSL_free(ctx); + return 0; + } + + bi->init = 1; + bi->ptr = (char *)ctx; + bi->flags = 0; + return 1; +} + +static int readbuffer_free(BIO *a) +{ + BIO_F_BUFFER_CTX *b; + + if (a == NULL) + return 0; + b = (BIO_F_BUFFER_CTX *)a->ptr; + OPENSSL_free(b->ibuf); + OPENSSL_free(a->ptr); + a->ptr = NULL; + a->init = 0; + a->flags = 0; + return 1; +} + +static int readbuffer_resize(BIO_F_BUFFER_CTX *ctx, int sz) +{ + char *tmp; + + /* Figure out how many blocks are required */ + sz += (ctx->ibuf_off + DEFAULT_BUFFER_SIZE - 1); + sz = DEFAULT_BUFFER_SIZE * (sz / DEFAULT_BUFFER_SIZE); + + /* Resize if the buffer is not big enough */ + if (sz > ctx->ibuf_size) { + tmp = OPENSSL_realloc(ctx->ibuf, sz); + if (tmp == NULL) + return 0; + ctx->ibuf = tmp; + ctx->ibuf_size = sz; + } + return 1; +} + +static int readbuffer_read(BIO *b, char *out, int outl) +{ + int i, num = 0; + BIO_F_BUFFER_CTX *ctx; + + if (out == NULL || outl == 0) + return 0; + ctx = (BIO_F_BUFFER_CTX *)b->ptr; + + if ((ctx == NULL) || (b->next_bio == NULL)) + return 0; + BIO_clear_retry_flags(b); + + for (;;) { + i = ctx->ibuf_len; + /* If there is something in the buffer just read it. */ + if (i != 0) { + if (i > outl) + i = outl; + memcpy(out, &(ctx->ibuf[ctx->ibuf_off]), i); + ctx->ibuf_off += i; + ctx->ibuf_len -= i; + num += i; + /* Exit if we have read the bytes required out of the buffer */ + if (outl == i) + return num; + outl -= i; + out += i; + } + + /* Only gets here if the buffer has been consumed */ + if (!readbuffer_resize(ctx, outl)) + return 0; + + /* Do some buffering by reading from the next bio */ + i = BIO_read(b->next_bio, ctx->ibuf + ctx->ibuf_off, outl); + if (i <= 0) { + BIO_copy_next_retry(b); + if (i < 0) + return ((num > 0) ? num : i); + else + return num; /* i == 0 */ + } + ctx->ibuf_len = i; + } +} + +static int readbuffer_write(BIO *b, const char *in, int inl) +{ + return 0; +} +static int readbuffer_puts(BIO *b, const char *str) +{ + return 0; +} + +static long readbuffer_ctrl(BIO *b, int cmd, long num, void *ptr) +{ + BIO_F_BUFFER_CTX *ctx; + long ret = 1, sz; + + ctx = (BIO_F_BUFFER_CTX *)b->ptr; + + switch (cmd) { + case BIO_CTRL_EOF: + if (ctx->ibuf_len > 0) + return 0; + if (b->next_bio == NULL) + return 1; + ret = BIO_ctrl(b->next_bio, cmd, num, ptr); + break; + + case BIO_C_FILE_SEEK: + case BIO_CTRL_RESET: + sz = ctx->ibuf_off + ctx->ibuf_len; + /* Assume it can only seek backwards */ + if (num < 0 || num > sz) + return 0; + ctx->ibuf_off = num; + ctx->ibuf_len = sz - num; + break; + + case BIO_C_FILE_TELL: + case BIO_CTRL_INFO: + ret = (long)ctx->ibuf_off; + break; + case BIO_CTRL_PENDING: + ret = (long)ctx->ibuf_len; + if (ret == 0) { + if (b->next_bio == NULL) + return 0; + ret = BIO_ctrl(b->next_bio, cmd, num, ptr); + } + break; + case BIO_CTRL_DUP: + case BIO_CTRL_FLUSH: + ret = 1; + break; + default: + ret = 0; + break; + } + return ret; +} + +static long readbuffer_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp) +{ + if (b->next_bio == NULL) + return 0; + return BIO_callback_ctrl(b->next_bio, cmd, fp); +} + +static int readbuffer_gets(BIO *b, char *buf, int size) +{ + BIO_F_BUFFER_CTX *ctx; + int num = 0, num_chars, found_newline; + char *p; + + if (size == 0) + return 0; + --size; /* the passed in size includes the terminator - so remove it here */ + ctx = (BIO_F_BUFFER_CTX *)b->ptr; + BIO_clear_retry_flags(b); + + for (;;) { + if (ctx->ibuf_len > 0) { + p = &(ctx->ibuf[ctx->ibuf_off]); + found_newline = 0; + for (num_chars = 0; + (num_chars < ctx->ibuf_len) && (num_chars < size); + num_chars++) { + *(buf++) = p[num_chars]; + if (p[num_chars] == '\n') { + found_newline = 1; + num_chars++; + break; + } + } + num += num_chars; + size -= num_chars; + ctx->ibuf_len -= num_chars; + ctx->ibuf_off += num_chars; + if (found_newline || size == 0) { + *buf = '\0'; + return num; + } + } else { + /* read another line and resize if we have to */ + if (!readbuffer_resize(ctx, size)) + return 0; + + /* Read another line from the next bio using BIO_gets */ + num_chars = BIO_gets(b->next_bio, ctx->ibuf + ctx->ibuf_off, + 1 + size); + if (num_chars <= 0) { + BIO_copy_next_retry(b); + *buf = '\0'; + return num > 0 ? num : num_chars; + } + ctx->ibuf_len = num_chars; + } + } +} diff --git a/crypto/bio/build.info b/crypto/bio/build.info index 8e3f530f88..227071f0ce 100644 --- a/crypto/bio/build.info +++ b/crypto/bio/build.info @@ -15,4 +15,4 @@ SOURCE[../../libcrypto]=\ # Filters SOURCE[../../libcrypto]=\ - bf_null.c bf_buff.c bf_lbuf.c bf_nbio.c bf_prefix.c + bf_null.c bf_buff.c bf_lbuf.c bf_nbio.c bf_prefix.c bf_readbuff.c |