ssl/libcrypto/src/crypto/engine/eng_table.c
changeset 0 e4d67989cc36
equal deleted inserted replaced
-1:000000000000 0:e4d67989cc36
       
     1 /* ====================================================================
       
     2  * Copyright (c) 2001 The OpenSSL Project.  All rights reserved.
       
     3  *
       
     4  * Redistribution and use in source and binary forms, with or without
       
     5  * modification, are permitted provided that the following conditions
       
     6  * are met:
       
     7  *
       
     8  * 1. Redistributions of source code must retain the above copyright
       
     9  *    notice, this list of conditions and the following disclaimer. 
       
    10  *
       
    11  * 2. Redistributions in binary form must reproduce the above copyright
       
    12  *    notice, this list of conditions and the following disclaimer in
       
    13  *    the documentation and/or other materials provided with the
       
    14  *    distribution.
       
    15  *
       
    16  * 3. All advertising materials mentioning features or use of this
       
    17  *    software must display the following acknowledgment:
       
    18  *    "This product includes software developed by the OpenSSL Project
       
    19  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
       
    20  *
       
    21  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
       
    22  *    endorse or promote products derived from this software without
       
    23  *    prior written permission. For written permission, please contact
       
    24  *    licensing@OpenSSL.org.
       
    25  *
       
    26  * 5. Products derived from this software may not be called "OpenSSL"
       
    27  *    nor may "OpenSSL" appear in their names without prior written
       
    28  *    permission of the OpenSSL Project.
       
    29  *
       
    30  * 6. Redistributions of any form whatsoever must retain the following
       
    31  *    acknowledgment:
       
    32  *    "This product includes software developed by the OpenSSL Project
       
    33  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
       
    34  *
       
    35  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
       
    36  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       
    37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    38  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
       
    39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
       
    40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
       
    41  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
       
    42  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
       
    43  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
       
    44  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
       
    45  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
       
    46  * OF THE POSSIBILITY OF SUCH DAMAGE.
       
    47  * ====================================================================
       
    48  *
       
    49  * This product includes cryptographic software written by Eric Young
       
    50  * (eay@cryptsoft.com).  This product includes software written by Tim
       
    51  * Hudson (tjh@cryptsoft.com).
       
    52  *
       
    53  */
       
    54  /*
       
    55  © Portions copyright (c) 2006 Nokia Corporation.  All rights reserved.
       
    56  */
       
    57  
       
    58 
       
    59 #include "cryptlib.h"
       
    60 #include <openssl/evp.h>
       
    61 #include <openssl/lhash.h>
       
    62 #include "eng_int.h"
       
    63 #if (defined(SYMBIAN) && (defined(__WINSCW__) || defined(__WINS__)))
       
    64 #include "libcrypto_wsd_macros.h"
       
    65 #include "libcrypto_wsd.h"
       
    66 #endif
       
    67 
       
    68 /* The type of the items in the table */
       
    69 typedef struct st_engine_pile
       
    70 	{
       
    71 	/* The 'nid' of this algorithm/mode */
       
    72 	int nid;
       
    73 	/* ENGINEs that implement this algorithm/mode. */
       
    74 	STACK_OF(ENGINE) *sk;
       
    75 	/* The default ENGINE to perform this algorithm/mode. */
       
    76 	ENGINE *funct;
       
    77 	/* Zero if 'sk' is newer than the cached 'funct', non-zero otherwise */
       
    78 	int uptodate;
       
    79 	} ENGINE_PILE;
       
    80 
       
    81 /* The type exposed in eng_int.h */
       
    82 struct st_engine_table
       
    83 	{
       
    84 	LHASH piles;
       
    85 	}; /* ENGINE_TABLE */
       
    86 
       
    87 /* Global flags (ENGINE_TABLE_FLAG_***). */
       
    88 #ifndef EMULATOR
       
    89 static unsigned int table_flags = 0;
       
    90 #else
       
    91 GET_STATIC_VAR_FROM_TLS(table_flags,eng_table,unsigned int)
       
    92 #define table_flags (*GET_WSD_VAR_NAME(table_flags,eng_table, s)())
       
    93 #endif
       
    94 
       
    95 /* API function manipulating 'table_flags' */
       
    96 EXPORT_C unsigned int ENGINE_get_table_flags(void)
       
    97 	{
       
    98 	return table_flags;
       
    99 	}
       
   100 EXPORT_C void ENGINE_set_table_flags(unsigned int flags)
       
   101 	{
       
   102 	table_flags = flags;
       
   103 	}
       
   104 
       
   105 /* Internal functions for the "piles" hash table */
       
   106 static unsigned long engine_pile_hash(const ENGINE_PILE *c)
       
   107 	{
       
   108 	return c->nid;
       
   109 	}
       
   110 static int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b)
       
   111 	{
       
   112 	return a->nid - b->nid;
       
   113 	}
       
   114 static IMPLEMENT_LHASH_HASH_FN(engine_pile_hash, const ENGINE_PILE *)
       
   115 static IMPLEMENT_LHASH_COMP_FN(engine_pile_cmp, const ENGINE_PILE *)
       
   116 static int int_table_check(ENGINE_TABLE **t, int create)
       
   117 	{
       
   118 	LHASH *lh;
       
   119 	if(*t) return 1;
       
   120 	if(!create) return 0;
       
   121 	if((lh = lh_new(LHASH_HASH_FN(engine_pile_hash),
       
   122 			LHASH_COMP_FN(engine_pile_cmp))) == NULL)
       
   123 		return 0;
       
   124 	*t = (ENGINE_TABLE *)lh;
       
   125 	return 1;
       
   126 	}
       
   127 
       
   128 /* Privately exposed (via eng_int.h) functions for adding and/or removing
       
   129  * ENGINEs from the implementation table */
       
   130 EXPORT_C int engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup,
       
   131 		ENGINE *e, const int *nids, int num_nids, int setdefault)
       
   132 	{
       
   133 	int ret = 0, added = 0;
       
   134 	ENGINE_PILE tmplate, *fnd;
       
   135 	CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
       
   136 	if(!(*table))
       
   137 		added = 1;
       
   138 	if(!int_table_check(table, 1))
       
   139 		goto end;
       
   140 	if(added)
       
   141 		/* The cleanup callback needs to be added */
       
   142 		engine_cleanup_add_first(cleanup);
       
   143 	while(num_nids--)
       
   144 		{
       
   145 		tmplate.nid = *nids;
       
   146 		fnd = lh_retrieve(&(*table)->piles, &tmplate);
       
   147 		if(!fnd)
       
   148 			{
       
   149 			fnd = OPENSSL_malloc(sizeof(ENGINE_PILE));
       
   150 			if(!fnd) goto end;
       
   151 			fnd->uptodate = 0;
       
   152 			fnd->nid = *nids;
       
   153 			fnd->sk = sk_ENGINE_new_null();
       
   154 			if(!fnd->sk)
       
   155 				{
       
   156 				OPENSSL_free(fnd);
       
   157 				goto end;
       
   158 				}
       
   159 			fnd->funct = NULL;
       
   160 			lh_insert(&(*table)->piles, fnd);
       
   161 			}
       
   162 		/* A registration shouldn't add duplciate entries */
       
   163 		(void)sk_ENGINE_delete_ptr(fnd->sk, e);
       
   164 		/* if 'setdefault', this ENGINE goes to the head of the list */
       
   165 		if(!sk_ENGINE_push(fnd->sk, e))
       
   166 			goto end;
       
   167 		/* "touch" this ENGINE_PILE */
       
   168 		fnd->uptodate = 1;
       
   169 		if(setdefault)
       
   170 			{
       
   171 			if(!engine_unlocked_init(e))
       
   172 				{
       
   173 				ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER,
       
   174 						ENGINE_R_INIT_FAILED);
       
   175 				goto end;
       
   176 				}
       
   177 			if(fnd->funct)
       
   178 				engine_unlocked_finish(fnd->funct, 0);
       
   179 			fnd->funct = e;
       
   180 			}
       
   181 		nids++;
       
   182 		}
       
   183 	ret = 1;
       
   184 end:
       
   185 	CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
       
   186 	return ret;
       
   187 	}
       
   188 static void int_unregister_cb(ENGINE_PILE *pile, ENGINE *e)
       
   189 	{
       
   190 	int n;
       
   191 	/* Iterate the 'c->sk' stack removing any occurance of 'e' */
       
   192 	while((n = sk_ENGINE_find(pile->sk, e)) >= 0)
       
   193 		{
       
   194 		(void)sk_ENGINE_delete(pile->sk, n);
       
   195 		/* "touch" this ENGINE_CIPHER */
       
   196 		pile->uptodate = 1;
       
   197 		}
       
   198 	if(pile->funct == e)
       
   199 		{
       
   200 		engine_unlocked_finish(e, 0);
       
   201 		pile->funct = NULL;
       
   202 		}
       
   203 	}
       
   204 static IMPLEMENT_LHASH_DOALL_ARG_FN(int_unregister_cb,ENGINE_PILE *,ENGINE *)
       
   205 EXPORT_C void engine_table_unregister(ENGINE_TABLE **table, ENGINE *e)
       
   206 	{
       
   207 	CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
       
   208 	if(int_table_check(table, 0))
       
   209 		lh_doall_arg(&(*table)->piles,
       
   210 			LHASH_DOALL_ARG_FN(int_unregister_cb), e);
       
   211 	CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
       
   212 	}
       
   213 
       
   214 static void int_cleanup_cb(ENGINE_PILE *p)
       
   215 	{
       
   216 	sk_ENGINE_free(p->sk);
       
   217 	if(p->funct)
       
   218 		engine_unlocked_finish(p->funct, 0);
       
   219 	OPENSSL_free(p);
       
   220 	}
       
   221 static IMPLEMENT_LHASH_DOALL_FN(int_cleanup_cb,ENGINE_PILE *)
       
   222 EXPORT_C void engine_table_cleanup(ENGINE_TABLE **table)
       
   223 	{
       
   224 	CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
       
   225 	if(*table)
       
   226 		{
       
   227 		lh_doall(&(*table)->piles, LHASH_DOALL_FN(int_cleanup_cb));
       
   228 		lh_free(&(*table)->piles);
       
   229 		*table = NULL;
       
   230 		}
       
   231 	CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
       
   232 	}
       
   233 
       
   234 /* return a functional reference for a given 'nid' */
       
   235 #ifndef ENGINE_TABLE_DEBUG
       
   236 EXPORT_C ENGINE *engine_table_select(ENGINE_TABLE **table, int nid)
       
   237 #else
       
   238 EXPORT_C ENGINE *engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f, int l)
       
   239 #endif
       
   240 	{
       
   241 	ENGINE *ret = NULL;
       
   242 	ENGINE_PILE tmplate, *fnd=NULL;
       
   243 	int initres, loop = 0;
       
   244 
       
   245 	if(!(*table))
       
   246 		{
       
   247 #ifdef ENGINE_TABLE_DEBUG
       
   248 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing "
       
   249 			"registered!\n", f, l, nid);
       
   250 #endif
       
   251 		return NULL;
       
   252 		}
       
   253 	CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
       
   254 	/* Check again inside the lock otherwise we could race against cleanup
       
   255 	 * operations. But don't worry about a fprintf(stderr). */
       
   256 	if(!int_table_check(table, 0)) goto end;
       
   257 	tmplate.nid = nid;
       
   258 	fnd = lh_retrieve(&(*table)->piles, &tmplate);
       
   259 	if(!fnd) goto end;
       
   260 	if(fnd->funct && engine_unlocked_init(fnd->funct))
       
   261 		{
       
   262 #ifdef ENGINE_TABLE_DEBUG
       
   263 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
       
   264 			"ENGINE '%s' cached\n", f, l, nid, fnd->funct->id);
       
   265 #endif
       
   266 		ret = fnd->funct;
       
   267 		goto end;
       
   268 		}
       
   269 	if(fnd->uptodate)
       
   270 		{
       
   271 		ret = fnd->funct;
       
   272 		goto end;
       
   273 		}
       
   274 trynext:
       
   275 	ret = sk_ENGINE_value(fnd->sk, loop++);
       
   276 	if(!ret)
       
   277 		{
       
   278 #ifdef ENGINE_TABLE_DEBUG
       
   279 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no "
       
   280 				"registered implementations would initialise\n",
       
   281 				f, l, nid);
       
   282 #endif
       
   283 		goto end;
       
   284 		}
       
   285 	/* Try to initialise the ENGINE? */
       
   286 	if((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT))
       
   287 		initres = engine_unlocked_init(ret);
       
   288 	else
       
   289 		initres = 0;
       
   290 	if(initres)
       
   291 		{
       
   292 		/* Update 'funct' */
       
   293 		if((fnd->funct != ret) && engine_unlocked_init(ret))
       
   294 			{
       
   295 			/* If there was a previous default we release it. */
       
   296 			if(fnd->funct)
       
   297 				engine_unlocked_finish(fnd->funct, 0);
       
   298 			fnd->funct = ret;
       
   299 #ifdef ENGINE_TABLE_DEBUG
       
   300 			fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, "
       
   301 				"setting default to '%s'\n", f, l, nid, ret->id);
       
   302 #endif
       
   303 			}
       
   304 #ifdef ENGINE_TABLE_DEBUG
       
   305 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
       
   306 				"newly initialised '%s'\n", f, l, nid, ret->id);
       
   307 #endif
       
   308 		goto end;
       
   309 		}
       
   310 	goto trynext;
       
   311 end:
       
   312 	/* If it failed, it is unlikely to succeed again until some future
       
   313 	 * registrations have taken place. In all cases, we cache. */
       
   314 	if(fnd) fnd->uptodate = 1;
       
   315 #ifdef ENGINE_TABLE_DEBUG
       
   316 	if(ret)
       
   317 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching "
       
   318 				"ENGINE '%s'\n", f, l, nid, ret->id);
       
   319 	else
       
   320 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching "
       
   321 				"'no matching ENGINE'\n", f, l, nid);
       
   322 #endif
       
   323 	CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
       
   324 	/* Whatever happened, any failed init()s are not failures in this
       
   325 	 * context, so clear our error state. */
       
   326 	ERR_clear_error();
       
   327 	return ret;
       
   328 	}