Generic support for tcp/tls servers in Asterisk. More...
#include "asterisk/netsock2.h"#include "asterisk/utils.h"#include <openssl/ssl.h>#include <openssl/err.h>

Go to the source code of this file.
Data Structures | |
| struct | ast_tcptls_session_args |
| arguments for the accepting thread More... | |
| struct | ast_tcptls_session_instance |
| struct | ast_tls_config |
Defines | |
| #define | AST_CERTFILE "asterisk.pem" |
| #define | DO_SSL |
| #define | HOOK_T ssize_t |
| #define | LEN_T size_t |
Enumerations | |
| enum | ast_ssl_flags { AST_SSL_VERIFY_CLIENT = (1 << 0), AST_SSL_DONT_VERIFY_SERVER = (1 << 1), AST_SSL_IGNORE_COMMON_NAME = (1 << 2), AST_SSL_SSLV2_CLIENT = (1 << 3), AST_SSL_SSLV3_CLIENT = (1 << 4), AST_SSL_TLSV1_CLIENT = (1 << 5) } |
Functions | |
| int | ast_ssl_setup (struct ast_tls_config *cfg) |
| 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. | |
| HOOK_T | ast_tcptls_server_read (struct ast_tcptls_session_instance *ser, void *buf, size_t count) |
| void * | ast_tcptls_server_root (void *) |
| 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 *ser, 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. | |
Generic support for tcp/tls servers in Asterisk.
TLS/SSL support is basically implemented by reading from a config file (currently http.conf and sip.conf) the names of the certificate and cipher to use, and then run ssl_setup() to create an appropriate SSL_CTX (ssl_ctx) If we support multiple domains, presumably we need to read multiple certificates.
When we are requested to open a TLS socket, we run make_file_from_fd() on the socket, to do the necessary setup. At the moment the context's name is hardwired in the function, but we can certainly make it into an extra parameter to the function.
We declare most of ssl support variables unconditionally, because their number is small and this simplifies the code.
Definition in file tcptls.h.
| #define AST_CERTFILE "asterisk.pem" |
SSL support
Definition at line 68 of file tcptls.h.
Referenced by reload_config(), __ast_http_load(), and __init_manager().
| enum ast_ssl_flags |
Definition at line 70 of file tcptls.h.
{
/*! Verify certificate when acting as server */
AST_SSL_VERIFY_CLIENT = (1 << 0),
/*! Don't verify certificate when connecting to a server */
AST_SSL_DONT_VERIFY_SERVER = (1 << 1),
/*! Don't compare "Common Name" against IP or hostname */
AST_SSL_IGNORE_COMMON_NAME = (1 << 2),
/*! Use SSLv2 for outgoing client connections */
AST_SSL_SSLV2_CLIENT = (1 << 3),
/*! Use SSLv3 for outgoing client connections */
AST_SSL_SSLV3_CLIENT = (1 << 4),
/*! Use TLSv1 for outgoing client connections */
AST_SSL_TLSV1_CLIENT = (1 << 5)
};
| int ast_ssl_setup | ( | struct ast_tls_config * | cfg | ) |
Definition at line 367 of file tcptls.c.
References __ssl_setup().
Referenced by reload_config(), __ast_http_load(), and __init_manager().
{
return __ssl_setup(cfg, 0);
}
| struct ast_tcptls_session_instance* ast_tcptls_client_create | ( | struct ast_tcptls_session_args * | desc | ) | [read] |
Definition at line 409 of file tcptls.c.
References ast_sockaddr_cmp(), ast_tcptls_session_args::old_address, ast_tcptls_session_args::remote_address, ast_debug, ast_tcptls_session_args::name, ast_sockaddr_setnull(), ast_tcptls_session_args::accept_fd, ast_sockaddr_is_ipv6(), ast_log(), LOG_WARNING, errno, ast_sockaddr_isnull(), ast_tcptls_session_args::local_address, ast_bind(), LOG_ERROR, ast_sockaddr_stringify(), ao2_alloc, session_instance_destructor(), ast_mutex_init, ast_tcptls_session_instance::lock, ast_tcptls_session_instance::client, ast_tcptls_session_instance::fd, ast_tcptls_session_instance::parent, desc, ast_tcptls_session_args::worker_fn, ast_sockaddr_copy(), ast_tcptls_session_instance::remote_address, and ao2_ref.
Referenced by app_exec(), and sip_prepare_socket().
{
int x = 1;
struct ast_tcptls_session_instance *tcptls_session = NULL;
/* Do nothing if nothing has changed */
if (!ast_sockaddr_cmp(&desc->old_address, &desc->remote_address)) {
ast_debug(1, "Nothing changed in %s\n", desc->name);
return NULL;
}
/* If we return early, there is no connection */
ast_sockaddr_setnull(&desc->old_address);
if (desc->accept_fd != -1)
close(desc->accept_fd);
desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->remote_address) ?
AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (desc->accept_fd < 0) {
ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
desc->name, strerror(errno));
return NULL;
}
/* if a local address was specified, bind to it so the connection will
originate from the desired address */
if (!ast_sockaddr_isnull(&desc->local_address)) {
setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
if (ast_bind(desc->accept_fd, &desc->local_address)) {
ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
desc->name,
ast_sockaddr_stringify(&desc->local_address),
strerror(errno));
goto error;
}
}
if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor)))
goto error;
ast_mutex_init(&tcptls_session->lock);
tcptls_session->client = 1;
tcptls_session->fd = desc->accept_fd;
tcptls_session->parent = desc;
tcptls_session->parent->worker_fn = NULL;
ast_sockaddr_copy(&tcptls_session->remote_address,
&desc->remote_address);
/* Set current info */
ast_sockaddr_copy(&desc->old_address, &desc->remote_address);
return tcptls_session;
error:
close(desc->accept_fd);
desc->accept_fd = -1;
if (tcptls_session)
ao2_ref(tcptls_session, -1);
return NULL;
}
| 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 372 of file tcptls.c.
References desc, ast_tcptls_session_instance::parent, ast_connect(), ast_tcptls_session_args::accept_fd, ast_tcptls_session_args::remote_address, ast_log(), LOG_ERROR, ast_tcptls_session_args::name, ast_sockaddr_stringify(), errno, ast_tcptls_session_args::tls_cfg, ast_tls_config::enabled, __ssl_setup(), handle_tcptls_connection(), and ao2_ref.
Referenced by app_exec(), and _sip_tcp_helper_thread().
{
struct ast_tcptls_session_args *desc;
int flags;
if (!(desc = tcptls_session->parent)) {
goto client_start_error;
}
if (ast_connect(desc->accept_fd, &desc->remote_address)) {
ast_log(LOG_ERROR, "Unable to connect %s to %s: %s\n",
desc->name,
ast_sockaddr_stringify(&desc->remote_address),
strerror(errno));
goto client_start_error;
}
flags = fcntl(desc->accept_fd, F_GETFL);
fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK);
if (desc->tls_cfg) {
desc->tls_cfg->enabled = 1;
__ssl_setup(desc->tls_cfg, 1);
}
return handle_tcptls_connection(tcptls_session);
client_start_error:
close(desc->accept_fd);
desc->accept_fd = -1;
if (tcptls_session) {
ao2_ref(tcptls_session, -1);
}
return NULL;
}
| HOOK_T ast_tcptls_server_read | ( | struct ast_tcptls_session_instance * | ser, |
| void * | buf, | ||
| size_t | count | ||
| ) |
Definition at line 86 of file tcptls.c.
References ast_tcptls_session_instance::fd, ast_log(), LOG_ERROR, errno, ast_tcptls_session_instance::ssl, and ssl_read().
| void* ast_tcptls_server_root | ( | void * | ) |
Definition at line 238 of file tcptls.c.
References desc, ast_tcptls_session_args::periodic_fn, ast_wait_for_input(), ast_tcptls_session_args::accept_fd, ast_tcptls_session_args::poll_timeout, ast_accept(), errno, ast_log(), LOG_WARNING, ao2_alloc, session_instance_destructor(), ast_mutex_init, ast_tcptls_session_instance::lock, ast_tcptls_session_instance::fd, ast_tcptls_session_instance::parent, ast_sockaddr_copy(), ast_tcptls_session_instance::remote_address, ast_tcptls_session_instance::client, ast_pthread_create_detached_background, handle_tcptls_connection(), and ao2_ref.
{
struct ast_tcptls_session_args *desc = data;
int fd;
struct ast_sockaddr addr;
struct ast_tcptls_session_instance *tcptls_session;
pthread_t launched;
for (;;) {
int i, flags;
if (desc->periodic_fn)
desc->periodic_fn(desc);
i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
if (i <= 0)
continue;
fd = ast_accept(desc->accept_fd, &addr);
if (fd < 0) {
if ((errno != EAGAIN) && (errno != EINTR))
ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
continue;
}
tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor);
if (!tcptls_session) {
ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
close(fd);
continue;
}
ast_mutex_init(&tcptls_session->lock);
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
tcptls_session->fd = fd;
tcptls_session->parent = desc;
ast_sockaddr_copy(&tcptls_session->remote_address, &addr);
tcptls_session->client = 0;
/* This thread is now the only place that controls the single ref to tcptls_session */
if (ast_pthread_create_detached_background(&launched, NULL, handle_tcptls_connection, tcptls_session)) {
ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
close(tcptls_session->fd);
ao2_ref(tcptls_session, -1);
}
}
return NULL;
}
| 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 470 of file tcptls.c.
References ast_sockaddr_cmp(), ast_tcptls_session_args::old_address, ast_tcptls_session_args::local_address, ast_debug, ast_tcptls_session_args::name, ast_sockaddr_setnull(), ast_tcptls_session_args::master, AST_PTHREADT_NULL, ast_tcptls_session_args::accept_fd, ast_sockaddr_isnull(), ast_sockaddr_is_ipv6(), ast_log(), LOG_ERROR, errno, ast_bind(), ast_sockaddr_stringify(), ast_pthread_create_background, ast_tcptls_session_args::accept_fn, and ast_sockaddr_copy().
Referenced by reload_config(), __ast_http_load(), and __init_manager().
{
int flags;
int x = 1;
/* Do nothing if nothing has changed */
if (!ast_sockaddr_cmp(&desc->old_address, &desc->local_address)) {
ast_debug(1, "Nothing changed in %s\n", desc->name);
return;
}
/* If we return early, there is no one listening */
ast_sockaddr_setnull(&desc->old_address);
/* Shutdown a running server if there is one */
if (desc->master != AST_PTHREADT_NULL) {
pthread_cancel(desc->master);
pthread_kill(desc->master, SIGURG);
pthread_join(desc->master, NULL);
}
if (desc->accept_fd != -1)
close(desc->accept_fd);
/* If there's no new server, stop here */
if (ast_sockaddr_isnull(&desc->local_address)) {
ast_debug(2, "Server disabled: %s\n", desc->name);
return;
}
desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->local_address) ?
AF_INET6 : AF_INET, SOCK_STREAM, 0);
if (desc->accept_fd < 0) {
ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", desc->name, strerror(errno));
return;
}
setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
if (ast_bind(desc->accept_fd, &desc->local_address)) {
ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
desc->name,
ast_sockaddr_stringify(&desc->local_address),
strerror(errno));
goto error;
}
if (listen(desc->accept_fd, 10)) {
ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name);
goto error;
}
flags = fcntl(desc->accept_fd, F_GETFL);
fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
ast_log(LOG_ERROR, "Unable to launch thread for %s on %s: %s\n",
desc->name,
ast_sockaddr_stringify(&desc->local_address),
strerror(errno));
goto error;
}
/* Set current info */
ast_sockaddr_copy(&desc->old_address, &desc->local_address);
return;
error:
close(desc->accept_fd);
desc->accept_fd = -1;
}
| void ast_tcptls_server_stop | ( | struct ast_tcptls_session_args * | desc | ) |
Shutdown a running server if there is one.
Definition at line 539 of file tcptls.c.
References ast_tcptls_session_args::master, AST_PTHREADT_NULL, ast_tcptls_session_args::accept_fd, ast_debug, and ast_tcptls_session_args::name.
Referenced by unload_module().
{
if (desc->master != AST_PTHREADT_NULL) {
pthread_cancel(desc->master);
pthread_kill(desc->master, SIGURG);
pthread_join(desc->master, NULL);
desc->master = AST_PTHREADT_NULL;
}
if (desc->accept_fd != -1)
close(desc->accept_fd);
desc->accept_fd = -1;
ast_debug(2, "Stopped server :: %s\n", desc->name);
}
| HOOK_T ast_tcptls_server_write | ( | struct ast_tcptls_session_instance * | ser, |
| const void * | buf, | ||
| size_t | count | ||
| ) |
Definition at line 101 of file tcptls.c.
References ast_tcptls_session_instance::fd, ast_log(), LOG_ERROR, errno, ast_tcptls_session_instance::ssl, and ssl_write().
Referenced by _sip_tcp_helper_thread().
| 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 553 of file tcptls.c.
References ast_tls_config::enabled, ast_true(), ast_free, ast_tls_config::certfile, ast_strdup, ast_tls_config::pvtfile, ast_tls_config::cipher, ast_tls_config::cafile, ast_tls_config::capath, ast_set2_flag, ast_tls_config::flags, AST_SSL_VERIFY_CLIENT, AST_SSL_DONT_VERIFY_SERVER, ast_parse_arg(), PARSE_ADDR, ast_tcptls_session_args::local_address, ast_log(), LOG_WARNING, ast_set_flag, AST_SSL_TLSV1_CLIENT, ast_clear_flag, AST_SSL_SSLV3_CLIENT, and AST_SSL_SSLV2_CLIENT.
Referenced by reload_config(), __ast_http_load(), and __init_manager().
{
if (!strcasecmp(varname, "tlsenable") || !strcasecmp(varname, "sslenable")) {
tls_cfg->enabled = ast_true(value) ? 1 : 0;
} else if (!strcasecmp(varname, "tlscertfile") || !strcasecmp(varname, "sslcert") || !strcasecmp(varname, "tlscert")) {
ast_free(tls_cfg->certfile);
tls_cfg->certfile = ast_strdup(value);
} else if (!strcasecmp(varname, "tlsprivatekey") || !strcasecmp(varname, "sslprivatekey")) {
ast_free(tls_cfg->pvtfile);
tls_cfg->pvtfile = ast_strdup(value);
} else if (!strcasecmp(varname, "tlscipher") || !strcasecmp(varname, "sslcipher")) {
ast_free(tls_cfg->cipher);
tls_cfg->cipher = ast_strdup(value);
} else if (!strcasecmp(varname, "tlscafile")) {
ast_free(tls_cfg->cafile);
tls_cfg->cafile = ast_strdup(value);
} else if (!strcasecmp(varname, "tlscapath") || !strcasecmp(varname, "tlscadir")) {
ast_free(tls_cfg->capath);
tls_cfg->capath = ast_strdup(value);
} else if (!strcasecmp(varname, "tlsverifyclient")) {
ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_VERIFY_CLIENT);
} else if (!strcasecmp(varname, "tlsdontverifyserver")) {
ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_DONT_VERIFY_SERVER);
} else if (!strcasecmp(varname, "tlsbindaddr") || !strcasecmp(varname, "sslbindaddr")) {
if (ast_parse_arg(value, PARSE_ADDR, &tls_desc->local_address))
ast_log(LOG_WARNING, "Invalid %s '%s'\n", varname, value);
} else if (!strcasecmp(varname, "tlsclientmethod") || !strcasecmp(varname, "sslclientmethod")) {
if (!strcasecmp(value, "tlsv1")) {
ast_set_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
} else if (!strcasecmp(value, "sslv3")) {
ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
} else if (!strcasecmp(value, "sslv2")) {
ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
}
} else {
return -1;
}
return 0;
}