diff options
author | Werner Koch <wk@gnupg.org> | 1998-06-11 09:16:50 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 1998-06-11 09:16:50 +0200 |
commit | d9b3dc000085007bdd198ca4c0500e80a72475d7 (patch) | |
tree | bb3ffa42fa8b9e30476fdbc843632c896fcfec37 /cipher/dynload.c | |
parent | . (diff) | |
download | gnupg2-d9b3dc000085007bdd198ca4c0500e80a72475d7.tar.xz gnupg2-d9b3dc000085007bdd198ca4c0500e80a72475d7.zip |
update from tobold
Diffstat (limited to 'cipher/dynload.c')
-rw-r--r-- | cipher/dynload.c | 187 |
1 files changed, 184 insertions, 3 deletions
diff --git a/cipher/dynload.c b/cipher/dynload.c index 19e035d9d..b40eb4056 100644 --- a/cipher/dynload.c +++ b/cipher/dynload.c @@ -18,13 +18,194 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ - - #include <config.h> #include <stdio.h> #include <stdlib.h> - +#include <string.h> +#include <dlfcn.h> +#include "util.h" +#include "cipher.h" #include "dynload.h" +typedef struct ext_list { + struct ext_list *next; + void *handle; /* handle from dlopen() */ + int failed; /* already tried but failed */ + void * (*enumfunc)(int, int*, int*, int*); + char name[1]; +} *EXTLIST; + +static EXTLIST extensions; + +typedef struct { + EXTLIST r; + int seq1; + int seq2; + void *sym; +} ENUMCONTEXT; + +/**************** + * Register an extension module. The last registered module will + * be loaded first. + */ +void +register_cipher_extension( const char *fname ) +{ + EXTLIST r, el; + + if( *fname != '/' ) { /* do tilde expansion etc */ + char *p ; + + if( strchr(fname, '/') ) + p = make_filename(fname, NULL); + else + p = make_filename(GNUPG_LIBDIR, fname, NULL); + el = m_alloc_clear( sizeof *el + strlen(p) ); + strcpy(el->name, p ); + m_free(p); + } + else { + el = m_alloc_clear( sizeof *el + strlen(fname) ); + strcpy(el->name, fname ); + } + /* check that it is not already registered */ + for(r = extensions; r; r = r->next ) + if( !compare_filenames(r->name, el->name) ) { + log_debug("extension '%s' already registered\n", el->name ); + m_free(el); + return; + } + log_debug("extension '%s' registered\n", el->name ); + /* and register */ + el->next = extensions; + extensions = el; +} + + +static int +load_extension( EXTLIST el ) +{ + char **name; + void *sym; + const char *err; + int seq = 0; + int class, vers; + + el->handle = dlopen(el->name, RTLD_LAZY); + if( !el->handle ) { + log_error("%s: error loading extension: %s\n", el->name, dlerror() ); + goto failure; + } + name = (char**)dlsym(el->handle, "gnupgext_version"); + if( (err=dlerror()) ) { + log_error("%s: not a gnupg extension: %s\n", el->name, err ); + goto failure; + } + + log_info("%s: version '%s'\n", el->name, *name ); + + sym = dlsym(el->handle, "gnupgext_enum_func"); + if( (err=dlerror()) ) { + log_error("%s: invalid gnupg extension: %s\n", el->name, err ); + goto failure; + } + el->enumfunc = (void *(*)(int,int*,int*,int*))sym; + + /* list the contents of the module */ + while( (sym = (*el->enumfunc)(0, &seq, &class, &vers)) ) { + if( vers != 1 ) { + log_error("%s: ignoring func with version %d\n", el->name, vers); + continue; + } + switch( class ) { + case 11: + case 21: + case 31: + log_info("%s: provides %s algorithm %d\n", el->name, + class == 11? "md" : + class == 21? "cipher" : "pubkey", + *(int*)sym); + break; + default: + log_debug("%s: skipping class %d\n", el->name, class); + } + } + return 0; + + failure: + if( el->handle ) { + dlclose(el->handle); + el->handle = NULL; + } + el->failed = 1; + return -1; +} + + + +const char * +enum_gnupgext_ciphers( void **enum_context, int *algo, + size_t *keylen, size_t *blocksize, size_t *contextsize, + void (**setkey)( void *c, byte *key, unsigned keylen ), + void (**encrypt)( void *c, byte *outbuf, byte *inbuf ), + void (**decrypt)( void *c, byte *outbuf, byte *inbuf ) + ) +{ + EXTLIST r; + ENUMCONTEXT *ctx; + const char * (*finfo)(int, size_t*, size_t*, size_t*, + void (**)( void *, byte *, unsigned), + void (**)( void *, byte *, byte *), + void (**)( void *, byte *, byte *)); + + if( !*enum_context ) { /* init context */ + ctx = m_alloc_clear( sizeof( *ctx ) ); + ctx->r = extensions; + *enum_context = ctx; + } + else if( !algo ) { /* release the context */ + m_free(*enum_context); + *enum_context = NULL; + return NULL; + } + else + ctx = *enum_context; + + for( r = ctx->r; r; r = r->next ) { + int class, vers; + if( r->failed ) + continue; + if( !r->handle && load_extension(r) ) + continue; + /* get a cipher info function */ + if( ctx->sym ) + goto inner_loop; + while( (ctx->sym = (*r->enumfunc)(20, &ctx->seq1, &class, &vers)) ) { + void *sym; + /* must check class because enumfunc may be wrong coded */ + if( vers != 1 || class != 20 ) + continue; + inner_loop: + finfo = ctx->sym; + while( (sym = (*r->enumfunc)(21, &ctx->seq2, &class, &vers)) ) { + const char *algname; + if( vers != 1 || class != 21 ) + continue; + *algo = *(int*)sym; + algname = (*finfo)( *algo, keylen, blocksize, contextsize, + setkey, encrypt, decrypt ); + log_debug("found algo %d (%s)\n", *algo, algname ); + if( algname ) { + ctx->r = r; + return algname; + } + } + ctx->seq2 = 0; + } + ctx->seq1 = 0; + } + ctx->r = r; + return NULL; +} |