Code to support TCP and TLS server/client. More...
#include "asterisk.h"
#include <fcntl.h>
#include <signal.h>
#include <sys/signal.h>
#include "asterisk/compat.h"
#include "asterisk/tcptls.h"
#include "asterisk/http.h"
#include "asterisk/utils.h"
#include "asterisk/strings.h"
#include "asterisk/options.h"
#include "asterisk/manager.h"
#include "asterisk/astobj2.h"
Go to the source code of this file.
Functions | |
static int | __ssl_setup (struct ast_tls_config *cfg, int client) |
int | ast_ssl_setup (struct ast_tls_config *cfg) |
Set up an SSL server. | |
void | ast_ssl_teardown (struct ast_tls_config *cfg) |
free resources used by an SSL server | |
struct ast_tcptls_session_instance * | ast_tcptls_client_create (struct ast_tcptls_session_args *desc) |
struct ast_tcptls_session_instance * | ast_tcptls_client_start (struct ast_tcptls_session_instance *tcptls_session) |
attempts to connect and start tcptls session, on error the tcptls_session's ref count is decremented, fd and file are closed, and NULL is returned. | |
void | ast_tcptls_close_session_file (struct ast_tcptls_session_instance *tcptls_session) |
Closes a tcptls session instance's file and/or file descriptor. The tcptls_session will be set to NULL and it's file descriptor will be set to -1 by this function. | |
HOOK_T | ast_tcptls_server_read (struct ast_tcptls_session_instance *tcptls_session, void *buf, size_t count) |
void * | ast_tcptls_server_root (void *data) |
void | ast_tcptls_server_start (struct ast_tcptls_session_args *desc) |
This is a generic (re)start routine for a TCP server, which does the socket/bind/listen and starts a thread for handling accept(). | |
void | ast_tcptls_server_stop (struct ast_tcptls_session_args *desc) |
Shutdown a running server if there is one. | |
HOOK_T | ast_tcptls_server_write (struct ast_tcptls_session_instance *tcptls_session, const void *buf, size_t count) |
int | ast_tls_read_conf (struct ast_tls_config *tls_cfg, struct ast_tcptls_session_args *tls_desc, const char *varname, const char *value) |
Used to parse conf files containing tls/ssl options. | |
static void * | handle_tcptls_connection (void *data) |
creates a FILE * from the fd passed by the accept thread. This operation is potentially expensive (certificate verification), so we do it in the child thread context. | |
static void | session_instance_destructor (void *obj) |
static int | ssl_close (void *cookie) |
static HOOK_T | ssl_read (void *cookie, char *buf, LEN_T len) |
replacement read/write functions for SSL support. We use wrappers rather than SSL_read/SSL_write directly so we can put in some debugging. | |
static HOOK_T | ssl_write (void *cookie, const char *buf, LEN_T len) |
Code to support TCP and TLS server/client.
Definition in file tcptls.c.
static int __ssl_setup | ( | struct ast_tls_config * | cfg, | |
int | client | |||
) | [static] |
Definition at line 323 of file tcptls.c.
References ast_debug, AST_SSL_SSLV2_CLIENT, AST_SSL_SSLV3_CLIENT, AST_SSL_TLSV1_CLIENT, AST_SSL_VERIFY_CLIENT, ast_strlen_zero(), ast_test_flag, ast_verb, ast_tls_config::cafile, ast_tls_config::capath, ast_tls_config::certfile, ast_tls_config::cipher, ast_tls_config::enabled, ast_tls_config::flags, ast_tls_config::pvtfile, S_OR, and ast_tls_config::ssl_ctx.
Referenced by ast_ssl_setup(), and ast_tcptls_client_start().
00324 { 00325 #ifndef DO_SSL 00326 cfg->enabled = 0; 00327 return 0; 00328 #else 00329 if (!cfg->enabled) 00330 return 0; 00331 00332 /* Get rid of an old SSL_CTX since we're about to 00333 * allocate a new one 00334 */ 00335 if (cfg->ssl_ctx) { 00336 SSL_CTX_free(cfg->ssl_ctx); 00337 cfg->ssl_ctx = NULL; 00338 } 00339 00340 if (client) { 00341 #ifndef OPENSSL_NO_SSL2 00342 if (ast_test_flag(&cfg->flags, AST_SSL_SSLV2_CLIENT)) { 00343 cfg->ssl_ctx = SSL_CTX_new(SSLv2_client_method()); 00344 } else 00345 #endif 00346 if (ast_test_flag(&cfg->flags, AST_SSL_SSLV3_CLIENT)) { 00347 cfg->ssl_ctx = SSL_CTX_new(SSLv3_client_method()); 00348 } else if (ast_test_flag(&cfg->flags, AST_SSL_TLSV1_CLIENT)) { 00349 cfg->ssl_ctx = SSL_CTX_new(TLSv1_client_method()); 00350 } else { 00351 /* SSLv23_client_method() sends SSLv2, this was the original 00352 * default for ssl clients before the option was given to 00353 * pick what protocol a client should use. In order not 00354 * to break expected behavior it remains the default. */ 00355 cfg->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); 00356 } 00357 } else { 00358 /* SSLv23_server_method() supports TLSv1, SSLv2, and SSLv3 inbound connections. */ 00359 cfg->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); 00360 } 00361 00362 if (!cfg->ssl_ctx) { 00363 ast_debug(1, "Sorry, SSL_CTX_new call returned null...\n"); 00364 cfg->enabled = 0; 00365 return 0; 00366 } 00367 00368 SSL_CTX_set_verify(cfg->ssl_ctx, 00369 ast_test_flag(&cfg->flags, AST_SSL_VERIFY_CLIENT) ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE, 00370 NULL); 00371 00372 if (!ast_strlen_zero(cfg->certfile)) { 00373 char *tmpprivate = ast_strlen_zero(cfg->pvtfile) ? cfg->certfile : cfg->pvtfile; 00374 if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0) { 00375 if (!client) { 00376 /* Clients don't need a certificate, but if its setup we can use it */ 00377 ast_verb(0, "SSL error loading cert file. <%s>", cfg->certfile); 00378 cfg->enabled = 0; 00379 SSL_CTX_free(cfg->ssl_ctx); 00380 cfg->ssl_ctx = NULL; 00381 return 0; 00382 } 00383 } 00384 if ((SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, tmpprivate, SSL_FILETYPE_PEM) == 0) || (SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 )) { 00385 if (!client) { 00386 /* Clients don't need a private key, but if its setup we can use it */ 00387 ast_verb(0, "SSL error loading private key file. <%s>", tmpprivate); 00388 cfg->enabled = 0; 00389 SSL_CTX_free(cfg->ssl_ctx); 00390 cfg->ssl_ctx = NULL; 00391 return 0; 00392 } 00393 } 00394 } 00395 if (!ast_strlen_zero(cfg->cipher)) { 00396 if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) { 00397 if (!client) { 00398 ast_verb(0, "SSL cipher error <%s>", cfg->cipher); 00399 cfg->enabled = 0; 00400 SSL_CTX_free(cfg->ssl_ctx); 00401 cfg->ssl_ctx = NULL; 00402 return 0; 00403 } 00404 } 00405 } 00406 if (!ast_strlen_zero(cfg->cafile) || !ast_strlen_zero(cfg->capath)) { 00407 if (SSL_CTX_load_verify_locations(cfg->ssl_ctx, S_OR(cfg->cafile, NULL), S_OR(cfg->capath,NULL)) == 0) 00408 ast_verb(0, "SSL CA file(%s)/path(%s) error\n", cfg->cafile, cfg->capath); 00409 } 00410 00411 ast_verb(0, "SSL certificate ok\n"); 00412 return 1; 00413 #endif 00414 }
int ast_ssl_setup | ( | struct ast_tls_config * | cfg | ) |
Set up an SSL server.
cfg | Configuration for the SSL server |
1 | Success | |
0 | Failure |
Definition at line 416 of file tcptls.c.
References __ssl_setup().
Referenced by __ast_http_load(), __init_manager(), and reload_config().
00417 { 00418 return __ssl_setup(cfg, 0); 00419 }
void ast_ssl_teardown | ( | struct ast_tls_config * | cfg | ) |
free resources used by an SSL server
cfg | Configuration for the SSL server |
Definition at line 421 of file tcptls.c.
References ast_tls_config::ssl_ctx.
Referenced by sip_tcptls_client_args_destructor(), and unload_module().
struct ast_tcptls_session_instance* ast_tcptls_client_create | ( | struct ast_tcptls_session_args * | desc | ) | [read] |
Definition at line 468 of file tcptls.c.
References ast_tcptls_session_args::accept_fd, ao2_alloc, ao2_ref, ast_bind(), ast_debug, ast_log(), ast_mutex_init, ast_sockaddr_cmp(), ast_sockaddr_copy(), ast_sockaddr_is_ipv6(), ast_sockaddr_isnull(), ast_sockaddr_setnull(), ast_sockaddr_stringify(), ast_str_create(), ast_tcptls_session_instance::client, errno, ast_tcptls_session_instance::fd, ast_tcptls_session_args::local_address, ast_tcptls_session_instance::lock, LOG_ERROR, LOG_WARNING, ast_tcptls_session_args::name, ast_tcptls_session_args::old_address, ast_tcptls_session_instance::overflow_buf, ast_tcptls_session_instance::parent, ast_tcptls_session_instance::remote_address, ast_tcptls_session_args::remote_address, session_instance_destructor(), and ast_tcptls_session_args::worker_fn.
Referenced by app_exec(), and sip_prepare_socket().
00469 { 00470 int x = 1; 00471 struct ast_tcptls_session_instance *tcptls_session = NULL; 00472 00473 /* Do nothing if nothing has changed */ 00474 if (!ast_sockaddr_cmp(&desc->old_address, &desc->remote_address)) { 00475 ast_debug(1, "Nothing changed in %s\n", desc->name); 00476 return NULL; 00477 } 00478 00479 /* If we return early, there is no connection */ 00480 ast_sockaddr_setnull(&desc->old_address); 00481 00482 if (desc->accept_fd != -1) 00483 close(desc->accept_fd); 00484 00485 desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->remote_address) ? 00486 AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP); 00487 if (desc->accept_fd < 0) { 00488 ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n", 00489 desc->name, strerror(errno)); 00490 return NULL; 00491 } 00492 00493 /* if a local address was specified, bind to it so the connection will 00494 originate from the desired address */ 00495 if (!ast_sockaddr_isnull(&desc->local_address)) { 00496 setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); 00497 if (ast_bind(desc->accept_fd, &desc->local_address)) { 00498 ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n", 00499 desc->name, 00500 ast_sockaddr_stringify(&desc->local_address), 00501 strerror(errno)); 00502 goto error; 00503 } 00504 } 00505 00506 if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor))) 00507 goto error; 00508 00509 ast_mutex_init(&tcptls_session->lock); 00510 tcptls_session->overflow_buf = ast_str_create(128); 00511 tcptls_session->client = 1; 00512 tcptls_session->fd = desc->accept_fd; 00513 tcptls_session->parent = desc; 00514 tcptls_session->parent->worker_fn = NULL; 00515 ast_sockaddr_copy(&tcptls_session->remote_address, 00516 &desc->remote_address); 00517 00518 /* Set current info */ 00519 ast_sockaddr_copy(&desc->old_address, &desc->remote_address); 00520 return tcptls_session; 00521 00522 error: 00523 close(desc->accept_fd); 00524 desc->accept_fd = -1; 00525 if (tcptls_session) 00526 ao2_ref(tcptls_session, -1); 00527 return NULL; 00528 }
struct ast_tcptls_session_instance* ast_tcptls_client_start | ( | struct ast_tcptls_session_instance * | tcptls_session | ) | [read] |
attempts to connect and start tcptls session, on error the tcptls_session's ref count is decremented, fd and file are closed, and NULL is returned.
Definition at line 431 of file tcptls.c.
References __ssl_setup(), ast_tcptls_session_args::accept_fd, ao2_ref, ast_connect(), ast_log(), ast_sockaddr_stringify(), desc, ast_tls_config::enabled, errno, handle_tcptls_connection(), LOG_ERROR, ast_tcptls_session_args::name, ast_tcptls_session_instance::parent, ast_tcptls_session_args::remote_address, and ast_tcptls_session_args::tls_cfg.
Referenced by _sip_tcp_helper_thread(), and app_exec().
00432 { 00433 struct ast_tcptls_session_args *desc; 00434 int flags; 00435 00436 if (!(desc = tcptls_session->parent)) { 00437 goto client_start_error; 00438 } 00439 00440 if (ast_connect(desc->accept_fd, &desc->remote_address)) { 00441 ast_log(LOG_ERROR, "Unable to connect %s to %s: %s\n", 00442 desc->name, 00443 ast_sockaddr_stringify(&desc->remote_address), 00444 strerror(errno)); 00445 goto client_start_error; 00446 } 00447 00448 flags = fcntl(desc->accept_fd, F_GETFL); 00449 fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK); 00450 00451 if (desc->tls_cfg) { 00452 desc->tls_cfg->enabled = 1; 00453 __ssl_setup(desc->tls_cfg, 1); 00454 } 00455 00456 return handle_tcptls_connection(tcptls_session); 00457 00458 client_start_error: 00459 if (desc) { 00460 close(desc->accept_fd); 00461 desc->accept_fd = -1; 00462 } 00463 ao2_ref(tcptls_session, -1); 00464 return NULL; 00465 00466 }
void ast_tcptls_close_session_file | ( | struct ast_tcptls_session_instance * | tcptls_session | ) |
Closes a tcptls session instance's file and/or file descriptor. The tcptls_session will be set to NULL and it's file descriptor will be set to -1 by this function.
Definition at line 599 of file tcptls.c.
References ast_log(), errno, ast_tcptls_session_instance::f, ast_tcptls_session_instance::fd, and LOG_ERROR.
Referenced by _sip_tcp_helper_thread(), ast_tcptls_server_root(), handle_tcptls_connection(), and sip_prepare_socket().
00600 { 00601 if (tcptls_session->f) { 00602 if (fclose(tcptls_session->f)) { 00603 ast_log(LOG_ERROR, "fclose() failed: %s\n", strerror(errno)); 00604 } 00605 tcptls_session->f = NULL; 00606 tcptls_session->fd = -1; 00607 } else if (tcptls_session->fd != -1) { 00608 if (close(tcptls_session->fd)) { 00609 ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno)); 00610 } 00611 tcptls_session->fd = -1; 00612 } else { 00613 ast_log(LOG_ERROR, "ast_tcptls_close_session_file invoked on session instance without file or file descriptor\n"); 00614 } 00615 }
HOOK_T ast_tcptls_server_read | ( | struct ast_tcptls_session_instance * | tcptls_session, | |
void * | buf, | |||
size_t | count | |||
) |
Definition at line 111 of file tcptls.c.
References ast_log(), errno, ast_tcptls_session_instance::fd, LOG_ERROR, ast_tcptls_session_instance::ssl, and ssl_read().
00112 { 00113 if (tcptls_session->fd == -1) { 00114 ast_log(LOG_ERROR, "server_read called with an fd of -1\n"); 00115 errno = EIO; 00116 return -1; 00117 } 00118 00119 #ifdef DO_SSL 00120 if (tcptls_session->ssl) 00121 return ssl_read(tcptls_session->ssl, buf, count); 00122 #endif 00123 return read(tcptls_session->fd, buf, count); 00124 }
void* ast_tcptls_server_root | ( | void * | data | ) |
Definition at line 271 of file tcptls.c.
References ast_tcptls_session_args::accept_fd, ao2_alloc, ao2_ref, ast_accept(), ast_log(), ast_mutex_init, ast_pthread_create_detached_background, ast_sockaddr_copy(), ast_str_create(), ast_tcptls_close_session_file(), ast_wait_for_input(), ast_tcptls_session_instance::client, desc, errno, ast_tcptls_session_instance::fd, handle_tcptls_connection(), ast_tcptls_session_instance::lock, LOG_ERROR, LOG_WARNING, ast_tcptls_session_instance::overflow_buf, ast_tcptls_session_instance::parent, ast_tcptls_session_args::periodic_fn, ast_tcptls_session_args::poll_timeout, ast_tcptls_session_instance::remote_address, and session_instance_destructor().
00272 { 00273 struct ast_tcptls_session_args *desc = data; 00274 int fd; 00275 struct ast_sockaddr addr; 00276 struct ast_tcptls_session_instance *tcptls_session; 00277 pthread_t launched; 00278 00279 for (;;) { 00280 int i, flags; 00281 00282 if (desc->periodic_fn) 00283 desc->periodic_fn(desc); 00284 i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout); 00285 if (i <= 0) 00286 continue; 00287 fd = ast_accept(desc->accept_fd, &addr); 00288 if (fd < 0) { 00289 if ((errno != EAGAIN) && (errno != EINTR)) 00290 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno)); 00291 continue; 00292 } 00293 tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor); 00294 if (!tcptls_session) { 00295 ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno)); 00296 if (close(fd)) { 00297 ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno)); 00298 } 00299 continue; 00300 } 00301 00302 ast_mutex_init(&tcptls_session->lock); 00303 tcptls_session->overflow_buf = ast_str_create(128); 00304 00305 flags = fcntl(fd, F_GETFL); 00306 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); 00307 tcptls_session->fd = fd; 00308 tcptls_session->parent = desc; 00309 ast_sockaddr_copy(&tcptls_session->remote_address, &addr); 00310 00311 tcptls_session->client = 0; 00312 00313 /* This thread is now the only place that controls the single ref to tcptls_session */ 00314 if (ast_pthread_create_detached_background(&launched, NULL, handle_tcptls_connection, tcptls_session)) { 00315 ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno)); 00316 ast_tcptls_close_session_file(tcptls_session); 00317 ao2_ref(tcptls_session, -1); 00318 } 00319 } 00320 return NULL; 00321 }
void ast_tcptls_server_start | ( | struct ast_tcptls_session_args * | desc | ) |
This is a generic (re)start routine for a TCP server, which does the socket/bind/listen and starts a thread for handling accept().
Definition at line 530 of file tcptls.c.
References ast_tcptls_session_args::accept_fd, ast_tcptls_session_args::accept_fn, ast_bind(), ast_debug, ast_log(), ast_pthread_create_background, AST_PTHREADT_NULL, ast_sockaddr_cmp(), ast_sockaddr_copy(), ast_sockaddr_is_ipv6(), ast_sockaddr_isnull(), ast_sockaddr_setnull(), ast_sockaddr_stringify(), errno, ast_tcptls_session_args::local_address, LOG_ERROR, ast_tcptls_session_args::master, ast_tcptls_session_args::name, and ast_tcptls_session_args::old_address.
Referenced by __ast_http_load(), __init_manager(), and reload_config().
00531 { 00532 int flags; 00533 int x = 1; 00534 00535 /* Do nothing if nothing has changed */ 00536 if (!ast_sockaddr_cmp(&desc->old_address, &desc->local_address)) { 00537 ast_debug(1, "Nothing changed in %s\n", desc->name); 00538 return; 00539 } 00540 00541 /* If we return early, there is no one listening */ 00542 ast_sockaddr_setnull(&desc->old_address); 00543 00544 /* Shutdown a running server if there is one */ 00545 if (desc->master != AST_PTHREADT_NULL) { 00546 pthread_cancel(desc->master); 00547 pthread_kill(desc->master, SIGURG); 00548 pthread_join(desc->master, NULL); 00549 } 00550 00551 if (desc->accept_fd != -1) 00552 close(desc->accept_fd); 00553 00554 /* If there's no new server, stop here */ 00555 if (ast_sockaddr_isnull(&desc->local_address)) { 00556 ast_debug(2, "Server disabled: %s\n", desc->name); 00557 return; 00558 } 00559 00560 desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->local_address) ? 00561 AF_INET6 : AF_INET, SOCK_STREAM, 0); 00562 if (desc->accept_fd < 0) { 00563 ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", desc->name, strerror(errno)); 00564 return; 00565 } 00566 00567 setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); 00568 if (ast_bind(desc->accept_fd, &desc->local_address)) { 00569 ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n", 00570 desc->name, 00571 ast_sockaddr_stringify(&desc->local_address), 00572 strerror(errno)); 00573 goto error; 00574 } 00575 if (listen(desc->accept_fd, 10)) { 00576 ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name); 00577 goto error; 00578 } 00579 flags = fcntl(desc->accept_fd, F_GETFL); 00580 fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK); 00581 if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) { 00582 ast_log(LOG_ERROR, "Unable to launch thread for %s on %s: %s\n", 00583 desc->name, 00584 ast_sockaddr_stringify(&desc->local_address), 00585 strerror(errno)); 00586 goto error; 00587 } 00588 00589 /* Set current info */ 00590 ast_sockaddr_copy(&desc->old_address, &desc->local_address); 00591 00592 return; 00593 00594 error: 00595 close(desc->accept_fd); 00596 desc->accept_fd = -1; 00597 }
void ast_tcptls_server_stop | ( | struct ast_tcptls_session_args * | desc | ) |
Shutdown a running server if there is one.
Definition at line 617 of file tcptls.c.
References ast_tcptls_session_args::accept_fd, ast_debug, AST_PTHREADT_NULL, ast_tcptls_session_args::master, and ast_tcptls_session_args::name.
Referenced by __ast_http_load(), __init_manager(), manager_shutdown(), and unload_module().
00618 { 00619 if (desc->master != AST_PTHREADT_NULL) { 00620 pthread_cancel(desc->master); 00621 pthread_kill(desc->master, SIGURG); 00622 pthread_join(desc->master, NULL); 00623 desc->master = AST_PTHREADT_NULL; 00624 } 00625 if (desc->accept_fd != -1) 00626 close(desc->accept_fd); 00627 desc->accept_fd = -1; 00628 ast_debug(2, "Stopped server :: %s\n", desc->name); 00629 }
HOOK_T ast_tcptls_server_write | ( | struct ast_tcptls_session_instance * | tcptls_session, | |
const void * | buf, | |||
size_t | count | |||
) |
Definition at line 126 of file tcptls.c.
References ast_log(), errno, ast_tcptls_session_instance::fd, LOG_ERROR, ast_tcptls_session_instance::ssl, and ssl_write().
Referenced by _sip_tcp_helper_thread().
00127 { 00128 if (tcptls_session->fd == -1) { 00129 ast_log(LOG_ERROR, "server_write called with an fd of -1\n"); 00130 errno = EIO; 00131 return -1; 00132 } 00133 00134 #ifdef DO_SSL 00135 if (tcptls_session->ssl) 00136 return ssl_write(tcptls_session->ssl, buf, count); 00137 #endif 00138 return write(tcptls_session->fd, buf, count); 00139 }
int ast_tls_read_conf | ( | struct ast_tls_config * | tls_cfg, | |
struct ast_tcptls_session_args * | tls_desc, | |||
const char * | varname, | |||
const char * | value | |||
) |
Used to parse conf files containing tls/ssl options.
Definition at line 631 of file tcptls.c.
References ast_clear_flag, ast_free, ast_log(), ast_parse_arg(), ast_set2_flag, ast_set_flag, AST_SSL_DONT_VERIFY_SERVER, AST_SSL_SSLV2_CLIENT, AST_SSL_SSLV3_CLIENT, AST_SSL_TLSV1_CLIENT, AST_SSL_VERIFY_CLIENT, ast_strdup, ast_true(), ast_tls_config::cafile, ast_tls_config::capath, ast_tls_config::certfile, ast_tls_config::cipher, ast_tls_config::enabled, ast_tls_config::flags, ast_tcptls_session_args::local_address, LOG_WARNING, PARSE_ADDR, and ast_tls_config::pvtfile.
Referenced by __ast_http_load(), __init_manager(), and reload_config().
00632 { 00633 if (!strcasecmp(varname, "tlsenable") || !strcasecmp(varname, "sslenable")) { 00634 tls_cfg->enabled = ast_true(value) ? 1 : 0; 00635 } else if (!strcasecmp(varname, "tlscertfile") || !strcasecmp(varname, "sslcert") || !strcasecmp(varname, "tlscert")) { 00636 ast_free(tls_cfg->certfile); 00637 tls_cfg->certfile = ast_strdup(value); 00638 } else if (!strcasecmp(varname, "tlsprivatekey") || !strcasecmp(varname, "sslprivatekey")) { 00639 ast_free(tls_cfg->pvtfile); 00640 tls_cfg->pvtfile = ast_strdup(value); 00641 } else if (!strcasecmp(varname, "tlscipher") || !strcasecmp(varname, "sslcipher")) { 00642 ast_free(tls_cfg->cipher); 00643 tls_cfg->cipher = ast_strdup(value); 00644 } else if (!strcasecmp(varname, "tlscafile")) { 00645 ast_free(tls_cfg->cafile); 00646 tls_cfg->cafile = ast_strdup(value); 00647 } else if (!strcasecmp(varname, "tlscapath") || !strcasecmp(varname, "tlscadir")) { 00648 ast_free(tls_cfg->capath); 00649 tls_cfg->capath = ast_strdup(value); 00650 } else if (!strcasecmp(varname, "tlsverifyclient")) { 00651 ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_VERIFY_CLIENT); 00652 } else if (!strcasecmp(varname, "tlsdontverifyserver")) { 00653 ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_DONT_VERIFY_SERVER); 00654 } else if (!strcasecmp(varname, "tlsbindaddr") || !strcasecmp(varname, "sslbindaddr")) { 00655 if (ast_parse_arg(value, PARSE_ADDR, &tls_desc->local_address)) 00656 ast_log(LOG_WARNING, "Invalid %s '%s'\n", varname, value); 00657 } else if (!strcasecmp(varname, "tlsclientmethod") || !strcasecmp(varname, "sslclientmethod")) { 00658 if (!strcasecmp(value, "tlsv1")) { 00659 ast_set_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT); 00660 ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT); 00661 ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT); 00662 } else if (!strcasecmp(value, "sslv3")) { 00663 ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT); 00664 ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT); 00665 ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT); 00666 } else if (!strcasecmp(value, "sslv2")) { 00667 ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT); 00668 ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT); 00669 ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT); 00670 } 00671 } else { 00672 return -1; 00673 } 00674 00675 return 0; 00676 }
static void* handle_tcptls_connection | ( | void * | data | ) | [static] |
creates a FILE * from the fd passed by the accept thread. This operation is potentially expensive (certificate verification), so we do it in the child thread context.
Definition at line 155 of file tcptls.c.
References ao2_ref, ast_debug, ast_log(), AST_SSL_DONT_VERIFY_SERVER, AST_SSL_IGNORE_COMMON_NAME, AST_SSL_VERIFY_CLIENT, ast_tcptls_close_session_file(), ast_test_flag, ast_verb, ast_tcptls_session_instance::client, ast_tcptls_session_instance::f, ast_tcptls_session_instance::fd, ast_tls_config::flags, ast_tcptls_session_args::hostname, LOG_ERROR, LOG_WARNING, name, ast_tcptls_session_instance::parent, ast_tcptls_session_instance::ssl, ssl_close(), ast_tls_config::ssl_ctx, ssl_read(), ssl_write(), str, ast_tcptls_session_args::tls_cfg, and ast_tcptls_session_args::worker_fn.
Referenced by ast_tcptls_client_start(), and ast_tcptls_server_root().
00156 { 00157 struct ast_tcptls_session_instance *tcptls_session = data; 00158 #ifdef DO_SSL 00159 int (*ssl_setup)(SSL *) = (tcptls_session->client) ? SSL_connect : SSL_accept; 00160 int ret; 00161 char err[256]; 00162 #endif 00163 00164 /* 00165 * open a FILE * as appropriate. 00166 */ 00167 if (!tcptls_session->parent->tls_cfg) { 00168 if ((tcptls_session->f = fdopen(tcptls_session->fd, "w+"))) { 00169 if(setvbuf(tcptls_session->f, NULL, _IONBF, 0)) { 00170 ast_tcptls_close_session_file(tcptls_session); 00171 } 00172 } 00173 } 00174 #ifdef DO_SSL 00175 else if ( (tcptls_session->ssl = SSL_new(tcptls_session->parent->tls_cfg->ssl_ctx)) ) { 00176 SSL_set_fd(tcptls_session->ssl, tcptls_session->fd); 00177 if ((ret = ssl_setup(tcptls_session->ssl)) <= 0) { 00178 ast_verb(2, "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err)); 00179 } else { 00180 #if defined(HAVE_FUNOPEN) /* the BSD interface */ 00181 tcptls_session->f = funopen(tcptls_session->ssl, ssl_read, ssl_write, NULL, ssl_close); 00182 00183 #elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */ 00184 static const cookie_io_functions_t cookie_funcs = { 00185 ssl_read, ssl_write, NULL, ssl_close 00186 }; 00187 tcptls_session->f = fopencookie(tcptls_session->ssl, "w+", cookie_funcs); 00188 #else 00189 /* could add other methods here */ 00190 ast_debug(2, "no tcptls_session->f methods attempted!\n"); 00191 #endif 00192 if ((tcptls_session->client && !ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER)) 00193 || (!tcptls_session->client && ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_VERIFY_CLIENT))) { 00194 X509 *peer; 00195 long res; 00196 peer = SSL_get_peer_certificate(tcptls_session->ssl); 00197 if (!peer) { 00198 ast_log(LOG_ERROR, "No peer SSL certificate to verify\n"); 00199 ast_tcptls_close_session_file(tcptls_session); 00200 ao2_ref(tcptls_session, -1); 00201 return NULL; 00202 } 00203 00204 res = SSL_get_verify_result(tcptls_session->ssl); 00205 if (res != X509_V_OK) { 00206 ast_log(LOG_ERROR, "Certificate did not verify: %s\n", X509_verify_cert_error_string(res)); 00207 X509_free(peer); 00208 ast_tcptls_close_session_file(tcptls_session); 00209 ao2_ref(tcptls_session, -1); 00210 return NULL; 00211 } 00212 if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) { 00213 ASN1_STRING *str; 00214 unsigned char *str2; 00215 X509_NAME *name = X509_get_subject_name(peer); 00216 int pos = -1; 00217 int found = 0; 00218 00219 for (;;) { 00220 /* Walk the certificate to check all available "Common Name" */ 00221 /* XXX Probably should do a gethostbyname on the hostname and compare that as well */ 00222 pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos); 00223 if (pos < 0) 00224 break; 00225 str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos)); 00226 ASN1_STRING_to_UTF8(&str2, str); 00227 if (str2) { 00228 if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) 00229 found = 1; 00230 ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2); 00231 OPENSSL_free(str2); 00232 } 00233 if (found) 00234 break; 00235 } 00236 if (!found) { 00237 ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname); 00238 X509_free(peer); 00239 ast_tcptls_close_session_file(tcptls_session); 00240 ao2_ref(tcptls_session, -1); 00241 return NULL; 00242 } 00243 } 00244 X509_free(peer); 00245 } 00246 } 00247 if (!tcptls_session->f) /* no success opening descriptor stacking */ 00248 SSL_free(tcptls_session->ssl); 00249 } 00250 #endif /* DO_SSL */ 00251 00252 if (!tcptls_session->f) { 00253 ast_tcptls_close_session_file(tcptls_session); 00254 ast_log(LOG_WARNING, "FILE * open failed!\n"); 00255 #ifndef DO_SSL 00256 if (tcptls_session->parent->tls_cfg) { 00257 ast_log(LOG_WARNING, "Attempted a TLS connection without OpenSSL support. This will not work!\n"); 00258 } 00259 #endif 00260 ao2_ref(tcptls_session, -1); 00261 return NULL; 00262 } 00263 00264 if (tcptls_session->parent->worker_fn) { 00265 return tcptls_session->parent->worker_fn(tcptls_session); 00266 } else { 00267 return tcptls_session; 00268 } 00269 }
static void session_instance_destructor | ( | void * | obj | ) | [static] |
Definition at line 141 of file tcptls.c.
References ast_free, ast_mutex_destroy, ast_tcptls_session_instance::lock, and ast_tcptls_session_instance::overflow_buf.
Referenced by ast_tcptls_client_create(), and ast_tcptls_server_root().
00142 { 00143 struct ast_tcptls_session_instance *i = obj; 00144 ast_free(i->overflow_buf); 00145 ast_mutex_destroy(&i->lock); 00146 }
static int ssl_close | ( | void * | cookie | ) | [static] |
Definition at line 81 of file tcptls.c.
References ast_log(), errno, and LOG_ERROR.
Referenced by handle_tcptls_connection().
00082 { 00083 int cookie_fd = SSL_get_fd(cookie); 00084 int ret; 00085 00086 if (cookie_fd > -1) { 00087 /* 00088 * According to the TLS standard, it is acceptable for an application to only send its shutdown 00089 * alert and then close the underlying connection without waiting for the peer's response (this 00090 * way resources can be saved, as the process can already terminate or serve another connection). 00091 */ 00092 if ((ret = SSL_shutdown(cookie)) < 0) { 00093 ast_log(LOG_ERROR, "SSL_shutdown() failed: %d\n", SSL_get_error(cookie, ret)); 00094 } 00095 00096 if (!((SSL*)cookie)->server) { 00097 /* For client threads, ensure that the error stack is cleared */ 00098 ERR_remove_state(0); 00099 } 00100 00101 SSL_free(cookie); 00102 /* adding shutdown(2) here has no added benefit */ 00103 if (close(cookie_fd)) { 00104 ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno)); 00105 } 00106 } 00107 return 0; 00108 }
static HOOK_T ssl_read | ( | void * | cookie, | |
char * | buf, | |||
LEN_T | len | |||
) | [static] |
replacement read/write functions for SSL support. We use wrappers rather than SSL_read/SSL_write directly so we can put in some debugging.
Definition at line 59 of file tcptls.c.
References ast_verb.
Referenced by ast_tcptls_server_read(), and handle_tcptls_connection().
static HOOK_T ssl_write | ( | void * | cookie, | |
const char * | buf, | |||
LEN_T | len | |||
) | [static] |
Definition at line 70 of file tcptls.c.
References ast_alloca, and ast_verb.
Referenced by ast_tcptls_server_write(), and handle_tcptls_connection().