See ChangeLog: Thu Dec 10 20:15:36 CET 1998 Werner Koch
[gnupg.git] / cipher / dynload.c
index b40eb40..204f186 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <dlfcn.h>
+#include <unistd.h>
+#ifdef HAVE_DL_DLOPEN
+  #include <dlfcn.h>
+#elif defined(HAVE_DLD_DLD_LINK)
+  #include <dld.h>
+#endif
 #include "util.h"
 #include "cipher.h"
 #include "dynload.h"
 
+
+#ifndef RTLD_NOW
+  #define RTLD_NOW  1
+#endif
+
 typedef struct ext_list {
     struct ext_list *next;
+    int internal;
+  #ifdef HAVE_DL_DLOPEN
     void *handle; /* handle from dlopen() */
+  #else
+    int handle;   /* if the function has been loaded, this is true */
+  #endif
     int  failed;  /* already tried but failed */
     void * (*enumfunc)(int, int*, int*, int*);
+    char *hintstr; /* pointer into name */
     char name[1];
 } *EXTLIST;
 
@@ -44,15 +60,37 @@ typedef struct {
     void *sym;
 } ENUMCONTEXT;
 
+
+#ifdef HAVE_DLD_DLD_LINK
+static char *mainpgm_path;
+static int did_dld_init;
+static int dld_available;
+#endif
+
+
 /****************
  * Register an extension module.  The last registered module will
- * be loaded first.
+ * 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.
+ *
+ * mainpgm is the path to the program which wants to load a module
+ * it is only used in some environments.
  */
 void
-register_cipher_extension( const char *fname )
+register_cipher_extension( const char *mainpgm, const char *fname )
 {
-    EXTLIST r, el;
+    EXTLIST r, el, intex;
+    char *p, *pe;
 
+  #ifdef HAVE_DLD_DLD_LINK
+    if( !mainpgm_path && mainpgm && *mainpgm )
+       mainpgm_path = m_strdup(mainpgm);
+  #endif
     if( *fname != '/' ) { /* do tilde expansion etc */
        char *p ;
 
@@ -68,15 +106,62 @@ register_cipher_extension( const char *fname )
        el = m_alloc_clear( sizeof *el + strlen(fname) );
        strcpy(el->name, fname );
     }
+    /* 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;
+
+    /* 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 )
+    for(r = extensions; r; r = r->next ) {
        if( !compare_filenames(r->name, el->name) ) {
-           log_debug("extension '%s' already registered\n", el->name );
+           log_info("extension '%s' already registered\n", el->name );
            m_free(el);
            return;
        }
-    log_debug("extension '%s' registered\n", el->name );
+    }
     /* and register */
+    el->enumfunc = enumfunc;
+    el->handle = (void*)1;
     el->next = extensions;
     extensions = el;
 }
@@ -85,13 +170,25 @@ register_cipher_extension( const char *fname )
 static int
 load_extension( EXTLIST el )
 {
+  #ifdef USE_DYNAMIC_LINKING
     char **name;
-    void *sym;
+  #ifdef HAVE_DL_DLOPEN
     const char *err;
     int seq = 0;
     int class, vers;
+    void *sym;
+  #else
+    unsigned long addr;
+    int rc;
+  #endif
 
-    el->handle = dlopen(el->name, RTLD_LAZY);
+    /* make sure we are not setuid */
+    if( getuid() != geteuid() )
+       log_bug("trying to load an extension while still setuid\n");
+
+    /* now that we are not setuid anymore, we can safely load modules */
+  #ifdef HAVE_DL_DLOPEN
+    el->handle = dlopen(el->name, RTLD_NOW);
     if( !el->handle ) {
        log_error("%s: error loading extension: %s\n", el->name, dlerror() );
        goto failure;
@@ -101,52 +198,169 @@ load_extension( EXTLIST el )
        log_error("%s: not a gnupg extension: %s\n", el->name, err );
        goto failure;
     }
+  #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("gnupgext_version");
+    if( !addr ) {
+       log_error("%s: not a gnupg extension: %s\n",
+                               el->name, dld_strerror(dld_errno) );
+       goto failure;
+    }
+    name = (char**)addr;
+  #endif
 
-    log_info("%s: version '%s'\n", el->name, *name );
+    if( g10_opt_verbose > 1 )
+       log_info("%s: %s%s%s%s\n", el->name, *name,
+                 el->hintstr? " (":"",
+                 el->hintstr? el->hintstr:"",
+                 el->hintstr? ")":"");
 
+  #ifdef HAVE_DL_DLOPEN
     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;
+  #else /* dld */
+    addr = dld_get_func("gnupgext_enum_func");
+    if( !addr ) {
+       log_error("%s: invalid gnupg extension: %s\n",
+                               el->name, dld_strerror(dld_errno) );
+       goto failure;
+    }
+    rc = dld_function_executable_p("gnupgext_enum_func");
+    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
 
-    /* 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);
+  #ifdef HAVE_DL_DLOPEN
+    if( g10_opt_verbose > 2 ) {
+       /* 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;
+           }
        }
     }
+  #endif
     return 0;
 
   failure:
+  #ifdef HAVE_DL_DLOPEN
     if( el->handle ) {
        dlclose(el->handle);
        el->handle = NULL;
     }
+  #endif
     el->failed = 1;
+  #endif /*USE_DYNAMIC_LINKING*/
     return -1;
 }
 
 
 
+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;
+       *enum_context = ctx;
+    }
+    else if( !algo ) { /* release the context */
+       m_free(*enum_context);
+       *enum_context = NULL;
+       return 0;
+    }
+    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 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;
+}
+
 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 ),
+                      int  (**setkey)( void *c, byte *key, unsigned keylen ),
                       void (**encrypt)( void *c, byte *outbuf, byte *inbuf ),
                       void (**decrypt)( void *c, byte *outbuf, byte *inbuf )
                     )
@@ -154,7 +368,7 @@ enum_gnupgext_ciphers( void **enum_context, int *algo,
     EXTLIST r;
     ENUMCONTEXT *ctx;
     const char * (*finfo)(int, size_t*, size_t*, size_t*,
-                         void (**)( void *, byte *, unsigned),
+                         int  (**)( void *, byte *, unsigned),
                          void (**)( void *, byte *, byte *),
                          void (**)( void *, byte *, byte *));
 
@@ -195,7 +409,6 @@ enum_gnupgext_ciphers( void **enum_context, int *algo,
                *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;
@@ -209,3 +422,125 @@ enum_gnupgext_ciphers( void **enum_context, int *algo,
     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 ),
+    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 *),
+                          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 );
+               if( algname ) {
+                   ctx->r = r;
+                   return algname;
+               }
+           }
+           ctx->seq2 = 0;
+       }
+       ctx->seq1 = 0;
+    }
+    ctx->r = r;
+    return NULL;
+}
+
+
+int (*
+dynload_getfnc_gather_random())(byte*, 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 (*)(byte*, size_t*, int))sym;
+       }
+    }
+    return NULL;
+}
+
+
+void (*
+dynload_getfnc_fast_random_poll())( void (*)(const void*, 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)(41, &seq, &class, &vers)) ) {
+           if( vers != 1 || class != 41 )
+               continue;
+           return (void (*)( void (*)(const void*, size_t, int)))sym;
+       }
+    }
+    return NULL;
+}
+