56bd8cf0c288c90617427edb9929a87afea413ad
[openssl.git] / crypto / ex_data.c
1 /* crypto/ex_data.c */
2
3 /*
4  * Overhaul notes;
5  *
6  * This code is now *mostly* thread-safe. It is now easier to understand in what
7  * ways it is safe and in what ways it is not, which is an improvement. Firstly,
8  * all per-class stacks and index-counters for ex_data are stored in the same
9  * global LHASH table (keyed by class). This hash table uses locking for all
10  * access with the exception of CRYPTO_cleanup_all_ex_data(), which must only be
11  * called when no other threads can possibly race against it (even if it was
12  * locked, the race would mean it's possible the hash table might have been
13  * recreated after the cleanup). As classes can only be added to the hash table,
14  * and within each class, the stack of methods can only be incremented, the
15  * locking mechanics are simpler than they would otherwise be. For example, the
16  * new/dup/free ex_data functions will lock the hash table, copy the method
17  * pointers it needs from the relevant class, then unlock the hash table before
18  * actually applying those method pointers to the task of the new/dup/free
19  * operations. As they can't be removed from the method-stack, only
20  * supplemented, there's no race conditions associated with using them outside
21  * the lock. The get/set_ex_data functions are not locked because they do not
22  * involve this global state at all - they operate directly with a previously
23  * obtained per-class method index and a particular "ex_data" variable. These
24  * variables are usually instantiated per-context (eg. each RSA structure has
25  * one) so locking on read/write access to that variable can be locked locally
26  * if required (eg. using the "RSA" lock to synchronise access to a
27  * per-RSA-structure ex_data variable if required).
28  * [Geoff]
29  */
30
31 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
32  * All rights reserved.
33  *
34  * This package is an SSL implementation written
35  * by Eric Young (eay@cryptsoft.com).
36  * The implementation was written so as to conform with Netscapes SSL.
37  * 
38  * This library is free for commercial and non-commercial use as long as
39  * the following conditions are aheared to.  The following conditions
40  * apply to all code found in this distribution, be it the RC4, RSA,
41  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
42  * included with this distribution is covered by the same copyright terms
43  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
44  * 
45  * Copyright remains Eric Young's, and as such any Copyright notices in
46  * the code are not to be removed.
47  * If this package is used in a product, Eric Young should be given attribution
48  * as the author of the parts of the library used.
49  * This can be in the form of a textual message at program startup or
50  * in documentation (online or textual) provided with the package.
51  * 
52  * Redistribution and use in source and binary forms, with or without
53  * modification, are permitted provided that the following conditions
54  * are met:
55  * 1. Redistributions of source code must retain the copyright
56  *    notice, this list of conditions and the following disclaimer.
57  * 2. Redistributions in binary form must reproduce the above copyright
58  *    notice, this list of conditions and the following disclaimer in the
59  *    documentation and/or other materials provided with the distribution.
60  * 3. All advertising materials mentioning features or use of this software
61  *    must display the following acknowledgement:
62  *    "This product includes cryptographic software written by
63  *     Eric Young (eay@cryptsoft.com)"
64  *    The word 'cryptographic' can be left out if the rouines from the library
65  *    being used are not cryptographic related :-).
66  * 4. If you include any Windows specific code (or a derivative thereof) from 
67  *    the apps directory (application code) you must include an acknowledgement:
68  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
69  * 
70  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
71  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
72  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
73  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
74  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
75  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
76  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
77  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
78  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
79  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
80  * SUCH DAMAGE.
81  * 
82  * The licence and distribution terms for any publically available version or
83  * derivative of this code cannot be changed.  i.e. this code cannot simply be
84  * copied and put under another distribution licence
85  * [including the GNU Public Licence.]
86  */
87
88 #include <stdio.h>
89 #include <stdlib.h>
90 #include <openssl/buffer.h>
91 #include <openssl/bio.h>
92 #include <openssl/lhash.h>
93 #include "cryptlib.h"
94
95 /* What an "implementation of ex_data functionality" looks like */
96 struct st_CRYPTO_EX_DATA_IMPL
97         {
98         /*********************/
99         /* GLOBAL OPERATIONS */
100         /* Return a new class index */
101         int (*cb_new_class)(void);
102         /* Cleanup all state used by the implementation */
103         void (*cb_cleanup)(void);
104         /************************/
105         /* PER-CLASS OPERATIONS */
106         /* Get a new method index within a class */
107         int (*cb_get_new_index)(int class_index, long argl, void *argp,
108                         CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
109                         CRYPTO_EX_free *free_func);
110         /* Initialise a new CRYPTO_EX_DATA of a given class */
111         int (*cb_new_ex_data)(int class_index, void *obj,
112                         CRYPTO_EX_DATA *ad);
113         /* Duplicate a CRYPTO_EX_DATA of a given class onto a copy */
114         int (*cb_dup_ex_data)(int class_index, CRYPTO_EX_DATA *to,
115                         CRYPTO_EX_DATA *from);
116         /* Cleanup a CRYPTO_EX_DATA of a given class */
117         void (*cb_free_ex_data)(int class_index, void *obj,
118                         CRYPTO_EX_DATA *ad);
119         };
120
121 /* The implementation we use at run-time */
122 static const CRYPTO_EX_DATA_IMPL *impl = NULL;
123
124 /* To call "impl" functions, use this macro rather than referring to 'impl' directly, eg.
125  * EX_IMPL(get_new_index)(...); */
126 #define EX_IMPL(a) impl->cb_##a
127
128 /* Predeclare the "default" ex_data implementation */
129 static int int_new_class(void);
130 static void int_cleanup(void);
131 static int int_get_new_index(int class_index, long argl, void *argp,
132                 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
133                 CRYPTO_EX_free *free_func);
134 static int int_new_ex_data(int class_index, void *obj,
135                 CRYPTO_EX_DATA *ad);
136 static int int_dup_ex_data(int class_index, CRYPTO_EX_DATA *to,
137                 CRYPTO_EX_DATA *from);
138 static void int_free_ex_data(int class_index, void *obj,
139                 CRYPTO_EX_DATA *ad);
140 static CRYPTO_EX_DATA_IMPL impl_default =
141         {
142         int_new_class,
143         int_cleanup,
144         int_get_new_index,
145         int_new_ex_data,
146         int_dup_ex_data,
147         int_free_ex_data
148         };
149
150 /* Internal function that checks whether "impl" is set and if not, sets it to
151  * the default. */
152 static void impl_check(void)
153         {
154         CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
155         if(!impl)
156                 impl = &impl_default;
157         CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
158         }
159 /* A macro wrapper for impl_check that first uses a non-locked test before
160  * invoking the function (which checks again inside a lock). */
161 #define IMPL_CHECK if(!impl) impl_check();
162
163 /* API functions to get/set the "ex_data" implementation */
164 const CRYPTO_EX_DATA_IMPL *CRYPTO_get_ex_data_implementation(void)
165         {
166         IMPL_CHECK
167         return impl;
168         }
169 int CRYPTO_set_ex_data_implementation(const CRYPTO_EX_DATA_IMPL *i)
170         {
171         int toret = 0;
172         CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
173         if(!impl)
174                 {
175                 impl = i;
176                 toret = 1;
177                 }
178         CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
179         return toret;
180         }
181
182 /****************************************************************************/
183 /* Interal (default) implementation of "ex_data" support. API functions are
184  * further down. */
185
186 /* The type that represents what each "class" used to implement locally. A STACK
187  * of CRYPTO_EX_DATA_FUNCS plus a index-counter. The 'class_index' is the global
188  * value representing the class that is used to distinguish these items. */
189 typedef struct st_ex_class_item {
190         int class_index;
191         STACK_OF(CRYPTO_EX_DATA_FUNCS) *meth;
192         int meth_num;
193 } EX_CLASS_ITEM;
194
195 /* When assigning new class indexes, this is our counter */
196 static int ex_class = CRYPTO_EX_INDEX_USER;
197
198 /* The global hash table of EX_CLASS_ITEM items */
199 static LHASH *ex_data = NULL;
200
201 /* The callbacks required in the "ex_data" hash table */
202 static unsigned long ex_hash_cb(const void *a_void)
203         {
204         return ((const EX_CLASS_ITEM *)a_void)->class_index;
205         }
206 static int ex_cmp_cb(const void *a_void, const void *b_void)
207         {
208         return (((const EX_CLASS_ITEM *)a_void)->class_index -
209                 ((const EX_CLASS_ITEM *)b_void)->class_index);
210         }
211
212 /* Internal functions used by the "impl_default" implementation to access the
213  * state */
214
215 static int ex_data_check(void)
216         {
217         int toret = 1;
218         CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
219         if(!ex_data && ((ex_data = lh_new(ex_hash_cb, ex_cmp_cb)) == NULL))
220                 toret = 0;
221         CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
222         return toret;
223         }
224 /* This macros helps reduce the locking from repeated checks because the
225  * ex_data_check() function checks ex_data again inside a lock. */
226 #define EX_DATA_CHECK(iffail) if(!ex_data && !ex_data_check()) {iffail}
227
228 /* This "inner" callback is used by the callback function that follows it */
229 static void def_cleanup_util_cb(CRYPTO_EX_DATA_FUNCS *v)
230         {
231         OPENSSL_free(v);
232         }
233
234 /* This callback is used in lh_doall to destroy all EX_CLASS_ITEM values from
235  * "ex_data" prior to the ex_data hash table being itself destroyed. Doesn't do
236  * any locking. */
237 static void def_cleanup_cb(const void *a_void)
238         {
239         EX_CLASS_ITEM *item = (EX_CLASS_ITEM *)a_void;
240         sk_CRYPTO_EX_DATA_FUNCS_pop_free(item->meth, def_cleanup_util_cb);
241         OPENSSL_free(item);
242         }
243
244 /* Return the EX_CLASS_ITEM from the "ex_data" hash table that corresponds to a
245  * given class. Handles locking. */
246 static EX_CLASS_ITEM *def_get_class(int class_index)
247         {
248         EX_CLASS_ITEM d, *p, *gen;
249         EX_DATA_CHECK(return NULL;)
250         d.class_index = class_index;
251         CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
252         p = lh_retrieve(ex_data, &d);
253         if(!p)
254                 {
255                 gen = OPENSSL_malloc(sizeof(EX_CLASS_ITEM));
256                 if(gen)
257                         {
258                         gen->class_index = class_index;
259                         gen->meth_num = 0;
260                         gen->meth = sk_CRYPTO_EX_DATA_FUNCS_new_null();
261                         if(!gen->meth)
262                                 OPENSSL_free(gen);
263                         else
264                                 {
265                                 /* Because we're inside the ex_data lock, the
266                                  * return value from the insert will be NULL */
267                                 lh_insert(ex_data, gen);
268                                 p = gen;
269                                 }
270                         }
271                 }
272         CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
273         if(!p)
274                 CRYPTOerr(CRYPTO_F_DEF_GET_CLASS,ERR_R_MALLOC_FAILURE);
275         return p;
276         }
277
278 /* Add a new method to the given EX_CLASS_ITEM and return the corresponding
279  * index (or -1 for error). Handles locking. */
280 static int def_add_index(EX_CLASS_ITEM *item, long argl, void *argp,
281                 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
282                 CRYPTO_EX_free *free_func)
283         {
284         int toret = -1;
285         CRYPTO_EX_DATA_FUNCS *a = (CRYPTO_EX_DATA_FUNCS *)OPENSSL_malloc(
286                                         sizeof(CRYPTO_EX_DATA_FUNCS));
287         if(!a)
288                 {
289                 CRYPTOerr(CRYPTO_F_DEF_ADD_INDEX,ERR_R_MALLOC_FAILURE);
290                 return -1;
291                 }
292         a->argl=argl;
293         a->argp=argp;
294         a->new_func=new_func;
295         a->dup_func=dup_func;
296         a->free_func=free_func;
297         CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
298         while (sk_CRYPTO_EX_DATA_FUNCS_num(item->meth) <= item->meth_num)
299                 {
300                 if (!sk_CRYPTO_EX_DATA_FUNCS_push(item->meth, NULL))
301                         {
302                         CRYPTOerr(CRYPTO_F_DEF_ADD_INDEX,ERR_R_MALLOC_FAILURE);
303                         OPENSSL_free(a);
304                         goto err;
305                         }
306                 }
307         toret = item->meth_num++;
308         sk_CRYPTO_EX_DATA_FUNCS_set(item->meth, toret, a);
309 err:
310         CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
311         return toret;
312         }
313
314 /**************************************************************/
315 /* The functions in the default CRYPTO_EX_DATA_IMPL structure */
316
317 static int int_new_class(void)
318         {
319         int toret;
320         CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
321         toret = ex_class++;
322         CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
323         return toret;
324         }
325
326 static void int_cleanup(void)
327         {
328         EX_DATA_CHECK(return;)
329         lh_doall(ex_data, def_cleanup_cb);
330         lh_free(ex_data);
331         ex_data = NULL;
332         impl = NULL;
333         }
334
335 static int int_get_new_index(int class_index, long argl, void *argp,
336                 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
337                 CRYPTO_EX_free *free_func)
338         {
339         EX_CLASS_ITEM *item = def_get_class(class_index);
340         if(!item)
341                 return -1;
342         return def_add_index(item, argl, argp, new_func, dup_func, free_func);
343         }
344
345 /* Thread-safe by copying a class's array of "CRYPTO_EX_DATA_FUNCS" entries in
346  * the lock, then using them outside the lock. NB: Thread-safety only applies to
347  * the global "ex_data" state (ie. class definitions), not thread-safe on 'ad'
348  * itself. */
349 static int int_new_ex_data(int class_index, void *obj,
350                 CRYPTO_EX_DATA *ad)
351         {
352         int max,i;
353         void *ptr;
354         CRYPTO_EX_DATA_FUNCS **storage = NULL;
355         EX_CLASS_ITEM *item = def_get_class(class_index);
356         if(!item)
357                 /* error is already set */
358                 return 0;
359         ad->sk = NULL;
360         CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
361         max = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth);
362         if(max > 0)
363                 {
364                 storage = OPENSSL_malloc(max * sizeof(CRYPTO_EX_DATA_FUNCS*));
365                 if(!storage)
366                         goto skip;
367                 for(i = 0; i < max; i++)
368                         storage[i] = sk_CRYPTO_EX_DATA_FUNCS_value(item->meth,i);
369                 }
370 skip:
371         CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
372         if((max > 0) && !storage)
373                 {
374                 CRYPTOerr(CRYPTO_F_INT_NEW_EX_DATA,ERR_R_MALLOC_FAILURE);
375                 return 0;
376                 }
377         for(i = 0; i < max; i++)
378                 {
379                 if(storage[i] && storage[i]->new_func)
380                         {
381                         ptr = CRYPTO_get_ex_data(ad, i);
382                         storage[i]->new_func(obj,ptr,ad,i,
383                                 storage[i]->argl,storage[i]->argp);
384                         }
385                 }
386         if(storage)
387                 OPENSSL_free(storage);
388         return 1;
389         }
390
391 /* Same thread-safety notes as for "int_new_ex_data" */
392 static int int_dup_ex_data(int class_index, CRYPTO_EX_DATA *to,
393                 CRYPTO_EX_DATA *from)
394         {
395         int max, j, i;
396         char *ptr;
397         CRYPTO_EX_DATA_FUNCS **storage = NULL;
398         EX_CLASS_ITEM *item;
399         if(!from->sk)
400                 /* 'to' should be "blank" which *is* just like 'from' */
401                 return 1;
402         if((item = def_get_class(class_index)) == NULL)
403                 return 0;
404         CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
405         max = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth);
406         j = sk_num(from->sk);
407         if(j < max)
408                 max = j;
409         if(max > 0)
410                 {
411                 storage = OPENSSL_malloc(max * sizeof(CRYPTO_EX_DATA_FUNCS*));
412                 if(!storage)
413                         goto skip;
414                 for(i = 0; i < max; i++)
415                         storage[i] = sk_CRYPTO_EX_DATA_FUNCS_value(item->meth,i);
416                 }
417 skip:
418         CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
419         if((max > 0) && !storage)
420                 {
421                 CRYPTOerr(CRYPTO_F_INT_DUP_EX_DATA,ERR_R_MALLOC_FAILURE);
422                 return 0;
423                 }
424         for(i = 0; i < max; i++)
425                 {
426                 ptr = CRYPTO_get_ex_data(from, i);
427                 if(storage[i] && storage[i]->dup_func)
428                         storage[i]->dup_func(to,from,&ptr,i,
429                                 storage[i]->argl,storage[i]->argp);
430                 CRYPTO_set_ex_data(to,i,ptr);
431                 }
432         if(storage)
433                 OPENSSL_free(storage);
434         return 1;
435         }
436
437 /* Same thread-safety notes as for "int_new_ex_data" */
438 static void int_free_ex_data(int class_index, void *obj,
439                 CRYPTO_EX_DATA *ad)
440         {
441         int max,i;
442         EX_CLASS_ITEM *item;
443         void *ptr;
444         CRYPTO_EX_DATA_FUNCS **storage = NULL;
445         if((item = def_get_class(class_index)) == NULL)
446                 return;
447         CRYPTO_r_lock(CRYPTO_LOCK_EX_DATA);
448         max = sk_CRYPTO_EX_DATA_FUNCS_num(item->meth);
449         if(max > 0)
450                 {
451                 storage = OPENSSL_malloc(max * sizeof(CRYPTO_EX_DATA_FUNCS*));
452                 if(!storage)
453                         goto skip;
454                 for(i = 0; i < max; i++)
455                         storage[i] = sk_CRYPTO_EX_DATA_FUNCS_value(item->meth,i);
456                 }
457 skip:
458         CRYPTO_r_unlock(CRYPTO_LOCK_EX_DATA);
459         if((max > 0) && !storage)
460                 {
461                 CRYPTOerr(CRYPTO_F_INT_FREE_EX_DATA,ERR_R_MALLOC_FAILURE);
462                 return;
463                 }
464         for(i = 0; i < max; i++)
465                 {
466                 if(storage[i] && storage[i]->free_func)
467                         {
468                         ptr = CRYPTO_get_ex_data(ad,i);
469                         storage[i]->free_func(obj,ptr,ad,i,
470                                 storage[i]->argl,storage[i]->argp);
471                         }
472                 }
473         if(storage)
474                 OPENSSL_free(storage);
475         if(ad->sk)
476                 {
477                 sk_free(ad->sk);
478                 ad->sk=NULL;
479                 }
480         }
481
482 /********************************************************************/
483 /* API functions that defer all "state" operations to the "ex_data"
484  * implementation we have set. */
485
486 /* Obtain an index for a new class (not the same as getting a new index within
487  * an existing class - this is actually getting a new *class*) */
488 int CRYPTO_ex_data_new_class(void)
489         {
490         IMPL_CHECK
491         return EX_IMPL(new_class)();
492         }
493
494 /* Release all "ex_data" state to prevent memory leaks. This can't be made
495  * thread-safe without overhauling a lot of stuff, and shouldn't really be
496  * called under potential race-conditions anyway (it's for program shutdown
497  * after all). */
498 void CRYPTO_cleanup_all_ex_data(void)
499         {
500         IMPL_CHECK
501         EX_IMPL(cleanup)();
502         }
503
504 /* Inside an existing class, get/register a new index. */
505 int CRYPTO_get_ex_new_index(int class_index, long argl, void *argp,
506                 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
507                 CRYPTO_EX_free *free_func)
508         {
509         int ret = -1;
510
511         IMPL_CHECK
512         ret = EX_IMPL(get_new_index)(class_index,
513                         argl, argp, new_func, dup_func, free_func);
514         return ret;
515         }
516
517 /* Initialise a new CRYPTO_EX_DATA for use in a particular class - including
518  * calling new() callbacks for each index in the class used by this variable */
519 int CRYPTO_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
520         {
521         IMPL_CHECK
522         return EX_IMPL(new_ex_data)(class_index, obj, ad);
523         }
524
525 /* Duplicate a CRYPTO_EX_DATA variable - including calling dup() callbacks for
526  * each index in the class used by this variable */
527 int CRYPTO_dup_ex_data(int class_index, CRYPTO_EX_DATA *to,
528              CRYPTO_EX_DATA *from)
529         {
530         IMPL_CHECK
531         return EX_IMPL(dup_ex_data)(class_index, to, from);
532         }
533
534 /* Cleanup a CRYPTO_EX_DATA variable - including calling free() callbacks for
535  * each index in the class used by this variable */
536 void CRYPTO_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
537         {
538         IMPL_CHECK
539         EX_IMPL(free_ex_data)(class_index, obj, ad);
540         }
541
542 /* For a given CRYPTO_EX_DATA variable, set the value corresponding to a
543  * particular index in the class used by this variable */
544 int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int idx, void *val)
545         {
546         int i;
547
548         if (ad->sk == NULL)
549                 {
550                 if ((ad->sk=sk_new_null()) == NULL)
551                         {
552                         CRYPTOerr(CRYPTO_F_CRYPTO_SET_EX_DATA,ERR_R_MALLOC_FAILURE);
553                         return(0);
554                         }
555                 }
556         i=sk_num(ad->sk);
557
558         while (i <= idx)
559                 {
560                 if (!sk_push(ad->sk,NULL))
561                         {
562                         CRYPTOerr(CRYPTO_F_CRYPTO_SET_EX_DATA,ERR_R_MALLOC_FAILURE);
563                         return(0);
564                         }
565                 i++;
566                 }
567         sk_set(ad->sk,idx,val);
568         return(1);
569         }
570
571 /* For a given CRYPTO_EX_DATA_ variable, get the value corresponding to a
572  * particular index in the class used by this variable */
573 void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int idx)
574         {
575         if (ad->sk == NULL)
576                 return(0);
577         else if (idx >= sk_num(ad->sk))
578                 return(0);
579         else
580                 return(sk_value(ad->sk,idx));
581         }
582
583 IMPLEMENT_STACK_OF(CRYPTO_EX_DATA_FUNCS)