]> git.ipfire.org Git - thirdparty/openssl.git/blobdiff - crypto/objects/obj_dat.c
obj: make the OBJ_ calls thread safe
[thirdparty/openssl.git] / crypto / objects / obj_dat.c
index 653cc9ad94dcdc3983102f2c701085b8c405a87f..04d116eafeef37d635556baf4c96c767580d4b0c 100644 (file)
@@ -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);
+}