mirror of
https://github.com/openssl/openssl.git
synced 2025-03-01 19:28:10 +08:00
The passed in reference of a ref-counted object is free'd by d2i functions in the error handling. However if it is not the last reference, the in/out reference variable is not set to null here. This makes it impossible for the caller to handle the error correctly, because there are numerous cases where the passed in reference is free'd and set to null, while in other cases, where the passed in reference is not free'd, the reference is left untouched. Therefore the passed in reference must be set to NULL even when it was not the last reference. Fixes #23713 Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22809)
213 lines
5.6 KiB
C
213 lines
5.6 KiB
C
/*
|
|
* Copyright 2000-2021 The OpenSSL Project Authors. All Rights Reserved.
|
|
*
|
|
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
|
* this file except in compliance with the License. You can obtain a copy
|
|
* in the file LICENSE in the source distribution or at
|
|
* https://www.openssl.org/source/license.html
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <openssl/asn1.h>
|
|
#include <openssl/asn1t.h>
|
|
#include <openssl/objects.h>
|
|
#include "asn1_local.h"
|
|
|
|
/* Free up an ASN1 structure */
|
|
|
|
void ASN1_item_free(ASN1_VALUE *val, const ASN1_ITEM *it)
|
|
{
|
|
ossl_asn1_item_embed_free(&val, it, 0);
|
|
}
|
|
|
|
void ASN1_item_ex_free(ASN1_VALUE **pval, const ASN1_ITEM *it)
|
|
{
|
|
ossl_asn1_item_embed_free(pval, it, 0);
|
|
}
|
|
|
|
void ossl_asn1_item_embed_free(ASN1_VALUE **pval, const ASN1_ITEM *it, int embed)
|
|
{
|
|
const ASN1_TEMPLATE *tt = NULL, *seqtt;
|
|
const ASN1_EXTERN_FUNCS *ef;
|
|
const ASN1_AUX *aux = it->funcs;
|
|
ASN1_aux_cb *asn1_cb;
|
|
int i;
|
|
|
|
if (pval == NULL)
|
|
return;
|
|
if ((it->itype != ASN1_ITYPE_PRIMITIVE) && *pval == NULL)
|
|
return;
|
|
if (aux && aux->asn1_cb)
|
|
asn1_cb = aux->asn1_cb;
|
|
else
|
|
asn1_cb = 0;
|
|
|
|
switch (it->itype) {
|
|
|
|
case ASN1_ITYPE_PRIMITIVE:
|
|
if (it->templates)
|
|
ossl_asn1_template_free(pval, it->templates);
|
|
else
|
|
ossl_asn1_primitive_free(pval, it, embed);
|
|
break;
|
|
|
|
case ASN1_ITYPE_MSTRING:
|
|
ossl_asn1_primitive_free(pval, it, embed);
|
|
break;
|
|
|
|
case ASN1_ITYPE_CHOICE:
|
|
if (asn1_cb) {
|
|
i = asn1_cb(ASN1_OP_FREE_PRE, pval, it, NULL);
|
|
if (i == 2)
|
|
return;
|
|
}
|
|
i = ossl_asn1_get_choice_selector(pval, it);
|
|
if ((i >= 0) && (i < it->tcount)) {
|
|
ASN1_VALUE **pchval;
|
|
|
|
tt = it->templates + i;
|
|
pchval = ossl_asn1_get_field_ptr(pval, tt);
|
|
ossl_asn1_template_free(pchval, tt);
|
|
}
|
|
if (asn1_cb)
|
|
asn1_cb(ASN1_OP_FREE_POST, pval, it, NULL);
|
|
if (embed == 0) {
|
|
OPENSSL_free(*pval);
|
|
*pval = NULL;
|
|
}
|
|
break;
|
|
|
|
case ASN1_ITYPE_EXTERN:
|
|
ef = it->funcs;
|
|
if (ef && ef->asn1_ex_free)
|
|
ef->asn1_ex_free(pval, it);
|
|
break;
|
|
|
|
case ASN1_ITYPE_NDEF_SEQUENCE:
|
|
case ASN1_ITYPE_SEQUENCE:
|
|
if (ossl_asn1_do_lock(pval, -1, it) != 0) {
|
|
/* if error or ref-counter > 0 */
|
|
OPENSSL_assert(embed == 0);
|
|
*pval = NULL;
|
|
return;
|
|
}
|
|
if (asn1_cb) {
|
|
i = asn1_cb(ASN1_OP_FREE_PRE, pval, it, NULL);
|
|
if (i == 2)
|
|
return;
|
|
}
|
|
ossl_asn1_enc_free(pval, it);
|
|
/*
|
|
* If we free up as normal we will invalidate any ANY DEFINED BY
|
|
* field and we won't be able to determine the type of the field it
|
|
* defines. So free up in reverse order.
|
|
*/
|
|
tt = it->templates + it->tcount;
|
|
for (i = 0; i < it->tcount; i++) {
|
|
ASN1_VALUE **pseqval;
|
|
|
|
tt--;
|
|
seqtt = ossl_asn1_do_adb(*pval, tt, 0);
|
|
if (!seqtt)
|
|
continue;
|
|
pseqval = ossl_asn1_get_field_ptr(pval, seqtt);
|
|
ossl_asn1_template_free(pseqval, seqtt);
|
|
}
|
|
if (asn1_cb)
|
|
asn1_cb(ASN1_OP_FREE_POST, pval, it, NULL);
|
|
if (embed == 0) {
|
|
OPENSSL_free(*pval);
|
|
*pval = NULL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ossl_asn1_template_free(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt)
|
|
{
|
|
int embed = tt->flags & ASN1_TFLG_EMBED;
|
|
ASN1_VALUE *tval;
|
|
if (embed) {
|
|
tval = (ASN1_VALUE *)pval;
|
|
pval = &tval;
|
|
}
|
|
if (tt->flags & ASN1_TFLG_SK_MASK) {
|
|
STACK_OF(ASN1_VALUE) *sk = (STACK_OF(ASN1_VALUE) *)*pval;
|
|
int i;
|
|
|
|
for (i = 0; i < sk_ASN1_VALUE_num(sk); i++) {
|
|
ASN1_VALUE *vtmp = sk_ASN1_VALUE_value(sk, i);
|
|
|
|
ossl_asn1_item_embed_free(&vtmp, ASN1_ITEM_ptr(tt->item), embed);
|
|
}
|
|
sk_ASN1_VALUE_free(sk);
|
|
*pval = NULL;
|
|
} else {
|
|
ossl_asn1_item_embed_free(pval, ASN1_ITEM_ptr(tt->item), embed);
|
|
}
|
|
}
|
|
|
|
void ossl_asn1_primitive_free(ASN1_VALUE **pval, const ASN1_ITEM *it, int embed)
|
|
{
|
|
int utype;
|
|
|
|
/* Special case: if 'it' is a primitive with a free_func, use that. */
|
|
if (it) {
|
|
const ASN1_PRIMITIVE_FUNCS *pf = it->funcs;
|
|
|
|
if (embed) {
|
|
if (pf && pf->prim_clear) {
|
|
pf->prim_clear(pval, it);
|
|
return;
|
|
}
|
|
} else if (pf && pf->prim_free) {
|
|
pf->prim_free(pval, it);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Special case: if 'it' is NULL, free contents of ASN1_TYPE */
|
|
if (!it) {
|
|
ASN1_TYPE *typ = (ASN1_TYPE *)*pval;
|
|
|
|
utype = typ->type;
|
|
pval = &typ->value.asn1_value;
|
|
if (*pval == NULL)
|
|
return;
|
|
} else if (it->itype == ASN1_ITYPE_MSTRING) {
|
|
utype = -1;
|
|
if (*pval == NULL)
|
|
return;
|
|
} else {
|
|
utype = it->utype;
|
|
if ((utype != V_ASN1_BOOLEAN) && *pval == NULL)
|
|
return;
|
|
}
|
|
|
|
switch (utype) {
|
|
case V_ASN1_OBJECT:
|
|
ASN1_OBJECT_free((ASN1_OBJECT *)*pval);
|
|
break;
|
|
|
|
case V_ASN1_BOOLEAN:
|
|
if (it)
|
|
*(ASN1_BOOLEAN *)pval = it->size;
|
|
else
|
|
*(ASN1_BOOLEAN *)pval = -1;
|
|
return;
|
|
|
|
case V_ASN1_NULL:
|
|
break;
|
|
|
|
case V_ASN1_ANY:
|
|
ossl_asn1_primitive_free(pval, NULL, 0);
|
|
OPENSSL_free(*pval);
|
|
break;
|
|
|
|
default:
|
|
ossl_asn1_string_embed_free((ASN1_STRING *)*pval, embed);
|
|
break;
|
|
}
|
|
*pval = NULL;
|
|
}
|