1 /* $OpenBSD: bio_asn1.c,v 1.5 2023/07/21 20:22:47 tb Exp $ */
2
3 /*
4 * Copyright (c) 2023 Theo Buehler <tb@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <err.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22
23 #include <openssl/asn1.h>
24 #include <openssl/asn1t.h>
25 #include <openssl/bio.h>
26 #include <openssl/evp.h>
27 #include <openssl/objects.h>
28 #include <openssl/pkcs7.h>
29
30 #include "asn1_local.h"
31
32 /*
33 * Minimal reproducer for the BIO_new_NDEF() write after free fixed in
34 * bio_ndef.c r1.13.
35 */
36
37 static int
waf_cb(int op,ASN1_VALUE ** pval,const ASN1_ITEM * it,void * exarg)38 waf_cb(int op, ASN1_VALUE **pval, const ASN1_ITEM *it, void *exarg)
39 {
40 return 0;
41 }
42
43 static const ASN1_AUX WAF_aux = {
44 .asn1_cb = waf_cb,
45 };
46
47 static const ASN1_ITEM WAF_it = {
48 .funcs = &WAF_aux,
49 };
50
51 static int
test_bio_new_ndef_waf(void)52 test_bio_new_ndef_waf(void)
53 {
54 BIO *out = NULL;
55 int failed = 1;
56
57 if ((out = BIO_new(BIO_s_mem())) == NULL)
58 goto err;
59
60 /*
61 * BIO_new_NDEF() pushes out onto asn_bio. The waf_cb() call fails.
62 * Prior to bio_ndef.c r1.13, asn_bio was freed and out->prev_bio
63 * still pointed to it.
64 */
65
66 if (BIO_new_NDEF(out, NULL, &WAF_it) != NULL) {
67 fprintf(stderr, "%s: BIO_new_NDEF succeeded\n", __func__);
68 goto err;
69 }
70
71 /*
72 * If out->prev_bio != NULL, this writes to out->prev_bio->next_bio.
73 * After bio_ndef.c r1.13, out is an isolated BIO, so this is a noop.
74 */
75
76 BIO_pop(out);
77
78 failed = 0;
79
80 err:
81 BIO_free(out);
82
83 return failed;
84 }
85
86 /*
87 * test_prefix_leak() leaks before asn/bio_asn1.c r1.19.
88 */
89
90 static long
read_leak_cb(BIO * bio,int cmd,const char * argp,int argi,long argl,long ret)91 read_leak_cb(BIO *bio, int cmd, const char *argp, int argi, long argl, long ret)
92 {
93 int read_return = BIO_CB_READ | BIO_CB_RETURN;
94 char *set_me;
95
96 if ((cmd & read_return) != read_return)
97 return ret;
98
99 set_me = BIO_get_callback_arg(bio);
100 *set_me = 1;
101
102 return 0;
103 }
104
105 static int
test_prefix_leak(void)106 test_prefix_leak(void)
107 {
108 BIO *bio_in = NULL, *bio_out = NULL;
109 PKCS7 *pkcs7 = NULL;
110 char set_me = 0;
111 int failed = 1;
112
113 if ((bio_in = BIO_new_mem_buf("some data\n", -1)) == NULL)
114 goto err;
115
116 BIO_set_callback(bio_in, read_leak_cb);
117 BIO_set_callback_arg(bio_in, &set_me);
118
119 if ((pkcs7 = PKCS7_new()) == NULL)
120 goto err;
121 if (!PKCS7_set_type(pkcs7, NID_pkcs7_data))
122 goto err;
123
124 if ((bio_out = BIO_new(BIO_s_mem())) == NULL)
125 goto err;
126
127 if (!i2d_PKCS7_bio_stream(bio_out, pkcs7, bio_in,
128 SMIME_STREAM | SMIME_BINARY))
129 goto err;
130
131 if (set_me != 1) {
132 fprintf(stderr, "%s: read_leak_cb didn't set set_me", __func__);
133 goto err;
134 }
135
136 failed = 0;
137
138 err:
139 BIO_free(bio_in);
140 BIO_free(bio_out);
141 PKCS7_free(pkcs7);
142
143 return failed;
144 }
145
146 /*
147 * test_infinite_loop() would hang before asn/bio_asn1.c r1.18.
148 */
149
150 #define SENTINEL (-57)
151
152 static long
inf_loop_cb(BIO * bio,int cmd,const char * argp,int argi,long argl,long ret)153 inf_loop_cb(BIO *bio, int cmd, const char *argp, int argi, long argl, long ret)
154 {
155 int write_return = BIO_CB_WRITE | BIO_CB_RETURN;
156 char *set_me;
157
158 if ((cmd & write_return) != write_return)
159 return ret;
160
161 set_me = BIO_get_callback_arg(bio);
162
163 /* First time around: ASN1_STATE_HEADER_COPY - succeed. */
164 if (*set_me == 0) {
165 *set_me = 1;
166 return ret;
167 }
168
169 /* Second time around: ASN1_STATE_DATA_COPY - return sentinel value. */
170 if (*set_me == 1) {
171 *set_me = 2;
172 return SENTINEL;
173 }
174
175 /* Everything else is unexpected: return EOF. */
176 *set_me = 3;
177
178 return 0;
179
180 }
181
182 static int
test_infinite_loop(void)183 test_infinite_loop(void)
184 {
185 BIO *asn_bio = NULL, *bio = NULL;
186 char set_me = 0;
187 int failed = 1;
188 int write_ret;
189
190 if ((asn_bio = BIO_new(BIO_f_asn1())) == NULL)
191 goto err;
192
193 if ((bio = BIO_new(BIO_s_mem())) == NULL)
194 goto err;
195
196 BIO_set_callback(bio, inf_loop_cb);
197 BIO_set_callback_arg(bio, &set_me);
198
199 if (BIO_push(asn_bio, bio) == NULL) {
200 BIO_free(bio);
201 goto err;
202 }
203
204 if ((write_ret = BIO_write(asn_bio, "foo", 3)) != SENTINEL) {
205 fprintf(stderr, "%s: BIO_write: want %d, got %d", __func__,
206 SENTINEL, write_ret);
207 goto err;
208 }
209
210 if (set_me != 2) {
211 fprintf(stderr, "%s: set_me: %d != 2", __func__, set_me);
212 goto err;
213 }
214
215 failed = 0;
216 err:
217 BIO_free_all(asn_bio);
218
219 return failed;
220 }
221
222 int
main(void)223 main(void)
224 {
225 int failed = 0;
226
227 failed |= test_bio_new_ndef_waf();
228 failed |= test_prefix_leak();
229 failed |= test_infinite_loop();
230
231 return failed;
232 }
233