]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
fix X509_PURPOSE_add() to take |sname| as primary key and handle |id| in a backwd...
authorDr. David von Oheimb <dev@ddvo.net>
Fri, 20 Dec 2024 09:07:15 +0000 (10:07 +0100)
committerDr. David von Oheimb <dev@ddvo.net>
Mon, 20 Jan 2025 07:41:19 +0000 (08:41 +0100)
Fixes #25873

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
(Merged from https://github.com/openssl/openssl/pull/26221)

CHANGES.md
crypto/err/openssl.txt
crypto/x509/v3_purp.c
crypto/x509/v3err.c
doc/man3/X509_check_purpose.pod
include/openssl/x509v3.h.in
include/openssl/x509v3err.h
test/x509_internal_test.c
util/libcrypto.num
util/missingcrypto.txt

index dc3c1ae372d1447a87aaa1f6787b73835415d2fa..741dbcb5ec8ee38b124b251bd3e1ea91844cd724 100644 (file)
@@ -77,6 +77,14 @@ OpenSSL 3.5
 
    *David von Oheimb*
 
+ * `X509_PURPOSE_add()` has been modified
+   to take `sname` instead of `id` as the primary purpose identifier.
+   For its convenient use, `X509_PURPOSE_get_unused_id()` has been added.
+
+   This work was sponsored by Siemens AG.
+
+   *David von Oheimb*
+
  * Optionally allow the FIPS provider to use the `JITTER` entropy source.
    Note that using this option will require the resulting FIPS provider
    to undergo entropy source validation [ESV] by the [CMVP], without this
index d75c505c79a58e7ae6847b4f1765e68c0a691d77..e2ce4faede25c64bb147565abeca39adb4bedede 100644 (file)
@@ -1811,6 +1811,7 @@ X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED:157:\
        policy path length already defined
 X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY:159:\
        policy when proxy language requires no policy
+X509V3_R_PURPOSE_NOT_UNIQUE:173:purpose not unique
 X509V3_R_SECTION_NOT_FOUND:150:section not found
 X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS:122:unable to get issuer details
 X509V3_R_UNABLE_TO_GET_ISSUER_KEYID:123:unable to get issuer keyid
index c633474bdcf4fe236f7a1c1b856a3a9180146e02..3e3f4e95c1c9c1e0ad89c825f62283d18a18f746 100644 (file)
@@ -42,6 +42,7 @@ static int check_purpose_ocsp_helper(const X509_PURPOSE *xp, const X509 *x,
 static int xp_cmp(const X509_PURPOSE *const *a, const X509_PURPOSE *const *b);
 static void xptable_free(X509_PURPOSE *p);
 
+/* note that the id must be unique and for the standard entries == idx + 1 */
 static X509_PURPOSE xstandard[] = {
     {X509_PURPOSE_SSL_CLIENT, X509_TRUST_SSL_CLIENT, 0,
      check_purpose_ssl_client, "SSL client", "sslclient", NULL},
@@ -70,6 +71,7 @@ static X509_PURPOSE xstandard[] = {
 
 #define X509_PURPOSE_COUNT OSSL_NELEM(xstandard)
 
+/* the id must be unique, but there may be gaps and maybe table is not sorted */
 static STACK_OF(X509_PURPOSE) *xptable = NULL;
 
 static int xp_cmp(const X509_PURPOSE *const *a, const X509_PURPOSE *const *b)
@@ -118,6 +120,16 @@ int X509_PURPOSE_get_count(void)
     return sk_X509_PURPOSE_num(xptable) + X509_PURPOSE_COUNT;
 }
 
+/* find smallest identifier not yet taken - note there might be gaps */
+int X509_PURPOSE_get_unused_id(ossl_unused OSSL_LIB_CTX *libctx)
+{
+    int id = X509_PURPOSE_MAX + 1;
+
+    while (X509_PURPOSE_get_by_id(id) != -1)
+        id++;
+    return id; /* is guaranteed to be unique and > X509_PURPOSE_MAX and != 0 */
+}
+
 X509_PURPOSE *X509_PURPOSE_get0(int idx)
 {
     if (idx < 0)
@@ -157,26 +169,49 @@ int X509_PURPOSE_get_by_id(int purpose)
     return idx + X509_PURPOSE_COUNT;
 }
 
+/*
+ * Add purpose entry identified by |sname|. |id| must be >= X509_PURPOSE_MIN.
+ * May also be used to modify existing entry, including changing its id.
+ */
 int X509_PURPOSE_add(int id, int trust, int flags,
                      int (*ck) (const X509_PURPOSE *, const X509 *, int),
                      const char *name, const char *sname, void *arg)
 {
+    int old_id = 0;
     int idx;
     X509_PURPOSE *ptmp;
 
+    if (id < X509_PURPOSE_MIN) {
+        ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_PURPOSE);
+        return 0;
+    }
+    if (trust < X509_TRUST_DEFAULT || name == NULL || sname == NULL || ck == NULL) {
+        ERR_raise(ERR_LIB_X509, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+
     /* This is set according to what we change: application can't set it */
     flags &= ~X509_PURPOSE_DYNAMIC;
     /* This will always be set for application modified trust entries */
     flags |= X509_PURPOSE_DYNAMIC_NAME;
+
     /* Get existing entry if any */
-    idx = X509_PURPOSE_get_by_id(id);
-    /* Need a new entry */
-    if (idx == -1) {
+    idx = X509_PURPOSE_get_by_sname(sname);
+    if (idx == -1) { /* Need a new entry */
+        if (X509_PURPOSE_get_by_id(id) != -1) {
+            ERR_raise(ERR_LIB_X509V3, X509V3_R_PURPOSE_NOT_UNIQUE);
+            return 0;
+        }
         if ((ptmp = OPENSSL_malloc(sizeof(*ptmp))) == NULL)
             return 0;
         ptmp->flags = X509_PURPOSE_DYNAMIC;
     } else {
         ptmp = X509_PURPOSE_get0(idx);
+        old_id = ptmp->purpose;
+        if (id != old_id && X509_PURPOSE_get_by_id(id) != -1) {
+            ERR_raise(ERR_LIB_X509V3, X509V3_R_PURPOSE_NOT_UNIQUE);
+            return 0;
+        }
     }
 
     /* OPENSSL_free existing name if dynamic */
@@ -210,6 +245,9 @@ int X509_PURPOSE_add(int id, int trust, int flags,
             ERR_raise(ERR_LIB_X509V3, ERR_R_CRYPTO_LIB);
             goto err;
         }
+    } else if (id != old_id) {
+        /* on changing existing entry id, make sure to reset 'sorted' */
+        (void)sk_X509_PURPOSE_set(xptable, idx, ptmp);
     }
     return 1;
  err:
index 6a75af416834900c275933035d563de766cf0ba8..5512f1b317b87baa5f573d6d3267d38eabed02be 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2024 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
@@ -21,119 +21,121 @@ static const ERR_STRING_DATA X509V3_str_reasons[] = {
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_BAD_VALUE), "bad value"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_BN_DEC2BN_ERROR), "bn dec2bn error"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_BN_TO_ASN1_INTEGER_ERROR),
-    "bn to asn1 integer error"},
+     "bn to asn1 integer error"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DIRNAME_ERROR), "dirname error"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DISTPOINT_ALREADY_SET),
-    "distpoint already set"},
+     "distpoint already set"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_DUPLICATE_ZONE_ID),
-    "duplicate zone id"},
+     "duplicate zone id"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EMPTY_KEY_USAGE), "empty key usage"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ERROR_CONVERTING_ZONE),
-    "error converting zone"},
+     "error converting zone"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ERROR_CREATING_EXTENSION),
-    "error creating extension"},
+     "error creating extension"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ERROR_IN_EXTENSION),
-    "error in extension"},
+     "error in extension"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXPECTED_A_SECTION_NAME),
-    "expected a section name"},
+     "expected a section name"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_EXISTS),
-    "extension exists"},
+     "extension exists"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_NAME_ERROR),
-    "extension name error"},
+     "extension name error"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_NOT_FOUND),
-    "extension not found"},
+     "extension not found"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED),
-    "extension setting not supported"},
+     "extension setting not supported"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_EXTENSION_VALUE_ERROR),
-    "extension value error"},
+     "extension value error"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ILLEGAL_EMPTY_EXTENSION),
-    "illegal empty extension"},
+     "illegal empty extension"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INCORRECT_POLICY_SYNTAX_TAG),
-    "incorrect policy syntax tag"},
+     "incorrect policy syntax tag"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_ASNUMBER),
-    "invalid asnumber"},
+     "invalid asnumber"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_ASRANGE), "invalid asrange"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_BOOLEAN_STRING),
-    "invalid boolean string"},
+     "invalid boolean string"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_CERTIFICATE),
-    "invalid certificate"},
+     "invalid certificate"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_EMPTY_NAME),
-    "invalid empty name"},
+     "invalid empty name"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_EXTENSION_STRING),
-    "invalid extension string"},
+     "invalid extension string"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_INHERITANCE),
-    "invalid inheritance"},
+     "invalid inheritance"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_IPADDRESS),
-    "invalid ipaddress"},
+     "invalid ipaddress"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_MULTIPLE_RDNS),
-    "invalid multiple rdns"},
+     "invalid multiple rdns"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NAME), "invalid name"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NULL_ARGUMENT),
-    "invalid null argument"},
+     "invalid null argument"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NULL_VALUE),
-    "invalid null value"},
+     "invalid null value"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NUMBER), "invalid number"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_NUMBERS), "invalid numbers"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_OBJECT_IDENTIFIER),
-    "invalid object identifier"},
+     "invalid object identifier"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_OPTION), "invalid option"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_POLICY_IDENTIFIER),
-    "invalid policy identifier"},
+     "invalid policy identifier"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_PROXY_POLICY_SETTING),
-    "invalid proxy policy setting"},
+     "invalid proxy policy setting"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_PURPOSE), "invalid purpose"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_SAFI), "invalid safi"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_SECTION), "invalid section"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_INVALID_SYNTAX), "invalid syntax"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_ISSUER_DECODE_ERROR),
-    "issuer decode error"},
+     "issuer decode error"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_MISSING_VALUE), "missing value"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NEED_ORGANIZATION_AND_NUMBERS),
-    "need organization and numbers"},
+     "need organization and numbers"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NEGATIVE_PATHLEN),
-    "negative pathlen"},
+     "negative pathlen"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_CONFIG_DATABASE),
-    "no config database"},
+     "no config database"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_ISSUER_CERTIFICATE),
-    "no issuer certificate"},
+     "no issuer certificate"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_ISSUER_DETAILS),
-    "no issuer details"},
+     "no issuer details"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_POLICY_IDENTIFIER),
-    "no policy identifier"},
+     "no policy identifier"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED),
-    "no proxy cert policy language defined"},
+     "no proxy cert policy language defined"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_PUBLIC_KEY), "no public key"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_NO_SUBJECT_DETAILS),
-    "no subject details"},
+     "no subject details"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_OPERATION_NOT_DEFINED),
-    "operation not defined"},
+     "operation not defined"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_OTHERNAME_ERROR), "othername error"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED),
-    "policy language already defined"},
+     "policy language already defined"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_PATH_LENGTH),
-    "policy path length"},
+     "policy path length"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED),
-    "policy path length already defined"},
+     "policy path length already defined"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY),
-    "policy when proxy language requires no policy"},
+     "policy when proxy language requires no policy"},
+    {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_PURPOSE_NOT_UNIQUE),
+     "purpose not unique"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_SECTION_NOT_FOUND),
-    "section not found"},
+     "section not found"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS),
-    "unable to get issuer details"},
+     "unable to get issuer details"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNABLE_TO_GET_ISSUER_KEYID),
-    "unable to get issuer keyid"},
+     "unable to get issuer keyid"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT),
-    "unknown bit string argument"},
+     "unknown bit string argument"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_EXTENSION),
-    "unknown extension"},
+     "unknown extension"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_EXTENSION_NAME),
-    "unknown extension name"},
+     "unknown extension name"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_OPTION), "unknown option"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNKNOWN_VALUE), "unknown value"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNSUPPORTED_OPTION),
-    "unsupported option"},
+     "unsupported option"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_UNSUPPORTED_TYPE),
-    "unsupported type"},
+     "unsupported type"},
     {ERR_PACK(ERR_LIB_X509V3, 0, X509V3_R_USER_TOO_LONG), "user too long"},
     {0, NULL}
 };
index 4331cfad92c907f4efe96492cc6371a62b4a905e..70acbc3d58fffcd36250ecf7ec709e290185efe0 100644 (file)
@@ -2,7 +2,19 @@
 
 =head1 NAME
 
-X509_check_purpose - Check the purpose of a certificate
+X509_check_purpose,
+X509_PURPOSE_get_count,
+X509_PURPOSE_get_unused_id,
+X509_PURPOSE_get_by_sname,
+X509_PURPOSE_get_by_id,
+X509_PURPOSE_add,
+X509_PURPOSE_cleanup,
+X509_PURPOSE_get0,
+X509_PURPOSE_get_id,
+X509_PURPOSE_get0_name,
+X509_PURPOSE_get0_sname,
+X509_PURPOSE_get_trust,
+X509_PURPOSE_set - functions related to checking the purpose of a certificate
 
 =head1 SYNOPSIS
 
@@ -10,9 +22,25 @@ X509_check_purpose - Check the purpose of a certificate
 
  int X509_check_purpose(X509 *x, int id, int ca);
 
+ int X509_PURPOSE_get_count(void);
+ int X509_PURPOSE_get_unused_id(OSSL_LIB_CTX *libctx);
+ int X509_PURPOSE_get_by_sname(const char *sname);
+ int X509_PURPOSE_get_by_id(int id);
+ int X509_PURPOSE_add(int id, int trust, int flags,
+                      int (*ck) (const X509_PURPOSE *, const X509 *, int),
+                      const char *name, const char *sname, void *arg);
+ void X509_PURPOSE_cleanup(void);
+
+ X509_PURPOSE *X509_PURPOSE_get0(int idx);
+ int X509_PURPOSE_get_id(const X509_PURPOSE *);
+ char *X509_PURPOSE_get0_name(const X509_PURPOSE *xp);
+ char *X509_PURPOSE_get0_sname(const X509_PURPOSE *xp);
+ int X509_PURPOSE_get_trust(const X509_PURPOSE *xp);
+ int X509_PURPOSE_set(int *p, int purpose);
+
 =head1 DESCRIPTION
 
-This function checks if certificate I<x> was created with the purpose
+X509_check_purpose() checks if certificate I<x> was created with the purpose
 represented by I<id>. If I<ca> is nonzero, then certificate I<x> is
 checked to determine if it's a possible CA with various levels of certainty
 possibly returned. The certificate I<x> must be a complete certificate
@@ -34,8 +62,45 @@ Below are the potential ID's that can be checked:
 The checks performed take into account the X.509 extensions
 keyUsage, extendedKeyUsage, and basicConstraints.
 
+X509_PURPOSE_get_count() returns the number of currently defined purposes.
+
+X509_PURPOSE_get_unused_id() returns the smallest purpose id not yet used.
+The I<libctx> parameter should be used to provide the library context.
+It is currently ignored as the purpose mapping table is global.
+
+X509_PURPOSE_get_by_sname() returns the index of
+the purpose with the given short name or -1 if not found.
+
+X509_PURPOSE_get_by_id() returns the index of
+the purpose with the given id or -1 if not found.
+
+X509_PURPOSE_add() adds or modifies a purpose entry identified by I<sname>.
+Unless the id stays the same for an existing entry, I<id> must be fresh,
+which can be achieved by using the result of X509_PURPOSE_get_unused_id().
+The function also sets in the entry the trust id I<trust>, the given I<flags>,
+the purpose (long) name I<name>, the short name I<sname>, the purpose checking
+funktion I<ck> of type B<int (*) (const X509_PURPOSE *, const X509 *, int)>,
+and its user data I<arg> which may be retrieved via the B<X509_PURPOSE> pointer.
+
+X509_PURPOSE_cleanup() removes all purposes that are not pre-defined.
+
+X509_PURPOSE_get0() returns an B<X509_PURPOSE> pointer or NULL on error.
+
+X509_PURPOSE_get_id() returns the id of the given B<X509_PURPOSE> structure.
+
+X509_PURPOSE_get0_name() returns the (long) name of the given B<X509_PURPOSE>.
+
+X509_PURPOSE_get0_sname() returns the short name of the given B<X509_PURPOSE>.
+
+X509_PURPOSE_get_trust() returns the trust id of the given B<X509_PURPOSE>.
+
+X509_PURPOSE_set() assigns the given I<purpose> id to the location pointed at by
+I<p>.
+This resets to the any purpose if I<purpose> is B<X509_PURPOSE_DEFAULT_ANY>.
+
 =head1 RETURN VALUES
 
+X509_check_purpose() returns the following values.
 For non-CA checks
 
 =over 4
@@ -69,9 +134,45 @@ For CA checks the below integers could be returned with the following meanings:
 
 =back
 
+X509_PURPOSE_get_count() returns the number of currently defined purposes.
+
+X509_PURPOSE_get_unused_id() returns the smallest purpose id not yet used.
+
+X509_PURPOSE_get_by_sname() returns the index of
+the purpose with the given short name or -1 if not found.
+
+X509_PURPOSE_get_by_id() returns the index of
+the purpose with the given id or -1 if not found.
+
+int X509_PURPOSE_add() returns 1 on success, 0 on error.
+
+X509_PURPOSE_cleanup() does not return anything.
+
+X509_PURPOSE_get0() returns an B<X509_PURPOSE> pointer or NULL on error.
+
+X509_PURPOSE_get_id() returns the id of the given B<X509_PURPOSE> structure.
+
+X509_PURPOSE_get0_name() returns the (long) name of the given B<X509_PURPOSE>.
+
+X509_PURPOSE_get0_sname() returns the short name of the given B<X509_PURPOSE>.
+
+X509_PURPOSE_get_trust() returns the trust id of the given B<X509_PURPOSE>.
+
+X509_PURPOSE_set() returns 1 on success, 0 on error.
+
+=head1 BUGS
+
+The X509_PURPOSE implementation so far is not thread-safe.
+There may be race conditions retrieving purpose information while
+X509_PURPOSE_add() or X509_PURPOSE_cleanup(void) is being called.
+
+=head1 HISTORY
+
+X509_PURPOSE_get_unused_id() was added in OpensSL 3.5.
+
 =head1 COPYRIGHT
 
-Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2019-2024 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 L<https://www.openssl.org/source/license.html>.
index 0a5650353fa1b180218e432a8252b1255a99530e..86d91ea9d2c6ff629e6443fa476fffbd751b0880 100644 (file)
@@ -477,7 +477,6 @@ typedef struct x509_purpose_st {
     generate_stack_macros("X509_PURPOSE");
 -}
 
-
 # define X509_PURPOSE_DEFAULT_ANY        0
 # define X509_PURPOSE_SSL_CLIENT         1
 # define X509_PURPOSE_SSL_SERVER         2
@@ -727,7 +726,6 @@ int X509V3_extensions_print(BIO *out, const char *title,
 int X509_check_ca(X509 *x);
 int X509_check_purpose(X509 *x, int id, int ca);
 int X509_supported_extension(X509_EXTENSION *ex);
-int X509_PURPOSE_set(int *p, int purpose);
 int X509_check_issued(X509 *issuer, X509 *subject);
 int X509_check_akid(const X509 *issuer, const AUTHORITY_KEYID *akid);
 void X509_set_proxy_flag(X509 *x);
@@ -743,22 +741,26 @@ const GENERAL_NAMES *X509_get0_authority_issuer(X509 *x);
 const ASN1_INTEGER *X509_get0_authority_serial(X509 *x);
 
 int X509_PURPOSE_get_count(void);
-X509_PURPOSE *X509_PURPOSE_get0(int idx);
+int X509_PURPOSE_get_unused_id(OSSL_LIB_CTX *libctx);
 int X509_PURPOSE_get_by_sname(const char *sname);
 int X509_PURPOSE_get_by_id(int id);
 int X509_PURPOSE_add(int id, int trust, int flags,
                      int (*ck) (const X509_PURPOSE *, const X509 *, int),
                      const char *name, const char *sname, void *arg);
+void X509_PURPOSE_cleanup(void);
+
+X509_PURPOSE *X509_PURPOSE_get0(int idx);
+int X509_PURPOSE_get_id(const X509_PURPOSE *);
 char *X509_PURPOSE_get0_name(const X509_PURPOSE *xp);
 char *X509_PURPOSE_get0_sname(const X509_PURPOSE *xp);
 int X509_PURPOSE_get_trust(const X509_PURPOSE *xp);
-void X509_PURPOSE_cleanup(void);
-int X509_PURPOSE_get_id(const X509_PURPOSE *);
+int X509_PURPOSE_set(int *p, int purpose);
 
 STACK_OF(OPENSSL_STRING) *X509_get1_email(X509 *x);
 STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x);
 void X509_email_free(STACK_OF(OPENSSL_STRING) *sk);
 STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x);
+
 /* Flags for X509_check_* functions */
 
 /*
index deede279528520e184b2bffed8d2a82650ac7c81..1ed1b2e5b0190d31e5037e8c0615a3a4f582f291 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2024 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
@@ -81,6 +81,7 @@
 # define X509V3_R_POLICY_PATH_LENGTH                      156
 # define X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED      157
 # define X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY 159
+# define X509V3_R_PURPOSE_NOT_UNIQUE                      173
 # define X509V3_R_SECTION_NOT_FOUND                       150
 # define X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS            122
 # define X509V3_R_UNABLE_TO_GET_ISSUER_KEYID              123
index dfc8c0bb60d4a3a76bcaf970a4a7766cf20ef9af..e2d3910eeb1fecdd27bfe82f5f9e41909c2c186b 100644 (file)
@@ -172,9 +172,59 @@ static int test_a2i_ipaddress(int idx)
     return good;
 }
 
+static int ck_purp(ossl_unused const X509_PURPOSE *purpose,
+                   ossl_unused const X509 *x, int ca)
+{
+    return 1;
+}
+
+static int tests_X509_PURPOSE(void)
+{
+    OSSL_LIB_CTX *libctx = NULL;
+    int id, idx, *p;
+    X509_PURPOSE *xp;
+
+#undef LN
+#define LN "LN_test"
+#undef SN
+#define SN "SN_test"
+#undef ARGS
+#define ARGS(id, sn) id, X509_TRUST_MAX, 0, ck_purp, LN, sn, NULL
+    return TEST_int_gt((id = X509_PURPOSE_get_unused_id(libctx)), X509_PURPOSE_MAX)
+        && TEST_int_eq(X509_PURPOSE_get_count() + 1, id)
+        && TEST_int_eq(X509_PURPOSE_get_by_id(id), -1)
+        && TEST_int_eq(X509_PURPOSE_get_by_sname(SN), -1)
+
+        /* add new entry with fresh id and fresh sname: */
+        && TEST_int_eq(X509_PURPOSE_add(ARGS(id, SN)), 1)
+        && TEST_int_ne((idx = X509_PURPOSE_get_by_sname(SN)), -1)
+        && TEST_int_eq(X509_PURPOSE_get_by_id(id), idx)
+
+        /* overwrite same entry, should be idempotent: */
+        && TEST_int_eq(X509_PURPOSE_add(ARGS(id, SN)), 1)
+        && TEST_int_eq(X509_PURPOSE_get_by_sname(SN), idx)
+        && TEST_int_eq(X509_PURPOSE_get_by_id(id), idx)
+
+        /* fail adding entry with same sname but existing conflicting id: */
+        && TEST_int_eq(X509_PURPOSE_add(ARGS(X509_PURPOSE_MAX, SN)), 0)
+        /* fail adding entry with same existing id but conflicting sname: */
+        && TEST_int_eq(X509_PURPOSE_add(ARGS(id, SN"_different")), 0)
+
+        && TEST_ptr((xp = X509_PURPOSE_get0(idx)))
+        && TEST_int_eq(X509_PURPOSE_get_id(xp), id)
+        && TEST_str_eq(X509_PURPOSE_get0_name(xp), LN)
+        && TEST_str_eq(X509_PURPOSE_get0_sname(xp), SN)
+        && TEST_int_eq(X509_PURPOSE_get_trust(xp), X509_TRUST_MAX)
+
+        && TEST_int_eq(*(p = &xp->purpose), id)
+        && TEST_int_eq(X509_PURPOSE_set(p, X509_PURPOSE_DEFAULT_ANY), 1)
+        && TEST_int_eq(X509_PURPOSE_get_id(xp), X509_PURPOSE_DEFAULT_ANY);
+}
+
 int setup_tests(void)
 {
     ADD_TEST(test_standard_exts);
     ADD_ALL_TESTS(test_a2i_ipaddress, OSSL_NELEM(a2i_ipaddress_tests));
+    ADD_TEST(tests_X509_PURPOSE);
     return 1;
 }
index fe5d32e21c8bc68bc298e9acc883b36ba05b9491..1b96c81886fbc548783a41dd115085d9ea91c9a3 100644 (file)
@@ -5735,6 +5735,7 @@ EVP_PKEY_CTX_set_algor_params           5862      3_4_0   EXIST::FUNCTION:
 EVP_PKEY_CTX_get_algor_params           5863   3_4_0   EXIST::FUNCTION:
 EVP_PKEY_CTX_get_algor                  5864   3_4_0   EXIST::FUNCTION:
 EVP_get1_default_properties             ?      3_5_0   EXIST::FUNCTION:
+X509_PURPOSE_get_unused_id              ?      3_5_0   EXIST::FUNCTION:
 d2i_OSSL_AUTHORITY_ATTRIBUTE_ID_SYNTAX  ?      3_5_0   EXIST::FUNCTION:
 i2d_OSSL_AUTHORITY_ATTRIBUTE_ID_SYNTAX  ?      3_5_0   EXIST::FUNCTION:
 OSSL_AUTHORITY_ATTRIBUTE_ID_SYNTAX_free ?      3_5_0   EXIST::FUNCTION:
index a56491d0f8b9448efdf8e66bbc0c79fabb9a6225..3dd997b1927b3cd29302fc341cdd218fdd6b1bf0 100644 (file)
@@ -1108,17 +1108,6 @@ X509_PKEY_free(3)
 X509_PKEY_new(3)
 X509_POLICY_NODE_print(3)
 X509_PUBKEY_it(3)
-X509_PURPOSE_add(3)
-X509_PURPOSE_cleanup(3)
-X509_PURPOSE_get0(3)
-X509_PURPOSE_get0_name(3)
-X509_PURPOSE_get0_sname(3)
-X509_PURPOSE_get_by_id(3)
-X509_PURPOSE_get_by_sname(3)
-X509_PURPOSE_get_count(3)
-X509_PURPOSE_get_id(3)
-X509_PURPOSE_get_trust(3)
-X509_PURPOSE_set(3)
 X509_REQ_INFO_it(3)
 X509_REQ_extension_nid(3)
 X509_REQ_get1_email(3)