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;
383 int host_af, port, s_len, rc, sock, save_errno;
386 int sent_starttls = 0;
387 int tls_on_connect = 0;
391 struct sockaddr_in6 s_in6;
399 unsigned char outbuffer[10240];
400 unsigned char inbuffer[10240];
401 unsigned char *inptr = inbuffer;
403 *inptr = 0; /* Buffer empty */
407 while (argc >= argi + 1 && argv[argi][0] == '-')
409 if (strcmp(argv[argi], "-help") == 0 ||
410 strcmp(argv[argi], "--help") == 0 ||
411 strcmp(argv[argi], "-h") == 0)
413 printf(HELP_MESSAGE);
416 if (strcmp(argv[argi], "-tls-on-connect") == 0)
421 else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
423 tmplong = strtol(argv[argi]+2, &end, 10);
424 if (end == argv[argi]+2 || *end)
426 fprintf(stderr, "Failed to parse seconds from option <%s>\n",
430 if (tmplong > 10000L)
432 fprintf(stderr, "Unreasonably long wait of %d seconds requested\n",
438 fprintf(stderr, "Timeout must not be negative (%d)\n", tmplong);
441 timeout = (int) tmplong;
446 fprintf(stderr, "Unrecognized option %s\n", argv[argi]);
451 /* Mandatory 1st arg is IP address */
455 fprintf(stderr, "No IP address given\n");
459 address = argv[argi++];
460 host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
462 /* Mandatory 2nd arg is port */
466 fprintf(stderr, "No port number given\n");
470 port = atoi(argv[argi++]);
472 /* Optional next arg is interface */
475 (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
476 interface = argv[argi++];
478 /* Any more arguments are the name of a certificate file and key file */
480 if (argc > argi) certfile = argv[argi++];
481 if (argc > argi) keyfile = argv[argi++];
485 /* For an IPv6 address, use an IPv6 sockaddr structure. */
487 if (host_af == AF_INET6)
489 s_ptr = (struct sockaddr *)&s_in6;
490 s_len = sizeof(s_in6);
495 /* For an IPv4 address, use an IPv4 sockaddr structure,
496 even on an IPv6 system. */
499 s_ptr = (struct sockaddr *)&s_in4;
500 s_len = sizeof(s_in4);
503 printf("Connecting to %s port %d ... ", address, port);
505 sock = socket(host_af, SOCK_STREAM, 0);
508 printf("socket creation failed: %s\n", strerror(errno));
512 /* Bind to a specific interface if requested. On an IPv6 system, this has
513 to be of the same family as the address we are calling. On an IPv4 system the
514 test is redundant, but it keeps the code tidier. */
516 if (interface != NULL)
518 int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;
520 if (interface_af == host_af)
524 /* Set up for IPv6 binding */
526 if (host_af == AF_INET6)
528 memset(&s_in6, 0, sizeof(s_in6));
529 s_in6.sin6_family = AF_INET6;
531 if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
533 printf("Unable to parse \"%s\"", interface);
540 /* Set up for IPv4 binding */
543 memset(&s_in4, 0, sizeof(s_in4));
544 s_in4.sin_family = AF_INET;
546 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
551 if (bind(sock, s_ptr, s_len) < 0)
553 printf("Unable to bind outgoing SMTP call to %s: %s",
554 interface, strerror(errno));
560 /* Set up a remote IPv6 address */
563 if (host_af == AF_INET6)
565 memset(&s_in6, 0, sizeof(s_in6));
566 s_in6.sin6_family = AF_INET6;
567 s_in6.sin6_port = htons(port);
568 if (inet_pton(host_af, address, &s_in6.sin6_addr) != 1)
570 printf("Unable to parse \"%s\"", address);
577 /* Set up a remote IPv4 address */
580 memset(&s_in4, 0, sizeof(s_in4));
581 s_in4.sin_family = AF_INET;
582 s_in4.sin_port = htons(port);
583 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(address);
586 /* SIGALRM handler crashes out */
588 signal(SIGALRM, sigalrm_handler_crash);
590 rc = connect(sock, s_ptr, s_len);
594 /* A failure whose error code is "Interrupted system call" is in fact
595 an externally applied timeout if the signal handler has been run. */
600 printf("failed: %s\n", strerror(save_errno));
604 printf("connected\n");
607 /* --------------- Set up for OpenSSL --------------- */
611 SSL_load_error_strings();
613 ctx = SSL_CTX_new(SSLv23_method());
616 printf ("SSL_CTX_new failed\n");
620 if (certfile != NULL)
622 if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
624 printf("SSL_CTX_use_certificate_file failed\n");
627 printf("Certificate file = %s\n", certfile);
632 if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
634 printf("SSL_CTX_use_PrivateKey_file failed\n");
637 printf("Key file = %s\n", keyfile);
640 SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
641 SSL_CTX_set_timeout(ctx, 200);
642 SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
646 /* --------------- Set up for GnuTLS --------------- */
649 if (certfile != NULL) printf("Certificate file = %s\n", certfile);
650 if (keyfile != NULL) printf("Key file = %s\n", keyfile);
651 tls_init(certfile, keyfile);
652 tls_session = tls_session_init();
653 gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)sock);
655 /* When the server asks for a certificate and the client does not have one,
656 there is a SIGPIPE error in the gnutls_handshake() function for some reason
657 that is not understood. As luck would have it, this has never hit Exim itself
658 because it ignores SIGPIPE errors. Doing the same here allows it all to work as
661 signal(SIGPIPE, SIG_IGN);
664 /* ---------------------------------------------- */
667 /* Start TLS session if configured to do so without STARTTLS */
672 printf("Attempting to start TLS\n");
675 tls_active = tls_start(sock, &ssl, ctx);
679 sigalrm_seen = FALSE;
681 tls_active = gnutls_handshake(tls_session) >= 0;
686 printf("Failed to start TLS\n");
688 printf("Succeeded in starting TLS\n");
692 while (fgets(outbuffer, sizeof(outbuffer), stdin) != NULL)
694 int n = (int)strlen(outbuffer);
695 while (n > 0 && isspace(outbuffer[n-1])) n--;
698 /* Expect incoming */
700 if (strncmp(outbuffer, "??? ", 4) == 0)
702 unsigned char *lineptr;
703 printf("%s\n", outbuffer);
705 if (*inptr == 0) /* Refill input buffer */
710 rc = SSL_read (ssl, inbuffer, sizeof(inbuffer) - 1);
713 rc = gnutls_record_recv(tls_session, CS inbuffer, sizeof(inbuffer) - 1);
719 rc = read(sock, inbuffer, sizeof(inbuffer));
725 printf("Read error %s\n", strerror(errno));
730 printf("Unexpected EOF read\n");
742 while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
746 if (*inptr == '\n') inptr++;
749 printf("<<< %s\n", lineptr);
750 if (strncmp(lineptr, outbuffer + 4, (int)strlen(outbuffer) - 4) != 0)
752 printf("\n******** Input mismatch ********\n");
759 if (lineptr[0] == '2')
761 printf("Attempting to start TLS\n");
765 tls_active = tls_start(sock, &ssl, ctx);
769 sigalrm_seen = FALSE;
771 tls_active = gnutls_handshake(tls_session) >= 0;
777 printf("Failed to start TLS\n");
781 printf("Succeeded in starting TLS\n");
783 else printf("Abandoning TLS start attempt\n");
789 /* Wait for a bit before proceeding */
791 else if (strncmp(outbuffer, "+++ ", 4) == 0)
793 printf("%s\n", outbuffer);
794 sleep(atoi(outbuffer + 4));
797 /* Send outgoing, but barf if unconsumed incoming */
801 unsigned char *escape;
805 printf("Unconsumed input: %s", inptr);
806 printf(" About to send: %s\n", outbuffer);
814 if (strcmp(outbuffer, "stoptls") == 0 ||
815 strcmp(outbuffer, "STOPTLS") == 0)
819 printf("STOPTLS read when TLS not active\n");
822 printf("Shutting down TLS encryption\n");
830 gnutls_bye(tls_session, GNUTLS_SHUT_WR);
831 gnutls_deinit(tls_session);
833 gnutls_global_deinit();
840 /* Remember that we sent STARTTLS */
842 sent_starttls = (strcmp(outbuffer, "starttls") == 0 ||
843 strcmp(outbuffer, "STARTTLS") == 0);
845 /* Fudge: if the command is "starttls_wait", we send the starttls bit,
846 but we haven't set the flag, so that there is no negotiation. This is for
847 testing the server's timeout. */
849 if (strcmp(outbuffer, "starttls_wait") == 0)
856 printf(">>> %s\n", outbuffer);
857 strcpy(outbuffer + n, "\r\n");
859 /* Turn "\n" and "\r" into the relevant characters. This is a hack. */
861 while ((escape = strstr(outbuffer, "\\r")) != NULL)
864 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
868 while ((escape = strstr(outbuffer, "\\n")) != NULL)
871 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
881 rc = SSL_write (ssl, outbuffer, n + 2);
884 rc = gnutls_record_send(tls_session, CS outbuffer, n + 2);
887 printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
894 rc = write(sock, outbuffer, n + 2);
900 printf("Write error: %s\n", strerror(errno));
906 printf("End of script\n");
912 /* End of client.c */