1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
| /**
* Perform SSL handshake
*/
static void NativeCrypto_SSL_do_handshake(JNIEnv* env, jclass, jlong ssl_address, CONSCRYPT_UNUSED jobject ssl_holder, jobject fdObject,
jobject shc, jint timeout_millis) {
CHECK_ERROR_QUEUE_ON_RETURN;
SSL* ssl = to_SSL(env, ssl_address, true);
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake fd=%p shc=%p timeout_millis=%d", ssl, fdObject,
shc, timeout_millis);
if (ssl == nullptr) {
return;
}
if (fdObject == nullptr) {
conscrypt::jniutil::throwNullPointerException(env, "fd == null");
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake fd == null => exception", ssl);
return;
}
if (shc == nullptr) {
conscrypt::jniutil::throwNullPointerException(env, "sslHandshakeCallbacks == null");
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake sslHandshakeCallbacks == null => exception",
ssl);
return;
}
NetFd fd(env, fdObject);
if (fd.isClosed()) {
// SocketException thrown by NetFd.isClosed
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake fd.isClosed() => exception", ssl);
return;
}
int ret = SSL_set_fd(ssl, fd.get());
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake s=%d", ssl, fd.get());
if (ret != 1) {
conscrypt::jniutil::throwSSLExceptionWithSslErrors(env, ssl, SSL_ERROR_NONE,
"Error setting the file descriptor");
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake SSL_set_fd => exception", ssl);
return;
}
/*
* Make socket non-blocking, so SSL_connect SSL_read() and SSL_write() don't hang
* forever and we can use select() to find out if the socket is ready.
*/
if (!conscrypt::netutil::setBlocking(fd.get(), false)) {
conscrypt::jniutil::throwSSLExceptionStr(env, "Unable to make socket non blocking");
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake setBlocking => exception", ssl);
return;
}
AppData* appData = toAppData(ssl);
if (appData == nullptr) {
conscrypt::jniutil::throwSSLExceptionStr(env, "Unable to retrieve application data");
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake appData => exception", ssl);
return;
}
ret = 0;
SslError sslError;
while (appData->aliveAndKicking) {
errno = 0;
if (!appData->setCallbackState(env, shc, fdObject)) {
// SocketException thrown by NetFd.isClosed
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake setCallbackState => exception", ssl);
return;
}
ret = SSL_do_handshake(ssl);
appData->clearCallbackState();
// cert_verify_callback threw exception
if (env->ExceptionCheck()) {
ERR_clear_error();
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake exception => exception", ssl);
return;
}
// success case
if (ret == 1) {
break;
}
// retry case
if (errno == EINTR) {
continue;
}
// error case
sslError.reset(ssl, ret);
JNI_TRACE(
"ssl=%p NativeCrypto_SSL_do_handshake ret=%d errno=%d sslError=%d "
"timeout_millis=%d",
ssl, ret, errno, sslError.get(), timeout_millis);
/*
* If SSL_do_handshake doesn't succeed due to the socket being
* either unreadable or unwritable, we use sslSelect to
* wait for it to become ready. If that doesn't happen
* before the specified timeout or an error occurs, we
* cancel the handshake. Otherwise we try the SSL_connect
* again.
*/
if (sslError.get() == SSL_ERROR_WANT_READ || sslError.get() == SSL_ERROR_WANT_WRITE) {
appData->waitingThreads++;
int selectResult = sslSelect(env, sslError.get(), fdObject, appData, timeout_millis);
if (selectResult == THROWN_EXCEPTION) {
// SocketException thrown by NetFd.isClosed
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake sslSelect => exception", ssl);
return;
}
if (selectResult == -1) {
conscrypt::jniutil::throwSSLExceptionWithSslErrors(
env, ssl, SSL_ERROR_SYSCALL, "handshake error",
conscrypt::jniutil::throwSSLHandshakeExceptionStr);
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake selectResult == -1 => exception",
ssl);
return;
}
if (selectResult == 0) {
conscrypt::jniutil::throwSocketTimeoutException(env, "SSL handshake timed out");
ERR_clear_error();
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake selectResult == 0 => exception",
ssl);
return;
}
} else {
// ALOGE("Unknown error %d during handshake", error);
break;
}
}
// clean error. See SSL_do_handshake(3SSL) man page.
if (ret == 0) {
/*
* The other side closed the socket before the handshake could be
* completed, but everything is within the bounds of the TLS protocol.
* We still might want to find out the real reason of the failure.
*/
if (sslError.get() == SSL_ERROR_NONE ||
(sslError.get() == SSL_ERROR_SYSCALL && errno == 0) ||
(sslError.get() == SSL_ERROR_ZERO_RETURN)) {
conscrypt::jniutil::throwSSLHandshakeExceptionStr(env, "Connection closed by peer");
} else {
conscrypt::jniutil::throwSSLExceptionWithSslErrors(
env, ssl, sslError.release(), "SSL handshake terminated",
conscrypt::jniutil::throwSSLHandshakeExceptionStr);
}
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake clean error => exception", ssl);
return;
}
// unclean error. See SSL_do_handshake(3SSL) man page.
if (ret < 0) {
/*
* Translate the error and throw exception. We are sure it is an error
* at this point.
*/
conscrypt::jniutil::throwSSLExceptionWithSslErrors(
env, ssl, sslError.release(), "SSL handshake aborted",
conscrypt::jniutil::throwSSLHandshakeExceptionStr);
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake unclean error => exception", ssl);
return;
}
JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => success", ssl);
}
|