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