]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | /* |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | */ | |
19 | ||
20 | #include <errno.h> | |
21 | #include <netdb.h> | |
22 | #include <stdlib.h> | |
23 | #include <string.h> | |
24 | #include <unistd.h> | |
25 | #include <sys/socket.h> | |
26 | #include <netinet/in.h> | |
27 | #include <openssl/ssl.h> | |
28 | #include <pthread.h> | |
29 | ||
30 | #include <glib-object.h> | |
31 | #include <glib.h> | |
32 | ||
33 | #include <thrift/c_glib/thrift.h> | |
34 | #include <thrift/c_glib/transport/thrift_transport.h> | |
35 | #include <thrift/c_glib/transport/thrift_socket.h> | |
36 | #include <thrift/c_glib/transport/thrift_ssl_socket.h> | |
37 | ||
38 | ||
39 | #if defined(WIN32) | |
40 | #define MUTEX_TYPE HANDLE | |
41 | #define MUTEX_SETUP(x) (x) = CreateMutex(NULL, FALSE, NULL) | |
42 | #define MUTEX_CLEANUP(x) CloseHandle(x) | |
43 | #define MUTEX_LOCK(x) WaitForSingleObject((x), INFINITE) | |
44 | #define MUTEX_UNLOCK(x) ReleaseMutex(x) | |
45 | #else | |
46 | #define MUTEX_TYPE pthread_mutex_t | |
47 | #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) | |
48 | #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) | |
49 | #define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) | |
50 | #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) | |
51 | #endif | |
52 | ||
53 | #define OPENSSL_VERSION_NO_THREAD_ID 0x10000000L | |
54 | ||
55 | ||
56 | /* object properties */ | |
57 | enum _ThriftSSLSocketProperties | |
58 | { | |
59 | PROP_THRIFT_SSL_SOCKET_CONTEXT = 3, | |
60 | PROP_THRIFT_SSL_SELF_SIGNED | |
61 | }; | |
62 | ||
63 | /* To hold a global state management of openssl for all instances */ | |
64 | static gboolean thrift_ssl_socket_openssl_initialized=FALSE; | |
65 | /* This array will store all of the mutexes available to OpenSSL. */ | |
66 | static MUTEX_TYPE *thrift_ssl_socket_global_mutex_buf=NULL; | |
67 | ||
68 | ||
69 | /** | |
70 | * OpenSSL uniq id function. | |
71 | * | |
72 | * @return thread id | |
73 | */ | |
74 | static unsigned long thrift_ssl_socket_static_id_function(void) | |
75 | { | |
76 | #if defined(WIN32) | |
77 | return GetCurrentThreadId(); | |
78 | #else | |
79 | return ((unsigned long) pthread_self()); | |
80 | #endif | |
81 | } | |
82 | ||
83 | static void thrift_ssl_socket_static_locking_callback(int mode, int n, const char* unk, int id) { | |
84 | if (mode & CRYPTO_LOCK) | |
85 | MUTEX_LOCK(thrift_ssl_socket_global_mutex_buf[n]); | |
86 | else | |
87 | MUTEX_UNLOCK(thrift_ssl_socket_global_mutex_buf[n]); | |
88 | } | |
89 | ||
90 | static int thrift_ssl_socket_static_thread_setup(void) | |
91 | { | |
92 | int i; | |
93 | ||
94 | thrift_ssl_socket_global_mutex_buf = malloc(CRYPTO_num_locks() * sizeof(MUTEX_TYPE)); | |
95 | if (!thrift_ssl_socket_global_mutex_buf) | |
96 | return 0; | |
97 | for (i = 0; i < CRYPTO_num_locks( ); i++) | |
98 | MUTEX_SETUP(thrift_ssl_socket_global_mutex_buf[i]); | |
99 | CRYPTO_set_id_callback(thrift_ssl_socket_static_id_function); | |
100 | CRYPTO_set_locking_callback(thrift_ssl_socket_static_locking_callback); | |
101 | return 1; | |
102 | } | |
103 | ||
104 | static int thrift_ssl_socket_static_thread_cleanup(void) | |
105 | { | |
106 | int i; | |
107 | if (!thrift_ssl_socket_global_mutex_buf) | |
108 | return 0; | |
109 | CRYPTO_set_id_callback(NULL); | |
110 | CRYPTO_set_locking_callback(NULL); | |
111 | for (i = 0; i < CRYPTO_num_locks( ); i++) | |
112 | MUTEX_CLEANUP(thrift_ssl_socket_global_mutex_buf[i]); | |
113 | free(thrift_ssl_socket_global_mutex_buf); | |
114 | thrift_ssl_socket_global_mutex_buf = NULL; | |
115 | return 1; | |
116 | } | |
117 | ||
118 | /* | |
119 | static void* thrift_ssl_socket_dyn_lock_create_callback(const char* unk, int id) { | |
120 | g_print("We should create a lock\n"); | |
121 | return NULL; | |
122 | } | |
123 | ||
124 | static void thrift_ssl_socket_dyn_lock_callback(int mode, void* lock, const char* unk, int id) { | |
125 | if (lock != NULL) { | |
126 | if (mode & CRYPTO_LOCK) { | |
127 | g_printf("We should lock thread %d\n"); | |
128 | } else { | |
129 | g_printf("We should unlock thread %d\n"); | |
130 | } | |
131 | } | |
132 | } | |
133 | ||
134 | static void thrift_ssl_socket_dyn_lock_destroy_callback(void* lock, const char* unk, int id) { | |
135 | g_printf("We must destroy the lock\n"); | |
136 | } | |
137 | */ | |
138 | ||
139 | ||
140 | G_DEFINE_TYPE(ThriftSSLSocket, thrift_ssl_socket, THRIFT_TYPE_SOCKET) | |
141 | ||
142 | ||
143 | ||
144 | /** | |
145 | * When there's a thread context attached, we pass the SSL socket context so it | |
146 | * can check if the error is outside SSL, on I/O for example | |
147 | * @param socket | |
148 | * @param error_msg | |
149 | * @param thrift_error_no | |
150 | * @param ssl_error | |
151 | * @param error | |
152 | */ | |
153 | static | |
154 | void thrift_ssl_socket_get_ssl_error(ThriftSSLSocket *socket, const guchar *error_msg, guint thrift_error_no, int ssl_error, GError **error) | |
155 | { | |
156 | unsigned long error_code; | |
157 | char buffer[1024]; | |
158 | int buffer_size=1024; | |
159 | gboolean first_error = TRUE; | |
160 | int ssl_error_type = SSL_get_error(socket->ssl, ssl_error); | |
161 | if(ssl_error_type>0){ | |
162 | switch(ssl_error_type){ | |
163 | case SSL_ERROR_SSL: | |
164 | buffer_size-=snprintf(buffer, buffer_size, "SSL %s: ", error_msg); | |
165 | while ((error_code = ERR_get_error()) != 0 && buffer_size>1) { | |
166 | const char* reason = ERR_reason_error_string(error_code); | |
167 | if(reason!=NULL){ | |
168 | if(!first_error) { | |
169 | buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "\n\t"); | |
170 | first_error=FALSE; | |
171 | } | |
172 | buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX(%s) -> %s", error_code, reason, SSL_state_string(socket->ssl)); | |
173 | } | |
174 | } | |
175 | break; | |
176 | case SSL_ERROR_SYSCALL: | |
177 | buffer_size-=snprintf(buffer, buffer_size, "%s: ", error_msg); | |
178 | buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX -> %s", errno, strerror(errno)); | |
179 | break; | |
180 | case SSL_ERROR_WANT_READ: | |
181 | buffer_size-=snprintf(buffer, buffer_size, "%s: ", error_msg); | |
182 | buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX -> %s", ssl_error_type, "Error while reading from underlaying layer"); | |
183 | break; | |
184 | case SSL_ERROR_WANT_WRITE: | |
185 | buffer_size-=snprintf(buffer, buffer_size, "%s: ", error_msg); | |
186 | buffer_size-=snprintf(buffer+(1024-buffer_size), buffer_size, "%lX -> %s", ssl_error_type, "Error while writting to underlaying layer"); | |
187 | break; | |
188 | ||
189 | } | |
190 | g_set_error (error, THRIFT_TRANSPORT_ERROR, | |
191 | thrift_error_no, "%s", buffer); | |
192 | } | |
193 | } | |
194 | ||
195 | /** | |
196 | * For global SSL errors | |
197 | * @param error_msg | |
198 | * @param thrift_error_no | |
199 | * @param error | |
200 | */ | |
201 | static | |
202 | void thrift_ssl_socket_get_error(const guchar *error_msg, guint thrift_error_no, GError **error) | |
203 | { | |
204 | unsigned long error_code; | |
205 | while ((error_code = ERR_get_error()) != 0) { | |
206 | const char* reason = ERR_reason_error_string(error_code); | |
207 | if (reason == NULL) { | |
208 | g_set_error (error, THRIFT_TRANSPORT_ERROR, | |
209 | thrift_error_no, | |
210 | "SSL error %lX: %s", error_code, error_msg); | |
211 | }else{ | |
212 | g_set_error (error, THRIFT_TRANSPORT_ERROR, | |
213 | thrift_error_no, | |
214 | "SSL error %lX %s: %s", error_code,reason, error_msg); | |
215 | } | |
216 | } | |
217 | } | |
218 | ||
219 | ||
220 | ||
221 | /* implements thrift_transport_is_open */ | |
222 | gboolean | |
223 | thrift_ssl_socket_is_open (ThriftTransport *transport) | |
224 | { | |
225 | return thrift_socket_is_open(transport); | |
226 | } | |
227 | ||
228 | /* overrides thrift_transport_peek */ | |
229 | gboolean | |
230 | thrift_ssl_socket_peek (ThriftTransport *transport, GError **error) | |
231 | { | |
232 | gboolean retval = FALSE; | |
233 | ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); | |
234 | if (thrift_ssl_socket_is_open (transport)) | |
235 | { | |
236 | int rc; | |
237 | gchar byte; | |
238 | rc = SSL_peek(ssl_socket->ssl, &byte, 1); | |
239 | if (rc < 0) { | |
240 | thrift_ssl_socket_get_ssl_error(ssl_socket, "Check socket data", | |
241 | THRIFT_SSL_SOCKET_ERROR_SSL, rc, error); | |
242 | } | |
243 | if (rc == 0) { | |
244 | ERR_clear_error(); | |
245 | } | |
246 | retval = (rc > 0); | |
247 | } | |
248 | return retval; | |
249 | } | |
250 | ||
251 | /* implements thrift_transport_open */ | |
252 | gboolean | |
253 | thrift_ssl_socket_open (ThriftTransport *transport, GError **error) | |
254 | { | |
255 | ERR_clear_error(); | |
256 | ||
257 | if (!thrift_socket_open(transport, error)) { | |
258 | return FALSE; | |
259 | } | |
260 | ||
261 | if (!THRIFT_SSL_SOCKET_GET_CLASS(transport)->handle_handshake(transport, error)) { | |
262 | thrift_ssl_socket_close(transport, NULL); | |
263 | return FALSE; | |
264 | } | |
265 | ||
266 | return TRUE; | |
267 | } | |
268 | ||
269 | /* implements thrift_transport_close */ | |
270 | gboolean | |
271 | thrift_ssl_socket_close (ThriftTransport *transport, GError **error) | |
272 | { | |
273 | gboolean retval = FALSE; | |
274 | ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET(transport); | |
275 | if(ssl_socket!=NULL && ssl_socket->ssl) { | |
276 | int rc = SSL_shutdown(ssl_socket->ssl); | |
277 | /* if (rc < 0) { | |
278 | int errno_copy = THRIFT_SSL_SOCKET_ERROR_SSL; | |
279 | }*/ | |
280 | SSL_free(ssl_socket->ssl); | |
281 | ssl_socket->ssl = NULL; | |
282 | ERR_remove_state(0); | |
283 | } | |
284 | return thrift_socket_close(transport, error); | |
285 | } | |
286 | ||
287 | /* implements thrift_transport_read */ | |
288 | gint32 | |
289 | thrift_ssl_socket_read (ThriftTransport *transport, gpointer buf, | |
290 | guint32 len, GError **error) | |
291 | { | |
292 | guint maxRecvRetries_ = 10; | |
293 | ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); | |
294 | guint bytes = 0; | |
295 | guint retries = 0; | |
296 | ThriftSocket *socket = THRIFT_SOCKET (transport); | |
297 | g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET && ssl_socket->ssl!=NULL, FALSE); | |
298 | ||
299 | for (retries=0; retries < maxRecvRetries_; retries++) { | |
300 | bytes = SSL_read(ssl_socket->ssl, buf, len); | |
301 | if (bytes >= 0) | |
302 | break; | |
303 | int errno_copy = THRIFT_GET_SOCKET_ERROR; | |
304 | if (SSL_get_error(ssl_socket->ssl, bytes) == SSL_ERROR_SYSCALL) { | |
305 | if (ERR_get_error() == 0 && errno_copy == THRIFT_EINTR) { | |
306 | continue; | |
307 | } | |
308 | }else{ | |
309 | thrift_ssl_socket_get_ssl_error(ssl_socket, "Receive error", | |
310 | THRIFT_SSL_SOCKET_ERROR_SSL, bytes, error); | |
311 | ||
312 | } | |
313 | return -1; | |
314 | } | |
315 | return bytes; | |
316 | } | |
317 | ||
318 | /* implements thrift_transport_read_end | |
319 | * called when write is complete. nothing to do on our end. */ | |
320 | gboolean | |
321 | thrift_ssl_socket_read_end (ThriftTransport *transport, GError **error) | |
322 | { | |
323 | /* satisfy -Wall */ | |
324 | THRIFT_UNUSED_VAR (transport); | |
325 | THRIFT_UNUSED_VAR (error); | |
326 | return TRUE; | |
327 | } | |
328 | ||
329 | /* implements thrift_transport_write */ | |
330 | gboolean | |
331 | thrift_ssl_socket_write (ThriftTransport *transport, const gpointer buf, | |
332 | const guint32 len, GError **error) | |
333 | { | |
334 | ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); | |
335 | gint ret = 0; | |
336 | guint sent = 0; | |
337 | ThriftSocket *socket = THRIFT_SOCKET (transport); | |
338 | g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET && ssl_socket->ssl!=NULL, FALSE); | |
339 | ||
340 | while (sent < len) | |
341 | { | |
342 | ret = SSL_write (ssl_socket->ssl, (guint8 *)buf + sent, len - sent); | |
343 | if (ret < 0) | |
344 | { | |
345 | thrift_ssl_socket_get_ssl_error(ssl_socket, "Send error", | |
346 | THRIFT_SSL_SOCKET_ERROR_SSL, ret, error); | |
347 | return FALSE; | |
348 | } | |
349 | sent += ret; | |
350 | } | |
351 | ||
352 | return sent==len; | |
353 | } | |
354 | ||
355 | /* implements thrift_transport_write_end | |
356 | * called when write is complete. nothing to do on our end. */ | |
357 | gboolean | |
358 | thrift_ssl_socket_write_end (ThriftTransport *transport, GError **error) | |
359 | { | |
360 | /* satisfy -Wall */ | |
361 | THRIFT_UNUSED_VAR (transport); | |
362 | THRIFT_UNUSED_VAR (error); | |
363 | return TRUE; | |
364 | } | |
365 | ||
366 | /* implements thrift_transport_flush | |
367 | * flush pending data. since we are not buffered, this is a no-op */ | |
368 | gboolean | |
369 | thrift_ssl_socket_flush (ThriftTransport *transport, GError **error) | |
370 | { | |
371 | ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); | |
372 | ||
373 | ThriftSocket *socket = THRIFT_SOCKET (transport); | |
374 | g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET && ssl_socket->ssl!=NULL, FALSE); | |
375 | ||
376 | BIO* bio = SSL_get_wbio(ssl_socket->ssl); | |
377 | if (bio == NULL) { | |
378 | g_set_error (error, THRIFT_TRANSPORT_ERROR, | |
379 | THRIFT_TRANSPORT_ERROR_SEND, | |
380 | "failed to flush, wbio returned null"); | |
381 | return FALSE; | |
382 | } | |
383 | if (BIO_flush(bio) != 1) { | |
384 | g_set_error (error, THRIFT_TRANSPORT_ERROR, | |
385 | THRIFT_TRANSPORT_ERROR_SEND, | |
386 | "failed to flush it returned error"); | |
387 | return FALSE; | |
388 | } | |
389 | return TRUE; | |
390 | } | |
391 | ||
392 | ||
393 | gboolean | |
394 | thrift_ssl_socket_handle_handshake(ThriftTransport * transport, GError **error) | |
395 | { | |
396 | ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); | |
397 | ThriftSocket *socket = THRIFT_SOCKET (transport); | |
398 | g_return_val_if_fail (thrift_transport_is_open (transport), FALSE); | |
399 | ||
400 | if(THRIFT_SSL_SOCKET_GET_CLASS(ssl_socket)->create_ssl_context(transport, error)){ | |
401 | /*Context created*/ | |
402 | SSL_set_fd(ssl_socket->ssl, socket->sd); | |
403 | int rc; | |
404 | if(ssl_socket->server){ | |
405 | rc = SSL_accept(ssl_socket->ssl); | |
406 | }else{ | |
407 | rc = SSL_connect(ssl_socket->ssl); | |
408 | } | |
409 | if (rc <= 0) { | |
410 | thrift_ssl_socket_get_ssl_error(ssl_socket, "Error while connect/bind", THRIFT_SSL_SOCKET_ERROR_CONNECT_BIND, rc, error); | |
411 | return FALSE; | |
412 | } | |
413 | }else | |
414 | return FALSE; | |
415 | ||
416 | return thrift_ssl_socket_authorize(transport, error); | |
417 | } | |
418 | ||
419 | gboolean | |
420 | thrift_ssl_socket_create_ssl_context(ThriftTransport * transport, GError **error) | |
421 | { | |
422 | ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (transport); | |
423 | ||
424 | if(socket->ctx!=NULL){ | |
425 | if(socket->ssl!=NULL) { | |
426 | return TRUE; | |
427 | } | |
428 | ||
429 | socket->ssl = SSL_new(socket->ctx); | |
430 | if (socket->ssl == NULL) { | |
431 | g_set_error (error, THRIFT_TRANSPORT_ERROR, | |
432 | THRIFT_SSL_SOCKET_ERROR_TRANSPORT, | |
433 | "Unable to create default SSL context"); | |
434 | return FALSE; | |
435 | } | |
436 | } | |
437 | ||
438 | return TRUE; | |
439 | } | |
440 | ||
441 | ||
442 | gboolean thrift_ssl_load_cert_from_file(ThriftSSLSocket *ssl_socket, const char *file_name) | |
443 | { | |
444 | char error_buffer[255]; | |
445 | if (!thrift_ssl_socket_openssl_initialized) { | |
446 | g_error("OpenSSL is not initialized yet"); | |
447 | return FALSE; | |
448 | } | |
449 | int rc = SSL_CTX_load_verify_locations(ssl_socket->ctx, file_name, NULL); | |
450 | if (rc != 1) { /*verify authentication result*/ | |
451 | ERR_error_string_n(ERR_get_error(), error_buffer, 254); | |
452 | g_warning("Load of certificates failed: %s!", error_buffer); | |
453 | return FALSE; | |
454 | } | |
455 | return TRUE; | |
456 | } | |
457 | ||
458 | ||
459 | gboolean thrift_ssl_load_cert_from_buffer(ThriftSSLSocket *ssl_socket, const char chain_certs[]) | |
460 | { | |
461 | gboolean retval = FALSE; | |
462 | /* Load chain of certs*/ | |
463 | X509 *cacert=NULL; | |
464 | BIO *mem = BIO_new_mem_buf(chain_certs,strlen(chain_certs)); | |
465 | X509_STORE *cert_store = SSL_CTX_get_cert_store(ssl_socket->ctx); | |
466 | ||
467 | if(cert_store!=NULL){ | |
468 | int index = 0; | |
469 | while ((cacert = PEM_read_bio_X509(mem, NULL, 0, NULL))!=NULL) { | |
470 | if(cacert) { | |
471 | X509_STORE_add_cert(cert_store, cacert); | |
472 | X509_free(cacert); | |
473 | cacert=NULL; | |
474 | } /* Free immediately */ | |
475 | index++; | |
476 | } | |
477 | retval=TRUE; | |
478 | } | |
479 | BIO_free(mem); | |
480 | return retval; | |
481 | } | |
482 | ||
483 | gboolean | |
484 | thrift_ssl_socket_authorize(ThriftTransport * transport, GError **error) | |
485 | { | |
486 | ThriftSocket *socket = THRIFT_SOCKET (transport); | |
487 | ThriftSSLSocket *ssl_socket = THRIFT_SSL_SOCKET (transport); | |
488 | ThriftSSLSocketClass *cls = THRIFT_SSL_SOCKET_GET_CLASS(ssl_socket); | |
489 | gboolean authorization_result = FALSE; | |
490 | ||
491 | if(cls!=NULL && ssl_socket->ssl!=NULL){ | |
492 | int rc = SSL_get_verify_result(ssl_socket->ssl); | |
493 | if (rc != X509_V_OK) { /* verify authentication result */ | |
494 | if (rc == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && ssl_socket->allow_selfsigned) { | |
495 | g_debug("The certificate is a self-signed certificate and configuration allows it"); | |
496 | } else { | |
497 | g_set_error (error, | |
498 | THRIFT_TRANSPORT_ERROR, | |
499 | THRIFT_SSL_SOCKET_ERROR_SSL_CERT_VALIDATION_FAILED, | |
500 | "The certificate verification failed: %s (%d)", X509_verify_cert_error_string(rc), rc); | |
501 | return FALSE; | |
502 | } | |
503 | } | |
504 | ||
505 | X509* cert = SSL_get_peer_certificate(ssl_socket->ssl); | |
506 | if (cert == NULL) { | |
507 | if (SSL_get_verify_mode(ssl_socket->ssl) & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { | |
508 | g_set_error (error, | |
509 | THRIFT_TRANSPORT_ERROR, | |
510 | THRIFT_SSL_SOCKET_ERROR_SSL_CERT_VALIDATION_FAILED, | |
511 | "No certificate present. Are you connecting SSL server?"); | |
512 | return FALSE; | |
513 | } | |
514 | g_debug("No certificate required"); | |
515 | return TRUE; | |
516 | } | |
517 | ||
518 | /* certificate is present, since we don't support access manager we are done */ | |
519 | if (cls->authorize_peer == NULL) { | |
520 | X509_free(cert); | |
521 | g_debug("Certificate presented but we're not checking it"); | |
522 | return TRUE; | |
523 | } else { | |
524 | /* both certificate and access manager are present */ | |
525 | struct sockaddr_storage sa; | |
526 | socklen_t saLength = sizeof(struct sockaddr_storage); | |
527 | if (getpeername(socket->sd, (struct sockaddr*)&sa, &saLength) != 0) { | |
528 | sa.ss_family = AF_UNSPEC; | |
529 | } | |
530 | authorization_result = cls->authorize_peer(transport, cert, &sa, error); | |
531 | } | |
532 | if(cert != NULL) { | |
533 | X509_free(cert); | |
534 | } | |
535 | } | |
536 | ||
537 | return authorization_result; | |
538 | } | |
539 | ||
540 | ||
541 | /* initializes the instance */ | |
542 | static void | |
543 | thrift_ssl_socket_init (ThriftSSLSocket *socket) | |
544 | { | |
545 | GError *error = NULL; | |
546 | socket->ssl = NULL; | |
547 | socket->ctx = thrift_ssl_socket_context_initialize(SSLTLS, &error); | |
548 | if(socket->ctx == NULL) { | |
549 | g_info("The SSL context was not automatically initialized with protocol %d", SSLTLS); | |
550 | if(error!=NULL){ | |
551 | g_info("Reported reason %s", error->message); | |
552 | g_error_free (error); | |
553 | } | |
554 | } | |
555 | socket->server = FALSE; | |
556 | socket->allow_selfsigned = FALSE; | |
557 | ||
558 | } | |
559 | ||
560 | /* destructor */ | |
561 | static void | |
562 | thrift_ssl_socket_finalize (GObject *object) | |
563 | { | |
564 | ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (object); | |
565 | GError *error=NULL; | |
566 | if(socket!=NULL){ | |
567 | g_debug("Instance %p destroyed", (void *)socket); | |
568 | if(socket->ssl != NULL) | |
569 | { | |
570 | thrift_ssl_socket_close(THRIFT_TRANSPORT(object), &error); | |
571 | socket->ssl=NULL; | |
572 | } | |
573 | ||
574 | if(socket->ctx!=NULL){ | |
575 | g_debug("Freeing the context for the instance"); | |
576 | SSL_CTX_free(socket->ctx); | |
577 | socket->ctx=NULL; | |
578 | } | |
579 | } | |
580 | ||
581 | if (G_OBJECT_CLASS (thrift_ssl_socket_parent_class)->finalize) | |
582 | (*G_OBJECT_CLASS (thrift_ssl_socket_parent_class)->finalize) (object); | |
583 | } | |
584 | ||
585 | /* property accessor */ | |
586 | void | |
587 | thrift_ssl_socket_get_property (GObject *object, guint property_id, | |
588 | GValue *value, GParamSpec *pspec) | |
589 | { | |
590 | ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (object); | |
591 | THRIFT_UNUSED_VAR (pspec); | |
592 | ||
593 | switch (property_id) | |
594 | { | |
595 | case PROP_THRIFT_SSL_SOCKET_CONTEXT: | |
596 | g_value_set_pointer (value, socket->ctx); | |
597 | break; | |
598 | } | |
599 | } | |
600 | ||
601 | /* property mutator */ | |
602 | void | |
603 | thrift_ssl_socket_set_property (GObject *object, guint property_id, | |
604 | const GValue *value, GParamSpec *pspec) | |
605 | { | |
606 | ThriftSSLSocket *socket = THRIFT_SSL_SOCKET (object); | |
607 | ||
608 | THRIFT_UNUSED_VAR (pspec); | |
609 | switch (property_id) | |
610 | { | |
611 | case PROP_THRIFT_SSL_SOCKET_CONTEXT: | |
612 | if(socket->ctx!=NULL){ | |
613 | g_debug("Freeing the context since we are setting a new one"); | |
614 | SSL_CTX_free(socket->ctx); | |
615 | } | |
616 | socket->ctx = g_value_get_pointer(value); /* We copy the context */ | |
617 | break; | |
618 | ||
619 | case PROP_THRIFT_SSL_SELF_SIGNED: | |
620 | socket->allow_selfsigned = g_value_get_boolean(value); | |
621 | break; | |
622 | default: | |
623 | g_warning("Trying to set property %i that doesn't exists!", property_id); | |
624 | /* thrift_socket_set_property(object, property_id, value, pspec); */ | |
625 | break; | |
626 | } | |
627 | } | |
628 | ||
629 | void | |
630 | thrift_ssl_socket_initialize_openssl(void) | |
631 | { | |
632 | if(thrift_ssl_socket_openssl_initialized){ | |
633 | return; | |
634 | } | |
635 | thrift_ssl_socket_openssl_initialized=TRUE; | |
636 | SSL_library_init(); | |
637 | ERR_load_crypto_strings(); | |
638 | SSL_load_error_strings(); | |
639 | ERR_load_BIO_strings(); | |
640 | ||
641 | /* Setup locking */ | |
642 | g_debug("We setup %d threads locks", thrift_ssl_socket_static_thread_setup()); | |
643 | ||
644 | /* dynamic locking | |
645 | CRYPTO_set_dynlock_create_callback(thrift_ssl_socket_dyn_lock_create_callback); | |
646 | CRYPTO_set_dynlock_lock_callback(thrift_ssl_socket_dyn_lock_callback); | |
647 | CRYPTO_set_dynlock_destroy_callback(thrift_ssl_socket_dyn_lock_destroy_callback); | |
648 | */ | |
649 | } | |
650 | ||
651 | ||
652 | void thrift_ssl_socket_finalize_openssl(void) | |
653 | { | |
654 | if (!thrift_ssl_socket_openssl_initialized) { | |
655 | return; | |
656 | } | |
657 | thrift_ssl_socket_openssl_initialized = FALSE; | |
658 | ||
659 | g_debug("We cleared %d threads locks", thrift_ssl_socket_static_thread_cleanup()); | |
660 | /* Not supported | |
661 | CRYPTO_set_locking_callback(NULL); | |
662 | CRYPTO_set_dynlock_create_callback(NULL); | |
663 | CRYPTO_set_dynlock_lock_callback(NULL); | |
664 | CRYPTO_set_dynlock_destroy_callback(NULL); | |
665 | */ | |
666 | ERR_free_strings(); | |
667 | EVP_cleanup(); | |
668 | CRYPTO_cleanup_all_ex_data(); | |
669 | ERR_remove_state(0); | |
670 | } | |
671 | ||
672 | ||
673 | /* initializes the class */ | |
674 | static void | |
675 | thrift_ssl_socket_class_init (ThriftSSLSocketClass *cls) | |
676 | { | |
677 | ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls); | |
678 | GObjectClass *gobject_class = G_OBJECT_CLASS (cls); | |
679 | GParamSpec *param_spec = NULL; | |
680 | ||
681 | g_debug("Initialization of ThriftSSLSocketClass"); | |
682 | /* setup accessors and mutators */ | |
683 | gobject_class->get_property = thrift_ssl_socket_get_property; | |
684 | gobject_class->set_property = thrift_ssl_socket_set_property; | |
685 | param_spec = g_param_spec_pointer ("ssl_context", | |
686 | "SSLContext", | |
687 | "Set the SSL context for handshake with the remote host", | |
688 | G_PARAM_READWRITE); | |
689 | g_object_class_install_property (gobject_class, PROP_THRIFT_SSL_SOCKET_CONTEXT, | |
690 | param_spec); | |
691 | param_spec = g_param_spec_boolean ("ssl_accept_selfsigned", | |
692 | "Accept Self Signed", | |
693 | "Whether or not accept self signed certificate", | |
694 | FALSE, | |
695 | G_PARAM_READWRITE); | |
696 | g_object_class_install_property (gobject_class, PROP_THRIFT_SSL_SELF_SIGNED, | |
697 | param_spec); | |
698 | /* Class methods */ | |
699 | cls->handle_handshake = thrift_ssl_socket_handle_handshake; | |
700 | cls->create_ssl_context = thrift_ssl_socket_create_ssl_context; | |
701 | ||
702 | /* Override */ | |
703 | gobject_class->finalize = thrift_ssl_socket_finalize; | |
704 | ttc->is_open = thrift_ssl_socket_is_open; | |
705 | ttc->peek = thrift_ssl_socket_peek; | |
706 | ttc->open = thrift_ssl_socket_open; | |
707 | ttc->close = thrift_ssl_socket_close; | |
708 | ttc->read = thrift_ssl_socket_read; | |
709 | ttc->read_end = thrift_ssl_socket_read_end; | |
710 | ttc->write = thrift_ssl_socket_write; | |
711 | ttc->write_end = thrift_ssl_socket_write_end; | |
712 | ttc->flush = thrift_ssl_socket_flush; | |
713 | } | |
714 | ||
715 | ||
716 | /* | |
717 | * Public API | |
718 | */ | |
719 | ThriftSSLSocket* | |
720 | thrift_ssl_socket_new(ThriftSSLSocketProtocol ssl_protocol, GError **error) | |
721 | { | |
722 | ThriftSSLSocket *thriftSSLSocket = NULL; | |
723 | SSL_CTX *ssl_context = NULL; | |
724 | /* Create the context */ | |
725 | if((ssl_context=thrift_ssl_socket_context_initialize(ssl_protocol, error))==NULL){ | |
726 | g_warning("We cannot initialize context for protocol %d", ssl_protocol); | |
727 | return thriftSSLSocket; | |
728 | } | |
729 | ||
730 | /* FIXME if the protocol is different? */ | |
731 | thriftSSLSocket = g_object_new (THRIFT_TYPE_SSL_SOCKET, "ssl_context", ssl_context, NULL); | |
732 | return thriftSSLSocket; | |
733 | } | |
734 | ||
735 | ThriftSSLSocket* | |
736 | thrift_ssl_socket_new_with_host(ThriftSSLSocketProtocol ssl_protocol, gchar *hostname, guint port, GError **error) | |
737 | { | |
738 | ThriftSSLSocket *thriftSSLSocket = NULL; | |
739 | SSL_CTX *ssl_context = NULL; | |
740 | /* Create the context */ | |
741 | if((ssl_context=thrift_ssl_socket_context_initialize(ssl_protocol, error))==NULL){ | |
742 | /* FIXME Do error control */ | |
743 | return thriftSSLSocket; | |
744 | } | |
745 | /* FIXME if the protocol is different? */ | |
746 | thriftSSLSocket = g_object_new (THRIFT_TYPE_SSL_SOCKET, "ssl_context", ssl_context, "hostname", hostname, "port", port, NULL); | |
747 | return thriftSSLSocket; | |
748 | } | |
749 | ||
750 | void thrift_ssl_socket_set_manager(ThriftSSLSocket *ssl_socket, AUTHORIZATION_MANAGER_CALLBACK callback) | |
751 | { | |
752 | ThriftSSLSocketClass *sslSocketClass = THRIFT_SSL_SOCKET_GET_CLASS (ssl_socket); | |
753 | if(sslSocketClass){ | |
754 | sslSocketClass->authorize_peer = callback; | |
755 | } | |
756 | } | |
757 | ||
758 | ||
759 | SSL_CTX* | |
760 | thrift_ssl_socket_context_initialize(ThriftSSLSocketProtocol ssl_protocol, GError **error) | |
761 | { | |
762 | SSL_CTX* context = NULL; | |
763 | switch(ssl_protocol){ | |
764 | case SSLTLS: | |
765 | context = SSL_CTX_new(SSLv23_method()); | |
766 | break; | |
767 | #ifndef OPENSSL_NO_SSL3 | |
768 | case SSLv3: | |
769 | context = SSL_CTX_new(SSLv3_method()); | |
770 | break; | |
771 | #endif | |
772 | case TLSv1_0: | |
773 | context = SSL_CTX_new(TLSv1_method()); | |
774 | break; | |
775 | case TLSv1_1: | |
776 | context = SSL_CTX_new(TLSv1_1_method()); | |
777 | break; | |
778 | case TLSv1_2: | |
779 | context = SSL_CTX_new(TLSv1_2_method()); | |
780 | break; | |
781 | default: | |
782 | g_set_error (error, THRIFT_TRANSPORT_ERROR, | |
783 | THRIFT_SSL_SOCKET_ERROR_CIPHER_NOT_AVAILABLE, | |
784 | "The SSL protocol is unknown for %d", ssl_protocol); | |
785 | return NULL; | |
786 | break; | |
787 | } | |
788 | ||
789 | if (context == NULL) { | |
790 | thrift_ssl_socket_get_error("No cipher overlay", THRIFT_SSL_SOCKET_ERROR_CIPHER_NOT_AVAILABLE, error); | |
791 | return NULL; | |
792 | } | |
793 | SSL_CTX_set_mode(context, SSL_MODE_AUTO_RETRY); | |
794 | ||
795 | /* Disable horribly insecure SSLv2 and SSLv3 protocols but allow a handshake | |
796 | with older clients so they get a graceful denial. */ | |
797 | if (ssl_protocol == SSLTLS) { | |
798 | SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); | |
799 | SSL_CTX_set_options(context, SSL_OP_NO_SSLv3); /* THRIFT-3164 */ | |
800 | } | |
801 | ||
802 | return context; | |
803 | } | |
804 |