1 /* 2 * Copyright 2006-2022 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #include <openssl/objects.h> 11 #include "obj_xref.h" 12 #include "internal/nelem.h" 13 #include "internal/thread_once.h" 14 #include <openssl/err.h> 15 16 static STACK_OF(nid_triple) *sig_app, *sigx_app; 17 static CRYPTO_RWLOCK *sig_lock; 18 19 static int sig_cmp(const nid_triple *a, const nid_triple *b) 20 { 21 return a->sign_id - b->sign_id; 22 } 23 24 DECLARE_OBJ_BSEARCH_CMP_FN(nid_triple, nid_triple, sig); 25 IMPLEMENT_OBJ_BSEARCH_CMP_FN(nid_triple, nid_triple, sig); 26 27 static int sig_sk_cmp(const nid_triple *const *a, const nid_triple *const *b) 28 { 29 return (*a)->sign_id - (*b)->sign_id; 30 } 31 32 DECLARE_OBJ_BSEARCH_CMP_FN(const nid_triple *, const nid_triple *, sigx); 33 34 static int sigx_cmp(const nid_triple *const *a, const nid_triple *const *b) 35 { 36 int ret; 37 38 ret = (*a)->hash_id - (*b)->hash_id; 39 /* The "b" side of the comparison carries the algorithms already 40 * registered. A NID_undef for 'hash_id' there means that the 41 * signature algorithm doesn't need a digest to operate OK. In 42 * such case, any hash_id/digest algorithm on the test side (a), 43 * incl. NID_undef, is acceptable. signature algorithm NID 44 * (pkey_id) must match in any case. 45 */ 46 if ((ret != 0) && ((*b)->hash_id != NID_undef)) 47 return ret; 48 return (*a)->pkey_id - (*b)->pkey_id; 49 } 50 51 IMPLEMENT_OBJ_BSEARCH_CMP_FN(const nid_triple *, const nid_triple *, sigx); 52 53 static CRYPTO_ONCE sig_init = CRYPTO_ONCE_STATIC_INIT; 54 55 DEFINE_RUN_ONCE_STATIC(o_sig_init) 56 { 57 sig_lock = CRYPTO_THREAD_lock_new(); 58 return sig_lock != NULL; 59 } 60 61 static ossl_inline int obj_sig_init(void) 62 { 63 return RUN_ONCE(&sig_init, o_sig_init); 64 } 65 66 static int ossl_obj_find_sigid_algs(int signid, int *pdig_nid, int *ppkey_nid, 67 int lock) 68 { 69 nid_triple tmp; 70 const nid_triple *rv; 71 int idx; 72 73 if (signid == NID_undef) 74 return 0; 75 76 tmp.sign_id = signid; 77 rv = OBJ_bsearch_sig(&tmp, sigoid_srt, OSSL_NELEM(sigoid_srt)); 78 if (rv == NULL) { 79 if (!obj_sig_init()) 80 return 0; 81 if (lock && !CRYPTO_THREAD_read_lock(sig_lock)) { 82 ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_READ_LOCK); 83 return 0; 84 } 85 if (sig_app != NULL) { 86 idx = sk_nid_triple_find(sig_app, &tmp); 87 if (idx >= 0) 88 rv = sk_nid_triple_value(sig_app, idx); 89 } 90 if (lock) 91 CRYPTO_THREAD_unlock(sig_lock); 92 if (rv == NULL) 93 return 0; 94 } 95 96 if (pdig_nid != NULL) 97 *pdig_nid = rv->hash_id; 98 if (ppkey_nid != NULL) 99 *ppkey_nid = rv->pkey_id; 100 return 1; 101 } 102 103 int OBJ_find_sigid_algs(int signid, int *pdig_nid, int *ppkey_nid) 104 { 105 return ossl_obj_find_sigid_algs(signid, pdig_nid, ppkey_nid, 1); 106 } 107 108 int OBJ_find_sigid_by_algs(int *psignid, int dig_nid, int pkey_nid) 109 { 110 nid_triple tmp; 111 const nid_triple *t = &tmp; 112 const nid_triple **rv; 113 int idx; 114 115 /* permitting searches for sig algs without digest: */ 116 if (pkey_nid == NID_undef) 117 return 0; 118 119 tmp.hash_id = dig_nid; 120 tmp.pkey_id = pkey_nid; 121 122 rv = OBJ_bsearch_sigx(&t, sigoid_srt_xref, OSSL_NELEM(sigoid_srt_xref)); 123 if (rv == NULL) { 124 if (!obj_sig_init()) 125 return 0; 126 if (!CRYPTO_THREAD_read_lock(sig_lock)) { 127 ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_READ_LOCK); 128 return 0; 129 } 130 if (sigx_app != NULL) { 131 idx = sk_nid_triple_find(sigx_app, &tmp); 132 if (idx >= 0) { 133 t = sk_nid_triple_value(sigx_app, idx); 134 rv = &t; 135 } 136 } 137 CRYPTO_THREAD_unlock(sig_lock); 138 if (rv == NULL) 139 return 0; 140 } 141 142 if (psignid != NULL) 143 *psignid = (*rv)->sign_id; 144 return 1; 145 } 146 147 int OBJ_add_sigid(int signid, int dig_id, int pkey_id) 148 { 149 nid_triple *ntr; 150 int dnid = NID_undef, pnid = NID_undef, ret = 0; 151 152 if (signid == NID_undef || pkey_id == NID_undef) 153 return 0; 154 155 if (!obj_sig_init()) 156 return 0; 157 158 if ((ntr = OPENSSL_malloc(sizeof(*ntr))) == NULL) 159 return 0; 160 ntr->sign_id = signid; 161 ntr->hash_id = dig_id; 162 ntr->pkey_id = pkey_id; 163 164 if (!CRYPTO_THREAD_write_lock(sig_lock)) { 165 ERR_raise(ERR_LIB_OBJ, ERR_R_UNABLE_TO_GET_WRITE_LOCK); 166 OPENSSL_free(ntr); 167 return 0; 168 } 169 170 /* Check that the entry doesn't exist or exists as desired */ 171 if (ossl_obj_find_sigid_algs(signid, &dnid, &pnid, 0)) { 172 ret = dnid == dig_id && pnid == pkey_id; 173 goto err; 174 } 175 176 if (sig_app == NULL) { 177 sig_app = sk_nid_triple_new(sig_sk_cmp); 178 if (sig_app == NULL) 179 goto err; 180 } 181 if (sigx_app == NULL) { 182 sigx_app = sk_nid_triple_new(sigx_cmp); 183 if (sigx_app == NULL) 184 goto err; 185 } 186 187 /* 188 * Better might be to find where to insert the element and insert it there. 189 * This would avoid the sorting steps below. 190 */ 191 if (!sk_nid_triple_push(sig_app, ntr)) 192 goto err; 193 if (!sk_nid_triple_push(sigx_app, ntr)) { 194 ntr = NULL; /* This is referenced by sig_app still */ 195 goto err; 196 } 197 198 sk_nid_triple_sort(sig_app); 199 sk_nid_triple_sort(sigx_app); 200 201 ntr = NULL; 202 ret = 1; 203 err: 204 OPENSSL_free(ntr); 205 CRYPTO_THREAD_unlock(sig_lock); 206 return ret; 207 } 208 209 static void sid_free(nid_triple *tt) 210 { 211 OPENSSL_free(tt); 212 } 213 214 void OBJ_sigid_free(void) 215 { 216 sk_nid_triple_pop_free(sig_app, sid_free); 217 sk_nid_triple_free(sigx_app); 218 CRYPTO_THREAD_lock_free(sig_lock); 219 sig_app = NULL; 220 sigx_app = NULL; 221 sig_lock = NULL; 222 } 223