X509_EXTENSION *OCSP_REQUEST_delete_ext(OCSP_REQUEST *x, int loc)
{
- return X509v3_delete_ext(x->tbsRequest.requestExtensions, loc);
+ return X509v3_delete_extension(&x->tbsRequest.requestExtensions, loc);
}
void *OCSP_REQUEST_get1_ext_d2i(OCSP_REQUEST *x, int nid, int *crit, int *idx)
X509_EXTENSION *OCSP_ONEREQ_delete_ext(OCSP_ONEREQ *x, int loc)
{
- return X509v3_delete_ext(x->singleRequestExtensions, loc);
+ return X509v3_delete_extension(&x->singleRequestExtensions, loc);
}
void *OCSP_ONEREQ_get1_ext_d2i(OCSP_ONEREQ *x, int nid, int *crit, int *idx)
X509_EXTENSION *OCSP_BASICRESP_delete_ext(OCSP_BASICRESP *x, int loc)
{
- return X509v3_delete_ext(x->tbsResponseData.responseExtensions, loc);
+ return X509v3_delete_extension(&x->tbsResponseData.responseExtensions, loc);
}
void *OCSP_BASICRESP_get1_ext_d2i(OCSP_BASICRESP *x, int nid, int *crit,
X509_EXTENSION *OCSP_SINGLERESP_delete_ext(OCSP_SINGLERESP *x, int loc)
{
- return X509v3_delete_ext(x->singleExtensions, loc);
+ return X509v3_delete_extension(&x->singleExtensions, loc);
}
void *OCSP_SINGLERESP_get1_ext_d2i(OCSP_SINGLERESP *x, int nid, int *crit,
X509_EXTENSION *TS_REQ_delete_ext(TS_REQ *a, int loc)
{
- return X509v3_delete_ext(a->extensions, loc);
+ return X509v3_delete_extension(&a->extensions, loc);
}
int TS_REQ_add_ext(TS_REQ *a, X509_EXTENSION *ex, int loc)
X509_EXTENSION *TS_TST_INFO_delete_ext(TS_TST_INFO *a, int loc)
{
- return X509v3_delete_ext(a->extensions, loc);
+ return X509v3_delete_extension(&a->extensions, loc);
}
int TS_TST_INFO_add_ext(TS_TST_INFO *a, X509_EXTENSION *ex, int loc)
}
X509_EXTENSION_free(ext);
}
+ if (sk != NULL && sk_X509_EXTENSION_num(*sk) == 0) {
+ sk_X509_EXTENSION_free(*sk);
+ *sk = NULL;
+ }
return 1;
}
X509 *cert)
{
STACK_OF(X509_EXTENSION) **sk = NULL;
+
if (cert != NULL)
sk = &cert->cert_info.extensions;
return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk);
update_req_extensions(X509_REQ *req, int *pnid, STACK_OF(X509_EXTENSION) *exts)
{
unsigned char *ext = NULL;
- int ret = 0, loc = -1, extlen;
+ int ret = 0, loc = -1, extlen = 0;
if (pnid == NULL || *pnid == NID_undef)
if ((pnid = X509_REQ_get_extension_nids()) == NULL)
return 0;
loc = X509at_get_attr_by_NID(req->req_info.attributes, *pnid, -1);
- extlen = ASN1_item_i2d((const ASN1_VALUE *)exts,
- &ext, ASN1_ITEM_rptr(X509_EXTENSIONS));
- if (extlen <= 0)
- return ret;
+ if (exts != NULL) {
+ extlen = ASN1_item_i2d((const ASN1_VALUE *)exts,
+ &ext, ASN1_ITEM_rptr(X509_EXTENSIONS));
+ if (extlen <= 0)
+ return ret;
+ }
if (loc != -1) {
X509_ATTRIBUTE *att = X509at_delete_attr(req->req_info.attributes, loc);
ret = X509V3_EXT_add_nconf_sk(conf, ctx, section, &exts);
/* Replace original extension list (stack) with updated stack */
- if (ret && req != NULL && exts != NULL)
+ if (ret && req != NULL)
ret = update_req_extensions(req, pnid, exts);
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
return ret;
{
X509_EXTENSION *ret;
- if (x->cert_info.extensions == NULL)
- return NULL;
- if ((ret = delete_ext(&x->cert_info.extensions, loc)) != NULL)
+ ret = X509v3_delete_extension(&x->cert_info.extensions, loc);
+ if (ret != NULL)
x->cert_info.enc.modified = 1;
return ret;
}
int X509_add_ext(X509 *x, const X509_EXTENSION *ex, int loc)
{
- STACK_OF(X509_EXTENSION) *exts = x->cert_info.extensions;
+ STACK_OF(X509_EXTENSION) **exts = &x->cert_info.extensions;
- if (X509v3_add_ext(&exts, ex, loc) == NULL)
+ /* x->cert_info.extensions might initially be NULL */
+ if (X509v3_add_ext(exts, ex, loc) == NULL)
return 0;
/*
- * A duplicate empty SKID/AKID extension can displace a prior non-empty
- * one, but is then not itself added, so, somewhat counter-intutively, the
- * the extension list can become empty after an "add", in which case we must
- * drop the extension stack entirely, setting it to NULL. The extensions
- * list is either non-empty or absent.
+ * An ignored "empty" SKID or AKID extension will appear to be successfully
+ * added, even though nothing is pushed onto the resulting stack. However,
+ * if the stack was initially NULL or empty, it will now be non-NULL, but
+ * empty, deallocate and make it NULL in that case.
*/
- if (sk_X509_EXTENSION_num(exts) != 0) {
- x->cert_info.extensions = exts;
- } else {
- sk_X509_EXTENSION_free(exts);
- sk_X509_EXTENSION_pop_free(x->cert_info.extensions, X509_EXTENSION_free);
- x->cert_info.extensions = NULL;
+ if (sk_X509_EXTENSION_num(*exts) == 0) {
+ sk_X509_EXTENSION_free(*exts);
+ *exts = NULL;
}
x->cert_info.enc.modified = 1;
return 1;
return ret;
}
+X509_EXTENSION *X509v3_delete_extension(STACK_OF(X509_EXTENSION) **x, int loc)
+{
+ X509_EXTENSION *ext;
+
+ if (x == NULL)
+ return NULL;
+
+ /* Set extensions to NULL when last element dropped */
+ if ((ext = X509v3_delete_ext(*x, loc)) != NULL
+ && sk_X509_EXTENSION_num(*x) == 0) {
+ sk_X509_EXTENSION_free(*x);
+ *x = NULL;
+ }
+ return ext;
+}
+
STACK_OF(X509_EXTENSION) *X509v3_add_ext(STACK_OF(X509_EXTENSION) **x,
const X509_EXTENSION *ex, int loc)
{
X509v3_get_ext_count, X509v3_get_ext, X509v3_get_ext_by_NID,
X509v3_get_ext_by_OBJ, X509v3_get_ext_by_critical, X509v3_delete_ext,
-X509v3_add_ext, X509v3_add_extensions, X509_get_ext_count, X509_get_ext,
-X509_get_ext_by_NID, X509_get_ext_by_OBJ, X509_get_ext_by_critical,
-X509_delete_ext, X509_add_ext, X509_CRL_get_ext_count, X509_CRL_get_ext,
-X509_CRL_get_ext_by_NID, X509_CRL_get_ext_by_OBJ, X509_CRL_get_ext_by_critical,
-X509_CRL_delete_ext, X509_CRL_add_ext, X509_REVOKED_get_ext_count,
-X509_REVOKED_get_ext, X509_REVOKED_get_ext_by_NID, X509_REVOKED_get_ext_by_OBJ,
-X509_REVOKED_get_ext_by_critical, X509_REVOKED_delete_ext,
-X509_REVOKED_add_ext - extension stack utility functions
+X509v3_delete_extension, X509v3_add_ext, X509v3_add_extensions,
+X509_get_ext_count, X509_get_ext, X509_get_ext_by_NID, X509_get_ext_by_OBJ,
+X509_get_ext_by_critical, X509_delete_ext, X509_add_ext,
+X509_CRL_get_ext_count, X509_CRL_get_ext, X509_CRL_get_ext_by_NID,
+X509_CRL_get_ext_by_OBJ, X509_CRL_get_ext_by_critical, X509_CRL_delete_ext,
+X509_CRL_add_ext, X509_REVOKED_get_ext_count, X509_REVOKED_get_ext,
+X509_REVOKED_get_ext_by_NID, X509_REVOKED_get_ext_by_OBJ,
+X509_REVOKED_get_ext_by_critical, X509_REVOKED_delete_ext, X509_REVOKED_add_ext
+- extension stack utility functions
=head1 SYNOPSIS
int X509v3_get_ext_by_critical(const STACK_OF(X509_EXTENSION) *x,
int crit, int lastpos);
X509_EXTENSION *X509v3_delete_ext(STACK_OF(X509_EXTENSION) *x, int loc);
+ X509_EXTENSION *X509v3_delete_extension(STACK_OF(X509_EXTENSION) **x, int loc);
STACK_OF(X509_EXTENSION) *X509v3_add_ext(STACK_OF(X509_EXTENSION) **x,
X509_EXTENSION *ex, int loc);
STACK_OF(X509_EXTENSION)
The deleted extension is returned and must be freed by the caller.
If I<loc> is an invalid index value, NULL is returned.
+X509v3_delete_extension() extends X509v3_delete_ext() by deallocating the
+extension stack I<*x> if it becomes empty, and in that case also setting I<*x>
+to NULL.
+This is a convenience wrapper for cases in which extensions are optional and
+should be omitted if the stack becomes empty.
+
X509v3_add_ext() inserts extension I<ex> to STACK I<*x> at position I<loc>.
If I<loc> is -1, the new extension is added to the end.
A new STACK is allocated if I<*x> is NULL.
X509v3_get_ext_count() returns the extension count or 0 for failure.
-X509v3_get_ext(), X509v3_delete_ext() and X509_delete_ext() return an
-B<X509_EXTENSION> structure or NULL if an error occurs.
+X509v3_get_ext(), X509v3_delete_ext(), X509v3_delete_extension() and
+X509_delete_ext() return an B<X509_EXTENSION> structure or NULL if an error
+occurs.
X509v3_get_ext_by_OBJ() and X509v3_get_ext_by_critical() return
the extension index or -1 if an error occurs.
X509v3_add_extensions() was added in OpenSSL 3.4.
+X509v3_delete_extension() was added in OpenSSL 4.0.
+
=head1 COPYRIGHT
Copyright 2015-2024 The OpenSSL Project Authors. All Rights Reserved.
int crit, int lastpos);
const X509_EXTENSION *X509v3_get_ext(const STACK_OF(X509_EXTENSION) *x, int loc);
X509_EXTENSION *X509v3_delete_ext(STACK_OF(X509_EXTENSION) *x, int loc);
+X509_EXTENSION *X509v3_delete_extension(STACK_OF(X509_EXTENSION) **x, int loc);
STACK_OF(X509_EXTENSION) *X509v3_add_ext(STACK_OF(X509_EXTENSION) **x,
const X509_EXTENSION *ex, int loc);
STACK_OF(X509_EXTENSION) *X509v3_add_extensions(STACK_OF(X509_EXTENSION) **target,
static int test_drop_empty_cert_keyids(void)
{
static const unsigned char commonName[] = "test";
+ BIO *bio = NULL;
+ CONF *conf = NULL;
X509 *x = NULL;
X509_NAME *subject = NULL;
X509_NAME_ENTRY *name_entry = NULL;
X509_EXTENSION *ext = NULL;
+ const STACK_OF(X509_EXTENSION) *exts;
X509V3_CTX ctx;
int ret = 0;
|| !TEST_int_eq(X509_set_pubkey(x, pubkey), 1))
goto err;
- X509V3_set_ctx(&ctx, x, x, NULL, NULL, 0);
- if (!TEST_ptr(ext = X509V3_EXT_conf(NULL, &ctx, "subjectKeyIdentifier",
- "none"))
+ /*
+ * Check that X509_add_ext() does not create non-NULL empty stack when
+ * adding an ignored extension (from initial NULL state).
+ */
+ X509V3_set_ctx(&ctx, x, x, NULL, NULL, X509V3_CTX_REPLACE);
+ if (!TEST_ptr(ext = X509V3_EXT_conf(NULL, &ctx, "subjectKeyIdentifier", "none"))
|| !TEST_int_eq(X509_add_ext(x, ext, -1), 1)
|| !TEST_ptr_null(X509_get0_extensions(x)))
goto err;
+ /* Add non-empty SKID */
+ if (!TEST_ptr(bio = BIO_new(BIO_s_mem()))
+ || !TEST_int_ge(BIO_printf(bio, "subjectKeyIdentifier = hash\n"), 0)
+ || !TEST_ptr(conf = NCONF_new(NULL))
+ || !TEST_int_gt(NCONF_load_bio(conf, bio, NULL), 0))
+ goto err;
+ (void)BIO_reset(bio);
+
+ X509V3_set_nconf(&ctx, conf);
+ if (!TEST_true(X509V3_EXT_add_nconf(conf, &ctx, "default", x))
+ || !TEST_ptr(exts = X509_get0_extensions(x))
+ || !TEST_int_eq(sk_X509_EXTENSION_num(exts), 1))
+ goto err;
+
+ /* Request "empty" SKID and AKID in order to drop any previous values */
+ NCONF_free(conf);
+ if (!TEST_ptr(conf = NCONF_new(NULL))
+ || !TEST_int_ge(BIO_printf(bio, "subjectKeyIdentifier = none\n"), 0)
+ || !TEST_int_gt(NCONF_load_bio(conf, bio, NULL), 0))
+ goto err;
+
+ X509V3_set_nconf(&ctx, conf);
+ if (!TEST_true(X509V3_EXT_add_nconf(conf, &ctx, "default", x))
+ || !TEST_int_gt(X509_sign(x, privkey, signmd), 0)
+ || !TEST_ptr_null(X509_get0_extensions(x)))
+ goto err;
+
+ /*
+ * Now check that a non-empty extension is actually added via
+ * X509_add_ext().
+ */
X509_EXTENSION_free(ext);
- if (!TEST_ptr(ext = X509V3_EXT_conf(NULL, &ctx, "authorityKeyIdentifier",
- "none"))
+ if (!TEST_ptr(ext = X509V3_EXT_conf(NULL, &ctx, "subjectKeyIdentifier", "hash"))
|| !TEST_int_eq(X509_add_ext(x, ext, -1), 1)
- || !TEST_ptr_null(X509_get0_extensions(x))
- || !TEST_int_gt(X509_sign(x, privkey, signmd), 0))
+ || !TEST_int_gt(X509_sign(x, privkey, signmd), 0)
+ || !TEST_ptr(exts = X509_get0_extensions(x))
+ || !TEST_int_eq(sk_X509_EXTENSION_num(exts), 1))
goto err;
ret = 1;
err:
+ BIO_free(bio);
+ NCONF_free(conf);
X509_NAME_ENTRY_free(name_entry);
X509_NAME_free(subject);
X509_EXTENSION_free(ext);
OPENSSL_sk_set_cmp_thunks ? 4_0_0 EXIST::FUNCTION:
ASN1_BIT_STRING_set1 ? 4_0_0 EXIST::FUNCTION:
OSSL_ESS_check_signing_certs_ex ? 4_0_0 EXIST::FUNCTION:
+X509v3_delete_extension ? 4_0_0 EXIST::FUNCTION:
X509_new ? 4_0_0 EXIST::FUNCTION:
X509_free ? 4_0_0 EXIST::FUNCTION:
d2i_X509 ? 4_0_0 EXIST::FUNCTION: