| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed 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. |
| */ |
| |
| #define LOG_TAG "OpenSSLServerSocketImpl" |
| |
| #include <jni.h> |
| #include <JNIHelp.h> |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <arpa/inet.h> |
| #include <netinet/in.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <unistd.h> |
| |
| #include <openssl/err.h> |
| #include <openssl/rand.h> |
| #include <openssl/ssl.h> |
| |
| #include "org_apache_harmony_xnet_provider_jsse_common.h" |
| |
| /** |
| * Module scope variables initialized during JNI registration. |
| */ |
| static jfieldID field_ssl_ctx; |
| |
| /** |
| * Throws java.io.IOexception with the provided message. |
| */ |
| static void throwIOExceptionStr(JNIEnv* env, const char* message) |
| { |
| jclass exClass = env->FindClass("java/io/IOException"); |
| |
| if (exClass == NULL) |
| { |
| LOGE("Unable to find class java/io/IOException"); |
| } |
| else |
| { |
| env->ThrowNew(exClass, message); |
| } |
| } |
| |
| /** |
| * 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_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_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* ctx; |
| SSL* ssl; |
| STACK_OF(SSL_CIPHER) *sk; |
| jobjectArray ret; |
| int i; |
| const char *c; |
| |
| ctx = SSL_CTX_new(SSLv23_server_method()); |
| ssl = SSL_new(ctx); |
| sk=SSL_get_ciphers(ssl); |
| |
| ret= (jobjectArray)env->NewObjectArray(5, |
| env->FindClass("java/lang/String"), |
| env->NewStringUTF("")); |
| |
| i = 0; |
| while (SSL_get_cipher_list(ssl,i) != NULL) { |
| i++; |
| } |
| |
| ret = (jobjectArray)env->NewObjectArray(i, |
| env->FindClass("java/lang/String"), |
| env->NewStringUTF("")); |
| |
| for (i=0; ; i++) { |
| c=SSL_get_cipher_list(ssl,i); |
| if (c == NULL) break; |
| |
| env->SetObjectArrayElement(ret,i,env->NewStringUTF(c)); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Loads the ciphers suites that are enabled in the OpenSSL server |
| * and returns them in a string array. |
| */ |
| static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getenabledciphersuites(JNIEnv* env, |
| jobject object) |
| { |
| SSL_CTX* ctx; |
| SSL* ssl; |
| jobjectArray ret; |
| int i; |
| const char *c; |
| |
| ctx = (SSL_CTX*)env->GetIntField(object, field_ssl_ctx); |
| ssl = SSL_new(ctx); |
| |
| i = 0; |
| while (SSL_get_cipher_list(ssl,i) != NULL) { |
| i++; |
| } |
| |
| ret = (jobjectArray)env->NewObjectArray(i, |
| env->FindClass("java/lang/String"), |
| env->NewStringUTF("")); |
| |
| for (i = 0; ; i++) { |
| c = SSL_get_cipher_list(ssl,i); |
| if (c == NULL) break; |
| |
| env->SetObjectArrayElement(ret,i,env->NewStringUTF(c)); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Sets the ciphers suites that are enabled in the OpenSSL server. |
| */ |
| static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledciphersuites(JNIEnv* env, |
| jobject object, jstring controlstring) |
| { |
| SSL_CTX* ctx; |
| const char *str; |
| int ret; |
| |
| ctx = (SSL_CTX*)env->GetIntField(object, field_ssl_ctx); |
| str = env->GetStringUTFChars(controlstring, 0); |
| ret = SSL_CTX_set_cipher_list(ctx, str); |
| |
| if(ret == 0) { |
| jclass exClass = env->FindClass("java/lang/IllegalArgumentException"); |
| env->ThrowNew(exClass, "Illegal cipher suite strings."); |
| } |
| } |
| |
| /** |
| * 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_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) |
| { |
| SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_ssl_ctx); |
| SSL_CTX_free(ctx); |
| env->SetIntField(object, field_ssl_ctx, 0); |
| } |
| |
| /** |
| * The actual JNI methods' mapping table for the class OpenSSLServerSocketImpl. |
| */ |
| static JNINativeMethod sMethods[] = |
| { |
| {"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}, |
| {"nativegetenabledciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getenabledciphersuites}, |
| {"nativesetenabledciphersuites", "(Ljava/lang/String;)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledciphersuites}, |
| {"nativesetclientauth", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth}, |
| {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree} |
| }; |
| |
| /** |
| * Register the native methods with JNI for the class OpenSSLServerSocketImpl. |
| */ |
| extern "C" int register_org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl(JNIEnv* env) |
| { |
| int ret; |
| jclass clazz; |
| |
| clazz = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl"); |
| |
| if (clazz == NULL) { |
| LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl"); |
| return -1; |
| } |
| |
| ret = jniRegisterNativeMethods(env, "org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl", |
| sMethods, NELEM(sMethods)); |
| |
| if (ret >= 0) { |
| // 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_ssl_ctx = env->GetFieldID(clazz, "ssl_ctx", "I"); |
| if (field_ssl_ctx == NULL) { |
| LOGE("Can't find OpenSSLServerSocketImpl.ssl_ctx"); |
| return -1; |
| } |
| } |
| return ret; |
| } |