Mon Mar 19 11:30:56 2012

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

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

References __ssl_setup().

00383 {
00384    return __ssl_setup(cfg, 0);
00385 }

struct ast_tcptls_session_instance* ast_tcptls_client_create ( struct ast_tcptls_session_args desc  ) 

Definition at line 424 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_setnull(), ast_sockaddr_stringify(), desc, errno, LOG_ERROR, LOG_WARNING, and session_instance_destructor().

Referenced by sip_prepare_socket().

00425 {
00426    int x = 1;
00427    struct ast_tcptls_session_instance *tcptls_session = NULL;
00428 
00429    /* Do nothing if nothing has changed */
00430    if (!ast_sockaddr_cmp(&desc->old_address, &desc->remote_address)) {
00431       ast_debug(1, "Nothing changed in %s\n", desc->name);
00432       return NULL;
00433    }
00434 
00435    /* If we return early, there is no connection */
00436    ast_sockaddr_setnull(&desc->old_address);
00437 
00438    if (desc->accept_fd != -1)
00439       close(desc->accept_fd);
00440 
00441    desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->remote_address) ?
00442              AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP);
00443    if (desc->accept_fd < 0) {
00444       ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
00445          desc->name, strerror(errno));
00446       return NULL;
00447    }
00448 
00449    /* if a local address was specified, bind to it so the connection will
00450       originate from the desired address */
00451    if (!ast_sockaddr_isnull(&desc->local_address)) {
00452       setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00453       if (ast_bind(desc->accept_fd, &desc->local_address)) {
00454          ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
00455             desc->name,
00456             ast_sockaddr_stringify(&desc->local_address),
00457             strerror(errno));
00458          goto error;
00459       }
00460    }
00461 
00462    if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor)))
00463       goto error;
00464 
00465    ast_mutex_init(&tcptls_session->lock);
00466    tcptls_session->client = 1;
00467    tcptls_session->fd = desc->accept_fd;
00468    tcptls_session->parent = desc;
00469    tcptls_session->parent->worker_fn = NULL;
00470    ast_sockaddr_copy(&tcptls_session->remote_address,
00471            &desc->remote_address);
00472 
00473    /* Set current info */
00474    ast_sockaddr_copy(&desc->old_address, &desc->remote_address);
00475    return tcptls_session;
00476 
00477 error:
00478    close(desc->accept_fd);
00479    desc->accept_fd = -1;
00480    if (tcptls_session)
00481       ao2_ref(tcptls_session, -1);
00482    return NULL;
00483 }

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

00388 {
00389    struct ast_tcptls_session_args *desc;
00390    int flags;
00391 
00392    if (!(desc = tcptls_session->parent)) {
00393       goto client_start_error;
00394    }
00395 
00396    if (ast_connect(desc->accept_fd, &desc->remote_address)) {
00397       ast_log(LOG_ERROR, "Unable to connect %s to %s: %s\n",
00398          desc->name,
00399          ast_sockaddr_stringify(&desc->remote_address),
00400          strerror(errno));
00401       goto client_start_error;
00402    }
00403 
00404    flags = fcntl(desc->accept_fd, F_GETFL);
00405    fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK);
00406 
00407    if (desc->tls_cfg) {
00408       desc->tls_cfg->enabled = 1;
00409       __ssl_setup(desc->tls_cfg, 1);
00410    }
00411 
00412    return handle_tcptls_connection(tcptls_session);
00413 
00414 client_start_error:
00415    close(desc->accept_fd);
00416    desc->accept_fd = -1;
00417    if (tcptls_session) {
00418       ao2_ref(tcptls_session, -1);
00419    }
00420    return NULL;
00421 
00422 }

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

References ast_log(), errno, ast_tcptls_session_instance::f, ast_tcptls_session_instance::fd, and LOG_ERROR.

Referenced by ast_tcptls_server_root(), handle_tcptls_connection(), and sip_prepare_socket().

00555 {
00556    if (tcptls_session->f) {
00557       if (fclose(tcptls_session->f)) {
00558          ast_log(LOG_ERROR, "fclose() failed: %s\n", strerror(errno));
00559       }
00560       tcptls_session->f = NULL;
00561       tcptls_session->fd = -1;
00562    } else if (tcptls_session->fd != -1) {
00563       if (close(tcptls_session->fd)) {
00564          ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
00565       }
00566       tcptls_session->fd = -1;
00567    } else {
00568       ast_log(LOG_ERROR, "ast_tcptls_close_session_file invoked on session instance without file or file descriptor\n");
00569    }
00570 }

HOOK_T ast_tcptls_server_read ( struct ast_tcptls_session_instance ser,
void *  buf,
size_t  count 
)

Definition at line 100 of file tcptls.c.

References ast_log(), errno, ast_tcptls_session_instance::fd, LOG_ERROR, ast_tcptls_session_instance::ssl, and ssl_read().

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

void* ast_tcptls_server_root ( void *   ) 

Definition at line 251 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_tcptls_close_session_file(), ast_wait_for_input(), desc, errno, handle_tcptls_connection(), LOG_ERROR, LOG_WARNING, and session_instance_destructor().

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

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 485 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_setnull(), ast_sockaddr_stringify(), desc, errno, and LOG_ERROR.

00486 {
00487    int flags;
00488    int x = 1;
00489 
00490    /* Do nothing if nothing has changed */
00491    if (!ast_sockaddr_cmp(&desc->old_address, &desc->local_address)) {
00492       ast_debug(1, "Nothing changed in %s\n", desc->name);
00493       return;
00494    }
00495 
00496    /* If we return early, there is no one listening */
00497    ast_sockaddr_setnull(&desc->old_address);
00498 
00499    /* Shutdown a running server if there is one */
00500    if (desc->master != AST_PTHREADT_NULL) {
00501       pthread_cancel(desc->master);
00502       pthread_kill(desc->master, SIGURG);
00503       pthread_join(desc->master, NULL);
00504    }
00505 
00506    if (desc->accept_fd != -1)
00507       close(desc->accept_fd);
00508 
00509    /* If there's no new server, stop here */
00510    if (ast_sockaddr_isnull(&desc->local_address)) {
00511       ast_debug(2, "Server disabled:  %s\n", desc->name);
00512       return;
00513    }
00514 
00515    desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->local_address) ?
00516              AF_INET6 : AF_INET, SOCK_STREAM, 0);
00517    if (desc->accept_fd < 0) {
00518       ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", desc->name, strerror(errno));
00519       return;
00520    }
00521 
00522    setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00523    if (ast_bind(desc->accept_fd, &desc->local_address)) {
00524       ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
00525          desc->name,
00526          ast_sockaddr_stringify(&desc->local_address),
00527          strerror(errno));
00528       goto error;
00529    }
00530    if (listen(desc->accept_fd, 10)) {
00531       ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name);
00532       goto error;
00533    }
00534    flags = fcntl(desc->accept_fd, F_GETFL);
00535    fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
00536    if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
00537       ast_log(LOG_ERROR, "Unable to launch thread for %s on %s: %s\n",
00538          desc->name,
00539          ast_sockaddr_stringify(&desc->local_address),
00540          strerror(errno));
00541       goto error;
00542    }
00543 
00544    /* Set current info */
00545    ast_sockaddr_copy(&desc->old_address, &desc->local_address);
00546 
00547    return;
00548 
00549 error:
00550    close(desc->accept_fd);
00551    desc->accept_fd = -1;
00552 }

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

References ast_debug, AST_PTHREADT_NULL, and desc.

Referenced by unload_module().

00573 {
00574    if (desc->master != AST_PTHREADT_NULL) {
00575       pthread_cancel(desc->master);
00576       pthread_kill(desc->master, SIGURG);
00577       pthread_join(desc->master, NULL);
00578       desc->master = AST_PTHREADT_NULL;
00579    }
00580    if (desc->accept_fd != -1)
00581       close(desc->accept_fd);
00582    desc->accept_fd = -1;
00583    ast_debug(2, "Stopped server :: %s\n", desc->name);
00584 }

HOOK_T ast_tcptls_server_write ( struct ast_tcptls_session_instance ser,
const void *  buf,
size_t  count 
)

Definition at line 115 of file tcptls.c.

References ast_log(), errno, ast_tcptls_session_instance::fd, LOG_ERROR, ast_tcptls_session_instance::ssl, and ssl_write().

00116 {
00117    if (tcptls_session->fd == -1) {
00118       ast_log(LOG_ERROR, "server_write called with an fd of -1\n");
00119       errno = EIO;
00120       return -1;
00121    }
00122 
00123 #ifdef DO_SSL
00124    if (tcptls_session->ssl)
00125       return ssl_write(tcptls_session->ssl, buf, count);
00126 #endif
00127    return write(tcptls_session->fd, buf, count);
00128 }

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

00587 {
00588    if (!strcasecmp(varname, "tlsenable") || !strcasecmp(varname, "sslenable")) {
00589       tls_cfg->enabled = ast_true(value) ? 1 : 0;
00590    } else if (!strcasecmp(varname, "tlscertfile") || !strcasecmp(varname, "sslcert") || !strcasecmp(varname, "tlscert")) {
00591       ast_free(tls_cfg->certfile);
00592       tls_cfg->certfile = ast_strdup(value);
00593    } else if (!strcasecmp(varname, "tlsprivatekey") || !strcasecmp(varname, "sslprivatekey")) {
00594       ast_free(tls_cfg->pvtfile);
00595       tls_cfg->pvtfile = ast_strdup(value);
00596    } else if (!strcasecmp(varname, "tlscipher") || !strcasecmp(varname, "sslcipher")) {
00597       ast_free(tls_cfg->cipher);
00598       tls_cfg->cipher = ast_strdup(value);
00599    } else if (!strcasecmp(varname, "tlscafile")) {
00600       ast_free(tls_cfg->cafile);
00601       tls_cfg->cafile = ast_strdup(value);
00602    } else if (!strcasecmp(varname, "tlscapath") || !strcasecmp(varname, "tlscadir")) {
00603       ast_free(tls_cfg->capath);
00604       tls_cfg->capath = ast_strdup(value);
00605    } else if (!strcasecmp(varname, "tlsverifyclient")) {
00606       ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_VERIFY_CLIENT);
00607    } else if (!strcasecmp(varname, "tlsdontverifyserver")) {
00608       ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_DONT_VERIFY_SERVER);
00609    } else if (!strcasecmp(varname, "tlsbindaddr") || !strcasecmp(varname, "sslbindaddr")) {
00610       if (ast_parse_arg(value, PARSE_ADDR, &tls_desc->local_address))
00611          ast_log(LOG_WARNING, "Invalid %s '%s'\n", varname, value);
00612    } else if (!strcasecmp(varname, "tlsclientmethod") || !strcasecmp(varname, "sslclientmethod")) {
00613       if (!strcasecmp(value, "tlsv1")) {
00614          ast_set_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
00615          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
00616          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
00617       } else if (!strcasecmp(value, "sslv3")) {
00618          ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
00619          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
00620          ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
00621       } else if (!strcasecmp(value, "sslv2")) {
00622          ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
00623          ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
00624          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
00625       }
00626    } else {
00627       return -1;
00628    }
00629 
00630    return 0;
00631 }


Generated on Mon Mar 19 11:30:56 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7