X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=apps%2Fs_server.c;h=8fd47c4c3f29145bec0e7f97cccde4bac63f3c57;hp=ac43e5aac1de0b3d46cc48cc39fe0f6140fee4b5;hb=c8c6914aacb444caecab300769587c8f295285bc;hpb=5d20c4fb3582a0e6cbf8513c94c60e4cd326716d diff --git a/apps/s_server.c b/apps/s_server.c index ac43e5aac1..8fd47c4c3f 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -179,12 +179,16 @@ typedef unsigned int u_int; #include #include #include +#include #ifndef OPENSSL_NO_DH #include #endif #ifndef OPENSSL_NO_RSA #include #endif +#ifndef OPENSSL_NO_SRP +#include +#endif #include "s_apps.h" #include "timeouts.h" @@ -200,14 +204,17 @@ typedef unsigned int u_int; #ifndef OPENSSL_NO_RSA static RSA MS_CALLBACK *tmp_rsa_cb(SSL *s, int is_export, int keylength); #endif -static int sv_body(char *hostname, int s, unsigned char *context); -static int www_body(char *hostname, int s, unsigned char *context); +static int sv_body(char *hostname, int s, int stype, unsigned char *context); +static int www_body(char *hostname, int s, int stype, unsigned char *context); +static int rev_body(char *hostname, int s, int stype, unsigned char *context); static void close_accept_socket(void ); static void sv_usage(void); static int init_ssl_connection(SSL *s); static void print_stats(BIO *bp,SSL_CTX *ctx); static int generate_session_id(const SSL *ssl, unsigned char *id, unsigned int *id_len); +static void init_session_cache_ctx(SSL_CTX *sctx); +static void free_sessions(void); #ifndef OPENSSL_NO_DH static DH *load_dh_param(const char *dhfile); static DH *get_dh512(void); @@ -258,16 +265,15 @@ static int accept_socket= -1; #undef PROG #define PROG s_server_main -extern int verify_depth, verify_return_error; +extern int verify_depth, verify_return_error, verify_quiet; -static char *cipher=NULL; static int s_server_verify=SSL_VERIFY_NONE; static int s_server_session_id_context = 1; /* anything will do */ -static const char *s_cert_file=TEST_CERT,*s_key_file=NULL; +static const char *s_cert_file=TEST_CERT,*s_key_file=NULL, *s_chain_file=NULL; #ifndef OPENSSL_NO_TLSEXT static const char *s_cert_file2=TEST_CERT2,*s_key_file2=NULL; #endif -static char *s_dcert_file=NULL,*s_dkey_file=NULL; +static char *s_dcert_file=NULL,*s_dkey_file=NULL, *s_dchain_file=NULL; #ifdef FIONBIO static int s_nbio=0; #endif @@ -280,9 +286,21 @@ static SSL_CTX *ctx2=NULL; static int www=0; static BIO *bio_s_out=NULL; +static BIO *bio_s_msg = NULL; static int s_debug=0; +#ifndef OPENSSL_NO_TLSEXT +static int s_tlsextdebug=0; +static int s_tlsextstatus=0; +static int cert_status_cb(SSL *s, void *arg); +#endif +static int no_resume_ephemeral = 0; static int s_msg=0; static int s_quiet=0; +static int s_ign_eof=0; +static int s_brief=0; + +static char *keymatexportlabel=NULL; +static int keymatexportlen=20; static int hack=0; #ifndef OPENSSL_NO_ENGINE @@ -292,11 +310,20 @@ static const char *session_id_prefix=NULL; static int enable_timeouts = 0; static long socket_mtu; +#ifndef OPENSSL_NO_DTLS1 static int cert_chain = 0; +#endif + +#ifndef OPENSSL_NO_TLSEXT +static BIO *authz_in = NULL; +static const char *s_authz_file = NULL; +static BIO *serverinfo_in = NULL; +static const char *s_serverinfo_file = NULL; +#endif #ifndef OPENSSL_NO_PSK static char *psk_identity="Client_identity"; -static char *psk_key=NULL; /* by default PSK is not used */ +char *psk_key=NULL; /* by default PSK is not used */ static unsigned int psk_server_cb(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len) @@ -314,12 +341,14 @@ static unsigned int psk_server_cb(SSL *ssl, const char *identity, } if (s_debug) BIO_printf(bio_s_out,"identity_len=%d identity=%s\n", - identity ? strlen(identity) : 0, identity); + identity ? (int)strlen(identity) : 0, identity); /* here we could lookup the given identity e.g. from a database */ if (strcmp(identity, psk_identity) != 0) { - BIO_printf(bio_s_out, "PSK error: client identity not found\n"); + BIO_printf(bio_s_out, "PSK error: client identity not found" + " (got '%s' expected '%s')\n", identity, + psk_identity); goto out_err; } if (s_debug) @@ -359,16 +388,63 @@ static unsigned int psk_server_cb(SSL *ssl, const char *identity, } #endif +#ifndef OPENSSL_NO_SRP +/* This is a context that we pass to callbacks */ +typedef struct srpsrvparm_st + { + char *login; + SRP_VBASE *vb; + SRP_user_pwd *user; + } srpsrvparm; + +/* This callback pretends to require some asynchronous logic in order to obtain + a verifier. When the callback is called for a new connection we return + with a negative value. This will provoke the accept etc to return with + an LOOKUP_X509. The main logic of the reinvokes the suspended call + (which would normally occur after a worker has finished) and we + set the user parameters. +*/ +static int MS_CALLBACK ssl_srp_server_param_cb(SSL *s, int *ad, void *arg) + { + srpsrvparm *p = (srpsrvparm *)arg; + if (p->login == NULL && p->user == NULL ) + { + p->login = SSL_get_srp_username(s); + BIO_printf(bio_err, "SRP username = \"%s\"\n", p->login); + return (-1) ; + } + + if (p->user == NULL) + { + BIO_printf(bio_err, "User %s doesn't exist\n", p->login); + return SSL3_AL_FATAL; + } + if (SSL_set_srp_server_param(s, p->user->N, p->user->g, p->user->s, p->user->v, + p->user->info) < 0) + { + *ad = SSL_AD_INTERNAL_ERROR; + return SSL3_AL_FATAL; + } + BIO_printf(bio_err, "SRP parameters set: username = \"%s\" info=\"%s\" \n", p->login,p->user->info); + /* need to check whether there are memory leaks */ + p->user = NULL; + p->login = NULL; + return SSL_ERROR_NONE; + } + +#endif + #ifdef MONOLITH static void s_server_init(void) { accept_socket=-1; - cipher=NULL; s_server_verify=SSL_VERIFY_NONE; s_dcert_file=NULL; s_dkey_file=NULL; + s_dchain_file=NULL; s_cert_file=TEST_CERT; s_key_file=NULL; + s_chain_file=NULL; #ifndef OPENSSL_NO_TLSEXT s_cert_file2=TEST_CERT2; s_key_file2=NULL; @@ -385,6 +461,7 @@ static void s_server_init(void) s_debug=0; s_msg=0; s_quiet=0; + s_brief=0; hack=0; #ifndef OPENSSL_NO_ENGINE engine_id=NULL; @@ -397,11 +474,23 @@ static void sv_usage(void) BIO_printf(bio_err,"usage: s_server [args ...]\n"); BIO_printf(bio_err,"\n"); BIO_printf(bio_err," -accept arg - port to accept on (default is %d)\n",PORT); + BIO_printf(bio_err," -checkhost host - check peer certificate matches \"host\"\n"); + BIO_printf(bio_err," -checkemail email - check peer certificate matches \"email\"\n"); + BIO_printf(bio_err," -checkip ipaddr - check peer certificate matches \"ipaddr\"\n"); BIO_printf(bio_err," -context arg - set session ID context\n"); BIO_printf(bio_err," -verify arg - turn on peer certificate verification\n"); BIO_printf(bio_err," -Verify arg - turn on peer certificate verification, must have a cert.\n"); BIO_printf(bio_err," -cert arg - certificate file to use\n"); BIO_printf(bio_err," (default is %s)\n",TEST_CERT); + BIO_printf(bio_err," -authz arg - binary authz file for certificate\n"); +#ifndef OPENSSL_NO_TLSEXT + BIO_printf(bio_err," -serverinfo arg - PEM serverinfo file for certificate\n"); +#endif + BIO_printf(bio_err," -crl_check - check the peer certificate has not been revoked by its CA.\n" \ + " The CRL(s) are appended to the certificate file\n"); + BIO_printf(bio_err," -crl_check_all - check the peer certificate has not been revoked by its CA\n" \ + " or any other CRL in the CA chain. CRL(s) are appened to the\n" \ + " the certificate file.\n"); BIO_printf(bio_err," -certform arg - certificate format (PEM or DER) PEM default\n"); BIO_printf(bio_err," -key arg - Private Key file to use, in cert file if\n"); BIO_printf(bio_err," not specified (default is %s)\n",TEST_CERT); @@ -437,17 +526,29 @@ static void sv_usage(void) #ifndef OPENSSL_NO_PSK BIO_printf(bio_err," -psk_hint arg - PSK identity hint to use\n"); BIO_printf(bio_err," -psk arg - PSK in hex (without 0x)\n"); +# ifndef OPENSSL_NO_JPAKE + BIO_printf(bio_err," -jpake arg - JPAKE secret to use\n"); +# endif +#endif +#ifndef OPENSSL_NO_SRP + BIO_printf(bio_err," -srpvfile file - The verifier file for SRP\n"); + BIO_printf(bio_err," -srpuserseed string - A seed string for a default user salt.\n"); #endif BIO_printf(bio_err," -ssl2 - Just talk SSLv2\n"); BIO_printf(bio_err," -ssl3 - Just talk SSLv3\n"); + BIO_printf(bio_err," -tls1_2 - Just talk TLSv1.2\n"); + BIO_printf(bio_err," -tls1_1 - Just talk TLSv1.1\n"); BIO_printf(bio_err," -tls1 - Just talk TLSv1\n"); BIO_printf(bio_err," -dtls1 - Just talk DTLSv1\n"); + BIO_printf(bio_err," -dtls1_2 - Just talk DTLSv1.2\n"); BIO_printf(bio_err," -timeout - Enable timeouts\n"); - BIO_printf(bio_err," -mtu - Set MTU\n"); + BIO_printf(bio_err," -mtu - Set link layer MTU\n"); BIO_printf(bio_err," -chain - Read a certificate chain\n"); BIO_printf(bio_err," -no_ssl2 - Just disable SSLv2\n"); BIO_printf(bio_err," -no_ssl3 - Just disable SSLv3\n"); BIO_printf(bio_err," -no_tls1 - Just disable TLSv1\n"); + BIO_printf(bio_err," -no_tls1_1 - Just disable TLSv1.1\n"); + BIO_printf(bio_err," -no_tls1_2 - Just disable TLSv1.2\n"); #ifndef OPENSSL_NO_DH BIO_printf(bio_err," -no_dhe - Disable ephemeral DH\n"); #endif @@ -471,7 +572,17 @@ static void sv_usage(void) BIO_printf(bio_err," (default is %s)\n",TEST_CERT2); BIO_printf(bio_err," -key2 arg - Private Key file to use for servername, in cert file if\n"); BIO_printf(bio_err," not specified (default is %s)\n",TEST_CERT2); -#endif + BIO_printf(bio_err," -tlsextdebug - hex dump of all TLS extensions received\n"); + BIO_printf(bio_err," -no_ticket - disable use of RFC4507bis session tickets\n"); + BIO_printf(bio_err," -legacy_renegotiation - enable use of legacy renegotiation (dangerous)\n"); +# ifndef OPENSSL_NO_NEXTPROTONEG + BIO_printf(bio_err," -nextprotoneg arg - set the advertised protocols for the NPN extension (comma-separated list)\n"); +# endif + BIO_printf(bio_err," -use_srtp profiles - Offer SRTP key management with a colon-separated profile list\n"); + BIO_printf(bio_err," -alpn arg - set the advertised protocols for the ALPN extension (comma-separated list)\n"); +#endif + BIO_printf(bio_err," -keymatexport label - Export keying material using label\n"); + BIO_printf(bio_err," -keymatexportlen len - Export len bytes of keying material (default 20)\n"); } static int local_argc=0; @@ -653,63 +764,289 @@ static int MS_CALLBACK ssl_servername_cb(SSL *s, int *ad, void *arg) return p->extension_error; if (ctx2) { - BIO_printf(p->biodebug,"Swiching server context.\n"); + BIO_printf(p->biodebug,"Switching server context.\n"); SSL_set_SSL_CTX(s,ctx2); } } return SSL_TLSEXT_ERR_OK; } -#endif + +/* Structure passed to cert status callback */ + +typedef struct tlsextstatusctx_st { + /* Default responder to use */ + char *host, *path, *port; + int use_ssl; + int timeout; + BIO *err; + int verbose; +} tlsextstatusctx; + +static tlsextstatusctx tlscstatp = {NULL, NULL, NULL, 0, -1, NULL, 0}; + +/* Certificate Status callback. This is called when a client includes a + * certificate status request extension. + * + * This is a simplified version. It examines certificates each time and + * makes one OCSP responder query for each request. + * + * A full version would store details such as the OCSP certificate IDs and + * minimise the number of OCSP responses by caching them until they were + * considered "expired". + */ + +static int cert_status_cb(SSL *s, void *arg) + { + tlsextstatusctx *srctx = arg; + BIO *err = srctx->err; + char *host, *port, *path; + int use_ssl; + unsigned char *rspder = NULL; + int rspderlen; + STACK_OF(OPENSSL_STRING) *aia = NULL; + X509 *x = NULL; + X509_STORE_CTX inctx; + X509_OBJECT obj; + OCSP_REQUEST *req = NULL; + OCSP_RESPONSE *resp = NULL; + OCSP_CERTID *id = NULL; + STACK_OF(X509_EXTENSION) *exts; + int ret = SSL_TLSEXT_ERR_NOACK; + int i; +#if 0 +STACK_OF(OCSP_RESPID) *ids; +SSL_get_tlsext_status_ids(s, &ids); +BIO_printf(err, "cert_status: received %d ids\n", sk_OCSP_RESPID_num(ids)); +#endif + if (srctx->verbose) + BIO_puts(err, "cert_status: callback called\n"); + /* Build up OCSP query from server certificate */ + x = SSL_get_certificate(s); + aia = X509_get1_ocsp(x); + if (aia) + { + if (!OCSP_parse_url(sk_OPENSSL_STRING_value(aia, 0), + &host, &port, &path, &use_ssl)) + { + BIO_puts(err, "cert_status: can't parse AIA URL\n"); + goto err; + } + if (srctx->verbose) + BIO_printf(err, "cert_status: AIA URL: %s\n", + sk_OPENSSL_STRING_value(aia, 0)); + } + else + { + if (!srctx->host) + { + BIO_puts(srctx->err, "cert_status: no AIA and no default responder URL\n"); + goto done; + } + host = srctx->host; + path = srctx->path; + port = srctx->port; + use_ssl = srctx->use_ssl; + } + + if (!X509_STORE_CTX_init(&inctx, + SSL_CTX_get_cert_store(SSL_get_SSL_CTX(s)), + NULL, NULL)) + goto err; + if (X509_STORE_get_by_subject(&inctx,X509_LU_X509, + X509_get_issuer_name(x),&obj) <= 0) + { + BIO_puts(err, "cert_status: Can't retrieve issuer certificate.\n"); + X509_STORE_CTX_cleanup(&inctx); + goto done; + } + req = OCSP_REQUEST_new(); + if (!req) + goto err; + id = OCSP_cert_to_id(NULL, x, obj.data.x509); + X509_free(obj.data.x509); + X509_STORE_CTX_cleanup(&inctx); + if (!id) + goto err; + if (!OCSP_request_add0_id(req, id)) + goto err; + id = NULL; + /* Add any extensions to the request */ + SSL_get_tlsext_status_exts(s, &exts); + for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) + { + X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i); + if (!OCSP_REQUEST_add_ext(req, ext, -1)) + goto err; + } + resp = process_responder(err, req, host, path, port, use_ssl, NULL, + srctx->timeout); + if (!resp) + { + BIO_puts(err, "cert_status: error querying responder\n"); + goto done; + } + rspderlen = i2d_OCSP_RESPONSE(resp, &rspder); + if (rspderlen <= 0) + goto err; + SSL_set_tlsext_status_ocsp_resp(s, rspder, rspderlen); + if (srctx->verbose) + { + BIO_puts(err, "cert_status: ocsp response sent:\n"); + OCSP_RESPONSE_print(err, resp, 2); + } + ret = SSL_TLSEXT_ERR_OK; + done: + if (ret != SSL_TLSEXT_ERR_OK) + ERR_print_errors(err); + if (aia) + { + OPENSSL_free(host); + OPENSSL_free(path); + OPENSSL_free(port); + X509_email_free(aia); + } + if (id) + OCSP_CERTID_free(id); + if (req) + OCSP_REQUEST_free(req); + if (resp) + OCSP_RESPONSE_free(resp); + return ret; + err: + ret = SSL_TLSEXT_ERR_ALERT_FATAL; + goto done; + } + +# ifndef OPENSSL_NO_NEXTPROTONEG +/* This is the context that we pass to next_proto_cb */ +typedef struct tlsextnextprotoctx_st { + unsigned char *data; + unsigned int len; +} tlsextnextprotoctx; + +static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, void *arg) + { + tlsextnextprotoctx *next_proto = arg; + + *data = next_proto->data; + *len = next_proto->len; + + return SSL_TLSEXT_ERR_OK; + } +# endif /* ndef OPENSSL_NO_NEXTPROTONEG */ + +/* This the context that we pass to alpn_cb */ +typedef struct tlsextalpnctx_st { + unsigned char *data; + unsigned short len; +} tlsextalpnctx; + +static int alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) + { + tlsextalpnctx *alpn_ctx = arg; + + if (!s_quiet) + { + /* We can assume that |in| is syntactically valid. */ + unsigned i; + BIO_printf(bio_s_out, "ALPN protocols advertised by the client: "); + for (i = 0; i < inlen; ) + { + if (i) + BIO_write(bio_s_out, ", ", 2); + BIO_write(bio_s_out, &in[i + 1], in[i]); + i += in[i] + 1; + } + BIO_write(bio_s_out, "\n", 1); + } + + if (SSL_select_next_proto((unsigned char**) out, outlen, alpn_ctx->data, alpn_ctx->len, in, inlen) != + OPENSSL_NPN_NEGOTIATED) + { + return SSL_TLSEXT_ERR_NOACK; + } + + if (!s_quiet) + { + BIO_printf(bio_s_out, "ALPN protocols selected: "); + BIO_write(bio_s_out, *out, *outlen); + BIO_write(bio_s_out, "\n", 1); + } + + return SSL_TLSEXT_ERR_OK; + } +#endif /* ndef OPENSSL_NO_TLSEXT */ int MAIN(int, char **); +#ifndef OPENSSL_NO_JPAKE +static char *jpake_secret = NULL; +#define no_jpake !jpake_secret +#else +#define no_jpake 1 +#endif +#ifndef OPENSSL_NO_SRP + static srpsrvparm srp_callback_parm; +#endif +static char *srtp_profiles = NULL; + int MAIN(int argc, char *argv[]) { - X509_STORE *store = NULL; - int vflags = 0; + X509_VERIFY_PARAM *vpm = NULL; + int badarg = 0; short port=PORT; char *CApath=NULL,*CAfile=NULL; + char *chCApath=NULL,*chCAfile=NULL; + char *vfyCApath=NULL,*vfyCAfile=NULL; unsigned char *context = NULL; char *dhfile = NULL; -#ifndef OPENSSL_NO_ECDH - char *named_curve = NULL; -#endif - int badop=0,bugs=0; + int badop=0; int ret=1; - int off=0; + int build_chain = 0; int no_tmp_rsa=0,no_dhe=0,no_ecdhe=0,nocert=0; int state=0; const SSL_METHOD *meth=NULL; int socket_type=SOCK_STREAM; -#ifndef OPENSSL_NO_ENGINE ENGINE *e=NULL; -#endif char *inrand=NULL; int s_cert_format = FORMAT_PEM, s_key_format = FORMAT_PEM; char *passarg = NULL, *pass = NULL; char *dpassarg = NULL, *dpass = NULL; int s_dcert_format = FORMAT_PEM, s_dkey_format = FORMAT_PEM; X509 *s_cert = NULL, *s_dcert = NULL; + STACK_OF(X509) *s_chain = NULL, *s_dchain = NULL; EVP_PKEY *s_key = NULL, *s_dkey = NULL; + int no_cache = 0, ext_cache = 0; + int rev = 0, naccept = -1; #ifndef OPENSSL_NO_TLSEXT EVP_PKEY *s_key2 = NULL; X509 *s_cert2 = NULL; -#endif - -#ifndef OPENSSL_NO_TLSEXT tlsextctx tlsextcbp = {NULL, NULL, SSL_TLSEXT_ERR_ALERT_WARNING}; +# ifndef OPENSSL_NO_NEXTPROTONEG + const char *next_proto_neg_in = NULL; + tlsextnextprotoctx next_proto = { NULL, 0}; + const char *alpn_in = NULL; + tlsextalpnctx alpn_ctx = { NULL, 0}; +# endif #endif #ifndef OPENSSL_NO_PSK /* by default do not send a PSK identity hint */ static char *psk_identity_hint=NULL; #endif -#if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3) - meth=SSLv23_server_method(); -#elif !defined(OPENSSL_NO_SSL3) - meth=SSLv3_server_method(); -#elif !defined(OPENSSL_NO_SSL2) - meth=SSLv2_server_method(); +#ifndef OPENSSL_NO_SRP + char *srpuserseed = NULL; + char *srp_verifier_file = NULL; #endif + SSL_EXCERT *exc = NULL; + SSL_CONF_CTX *cctx = NULL; + STACK_OF(OPENSSL_STRING) *ssl_args = NULL; + + char *crl_file = NULL; + int crl_format = FORMAT_PEM; + int crl_download = 0; + STACK_OF(X509_CRL) *crls = NULL; + + meth=SSLv23_server_method(); local_argc=argc; local_argv=argv; @@ -725,6 +1062,12 @@ int MAIN(int argc, char *argv[]) if (!load_config(bio_err, NULL)) goto end; + cctx = SSL_CONF_CTX_new(); + if (!cctx) + goto end; + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CMDLINE); + verify_depth=0; #ifdef FIONBIO s_nbio=0; @@ -743,12 +1086,24 @@ int MAIN(int argc, char *argv[]) if (!extract_port(*(++argv),&port)) goto bad; } + else if (strcmp(*argv,"-naccept") == 0) + { + if (--argc < 1) goto bad; + naccept = atol(*(++argv)); + if (naccept <= 0) + { + BIO_printf(bio_err, "bad accept value %s\n", + *argv); + goto bad; + } + } else if (strcmp(*argv,"-verify") == 0) { s_server_verify=SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE; if (--argc < 1) goto bad; verify_depth=atoi(*(++argv)); - BIO_printf(bio_err,"verify depth is %d\n",verify_depth); + if (!s_quiet) + BIO_printf(bio_err,"verify depth is %d\n",verify_depth); } else if (strcmp(*argv,"-Verify") == 0) { @@ -756,7 +1111,8 @@ int MAIN(int argc, char *argv[]) SSL_VERIFY_CLIENT_ONCE; if (--argc < 1) goto bad; verify_depth=atoi(*(++argv)); - BIO_printf(bio_err,"verify depth is %d, must return a certificate\n",verify_depth); + if (!s_quiet) + BIO_printf(bio_err,"verify depth is %d, must return a certificate\n",verify_depth); } else if (strcmp(*argv,"-context") == 0) { @@ -768,6 +1124,25 @@ int MAIN(int argc, char *argv[]) if (--argc < 1) goto bad; s_cert_file= *(++argv); } + else if (strcmp(*argv,"-CRL") == 0) + { + if (--argc < 1) goto bad; + crl_file= *(++argv); + } + else if (strcmp(*argv,"-crl_download") == 0) + crl_download = 1; +#ifndef OPENSSL_NO_TLSEXT + else if (strcmp(*argv,"-authz") == 0) + { + if (--argc < 1) goto bad; + s_authz_file = *(++argv); + } + else if (strcmp(*argv,"-serverinfo") == 0) + { + if (--argc < 1) goto bad; + s_serverinfo_file = *(++argv); + } +#endif else if (strcmp(*argv,"-certform") == 0) { if (--argc < 1) goto bad; @@ -788,18 +1163,16 @@ int MAIN(int argc, char *argv[]) if (--argc < 1) goto bad; passarg = *(++argv); } - else if (strcmp(*argv,"-dhparam") == 0) + else if (strcmp(*argv,"-cert_chain") == 0) { if (--argc < 1) goto bad; - dhfile = *(++argv); + s_chain_file= *(++argv); } -#ifndef OPENSSL_NO_ECDH - else if (strcmp(*argv,"-named_curve") == 0) + else if (strcmp(*argv,"-dhparam") == 0) { if (--argc < 1) goto bad; - named_curve = *(++argv); + dhfile = *(++argv); } -#endif else if (strcmp(*argv,"-dcertform") == 0) { if (--argc < 1) goto bad; @@ -825,6 +1198,11 @@ int MAIN(int argc, char *argv[]) if (--argc < 1) goto bad; s_dkey_file= *(++argv); } + else if (strcmp(*argv,"-dcert_chain") == 0) + { + if (--argc < 1) goto bad; + s_dchain_file= *(++argv); + } else if (strcmp(*argv,"-nocert") == 0) { nocert=1; @@ -834,28 +1212,64 @@ int MAIN(int argc, char *argv[]) if (--argc < 1) goto bad; CApath= *(++argv); } - else if (strcmp(*argv,"-crl_check") == 0) + else if (strcmp(*argv,"-chainCApath") == 0) { - vflags |= X509_V_FLAG_CRL_CHECK; + if (--argc < 1) goto bad; + chCApath= *(++argv); } - else if (strcmp(*argv,"-crl_check") == 0) + else if (strcmp(*argv,"-verifyCApath") == 0) { - vflags |= X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL; + if (--argc < 1) goto bad; + vfyCApath= *(++argv); } - else if (strcmp(*argv,"-verify_return_error") == 0) - verify_return_error = 1; - else if (strcmp(*argv,"-serverpref") == 0) - { off|=SSL_OP_CIPHER_SERVER_PREFERENCE; } - else if (strcmp(*argv,"-cipher") == 0) + else if (strcmp(*argv,"-no_cache") == 0) + no_cache = 1; + else if (strcmp(*argv,"-ext_cache") == 0) + ext_cache = 1; + else if (strcmp(*argv,"-CRLform") == 0) { if (--argc < 1) goto bad; - cipher= *(++argv); + crl_format = str2fmt(*(++argv)); + } + else if (args_verify(&argv, &argc, &badarg, bio_err, &vpm)) + { + if (badarg) + goto bad; + continue; + } + else if (args_excert(&argv, &argc, &badarg, bio_err, &exc)) + { + if (badarg) + goto bad; + continue; + } + else if (args_ssl(&argv, &argc, cctx, &badarg, bio_err, &ssl_args)) + { + if (badarg) + goto bad; + continue; } + else if (strcmp(*argv,"-verify_return_error") == 0) + verify_return_error = 1; + else if (strcmp(*argv,"-verify_quiet") == 0) + verify_quiet = 1; + else if (strcmp(*argv,"-build_chain") == 0) + build_chain = 1; else if (strcmp(*argv,"-CAfile") == 0) { if (--argc < 1) goto bad; CAfile= *(++argv); } + else if (strcmp(*argv,"-chainCAfile") == 0) + { + if (--argc < 1) goto bad; + chCAfile= *(++argv); + } + else if (strcmp(*argv,"-verifyCAfile") == 0) + { + if (--argc < 1) goto bad; + vfyCAfile= *(++argv); + } #ifdef FIONBIO else if (strcmp(*argv,"-nbio") == 0) { s_nbio=1; } @@ -867,10 +1281,54 @@ int MAIN(int argc, char *argv[]) #endif s_nbio_test=1; } + else if (strcmp(*argv,"-ign_eof") == 0) + s_ign_eof=1; + else if (strcmp(*argv,"-no_ign_eof") == 0) + s_ign_eof=0; else if (strcmp(*argv,"-debug") == 0) { s_debug=1; } +#ifndef OPENSSL_NO_TLSEXT + else if (strcmp(*argv,"-tlsextdebug") == 0) + s_tlsextdebug=1; + else if (strcmp(*argv,"-status") == 0) + s_tlsextstatus=1; + else if (strcmp(*argv,"-status_verbose") == 0) + { + s_tlsextstatus=1; + tlscstatp.verbose = 1; + } + else if (!strcmp(*argv, "-status_timeout")) + { + s_tlsextstatus=1; + if (--argc < 1) goto bad; + tlscstatp.timeout = atoi(*(++argv)); + } + else if (!strcmp(*argv, "-status_url")) + { + s_tlsextstatus=1; + if (--argc < 1) goto bad; + if (!OCSP_parse_url(*(++argv), + &tlscstatp.host, + &tlscstatp.port, + &tlscstatp.path, + &tlscstatp.use_ssl)) + { + BIO_printf(bio_err, "Error parsing URL\n"); + goto bad; + } + } +#endif else if (strcmp(*argv,"-msg") == 0) { s_msg=1; } + else if (strcmp(*argv,"-msgfile") == 0) + { + if (--argc < 1) goto bad; + bio_s_msg = BIO_new_file(*(++argv), "w"); + } +#ifndef OPENSSL_NO_SSL_TRACE + else if (strcmp(*argv,"-trace") == 0) + { s_msg=2; } +#endif else if (strcmp(*argv,"-hack") == 0) { hack=1; } else if (strcmp(*argv,"-state") == 0) @@ -879,14 +1337,20 @@ int MAIN(int argc, char *argv[]) { s_crlf=1; } else if (strcmp(*argv,"-quiet") == 0) { s_quiet=1; } - else if (strcmp(*argv,"-bugs") == 0) - { bugs=1; } + else if (strcmp(*argv,"-brief") == 0) + { + s_quiet=1; + s_brief=1; + verify_quiet=1; + } else if (strcmp(*argv,"-no_tmp_rsa") == 0) { no_tmp_rsa=1; } else if (strcmp(*argv,"-no_dhe") == 0) { no_dhe=1; } else if (strcmp(*argv,"-no_ecdhe") == 0) { no_ecdhe=1; } + else if (strcmp(*argv,"-no_resume_ephemeral") == 0) + { no_resume_ephemeral = 1; } #ifndef OPENSSL_NO_PSK else if (strcmp(*argv,"-psk_hint") == 0) { @@ -901,27 +1365,35 @@ int MAIN(int argc, char *argv[]) psk_key=*(++argv); for (i=0; ikssl_ctx = kssl_ctx_new()) != NULL) + if ((kctx = kssl_ctx_new()) != NULL) { - kssl_ctx_setstring(con->kssl_ctx, KSSL_SERVICE, - KRB5SVC); - kssl_ctx_setstring(con->kssl_ctx, KSSL_KEYTAB, - KRB5KEYTAB); + SSL_set0_kssl_ctx(con, kctx); + kssl_ctx_setstring(kctx, KSSL_SERVICE, KRB5SVC); + kssl_ctx_setstring(kctx, KSSL_KEYTAB, KRB5KEYTAB); } #endif /* OPENSSL_NO_KRB5 */ if(context) @@ -1554,10 +2218,14 @@ static int sv_body(char *hostname, int s, unsigned char *context) strlen((char *)context)); } SSL_clear(con); +#if 0 +#ifdef TLSEXT_TYPE_opaque_prf_input + SSL_set_tlsext_opaque_prf_input(con, "Test server", 11); +#endif +#endif - if (SSL_version(con) == DTLS1_VERSION) + if (stype == SOCK_DGRAM) { - struct timeval timeout; sbio=BIO_new_dgram(s,BIO_NOCLOSE); @@ -1572,10 +2240,10 @@ static int sv_body(char *hostname, int s, unsigned char *context) BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout); } - if (socket_mtu > 0) + if (socket_mtu > 28) { SSL_set_options(con, SSL_OP_NO_QUERY_MTU); - SSL_set_mtu(con, socket_mtu); + SSL_set_mtu(con, socket_mtu - 28); } else /* want to do MTU discovery */ @@ -1594,21 +2262,38 @@ static int sv_body(char *hostname, int s, unsigned char *context) test=BIO_new(BIO_f_nbio_test()); sbio=BIO_push(test,sbio); } +#ifndef OPENSSL_NO_JPAKE + if(jpake_secret) + jpake_server_auth(bio_s_out, sbio, jpake_secret); +#endif + SSL_set_bio(con,sbio,sbio); SSL_set_accept_state(con); /* SSL_set_fd(con,s); */ if (s_debug) { - con->debug=1; + SSL_set_debug(con, 1); BIO_set_callback(SSL_get_rbio(con),bio_dump_callback); - BIO_set_callback_arg(SSL_get_rbio(con),bio_s_out); + BIO_set_callback_arg(SSL_get_rbio(con),(char *)bio_s_out); } if (s_msg) { - SSL_set_msg_callback(con, msg_cb); - SSL_set_msg_callback_arg(con, bio_s_out); +#ifndef OPENSSL_NO_SSL_TRACE + if (s_msg == 2) + SSL_set_msg_callback(con, SSL_trace); + else +#endif + SSL_set_msg_callback(con, msg_cb); + SSL_set_msg_callback_arg(con, bio_s_msg ? bio_s_msg : bio_s_out); + } +#ifndef OPENSSL_NO_TLSEXT + if (s_tlsextdebug) + { + SSL_set_tlsext_debug_callback(con, tlsext_cb); + SSL_set_tlsext_debug_arg(con, bio_s_out); } +#endif width=s+1; for (;;) @@ -1656,7 +2341,19 @@ static int sv_body(char *hostname, int s, unsigned char *context) read_from_terminal = 1; (void)fcntl(fileno(stdin), F_SETFL, 0); #else - i=select(width,(void *)&readfds,NULL,NULL,NULL); + if ((SSL_version(con) == DTLS1_VERSION) && + DTLSv1_get_timeout(con, &timeout)) + timeoutp = &timeout; + else + timeoutp = NULL; + + i=select(width,(void *)&readfds,NULL,NULL,timeoutp); + + if ((SSL_version(con) == DTLS1_VERSION) && DTLSv1_handle_timeout(con) > 0) + { + BIO_printf(bio_err,"TIMEOUT occured\n"); + } + if (i <= 0) continue; if (FD_ISSET(fileno(stdin),&readfds)) read_from_terminal = 1; @@ -1690,7 +2387,7 @@ static int sv_body(char *hostname, int s, unsigned char *context) } else i=raw_read_stdin(buf,bufsize); - if (!s_quiet) + if (!s_quiet && !s_brief) { if ((i <= 0) || (buf[0] == 'Q')) { @@ -1710,6 +2407,16 @@ static int sv_body(char *hostname, int s, unsigned char *context) goto err; } +#ifndef OPENSSL_NO_HEARTBEATS + if ((buf[0] == 'B') && + ((buf[1] == '\n') || (buf[1] == '\r'))) + { + BIO_printf(bio_err,"HEARTBEATING\n"); + SSL_heartbeat(con); + i=0; + continue; + } +#endif if ((buf[0] == 'r') && ((buf[1] == '\n') || (buf[1] == '\r'))) { @@ -1753,6 +2460,18 @@ static int sv_body(char *hostname, int s, unsigned char *context) { static count=0; if (++count == 100) { count=0; SSL_renegotiate(con); } } #endif k=SSL_write(con,&(buf[l]),(unsigned int)i); +#ifndef OPENSSL_NO_SRP + while (SSL_get_error(con,k) == SSL_ERROR_WANT_X509_LOOKUP) + { + BIO_printf(bio_s_out,"LOOKUP renego during write\n"); + srp_callback_parm.user = SRP_VBASE_get_by_user(srp_callback_parm.vb, srp_callback_parm.login); + if (srp_callback_parm.user) + BIO_printf(bio_s_out,"LOOKUP done %s\n",srp_callback_parm.user->info); + else + BIO_printf(bio_s_out,"LOOKUP not successful\n"); + k=SSL_write(con,&(buf[l]),(unsigned int)i); + } +#endif switch (SSL_get_error(con,k)) { case SSL_ERROR_NONE: @@ -1800,6 +2519,18 @@ static int sv_body(char *hostname, int s, unsigned char *context) { again: i=SSL_read(con,(char *)buf,bufsize); +#ifndef OPENSSL_NO_SRP + while (SSL_get_error(con,i) == SSL_ERROR_WANT_X509_LOOKUP) + { + BIO_printf(bio_s_out,"LOOKUP renego during read\n"); + srp_callback_parm.user = SRP_VBASE_get_by_user(srp_callback_parm.vb, srp_callback_parm.login); + if (srp_callback_parm.user) + BIO_printf(bio_s_out,"LOOKUP done %s\n",srp_callback_parm.user->info); + else + BIO_printf(bio_s_out,"LOOKUP not successful\n"); + i=SSL_read(con,(char *)buf,bufsize); + } +#endif switch (SSL_get_error(con,i)) { case SSL_ERROR_NONE: @@ -1812,7 +2543,6 @@ again: break; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_X509_LOOKUP: BIO_printf(bio_s_out,"Read BLOCK\n"); break; case SSL_ERROR_SYSCALL: @@ -1867,8 +2597,30 @@ static int init_ssl_connection(SSL *con) X509 *peer; long verify_error; MS_STATIC char buf[BUFSIZ]; +#ifndef OPENSSL_NO_KRB5 + char *client_princ; +#endif +#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) + const unsigned char *next_proto_neg; + unsigned next_proto_neg_len; +#endif + unsigned char *exportedkeymat; + - if ((i=SSL_accept(con)) <= 0) + i=SSL_accept(con); +#ifndef OPENSSL_NO_SRP + while (i <= 0 && SSL_get_error(con,i) == SSL_ERROR_WANT_X509_LOOKUP) + { + BIO_printf(bio_s_out,"LOOKUP during accept %s\n",srp_callback_parm.login); + srp_callback_parm.user = SRP_VBASE_get_by_user(srp_callback_parm.vb, srp_callback_parm.login); + if (srp_callback_parm.user) + BIO_printf(bio_s_out,"LOOKUP done %s\n",srp_callback_parm.user->info); + else + BIO_printf(bio_s_out,"LOOKUP not successful\n"); + i=SSL_accept(con); + } +#endif + if (i <= 0) { if (BIO_sock_should_retry(i)) { @@ -1883,11 +2635,14 @@ static int init_ssl_connection(SSL *con) BIO_printf(bio_err,"verify error:%s\n", X509_verify_cert_error_string(verify_error)); } - else - ERR_print_errors(bio_err); + /* Always print any error messages */ + ERR_print_errors(bio_err); return(0); } + if (s_brief) + print_ssl_summary(bio_err, con); + PEM_write_bio_SSL_SESSION(bio_s_out,SSL_get_session(con)); peer=SSL_get_peer_certificate(con); @@ -1905,18 +2660,74 @@ static int init_ssl_connection(SSL *con) if (SSL_get_shared_ciphers(con,buf,sizeof buf) != NULL) BIO_printf(bio_s_out,"Shared ciphers:%s\n",buf); str=SSL_CIPHER_get_name(SSL_get_current_cipher(con)); + ssl_print_sigalgs(bio_s_out, con); +#ifndef OPENSSL_NO_EC + ssl_print_point_formats(bio_s_out, con); + ssl_print_curves(bio_s_out, con, 0); +#endif BIO_printf(bio_s_out,"CIPHER is %s\n",(str != NULL)?str:"(NONE)"); - if (con->hit) BIO_printf(bio_s_out,"Reused session-id\n"); + +#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG) + SSL_get0_next_proto_negotiated(con, &next_proto_neg, &next_proto_neg_len); + if (next_proto_neg) + { + BIO_printf(bio_s_out,"NEXTPROTO is "); + BIO_write(bio_s_out, next_proto_neg, next_proto_neg_len); + BIO_printf(bio_s_out, "\n"); + } +#endif + { + SRTP_PROTECTION_PROFILE *srtp_profile + = SSL_get_selected_srtp_profile(con); + + if(srtp_profile) + BIO_printf(bio_s_out,"SRTP Extension negotiated, profile=%s\n", + srtp_profile->name); + } + if (SSL_cache_hit(con)) BIO_printf(bio_s_out,"Reused session-id\n"); if (SSL_ctrl(con,SSL_CTRL_GET_FLAGS,0,NULL) & TLS1_FLAGS_TLS_PADDING_BUG) - BIO_printf(bio_s_out,"Peer has incorrect TLSv1 block padding\n"); + BIO_printf(bio_s_out, + "Peer has incorrect TLSv1 block padding\n"); #ifndef OPENSSL_NO_KRB5 - if (con->kssl_ctx->client_princ != NULL) + client_princ = kssl_ctx_get0_client_princ(SSL_get0_kssl_ctx(con)); + if (client_princ != NULL) { BIO_printf(bio_s_out,"Kerberos peer principal is %s\n", - con->kssl_ctx->client_princ); + client_princ); } #endif /* OPENSSL_NO_KRB5 */ + BIO_printf(bio_s_out, "Secure Renegotiation IS%s supported\n", + SSL_get_secure_renegotiation_support(con) ? "" : " NOT"); + if (keymatexportlabel != NULL) + { + BIO_printf(bio_s_out, "Keying material exporter:\n"); + BIO_printf(bio_s_out, " Label: '%s'\n", keymatexportlabel); + BIO_printf(bio_s_out, " Length: %i bytes\n", + keymatexportlen); + exportedkeymat = OPENSSL_malloc(keymatexportlen); + if (exportedkeymat != NULL) + { + if (!SSL_export_keying_material(con, exportedkeymat, + keymatexportlen, + keymatexportlabel, + strlen(keymatexportlabel), + NULL, 0, 0)) + { + BIO_printf(bio_s_out, " Error\n"); + } + else + { + BIO_printf(bio_s_out, " Keying material: "); + for (i=0; ikssl_ctx = kssl_ctx_new()) != NULL) + if ((kctx = kssl_ctx_new()) != NULL) { - kssl_ctx_setstring(con->kssl_ctx, KSSL_SERVICE, KRB5SVC); - kssl_ctx_setstring(con->kssl_ctx, KSSL_KEYTAB, KRB5KEYTAB); + kssl_ctx_setstring(kctx, KSSL_SERVICE, KRB5SVC); + kssl_ctx_setstring(kctx, KSSL_KEYTAB, KRB5KEYTAB); } #endif /* OPENSSL_NO_KRB5 */ if(context) SSL_set_session_id_context(con, context, @@ -2018,23 +2841,38 @@ static int www_body(char *hostname, int s, unsigned char *context) if (s_debug) { - con->debug=1; + SSL_set_debug(con, 1); BIO_set_callback(SSL_get_rbio(con),bio_dump_callback); - BIO_set_callback_arg(SSL_get_rbio(con),bio_s_out); + BIO_set_callback_arg(SSL_get_rbio(con),(char *)bio_s_out); } if (s_msg) { - SSL_set_msg_callback(con, msg_cb); - SSL_set_msg_callback_arg(con, bio_s_out); +#ifndef OPENSSL_NO_SSL_TRACE + if (s_msg == 2) + SSL_set_msg_callback(con, SSL_trace); + else +#endif + SSL_set_msg_callback(con, msg_cb); + SSL_set_msg_callback_arg(con, bio_s_msg ? bio_s_msg : bio_s_out); } - blank=0; for (;;) { if (hack) { i=SSL_accept(con); - +#ifndef OPENSSL_NO_SRP + while (i <= 0 && SSL_get_error(con,i) == SSL_ERROR_WANT_X509_LOOKUP) + { + BIO_printf(bio_s_out,"LOOKUP during accept %s\n",srp_callback_parm.login); + srp_callback_parm.user = SRP_VBASE_get_by_user(srp_callback_parm.vb, srp_callback_parm.login); + if (srp_callback_parm.user) + BIO_printf(bio_s_out,"LOOKUP done %s\n",srp_callback_parm.user->info); + else + BIO_printf(bio_s_out,"LOOKUP not successful\n"); + i=SSL_accept(con); + } +#endif switch (SSL_get_error(con,i)) { case SSL_ERROR_NONE: @@ -2102,6 +2940,11 @@ static int www_body(char *hostname, int s, unsigned char *context) } BIO_puts(io,"\n"); + BIO_printf(io, + "Secure Renegotiation IS%s supported\n", + SSL_get_secure_renegotiation_support(con) ? + "" : " NOT"); + /* The following is evil and should not really * be done */ BIO_printf(io,"Ciphers supported in s_server binary\n"); @@ -2140,7 +2983,11 @@ static int www_body(char *hostname, int s, unsigned char *context) } BIO_puts(io,"\n"); } - BIO_printf(io,((con->hit) + ssl_print_sigalgs(io, con); +#ifndef OPENSSL_NO_EC + ssl_print_curves(io, con, 0); +#endif + BIO_printf(io,(SSL_cache_hit(con) ?"---\nReused, " :"---\nNew, ")); c=SSL_get_current_cipher(con); @@ -2258,7 +3105,6 @@ static int www_body(char *hostname, int s, unsigned char *context) BIO_puts(io,"HTTP/1.0 200 ok\r\nContent-type: text/plain\r\n\r\n"); } /* send the file */ - total_bytes=0; for (;;) { i=BIO_read(file,buf,bufsize); @@ -2334,6 +3180,152 @@ err: return(ret); } +static int rev_body(char *hostname, int s, int stype, unsigned char *context) + { + char *buf=NULL; + int i; + int ret=1; + SSL *con; + BIO *io,*ssl_bio,*sbio; +#ifndef OPENSSL_NO_KRB5 + KSSL_CTX *kctx; +#endif + + buf=OPENSSL_malloc(bufsize); + if (buf == NULL) return(0); + io=BIO_new(BIO_f_buffer()); + ssl_bio=BIO_new(BIO_f_ssl()); + if ((io == NULL) || (ssl_bio == NULL)) goto err; + + /* lets make the output buffer a reasonable size */ + if (!BIO_set_write_buffer_size(io,bufsize)) goto err; + + if ((con=SSL_new(ctx)) == NULL) goto err; +#ifndef OPENSSL_NO_TLSEXT + if (s_tlsextdebug) + { + SSL_set_tlsext_debug_callback(con, tlsext_cb); + SSL_set_tlsext_debug_arg(con, bio_s_out); + } +#endif +#ifndef OPENSSL_NO_KRB5 + if ((kctx = kssl_ctx_new()) != NULL) + { + kssl_ctx_setstring(kctx, KSSL_SERVICE, KRB5SVC); + kssl_ctx_setstring(kctx, KSSL_KEYTAB, KRB5KEYTAB); + } +#endif /* OPENSSL_NO_KRB5 */ + if(context) SSL_set_session_id_context(con, context, + strlen((char *)context)); + + sbio=BIO_new_socket(s,BIO_NOCLOSE); + SSL_set_bio(con,sbio,sbio); + SSL_set_accept_state(con); + + BIO_set_ssl(ssl_bio,con,BIO_CLOSE); + BIO_push(io,ssl_bio); +#ifdef CHARSET_EBCDIC + io = BIO_push(BIO_new(BIO_f_ebcdic_filter()),io); +#endif + + if (s_debug) + { + SSL_set_debug(con, 1); + BIO_set_callback(SSL_get_rbio(con),bio_dump_callback); + BIO_set_callback_arg(SSL_get_rbio(con),(char *)bio_s_out); + } + if (s_msg) + { +#ifndef OPENSSL_NO_SSL_TRACE + if (s_msg == 2) + SSL_set_msg_callback(con, SSL_trace); + else +#endif + SSL_set_msg_callback(con, msg_cb); + SSL_set_msg_callback_arg(con, bio_s_msg ? bio_s_msg : bio_s_out); + } + + for (;;) + { + i = BIO_do_handshake(io); + if (i > 0) + break; + if (!BIO_should_retry(io)) + { + BIO_puts(bio_err, "CONNECTION FAILURE\n"); + ERR_print_errors(bio_err); + goto end; + } + } + BIO_printf(bio_err, "CONNECTION ESTABLISHED\n"); + print_ssl_summary(bio_err, con); + + for (;;) + { + i=BIO_gets(io,buf,bufsize-1); + if (i < 0) /* error */ + { + if (!BIO_should_retry(io)) + { + if (!s_quiet) + ERR_print_errors(bio_err); + goto err; + } + else + { + BIO_printf(bio_s_out,"read R BLOCK\n"); +#if defined(OPENSSL_SYS_NETWARE) + delay(1000); +#elif !defined(OPENSSL_SYS_MSDOS) && !defined(__DJGPP__) + sleep(1); +#endif + continue; + } + } + else if (i == 0) /* end of input */ + { + ret=1; + BIO_printf(bio_err, "CONNECTION CLOSED\n"); + goto end; + } + else + { + char *p = buf + i - 1; + while(i && (*p == '\n' || *p == '\r')) + { + p--; + i--; + } + if (!s_ign_eof && i == 5 && !strncmp(buf, "CLOSE", 5)) + { + ret = 1; + BIO_printf(bio_err, "CONNECTION CLOSED\n"); + goto end; + } + BUF_reverse((unsigned char *)buf, NULL, i); + buf[i] = '\n'; + BIO_write(io, buf, i + 1); + for (;;) + { + i = BIO_flush(io); + if (i > 0) + break; + if (!BIO_should_retry(io)) + goto end; + } + } + } +end: + /* make sure we re-use sessions */ + SSL_set_shutdown(con,SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); + +err: + + if (buf != NULL) OPENSSL_free(buf); + if (io != NULL) BIO_free_all(io); + return(ret); + } + #ifndef OPENSSL_NO_RSA static RSA MS_CALLBACK *tmp_rsa_cb(SSL *s, int is_export, int keylength) { @@ -2388,3 +3380,106 @@ static int generate_session_id(const SSL *ssl, unsigned char *id, return 0; return 1; } + +/* By default s_server uses an in-memory cache which caches SSL_SESSION + * structures without any serialisation. This hides some bugs which only + * become apparent in deployed servers. By implementing a basic external + * session cache some issues can be debugged using s_server. + */ + +typedef struct simple_ssl_session_st + { + unsigned char *id; + unsigned int idlen; + unsigned char *der; + int derlen; + struct simple_ssl_session_st *next; + } simple_ssl_session; + +static simple_ssl_session *first = NULL; + +static int add_session(SSL *ssl, SSL_SESSION *session) + { + simple_ssl_session *sess; + unsigned char *p; + + sess = OPENSSL_malloc(sizeof(simple_ssl_session)); + + SSL_SESSION_get_id(session, &sess->idlen); + sess->derlen = i2d_SSL_SESSION(session, NULL); + + sess->id = BUF_memdup(SSL_SESSION_get_id(session, NULL), sess->idlen); + + sess->der = OPENSSL_malloc(sess->derlen); + p = sess->der; + i2d_SSL_SESSION(session, &p); + + sess->next = first; + first = sess; + BIO_printf(bio_err, "New session added to external cache\n"); + return 0; + } + +static SSL_SESSION *get_session(SSL *ssl, unsigned char *id, int idlen, + int *do_copy) + { + simple_ssl_session *sess; + *do_copy = 0; + for (sess = first; sess; sess = sess->next) + { + if (idlen == (int)sess->idlen && !memcmp(sess->id, id, idlen)) + { + const unsigned char *p = sess->der; + BIO_printf(bio_err, "Lookup session: cache hit\n"); + return d2i_SSL_SESSION(NULL, &p, sess->derlen); + } + } + BIO_printf(bio_err, "Lookup session: cache miss\n"); + return NULL; + } + +static void del_session(SSL_CTX *sctx, SSL_SESSION *session) + { + simple_ssl_session *sess, *prev = NULL; + const unsigned char *id; + unsigned int idlen; + id = SSL_SESSION_get_id(session, &idlen); + for (sess = first; sess; sess = sess->next) + { + if (idlen == sess->idlen && !memcmp(sess->id, id, idlen)) + { + if(prev) + prev->next = sess->next; + else + first = sess->next; + OPENSSL_free(sess->id); + OPENSSL_free(sess->der); + OPENSSL_free(sess); + return; + } + prev = sess; + } + } + +static void init_session_cache_ctx(SSL_CTX *sctx) + { + SSL_CTX_set_session_cache_mode(sctx, + SSL_SESS_CACHE_NO_INTERNAL|SSL_SESS_CACHE_SERVER); + SSL_CTX_sess_set_new_cb(sctx, add_session); + SSL_CTX_sess_set_get_cb(sctx, get_session); + SSL_CTX_sess_set_remove_cb(sctx, del_session); + } + +static void free_sessions(void) + { + simple_ssl_session *sess, *tsess; + for (sess = first; sess;) + { + OPENSSL_free(sess->id); + OPENSSL_free(sess->der); + tsess = sess; + sess = sess->next; + OPENSSL_free(tsess); + } + first = NULL; + }