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. */
6 /* ANSI C standard includes */
21 #include <sys/types.h>
23 #include <netinet/in_systm.h>
24 #include <netinet/in.h>
25 #include <netinet/ip.h>
28 #include <arpa/inet.h>
30 #include <sys/resource.h>
31 #include <sys/socket.h>
42 #define S_ADDR_TYPE u_long
45 typedef unsigned char uschar;
48 #define US (unsigned char *)
55 static int sigalrm_seen = 0;
58 /* TLS support can be optionally included, either for OpenSSL or GnuTLS. The
59 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>
74 #include <gnutls/gnutls.h>
75 #include <gnutls/x509.h>
79 /* Local static variables for GNUTLS */
81 static gnutls_dh_params dh_params = NULL;
83 static gnutls_certificate_credentials_t x509_cred = NULL;
84 static gnutls_session tls_session = NULL;
86 static int ssl_session_timeout = 200;
88 /* Priorities for TLS algorithms to use. */
90 static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
92 static const int kx_priority[16] = {
98 static int default_cipher_priority[16] = {
99 GNUTLS_CIPHER_AES_256_CBC,
100 GNUTLS_CIPHER_AES_128_CBC,
101 GNUTLS_CIPHER_3DES_CBC,
102 GNUTLS_CIPHER_ARCFOUR_128,
105 static const int mac_priority[16] = {
110 static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
111 static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 };
118 /*************************************************
119 * SIGALRM handler - crash out *
120 *************************************************/
123 sigalrm_handler_crash(int sig)
125 sig = sig; /* Keep picky compilers happy */
126 printf("\nClient timed out\n");
131 /*************************************************
132 * SIGALRM handler - set flag *
133 *************************************************/
136 sigalrm_handler_flag(int sig)
138 sig = sig; /* Keep picky compilers happy */
144 /****************************************************************************/
145 /****************************************************************************/
148 /*************************************************
149 * Start an OpenSSL TLS session *
150 *************************************************/
152 int tls_start(int sock, SSL **ssl, SSL_CTX *ctx)
155 static const char *sid_ctx = "exim";
157 RAND_load_file("client.c", -1); /* Not *very* random! */
159 *ssl = SSL_new (ctx);
160 SSL_set_session_id_context(*ssl, sid_ctx, strlen(sid_ctx));
161 SSL_set_fd (*ssl, sock);
162 SSL_set_connect_state(*ssl);
164 signal(SIGALRM, sigalrm_handler_flag);
167 rc = SSL_connect (*ssl);
172 printf("SSL_connect timed out\n");
178 ERR_print_errors_fp(stdout);
182 printf("SSL connection using %s\n", SSL_get_cipher (*ssl));
187 /*************************************************
188 * SSL Information callback *
189 *************************************************/
192 info_callback(SSL *s, int where, int ret)
196 printf("SSL info: %s\n", SSL_state_string_long(s));
201 /****************************************************************************/
202 /****************************************************************************/
206 /*************************************************
207 * Handle GnuTLS error *
208 *************************************************/
210 /* Called from lots of places when errors occur before actually starting to do
211 the TLS handshake, that is, while the session is still in clear.
215 err a GnuTLS error number, or 0 if local error
217 Returns: doesn't - it dies
221 gnutls_error(uschar *prefix, int err)
223 fprintf(stderr, "GnuTLS connection error: %s:", prefix);
224 if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err));
225 fprintf(stderr, "\n");
231 /*************************************************
232 * Setup up DH parameters *
233 *************************************************/
235 /* For the test suite, the parameters should always be available in the spool
244 uschar filename[200];
247 /* Initialize the data structures for holding the parameters */
249 ret = gnutls_dh_params_init(&dh_params);
250 if (ret < 0) gnutls_error(US"init dh_params", ret);
252 /* Open the cache file for reading and if successful, read it and set up the
255 fd = open("aux-fixed/gnutls-params", O_RDONLY, 0);
258 fprintf(stderr, "Failed to open spool/gnutls-params: %s\n", strerror(errno));
262 if (fstat(fd, &statbuf) < 0)
265 return gnutls_error(US"TLS cache stat failed", 0);
268 m.size = statbuf.st_size;
269 m.data = malloc(m.size);
271 return gnutls_error(US"memory allocation failed", 0);
272 if (read(fd, m.data, m.size) != m.size)
273 return gnutls_error(US"TLS cache read failed", 0);
276 ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM);
277 if (ret < 0) return gnutls_error(US"DH params import", ret);
284 /*************************************************
285 * Initialize for GnuTLS *
286 *************************************************/
290 certificate certificate file
291 privatekey private key file
295 tls_init(uschar *certificate, uschar *privatekey)
299 rc = gnutls_global_init();
300 if (rc < 0) gnutls_error(US"gnutls_global_init", rc);
302 /* Read D-H parameters from the cache file. */
306 /* Create the credentials structure */
308 rc = gnutls_certificate_allocate_credentials(&x509_cred);
309 if (rc < 0) gnutls_error(US"certificate_allocate_credentials", rc);
311 /* Set the certificate and private keys */
313 if (certificate != NULL)
315 rc = gnutls_certificate_set_x509_key_file(x509_cred, CS certificate,
316 CS privatekey, GNUTLS_X509_FMT_PEM);
317 if (rc < 0) gnutls_error("gnutls_certificate", rc);
320 /* Associate the parameters with the x509 credentials structure. */
322 gnutls_certificate_set_dh_params(x509_cred, dh_params);
327 /*************************************************
328 * Initialize a single GNUTLS session *
329 *************************************************/
331 static gnutls_session
332 tls_session_init(void)
334 gnutls_session session;
336 gnutls_init(&session, GNUTLS_CLIENT);
338 gnutls_cipher_set_priority(session, default_cipher_priority);
339 gnutls_compression_set_priority(session, comp_priority);
340 gnutls_kx_set_priority(session, kx_priority);
341 gnutls_protocol_set_priority(session, protocol_priority);
342 gnutls_mac_set_priority(session, mac_priority);
344 gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
346 gnutls_dh_set_prime_bits(session, DH_BITS);
347 gnutls_db_set_cache_expiration(session, ssl_session_timeout);
354 /****************************************************************************/
355 /****************************************************************************/
360 /*************************************************
362 *************************************************/
364 const char * const HELP_MESSAGE = "\n\
368 [<outgoing interface>]\n\
373 int main(int argc, char **argv)
375 struct sockaddr *s_ptr;
376 struct sockaddr_in s_in4;
377 char *interface = NULL;
378 char *address = NULL;
379 char *certfile = NULL;
380 char *keyfile = NULL;
382 int host_af, port, s_len, rc, sock, save_errno;
385 int sent_starttls = 0;
386 int tls_on_connect = 0;
389 struct sockaddr_in6 s_in6;
397 unsigned char outbuffer[10240];
398 unsigned char inbuffer[10240];
399 unsigned char *inptr = inbuffer;
401 *inptr = 0; /* Buffer empty */
405 while (argc >= argi + 1 && argv[argi][0] == '-')
407 if (strcmp(argv[argi], "-help") == 0 ||
408 strcmp(argv[argi], "--help") == 0 ||
409 strcmp(argv[argi], "-h") == 0)
411 printf(HELP_MESSAGE);
414 if (strcmp(argv[argi], "-tls-on-connect") == 0)
419 else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
421 timeout = atoi(argv[argi]+1);
426 printf("Unrecognized option %s\n", argv[argi]);
431 /* Mandatory 1st arg is IP address */
435 printf("No IP address given\n");
439 address = argv[argi++];
440 host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
442 /* Mandatory 2nd arg is port */
446 printf("No port number given\n");
450 port = atoi(argv[argi++]);
452 /* Optional next arg is interface */
455 (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
456 interface = argv[argi++];
458 /* Any more arguments are the name of a certificate file and key file */
460 if (argc > argi) certfile = argv[argi++];
461 if (argc > argi) keyfile = argv[argi++];
465 /* For an IPv6 address, use an IPv6 sockaddr structure. */
467 if (host_af == AF_INET6)
469 s_ptr = (struct sockaddr *)&s_in6;
470 s_len = sizeof(s_in6);
475 /* For an IPv4 address, use an IPv4 sockaddr structure,
476 even on an IPv6 system. */
479 s_ptr = (struct sockaddr *)&s_in4;
480 s_len = sizeof(s_in4);
483 printf("Connecting to %s port %d ... ", address, port);
485 sock = socket(host_af, SOCK_STREAM, 0);
488 printf("socket creation failed: %s\n", strerror(errno));
492 /* Bind to a specific interface if requested. On an IPv6 system, this has
493 to be of the same family as the address we are calling. On an IPv4 system the
494 test is redundant, but it keeps the code tidier. */
496 if (interface != NULL)
498 int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;
500 if (interface_af == host_af)
504 /* Set up for IPv6 binding */
506 if (host_af == AF_INET6)
508 memset(&s_in6, 0, sizeof(s_in6));
509 s_in6.sin6_family = AF_INET6;
511 if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
513 printf("Unable to parse \"%s\"", interface);
520 /* Set up for IPv4 binding */
523 memset(&s_in4, 0, sizeof(s_in4));
524 s_in4.sin_family = AF_INET;
526 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
531 if (bind(sock, s_ptr, s_len) < 0)
533 printf("Unable to bind outgoing SMTP call to %s: %s",
534 interface, strerror(errno));
540 /* Set up a remote IPv6 address */
543 if (host_af == AF_INET6)
545 memset(&s_in6, 0, sizeof(s_in6));
546 s_in6.sin6_family = AF_INET6;
547 s_in6.sin6_port = htons(port);
548 if (inet_pton(host_af, address, &s_in6.sin6_addr) != 1)
550 printf("Unable to parse \"%s\"", address);
557 /* Set up a remote IPv4 address */
560 memset(&s_in4, 0, sizeof(s_in4));
561 s_in4.sin_family = AF_INET;
562 s_in4.sin_port = htons(port);
563 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(address);
566 /* SIGALRM handler crashes out */
568 signal(SIGALRM, sigalrm_handler_crash);
570 rc = connect(sock, s_ptr, s_len);
574 /* A failure whose error code is "Interrupted system call" is in fact
575 an externally applied timeout if the signal handler has been run. */
580 printf("failed: %s\n", strerror(save_errno));
584 printf("connected\n");
587 /* --------------- Set up for OpenSSL --------------- */
591 SSL_load_error_strings();
593 ctx = SSL_CTX_new(SSLv23_method());
596 printf ("SSL_CTX_new failed\n");
600 if (certfile != NULL)
602 if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
604 printf("SSL_CTX_use_certificate_file failed\n");
607 printf("Certificate file = %s\n", certfile);
612 if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
614 printf("SSL_CTX_use_PrivateKey_file failed\n");
617 printf("Key file = %s\n", keyfile);
620 SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
621 SSL_CTX_set_timeout(ctx, 200);
622 SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
626 /* --------------- Set up for GnuTLS --------------- */
629 if (certfile != NULL) printf("Certificate file = %s\n", certfile);
630 if (keyfile != NULL) printf("Key file = %s\n", keyfile);
631 tls_init(certfile, keyfile);
632 tls_session = tls_session_init();
633 gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)sock);
635 /* When the server asks for a certificate and the client does not have one,
636 there is a SIGPIPE error in the gnutls_handshake() function for some reason
637 that is not understood. As luck would have it, this has never hit Exim itself
638 because it ignores SIGPIPE errors. Doing the same here allows it all to work as
641 signal(SIGPIPE, SIG_IGN);
644 /* ---------------------------------------------- */
647 /* Start TLS session if configured to do so without STARTTLS */
652 printf("Attempting to start TLS\n");
655 tls_active = tls_start(sock, &ssl, ctx);
659 sigalrm_seen = FALSE;
661 tls_active = gnutls_handshake(tls_session) >= 0;
666 printf("Failed to start TLS\n");
668 printf("Succeeded in starting TLS\n");
672 while (fgets(outbuffer, sizeof(outbuffer), stdin) != NULL)
674 int n = (int)strlen(outbuffer);
675 while (n > 0 && isspace(outbuffer[n-1])) n--;
678 /* Expect incoming */
680 if (strncmp(outbuffer, "??? ", 4) == 0)
682 unsigned char *lineptr;
683 printf("%s\n", outbuffer);
685 if (*inptr == 0) /* Refill input buffer */
690 rc = SSL_read (ssl, inbuffer, sizeof(inbuffer) - 1);
693 rc = gnutls_record_recv(tls_session, CS inbuffer, sizeof(inbuffer) - 1);
699 rc = read(sock, inbuffer, sizeof(inbuffer));
705 printf("Read error %s\n", strerror(errno));
710 printf("Unexpected EOF read\n");
722 while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
726 if (*inptr == '\n') inptr++;
729 printf("<<< %s\n", lineptr);
730 if (strncmp(lineptr, outbuffer + 4, (int)strlen(outbuffer) - 4) != 0)
732 printf("\n******** Input mismatch ********\n");
739 if (lineptr[0] == '2')
741 printf("Attempting to start TLS\n");
745 tls_active = tls_start(sock, &ssl, ctx);
749 sigalrm_seen = FALSE;
751 tls_active = gnutls_handshake(tls_session) >= 0;
757 printf("Failed to start TLS\n");
761 printf("Succeeded in starting TLS\n");
763 else printf("Abandoning TLS start attempt\n");
769 /* Wait for a bit before proceeding */
771 else if (strncmp(outbuffer, "+++ ", 4) == 0)
773 printf("%s\n", outbuffer);
774 sleep(atoi(outbuffer + 4));
777 /* Send outgoing, but barf if unconsumed incoming */
781 unsigned char *escape;
785 printf("Unconsumed input: %s", inptr);
786 printf(" About to send: %s\n", outbuffer);
794 if (strcmp(outbuffer, "stoptls") == 0 ||
795 strcmp(outbuffer, "STOPTLS") == 0)
799 printf("STOPTLS read when TLS not active\n");
802 printf("Shutting down TLS encryption\n");
810 gnutls_bye(tls_session, GNUTLS_SHUT_WR);
811 gnutls_deinit(tls_session);
813 gnutls_global_deinit();
820 /* Remember that we sent STARTTLS */
822 sent_starttls = (strcmp(outbuffer, "starttls") == 0 ||
823 strcmp(outbuffer, "STARTTLS") == 0);
825 /* Fudge: if the command is "starttls_wait", we send the starttls bit,
826 but we haven't set the flag, so that there is no negotiation. This is for
827 testing the server's timeout. */
829 if (strcmp(outbuffer, "starttls_wait") == 0)
836 printf(">>> %s\n", outbuffer);
837 strcpy(outbuffer + n, "\r\n");
839 /* Turn "\n" and "\r" into the relevant characters. This is a hack. */
841 while ((escape = strstr(outbuffer, "\\r")) != NULL)
844 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
848 while ((escape = strstr(outbuffer, "\\n")) != NULL)
851 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
861 rc = SSL_write (ssl, outbuffer, n + 2);
864 rc = gnutls_record_send(tls_session, CS outbuffer, n + 2);
867 printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
874 rc = write(sock, outbuffer, n + 2);
880 printf("Write error: %s\n", strerror(errno));
886 printf("End of script\n");
892 /* End of client.c */