Wed Apr 6 11:30:11 2011

Asterisk developer's documentation


tcptls.h File Reference

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)
ast_tcptls_session_instanceast_tcptls_client_create (struct ast_tcptls_session_args *desc)
ast_tcptls_session_instanceast_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.


Detailed Description

Generic support for tcp/tls servers in Asterisk.

Note:
In order to have TLS/SSL support, we need the openssl libraries. Still we can decide whether or not to use them by commenting in or out the DO_SSL macro.
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.

Note:
The ssl-support variables (ssl_ctx, do_ssl, certfile, cipher) and their setup should be moved to a more central place, e.g. asterisk.conf and the source files that processes it. Similarly, ssl_setup() should be run earlier in the startup process so modules have it available.

Definition in file tcptls.h.


Define Documentation

#define AST_CERTFILE   "asterisk.pem"

SSL support

Definition at line 68 of file tcptls.h.

Referenced by __ast_http_load(), and __init_manager().

#define DO_SSL

Definition at line 55 of file tcptls.h.

#define HOOK_T   ssize_t

Definition at line 156 of file tcptls.h.

#define LEN_T   size_t

Definition at line 157 of file tcptls.h.


Enumeration Type Documentation

enum ast_ssl_flags

Enumerator:
AST_SSL_VERIFY_CLIENT  Verify certificate when acting as server
AST_SSL_DONT_VERIFY_SERVER  Don't verify certificate when connecting to a server
AST_SSL_IGNORE_COMMON_NAME  Don't compare "Common Name" against IP or hostname
AST_SSL_SSLV2_CLIENT  Use SSLv2 for outgoing client connections
AST_SSL_SSLV3_CLIENT  Use SSLv3 for outgoing client connections
AST_SSL_TLSV1_CLIENT  Use TLSv1 for outgoing client connections

Definition at line 70 of file tcptls.h.

00070                    {
00071    /*! Verify certificate when acting as server */
00072    AST_SSL_VERIFY_CLIENT = (1 << 0),
00073    /*! Don't verify certificate when connecting to a server */
00074    AST_SSL_DONT_VERIFY_SERVER = (1 << 1),
00075    /*! Don't compare "Common Name" against IP or hostname */
00076    AST_SSL_IGNORE_COMMON_NAME = (1 << 2),
00077    /*! Use SSLv2 for outgoing client connections */
00078    AST_SSL_SSLV2_CLIENT = (1 << 3),
00079    /*! Use SSLv3 for outgoing client connections */
00080    AST_SSL_SSLV3_CLIENT = (1 << 4),
00081    /*! Use TLSv1 for outgoing client connections */
00082    AST_SSL_TLSV1_CLIENT = (1 << 5)
00083 };


Function Documentation

int ast_ssl_setup ( struct ast_tls_config cfg  ) 

Definition at line 364 of file tcptls.c.

References __ssl_setup().

00365 {
00366    return __ssl_setup(cfg, 0);
00367 }

struct ast_tcptls_session_instance* ast_tcptls_client_create ( struct ast_tcptls_session_args desc  ) 

Definition at line 406 of file tcptls.c.

References 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_stringify(), desc, errno, LOG_ERROR, LOG_WARNING, and session_instance_destructor().

Referenced by sip_prepare_socket().

00407 {
00408    int x = 1;
00409    struct ast_tcptls_session_instance *tcptls_session = NULL;
00410 
00411    /* Do nothing if nothing has changed */
00412    if (!ast_sockaddr_cmp(&desc->old_address, &desc->remote_address)) {
00413       ast_debug(1, "Nothing changed in %s\n", desc->name);
00414       return NULL;
00415    }
00416 
00417    ast_sockaddr_copy(&desc->old_address, &desc->remote_address);
00418 
00419    if (desc->accept_fd != -1)
00420       close(desc->accept_fd);
00421 
00422    desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->remote_address) ?
00423              AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP);
00424    if (desc->accept_fd < 0) {
00425       ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
00426          desc->name, strerror(errno));
00427       return NULL;
00428    }
00429 
00430    /* if a local address was specified, bind to it so the connection will
00431       originate from the desired address */
00432    if (!ast_sockaddr_isnull(&desc->local_address)) {
00433       setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00434       if (ast_bind(desc->accept_fd, &desc->local_address)) {
00435          ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
00436             desc->name,
00437             ast_sockaddr_stringify(&desc->local_address),
00438             strerror(errno));
00439          goto error;
00440       }
00441    }
00442 
00443    if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor)))
00444       goto error;
00445 
00446    ast_mutex_init(&tcptls_session->lock);
00447    tcptls_session->client = 1;
00448    tcptls_session->fd = desc->accept_fd;
00449    tcptls_session->parent = desc;
00450    tcptls_session->parent->worker_fn = NULL;
00451    ast_sockaddr_copy(&tcptls_session->remote_address,
00452            &desc->remote_address);
00453 
00454    return tcptls_session;
00455 
00456 error:
00457    close(desc->accept_fd);
00458    desc->accept_fd = -1;
00459    if (tcptls_session)
00460       ao2_ref(tcptls_session, -1);
00461    return NULL;
00462 }

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.

Definition at line 369 of file tcptls.c.

References __ssl_setup(), ao2_ref, ast_connect(), ast_log(), ast_sockaddr_stringify(), desc, errno, handle_tcptls_connection(), LOG_ERROR, and ast_tcptls_session_instance::parent.

Referenced by _sip_tcp_helper_thread().

00370 {
00371    struct ast_tcptls_session_args *desc;
00372    int flags;
00373 
00374    if (!(desc = tcptls_session->parent)) {
00375       goto client_start_error;
00376    }
00377 
00378    if (ast_connect(desc->accept_fd, &desc->remote_address)) {
00379       ast_log(LOG_ERROR, "Unable to connect %s to %s: %s\n",
00380          desc->name,
00381          ast_sockaddr_stringify(&desc->remote_address),
00382          strerror(errno));
00383       goto client_start_error;
00384    }
00385 
00386    flags = fcntl(desc->accept_fd, F_GETFL);
00387    fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK);
00388 
00389    if (desc->tls_cfg) {
00390       desc->tls_cfg->enabled = 1;
00391       __ssl_setup(desc->tls_cfg, 1);
00392    }
00393 
00394    return handle_tcptls_connection(tcptls_session);
00395 
00396 client_start_error:
00397    close(desc->accept_fd);
00398    desc->accept_fd = -1;
00399    if (tcptls_session) {
00400       ao2_ref(tcptls_session, -1);
00401    }
00402    return NULL;
00403 
00404 }

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_log(), errno, ast_tcptls_session_instance::fd, LOG_ERROR, ast_tcptls_session_instance::ssl, and ssl_read().

00087 {
00088    if (tcptls_session->fd == -1) {
00089       ast_log(LOG_ERROR, "server_read called with an fd of -1\n");
00090       errno = EIO;
00091       return -1;
00092    }
00093 
00094 #ifdef DO_SSL
00095    if (tcptls_session->ssl)
00096       return ssl_read(tcptls_session->ssl, buf, count);
00097 #endif
00098    return read(tcptls_session->fd, buf, count);
00099 }

void* ast_tcptls_server_root ( void *   ) 

Definition at line 238 of file tcptls.c.

References ao2_alloc, ao2_ref, ast_accept(), ast_log(), ast_mutex_init, ast_pthread_create_detached_background, ast_sockaddr_copy(), ast_wait_for_input(), desc, errno, handle_tcptls_connection(), LOG_WARNING, and session_instance_destructor().

00239 {
00240    struct ast_tcptls_session_args *desc = data;
00241    int fd;
00242    struct ast_sockaddr addr;
00243    struct ast_tcptls_session_instance *tcptls_session;
00244    pthread_t launched;
00245 
00246    for (;;) {
00247       int i, flags;
00248 
00249       if (desc->periodic_fn)
00250          desc->periodic_fn(desc);
00251       i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
00252       if (i <= 0)
00253          continue;
00254       fd = ast_accept(desc->accept_fd, &addr);
00255       if (fd < 0) {
00256          if ((errno != EAGAIN) && (errno != EINTR))
00257             ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
00258          continue;
00259       }
00260       tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor);
00261       if (!tcptls_session) {
00262          ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
00263          close(fd);
00264          continue;
00265       }
00266 
00267       ast_mutex_init(&tcptls_session->lock);
00268 
00269       flags = fcntl(fd, F_GETFL);
00270       fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
00271       tcptls_session->fd = fd;
00272       tcptls_session->parent = desc;
00273       ast_sockaddr_copy(&tcptls_session->remote_address, &addr);
00274 
00275       tcptls_session->client = 0;
00276 
00277       /* This thread is now the only place that controls the single ref to tcptls_session */
00278       if (ast_pthread_create_detached_background(&launched, NULL, handle_tcptls_connection, tcptls_session)) {
00279          ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
00280          close(tcptls_session->fd);
00281          ao2_ref(tcptls_session, -1);
00282       }
00283    }
00284    return NULL;
00285 }

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().

Version:
1.6.1 changed desc parameter to be of ast_tcptls_session_args type

Definition at line 464 of file tcptls.c.

References 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_stringify(), desc, errno, and LOG_ERROR.

00465 {
00466    int flags;
00467    int x = 1;
00468 
00469    /* Do nothing if nothing has changed */
00470    if (!ast_sockaddr_cmp(&desc->old_address, &desc->local_address)) {
00471       ast_debug(1, "Nothing changed in %s\n", desc->name);
00472       return;
00473    }
00474 
00475    ast_sockaddr_copy(&desc->old_address, &desc->local_address);
00476 
00477    /* Shutdown a running server if there is one */
00478    if (desc->master != AST_PTHREADT_NULL) {
00479       pthread_cancel(desc->master);
00480       pthread_kill(desc->master, SIGURG);
00481       pthread_join(desc->master, NULL);
00482    }
00483 
00484    if (desc->accept_fd != -1)
00485       close(desc->accept_fd);
00486 
00487    /* If there's no new server, stop here */
00488    if (ast_sockaddr_isnull(&desc->local_address)) {
00489       ast_debug(2, "Server disabled:  %s\n", desc->name);
00490       return;
00491    }
00492 
00493    desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->local_address) ?
00494              AF_INET6 : AF_INET, SOCK_STREAM, 0);
00495    if (desc->accept_fd < 0) {
00496       ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", desc->name, strerror(errno));
00497       return;
00498    }
00499 
00500    setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00501    if (ast_bind(desc->accept_fd, &desc->local_address)) {
00502       ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
00503          desc->name,
00504          ast_sockaddr_stringify(&desc->local_address),
00505          strerror(errno));
00506       goto error;
00507    }
00508    if (listen(desc->accept_fd, 10)) {
00509       ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name);
00510       goto error;
00511    }
00512    flags = fcntl(desc->accept_fd, F_GETFL);
00513    fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
00514    if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
00515       ast_log(LOG_ERROR, "Unable to launch thread for %s on %s: %s\n",
00516          desc->name,
00517          ast_sockaddr_stringify(&desc->local_address),
00518          strerror(errno));
00519       goto error;
00520    }
00521    return;
00522 
00523 error:
00524    close(desc->accept_fd);
00525    desc->accept_fd = -1;
00526 }

void ast_tcptls_server_stop ( struct ast_tcptls_session_args desc  ) 

Shutdown a running server if there is one.

Version:
1.6.1 changed desc parameter to be of ast_tcptls_session_args type

Definition at line 528 of file tcptls.c.

References ast_debug, AST_PTHREADT_NULL, and desc.

Referenced by unload_module().

00529 {
00530    if (desc->master != AST_PTHREADT_NULL) {
00531       pthread_cancel(desc->master);
00532       pthread_kill(desc->master, SIGURG);
00533       pthread_join(desc->master, NULL);
00534    }
00535    if (desc->accept_fd != -1)
00536       close(desc->accept_fd);
00537    desc->accept_fd = -1;
00538    ast_debug(2, "Stopped server :: %s\n", desc->name);
00539 }

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_log(), errno, ast_tcptls_session_instance::fd, LOG_ERROR, ast_tcptls_session_instance::ssl, and ssl_write().

Referenced by _sip_tcp_helper_thread().

00102 {
00103    if (tcptls_session->fd == -1) {
00104       ast_log(LOG_ERROR, "server_write called with an fd of -1\n");
00105       errno = EIO;
00106       return -1;
00107    }
00108 
00109 #ifdef DO_SSL
00110    if (tcptls_session->ssl)
00111       return ssl_write(tcptls_session->ssl, buf, count);
00112 #endif
00113    return write(tcptls_session->fd, buf, count);
00114 }

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 541 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(), and __init_manager().

00542 {
00543    if (!strcasecmp(varname, "tlsenable") || !strcasecmp(varname, "sslenable")) {
00544       tls_cfg->enabled = ast_true(value) ? 1 : 0;
00545    } else if (!strcasecmp(varname, "tlscertfile") || !strcasecmp(varname, "sslcert") || !strcasecmp(varname, "tlscert")) {
00546       ast_free(tls_cfg->certfile);
00547       tls_cfg->certfile = ast_strdup(value);
00548    } else if (!strcasecmp(varname, "tlsprivatekey") || !strcasecmp(varname, "sslprivatekey")) {
00549       ast_free(tls_cfg->pvtfile);
00550       tls_cfg->pvtfile = ast_strdup(value);
00551    } else if (!strcasecmp(varname, "tlscipher") || !strcasecmp(varname, "sslcipher")) {
00552       ast_free(tls_cfg->cipher);
00553       tls_cfg->cipher = ast_strdup(value);
00554    } else if (!strcasecmp(varname, "tlscafile")) {
00555       ast_free(tls_cfg->cafile);
00556       tls_cfg->cafile = ast_strdup(value);
00557    } else if (!strcasecmp(varname, "tlscapath")) {
00558       ast_free(tls_cfg->capath);
00559       tls_cfg->capath = ast_strdup(value);
00560    } else if (!strcasecmp(varname, "tlsverifyclient")) {
00561       ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_VERIFY_CLIENT);
00562    } else if (!strcasecmp(varname, "tlsdontverifyserver")) {
00563       ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_DONT_VERIFY_SERVER);
00564    } else if (!strcasecmp(varname, "tlsbindaddr") || !strcasecmp(varname, "sslbindaddr")) {
00565       if (ast_parse_arg(value, PARSE_ADDR, &tls_desc->local_address))
00566          ast_log(LOG_WARNING, "Invalid %s '%s'\n", varname, value);
00567    } else if (!strcasecmp(varname, "tlsclientmethod") || !strcasecmp(varname, "sslclientmethod")) {
00568       if (!strcasecmp(value, "tlsv1")) {
00569          ast_set_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
00570          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
00571          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
00572       } else if (!strcasecmp(value, "sslv3")) {
00573          ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
00574          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
00575          ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
00576       } else if (!strcasecmp(value, "sslv2")) {
00577          ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
00578          ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
00579          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
00580       }
00581    } else {
00582       return -1;
00583    }
00584 
00585    return 0;
00586 }


Generated on Wed Apr 6 11:30:11 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7