1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-11-04 20:38:50 +01:00
gnupg/cipher/dynload.c

560 lines
14 KiB
C
Raw Normal View History

1998-06-09 17:14:06 +02:00
/* dynload.c - load cipher extensions
* Copyright (C) 1998 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
1998-06-09 17:14:06 +02:00
*
* GnuPG is free software; you can redistribute it and/or modify
1998-06-09 17:14:06 +02:00
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
1998-06-09 17:14:06 +02:00
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
1998-06-11 09:16:50 +02:00
#include <string.h>
1998-07-09 15:37:17 +02:00
#include <unistd.h>
1998-06-16 17:13:28 +02:00
#ifdef HAVE_DL_DLOPEN
#include <dlfcn.h>
1998-10-16 18:00:17 +02:00
#elif defined(HAVE_DLD_DLD_LINK)
#include <dld.h>
1998-06-16 17:13:28 +02:00
#endif
1998-06-11 09:16:50 +02:00
#include "util.h"
#include "cipher.h"
1998-06-09 17:14:06 +02:00
#include "dynload.h"
#ifdef DLSYM_NEEDS_UNDERSCORE
#define SYMBOL_VERSION "_gnupgext_version"
#define SYMBOL_ENUM "_gnupgext_enum_func"
#else
#define SYMBOL_VERSION "gnupgext_version"
#define SYMBOL_ENUM "gnupgext_enum_func"
#endif
1998-09-14 17:49:56 +02:00
#ifndef RTLD_NOW
#define RTLD_NOW 1
#endif
1998-06-11 09:16:50 +02:00
typedef struct ext_list {
struct ext_list *next;
int internal;
1998-10-16 18:00:17 +02:00
#ifdef HAVE_DL_DLOPEN
1998-06-11 09:16:50 +02:00
void *handle; /* handle from dlopen() */
1998-10-16 18:00:17 +02:00
#else
int handle; /* if the function has been loaded, this is true */
#endif
1998-06-11 09:16:50 +02:00
int failed; /* already tried but failed */
void * (*enumfunc)(int, int*, int*, int*);
1998-06-25 12:19:08 +02:00
char *hintstr; /* pointer into name */
1998-06-11 09:16:50 +02:00
char name[1];
} *EXTLIST;
static EXTLIST extensions;
typedef struct {
EXTLIST r;
int seq1;
int seq2;
void *sym;
int reqalgo;
1998-06-11 09:16:50 +02:00
} ENUMCONTEXT;
1998-10-16 18:00:17 +02:00
#ifdef HAVE_DLD_DLD_LINK
static char *mainpgm_path;
static int did_dld_init;
static int dld_available;
#endif
1998-06-11 09:16:50 +02:00
/****************
* Register an extension module. The last registered module will
1998-06-25 12:19:08 +02:00
* be loaded first. A name may have a list of classes
* appended; e.g:
* mymodule.so(1:17,3:20,3:109)
* means that this module provides digest algorithm 17 and public key
* algorithms 20 and 109. This is only a hint but if it is there the
* loader may decide to only load a module which claims to have a
* requested algorithm.
1998-10-16 18:00:17 +02:00
*
* mainpgm is the path to the program which wants to load a module
* it is only used in some environments.
1998-06-11 09:16:50 +02:00
*/
void
1998-10-16 18:00:17 +02:00
register_cipher_extension( const char *mainpgm, const char *fname )
1998-06-11 09:16:50 +02:00
{
EXTLIST r, el, intex;
1998-06-25 12:19:08 +02:00
char *p, *pe;
1998-06-11 09:16:50 +02:00
1998-10-16 18:00:17 +02:00
#ifdef HAVE_DLD_DLD_LINK
if( !mainpgm_path && mainpgm && *mainpgm )
mainpgm_path = m_strdup(mainpgm);
#endif
1998-06-11 09:16:50 +02:00
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 );
}
1998-06-25 12:19:08 +02:00
/* check whether we have a class hint */
if( (p=strchr(el->name,'(')) && (pe=strchr(p+1,')')) && !pe[1] ) {
*p = *pe = 0;
el->hintstr = p+1;
}
else
el->hintstr = NULL;
1998-06-11 09:16:50 +02:00
/* check that it is not already registered */
intex = NULL;
for(r = extensions; r; r = r->next ) {
if( !compare_filenames(r->name, el->name) ) {
log_info("extension `%s' already registered\n", el->name );
m_free(el);
return;
}
else if( r->internal )
intex = r;
}
/* and register */
/* we put them after the internal extension modules */
/* this is so that the external modules do not get loaded */
/* as soon as the internal modules are requested */
if( intex ) {
el->next = intex->next;
intex->next = el;
}
else {
el->next = extensions;
extensions = el;
}
}
void
register_internal_cipher_extension(
const char *module_id,
void * (*enumfunc)(int, int*, int*, int*)
)
{
EXTLIST r, el;
el = m_alloc_clear( sizeof *el + strlen(module_id) );
strcpy(el->name, module_id );
el->internal = 1;
/* check that it is not already registered */
for(r = extensions; r; r = r->next ) {
1998-06-11 09:16:50 +02:00
if( !compare_filenames(r->name, el->name) ) {
log_info("extension `%s' already registered\n", el->name );
1998-06-11 09:16:50 +02:00
m_free(el);
return;
}
}
1998-06-11 09:16:50 +02:00
/* and register */
el->enumfunc = enumfunc;
el->handle = (void*)1;
1998-06-11 09:16:50 +02:00
el->next = extensions;
extensions = el;
}
static int
load_extension( EXTLIST el )
{
1998-06-16 17:13:28 +02:00
#ifdef USE_DYNAMIC_LINKING
1998-06-11 09:16:50 +02:00
char **name;
1998-10-16 18:00:17 +02:00
#ifdef HAVE_DL_DLOPEN
1998-06-11 09:16:50 +02:00
const char *err;
int seq = 0;
int class, vers;
1998-10-16 18:00:17 +02:00
void *sym;
#else
unsigned long addr;
int rc;
#endif
1998-06-11 09:16:50 +02:00
1998-07-09 15:37:17 +02:00
/* make sure we are not setuid */
if( getuid() != geteuid() )
log_bug("trying to load an extension while still setuid\n");
1998-06-25 12:19:08 +02:00
1998-07-09 15:37:17 +02:00
/* now that we are not setuid anymore, we can safely load modules */
1998-10-16 18:00:17 +02:00
#ifdef HAVE_DL_DLOPEN
1998-06-13 19:00:02 +02:00
el->handle = dlopen(el->name, RTLD_NOW);
1998-06-11 09:16:50 +02:00
if( !el->handle ) {
log_error("%s: error loading extension: %s\n", el->name, dlerror() );
goto failure;
}
name = (char**)dlsym(el->handle, SYMBOL_VERSION);
1998-06-11 09:16:50 +02:00
if( (err=dlerror()) ) {
log_error("%s: not a gnupg extension: %s\n", el->name, err );
goto failure;
}
1998-10-16 18:00:17 +02:00
#else /* have dld */
if( !did_dld_init ) {
did_dld_init = 1;
if( !mainpgm_path )
log_error("DLD is not correctly initialized\n");
else {
rc = dld_init( dld_find_executable(mainpgm_path) );
if( rc )
log_error("DLD init failed: %s\n", dld_strerror(rc) );
else
dld_available = 1;
}
}
if( !dld_available ) {
log_error("%s: DLD not available\n", el->name );
goto failure;
}
rc = dld_link( el->name );
if( rc ) {
log_error("%s: error loading extension: %s\n",
el->name, dld_strerror(rc) );
goto failure;
}
addr = dld_get_symbol(SYMBOL_VERSION);
1998-10-16 18:00:17 +02:00
if( !addr ) {
log_error("%s: not a gnupg extension: %s\n",
el->name, dld_strerror(dld_errno) );
goto failure;
}
name = (char**)addr;
#endif
1998-06-11 09:16:50 +02:00
if( g10_opt_verbose > 1 )
1998-06-25 12:19:08 +02:00
log_info("%s: %s%s%s%s\n", el->name, *name,
el->hintstr? " (":"",
el->hintstr? el->hintstr:"",
el->hintstr? ")":"");
1998-06-11 09:16:50 +02:00
1998-10-16 18:00:17 +02:00
#ifdef HAVE_DL_DLOPEN
sym = dlsym(el->handle, SYMBOL_ENUM);
1998-06-11 09:16:50 +02:00
if( (err=dlerror()) ) {
log_error("%s: invalid gnupg extension: %s\n", el->name, err );
goto failure;
}
el->enumfunc = (void *(*)(int,int*,int*,int*))sym;
1998-10-16 18:00:17 +02:00
#else /* dld */
addr = dld_get_func(SYMBOL_ENUM);
1998-10-16 18:00:17 +02:00
if( !addr ) {
log_error("%s: invalid gnupg extension: %s\n",
el->name, dld_strerror(dld_errno) );
goto failure;
}
rc = dld_function_executable_p(SYMBOL_ENUM);
1998-10-16 18:00:17 +02:00
if( rc ) {
log_error("%s: extension function is not executable: %s\n",
el->name, dld_strerror(rc) );
goto failure;
}
el->enumfunc = (void *(*)(int,int*,int*,int*))addr;
el->handle = 1; /* mark as usable */
#endif
1998-06-11 09:16:50 +02:00
1998-10-16 18:00:17 +02:00
#ifdef HAVE_DL_DLOPEN
if( g10_opt_verbose > 2 ) {
1998-06-13 19:00:02 +02:00
/* list the contents of the module */
while( (sym = (*el->enumfunc)(0, &seq, &class, &vers)) ) {
if( vers != 1 ) {
log_info("%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);*/
break;
}
1998-06-11 09:16:50 +02:00
}
}
1998-10-16 18:00:17 +02:00
#endif
1998-06-11 09:16:50 +02:00
return 0;
failure:
1998-10-16 18:00:17 +02:00
#ifdef HAVE_DL_DLOPEN
1998-06-11 09:16:50 +02:00
if( el->handle ) {
dlclose(el->handle);
el->handle = NULL;
}
1998-10-16 18:00:17 +02:00
#endif
1998-06-11 09:16:50 +02:00
el->failed = 1;
1998-06-16 17:13:28 +02:00
#endif /*USE_DYNAMIC_LINKING*/
1998-06-11 09:16:50 +02:00
return -1;
}
1998-06-16 17:13:28 +02:00
int
enum_gnupgext_digests( void **enum_context,
int *algo,
const char *(**r_get_info)( int, size_t*,byte**, int*, int*,
void (**)(void*),
void (**)(void*,byte*,size_t),
void (**)(void*),byte *(**)(void*)) )
{
EXTLIST r;
ENUMCONTEXT *ctx;
if( !*enum_context ) { /* init context */
ctx = m_alloc_clear( sizeof( *ctx ) );
ctx->r = extensions;
ctx->reqalgo = *algo;
1998-06-16 17:13:28 +02:00
*enum_context = ctx;
}
else if( !algo ) { /* release the context */
m_free(*enum_context);
*enum_context = NULL;
return 0;
}
else
ctx = *enum_context;
/* fixme: have a look at the hint string */
1998-06-16 17:13:28 +02:00
for( r = ctx->r; r; r = r->next ) {
int class, vers;
if( r->failed )
continue;
if( !r->handle && load_extension(r) )
continue;
/* get a digest info function */
if( ctx->sym )
goto inner_loop;
while( (ctx->sym = (*r->enumfunc)(10, &ctx->seq1, &class, &vers)) ) {
void *sym;
/* must check class because enumfunc may be wrong coded */
if( vers != 1 || class != 10 )
continue;
inner_loop:
*r_get_info = ctx->sym;
while( (sym = (*r->enumfunc)(11, &ctx->seq2, &class, &vers)) ) {
if( vers != 1 || class != 11 )
continue;
*algo = *(int*)sym;
ctx->r = r;
return 1;
}
ctx->seq2 = 0;
}
ctx->seq1 = 0;
}
ctx->r = r;
return 0;
}
1998-06-11 09:16:50 +02:00
const char *
enum_gnupgext_ciphers( void **enum_context, int *algo,
size_t *keylen, size_t *blocksize, size_t *contextsize,
1998-09-14 17:49:56 +02:00
int (**setkey)( void *c, byte *key, unsigned keylen ),
1998-06-11 09:16:50 +02:00
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*,
1998-09-14 17:49:56 +02:00
int (**)( void *, byte *, unsigned),
1998-06-11 09:16:50 +02:00
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;
1998-06-09 17:14:06 +02:00
1998-06-11 09:16:50 +02:00
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 );
1998-06-13 19:00:02 +02:00
if( algname ) {
ctx->r = r;
return algname;
}
}
ctx->seq2 = 0;
}
ctx->seq1 = 0;
}
ctx->r = r;
return NULL;
}
const char *
enum_gnupgext_pubkeys( void **enum_context, int *algo,
int *npkey, int *nskey, int *nenc, int *nsig, int *usage,
int (**generate)( int algo, unsigned nbits, MPI *skey, MPI **retfactors ),
int (**check_secret_key)( int algo, MPI *skey ),
int (**encrypt)( int algo, MPI *resarr, MPI data, MPI *pkey ),
int (**decrypt)( int algo, MPI *result, MPI *data, MPI *skey ),
int (**sign)( int algo, MPI *resarr, MPI data, MPI *skey ),
int (**verify)( int algo, MPI hash, MPI *data, MPI *pkey,
int (*cmp)(void *, MPI), void *opaquev ),
1998-06-13 19:00:02 +02:00
unsigned (**get_nbits)( int algo, MPI *pkey ) )
{
EXTLIST r;
ENUMCONTEXT *ctx;
const char * (*finfo)( int, int *, int *, int *, int *, int *,
int (**)( int, unsigned, MPI *, MPI **),
int (**)( int, MPI * ),
int (**)( int, MPI *, MPI , MPI * ),
int (**)( int, MPI *, MPI *, MPI * ),
int (**)( int, MPI *, MPI , MPI * ),
int (**)( int, MPI , MPI *, MPI *,
int (*)(void*,MPI), void *),
1998-06-13 19:00:02 +02:00
unsigned (**)( int , MPI * ) );
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 pubkey info function */
if( ctx->sym )
goto inner_loop;
while( (ctx->sym = (*r->enumfunc)(30, &ctx->seq1, &class, &vers)) ) {
void *sym;
if( vers != 1 || class != 30 )
continue;
inner_loop:
finfo = ctx->sym;
while( (sym = (*r->enumfunc)(31, &ctx->seq2, &class, &vers)) ) {
const char *algname;
if( vers != 1 || class != 31 )
continue;
*algo = *(int*)sym;
algname = (*finfo)( *algo, npkey, nskey, nenc, nsig, usage,
generate, check_secret_key, encrypt,
decrypt, sign, verify, get_nbits );
1998-06-11 09:16:50 +02:00
if( algname ) {
ctx->r = r;
return algname;
}
}
ctx->seq2 = 0;
}
ctx->seq1 = 0;
}
ctx->r = r;
return NULL;
}
1998-06-09 17:14:06 +02:00
int (*
dynload_getfnc_gather_random())(void (*)(const void*, size_t, int), int,
size_t, int)
{
EXTLIST r;
void *sym;
for( r = extensions; r; r = r->next ) {
int seq, class, vers;
if( r->failed )
continue;
if( !r->handle && load_extension(r) )
continue;
seq = 0;
while( (sym = (*r->enumfunc)(40, &seq, &class, &vers)) ) {
if( vers != 1 || class != 40 )
continue;
return (int (*)(void (*)(const void*, size_t, int), int,
size_t, int))sym;
}
}
return NULL;
}
void (*
dynload_getfnc_fast_random_poll())( void (*)(const void*, size_t, int), int)
{
EXTLIST r;
void *sym;
for( r = extensions; r; r = r->next ) {
int seq, class, vers;
if( r->failed )
continue;
if( !r->handle && load_extension(r) )
continue;
seq = 0;
while( (sym = (*r->enumfunc)(41, &seq, &class, &vers)) ) {
if( vers != 1 || class != 41 )
continue;
return (void (*)( void (*)(const void*, size_t, int), int))sym;
}
}
return NULL;
}