Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2020 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (net_socket.c). | |
5 | * --------------------------------------------------------------------------------------- | |
6 | * | |
7 | * Permission is hereby granted, free of charge, | |
8 | * to any person obtaining a copy of this software and associated documentation files (the "Software"), | |
9 | * to deal in the Software without restriction, including without limitation the rights to | |
10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
11 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
16 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #include <net/net_socket_ssl.h> | |
24 | #include <net/net_socket.h> | |
25 | #include <encodings/base64.h> | |
26 | #include <streams/file_stream.h> | |
27 | #include <string/stdstring.h> | |
28 | ||
29 | #include "../../deps/bearssl-0.6/inc/bearssl.h" | |
30 | ||
31 | struct ssl_state | |
32 | { | |
33 | int fd; | |
34 | br_ssl_client_context sc; | |
35 | br_x509_minimal_context xc; | |
36 | uint8_t iobuf[BR_SSL_BUFSIZE_BIDI]; | |
37 | }; | |
38 | ||
39 | /* TODO/FIXME - static global variables */ | |
40 | static br_x509_trust_anchor TAs[500] = {}; | |
41 | static size_t TAs_NUM = 0; | |
42 | ||
43 | static uint8_t* current_vdn; | |
44 | static size_t current_vdn_size; | |
45 | ||
46 | static uint8_t* blobdup(const void * src, size_t len) | |
47 | { | |
48 | uint8_t * ret = malloc(len); | |
49 | memcpy(ret, src, len); | |
50 | return ret; | |
51 | } | |
52 | static void vdn_append(void* dest_ctx, const void * src, size_t len) | |
53 | { | |
54 | current_vdn = realloc(current_vdn, current_vdn_size + len); | |
55 | memcpy(current_vdn+current_vdn_size, src, len); | |
56 | current_vdn_size += len; | |
57 | } | |
58 | ||
59 | static bool append_cert_x509(void* x509, size_t len) | |
60 | { | |
61 | br_x509_pkey* pk; | |
62 | br_x509_decoder_context dc; | |
63 | br_x509_trust_anchor* ta = &TAs[TAs_NUM]; | |
64 | ||
65 | current_vdn = NULL; | |
66 | current_vdn_size = 0; | |
67 | ||
68 | br_x509_decoder_init(&dc, vdn_append, NULL); | |
69 | br_x509_decoder_push(&dc, x509, len); | |
70 | pk = br_x509_decoder_get_pkey(&dc); | |
71 | if (!pk || !br_x509_decoder_isCA(&dc)) | |
72 | return false; | |
73 | ||
74 | ta->dn.len = current_vdn_size; | |
75 | ta->dn.data = current_vdn; | |
76 | ta->flags = BR_X509_TA_CA; | |
77 | ||
78 | switch (pk->key_type) | |
79 | { | |
80 | case BR_KEYTYPE_RSA: | |
81 | ta->pkey.key_type = BR_KEYTYPE_RSA; | |
82 | ta->pkey.key.rsa.nlen = pk->key.rsa.nlen; | |
83 | ta->pkey.key.rsa.n = blobdup(pk->key.rsa.n, pk->key.rsa.nlen); | |
84 | ta->pkey.key.rsa.elen = pk->key.rsa.elen; | |
85 | ta->pkey.key.rsa.e = blobdup(pk->key.rsa.e, pk->key.rsa.elen); | |
86 | break; | |
87 | case BR_KEYTYPE_EC: | |
88 | ta->pkey.key_type = BR_KEYTYPE_EC; | |
89 | ta->pkey.key.ec.curve = pk->key.ec.curve; | |
90 | ta->pkey.key.ec.qlen = pk->key.ec.qlen; | |
91 | ta->pkey.key.ec.q = blobdup(pk->key.ec.q, pk->key.ec.qlen); | |
92 | break; | |
93 | default: | |
94 | return false; | |
95 | } | |
96 | ||
97 | TAs_NUM++; | |
98 | return true; | |
99 | } | |
100 | ||
101 | static char* delete_linebreaks(char* in) | |
102 | { | |
103 | char* iter_in; | |
104 | char* iter_out; | |
105 | while (*in == '\n') | |
106 | in++; | |
107 | ||
108 | iter_in = in; | |
109 | ||
110 | while (*iter_in != '\n' && *iter_in != '\0') | |
111 | iter_in++; | |
112 | iter_out = iter_in; | |
113 | while (*iter_in != '\0') | |
114 | { | |
115 | while (*iter_in == '\n') | |
116 | iter_in++; | |
117 | *iter_out++ = *iter_in++; | |
118 | } | |
119 | ||
120 | return in; | |
121 | } | |
122 | ||
123 | /* this rearranges its input, it's easier to implement | |
124 | * that way and caller doesn't need it anymore anyways */ | |
125 | static void append_certs_pem_x509(char * certs_pem) | |
126 | { | |
127 | void * cert_bin; | |
128 | int cert_bin_len; | |
129 | char *cert = certs_pem; | |
130 | char *cert_end = certs_pem; | |
131 | ||
132 | for (;;) | |
133 | { | |
134 | cert = strstr(cert_end, "-----BEGIN CERTIFICATE-----"); | |
135 | if (!cert) | |
136 | break; | |
137 | cert += STRLEN_CONST("-----BEGIN CERTIFICATE-----"); | |
138 | cert_end = strstr(cert, "-----END CERTIFICATE-----"); | |
139 | ||
140 | *cert_end = '\0'; | |
141 | cert = delete_linebreaks(cert); | |
142 | ||
143 | cert_bin = unbase64(cert, cert_end-cert, &cert_bin_len); | |
144 | append_cert_x509(cert_bin, cert_bin_len); | |
145 | free(cert_bin); | |
146 | ||
147 | cert_end++; /* skip the NUL we just added */ | |
148 | } | |
149 | } | |
150 | ||
151 | /* TODO: not thread safe, rthreads doesn't provide any | |
152 | * statically allocatable mutex/etc */ | |
153 | static void initialize(void) | |
154 | { | |
155 | void* certs_pem; | |
156 | if (TAs_NUM) | |
157 | return; | |
158 | /* filestream_read_file appends a NUL */ | |
159 | filestream_read_file("/etc/ssl/certs/ca-certificates.crt", &certs_pem, NULL); | |
160 | append_certs_pem_x509((char*)certs_pem); | |
161 | free(certs_pem); | |
162 | } | |
163 | ||
164 | void* ssl_socket_init(int fd, const char *domain) | |
165 | { | |
166 | struct ssl_state *state = (struct ssl_state*)calloc(1, sizeof(*state)); | |
167 | ||
168 | initialize(); | |
169 | ||
170 | br_ssl_client_init_full(&state->sc, &state->xc, TAs, TAs_NUM); | |
171 | br_ssl_engine_set_buffer(&state->sc.eng, | |
172 | state->iobuf, sizeof(state->iobuf), true); | |
173 | br_ssl_client_reset(&state->sc, domain, false); | |
174 | ||
175 | state->fd = fd; | |
176 | return state; | |
177 | } | |
178 | ||
179 | static bool process_inner(struct ssl_state *state, bool blocking) | |
180 | { | |
181 | bool dummy; | |
182 | size_t buflen; | |
183 | ssize_t bytes; | |
184 | uint8_t *buf = br_ssl_engine_sendrec_buf(&state->sc.eng, &buflen); | |
185 | ||
186 | if (buflen) | |
187 | { | |
188 | if (blocking) | |
189 | bytes = (socket_send_all_blocking(state->fd, buf, buflen, true) | |
190 | ? buflen | |
191 | : -1); | |
192 | else | |
193 | bytes = socket_send_all_nonblocking(state->fd, buf, buflen, true); | |
194 | ||
195 | if (bytes > 0) | |
196 | br_ssl_engine_sendrec_ack(&state->sc.eng, bytes); | |
197 | if (bytes < 0) | |
198 | return false; | |
199 | /* if we did something, return immediately so we | |
200 | * don't try to read if Bear still wants to send */ | |
201 | return true; | |
202 | } | |
203 | ||
204 | buf = br_ssl_engine_recvrec_buf(&state->sc.eng, &buflen); | |
205 | if (buflen) | |
206 | { | |
207 | /* if the socket is blocking, socket_receive_all_nonblocking blocks, | |
208 | * but only to read at least 1 byte which is exactly what we want */ | |
209 | bytes = socket_receive_all_nonblocking(state->fd, &dummy, buf, buflen); | |
210 | if (bytes > 0) | |
211 | br_ssl_engine_recvrec_ack(&state->sc.eng, bytes); | |
212 | if (bytes < 0) | |
213 | return false; | |
214 | } | |
215 | ||
216 | return true; | |
217 | } | |
218 | ||
219 | int ssl_socket_connect(void *state_data, | |
220 | void *data, bool timeout_enable, bool nonblock) | |
221 | { | |
222 | struct ssl_state *state = (struct ssl_state*)state_data; | |
223 | unsigned bearstate; | |
224 | ||
225 | if (timeout_enable) | |
226 | { | |
227 | if (!socket_connect_with_timeout(state->fd, data, 5000)) | |
228 | return -1; | |
229 | /* socket_connect_with_timeout makes the socket non-blocking. */ | |
230 | if (!socket_set_block(state->fd, true)) | |
231 | return -1; | |
232 | } | |
233 | else | |
234 | { | |
235 | if (socket_connect(state->fd, data)) | |
236 | return -1; | |
237 | } | |
238 | ||
239 | for (;;) | |
240 | { | |
241 | if (!process_inner(state, true)) | |
242 | return -1; | |
243 | ||
244 | bearstate = br_ssl_engine_current_state(&state->sc.eng); | |
245 | if (bearstate & BR_SSL_SENDAPP) | |
246 | break; /* handshake done */ | |
247 | if (bearstate & BR_SSL_CLOSED) | |
248 | return -1; /* failed */ | |
249 | } | |
250 | ||
251 | return 1; | |
252 | } | |
253 | ||
254 | ssize_t ssl_socket_receive_all_nonblocking(void *state_data, | |
255 | bool *error, void *data_, size_t size) | |
256 | { | |
257 | struct ssl_state *state = (struct ssl_state*)state_data; | |
258 | uint8_t * bear_data; | |
259 | size_t bear_data_size; | |
260 | ||
261 | socket_set_block(state->fd, false); | |
262 | ||
263 | if (!process_inner(state, false)) | |
264 | { | |
265 | *error = true; | |
266 | return -1; | |
267 | } | |
268 | ||
269 | bear_data = br_ssl_engine_recvapp_buf(&state->sc.eng, &bear_data_size); | |
270 | if (bear_data_size > size) bear_data_size = size; | |
271 | memcpy(data_, bear_data, bear_data_size); | |
272 | if (bear_data_size) | |
273 | br_ssl_engine_recvapp_ack(&state->sc.eng, bear_data_size); | |
274 | ||
275 | return bear_data_size; | |
276 | } | |
277 | ||
278 | int ssl_socket_receive_all_blocking(void *state_data, | |
279 | void *data_, size_t size) | |
280 | { | |
281 | struct ssl_state *state = (struct ssl_state*)state_data; | |
282 | uint8_t *data = (uint8_t*)data_; | |
283 | uint8_t * bear_data; | |
284 | size_t bear_data_size; | |
285 | ||
286 | socket_set_block(state->fd, true); | |
287 | ||
288 | for (;;) | |
289 | { | |
290 | bear_data = br_ssl_engine_recvapp_buf(&state->sc.eng, &bear_data_size); | |
291 | if (bear_data_size > size) | |
292 | bear_data_size = size; | |
293 | memcpy(data, bear_data, bear_data_size); | |
294 | if (bear_data_size) | |
295 | br_ssl_engine_recvapp_ack(&state->sc.eng, bear_data_size); | |
296 | data += bear_data_size; | |
297 | size -= bear_data_size; | |
298 | ||
299 | if (size) | |
300 | process_inner(state, true); | |
301 | else | |
302 | break; | |
303 | } | |
304 | return 1; | |
305 | } | |
306 | ||
307 | int ssl_socket_send_all_blocking(void *state_data, | |
308 | const void *data_, size_t size, bool no_signal) | |
309 | { | |
310 | struct ssl_state *state = (struct ssl_state*)state_data; | |
311 | const uint8_t *data = (const uint8_t*)data_; | |
312 | uint8_t * bear_data; | |
313 | size_t bear_data_size; | |
314 | ||
315 | socket_set_block(state->fd, true); | |
316 | ||
317 | for (;;) | |
318 | { | |
319 | bear_data = br_ssl_engine_sendapp_buf(&state->sc.eng, &bear_data_size); | |
320 | if (bear_data_size > size) | |
321 | bear_data_size = size; | |
322 | memcpy(bear_data, data_, bear_data_size); | |
323 | if (bear_data_size) | |
324 | br_ssl_engine_sendapp_ack(&state->sc.eng, bear_data_size); | |
325 | data += bear_data_size; | |
326 | size -= bear_data_size; | |
327 | ||
328 | if (size) | |
329 | process_inner(state, true); | |
330 | else | |
331 | break; | |
332 | } | |
333 | ||
334 | br_ssl_engine_flush(&state->sc.eng, false); | |
335 | process_inner(state, false); | |
336 | return 1; | |
337 | } | |
338 | ||
339 | ssize_t ssl_socket_send_all_nonblocking(void *state_data, | |
340 | const void *data_, size_t size, bool no_signal) | |
341 | { | |
342 | struct ssl_state *state = (struct ssl_state*)state_data; | |
343 | uint8_t * bear_data; | |
344 | size_t bear_data_size; | |
345 | ||
346 | socket_set_block(state->fd, false); | |
347 | ||
348 | bear_data = br_ssl_engine_sendapp_buf(&state->sc.eng, &bear_data_size); | |
349 | if (bear_data_size > size) | |
350 | bear_data_size = size; | |
351 | memcpy(bear_data, data_, bear_data_size); | |
352 | if (bear_data_size) | |
353 | { | |
354 | br_ssl_engine_sendapp_ack(&state->sc.eng, bear_data_size); | |
355 | br_ssl_engine_flush(&state->sc.eng, false); | |
356 | } | |
357 | ||
358 | if (!process_inner(state, false)) | |
359 | return -1; | |
360 | ||
361 | return bear_data_size; | |
362 | } | |
363 | ||
364 | void ssl_socket_close(void *state_data) | |
365 | { | |
366 | struct ssl_state *state = (struct ssl_state*)state_data; | |
367 | ||
368 | br_ssl_engine_close(&state->sc.eng); | |
369 | process_inner(state, false); /* send close notification */ | |
370 | socket_close(state->fd); /* but immediately close socket | |
371 | and don't worry about recipient | |
372 | getting our message */ | |
373 | } | |
374 | ||
375 | void ssl_socket_free(void *state_data) | |
376 | { | |
377 | struct ssl_state *state = (struct ssl_state*)state_data; | |
378 | /* BearSSL does zero allocations of its own, | |
379 | * so other than this struct, there is nothing to free */ | |
380 | free(state); | |
381 | } |