| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** SSL network wrapper |
| */ |
| |
| #include "tcn.h" |
| #include "apr_thread_mutex.h" |
| #include "apr_poll.h" |
| |
| |
| #ifdef HAVE_OPENSSL |
| #include "ssl_private.h" |
| |
| #ifdef TCN_DO_STATISTICS |
| #include "apr_atomic.h" |
| |
| static volatile apr_uint32_t ssl_created = 0; |
| static volatile apr_uint32_t ssl_closed = 0; |
| static volatile apr_uint32_t ssl_cleared = 0; |
| static volatile apr_uint32_t ssl_accepted = 0; |
| |
| void ssl_network_dump_statistics() |
| { |
| fprintf(stderr, "SSL Network Statistics ..\n"); |
| fprintf(stderr, "Sockets created : %d\n", ssl_created); |
| fprintf(stderr, "Sockets accepted : %d\n", ssl_accepted); |
| fprintf(stderr, "Sockets closed : %d\n", ssl_closed); |
| fprintf(stderr, "Sockets cleared : %d\n", ssl_cleared); |
| } |
| |
| #endif |
| |
| static int ssl_smart_shutdown(SSL *ssl, int shutdown_type) |
| { |
| int i; |
| int rc = 0; |
| |
| switch (shutdown_type) { |
| case SSL_SHUTDOWN_TYPE_UNCLEAN: |
| /* perform no close notify handshake at all |
| * (violates the SSL/TLS standard!) |
| */ |
| shutdown_type = SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN; |
| break; |
| case SSL_SHUTDOWN_TYPE_ACCURATE: |
| /* send close notify and wait for clients close notify |
| * (standard compliant, but usually causes connection hangs) |
| */ |
| shutdown_type = 0; |
| break; |
| default: |
| /* |
| * case SSL_SHUTDOWN_TYPE_UNSET: |
| * case SSL_SHUTDOWN_TYPE_STANDARD: |
| * send close notify, but don't wait for clients close notify |
| * (standard compliant and safe, so it's the DEFAULT!) |
| */ |
| shutdown_type = SSL_RECEIVED_SHUTDOWN; |
| break; |
| } |
| |
| SSL_set_shutdown(ssl, shutdown_type); |
| /* |
| * Repeat the calls, because SSL_shutdown internally dispatches through a |
| * little state machine. Usually only one or two interation should be |
| * needed, so we restrict the total number of restrictions in order to |
| * avoid process hangs in case the client played bad with the socket |
| * connection and OpenSSL cannot recognize it. |
| * max 2x pending + 2x data = 4 |
| */ |
| for (i = 0; i < 4; i++) { |
| if ((rc = SSL_shutdown(ssl))) |
| break; |
| } |
| return rc; |
| } |
| |
| static apr_status_t ssl_cleanup(void *data) |
| { |
| tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)data; |
| |
| if (con) { |
| /* Pollset was already destroyed by |
| * the pool cleanup/destroy. |
| */ |
| con->pollset = NULL; |
| if (con->ssl) { |
| SSL *ssl = con->ssl; |
| con->ssl = NULL; |
| ssl_smart_shutdown(ssl, con->shutdown_type); |
| SSL_free(ssl); |
| } |
| if (con->peer) { |
| X509_free(con->peer); |
| con->peer = NULL; |
| } |
| } |
| |
| #ifdef TCN_DO_STATISTICS |
| apr_atomic_inc32(&ssl_cleared); |
| #endif |
| return APR_SUCCESS; |
| } |
| |
| static tcn_ssl_conn_t *ssl_create(JNIEnv *env, tcn_ssl_ctxt_t *ctx, apr_pool_t *pool) |
| { |
| tcn_ssl_conn_t *con; |
| SSL *ssl; |
| |
| if ((con = apr_pcalloc(pool, sizeof(tcn_ssl_conn_t))) == NULL) { |
| tcn_ThrowAPRException(env, apr_get_os_error()); |
| return NULL; |
| } |
| if ((ssl = SSL_new(ctx->ctx)) == NULL) { |
| char err[256]; |
| ERR_error_string(SSL_ERR_get(), err); |
| tcn_Throw(env, "SSL_new failed (%s)", err); |
| con = NULL; |
| return NULL; |
| } |
| SSL_clear(ssl); |
| con->pool = pool; |
| con->ctx = ctx; |
| con->ssl = ssl; |
| con->shutdown_type = ctx->shutdown_type; |
| apr_pollset_create(&(con->pollset), 1, pool, 0); |
| |
| SSL_set_app_data(ssl, (void *)con); |
| |
| /* store for later usage in SSL_callback_SSL_verify */ |
| SSL_set_app_data2(ssl, ctx); |
| |
| if (ctx->mode) { |
| /* |
| * Configure callbacks for SSL connection |
| */ |
| SSL_set_session_id_context(ssl, &(ctx->context_id[0]), |
| sizeof ctx->context_id); |
| } |
| SSL_set_verify_result(ssl, X509_V_OK); |
| SSL_rand_seed(ctx->rand_file); |
| |
| #ifdef TCN_DO_STATISTICS |
| ssl_created++; |
| #endif |
| return con; |
| } |
| |
| #ifdef WIN32 |
| #define APR_INVALID_SOCKET INVALID_SOCKET |
| #else |
| #define APR_INVALID_SOCKET -1 |
| #endif |
| |
| static apr_status_t wait_for_io_or_timeout(tcn_ssl_conn_t *con, |
| int for_what, |
| apr_interval_time_t timeout) |
| { |
| apr_pollfd_t pfd; |
| int type; |
| apr_status_t status; |
| apr_os_sock_t sock; |
| |
| if (!con->pollset) |
| return APR_ENOPOLL; |
| if (!con->sock) |
| return APR_ENOTSOCK; |
| if (con->reneg_state == RENEG_ABORT) { |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN; |
| return APR_ECONNABORTED; |
| } |
| |
| /* Check if the socket was already closed |
| */ |
| apr_os_sock_get(&sock, con->sock); |
| if (sock == APR_INVALID_SOCKET) |
| return APR_ENOTSOCK; |
| |
| /* Figure out the the poll direction */ |
| switch (for_what) { |
| case SSL_ERROR_WANT_WRITE: |
| case SSL_ERROR_WANT_CONNECT: |
| case SSL_ERROR_WANT_ACCEPT: |
| type = APR_POLLOUT; |
| break; |
| case SSL_ERROR_WANT_READ: |
| type = APR_POLLIN; |
| break; |
| default: |
| return APR_EINVAL; |
| break; |
| } |
| if (timeout <= 0) { |
| /* Waiting on zero or infinite timeouts is not allowed |
| */ |
| return APR_EAGAIN; |
| } |
| pfd.desc_type = APR_POLL_SOCKET; |
| pfd.desc.s = con->sock; |
| pfd.reqevents = type; |
| |
| /* Remove the object if it was in the pollset, then add in the new |
| * object with the correct reqevents value. Ignore the status result |
| * on the remove, because it might not be in there (yet). |
| */ |
| apr_pollset_remove(con->pollset, &pfd); |
| |
| /* ### check status code */ |
| apr_pollset_add(con->pollset, &pfd); |
| |
| do { |
| int numdesc; |
| const apr_pollfd_t *pdesc; |
| |
| status = apr_pollset_poll(con->pollset, timeout, &numdesc, &pdesc); |
| if (numdesc == 1 && (pdesc[0].rtnevents & type) != 0) |
| return APR_SUCCESS; |
| } while (APR_STATUS_IS_EINTR(status)); |
| |
| return status; |
| } |
| |
| static apr_status_t APR_THREAD_FUNC |
| ssl_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t) |
| { |
| tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock; |
| return apr_socket_timeout_set(con->sock, t); |
| } |
| |
| static apr_status_t APR_THREAD_FUNC |
| ssl_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t) |
| { |
| tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock; |
| return apr_socket_timeout_get(con->sock, t); |
| } |
| |
| static APR_INLINE apr_status_t APR_THREAD_FUNC |
| ssl_socket_opt_set(apr_socket_t *sock, apr_int32_t opt, apr_int32_t on) |
| { |
| tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock; |
| return apr_socket_opt_set(con->sock, opt, on); |
| } |
| |
| static APR_INLINE apr_status_t APR_THREAD_FUNC |
| ssl_socket_opt_get(apr_socket_t *sock, apr_int32_t opt, apr_int32_t *on) |
| { |
| tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock; |
| return apr_socket_opt_get(con->sock, opt, on); |
| } |
| |
| static apr_status_t APR_THREAD_FUNC |
| ssl_socket_shutdown(apr_socket_t *sock, apr_shutdown_how_e how) |
| { |
| apr_status_t rv = APR_SUCCESS; |
| tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock; |
| |
| if (con->ssl) { |
| SSL *ssl = con->ssl; |
| con->ssl = NULL; |
| if (how < 1) |
| how = con->shutdown_type; |
| rv = ssl_smart_shutdown(ssl, how); |
| /* TODO: Translate OpenSSL Error codes */ |
| SSL_free(ssl); |
| } |
| return rv; |
| } |
| |
| static apr_status_t APR_THREAD_FUNC |
| ssl_socket_close(apr_socket_t *sock) |
| { |
| tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock; |
| apr_status_t rv = APR_SUCCESS; |
| |
| #ifdef TCN_DO_STATISTICS |
| apr_atomic_inc32(&ssl_closed); |
| #endif |
| if (con->ssl) { |
| SSL *ssl = con->ssl; |
| con->ssl = NULL; |
| rv = ssl_smart_shutdown(ssl, con->shutdown_type); |
| SSL_free(ssl); |
| } |
| if (con->peer) { |
| X509_free(con->peer); |
| con->peer = NULL; |
| } |
| return rv; |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, SSLSocket, handshake)(TCN_STDARGS, jlong sock) |
| { |
| tcn_socket_t *ss = J2P(sock, tcn_socket_t *); |
| tcn_ssl_conn_t *con; |
| apr_interval_time_t timeout; |
| int s, i; |
| long vr; |
| apr_status_t rv; |
| X509 *peer; |
| |
| UNREFERENCED_STDARGS; |
| TCN_ASSERT(sock != 0); |
| if (ss->net->type != TCN_SOCKET_SSL) |
| return APR_EINVAL; |
| con = (tcn_ssl_conn_t *)ss->opaque; |
| |
| apr_socket_timeout_get(con->sock, &timeout); |
| while (!SSL_is_init_finished(con->ssl)) { |
| SSL_ERR_clear(); |
| if ((s = SSL_do_handshake(con->ssl)) <= 0) { |
| if (!con->ssl) |
| return APR_ENOTSOCK; |
| rv = apr_get_netos_error(); |
| i = SSL_get_error(con->ssl, s); |
| switch (i) { |
| case SSL_ERROR_NONE: |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD; |
| return APR_SUCCESS; |
| break; |
| case SSL_ERROR_WANT_READ: |
| case SSL_ERROR_WANT_WRITE: |
| if ((rv = wait_for_io_or_timeout(con, i, timeout)) != APR_SUCCESS) { |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN; |
| return rv; |
| } |
| break; |
| case SSL_ERROR_SYSCALL: |
| if (APR_STATUS_IS_EAGAIN(rv)) { |
| return APR_EAGAIN; |
| } |
| #if !defined(_WIN32) |
| else if (APR_STATUS_IS_EINTR(rv)) { |
| /* Interrupted by signal */ |
| continue; |
| } |
| #endif |
| /* Fall through */ |
| default: |
| /* |
| * Anything else is a fatal error |
| */ |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN; |
| return SSL_TO_APR_ERROR(i); |
| break; |
| } |
| } |
| if (!con->ssl) |
| return APR_ENOTSOCK; |
| |
| /* |
| * Check for failed client authentication |
| */ |
| if (con->ctx->verify_mode != SSL_VERIFY_NONE && |
| (vr = SSL_get_verify_result(con->ssl)) != X509_V_OK) { |
| |
| if (SSL_VERIFY_ERROR_IS_OPTIONAL(vr) && |
| con->ctx->verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA) { |
| /* TODO: Log optionalNoCA */ |
| } else { |
| /* TODO: Log SSL client authentication failed */ |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN; |
| /* TODO: Figure out the correct return value */ |
| return APR_EGENERAL; |
| } |
| } |
| |
| /* |
| * Remember the peer certificate |
| */ |
| if ((peer = SSL_get_peer_certificate(con->ssl)) != NULL) { |
| if (con->peer) |
| X509_free(con->peer); |
| con->peer = peer; |
| } |
| } |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t APR_THREAD_FUNC |
| ssl_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len) |
| { |
| tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock; |
| int s, i, rd = (int)(*len); |
| apr_status_t rv; |
| apr_interval_time_t timeout; |
| |
| *len = 0; |
| if (con->reneg_state == RENEG_ABORT) { |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN; |
| return APR_ECONNABORTED; |
| } |
| apr_socket_timeout_get(con->sock, &timeout); |
| for (;;) { |
| SSL_ERR_clear(); |
| if ((s = SSL_read(con->ssl, buf, rd)) <= 0) { |
| if (!con->ssl) |
| return APR_ENOTSOCK; |
| rv = apr_get_netos_error(); |
| i = SSL_get_error(con->ssl, s); |
| /* Special case if the "close notify" alert send by peer */ |
| if (s == 0 && (SSL_get_shutdown(con->ssl) & SSL_RECEIVED_SHUTDOWN)) { |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD; |
| return APR_EOF; |
| } |
| switch (i) { |
| case SSL_ERROR_WANT_READ: |
| case SSL_ERROR_WANT_WRITE: |
| if ((rv = wait_for_io_or_timeout(con, i, timeout)) != APR_SUCCESS) { |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN; |
| return rv; |
| } |
| break; |
| case SSL_ERROR_SYSCALL: |
| if (APR_STATUS_IS_EPIPE(rv) || APR_STATUS_IS_ECONNRESET(rv)) { |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD; |
| return APR_EOF; |
| } |
| else if (APR_STATUS_IS_EAGAIN(rv)) { |
| return APR_EAGAIN; |
| } |
| #if !defined(_WIN32) |
| else if (APR_STATUS_IS_EINTR(rv)) { |
| /* Interrupted by signal |
| */ |
| continue; |
| } |
| #endif |
| /* Fall through */ |
| case SSL_ERROR_ZERO_RETURN: |
| if (s == 0) { |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD; |
| return APR_EOF; |
| } |
| /* Fall through */ |
| default: |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN; |
| return APR_EGENERAL; |
| break; |
| } |
| } |
| else { |
| *len = s; |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD; |
| break; |
| } |
| } |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t APR_THREAD_FUNC |
| ssl_socket_send(apr_socket_t *sock, const char *buf, |
| apr_size_t *len) |
| { |
| tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock; |
| int s, i, wr = (int)(*len); |
| apr_status_t rv; |
| apr_interval_time_t timeout; |
| |
| *len = 0; |
| if (con->reneg_state == RENEG_ABORT) { |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN; |
| return APR_ECONNABORTED; |
| } |
| if (!SSL_is_init_finished(con->ssl)) { |
| /* XXX: Is this a correct retval ? */ |
| return APR_EINPROGRESS; |
| } |
| if (wr == 0) { |
| /* According to docs calling SSL_write() with num=0 bytes |
| * to be sent the behaviour is undefined. |
| */ |
| return APR_EINVAL; |
| } |
| apr_socket_timeout_get(con->sock, &timeout); |
| for (;;) { |
| SSL_ERR_clear(); |
| if ((s = SSL_write(con->ssl, buf, wr)) <= 0) { |
| if (!con->ssl) |
| return APR_ENOTSOCK; |
| rv = apr_get_netos_error(); |
| i = SSL_get_error(con->ssl, s); |
| switch (i) { |
| case SSL_ERROR_WANT_READ: |
| case SSL_ERROR_WANT_WRITE: |
| if ((rv = wait_for_io_or_timeout(con, i, timeout)) != APR_SUCCESS) { |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN; |
| return rv; |
| } |
| break; |
| case SSL_ERROR_SYSCALL: |
| if (s == -1) { |
| if (APR_STATUS_IS_EPIPE(rv) || APR_STATUS_IS_ECONNRESET(rv)) { |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD; |
| return APR_EOF; |
| } |
| #if !defined(_WIN32) |
| else if (APR_STATUS_IS_EINTR(rv)) { |
| /* Interrupted by signal |
| */ |
| continue; |
| } |
| #endif |
| } |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN; |
| return rv; |
| break; |
| case SSL_ERROR_SSL: |
| /* Probably caused by buffer missmatch */ |
| rv = APR_EINVAL; |
| case SSL_ERROR_ZERO_RETURN: |
| if (s == 0) { |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD; |
| return APR_EOF; |
| } |
| /* Fall through */ |
| default: |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN; |
| return rv; |
| break; |
| } |
| } |
| else { |
| *len = s; |
| break; |
| } |
| } |
| return APR_SUCCESS; |
| } |
| |
| static apr_status_t APR_THREAD_FUNC |
| ssl_socket_sendv(apr_socket_t *sock, |
| const struct iovec *vec, |
| apr_int32_t nvec, apr_size_t *len) |
| { |
| tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock; |
| apr_status_t rv; |
| apr_size_t written = 0; |
| apr_int32_t i; |
| |
| for (i = 0; i < nvec; i++) { |
| apr_size_t rd = vec[i].iov_len; |
| if ((rv = ssl_socket_send((apr_socket_t *)con, |
| vec[i].iov_base, &rd)) != APR_SUCCESS) { |
| *len = written; |
| return rv; |
| } |
| written += rd; |
| } |
| *len = written; |
| return APR_SUCCESS; |
| } |
| |
| static tcn_nlayer_t ssl_socket_layer = { |
| TCN_SOCKET_SSL, |
| ssl_cleanup, |
| ssl_socket_close, |
| ssl_socket_shutdown, |
| ssl_socket_opt_get, |
| ssl_socket_opt_set, |
| ssl_socket_timeout_get, |
| ssl_socket_timeout_set, |
| ssl_socket_send, |
| ssl_socket_sendv, |
| ssl_socket_recv |
| }; |
| |
| |
| TCN_IMPLEMENT_CALL(jint, SSLSocket, attach)(TCN_STDARGS, jlong ctx, |
| jlong sock) |
| { |
| tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *); |
| tcn_socket_t *s = J2P(sock, tcn_socket_t *); |
| tcn_ssl_conn_t *con; |
| apr_os_sock_t oss; |
| apr_status_t rv; |
| |
| UNREFERENCED(o); |
| TCN_ASSERT(ctx != 0); |
| TCN_ASSERT(sock != 0); |
| |
| if (!s->sock) |
| return APR_ENOTSOCK; |
| |
| if ((rv = apr_os_sock_get(&oss, s->sock)) != APR_SUCCESS) |
| return rv; |
| if (oss == APR_INVALID_SOCKET) |
| return APR_ENOTSOCK; |
| |
| if ((con = ssl_create(e, c, s->pool)) == NULL) |
| return APR_EGENERAL; |
| con->sock = s->sock; |
| |
| SSL_set_fd(con->ssl, (int)oss); |
| if (c->mode) |
| SSL_set_accept_state(con->ssl); |
| else |
| SSL_set_connect_state(con->ssl); |
| /* Change socket type */ |
| s->net = &ssl_socket_layer; |
| s->opaque = con; |
| |
| return APR_SUCCESS; |
| } |
| |
| static int ssl_do_renegotiate(tcn_ssl_conn_t *con, int use_pha) |
| { |
| int retVal; |
| int error = 0; |
| char peekbuf[1]; |
| apr_interval_time_t timeout; |
| |
| apr_socket_timeout_get(con->sock, &timeout); |
| |
| /* Trigger reading of the certs from the client. |
| * Peeking 0 bytes actually works. |
| * Before TLS 1.3 this will result in a renegotiation. |
| * for TLS 1.3 in PHA. |
| * See: http://marc.info/?t=145493359200002&r=1&w=2 |
| * |
| * This will normally return SSL_ERROR_WANT_READ whether the renegotiation |
| * has been completed or not. Afterwards, need to determine if I/O needs to |
| * be triggered or not. |
| */ |
| retVal = SSL_peek(con->ssl, peekbuf, 0); |
| if (retVal < 1) { |
| error = SSL_get_error(con->ssl, retVal); |
| } |
| |
| // If the certs have not been received, then need to wait for I/O |
| while ((use_pha && con->pha_state == PHA_STARTED) || (!use_pha && SSL_renegotiate_pending(con->ssl))) { |
| // SSL_ERROR_WANT_READ is expected. Anything else is an error. |
| if (error == SSL_ERROR_WANT_READ) { |
| retVal = wait_for_io_or_timeout(con, error, timeout); |
| /* |
| * Since this is blocking I/O, anything other than APR_SUCCESS is an |
| * error. |
| */ |
| if (retVal != APR_SUCCESS) { |
| con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN; |
| return retVal; |
| } |
| } else { |
| return APR_EGENERAL; |
| } |
| |
| // Re-try SSL_peek after I/O |
| retVal = SSL_peek(con->ssl, peekbuf, 0); |
| if (retVal < 1) { |
| error = SSL_get_error(con->ssl, retVal); |
| } else { |
| /* |
| * Reset error to handle case where SSL_Peek returns 0 but |
| * the pha resp. renegotiation state has not changed. |
| * This will trigger an error to be returned. |
| */ |
| error = 0; |
| } |
| } |
| return APR_SUCCESS; |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, SSLSocket, renegotiate)(TCN_STDARGS, |
| jlong sock) |
| { |
| tcn_socket_t *s = J2P(sock, tcn_socket_t *); |
| tcn_ssl_conn_t *con; |
| int retVal; |
| #if defined(SSL_OP_NO_TLSv1_3) |
| const SSL_SESSION *session; |
| #endif |
| |
| UNREFERENCED_STDARGS; |
| TCN_ASSERT(sock != 0); |
| con = (tcn_ssl_conn_t *)s->opaque; |
| |
| #if defined(SSL_OP_NO_TLSv1_3) |
| session = SSL_get_session(con->ssl); |
| if (SSL_SESSION_get_protocol_version(session) == TLS1_3_VERSION) { |
| // TLS 1.3 renegotiation |
| retVal = SSL_verify_client_post_handshake(con->ssl); |
| if (retVal <= 0) { |
| return APR_EGENERAL; |
| } |
| |
| con->pha_state = PHA_STARTED; |
| |
| // Need to trigger a write operation to sent the cert request to the |
| // client. As per OpenSSL docs, use SSL_do_handshake() for this. |
| retVal = SSL_do_handshake(con->ssl); |
| if (retVal <= 0) { |
| return APR_EGENERAL; |
| } |
| retVal = ssl_do_renegotiate(con, 1); |
| if (retVal != APR_SUCCESS) { |
| return retVal; |
| } |
| |
| } else { |
| #endif |
| // TLS 1.2 and earlier renegotiation |
| |
| /* Toggle the renegotiation state to allow the new |
| * handshake to proceed. |
| */ |
| con->reneg_state = RENEG_ALLOW; |
| |
| // Schedule a renegotiation request |
| retVal = SSL_renegotiate(con->ssl); |
| if (retVal <= 0) { |
| return APR_EGENERAL; |
| } |
| retVal = ssl_do_renegotiate(con, 0); |
| if (retVal != APR_SUCCESS) { |
| return retVal; |
| } |
| |
| con->reneg_state = RENEG_REJECT; |
| #if defined(SSL_OP_NO_TLSv1_3) |
| } |
| #endif |
| return APR_SUCCESS; |
| } |
| |
| TCN_IMPLEMENT_CALL(void, SSLSocket, setVerify)(TCN_STDARGS, |
| jlong sock, |
| jint cverify, |
| jint depth) |
| { |
| tcn_socket_t *s = J2P(sock, tcn_socket_t *); |
| tcn_ssl_conn_t *con; |
| int verify = SSL_VERIFY_NONE; |
| |
| UNREFERENCED_STDARGS; |
| TCN_ASSERT(sock != 0); |
| con = (tcn_ssl_conn_t *)s->opaque; |
| |
| if (cverify == SSL_CVERIFY_UNSET) |
| cverify = SSL_CVERIFY_NONE; |
| if (depth > 0) |
| SSL_set_verify_depth(con->ssl, depth); |
| |
| if (cverify == SSL_CVERIFY_REQUIRE) |
| verify |= SSL_VERIFY_PEER_STRICT; |
| if ((cverify == SSL_CVERIFY_OPTIONAL) || |
| (cverify == SSL_CVERIFY_OPTIONAL_NO_CA)) |
| verify |= SSL_VERIFY_PEER; |
| |
| SSL_set_verify(con->ssl, verify, NULL); |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, SSLSocket, getALPN)(TCN_STDARGS, jlong sock, jbyteArray buf) |
| { |
| const unsigned char *alpn; |
| unsigned alpn_len; |
| int len; |
| tcn_socket_t *s = J2P(sock, tcn_socket_t *); |
| tcn_ssl_conn_t *tcssl = (tcn_ssl_conn_t *)s->opaque; |
| int bufLen = (*e)->GetArrayLength(e, buf); |
| |
| SSL_get0_alpn_selected(tcssl->ssl, &alpn, &alpn_len); |
| |
| if (alpn_len == 0 || bufLen < alpn_len) { |
| return 0; |
| } |
| len = (int)alpn_len; |
| (*e)->SetByteArrayRegion(e, buf, 0, len, (jbyte *)alpn); |
| |
| return len; |
| } |
| |
| #else |
| /* OpenSSL is not supported. |
| * Create empty stubs. |
| */ |
| |
| TCN_IMPLEMENT_CALL(jint, SSLSocket, handshake)(TCN_STDARGS, jlong sock) |
| { |
| UNREFERENCED_STDARGS; |
| UNREFERENCED(sock); |
| return (jint)APR_ENOTIMPL; |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, SSLSocket, attach)(TCN_STDARGS, jlong ctx, |
| jlong sock) |
| { |
| UNREFERENCED_STDARGS; |
| UNREFERENCED(ctx); |
| UNREFERENCED(sock); |
| return (jint)APR_ENOTIMPL; |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, SSLSocket, renegotiate)(TCN_STDARGS, |
| jlong sock) |
| { |
| UNREFERENCED_STDARGS; |
| UNREFERENCED(sock); |
| return (jint)APR_ENOTIMPL; |
| } |
| |
| TCN_IMPLEMENT_CALL(jint, SSLSocket, getALPN)(TCN_STDARGS, jlong sock, jbyteArray buf) |
| { |
| UNREFERENCED(sock); |
| UNREFERENCED(buf); |
| return (jint)APR_ENOTIMPL; |
| } |
| |
| #endif |