Fri Jul 24 00:42:00 2009

Asterisk developer's documentation


tcptls.c File Reference

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

#include "asterisk.h"
#include <fcntl.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 "asterisk/astobj2.h"

Go to the source code of this file.

Functions

static int __ssl_setup (struct ast_tls_config *cfg, int client)
int ast_ssl_setup (struct ast_tls_config *cfg)
ast_tcptls_session_instanceast_tcptls_client_start (struct ast_tcptls_session_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)
void * ast_tcptls_server_root (void *data)
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 *tcptls_session, void *buf, size_t count)
static void * handle_tls_connection (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.
static void session_instance_destructor (void *obj)
static int ssl_close (void *cookie)
static HOOK_T ssl_read (void *cookie, char *buf, LEN_T len)
 replacement read/write functions for SSL support. We use wrappers rather than SSL_read/SSL_write directly so we can put in some debugging.
static HOOK_T ssl_write (void *cookie, const char *buf, LEN_T len)


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

00275 {
00276 #ifndef DO_SSL
00277    cfg->enabled = 0;
00278    return 0;
00279 #else
00280    if (!cfg->enabled)
00281       return 0;
00282 
00283    SSL_load_error_strings();
00284    SSLeay_add_ssl_algorithms();
00285 
00286    if (!(cfg->ssl_ctx = SSL_CTX_new( client ? SSLv23_client_method() : SSLv23_server_method() ))) {
00287       ast_debug(1, "Sorry, SSL_CTX_new call returned null...\n");
00288       cfg->enabled = 0;
00289       return 0;
00290    }
00291    if (!ast_strlen_zero(cfg->certfile)) {
00292       if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
00293           SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
00294           SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 ) {
00295          if (!client) {
00296             /* Clients don't need a certificate, but if its setup we can use it */
00297             ast_verb(0, "SSL cert error <%s>", cfg->certfile);
00298             sleep(2);
00299             cfg->enabled = 0;
00300             return 0;
00301          }
00302       }
00303    }
00304    if (!ast_strlen_zero(cfg->cipher)) {
00305       if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) {
00306          if (!client) {
00307             ast_verb(0, "SSL cipher error <%s>", cfg->cipher);
00308             sleep(2);
00309             cfg->enabled = 0;
00310             return 0;
00311          }
00312       }
00313    }
00314    if (!ast_strlen_zero(cfg->cafile) || !ast_strlen_zero(cfg->capath)) {
00315       if (SSL_CTX_load_verify_locations(cfg->ssl_ctx, S_OR(cfg->cafile, NULL), S_OR(cfg->capath,NULL)) == 0)
00316          ast_verb(0, "SSL CA file(%s)/path(%s) error\n", cfg->cafile, cfg->capath);
00317    }
00318 
00319    ast_verb(0, "SSL certificate ok\n");
00320    return 1;
00321 #endif
00322 }

int ast_ssl_setup ( struct ast_tls_config cfg  ) 

Definition at line 324 of file tcptls.c.

References __ssl_setup().

00325 {
00326    return __ssl_setup(cfg, 0);
00327 }

struct ast_tcptls_session_instance* ast_tcptls_client_start ( struct ast_tcptls_session_args desc  ) 

A generic client routine for a TCP client 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 329 of file tcptls.c.

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

Referenced by sip_prepare_socket().

00330 {
00331    int flags;
00332    int x = 1;
00333    struct ast_tcptls_session_instance *tcptls_session = NULL;
00334 
00335    /* Do nothing if nothing has changed */
00336    if (!memcmp(&desc->old_address, &desc->remote_address, sizeof(desc->old_address))) {
00337       ast_debug(1, "Nothing changed in %s\n", desc->name);
00338       return NULL;
00339    }
00340 
00341    desc->old_address = desc->remote_address;
00342 
00343    if (desc->accept_fd != -1)
00344       close(desc->accept_fd);
00345 
00346    desc->accept_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00347    if (desc->accept_fd < 0) {
00348       ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
00349          desc->name, strerror(errno));
00350       return NULL;
00351    }
00352 
00353    /* if a local address was specified, bind to it so the connection will
00354       originate from the desired address */
00355    if (desc->local_address.sin_family != 0) {
00356       setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00357       if (bind(desc->accept_fd, (struct sockaddr *) &desc->local_address, sizeof(desc->local_address))) {
00358          ast_log(LOG_ERROR, "Unable to bind %s to %s:%d: %s\n",
00359          desc->name,
00360             ast_inet_ntoa(desc->local_address.sin_addr), ntohs(desc->local_address.sin_port),
00361             strerror(errno));
00362          goto error;
00363       }
00364    }
00365 
00366    if (connect(desc->accept_fd, (const struct sockaddr *) &desc->remote_address, sizeof(desc->remote_address))) {
00367       ast_log(LOG_ERROR, "Unable to connect %s to %s:%d: %s\n",
00368          desc->name,
00369          ast_inet_ntoa(desc->remote_address.sin_addr), ntohs(desc->remote_address.sin_port),
00370          strerror(errno));
00371       goto error;
00372    }
00373 
00374    if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor)))
00375       goto error;
00376 
00377    ast_mutex_init(&tcptls_session->lock);
00378 
00379    flags = fcntl(desc->accept_fd, F_GETFL);
00380    fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK);
00381 
00382    tcptls_session->fd = desc->accept_fd;
00383    tcptls_session->parent = desc;
00384    tcptls_session->parent->worker_fn = NULL;
00385    memcpy(&tcptls_session->remote_address, &desc->remote_address, sizeof(tcptls_session->remote_address));
00386 
00387    tcptls_session->client = 1;
00388 
00389    if (desc->tls_cfg) {
00390       desc->tls_cfg->enabled = 1;
00391       __ssl_setup(desc->tls_cfg, 1);
00392    }
00393 
00394    ao2_ref(tcptls_session, +1);
00395    if (!handle_tls_connection(tcptls_session))
00396       goto error;
00397 
00398    return tcptls_session;
00399 
00400 error:
00401    close(desc->accept_fd);
00402    desc->accept_fd = -1;
00403    if (tcptls_session)
00404       ao2_ref(tcptls_session, -1);
00405    return NULL;
00406 }

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

Definition at line 85 of file tcptls.c.

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

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

void* ast_tcptls_server_root ( void *  data  ) 

Definition at line 224 of file tcptls.c.

References ao2_alloc, ao2_ref, ast_log(), ast_mutex_init(), ast_pthread_create_detached_background, ast_wait_for_input(), desc, errno, handle_tls_connection(), LOG_WARNING, and session_instance_destructor().

00225 {
00226    struct ast_tcptls_session_args *desc = data;
00227    int fd;
00228    struct sockaddr_in sin;
00229    socklen_t sinlen;
00230    struct ast_tcptls_session_instance *tcptls_session;
00231    pthread_t launched;
00232    
00233    for (;;) {
00234       int i, flags;
00235 
00236       if (desc->periodic_fn)
00237          desc->periodic_fn(desc);
00238       i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
00239       if (i <= 0)
00240          continue;
00241       sinlen = sizeof(sin);
00242       fd = accept(desc->accept_fd, (struct sockaddr *) &sin, &sinlen);
00243       if (fd < 0) {
00244          if ((errno != EAGAIN) && (errno != EINTR))
00245             ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
00246          continue;
00247       }
00248       tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor);
00249       if (!tcptls_session) {
00250          ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
00251          close(fd);
00252          continue;
00253       }
00254 
00255       ast_mutex_init(&tcptls_session->lock);
00256 
00257       flags = fcntl(fd, F_GETFL);
00258       fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
00259       tcptls_session->fd = fd;
00260       tcptls_session->parent = desc;
00261       memcpy(&tcptls_session->remote_address, &sin, sizeof(tcptls_session->remote_address));
00262 
00263       tcptls_session->client = 0;
00264          
00265       if (ast_pthread_create_detached_background(&launched, NULL, handle_tls_connection, tcptls_session)) {
00266          ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
00267          close(tcptls_session->fd);
00268          ao2_ref(tcptls_session, -1);
00269       }
00270    }
00271    return NULL;
00272 }

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

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

00409 {
00410    int flags;
00411    int x = 1;
00412    
00413    /* Do nothing if nothing has changed */
00414    if (!memcmp(&desc->old_address, &desc->local_address, sizeof(desc->old_address))) {
00415       ast_debug(1, "Nothing changed in %s\n", desc->name);
00416       return;
00417    }
00418    
00419    desc->old_address = desc->local_address;
00420    
00421    /* Shutdown a running server if there is one */
00422    if (desc->master != AST_PTHREADT_NULL) {
00423       pthread_cancel(desc->master);
00424       pthread_kill(desc->master, SIGURG);
00425       pthread_join(desc->master, NULL);
00426    }
00427    
00428    if (desc->accept_fd != -1)
00429       close(desc->accept_fd);
00430 
00431    /* If there's no new server, stop here */
00432    if (desc->local_address.sin_family == 0) {
00433       return;
00434    }
00435 
00436    desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0);
00437    if (desc->accept_fd < 0) {
00438       ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n",
00439          desc->name, strerror(errno));
00440       return;
00441    }
00442    
00443    setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00444    if (bind(desc->accept_fd, (struct sockaddr *) &desc->local_address, sizeof(desc->local_address))) {
00445       ast_log(LOG_ERROR, "Unable to bind %s to %s:%d: %s\n",
00446          desc->name,
00447          ast_inet_ntoa(desc->local_address.sin_addr), ntohs(desc->local_address.sin_port),
00448          strerror(errno));
00449       goto error;
00450    }
00451    if (listen(desc->accept_fd, 10)) {
00452       ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name);
00453       goto error;
00454    }
00455    flags = fcntl(desc->accept_fd, F_GETFL);
00456    fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
00457    if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
00458       ast_log(LOG_ERROR, "Unable to launch thread for %s on %s:%d: %s\n",
00459          desc->name,
00460          ast_inet_ntoa(desc->local_address.sin_addr), ntohs(desc->local_address.sin_port),
00461          strerror(errno));
00462       goto error;
00463    }
00464    return;
00465 
00466 error:
00467    close(desc->accept_fd);
00468    desc->accept_fd = -1;
00469 }

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

References AST_PTHREADT_NULL, and desc.

Referenced by unload_module().

00472 {
00473    if (desc->master != AST_PTHREADT_NULL) {
00474       pthread_cancel(desc->master);
00475       pthread_kill(desc->master, SIGURG);
00476       pthread_join(desc->master, NULL);
00477    }
00478    if (desc->accept_fd != -1)
00479       close(desc->accept_fd);
00480    desc->accept_fd = -1;
00481 }

HOOK_T ast_tcptls_server_write ( struct ast_tcptls_session_instance tcptls_session,
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_write().

Referenced by __sip_xmit().

00101 {
00102    if (tcptls_session->fd == -1) {
00103       ast_log(LOG_ERROR, "server_write 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_write(tcptls_session->ssl, buf, count);
00111 #endif
00112    return write(tcptls_session->fd, buf, count);
00113 }

static void* handle_tls_connection ( void *  data  )  [static]

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 126 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, ast_tcptls_session_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, ast_tcptls_session_args::tls_cfg, and ast_tcptls_session_args::worker_fn.

Referenced by ast_tcptls_client_start(), and ast_tcptls_server_root().

00127 {
00128    struct ast_tcptls_session_instance *tcptls_session = data;
00129 #ifdef DO_SSL
00130    int (*ssl_setup)(SSL *) = (tcptls_session->client) ? SSL_connect : SSL_accept;
00131    int ret;
00132    char err[256];
00133 #endif
00134 
00135    /*
00136    * open a FILE * as appropriate.
00137    */
00138    if (!tcptls_session->parent->tls_cfg) {
00139       tcptls_session->f = fdopen(tcptls_session->fd, "w+");
00140       setvbuf(tcptls_session->f, NULL, _IONBF, 0);
00141    }
00142 #ifdef DO_SSL
00143    else if ( (tcptls_session->ssl = SSL_new(tcptls_session->parent->tls_cfg->ssl_ctx)) ) {
00144       SSL_set_fd(tcptls_session->ssl, tcptls_session->fd);
00145       if ((ret = ssl_setup(tcptls_session->ssl)) <= 0) {
00146          ast_verb(2, "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err));
00147       } else {
00148 #if defined(HAVE_FUNOPEN)  /* the BSD interface */
00149          tcptls_session->f = funopen(tcptls_session->ssl, ssl_read, ssl_write, NULL, ssl_close);
00150 
00151 #elif defined(HAVE_FOPENCOOKIE)  /* the glibc/linux interface */
00152          static const cookie_io_functions_t cookie_funcs = {
00153             ssl_read, ssl_write, NULL, ssl_close
00154          };
00155          tcptls_session->f = fopencookie(tcptls_session->ssl, "w+", cookie_funcs);
00156 #else
00157          /* could add other methods here */
00158          ast_debug(2, "no tcptls_session->f methods attempted!");
00159 #endif
00160          if ((tcptls_session->client && !ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER))
00161             || (!tcptls_session->client && ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_VERIFY_CLIENT))) {
00162             X509 *peer;
00163             long res;
00164             peer = SSL_get_peer_certificate(tcptls_session->ssl);
00165             if (!peer)
00166                ast_log(LOG_WARNING, "No peer SSL certificate\n");
00167             res = SSL_get_verify_result(tcptls_session->ssl);
00168             if (res != X509_V_OK)
00169                ast_log(LOG_ERROR, "Certificate did not verify: %s\n", X509_verify_cert_error_string(res));
00170             if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) {
00171                ASN1_STRING *str;
00172                unsigned char *str2;
00173                X509_NAME *name = X509_get_subject_name(peer);
00174                int pos = -1;
00175                int found = 0;
00176             
00177                for (;;) {
00178                   /* Walk the certificate to check all available "Common Name" */
00179                   /* XXX Probably should do a gethostbyname on the hostname and compare that as well */
00180                   pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos);
00181                   if (pos < 0)
00182                      break;
00183                   str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos));
00184                   ASN1_STRING_to_UTF8(&str2, str);
00185                   if (str2) {
00186                      if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2))
00187                         found = 1;
00188                      ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2);
00189                      OPENSSL_free(str2);
00190                   }
00191                   if (found)
00192                      break;
00193                }
00194                if (!found) {
00195                   ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname);
00196                   if (peer)
00197                      X509_free(peer);
00198                   fclose(tcptls_session->f);
00199                   return NULL;
00200                }
00201             }
00202             if (peer)
00203                X509_free(peer);
00204          }
00205       }
00206       if (!tcptls_session->f) /* no success opening descriptor stacking */
00207          SSL_free(tcptls_session->ssl);
00208    }
00209 #endif /* DO_SSL */
00210 
00211    if (!tcptls_session->f) {
00212       close(tcptls_session->fd);
00213       ast_log(LOG_WARNING, "FILE * open failed!\n");
00214       ao2_ref(tcptls_session, -1);
00215       return NULL;
00216    }
00217 
00218    if (tcptls_session && tcptls_session->parent->worker_fn)
00219       return tcptls_session->parent->worker_fn(tcptls_session);
00220    else
00221       return tcptls_session;
00222 }

static void session_instance_destructor ( void *  obj  )  [static]

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

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

static int ssl_close ( void *  cookie  )  [static]

Definition at line 76 of file tcptls.c.

Referenced by handle_tls_connection().

00077 {
00078    close(SSL_get_fd(cookie));
00079    SSL_shutdown(cookie);
00080    SSL_free(cookie);
00081    return 0;
00082 }

static HOOK_T ssl_read ( void *  cookie,
char *  buf,
LEN_T  len 
) [static]

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

References ast_verb.

Referenced by ast_tcptls_server_read(), and handle_tls_connection().

00055 {
00056    int i = SSL_read(cookie, buf, len-1);
00057 #if 0
00058    if (i >= 0)
00059       buf[i] = '\0';
00060    ast_verb(0, "ssl read size %d returns %d <%s>\n", (int)len, i, buf);
00061 #endif
00062    return i;
00063 }

static HOOK_T ssl_write ( void *  cookie,
const char *  buf,
LEN_T  len 
) [static]

Definition at line 65 of file tcptls.c.

References ast_verb, and s.

Referenced by ast_tcptls_server_write(), and handle_tls_connection().

00066 {
00067 #if 0
00068    char *s = alloca(len+1);
00069    strncpy(s, buf, len);
00070    s[len] = '\0';
00071    ast_verb(0, "ssl write size %d <%s>\n", (int)len, s);
00072 #endif
00073    return SSL_write(cookie, buf, len);
00074 }


Generated on Fri Jul 24 00:42:00 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7