Wed Oct 28 11:46:21 2009

Asterisk developer's documentation


tcptls.h File Reference

Generic support for tcp/tls servers in Asterisk. More...

#include "asterisk/utils.h"
#include "asterisk/astobj2.h"

Include dependency graph for tcptls.h:

This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  ast_tcptls_session_instance
 describes a server instance More...
struct  ast_tls_config
struct  server_args
 arguments for the accepting thread More...
struct  SSL
struct  SSL_CTX

Defines

#define AST_CERTFILE   "asterisk.pem"
#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) }

Functions

void * ast_make_file_from_fd (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.
int ast_ssl_setup (struct ast_tls_config *cfg)
struct
ast_tcptls_session_instance
ast_tcptls_client_start (struct server_args *desc)
 A generic client routine for a TCP client and starts a thread for handling accept().
HOOK_T ast_tcptls_server_read (struct ast_tcptls_session_instance *ser, void *buf, size_t count)
 replacement read/write functions for SSL support. We use wrappers rather than SSL_read/SSL_write directly so we can put in some debugging.
void * ast_tcptls_server_root (void *)
void ast_tcptls_server_start (struct server_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 server_args *desc)
 Shutdown a running server if there is one.
HOOK_T ast_tcptls_server_write (struct ast_tcptls_session_instance *ser, void *buf, size_t count)


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 69 of file tcptls.h.

Referenced by __ast_http_load(), __init_manager(), and reload_config().

#define HOOK_T   ssize_t

Definition at line 155 of file tcptls.h.

#define LEN_T   size_t

Definition at line 156 of file tcptls.h.


Enumeration Type Documentation

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

Definition at line 71 of file tcptls.h.

00071                    {
00072    /*! Verify certificate when acting as server */
00073    AST_SSL_VERIFY_CLIENT = (1 << 0),
00074    /*! Don't verify certificate when connecting to a server */
00075    AST_SSL_DONT_VERIFY_SERVER = (1 << 1),
00076    /*! Don't compare "Common Name" against IP or hostname */
00077    AST_SSL_IGNORE_COMMON_NAME = (1 << 2)
00078 };


Function Documentation

void* ast_make_file_from_fd ( 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.

Note:
must decrement ref count before returning NULL on error

Definition at line 379 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_test_flag, ast_verb, ast_tcptls_session_instance::client, ast_tcptls_session_instance::f, ast_tcptls_session_instance::fd, ast_tls_config::flags, server_args::hostname, LOG_ERROR, LOG_WARNING, name, ast_tcptls_session_instance::parent, ast_tcptls_session_instance::ssl, ast_tls_config::ssl_ctx, str, server_args::tls_cfg, and server_args::worker_fn.

Referenced by ast_tcptls_client_start(), and ast_tcptls_server_root().

00380 {
00381    struct ast_tcptls_session_instance *tcptls_session = data;
00382 #ifdef DO_SSL
00383    int (*ssl_setup)(SSL *) = (tcptls_session->client) ? SSL_connect : SSL_accept;
00384    int ret;
00385    char err[256];
00386 #endif
00387 
00388    /*
00389    * open a FILE * as appropriate.
00390    */
00391    if (!tcptls_session->parent->tls_cfg) {
00392       tcptls_session->f = fdopen(tcptls_session->fd, "w+");
00393       setvbuf(tcptls_session->f, NULL, _IONBF, 0);
00394    }
00395 #ifdef DO_SSL
00396    else if ( (tcptls_session->ssl = SSL_new(tcptls_session->parent->tls_cfg->ssl_ctx)) ) {
00397       SSL_set_fd(tcptls_session->ssl, tcptls_session->fd);
00398       if ((ret = ssl_setup(tcptls_session->ssl)) <= 0) {
00399          ast_verb(2, "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err));
00400       } else {
00401 #if defined(HAVE_FUNOPEN)  /* the BSD interface */
00402          tcptls_session->f = funopen(tcptls_session->ssl, ssl_read, ssl_write, NULL, ssl_close);
00403 
00404 #elif defined(HAVE_FOPENCOOKIE)  /* the glibc/linux interface */
00405          static const cookie_io_functions_t cookie_funcs = {
00406             ssl_read, ssl_write, NULL, ssl_close
00407          };
00408          tcptls_session->f = fopencookie(tcptls_session->ssl, "w+", cookie_funcs);
00409 #else
00410          /* could add other methods here */
00411          ast_debug(2, "no tcptls_session->f methods attempted!");
00412 #endif
00413          if ((tcptls_session->client && !ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER))
00414             || (!tcptls_session->client && ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_VERIFY_CLIENT))) {
00415             X509 *peer;
00416             long res;
00417             peer = SSL_get_peer_certificate(tcptls_session->ssl);
00418             if (!peer)
00419                ast_log(LOG_WARNING, "No peer SSL certificate\n");
00420             res = SSL_get_verify_result(tcptls_session->ssl);
00421             if (res != X509_V_OK)
00422                ast_log(LOG_ERROR, "Certificate did not verify: %s\n", X509_verify_cert_error_string(res));
00423             if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) {
00424                ASN1_STRING *str;
00425                unsigned char *str2;
00426                X509_NAME *name = X509_get_subject_name(peer);
00427                int pos = -1;
00428                int found = 0;
00429             
00430                for (;;) {
00431                   /* Walk the certificate to check all available "Common Name" */
00432                   /* XXX Probably should do a gethostbyname on the hostname and compare that as well */
00433                   pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos);
00434                   if (pos < 0)
00435                      break;
00436                   str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos));
00437                   ASN1_STRING_to_UTF8(&str2, str);
00438                   if (str2) {
00439                      if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2))
00440                         found = 1;
00441                      ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2);
00442                      OPENSSL_free(str2);
00443                   }
00444                   if (found)
00445                      break;
00446                }
00447                if (!found) {
00448                   ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname);
00449                   if (peer)
00450                      X509_free(peer);
00451                   fclose(tcptls_session->f);
00452                   ao2_ref(tcptls_session, -1);
00453                   return NULL;
00454                }
00455             }
00456             if (peer)
00457                X509_free(peer);
00458          }
00459       }
00460       if (!tcptls_session->f) /* no success opening descriptor stacking */
00461          SSL_free(tcptls_session->ssl);
00462    }
00463 #endif /* DO_SSL */
00464 
00465    if (!tcptls_session->f) {
00466       close(tcptls_session->fd);
00467       ast_log(LOG_WARNING, "FILE * open failed!\n");
00468       ao2_ref(tcptls_session, -1);
00469       return NULL;
00470    }
00471 
00472    if (tcptls_session && tcptls_session->parent->worker_fn)
00473       return tcptls_session->parent->worker_fn(tcptls_session);
00474    else
00475       return tcptls_session;
00476 }

int ast_ssl_setup ( struct ast_tls_config cfg  ) 

Definition at line 220 of file tcptls.c.

References __ssl_setup().

Referenced by __ast_http_load(), __init_manager(), and reload_config().

00221 {
00222    return __ssl_setup(cfg, 0);
00223 }

struct ast_tcptls_session_instance* ast_tcptls_client_start ( struct server_args desc  )  [read]

A generic client routine for a TCP client and starts a thread for handling accept().

Definition at line 228 of file tcptls.c.

References __ssl_setup(), server_args::accept_fd, ao2_alloc(), ao2_ref(), ast_debug, ast_inet_ntoa(), ast_log(), ast_make_file_from_fd(), ast_mutex_init(), ast_tcptls_session_instance::client, ast_tls_config::enabled, errno, ast_tcptls_session_instance::fd, ast_tcptls_session_instance::lock, LOG_ERROR, LOG_WARNING, server_args::name, server_args::oldsin, ast_tcptls_session_instance::parent, ast_tcptls_session_instance::requestor, session_instance_destructor(), server_args::sin, server_args::tls_cfg, and server_args::worker_fn.

Referenced by sip_prepare_socket().

00229 {
00230    int flags;
00231    struct ast_tcptls_session_instance *tcptls_session = NULL;
00232 
00233    /* Do nothing if nothing has changed */
00234    if(!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
00235       ast_debug(1, "Nothing changed in %s\n", desc->name);
00236       return NULL;
00237    }
00238 
00239    desc->oldsin = desc->sin;
00240 
00241    if (desc->accept_fd != -1)
00242       close(desc->accept_fd);
00243 
00244    desc->accept_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00245    if (desc->accept_fd < 0) {
00246       ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
00247          desc->name, strerror(errno));
00248       return NULL;
00249    }
00250 
00251    if (connect(desc->accept_fd, (const struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
00252       ast_log(LOG_ERROR, "Unable to connect %s to %s:%d: %s\n",
00253          desc->name,
00254          ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
00255          strerror(errno));
00256       goto error;
00257    }
00258 
00259    if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor)))
00260       goto error;
00261 
00262    ast_mutex_init(&tcptls_session->lock);
00263 
00264    flags = fcntl(desc->accept_fd, F_GETFL);
00265    fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK);
00266 
00267    tcptls_session->fd = desc->accept_fd;
00268    tcptls_session->parent = desc;
00269    tcptls_session->parent->worker_fn = NULL;
00270    memcpy(&tcptls_session->requestor, &desc->sin, sizeof(tcptls_session->requestor));
00271 
00272    tcptls_session->client = 1;
00273 
00274    if (desc->tls_cfg) {
00275       desc->tls_cfg->enabled = 1;
00276       __ssl_setup(desc->tls_cfg, 1);
00277    }
00278 
00279    if (!ast_make_file_from_fd(tcptls_session))
00280       goto error;
00281 
00282    return tcptls_session;
00283 
00284 error:
00285    close(desc->accept_fd);
00286    desc->accept_fd = -1;
00287    if (tcptls_session)
00288       ao2_ref(tcptls_session, -1);
00289    return NULL;
00290 }

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

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

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

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

void* ast_tcptls_server_root ( void *   ) 

Definition at line 120 of file tcptls.c.

References server_args::accept_fd, ao2_alloc(), ao2_ref(), ast_log(), ast_make_file_from_fd(), ast_mutex_init(), ast_pthread_create_detached_background, ast_wait_for_input(), ast_tcptls_session_instance::client, desc, errno, ast_tcptls_session_instance::fd, ast_tcptls_session_instance::lock, LOG_WARNING, ast_tcptls_session_instance::parent, server_args::periodic_fn, server_args::poll_timeout, ast_tcptls_session_instance::requestor, and session_instance_destructor().

00121 {
00122    struct server_args *desc = data;
00123    int fd;
00124    struct sockaddr_in sin;
00125    socklen_t sinlen;
00126    struct ast_tcptls_session_instance *tcptls_session;
00127    pthread_t launched;
00128    
00129    for (;;) {
00130       int i, flags;
00131 
00132       if (desc->periodic_fn)
00133          desc->periodic_fn(desc);
00134       i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
00135       if (i <= 0)
00136          continue;
00137       sinlen = sizeof(sin);
00138       fd = accept(desc->accept_fd, (struct sockaddr *)&sin, &sinlen);
00139       if (fd < 0) {
00140          if ((errno != EAGAIN) && (errno != EINTR))
00141             ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
00142          continue;
00143       }
00144       tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor);
00145       if (!tcptls_session) {
00146          ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
00147          close(fd);
00148          continue;
00149       }
00150 
00151       ast_mutex_init(&tcptls_session->lock);
00152 
00153       flags = fcntl(fd, F_GETFL);
00154       fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
00155       tcptls_session->fd = fd;
00156       tcptls_session->parent = desc;
00157       memcpy(&tcptls_session->requestor, &sin, sizeof(tcptls_session->requestor));
00158 
00159       tcptls_session->client = 0;
00160          
00161       if (ast_pthread_create_detached_background(&launched, NULL, ast_make_file_from_fd, tcptls_session)) {
00162          ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
00163          close(tcptls_session->fd);
00164          ao2_ref(tcptls_session, -1);
00165       }
00166    }
00167    return NULL;
00168 }

void ast_tcptls_server_start ( struct server_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 297 of file tcptls.c.

References server_args::accept_fd, server_args::accept_fn, ast_debug, ast_inet_ntoa(), ast_log(), ast_pthread_create_background, AST_PTHREADT_NULL, errno, LOG_ERROR, server_args::master, server_args::name, server_args::oldsin, and server_args::sin.

Referenced by __ast_http_load(), __init_manager(), and reload_config().

00298 {
00299    int flags;
00300    int x = 1;
00301    
00302    /* Do nothing if nothing has changed */
00303    if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
00304       ast_debug(1, "Nothing changed in %s\n", desc->name);
00305       return;
00306    }
00307    
00308    desc->oldsin = desc->sin;
00309    
00310    /* Shutdown a running server if there is one */
00311    if (desc->master != AST_PTHREADT_NULL) {
00312       pthread_cancel(desc->master);
00313       pthread_kill(desc->master, SIGURG);
00314       pthread_join(desc->master, NULL);
00315    }
00316    
00317    if (desc->accept_fd != -1)
00318       close(desc->accept_fd);
00319 
00320    /* If there's no new server, stop here */
00321    if (desc->sin.sin_family == 0)
00322       return;
00323 
00324    desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0);
00325    if (desc->accept_fd < 0) {
00326       ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n",
00327          desc->name, strerror(errno));
00328       return;
00329    }
00330    
00331    setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00332    if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
00333       ast_log(LOG_ERROR, "Unable to bind %s to %s:%d: %s\n",
00334          desc->name,
00335          ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
00336          strerror(errno));
00337       goto error;
00338    }
00339    if (listen(desc->accept_fd, 10)) {
00340       ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name);
00341       goto error;
00342    }
00343    flags = fcntl(desc->accept_fd, F_GETFL);
00344    fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
00345    if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
00346       ast_log(LOG_ERROR, "Unable to launch thread for %s on %s:%d: %s\n",
00347          desc->name,
00348          ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
00349          strerror(errno));
00350       goto error;
00351    }
00352    return;
00353 
00354 error:
00355    close(desc->accept_fd);
00356    desc->accept_fd = -1;
00357 }

void ast_tcptls_server_stop ( struct server_args desc  ) 

Shutdown a running server if there is one.

Definition at line 360 of file tcptls.c.

References server_args::accept_fd, AST_PTHREADT_NULL, and server_args::master.

Referenced by unload_module().

00361 {
00362    if (desc->master != AST_PTHREADT_NULL) {
00363       pthread_cancel(desc->master);
00364       pthread_kill(desc->master, SIGURG);
00365       pthread_join(desc->master, NULL);
00366    }
00367    if (desc->accept_fd != -1)
00368       close(desc->accept_fd);
00369    desc->accept_fd = -1;
00370 }

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

Definition at line 99 of file tcptls.c.

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

Referenced by __sip_xmit().

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


Generated on Wed Oct 28 11:46:21 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6