]>
Commit | Line | Data |
---|---|---|
c3e4c1f3 RL |
1 | /* |
2 | * Copyright 2020 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 | ||
10 | #include <openssl/core_names.h> | |
11 | #include <openssl/bio.h> | |
12 | #include <openssl/params.h> | |
13 | #include <openssl/provider.h> | |
14 | #include "serializer_local.h" | |
15 | #include "e_os.h" | |
16 | ||
17 | struct deser_process_data_st { | |
18 | OSSL_DESERIALIZER_CTX *ctx; | |
19 | ||
20 | /* Current BIO */ | |
21 | BIO *bio; | |
22 | ||
23 | /* Index of the current deserializer instance to be processed */ | |
24 | size_t current_deser_inst_index; | |
25 | }; | |
26 | ||
27 | static int deser_process(const OSSL_PARAM params[], void *arg); | |
28 | ||
29 | int OSSL_DESERIALIZER_from_bio(OSSL_DESERIALIZER_CTX *ctx, BIO *in) | |
30 | { | |
31 | struct deser_process_data_st data; | |
7524b7b7 | 32 | int ok = 0; |
c3e4c1f3 RL |
33 | |
34 | memset(&data, 0, sizeof(data)); | |
35 | data.ctx = ctx; | |
36 | data.bio = in; | |
37 | ||
7524b7b7 RL |
38 | ok = deser_process(NULL, &data); |
39 | ||
4701f0a9 RL |
40 | /* Clear any internally cached passphrase */ |
41 | if (!ctx->flag_user_passphrase) { | |
42 | OSSL_DESERIALIZER_CTX_set_passphrase(ctx, NULL, 0); | |
43 | ctx->flag_user_passphrase = 0; | |
44 | } | |
7524b7b7 | 45 | return ok; |
c3e4c1f3 RL |
46 | } |
47 | ||
48 | #ifndef OPENSSL_NO_STDIO | |
49 | static BIO *bio_from_file(FILE *fp) | |
50 | { | |
51 | BIO *b; | |
52 | ||
53 | if ((b = BIO_new(BIO_s_file())) == NULL) { | |
54 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_BIO_LIB); | |
55 | return NULL; | |
56 | } | |
57 | BIO_set_fp(b, fp, BIO_NOCLOSE); | |
58 | return b; | |
59 | } | |
60 | ||
61 | int OSSL_DESERIALIZER_from_fp(OSSL_DESERIALIZER_CTX *ctx, FILE *fp) | |
62 | { | |
63 | BIO *b = bio_from_file(fp); | |
64 | int ret = 0; | |
65 | ||
66 | if (b != NULL) | |
67 | ret = OSSL_DESERIALIZER_from_bio(ctx, b); | |
68 | ||
69 | BIO_free(b); | |
70 | return ret; | |
71 | } | |
72 | #endif | |
73 | ||
74 | int OSSL_DESERIALIZER_CTX_set_input_type(OSSL_DESERIALIZER_CTX *ctx, | |
75 | const char *input_type) | |
76 | { | |
77 | if (!ossl_assert(ctx != NULL)) { | |
78 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER); | |
79 | return 0; | |
80 | } | |
81 | ||
82 | /* | |
83 | * NULL is a valid starting input type, and means that the caller leaves | |
84 | * it to code to discover what the starting input type is. | |
85 | */ | |
86 | ctx->start_input_type = input_type; | |
87 | return 1; | |
88 | } | |
89 | ||
90 | int OSSL_DESERIALIZER_CTX_add_deserializer(OSSL_DESERIALIZER_CTX *ctx, | |
91 | OSSL_DESERIALIZER *deser) | |
92 | { | |
93 | OSSL_DESERIALIZER_INSTANCE *deser_inst = NULL; | |
94 | const OSSL_PROVIDER *prov = NULL; | |
95 | OSSL_PARAM params[2]; | |
96 | void *provctx = NULL; | |
97 | ||
98 | if (!ossl_assert(ctx != NULL) || !ossl_assert(deser != NULL)) { | |
99 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER); | |
100 | return 0; | |
101 | } | |
102 | ||
103 | if (deser->get_params == NULL) { | |
104 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, | |
105 | OSSL_DESERIALIZER_R_MISSING_GET_PARAMS); | |
106 | return 0; | |
107 | } | |
108 | ||
109 | if (ctx->deser_insts == NULL | |
110 | && (ctx->deser_insts = | |
111 | sk_OSSL_DESERIALIZER_INSTANCE_new_null()) == NULL) { | |
112 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_MALLOC_FAILURE); | |
113 | return 0; | |
114 | } | |
115 | if ((deser_inst = OPENSSL_zalloc(sizeof(*deser_inst))) == NULL) { | |
116 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_MALLOC_FAILURE); | |
117 | return 0; | |
118 | } | |
119 | if (!OSSL_DESERIALIZER_up_ref(deser)) { | |
120 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_INTERNAL_ERROR); | |
121 | goto err; | |
122 | } | |
123 | deser_inst->deser = deser; | |
124 | ||
125 | prov = OSSL_DESERIALIZER_provider(deser_inst->deser); | |
126 | provctx = OSSL_PROVIDER_get0_provider_ctx(prov); | |
127 | ||
128 | /* Cache the input type for this serializer */ | |
129 | params[0] = | |
130 | OSSL_PARAM_construct_utf8_ptr(OSSL_DESERIALIZER_PARAM_INPUT_TYPE, | |
131 | (char **)&deser_inst->input_type, 0); | |
132 | params[1] = OSSL_PARAM_construct_end(); | |
133 | ||
134 | if (!deser_inst->deser->get_params(params) | |
135 | || !OSSL_PARAM_modified(¶ms[0])) | |
136 | goto err; | |
137 | ||
138 | if ((deser_inst->deserctx = deser_inst->deser->newctx(provctx)) | |
139 | == NULL) | |
140 | goto err; | |
141 | ||
142 | if (sk_OSSL_DESERIALIZER_INSTANCE_push(ctx->deser_insts, deser_inst) <= 0) | |
143 | goto err; | |
144 | ||
145 | return 1; | |
146 | err: | |
147 | if (deser_inst != NULL) { | |
148 | if (deser_inst->deser != NULL) | |
149 | deser_inst->deser->freectx(deser_inst->deserctx); | |
150 | OSSL_DESERIALIZER_free(deser_inst->deser); | |
151 | OPENSSL_free(deser_inst); | |
152 | } | |
153 | return 0; | |
154 | } | |
155 | ||
156 | int OSSL_DESERIALIZER_CTX_add_extra(OSSL_DESERIALIZER_CTX *ctx, | |
157 | OPENSSL_CTX *libctx, const char *propq) | |
158 | { | |
159 | /* | |
160 | * This function goes through existing deserializer methods in | |
161 | * |ctx->deser_insts|, and tries to fetch new deserializers that produce | |
162 | * what the existing ones want as input, and push those newly fetched | |
163 | * deserializers on top of the same stack. | |
164 | * Then it does the same again, but looping over the newly fetched | |
165 | * deserializers, until there are no more serializers to be fetched, or | |
166 | * when we have done this 10 times. | |
167 | * | |
168 | * we do this with sliding windows on the stack by keeping track of indexes | |
169 | * and of the end. | |
170 | * | |
171 | * +----------------+ | |
172 | * | DER to RSA | <--- w_prev_start | |
173 | * +----------------+ | |
174 | * | DER to DSA | | |
175 | * +----------------+ | |
176 | * | DER to DH | | |
177 | * +----------------+ | |
178 | * | PEM to DER | <--- w_prev_end, w_new_start | |
179 | * +----------------+ | |
180 | * <--- w_new_end | |
181 | */ | |
182 | size_t w_prev_start, w_prev_end; /* "previous" deserializers */ | |
183 | size_t w_new_start, w_new_end; /* "new" deserializers */ | |
184 | size_t count = 0; /* Calculates how many were added in each iteration */ | |
185 | size_t depth = 0; /* Counts the number of iterations */ | |
186 | ||
187 | if (!ossl_assert(ctx != NULL)) { | |
188 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER); | |
189 | return 0; | |
190 | } | |
191 | ||
192 | /* | |
193 | * If there is no stack of OSSL_DESERIALIZER_INSTANCE, we have nothing | |
194 | * more to add. That's fine. | |
195 | */ | |
196 | if (ctx->deser_insts == NULL) | |
197 | return 1; | |
198 | ||
199 | w_prev_start = 0; | |
200 | w_prev_end = sk_OSSL_DESERIALIZER_INSTANCE_num(ctx->deser_insts); | |
201 | do { | |
202 | size_t i; | |
203 | ||
204 | w_new_start = w_new_end = w_prev_end; | |
205 | ||
206 | for (i = w_prev_start; i < w_prev_end; i++) { | |
207 | OSSL_DESERIALIZER_INSTANCE *deser_inst = | |
208 | sk_OSSL_DESERIALIZER_INSTANCE_value(ctx->deser_insts, i); | |
209 | const char *name = deser_inst->input_type; | |
210 | OSSL_DESERIALIZER *deser = NULL; | |
211 | ||
212 | /* | |
213 | * If the caller has specified what the initial input should be, | |
214 | * and the deserializer implementation we're looking at has that | |
215 | * input type, there's no point adding on more implementations | |
216 | * on top of this one, so we don't. | |
217 | */ | |
218 | if (ctx->start_input_type != NULL | |
219 | && strcasecmp(ctx->start_input_type, | |
220 | deser_inst->input_type) != 0) | |
221 | continue; | |
222 | ||
223 | ERR_set_mark(); | |
224 | deser = OSSL_DESERIALIZER_fetch(libctx, name, propq); | |
225 | ERR_pop_to_mark(); | |
226 | ||
227 | if (deser != NULL) { | |
228 | size_t j; | |
229 | ||
230 | /* | |
231 | * Check that we don't already have this deserializer in our | |
232 | * stack We only need to check among the newly added ones. | |
233 | */ | |
234 | for (j = w_new_start; j < w_new_end; j++) { | |
235 | OSSL_DESERIALIZER_INSTANCE *check_inst = | |
236 | sk_OSSL_DESERIALIZER_INSTANCE_value(ctx->deser_insts, j); | |
237 | ||
238 | if (deser == check_inst->deser) { | |
239 | /* We found it, so drop the new fetch */ | |
240 | OSSL_DESERIALIZER_free(deser); | |
241 | deser = NULL; | |
242 | break; | |
243 | } | |
244 | } | |
245 | } | |
246 | ||
247 | if (deser == NULL) | |
248 | continue; | |
249 | ||
250 | /* | |
251 | * Apart from keeping w_new_end up to date, We don't care about | |
252 | * errors here. If it doesn't collect, then it doesn't... | |
253 | */ | |
254 | if (OSSL_DESERIALIZER_CTX_add_deserializer(ctx, deser)) /* ref++ */ | |
255 | w_new_end++; | |
256 | OSSL_DESERIALIZER_free(deser); /* ref-- */ | |
257 | } | |
258 | /* How many were added in this iteration */ | |
259 | count = w_new_end - w_new_start; | |
260 | ||
261 | /* Slide the "previous deserializer" windows */ | |
262 | w_prev_start = w_new_start; | |
263 | w_prev_end = w_new_end; | |
264 | ||
265 | depth++; | |
266 | } while (count != 0 && depth <= 10); | |
267 | ||
268 | return 1; | |
269 | } | |
270 | ||
271 | int OSSL_DESERIALIZER_CTX_num_deserializers(OSSL_DESERIALIZER_CTX *ctx) | |
272 | { | |
273 | if (ctx == NULL || ctx->deser_insts == NULL) | |
274 | return 0; | |
275 | return sk_OSSL_DESERIALIZER_INSTANCE_num(ctx->deser_insts); | |
276 | } | |
277 | ||
3c033c5b RL |
278 | int OSSL_DESERIALIZER_CTX_set_construct(OSSL_DESERIALIZER_CTX *ctx, |
279 | OSSL_DESERIALIZER_CONSTRUCT *construct) | |
c3e4c1f3 RL |
280 | { |
281 | if (!ossl_assert(ctx != NULL)) { | |
282 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER); | |
283 | return 0; | |
284 | } | |
3c033c5b | 285 | ctx->construct = construct; |
c3e4c1f3 RL |
286 | return 1; |
287 | } | |
288 | ||
3c033c5b RL |
289 | int OSSL_DESERIALIZER_CTX_set_construct_data(OSSL_DESERIALIZER_CTX *ctx, |
290 | void *construct_data) | |
291 | { | |
292 | if (!ossl_assert(ctx != NULL)) { | |
293 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER); | |
294 | return 0; | |
295 | } | |
296 | ctx->construct_data = construct_data; | |
297 | return 1; | |
298 | } | |
299 | ||
300 | int OSSL_DESERIALIZER_CTX_set_cleanup(OSSL_DESERIALIZER_CTX *ctx, | |
301 | OSSL_DESERIALIZER_CLEANUP *cleanup) | |
302 | { | |
303 | if (!ossl_assert(ctx != NULL)) { | |
304 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER); | |
305 | return 0; | |
306 | } | |
307 | ctx->cleanup = cleanup; | |
308 | return 1; | |
309 | } | |
310 | ||
311 | OSSL_DESERIALIZER_CONSTRUCT * | |
312 | OSSL_DESERIALIZER_CTX_get_construct(OSSL_DESERIALIZER_CTX *ctx) | |
313 | { | |
314 | if (ctx == NULL) | |
315 | return NULL; | |
316 | return ctx->construct; | |
317 | } | |
318 | ||
319 | void *OSSL_DESERIALIZER_CTX_get_construct_data(OSSL_DESERIALIZER_CTX *ctx) | |
320 | { | |
321 | if (ctx == NULL) | |
322 | return NULL; | |
323 | return ctx->construct_data; | |
324 | } | |
325 | ||
326 | OSSL_DESERIALIZER_CLEANUP * | |
327 | OSSL_DESERIALIZER_CTX_get_cleanup(OSSL_DESERIALIZER_CTX *ctx) | |
328 | { | |
329 | if (ctx == NULL) | |
330 | return NULL; | |
331 | return ctx->cleanup; | |
332 | } | |
333 | ||
c3e4c1f3 RL |
334 | int OSSL_DESERIALIZER_export(OSSL_DESERIALIZER_INSTANCE *deser_inst, |
335 | void *reference, size_t reference_sz, | |
336 | OSSL_CALLBACK *export_cb, void *export_cbarg) | |
337 | { | |
338 | if (!(ossl_assert(deser_inst != NULL) | |
339 | && ossl_assert(reference != NULL) | |
340 | && ossl_assert(export_cb != NULL) | |
341 | && ossl_assert(export_cbarg != NULL))) { | |
342 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_PASSED_NULL_PARAMETER); | |
343 | return 0; | |
344 | } | |
345 | ||
346 | return deser_inst->deser->export_object(deser_inst->deserctx, | |
347 | reference, reference_sz, | |
348 | export_cb, export_cbarg); | |
349 | } | |
350 | ||
351 | OSSL_DESERIALIZER *OSSL_DESERIALIZER_INSTANCE_deserializer | |
352 | (OSSL_DESERIALIZER_INSTANCE *deser_inst) | |
353 | { | |
354 | if (deser_inst == NULL) | |
355 | return NULL; | |
356 | return deser_inst->deser; | |
357 | } | |
358 | ||
359 | void *OSSL_DESERIALIZER_INSTANCE_deserializer_ctx | |
360 | (OSSL_DESERIALIZER_INSTANCE *deser_inst) | |
361 | { | |
362 | if (deser_inst == NULL) | |
363 | return NULL; | |
364 | return deser_inst->deserctx; | |
365 | } | |
366 | ||
367 | static int deser_process(const OSSL_PARAM params[], void *arg) | |
368 | { | |
369 | struct deser_process_data_st *data = arg; | |
370 | OSSL_DESERIALIZER_CTX *ctx = data->ctx; | |
371 | OSSL_DESERIALIZER_INSTANCE *deser_inst = NULL; | |
372 | OSSL_DESERIALIZER *deser = NULL; | |
373 | BIO *bio = data->bio; | |
374 | long loc; | |
375 | size_t i; | |
376 | int ok = 0; | |
377 | /* For recursions */ | |
378 | struct deser_process_data_st new_data; | |
379 | ||
380 | memset(&new_data, 0, sizeof(new_data)); | |
381 | new_data.ctx = data->ctx; | |
382 | ||
383 | if (params == NULL) { | |
384 | /* First iteration, where we prepare for what is to come */ | |
385 | ||
386 | data->current_deser_inst_index = | |
387 | OSSL_DESERIALIZER_CTX_num_deserializers(ctx); | |
388 | ||
389 | bio = data->bio; | |
390 | } else { | |
391 | const OSSL_PARAM *p; | |
392 | ||
393 | deser_inst = | |
394 | sk_OSSL_DESERIALIZER_INSTANCE_value(ctx->deser_insts, | |
395 | data->current_deser_inst_index); | |
396 | deser = OSSL_DESERIALIZER_INSTANCE_deserializer(deser_inst); | |
397 | ||
3c033c5b RL |
398 | if (ctx->construct != NULL |
399 | && ctx->construct(deser_inst, params, ctx->construct_data)) { | |
c3e4c1f3 RL |
400 | ok = 1; |
401 | goto end; | |
402 | } | |
403 | ||
3c033c5b | 404 | /* The constructor didn't return success */ |
c3e4c1f3 RL |
405 | |
406 | /* | |
407 | * so we try to use the object we got and feed it to any next | |
408 | * deserializer that will take it. Object references are not | |
409 | * allowed for this. | |
410 | * If this data isn't present, deserialization has failed. | |
411 | */ | |
412 | ||
413 | p = OSSL_PARAM_locate_const(params, OSSL_DESERIALIZER_PARAM_DATA); | |
414 | if (p == NULL || p->data_type != OSSL_PARAM_OCTET_STRING) | |
415 | goto end; | |
416 | new_data.bio = BIO_new_mem_buf(p->data, (int)p->data_size); | |
417 | if (new_data.bio == NULL) | |
418 | goto end; | |
419 | bio = new_data.bio; | |
420 | } | |
421 | ||
422 | /* | |
423 | * If we have no more deserializers to look through at this point, | |
424 | * we failed | |
425 | */ | |
426 | if (data->current_deser_inst_index == 0) | |
427 | goto end; | |
428 | ||
429 | if ((loc = BIO_tell(bio)) < 0) { | |
430 | ERR_raise(ERR_LIB_OSSL_DESERIALIZER, ERR_R_BIO_LIB); | |
431 | goto end; | |
432 | } | |
433 | ||
434 | for (i = data->current_deser_inst_index; i-- > 0;) { | |
435 | OSSL_DESERIALIZER_INSTANCE *new_deser_inst = | |
436 | sk_OSSL_DESERIALIZER_INSTANCE_value(ctx->deser_insts, i); | |
437 | OSSL_DESERIALIZER *new_deser = | |
438 | OSSL_DESERIALIZER_INSTANCE_deserializer(new_deser_inst); | |
439 | ||
440 | /* | |
441 | * If |deser| is NULL, it means we've just started, and the caller | |
442 | * may have specified what it expects the initial input to be. If | |
443 | * that's the case, we do this extra check. | |
444 | */ | |
445 | if (deser == NULL && ctx->start_input_type != NULL | |
790a1b03 RL |
446 | && strcasecmp(ctx->start_input_type, |
447 | new_deser_inst->input_type) != 0) | |
c3e4c1f3 RL |
448 | continue; |
449 | ||
450 | /* | |
451 | * If we have a previous deserializer, we check that the input type | |
452 | * of the next to be used matches the type of this previous one. | |
453 | * deser_inst->input_type is a cache of the parameter "input-type" | |
454 | * value for that deserializer. | |
455 | */ | |
456 | if (deser != NULL | |
457 | && !OSSL_DESERIALIZER_is_a(deser, new_deser_inst->input_type)) | |
458 | continue; | |
459 | ||
1dbf4537 RL |
460 | /* |
461 | * Checking the return value of BIO_reset() or BIO_seek() is unsafe. | |
462 | * Furthermore, BIO_reset() is unsafe to use if the source BIO happens | |
463 | * to be a BIO_s_mem(), because the earlier BIO_tell() gives us zero | |
464 | * no matter where we are in the underlying buffer we're reading from. | |
465 | * | |
466 | * So, we simply do a BIO_seek(), and use BIO_tell() that we're back | |
467 | * at the same position. This is a best effort attempt, but BIO_seek() | |
468 | * and BIO_tell() should come as a pair... | |
469 | */ | |
470 | (void)BIO_seek(bio, loc); | |
471 | if (BIO_tell(bio) != loc) | |
472 | goto end; | |
c3e4c1f3 RL |
473 | |
474 | /* Recurse */ | |
475 | new_data.current_deser_inst_index = i; | |
476 | ok = new_deser->deserialize(new_deser_inst->deserctx, | |
477 | (OSSL_CORE_BIO *)bio, | |
478 | deser_process, &new_data, | |
4701f0a9 | 479 | ctx->passphrase_cb, |
c3e4c1f3 RL |
480 | new_data.ctx); |
481 | if (ok) | |
482 | break; | |
483 | } | |
484 | ||
485 | end: | |
486 | BIO_free(new_data.bio); | |
487 | return ok; | |
488 | } |