1204431Sraj/*
2204431Sraj * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
3204431Sraj *
4 * Licensed under the Apache License 2.0 (the "License").  You may not use
5 * this file except in compliance with the License.  You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10/*
11 * This is a read only BIO filter that can be used to add BIO_tell() and
12 * BIO_seek() support to source/sink BIO's (such as a file BIO that uses stdin).
13 * It does this by caching ALL data read from the BIO source/sink into a
14 * resizable memory buffer.
15 */
16
17#include <stdio.h>
18#include <errno.h>
19#include "bio_local.h"
20#include "internal/cryptlib.h"
21
22#define DEFAULT_BUFFER_SIZE     4096
23
24static int readbuffer_write(BIO *h, const char *buf, int num);
25static int readbuffer_read(BIO *h, char *buf, int size);
26static int readbuffer_puts(BIO *h, const char *str);
27static int readbuffer_gets(BIO *h, char *str, int size);
28static long readbuffer_ctrl(BIO *h, int cmd, long arg1, void *arg2);
29static int readbuffer_new(BIO *h);
30static int readbuffer_free(BIO *data);
31static long readbuffer_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp);
32
33static const BIO_METHOD methods_readbuffer = {
34    BIO_TYPE_BUFFER,
35    "readbuffer",
36    bwrite_conv,
37    readbuffer_write,
38    bread_conv,
39    readbuffer_read,
40    readbuffer_puts,
41    readbuffer_gets,
42    readbuffer_ctrl,
43    readbuffer_new,
44    readbuffer_free,
45    readbuffer_callback_ctrl,
46};
47
48const BIO_METHOD *BIO_f_readbuffer(void)
49{
50    return &methods_readbuffer;
51}
52
53static int readbuffer_new(BIO *bi)
54{
55    BIO_F_BUFFER_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
56
57    if (ctx == NULL)
58        return 0;
59    ctx->ibuf_size = DEFAULT_BUFFER_SIZE;
60    ctx->ibuf = OPENSSL_zalloc(DEFAULT_BUFFER_SIZE);
61    if (ctx->ibuf == NULL) {
62        OPENSSL_free(ctx);
63        return 0;
64    }
65
66    bi->init = 1;
67    bi->ptr = (char *)ctx;
68    bi->flags = 0;
69    return 1;
70}
71
72static int readbuffer_free(BIO *a)
73{
74    BIO_F_BUFFER_CTX *b;
75
76    if (a == NULL)
77        return 0;
78    b = (BIO_F_BUFFER_CTX *)a->ptr;
79    OPENSSL_free(b->ibuf);
80    OPENSSL_free(a->ptr);
81    a->ptr = NULL;
82    a->init = 0;
83    a->flags = 0;
84    return 1;
85}
86
87static int readbuffer_resize(BIO_F_BUFFER_CTX *ctx, int sz)
88{
89    char *tmp;
90
91    /* Figure out how many blocks are required */
92    sz += (ctx->ibuf_off + DEFAULT_BUFFER_SIZE - 1);
93    sz = DEFAULT_BUFFER_SIZE * (sz / DEFAULT_BUFFER_SIZE);
94
95    /* Resize if the buffer is not big enough */
96    if (sz > ctx->ibuf_size) {
97        tmp = OPENSSL_realloc(ctx->ibuf, sz);
98        if (tmp == NULL)
99            return 0;
100        ctx->ibuf = tmp;
101        ctx->ibuf_size = sz;
102    }
103    return 1;
104}
105
106static int readbuffer_read(BIO *b, char *out, int outl)
107{
108    int i, num = 0;
109    BIO_F_BUFFER_CTX *ctx;
110
111    if (out == NULL || outl == 0)
112        return 0;
113    ctx = (BIO_F_BUFFER_CTX *)b->ptr;
114
115    if ((ctx == NULL) || (b->next_bio == NULL))
116        return 0;
117    BIO_clear_retry_flags(b);
118
119    for (;;) {
120        i = ctx->ibuf_len;
121        /* If there is something in the buffer just read it. */
122        if (i != 0) {
123            if (i > outl)
124                i = outl;
125            memcpy(out, &(ctx->ibuf[ctx->ibuf_off]), i);
126            ctx->ibuf_off += i;
127            ctx->ibuf_len -= i;
128            num += i;
129            /* Exit if we have read the bytes required out of the buffer */
130            if (outl == i)
131                return num;
132            outl -= i;
133            out += i;
134        }
135
136        /* Only gets here if the buffer has been consumed */
137        if (!readbuffer_resize(ctx, outl))
138            return 0;
139
140        /* Do some buffering by reading from the next bio */
141        i = BIO_read(b->next_bio, ctx->ibuf + ctx->ibuf_off, outl);
142        if (i <= 0) {
143            BIO_copy_next_retry(b);
144            if (i < 0)
145                return ((num > 0) ? num : i);
146            else
147                return num; /* i == 0 */
148        }
149        ctx->ibuf_len = i;
150    }
151}
152
153static int readbuffer_write(BIO *b, const char *in, int inl)
154{
155    return 0;
156}
157static int readbuffer_puts(BIO *b, const char *str)
158{
159    return 0;
160}
161
162static long readbuffer_ctrl(BIO *b, int cmd, long num, void *ptr)
163{
164    BIO_F_BUFFER_CTX *ctx;
165    long ret = 1, sz;
166
167    ctx = (BIO_F_BUFFER_CTX *)b->ptr;
168
169    switch (cmd) {
170    case BIO_CTRL_EOF:
171        if (ctx->ibuf_len > 0)
172            return 0;
173        if (b->next_bio == NULL)
174            return 1;
175        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
176        break;
177
178    case BIO_C_FILE_SEEK:
179    case BIO_CTRL_RESET:
180        sz = ctx->ibuf_off + ctx->ibuf_len;
181        /* Assume it can only seek backwards */
182        if (num < 0 || num > sz)
183            return 0;
184        ctx->ibuf_off = num;
185        ctx->ibuf_len = sz - num;
186        break;
187
188    case BIO_C_FILE_TELL:
189    case BIO_CTRL_INFO:
190        ret = (long)ctx->ibuf_off;
191        break;
192    case BIO_CTRL_PENDING:
193        ret = (long)ctx->ibuf_len;
194        if (ret == 0) {
195            if (b->next_bio == NULL)
196                return 0;
197            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
198        }
199        break;
200    case BIO_CTRL_DUP:
201    case BIO_CTRL_FLUSH:
202        ret = 1;
203        break;
204    default:
205        ret = 0;
206        break;
207    }
208    return ret;
209}
210
211static long readbuffer_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
212{
213    if (b->next_bio == NULL)
214        return 0;
215    return BIO_callback_ctrl(b->next_bio, cmd, fp);
216}
217
218static int readbuffer_gets(BIO *b, char *buf, int size)
219{
220    BIO_F_BUFFER_CTX *ctx;
221    int num = 0, num_chars, found_newline;
222    char *p;
223    int i, j;
224
225    if (size == 0)
226        return 0;
227    --size; /* the passed in size includes the terminator - so remove it here */
228    ctx = (BIO_F_BUFFER_CTX *)b->ptr;
229    BIO_clear_retry_flags(b);
230
231    /* If data is already buffered then use this first */
232    if (ctx->ibuf_len > 0) {
233        p = ctx->ibuf + ctx->ibuf_off;
234        found_newline = 0;
235        for (num_chars = 0;
236             (num_chars < ctx->ibuf_len) && (num_chars < size);
237             num_chars++) {
238            *buf++ = p[num_chars];
239            if (p[num_chars] == '\n') {
240                found_newline = 1;
241                num_chars++;
242                break;
243            }
244        }
245        num += num_chars;
246        size -= num_chars;
247        ctx->ibuf_len -= num_chars;
248        ctx->ibuf_off += num_chars;
249        if (found_newline || size == 0) {
250            *buf = '\0';
251            return num;
252        }
253    }
254    /*
255     * If there is no buffered data left then read any remaining data from the
256     * next bio.
257     */
258
259     /* Resize if we have to */
260     if (!readbuffer_resize(ctx, 1 + size))
261         return 0;
262     /*
263      * Read more data from the next bio using BIO_read_ex:
264      * Note we cannot use BIO_gets() here as it does not work on a
265      * binary stream that contains 0x00. (Since strlen() will stop at
266      * any 0x00 not at the last read '\n' in a FILE bio).
267      * Also note that some applications open and close the file bio
268      * multiple times and need to read the next available block when using
269      * stdin - so we need to READ one byte at a time!
270      */
271     p = ctx->ibuf + ctx->ibuf_off;
272     for (i = 0; i < size; ++i) {
273         j = BIO_read(b->next_bio, p, 1);
274         if (j <= 0) {
275             BIO_copy_next_retry(b);
276             *buf = '\0';
277             return num > 0 ? num : j;
278         }
279         *buf++ = *p;
280         num++;
281         ctx->ibuf_off++;
282         if (*p == '\n')
283             break;
284         ++p;
285     }
286     *buf = '\0';
287     return num;
288}
289