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