diff options
author | Pauli <pauli@openssl.org> | 2021-06-11 17:05:20 +1000 |
---|---|---|
committer | Pauli <ppzgs1@gmail.com> | 2021-09-25 10:39:20 +1000 |
commit | 397065c621e733fff80dedb28252120ec143693e (patch) | |
tree | 50d2712cc0401e80a0397243aee925bc7849564e /crypto/objects | |
parent | b0b456f8c8b628c3d7e212339e31cbfd06ac4ec8 (diff) |
obj: make the OBJ_ calls thread safe
This is done using a single global lock. The premise for this is that new
objects will most frequently be added at start up and never added subsequently.
Thus, the locking will be for read most of the time.
This does, however, introduce the overhead of taking an uncontested read lock
when accessing the object database.
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/15713)
Diffstat (limited to 'crypto/objects')
-rw-r--r-- | crypto/objects/obj_dat.c | 331 |
1 files changed, 178 insertions, 153 deletions
diff --git a/crypto/objects/obj_dat.c b/crypto/objects/obj_dat.c index 653cc9ad94..04d116eafe 100644 --- a/crypto/objects/obj_dat.c +++ b/crypto/objects/obj_dat.c @@ -11,6 +11,7 @@ #include "crypto/ctype.h" #include <limits.h> #include "internal/cryptlib.h" +#include "internal/thread_once.h" #include <openssl/lhash.h> #include <openssl/asn1.h> #include "crypto/objects.h" @@ -37,6 +38,43 @@ struct added_obj_st { static int new_nid = NUM_NID; static LHASH_OF(ADDED_OBJ) *added = NULL; +static CRYPTO_RWLOCK *ossl_obj_lock = NULL; + +static CRYPTO_ONCE ossl_obj_lock_init = CRYPTO_ONCE_STATIC_INIT; + +DEFINE_RUN_ONCE_STATIC(obj_lock_initialise) +{ + /* Make sure we've loaded config before checking for any "added" objects */ + OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL); + + ossl_obj_lock = CRYPTO_THREAD_lock_new(); + return ossl_obj_lock != NULL; +} + +static ossl_inline int ossl_init_added_lock(void) +{ + return RUN_ONCE(&ossl_obj_lock_init, obj_lock_initialise); +} + +static ossl_inline int ossl_obj_write_lock(int lock) +{ + if (!ossl_init_added_lock()) + return 0; + return !lock || CRYPTO_THREAD_write_lock(ossl_obj_lock); +} + +static ossl_inline int ossl_obj_read_lock(int lock) +{ + if (!ossl_init_added_lock()) + return 0; + return !lock || CRYPTO_THREAD_read_lock(ossl_obj_lock); +} + +static ossl_inline void ossl_obj_unlock(int lock) +{ + if (lock) + CRYPTO_THREAD_unlock(ossl_obj_lock); +} static int sn_cmp(const ASN1_OBJECT *const *a, const unsigned int *b) { @@ -123,14 +161,6 @@ static int added_obj_cmp(const ADDED_OBJ *ca, const ADDED_OBJ *cb) } } -static int init_added(void) -{ - if (added != NULL) - return 1; - added = lh_ADDED_OBJ_new(added_obj_hash, added_obj_cmp); - return added != NULL; -} - static void cleanup1_doall(ADDED_OBJ *a) { a->obj->nid = 0; @@ -152,47 +182,63 @@ static void cleanup3_doall(ADDED_OBJ *a) void ossl_obj_cleanup_int(void) { - if (added == NULL) - return; - lh_ADDED_OBJ_set_down_load(added, 0); - lh_ADDED_OBJ_doall(added, cleanup1_doall); /* zero counters */ - lh_ADDED_OBJ_doall(added, cleanup2_doall); /* set counters */ - lh_ADDED_OBJ_doall(added, cleanup3_doall); /* free objects */ - lh_ADDED_OBJ_free(added); - added = NULL; + if (added != NULL) { + lh_ADDED_OBJ_set_down_load(added, 0); + lh_ADDED_OBJ_doall(added, cleanup1_doall); /* zero counters */ + lh_ADDED_OBJ_doall(added, cleanup2_doall); /* set counters */ + lh_ADDED_OBJ_doall(added, cleanup3_doall); /* free objects */ + lh_ADDED_OBJ_free(added); + added = NULL; + } + CRYPTO_THREAD_lock_free(ossl_obj_lock); + ossl_obj_lock = NULL; } -int OBJ_new_nid(int num) +static int ossl_obj_new_nid(int num, int lock) { int i; + if (!CRYPTO_THREAD_write_lock(ossl_obj_nid_lock)) { + ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_WRITE_LOCK); + return NID_undef; + } i = new_nid; new_nid += num; + ossl_obj_unlock(lock); return i; } -int OBJ_add_object(const ASN1_OBJECT *obj) +static int ossl_obj_add_object(const ASN1_OBJECT *obj, int lock) { - ASN1_OBJECT *o; + ASN1_OBJECT *o = NULL; ADDED_OBJ *ao[4] = { NULL, NULL, NULL, NULL }, *aop; int i; - if (added == NULL) - if (!init_added()) - return 0; if ((o = OBJ_dup(obj)) == NULL) - goto err; - if ((ao[ADDED_NID] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL) + return NID_undef; + if ((ao[ADDED_NID] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL + || (o->length != 0 + && obj->data != NULL + && (ao[ADDED_DATA] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL) + || (o->sn != NULL + && (ao[ADDED_SNAME] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL) + || (o->ln != NULL + && (ao[ADDED_LNAME] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL)) { + ERR_raise(ERR_LIB_OBJ, ERR_R_MALLOC_FAILURE); + goto err2; + } + + if (!ossl_obj_write_lock(lock)) { + ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_WRITE_LOCK); goto err2; - if ((o->length != 0) && (obj->data != NULL)) - if ((ao[ADDED_DATA] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL) - goto err2; - if (o->sn != NULL) - if ((ao[ADDED_SNAME] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL) - goto err2; - if (o->ln != NULL) - if ((ao[ADDED_LNAME] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL) - goto err2; + } + if (added == NULL) { + added = lh_ADDED_OBJ_new(added_obj_hash, added_obj_cmp); + if (added == NULL) { + ERR_raise(ERR_LIB_OBJ, ERR_R_MALLOC_FAILURE); + goto err; + } + } for (i = ADDED_DATA; i <= ADDED_NID; i++) { if (ao[i] != NULL) { @@ -207,10 +253,12 @@ int OBJ_add_object(const ASN1_OBJECT *obj) ~(ASN1_OBJECT_FLAG_DYNAMIC | ASN1_OBJECT_FLAG_DYNAMIC_STRINGS | ASN1_OBJECT_FLAG_DYNAMIC_DATA); + ossl_obj_unlock(lock); return o->nid; - err2: - ERR_raise(ERR_LIB_OBJ, ERR_R_MALLOC_FAILURE); + err: + ossl_obj_unlock(lock); + err2: for (i = ADDED_DATA; i <= ADDED_NID; i++) OPENSSL_free(ao[i]); ASN1_OBJECT_free(o); @@ -219,27 +267,24 @@ int OBJ_add_object(const ASN1_OBJECT *obj) ASN1_OBJECT *OBJ_nid2obj(int n) { - ADDED_OBJ ad, *adp; + ADDED_OBJ ad, *adp = NULL; ASN1_OBJECT ob; - if ((n >= 0) && (n < NUM_NID)) { - if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) { - ERR_raise(ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID); - return NULL; - } - return (ASN1_OBJECT *)&(nid_objs[n]); - } - - /* Make sure we've loaded config before checking for any "added" objects */ - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL); - - if (added == NULL) + if (n == NID_undef) return NULL; + if (n >= 0 && n < NUM_NID && nid_objs[n].nid != NID_undef) + return (ASN1_OBJECT *)&(nid_objs[n]); ad.type = ADDED_NID; ad.obj = &ob; ob.nid = n; - adp = lh_ADDED_OBJ_retrieve(added, &ad); + if (!ossl_obj_read_lock(1)) { + ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_READ_LOCK); + return NULL; + } + if (added != NULL) + adp = lh_ADDED_OBJ_retrieve(added, &ad); + ossl_obj_unlock(1); if (adp != NULL) return adp->obj; @@ -249,62 +294,16 @@ ASN1_OBJECT *OBJ_nid2obj(int n) const char *OBJ_nid2sn(int n) { - ADDED_OBJ ad, *adp; - ASN1_OBJECT ob; - - if ((n >= 0) && (n < NUM_NID)) { - if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) { - ERR_raise(ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID); - return NULL; - } - return nid_objs[n].sn; - } - - /* Make sure we've loaded config before checking for any "added" objects */ - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL); - - if (added == NULL) - return NULL; + ASN1_OBJECT *ob = OBJ_nid2obj(n); - ad.type = ADDED_NID; - ad.obj = &ob; - ob.nid = n; - adp = lh_ADDED_OBJ_retrieve(added, &ad); - if (adp != NULL) - return adp->obj->sn; - - ERR_raise(ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID); - return NULL; + return ob == NULL ? NULL : ob->sn; } const char *OBJ_nid2ln(int n) { - ADDED_OBJ ad, *adp; - ASN1_OBJECT ob; - - if ((n >= 0) && (n < NUM_NID)) { - if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) { - ERR_raise(ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID); - return NULL; - } - return nid_objs[n].ln; - } - - /* Make sure we've loaded config before checking for any "added" objects */ - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL); - - if (added == NULL) - return NULL; + ASN1_OBJECT *ob = OBJ_nid2obj(n); - ad.type = ADDED_NID; - ad.obj = &ob; - ob.nid = n; - adp = lh_ADDED_OBJ_retrieve(added, &ad); - if (adp != NULL) - return adp->obj->ln; - - ERR_raise(ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID); - return NULL; + return ob == NULL ? NULL : ob->ln; } static int obj_cmp(const ASN1_OBJECT *const *ap, const unsigned int *bp) @@ -323,33 +322,35 @@ static int obj_cmp(const ASN1_OBJECT *const *ap, const unsigned int *bp) IMPLEMENT_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, obj); -int OBJ_obj2nid(const ASN1_OBJECT *a) +static int ossl_obj_obj2nid(const ASN1_OBJECT *a, const int lock) { + int nid = NID_undef; const unsigned int *op; ADDED_OBJ ad, *adp; if (a == NULL) return NID_undef; - if (a->nid != 0) + if (a->nid != NID_undef) return a->nid; - if (a->length == 0) return NID_undef; - /* Make sure we've loaded config before checking for any "added" objects */ - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL); - + op = OBJ_bsearch_obj(&a, obj_objs, NUM_OBJ); + if (op != NULL) + return nid_objs[*op].nid; + if (!ossl_obj_read_lock(lock)) { + ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_READ_LOCK); + return NID_undef; + } if (added != NULL) { ad.type = ADDED_DATA; - ad.obj = (ASN1_OBJECT *)a; /* XXX: ugly but harmless */ + ad.obj = (ASN1_OBJECT *)a; /* casting away const is harmless here */ adp = lh_ADDED_OBJ_retrieve(added, &ad); if (adp != NULL) - return adp->obj->nid; + nid = adp->obj->nid; } - op = OBJ_bsearch_obj(&a, obj_objs, NUM_OBJ); - if (op == NULL) - return NID_undef; - return nid_objs[*op].nid; + ossl_obj_unlock(lock); + return nid; } /* @@ -358,20 +359,20 @@ int OBJ_obj2nid(const ASN1_OBJECT *a) * into an object: unlike OBJ_txt2nid it can be used with any objects, not * just registered ones. */ - ASN1_OBJECT *OBJ_txt2obj(const char *s, int no_name) { int nid = NID_undef; - ASN1_OBJECT *op; + ASN1_OBJECT *op = NULL; unsigned char *buf; unsigned char *p; const unsigned char *cp; int i, j; if (!no_name) { - if (((nid = OBJ_sn2nid(s)) != NID_undef) || - ((nid = OBJ_ln2nid(s)) != NID_undef)) + if ((nid = OBJ_sn2nid(s)) != NID_undef || + (nid = OBJ_ln2nid(s)) != NID_undef) { return OBJ_nid2obj(nid); + } if (!ossl_isdigit(*s)) { ERR_raise(ERR_LIB_OBJ, OBJ_R_UNKNOWN_OBJECT_NAME); return NULL; @@ -380,13 +381,9 @@ ASN1_OBJECT *OBJ_txt2obj(const char *s, int no_name) /* Work out size of content octets */ i = a2d_ASN1_OBJECT(NULL, 0, s, -1); - if (i <= 0) { - /* Don't clear the error */ - /* - * ERR_clear_error(); - */ + if (i <= 0) return NULL; - } + /* Work out total size */ j = ASN1_object_size(0, i, V_ASN1_OBJECT); if (j < 0) @@ -416,24 +413,23 @@ int OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *a, int no_name) unsigned long l; const unsigned char *p; char tbuf[DECIMAL_SIZE(i) + DECIMAL_SIZE(l) + 2]; + const char *s; /* Ensure that, at every state, |buf| is NUL-terminated. */ - if (buf && buf_len > 0) + if (buf != NULL && buf_len > 0) buf[0] = '\0'; - if ((a == NULL) || (a->data == NULL)) + if (a == NULL || a->data == NULL) return 0; if (!no_name && (nid = OBJ_obj2nid(a)) != NID_undef) { - const char *s; s = OBJ_nid2ln(nid); if (s == NULL) s = OBJ_nid2sn(nid); - if (s) { - if (buf) + if (s != NULL) { + if (buf != NULL) OPENSSL_strlcpy(buf, s, buf_len); - n = strlen(s); - return n; + return (int)strlen(s); } } @@ -545,11 +541,13 @@ int OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *a, int no_name) int OBJ_txt2nid(const char *s) { - ASN1_OBJECT *obj; - int nid; - obj = OBJ_txt2obj(s, 0); - nid = OBJ_obj2nid(obj); - ASN1_OBJECT_free(obj); + ASN1_OBJECT *obj = OBJ_txt2obj(s, 0); + int nid = NID_undef; + + if (obj != NULL) { + nid = OBJ_obj2nid(obj); + ASN1_OBJECT_free(obj); + } return nid; } @@ -559,22 +557,25 @@ int OBJ_ln2nid(const char *s) const ASN1_OBJECT *oo = &o; ADDED_OBJ ad, *adp; const unsigned int *op; - - /* Make sure we've loaded config before checking for any "added" objects */ - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL); + int nid = NID_undef; o.ln = s; + op = OBJ_bsearch_ln(&oo, ln_objs, NUM_LN); + if (op != NULL) + return nid_objs[*op].nid; + if (!ossl_obj_read_lock(1)) { + ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_READ_LOCK); + return NID_undef; + } if (added != NULL) { ad.type = ADDED_LNAME; ad.obj = &o; adp = lh_ADDED_OBJ_retrieve(added, &ad); if (adp != NULL) - return adp->obj->nid; + nid = adp->obj->nid; } - op = OBJ_bsearch_ln(&oo, ln_objs, NUM_LN); - if (op == NULL) - return NID_undef; - return nid_objs[*op].nid; + ossl_obj_unlock(1); + return nid; } int OBJ_sn2nid(const char *s) @@ -583,22 +584,25 @@ int OBJ_sn2nid(const char *s) const ASN1_OBJECT *oo = &o; ADDED_OBJ ad, *adp; const unsigned int *op; - - /* Make sure we've loaded config before checking for any "added" objects */ - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL); + int nid = NID_undef; o.sn = s; + op = OBJ_bsearch_sn(&oo, sn_objs, NUM_SN); + if (op != NULL) + return nid_objs[*op].nid; + if (!ossl_obj_read_lock(1)) { + ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_READ_LOCK); + return NID_undef; + } if (added != NULL) { ad.type = ADDED_SNAME; ad.obj = &o; adp = lh_ADDED_OBJ_retrieve(added, &ad); if (adp != NULL) - return adp->obj->nid; + nid = adp->obj->nid; } - op = OBJ_bsearch_sn(&oo, sn_objs, NUM_SN); - if (op == NULL) - return NID_undef; - return nid_objs[*op].nid; + ossl_obj_unlock(1); + return nid; } const void *OBJ_bsearch_(const void *key, const void *base, int num, int size, @@ -698,30 +702,36 @@ int OBJ_create(const char *oid, const char *sn, const char *ln) if ((sn != NULL && OBJ_sn2nid(sn) != NID_undef) || (ln != NULL && OBJ_ln2nid(ln) != NID_undef)) { ERR_raise(ERR_LIB_OBJ, OBJ_R_OID_EXISTS); - return 0; + goto err; } /* Convert numerical OID string to an ASN1_OBJECT structure */ tmpoid = OBJ_txt2obj(oid, 1); if (tmpoid == NULL) + goto err; + + if (!ossl_obj_write_lock(1)) { + ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_WRITE_LOCK); return 0; + } /* If NID is not NID_undef then object already exists */ - if (OBJ_obj2nid(tmpoid) != NID_undef) { + if (ossl_obj_obj2nid(tmpoid, 0) != NID_undef) { ERR_raise(ERR_LIB_OBJ, OBJ_R_OID_EXISTS); goto err; } - tmpoid->nid = OBJ_new_nid(1); + tmpoid->nid = ossl_obj_new_nid(1, 0); tmpoid->sn = (char *)sn; tmpoid->ln = (char *)ln; - ok = OBJ_add_object(tmpoid); + ok = ossl_obj_add_object(tmpoid, 0); tmpoid->sn = NULL; tmpoid->ln = NULL; err: + ossl_obj_unlock(1); ASN1_OBJECT_free(tmpoid); return ok; } @@ -739,3 +749,18 @@ const unsigned char *OBJ_get0_data(const ASN1_OBJECT *obj) return NULL; return obj->data; } + +int OBJ_new_nid(int num) +{ + return ossl_obj_new_nid(num, 1); +} + +int OBJ_add_object(const ASN1_OBJECT *obj) +{ + return ossl_obj_add_object(obj, 1); +} + +int OBJ_obj2nid(const ASN1_OBJECT *a) +{ + return ossl_obj_obj2nid(a, 1); +} |