X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/c55a77db55ebf46a399f136eeb3a928b1e862772..972af88e604546ee2ab1f817b8a098056065dd78:/test/src/client.c diff --git a/test/src/client.c b/test/src/client.c index 0a2159472..e7210f223 100644 --- a/test/src/client.c +++ b/test/src/client.c @@ -1,9 +1,8 @@ -/* $Cambridge: exim/test/src/client.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */ - /* A little hacked up program that makes a TCP/IP call and reads a script to drive it, for testing Exim server code running as a daemon. It's got a bit messy with the addition of support for either OpenSSL or GnuTLS. The code for -those was hacked out of Exim itself. */ +those was hacked out of Exim itself, then code for OpenSSL OCSP stapling was +ripped from the openssl ocsp and s_client utilities. */ /* ANSI C standard includes */ @@ -61,27 +60,30 @@ static int sigalrm_seen = 0; latter needs a whole pile of tables. */ #ifdef HAVE_OPENSSL -#define HAVE_TLS -#include -#include -#include -#include -#include -#include +# define HAVE_TLS +# include +# include +# include +# include +# include +# include +# include #endif #ifdef HAVE_GNUTLS -#define HAVE_TLS -#include -#include +# define HAVE_TLS +# include +# include +# if GNUTLS_VERSION_NUMBER >= 0x030103 +# define HAVE_OCSP +# include +# endif -#define DH_BITS 768 -#define RSA_BITS 512 +# define DH_BITS 768 /* Local static variables for GNUTLS */ -static gnutls_rsa_params rsa_params = NULL; static gnutls_dh_params dh_params = NULL; static gnutls_certificate_credentials_t x509_cred = NULL; @@ -97,7 +99,6 @@ static const int kx_priority[16] = { GNUTLS_KX_RSA, GNUTLS_KX_DHE_DSS, GNUTLS_KX_DHE_RSA, - GNUTLS_KX_RSA_EXPORT, 0 }; static int default_cipher_priority[16] = { @@ -115,10 +116,14 @@ static const int mac_priority[16] = { static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 }; -#endif +#endif /*HAVE_GNUTLS*/ +#ifdef HAVE_TLS +char * ocsp_stapling = NULL; +#endif + /************************************************* * SIGALRM handler - crash out * @@ -150,11 +155,97 @@ sigalrm_seen = 1; /****************************************************************************/ #ifdef HAVE_OPENSSL + +X509_STORE * +setup_verify(BIO *bp, char *CAfile, char *CApath) +{ + X509_STORE *store; + X509_LOOKUP *lookup; + if(!(store = X509_STORE_new())) goto end; + lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file()); + if (lookup == NULL) goto end; + if (CAfile) { + if(!X509_LOOKUP_load_file(lookup,CAfile,X509_FILETYPE_PEM)) { + BIO_printf(bp, "Error loading file %s\n", CAfile); + goto end; + } + } else X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT); + + lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir()); + if (lookup == NULL) goto end; + if (CApath) { + if(!X509_LOOKUP_add_dir(lookup,CApath,X509_FILETYPE_PEM)) { + BIO_printf(bp, "Error loading directory %s\n", CApath); + goto end; + } + } else X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT); + + ERR_clear_error(); + return store; + end: + X509_STORE_free(store); + return NULL; +} + + +static int +tls_client_stapling_cb(SSL *s, void *arg) +{ +const unsigned char *p; +int len; +OCSP_RESPONSE *rsp; +OCSP_BASICRESP *bs; +char *CAfile = NULL; +X509_STORE *store = NULL; +int ret = 1; + +len = SSL_get_tlsext_status_ocsp_resp(s, &p); +/*BIO_printf(arg, "OCSP response: ");*/ +if (!p) + { + BIO_printf(arg, "no response received\n"); + return 1; + } +if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) + { + BIO_printf(arg, "response parse error\n"); + BIO_dump_indent(arg, (char *)p, len, 4); + return 0; + } +if(!(bs = OCSP_response_get1_basic(rsp))) + { + BIO_printf(arg, "error parsing response\n"); + return 0; + } + +CAfile = ocsp_stapling; +if(!(store = setup_verify(arg, CAfile, NULL))) + { + BIO_printf(arg, "error in cert setup\n"); + return 0; + } + +/* No file of alternate certs, no options */ +if(OCSP_basic_verify(bs, NULL, store, 0) <= 0) + { + BIO_printf(arg, "Response Verify Failure\n"); + ERR_print_errors(arg); + ret = 0; + } +else + BIO_printf(arg, "Response verify OK\n"); + +X509_STORE_free(store); +return ret; +} + + /************************************************* * Start an OpenSSL TLS session * *************************************************/ -int tls_start(int sock, SSL **ssl, SSL_CTX *ctx) +int +tls_start(int sock, SSL **ssl, SSL_CTX *ctx) { int rc; static const char *sid_ctx = "exim"; @@ -166,6 +257,13 @@ SSL_set_session_id_context(*ssl, sid_ctx, strlen(sid_ctx)); SSL_set_fd (*ssl, sock); SSL_set_connect_state(*ssl); +if (ocsp_stapling) + { + SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb); + SSL_CTX_set_tlsext_status_arg(ctx, BIO_new_fp(stdout, BIO_NOCLOSE)); + SSL_set_tlsext_status_type(*ssl, TLSEXT_STATUSTYPE_ocsp); + } + signal(SIGALRM, sigalrm_handler_flag); sigalrm_seen = 0; alarm(5); @@ -225,7 +323,7 @@ Returns: doesn't - it dies static void gnutls_error(uschar *prefix, int err) { -fprintf(stderr, "GnuTLS connection error:%s", prefix); +fprintf(stderr, "GnuTLS connection error: %s:", prefix); if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err)); fprintf(stderr, "\n"); exit(1); @@ -234,14 +332,14 @@ exit(1); /************************************************* -* Setup up RSA and DH parameters * +* Setup up DH parameters * *************************************************/ /* For the test suite, the parameters should always be available in the spool directory. */ static void -init_rsa_dh(void) +init_dh(void) { int fd; int ret; @@ -251,17 +349,11 @@ struct stat statbuf; /* Initialize the data structures for holding the parameters */ -ret = gnutls_rsa_params_init(&rsa_params); -if (ret < 0) gnutls_error(US"init rsa_params", ret); - ret = gnutls_dh_params_init(&dh_params); if (ret < 0) gnutls_error(US"init dh_params", ret); /* Open the cache file for reading and if successful, read it and set up the -parameters. If we can't set up the RSA parameters, assume that we are dealing -with an old-style cache file that is in another format, and fall through to -compute new values. However, if we correctly get RSA parameters, a failure to -set up D-H parameters is treated as an error. */ +parameters. */ fd = open("aux-fixed/gnutls-params", O_RDONLY, 0); if (fd < 0) @@ -284,8 +376,6 @@ if (read(fd, m.data, m.size) != m.size) return gnutls_error(US"TLS cache read failed", 0); (void)close(fd); -ret = gnutls_rsa_params_import_pkcs1(rsa_params, &m, GNUTLS_X509_FMT_PEM); -if (ret < 0) return gnutls_error(US"RSA params import", ret); ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM); if (ret < 0) return gnutls_error(US"DH params import", ret); free(m.data); @@ -312,9 +402,9 @@ int rc; rc = gnutls_global_init(); if (rc < 0) gnutls_error(US"gnutls_global_init", rc); -/* Read RSA and D-H parameters from the cache file. */ +/* Read D-H parameters from the cache file. */ -init_rsa_dh(); +init_dh(); /* Create the credentials structure */ @@ -333,7 +423,11 @@ if (certificate != NULL) /* Associate the parameters with the x509 credentials structure. */ gnutls_certificate_set_dh_params(x509_cred, dh_params); -gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params); + +/* set the CA info for server-cert verify */ +if (ocsp_stapling) + gnutls_certificate_set_x509_trust_file(x509_cred, ocsp_stapling, + GNUTLS_X509_FMT_PEM); } @@ -375,13 +469,14 @@ return session; * Main Program * *************************************************/ -/* Usage: client - - - [] - [] - [] -*/ +const char * const HELP_MESSAGE = "\n\ +Usage: client\n\ + \n\ + \n\ + []\n\ + []\n\ + []\n\ +\n"; int main(int argc, char **argv) { @@ -391,12 +486,14 @@ char *interface = NULL; char *address = NULL; char *certfile = NULL; char *keyfile = NULL; +char *end = NULL; int argi = 1; int host_af, port, s_len, rc, sock, save_errno; int timeout = 1; int tls_active = 0; int sent_starttls = 0; int tls_on_connect = 0; +long tmplong; #if HAVE_IPV6 struct sockaddr_in6 s_in6; @@ -417,19 +514,56 @@ unsigned char *inptr = inbuffer; while (argc >= argi + 1 && argv[argi][0] == '-') { + if (strcmp(argv[argi], "-help") == 0 || + strcmp(argv[argi], "--help") == 0 || + strcmp(argv[argi], "-h") == 0) + { + printf(HELP_MESSAGE); + exit(0); + } if (strcmp(argv[argi], "-tls-on-connect") == 0) { tls_on_connect = 1; argi++; } +#ifdef HAVE_TLS + else if (strcmp(argv[argi], "-ocsp") == 0) + { + if (argc < ++argi + 1) + { + fprintf(stderr, "Missing required certificate file for ocsp option\n"); + exit(1); + } + ocsp_stapling = argv[argi++]; + } + +#endif else if (argv[argi][1] == 't' && isdigit(argv[argi][2])) { - timeout = atoi(argv[argi]+1); + tmplong = strtol(argv[argi]+2, &end, 10); + if (end == argv[argi]+2 || *end) + { + fprintf(stderr, "Failed to parse seconds from option <%s>\n", + argv[argi]); + exit(1); + } + if (tmplong > 10000L) + { + fprintf(stderr, "Unreasonably long wait of %d seconds requested\n", + tmplong); + exit(1); + } + if (tmplong < 0L) + { + fprintf(stderr, "Timeout must not be negative (%d)\n", tmplong); + exit(1); + } + timeout = (int) tmplong; argi++; } else { - printf("Unrecognized option %s\n", argv[argi]); + fprintf(stderr, "Unrecognized option %s\n", argv[argi]); exit(1); } } @@ -438,7 +572,7 @@ while (argc >= argi + 1 && argv[argi][0] == '-') if (argc < argi+1) { - printf("No IP address given\n"); + fprintf(stderr, "No IP address given\n"); exit(1); } @@ -449,7 +583,7 @@ host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET; if (argc < argi+1) { - printf("No port number given\n"); + fprintf(stderr, "No port number given\n"); exit(1); } @@ -636,6 +770,10 @@ if (certfile != NULL) printf("Certificate file = %s\n", certfile); if (keyfile != NULL) printf("Key file = %s\n", keyfile); tls_init(certfile, keyfile); tls_session = tls_session_init(); +#ifdef HAVE_OCSP +if (ocsp_stapling) + gnutls_ocsp_status_request_enable_client(tls_session, NULL, 0, NULL); +#endif gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)sock); /* When the server asks for a certificate and the client does not have one, @@ -670,6 +808,11 @@ if (tls_on_connect) if (!tls_active) printf("Failed to start TLS\n"); + #if defined(HAVE_GNUTLS) && defined(HAVE_OCSP) + else if ( ocsp_stapling + && gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0) + printf("Failed to verify certificate status\n"); + #endif else printf("Succeeded in starting TLS\n"); } @@ -744,6 +887,9 @@ while (fgets(outbuffer, sizeof(outbuffer), stdin) != NULL) { if (lineptr[0] == '2') { +int rc; + unsigned int verify; + printf("Attempting to start TLS\n"); fflush(stdout); @@ -763,6 +909,44 @@ while (fgets(outbuffer, sizeof(outbuffer), stdin) != NULL) printf("Failed to start TLS\n"); fflush(stdout); } + #ifdef HAVE_GNUTLS + else if (ocsp_stapling) + { + if ((rc= gnutls_certificate_verify_peers2(tls_session, &verify)) < 0) + { + printf("Failed to verify certificate: %s\n", gnutls_strerror(rc)); + fflush(stdout); + } + else if (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) + { + printf("Bad certificate\n"); + fflush(stdout); + } + #ifdef HAVE_OCSP + else if (gnutls_ocsp_status_request_is_checked(tls_session, 0) == 0) + { + printf("Failed to verify certificate status\n"); + { + gnutls_datum_t stapling; + gnutls_ocsp_resp_t resp; + gnutls_datum_t printed; + if ( (rc= gnutls_ocsp_status_request_get(tls_session, &stapling)) == 0 + && (rc= gnutls_ocsp_resp_init(&resp)) == 0 + && (rc= gnutls_ocsp_resp_import(resp, &stapling)) == 0 + && (rc= gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &printed)) == 0 + ) + { + fprintf(stderr, "%.4096s", printed.data); + gnutls_free(printed.data); + } + else + (void) fprintf(stderr,"ocsp decode: %s", gnutls_strerror(rc)); + } + fflush(stdout); + } + #endif + } + #endif else printf("Succeeded in starting TLS\n"); }