1/* module.c - Module management for libgcrypt.
2 * Copyright (C) 2003, 2008 Free Software Foundation, Inc.
3 *
4 * This file is part of Libgcrypt.
5 *
6 * Libgcrypt is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser general Public License as
8 * published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
10 *
11 * Libgcrypt is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <config.h>
21#include <errno.h>
22#include "g10lib.h"
23
24/* Please match these numbers with the allocated algorithm
25   numbers.  */
26#define MODULE_ID_MIN 600
27#define MODULE_ID_LAST 65500
28#define MODULE_ID_USER GCRY_MODULE_ID_USER
29#define MODULE_ID_USER_LAST GCRY_MODULE_ID_USER_LAST
30
31#if MODULE_ID_MIN >= MODULE_ID_USER
32#error Need to implement a different search strategy
33#endif
34
35/* Internal function.  Generate a new, unique module ID for a module
36   that should be inserted into the module chain starting at
37   MODULES.  */
38static gcry_err_code_t
39_gcry_module_id_new (gcry_module_t modules, unsigned int *id_new)
40{
41  unsigned int mod_id;
42  gcry_err_code_t err = GPG_ERR_NO_ERROR;
43  gcry_module_t module;
44
45  /* Search for unused ID.  */
46  for (mod_id = MODULE_ID_MIN; mod_id < MODULE_ID_LAST; mod_id++)
47    {
48      if (mod_id == MODULE_ID_USER)
49        {
50          mod_id = MODULE_ID_USER_LAST;
51          continue;
52        }
53
54      /* Search for a module with the current ID.  */
55      for (module = modules; module; module = module->next)
56	if (mod_id == module->mod_id)
57	  break;
58
59      if (! module)
60	/* None found -> the ID is available for use.  */
61	break;
62    }
63
64  if (mod_id < MODULE_ID_LAST)
65    /* Done.  */
66    *id_new = mod_id;
67  else
68    /* No free ID found.  */
69    err = GPG_ERR_INTERNAL;
70
71  return err;
72}
73
74/* Add a module specification to the list ENTRIES.  The new module has
75   it's use-counter set to one.  */
76gcry_err_code_t
77_gcry_module_add (gcry_module_t *entries, unsigned int mod_id,
78		  void *spec, void *extraspec, gcry_module_t *module)
79{
80  gcry_err_code_t err = 0;
81  gcry_module_t entry;
82
83  if (! mod_id)
84    err = _gcry_module_id_new (*entries, &mod_id);
85
86  if (! err)
87    {
88      entry = gcry_malloc (sizeof (struct gcry_module));
89      if (! entry)
90	err = gpg_err_code_from_errno (errno);
91    }
92
93  if (! err)
94    {
95      /* Fill new module entry.  */
96      entry->flags = 0;
97      entry->counter = 1;
98      entry->spec = spec;
99      entry->extraspec = extraspec;
100      entry->mod_id = mod_id;
101
102      /* Link it into the list.  */
103      entry->next = *entries;
104      entry->prevp = entries;
105      if (*entries)
106	(*entries)->prevp = &entry->next;
107      *entries = entry;
108
109      /* And give it to the caller.  */
110      if (module)
111	*module = entry;
112    }
113  return err;
114}
115
116/* Internal function.  Unlink CIPHER_ENTRY from the list of registered
117   ciphers and destroy it.  */
118static void
119_gcry_module_drop (gcry_module_t entry)
120{
121  *entry->prevp = entry->next;
122  if (entry->next)
123    entry->next->prevp = entry->prevp;
124
125  gcry_free (entry);
126}
127
128/* Lookup a module specification by it's ID.  After a successful
129   lookup, the module has it's resource counter incremented.  */
130gcry_module_t
131_gcry_module_lookup_id (gcry_module_t entries, unsigned int mod_id)
132{
133  gcry_module_t entry;
134
135  for (entry = entries; entry; entry = entry->next)
136    if (entry->mod_id == mod_id)
137      {
138	entry->counter++;
139	break;
140      }
141
142  return entry;
143}
144
145/* Lookup a module specification.  After a successful lookup, the
146   module has it's resource counter incremented.  FUNC is a function
147   provided by the caller, which is responsible for identifying the
148   wanted module.  */
149gcry_module_t
150_gcry_module_lookup (gcry_module_t entries, void *data,
151		     gcry_module_lookup_t func)
152{
153  gcry_module_t entry;
154
155  for (entry = entries; entry; entry = entry->next)
156    if ((*func) (entry->spec, data))
157      {
158	entry->counter++;
159	break;
160      }
161
162  return entry;
163}
164
165/* Release a module.  In case the use-counter reaches zero, destroy
166   the module.  Passing MODULE as NULL is a dummy operation (similar
167   to free()). */
168void
169_gcry_module_release (gcry_module_t module)
170{
171  if (module && ! --module->counter)
172    _gcry_module_drop (module);
173}
174
175/* Add a reference to a module.  */
176void
177_gcry_module_use (gcry_module_t module)
178{
179  ++module->counter;
180}
181
182/* If LIST is zero, write the number of modules identified by MODULES
183   to LIST_LENGTH and return.  If LIST is non-zero, the first
184   *LIST_LENGTH algorithm IDs are stored in LIST, which must be of
185   according size.  In case there are less cipher modules than
186   *LIST_LENGTH, *LIST_LENGTH is updated to the correct number.  */
187gcry_err_code_t
188_gcry_module_list (gcry_module_t modules,
189		   int *list, int *list_length)
190{
191  gcry_err_code_t err = GPG_ERR_NO_ERROR;
192  gcry_module_t module;
193  int length, i;
194
195  for (module = modules, length = 0; module; module = module->next, length++);
196
197  if (list)
198    {
199      if (length > *list_length)
200	length = *list_length;
201
202      for (module = modules, i = 0; i < length; module = module->next, i++)
203	list[i] = module->mod_id;
204
205      if (length < *list_length)
206	*list_length = length;
207    }
208  else
209    *list_length = length;
210
211  return err;
212}
213