/**
* Frees the SSL error state.
- *
+ *
* OpenSSL keeps an "error stack" per thread, and given that this code
* can be called from arbitrary threads that we don't keep track of,
* we err on the side of freeing the error state promptly (instead of,
/*
* Checks this thread's OpenSSL error queue and throws a RuntimeException if
* necessary.
- *
+ *
* @return 1 if an exception was thrown, 0 if not.
*/
static int throwExceptionIfNecessary(JNIEnv* env) {
return result;
}
+
+/**
+ * Throws an SocketTimeoutException with the given string as a message.
+ */
+static void throwSocketTimeoutException(JNIEnv* env, const char* message) {
+ if (jniThrowException(env, "java/net/SocketTimeoutException", message)) {
+ LOGE("Unable to throw");
+ }
+}
+
+/**
+ * Throws a java.io.IOException with the given string as a message.
+ */
+static void throwIOExceptionStr(JNIEnv* env, const char* message) {
+ if (jniThrowException(env, "java/io/IOException", message)) {
+ LOGE("Unable to throw");
+ }
+}
+
+/**
+ * Throws an IOException with a message constructed from the current
+ * SSL errors. This will also log the errors.
+ *
+ * @param env the JNI environment
+ * @param sslReturnCode return code from failing SSL function
+ * @param sslErrorCode error code returned from SSL_get_error()
+ * @param message null-ok; general error message
+ */
+static void throwIOExceptionWithSslErrors(JNIEnv* env, int sslReturnCode,
+ int sslErrorCode, const char* message) {
+ const char* messageStr = NULL;
+ char* str;
+ int ret;
+
+ // First consult the SSL error code for the general message.
+ switch (sslErrorCode) {
+ case SSL_ERROR_NONE:
+ messageStr = "Ok";
+ break;
+ case SSL_ERROR_SSL:
+ messageStr = "Failure in SSL library, usually a protocol error";
+ break;
+ case SSL_ERROR_WANT_READ:
+ messageStr = "SSL_ERROR_WANT_READ occured. You should never see this.";
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ messageStr = "SSL_ERROR_WANT_WRITE occured. You should never see this.";
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ messageStr = "SSL_ERROR_WANT_X509_LOOKUP occured. You should never see this.";
+ break;
+ case SSL_ERROR_SYSCALL:
+ messageStr = "I/O error during system call";
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ messageStr = "SSL_ERROR_ZERO_RETURN occured. You should never see this.";
+ break;
+ case SSL_ERROR_WANT_CONNECT:
+ messageStr = "SSL_ERROR_WANT_CONNECT occured. You should never see this.";
+ break;
+ case SSL_ERROR_WANT_ACCEPT:
+ messageStr = "SSL_ERROR_WANT_ACCEPT occured. You should never see this.";
+ break;
+ default:
+ messageStr = "Unknown SSL error";
+ }
+
+ // Prepend either our explicit message or a default one.
+ if (asprintf(&str, "%s: %s",
+ (message != NULL) ? message : "SSL error", messageStr) == 0) {
+ throwIOExceptionStr(env, messageStr);
+ LOGV("%s", messageStr);
+ freeSslErrorState();
+ return;
+ }
+
+ char* allocStr = str;
+
+ // For SSL protocol errors, SSL might have more information.
+ if (sslErrorCode == SSL_ERROR_SSL) {
+ // Append each error as an additional line to the message.
+ for (;;) {
+ char errStr[256];
+ const char* file;
+ int line;
+ const char* data;
+ int flags;
+ unsigned long err =
+ ERR_get_error_line_data(&file, &line, &data, &flags);
+ if (err == 0) {
+ break;
+ }
+
+ ERR_error_string_n(err, errStr, sizeof(errStr));
+
+ ret = asprintf(&str, "%s\n%s (%s:%d %p:0x%08x)",
+ (allocStr == NULL) ? "" : allocStr,
+ errStr,
+ file,
+ line,
+ data,
+ flags);
+
+ if (ret < 0) {
+ break;
+ }
+
+ free(allocStr);
+ allocStr = str;
+ }
+ // For errors during system calls, errno might be our friend.
+ } else if (sslErrorCode == SSL_ERROR_SYSCALL) {
+ if (asprintf(&str, "%s, %s", allocStr, strerror(errno)) >= 0) {
+ free(allocStr);
+ allocStr = str;
+ }
+ // If the error code is invalid, print it.
+ } else if (sslErrorCode > SSL_ERROR_WANT_ACCEPT) {
+ if (asprintf(&str, ", error code is %d", sslErrorCode) >= 0) {
+ free(allocStr);
+ allocStr = str;
+ }
+ }
+
+ throwIOExceptionStr(env, allocStr);
+
+ LOGV("%s", allocStr);
+ free(allocStr);
+ freeSslErrorState();
+}
+
+/**
+ * Helper function that grabs the casts an ssl pointer and then checks for nullness.
+ * If this function returns NULL and <code>throwIfNull</code> is
+ * passed as <code>true</code>, then this function will call
+ * <code>throwIOExceptionStr</code> before returning, so in this case of
+ * NULL, a caller of this function should simply return and allow JNI
+ * to do its thing.
+ *
+ * @param env the JNI environment
+ * @param ssl_address; the ssl_address pointer as an integer
+ * @param throwIfNull whether to throw if the SSL pointer is NULL
+ * @returns the pointer, which may be NULL
+ */
+static SSL* getSslPointer(JNIEnv* env, int ssl_address, bool throwIfNull) {
+ SSL* ssl = reinterpret_cast<SSL*>(static_cast<uintptr_t>(ssl_address));
+ if ((ssl == NULL) && throwIfNull) {
+ throwIOExceptionStr(env, "null SSL pointer");
+ }
+
+ return ssl;
+}
+
/**
* Converts a Java byte[] to an OpenSSL BIGNUM, allocating the BIGNUM on the
* fly.
*/
static BIGNUM* arrayToBignum(JNIEnv* env, jbyteArray source) {
// LOGD("Entering arrayToBignum()");
-
+
jbyte* sourceBytes = env->GetByteArrayElements(source, NULL);
int sourceLength = env->GetArrayLength(source);
BIGNUM* bignum = BN_bin2bn((unsigned char*) sourceBytes, sourceLength, NULL);
}
/**
- * private static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] pub_key, byte[] priv_key);
+ * OpenSSL locking support. Taken from the O'Reilly book by Viega et al., but I
+ * suppose there are not many other ways to do this on a Linux system (modulo
+ * isomorphism).
+ */
+#define MUTEX_TYPE pthread_mutex_t
+#define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL)
+#define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
+#define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
+#define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
+#define THREAD_ID pthread_self()
+#define THROW_EXCEPTION (-2)
+#define THROW_SOCKETTIMEOUTEXCEPTION (-3)
+
+static MUTEX_TYPE *mutex_buf = NULL;
+
+static void locking_function(int mode, int n, const char * file, int line) {
+ if (mode & CRYPTO_LOCK) {
+ MUTEX_LOCK(mutex_buf[n]);
+ } else {
+ MUTEX_UNLOCK(mutex_buf[n]);
+ }
+}
+
+static unsigned long id_function(void) {
+ return ((unsigned long)THREAD_ID);
+}
+
+int THREAD_setup(void) {
+ int i;
+
+ mutex_buf = (MUTEX_TYPE *)malloc(CRYPTO_num_locks( ) * sizeof(MUTEX_TYPE));
+
+ if(!mutex_buf) {
+ return 0;
+ }
+
+ for (i = 0; i < CRYPTO_num_locks( ); i++) {
+ MUTEX_SETUP(mutex_buf[i]);
+ }
+
+ CRYPTO_set_id_callback(id_function);
+ CRYPTO_set_locking_callback(locking_function);
+
+ return 1;
+}
+
+int THREAD_cleanup(void) {
+ int i;
+
+ if (!mutex_buf) {
+ return 0;
+ }
+
+ CRYPTO_set_id_callback(NULL);
+ CRYPTO_set_locking_callback(NULL);
+
+ for (i = 0; i < CRYPTO_num_locks( ); i++) {
+ MUTEX_CLEANUP(mutex_buf[i]);
+ }
+
+ free(mutex_buf);
+ mutex_buf = NULL;
+
+ return 1;
+}
+
+/**
+ * Initialization phase for every OpenSSL job: Loads the Error strings, the
+ * crypto algorithms and reset the OpenSSL library
+ */
+static void NativeCrypto_clinit(JNIEnv* env, jclass)
+{
+ SSL_load_error_strings();
+ ERR_load_crypto_strings();
+ SSL_library_init();
+ OpenSSL_add_all_algorithms();
+ THREAD_setup();
+}
+
+/**
+ * public static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] pub_key, byte[] priv_key);
*/
static EVP_PKEY* NativeCrypto_EVP_PKEY_new_DSA(JNIEnv* env, jclass clazz, jbyteArray p, jbyteArray q, jbyteArray g, jbyteArray pub_key, jbyteArray priv_key) {
// LOGD("Entering EVP_PKEY_new_DSA()");
-
+
DSA* dsa = DSA_new();
-
+
dsa->p = arrayToBignum(env, p);
dsa->q = arrayToBignum(env, q);
dsa->g = arrayToBignum(env, g);
dsa->pub_key = arrayToBignum(env, pub_key);
-
+
if (priv_key != NULL) {
dsa->priv_key = arrayToBignum(env, priv_key);
}
jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
return NULL;
}
-
+
EVP_PKEY* pkey = EVP_PKEY_new();
EVP_PKEY_assign_DSA(pkey, dsa);
-
+
return pkey;
}
*/
static EVP_PKEY* NativeCrypto_EVP_PKEY_new_RSA(JNIEnv* env, jclass clazz, jbyteArray n, jbyteArray e, jbyteArray d, jbyteArray p, jbyteArray q) {
// LOGD("Entering EVP_PKEY_new_RSA()");
-
+
RSA* rsa = RSA_new();
-
+
rsa->n = arrayToBignum(env, n);
rsa->e = arrayToBignum(env, e);
-
+
if (d != NULL) {
rsa->d = arrayToBignum(env, d);
}
-
+
if (p != NULL) {
rsa->p = arrayToBignum(env, p);
}
-
+
if (q != NULL) {
rsa->q = arrayToBignum(env, q);
}
// int check = RSA_check_key(rsa);
// LOGI("RSA_check_key returns %d", check);
-
+
if (rsa->n == NULL || rsa->e == NULL) {
RSA_free(rsa);
jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
return NULL;
}
-
+
EVP_PKEY* pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa);
-
+
return pkey;
}
*/
static jint NativeCrypto_EVP_new(JNIEnv* env, jclass clazz) {
// LOGI("NativeCrypto_EVP_DigestNew");
-
+
return (jint)EVP_MD_CTX_create();
}
*/
static void NativeCrypto_EVP_free(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) {
// LOGI("NativeCrypto_EVP_DigestFree");
-
+
if (ctx != NULL) {
EVP_MD_CTX_destroy(ctx);
}
*/
static jint NativeCrypto_EVP_DigestFinal(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray hash, jint offset) {
// LOGI("NativeCrypto_EVP_DigestFinal%x, %x, %d, %d", ctx, hash, offset);
-
+
if (ctx == NULL || hash == NULL) {
jniThrowNullPointerException(env, NULL);
return -1;
}
int result = -1;
-
+
jbyte* hashBytes = env->GetByteArrayElements(hash, NULL);
EVP_DigestFinal(ctx, (unsigned char*) (hashBytes + offset), (unsigned int*)&result);
env->ReleaseByteArrayElements(hash, hashBytes, 0);
throwExceptionIfNecessary(env);
-
+
return result;
}
*/
static void NativeCrypto_EVP_DigestInit(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jstring algorithm) {
// LOGI("NativeCrypto_EVP_DigestInit");
-
+
if (ctx == NULL || algorithm == NULL) {
jniThrowNullPointerException(env, NULL);
return;
}
-
+
const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
-
+
const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars));
env->ReleaseStringUTFChars(algorithm, algorithmChars);
-
+
if (digest == NULL) {
jniThrowRuntimeException(env, "Hash algorithm not found");
return;
}
-
+
EVP_DigestInit(ctx, digest);
-
+
throwExceptionIfNecessary(env);
}
*/
static jint NativeCrypto_EVP_DigestSize(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) {
// LOGI("NativeCrypto_EVP_DigestSize");
-
+
if (ctx == NULL) {
jniThrowNullPointerException(env, NULL);
return -1;
}
-
+
int result = EVP_MD_CTX_size(ctx);
-
+
throwExceptionIfNecessary(env);
-
+
return result;
}
*/
static jint NativeCrypto_EVP_DigestBlockSize(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) {
// LOGI("NativeCrypto_EVP_DigestBlockSize");
-
+
if (ctx == NULL) {
jniThrowNullPointerException(env, NULL);
return -1;
}
-
+
int result = EVP_MD_CTX_block_size(ctx);
-
+
throwExceptionIfNecessary(env);
-
+
return result;
}
*/
static void NativeCrypto_EVP_DigestUpdate(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length) {
// LOGI("NativeCrypto_EVP_DigestUpdate %x, %x, %d, %d", ctx, buffer, offset, length);
-
+
if (ctx == NULL || buffer == NULL) {
jniThrowNullPointerException(env, NULL);
return;
}
-
+
jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
EVP_DigestUpdate(ctx, (unsigned char*) (bufferBytes + offset), length);
env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
-
+
throwExceptionIfNecessary(env);
}
*/
static void NativeCrypto_EVP_VerifyInit(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jstring algorithm) {
// LOGI("NativeCrypto_EVP_VerifyInit");
-
+
if (ctx == NULL || algorithm == NULL) {
jniThrowNullPointerException(env, NULL);
return;
}
-
+
const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
-
+
const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars));
env->ReleaseStringUTFChars(algorithm, algorithmChars);
-
+
if (digest == NULL) {
jniThrowRuntimeException(env, "Hash algorithm not found");
return;
}
-
+
EVP_VerifyInit(ctx, digest);
-
+
throwExceptionIfNecessary(env);
}
*/
static void NativeCrypto_EVP_VerifyUpdate(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length) {
// LOGI("NativeCrypto_EVP_VerifyUpdate %x, %x, %d, %d", ctx, buffer, offset, length);
-
+
if (ctx == NULL || buffer == NULL) {
jniThrowNullPointerException(env, NULL);
return;
}
-
+
jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
EVP_VerifyUpdate(ctx, (unsigned char*) (bufferBytes + offset), length);
env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
-
+
throwExceptionIfNecessary(env);
}
*/
static int NativeCrypto_EVP_VerifyFinal(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length, EVP_PKEY* pkey) {
// LOGI("NativeCrypto_EVP_VerifyFinal %x, %x, %d, %d %x", ctx, buffer, offset, length, pkey);
-
+
if (ctx == NULL || buffer == NULL || pkey == NULL) {
jniThrowNullPointerException(env, NULL);
return -1;
}
-
+
jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
int result = EVP_VerifyFinal(ctx, (unsigned char*) (bufferBytes + offset), length, pkey);
env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
-
- throwExceptionIfNecessary(env);
-
- return result;
-}
-/*
- * Defines the mapping from Java methods and their signatures
- * to native functions. Order is (1) Java name, (2) signature,
- * (3) pointer to C function.
- */
-static JNINativeMethod sNativeCryptoMethods[] = {
- { "EVP_PKEY_new_DSA", "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_DSA },
- { "EVP_PKEY_new_RSA", "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_RSA },
- { "EVP_PKEY_free", "(I)V", (void*)NativeCrypto_EVP_PKEY_free },
- { "EVP_new", "()I", (void*)NativeCrypto_EVP_new },
- { "EVP_free", "(I)V", (void*)NativeCrypto_EVP_free },
- { "EVP_DigestFinal", "(I[BI)I", (void*)NativeCrypto_EVP_DigestFinal },
- { "EVP_DigestInit", "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_DigestInit },
- { "EVP_DigestBlockSize", "(I)I", (void*)NativeCrypto_EVP_DigestBlockSize },
- { "EVP_DigestSize", "(I)I", (void*)NativeCrypto_EVP_DigestSize },
- { "EVP_DigestUpdate", "(I[BII)V", (void*)NativeCrypto_EVP_DigestUpdate },
- { "EVP_VerifyInit", "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_VerifyInit },
- { "EVP_VerifyUpdate", "(I[BII)V", (void*)NativeCrypto_EVP_VerifyUpdate },
- { "EVP_VerifyFinal", "(I[BIII)I", (void*)NativeCrypto_EVP_VerifyFinal }
-};
-
-/**
- * Module scope variables initialized during JNI registration.
- */
-static jfieldID field_Socket_ssl_ctx;
-static jfieldID field_Socket_ssl;
-static jfieldID field_FileDescriptor_descriptor;
-static jfieldID field_Socket_mImpl;
-static jfieldID field_Socket_mFD;
-static jfieldID field_Socket_timeout;
-
-/**
- * Gets the chars of a String object as a '\0'-terminated UTF-8 string,
- * stored in a freshly-allocated BIO memory buffer.
- */
-static BIO *stringToMemBuf(JNIEnv* env, jstring string) {
- jsize byteCount = env->GetStringUTFLength(string);
- LocalArray<1024> buf(byteCount + 1);
- env->GetStringUTFRegion(string, 0, env->GetStringLength(string), &buf[0]);
+ throwExceptionIfNecessary(env);
- BIO* result = BIO_new(BIO_s_mem());
- BIO_puts(result, &buf[0]);
return result;
}
/**
- * Throws an SocketTimeoutException with the given string as a message.
+ * Convert ssl version constant to string. Based on SSL_get_version
*/
-static void throwSocketTimeoutException(JNIEnv* env, const char* message) {
- if (jniThrowException(env, "java/net/SocketTimeoutException", message)) {
- LOGE("Unable to throw");
+static const char* get_ssl_version(int ssl_version) {
+ switch (ssl_version) {
+ // newest to oldest
+ case TLS1_VERSION: {
+ return SSL_TXT_TLSV1;
+ }
+ case SSL3_VERSION: {
+ return SSL_TXT_SSLV3;
+ }
+ case SSL2_VERSION: {
+ return SSL_TXT_SSLV2;
+ }
+ default: {
+ return "unknown";
+ }
}
}
/**
- * Throws a java.io.IOException with the given string as a message.
+ * Convert content type constant to string.
*/
-static void throwIOExceptionStr(JNIEnv* env, const char* message) {
- if (jniThrowException(env, "java/io/IOException", message)) {
- LOGE("Unable to throw");
+static const char* get_content_type(int content_type) {
+ switch (content_type) {
+ case SSL3_RT_CHANGE_CIPHER_SPEC: {
+ return "SSL3_RT_CHANGE_CIPHER_SPEC";
+ }
+ case SSL3_RT_ALERT: {
+ return "SSL3_RT_ALERT";
+ }
+ case SSL3_RT_HANDSHAKE: {
+ return "SSL3_RT_HANDSHAKE";
+ }
+ case SSL3_RT_APPLICATION_DATA: {
+ return "SSL3_RT_APPLICATION_DATA";
+ }
+ default: {
+ LOGD("Unknown TLS/SSL content type %d", content_type);
+ return "<unknown>";
+ }
}
}
/**
- * Throws an IOException with a message constructed from the current
- * SSL errors. This will also log the errors.
- *
- * @param env the JNI environment
- * @param sslReturnCode return code from failing SSL function
- * @param sslErrorCode error code returned from SSL_get_error()
- * @param message null-ok; general error message
+ * Simple logging call back to show hand shake messages
*/
-static void throwIOExceptionWithSslErrors(JNIEnv* env, int sslReturnCode,
- int sslErrorCode, const char* message) {
- const char* messageStr = NULL;
- char* str;
- int ret;
-
- // First consult the SSL error code for the general message.
- switch (sslErrorCode) {
- case SSL_ERROR_NONE:
- messageStr = "Ok";
- break;
- case SSL_ERROR_SSL:
- messageStr = "Failure in SSL library, usually a protocol error";
- break;
- case SSL_ERROR_WANT_READ:
- messageStr = "SSL_ERROR_WANT_READ occured. You should never see this.";
- break;
- case SSL_ERROR_WANT_WRITE:
- messageStr = "SSL_ERROR_WANT_WRITE occured. You should never see this.";
- break;
- case SSL_ERROR_WANT_X509_LOOKUP:
- messageStr = "SSL_ERROR_WANT_X509_LOOKUP occured. You should never see this.";
- break;
- case SSL_ERROR_SYSCALL:
- messageStr = "I/O error during system call";
- break;
- case SSL_ERROR_ZERO_RETURN:
- messageStr = "SSL_ERROR_ZERO_RETURN occured. You should never see this.";
- break;
- case SSL_ERROR_WANT_CONNECT:
- messageStr = "SSL_ERROR_WANT_CONNECT occured. You should never see this.";
- break;
- case SSL_ERROR_WANT_ACCEPT:
- messageStr = "SSL_ERROR_WANT_ACCEPT occured. You should never see this.";
- break;
- default:
- messageStr = "Unknown SSL error";
- }
-
- // Prepend either our explicit message or a default one.
- if (asprintf(&str, "%s: %s",
- (message != NULL) ? message : "SSL error", messageStr) == 0) {
- throwIOExceptionStr(env, messageStr);
- LOGV("%s", messageStr);
- freeSslErrorState();
- return;
- }
-
- char* allocStr = str;
-
- // For SSL protocol errors, SSL might have more information.
- if (sslErrorCode == SSL_ERROR_SSL) {
- // Append each error as an additional line to the message.
- for (;;) {
- char errStr[256];
- const char* file;
- int line;
- const char* data;
- int flags;
- unsigned long err =
- ERR_get_error_line_data(&file, &line, &data, &flags);
- if (err == 0) {
- break;
- }
+static void ssl_msg_callback_LOG(int write_p, int ssl_version, int content_type,
+ const void *buf, size_t len, SSL* ssl, void* arg) {
+ LOGD("SSL %p %s %s %s %p %d %p",
+ ssl,
+ (write_p) ? "send" : "recv",
+ get_ssl_version(ssl_version),
+ get_content_type(content_type),
+ buf,
+ len,
+ arg);
+}
- ERR_error_string_n(err, errStr, sizeof(errStr));
+/*
+ * public static native int SSL_CTX_new();
+ */
+static int NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass clazz) {
+ SSL_CTX* sslCtx = SSL_CTX_new(SSLv23_method());
+ // Note: We explicitly do not allow SSLv2 to be used.
+ SSL_CTX_set_options(sslCtx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
- ret = asprintf(&str, "%s\n%s (%s:%d %p:0x%08x)",
- (allocStr == NULL) ? "" : allocStr,
- errStr,
- file,
- line,
- data,
- flags);
+ int mode = SSL_CTX_get_mode(sslCtx);
+ /*
+ * Turn on "partial write" mode. This means that SSL_write() will
+ * behave like Posix write() and possibly return after only
+ * writing a partial buffer. Note: The alternative, perhaps
+ * surprisingly, is not that SSL_write() always does full writes
+ * but that it will force you to retry write calls having
+ * preserved the full state of the original call. (This is icky
+ * and undesirable.)
+ */
+ mode |= SSL_MODE_ENABLE_PARTIAL_WRITE;
+#if defined(SSL_MODE_SMALL_BUFFERS) /* not all SSL versions have this */
+ mode |= SSL_MODE_SMALL_BUFFERS; /* lazily allocate record buffers; usually saves
+ * 44k over the default */
+#endif
+#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) /* not all SSL versions have this */
+ mode |= SSL_MODE_HANDSHAKE_CUTTHROUGH; /* enable sending of client data as soon as
+ * ClientCCS and ClientFinished are sent */
+#endif
+ SSL_CTX_set_mode(sslCtx, mode);
- if (ret < 0) {
- break;
- }
+ // SSL_CTX_set_msg_callback(sslCtx, ssl_msg_callback_LOG); /* enable for handshake debug */
+ return (jint) sslCtx;
+}
- free(allocStr);
- allocStr = str;
- }
- // For errors during system calls, errno might be our friend.
- } else if (sslErrorCode == SSL_ERROR_SYSCALL) {
- if (asprintf(&str, "%s, %s", allocStr, strerror(errno)) >= 0) {
- free(allocStr);
- allocStr = str;
- }
- // If the error code is invalid, print it.
- } else if (sslErrorCode > SSL_ERROR_WANT_ACCEPT) {
- if (asprintf(&str, ", error code is %d", sslErrorCode) >= 0) {
- free(allocStr);
- allocStr = str;
- }
+static jobjectArray makeCipherList(JNIEnv* env, STACK_OF(SSL_CIPHER)* cipher_list) {
+ // Create a String[].
+ jclass stringClass = env->FindClass("java/lang/String");
+ if (stringClass == NULL) {
+ return NULL;
+ }
+ int cipherCount = sk_SSL_CIPHER_num(cipher_list);
+ jobjectArray array = env->NewObjectArray(cipherCount, stringClass, NULL);
+ if (array == NULL) {
+ return NULL;
}
- throwIOExceptionStr(env, allocStr);
-
- LOGV("%s", allocStr);
- free(allocStr);
- freeSslErrorState();
+ // Fill in the cipher names.
+ for (int i = 0; i < cipherCount; ++i) {
+ const char* c = sk_SSL_CIPHER_value(cipher_list, i)->name;
+ env->SetObjectArrayElement(array, i, env->NewStringUTF(c));
+ }
+ return array;
}
/**
- * Helper function that grabs the ssl pointer out of the given object.
- * If this function returns NULL and <code>throwIfNull</code> is
- * passed as <code>true</code>, then this function will call
- * <code>throwIOExceptionStr</code> before returning, so in this case of
- * NULL, a caller of this function should simply return and allow JNI
- * to do its thing.
- *
- * @param env non-null; the JNI environment
- * @param obj non-null; socket object
- * @param throwIfNull whether to throw if the SSL pointer is NULL
- * @returns the pointer, which may be NULL
+ * Loads the ciphers suites that are supported by an SSL_CTX
+ * and returns them in a string array.
*/
-static SSL *getSslPointer(JNIEnv* env, jobject obj, bool throwIfNull) {
- SSL *ssl = (SSL *)env->GetIntField(obj, field_Socket_ssl);
-
- if ((ssl == NULL) && throwIfNull) {
- throwIOExceptionStr(env, "null SSL pointer");
+static jobjectArray NativeCrypto_SSL_CTX_get_ciphers(JNIEnv* env,
+ jclass, jint ssl_ctx_address)
+{
+ SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+ if (ssl_ctx == NULL) {
+ jniThrowNullPointerException(env, "SSL_CTX is null");
+ return NULL;
}
-
- return ssl;
+ return makeCipherList(env, ssl_ctx->cipher_list);
}
-// ============================================================================
-// === OpenSSL-related helper stuff begins here. ==============================
-// ============================================================================
-
/**
- * OpenSSL locking support. Taken from the O'Reilly book by Viega et al., but I
- * suppose there are not many other ways to do this on a Linux system (modulo
- * isomorphism).
+ * public static native void SSL_CTX_free(int ssl_ctx)
*/
-#define MUTEX_TYPE pthread_mutex_t
-#define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL)
-#define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
-#define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
-#define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
-#define THREAD_ID pthread_self()
-#define THROW_EXCEPTION (-2)
-#define THROW_SOCKETTIMEOUTEXCEPTION (-3)
-
-static MUTEX_TYPE *mutex_buf = NULL;
-
-static void locking_function(int mode, int n, const char * file, int line) {
- if (mode & CRYPTO_LOCK) {
- MUTEX_LOCK(mutex_buf[n]);
- } else {
- MUTEX_UNLOCK(mutex_buf[n]);
+static void NativeCrypto_SSL_CTX_free(JNIEnv* env,
+ jclass, jint ssl_ctx_address)
+{
+ SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+ if (ssl_ctx == NULL) {
+ jniThrowNullPointerException(env, "SSL_CTX is null");
+ return;
}
+ SSL_CTX_free(ssl_ctx);
}
-static unsigned long id_function(void) {
- return ((unsigned long)THREAD_ID);
-}
-
-int THREAD_setup(void) {
- int i;
+/**
+ * Gets the chars of a String object as a '\0'-terminated UTF-8 string,
+ * stored in a freshly-allocated BIO memory buffer.
+ */
+static BIO *stringToMemBuf(JNIEnv* env, jstring string) {
+ jsize byteCount = env->GetStringUTFLength(string);
+ LocalArray<1024> buf(byteCount + 1);
+ env->GetStringUTFRegion(string, 0, env->GetStringLength(string), &buf[0]);
- mutex_buf = (MUTEX_TYPE *)malloc(CRYPTO_num_locks( ) * sizeof(MUTEX_TYPE));
+ BIO* result = BIO_new(BIO_s_mem());
+ BIO_puts(result, &buf[0]);
+ return result;
+}
- if(!mutex_buf) {
+/**
+ * public static native int SSL_new(int ssl_ctx, String privatekey, String certificate, byte[] seed) throws IOException;
+ */
+static jint NativeCrypto_SSL_new(JNIEnv* env, jclass,
+ jint ssl_ctx_address, jstring privatekey, jstring certificates, jbyteArray seed)
+{
+ SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+ if (ssl_ctx == NULL) {
+ jniThrowNullPointerException(env, "SSL_CTX is null");
return 0;
}
- for (i = 0; i < CRYPTO_num_locks( ); i++) {
- MUTEX_SETUP(mutex_buf[i]);
+ // 'seed == null' when no SecureRandom Object is set
+ // in the SSLContext.
+ if (seed != NULL) {
+ jbyte* randseed = env->GetByteArrayElements(seed, NULL);
+ RAND_seed((unsigned char*) randseed, 1024);
+ env->ReleaseByteArrayElements(seed, randseed, 0);
+ } else {
+ RAND_load_file("/dev/urandom", 1024);
}
- CRYPTO_set_id_callback(id_function);
- CRYPTO_set_locking_callback(locking_function);
+ SSL* ssl = SSL_new(ssl_ctx);
+ if (ssl == NULL) {
+ throwIOExceptionWithSslErrors(env, 0, 0,
+ "Unable to create SSL structure");
+ return NULL;
+ }
- return 1;
-}
+ /* Java code in class OpenSSLSocketImpl does the verification. Meaning of
+ * SSL_VERIFY_NONE flag in client mode: if not using an anonymous cipher
+ * (by default disabled), the server will send a certificate which will
+ * be checked. The result of the certificate verification process can be
+ * checked after the TLS/SSL handshake using the SSL_get_verify_result(3)
+ * function. The handshake will be continued regardless of the
+ * verification result.
+ */
+ SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
-int THREAD_cleanup(void) {
- int i;
+ if (privatekey != NULL) {
+ BIO* privatekeybio = stringToMemBuf(env, (jstring) privatekey);
+ EVP_PKEY* privatekeyevp =
+ PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL);
+ BIO_free(privatekeybio);
- if (!mutex_buf) {
- return 0;
- }
+ if (privatekeyevp == NULL) {
+ LOGE(ERR_error_string(ERR_get_error(), NULL));
+ throwIOExceptionWithSslErrors(env, 0, 0,
+ "Error parsing the private key");
+ SSL_free(ssl);
+ return NULL;
+ }
- CRYPTO_set_id_callback(NULL);
- CRYPTO_set_locking_callback(NULL);
+ BIO* certificatesbio = stringToMemBuf(env, (jstring) certificates);
+ X509* certificatesx509 =
+ PEM_read_bio_X509(certificatesbio, NULL, 0, NULL);
+ BIO_free(certificatesbio);
- for (i = 0; i < CRYPTO_num_locks( ); i++) {
- MUTEX_CLEANUP(mutex_buf[i]);
- }
+ if (certificatesx509 == NULL) {
+ LOGE(ERR_error_string(ERR_get_error(), NULL));
+ throwIOExceptionWithSslErrors(env, 0, 0,
+ "Error parsing the certificates");
+ EVP_PKEY_free(privatekeyevp);
+ SSL_free(ssl);
+ return NULL;
+ }
- free(mutex_buf);
- mutex_buf = NULL;
+ int ret = SSL_use_certificate(ssl, certificatesx509);
+ if (ret != 1) {
+ LOGE(ERR_error_string(ERR_get_error(), NULL));
+ throwIOExceptionWithSslErrors(env, ret, 0,
+ "Error setting the certificates");
+ X509_free(certificatesx509);
+ EVP_PKEY_free(privatekeyevp);
+ SSL_free(ssl);
+ return NULL;
+ }
- return 1;
-}
+ ret = SSL_use_PrivateKey(ssl, privatekeyevp);
+ if (ret != 1) {
+ LOGE(ERR_error_string(ERR_get_error(), NULL));
+ throwIOExceptionWithSslErrors(env, ret, 0,
+ "Error setting the private key");
+ X509_free(certificatesx509);
+ EVP_PKEY_free(privatekeyevp);
+ SSL_free(ssl);
+ return NULL;
+ }
-int get_socket_timeout(int type, int sd) {
- struct timeval tv;
- socklen_t len = sizeof(tv);
- if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
- LOGE("getsockopt(%d, SOL_SOCKET): %s (%d)",
- sd,
- strerror(errno),
- errno);
- return 0;
+ ret = SSL_check_private_key(ssl);
+ if (ret != 1) {
+ throwIOExceptionWithSslErrors(env, ret, 0,
+ "Error checking the private key");
+ X509_free(certificatesx509);
+ EVP_PKEY_free(privatekeyevp);
+ SSL_free(ssl);
+ return NULL;
+ }
}
- // LOGI("Current socket timeout (%d(s), %d(us))!",
- // (int)tv.tv_sec, (int)tv.tv_usec);
- int timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
- return timeout;
+ return (jint)ssl;
}
-#ifdef TIMEOUT_DEBUG_SSL
+/**
+ * public static native long SSL_get_options(int ssl);
+ */
+static jlong NativeCrypto_SSL_get_options(JNIEnv* env, jclass,
+ jint ssl_address) {
+ SSL* ssl = getSslPointer(env, ssl_address, true);
+ if (ssl == NULL) {
+ return 0;
+ }
+ return SSL_get_options(ssl);
+}
-void print_socket_timeout(const char* name, int type, int sd) {
- struct timeval tv;
- int len = sizeof(tv);
- if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
- LOGE("getsockopt(%d, SOL_SOCKET, %s): %s (%d)",
- sd,
- name,
- strerror(errno),
- errno);
+/**
+ * public static native long SSL_set_options(int ssl, long options);
+ */
+static jlong NativeCrypto_SSL_set_options(JNIEnv* env, jclass,
+ jint ssl_address, jlong options) {
+ SSL* ssl = getSslPointer(env, ssl_address, true);
+ if (ssl == NULL) {
+ return 0 ;
}
- LOGI("Current socket %s is (%d(s), %d(us))!",
- name, (int)tv.tv_sec, (int)tv.tv_usec);
+ return SSL_set_options(ssl, options);
}
-void print_timeout(const char* method, SSL* ssl) {
- LOGI("SSL_get_default_timeout %d in %s", SSL_get_default_timeout(ssl), method);
- int fd = SSL_get_fd(ssl);
- print_socket_timeout("SO_RCVTIMEO", SO_RCVTIMEO, fd);
- print_socket_timeout("SO_SNDTIMEO", SO_SNDTIMEO, fd);
+/**
+ * Loads the ciphers suites that are enabled in the SSL
+ * and returns them in a string array.
+ */
+static jobjectArray NativeCrypto_SSL_get_ciphers(JNIEnv* env,
+ jclass, jint ssl_address)
+{
+ SSL* ssl = getSslPointer(env, ssl_address, true);
+ if (ssl == NULL) {
+ return NULL;
+ }
+ return makeCipherList(env, SSL_get_ciphers(ssl));
}
-#endif
+/**
+ * Sets the ciphers suites that are enabled in the SSL
+ */
+static void NativeCrypto_SSL_set_cipher_list(JNIEnv* env, jclass,
+ jint ssl_address, jstring controlString)
+{
+ SSL* ssl = getSslPointer(env, ssl_address, true);
+ if (ssl == NULL) {
+ return;
+ }
+ const char* str = env->GetStringUTFChars(controlString, NULL);
+ int rc = SSL_set_cipher_list(ssl, str);
+ env->ReleaseStringUTFChars(controlString, str);
+ if (rc == 0) {
+ freeSslErrorState();
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Illegal cipher suite strings.");
+ }
+}
/**
* Our additional application data needed for getting synchronization right.
* This maybe warrants a bit of lengthy prose:
- *
+ *
* (1) We use a flag to reflect whether we consider the SSL connection alive.
* Any read or write attempt loops will be cancelled once this flag becomes 0.
- *
+ *
* (2) We use an int to count the number of threads that are blocked by the
* underlying socket. This may be at most two (one reader and one writer), since
* the Java layer ensures that no more threads will enter the native code at the
* same time.
- *
+ *
* (3) The pipe is used primarily as a means of cancelling a blocking select()
* when we want to close the connection (aka "emergency button"). It is also
* necessary for dealing with a possible race condition situation: There might
* If one leaves the select() successfully before the other enters it, the
* "success" event is already consumed and the second thread will be blocked,
* possibly forever (depending on network conditions).
- *
+ *
* The idea for solving the problem looks like this: Whenever a thread is
* successful in moving around data on the network, and it knows there is
* another thread stuck in a select(), it will write a byte to the pipe, waking
* up the other thread. A thread that returned from select(), on the other hand,
* knows whether it's been woken up by the pipe. If so, it will consume the
* byte, and the original state of affairs has been restored.
- *
+ *
* The pipe may seem like a bit of overhead, but it fits in nicely with the
* other file descriptors of the select(), so there's only one condition to wait
* for.
- *
+ *
* (4) Finally, a mutex is needed to make sure that at most one thread is in
* either SSL_read() or SSL_write() at any given time. This is an OpenSSL
* requirement. We use the same mutex to guard the field for counting the
* waiting threads.
- *
+ *
* Note: The current implementation assumes that we don't have to deal with
* problems induced by multiple cores or processors and their respective
* memory caches. One possible problem is that of inconsistent views on the
* currently this seems a bit like overkill.
*/
typedef struct app_data {
- int aliveAndKicking;
+ volatile int aliveAndKicking;
int waitingThreads;
int fdsEmergency[2];
MUTEX_TYPE mutex;
/**
* Creates our application data and attaches it to a given SSL connection.
- *
+ *
* @param ssl The SSL connection to attach the data to.
* @return 0 on success, -1 on failure.
*/
/**
* Destroys our application data, cleaning up everything in the process.
- *
+ *
* @param ssl The SSL connection to take the data from.
- */
+ */
static void sslDestroyAppData(SSL* ssl) {
APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
if (data != NULL) {
SSL_set_app_data(ssl, NULL);
- data -> aliveAndKicking = 0;
+ data->aliveAndKicking = 0;
if (data->fdsEmergency[0] != -1) {
close(data->fdsEmergency[0]);
}
}
-
/**
- * Frees the SSL_CTX struct for the given instance.
+ * public static native void SSL_free(int ssl);
*/
-static void free_ssl_ctx(JNIEnv* env, jobject object) {
- /*
- * Preserve and restore the exception state around this call, so
- * that GetIntField and SetIntField will operate without complaint.
- */
- jthrowable exception = env->ExceptionOccurred();
-
- if (exception != NULL) {
- env->ExceptionClear();
- }
-
- SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_Socket_ssl_ctx);
-
- if (ctx != NULL) {
- SSL_CTX_free(ctx);
- env->SetIntField(object, field_Socket_ssl_ctx, (int) NULL);
- }
-
- if (exception != NULL) {
- env->Throw(exception);
+static void NativeCrypto_SSL_free(JNIEnv* env, jclass, jint ssl_address)
+{
+ SSL* ssl = getSslPointer(env, ssl_address, true);
+ if (ssl == NULL) {
+ return;
}
+ sslDestroyAppData(ssl);
+ SSL_free(ssl);
}
-/**
- * Frees the SSL struct for the given instance.
+/*
+ * Defines the mapping from Java methods and their signatures
+ * to native functions. Order is (1) Java name, (2) signature,
+ * (3) pointer to C function.
*/
-static void free_ssl(JNIEnv* env, jobject object) {
- /*
- * Preserve and restore the exception state around this call, so
- * that GetIntField and SetIntField will operate without complaint.
- */
- jthrowable exception = env->ExceptionOccurred();
+static JNINativeMethod sNativeCryptoMethods[] = {
+ { "clinit", "()V", (void*)NativeCrypto_clinit},
+ { "EVP_PKEY_new_DSA", "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_DSA },
+ { "EVP_PKEY_new_RSA", "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_RSA },
+ { "EVP_PKEY_free", "(I)V", (void*)NativeCrypto_EVP_PKEY_free },
+ { "EVP_new", "()I", (void*)NativeCrypto_EVP_new },
+ { "EVP_free", "(I)V", (void*)NativeCrypto_EVP_free },
+ { "EVP_DigestFinal", "(I[BI)I", (void*)NativeCrypto_EVP_DigestFinal },
+ { "EVP_DigestInit", "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_DigestInit },
+ { "EVP_DigestBlockSize", "(I)I", (void*)NativeCrypto_EVP_DigestBlockSize },
+ { "EVP_DigestSize", "(I)I", (void*)NativeCrypto_EVP_DigestSize },
+ { "EVP_DigestUpdate", "(I[BII)V", (void*)NativeCrypto_EVP_DigestUpdate },
+ { "EVP_VerifyInit", "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_VerifyInit },
+ { "EVP_VerifyUpdate", "(I[BII)V", (void*)NativeCrypto_EVP_VerifyUpdate },
+ { "EVP_VerifyFinal", "(I[BIII)I", (void*)NativeCrypto_EVP_VerifyFinal },
+ { "SSL_CTX_new", "()I", (void*)NativeCrypto_SSL_CTX_new },
+ { "SSL_CTX_get_ciphers", "(I)[Ljava/lang/String;", (void*)NativeCrypto_SSL_CTX_get_ciphers},
+ { "SSL_CTX_free", "(I)V", (void*)NativeCrypto_SSL_CTX_free },
+ { "SSL_new", "(ILjava/lang/String;Ljava/lang/String;[B)I", (void*)NativeCrypto_SSL_new},
+ { "SSL_get_options", "(I)J", (void*)NativeCrypto_SSL_get_options },
+ { "SSL_set_options", "(IJ)J", (void*)NativeCrypto_SSL_set_options },
+ { "SSL_get_ciphers", "(I)[Ljava/lang/String;", (void*)NativeCrypto_SSL_get_ciphers},
+ { "SSL_set_cipher_list", "(ILjava/lang/String;)V", (void*)NativeCrypto_SSL_set_cipher_list},
+ { "SSL_free", "(I)V", (void*)NativeCrypto_SSL_free},
+};
- if (exception != NULL) {
- env->ExceptionClear();
- }
+/**
+ * Module scope variables initialized during JNI registration.
+ */
+static jfieldID field_Socket_mImpl;
+static jfieldID field_Socket_mFD;
- SSL *ssl = (SSL *)env->GetIntField(object, field_Socket_ssl);
+// ============================================================================
+// === OpenSSL-related helper stuff begins here. ==============================
+// ============================================================================
- if (ssl != NULL) {
- sslDestroyAppData(ssl);
- SSL_free(ssl);
- env->SetIntField(object, field_Socket_ssl, (int) NULL);
+int get_socket_timeout(int type, int sd) {
+ struct timeval tv;
+ socklen_t len = sizeof(tv);
+ if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
+ LOGE("getsockopt(%d, SOL_SOCKET): %s (%d)",
+ sd,
+ strerror(errno),
+ errno);
+ return 0;
}
+ // LOGI("Current socket timeout (%d(s), %d(us))!",
+ // (int)tv.tv_sec, (int)tv.tv_usec);
+ int timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+ return timeout;
+}
- if (exception != NULL) {
- env->Throw(exception);
+#ifdef TIMEOUT_DEBUG_SSL
+
+void print_socket_timeout(const char* name, int type, int sd) {
+ struct timeval tv;
+ int len = sizeof(tv);
+ if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
+ LOGE("getsockopt(%d, SOL_SOCKET, %s): %s (%d)",
+ sd,
+ name,
+ strerror(errno),
+ errno);
}
+ LOGI("Current socket %s is (%d(s), %d(us))!",
+ name, (int)tv.tv_sec, (int)tv.tv_usec);
}
-/**
- * Constructs the SSL struct for the given instance, replacing one
- * that was already made, if any.
- */
-static SSL* create_ssl(JNIEnv* env, jobject object, SSL_CTX* ssl_ctx) {
- free_ssl(env, object);
-
- SSL *ssl = SSL_new(ssl_ctx);
- env->SetIntField(object, field_Socket_ssl, (int) ssl);
- return ssl;
+void print_timeout(const char* method, SSL* ssl) {
+ LOGI("SSL_get_default_timeout %d in %s", SSL_get_default_timeout(ssl), method);
+ int fd = SSL_get_fd(ssl);
+ print_socket_timeout("SO_RCVTIMEO", SO_RCVTIMEO, fd);
+ print_socket_timeout("SO_SNDTIMEO", SO_SNDTIMEO, fd);
}
+#endif
+
/**
* Dark magic helper function that checks, for a given SSL session, whether it
* can SSL_read() or SSL_write() without blocking. Takes into account any
* specifies whether we are waiting for readability or writability. It expects
* to be passed either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, since we
* only need to wait in case one of these problems occurs.
- *
+ *
* @param type Either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE
* @param fd The file descriptor to wait for (the underlying socket)
- * @param data The application data structure with mutex info etc.
+ * @param data The application data structure with mutex info etc.
* @param timeout The timeout value for select call, with the special value
* 0 meaning no timeout at all (wait indefinitely). Note: This is
* the Java semantics of the timeout value, not the usual
* select() semantics.
- * @return The result of the inner select() call, -1 on additional errors
+ * @return The result of the inner select() call, -1 on additional errors
*/
static int sslSelect(int type, int fd, APP_DATA *data, int timeout) {
fd_set rfds;
} else {
ptv = NULL;
}
-
+
// LOGD("Doing select() for SSL_ERROR_WANT_%s...", type == SSL_ERROR_WANT_READ ? "READ" : "WRITE");
int result = select(max + 1, &rfds, &wfds, NULL, ptv);
// LOGD("Returned from select(), result is %d", result);
-
+
// Lock
if (MUTEX_LOCK(data->mutex) == -1) {
return -1;
}
-
+
// If we have been woken up by the emergency pipe, there must be a token in
// it. Thus we can safely read it (even in a blocking way).
if (FD_ISSET(data->fdsEmergency[0], &rfds)) {
// Tell the world that there is now one thread less waiting for the
// underlying network.
data->waitingThreads--;
-
+
// Unlock
MUTEX_UNLOCK(data->mutex);
// LOGD("leave sslSelect");
* Helper function that wakes up a thread blocked in select(), in case there is
* one. Is being called by sslRead() and sslWrite() as well as by JNI glue
* before closing the connection.
- *
- * @param data The application data structure with mutex info etc.
+ *
+ * @param data The application data structure with mutex info etc.
*/
static void sslNotify(APP_DATA *data) {
// Write a byte to the emergency pipe, so a concurrent select() can return.
/**
* Helper function which does the actual reading. The Java layer guarantees that
* at most one thread will enter this function at any given time.
- *
+ *
* @param ssl non-null; the SSL context
* @param buf non-null; buffer to read into
* @param len length of the buffer, in bytes
int* sslErrorCode, int timeout) {
// LOGD("Entering sslRead, caller requests to read %d bytes...", len);
-
+
if (len == 0) {
// Don't bother doing anything in this case.
return 0;
int fd = SSL_get_fd(ssl);
BIO *bio = SSL_get_rbio(ssl);
-
+
APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
while (data->aliveAndKicking) {
}
unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
-
+
// LOGD("Doing SSL_Read()");
int result = SSL_read(ssl, buf, len);
int error = SSL_ERROR_NONE;
if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && data->waitingThreads > 0) {
sslNotify(data);
}
-
+
// If we are blocked by the underlying socket, tell the world that
// there will be one more waiting thread now.
if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
data->waitingThreads++;
}
-
+
// Unlock
MUTEX_UNLOCK(data->mutex);
return -1;
}
- // Need to wait for availability of underlying layer, then retry.
+ // Need to wait for availability of underlying layer, then retry.
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE: {
int selectResult = sslSelect(error, fd, data, timeout);
} else if (selectResult == 0) {
return THROW_SOCKETTIMEOUTEXCEPTION;
}
-
+
break;
}
if (result == 0) {
return -1;
}
-
+
// System call has been interrupted. Simply retry.
if (errno == EINTR) {
break;
}
-
+
// Note that for all other system call errors we fall through
- // to the default case, which results in an Exception.
+ // to the default case, which results in an Exception.
}
-
+
// Everything else is basically an error.
default: {
*sslReturnCode = result;
}
}
}
-
+
return -1;
}
/**
* Helper function which does the actual writing. The Java layer guarantees that
* at most one thread will enter this function at any given time.
- *
+ *
* @param ssl non-null; the SSL context
* @param buf non-null; buffer to write
* @param len length of the buffer, in bytes
*/
static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode,
int* sslErrorCode) {
-
+
// LOGD("Entering sslWrite(), caller requests to write %d bytes...", len);
if (len == 0) {
// Don't bother doing anything in this case.
return 0;
}
-
+
int fd = SSL_get_fd(ssl);
BIO *bio = SSL_get_wbio(ssl);
-
+
APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
-
+
int count = len;
-
- while(data->aliveAndKicking && len > 0) {
+
+ while (data->aliveAndKicking && len > 0) {
errno = 0;
if (MUTEX_LOCK(data->mutex) == -1) {
return -1;
}
-
+
unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
-
+
// LOGD("Doing SSL_write() with %d bytes to go", len);
int result = SSL_write(ssl, buf, len);
int error = SSL_ERROR_NONE;
if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && data->waitingThreads > 0) {
sslNotify(data);
}
-
+
// If we are blocked by the underlying socket, tell the world that
// there will be one more waiting thread now.
if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
data->waitingThreads++;
}
-
+
MUTEX_UNLOCK(data->mutex);
-
+
switch (error) {
// Sucessfully write at least one byte.
case SSL_ERROR_NONE: {
case SSL_ERROR_ZERO_RETURN: {
return -1;
}
-
+
// Need to wait for availability of underlying layer, then retry.
// The concept of a write timeout doesn't really make sense, and
// it's also not standard Java behavior, so we wait forever here.
} else if (selectResult == 0) {
return THROW_SOCKETTIMEOUTEXCEPTION;
}
-
+
break;
}
if (result == 0) {
return -1;
}
-
+
// System call has been interrupted. Simply retry.
if (errno == EINTR) {
break;
}
-
+
// Note that for all other system call errors we fall through
- // to the default case, which results in an Exception.
+ // to the default case, which results in an Exception.
}
-
+
// Everything else is basically an error.
default: {
*sslReturnCode = result;
}
}
// LOGD("Successfully wrote %d bytes", count);
-
+
return count;
}
/**
* Helper function that creates an RSA public key from two buffers containing
* the big-endian bit representation of the modulus and the public exponent.
- *
+ *
* @param mod The data of the modulus
* @param modLen The length of the modulus data
* @param exp The data of the exponent
* @param expLen The length of the exponent data
- *
- * @return A pointer to the new RSA structure, or NULL on error
+ *
+ * @return A pointer to the new RSA structure, or NULL on error
*/
static RSA* rsaCreateKey(unsigned char* mod, int modLen, unsigned char* exp, int expLen) {
// LOGD("Entering rsaCreateKey()");
/**
* Helper function that frees an RSA key. Just calls the corresponding OpenSSL
* function.
- *
+ *
* @param rsa The pointer to the new RSA structure to free.
*/
static void rsaFreeKey(RSA* rsa) {
/**
* Helper function that verifies a given RSA signature for a given message.
- *
+ *
* @param msg The message to verify
* @param msgLen The length of the message
* @param sig The signature to verify
* @param sigLen The length of the signature
* @param algorithm The name of the hash/sign algorithm to use, e.g. "RSA-SHA1"
* @param rsa The RSA public key to use
- *
+ *
* @return 1 on success, 0 on failure, -1 on error (check SSL errors then)
- *
+ *
*/
static int rsaVerify(unsigned char* msg, unsigned int msgLen, unsigned char* sig,
unsigned int sigLen, char* algorithm, RSA* rsa) {
EVP_MD_CTX ctx;
- EVP_MD_CTX_init(&ctx);
+ EVP_MD_CTX_init(&ctx);
if (EVP_VerifyInit_ex(&ctx, type, NULL) == 0) {
goto cleanup;
}
EVP_VerifyUpdate(&ctx, msg, msgLen);
- result = EVP_VerifyFinal(&ctx, sig, sigLen, key);
- EVP_MD_CTX_cleanup(&ctx);
-
- cleanup:
-
- if (key != NULL) {
- EVP_PKEY_free(key);
- }
-
- return result;
-}
-
-// ============================================================================
-// === OpenSSL-related helper stuff ends here. JNI glue follows. ==============
-// ============================================================================
-
-/**
- * Initialization phase for every OpenSSL job: Loads the Error strings, the
- * crypto algorithms and reset the OpenSSL library
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_initstatic(JNIEnv* env, jobject obj)
-{
- SSL_load_error_strings();
- ERR_load_crypto_strings();
- SSL_library_init();
- OpenSSL_add_all_algorithms();
- THREAD_setup();
-}
-
-/**
- * Initialization phase for a socket with OpenSSL. The server's private key
- * and X509 certificate are read and the Linux /dev/urandom file is loaded
- * as RNG for the session keys.
- *
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init(JNIEnv* env, jobject object,
- jstring privatekey, jstring certificates, jbyteArray seed)
-{
- SSL_CTX* ssl_ctx;
-
- // 'seed == null' when no SecureRandom Object is set
- // in the SSLContext.
- if (seed != NULL) {
- jbyte* randseed = env->GetByteArrayElements(seed, NULL);
- RAND_seed((unsigned char*) randseed, 1024);
- env->ReleaseByteArrayElements(seed, randseed, 0);
- } else {
- RAND_load_file("/dev/urandom", 1024);
- }
-
- ssl_ctx = SSL_CTX_new(SSLv23_client_method());
-
- // Note: We explicitly do not allow SSLv2 to be used. It
- SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
-
- /* Java code in class OpenSSLSocketImpl does the verification. Meaning of
- * SSL_VERIFY_NONE flag in client mode: if not using an anonymous cipher
- * (by default disabled), the server will send a certificate which will
- * be checked. The result of the certificate verification process can be
- * checked after the TLS/SSL handshake using the SSL_get_verify_result(3)
- * function. The handshake will be continued regardless of the
- * verification result.
- */
- SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
-
- int mode = SSL_CTX_get_mode(ssl_ctx);
- /*
- * Turn on "partial write" mode. This means that SSL_write() will
- * behave like Posix write() and possibly return after only
- * writing a partial buffer. Note: The alternative, perhaps
- * surprisingly, is not that SSL_write() always does full writes
- * but that it will force you to retry write calls having
- * preserved the full state of the original call. (This is icky
- * and undesirable.)
- */
- mode |= SSL_MODE_ENABLE_PARTIAL_WRITE;
-#if defined(SSL_MODE_SMALL_BUFFERS) /* not all SSL versions have this */
- mode |= SSL_MODE_SMALL_BUFFERS; /* lazily allocate record buffers; usually saves
- * 44k over the default */
-#endif
-#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) /* not all SSL versions have this */
- mode |= SSL_MODE_HANDSHAKE_CUTTHROUGH; /* enable sending of client data as soon as
- * ClientCCS and ClientFinished are sent */
-#endif
-
- SSL_CTX_set_mode(ssl_ctx, mode);
-
- if (privatekey != NULL) {
- BIO* privatekeybio = stringToMemBuf(env, (jstring) privatekey);
- EVP_PKEY* privatekeyevp =
- PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL);
- BIO_free(privatekeybio);
-
- if (privatekeyevp == NULL) {
- throwIOExceptionWithSslErrors(env, 0, 0,
- "Error parsing the private key");
- SSL_CTX_free(ssl_ctx);
- return;
- }
-
- BIO* certificatesbio = stringToMemBuf(env, (jstring) certificates);
- X509* certificatesx509 =
- PEM_read_bio_X509(certificatesbio, NULL, 0, NULL);
- BIO_free(certificatesbio);
-
- if (certificatesx509 == NULL) {
- throwIOExceptionWithSslErrors(env, 0, 0,
- "Error parsing the certificates");
- EVP_PKEY_free(privatekeyevp);
- SSL_CTX_free(ssl_ctx);
- return;
- }
-
- int ret = SSL_CTX_use_certificate(ssl_ctx, certificatesx509);
- if (ret != 1) {
- throwIOExceptionWithSslErrors(env, ret, 0,
- "Error setting the certificates");
- X509_free(certificatesx509);
- EVP_PKEY_free(privatekeyevp);
- SSL_CTX_free(ssl_ctx);
- return;
- }
-
- ret = SSL_CTX_use_PrivateKey(ssl_ctx, privatekeyevp);
- if (ret != 1) {
- throwIOExceptionWithSslErrors(env, ret, 0,
- "Error setting the private key");
- X509_free(certificatesx509);
- EVP_PKEY_free(privatekeyevp);
- SSL_CTX_free(ssl_ctx);
- return;
- }
+ result = EVP_VerifyFinal(&ctx, sig, sigLen, key);
+ EVP_MD_CTX_cleanup(&ctx);
- ret = SSL_CTX_check_private_key(ssl_ctx);
- if (ret != 1) {
- throwIOExceptionWithSslErrors(env, ret, 0,
- "Error checking the private key");
- X509_free(certificatesx509);
- EVP_PKEY_free(privatekeyevp);
- SSL_CTX_free(ssl_ctx);
- return;
- }
+ cleanup:
+
+ if (key != NULL) {
+ EVP_PKEY_free(key);
}
- env->SetIntField(object, field_Socket_ssl_ctx, (int)ssl_ctx);
+ return result;
}
+// ============================================================================
+// === OpenSSL-related helper stuff ends here. JNI glue follows. ==============
+// ============================================================================
+
/**
* A connection within an OpenSSL context is established. (1) A new socket is
- * constructed, (2) the TLS/SSL handshake with a server is initiated.
+ * constructed, (2) the TLS/SSL handshake with a server is initiated.
*/
-static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect(JNIEnv* env, jobject object,
- jint ctx, jobject socketObject, jboolean client_mode, jint session)
+static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect(JNIEnv* env, jclass,
+ jint ssl_address, jobject socketObject, jint timeout, jboolean client_mode, jint ssl_session_address)
{
// LOGD("ENTER connect");
- int ret, fd;
- SSL_CTX* ssl_ctx;
- SSL* ssl;
- SSL_SESSION* ssl_session;
-
- ssl_ctx = (SSL_CTX*)env->GetIntField(object, field_Socket_ssl_ctx);
-
- ssl = create_ssl(env, object, ssl_ctx);
+ SSL* ssl = getSslPointer(env, ssl_address, true);
if (ssl == NULL) {
- throwIOExceptionWithSslErrors(env, 0, 0,
- "Unable to create SSL structure");
- free_ssl_ctx(env, object);
- return (jboolean) false;
+ return (jboolean) false;
}
+ SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
jobject socketImplObject = env->GetObjectField(socketObject, field_Socket_mImpl);
if (socketImplObject == NULL) {
- free_ssl(env, object);
- free_ssl_ctx(env, object);
throwIOExceptionStr(env,
"couldn't get the socket impl from the socket");
return (jboolean) false;
jobject fdObject = env->GetObjectField(socketImplObject, field_Socket_mFD);
if (fdObject == NULL) {
- free_ssl(env, object);
- free_ssl_ctx(env, object);
throwIOExceptionStr(env,
"couldn't get the file descriptor from the socket impl");
return (jboolean) false;
}
- fd = jniGetFDFromFileDescriptor(env, fdObject);
+ int fd = jniGetFDFromFileDescriptor(env, fdObject);
- ssl_session = (SSL_SESSION *) session;
-
- ret = SSL_set_fd(ssl, fd);
+ int ret = SSL_set_fd(ssl, fd);
if (ret != 1) {
throwIOExceptionWithSslErrors(env, ret, 0,
"Error setting the file descriptor");
- free_ssl(env, object);
- free_ssl_ctx(env, object);
+ SSL_clear(ssl);
return (jboolean) false;
}
if (ssl_session != NULL) {
+ // LOGD("Trying to reuse session %p", ssl_session);
ret = SSL_set_session(ssl, ssl_session);
-
if (ret != 1) {
/*
* Translate the error, and throw if it turns out to be a real
if (sslErrorCode != SSL_ERROR_ZERO_RETURN) {
throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
"SSL session set");
- free_ssl(env, object);
- free_ssl_ctx(env, object);
+ SSL_clear(ssl);
return (jboolean) false;
}
}
int mode = fcntl(fd, F_GETFL);
if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) {
throwIOExceptionStr(env, "Unable to make socket non blocking");
- free_ssl(env, object);
- free_ssl_ctx(env, object);
+ SSL_clear(ssl);
return (jboolean) false;
}
*/
if (sslCreateAppData(ssl) == -1) {
throwIOExceptionStr(env, "Unable to create application data");
- free_ssl(env, object);
- free_ssl_ctx(env, object);
+ SSL_clear(ssl);
// TODO
return (jboolean) false;
}
-
+
APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
- env->SetIntField(object, field_Socket_ssl, (int)ssl);
-
- int timeout = (int)env->GetIntField(object, field_Socket_timeout);
-
+
while (data->aliveAndKicking) {
- errno = 0;
+ errno = 0;
ret = SSL_connect(ssl);
if (ret == 1) {
break;
if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
data->waitingThreads++;
int selectResult = sslSelect(error, fd, data, timeout);
-
+
if (selectResult == -1) {
throwIOExceptionWithSslErrors(env, -1, error,
"Connect error");
- free_ssl(env, object);
- free_ssl_ctx(env, object);
+ SSL_clear(ssl);
return (jboolean) false;
} else if (selectResult == 0) {
throwSocketTimeoutException(env, "SSL handshake timed out");
+ SSL_clear(ssl);
freeSslErrorState();
- free_ssl(env, object);
- free_ssl_ctx(env, object);
return (jboolean) false;
}
} else {
LOGE("Unknown error %d during connect", error);
break;
}
- }
- }
+ }
+ }
if (ret != 1) {
/*
if (sslErrorCode != SSL_ERROR_ZERO_RETURN) {
throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
"SSL handshake failure");
- free_ssl(env, object);
- free_ssl_ctx(env, object);
+ SSL_clear(ssl);
return (jboolean) false;
}
}
if (ssl_session != NULL) {
ret = SSL_session_reused(ssl);
- // if (ret == 1) LOGD("A session was reused");
- // else LOGD("A new session was negotiated");
+ // if (ret == 1) LOGD("Session %p was reused", ssl_session);
+ // else LOGD("Session %p was not reused, using new session %p", ssl_session, SSL_get_session(ssl));
return (jboolean) ret;
} else {
- // LOGD("A new session was negotiated");
+ // LOGD("New session %p was negotiated", SSL_get_session(ssl));
return (jboolean) 0;
}
// LOGD("LEAVE connect");
}
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession(JNIEnv* env, jobject object,
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession(JNIEnv* env, jclass,
jint jssl)
{
- return (jint) SSL_get1_session((SSL *) jssl);
+ return (jint) SSL_get1_session((SSL*) jssl);
}
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEnv* env, jobject object,
- jobject socketObject, jint jssl_ctx, jboolean client_mode)
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEnv* env, jclass,
+ jint ssl_address, jobject socketObject)
{
- int sd, ret;
- BIO *bio;
- SSL *ssl;
- SSL_CTX *ssl_ctx;
- jsse_ssl_app_data_t appdata;
-
- ssl_ctx = (SSL_CTX *)jssl_ctx;
+ SSL* serverSocketSsl = reinterpret_cast<SSL*>(static_cast<uintptr_t>(ssl_address));
+ if (serverSocketSsl == NULL) {
+ throwIOExceptionWithSslErrors(env, 0, 0,
+ "Unusable SSL structure");
+ return NULL;
+ }
- ssl = create_ssl(env, object, ssl_ctx);
+ SSL* ssl = SSL_dup(serverSocketSsl);
if (ssl == NULL) {
throwIOExceptionWithSslErrors(env, 0, 0,
"Unable to create SSL structure");
- return;
+ return NULL;
}
jobject socketImplObject = env->GetObjectField(socketObject, field_Socket_mImpl);
if (socketImplObject == NULL) {
- free_ssl(env, object);
throwIOExceptionStr(env, "couldn't get the socket impl from the socket");
- return;
+ return NULL;
}
jobject fdObject = env->GetObjectField(socketImplObject, field_Socket_mFD);
if (fdObject == NULL) {
- free_ssl(env, object);
throwIOExceptionStr(env, "couldn't get the file descriptor from the socket impl");
- return;
+ return NULL;
}
- sd = jniGetFDFromFileDescriptor(env, fdObject);
-
- bio = BIO_new_socket(sd, BIO_NOCLOSE);
-
- /* The parameter client_mode must be 1 */
- if (client_mode != 0)
- client_mode = 1;
- BIO_set_ssl_mode(bio, client_mode);
+ int sd = jniGetFDFromFileDescriptor(env, fdObject);
+ BIO* bio = BIO_new_socket(sd, BIO_NOCLOSE);
SSL_set_bio(ssl, bio, bio);
/*
- * Fill in the appdata structure needed for the certificate callback and
- * store this in the SSL application data slot.
+ * Fill in the stack allocated appdata structure needed for the
+ * certificate callback and store this in the SSL application data
+ * slot.
*/
+ jsse_ssl_app_data_t appdata;
appdata.env = env;
- appdata.object = object;
+ appdata.object = socketObject;
SSL_set_app_data(ssl, &appdata);
/*
* Maybe we need to deal with all the special SSL error cases (WANT_*),
* just like we do for SSL_connect(). But currently it is looking ok.
*/
- ret = SSL_accept(ssl);
+ int ret = SSL_accept(ssl);
/*
* Clear the SSL application data slot again, so we can safely use it for
throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
"Trouble accepting connection");
}
- free_ssl(env, object);
- return;
+ SSL_clear(ssl);
+ return NULL;
} else if (ret < 0) {
/*
* Translate the error and throw exception. We are sure it is an error
int sslErrorCode = SSL_get_error(ssl, ret);
throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
"Trouble accepting connection");
- free_ssl(env, object);
- return;
+ SSL_clear(ssl);
+ return NULL;
}
/*
int mode = fcntl(fd, F_GETFL);
if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) {
throwIOExceptionStr(env, "Unable to make socket non blocking");
- free_ssl(env, object);
- return;
+ SSL_clear(ssl);
+ return NULL;
}
/*
*/
if (sslCreateAppData(ssl) == -1) {
throwIOExceptionStr(env, "Unable to create application data");
- free_ssl(env, object);
- return;
- }
-}
-
-/**
- * Loads the desired protocol for the OpenSSL client and enables it.
- * For example SSL_OP_NO_TLSv1 means do not use TLS v. 1.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledprotocols(JNIEnv* env, jobject object,
- jlong protocol)
-{
- if (protocol != 0x00000000L) {
- if (protocol & SSL_OP_NO_SSLv3)
- LOGD("SSL_OP_NO_SSLv3 is set");
- if (protocol & SSL_OP_NO_TLSv1)
- LOGD("SSL_OP_NO_TLSv1 is set");
-
- SSL_CTX* ctx = (SSL_CTX*)env->GetIntField(object, field_Socket_ssl_ctx);
- int options = SSL_CTX_get_options(ctx);
- options |= protocol; // Note: SSLv2 disabled earlier.
- SSL_CTX_set_options(ctx, options);
- }
-}
-
-static jobjectArray makeCipherList(JNIEnv* env, SSL* ssl) {
- STACK_OF(SSL_CIPHER)* cipher_list = SSL_get_ciphers(ssl);
- // Count the ciphers.
- int num = sk_SSL_CIPHER_num(cipher_list);
- int cipherCount = 0;
- for (int i = 0; i < num; ++i) {
- SSL_CIPHER* cipher = sk_SSL_CIPHER_value(cipher_list, i);
- if (strcmp(SSL_CIPHER_get_version(cipher), SSL_TXT_SSLV2) == 0) {
- // openssl-1.0.0 includes duplicate names for SSLv2 and SSLv3 ciphers
- continue;
- }
- ++cipherCount;
- }
-
- // Create a String[].
- jclass stringClass = env->FindClass("java/lang/String");
- if (stringClass == NULL) {
+ SSL_clear(ssl);
return NULL;
}
- jobjectArray array = env->NewObjectArray(cipherCount, stringClass, NULL);
- if (array == NULL) {
- return NULL;
- }
-
- // Fill in the cipher names.
- int cipherIndex = 0;
- for (int i = 0; i < num; ++i) {
- SSL_CIPHER* cipher = sk_SSL_CIPHER_value(cipher_list, i);
- if (strcmp(SSL_CIPHER_get_version(cipher), SSL_TXT_SSLV2) == 0) {
- continue;
- }
- env->SetObjectArrayElement(array, cipherIndex, env->NewStringUTF(cipher->name));
- ++cipherIndex;
- }
- return array;
-}
-
-jobjectArray makeCipherList(JNIEnv* env, SSL_CTX* ssl_ctx) {
- SSL* ssl = SSL_new(ssl_ctx);
- if (ssl == NULL) {
- return NULL;
- }
- jobjectArray result = makeCipherList(env, ssl);
- SSL_free(ssl);
- return result;
-}
-
-/**
- * Loads the ciphers suites that are supported by the OpenSSL client
- * and returns them in a string array.
- */
-static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsupportedciphersuites(JNIEnv* env,
- jobject object)
-{
- SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
- if (ssl_ctx == NULL) {
- return NULL;
- }
- jobjectArray result = makeCipherList(env, ssl_ctx);
- SSL_CTX_free(ssl_ctx);
- return result;
-}
-
-/**
- * Loads the ciphers suites that are enabled in the OpenSSL client
- * and returns them in a string array.
- */
-static jobjectArray OpenSSLSocketImpl_nativeGetEnabledCipherSuites(JNIEnv* env,
- jclass, jint ssl_ctx_address)
-{
- SSL_CTX* ssl_ctx =
- reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
- return makeCipherList(env, ssl_ctx);
-}
-
-void setEnabledCipherSuites(JNIEnv* env, jstring controlString, SSL_CTX* ssl_ctx) {
- const char* str = env->GetStringUTFChars(controlString, NULL);
- int rc = SSL_CTX_set_cipher_list(ssl_ctx, str);
- env->ReleaseStringUTFChars(controlString, str);
- if (rc == 0) {
- freeSslErrorState();
- jniThrowException(env, "java/lang/IllegalArgumentException",
- "Illegal cipher suite strings.");
- }
-}
-/**
- * Sets the ciphers suites that are enabled in the OpenSSL client.
- */
-static void OpenSSLSocketImpl_nativeSetEnabledCipherSuites(JNIEnv* env, jclass,
- jint ssl_ctx_address, jstring controlString)
-{
- SSL_CTX* ssl_ctx =
- reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
- setEnabledCipherSuites(env, controlString, ssl_ctx);
+ return (jint) ssl;
}
#define SSL_aRSA 0x00000001L
* Sets the client's crypto algorithms and authentication methods.
*/
static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod(JNIEnv* env,
- jobject object)
+ jclass, jint ssl_address)
{
- SSL* ssl;
- const SSL_CIPHER *cipher;
- jstring ret;
- char buf[512];
- unsigned long alg_auth;
- const char *au;
-
- ssl = getSslPointer(env, object, true);
+ SSL* ssl = getSslPointer(env, ssl_address, true);
if (ssl == NULL) {
return NULL;
}
- cipher = SSL_get_current_cipher(ssl);
+ const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl);
- alg_auth = cipher->algorithm_auth;
+ unsigned long alg_auth = cipher->algorithm_auth;
+ const char *au;
switch (alg_auth) {
case SSL_aRSA:
au="RSA";
break;
}
- ret = env->NewStringUTF(au);
+ jstring ret = env->NewStringUTF(au);
return ret;
}
/**
* OpenSSL read function (1): only one chunk is read (returned as jint).
*/
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv* env, jobject object, jint timeout)
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv* env, jclass, jint ssl_address, jint timeout)
{
- SSL *ssl = getSslPointer(env, object, true);
+ SSL* ssl = getSslPointer(env, ssl_address, true);
if (ssl == NULL) {
return 0;
}
}
/**
- * OpenSSL read function (2): read into buffer at offset n chunks.
+ * OpenSSL read function (2): read into buffer at offset n chunks.
* Returns 1 (success) or value <= 0 (failure).
*/
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEnv* env, jobject obj, jbyteArray dest, jint offset, jint len, jint timeout)
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEnv* env, jclass, jint ssl_address, jbyteArray dest, jint offset, jint len, jint timeout)
{
- SSL *ssl = getSslPointer(env, obj, true);
+ SSL* ssl = getSslPointer(env, ssl_address, true);
if (ssl == NULL) {
return 0;
}
/**
* OpenSSL write function (1): only one chunk is written.
*/
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(JNIEnv* env, jobject object, jint b)
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(JNIEnv* env, jclass, jint ssl_address, jint b)
{
- SSL *ssl = getSslPointer(env, object, true);
+ SSL* ssl = getSslPointer(env, ssl_address, true);
if (ssl == NULL) {
return;
}
}
/**
- * OpenSSL write function (2): write into buffer at offset n chunks.
+ * OpenSSL write function (2): write into buffer at offset n chunks.
*/
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIEnv* env, jobject obj,
- jbyteArray dest, jint offset, jint len)
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIEnv* env, jclass,
+ jint ssl_address, jbyteArray dest, jint offset, jint len)
{
- SSL *ssl = getSslPointer(env, obj, true);
+ SSL* ssl = getSslPointer(env, ssl_address, true);
if (ssl == NULL) {
return;
}
jbyte* bytes = env->GetByteArrayElements(dest, NULL);
int returnCode = 0;
int errorCode = 0;
- int timeout = (int)env->GetIntField(obj, field_Socket_timeout);
- int ret = sslWrite(ssl, (const char *) (bytes + offset), len,
+ int ret = sslWrite(ssl, (const char *) (bytes + offset), len,
&returnCode, &errorCode);
env->ReleaseByteArrayElements(dest, bytes, 0);
}
/**
- * Interrupt any pending IO before closing the socket.
+ * Interrupt any pending IO before closing the socket.
*/
static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt(
- JNIEnv* env, jobject object) {
- SSL *ssl = getSslPointer(env, object, false);
+ JNIEnv* env, jclass, jint ssl_address) {
+ SSL* ssl = getSslPointer(env, ssl_address, false);
if (ssl == NULL) {
return;
}
}
/**
- * OpenSSL close SSL socket function.
+ * OpenSSL close SSL socket function.
*/
static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close(
- JNIEnv* env, jobject object) {
- SSL *ssl = getSslPointer(env, object, false);
+ JNIEnv* env, jclass, jint ssl_address) {
+ SSL* ssl = getSslPointer(env, ssl_address, false);
if (ssl == NULL) {
return;
}
* Everything else is a real error condition. We should
* let the Java layer know about this by throwing an
* exception.
- */
+ */
int sslErrorCode = SSL_get_error(ssl, ret);
throwIOExceptionWithSslErrors(env, ret, sslErrorCode, "SSL shutdown failed");
break;
}
+ SSL_clear(ssl);
freeSslErrorState();
- free_ssl(env, object);
- free_ssl_ctx(env, object);
-}
-
-/**
- * OpenSSL free SSL socket function.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_free(JNIEnv* env, jobject object)
-{
- free_ssl(env, object);
- free_ssl_ctx(env, object);
}
/**
static JNINativeMethod sSocketImplMethods[] =
{
- {"nativeinitstatic", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_initstatic},
- {"nativeinit", "(Ljava/lang/String;Ljava/lang/String;[B)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init},
- {"nativeconnect", "(ILjava/net/Socket;ZI)Z", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect},
+ {"nativeconnect", "(ILjava/net/Socket;IZI)Z", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect},
{"nativegetsslsession", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession},
- {"nativeread", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read},
- {"nativeread", "([BIII)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba},
- {"nativewrite", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write},
- {"nativewrite", "([BII)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba},
- {"nativeaccept", "(Ljava/net/Socket;IZ)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept},
- {"nativesetenabledprotocols", "(J)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledprotocols},
- {"nativegetsupportedciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsupportedciphersuites},
- {"nativeGetEnabledCipherSuites", "(I)[Ljava/lang/String;", (void*) OpenSSLSocketImpl_nativeGetEnabledCipherSuites},
- {"nativeSetEnabledCipherSuites", "(ILjava/lang/String;)V", (void*) OpenSSLSocketImpl_nativeSetEnabledCipherSuites},
- {"nativecipherauthenticationmethod", "()Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod},
- {"nativeinterrupt", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt},
- {"nativeclose", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close},
- {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_free},
+ {"nativeread", "(II)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read},
+ {"nativeread", "(I[BIII)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba},
+ {"nativewrite", "(II)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write},
+ {"nativewrite", "(I[BII)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba},
+ {"nativeaccept", "(ILjava/net/Socket;)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept},
+ {"nativecipherauthenticationmethod", "(I)Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod},
+ {"nativeinterrupt", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt},
+ {"nativeclose", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close},
{"nativeverifysignature", "([B[BLjava/lang/String;[B[B)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignature},
};
/**
- * Module scope variables initialized during JNI registration.
- */
-static jfieldID field_ServerSocket_ssl_ctx;
-
-/**
- * Initialization phase of OpenSSL: Loads the Error strings, the crypto algorithms and reset the OpenSSL library
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_initstatic(JNIEnv* env, jobject obj)
-{
- SSL_load_error_strings();
- ERR_load_crypto_strings();
- SSL_library_init();
- OpenSSL_add_all_algorithms();
-}
-
-/**
- * Initialization phase for a server socket with OpenSSL. The server's private key and X509 certificate are read and
- * the Linux /dev/random file is loaded as RNG for the session keys.
- *
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init(JNIEnv* env, jobject object,
- jstring privatekey, jstring certificates, jbyteArray seed)
-{
- SSL_CTX *ssl_ctx;
- const char *privatekeychar;
- const char *certificateschar;
- EVP_PKEY * privatekeyevp;
-
- BIO *privatekeybio;
- BIO *certificatesbio;
-
- // 'seed == null' when no SecureRandom Object is set
- // in the SSLContext.
- if (seed != NULL) {
- jbyte* randseed = env->GetByteArrayElements(seed, NULL);
- RAND_seed((unsigned char*) randseed, 1024);
- env->ReleaseByteArrayElements(seed, randseed, 0);
- } else {
- RAND_load_file("/dev/urandom", 1024);
- }
-
- ssl_ctx = SSL_CTX_new(SSLv23_server_method());
- SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
-
- privatekeychar = env->GetStringUTFChars((jstring)privatekey, NULL);
- privatekeybio = BIO_new_mem_buf((void*)privatekeychar, -1);
-
- privatekeyevp = PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL);
- env->ReleaseStringUTFChars(privatekey, privatekeychar);
-
- if (privatekeyevp == NULL) {
- LOGE(ERR_error_string(ERR_get_error(), NULL));
- throwIOExceptionStr(env, "Error parsing the private key");
- return;
- }
-
- certificateschar = env->GetStringUTFChars((jstring)certificates, NULL);
- certificatesbio = BIO_new_mem_buf((void*)certificateschar, -1);
-
- X509 * certificatesx509 = PEM_read_bio_X509(certificatesbio, NULL, 0, NULL);
- env->ReleaseStringUTFChars(certificates, certificateschar);
-
- if (certificatesx509 == NULL) {
- LOGE(ERR_error_string(ERR_get_error(), NULL));
- throwIOExceptionStr(env, "Error parsing the certificates");
- return;
- }
-
- if (!SSL_CTX_use_certificate(ssl_ctx, certificatesx509)) {
- LOGE(ERR_error_string(ERR_get_error(), NULL));
- throwIOExceptionStr(env, "Error setting the certificates");
- return;
- }
-
- if (!SSL_CTX_use_PrivateKey(ssl_ctx, privatekeyevp)) {
- LOGE(ERR_error_string(ERR_get_error(), NULL));
- throwIOExceptionStr(env, "Error setting the private key");
- return;
- }
-
- if (!SSL_CTX_check_private_key(ssl_ctx)) {
- LOGE(ERR_error_string(ERR_get_error(), NULL));
- throwIOExceptionStr(env, "Error checking private key");
- return;
- }
-
- env->SetIntField(object, field_ServerSocket_ssl_ctx, (int)ssl_ctx);
-}
-
-/**
- * Loads the desired protocol for the OpenSSL server and enables it.
- * For example SSL_OP_NO_TLSv1 means do not use TLS v. 1.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledprotocols(JNIEnv* env,
- jobject object, jlong protocol)
-{
- if (protocol != 0x00000000L) {
- if (protocol & SSL_OP_NO_SSLv3)
- LOGD("SSL_OP_NO_SSLv3 is set");
- if (protocol & SSL_OP_NO_TLSv1)
- LOGD("SSL_OP_NO_TLSv1 is set");
-
- SSL_CTX* ctx = (SSL_CTX*)env->GetIntField(object, field_ServerSocket_ssl_ctx);
- SSL_CTX_set_options((SSL_CTX*)ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2|(long)protocol);
- }
-}
-
-/**
- * Loads the ciphers suites that are supported by the OpenSSL server
- * and returns them in a string array.
- */
-static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getsupportedciphersuites(JNIEnv* env,
- jobject object)
-{
- SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_server_method());
- if (ssl_ctx == NULL) {
- return NULL;
- }
- jobjectArray result = makeCipherList(env, ssl_ctx);
- SSL_CTX_free(ssl_ctx);
- return result;
-}
-
-/**
* Gives an array back containing all the X509 certificate's bytes.
*/
static jobjectArray getcertificatebytes(JNIEnv* env,
*/
static int verify_callback(int preverify_ok, X509_STORE_CTX *x509_store_ctx)
{
- SSL *ssl;
- jsse_ssl_app_data_t *appdata;
- jclass cls;
-
- jobjectArray objectArray;
-
/* Get the correct index to the SSLobject stored into X509_STORE_CTX. */
- ssl = (SSL*)X509_STORE_CTX_get_ex_data(x509_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+ SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(x509_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
- appdata = (jsse_ssl_app_data_t*)SSL_get_app_data(ssl);
+ jsse_ssl_app_data_t* appdata = (jsse_ssl_app_data_t*)SSL_get_app_data(ssl);
- cls = appdata->env->GetObjectClass(appdata->object);
+ jclass cls = appdata->env->GetObjectClass(appdata->object);
- jmethodID methodID = appdata->env->GetMethodID(cls, "verify_callback", "([[B)I");
+ jmethodID methodID = appdata->env->GetMethodID(cls, "verifyCertificateChain", "([[B)Z");
- objectArray = getcertificatebytes(appdata->env, x509_store_ctx->untrusted);
+ jobjectArray objectArray = getcertificatebytes(appdata->env, x509_store_ctx->untrusted);
- appdata->env->CallIntMethod(appdata->object, methodID, objectArray);
+ jboolean verified = appdata->env->CallBooleanMethod(appdata->object, methodID, objectArray);
- return 1;
+ return (verified) ? 1 : 0;
}
/**
* Sets the client's credentials and the depth of theirs verification.
*/
static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth(JNIEnv* env,
- jobject object, jint value)
-{
- SSL_CTX *ssl_ctx = (SSL_CTX *)env->GetIntField(object, field_ServerSocket_ssl_ctx);
- SSL_CTX_set_verify(ssl_ctx, (int)value, verify_callback);
-}
-
-/**
- * The actual SSL context is reset.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree(JNIEnv* env, jobject object)
+ jclass, jint ssl_address, jint value)
{
- SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_ServerSocket_ssl_ctx);
- SSL_CTX_free(ctx);
- env->SetIntField(object, field_ServerSocket_ssl_ctx, 0);
+ SSL* ssl = getSslPointer(env, ssl_address, true);
+ if (ssl == NULL) {
+ return;
+ }
+ SSL_set_verify(ssl, (int)value, verify_callback);
}
static JNINativeMethod sServerSocketImplMethods[] =
{
- {"nativeinitstatic", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_initstatic},
- {"nativeinit", "(Ljava/lang/String;Ljava/lang/String;[B)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init},
- {"nativesetenabledprotocols", "(J)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledprotocols},
- {"nativegetsupportedciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getsupportedciphersuites},
- {"nativesetclientauth", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth},
- {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree}
+ {"nativesetclientauth", "(II)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth},
};
-static jfieldID field_Session_session;
-
-static SSL_SESSION* getSslSessionPointer(JNIEnv* env, jobject object) {
- return reinterpret_cast<SSL_SESSION*>(env->GetIntField(object, field_Session_session));
+/**
+ * Our implementation of what might be considered
+ * SSL_SESSION_get_peer_cert_chain
+ */
+static STACK_OF(X509)* SSL_SESSION_get_peer_cert_chain(SSL_CTX* ssl_ctx, SSL_SESSION* ssl_session) {
+ SSL* ssl = SSL_new(ssl_ctx);
+ SSL_set_session(ssl, ssl_session);
+ STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl);
+ SSL_free(ssl);
+ return chain;
}
// Fills a byte[][] with the peer certificates in the chain.
static jobjectArray OpenSSLSessionImpl_getPeerCertificatesImpl(JNIEnv* env,
- jobject object, jint jssl)
+ jclass, jint ssl_ctx_address, jint ssl_session_address)
{
- SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
- SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
- SSL* ssl = SSL_new(ssl_ctx);
-
- SSL_set_session(ssl, ssl_session);
-
- STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl);
+ SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+ if (ssl_ctx == NULL) {
+ jniThrowNullPointerException(env, "SSL_CTX is null");
+ return NULL;
+ }
+ SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
+ STACK_OF(X509)* chain = SSL_SESSION_get_peer_cert_chain(ssl_ctx, ssl_session);
jobjectArray objectArray = getcertificatebytes(env, chain);
-
- SSL_free(ssl);
- SSL_CTX_free(ssl_ctx);
return objectArray;
}
* not certificates). Returns a byte[] containing the DER-encoded state.
* See apache mod_ssl.
*/
-static jbyteArray OpenSSLSessionImpl_getEncoded(JNIEnv* env, jobject object) {
- SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
+static jbyteArray OpenSSLSessionImpl_getEncoded(JNIEnv* env, jclass, jint ssl_session_address) {
+ SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
if (ssl_session == NULL) {
return NULL;
}
/**
* Deserialize the session.
*/
-static jint OpenSSLSessionImpl_initializeNativeImpl(JNIEnv* env, jobject object, jbyteArray bytes, jint size) {
+static jint OpenSSLSessionImpl_initializeNativeImpl(JNIEnv* env, jclass, jbyteArray bytes, jint size) {
if (bytes == NULL) {
return 0;
}
/**
* Gets and returns in a byte array the ID of the actual SSL session.
*/
-static jbyteArray OpenSSLSessionImpl_getId(JNIEnv* env, jobject object) {
- SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
+static jbyteArray OpenSSLSessionImpl_getId(JNIEnv* env, jclass, jint ssl_session_address) {
+ SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
jbyteArray result = env->NewByteArray(ssl_session->session_id_length);
if (result != NULL) {
* Gets and returns in a long integer the creation's time of the
* actual SSL session.
*/
-static jlong OpenSSLSessionImpl_getCreationTime(JNIEnv* env, jobject object) {
- SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
+static jlong OpenSSLSessionImpl_getCreationTime(JNIEnv* env, jclass, jint ssl_session_address) {
+ SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
jlong result = SSL_SESSION_get_time(ssl_session);
result *= 1000; // OpenSSL uses seconds, Java uses milliseconds.
return result;
}
/**
+ * Our implementation of what might be considered
+ * SSL_SESSION_get_version, based on SSL_get_version.
+ * See get_ssl_version above.
+ */
+static const char* SSL_SESSION_get_version(SSL_SESSION* ssl_session) {
+ return get_ssl_version(ssl_session->ssl_version);
+}
+
+/**
* Gets and returns in a string the version of the SSL protocol. If it
* returns the string "unknown" it means that no connection is established.
*/
-static jstring OpenSSLSessionImpl_getProtocol(JNIEnv* env, jobject object) {
- SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
- SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
- SSL* ssl = SSL_new(ssl_ctx);
-
- SSL_set_session(ssl, ssl_session);
-
- const char* protocol = SSL_get_version(ssl);
+static jstring OpenSSLSessionImpl_getProtocol(JNIEnv* env, jclass, jint ssl_session_address) {
+ SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
+ const char* protocol = SSL_SESSION_get_version(ssl_session);
jstring result = env->NewStringUTF(protocol);
-
- SSL_free(ssl);
- SSL_CTX_free(ssl_ctx);
return result;
}
/**
* Gets and returns in a string the set of ciphers the actual SSL session uses.
*/
-static jstring OpenSSLSessionImpl_getCipherSuite(JNIEnv* env, jobject object) {
- SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
- SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
- SSL* ssl = SSL_new(ssl_ctx);
-
- SSL_set_session(ssl, ssl_session);
-
- const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl);
+static jstring OpenSSLSessionImpl_getCipherSuite(JNIEnv* env, jclass, jint ssl_session_address) {
+ SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
+ const SSL_CIPHER* cipher = ssl_session->cipher;
jstring result = env->NewStringUTF(SSL_CIPHER_get_name(cipher));
-
- SSL_free(ssl);
- SSL_CTX_free(ssl_ctx);
return result;
}
/**
* Frees the SSL session.
*/
-static void OpenSSLSessionImpl_freeImpl(JNIEnv* env, jobject object, jint session) {
- LOGD("Freeing OpenSSL session");
+static void OpenSSLSessionImpl_freeImpl(JNIEnv* env, jclass, jint session) {
SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(session);
+ // LOGD("Freeing OpenSSL session %p", session);
SSL_SESSION_free(ssl_session);
}
static JNINativeMethod sSessionImplMethods[] = {
{ "freeImpl", "(I)V", (void*) OpenSSLSessionImpl_freeImpl },
- { "getCipherSuite", "()Ljava/lang/String;", (void*) OpenSSLSessionImpl_getCipherSuite },
- { "getCreationTime", "()J", (void*) OpenSSLSessionImpl_getCreationTime },
- { "getEncoded", "()[B", (void*) OpenSSLSessionImpl_getEncoded },
- { "getId", "()[B", (void*) OpenSSLSessionImpl_getId },
- { "getPeerCertificatesImpl", "()[[B", (void*) OpenSSLSessionImpl_getPeerCertificatesImpl },
- { "getProtocol", "()Ljava/lang/String;", (void*) OpenSSLSessionImpl_getProtocol },
- { "initializeNativeImpl", "([BI)I", (void*) OpenSSLSessionImpl_initializeNativeImpl }
+ { "getCipherSuite", "(I)Ljava/lang/String;", (void*) OpenSSLSessionImpl_getCipherSuite },
+ { "getCreationTime", "(I)J", (void*) OpenSSLSessionImpl_getCreationTime },
+ { "getEncoded", "(I)[B", (void*) OpenSSLSessionImpl_getEncoded },
+ { "getId", "(I)[B", (void*) OpenSSLSessionImpl_getId },
+ { "getPeerCertificatesImpl", "(II)[[B", (void*) OpenSSLSessionImpl_getPeerCertificatesImpl },
+ { "getProtocol", "(I)Ljava/lang/String;", (void*) OpenSSLSessionImpl_getProtocol },
+ { "initializeNativeImpl", "([BI)I", (void*) OpenSSLSessionImpl_initializeNativeImpl },
};
typedef struct {
{ "org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl", sServerSocketImplMethods, NELEM(sServerSocketImplMethods) },
{ "org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl", sSessionImplMethods, NELEM(sSessionImplMethods) },
};
-
-/*
- * Peforms the actual registration of the native methods.
- * Also looks up the fields that belong to the class (if
- * any) and stores the field IDs. Simply remove what you
- * don't need.
- */
-extern "C" int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEnv* env) {
-
+int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEnv* env) {
// Register org.apache.harmony.xnet.provider.jsse.* methods
for (int i = 0; i < NELEM(sClasses); i++) {
int result = jniRegisterNativeMethods(env,
}
}
- // java.io.FileDescriptor
- jclass fileDescriptor = env->FindClass("java/io/FileDescriptor");
- if (fileDescriptor == NULL) {
- LOGE("Can't find java/io/FileDescriptor");
- return -1;
- }
- field_FileDescriptor_descriptor = env->GetFieldID(fileDescriptor, "descriptor", "I");
- if (field_FileDescriptor_descriptor == NULL) {
- LOGE("Can't find FileDescriptor.descriptor");
- return -1;
- }
-
// java.net.Socket
jclass socket = env->FindClass("java/net/Socket");
if (socket == NULL) {
return -1;
}
- // org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl
- jclass socketImpl = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl");
- if (socketImpl == NULL) {
- LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl");
- return -1;
- }
- // Note: do these after the registration of native methods, because
- // there is a static method "initstatic" that's called when the
- // OpenSSLSocketImpl class is first loaded, and that required
- // a native method to be associated with it.
- field_Socket_ssl_ctx = env->GetFieldID(socketImpl, "ssl_ctx", "I");
- if (field_Socket_ssl_ctx == NULL) {
- LOGE("Can't find OpenSSLSocketImpl.ssl_ctx");
- return -1;
- }
- field_Socket_ssl = env->GetFieldID(socketImpl, "ssl", "I");
- if (field_Socket_ssl == NULL) {
- LOGE("Can't find OpenSSLSocketImpl.ssl");
- return -1;
- }
- field_Socket_timeout = env->GetFieldID(socketImpl, "timeout", "I");
- if (field_Socket_timeout == NULL) {
- LOGE("Can't find OpenSSLSocketImpl.timeout");
- return -1;
- }
-
- // org.apache.harmony.xnet.provider.jsse.OpenSSLServerSocketImpl
- jclass serverSocketImpl = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl");
- if (serverSocketImpl == NULL) {
- LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl");
- return -1;
- }
- // Note: do these after the registration of native methods, because
- // there is a static method "initstatic" that's called when the
- // OpenSSLServerSocketImpl class is first loaded, and that required
- // a native method to be associated with it.
- field_ServerSocket_ssl_ctx = env->GetFieldID(serverSocketImpl, "ssl_ctx", "I");
- if (field_ServerSocket_ssl_ctx == NULL) {
- LOGE("Can't find OpenSSLServerSocketImpl.ssl_ctx");
- return -1;
- }
-
- // org.apache.harmony.xnet.provider.jsse.OpenSSLSessionImpl
- jclass sessionImpl = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl");
- if (sessionImpl == NULL) {
- return -1;
- }
- field_Session_session = env->GetFieldID(sessionImpl, "session", "I");
- if (field_Session_session == NULL) {
- LOGE("Can't find OpenSSLSessionImpl.session");
- return -1;
- }
-
return 0;
}