Mon Jun 27 16:51:21 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 363 of file tcptls.c.

References __ssl_setup().

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

struct ast_tcptls_session_instance* ast_tcptls_client_create ( struct ast_tcptls_session_args desc  ) 

Definition at line 405 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().

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

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

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

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

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

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 463 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.

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

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 527 of file tcptls.c.

References ast_debug, AST_PTHREADT_NULL, and desc.

Referenced by unload_module().

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

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

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

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


Generated on Mon Jun 27 16:51:21 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7