blob: 10491e8ea3378679aaea7434f9d688a14dc9b300 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sslEngine.h"
#include <stdio.h>
#include "jni.h"
#include "hysock.h"
#include "openssl/bio.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
typedef struct {
BIO *bio;
BIO *bio_io;
} _sslengine;
static jobject handshake_need_wrap, handshake_need_unwrap, handshake_finished, handshake_not_handshaking;
static jobject engine_buffer_overflow, engine_buffer_underflow, engine_closed, engine_ok;
int check_ssl_error(JNIEnv *env, int state) {
jclass exception;
switch(state) {
case SSL_ERROR_SYSCALL:
case SSL_ERROR_SSL:
exception = (*env)->FindClass(env, "javax/net/ssl/SSLHandshakeException");
(*env)->ThrowNew(env, exception, ERR_reason_error_string(ERR_get_error()));
return 1;
}
return 0;
}
JNIEXPORT void JNICALL Java_org_apache_harmony_xnet_provider_jsse_SSLEngineImpl_initImpl
(JNIEnv *env, jclass clazz) {
jclass class;
jfieldID fieldID;
// initialise handshake status enum
class = (*env)->FindClass(env, "javax/net/ssl/SSLEngineResult$HandshakeStatus");
fieldID = (*env)->GetStaticFieldID(env, class, "NEED_WRAP", "Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;");
handshake_need_wrap = (*env)->NewGlobalRef(env, (*env)->GetStaticObjectField(env, class, fieldID));
fieldID = (*env)->GetStaticFieldID(env, class, "NEED_UNWRAP", "Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;");
handshake_need_unwrap = (*env)->NewGlobalRef(env, (*env)->GetStaticObjectField(env, class, fieldID));
fieldID = (*env)->GetStaticFieldID(env, class, "FINISHED", "Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;");
handshake_finished = (*env)->NewGlobalRef(env, (*env)->GetStaticObjectField(env, class, fieldID));
fieldID = (*env)->GetStaticFieldID(env, class, "NOT_HANDSHAKING", "Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;");
handshake_not_handshaking = (*env)->NewGlobalRef(env, (*env)->GetStaticObjectField(env, class, fieldID));
// initialise engine status enum
class = (*env)->FindClass(env, "javax/net/ssl/SSLEngineResult$Status");
fieldID = (*env)->GetStaticFieldID(env, class, "BUFFER_OVERFLOW", "Ljavax/net/ssl/SSLEngineResult$Status;");
engine_buffer_overflow = (*env)->NewGlobalRef(env, (*env)->GetStaticObjectField(env, class, fieldID));
fieldID = (*env)->GetStaticFieldID(env, class, "BUFFER_UNDERFLOW", "Ljavax/net/ssl/SSLEngineResult$Status;");
engine_buffer_underflow = (*env)->NewGlobalRef(env, (*env)->GetStaticObjectField(env, class, fieldID));
fieldID = (*env)->GetStaticFieldID(env, class, "CLOSED", "Ljavax/net/ssl/SSLEngineResult$Status;");
engine_closed = (*env)->NewGlobalRef(env, (*env)->GetStaticObjectField(env, class, fieldID));
fieldID = (*env)->GetStaticFieldID(env, class, "OK", "Ljavax/net/ssl/SSLEngineResult$Status;");
engine_ok = (*env)->NewGlobalRef(env, (*env)->GetStaticObjectField(env, class, fieldID));
}
JNIEXPORT jlong JNICALL Java_org_apache_harmony_xnet_provider_jsse_SSLEngineImpl_initSSL
(JNIEnv *env, jclass clazz, jlong context) {
SSL *ssl;
ssl = SSL_new(jlong2addr(SSL_CTX, context));
return addr2jlong(ssl);
}
JNIEXPORT jlong JNICALL Java_org_apache_harmony_xnet_provider_jsse_SSLEngineImpl_initSSLEngine
(JNIEnv *env, jclass clazz, jlong jssl) {
_sslengine *sslengine;
SSL *ssl = jlong2addr(SSL, jssl);
BIO *bio, *bio_in, *bio_out;
sslengine = malloc(sizeof(_sslengine));
bio = BIO_new(BIO_f_ssl());
BIO_set_ssl(bio, ssl, BIO_NOCLOSE);
// create a bio pair
BIO_new_bio_pair(&bio_in, 0, &bio_out, 0);
BIO_get_ssl(bio, &ssl);
SSL_set_bio(ssl, bio_in, bio_in);
sslengine->bio = bio;
sslengine->bio_io = bio_out;
return addr2jlong(sslengine);
}
JNIEXPORT jobject JNICALL Java_org_apache_harmony_xnet_provider_jsse_SSLEngineImpl_acceptImpl
(JNIEnv *env, jclass clazz, jlong jssl) {
SSL *ssl = jlong2addr(SSL, jssl);
int ret;
// Put our SSL into accept state
SSL_set_accept_state(ssl);
// Start the client handshake
ret = SSL_do_handshake(ssl);
if (check_ssl_error(env, SSL_get_error(ssl, ret))) {
return NULL;
}
return handshake_need_unwrap;
}
JNIEXPORT jobject JNICALL Java_org_apache_harmony_xnet_provider_jsse_SSLEngineImpl_connectImpl
(JNIEnv *env, jclass clazz, jlong jssl) {
SSL *ssl = jlong2addr(SSL, jssl);
int ret;
// Put our SSL into accept state
SSL_set_connect_state(ssl);
// Start the server handshake
ret = SSL_do_handshake(ssl);
if (check_ssl_error(env, SSL_get_error(ssl, ret))) {
return NULL;
}
return handshake_need_wrap;
}
JNIEXPORT jobject JNICALL Java_org_apache_harmony_xnet_provider_jsse_SSLEngineImpl_wrapImpl
(JNIEnv *env, jclass clazz, jlong jssl, jlong jsslengine, jlong src_address, int src_len,
jlong dst_address, int dst_len) {
_sslengine *sslengine = jlong2addr(_sslengine, jsslengine);
BIO *bio = sslengine->bio;
BIO *bio_io = sslengine->bio_io;
SSL *ssl = jlong2addr(SSL, jssl);
int write_result = 0, read_result = 0, shutdownState = 0;
jobject handshake_state = NULL, engine_state = engine_ok, result = NULL;
jclass result_class;
jmethodID result_constructor;
jbyte *src_buffer = jlong2addr(jbyte, src_address);
jbyte *dst_buffer = jlong2addr(jbyte, dst_address);
int initial_init_state, init_state;
initial_init_state = SSL_in_init(ssl);
// write input data
if (BIO_ctrl_get_write_guarantee(bio) >= (size_t)src_len) {
write_result = BIO_write(bio, (const void *)src_buffer, (int)src_len);
} else {
write_result = 0;
}
if (write_result > 0) {
// wrote some data so must not be handshaking
handshake_state = handshake_not_handshaking;
} else {
write_result = 0;
handshake_state = handshake_need_unwrap;
}
// Check if close_notify has been sent or received
shutdownState = SSL_get_shutdown(ssl);
if (shutdownState) {
engine_state = engine_closed;
}
// read output data
// if the destination buffer is too small
if (BIO_ctrl_pending(bio_io) > (size_t)dst_len) {
engine_state = engine_buffer_overflow;
} else {
read_result = BIO_read(bio_io, dst_buffer, dst_len);
}
if (read_result < 0) {
// change state?
read_result = 0;
}
init_state = SSL_in_init(ssl);
// if not in SSL init state
if (!init_state) {
// if we were in init state when we entered this function
if (initial_init_state) {
handshake_state = handshake_finished;
} else {
handshake_state = handshake_not_handshaking;
}
}
// construct return object
result_class = (*env)->FindClass(env, "javax/net/ssl/SSLEngineResult");
result_constructor = (*env)->GetMethodID(env, result_class, "<init>",
"(Ljavax/net/ssl/SSLEngineResult$Status;Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;II)V");
result = (*env)->NewObject(env, result_class, result_constructor,
engine_state, handshake_state, write_result, read_result);
return result;
}
JNIEXPORT jobject JNICALL Java_org_apache_harmony_xnet_provider_jsse_SSLEngineImpl_unwrapImpl
(JNIEnv *env, jclass clazz, jlong jssl, jlong jsslengine, jlong src_address, int src_len,
jlong dst_address, int dst_len) {
_sslengine *sslengine = jlong2addr(_sslengine, jsslengine);
BIO *bio = sslengine->bio;
BIO *bio_io = sslengine->bio_io;
SSL *ssl = jlong2addr(SSL, jssl);
int write_result = 0, read_result = 0, shutdownState = 0;
jobject handshake_state = handshake_not_handshaking, engine_state = engine_ok, result = NULL;
jclass result_class;
jmethodID result_constructor;
jbyte *src_buffer = jlong2addr(jbyte, src_address);
jbyte *dst_buffer = jlong2addr(jbyte, dst_address);
int initial_init_state;
int read_pending = 0;
initial_init_state = SSL_in_init(ssl);
// write input data
write_result = BIO_write(bio_io, (const void *)src_buffer, (int)src_len);
if (write_result < 0) {
// change state?
write_result = 0;
}
// TODO check for errors
// read output data
read_pending = BIO_ctrl_pending(bio);
if (read_pending != 0) {
if (read_pending > dst_len) {
engine_state = engine_buffer_overflow;
} else {
read_result = SSL_read(ssl, dst_buffer, dst_len);
// Check if close_notify has been sent or received
shutdownState = SSL_get_shutdown(ssl);
if (shutdownState) {
engine_state = engine_closed;
}
if (read_result < 0) {
read_result = 0;
}
}
// if not in SSL init state
if (SSL_in_init(ssl)) {
handshake_state = handshake_need_wrap;
} else {
// if we were in init state when we entered this function
if (initial_init_state) {
handshake_state = handshake_finished;
} else {
handshake_state = handshake_not_handshaking;
}
}
}
// construct return object
result_class = (*env)->FindClass(env, "javax/net/ssl/SSLEngineResult");
result_constructor = (*env)->GetMethodID(env, result_class, "<init>",
"(Ljavax/net/ssl/SSLEngineResult$Status;Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;II)V");
result = (*env)->NewObject(env, result_class, result_constructor,
engine_state, handshake_state, write_result, read_result);
return result;
}
void shutdownImpl(JNIEnv *env, SSL *ssl) {
int ret = SSL_shutdown(ssl);
if (ret == -1) {
jclass exception = (*env)->FindClass(env, "javax/net/ssl/SSLException");
(*env)->ThrowNew(env, exception, ERR_reason_error_string(ERR_get_error()));
}
if (ret == 0) {
ret = SSL_shutdown(ssl);
if (((ret == -1) && (SSL_get_error(ssl, ret) != SSL_ERROR_WANT_READ)) || (ret == 0)) {
jclass exception = (*env)->FindClass(env, "javax/net/ssl/SSLException");
(*env)->ThrowNew(env, exception, ERR_reason_error_string(ERR_get_error()));
}
}
}
JNIEXPORT void JNICALL Java_org_apache_harmony_xnet_provider_jsse_SSLEngineImpl_shutdownImpl
(JNIEnv *env, jclass clazz, jlong jssl) {
SSL *ssl = jlong2addr(SSL, jssl);
shutdownImpl(env, ssl);
}
JNIEXPORT void JNICALL Java_org_apache_harmony_xnet_provider_jsse_SSLEngineImpl_closeInboundImpl
(JNIEnv *env, jclass clazz, jlong jsslengine) {
_sslengine *sslengine = jlong2addr(_sslengine, jsslengine);
BIO_shutdown_wr(sslengine->bio_io);
}