xref: /openbsd-src/regress/lib/libcrypto/bio/bio_asn1.c (revision 90bab393299fd6a0a07552ea96796432dc8068e4)
1*90bab393Stb /*	$OpenBSD: bio_asn1.c,v 1.5 2023/07/21 20:22:47 tb Exp $ */
2354d32c7Stb 
3354d32c7Stb /*
4354d32c7Stb  * Copyright (c) 2023 Theo Buehler <tb@openbsd.org>
5354d32c7Stb  *
6354d32c7Stb  * Permission to use, copy, modify, and distribute this software for any
7354d32c7Stb  * purpose with or without fee is hereby granted, provided that the above
8354d32c7Stb  * copyright notice and this permission notice appear in all copies.
9354d32c7Stb  *
10354d32c7Stb  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11354d32c7Stb  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12354d32c7Stb  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13354d32c7Stb  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14354d32c7Stb  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15354d32c7Stb  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16354d32c7Stb  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17354d32c7Stb  */
18354d32c7Stb 
19354d32c7Stb #include <err.h>
20354d32c7Stb #include <stdio.h>
21354d32c7Stb #include <stdlib.h>
22354d32c7Stb 
23354d32c7Stb #include <openssl/asn1.h>
2413ffeb01Stb #include <openssl/asn1t.h>
25354d32c7Stb #include <openssl/bio.h>
2613ffeb01Stb #include <openssl/evp.h>
27354d32c7Stb #include <openssl/objects.h>
2813ffeb01Stb #include <openssl/pkcs7.h>
2913ffeb01Stb 
305f7d54afStb #include "asn1_local.h"
315f7d54afStb 
3213ffeb01Stb /*
3313ffeb01Stb  * Minimal reproducer for the BIO_new_NDEF() write after free fixed in
3413ffeb01Stb  * bio_ndef.c r1.13.
3513ffeb01Stb  */
3613ffeb01Stb 
3713ffeb01Stb static int
waf_cb(int op,ASN1_VALUE ** pval,const ASN1_ITEM * it,void * exarg)3813ffeb01Stb waf_cb(int op, ASN1_VALUE **pval, const ASN1_ITEM *it, void *exarg)
3913ffeb01Stb {
4013ffeb01Stb 	return 0;
4113ffeb01Stb }
4213ffeb01Stb 
4313ffeb01Stb static const ASN1_AUX WAF_aux = {
4413ffeb01Stb 	.asn1_cb = waf_cb,
4513ffeb01Stb };
4613ffeb01Stb 
4713ffeb01Stb static const ASN1_ITEM WAF_it = {
4813ffeb01Stb 	.funcs = &WAF_aux,
4913ffeb01Stb };
5013ffeb01Stb 
5113ffeb01Stb static int
test_bio_new_ndef_waf(void)5213ffeb01Stb test_bio_new_ndef_waf(void)
5313ffeb01Stb {
5413ffeb01Stb 	BIO *out = NULL;
5513ffeb01Stb 	int failed = 1;
5613ffeb01Stb 
5713ffeb01Stb 	if ((out = BIO_new(BIO_s_mem())) == NULL)
5813ffeb01Stb 		goto err;
5913ffeb01Stb 
6013ffeb01Stb 	/*
6113ffeb01Stb 	 * BIO_new_NDEF() pushes out onto asn_bio. The waf_cb() call fails.
6213ffeb01Stb 	 * Prior to bio_ndef.c r1.13, asn_bio was freed and out->prev_bio
6313ffeb01Stb 	 * still pointed to it.
6413ffeb01Stb 	 */
6513ffeb01Stb 
6613ffeb01Stb 	if (BIO_new_NDEF(out, NULL, &WAF_it) != NULL) {
6713ffeb01Stb 		fprintf(stderr, "%s: BIO_new_NDEF succeeded\n", __func__);
6813ffeb01Stb 		goto err;
6913ffeb01Stb 	}
7013ffeb01Stb 
7113ffeb01Stb 	/*
7213ffeb01Stb 	 * If out->prev_bio != NULL, this writes to out->prev_bio->next_bio.
7313ffeb01Stb 	 * After bio_ndef.c r1.13, out is an isolated BIO, so this is a noop.
7413ffeb01Stb 	 */
7513ffeb01Stb 
7613ffeb01Stb 	BIO_pop(out);
7713ffeb01Stb 
7813ffeb01Stb 	failed = 0;
7913ffeb01Stb 
8013ffeb01Stb  err:
8113ffeb01Stb 	BIO_free(out);
8213ffeb01Stb 
8313ffeb01Stb 	return failed;
8413ffeb01Stb }
85354d32c7Stb 
86354d32c7Stb /*
87354d32c7Stb  * test_prefix_leak() leaks before asn/bio_asn1.c r1.19.
88354d32c7Stb  */
89354d32c7Stb 
90354d32c7Stb static long
read_leak_cb(BIO * bio,int cmd,const char * argp,int argi,long argl,long ret)91354d32c7Stb read_leak_cb(BIO *bio, int cmd, const char *argp, int argi, long argl, long ret)
92354d32c7Stb {
93354d32c7Stb 	int read_return = BIO_CB_READ | BIO_CB_RETURN;
94354d32c7Stb 	char *set_me;
95354d32c7Stb 
96354d32c7Stb 	if ((cmd & read_return) != read_return)
97354d32c7Stb 		return ret;
98354d32c7Stb 
99354d32c7Stb 	set_me = BIO_get_callback_arg(bio);
100354d32c7Stb 	*set_me = 1;
101354d32c7Stb 
102354d32c7Stb 	return 0;
103354d32c7Stb }
104354d32c7Stb 
105354d32c7Stb static int
test_prefix_leak(void)106354d32c7Stb test_prefix_leak(void)
107354d32c7Stb {
108354d32c7Stb 	BIO *bio_in = NULL, *bio_out = NULL;
109354d32c7Stb 	PKCS7 *pkcs7 = NULL;
110354d32c7Stb 	char set_me = 0;
111354d32c7Stb 	int failed = 1;
112354d32c7Stb 
113*90bab393Stb 	if ((bio_in = BIO_new_mem_buf("some data\n", -1)) == NULL)
114354d32c7Stb 		goto err;
115354d32c7Stb 
116354d32c7Stb 	BIO_set_callback(bio_in, read_leak_cb);
117354d32c7Stb 	BIO_set_callback_arg(bio_in, &set_me);
118354d32c7Stb 
119354d32c7Stb 	if ((pkcs7 = PKCS7_new()) == NULL)
120354d32c7Stb 		goto err;
121354d32c7Stb 	if (!PKCS7_set_type(pkcs7, NID_pkcs7_data))
122354d32c7Stb 		goto err;
123354d32c7Stb 
124354d32c7Stb 	if ((bio_out = BIO_new(BIO_s_mem())) == NULL)
125354d32c7Stb 		goto err;
126354d32c7Stb 
127845c6a73Stb 	if (!i2d_PKCS7_bio_stream(bio_out, pkcs7, bio_in,
128845c6a73Stb 	    SMIME_STREAM | SMIME_BINARY))
129354d32c7Stb 		goto err;
130354d32c7Stb 
131354d32c7Stb 	if (set_me != 1) {
132354d32c7Stb 		fprintf(stderr, "%s: read_leak_cb didn't set set_me", __func__);
133354d32c7Stb 		goto err;
134354d32c7Stb 	}
135354d32c7Stb 
136354d32c7Stb 	failed = 0;
137354d32c7Stb 
138354d32c7Stb  err:
139354d32c7Stb 	BIO_free(bio_in);
140354d32c7Stb 	BIO_free(bio_out);
141354d32c7Stb 	PKCS7_free(pkcs7);
142354d32c7Stb 
143354d32c7Stb 	return failed;
144354d32c7Stb }
145354d32c7Stb 
146354d32c7Stb /*
147354d32c7Stb  * test_infinite_loop() would hang before asn/bio_asn1.c r1.18.
148354d32c7Stb  */
149354d32c7Stb 
150354d32c7Stb #define SENTINEL (-57)
151354d32c7Stb 
152354d32c7Stb static long
inf_loop_cb(BIO * bio,int cmd,const char * argp,int argi,long argl,long ret)153354d32c7Stb inf_loop_cb(BIO *bio, int cmd, const char *argp, int argi, long argl, long ret)
154354d32c7Stb {
155354d32c7Stb 	int write_return = BIO_CB_WRITE | BIO_CB_RETURN;
156354d32c7Stb 	char *set_me;
157354d32c7Stb 
158354d32c7Stb 	if ((cmd & write_return) != write_return)
159354d32c7Stb 		return ret;
160354d32c7Stb 
161354d32c7Stb 	set_me = BIO_get_callback_arg(bio);
162354d32c7Stb 
163354d32c7Stb 	/* First time around: ASN1_STATE_HEADER_COPY - succeed. */
164354d32c7Stb 	if (*set_me == 0) {
165354d32c7Stb 		*set_me = 1;
166354d32c7Stb 		return ret;
167354d32c7Stb 	}
168354d32c7Stb 
169354d32c7Stb 	/* Second time around: ASN1_STATE_DATA_COPY - return sentinel value. */
170354d32c7Stb 	if (*set_me == 1) {
171354d32c7Stb 		*set_me = 2;
172354d32c7Stb 		return SENTINEL;
173354d32c7Stb 	}
174354d32c7Stb 
175354d32c7Stb 	/* Everything else is unexpected: return EOF. */
176354d32c7Stb 	*set_me = 3;
177354d32c7Stb 
178354d32c7Stb 	return 0;
179354d32c7Stb 
180354d32c7Stb }
181354d32c7Stb 
182354d32c7Stb static int
test_infinite_loop(void)183354d32c7Stb test_infinite_loop(void)
184354d32c7Stb {
185354d32c7Stb 	BIO *asn_bio = NULL, *bio = NULL;
186354d32c7Stb 	char set_me = 0;
187354d32c7Stb 	int failed = 1;
188354d32c7Stb 	int write_ret;
189354d32c7Stb 
190354d32c7Stb 	if ((asn_bio = BIO_new(BIO_f_asn1())) == NULL)
191354d32c7Stb 		goto err;
192354d32c7Stb 
193354d32c7Stb 	if ((bio = BIO_new(BIO_s_mem())) == NULL)
194354d32c7Stb 		goto err;
195354d32c7Stb 
196354d32c7Stb 	BIO_set_callback(bio, inf_loop_cb);
197354d32c7Stb 	BIO_set_callback_arg(bio, &set_me);
198354d32c7Stb 
199354d32c7Stb 	if (BIO_push(asn_bio, bio) == NULL) {
200354d32c7Stb 		BIO_free(bio);
201354d32c7Stb 		goto err;
202354d32c7Stb 	}
203354d32c7Stb 
204354d32c7Stb 	if ((write_ret = BIO_write(asn_bio, "foo", 3)) != SENTINEL) {
205354d32c7Stb 		fprintf(stderr, "%s: BIO_write: want %d, got %d", __func__,
206354d32c7Stb 		    SENTINEL, write_ret);
207354d32c7Stb 		goto err;
208354d32c7Stb 	}
209354d32c7Stb 
210354d32c7Stb 	if (set_me != 2) {
211354d32c7Stb 		fprintf(stderr, "%s: set_me: %d != 2", __func__, set_me);
212354d32c7Stb 		goto err;
213354d32c7Stb 	}
214354d32c7Stb 
215354d32c7Stb 	failed = 0;
216354d32c7Stb  err:
217354d32c7Stb 	BIO_free_all(asn_bio);
218354d32c7Stb 
219354d32c7Stb 	return failed;
220354d32c7Stb }
221354d32c7Stb 
222354d32c7Stb int
main(void)223354d32c7Stb main(void)
224354d32c7Stb {
225354d32c7Stb 	int failed = 0;
226354d32c7Stb 
22713ffeb01Stb 	failed |= test_bio_new_ndef_waf();
228354d32c7Stb 	failed |= test_prefix_leak();
229354d32c7Stb 	failed |= test_infinite_loop();
230354d32c7Stb 
231354d32c7Stb 	return failed;
232354d32c7Stb }
233