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 *************************************************/
367 [<outgoing interface>]
372 int main(int argc, char **argv)
374 struct sockaddr *s_ptr;
375 struct sockaddr_in s_in4;
376 char *interface = NULL;
377 char *address = NULL;
378 char *certfile = NULL;
379 char *keyfile = NULL;
381 int host_af, port, s_len, rc, sock, save_errno;
384 int sent_starttls = 0;
385 int tls_on_connect = 0;
388 struct sockaddr_in6 s_in6;
396 unsigned char outbuffer[10240];
397 unsigned char inbuffer[10240];
398 unsigned char *inptr = inbuffer;
400 *inptr = 0; /* Buffer empty */
404 while (argc >= argi + 1 && argv[argi][0] == '-')
406 if (strcmp(argv[argi], "-tls-on-connect") == 0)
411 else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
413 timeout = atoi(argv[argi]+1);
418 printf("Unrecognized option %s\n", argv[argi]);
423 /* Mandatory 1st arg is IP address */
427 printf("No IP address given\n");
431 address = argv[argi++];
432 host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
434 /* Mandatory 2nd arg is port */
438 printf("No port number given\n");
442 port = atoi(argv[argi++]);
444 /* Optional next arg is interface */
447 (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
448 interface = argv[argi++];
450 /* Any more arguments are the name of a certificate file and key file */
452 if (argc > argi) certfile = argv[argi++];
453 if (argc > argi) keyfile = argv[argi++];
457 /* For an IPv6 address, use an IPv6 sockaddr structure. */
459 if (host_af == AF_INET6)
461 s_ptr = (struct sockaddr *)&s_in6;
462 s_len = sizeof(s_in6);
467 /* For an IPv4 address, use an IPv4 sockaddr structure,
468 even on an IPv6 system. */
471 s_ptr = (struct sockaddr *)&s_in4;
472 s_len = sizeof(s_in4);
475 printf("Connecting to %s port %d ... ", address, port);
477 sock = socket(host_af, SOCK_STREAM, 0);
480 printf("socket creation failed: %s\n", strerror(errno));
484 /* Bind to a specific interface if requested. On an IPv6 system, this has
485 to be of the same family as the address we are calling. On an IPv4 system the
486 test is redundant, but it keeps the code tidier. */
488 if (interface != NULL)
490 int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;
492 if (interface_af == host_af)
496 /* Set up for IPv6 binding */
498 if (host_af == AF_INET6)
500 memset(&s_in6, 0, sizeof(s_in6));
501 s_in6.sin6_family = AF_INET6;
503 if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
505 printf("Unable to parse \"%s\"", interface);
512 /* Set up for IPv4 binding */
515 memset(&s_in4, 0, sizeof(s_in4));
516 s_in4.sin_family = AF_INET;
518 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
523 if (bind(sock, s_ptr, s_len) < 0)
525 printf("Unable to bind outgoing SMTP call to %s: %s",
526 interface, strerror(errno));
532 /* Set up a remote IPv6 address */
535 if (host_af == AF_INET6)
537 memset(&s_in6, 0, sizeof(s_in6));
538 s_in6.sin6_family = AF_INET6;
539 s_in6.sin6_port = htons(port);
540 if (inet_pton(host_af, address, &s_in6.sin6_addr) != 1)
542 printf("Unable to parse \"%s\"", address);
549 /* Set up a remote IPv4 address */
552 memset(&s_in4, 0, sizeof(s_in4));
553 s_in4.sin_family = AF_INET;
554 s_in4.sin_port = htons(port);
555 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(address);
558 /* SIGALRM handler crashes out */
560 signal(SIGALRM, sigalrm_handler_crash);
562 rc = connect(sock, s_ptr, s_len);
566 /* A failure whose error code is "Interrupted system call" is in fact
567 an externally applied timeout if the signal handler has been run. */
572 printf("failed: %s\n", strerror(save_errno));
576 printf("connected\n");
579 /* --------------- Set up for OpenSSL --------------- */
583 SSL_load_error_strings();
585 ctx = SSL_CTX_new(SSLv23_method());
588 printf ("SSL_CTX_new failed\n");
592 if (certfile != NULL)
594 if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
596 printf("SSL_CTX_use_certificate_file failed\n");
599 printf("Certificate file = %s\n", certfile);
604 if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
606 printf("SSL_CTX_use_PrivateKey_file failed\n");
609 printf("Key file = %s\n", keyfile);
612 SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
613 SSL_CTX_set_timeout(ctx, 200);
614 SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
618 /* --------------- Set up for GnuTLS --------------- */
621 if (certfile != NULL) printf("Certificate file = %s\n", certfile);
622 if (keyfile != NULL) printf("Key file = %s\n", keyfile);
623 tls_init(certfile, keyfile);
624 tls_session = tls_session_init();
625 gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)sock);
627 /* When the server asks for a certificate and the client does not have one,
628 there is a SIGPIPE error in the gnutls_handshake() function for some reason
629 that is not understood. As luck would have it, this has never hit Exim itself
630 because it ignores SIGPIPE errors. Doing the same here allows it all to work as
633 signal(SIGPIPE, SIG_IGN);
636 /* ---------------------------------------------- */
639 /* Start TLS session if configured to do so without STARTTLS */
644 printf("Attempting to start TLS\n");
647 tls_active = tls_start(sock, &ssl, ctx);
651 sigalrm_seen = FALSE;
653 tls_active = gnutls_handshake(tls_session) >= 0;
658 printf("Failed to start TLS\n");
660 printf("Succeeded in starting TLS\n");
664 while (fgets(outbuffer, sizeof(outbuffer), stdin) != NULL)
666 int n = (int)strlen(outbuffer);
667 while (n > 0 && isspace(outbuffer[n-1])) n--;
670 /* Expect incoming */
672 if (strncmp(outbuffer, "??? ", 4) == 0)
674 unsigned char *lineptr;
675 printf("%s\n", outbuffer);
677 if (*inptr == 0) /* Refill input buffer */
682 rc = SSL_read (ssl, inbuffer, sizeof(inbuffer) - 1);
685 rc = gnutls_record_recv(tls_session, CS inbuffer, sizeof(inbuffer) - 1);
691 rc = read(sock, inbuffer, sizeof(inbuffer));
697 printf("Read error %s\n", strerror(errno));
702 printf("Unexpected EOF read\n");
714 while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
718 if (*inptr == '\n') inptr++;
721 printf("<<< %s\n", lineptr);
722 if (strncmp(lineptr, outbuffer + 4, (int)strlen(outbuffer) - 4) != 0)
724 printf("\n******** Input mismatch ********\n");
731 if (lineptr[0] == '2')
733 printf("Attempting to start TLS\n");
737 tls_active = tls_start(sock, &ssl, ctx);
741 sigalrm_seen = FALSE;
743 tls_active = gnutls_handshake(tls_session) >= 0;
749 printf("Failed to start TLS\n");
753 printf("Succeeded in starting TLS\n");
755 else printf("Abandoning TLS start attempt\n");
761 /* Wait for a bit before proceeding */
763 else if (strncmp(outbuffer, "+++ ", 4) == 0)
765 printf("%s\n", outbuffer);
766 sleep(atoi(outbuffer + 4));
769 /* Send outgoing, but barf if unconsumed incoming */
773 unsigned char *escape;
777 printf("Unconsumed input: %s", inptr);
778 printf(" About to send: %s\n", outbuffer);
786 if (strcmp(outbuffer, "stoptls") == 0 ||
787 strcmp(outbuffer, "STOPTLS") == 0)
791 printf("STOPTLS read when TLS not active\n");
794 printf("Shutting down TLS encryption\n");
802 gnutls_bye(tls_session, GNUTLS_SHUT_WR);
803 gnutls_deinit(tls_session);
805 gnutls_global_deinit();
812 /* Remember that we sent STARTTLS */
814 sent_starttls = (strcmp(outbuffer, "starttls") == 0 ||
815 strcmp(outbuffer, "STARTTLS") == 0);
817 /* Fudge: if the command is "starttls_wait", we send the starttls bit,
818 but we haven't set the flag, so that there is no negotiation. This is for
819 testing the server's timeout. */
821 if (strcmp(outbuffer, "starttls_wait") == 0)
828 printf(">>> %s\n", outbuffer);
829 strcpy(outbuffer + n, "\r\n");
831 /* Turn "\n" and "\r" into the relevant characters. This is a hack. */
833 while ((escape = strstr(outbuffer, "\\r")) != NULL)
836 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
840 while ((escape = strstr(outbuffer, "\\n")) != NULL)
843 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
853 rc = SSL_write (ssl, outbuffer, n + 2);
856 rc = gnutls_record_send(tls_session, CS outbuffer, n + 2);
859 printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
866 rc = write(sock, outbuffer, n + 2);
872 printf("Write error: %s\n", strerror(errno));
878 printf("End of script\n");
884 /* End of client.c */