gnupg extension are now working
[gnupg.git] / cipher / dynload.c
1 /* dynload.c - load cipher extensions
2  *      Copyright (C) 1998 Free Software Foundation, Inc.
3  *
4  * This file is part of GNUPG.
5  *
6  * GNUPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GNUPG 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 General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <dlfcn.h>
26 #include "util.h"
27 #include "cipher.h"
28 #include "dynload.h"
29
30 typedef struct ext_list {
31     struct ext_list *next;
32     void *handle; /* handle from dlopen() */
33     int  failed;  /* already tried but failed */
34     void * (*enumfunc)(int, int*, int*, int*);
35     char name[1];
36 } *EXTLIST;
37
38 static EXTLIST extensions;
39
40 typedef struct {
41     EXTLIST r;
42     int seq1;
43     int seq2;
44     void *sym;
45 } ENUMCONTEXT;
46
47 /****************
48  * Register an extension module.  The last registered module will
49  * be loaded first.
50  */
51 void
52 register_cipher_extension( const char *fname )
53 {
54     EXTLIST r, el;
55
56     if( *fname != '/' ) { /* do tilde expansion etc */
57         char *p ;
58
59         if( strchr(fname, '/') )
60             p = make_filename(fname, NULL);
61         else
62             p = make_filename(GNUPG_LIBDIR, fname, NULL);
63         el = m_alloc_clear( sizeof *el + strlen(p) );
64         strcpy(el->name, p );
65         m_free(p);
66     }
67     else {
68         el = m_alloc_clear( sizeof *el + strlen(fname) );
69         strcpy(el->name, fname );
70     }
71     /* check that it is not already registered */
72     for(r = extensions; r; r = r->next )
73         if( !compare_filenames(r->name, el->name) ) {
74             log_info("extension '%s' already registered\n", el->name );
75             m_free(el);
76             return;
77         }
78     if( DBG_CIPHER )
79         log_debug("extension '%s' registered\n", el->name );
80     /* and register */
81     el->next = extensions;
82     extensions = el;
83 }
84
85
86 static int
87 load_extension( EXTLIST el )
88 {
89     char **name;
90     void *sym;
91     const char *err;
92     int seq = 0;
93     int class, vers;
94
95     el->handle = dlopen(el->name, RTLD_NOW);
96     if( !el->handle ) {
97         log_error("%s: error loading extension: %s\n", el->name, dlerror() );
98         goto failure;
99     }
100     name = (char**)dlsym(el->handle, "gnupgext_version");
101     if( (err=dlerror()) ) {
102         log_error("%s: not a gnupg extension: %s\n", el->name, err );
103         goto failure;
104     }
105
106     if( g10_opt_verbose )
107         log_info("%s: version '%s'\n", el->name, *name );
108
109     sym = dlsym(el->handle, "gnupgext_enum_func");
110     if( (err=dlerror()) ) {
111         log_error("%s: invalid gnupg extension: %s\n", el->name, err );
112         goto failure;
113     }
114     el->enumfunc = (void *(*)(int,int*,int*,int*))sym;
115
116     if( g10_opt_verbose > 1 ) {
117         /* list the contents of the module */
118         while( (sym = (*el->enumfunc)(0, &seq, &class, &vers)) ) {
119             if( vers != 1 ) {
120                 log_info("%s: ignoring func with version %d\n",el->name,vers);
121                 continue;
122             }
123             switch( class ) {
124               case 11:
125               case 21:
126               case 31:
127                 log_info("%s: provides %s algorithm %d\n", el->name,
128                                 class == 11? "md"     :
129                                 class == 21? "cipher" : "pubkey",
130                                                        *(int*)sym);
131                 break;
132               default:
133                 /*log_debug("%s: skipping class %d\n", el->name, class);*/
134                 break;
135             }
136         }
137     }
138     return 0;
139
140   failure:
141     if( el->handle ) {
142         dlclose(el->handle);
143         el->handle = NULL;
144     }
145     el->failed = 1;
146     return -1;
147 }
148
149
150
151 const char *
152 enum_gnupgext_ciphers( void **enum_context, int *algo,
153                        size_t *keylen, size_t *blocksize, size_t *contextsize,
154                        void (**setkey)( void *c, byte *key, unsigned keylen ),
155                        void (**encrypt)( void *c, byte *outbuf, byte *inbuf ),
156                        void (**decrypt)( void *c, byte *outbuf, byte *inbuf )
157                      )
158 {
159     EXTLIST r;
160     ENUMCONTEXT *ctx;
161     const char * (*finfo)(int, size_t*, size_t*, size_t*,
162                           void (**)( void *, byte *, unsigned),
163                           void (**)( void *, byte *, byte *),
164                           void (**)( void *, byte *, byte *));
165
166     if( !*enum_context ) { /* init context */
167         ctx = m_alloc_clear( sizeof( *ctx ) );
168         ctx->r = extensions;
169         *enum_context = ctx;
170     }
171     else if( !algo ) { /* release the context */
172         m_free(*enum_context);
173         *enum_context = NULL;
174         return NULL;
175     }
176     else
177         ctx = *enum_context;
178
179     for( r = ctx->r; r; r = r->next )  {
180         int class, vers;
181
182         if( r->failed )
183             continue;
184         if( !r->handle && load_extension(r) )
185             continue;
186         /* get a cipher info function */
187         if( ctx->sym )
188             goto inner_loop;
189         while( (ctx->sym = (*r->enumfunc)(20, &ctx->seq1, &class, &vers)) ) {
190             void *sym;
191             /* must check class because enumfunc may be wrong coded */
192             if( vers != 1 || class != 20 )
193                 continue;
194           inner_loop:
195             finfo = ctx->sym;
196             while( (sym = (*r->enumfunc)(21, &ctx->seq2, &class, &vers)) ) {
197                 const char *algname;
198                 if( vers != 1 || class != 21 )
199                     continue;
200                 *algo = *(int*)sym;
201                 algname = (*finfo)( *algo, keylen, blocksize, contextsize,
202                                     setkey, encrypt, decrypt );
203                 if( algname ) {
204                     ctx->r = r;
205                     return algname;
206                 }
207             }
208             ctx->seq2 = 0;
209         }
210         ctx->seq1 = 0;
211     }
212     ctx->r = r;
213     return NULL;
214 }
215
216 const char *
217 enum_gnupgext_pubkeys( void **enum_context, int *algo,
218     int *npkey, int *nskey, int *nenc, int *nsig, int *usage,
219     int (**generate)( int algo, unsigned nbits, MPI *skey, MPI **retfactors ),
220     int (**check_secret_key)( int algo, MPI *skey ),
221     int (**encrypt)( int algo, MPI *resarr, MPI data, MPI *pkey ),
222     int (**decrypt)( int algo, MPI *result, MPI *data, MPI *skey ),
223     int (**sign)( int algo, MPI *resarr, MPI data, MPI *skey ),
224     int (**verify)( int algo, MPI hash, MPI *data, MPI *pkey ),
225     unsigned (**get_nbits)( int algo, MPI *pkey ) )
226 {
227     EXTLIST r;
228     ENUMCONTEXT *ctx;
229     const char * (*finfo)( int, int *, int *, int *, int *, int *,
230                            int (**)( int, unsigned, MPI *, MPI **),
231                            int (**)( int, MPI * ),
232                            int (**)( int, MPI *, MPI , MPI * ),
233                            int (**)( int, MPI *, MPI *, MPI * ),
234                            int (**)( int, MPI *, MPI , MPI * ),
235                            int (**)( int, MPI , MPI *, MPI * ),
236                            unsigned (**)( int , MPI * ) );
237
238     if( !*enum_context ) { /* init context */
239         ctx = m_alloc_clear( sizeof( *ctx ) );
240         ctx->r = extensions;
241         *enum_context = ctx;
242     }
243     else if( !algo ) { /* release the context */
244         m_free(*enum_context);
245         *enum_context = NULL;
246         return NULL;
247     }
248     else
249         ctx = *enum_context;
250
251     for( r = ctx->r; r; r = r->next )  {
252         int class, vers;
253
254         if( r->failed )
255             continue;
256         if( !r->handle && load_extension(r) )
257             continue;
258         /* get a pubkey info function */
259         if( ctx->sym )
260             goto inner_loop;
261         while( (ctx->sym = (*r->enumfunc)(30, &ctx->seq1, &class, &vers)) ) {
262             void *sym;
263             if( vers != 1 || class != 30 )
264                 continue;
265           inner_loop:
266             finfo = ctx->sym;
267             while( (sym = (*r->enumfunc)(31, &ctx->seq2, &class, &vers)) ) {
268                 const char *algname;
269                 if( vers != 1 || class != 31 )
270                     continue;
271                 *algo = *(int*)sym;
272                 algname = (*finfo)( *algo, npkey, nskey, nenc, nsig, usage,
273                                     generate, check_secret_key, encrypt,
274                                     decrypt, sign, verify, get_nbits );
275                 if( algname ) {
276                     ctx->r = r;
277                     return algname;
278                 }
279             }
280             ctx->seq2 = 0;
281         }
282         ctx->seq1 = 0;
283     }
284     ctx->r = r;
285     return NULL;
286 }
287