From b79da97cf8751d7b196a87cc8bced0bb3334a0d3 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Fri, 9 Dec 2022 16:57:28 +0100 Subject: [PATCH] Allow OBJ_create() to create an OBJ and NID with a NULL OID We already permit this in crypto/objects/objects.txt, but not programatically, although being able to do so programatically would be beneficial. Reviewed-by: Dmitry Belyavskiy Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/19876) --- crypto/objects/obj_dat.c | 22 ++++++++++--- doc/man3/OBJ_nid2obj.pod | 3 +- test/asn1_internal_test.c | 65 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 6 deletions(-) diff --git a/crypto/objects/obj_dat.c b/crypto/objects/obj_dat.c index ea5d57a77e..ad476136ae 100644 --- a/crypto/objects/obj_dat.c +++ b/crypto/objects/obj_dat.c @@ -729,6 +729,12 @@ int OBJ_create(const char *oid, const char *sn, const char *ln) ASN1_OBJECT *tmpoid = NULL; int ok = 0; + /* With no arguments at all, nothing can be done */ + if (oid == NULL && sn == NULL && ln == NULL) { + ERR_raise(ERR_LIB_OBJ, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + /* Check to see if short or long name already present */ if ((sn != NULL && OBJ_sn2nid(sn) != NID_undef) || (ln != NULL && OBJ_ln2nid(ln) != NID_undef)) { @@ -736,10 +742,15 @@ int OBJ_create(const char *oid, const char *sn, const char *ln) return 0; } - /* Convert numerical OID string to an ASN1_OBJECT structure */ - tmpoid = OBJ_txt2obj(oid, 1); - if (tmpoid == NULL) - return 0; + if (oid != NULL) { + /* Convert numerical OID string to an ASN1_OBJECT structure */ + tmpoid = OBJ_txt2obj(oid, 1); + if (tmpoid == NULL) + return 0; + } else { + /* Create a no-OID ASN1_OBJECT */ + tmpoid = ASN1_OBJECT_new(); + } if (!ossl_obj_write_lock(1)) { ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_WRITE_LOCK); @@ -748,7 +759,8 @@ int OBJ_create(const char *oid, const char *sn, const char *ln) } /* If NID is not NID_undef then object already exists */ - if (ossl_obj_obj2nid(tmpoid, 0) != NID_undef) { + if (oid != NULL + && ossl_obj_obj2nid(tmpoid, 0) != NID_undef) { ERR_raise(ERR_LIB_OBJ, OBJ_R_OID_EXISTS); goto err; } diff --git a/doc/man3/OBJ_nid2obj.pod b/doc/man3/OBJ_nid2obj.pod index 748e76a584..62f5061499 100644 --- a/doc/man3/OBJ_nid2obj.pod +++ b/doc/man3/OBJ_nid2obj.pod @@ -89,7 +89,8 @@ OBJ_dup() returns a copy of I. OBJ_create() adds a new object to the internal table. I is the numerical form of the object, I the short name and I the long name. A new NID is returned for the created object in case of -success and NID_undef in case of failure. +success and NID_undef in case of failure. Any of I, I and +I may be NULL, but not all at once. OBJ_length() returns the size of the content octets of I. diff --git a/test/asn1_internal_test.c b/test/asn1_internal_test.c index 61e4265c8b..d97ca92129 100644 --- a/test/asn1_internal_test.c +++ b/test/asn1_internal_test.c @@ -190,11 +190,76 @@ static int test_unicode_range(void) return ok; } +/********************************************************************** + * + * Tests of object creation + * + ***/ + +static int test_obj_create_once(const char *oid, const char *sn, const char *ln) +{ + int nid; + + ERR_set_mark(); + + nid = OBJ_create(oid, sn, ln); + + if (nid == NID_undef) { + unsigned long err = ERR_peek_last_error(); + int l = ERR_GET_LIB(err); + int r = ERR_GET_REASON(err); + + /* If it exists, that's fine, otherwise not */ + if (l != ERR_LIB_OBJ || r != OBJ_R_OID_EXISTS) { + ERR_clear_last_mark(); + return 0; + } + } + ERR_pop_to_mark(); + return 1; +} + +static int test_obj_create(void) +{ +/* Stolen from evp_extra_test.c */ +#define arc "1.3.6.1.4.1.16604.998866." +#define broken_arc "25." +#define sn_prefix "custom" +#define ln_prefix "custom" + + /* Try different combinations of correct object creation */ + if (!TEST_true(test_obj_create_once(NULL, sn_prefix "1", NULL)) + || !TEST_int_ne(OBJ_sn2nid(sn_prefix "1"), NID_undef) + || !TEST_true(test_obj_create_once(NULL, NULL, ln_prefix "2")) + || !TEST_int_ne(OBJ_ln2nid(ln_prefix "2"), NID_undef) + || !TEST_true(test_obj_create_once(NULL, sn_prefix "3", ln_prefix "3")) + || !TEST_int_ne(OBJ_sn2nid(sn_prefix "3"), NID_undef) + || !TEST_int_ne(OBJ_ln2nid(ln_prefix "3"), NID_undef) + || !TEST_true(test_obj_create_once(arc "4", NULL, NULL)) + || !TEST_true(test_obj_create_once(arc "5", sn_prefix "5", NULL)) + || !TEST_int_ne(OBJ_sn2nid(sn_prefix "5"), NID_undef) + || !TEST_true(test_obj_create_once(arc "6", NULL, ln_prefix "6")) + || !TEST_int_ne(OBJ_ln2nid(ln_prefix "6"), NID_undef) + || !TEST_true(test_obj_create_once(arc "7", + sn_prefix "7", ln_prefix "7")) + || !TEST_int_ne(OBJ_sn2nid(sn_prefix "7"), NID_undef) + || !TEST_int_ne(OBJ_ln2nid(ln_prefix "7"), NID_undef)) + return 0; + + if (!TEST_false(test_obj_create_once(NULL, NULL, NULL)) + || !TEST_false(test_obj_create_once(broken_arc "8", + sn_prefix "8", ln_prefix "8"))) + return 0; + + return 1; +} + int setup_tests(void) { ADD_TEST(test_tbl_standard); ADD_TEST(test_standard_methods); ADD_TEST(test_empty_nonoptional_content); ADD_TEST(test_unicode_range); + ADD_TEST(test_obj_create); return 1; }