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