Debug SSL connections with a shim
March 24, 2013  

End-to-end encryption is becoming more common. This is great for privacy and security, but it also makes debugging misbehaving network applications more difficult. If you need to see the unencrypted traffic, packet-capturing tools like wireshark won’t help.

There are a number of ways to get around this. Most often, I see people recommend using an SSL proxy, but this can be hard to set up. Other common recommendations are to use dtrace (not available on Linux, and the pid provider is tricky to use), add print statements to the source code (but the source might not be available), or use gdb (not easy if you’re working with stripped executables).

My favorite solution is rarely mentioned: insert a shim between the application and the SSL/TLS library that prints out the unencrypted network traffic. It’s dependable and I find it much easier to use than the other methods.

For reference, here’s a shim for debugging Linux applications that use OpenSSL:


    /* ssl_shim.c
       Shim for printing a transcript of an SSL session
       Compile with
         $ gcc -Wall -O2 -fpic -shared -ldl -o ssl_shim.so ssl_shim.c 
       Run with
         $ LD_PRELOAD=/absolute/path/to/ssl_shim.so program_to_debug
    */

    #include 
    #include 
    #include 

    int (*shim_SSL_read)(SSL *ssl,void *buf,int num);
    int (*shim_SSL_peek)(SSL *ssl,void *buf,int num);
    int (*shim_SSL_write)(SSL *ssl,const void *buf,int num);
    int (*shim_SSL_connect)(SSL *ssl);
    int (*shim_SSL_accept)(SSL *ssl);

    void shim_init() {
      if (shim_SSL_read) return;

      void *handle = dlopen("/usr/lib/libssl.so",
                            RTLD_LAZY);
      if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
      }
      shim_SSL_read = dlsym(handle, "SSL_read");
      if (dlerror() != NULL) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
      }
      shim_SSL_peek = dlsym(handle, "SSL_peek");
      if (dlerror() != NULL) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
      }
      shim_SSL_write = dlsym(handle, "SSL_write");
      if (dlerror() != NULL) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
      }
      shim_SSL_connect = dlsym(handle, "SSL_connect");
      if (dlerror() != NULL) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
      }
      shim_SSL_accept = dlsym(handle, "SSL_accept");
      if (dlerror() != NULL) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
      }
    }
    int SSL_read(SSL *ssl,void *buf,int num) {
      shim_init();
      int n = shim_SSL_read(ssl,buf,num);
      printf("<---(%d)\n",n);
      if (n>0)
        printf("%.*s\n<---\n",n,(char *)buf);
      else
        printf("<---\n");
      return n;
    }
    int SSL_peek(SSL *ssl,void *buf,int num) {
      shim_init();
      int n = shim_SSL_peek(ssl,buf,num);
      printf("---<(%d)\n",n);
      if (n>0)
        printf("%.*s\n---<\n",n,(char *)buf);
      else
        printf("---<\n");
      return n;
    }
    int SSL_write(SSL *ssl,const void *buf,int num) {
      shim_init();
      int n = shim_SSL_write(ssl,buf,num);
      printf("--->(%d)\n",n);
      if (n>0)
        printf("%.*s\n---<\n",n,(char *)buf);
      else
        printf("--->\n");
      return n;
    }
    int SSL_connect(SSL *ssl) {
      shim_init();
      int n = shim_SSL_connect(ssl);
      printf(">---connect(%d)\n",n);
      return n;
    }
    int SSL_accept(SSL *ssl) {
      shim_init();
      int n = shim_SSL_accept(ssl);
      printf("---