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