1/*
2 * Copyright 2018-2021 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#include <stdio.h>
10#include <string.h>
11#include <openssl/buffer.h>
12#include <openssl/bio.h>
13
14#include "testutil.h"
15
16static int test_bio_memleak(void)
17{
18    int ok = 0;
19    BIO *bio;
20    BUF_MEM bufmem;
21    static const char str[] = "BIO test\n";
22    char buf[100];
23
24    bio = BIO_new(BIO_s_mem());
25    if (!TEST_ptr(bio))
26        goto finish;
27    bufmem.length = sizeof(str);
28    bufmem.data = (char *) str;
29    bufmem.max = bufmem.length;
30    BIO_set_mem_buf(bio, &bufmem, BIO_NOCLOSE);
31    BIO_set_flags(bio, BIO_FLAGS_MEM_RDONLY);
32    if (!TEST_int_eq(BIO_read(bio, buf, sizeof(buf)), sizeof(str)))
33        goto finish;
34    if (!TEST_mem_eq(buf, sizeof(str), str, sizeof(str)))
35        goto finish;
36    ok = 1;
37
38 finish:
39    BIO_free(bio);
40    return ok;
41}
42
43static int test_bio_get_mem(void)
44{
45    int ok = 0;
46    BIO *bio = NULL;
47    BUF_MEM *bufmem = NULL;
48
49    bio = BIO_new(BIO_s_mem());
50    if (!TEST_ptr(bio))
51        goto finish;
52    if (!TEST_int_eq(BIO_puts(bio, "Hello World\n"), 12))
53        goto finish;
54    BIO_get_mem_ptr(bio, &bufmem);
55    if (!TEST_ptr(bufmem))
56        goto finish;
57    if (!TEST_int_gt(BIO_set_close(bio, BIO_NOCLOSE), 0))
58        goto finish;
59    BIO_free(bio);
60    bio = NULL;
61    if (!TEST_mem_eq(bufmem->data, bufmem->length, "Hello World\n", 12))
62        goto finish;
63    ok = 1;
64
65 finish:
66    BIO_free(bio);
67    BUF_MEM_free(bufmem);
68    return ok;
69}
70
71static int test_bio_new_mem_buf(void)
72{
73    int ok = 0;
74    BIO *bio;
75    BUF_MEM *bufmem;
76    char data[16];
77
78    bio = BIO_new_mem_buf("Hello World\n", 12);
79    if (!TEST_ptr(bio))
80        goto finish;
81    if (!TEST_int_eq(BIO_read(bio, data, 5), 5))
82        goto finish;
83    if (!TEST_mem_eq(data, 5, "Hello", 5))
84        goto finish;
85    if (!TEST_int_gt(BIO_get_mem_ptr(bio, &bufmem), 0))
86        goto finish;
87    if (!TEST_int_lt(BIO_write(bio, "test", 4), 0))
88        goto finish;
89    if (!TEST_int_eq(BIO_read(bio, data, 16), 7))
90        goto finish;
91    if (!TEST_mem_eq(data, 7, " World\n", 7))
92        goto finish;
93    if (!TEST_int_gt(BIO_reset(bio), 0))
94        goto finish;
95    if (!TEST_int_eq(BIO_read(bio, data, 16), 12))
96        goto finish;
97    if (!TEST_mem_eq(data, 12, "Hello World\n", 12))
98        goto finish;
99    ok = 1;
100
101 finish:
102    BIO_free(bio);
103    return ok;
104}
105
106static int test_bio_rdonly_mem_buf(void)
107{
108    int ok = 0;
109    BIO *bio, *bio2 = NULL;
110    BUF_MEM *bufmem;
111    char data[16];
112
113    bio = BIO_new_mem_buf("Hello World\n", 12);
114    if (!TEST_ptr(bio))
115        goto finish;
116    if (!TEST_int_eq(BIO_read(bio, data, 5), 5))
117        goto finish;
118    if (!TEST_mem_eq(data, 5, "Hello", 5))
119        goto finish;
120    if (!TEST_int_gt(BIO_get_mem_ptr(bio, &bufmem), 0))
121        goto finish;
122    (void)BIO_set_close(bio, BIO_NOCLOSE);
123
124    bio2 = BIO_new(BIO_s_mem());
125    if (!TEST_ptr(bio2))
126        goto finish;
127    BIO_set_mem_buf(bio2, bufmem, BIO_CLOSE);
128    BIO_set_flags(bio2, BIO_FLAGS_MEM_RDONLY);
129
130    if (!TEST_int_eq(BIO_read(bio2, data, 16), 7))
131        goto finish;
132    if (!TEST_mem_eq(data, 7, " World\n", 7))
133        goto finish;
134    if (!TEST_int_gt(BIO_reset(bio2), 0))
135        goto finish;
136    if (!TEST_int_eq(BIO_read(bio2, data, 16), 7))
137        goto finish;
138    if (!TEST_mem_eq(data, 7, " World\n", 7))
139        goto finish;
140    ok = 1;
141
142 finish:
143    BIO_free(bio);
144    BIO_free(bio2);
145    return ok;
146}
147
148static int test_bio_rdwr_rdonly(void)
149{
150    int ok = 0;
151    BIO *bio = NULL;
152    char data[16];
153
154    bio = BIO_new(BIO_s_mem());
155    if (!TEST_ptr(bio))
156        goto finish;
157    if (!TEST_int_eq(BIO_puts(bio, "Hello World\n"), 12))
158        goto finish;
159
160    BIO_set_flags(bio, BIO_FLAGS_MEM_RDONLY);
161    if (!TEST_int_eq(BIO_read(bio, data, 16), 12))
162        goto finish;
163    if (!TEST_mem_eq(data, 12, "Hello World\n", 12))
164        goto finish;
165    if (!TEST_int_gt(BIO_reset(bio), 0))
166        goto finish;
167
168    BIO_clear_flags(bio, BIO_FLAGS_MEM_RDONLY);
169    if (!TEST_int_eq(BIO_puts(bio, "Hi!\n"), 4))
170        goto finish;
171    if (!TEST_int_eq(BIO_read(bio, data, 16), 16))
172        goto finish;
173
174    if (!TEST_mem_eq(data, 16, "Hello World\nHi!\n", 16))
175        goto finish;
176
177    ok = 1;
178
179 finish:
180    BIO_free(bio);
181    return ok;
182}
183
184static int test_bio_nonclear_rst(void)
185{
186    int ok = 0;
187    BIO *bio = NULL;
188    char data[16];
189
190    bio = BIO_new(BIO_s_mem());
191    if (!TEST_ptr(bio))
192        goto finish;
193    if (!TEST_int_eq(BIO_puts(bio, "Hello World\n"), 12))
194        goto finish;
195
196    BIO_set_flags(bio, BIO_FLAGS_NONCLEAR_RST);
197
198    if (!TEST_int_eq(BIO_read(bio, data, 16), 12))
199        goto finish;
200    if (!TEST_mem_eq(data, 12, "Hello World\n", 12))
201        goto finish;
202    if (!TEST_int_gt(BIO_reset(bio), 0))
203        goto finish;
204
205    if (!TEST_int_eq(BIO_read(bio, data, 16), 12))
206        goto finish;
207    if (!TEST_mem_eq(data, 12, "Hello World\n", 12))
208        goto finish;
209
210    BIO_clear_flags(bio, BIO_FLAGS_NONCLEAR_RST);
211    if (!TEST_int_gt(BIO_reset(bio), 0))
212        goto finish;
213
214    if (!TEST_int_lt(BIO_read(bio, data, 16), 1))
215        goto finish;
216
217    ok = 1;
218
219 finish:
220    BIO_free(bio);
221    return ok;
222}
223
224static int error_callback_fired;
225static long BIO_error_callback(BIO *bio, int cmd, const char *argp,
226                               size_t len, int argi,
227                               long argl, int ret, size_t *processed)
228{
229    if ((cmd & (BIO_CB_READ | BIO_CB_RETURN)) != 0) {
230        error_callback_fired = 1;
231        ret = 0;  /* fail for read operations to simulate error in input BIO */
232    }
233    return ret;
234}
235
236/* Checks i2d_ASN1_bio_stream() is freeing all memory when input BIO ends unexpectedly. */
237static int test_bio_i2d_ASN1_mime(void)
238{
239    int ok = 0;
240    BIO *bio = NULL, *out = NULL;
241    BUF_MEM bufmem;
242    static const char str[] = "BIO mime test\n";
243    PKCS7 *p7 = NULL;
244
245    if (!TEST_ptr(bio = BIO_new(BIO_s_mem())))
246        goto finish;
247
248    bufmem.length = sizeof(str);
249    bufmem.data = (char *) str;
250    bufmem.max = bufmem.length;
251    BIO_set_mem_buf(bio, &bufmem, BIO_NOCLOSE);
252    BIO_set_flags(bio, BIO_FLAGS_MEM_RDONLY);
253    BIO_set_callback_ex(bio, BIO_error_callback);
254
255    if (!TEST_ptr(out = BIO_new(BIO_s_mem())))
256        goto finish;
257    if (!TEST_ptr(p7 = PKCS7_new()))
258        goto finish;
259    if (!TEST_true(PKCS7_set_type(p7, NID_pkcs7_data)))
260        goto finish;
261
262    error_callback_fired = 0;
263
264    if (!TEST_false(i2d_ASN1_bio_stream(out, (ASN1_VALUE*) p7, bio,
265                                        SMIME_STREAM | SMIME_BINARY,
266                                        ASN1_ITEM_rptr(PKCS7))))
267        goto finish;
268
269    if (!TEST_int_eq(error_callback_fired, 1))
270        goto finish;
271
272    ok = 1;
273
274 finish:
275    BIO_free(bio);
276    BIO_free(out);
277    PKCS7_free(p7);
278    return ok;
279}
280
281int setup_tests(void)
282{
283    ADD_TEST(test_bio_memleak);
284    ADD_TEST(test_bio_get_mem);
285    ADD_TEST(test_bio_new_mem_buf);
286    ADD_TEST(test_bio_rdonly_mem_buf);
287    ADD_TEST(test_bio_rdwr_rdonly);
288    ADD_TEST(test_bio_nonclear_rst);
289    ADD_TEST(test_bio_i2d_ASN1_mime);
290    return 1;
291}
292