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