Thu Jul 9 13:41:34 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 <openssl/ssl.h>
#include <openssl/err.h>

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

#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

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

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.

Definition at line 378 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, ssl_close(), ast_tls_config::ssl_ctx, ssl_read(), ssl_write(), str, server_args::tls_cfg, and server_args::worker_fn.

Referenced by ast_tcptls_client_start(), and ast_tcptls_server_root().

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

int ast_ssl_setup ( struct ast_tls_config cfg  ) 

Definition at line 220 of file tcptls.c.

References __ssl_setup().

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

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

Definition at line 228 of file tcptls.c.

References __ssl_setup(), ao2_alloc(), ao2_ref(), ast_debug, ast_inet_ntoa(), ast_log(), ast_make_file_from_fd(), ast_mutex_init(), desc, errno, LOG_ERROR, LOG_WARNING, and session_instance_destructor().

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    ao2_ref(tcptls_session, +1);
00280    if (!ast_make_file_from_fd(tcptls_session))
00281       goto error;
00282 
00283    return tcptls_session;
00284 
00285 error:
00286    close(desc->accept_fd);
00287    desc->accept_fd = -1;
00288    if (tcptls_session)
00289       ao2_ref(tcptls_session, -1);
00290    return NULL;
00291 }

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

Definition at line 84 of file tcptls.c.

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

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 ao2_alloc(), ao2_ref(), ast_log(), ast_make_file_from_fd(), ast_mutex_init(), ast_pthread_create_detached_background, ast_wait_for_input(), desc, errno, LOG_WARNING, 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 298 of file tcptls.c.

References ast_debug, ast_inet_ntoa(), ast_log(), ast_pthread_create_background, AST_PTHREADT_NULL, desc, errno, and LOG_ERROR.

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

void ast_tcptls_server_stop ( struct server_args desc  ) 

Shutdown a running server if there is one.

Definition at line 361 of file tcptls.c.

References AST_PTHREADT_NULL, and desc.

Referenced by unload_module().

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

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

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 Thu Jul 9 13:41:34 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7