1 /* $OpenBSD: bio_asn1.c,v 1.17 2022/01/14 08:40:57 tb Exp $ */
2 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
3  * project.
4  */
5 /* ====================================================================
6  * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. All advertising materials mentioning features or use of this
21  *    software must display the following acknowledgment:
22  *    "This product includes software developed by the OpenSSL Project
23  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24  *
25  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26  *    endorse or promote products derived from this software without
27  *    prior written permission. For written permission, please contact
28  *    licensing@OpenSSL.org.
29  *
30  * 5. Products derived from this software may not be called "OpenSSL"
31  *    nor may "OpenSSL" appear in their names without prior written
32  *    permission of the OpenSSL Project.
33  *
34  * 6. Redistributions of any form whatsoever must retain the following
35  *    acknowledgment:
36  *    "This product includes software developed by the OpenSSL Project
37  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38  *
39  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50  * OF THE POSSIBILITY OF SUCH DAMAGE.
51  * ====================================================================
52  *
53  * This product includes cryptographic software written by Eric Young
54  * (eay@cryptsoft.com).  This product includes software written by Tim
55  * Hudson (tjh@cryptsoft.com).
56  *
57  */
58 
59 /* Experimental ASN1 BIO. When written through the data is converted
60  * to an ASN1 string type: default is OCTET STRING. Additional functions
61  * can be provided to add prefix and suffix data.
62  */
63 
64 #include <stdlib.h>
65 #include <string.h>
66 
67 #include <openssl/bio.h>
68 #include <openssl/asn1.h>
69 
70 #include "bio_local.h"
71 
72 /* Must be large enough for biggest tag+length */
73 #define DEFAULT_ASN1_BUF_SIZE 20
74 
75 typedef enum {
76 	ASN1_STATE_START,
77 	ASN1_STATE_PRE_COPY,
78 	ASN1_STATE_HEADER,
79 	ASN1_STATE_HEADER_COPY,
80 	ASN1_STATE_DATA_COPY,
81 	ASN1_STATE_POST_COPY,
82 	ASN1_STATE_DONE
83 } asn1_bio_state_t;
84 
85 typedef struct BIO_ASN1_EX_FUNCS_st {
86 	asn1_ps_func	*ex_func;
87 	asn1_ps_func	*ex_free_func;
88 } BIO_ASN1_EX_FUNCS;
89 
90 typedef struct BIO_ASN1_BUF_CTX_t {
91 	/* Internal state */
92 	asn1_bio_state_t state;
93 	/* Internal buffer */
94 	unsigned char *buf;
95 	/* Size of buffer */
96 	int bufsize;
97 	/* Current position in buffer */
98 	int bufpos;
99 	/* Current buffer length */
100 	int buflen;
101 	/* Amount of data to copy */
102 	int copylen;
103 	/* Class and tag to use */
104 	int asn1_class, asn1_tag;
105 	asn1_ps_func *prefix, *prefix_free, *suffix, *suffix_free;
106 	/* Extra buffer for prefix and suffix data */
107 	unsigned char *ex_buf;
108 	int ex_len;
109 	int ex_pos;
110 	void *ex_arg;
111 } BIO_ASN1_BUF_CTX;
112 
113 
114 static int asn1_bio_write(BIO *h, const char *buf, int num);
115 static int asn1_bio_read(BIO *h, char *buf, int size);
116 static int asn1_bio_puts(BIO *h, const char *str);
117 static int asn1_bio_gets(BIO *h, char *str, int size);
118 static long asn1_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2);
119 static int asn1_bio_new(BIO *h);
120 static int asn1_bio_free(BIO *data);
121 static long asn1_bio_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp);
122 
123 static int asn1_bio_flush_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
124     asn1_ps_func *cleanup, asn1_bio_state_t next);
125 static int asn1_bio_setup_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx,
126     asn1_ps_func *setup, asn1_bio_state_t ex_state,
127     asn1_bio_state_t other_state);
128 
129 static const BIO_METHOD methods_asn1 = {
130 	.type = BIO_TYPE_ASN1,
131 	.name = "asn1",
132 	.bwrite = asn1_bio_write,
133 	.bread = asn1_bio_read,
134 	.bputs = asn1_bio_puts,
135 	.bgets = asn1_bio_gets,
136 	.ctrl = asn1_bio_ctrl,
137 	.create = asn1_bio_new,
138 	.destroy = asn1_bio_free,
139 	.callback_ctrl = asn1_bio_callback_ctrl
140 };
141 
142 const BIO_METHOD *
143 BIO_f_asn1(void)
144 {
145 	return (&methods_asn1);
146 }
147 
148 static int
149 asn1_bio_new(BIO *b)
150 {
151 	BIO_ASN1_BUF_CTX *ctx;
152 
153 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
154 		return 0;
155 
156 	if ((ctx->buf = malloc(DEFAULT_ASN1_BUF_SIZE)) == NULL) {
157 		free(ctx);
158 		return 0;
159 	}
160 	ctx->bufsize = DEFAULT_ASN1_BUF_SIZE;
161 	ctx->asn1_class = V_ASN1_UNIVERSAL;
162 	ctx->asn1_tag = V_ASN1_OCTET_STRING;
163 	ctx->state = ASN1_STATE_START;
164 
165 	b->init = 1;
166 	b->ptr = (char *)ctx;
167 	b->flags = 0;
168 
169 	return 1;
170 }
171 
172 static int
173 asn1_bio_free(BIO *b)
174 {
175 	BIO_ASN1_BUF_CTX *ctx;
176 
177 	ctx = (BIO_ASN1_BUF_CTX *) b->ptr;
178 	if (ctx == NULL)
179 		return 0;
180 	free(ctx->buf);
181 	free(ctx);
182 	b->init = 0;
183 	b->ptr = NULL;
184 	b->flags = 0;
185 	return 1;
186 }
187 
188 static int
189 asn1_bio_write(BIO *b, const char *in , int inl)
190 {
191 	BIO_ASN1_BUF_CTX *ctx;
192 	int wrmax, wrlen, ret, buflen;
193 	unsigned char *p;
194 
195 	if (!in || (inl < 0) || (b->next_bio == NULL))
196 		return 0;
197 	ctx = (BIO_ASN1_BUF_CTX *) b->ptr;
198 	if (ctx == NULL)
199 		return 0;
200 
201 	wrlen = 0;
202 	ret = -1;
203 
204 	for (;;) {
205 		switch (ctx->state) {
206 
207 			/* Setup prefix data, call it */
208 		case ASN1_STATE_START:
209 			if (!asn1_bio_setup_ex(b, ctx, ctx->prefix,
210 				    ASN1_STATE_PRE_COPY, ASN1_STATE_HEADER))
211 				return 0;
212 			break;
213 
214 			/* Copy any pre data first */
215 		case ASN1_STATE_PRE_COPY:
216 			ret = asn1_bio_flush_ex(b, ctx, ctx->prefix_free,
217 			    ASN1_STATE_HEADER);
218 			if (ret <= 0)
219 				goto done;
220 			break;
221 
222 		case ASN1_STATE_HEADER:
223 			buflen = ASN1_object_size(0, inl, ctx->asn1_tag) - inl;
224 			if (buflen <= 0 || buflen > ctx->bufsize)
225 				return -1;
226 			ctx->buflen = buflen;
227 			p = ctx->buf;
228 			ASN1_put_object(&p, 0, inl,
229 			    ctx->asn1_tag, ctx->asn1_class);
230 			ctx->copylen = inl;
231 			ctx->state = ASN1_STATE_HEADER_COPY;
232 			break;
233 
234 		case ASN1_STATE_HEADER_COPY:
235 			ret = BIO_write(b->next_bio,
236 			    ctx->buf + ctx->bufpos, ctx->buflen);
237 			if (ret <= 0)
238 				goto done;
239 
240 			ctx->buflen -= ret;
241 			if (ctx->buflen)
242 				ctx->bufpos += ret;
243 			else {
244 				ctx->bufpos = 0;
245 				ctx->state = ASN1_STATE_DATA_COPY;
246 			}
247 			break;
248 
249 		case ASN1_STATE_DATA_COPY:
250 
251 			if (inl > ctx->copylen)
252 				wrmax = ctx->copylen;
253 			else
254 				wrmax = inl;
255 			ret = BIO_write(b->next_bio, in, wrmax);
256 			if (ret <= 0)
257 				break;
258 			wrlen += ret;
259 			ctx->copylen -= ret;
260 			in += ret;
261 			inl -= ret;
262 
263 			if (ctx->copylen == 0)
264 				ctx->state = ASN1_STATE_HEADER;
265 			if (inl == 0)
266 				goto done;
267 			break;
268 
269 		default:
270 			BIO_clear_retry_flags(b);
271 			return 0;
272 		}
273 
274 	}
275 
276  done:
277 	BIO_clear_retry_flags(b);
278 	BIO_copy_next_retry(b);
279 
280 	return (wrlen > 0) ? wrlen : ret;
281 }
282 
283 static int
284 asn1_bio_flush_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx, asn1_ps_func *cleanup,
285     asn1_bio_state_t next)
286 {
287 	int ret;
288 
289 	if (ctx->ex_len <= 0)
290 		return 1;
291 	for (;;) {
292 		ret = BIO_write(b->next_bio, ctx->ex_buf + ctx->ex_pos,
293 		    ctx->ex_len);
294 		if (ret <= 0)
295 			break;
296 		ctx->ex_len -= ret;
297 		if (ctx->ex_len > 0)
298 			ctx->ex_pos += ret;
299 		else {
300 			if (cleanup)
301 				cleanup(b, &ctx->ex_buf, &ctx->ex_len,
302 				    &ctx->ex_arg);
303 			ctx->state = next;
304 			ctx->ex_pos = 0;
305 			break;
306 		}
307 	}
308 	return ret;
309 }
310 
311 static int
312 asn1_bio_setup_ex(BIO *b, BIO_ASN1_BUF_CTX *ctx, asn1_ps_func *setup,
313     asn1_bio_state_t ex_state, asn1_bio_state_t other_state)
314 {
315 	if (setup && !setup(b, &ctx->ex_buf, &ctx->ex_len, &ctx->ex_arg)) {
316 		BIO_clear_retry_flags(b);
317 		return 0;
318 	}
319 	if (ctx->ex_len > 0)
320 		ctx->state = ex_state;
321 	else
322 		ctx->state = other_state;
323 	return 1;
324 }
325 
326 static int
327 asn1_bio_read(BIO *b, char *in , int inl)
328 {
329 	if (!b->next_bio)
330 		return 0;
331 	return BIO_read(b->next_bio, in , inl);
332 }
333 
334 static int
335 asn1_bio_puts(BIO *b, const char *str)
336 {
337 	return asn1_bio_write(b, str, strlen(str));
338 }
339 
340 static int
341 asn1_bio_gets(BIO *b, char *str, int size)
342 {
343 	if (!b->next_bio)
344 		return 0;
345 	return BIO_gets(b->next_bio, str , size);
346 }
347 
348 static long
349 asn1_bio_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
350 {
351 	if (b->next_bio == NULL)
352 		return (0);
353 	return BIO_callback_ctrl(b->next_bio, cmd, fp);
354 }
355 
356 static long
357 asn1_bio_ctrl(BIO *b, int cmd, long arg1, void *arg2)
358 {
359 	BIO_ASN1_BUF_CTX *ctx;
360 	BIO_ASN1_EX_FUNCS *ex_func;
361 	long ret = 1;
362 
363 	ctx = (BIO_ASN1_BUF_CTX *) b->ptr;
364 	if (ctx == NULL)
365 		return 0;
366 	switch (cmd) {
367 
368 	case BIO_C_SET_PREFIX:
369 		ex_func = arg2;
370 		ctx->prefix = ex_func->ex_func;
371 		ctx->prefix_free = ex_func->ex_free_func;
372 		break;
373 
374 	case BIO_C_GET_PREFIX:
375 		ex_func = arg2;
376 		ex_func->ex_func = ctx->prefix;
377 		ex_func->ex_free_func = ctx->prefix_free;
378 		break;
379 
380 	case BIO_C_SET_SUFFIX:
381 		ex_func = arg2;
382 		ctx->suffix = ex_func->ex_func;
383 		ctx->suffix_free = ex_func->ex_free_func;
384 		break;
385 
386 	case BIO_C_GET_SUFFIX:
387 		ex_func = arg2;
388 		ex_func->ex_func = ctx->suffix;
389 		ex_func->ex_free_func = ctx->suffix_free;
390 		break;
391 
392 	case BIO_C_SET_EX_ARG:
393 		ctx->ex_arg = arg2;
394 		break;
395 
396 	case BIO_C_GET_EX_ARG:
397 		*(void **)arg2 = ctx->ex_arg;
398 		break;
399 
400 	case BIO_CTRL_FLUSH:
401 		if (!b->next_bio)
402 			return 0;
403 
404 		/* Call post function if possible */
405 		if (ctx->state == ASN1_STATE_HEADER) {
406 			if (!asn1_bio_setup_ex(b, ctx, ctx->suffix,
407 			    ASN1_STATE_POST_COPY, ASN1_STATE_DONE))
408 				return 0;
409 		}
410 
411 		if (ctx->state == ASN1_STATE_POST_COPY) {
412 			ret = asn1_bio_flush_ex(b, ctx, ctx->suffix_free,
413 			    ASN1_STATE_DONE);
414 			if (ret <= 0)
415 				return ret;
416 		}
417 
418 		if (ctx->state == ASN1_STATE_DONE)
419 			return BIO_ctrl(b->next_bio, cmd, arg1, arg2);
420 		else {
421 			BIO_clear_retry_flags(b);
422 			return 0;
423 		}
424 		break;
425 
426 
427 	default:
428 		if (!b->next_bio)
429 			return 0;
430 		return BIO_ctrl(b->next_bio, cmd, arg1, arg2);
431 
432 	}
433 
434 	return ret;
435 }
436 
437 static int
438 asn1_bio_set_ex(BIO *b, int cmd, asn1_ps_func *ex_func, asn1_ps_func
439     *ex_free_func)
440 {
441 	BIO_ASN1_EX_FUNCS extmp;
442 
443 	extmp.ex_func = ex_func;
444 	extmp.ex_free_func = ex_free_func;
445 	return BIO_ctrl(b, cmd, 0, &extmp);
446 }
447 
448 static int
449 asn1_bio_get_ex(BIO *b, int cmd, asn1_ps_func **ex_func,
450     asn1_ps_func **ex_free_func)
451 {
452 	BIO_ASN1_EX_FUNCS extmp;
453 	int ret;
454 
455 	ret = BIO_ctrl(b, cmd, 0, &extmp);
456 	if (ret > 0) {
457 		*ex_func = extmp.ex_func;
458 		*ex_free_func = extmp.ex_free_func;
459 	}
460 	return ret;
461 }
462 
463 int
464 BIO_asn1_set_prefix(BIO *b, asn1_ps_func *prefix, asn1_ps_func *prefix_free)
465 {
466 	return asn1_bio_set_ex(b, BIO_C_SET_PREFIX, prefix, prefix_free);
467 }
468 
469 int
470 BIO_asn1_get_prefix(BIO *b, asn1_ps_func **pprefix, asn1_ps_func **pprefix_free)
471 {
472 	return asn1_bio_get_ex(b, BIO_C_GET_PREFIX, pprefix, pprefix_free);
473 }
474 
475 int
476 BIO_asn1_set_suffix(BIO *b, asn1_ps_func *suffix, asn1_ps_func *suffix_free)
477 {
478 	return asn1_bio_set_ex(b, BIO_C_SET_SUFFIX, suffix, suffix_free);
479 }
480 
481 int
482 BIO_asn1_get_suffix(BIO *b, asn1_ps_func **psuffix, asn1_ps_func **psuffix_free)
483 {
484 	return asn1_bio_get_ex(b, BIO_C_GET_SUFFIX, psuffix, psuffix_free);
485 }
486