NativeCrypto: special case for empty cipher list

For the Java language, setting an empty cipher list is not an error but
it's an error in OpenSSL. However, the underlying API actually updates
the cipher list to an empty string as intended. So we need to handle
this special case by clearing the error stack and making sure that our
expectation is satisfied.

(cherry picked from commit 5b6a5ecc98d1798d806024160fe97738527980d1)

Bug: 21195269
Change-Id: Id21792215513f4e0d6e051160f69e5f830d39015
diff --git a/src/main/native/org_conscrypt_NativeCrypto.cpp b/src/main/native/org_conscrypt_NativeCrypto.cpp
index ea22091..ae746e9 100644
--- a/src/main/native/org_conscrypt_NativeCrypto.cpp
+++ b/src/main/native/org_conscrypt_NativeCrypto.cpp
@@ -8611,9 +8611,8 @@
 /**
  * Sets the ciphers suites that are enabled in the SSL
  */
-static void NativeCrypto_SSL_set_cipher_lists(JNIEnv* env, jclass,
-        jlong ssl_address, jobjectArray cipherSuites)
-{
+static void NativeCrypto_SSL_set_cipher_lists(JNIEnv* env, jclass, jlong ssl_address,
+                                              jobjectArray cipherSuites) {
     SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_set_cipher_lists cipherSuites=%p", ssl, cipherSuites);
     if (ssl == NULL) {
@@ -8625,6 +8624,25 @@
     }
 
     int length = env->GetArrayLength(cipherSuites);
+
+    /*
+     * Special case for empty cipher list. This is considered an error by the
+     * SSL_set_cipher_list API, but Java allows this silly configuration.
+     * However, the SSL cipher list is still set even when SSL_set_cipher_list
+     * returns 0 in this case. Just to make sure, we check the resulting cipher
+     * list to make sure it's zero length.
+     */
+    if (length == 0) {
+        JNI_TRACE("ssl=%p NativeCrypto_SSL_set_cipher_lists cipherSuites=empty", ssl);
+        SSL_set_cipher_list(ssl, "");
+        freeOpenSslErrorState();
+        if (sk_SSL_CIPHER_num(SSL_get_ciphers(ssl)) != 0) {
+            JNI_TRACE("ssl=%p NativeCrypto_SSL_set_cipher_lists cipherSuites=empty => error", ssl);
+            jniThrowRuntimeException(env, "SSL_set_cipher_list did not update ciphers!");
+        }
+        return;
+    }
+
     static const char noSSLv2[] = "!SSLv2";
     size_t cipherStringLen = strlen(noSSLv2);
 
@@ -8683,6 +8701,7 @@
         return;
     }
 
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_set_cipher_lists cipherSuites=%s", ssl, cipherString.get());
     if (!SSL_set_cipher_list(ssl, cipherString.get())) {
         freeOpenSslErrorState();
         jniThrowException(env, "java/lang/IllegalArgumentException",