Wed Oct 28 11:46:21 2009

Asterisk developer's documentation


tcptls.c File Reference

Code to support TCP and TLS server/client. More...

#include "asterisk.h"
#include <sys/signal.h>
#include "asterisk/compat.h"
#include "asterisk/tcptls.h"
#include "asterisk/http.h"
#include "asterisk/utils.h"
#include "asterisk/strings.h"
#include "asterisk/options.h"
#include "asterisk/manager.h"

Include dependency graph for tcptls.c:

Go to the source code of this file.

Functions

static int __ssl_setup (struct ast_tls_config *cfg, int client)
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 *tcptls_session, 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 *data)
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 *tcptls_session, void *buf, size_t count)
static void session_instance_destructor (void *obj)


Detailed Description

Code to support TCP and TLS server/client.

Author:
Luigi Rizzo

Brett Bryant <brettbryant@gmail.com>

Definition in file tcptls.c.


Function Documentation

static int __ssl_setup ( struct ast_tls_config cfg,
int  client 
) [static]

Definition at line 170 of file tcptls.c.

References ast_debug, ast_strlen_zero(), ast_verb, ast_tls_config::cafile, ast_tls_config::capath, ast_tls_config::certfile, ast_tls_config::cipher, ast_tls_config::enabled, S_OR, and ast_tls_config::ssl_ctx.

Referenced by ast_ssl_setup(), and ast_tcptls_client_start().

00171 {
00172 #ifndef DO_SSL
00173    cfg->enabled = 0;
00174    return 0;
00175 #else
00176    if (!cfg->enabled)
00177       return 0;
00178 
00179    SSL_load_error_strings();
00180    SSLeay_add_ssl_algorithms();
00181 
00182    if (!(cfg->ssl_ctx = SSL_CTX_new( client ? SSLv23_client_method() : SSLv23_server_method() ))) {
00183       ast_debug(1, "Sorry, SSL_CTX_new call returned null...\n");
00184       cfg->enabled = 0;
00185       return 0;
00186    }
00187    if (!ast_strlen_zero(cfg->certfile)) {
00188       if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
00189           SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
00190           SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 ) {
00191          if (!client) {
00192             /* Clients don't need a certificate, but if its setup we can use it */
00193             ast_verb(0, "SSL cert error <%s>", cfg->certfile);
00194             sleep(2);
00195             cfg->enabled = 0;
00196             return 0;
00197          }
00198       }
00199    }
00200    if (!ast_strlen_zero(cfg->cipher)) {
00201       if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) {
00202          if (!client) {
00203             ast_verb(0, "SSL cipher error <%s>", cfg->cipher);
00204             sleep(2);
00205             cfg->enabled = 0;
00206             return 0;
00207          }
00208       }
00209    }
00210    if (!ast_strlen_zero(cfg->cafile) || !ast_strlen_zero(cfg->capath)) {
00211       if (SSL_CTX_load_verify_locations(cfg->ssl_ctx, S_OR(cfg->cafile, NULL), S_OR(cfg->capath,NULL)) == 0)
00212          ast_verb(0, "SSL CA file(%s)/path(%s) error\n", cfg->cafile, cfg->capath);
00213    }
00214 
00215    ast_verb(0, "SSL certificate ok\n");
00216    return 1;
00217 #endif
00218 }

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 tcptls_session,
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 *  data  ) 

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 tcptls_session,
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 }

static void session_instance_destructor ( void *  obj  )  [static]

Definition at line 114 of file tcptls.c.

References ast_mutex_destroy(), and ast_tcptls_session_instance::lock.

Referenced by ast_tcptls_client_start(), and ast_tcptls_server_root().

00115 {
00116    struct ast_tcptls_session_instance *i = obj;
00117    ast_mutex_destroy(&i->lock);
00118 }


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