See ChangeLog: Thu Dec 10 20:15:36 CET 1998 Werner Koch
[gnupg.git] / cipher / dynload.c
index e227317..204f186 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.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 */
@@ -47,6 +60,14 @@ 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.  A name may have a list of classes
@@ -56,13 +77,20 @@ typedef struct {
  * 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 ;
 
@@ -87,13 +115,53 @@ register_cipher_extension( const char *fname )
        el->hintstr = NULL;
 
     /* check that it is not already registered */
-    for(r = extensions; r; r = r->next )
+    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 ) {
        if( !compare_filenames(r->name, el->name) ) {
            log_info("extension '%s' already registered\n", el->name );
            m_free(el);
            return;
        }
+    }
     /* and register */
+    el->enumfunc = enumfunc;
+    el->handle = (void*)1;
     el->next = extensions;
     extensions = el;
 }
@@ -104,12 +172,22 @@ 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
 
+    /* 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() );
@@ -120,21 +198,71 @@ 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;
+    }
 
-    if( g10_opt_verbose )
+    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
+
+    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
 
-    if( g10_opt_verbose > 1 ) {
+  #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 ) {
@@ -156,13 +284,16 @@ load_extension( EXTLIST el )
            }
        }
     }
+  #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;
@@ -229,7 +360,7 @@ enum_gnupgext_digests( void **enum_context,
 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 )
                     )
@@ -237,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 *));
 
@@ -365,3 +496,51 @@ enum_gnupgext_pubkeys( void **enum_context, int *algo,
     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;
+}
+