| /* |
| * 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); |
| } |
| |