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