1 /* A little hacked up program that makes a TCP/IP call and reads a script to
2 drive it, for testing Exim server code running as a daemon. It's got a bit
3 messy with the addition of support for either OpenSSL or GnuTLS. The code for
4 those was hacked out of Exim itself, then code for OpenSSL OCSP stapling was
5 ripped from the openssl ocsp and s_client utilities. */
7 /* ANSI C standard includes */
22 #include <sys/types.h>
24 #include <netinet/in_systm.h>
25 #include <netinet/in.h>
26 #include <netinet/ip.h>
29 #include <arpa/inet.h>
31 #include <sys/resource.h>
32 #include <sys/socket.h>
43 #define S_ADDR_TYPE u_long
46 typedef unsigned char uschar;
49 #define US (unsigned char *)
56 static int sigalrm_seen = 0;
59 /* TLS support can be optionally included, either for OpenSSL or GnuTLS. The
60 latter needs a whole pile of tables. */
63 # include <openssl/crypto.h>
64 # include <openssl/x509.h>
65 # include <openssl/pem.h>
66 # include <openssl/ssl.h>
67 # include <openssl/err.h>
68 # include <openssl/rand.h>
70 # if OPENSSL_VERSION_NUMBER < 0x0090806fL && !defined(DISABLE_OCSP) && !defined(OPENSSL_NO_TLSEXT)
71 # warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
75 # include <openssl/ocsp.h>
82 # include <gnutls/gnutls.h>
83 # include <gnutls/x509.h>
84 # if GNUTLS_VERSION_NUMBER >= 0x030103
86 # include <gnutls/ocsp.h>
88 # ifndef GNUTLS_NO_EXTENSIONS
89 # define GNUTLS_NO_EXTENSIONS 0
94 /* Local static variables for GNUTLS */
96 static gnutls_dh_params dh_params = NULL;
98 static gnutls_certificate_credentials_t x509_cred = NULL;
99 static gnutls_session tls_session = NULL;
101 static int ssl_session_timeout = 200;
103 /* Priorities for TLS algorithms to use. */
105 static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
107 static const int kx_priority[16] = {
113 static int default_cipher_priority[16] = {
114 GNUTLS_CIPHER_AES_256_CBC,
115 GNUTLS_CIPHER_AES_128_CBC,
116 GNUTLS_CIPHER_3DES_CBC,
117 GNUTLS_CIPHER_ARCFOUR_128,
120 static const int mac_priority[16] = {
125 static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
126 static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 };
128 #endif /*HAVE_GNUTLS*/
133 char * ocsp_stapling = NULL;
137 /*************************************************
138 * SIGALRM handler - crash out *
139 *************************************************/
142 sigalrm_handler_crash(int sig)
144 sig = sig; /* Keep picky compilers happy */
145 printf("\nClient timed out\n");
150 /*************************************************
151 * SIGALRM handler - set flag *
152 *************************************************/
155 sigalrm_handler_flag(int sig)
157 sig = sig; /* Keep picky compilers happy */
163 /****************************************************************************/
164 /****************************************************************************/
169 setup_verify(BIO *bp, char *CAfile, char *CApath)
173 if(!(store = X509_STORE_new())) goto end;
174 lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
175 if (lookup == NULL) goto end;
177 if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) {
178 BIO_printf(bp, "Error loading file %s\n", CAfile);
181 } else X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
183 lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
184 if (lookup == NULL) goto end;
186 if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) {
187 BIO_printf(bp, "Error loading directory %s\n", CApath);
190 } else X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
195 X509_STORE_free(store);
202 tls_client_stapling_cb(SSL *s, void *arg)
204 const unsigned char *p;
209 X509_STORE *store = NULL;
212 len = SSL_get_tlsext_status_ocsp_resp(s, &p);
213 /*BIO_printf(arg, "OCSP response: ");*/
216 BIO_printf(arg, "no response received\n");
219 if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
221 BIO_printf(arg, "response parse error\n");
222 BIO_dump_indent(arg, (char *)p, len, 4);
225 if(!(bs = OCSP_response_get1_basic(rsp)))
227 BIO_printf(arg, "error parsing response\n");
231 CAfile = ocsp_stapling;
232 if(!(store = setup_verify(arg, CAfile, NULL)))
234 BIO_printf(arg, "error in cert setup\n");
238 /* No file of alternate certs, no options */
239 if(OCSP_basic_verify(bs, NULL, store, 0) <= 0)
241 BIO_printf(arg, "Response Verify Failure\n");
242 ERR_print_errors(arg);
246 BIO_printf(arg, "Response verify OK\n");
248 X509_STORE_free(store);
254 /*************************************************
255 * Start an OpenSSL TLS session *
256 *************************************************/
259 tls_start(int sock, SSL **ssl, SSL_CTX *ctx)
262 static const unsigned char *sid_ctx = US"exim";
264 RAND_load_file("client.c", -1); /* Not *very* random! */
266 *ssl = SSL_new (ctx);
267 SSL_set_session_id_context(*ssl, sid_ctx, strlen(CS sid_ctx));
268 SSL_set_fd (*ssl, sock);
269 SSL_set_connect_state(*ssl);
274 SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb);
275 SSL_CTX_set_tlsext_status_arg(ctx, BIO_new_fp(stdout, BIO_NOCLOSE));
276 SSL_set_tlsext_status_type(*ssl, TLSEXT_STATUSTYPE_ocsp);
280 signal(SIGALRM, sigalrm_handler_flag);
283 rc = SSL_connect (*ssl);
288 printf("SSL_connect timed out\n");
294 ERR_print_errors_fp(stdout);
298 printf("SSL connection using %s\n", SSL_get_cipher (*ssl));
303 /*************************************************
304 * SSL Information callback *
305 *************************************************/
308 info_callback(SSL *s, int where, int ret)
312 printf("SSL info: %s\n", SSL_state_string_long(s));
317 /****************************************************************************/
318 /****************************************************************************/
322 /*************************************************
323 * Handle GnuTLS error *
324 *************************************************/
326 /* Called from lots of places when errors occur before actually starting to do
327 the TLS handshake, that is, while the session is still in clear.
331 err a GnuTLS error number, or 0 if local error
333 Returns: doesn't - it dies
337 gnutls_error(uschar *prefix, int err)
339 fprintf(stderr, "GnuTLS connection error: %s:", prefix);
340 if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err));
341 fprintf(stderr, "\n");
347 /*************************************************
348 * Setup up DH parameters *
349 *************************************************/
351 /* For the test suite, the parameters should always be available in the spool
360 uschar filename[200];
363 /* Initialize the data structures for holding the parameters */
365 ret = gnutls_dh_params_init(&dh_params);
366 if (ret < 0) gnutls_error(US"init dh_params", ret);
368 /* Open the cache file for reading and if successful, read it and set up the
371 fd = open("aux-fixed/gnutls-params", O_RDONLY, 0);
374 fprintf(stderr, "Failed to open spool/gnutls-params: %s\n", strerror(errno));
378 if (fstat(fd, &statbuf) < 0)
381 return gnutls_error(US"TLS cache stat failed", 0);
384 m.size = statbuf.st_size;
385 m.data = malloc(m.size);
387 return gnutls_error(US"memory allocation failed", 0);
388 if (read(fd, m.data, m.size) != m.size)
389 return gnutls_error(US"TLS cache read failed", 0);
392 ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM);
393 if (ret < 0) return gnutls_error(US"DH params import", ret);
400 /*************************************************
401 * Initialize for GnuTLS *
402 *************************************************/
406 certificate certificate file
407 privatekey private key file
411 tls_init(uschar *certificate, uschar *privatekey)
415 rc = gnutls_global_init();
416 if (rc < 0) gnutls_error(US"gnutls_global_init", rc);
418 /* Read D-H parameters from the cache file. */
422 /* Create the credentials structure */
424 rc = gnutls_certificate_allocate_credentials(&x509_cred);
425 if (rc < 0) gnutls_error(US"certificate_allocate_credentials", rc);
427 /* Set the certificate and private keys */
429 if (certificate != NULL)
431 rc = gnutls_certificate_set_x509_key_file(x509_cred, CS certificate,
432 CS privatekey, GNUTLS_X509_FMT_PEM);
433 if (rc < 0) gnutls_error("gnutls_certificate", rc);
436 /* Associate the parameters with the x509 credentials structure. */
438 gnutls_certificate_set_dh_params(x509_cred, dh_params);
440 /* set the CA info for server-cert verify */
442 gnutls_certificate_set_x509_trust_file(x509_cred, ocsp_stapling,
443 GNUTLS_X509_FMT_PEM);
448 /*************************************************
449 * Initialize a single GNUTLS session *
450 *************************************************/
452 static gnutls_session
453 tls_session_init(void)
455 gnutls_session session;
457 gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_NO_EXTENSIONS);
459 gnutls_cipher_set_priority(session, default_cipher_priority);
460 gnutls_compression_set_priority(session, comp_priority);
461 gnutls_kx_set_priority(session, kx_priority);
462 gnutls_protocol_set_priority(session, protocol_priority);
463 gnutls_mac_set_priority(session, mac_priority);
465 gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
467 gnutls_dh_set_prime_bits(session, DH_BITS);
468 gnutls_db_set_cache_expiration(session, ssl_session_timeout);
475 /****************************************************************************/
476 /****************************************************************************/
481 /*************************************************
483 *************************************************/
485 const char * const HELP_MESSAGE = "\n\
493 [-tn] n seconds timeout\n\
496 [<outgoing interface>]\n\
501 int main(int argc, char **argv)
503 struct sockaddr *s_ptr;
504 struct sockaddr_in s_in4;
505 char *interface = NULL;
506 char *address = NULL;
507 char *certfile = NULL;
508 char *keyfile = NULL;
511 int host_af, port, s_len, rc, sock, save_errno;
514 int sent_starttls = 0;
515 int tls_on_connect = 0;
519 struct sockaddr_in6 s_in6;
527 unsigned char outbuffer[10240];
528 unsigned char inbuffer[10240];
529 unsigned char *inptr = inbuffer;
531 *inptr = 0; /* Buffer empty */
535 while (argc >= argi + 1 && argv[argi][0] == '-')
537 if (strcmp(argv[argi], "-help") == 0 ||
538 strcmp(argv[argi], "--help") == 0 ||
539 strcmp(argv[argi], "-h") == 0)
544 if (strcmp(argv[argi], "-tls-on-connect") == 0)
550 else if (strcmp(argv[argi], "-ocsp") == 0)
552 if (argc < ++argi + 1)
554 fprintf(stderr, "Missing required certificate file for ocsp option\n");
557 ocsp_stapling = argv[argi++];
561 else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
563 tmplong = strtol(argv[argi]+2, &end, 10);
564 if (end == argv[argi]+2 || *end)
566 fprintf(stderr, "Failed to parse seconds from option <%s>\n",
570 if (tmplong > 10000L)
572 fprintf(stderr, "Unreasonably long wait of %ld seconds requested\n",
578 fprintf(stderr, "Timeout must not be negative (%ld)\n", tmplong);
581 timeout = (int) tmplong;
586 fprintf(stderr, "Unrecognized option %s\n", argv[argi]);
591 /* Mandatory 1st arg is IP address */
595 fprintf(stderr, "No IP address given\n");
599 address = argv[argi++];
600 host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
602 /* Mandatory 2nd arg is port */
606 fprintf(stderr, "No port number given\n");
610 port = atoi(argv[argi++]);
612 /* Optional next arg is interface */
615 (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
616 interface = argv[argi++];
618 /* Any more arguments are the name of a certificate file and key file */
620 if (argc > argi) certfile = argv[argi++];
621 if (argc > argi) keyfile = argv[argi++];
625 /* For an IPv6 address, use an IPv6 sockaddr structure. */
627 if (host_af == AF_INET6)
629 s_ptr = (struct sockaddr *)&s_in6;
630 s_len = sizeof(s_in6);
635 /* For an IPv4 address, use an IPv4 sockaddr structure,
636 even on an IPv6 system. */
639 s_ptr = (struct sockaddr *)&s_in4;
640 s_len = sizeof(s_in4);
643 printf("Connecting to %s port %d ... ", address, port);
645 sock = socket(host_af, SOCK_STREAM, 0);
648 printf("socket creation failed: %s\n", strerror(errno));
652 /* Bind to a specific interface if requested. On an IPv6 system, this has
653 to be of the same family as the address we are calling. On an IPv4 system the
654 test is redundant, but it keeps the code tidier. */
656 if (interface != NULL)
658 int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;
660 if (interface_af == host_af)
664 /* Set up for IPv6 binding */
666 if (host_af == AF_INET6)
668 memset(&s_in6, 0, sizeof(s_in6));
669 s_in6.sin6_family = AF_INET6;
671 if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
673 printf("Unable to parse \"%s\"", interface);
680 /* Set up for IPv4 binding */
683 memset(&s_in4, 0, sizeof(s_in4));
684 s_in4.sin_family = AF_INET;
686 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
691 if (bind(sock, s_ptr, s_len) < 0)
693 printf("Unable to bind outgoing SMTP call to %s: %s",
694 interface, strerror(errno));
700 /* Set up a remote IPv6 address */
703 if (host_af == AF_INET6)
705 memset(&s_in6, 0, sizeof(s_in6));
706 s_in6.sin6_family = AF_INET6;
707 s_in6.sin6_port = htons(port);
708 if (inet_pton(host_af, address, &s_in6.sin6_addr) != 1)
710 printf("Unable to parse \"%s\"", address);
717 /* Set up a remote IPv4 address */
720 memset(&s_in4, 0, sizeof(s_in4));
721 s_in4.sin_family = AF_INET;
722 s_in4.sin_port = htons(port);
723 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(address);
726 /* SIGALRM handler crashes out */
728 signal(SIGALRM, sigalrm_handler_crash);
730 rc = connect(sock, s_ptr, s_len);
734 /* A failure whose error code is "Interrupted system call" is in fact
735 an externally applied timeout if the signal handler has been run. */
740 printf("connect failed: %s\n", strerror(save_errno));
744 printf("connected\n");
747 /* --------------- Set up for OpenSSL --------------- */
751 SSL_load_error_strings();
753 ctx = SSL_CTX_new(SSLv23_method());
756 printf ("SSL_CTX_new failed\n");
760 if (certfile != NULL)
762 if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
764 printf("SSL_CTX_use_certificate_file failed\n");
767 printf("Certificate file = %s\n", certfile);
772 if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
774 printf("SSL_CTX_use_PrivateKey_file failed\n");
777 printf("Key file = %s\n", keyfile);
780 SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
781 SSL_CTX_set_timeout(ctx, 200);
782 SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
786 /* --------------- Set up for GnuTLS --------------- */
789 if (certfile != NULL) printf("Certificate file = %s\n", certfile);
790 if (keyfile != NULL) printf("Key file = %s\n", keyfile);
791 tls_init(certfile, keyfile);
792 tls_session = tls_session_init();
795 gnutls_ocsp_status_request_enable_client(tls_session, NULL, 0, NULL);
797 gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)sock);
799 /* When the server asks for a certificate and the client does not have one,
800 there is a SIGPIPE error in the gnutls_handshake() function for some reason
801 that is not understood. As luck would have it, this has never hit Exim itself
802 because it ignores SIGPIPE errors. Doing the same here allows it all to work as
805 signal(SIGPIPE, SIG_IGN);
808 /* ---------------------------------------------- */
811 /* Start TLS session if configured to do so without STARTTLS */
816 printf("Attempting to start TLS\n");
819 tls_active = tls_start(sock, &ssl, ctx);
823 sigalrm_seen = FALSE;
825 tls_active = gnutls_handshake(tls_session) >= 0;
830 printf("Failed to start TLS\n");
831 #if defined(HAVE_GNUTLS) && defined(HAVE_OCSP)
832 else if ( ocsp_stapling
833 && gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0)
834 printf("Failed to verify certificate status\n");
837 printf("Succeeded in starting TLS\n");
841 while (fgets(CS outbuffer, sizeof(outbuffer), stdin) != NULL)
843 int n = (int)strlen(CS outbuffer);
844 while (n > 0 && isspace(outbuffer[n-1])) n--;
847 /* Expect incoming */
849 if (strncmp(CS outbuffer, "??? ", 4) == 0)
851 unsigned char *lineptr;
852 printf("%s\n", outbuffer);
854 if (*inptr == 0) /* Refill input buffer */
859 rc = SSL_read (ssl, inbuffer, sizeof(inbuffer) - 1);
862 rc = gnutls_record_recv(tls_session, CS inbuffer, sizeof(inbuffer) - 1);
868 rc = read(sock, inbuffer, sizeof(inbuffer));
874 printf("Read error %s\n", strerror(errno));
879 printf("Unexpected EOF read\n");
891 while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
895 if (*inptr == '\n') inptr++;
898 printf("<<< %s\n", lineptr);
899 if (strncmp(CS lineptr, CS outbuffer + 4, (int)strlen(CS outbuffer) - 4) != 0)
901 printf("\n******** Input mismatch ********\n");
908 if (lineptr[0] == '2')
913 printf("Attempting to start TLS\n");
917 tls_active = tls_start(sock, &ssl, ctx);
921 sigalrm_seen = FALSE;
923 tls_active = gnutls_handshake(tls_session) >= 0;
929 printf("Failed to start TLS\n");
933 else if (ocsp_stapling)
935 if ((rc= gnutls_certificate_verify_peers2(tls_session, &verify)) < 0)
937 printf("Failed to verify certificate: %s\n", gnutls_strerror(rc));
940 else if (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED))
942 printf("Bad certificate\n");
946 else if (gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0)
948 printf("Failed to verify certificate status\n");
950 gnutls_datum_t stapling;
951 gnutls_ocsp_resp_t resp;
952 gnutls_datum_t printed;
953 if ( (rc= gnutls_ocsp_status_request_get(tls_session, &stapling)) == 0
954 && (rc= gnutls_ocsp_resp_init(&resp)) == 0
955 && (rc= gnutls_ocsp_resp_import(resp, &stapling)) == 0
956 && (rc= gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &printed)) == 0
959 fprintf(stderr, "%.4096s", printed.data);
960 gnutls_free(printed.data);
963 (void) fprintf(stderr,"ocsp decode: %s", gnutls_strerror(rc));
971 printf("Succeeded in starting TLS\n");
973 else printf("Abandoning TLS start attempt\n");
979 /* Wait for a bit before proceeding */
981 else if (strncmp(CS outbuffer, "+++ ", 4) == 0)
983 printf("%s\n", outbuffer);
984 sleep(atoi(CS outbuffer + 4));
987 /* Send outgoing, but barf if unconsumed incoming */
991 unsigned char *escape;
995 printf("Unconsumed input: %s", inptr);
996 printf(" About to send: %s\n", outbuffer);
1004 if (strcmp(CS outbuffer, "stoptls") == 0 ||
1005 strcmp(CS outbuffer, "STOPTLS") == 0)
1009 printf("STOPTLS read when TLS not active\n");
1012 printf("Shutting down TLS encryption\n");
1020 gnutls_bye(tls_session, GNUTLS_SHUT_WR);
1021 gnutls_deinit(tls_session);
1023 gnutls_global_deinit();
1030 /* Remember that we sent STARTTLS */
1032 sent_starttls = (strcmp(CS outbuffer, "starttls") == 0 ||
1033 strcmp(CS outbuffer, "STARTTLS") == 0);
1035 /* Fudge: if the command is "starttls_wait", we send the starttls bit,
1036 but we haven't set the flag, so that there is no negotiation. This is for
1037 testing the server's timeout. */
1039 if (strcmp(CS outbuffer, "starttls_wait") == 0)
1046 printf(">>> %s\n", outbuffer);
1047 strcpy(CS outbuffer + n, "\r\n");
1049 /* Turn "\n" and "\r" into the relevant characters. This is a hack. */
1051 while ((escape = US strstr(CS outbuffer, "\\r")) != NULL)
1054 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
1058 while ((escape = US strstr(CS outbuffer, "\\n")) != NULL)
1061 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
1071 rc = SSL_write (ssl, outbuffer, n + 2);
1074 rc = gnutls_record_send(tls_session, CS outbuffer, n + 2);
1077 printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
1084 rc = write(sock, outbuffer, n + 2);
1090 printf("Write error: %s\n", strerror(errno));
1096 printf("End of script\n");
1102 /* End of client.c */