Thu Jul 9 13:41:34 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"

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)
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 *tcptls_session, void *buf, size_t count)
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)
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 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.

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

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

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 }

static int ssl_close ( void *  cookie  )  [static]

Definition at line 75 of file tcptls.c.

Referenced by ast_make_file_from_fd().

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

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

References ast_verb.

Referenced by ast_make_file_from_fd(), and ast_tcptls_server_read().

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

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

Definition at line 64 of file tcptls.c.

References ast_verb, and s.

Referenced by ast_make_file_from_fd(), and ast_tcptls_server_write().

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


Generated on Thu Jul 9 13:41:34 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7