JNI/JSSE: pass socket timeout to select() for wolfSSL_accept()

pull/154/head
Chris Conlon 2023-10-19 15:04:30 -06:00
parent 15a1c90a8c
commit 3d6ceb09a3
4 changed files with 174 additions and 28 deletions

View File

@ -33,6 +33,11 @@
#include <arpa/inet.h>
#endif
#ifndef WOLFSSL_JNI_DEFAULT_PEEK_TIMEOUT
/* Default wolfSSL_peek() timeout for wolfSSL_get_session(), ms */
#define WOLFSSL_JNI_DEFAULT_PEEK_TIMEOUT 2000
#endif
#include <wolfssl/ssl.h>
#include <wolfssl/error-ssl.h>
@ -898,7 +903,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_read(JNIEnv* jenv,
}
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_accept
(JNIEnv* jenv, jobject jcl, jlong sslPtr)
(JNIEnv* jenv, jobject jcl, jlong sslPtr, jint timeout)
{
int ret = 0, err, sockfd;
wolfSSL_Mutex* jniSessLock = NULL;
@ -954,7 +959,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_accept
break;
}
ret = socketSelect(sockfd, 0, 1);
ret = socketSelect(sockfd, (int)timeout, 1);
if (ret == WOLFJNI_RECV_READY || ret == WOLFJNI_SEND_READY) {
/* I/O ready, continue handshake and try again */
continue;
@ -1221,26 +1226,82 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setSession
{
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
WOLFSSL_SESSION* session = (WOLFSSL_SESSION*)(uintptr_t)sessionPtr;
wolfSSL_Mutex* jniSessLock = NULL;
SSLAppData* appData = NULL;
int ret = 0;
(void)jcl;
if (jenv == NULL || ssl == NULL) {
return SSL_FAILURE;
}
/* get session mutex from SSL app data */
appData = (SSLAppData*)wolfSSL_get_app_data(ssl);
if (appData == NULL) {
printf("Failed to get SSLAppData* in native setSession()\n");
return (jlong)0;
}
jniSessLock = appData->jniSessLock;
if (jniSessLock == NULL) {
printf("SSLAppData* NULL in native setSession()\n");
return (jlong)0;
}
/* get WOLFSSL session I/O lock */
if (wc_LockMutex(jniSessLock) != 0) {
printf("Failed to lock native jniSessLock in setSession()");
return (jlong)0;
}
/* wolfSSL checks session for NULL, but not ssl */
return wolfSSL_set_session(ssl, session);
ret = wolfSSL_set_session(ssl, session);
if (wc_UnLockMutex(jniSessLock) != 0) {
printf("Failed to unlock jniSessLock in setSession()");
}
return ret;
}
JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_getSession
(JNIEnv* jenv, jobject jcl, jlong sslPtr)
{
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
WOLFSSL_SESSION* sess = NULL;
wolfSSL_Mutex* jniSessLock = NULL;
SSLAppData* appData = NULL;
(void)jenv;
(void)jcl;
/* get session mutex from SSL app data */
appData = (SSLAppData*)wolfSSL_get_app_data(ssl);
if (appData == NULL) {
printf("Failed to get SSLAppData* in native getSession()\n");
return (jlong)0;
}
jniSessLock = appData->jniSessLock;
if (jniSessLock == NULL) {
printf("SSLAppData* NULL in native getSession()\n");
return (jlong)0;
}
/* get WOLFSSL session I/O lock */
if (wc_LockMutex(jniSessLock) != 0) {
printf("Failed to lock native jniSessLock in getSession()");
return (jlong)0;
}
/* wolfSSL checks ssl for NULL, returns pointer into WOLFSSL which is
* freed when wolfSSL_free() is called. */
return (jlong)(uintptr_t)wolfSSL_get_session(ssl);
sess = wolfSSL_get_session(ssl);
if (wc_UnLockMutex(jniSessLock) != 0) {
printf("Failed to unlock jniSessLock in getSession()");
}
return (jlong)(uintptr_t)sess;
}
JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_get1Session
@ -1254,6 +1315,9 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_get1Session
/* tmpBuf is only 1 byte since wolfSSL_peek() doesn't need to read
* any app data, only session ticket internally */
char tmpBuf[1];
int ret = 0;
int err = 0;
int sockfd = 0;
(void)jenv;
(void)jcl;
@ -1281,8 +1345,32 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_get1Session
* session ticket message. */
sess = wolfSSL_get_session(ssl);
if (sess == NULL) {
/* session not available yet (TLS 1.3), try peeking to get ticket */
wolfSSL_peek(ssl, tmpBuf, (int)sizeof(tmpBuf));
do {
ret = wolfSSL_peek(ssl, tmpBuf, (int)sizeof(tmpBuf));
err = wolfSSL_get_error(ssl, ret);
if (ret <= 0 && (err == SSL_ERROR_WANT_READ)) {
sockfd = wolfSSL_get_fd(ssl);
if (sockfd == -1) {
/* For I/O that does not use sockets, sockfd may be -1,
* skip try to call select() */
break;
}
ret = socketSelect(sockfd,
(int)WOLFSSL_JNI_DEFAULT_PEEK_TIMEOUT, 1);
if (ret == WOLFJNI_RECV_READY || ret == WOLFJNI_SEND_READY) {
/* I/O ready, continue handshake and try again */
continue;
} else {
/* other error, continue on */
break;
}
}
} while (err == SSL_ERROR_WANT_READ);
sess = wolfSSL_get_session(ssl);
}

View File

@ -106,10 +106,10 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_read
/*
* Class: com_wolfssl_WolfSSLSession
* Method: accept
* Signature: (J)I
* Signature: (JI)I
*/
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_accept
(JNIEnv *, jobject, jlong);
(JNIEnv *, jobject, jlong, jint);
/*
* Class: com_wolfssl_WolfSSLSession

View File

@ -212,7 +212,7 @@ public class WolfSSLSession {
private native int connect(long ssl, int timeout);
private native int write(long ssl, byte[] data, int length, int timeout);
private native int read(long ssl, byte[] data, int sz, int timeout);
private native int accept(long ssl);
private native int accept(long ssl, int timeout);
private native void freeSSL(long ssl);
private native int shutdownSSL(long ssl, int timeout);
private native int getError(long ssl, int ret);
@ -847,16 +847,76 @@ public class WolfSSLSession {
* </code> if an error occurred. To get a more detailed
* error code, call <code>getError()</code>.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketTimeoutException if underlying socket timed out
* @see #getError(int)
* @see #connect()
*/
public int accept() throws IllegalStateException {
public int accept()
throws IllegalStateException, SocketTimeoutException {
int ret;
confirmObjectIsActive();
synchronized (sslLock) {
return accept(getSessionPtr());
ret = accept(getSessionPtr(), 0);
}
if (ret == WolfSSL.WOLFJNI_TIMEOUT) {
throw new SocketTimeoutException(
"Native socket timed out during SSL_accept()");
}
return ret;
}
/**
* Waits for an SSL client to initiate the SSL/TLS handshake, using socket
* timeout value in milliseconds.
* This method is called on the server side. When it is called, the
* underlying communication channel has already been set up.
* <p>
* <code>accept()</code> works with both blocking and non-blocking I/O.
* When the underlying I/O is non-blocking, <code>accept()</code> will
* return when the underlying I/O could not satisfy the needs of
* <code>accept()</code> to continue the handshake. In this case, a call to
* <code>getError()</code> will yield either <b>SSL_ERROR_WANT_READ</b> or
* <b>SSL_ERROR_WANT_WRITE</b>. The calling process must then repeat the
* call to <code>accept()</code> when data is available to be read and
* wolfSSL will pick up where it left off. When using a non-blocking
* socket, nothing needs to be done, but <code>select()</code> can be used
* to check for the required condition.
* <p>
* If the underlying I/O is blocking, <code>accept()</code> will only
* return once the handshake has been finished or an error occurred.
*
* @param timeout read timeout, milliseconds.
*
* @return <code>SSL_SUCCESS</code> on success. <code>SSL_FATAL_ERROR
* </code> if an error occurred. To get a more detailed
* error code, call <code>getError()</code>.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketTimeoutException if underlying socket timed out
* @see #getError(int)
* @see #connect()
*/
public int accept(int timeout)
throws IllegalStateException, SocketTimeoutException {
int ret;
confirmObjectIsActive();
synchronized (sslLock) {
ret = accept(getSessionPtr(), timeout);
}
if (ret == WolfSSL.WOLFJNI_TIMEOUT) {
throw new SocketTimeoutException(
"Native socket timed out during SSL_accept()");
}
return ret;
}
/**
@ -1038,7 +1098,7 @@ public class WolfSSLSession {
* the connection without a new handshake.
* <p>
* For session resumption, before calling <code>shutdownSSL()</code>
* with your session object, an appliation should save the session ID
* with your session object, an application should save the session ID
* from the object with a call to <code>getSession()</code>, which returns
* a pointer to the session. Later, the application should create a new
* SSL object and assign the saved session with <code>setSession</code>.

View File

@ -138,7 +138,12 @@ public class WolfSSLEngineHelper {
* @return WolfSSLImplementSession for this object
*/
protected WolfSSLImplementSSLSession getSession() {
if (this.session == null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"this.session is null, creating new " +
"WolfSSLImplementSSLSession");
this.session = new WolfSSLImplementSSLSession(authStore);
}
return this.session;
@ -795,7 +800,6 @@ public class WolfSSLEngineHelper {
* or not.
* @param timeout socket timeout (milliseconds) for connect(), or 0 for
* infinite/no timeout.
*
* @return WolfSSL.SSL_SUCCESS on success or either WolfSSL.SSL_FAILURE
* or WolfSSL.SSL_HANDSHAKE_FAILURE on error
*
@ -826,7 +830,7 @@ public class WolfSSLEngineHelper {
if ((this.session == null) || !this.session.isValid()) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"session is marked as invalid, try creating a new seesion");
"session is marked as invalid, try creating a new session");
if (this.sessionCreation == false) {
/* new handshakes can not be made in this case. */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
@ -834,7 +838,7 @@ public class WolfSSLEngineHelper {
return WolfSSL.SSL_HANDSHAKE_FAILURE;
}
this.session = this.authStore.getSession(ssl);
this.session = this.authStore.getSession(ssl, this.clientMode);
}
if (this.clientMode) {
@ -863,7 +867,7 @@ public class WolfSSLEngineHelper {
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"calling native wolfSSL_accept()");
ret = this.ssl.accept();
ret = this.ssl.accept(timeout);
}
err = ssl.getError(ret);
@ -871,32 +875,26 @@ public class WolfSSLEngineHelper {
(err == WolfSSL.SSL_ERROR_WANT_READ ||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
if (this.sessionCreation && ret == WolfSSL.SSL_SUCCESS) {
/* can only add new sessions to the resumption table if session
* creation is allowed */
if (this.clientMode) {
/* Only need to set resume on client side, server-side
* maintains session cache at native level. */
this.session.setResume();
}
this.authStore.addSession(this.session);
}
return ret;
}
/**
* Saves session on connection close for resumption
*
* @return WolfSSL.SSL_SUCCESS if session was saved into cache, otherwise
* WolfSSL.SSL_FAILURE
*/
protected synchronized void saveSession() {
protected synchronized int saveSession() {
if (this.session != null && this.session.isValid()) {
if (this.clientMode) {
/* Only need to set resume on client side, server-side
* maintains session cache at native level. */
this.session.setResume();
}
this.authStore.addSession(this.session);
return this.authStore.addSession(this.session);
}
return WolfSSL.SSL_FAILURE;
}
@SuppressWarnings("deprecation")