Make success expansion a per-transport option
[users/jgh/exim.git] / test / src / client.c
index 0a21594724af1e2bbf114681edb79aed7b378fac..3b782f3fdecf3f8823c53dea6b599274d42389c5 100644 (file)
@@ -1,9 +1,8 @@
-/* $Cambridge: exim/test/src/client.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */
-
 /* A little hacked up program that makes a TCP/IP call and reads a script to
 drive it, for testing Exim server code running as a daemon. It's got a bit
 messy with the addition of support for either OpenSSL or GnuTLS. The code for
-those was hacked out of Exim itself. */
+those was hacked out of Exim itself, then code for OCSP stapling was ripped
+from the openssl ocsp and s_client utilities. */
 
 /* ANSI C standard includes */
 
@@ -68,6 +67,9 @@ latter needs a whole pile of tables. */
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #include <openssl/rand.h>
+#include <openssl/ocsp.h>
+
+char * ocsp_stapling = NULL;
 #endif
 
 
@@ -77,11 +79,9 @@ latter needs a whole pile of tables. */
 #include <gnutls/x509.h>
 
 #define DH_BITS      768
-#define RSA_BITS     512
 
 /* Local static variables for GNUTLS */
 
-static gnutls_rsa_params rsa_params = NULL;
 static gnutls_dh_params dh_params = NULL;
 
 static gnutls_certificate_credentials_t x509_cred = NULL;
@@ -97,7 +97,6 @@ static const int kx_priority[16] = {
   GNUTLS_KX_RSA,
   GNUTLS_KX_DHE_DSS,
   GNUTLS_KX_DHE_RSA,
-  GNUTLS_KX_RSA_EXPORT,
   0 };
 
 static int default_cipher_priority[16] = {
@@ -150,6 +149,91 @@ sigalrm_seen = 1;
 /****************************************************************************/
 
 #ifdef HAVE_OPENSSL
+
+X509_STORE *
+setup_verify(BIO *bp, char *CAfile, char *CApath)
+{
+        X509_STORE *store;
+        X509_LOOKUP *lookup;
+        if(!(store = X509_STORE_new())) goto end;
+        lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
+        if (lookup == NULL) goto end;
+        if (CAfile) {
+                if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) {
+                        BIO_printf(bp, "Error loading file %s\n", CAfile);
+                        goto end;
+                }
+        } else X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
+
+        lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
+        if (lookup == NULL) goto end;
+        if (CApath) {
+                if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) {
+                        BIO_printf(bp, "Error loading directory %s\n", CApath);
+                        goto end;
+                }
+        } else X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
+
+        ERR_clear_error();
+        return store;
+        end:
+        X509_STORE_free(store);
+        return NULL;
+}
+
+
+static int
+tls_client_stapling_cb(SSL *s, void *arg)
+{
+const unsigned char *p;
+int len;
+OCSP_RESPONSE *rsp;
+OCSP_BASICRESP *bs;
+char *CAfile = NULL;
+X509_STORE *store = NULL;
+int ret = 1;
+
+len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+/*BIO_printf(arg, "OCSP response: ");*/
+if (!p)
+       {
+       BIO_printf(arg, "no response received\n");
+       return 1;
+       }
+if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
+       {
+       BIO_printf(arg, "response parse error\n");
+       BIO_dump_indent(arg, (char *)p, len, 4);
+       return 0;
+       }
+if(!(bs = OCSP_response_get1_basic(rsp)))
+  {
+  BIO_printf(arg, "error parsing response\n");
+  return 0;
+  }
+
+CAfile = ocsp_stapling;
+if(!(store = setup_verify(arg, CAfile, NULL)))
+  {
+  BIO_printf(arg, "error in cert setup\n");
+  return 0;
+  }
+
+/* No file of alternate certs, no options */
+if(OCSP_basic_verify(bs, NULL, store, 0) <= 0)
+  {
+  BIO_printf(arg, "Response Verify Failure\n");
+  ERR_print_errors(arg);
+  ret = 0;
+  }
+else
+  BIO_printf(arg, "Response verify OK\n");
+
+X509_STORE_free(store);
+return ret;
+}
+
+
 /*************************************************
 *         Start an OpenSSL TLS session           *
 *************************************************/
@@ -166,6 +250,13 @@ SSL_set_session_id_context(*ssl, sid_ctx, strlen(sid_ctx));
 SSL_set_fd (*ssl, sock);
 SSL_set_connect_state(*ssl);
 
+if (ocsp_stapling)
+  {
+  SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb);
+  SSL_CTX_set_tlsext_status_arg(ctx, BIO_new_fp(stdout, BIO_NOCLOSE));
+  SSL_set_tlsext_status_type(*ssl, TLSEXT_STATUSTYPE_ocsp);
+  }
+
 signal(SIGALRM, sigalrm_handler_flag);
 sigalrm_seen = 0;
 alarm(5);
@@ -225,7 +316,7 @@ Returns:    doesn't - it dies
 static void
 gnutls_error(uschar *prefix, int err)
 {
-fprintf(stderr, "GnuTLS connection error:%s", prefix);
+fprintf(stderr, "GnuTLS connection error: %s:", prefix);
 if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err));
 fprintf(stderr, "\n");
 exit(1);
@@ -234,14 +325,14 @@ exit(1);
 
 
 /*************************************************
-*          Setup up RSA and DH parameters        *
+*             Setup up DH parameters             *
 *************************************************/
 
 /* For the test suite, the parameters should always be available in the spool
 directory. */
 
 static void
-init_rsa_dh(void)
+init_dh(void)
 {
 int fd;
 int ret;
@@ -251,17 +342,11 @@ struct stat statbuf;
 
 /* Initialize the data structures for holding the parameters */
 
-ret = gnutls_rsa_params_init(&rsa_params);
-if (ret < 0) gnutls_error(US"init rsa_params", ret);
-
 ret = gnutls_dh_params_init(&dh_params);
 if (ret < 0) gnutls_error(US"init dh_params", ret);
 
 /* Open the cache file for reading and if successful, read it and set up the
-parameters. If we can't set up the RSA parameters, assume that we are dealing
-with an old-style cache file that is in another format, and fall through to
-compute new values. However, if we correctly get RSA parameters, a failure to
-set up D-H parameters is treated as an error. */
+parameters. */
 
 fd = open("aux-fixed/gnutls-params", O_RDONLY, 0);
 if (fd < 0)
@@ -284,8 +369,6 @@ if (read(fd, m.data, m.size) != m.size)
   return gnutls_error(US"TLS cache read failed", 0);
 (void)close(fd);
 
-ret = gnutls_rsa_params_import_pkcs1(rsa_params, &m, GNUTLS_X509_FMT_PEM);
-if (ret < 0) return gnutls_error(US"RSA params import", ret);
 ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM);
 if (ret < 0) return gnutls_error(US"DH params import", ret);
 free(m.data);
@@ -312,9 +395,9 @@ int rc;
 rc = gnutls_global_init();
 if (rc < 0) gnutls_error(US"gnutls_global_init", rc);
 
-/* Read RSA and D-H parameters from the cache file. */
+/* Read D-H parameters from the cache file. */
 
-init_rsa_dh();
+init_dh();
 
 /* Create the credentials structure */
 
@@ -333,7 +416,6 @@ if (certificate != NULL)
 /* Associate the parameters with the x509 credentials structure. */
 
 gnutls_certificate_set_dh_params(x509_cred, dh_params);
-gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params);
 }
 
 
@@ -375,13 +457,14 @@ return session;
 *                 Main Program                   *
 *************************************************/
 
-/* Usage: client
-          <IP address>
-          <port>
-          [<outgoing interface>]
-          [<cert file>]
-          [<key file>]
-*/
+const char * const HELP_MESSAGE = "\n\
+Usage: client\n\
+          <IP address>\n\
+          <port>\n\
+          [<outgoing interface>]\n\
+          [<cert file>]\n\
+          [<key file>]\n\
+\n";
 
 int main(int argc, char **argv)
 {
@@ -391,12 +474,14 @@ char *interface = NULL;
 char *address = NULL;
 char *certfile = NULL;
 char *keyfile = NULL;
+char *end = NULL;
 int argi = 1;
 int host_af, port, s_len, rc, sock, save_errno;
 int timeout = 1;
 int tls_active = 0;
 int sent_starttls = 0;
 int tls_on_connect = 0;
+long tmplong;
 
 #if HAVE_IPV6
 struct sockaddr_in6 s_in6;
@@ -417,19 +502,55 @@ unsigned char *inptr = inbuffer;
 
 while (argc >= argi + 1 && argv[argi][0] == '-')
   {
+  if (strcmp(argv[argi], "-help") == 0 ||
+      strcmp(argv[argi], "--help") == 0 ||
+      strcmp(argv[argi], "-h") == 0)
+    {
+    printf(HELP_MESSAGE);
+    exit(0);
+    }
   if (strcmp(argv[argi], "-tls-on-connect") == 0)
     {
     tls_on_connect = 1;
     argi++;
     }
+#ifdef HAVE_OPENSSL
+  else if (strcmp(argv[argi], "-ocsp") == 0)
+    {
+    if (argc < ++argi + 1)
+      {
+      fprintf(stderr, "Missing required certificate file for ocsp option\n");
+      exit(1);
+      }
+    ocsp_stapling = argv[argi++];
+    }
+#endif
   else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
     {
-    timeout = atoi(argv[argi]+1);
+    tmplong = strtol(argv[argi]+2, &end, 10);
+    if (end == argv[argi]+2 || *end)
+      {
+      fprintf(stderr, "Failed to parse seconds from option <%s>\n",
+        argv[argi]);
+      exit(1);
+      }
+    if (tmplong > 10000L)
+      {
+      fprintf(stderr, "Unreasonably long wait of %d seconds requested\n",
+        tmplong);
+      exit(1);
+      }
+    if (tmplong < 0L)
+      {
+      fprintf(stderr, "Timeout must not be negative (%d)\n", tmplong);
+      exit(1);
+      }
+    timeout = (int) tmplong;
     argi++;
     }
   else
     {
-    printf("Unrecognized option %s\n", argv[argi]);
+    fprintf(stderr, "Unrecognized option %s\n", argv[argi]);
     exit(1);
     }
   }
@@ -438,7 +559,7 @@ while (argc >= argi + 1 && argv[argi][0] == '-')
 
 if (argc < argi+1)
   {
-  printf("No IP address given\n");
+  fprintf(stderr, "No IP address given\n");
   exit(1);
   }
 
@@ -449,7 +570,7 @@ host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
 
 if (argc < argi+1)
   {
-  printf("No port number given\n");
+  fprintf(stderr, "No port number given\n");
   exit(1);
   }