Merge branch 'CHUNKING'
[exim.git] / test / src / client.c
CommitLineData
c55a77db
PH
1/* A little hacked up program that makes a TCP/IP call and reads a script to
2drive it, for testing Exim server code running as a daemon. It's got a bit
3messy with the addition of support for either OpenSSL or GnuTLS. The code for
2b4a568d
JH
4those was hacked out of Exim itself, then code for OpenSSL OCSP stapling was
5ripped from the openssl ocsp and s_client utilities. */
c55a77db
PH
6
7/* ANSI C standard includes */
8
9#include <ctype.h>
10#include <signal.h>
11#include <stdarg.h>
12#include <stddef.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <time.h>
17
18/* Unix includes */
19
20#include <errno.h>
21#include <dirent.h>
22#include <sys/types.h>
23
24#include <netinet/in_systm.h>
25#include <netinet/in.h>
26#include <netinet/ip.h>
27
28#include <netdb.h>
29#include <arpa/inet.h>
30#include <sys/time.h>
31#include <sys/resource.h>
32#include <sys/socket.h>
33#include <sys/stat.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <utime.h>
37
38#ifdef AF_INET6
39#define HAVE_IPV6 1
40#endif
41
42#ifndef S_ADDR_TYPE
43#define S_ADDR_TYPE u_long
44#endif
45
46typedef unsigned char uschar;
47
48#define CS (char *)
49#define US (unsigned char *)
50
51#define FALSE 0
52#define TRUE 1
53
54
55
56static int sigalrm_seen = 0;
57
58
59/* TLS support can be optionally included, either for OpenSSL or GnuTLS. The
60latter needs a whole pile of tables. */
c55a77db 61#ifdef HAVE_OPENSSL
348051ad
JH
62# define HAVE_TLS
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>
839a3b0d
JH
69
70# if OPENSSL_VERSION_NUMBER < 0x0090806fL && !defined(DISABLE_OCSP) && !defined(OPENSSL_NO_TLSEXT)
eca4debb
TL
71# warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
72# define DISABLE_OCSP
73# endif
6d68e1c7
TL
74# ifndef DISABLE_OCSP
75# include <openssl/ocsp.h>
76# endif
c55a77db
PH
77#endif
78
79
80#ifdef HAVE_GNUTLS
348051ad
JH
81# define HAVE_TLS
82# include <gnutls/gnutls.h>
83# include <gnutls/x509.h>
84# if GNUTLS_VERSION_NUMBER >= 0x030103
85# define HAVE_OCSP
86# include <gnutls/ocsp.h>
5597be31
JH
87# endif
88# ifndef GNUTLS_NO_EXTENSIONS
89# define GNUTLS_NO_EXTENSIONS 0
348051ad 90# endif
c55a77db 91
348051ad 92# define DH_BITS 768
c55a77db
PH
93
94/* Local static variables for GNUTLS */
95
fc4fcc34 96static gnutls_dh_params_t dh_params = NULL;
c55a77db
PH
97
98static gnutls_certificate_credentials_t x509_cred = NULL;
fc4fcc34 99static gnutls_session_t tls_session = NULL;
c55a77db
PH
100
101static int ssl_session_timeout = 200;
102
103/* Priorities for TLS algorithms to use. */
104
fc4fcc34 105#if GNUTLS_VERSION_NUMBER < 0x030400
c55a77db
PH
106static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };
107
108static const int kx_priority[16] = {
109 GNUTLS_KX_RSA,
110 GNUTLS_KX_DHE_DSS,
111 GNUTLS_KX_DHE_RSA,
c55a77db
PH
112 0 };
113
114static int default_cipher_priority[16] = {
115 GNUTLS_CIPHER_AES_256_CBC,
116 GNUTLS_CIPHER_AES_128_CBC,
117 GNUTLS_CIPHER_3DES_CBC,
118 GNUTLS_CIPHER_ARCFOUR_128,
119 0 };
120
121static const int mac_priority[16] = {
122 GNUTLS_MAC_SHA,
123 GNUTLS_MAC_MD5,
124 0 };
125
126static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
fc4fcc34 127#endif
c55a77db 128
348051ad 129#endif /*HAVE_GNUTLS*/
c55a77db
PH
130
131
132
2b4a568d
JH
133#ifdef HAVE_TLS
134char * ocsp_stapling = NULL;
135#endif
136
c55a77db
PH
137
138/*************************************************
139* SIGALRM handler - crash out *
140*************************************************/
141
142static void
143sigalrm_handler_crash(int sig)
144{
145sig = sig; /* Keep picky compilers happy */
146printf("\nClient timed out\n");
147exit(99);
148}
149
150
151/*************************************************
152* SIGALRM handler - set flag *
153*************************************************/
154
155static void
156sigalrm_handler_flag(int sig)
157{
158sig = sig; /* Keep picky compilers happy */
159sigalrm_seen = 1;
160}
161
162
163
164/****************************************************************************/
165/****************************************************************************/
166
167#ifdef HAVE_OPENSSL
f5d78688
JH
168
169X509_STORE *
170setup_verify(BIO *bp, char *CAfile, char *CApath)
171{
172 X509_STORE *store;
173 X509_LOOKUP *lookup;
174 if(!(store = X509_STORE_new())) goto end;
175 lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
176 if (lookup == NULL) goto end;
177 if (CAfile) {
178 if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) {
179 BIO_printf(bp, "Error loading file %s\n", CAfile);
180 goto end;
181 }
182 } else X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
183
184 lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
185 if (lookup == NULL) goto end;
186 if (CApath) {
187 if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) {
188 BIO_printf(bp, "Error loading directory %s\n", CApath);
189 goto end;
190 }
191 } else X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
192
193 ERR_clear_error();
194 return store;
195 end:
196 X509_STORE_free(store);
197 return NULL;
198}
199
200
6d68e1c7 201#ifndef DISABLE_OCSP
f5d78688
JH
202static int
203tls_client_stapling_cb(SSL *s, void *arg)
204{
205const unsigned char *p;
206int len;
207OCSP_RESPONSE *rsp;
208OCSP_BASICRESP *bs;
209char *CAfile = NULL;
210X509_STORE *store = NULL;
211int ret = 1;
212
213len = SSL_get_tlsext_status_ocsp_resp(s, &p);
214/*BIO_printf(arg, "OCSP response: ");*/
215if (!p)
216 {
217 BIO_printf(arg, "no response received\n");
218 return 1;
219 }
220if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
221 {
222 BIO_printf(arg, "response parse error\n");
223 BIO_dump_indent(arg, (char *)p, len, 4);
224 return 0;
225 }
226if(!(bs = OCSP_response_get1_basic(rsp)))
227 {
228 BIO_printf(arg, "error parsing response\n");
229 return 0;
230 }
231
232CAfile = ocsp_stapling;
233if(!(store = setup_verify(arg, CAfile, NULL)))
234 {
235 BIO_printf(arg, "error in cert setup\n");
236 return 0;
237 }
238
239/* No file of alternate certs, no options */
240if(OCSP_basic_verify(bs, NULL, store, 0) <= 0)
241 {
242 BIO_printf(arg, "Response Verify Failure\n");
243 ERR_print_errors(arg);
244 ret = 0;
245 }
246else
247 BIO_printf(arg, "Response verify OK\n");
248
249X509_STORE_free(store);
250return ret;
251}
6d68e1c7 252#endif
f5d78688
JH
253
254
c55a77db
PH
255/*************************************************
256* Start an OpenSSL TLS session *
257*************************************************/
258
2b4a568d
JH
259int
260tls_start(int sock, SSL **ssl, SSL_CTX *ctx)
c55a77db
PH
261{
262int rc;
57cde6e4 263static const unsigned char *sid_ctx = US"exim";
c55a77db
PH
264
265RAND_load_file("client.c", -1); /* Not *very* random! */
266
267*ssl = SSL_new (ctx);
57cde6e4 268SSL_set_session_id_context(*ssl, sid_ctx, strlen(CS sid_ctx));
c55a77db
PH
269SSL_set_fd (*ssl, sock);
270SSL_set_connect_state(*ssl);
271
6d68e1c7 272#ifndef DISABLE_OCSP
f5d78688
JH
273if (ocsp_stapling)
274 {
275 SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb);
276 SSL_CTX_set_tlsext_status_arg(ctx, BIO_new_fp(stdout, BIO_NOCLOSE));
277 SSL_set_tlsext_status_type(*ssl, TLSEXT_STATUSTYPE_ocsp);
278 }
6d68e1c7 279#endif
f5d78688 280
c55a77db
PH
281signal(SIGALRM, sigalrm_handler_flag);
282sigalrm_seen = 0;
283alarm(5);
284rc = SSL_connect (*ssl);
285alarm(0);
286
287if (sigalrm_seen)
288 {
289 printf("SSL_connect timed out\n");
290 return 0;
291 }
292
293if (rc <= 0)
294 {
295 ERR_print_errors_fp(stdout);
296 return 0;
297 }
298
299printf("SSL connection using %s\n", SSL_get_cipher (*ssl));
300return 1;
301}
302
303
304/*************************************************
305* SSL Information callback *
306*************************************************/
307
308static void
309info_callback(SSL *s, int where, int ret)
310{
311where = where;
312ret = ret;
313printf("SSL info: %s\n", SSL_state_string_long(s));
314}
315#endif
316
317
318/****************************************************************************/
319/****************************************************************************/
320
321
322#ifdef HAVE_GNUTLS
323/*************************************************
324* Handle GnuTLS error *
325*************************************************/
326
327/* Called from lots of places when errors occur before actually starting to do
328the TLS handshake, that is, while the session is still in clear.
329
330Argument:
331 prefix prefix text
332 err a GnuTLS error number, or 0 if local error
333
334Returns: doesn't - it dies
335*/
336
337static void
338gnutls_error(uschar *prefix, int err)
339{
bb727765 340fprintf(stderr, "GnuTLS connection error: %s:", prefix);
c55a77db
PH
341if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err));
342fprintf(stderr, "\n");
9ff403f8 343exit(98);
c55a77db
PH
344}
345
346
347
348/*************************************************
bb727765 349* Setup up DH parameters *
c55a77db
PH
350*************************************************/
351
352/* For the test suite, the parameters should always be available in the spool
353directory. */
354
355static void
bb727765 356init_dh(void)
c55a77db
PH
357{
358int fd;
359int ret;
fc4fcc34 360gnutls_datum_t m;
c55a77db
PH
361uschar filename[200];
362struct stat statbuf;
363
364/* Initialize the data structures for holding the parameters */
365
c55a77db
PH
366ret = gnutls_dh_params_init(&dh_params);
367if (ret < 0) gnutls_error(US"init dh_params", ret);
368
369/* Open the cache file for reading and if successful, read it and set up the
bb727765 370parameters. */
c55a77db
PH
371
372fd = open("aux-fixed/gnutls-params", O_RDONLY, 0);
373if (fd < 0)
374 {
375 fprintf(stderr, "Failed to open spool/gnutls-params: %s\n", strerror(errno));
9ff403f8 376 exit(97);
c55a77db
PH
377 }
378
379if (fstat(fd, &statbuf) < 0)
380 {
381 (void)close(fd);
382 return gnutls_error(US"TLS cache stat failed", 0);
383 }
384
385m.size = statbuf.st_size;
386m.data = malloc(m.size);
387if (m.data == NULL)
388 return gnutls_error(US"memory allocation failed", 0);
389if (read(fd, m.data, m.size) != m.size)
390 return gnutls_error(US"TLS cache read failed", 0);
391(void)close(fd);
392
c55a77db
PH
393ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM);
394if (ret < 0) return gnutls_error(US"DH params import", ret);
395free(m.data);
396}
397
398
399
400
401/*************************************************
402* Initialize for GnuTLS *
403*************************************************/
404
405/*
406Arguments:
407 certificate certificate file
408 privatekey private key file
409*/
410
411static void
412tls_init(uschar *certificate, uschar *privatekey)
413{
414int rc;
415
416rc = gnutls_global_init();
417if (rc < 0) gnutls_error(US"gnutls_global_init", rc);
418
bb727765 419/* Read D-H parameters from the cache file. */
c55a77db 420
bb727765 421init_dh();
c55a77db
PH
422
423/* Create the credentials structure */
424
425rc = gnutls_certificate_allocate_credentials(&x509_cred);
426if (rc < 0) gnutls_error(US"certificate_allocate_credentials", rc);
427
428/* Set the certificate and private keys */
429
430if (certificate != NULL)
431 {
432 rc = gnutls_certificate_set_x509_key_file(x509_cred, CS certificate,
433 CS privatekey, GNUTLS_X509_FMT_PEM);
434 if (rc < 0) gnutls_error("gnutls_certificate", rc);
435 }
436
437/* Associate the parameters with the x509 credentials structure. */
438
439gnutls_certificate_set_dh_params(x509_cred, dh_params);
2b4a568d
JH
440
441/* set the CA info for server-cert verify */
442if (ocsp_stapling)
443 gnutls_certificate_set_x509_trust_file(x509_cred, ocsp_stapling,
444 GNUTLS_X509_FMT_PEM);
c55a77db
PH
445}
446
447
448
449/*************************************************
450* Initialize a single GNUTLS session *
451*************************************************/
452
fc4fcc34 453static gnutls_session_t
c55a77db
PH
454tls_session_init(void)
455{
fc4fcc34 456gnutls_session_t session;
c55a77db 457
0886a95e 458gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_NO_EXTENSIONS);
c55a77db 459
fc4fcc34 460#if GNUTLS_VERSION_NUMBER < 0x030400
c55a77db
PH
461gnutls_cipher_set_priority(session, default_cipher_priority);
462gnutls_compression_set_priority(session, comp_priority);
463gnutls_kx_set_priority(session, kx_priority);
464gnutls_protocol_set_priority(session, protocol_priority);
465gnutls_mac_set_priority(session, mac_priority);
466
467gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
fc4fcc34
JH
468#else
469gnutls_set_default_priority(session);
470gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
471#endif
c55a77db
PH
472
473gnutls_dh_set_prime_bits(session, DH_BITS);
474gnutls_db_set_cache_expiration(session, ssl_session_timeout);
475
476return session;
477}
478#endif
479
480
481/****************************************************************************/
482/****************************************************************************/
483
484
485
486
487/*************************************************
488* Main Program *
489*************************************************/
490
4fe99a6c 491const char * const HELP_MESSAGE = "\n\
972e83f5
HSHR
492Usage: client\n"
493#ifdef HAVE_TLS
494"\
495 [-tls-on-connect]\n\
496 [-ocsp]\n"
497#endif
498"\
499 [-tn] n seconds timeout\n\
4fe99a6c
PP
500 <IP address>\n\
501 <port>\n\
502 [<outgoing interface>]\n\
503 [<cert file>]\n\
504 [<key file>]\n\
505\n";
c55a77db
PH
506
507int main(int argc, char **argv)
508{
509struct sockaddr *s_ptr;
510struct sockaddr_in s_in4;
511char *interface = NULL;
512char *address = NULL;
513char *certfile = NULL;
514char *keyfile = NULL;
1ec3f27d 515char *end = NULL;
c55a77db
PH
516int argi = 1;
517int host_af, port, s_len, rc, sock, save_errno;
d528a389 518int timeout = 5;
c55a77db
PH
519int tls_active = 0;
520int sent_starttls = 0;
521int tls_on_connect = 0;
1ec3f27d 522long tmplong;
c55a77db
PH
523
524#if HAVE_IPV6
525struct sockaddr_in6 s_in6;
526#endif
527
528#ifdef HAVE_OPENSSL
529SSL_CTX* ctx;
530SSL* ssl;
531#endif
532
533unsigned char outbuffer[10240];
534unsigned char inbuffer[10240];
535unsigned char *inptr = inbuffer;
536
537*inptr = 0; /* Buffer empty */
538
539/* Options */
540
541while (argc >= argi + 1 && argv[argi][0] == '-')
542 {
4fe99a6c
PP
543 if (strcmp(argv[argi], "-help") == 0 ||
544 strcmp(argv[argi], "--help") == 0 ||
545 strcmp(argv[argi], "-h") == 0)
546 {
dd4c8678 547 puts(HELP_MESSAGE);
4fe99a6c
PP
548 exit(0);
549 }
c55a77db
PH
550 if (strcmp(argv[argi], "-tls-on-connect") == 0)
551 {
552 tls_on_connect = 1;
553 argi++;
554 }
2b4a568d 555#ifdef HAVE_TLS
f5d78688
JH
556 else if (strcmp(argv[argi], "-ocsp") == 0)
557 {
558 if (argc < ++argi + 1)
559 {
560 fprintf(stderr, "Missing required certificate file for ocsp option\n");
9ff403f8 561 exit(96);
f5d78688
JH
562 }
563 ocsp_stapling = argv[argi++];
564 }
2b4a568d 565
f5d78688 566#endif
c55a77db
PH
567 else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
568 {
1ec3f27d
PP
569 tmplong = strtol(argv[argi]+2, &end, 10);
570 if (end == argv[argi]+2 || *end)
571 {
572 fprintf(stderr, "Failed to parse seconds from option <%s>\n",
573 argv[argi]);
9ff403f8 574 exit(95);
1ec3f27d
PP
575 }
576 if (tmplong > 10000L)
577 {
dd4c8678 578 fprintf(stderr, "Unreasonably long wait of %ld seconds requested\n",
1ec3f27d 579 tmplong);
9ff403f8 580 exit(94);
1ec3f27d
PP
581 }
582 if (tmplong < 0L)
583 {
dd4c8678 584 fprintf(stderr, "Timeout must not be negative (%ld)\n", tmplong);
9ff403f8 585 exit(93);
1ec3f27d
PP
586 }
587 timeout = (int) tmplong;
c55a77db
PH
588 argi++;
589 }
590 else
591 {
1ec3f27d 592 fprintf(stderr, "Unrecognized option %s\n", argv[argi]);
9ff403f8 593 exit(92);
c55a77db
PH
594 }
595 }
596
597/* Mandatory 1st arg is IP address */
598
599if (argc < argi+1)
600 {
1ec3f27d 601 fprintf(stderr, "No IP address given\n");
9ff403f8 602 exit(91);
c55a77db
PH
603 }
604
605address = argv[argi++];
606host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;
607
608/* Mandatory 2nd arg is port */
609
610if (argc < argi+1)
611 {
1ec3f27d 612 fprintf(stderr, "No port number given\n");
9ff403f8 613 exit(90);
c55a77db
PH
614 }
615
616port = atoi(argv[argi++]);
617
618/* Optional next arg is interface */
619
620if (argc > argi &&
621 (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
622 interface = argv[argi++];
623
624/* Any more arguments are the name of a certificate file and key file */
625
626if (argc > argi) certfile = argv[argi++];
627if (argc > argi) keyfile = argv[argi++];
628
629
630#if HAVE_IPV6
631/* For an IPv6 address, use an IPv6 sockaddr structure. */
632
633if (host_af == AF_INET6)
634 {
635 s_ptr = (struct sockaddr *)&s_in6;
636 s_len = sizeof(s_in6);
637 }
638else
639#endif
640
641/* For an IPv4 address, use an IPv4 sockaddr structure,
642even on an IPv6 system. */
643
644 {
645 s_ptr = (struct sockaddr *)&s_in4;
646 s_len = sizeof(s_in4);
647 }
648
649printf("Connecting to %s port %d ... ", address, port);
650
651sock = socket(host_af, SOCK_STREAM, 0);
652if (sock < 0)
653 {
654 printf("socket creation failed: %s\n", strerror(errno));
9ff403f8 655 exit(89);
c55a77db
PH
656 }
657
658/* Bind to a specific interface if requested. On an IPv6 system, this has
659to be of the same family as the address we are calling. On an IPv4 system the
660test is redundant, but it keeps the code tidier. */
661
662if (interface != NULL)
663 {
664 int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;
665
666 if (interface_af == host_af)
667 {
668 #if HAVE_IPV6
669
670 /* Set up for IPv6 binding */
671
672 if (host_af == AF_INET6)
673 {
674 memset(&s_in6, 0, sizeof(s_in6));
675 s_in6.sin6_family = AF_INET6;
676 s_in6.sin6_port = 0;
677 if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
678 {
679 printf("Unable to parse \"%s\"", interface);
9ff403f8 680 exit(88);
c55a77db
PH
681 }
682 }
683 else
684 #endif
685
686 /* Set up for IPv4 binding */
687
688 {
689 memset(&s_in4, 0, sizeof(s_in4));
690 s_in4.sin_family = AF_INET;
691 s_in4.sin_port = 0;
692 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
693 }
694
695 /* Bind */
696
697 if (bind(sock, s_ptr, s_len) < 0)
698 {
699 printf("Unable to bind outgoing SMTP call to %s: %s",
700 interface, strerror(errno));
9ff403f8 701 exit(87);
c55a77db
PH
702 }
703 }
704 }
705
706/* Set up a remote IPv6 address */
707
708#if HAVE_IPV6
709if (host_af == AF_INET6)
710 {
711 memset(&s_in6, 0, sizeof(s_in6));
712 s_in6.sin6_family = AF_INET6;
713 s_in6.sin6_port = htons(port);
714 if (inet_pton(host_af, address, &s_in6.sin6_addr) != 1)
715 {
716 printf("Unable to parse \"%s\"", address);
9ff403f8 717 exit(86);
c55a77db
PH
718 }
719 }
720else
721#endif
722
723/* Set up a remote IPv4 address */
724
725 {
726 memset(&s_in4, 0, sizeof(s_in4));
727 s_in4.sin_family = AF_INET;
728 s_in4.sin_port = htons(port);
729 s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(address);
730 }
731
732/* SIGALRM handler crashes out */
733
734signal(SIGALRM, sigalrm_handler_crash);
735alarm(timeout);
736rc = connect(sock, s_ptr, s_len);
737save_errno = errno;
738alarm(0);
739
740/* A failure whose error code is "Interrupted system call" is in fact
741an externally applied timeout if the signal handler has been run. */
742
743if (rc < 0)
744 {
745 close(sock);
41fdef91 746 printf("connect failed: %s\n", strerror(save_errno));
9ff403f8 747 exit(85);
c55a77db
PH
748 }
749
750printf("connected\n");
751
752
753/* --------------- Set up for OpenSSL --------------- */
754
755#ifdef HAVE_OPENSSL
756SSL_library_init();
757SSL_load_error_strings();
758
759ctx = SSL_CTX_new(SSLv23_method());
760if (ctx == NULL)
761 {
762 printf ("SSL_CTX_new failed\n");
9ff403f8 763 exit(84);
c55a77db
PH
764 }
765
766if (certfile != NULL)
767 {
768 if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
769 {
770 printf("SSL_CTX_use_certificate_file failed\n");
9ff403f8 771 exit(83);
c55a77db
PH
772 }
773 printf("Certificate file = %s\n", certfile);
774 }
775
776if (keyfile != NULL)
777 {
778 if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
779 {
780 printf("SSL_CTX_use_PrivateKey_file failed\n");
9ff403f8 781 exit(82);
c55a77db
PH
782 }
783 printf("Key file = %s\n", keyfile);
784 }
785
786SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
787SSL_CTX_set_timeout(ctx, 200);
788SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
789#endif
790
791
792/* --------------- Set up for GnuTLS --------------- */
793
794#ifdef HAVE_GNUTLS
795if (certfile != NULL) printf("Certificate file = %s\n", certfile);
796if (keyfile != NULL) printf("Key file = %s\n", keyfile);
797tls_init(certfile, keyfile);
798tls_session = tls_session_init();
348051ad 799#ifdef HAVE_OCSP
2b4a568d
JH
800if (ocsp_stapling)
801 gnutls_ocsp_status_request_enable_client(tls_session, NULL, 0, NULL);
348051ad 802#endif
fc4fcc34 803gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr_t)(intptr_t)sock);
c55a77db
PH
804
805/* When the server asks for a certificate and the client does not have one,
806there is a SIGPIPE error in the gnutls_handshake() function for some reason
807that is not understood. As luck would have it, this has never hit Exim itself
808because it ignores SIGPIPE errors. Doing the same here allows it all to work as
809one wants. */
810
811signal(SIGPIPE, SIG_IGN);
812#endif
813
814/* ---------------------------------------------- */
815
816
817/* Start TLS session if configured to do so without STARTTLS */
818
819#ifdef HAVE_TLS
820if (tls_on_connect)
821 {
822 printf("Attempting to start TLS\n");
823
fc4fcc34 824#ifdef HAVE_OPENSSL
c55a77db 825 tls_active = tls_start(sock, &ssl, ctx);
fc4fcc34 826#endif
c55a77db 827
fc4fcc34
JH
828#ifdef HAVE_GNUTLS
829 {
830 int rc;
c55a77db
PH
831 sigalrm_seen = FALSE;
832 alarm(timeout);
fc4fcc34
JH
833 do {
834 rc = gnutls_handshake(tls_session);
835 } while (rc < 0 && gnutls_error_is_fatal(rc) == 0);
836 tls_active = rc >= 0;
c55a77db 837 alarm(0);
fc4fcc34
JH
838
839 if (!tls_active) printf("%s\n", gnutls_strerror(rc));
840 }
841#endif
c55a77db
PH
842
843 if (!tls_active)
844 printf("Failed to start TLS\n");
fc4fcc34 845#if defined(HAVE_GNUTLS) && defined(HAVE_OCSP)
2b4a568d
JH
846 else if ( ocsp_stapling
847 && gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0)
848 printf("Failed to verify certificate status\n");
fc4fcc34 849#endif
c55a77db
PH
850 else
851 printf("Succeeded in starting TLS\n");
852 }
853#endif
854
a719fce4 855while (fgets(CS outbuffer, sizeof(outbuffer), stdin) != NULL)
c55a77db 856 {
a719fce4 857 int n = (int)strlen(CS outbuffer);
8dbb958a
JH
858
859 /* Strip trailing newline */
860 if (outbuffer[n-1] == '\n') outbuffer[--n] = 0;
c55a77db
PH
861
862 /* Expect incoming */
863
a719fce4 864 if (strncmp(CS outbuffer, "??? ", 4) == 0)
c55a77db
PH
865 {
866 unsigned char *lineptr;
867 printf("%s\n", outbuffer);
868
869 if (*inptr == 0) /* Refill input buffer */
870 {
871 if (tls_active)
872 {
873 #ifdef HAVE_OPENSSL
874 rc = SSL_read (ssl, inbuffer, sizeof(inbuffer) - 1);
875 #endif
876 #ifdef HAVE_GNUTLS
877 rc = gnutls_record_recv(tls_session, CS inbuffer, sizeof(inbuffer) - 1);
878 #endif
879 }
880 else
881 {
882 alarm(timeout);
883 rc = read(sock, inbuffer, sizeof(inbuffer));
884 alarm(0);
885 }
886
887 if (rc < 0)
888 {
889 printf("Read error %s\n", strerror(errno));
9ff403f8 890 exit(81) ;
c55a77db
PH
891 }
892 else if (rc == 0)
893 {
894 printf("Unexpected EOF read\n");
895 close(sock);
9ff403f8 896 exit(80);
c55a77db
PH
897 }
898 else
899 {
900 inbuffer[rc] = 0;
901 inptr = inbuffer;
902 }
903 }
904
905 lineptr = inptr;
906 while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
907 if (*inptr != 0)
908 {
909 *inptr++ = 0;
910 if (*inptr == '\n') inptr++;
911 }
912
913 printf("<<< %s\n", lineptr);
a719fce4 914 if (strncmp(CS lineptr, CS outbuffer + 4, (int)strlen(CS outbuffer) - 4) != 0)
c55a77db
PH
915 {
916 printf("\n******** Input mismatch ********\n");
9ff403f8 917 exit(79);
c55a77db
PH
918 }
919
920 #ifdef HAVE_TLS
921 if (sent_starttls)
922 {
923 if (lineptr[0] == '2')
924 {
2b4a568d
JH
925int rc;
926 unsigned int verify;
927
c55a77db
PH
928 printf("Attempting to start TLS\n");
929 fflush(stdout);
930
931 #ifdef HAVE_OPENSSL
932 tls_active = tls_start(sock, &ssl, ctx);
933 #endif
934
935 #ifdef HAVE_GNUTLS
fc4fcc34
JH
936 {
937 int rc;
938 sigalrm_seen = FALSE;
939 alarm(timeout);
940 do {
941 rc = gnutls_handshake(tls_session);
942 } while (rc < 0 && gnutls_error_is_fatal(rc) == 0);
943 tls_active = rc >= 0;
944 alarm(0);
945
946 if (!tls_active) printf("%s\n", gnutls_strerror(rc));
947 }
c55a77db
PH
948 #endif
949
950 if (!tls_active)
951 {
952 printf("Failed to start TLS\n");
953 fflush(stdout);
954 }
2b4a568d
JH
955 #ifdef HAVE_GNUTLS
956 else if (ocsp_stapling)
957 {
958 if ((rc= gnutls_certificate_verify_peers2(tls_session, &verify)) < 0)
959 {
960 printf("Failed to verify certificate: %s\n", gnutls_strerror(rc));
961 fflush(stdout);
962 }
963 else if (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED))
964 {
965 printf("Bad certificate\n");
966 fflush(stdout);
967 }
348051ad 968 #ifdef HAVE_OCSP
2b4a568d
JH
969 else if (gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0)
970 {
971 printf("Failed to verify certificate status\n");
972 {
973 gnutls_datum_t stapling;
974 gnutls_ocsp_resp_t resp;
975 gnutls_datum_t printed;
976 if ( (rc= gnutls_ocsp_status_request_get(tls_session, &stapling)) == 0
977 && (rc= gnutls_ocsp_resp_init(&resp)) == 0
978 && (rc= gnutls_ocsp_resp_import(resp, &stapling)) == 0
979 && (rc= gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &printed)) == 0
980 )
981 {
982 fprintf(stderr, "%.4096s", printed.data);
983 gnutls_free(printed.data);
984 }
985 else
986 (void) fprintf(stderr,"ocsp decode: %s", gnutls_strerror(rc));
987 }
988 fflush(stdout);
989 }
348051ad 990 #endif
2b4a568d
JH
991 }
992 #endif
c55a77db
PH
993 else
994 printf("Succeeded in starting TLS\n");
995 }
996 else printf("Abandoning TLS start attempt\n");
997 }
998 sent_starttls = 0;
999 #endif
1000 }
1001
1002 /* Wait for a bit before proceeding */
1003
a719fce4 1004 else if (strncmp(CS outbuffer, "+++ ", 4) == 0)
c55a77db
PH
1005 {
1006 printf("%s\n", outbuffer);
a719fce4 1007 sleep(atoi(CS outbuffer + 4));
c55a77db
PH
1008 }
1009
1010 /* Send outgoing, but barf if unconsumed incoming */
1011
1012 else
1013 {
1014 unsigned char *escape;
1015
1016 if (*inptr != 0)
1017 {
1018 printf("Unconsumed input: %s", inptr);
1019 printf(" About to send: %s\n", outbuffer);
9ff403f8 1020 exit(78);
c55a77db
PH
1021 }
1022
1023 #ifdef HAVE_TLS
1024
1025 /* Shutdown TLS */
1026
e265af1f
JH
1027 if (strcmp(CS outbuffer, "stoptls") == 0 ||
1028 strcmp(CS outbuffer, "STOPTLS") == 0)
c55a77db
PH
1029 {
1030 if (!tls_active)
1031 {
1032 printf("STOPTLS read when TLS not active\n");
9ff403f8 1033 exit(77);
c55a77db
PH
1034 }
1035 printf("Shutting down TLS encryption\n");
1036
1037 #ifdef HAVE_OPENSSL
1038 SSL_shutdown(ssl);
1039 SSL_free(ssl);
1040 #endif
1041
1042 #ifdef HAVE_GNUTLS
1043 gnutls_bye(tls_session, GNUTLS_SHUT_WR);
1044 gnutls_deinit(tls_session);
1045 tls_session = NULL;
1046 gnutls_global_deinit();
1047 #endif
1048
1049 tls_active = 0;
1050 continue;
1051 }
1052
1053 /* Remember that we sent STARTTLS */
1054
e265af1f
JH
1055 sent_starttls = (strcmp(CS outbuffer, "starttls") == 0 ||
1056 strcmp(CS outbuffer, "STARTTLS") == 0);
c55a77db
PH
1057
1058 /* Fudge: if the command is "starttls_wait", we send the starttls bit,
1059 but we haven't set the flag, so that there is no negotiation. This is for
1060 testing the server's timeout. */
1061
e265af1f 1062 if (strcmp(CS outbuffer, "starttls_wait") == 0)
c55a77db
PH
1063 {
1064 outbuffer[8] = 0;
1065 n = 8;
1066 }
1067 #endif
1068
1069 printf(">>> %s\n", outbuffer);
a719fce4 1070 strcpy(CS outbuffer + n, "\r\n");
c55a77db
PH
1071
1072 /* Turn "\n" and "\r" into the relevant characters. This is a hack. */
1073
a719fce4 1074 while ((escape = US strstr(CS outbuffer, "\\r")) != NULL)
c55a77db
PH
1075 {
1076 *escape = '\r';
1077 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
1078 n--;
1079 }
1080
e265af1f 1081 while ((escape = US strstr(CS outbuffer, "\\n")) != NULL)
c55a77db
PH
1082 {
1083 *escape = '\n';
1084 memmove(escape + 1, escape + 2, (n + 2) - (escape - outbuffer) - 2);
1085 n--;
1086 }
1087
1088 /* OK, do it */
1089
1090 alarm(timeout);
1091 if (tls_active)
1092 {
1093 #ifdef HAVE_OPENSSL
1094 rc = SSL_write (ssl, outbuffer, n + 2);
1095 #endif
1096 #ifdef HAVE_GNUTLS
1097 rc = gnutls_record_send(tls_session, CS outbuffer, n + 2);
1098 if (rc < 0)
1099 {
1100 printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
9ff403f8 1101 exit(76);
c55a77db
PH
1102 }
1103 #endif
1104 }
1105 else
1106 {
1107 rc = write(sock, outbuffer, n + 2);
1108 }
1109 alarm(0);
1110
1111 if (rc < 0)
1112 {
1113 printf("Write error: %s\n", strerror(errno));
9ff403f8 1114 exit(75);
c55a77db
PH
1115 }
1116 }
1117 }
1118
1119printf("End of script\n");
1120close(sock);
1121
1122exit(0);
1123}
1124
1125/* End of client.c */