Hi Steve,

> Received: from celocom.com Tue, 8 Jun 1999 20:57:29 +0000
> [...]
> 
> 3. Do something evil with the cb parameter...

looks like the easy way to go, although quite unelegant.

> EVP_PKEY *PEM_read_PrivateKey(FILE *fp,EVP_PKEY **x, void *x);
> 
> This has a companion "default callback":
> 
> int default_pem_callback(void *x, char *buf, int size, int rwflag);
> 
> This needs a bit more explanation. Any function calling
> PEM_read_PrivateKey() in the "old way" will end up calling the
> default_pem_callback() which retains the old behaviour: treating 'x' as
> a passphrase callback in the "old way". Anything that wants to pass
> parameters to the callback can replace the default_pem_callback() and
> interpret the 'x' parameter in any appropriate way.
> 
> This does however lose typechecking of the 'x' parameter and is a bit
> awkward to use. Interpreting a void * as a function pointer might also
> be a potential problem.

It is not completely clear to me. Perhaps an example would illustrate
better the point? Let me try...

the client code just defines the callback

        int my_smart_callback(char *buf, int num, int w, void *arg)
        {
           my_arg_type * my_arg = (my_arg_type*) arg;
           // etc...
        }

and then uses it as follows:

        evil thing;
        int (*obscure_pointer)();
        my_arg_type arg;
        RSA *rsa;

        // ...
        // set arg values, e.g.
        arg.value_1 = 1;

        obscure_pointer = PEM_the_evil(&my_smart_callback, &arg, &thing);
        rsa = PEM_read_RSAPrivateKey(in, NULL, obscure_pointer);
        // ...

Really, only the evil is necessary, since obscure_pointer, which is
the address of the thing, might be a temporary value, as in

        rsa = PEM_read_RSAPrivateKey(in, NULL,
           PEM_the_evil(&my_smart_callback, &arg, &thing));

that function should prepare the evil thing and cast its address for the client

        int (*PEM_the_evil(int (*smart_callback)(), void *arg, evil *thing))()
        {
           memset(thing, 0, sizeof *thing);
           // do some other tricky stuff in order to be
           // 100% sure this thing cannot be a real function,
           // and finally
           thing->smart_callback = smart_callback;
           thing->arg = arg;
           return (int (*)()) thing;
        }

now it is possible for worker functions that actually dereference the callback
arguments to do their work quite safely, e.g.

        int PEM_some_function(int (*callback)(), ...)
        {
           evil *thing;
           int klen;
           char buf[PEM_BUFSIZE];

           // ...

           if (callback == NULL)
              klen = def_callback(buf, PEM_BUFSIZE, 0);
           else if ((thing = check_for_the_evil_thing(callback)) != NULL)
              klen = (*thing->smart_callback)(buf, PEM_BUFSIZE, 0, thing->arg)
           else // old callback
              klen = (*callback)(buf, PEM_BUFSIZE, 0);

           // ...
        }

All that assumes that the evil is defined as some chnk of data, followed by
its useful values, e.g.

        typedef struct evil_struct
        {
           unsigned char stuff[128];
           int (*smart_callback)();
           void *arg;
        } evil;

Finally, check_for_the_evil_thing will return NULL unless its parameter
actually points to an evil object. In the latter case it casts the address
for the worker function.

The disadvantage of this approach is that the hack to discriminate
between a honest old callback and the evil must be verified separately
for each different architecture. Coding a jump to an impossible place
could be an idea, e.g.

        static void the_dummy_function()
        {
           the_dummy_function():
           the_dummy_function():
        }

        static void set_evil_check_stuff(evil *thing)
        // called from PEM_the_evil
        {
           memcpy(thing, &the_dummy_function, 32);
        }

        evil *check_for_the_evil_thing(int (*thing)())
        {
           if (memcmp(thing, &the_dummy_function, 32) != 0)
              return NULL;
           // more checks...
           return (evil*)thing;
        }

However, care must be taken against similar techniques used for
chunking code, optimizations, code being dynamically changed as
a result of dynamic linking and the like. But then, I'm new to
this crypto code and I'm talking to experts: what about including
a signature in the evil thing?

> 
> This does however have the advantage that existing code will still work
> and the neccessary functionality is available.
> 
> Anyway thats three options, none of which IMHO is ideal. Any alternative
> solutions or comments anyone?

Ciao
Ale
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
Development Mailing List                       [EMAIL PROTECTED]
Automated List Manager                           [EMAIL PROTECTED]

Reply via email to