* Exim - an Internet mail transport agent *
*************************************************/
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
/* Copyright (c) University of Cambridge 1995 - 2019 */
-/* Copyright (c) The Exim Maintainers 2020 - 2021 */
/* See the file NOTICE for conditions of use and distribution. */
/* Portions Copyright (c) The OpenSSL Project 1999 */
# define EXIM_HAVE_OPENSSL_CIPHER_GET_ID
#endif
+#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x030000000L)
+# define EXIM_HAVE_EXPORT_CHNL_BNGNG
+#endif
+
#if !defined(LIBRESSL_VERSION_NUMBER) \
|| LIBRESSL_VERSION_NUMBER >= 0x20010000L
# if !defined(OPENSSL_NO_ECDH)
# define OPENSSL_HAVE_KEYLOG_CB
# define OPENSSL_HAVE_NUM_TICKETS
# define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME
+# define EXIM_HAVE_EXP_CHNL_BNGNG
# else
# define OPENSSL_BAD_SRVR_OURCERT
# endif
#endif
+#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x010002000L)
+# define EXIM_HAVE_EXPORT_CHNL_BNGNG
+#endif
+
#if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP)
# warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
# define DISABLE_OCSP
*/
static void
-info_callback(SSL *s, int where, int ret)
+info_callback(SSL * s, int where, int ret)
{
DEBUG(D_tls)
{
- const uschar * str;
-
- if (where & SSL_ST_CONNECT)
- str = US"SSL_connect";
- else if (where & SSL_ST_ACCEPT)
- str = US"SSL_accept";
- else
- str = US"SSL info (undefined)";
+ gstring * g = NULL;
+
+ if (where & SSL_ST_CONNECT) g = string_append_listele(g, ',', US"SSL_connect");
+ if (where & SSL_ST_ACCEPT) g = string_append_listele(g, ',', US"SSL_accept");
+ if (where & SSL_CB_LOOP) g = string_append_listele(g, ',', US"state_chg");
+ if (where & SSL_CB_EXIT) g = string_append_listele(g, ',', US"hshake_exit");
+ if (where & SSL_CB_READ) g = string_append_listele(g, ',', US"read");
+ if (where & SSL_CB_WRITE) g = string_append_listele(g, ',', US"write");
+ if (where & SSL_CB_ALERT) g = string_append_listele(g, ',', US"alert");
+ if (where & SSL_CB_HANDSHAKE_START) g = string_append_listele(g, ',', US"hshake_start");
+ if (where & SSL_CB_HANDSHAKE_DONE) g = string_append_listele(g, ',', US"hshake_done");
if (where & SSL_CB_LOOP)
- debug_printf("%s: %s\n", str, SSL_state_string_long(s));
+ debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s));
else if (where & SSL_CB_ALERT)
- debug_printf("SSL3 alert %s:%s:%s\n",
- str = where & SSL_CB_READ ? US"read" : US"write",
+ debug_printf("SSL %s %s:%s\n", g->s,
SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
else if (where & SSL_CB_EXIT)
{
- if (ret == 0)
- debug_printf("%s: failed in %s\n", str, SSL_state_string_long(s));
- else if (ret < 0)
- debug_printf("%s: error in %s\n", str, SSL_state_string_long(s));
+ if (ret <= 0)
+ debug_printf("SSL %s: %s in %s\n", g->s,
+ ret == 0 ? "failed" : "error", SSL_state_string_long(s));
}
- else if (where & SSL_CB_HANDSHAKE_START)
- debug_printf("%s: hshake start: %s\n", str, SSL_state_string_long(s));
- else if (where & SSL_CB_HANDSHAKE_DONE)
- debug_printf("%s: hshake done: %s\n", str, SSL_state_string_long(s));
+ else if (where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE))
+ debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s));
}
}
static void
-ocsp_free_response_list(exim_openssl_state_st * cbinfo)
+ocsp_free_response_list(exim_openssl_state_st * state)
{
-for (ocsp_resplist * olist = cbinfo->u_ocsp.server.olist; olist;
+for (ocsp_resplist * olist = state->u_ocsp.server.olist; olist;
olist = olist->next)
OCSP_RESPONSE_free(olist->resp);
-cbinfo->u_ocsp.server.olist = NULL;
+state->u_ocsp.server.olist = NULL;
}
#endif /*!DISABLE_OCSP*/
if (olist && !*olist)
olist = NULL;
+ /* If doing a re-expand after SNI, avoid reloading the OCSP
+ responses when the list of filenames has not changed.
+ The creds-invali on content change wipes file_expanded, so that
+ always reloads here. */
+
if ( state->u_ocsp.server.file_expanded && olist
&& (Ustrcmp(olist, state->u_ocsp.server.file_expanded) == 0))
{
{
SSL_CTX_free(state_server.lib_state.lib_ctx);
state_server.lib_state = null_tls_preload;
+#ifndef DISABLE_OCSP
+state_server.u_ocsp.server.file_expanded = NULL;
+#endif
}
SSL_CTX * ctx = state_server.lib_state.lib_ctx;
SSL_CTX_set_info_callback(server_sni, SSL_CTX_get_info_callback(ctx));
SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(ctx));
+ SSL_CTX_set_min_proto_version(server_sni, SSL3_VERSION);
SSL_CTX_set_options(server_sni, SSL_CTX_get_options(ctx));
+ SSL_CTX_clear_options(server_sni, ~SSL_CTX_get_options(ctx));
SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(ctx));
SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb);
SSL_CTX_set_tlsext_servername_arg(server_sni, state);
}
static int
-tls_client_stapling_cb(SSL *s, void *arg)
+tls_client_stapling_cb(SSL * ssl, void * arg)
{
exim_openssl_state_st * cbinfo = arg;
const unsigned char * p;
int i;
DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
-len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+len = SSL_get_tlsext_status_ocsp_resp(ssl, &p);
if(!p)
- {
- /* Expect this when we requested ocsp but got none */
+ { /* Expect this when we requested ocsp but got none */
+ if (SSL_session_reused(ssl) && tls_out.ocsp == OCSP_VFIED)
+ {
+ DEBUG(D_tls) debug_printf(" null, but resumed; ocsp vfy stored with session is good\n");
+ return 1;
+ }
if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
log_write(0, LOG_MAIN, "Required TLS certificate status not received");
else
if (ERR_peek_error())
{
tls_out.ocsp = OCSP_FAILED;
- if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN,
- "Received TLS cert status response, itself unverifiable: %s",
- ERR_reason_error_string(ERR_peek_error()));
+ if (LOGGING(tls_cipher))
+ {
+ const uschar * errstr = CUS ERR_reason_error_string(ERR_peek_error());
+ static uschar peerdn[256];
+ X509_NAME_oneline(X509_get_subject_name(SSL_get_peer_certificate(ssl)),
+ CS peerdn, sizeof(peerdn));
+ log_write(0, LOG_MAIN,
+ "[%s] %s Received TLS cert (DN: '%.*s') status response, "
+ "itself unverifiable: %s",
+ sender_host_address, sender_host_name,
+ (int)sizeof(peerdn), peerdn,
+ errstr);
+ }
DEBUG(D_tls)
{
BIO_printf(bp, "OCSP response verify failure\n");
}
#endif
- DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options);
- if (!(SSL_CTX_set_options(ctx, init_options)))
- return tls_error(string_sprintf(
+ SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION);
+ DEBUG(D_tls) debug_printf("setting SSL CTX options: %016lx\n", init_options);
+ SSL_CTX_set_options(ctx, init_options);
+ {
+ ulong readback = SSL_CTX_clear_options(ctx, ~init_options);
+ if (readback != init_options)
+ return tls_error(string_sprintf(
"SSL_CTX_set_option(%#lx)", init_options), host, NULL, errstr);
+ }
}
else
DEBUG(D_tls) debug_printf("no SSL CTX options to set\n");
else
{
#ifndef DISABLE_OCSP
- if (!host)
+ if (!host) /* server */
{
state->u_ocsp.server.file = ocsp_file;
state->u_ocsp.server.file_expanded = NULL;
}
+/* Channel-binding info for authenticators
+See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/
+for pre-TLS1.3
+*/
+
+static void
+tls_get_channel_binding(SSL * ssl, tls_support * tlsp, const void * taintval)
+{
+uschar c, * s;
+size_t len;
+
+#ifdef EXIM_HAVE_EXPORT_CHNL_BNGNG
+if (SSL_version(ssl) > TLS1_2_VERSION)
+ {
+ /* It's not documented by OpenSSL how big the output buffer must be.
+ The OpenSSL testcases use 80 bytes but don't say why. The GnuTLS impl only
+ serves out 32B. RFC 9266 says it is 32B.
+ Interop fails unless we use the same each end. */
+ len = 32;
+
+ tlsp->channelbind_exporter = TRUE;
+ taintval = GET_UNTAINTED;
+ if (SSL_export_keying_material(ssl,
+ s = store_get((int)len, taintval), len,
+ "EXPORTER-Channel-Binding", (size_t) 24,
+ NULL, 0, 0) != 1)
+ len = 0;
+ }
+else
+#endif
+ {
+ len = SSL_get_peer_finished(ssl, &c, 0);
+ len = SSL_get_peer_finished(ssl, s = store_get((int)len, taintval), len);
+ }
+
+if (len > 0)
+ {
+ int old_pool = store_pool;
+ store_pool = POOL_PERM;
+ tlsp->channelbinding = b64encode_taint(CUS s, (int)len, taintval);
+ store_pool = old_pool;
+ DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
+ }
+}
+
+
/*************************************************
* Start a TLS session in a server *
*************************************************/
case SSL_ERROR_ZERO_RETURN:
DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
(void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
+#ifndef DISABLE_EVENT
(void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
-
+#endif
if (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN)
SSL_shutdown(ssl);
|| r == SSL_R_UNKNOWN_PROTOCOL || r == SSL_R_UNSUPPORTED_PROTOCOL)
s = string_sprintf("(%s)", SSL_get_version(ssl));
(void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : s, errstr);
+#ifndef DISABLE_EVENT
(void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
return FAIL;
}
if (!errno)
{
*errstr = US"SSL_accept: TCP connection closed by peer";
+#ifndef DISABLE_EVENT
(void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
return FAIL;
}
DEBUG(D_tls) debug_printf(" - syscall %s\n", strerror(errno));
sigalrm_seen ? US"timed out"
: ERR_peek_error() ? NULL : string_sprintf("ret %d", error),
errstr);
+#ifndef DISABLE_EVENT
(void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
return FAIL;
}
}
adjust the input functions to read via TLS, and initialize things. */
#ifdef SSL_get_extms_support
+/*XXX what does this return for tls1.3 ? */
tls_in.ext_master_secret = SSL_get_extms_support(ssl) == 1;
#endif
peer_cert(ssl, &tls_in, peerdn, sizeof(peerdn));
tls_in.ourcert = crt ? X509_dup(crt) : NULL;
}
-/* Channel-binding info for authenticators
-See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */
- {
- uschar c, * s;
- size_t len = SSL_get_peer_finished(ssl, &c, 0);
- int old_pool = store_pool;
-
- SSL_get_peer_finished(ssl, s = store_get((int)len, GET_UNTAINTED), len);
- store_pool = POOL_PERM;
- tls_in.channelbinding = b64encode_taint(CUS s, (int)len, GET_UNTAINTED);
- store_pool = old_pool;
- DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p\n", tls_in.channelbinding);
- }
+tls_get_channel_binding(ssl, &tls_in, GET_UNTAINTED);
/* Only used by the server-side tls (tls_in), including tls_getc.
Client-side (tls_out) reads (seem to?) go via
and apply it to the ssl-connection for attempted resumption. */
static void
-tls_retrieve_session(tls_support * tlsp, SSL * ssl, const uschar * key)
+tls_retrieve_session(tls_support * tlsp, SSL * ssl)
{
-tlsp->resumption |= RESUME_SUPPORTED;
if (tlsp->host_resumable)
{
+ const uschar * key = tlsp->resume_index;
dbdata_tls_session * dt;
int len;
open_db dbblock, * dbm_file;
tlsp->resumption |= RESUME_CLIENT_REQUESTED;
- DEBUG(D_tls) debug_printf("checking for resumable session for %s\n", key);
+ DEBUG(D_tls)
+ debug_printf("checking for resumable session for %s\n", tlsp->resume_index);
if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
{
- /* key for the db is the IP */
- if ((dt = dbfn_read_with_length(dbm_file, key, &len)))
+ if ((dt = dbfn_read_with_length(dbm_file, tlsp->resume_index, &len)))
{
SSL_SESSION * ss = NULL;
const uschar * sess_asn1 = dt->session;
if (lifetime + dt->time_stamp < time(NULL))
{
DEBUG(D_tls) debug_printf("session expired\n");
- dbfn_delete(dbm_file, key);
- }
- else if (!SSL_set_session(ssl, ss))
- {
- DEBUG(D_tls)
- {
- ERR_error_string_n(ERR_get_error(),
- ssl_errstring, sizeof(ssl_errstring));
- debug_printf("applying session to ssl: %s\n", ssl_errstring);
- }
+ dbfn_delete(dbm_file, tlsp->resume_index);
}
- else
+ else if (SSL_set_session(ssl, ss))
{
DEBUG(D_tls) debug_printf("good session\n");
tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
tlsp->verify_override = dt->verify_override;
tlsp->ocsp = dt->ocsp;
}
+ else DEBUG(D_tls)
+ {
+ ERR_error_string_n(ERR_get_error(),
+ ssl_errstring, sizeof(ssl_errstring));
+ debug_printf("applying session to ssl: %s\n", ssl_errstring);
+ }
}
}
else
if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
{
- const uschar * key = cbinfo->host->address;
- dbfn_delete(dbm_file, key);
- dbfn_write(dbm_file, key, dt, dlen);
+ dbfn_write(dbm_file, tlsp->resume_index, dt, dlen);
dbfn_close(dbm_file);
DEBUG(D_tls) debug_printf("wrote session (len %u) to db\n",
(unsigned)dlen);
}
+/* Construct a key for session DB lookup, and setup the SSL_CTX for resumption */
+
static void
tls_client_ctx_resume_prehandshake(
- exim_openssl_client_tls_ctx * exim_client_ctx, tls_support * tlsp,
- smtp_transport_options_block * ob, host_item * host)
+ exim_openssl_client_tls_ctx * exim_client_ctx, smtp_connect_args * conn_args,
+ tls_support * tlsp, smtp_transport_options_block * ob)
{
-/* Should the client request a session resumption ticket? */
-if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK)
- {
- tlsp->host_resumable = TRUE;
+tlsp->host_resumable = TRUE;
+tls_client_resmption_key(tlsp, conn_args, ob);
- SSL_CTX_set_session_cache_mode(exim_client_ctx->ctx,
- SSL_SESS_CACHE_CLIENT
- | SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_NO_AUTO_CLEAR);
- SSL_CTX_sess_set_new_cb(exim_client_ctx->ctx, tls_save_session_cb);
- }
+SSL_CTX_set_session_cache_mode(exim_client_ctx->ctx,
+ SSL_SESS_CACHE_CLIENT
+ | SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_NO_AUTO_CLEAR);
+SSL_CTX_sess_set_new_cb(exim_client_ctx->ctx, tls_save_session_cb);
}
static BOOL
tlsp->resumption = RESUME_SUPPORTED;
/* Pick up a previous session, saved on an old ticket */
-tls_retrieve_session(tlsp, ssl, host->address);
+tls_retrieve_session(tlsp, ssl);
return TRUE;
}
#ifdef EXIM_HAVE_ALPN
/* Expand and convert an Exim list to an ALPN list. False return for fail.
NULL plist return for silent no-ALPN.
+
+Overwite the passed-in list with the expanded version.
*/
static BOOL
-tls_alpn_plist(const uschar * tls_alpn, const uschar ** plist, unsigned * plen,
+tls_alpn_plist(uschar ** tls_alpn, const uschar ** plist, unsigned * plen,
uschar ** errstr)
{
uschar * exp_alpn;
-if (!expand_check(tls_alpn, US"tls_alpn", &exp_alpn, errstr))
+if (!expand_check(*tls_alpn, US"tls_alpn", &exp_alpn, errstr))
return FALSE;
+*tls_alpn = exp_alpn;
if (!exp_alpn)
{
client_static_state, errstr) != OK)
return FALSE;
-#ifndef DISABLE_TLS_RESUME
-tls_client_ctx_resume_prehandshake(exim_client_ctx, tlsp, ob, host);
-#endif
-
-
-if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
- {
- tls_error(US"SSL_new", host, NULL, errstr);
- return FALSE;
- }
-SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx));
-
-SSL_set_fd(exim_client_ctx->ssl, cctx->sock);
-SSL_set_connect_state(exim_client_ctx->ssl);
-
if (ob->tls_sni)
{
if (!expand_check(ob->tls_sni, US"tls_sni", &tlsp->sni, errstr))
return FALSE;
if (!tlsp->sni)
- {
- DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n");
- }
+ { DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n"); }
else if (!Ustrlen(tlsp->sni))
tlsp->sni = NULL;
else
{
-#ifdef EXIM_HAVE_OPENSSL_TLSEXT
- DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tlsp->sni);
- SSL_set_tlsext_host_name(exim_client_ctx->ssl, tlsp->sni);
-#else
+#ifndef EXIM_HAVE_OPENSSL_TLSEXT
log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
tlsp->sni);
+ tlsp->sni = NULL;
#endif
}
}
const uschar * plist;
unsigned plen;
- if (!tls_alpn_plist(ob->tls_alpn, &plist, &plen, errstr))
+ if (!tls_alpn_plist(&ob->tls_alpn, &plist, &plen, errstr))
return FALSE;
if (plist)
- if (SSL_set_alpn_protos(exim_client_ctx->ssl, plist, plen) != 0)
+ if (SSL_CTX_set_alpn_protos(exim_client_ctx->ctx, plist, plen) != 0)
{
tls_error(US"alpn init", host, NULL, errstr);
return FALSE;
ob->tls_alpn);
#endif
+#ifndef DISABLE_TLS_RESUME
+/*XXX have_lbserver: another cmdline arg possibly, for continued-conn, but use
+will be very low. */
+
+if (!conn_args->have_lbserver) /* wanted for tls_client_resmption_key() */
+ { DEBUG(D_tls) debug_printf("resumption not supported on continued-connection\n"); }
+else if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK)
+ tls_client_ctx_resume_prehandshake(exim_client_ctx, conn_args, tlsp, ob);
+#endif
+
+
+if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
+ {
+ tls_error(US"SSL_new", host, NULL, errstr);
+ return FALSE;
+ }
+SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx));
+SSL_set_fd(exim_client_ctx->ssl, cctx->sock);
+SSL_set_connect_state(exim_client_ctx->ssl);
+
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+if (tlsp->sni)
+ {
+ DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tlsp->sni);
+ SSL_set_tlsext_host_name(exim_client_ctx->ssl, tlsp->sni);
+ }
+#endif
+
#ifdef SUPPORT_DANE
if (conn_args->dane)
if (dane_tlsa_load(exim_client_ctx->ssl, host, &conn_args->tlsa_dnsa, errstr) != OK)
}
/*XXX will this work with continued-TLS? */
-/* Channel-binding info for authenticators */
- {
- uschar c, * s;
- size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0);
- int old_pool = store_pool;
-
- SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, GET_TAINTED), len);
- store_pool = POOL_PERM;
- tlsp->channelbinding = b64encode_taint(CUS s, (int)len, GET_TAINTED);
- store_pool = old_pool;
- DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
- }
+tls_get_channel_binding(exim_client_ctx->ssl, tlsp, GET_TAINTED);
tlsp->active.sock = cctx->sock;
tlsp->active.tls_ctx = exim_client_ctx;
if (*fdp < 0) return; /* TLS was not active */
-if (do_shutdown)
+if (do_shutdown > TLS_NO_SHUTDOWN)
{
int rc;
DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
- do_shutdown > 1 ? " (with response-wait)" : "");
+ do_shutdown > TLS_SHUTDOWN_NOWAIT ? " (with response-wait)" : "");
tls_write(ct_ctx, NULL, 0, FALSE); /* flush write buffer */
- if ( (rc = SSL_shutdown(*sslp)) == 0 /* send "close notify" alert */
- && do_shutdown > 1)
+ if ( ( do_shutdown >= TLS_SHUTDOWN_WONLY
+ || (rc = SSL_shutdown(*sslp)) == 0 /* send "close notify" alert */
+ )
+ && do_shutdown > TLS_SHUTDOWN_NOWAIT
+ )
{
#ifdef EXIM_TCP_CORK
(void) setsockopt(*fdp, IPPROTO_TCP, EXIM_TCP_CORK, US &off, sizeof(off));
#endif
ALARM(2);
- rc = SSL_shutdown(*sslp); /* wait for response */
+ rc = SSL_shutdown(*sslp); /* wait for response */
ALARM_CLR(0);
}