ret_t cherokee_cryptor_libssl_find_vserver (SSL *ssl, cherokee_server_t *srv, cherokee_buffer_t *servername, cherokee_connection_t *conn) { ret_t ret; cherokee_virtual_server_t *vsrv = NULL; SSL_CTX *ctx; /* Try to match the connection to a server */ ret = cherokee_server_get_vserver(srv, servername, conn, &vsrv); if ((ret != ret_ok) || (vsrv == NULL)) { LOG_ERROR (CHEROKEE_ERROR_SSL_SRV_MATCH, servername->buf); return ret_error; } TRACE (ENTRIES, "Setting new TLS context. Virtual host='%s'\n", vsrv->name.buf); /* Check whether the Virtual Server supports TLS */ if ((vsrv->cryptor == NULL) || (CRYPTOR_VSRV_SSL(vsrv->cryptor)->context == NULL)) { TRACE (ENTRIES, "Virtual server '%s' does not support SSL\n", servername->buf); return ret_error; } /* Set the new SSL context */ ctx = SSL_set_SSL_CTX (ssl, CRYPTOR_VSRV_SSL(vsrv->cryptor)->context); if (ctx != CRYPTOR_VSRV_SSL(vsrv->cryptor)->context) { LOG_ERROR (CHEROKEE_ERROR_SSL_CHANGE_CTX, servername->buf); } /* SSL_set_SSL_CTX() only change certificates. We need to * changes more options by hand. */ SSL_set_options(ssl, SSL_CTX_get_options(ssl->ctx)); if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) || (SSL_num_renegotiations(ssl) == 0)) { SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ssl->ctx), SSL_CTX_get_verify_callback(ssl->ctx)); } return ret_ok; }
/* * Write data to a secure connection. */ ssize_t be_tls_write(Port *port, void *ptr, size_t len, int *waitfor) { ssize_t n; int err; /* * If SSL renegotiations are enabled and we're getting close to the * limit, start one now; but avoid it if there's one already in * progress. Request the renegotiation 1kB before the limit has * actually expired. */ if (ssl_renegotiation_limit && !in_ssl_renegotiation && port->count > (ssl_renegotiation_limit - 1) * 1024L) { in_ssl_renegotiation = true; /* * The way we determine that a renegotiation has completed is by * observing OpenSSL's internal renegotiation counter. Make sure * we start out at zero, and assume that the renegotiation is * complete when the counter advances. * * OpenSSL provides SSL_renegotiation_pending(), but this doesn't * seem to work in testing. */ SSL_clear_num_renegotiations(port->ssl); /* without this, renegotiation fails when a client cert is used */ SSL_set_session_id_context(port->ssl, (void *) &SSL_context, sizeof(SSL_context)); if (SSL_renegotiate(port->ssl) <= 0) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL failure during renegotiation start"))); } errno = 0; n = SSL_write(port->ssl, ptr, len); err = SSL_get_error(port->ssl, n); switch (err) { case SSL_ERROR_NONE: port->count += n; break; case SSL_ERROR_WANT_READ: *waitfor = WL_SOCKET_READABLE; errno = EWOULDBLOCK; n = -1; break; case SSL_ERROR_WANT_WRITE: *waitfor = WL_SOCKET_WRITEABLE; errno = EWOULDBLOCK; n = -1; break; case SSL_ERROR_SYSCALL: /* leave it to caller to ereport the value of errno */ if (n != -1) { errno = ECONNRESET; n = -1; } break; case SSL_ERROR_SSL: ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL error: %s", SSLerrmessage()))); /* fall through */ case SSL_ERROR_ZERO_RETURN: errno = ECONNRESET; n = -1; break; default: ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("unrecognized SSL error code: %d", err))); errno = ECONNRESET; n = -1; break; } if (n >= 0) { /* is renegotiation complete? */ if (in_ssl_renegotiation && SSL_num_renegotiations(port->ssl) >= 1) { in_ssl_renegotiation = false; port->count = 0; } /* * if renegotiation is still ongoing, and we've gone beyond the * limit, kill the connection now -- continuing to use it can be * considered a security problem. */ if (in_ssl_renegotiation && port->count > ssl_renegotiation_limit * 1024L) ereport(FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL failed to renegotiate connection before limit expired"))); } return n; }
/* * Write data to a secure connection. */ ssize_t secure_write(Port *port, void *ptr, size_t len) { ssize_t n; #ifdef USE_SSL if (port->ssl) { int err; /* * If SSL renegotiations are enabled and we're getting close to the * limit, start one now; but avoid it if there's one already in * progress. Request the renegotiation 1kB before the limit has * actually expired. */ if (ssl_renegotiation_limit && !in_ssl_renegotiation && port->count > (ssl_renegotiation_limit - 1) * 1024L) { in_ssl_renegotiation = true; /* * The way we determine that a renegotiation has completed is by * observing OpenSSL's internal renegotiation counter. Make sure * we start out at zero, and assume that the renegotiation is * complete when the counter advances. * * OpenSSL provides SSL_renegotiation_pending(), but this doesn't * seem to work in testing. */ SSL_clear_num_renegotiations(port->ssl); SSL_set_session_id_context(port->ssl, (void *) &SSL_context, sizeof(SSL_context)); if (SSL_renegotiate(port->ssl) <= 0) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL failure during renegotiation start"))); else { int retries; /* * A handshake can fail, so be prepared to retry it, but only * a few times. */ for (retries = 0; retries++;) { if (SSL_do_handshake(port->ssl) > 0) break; /* done */ ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL handshake failure on renegotiation, retrying"))); if (retries >= 20) ereport(FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("unable to complete SSL handshake"))); } } } wloop: errno = 0; n = SSL_write(port->ssl, ptr, len); err = SSL_get_error(port->ssl, n); switch (err) { case SSL_ERROR_NONE: port->count += n; break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: #ifdef WIN32 pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl), (err == SSL_ERROR_WANT_READ) ? FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE, INFINITE); #endif goto wloop; case SSL_ERROR_SYSCALL: /* leave it to caller to ereport the value of errno */ if (n != -1) { errno = ECONNRESET; n = -1; } break; case SSL_ERROR_SSL: ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL error: %s", SSLerrmessage()))); /* fall through */ case SSL_ERROR_ZERO_RETURN: errno = ECONNRESET; n = -1; break; default: ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("unrecognized SSL error code: %d", err))); errno = ECONNRESET; n = -1; break; } if (n >= 0) { /* is renegotiation complete? */ if (in_ssl_renegotiation && SSL_num_renegotiations(port->ssl) >= 1) { in_ssl_renegotiation = false; port->count = 0; } /* * if renegotiation is still ongoing, and we've gone beyond the * limit, kill the connection now -- continuing to use it can be * considered a security problem. */ if (in_ssl_renegotiation && port->count > ssl_renegotiation_limit * 1024L) ereport(FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("SSL failed to renegotiate connection before limit expired"))); } } else #endif n = send(port->sock, ptr, len, 0); return n; }