1 /*************************************************************************/
2 /*  stream_peer_mbedtls.cpp                                              */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 
31 #include "stream_peer_mbedtls.h"
32 
33 #include "core/io/stream_peer_tcp.h"
34 #include "core/os/file_access.h"
35 
bio_send(void * ctx,const unsigned char * buf,size_t len)36 int StreamPeerMbedTLS::bio_send(void *ctx, const unsigned char *buf, size_t len) {
37 
38 	if (buf == NULL || len <= 0) return 0;
39 
40 	StreamPeerMbedTLS *sp = (StreamPeerMbedTLS *)ctx;
41 
42 	ERR_FAIL_COND_V(sp == NULL, 0);
43 
44 	int sent;
45 	Error err = sp->base->put_partial_data((const uint8_t *)buf, len, sent);
46 	if (err != OK) {
47 		return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
48 	}
49 	if (sent == 0) {
50 		return MBEDTLS_ERR_SSL_WANT_WRITE;
51 	}
52 	return sent;
53 }
54 
bio_recv(void * ctx,unsigned char * buf,size_t len)55 int StreamPeerMbedTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) {
56 
57 	if (buf == NULL || len <= 0) return 0;
58 
59 	StreamPeerMbedTLS *sp = (StreamPeerMbedTLS *)ctx;
60 
61 	ERR_FAIL_COND_V(sp == NULL, 0);
62 
63 	int got;
64 	Error err = sp->base->get_partial_data((uint8_t *)buf, len, got);
65 	if (err != OK) {
66 		return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
67 	}
68 	if (got == 0) {
69 		return MBEDTLS_ERR_SSL_WANT_READ;
70 	}
71 	return got;
72 }
73 
_cleanup()74 void StreamPeerMbedTLS::_cleanup() {
75 
76 	ssl_ctx->clear();
77 	base = Ref<StreamPeer>();
78 	status = STATUS_DISCONNECTED;
79 }
80 
_do_handshake()81 Error StreamPeerMbedTLS::_do_handshake() {
82 	int ret = 0;
83 	while ((ret = mbedtls_ssl_handshake(ssl_ctx->get_context())) != 0) {
84 		if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
85 			// An error occurred.
86 			ERR_PRINT("TLS handshake error: " + itos(ret));
87 			SSLContextMbedTLS::print_mbedtls_error(ret);
88 			disconnect_from_stream();
89 			status = STATUS_ERROR;
90 			return FAILED;
91 		}
92 
93 		// Handshake is still in progress.
94 		if (!blocking_handshake) {
95 			// Will retry via poll later
96 			return OK;
97 		}
98 	}
99 
100 	status = STATUS_CONNECTED;
101 	return OK;
102 }
103 
connect_to_stream(Ref<StreamPeer> p_base,bool p_validate_certs,const String & p_for_hostname,Ref<X509Certificate> p_ca_certs)104 Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs, const String &p_for_hostname, Ref<X509Certificate> p_ca_certs) {
105 
106 	ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER);
107 
108 	base = p_base;
109 	int authmode = p_validate_certs ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE;
110 
111 	Error err = ssl_ctx->init_client(MBEDTLS_SSL_TRANSPORT_STREAM, authmode, p_ca_certs);
112 	ERR_FAIL_COND_V(err != OK, err);
113 
114 	mbedtls_ssl_set_hostname(ssl_ctx->get_context(), p_for_hostname.utf8().get_data());
115 	mbedtls_ssl_set_bio(ssl_ctx->get_context(), this, bio_send, bio_recv, NULL);
116 
117 	status = STATUS_HANDSHAKING;
118 
119 	if (_do_handshake() != OK) {
120 		status = STATUS_ERROR_HOSTNAME_MISMATCH;
121 		return FAILED;
122 	}
123 
124 	return OK;
125 }
126 
accept_stream(Ref<StreamPeer> p_base,Ref<CryptoKey> p_key,Ref<X509Certificate> p_cert,Ref<X509Certificate> p_ca_chain)127 Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain) {
128 
129 	ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER);
130 
131 	Error err = ssl_ctx->init_server(MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_VERIFY_NONE, p_key, p_cert);
132 	ERR_FAIL_COND_V(err != OK, err);
133 
134 	base = p_base;
135 
136 	mbedtls_ssl_set_bio(ssl_ctx->get_context(), this, bio_send, bio_recv, NULL);
137 
138 	status = STATUS_HANDSHAKING;
139 
140 	if (_do_handshake() != OK) {
141 		return FAILED;
142 	}
143 
144 	status = STATUS_CONNECTED;
145 	return OK;
146 }
put_data(const uint8_t * p_data,int p_bytes)147 Error StreamPeerMbedTLS::put_data(const uint8_t *p_data, int p_bytes) {
148 
149 	ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
150 
151 	Error err;
152 	int sent = 0;
153 
154 	while (p_bytes > 0) {
155 		err = put_partial_data(p_data, p_bytes, sent);
156 
157 		if (err != OK) {
158 			return err;
159 		}
160 
161 		p_data += sent;
162 		p_bytes -= sent;
163 	}
164 
165 	return OK;
166 }
167 
put_partial_data(const uint8_t * p_data,int p_bytes,int & r_sent)168 Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
169 
170 	ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
171 
172 	r_sent = 0;
173 
174 	if (p_bytes == 0)
175 		return OK;
176 
177 	int ret = mbedtls_ssl_write(ssl_ctx->get_context(), p_data, p_bytes);
178 	if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
179 		// Non blocking IO
180 		ret = 0;
181 	} else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
182 		// Clean close
183 		disconnect_from_stream();
184 		return ERR_FILE_EOF;
185 	} else if (ret <= 0) {
186 		SSLContextMbedTLS::print_mbedtls_error(ret);
187 		disconnect_from_stream();
188 		return ERR_CONNECTION_ERROR;
189 	}
190 
191 	r_sent = ret;
192 	return OK;
193 }
194 
get_data(uint8_t * p_buffer,int p_bytes)195 Error StreamPeerMbedTLS::get_data(uint8_t *p_buffer, int p_bytes) {
196 
197 	ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
198 
199 	Error err;
200 
201 	int got = 0;
202 	while (p_bytes > 0) {
203 
204 		err = get_partial_data(p_buffer, p_bytes, got);
205 
206 		if (err != OK) {
207 			return err;
208 		}
209 
210 		p_buffer += got;
211 		p_bytes -= got;
212 	}
213 
214 	return OK;
215 }
216 
get_partial_data(uint8_t * p_buffer,int p_bytes,int & r_received)217 Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
218 
219 	ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
220 
221 	r_received = 0;
222 
223 	int ret = mbedtls_ssl_read(ssl_ctx->get_context(), p_buffer, p_bytes);
224 	if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
225 		ret = 0; // non blocking io
226 	} else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
227 		// Clean close
228 		disconnect_from_stream();
229 		return ERR_FILE_EOF;
230 	} else if (ret <= 0) {
231 		SSLContextMbedTLS::print_mbedtls_error(ret);
232 		disconnect_from_stream();
233 		return ERR_CONNECTION_ERROR;
234 	}
235 
236 	r_received = ret;
237 	return OK;
238 }
239 
poll()240 void StreamPeerMbedTLS::poll() {
241 
242 	ERR_FAIL_COND(status != STATUS_CONNECTED && status != STATUS_HANDSHAKING);
243 	ERR_FAIL_COND(!base.is_valid());
244 
245 	if (status == STATUS_HANDSHAKING) {
246 		_do_handshake();
247 		return;
248 	}
249 
250 	// We could pass NULL as second parameter, but some behaviour sanitizers doesn't seem to like that.
251 	// Passing a 1 byte buffer to workaround it.
252 	uint8_t byte;
253 	int ret = mbedtls_ssl_read(ssl_ctx->get_context(), &byte, 0);
254 
255 	if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
256 		// Nothing to read/write (non blocking IO)
257 	} else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
258 		// Clean close (disconnect)
259 		disconnect_from_stream();
260 		return;
261 	} else if (ret < 0) {
262 		SSLContextMbedTLS::print_mbedtls_error(ret);
263 		disconnect_from_stream();
264 		return;
265 	}
266 
267 	Ref<StreamPeerTCP> tcp = base;
268 	if (tcp.is_valid() && tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
269 		disconnect_from_stream();
270 		return;
271 	}
272 }
273 
get_available_bytes() const274 int StreamPeerMbedTLS::get_available_bytes() const {
275 
276 	ERR_FAIL_COND_V(status != STATUS_CONNECTED, 0);
277 
278 	return mbedtls_ssl_get_bytes_avail(&(ssl_ctx->ssl));
279 }
StreamPeerMbedTLS()280 StreamPeerMbedTLS::StreamPeerMbedTLS() {
281 
282 	ssl_ctx.instance();
283 	status = STATUS_DISCONNECTED;
284 }
285 
~StreamPeerMbedTLS()286 StreamPeerMbedTLS::~StreamPeerMbedTLS() {
287 	disconnect_from_stream();
288 }
289 
disconnect_from_stream()290 void StreamPeerMbedTLS::disconnect_from_stream() {
291 
292 	if (status != STATUS_CONNECTED && status != STATUS_HANDSHAKING)
293 		return;
294 
295 	Ref<StreamPeerTCP> tcp = base;
296 	if (tcp.is_valid() && tcp->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
297 		// We are still connected on the socket, try to send close notify.
298 		mbedtls_ssl_close_notify(ssl_ctx->get_context());
299 	}
300 
301 	_cleanup();
302 }
303 
get_status() const304 StreamPeerMbedTLS::Status StreamPeerMbedTLS::get_status() const {
305 
306 	return status;
307 }
308 
_create_func()309 StreamPeerSSL *StreamPeerMbedTLS::_create_func() {
310 
311 	return memnew(StreamPeerMbedTLS);
312 }
313 
initialize_ssl()314 void StreamPeerMbedTLS::initialize_ssl() {
315 
316 	_create = _create_func;
317 	available = true;
318 }
319 
finalize_ssl()320 void StreamPeerMbedTLS::finalize_ssl() {
321 
322 	available = false;
323 	_create = NULL;
324 }
325