• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <conscrypt/jniutil.h>
18 
19 #include <conscrypt/compat.h>
20 #include <conscrypt/trace.h>
21 #include <cstdlib>
22 #include <errno.h>
23 
24 namespace conscrypt {
25 namespace jniutil {
26 
27 JavaVM *gJavaVM;
28 jclass cryptoUpcallsClass;
29 jclass openSslInputStreamClass;
30 jclass nativeRefClass;
31 
32 jclass byteArrayClass;
33 jclass calendarClass;
34 jclass objectClass;
35 jclass objectArrayClass;
36 jclass integerClass;
37 jclass inputStreamClass;
38 jclass outputStreamClass;
39 jclass stringClass;
40 jclass byteBufferClass;
41 static jclass bufferClass;
42 static jclass fileDescriptorClass;
43 static jclass sslHandshakeCallbacksClass;
44 
45 jfieldID nativeRef_address;
46 static jfieldID fileDescriptor_fd;
47 
48 jmethodID calendar_setMethod;
49 jmethodID inputStream_readMethod;
50 jmethodID integer_valueOfMethod;
51 jmethodID openSslInputStream_readLineMethod;
52 jmethodID outputStream_writeMethod;
53 jmethodID outputStream_flushMethod;
54 jmethodID buffer_positionMethod;
55 jmethodID buffer_limitMethod;
56 jmethodID buffer_isDirectMethod;
57 jmethodID cryptoUpcallsClass_rawSignMethod;
58 jmethodID cryptoUpcallsClass_rsaSignMethod;
59 jmethodID cryptoUpcallsClass_rsaDecryptMethod;
60 jmethodID sslHandshakeCallbacks_verifyCertificateChain;
61 jmethodID sslHandshakeCallbacks_onSSLStateChange;
62 jmethodID sslHandshakeCallbacks_clientCertificateRequested;
63 jmethodID sslHandshakeCallbacks_serverCertificateRequested;
64 jmethodID sslHandshakeCallbacks_clientPSKKeyRequested;
65 jmethodID sslHandshakeCallbacks_serverPSKKeyRequested;
66 jmethodID sslHandshakeCallbacks_onNewSessionEstablished;
67 jmethodID sslHandshakeCallbacks_selectApplicationProtocol;
68 jmethodID sslHandshakeCallbacks_serverSessionRequested;
69 
init(JavaVM * vm,JNIEnv * env)70 void init(JavaVM* vm, JNIEnv* env) {
71     gJavaVM = vm;
72 
73     byteArrayClass = findClass(env, "[B");
74     calendarClass = findClass(env, "java/util/Calendar");
75     inputStreamClass = findClass(env, "java/io/InputStream");
76     integerClass = findClass(env, "java/lang/Integer");
77     objectClass = findClass(env, "java/lang/Object");
78     objectArrayClass = findClass(env, "[Ljava/lang/Object;");
79     outputStreamClass = findClass(env, "java/io/OutputStream");
80     stringClass = findClass(env, "java/lang/String");
81     byteBufferClass = findClass(env, "java/nio/ByteBuffer");
82     bufferClass = findClass(env, "java/nio/Buffer");
83     fileDescriptorClass = findClass(env, "java/io/FileDescriptor");
84 
85     cryptoUpcallsClass = getGlobalRefToClass(
86             env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/CryptoUpcalls");
87     nativeRefClass = getGlobalRefToClass(
88             env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeRef");
89     openSslInputStreamClass = getGlobalRefToClass(
90             env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLBIOInputStream");
91     sslHandshakeCallbacksClass = getGlobalRefToClass(
92             env, TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/NativeCrypto$SSLHandshakeCallbacks");
93 
94     nativeRef_address = getFieldRef(env, nativeRefClass, "address", "J");
95 #if defined(ANDROID) && !defined(CONSCRYPT_OPENJDK)
96     fileDescriptor_fd = getFieldRef(env, fileDescriptorClass, "descriptor", "I");
97 #else /* !ANDROID || CONSCRYPT_OPENJDK */
98     fileDescriptor_fd = getFieldRef(env, fileDescriptorClass, "fd", "I");
99 #endif
100 
101     calendar_setMethod = getMethodRef(env, calendarClass, "set", "(IIIIII)V");
102     inputStream_readMethod = getMethodRef(env, inputStreamClass, "read", "([B)I");
103     integer_valueOfMethod =
104             env->GetStaticMethodID(integerClass, "valueOf", "(I)Ljava/lang/Integer;");
105     openSslInputStream_readLineMethod =
106             getMethodRef(env, openSslInputStreamClass, "gets", "([B)I");
107     outputStream_writeMethod = getMethodRef(env, outputStreamClass, "write", "([B)V");
108     outputStream_flushMethod = getMethodRef(env, outputStreamClass, "flush", "()V");
109     buffer_positionMethod = getMethodRef(env, bufferClass, "position", "()I");
110     buffer_limitMethod = getMethodRef(env, bufferClass, "limit", "()I");
111     buffer_isDirectMethod = getMethodRef(env, bufferClass, "isDirect", "()Z");
112     sslHandshakeCallbacks_verifyCertificateChain = getMethodRef(
113             env, sslHandshakeCallbacksClass, "verifyCertificateChain", "([[BLjava/lang/String;)V");
114     sslHandshakeCallbacks_onSSLStateChange =
115             getMethodRef(env, sslHandshakeCallbacksClass, "onSSLStateChange", "(II)V");
116     sslHandshakeCallbacks_clientCertificateRequested = getMethodRef(
117             env, sslHandshakeCallbacksClass, "clientCertificateRequested", "([B[I[[B)V");
118     sslHandshakeCallbacks_serverCertificateRequested =
119             getMethodRef(env, sslHandshakeCallbacksClass, "serverCertificateRequested", "()V");
120     sslHandshakeCallbacks_clientPSKKeyRequested = getMethodRef(
121             env, sslHandshakeCallbacksClass, "clientPSKKeyRequested", "(Ljava/lang/String;[B[B)I");
122     sslHandshakeCallbacks_serverPSKKeyRequested =
123             getMethodRef(env, sslHandshakeCallbacksClass, "serverPSKKeyRequested",
124                          "(Ljava/lang/String;Ljava/lang/String;[B)I");
125     sslHandshakeCallbacks_onNewSessionEstablished =
126             getMethodRef(env, sslHandshakeCallbacksClass, "onNewSessionEstablished", "(J)V");
127     sslHandshakeCallbacks_serverSessionRequested =
128             getMethodRef(env, sslHandshakeCallbacksClass, "serverSessionRequested", "([B)J");
129     sslHandshakeCallbacks_selectApplicationProtocol =
130             getMethodRef(env, sslHandshakeCallbacksClass, "selectApplicationProtocol", "([B)I");
131     cryptoUpcallsClass_rawSignMethod = env->GetStaticMethodID(
132             cryptoUpcallsClass, "ecSignDigestWithPrivateKey", "(Ljava/security/PrivateKey;[B)[B");
133     if (cryptoUpcallsClass_rawSignMethod == nullptr) {
134         env->FatalError("Could not find ecSignDigestWithPrivateKey");
135     }
136     cryptoUpcallsClass_rsaSignMethod = env->GetStaticMethodID(
137             cryptoUpcallsClass, "rsaSignDigestWithPrivateKey", "(Ljava/security/PrivateKey;I[B)[B");
138     if (cryptoUpcallsClass_rsaSignMethod == nullptr) {
139         env->FatalError("Could not find rsaSignDigestWithPrivateKey");
140     }
141     cryptoUpcallsClass_rsaDecryptMethod = env->GetStaticMethodID(
142             cryptoUpcallsClass, "rsaDecryptWithPrivateKey", "(Ljava/security/PrivateKey;I[B)[B");
143     if (cryptoUpcallsClass_rsaDecryptMethod == nullptr) {
144         env->FatalError("Could not find rsaDecryptWithPrivateKey");
145     }
146 }
147 
jniRegisterNativeMethods(JNIEnv * env,const char * className,const JNINativeMethod * gMethods,int numMethods)148 void jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods,
149                               int numMethods) {
150     CONSCRYPT_LOG_VERBOSE("Registering %s's %d native methods...", className, numMethods);
151 
152     ScopedLocalRef<jclass> c(env, env->FindClass(className));
153     if (c.get() == nullptr) {
154         char* msg;
155         (void)asprintf(&msg, "Native registration unable to find class '%s'; aborting...",
156                        className);
157         env->FatalError(msg);
158     }
159 
160     if (env->RegisterNatives(c.get(), gMethods, numMethods) < 0) {
161         char* msg;
162         (void)asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
163         env->FatalError(msg);
164     }
165 }
166 
jniGetFDFromFileDescriptor(JNIEnv * env,jobject fileDescriptor)167 int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
168     if (fileDescriptor != nullptr) {
169         return env->GetIntField(fileDescriptor, fileDescriptor_fd);
170     } else {
171         return -1;
172     }
173 }
174 
isDirectByteBufferInstance(JNIEnv * env,jobject buffer)175 extern bool isDirectByteBufferInstance(JNIEnv* env, jobject buffer) {
176     // Some versions of ART do not check the buffer validity when handling GetDirectBufferAddress()
177     // and GetDirectBufferCapacity().
178     if (buffer == nullptr) {
179         return false;
180     }
181     if (!env->IsInstanceOf(buffer, conscrypt::jniutil::byteBufferClass)) {
182         return false;
183     }
184     return env->CallBooleanMethod(buffer, conscrypt::jniutil::buffer_isDirectMethod) == JNI_TRUE;
185 }
186 
isGetByteArrayElementsLikelyToReturnACopy(size_t size)187 bool isGetByteArrayElementsLikelyToReturnACopy(size_t size) {
188 #if defined(ANDROID) && !defined(CONSCRYPT_OPENJDK)
189     // ART's GetByteArrayElements creates copies only for arrays smaller than 12 kB.
190     return size <= 12 * 1024;
191 #else
192     (void)size;
193     // On OpenJDK based VMs GetByteArrayElements appears to always create a copy.
194     return true;
195 #endif
196 }
197 
throwException(JNIEnv * env,const char * className,const char * msg)198 int throwException(JNIEnv* env, const char* className, const char* msg) {
199     jclass exceptionClass = env->FindClass(className);
200 
201     if (exceptionClass == nullptr) {
202         CONSCRYPT_LOG_ERROR("Unable to find exception class %s", className);
203         /* ClassNotFoundException now pending */
204         return -1;
205     }
206 
207     if (env->ThrowNew(exceptionClass, msg) != JNI_OK) {
208         CONSCRYPT_LOG_ERROR("Failed throwing '%s' '%s'", className, msg);
209         /* an exception, most likely OOM, will now be pending */
210         return -1;
211     }
212 
213     env->DeleteLocalRef(exceptionClass);
214     return 0;
215 }
216 
throwRuntimeException(JNIEnv * env,const char * msg)217 int throwRuntimeException(JNIEnv* env, const char* msg) {
218     return conscrypt::jniutil::throwException(env, "java/lang/RuntimeException", msg);
219 }
220 
221 #ifdef CONSCRYPT_CHECK_ERROR_QUEUE
throwAssertionError(JNIEnv * env,const char * msg)222 int throwAssertionError(JNIEnv* env, const char* msg) {
223     return conscrypt::jniutil::throwException(env, "java/lang/AssertionError", msg);
224 }
225 #endif
226 
throwNullPointerException(JNIEnv * env,const char * msg)227 int throwNullPointerException(JNIEnv* env, const char* msg) {
228     return conscrypt::jniutil::throwException(env, "java/lang/NullPointerException", msg);
229 }
230 
throwOutOfMemory(JNIEnv * env,const char * message)231 int throwOutOfMemory(JNIEnv* env, const char* message) {
232     return conscrypt::jniutil::throwException(env, "java/lang/OutOfMemoryError", message);
233 }
234 
throwBadPaddingException(JNIEnv * env,const char * message)235 int throwBadPaddingException(JNIEnv* env, const char* message) {
236     JNI_TRACE("throwBadPaddingException %s", message);
237     return conscrypt::jniutil::throwException(env, "javax/crypto/BadPaddingException", message);
238 }
239 
throwSignatureException(JNIEnv * env,const char * message)240 int throwSignatureException(JNIEnv* env, const char* message) {
241     JNI_TRACE("throwSignatureException %s", message);
242     return conscrypt::jniutil::throwException(env, "java/security/SignatureException", message);
243 }
244 
throwInvalidKeyException(JNIEnv * env,const char * message)245 int throwInvalidKeyException(JNIEnv* env, const char* message) {
246     JNI_TRACE("throwInvalidKeyException %s", message);
247     return conscrypt::jniutil::throwException(env, "java/security/InvalidKeyException", message);
248 }
249 
throwIllegalBlockSizeException(JNIEnv * env,const char * message)250 int throwIllegalBlockSizeException(JNIEnv* env, const char* message) {
251     JNI_TRACE("throwIllegalBlockSizeException %s", message);
252     return conscrypt::jniutil::throwException(
253             env, "javax/crypto/IllegalBlockSizeException", message);
254 }
255 
throwShortBufferException(JNIEnv * env,const char * message)256 int throwShortBufferException(JNIEnv* env, const char* message) {
257     JNI_TRACE("throwShortBufferException %s", message);
258     return conscrypt::jniutil::throwException(
259             env, "javax/crypto/ShortBufferException", message);
260 }
261 
throwNoSuchAlgorithmException(JNIEnv * env,const char * message)262 int throwNoSuchAlgorithmException(JNIEnv* env, const char* message) {
263     JNI_TRACE("throwUnknownAlgorithmException %s", message);
264     return conscrypt::jniutil::throwException(
265             env, "java/security/NoSuchAlgorithmException", message);
266 }
267 
throwIOException(JNIEnv * env,const char * message)268 int throwIOException(JNIEnv* env, const char* message) {
269     JNI_TRACE("throwIOException %s", message);
270     return conscrypt::jniutil::throwException(env, "java/io/IOException", message);
271 }
272 
throwCertificateException(JNIEnv * env,const char * message)273 int throwCertificateException(JNIEnv* env, const char* message) {
274     JNI_TRACE("throwCertificateException %s", message);
275     return conscrypt::jniutil::throwException(
276             env, "java/security/cert/CertificateException", message);
277 }
278 
throwParsingException(JNIEnv * env,const char * message)279 int throwParsingException(JNIEnv* env, const char* message) {
280     return conscrypt::jniutil::throwException(env, TO_STRING(JNI_JARJAR_PREFIX)
281                             "org/conscrypt/OpenSSLX509CertificateFactory$ParsingException",
282                             message);
283 }
284 
throwInvalidAlgorithmParameterException(JNIEnv * env,const char * message)285 int throwInvalidAlgorithmParameterException(JNIEnv* env, const char* message) {
286     JNI_TRACE("throwInvalidAlgorithmParameterException %s", message);
287     return conscrypt::jniutil::throwException(
288             env, "java/security/InvalidAlgorithmParameterException", message);
289 }
290 
throwForAsn1Error(JNIEnv * env,int reason,const char * message,int (* defaultThrow)(JNIEnv *,const char *))291 int throwForAsn1Error(JNIEnv* env, int reason, const char* message,
292                       int (*defaultThrow)(JNIEnv*, const char*)) {
293     switch (reason) {
294         case ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE:
295         case ASN1_R_WRONG_PUBLIC_KEY_TYPE:
296             return throwInvalidKeyException(env, message);
297             break;
298         case ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM:
299         case ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM:
300             return throwNoSuchAlgorithmException(env, message);
301             break;
302     }
303     return defaultThrow(env, message);
304 }
305 
throwForCipherError(JNIEnv * env,int reason,const char * message,int (* defaultThrow)(JNIEnv *,const char *))306 int throwForCipherError(JNIEnv* env, int reason, const char* message,
307                         int (*defaultThrow)(JNIEnv*, const char*)) {
308     switch (reason) {
309         case CIPHER_R_BAD_DECRYPT:
310             return throwBadPaddingException(env, message);
311             break;
312         case CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
313         case CIPHER_R_WRONG_FINAL_BLOCK_LENGTH:
314             return throwIllegalBlockSizeException(env, message);
315             break;
316         // TODO(davidben): Remove these ifdefs after
317         // https://boringssl-review.googlesource.com/c/boringssl/+/35565 has
318         // rolled out to relevant BoringSSL copies.
319 #if defined(CIPHER_R_BAD_KEY_LENGTH)
320         case CIPHER_R_BAD_KEY_LENGTH:
321 #endif
322 #if defined(CIPHER_R_UNSUPPORTED_KEY_SIZE)
323         case CIPHER_R_UNSUPPORTED_KEY_SIZE:
324 #endif
325         case CIPHER_R_INVALID_KEY_LENGTH:
326             return throwInvalidKeyException(env, message);
327             break;
328         case CIPHER_R_BUFFER_TOO_SMALL:
329             return throwShortBufferException(env, message);
330             break;
331     }
332     return defaultThrow(env, message);
333 }
334 
throwForEvpError(JNIEnv * env,int reason,const char * message,int (* defaultThrow)(JNIEnv *,const char *))335 int throwForEvpError(JNIEnv* env, int reason, const char* message,
336                      int (*defaultThrow)(JNIEnv*, const char*)) {
337     switch (reason) {
338         case EVP_R_MISSING_PARAMETERS:
339             return throwInvalidKeyException(env, message);
340             break;
341         case EVP_R_UNSUPPORTED_ALGORITHM:
342             return throwNoSuchAlgorithmException(env, message);
343             break;
344         default:
345             return defaultThrow(env, message);
346             break;
347     }
348 }
349 
throwForRsaError(JNIEnv * env,int reason,const char * message,int (* defaultThrow)(JNIEnv *,const char *))350 int throwForRsaError(JNIEnv* env, int reason, const char* message,
351                      int (*defaultThrow)(JNIEnv*, const char*)) {
352     switch (reason) {
353         case RSA_R_BLOCK_TYPE_IS_NOT_01:
354         case RSA_R_PKCS_DECODING_ERROR:
355             return throwBadPaddingException(env, message);
356             break;
357         case RSA_R_BAD_SIGNATURE:
358         case RSA_R_INVALID_MESSAGE_LENGTH:
359         case RSA_R_WRONG_SIGNATURE_LENGTH:
360             return throwSignatureException(env, message);
361             break;
362         case RSA_R_UNKNOWN_ALGORITHM_TYPE:
363             return throwNoSuchAlgorithmException(env, message);
364             break;
365         case RSA_R_MODULUS_TOO_LARGE:
366         case RSA_R_NO_PUBLIC_EXPONENT:
367             return throwInvalidKeyException(env, message);
368             break;
369         case RSA_R_DATA_TOO_LARGE:
370         case RSA_R_DATA_TOO_LARGE_FOR_MODULUS:
371         case RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE:
372             return throwIllegalBlockSizeException(env, message);
373             break;
374     }
375     return defaultThrow(env, message);
376 }
377 
throwForX509Error(JNIEnv * env,int reason,const char * message,int (* defaultThrow)(JNIEnv *,const char *))378 int throwForX509Error(JNIEnv* env, int reason, const char* message,
379                       int (*defaultThrow)(JNIEnv*, const char*)) {
380     switch (reason) {
381         case X509_R_UNSUPPORTED_ALGORITHM:
382             return throwNoSuchAlgorithmException(env, message);
383             break;
384         default:
385             return defaultThrow(env, message);
386             break;
387     }
388 }
389 
throwExceptionFromBoringSSLError(JNIEnv * env,CONSCRYPT_UNUSED const char * location,int (* defaultThrow)(JNIEnv *,const char *))390 void throwExceptionFromBoringSSLError(JNIEnv* env, CONSCRYPT_UNUSED const char* location,
391                                       int (*defaultThrow)(JNIEnv*, const char*)) {
392     const char* file;
393     int line;
394     const char* data;
395     int flags;
396     // NOLINTNEXTLINE(runtime/int)
397     unsigned long error = ERR_get_error_line_data(&file, &line, &data, &flags);
398 
399     if (error == 0) {
400         defaultThrow(env, "Unknown BoringSSL error");
401         return;
402     }
403 
404     // If there's an error from BoringSSL it may have been caused by an exception in Java code, so
405     // ensure there isn't a pending exception before we throw a new one.
406     if (!env->ExceptionCheck()) {
407         char message[256];
408         ERR_error_string_n(error, message, sizeof(message));
409         int library = ERR_GET_LIB(error);
410         int reason = ERR_GET_REASON(error);
411         JNI_TRACE("BoringSSL error in %s error=%lx library=%x reason=%x (%s:%d): %s %s", location,
412                   error, library, reason, file, line, message,
413                   (flags & ERR_TXT_STRING) ? data : "(no data)");
414         switch (library) {
415             case ERR_LIB_RSA:
416                 throwForRsaError(env, reason, message, defaultThrow);
417                 break;
418             case ERR_LIB_ASN1:
419                 throwForAsn1Error(env, reason, message, defaultThrow);
420                 break;
421             case ERR_LIB_CIPHER:
422                 throwForCipherError(env, reason, message, defaultThrow);
423                 break;
424             case ERR_LIB_EVP:
425                 throwForEvpError(env, reason, message, defaultThrow);
426                 break;
427             case ERR_LIB_X509:
428                 throwForX509Error(env, reason, message, defaultThrow);
429                 break;
430             case ERR_LIB_DSA:
431                 throwInvalidKeyException(env, message);
432                 break;
433             default:
434                 defaultThrow(env, message);
435                 break;
436         }
437     }
438 
439     ERR_clear_error();
440 }
441 
throwSocketTimeoutException(JNIEnv * env,const char * message)442 int throwSocketTimeoutException(JNIEnv* env, const char* message) {
443     JNI_TRACE("throwSocketTimeoutException %s", message);
444     return conscrypt::jniutil::throwException(env, "java/net/SocketTimeoutException", message);
445 }
446 
throwSSLHandshakeExceptionStr(JNIEnv * env,const char * message)447 int throwSSLHandshakeExceptionStr(JNIEnv* env, const char* message) {
448     JNI_TRACE("throwSSLExceptionStr %s", message);
449     return conscrypt::jniutil::throwException(
450             env, "javax/net/ssl/SSLHandshakeException", message);
451 }
452 
throwSSLExceptionStr(JNIEnv * env,const char * message)453 int throwSSLExceptionStr(JNIEnv* env, const char* message) {
454     JNI_TRACE("throwSSLExceptionStr %s", message);
455     return conscrypt::jniutil::throwException(env, "javax/net/ssl/SSLException", message);
456 }
457 
throwSSLProtocolExceptionStr(JNIEnv * env,const char * message)458 int throwSSLProtocolExceptionStr(JNIEnv* env, const char* message) {
459     JNI_TRACE("throwSSLProtocolExceptionStr %s", message);
460     return conscrypt::jniutil::throwException(
461             env, "javax/net/ssl/SSLProtocolException", message);
462 }
463 
throwSSLExceptionWithSslErrors(JNIEnv * env,SSL * ssl,int sslErrorCode,const char * message,int (* actualThrow)(JNIEnv *,const char *))464 int throwSSLExceptionWithSslErrors(JNIEnv* env, SSL* ssl, int sslErrorCode, const char* message,
465                                    int (*actualThrow)(JNIEnv*, const char*)) {
466     if (message == nullptr) {
467         message = "SSL error";
468     }
469 
470     // First consult the SSL error code for the general message.
471     const char* sslErrorStr = nullptr;
472     switch (sslErrorCode) {
473         case SSL_ERROR_NONE:
474             if (ERR_peek_error() == 0) {
475                 sslErrorStr = "OK";
476             } else {
477                 sslErrorStr = "";
478             }
479             break;
480         case SSL_ERROR_SSL:
481             sslErrorStr = "Failure in SSL library, usually a protocol error";
482             break;
483         case SSL_ERROR_WANT_READ:
484             sslErrorStr = "SSL_ERROR_WANT_READ occurred. You should never see this.";
485             break;
486         case SSL_ERROR_WANT_WRITE:
487             sslErrorStr = "SSL_ERROR_WANT_WRITE occurred. You should never see this.";
488             break;
489         case SSL_ERROR_WANT_X509_LOOKUP:
490             sslErrorStr = "SSL_ERROR_WANT_X509_LOOKUP occurred. You should never see this.";
491             break;
492         case SSL_ERROR_SYSCALL:
493             sslErrorStr = "I/O error during system call";
494             break;
495         case SSL_ERROR_ZERO_RETURN:
496             sslErrorStr = "SSL_ERROR_ZERO_RETURN occurred. You should never see this.";
497             break;
498         case SSL_ERROR_WANT_CONNECT:
499             sslErrorStr = "SSL_ERROR_WANT_CONNECT occurred. You should never see this.";
500             break;
501         case SSL_ERROR_WANT_ACCEPT:
502             sslErrorStr = "SSL_ERROR_WANT_ACCEPT occurred. You should never see this.";
503             break;
504         default:
505             sslErrorStr = "Unknown SSL error";
506     }
507 
508     // Prepend either our explicit message or a default one.
509     char* str;
510     if (asprintf(&str, "%s: ssl=%p: %s", message, ssl, sslErrorStr) <= 0) {
511         // problem with asprintf, just throw argument message, log everything
512         int ret = actualThrow(env, message);
513         CONSCRYPT_LOG_VERBOSE("%s: ssl=%p: %s", message, ssl, sslErrorStr);
514         ERR_clear_error();
515         return ret;
516     }
517 
518     char* allocStr = str;
519 
520     // For protocol errors, SSL might have more information.
521     if (sslErrorCode == SSL_ERROR_NONE || sslErrorCode == SSL_ERROR_SSL) {
522         // Append each error as an additional line to the message.
523         for (;;) {
524             char errStr[256];
525             const char* file;
526             int line;
527             const char* data;
528             int flags;
529             // NOLINTNEXTLINE(runtime/int)
530             unsigned long err = ERR_get_error_line_data(&file, &line, &data, &flags);
531             if (err == 0) {
532                 break;
533             }
534 
535             ERR_error_string_n(err, errStr, sizeof(errStr));
536 
537             int ret = asprintf(&str, "%s\n%s (%s:%d %p:0x%08x)",
538                                (allocStr == nullptr) ? "" : allocStr, errStr, file, line,
539                                (flags & ERR_TXT_STRING) ? data : "(no data)", flags);
540 
541             if (ret < 0) {
542                 break;
543             }
544 
545             free(allocStr);
546             allocStr = str;
547         }
548         // For errors during system calls, errno might be our friend.
549     } else if (sslErrorCode == SSL_ERROR_SYSCALL) {
550         if (asprintf(&str, "%s, %s", allocStr, strerror(errno)) >= 0) {
551             free(allocStr);
552             allocStr = str;
553         }
554         // If the error code is invalid, print it.
555     } else if (sslErrorCode > SSL_ERROR_WANT_ACCEPT) {
556         if (asprintf(&str, ", error code is %d", sslErrorCode) >= 0) {
557             free(allocStr);
558             allocStr = str;
559         }
560     }
561 
562     int ret;
563     if (sslErrorCode == SSL_ERROR_SSL) {
564         ret = throwSSLProtocolExceptionStr(env, allocStr);
565     } else {
566         ret = actualThrow(env, allocStr);
567     }
568 
569     CONSCRYPT_LOG_VERBOSE("%s", allocStr);
570     free(allocStr);
571     ERR_clear_error();
572     return ret;
573 }
574 
575 }  // namespace jniutil
576 }  // namespace conscrypt
577