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