update from tobold
[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_debug("extension '%s' already registered\n", el->name );
75             m_free(el);
76             return;
77         }
78     log_debug("extension '%s' registered\n", el->name );
79     /* and register */
80     el->next = extensions;
81     extensions = el;
82 }
83
84
85 static int
86 load_extension( EXTLIST el )
87 {
88     char **name;
89     void *sym;
90     const char *err;
91     int seq = 0;
92     int class, vers;
93
94     el->handle = dlopen(el->name, RTLD_LAZY);
95     if( !el->handle ) {
96         log_error("%s: error loading extension: %s\n", el->name, dlerror() );
97         goto failure;
98     }
99     name = (char**)dlsym(el->handle, "gnupgext_version");
100     if( (err=dlerror()) ) {
101         log_error("%s: not a gnupg extension: %s\n", el->name, err );
102         goto failure;
103     }
104
105     log_info("%s: version '%s'\n", el->name, *name );
106
107     sym = dlsym(el->handle, "gnupgext_enum_func");
108     if( (err=dlerror()) ) {
109         log_error("%s: invalid gnupg extension: %s\n", el->name, err );
110         goto failure;
111     }
112     el->enumfunc = (void *(*)(int,int*,int*,int*))sym;
113
114     /* list the contents of the module */
115     while( (sym = (*el->enumfunc)(0, &seq, &class, &vers)) ) {
116         if( vers != 1 ) {
117             log_error("%s: ignoring func with version %d\n", el->name, vers);
118             continue;
119         }
120         switch( class ) {
121           case 11:
122           case 21:
123           case 31:
124             log_info("%s: provides %s algorithm %d\n", el->name,
125                             class == 11? "md"     :
126                             class == 21? "cipher" : "pubkey",
127                                                    *(int*)sym);
128             break;
129           default:
130             log_debug("%s: skipping class %d\n", el->name, class);
131         }
132     }
133     return 0;
134
135   failure:
136     if( el->handle ) {
137         dlclose(el->handle);
138         el->handle = NULL;
139     }
140     el->failed = 1;
141     return -1;
142 }
143
144
145
146 const char *
147 enum_gnupgext_ciphers( void **enum_context, int *algo,
148                        size_t *keylen, size_t *blocksize, size_t *contextsize,
149                        void (**setkey)( void *c, byte *key, unsigned keylen ),
150                        void (**encrypt)( void *c, byte *outbuf, byte *inbuf ),
151                        void (**decrypt)( void *c, byte *outbuf, byte *inbuf )
152                      )
153 {
154     EXTLIST r;
155     ENUMCONTEXT *ctx;
156     const char * (*finfo)(int, size_t*, size_t*, size_t*,
157                           void (**)( void *, byte *, unsigned),
158                           void (**)( void *, byte *, byte *),
159                           void (**)( void *, byte *, byte *));
160
161     if( !*enum_context ) { /* init context */
162         ctx = m_alloc_clear( sizeof( *ctx ) );
163         ctx->r = extensions;
164         *enum_context = ctx;
165     }
166     else if( !algo ) { /* release the context */
167         m_free(*enum_context);
168         *enum_context = NULL;
169         return NULL;
170     }
171     else
172         ctx = *enum_context;
173
174     for( r = ctx->r; r; r = r->next )  {
175         int class, vers;
176
177         if( r->failed )
178             continue;
179         if( !r->handle && load_extension(r) )
180             continue;
181         /* get a cipher info function */
182         if( ctx->sym )
183             goto inner_loop;
184         while( (ctx->sym = (*r->enumfunc)(20, &ctx->seq1, &class, &vers)) ) {
185             void *sym;
186             /* must check class because enumfunc may be wrong coded */
187             if( vers != 1 || class != 20 )
188                 continue;
189           inner_loop:
190             finfo = ctx->sym;
191             while( (sym = (*r->enumfunc)(21, &ctx->seq2, &class, &vers)) ) {
192                 const char *algname;
193                 if( vers != 1 || class != 21 )
194                     continue;
195                 *algo = *(int*)sym;
196                 algname = (*finfo)( *algo, keylen, blocksize, contextsize,
197                                     setkey, encrypt, decrypt );
198                 log_debug("found algo %d (%s)\n", *algo, algname );
199                 if( algname ) {
200                     ctx->r = r;
201                     return algname;
202                 }
203             }
204             ctx->seq2 = 0;
205         }
206         ctx->seq1 = 0;
207     }
208     ctx->r = r;
209     return NULL;
210 }
211