xref: /openbsd-src/regress/lib/libcrypto/bio/bio_asn1.c (revision 90bab393299fd6a0a07552ea96796432dc8068e4)
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