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