diff --git a/build.gradle b/build.gradle index a3952384..6a86f26f 100644 --- a/build.gradle +++ b/build.gradle @@ -104,6 +104,16 @@ publishing { publications { maven(MavenPublication) { from components.java + pom { + url = 'https://github.com/linkedin/ktls-jni' + licenses { + license { + name = 'BSD 2-Clause License' + url = 'https://opensource.org/licenses/BSD-2-Clause' + distribution = 'repo' + } + } + } } } } diff --git a/src/main/cpp/KernelTLSNativeHelper.cpp b/src/main/cpp/KernelTLSNativeHelper.cpp index 84767b99..76925deb 100644 --- a/src/main/cpp/KernelTLSNativeHelper.cpp +++ b/src/main/cpp/KernelTLSNativeHelper.cpp @@ -40,13 +40,20 @@ int enableTlsWithCryptoInfo(int socketFd, bool sendingMode, void* crypto_info, u return 0; } -void copyArray(JNIEnv *env, jbyteArray &src, unsigned char *dest) { +int copyArray(JNIEnv *env, jbyteArray &src, unsigned char *dest, size_t destSize) { jbyte* srcArr = env->GetByteArrayElements(src, NULL); jsize len = env->GetArrayLength(src); + + if (len > destSize) { + env->ReleaseByteArrayElements(src, srcArr, JNI_ABORT); + return -1; + } + for (int idx = 0; idx < len; idx++) { dest[idx] = (unsigned char) srcArr[idx]; } env->ReleaseByteArrayElements(src, srcArr, JNI_ABORT); + return len; } #endif @@ -59,10 +66,12 @@ JNIEXPORT jint JNICALL Java_com_linkedin_ktls_KernelTLSNativeHelper_enableKernel struct tls12_crypto_info_aes_gcm_128 crypto_info; crypto_info.info.version = versionCode; crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128; - copyArray(env, iv, crypto_info.iv); - copyArray(env, key, crypto_info.key); - copyArray(env, salt, crypto_info.salt); - copyArray(env, rec_seq, crypto_info.rec_seq); + int result_iv = copyArray(env, iv, crypto_info.iv, sizeof(crypto_info.iv)); + int result_key = copyArray(env, key, crypto_info.key, sizeof(crypto_info.key)); + int result_salt = copyArray(env, salt, crypto_info.salt, sizeof(crypto_info.salt)); + int result_rec_seq = copyArray(env, rec_seq, crypto_info.rec_seq, sizeof(crypto_info.rec_seq)); + if(result_iv == -1 || result_key == -1 || result_salt == -1 || result_rec_seq == -1) + return com_linkedin_ktls_KernelTLSNativeHelper_BUFFER_OVERRUN; return enableTlsWithCryptoInfo(socketFd, true, &crypto_info, sizeof(crypto_info)); #endif } @@ -76,10 +85,12 @@ JNIEXPORT jint JNICALL Java_com_linkedin_ktls_KernelTLSNativeHelper_enableKernel struct tls12_crypto_info_aes_gcm_256 crypto_info; crypto_info.info.version = versionCode; crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_256; - copyArray(env, iv, crypto_info.iv); - copyArray(env, key, crypto_info.key); - copyArray(env, salt, crypto_info.salt); - copyArray(env, rec_seq, crypto_info.rec_seq); + int result_iv = copyArray(env, iv, crypto_info.iv, sizeof(crypto_info.iv)); + int result_key = copyArray(env, key, crypto_info.key, sizeof(crypto_info.key)); + int result_salt = copyArray(env, salt, crypto_info.salt, sizeof(crypto_info.salt)); + int result_rec_seq = copyArray(env, rec_seq, crypto_info.rec_seq, sizeof(crypto_info.rec_seq)); + if(result_iv == -1 || result_key == -1 || result_salt == -1 || result_rec_seq == -1) + return com_linkedin_ktls_KernelTLSNativeHelper_BUFFER_OVERRUN; return enableTlsWithCryptoInfo(socketFd, true, &crypto_info, sizeof(crypto_info)); #endif } @@ -93,10 +104,12 @@ JNIEXPORT jint JNICALL Java_com_linkedin_ktls_KernelTLSNativeHelper_enableKernel struct tls12_crypto_info_chacha20_poly1305 crypto_info; crypto_info.info.version = versionCode; crypto_info.info.cipher_type = TLS_CIPHER_CHACHA20_POLY1305; - copyArray(env, iv, crypto_info.iv); - copyArray(env, key, crypto_info.key); - copyArray(env, salt, crypto_info.salt); - copyArray(env, rec_seq, crypto_info.rec_seq); + int result_iv = copyArray(env, iv, crypto_info.iv, sizeof(crypto_info.iv)); + int result_key = copyArray(env, key, crypto_info.key, sizeof(crypto_info.key)); + int result_salt = copyArray(env, salt, crypto_info.salt, sizeof(crypto_info.salt)); + int result_rec_seq = copyArray(env, rec_seq, crypto_info.rec_seq, sizeof(crypto_info.rec_seq)); + if(result_iv == -1 || result_key == -1 || result_salt == -1 || result_rec_seq == -1) + return com_linkedin_ktls_KernelTLSNativeHelper_BUFFER_OVERRUN; return enableTlsWithCryptoInfo(socketFd, true, &crypto_info, sizeof(crypto_info)); #endif } diff --git a/src/main/include/jni_generated/com_linkedin_ktls_KernelTLSNativeHelper.h b/src/main/include/jni_generated/com_linkedin_ktls_KernelTLSNativeHelper.h index 83619d90..82907f58 100644 --- a/src/main/include/jni_generated/com_linkedin_ktls_KernelTLSNativeHelper.h +++ b/src/main/include/jni_generated/com_linkedin_ktls_KernelTLSNativeHelper.h @@ -17,6 +17,8 @@ extern "C" { #define com_linkedin_ktls_KernelTLSNativeHelper_UNABLE_TO_SET_TLS_MODE 6004L #undef com_linkedin_ktls_KernelTLSNativeHelper_UNABLE_TO_SET_TLS_PARAMS #define com_linkedin_ktls_KernelTLSNativeHelper_UNABLE_TO_SET_TLS_PARAMS 6005L +#undef com_linkedin_ktls_KernelTLSNativeHelper_BUFFER_OVERRUN +#define com_linkedin_ktls_KernelTLSNativeHelper_BUFFER_OVERRUN 6006L #undef com_linkedin_ktls_KernelTLSNativeHelper_RECORD_TYPE_ALERT #define com_linkedin_ktls_KernelTLSNativeHelper_RECORD_TYPE_ALERT 21L #undef com_linkedin_ktls_KernelTLSNativeHelper_ALERT_LEVEL_WARNING diff --git a/src/main/java/com/linkedin/ktls/KernelTLSNativeHelper.java b/src/main/java/com/linkedin/ktls/KernelTLSNativeHelper.java index e12b2cee..f0ce3259 100644 --- a/src/main/java/com/linkedin/ktls/KernelTLSNativeHelper.java +++ b/src/main/java/com/linkedin/ktls/KernelTLSNativeHelper.java @@ -17,6 +17,7 @@ class KernelTLSNativeHelper { private static final int UNSUPPORTED_OPERATION = 6003; private static final int UNABLE_TO_SET_TLS_MODE = 6004; private static final int UNABLE_TO_SET_TLS_PARAMS = 6005; + private static final int BUFFER_OVERRUN = 6006; private static final byte RECORD_TYPE_ALERT = 21; private static final byte ALERT_LEVEL_WARNING = 1; @@ -26,16 +27,33 @@ class KernelTLSNativeHelper { Native.load(); } + /** + * Extracts the file descriptor associated with the given SocketChannel. + * + * @param socketChannel The SocketChannel from which to extract the file descriptor. + * @return The integer file descriptor associated with the SocketChannel. + * @throws IllegalArgumentException If there is an error while extracting the file descriptor. + */ int extractFd(SocketChannel socketChannel) { try { + // Retrieve the 'fd' field from the 'sun.nio.ch.SocketChannelImpl' class using ReflectionUtils. final Object fileDescriptor = ReflectionUtils.getValueAtField("sun.nio.ch.SocketChannelImpl", "fd", socketChannel); + // Convert the result to an integer and return it as the file descriptor. return (int) ReflectionUtils.getValueAtField("java.io.FileDescriptor", "fd", fileDescriptor); } catch (Exception e) { + // If there is any exception while extracting the file descriptor, wrap it in an IllegalArgumentException + // and rethrow it to signal an error. throw new IllegalArgumentException(e); } } + /** + * This function tries to enable kernelTLS for send based on the symmetric cipher value. + * @param socketChannel SocketChannel object to enable kernelTLS on + * @param tlsParameters TlsParameters with the symmetric cipher based on which we decide if kernel TLS can be enabled. + * @throws KTLSEnableFailedException + */ void enableKernelTlsForSend(SocketChannel socketChannel, TlsParameters tlsParameters) throws KTLSEnableFailedException { final int fd; @@ -62,27 +80,47 @@ void enableKernelTlsForSend(SocketChannel socketChannel, TlsParameters tlsParame throw new IllegalStateException(); } if (retCode != 0) { - throw buildExceptionForReturnCode(retCode); + throw buildExceptionForReturnCode(retCode, tlsParameters.symmetricCipher); } } - private KTLSEnableFailedException buildExceptionForReturnCode(int retCode) { + /** + * This function is to throw the respective exception based on the return value from the underlying JNI kernel TLS enable call. + * Also used OS version and symmetric cipher version for logging the exceptions. + * @param retCode Return value after performing kernel TLS enabled send + * @param symmetricCipher Symmetric cipher used for logging in the exceptions. + * @return + */ + private KTLSEnableFailedException buildExceptionForReturnCode(int retCode, SymmetricCipher symmetricCipher) { + String osVersion = System.getProperty("os.version"); switch (retCode) { case UNSUPPORTED_OPERATING_SYSTEM: - return new KTLSEnableFailedException("ktls-jni was not built with support for this operating system"); + return new KTLSEnableFailedException( + "ktls-jni was not built with support for this operating system. os version: " + osVersion + + ", symmetricCipher: " + symmetricCipher); case UNSUPPORTED_CIPHER: - return new KTLSEnableFailedException("ktls-jni was not built with support for the specified cipher"); + return new KTLSEnableFailedException( + "ktls-jni was not built with support for the specified cipher. os version: " + osVersion + + ", symmetricCipher: " + symmetricCipher); case UNSUPPORTED_OPERATION: - return new KTLSEnableFailedException("This action is not supported."); + return new KTLSEnableFailedException( + "This action is not supported. os version: " + osVersion + ", symmetricCipher: " + symmetricCipher); case UNABLE_TO_SET_TLS_MODE: return new KTLSEnableFailedException("Unable to set socket to TLS mode. " - + "This may indicate that the \"tls\" kernel module is not enabled."); + + "This may indicate that the \"tls\" kernel module is not enabled on os version: " + osVersion + + ", symmetricCipher: " + symmetricCipher); case UNABLE_TO_SET_TLS_PARAMS: return new KTLSEnableFailedException("Unable to set TLS parameters on socket. " - + "This is an unexpected scenario and needs further investigation."); + + "This is an unexpected scenario and needs further investigation. os version: " + osVersion + + ", symmetricCipher: " + symmetricCipher); + case BUFFER_OVERRUN: + return new KTLSEnableFailedException( + "Found buffer overrun during copy array call. os version: " + osVersion + ", symmetricCipher: " + + symmetricCipher); default: return new KTLSEnableFailedException(String.format( - "Unexpected error when trying to initialize Kernel TLS, return code %s", retCode)); + "Unexpected error when trying to initialize Kernel TLS, return code %s. os version : %s , symmetricCipher: %s", + retCode, osVersion, symmetricCipher)); } } @@ -93,6 +131,10 @@ private native int enableKernelTlsForSend_AES_256_GCM( private native int enableKernelTlsForSend_CHACHA20_POLY1305( int fd, int version_code, byte[] iv, byte[] key, byte[] salt, byte[] rec_seq); + /** + * This method is used to populate the supported symmetric ciphers using an ciphers array returned from NativeHelper.cpp. + * @return List containing the cipher suites list. + */ public List getSupportedCipherSuites() { final Set supportedSymmetricCiphers = new HashSet<>(Arrays.asList(getSupportedSymmetricCiphers())); return Arrays.stream(CipherSuite.values()) @@ -103,6 +145,11 @@ public List getSupportedCipherSuites() { private native String[] getSupportedSymmetricCiphers(); + /** + * This method is used to extract the file descriptor and send a close alert to the file descriptor. + * @param socketChannel SocketChannel object that is to be closed + * @throws IOException + */ public void sendCloseNotify(SocketChannel socketChannel) throws IOException { final int socketFd = extractFd(socketChannel); final byte[] data = new byte[2];