Commit fdab63f538 for openssl.org

commit fdab63f53859695a29c11f7925802a9b78daf161
Author: mi11ione <i@mi11ion.io>
Date:   Thu Oct 22 14:58:28 2026 +0200

    apps/pkcs12.c: fix double free when -jdktrust OID is used

    jdk_trust() passed the OID it gets from pkcs12_main()
    to X509_ATTRIBUTE_create(), which takes the ownership of it, and then freed
    the attribute, freeing the OID;  pkcs12_main() frees the OID again
    at export_end.  With an OID that is not built in (for example,
    -jdktrust 1.2.3), the export aborts on a double free, and with more
    than one certificate, the second jdk_trust() call reads the freed OID.
    Built-in names are static objects, so they were not affected.

    Give the attribute its own copy with OBJ_dup(), and return -1 (failure)
    from the callback if the copy or the attribute cannot be created.

    Resolves: https://github.com/openssl/openssl/issues/33319
    Fixes: e869c867c1c4 "Allow PKCS12 export to set arbitrary bag attributes"
    Assisted-by: Claude Code:claude-opus-5-5
    Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
    Merge-date: Wed Oct 28 10:34:05 2026
    Merged-from: https://github.com/openssl/openssl/pull/33320

diff --git a/apps/pkcs12.c b/apps/pkcs12.c
index f97149f8f7..7213f9c137 100644
--- a/apps/pkcs12.c
+++ b/apps/pkcs12.c
@@ -952,6 +952,7 @@ static int jdk_trust(PKCS12_SAFEBAG *bag, void *cbarg)
 {
     STACK_OF(X509_ATTRIBUTE) *attrs = NULL;
     X509_ATTRIBUTE *attr = NULL;
+    ASN1_OBJECT *obj = NULL;

     /* Nothing to do */
     if (cbarg == NULL)
@@ -960,8 +961,17 @@ static int jdk_trust(PKCS12_SAFEBAG *bag, void *cbarg)
     /* Get the current attrs */
     attrs = (STACK_OF(X509_ATTRIBUTE) *)PKCS12_SAFEBAG_get0_attrs(bag);

-    /* Create a new attr for the JDK Trusted Usage and add it */
-    attr = X509_ATTRIBUTE_create(NID_oracle_jdk_trustedkeyusage, V_ASN1_OBJECT, (ASN1_OBJECT *)cbarg);
+    /*
+     * Create a new attr for the JDK Trusted Usage and add it. The attr owns
+     * its value, so give it a copy: cbarg stays owned by the caller.
+     */
+    if ((obj = OBJ_dup((ASN1_OBJECT *)cbarg)) == NULL)
+        return -1;
+    attr = X509_ATTRIBUTE_create(NID_oracle_jdk_trustedkeyusage, V_ASN1_OBJECT, obj);
+    if (attr == NULL) {
+        ASN1_OBJECT_free(obj);
+        return -1;
+    }

     /* Add the new attr, if attrs is NULL, it'll be initialised */
     X509at_add1_attr(&attrs, attr);
diff --git a/test/recipes/80-test_pkcs12.t b/test/recipes/80-test_pkcs12.t
index 16652b1fb1..deea098c8b 100644
--- a/test/recipes/80-test_pkcs12.t
+++ b/test/recipes/80-test_pkcs12.t
@@ -57,7 +57,7 @@ $ENV{OPENSSL_WIN32_UTF8}=1;
 my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0);
 my $no_err =  disabled('err') || disabled('autoerrinit');

-plan tests => 117 + ($no_fips ? 0 : 5);
+plan tests => 119 + ($no_fips ? 0 : 5);

 # Test different PKCS#12 formats
 ok(run(test(["pkcs12_format_test"])), "test pkcs12 formats");
@@ -333,6 +333,21 @@ with({ exit_checker => sub { return shift == 1; } },
     delete $ENV{OPENSSL_CONF}
 }

+# Test Oracle Trusted Key Usage with an OID that is not built in, on two certs
+{
+    my $outfile_oid = "out7-custom-oid.p12";
+    ok(run(app(["openssl", "pkcs12", "-export", "-out", $outfile_oid,
+                "-jdktrust", "1.2.3", "-in", srctop_file(@path, "ee-cert.pem"),
+                "-certfile", srctop_file(@path, "ca-cert.pem"),
+                "-nokeys", "-passout", "pass:", "-certpbe", "NONE"])),
+       "test nokeys two certs with a custom trusted key usage OID");
+
+    my @pkcs12info = run(app(["openssl", "pkcs12", "-info", "-in", $outfile_oid,
+                              "-passin", "pass:"]), capture => 1);
+    ok(grep(/Trusted key usage \(Oracle\): undefined \(1\.2\.3\)$/, @pkcs12info) == 2,
+       "test custom trusted key usage OID is set on both certs");
+}
+
 # Test -clcerts/-cacerts cert filtering and the -name friendly name.
 subtest "pkcs12 -clcerts/-cacerts filtering and -name" => sub {
     plan tests => 7;