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