Thu Oct 11 06:37:33 2012

Asterisk developer's documentation


tcptls.c File Reference

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

#include "asterisk.h"
#include <signal.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"

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)
int ast_ssl_setup (struct ast_tls_config *cfg)
 Set up an SSL server.
void ast_ssl_teardown (struct ast_tls_config *cfg)
 free resources used by an SSL server
struct
ast_tcptls_session_instance
ast_tcptls_client_create (struct ast_tcptls_session_args *desc)
struct
ast_tcptls_session_instance
ast_tcptls_client_start (struct ast_tcptls_session_instance *tcptls_session)
 attempts to connect and start tcptls session, on error the tcptls_session's ref count is decremented, fd and file are closed, and NULL is returned.
void ast_tcptls_close_session_file (struct ast_tcptls_session_instance *tcptls_session)
 Closes a tcptls session instance's file and/or file descriptor. The tcptls_session will be set to NULL and it's file descriptor will be set to -1 by this function.
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 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, const void *buf, size_t count)
int ast_tls_read_conf (struct ast_tls_config *tls_cfg, struct ast_tcptls_session_args *tls_desc, const char *varname, const char *value)
 Used to parse conf files containing tls/ssl options.
static void * handle_tcptls_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.


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

References ast_debug, AST_SSL_SSLV2_CLIENT, AST_SSL_SSLV3_CLIENT, AST_SSL_TLSV1_CLIENT, ast_strlen_zero(), ast_test_flag, ast_verb, ast_tls_config::cafile, ast_tls_config::capath, ast_tls_config::certfile, ast_tls_config::cipher, ast_tls_config::enabled, ast_tls_config::flags, ast_tls_config::pvtfile, S_OR, and ast_tls_config::ssl_ctx.

Referenced by ast_ssl_setup(), and ast_tcptls_client_start().

00321 {
00322 #ifndef DO_SSL
00323    cfg->enabled = 0;
00324    return 0;
00325 #else
00326    if (!cfg->enabled) {
00327       return 0;
00328    }
00329 
00330    /* Get rid of an old SSL_CTX since we're about to
00331     * allocate a new one
00332     */
00333    if (cfg->ssl_ctx) {
00334       SSL_CTX_free(cfg->ssl_ctx);
00335       cfg->ssl_ctx = NULL;
00336    }
00337 
00338    if (client) {
00339 #ifndef OPENSSL_NO_SSL2
00340       if (ast_test_flag(&cfg->flags, AST_SSL_SSLV2_CLIENT)) {
00341          cfg->ssl_ctx = SSL_CTX_new(SSLv2_client_method());
00342       } else
00343 #endif
00344       if (ast_test_flag(&cfg->flags, AST_SSL_SSLV3_CLIENT)) {
00345          cfg->ssl_ctx = SSL_CTX_new(SSLv3_client_method());
00346       } else if (ast_test_flag(&cfg->flags, AST_SSL_TLSV1_CLIENT)) {
00347          cfg->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
00348       } else {
00349          /* SSLv23_client_method() sends SSLv2, this was the original
00350           * default for ssl clients before the option was given to
00351           * pick what protocol a client should use.  In order not
00352           * to break expected behavior it remains the default. */
00353          cfg->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
00354       }
00355    } else {
00356       /* SSLv23_server_method() supports TLSv1, SSLv2, and SSLv3 inbound connections. */
00357       cfg->ssl_ctx = SSL_CTX_new(SSLv23_server_method());
00358    }
00359 
00360    if (!cfg->ssl_ctx) {
00361       ast_debug(1, "Sorry, SSL_CTX_new call returned null...\n");
00362       cfg->enabled = 0;
00363       return 0;
00364    }
00365    if (!ast_strlen_zero(cfg->certfile)) {
00366       char *tmpprivate = ast_strlen_zero(cfg->pvtfile) ? cfg->certfile : cfg->pvtfile;
00367       if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0) {
00368          if (!client) {
00369             /* Clients don't need a certificate, but if its setup we can use it */
00370             ast_verb(0, "SSL error loading cert file. <%s>\n", cfg->certfile);
00371             cfg->enabled = 0;
00372             SSL_CTX_free(cfg->ssl_ctx);
00373             cfg->ssl_ctx = NULL;
00374             return 0;
00375          }
00376       }
00377       if ((SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, tmpprivate, SSL_FILETYPE_PEM) == 0) || (SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 )) {
00378          if (!client) {
00379             /* Clients don't need a private key, but if its setup we can use it */
00380             ast_verb(0, "SSL error loading private key file. <%s>\n", tmpprivate);
00381             cfg->enabled = 0;
00382             SSL_CTX_free(cfg->ssl_ctx);
00383             cfg->ssl_ctx = NULL;
00384             return 0;
00385          }
00386       }
00387    }
00388    if (!ast_strlen_zero(cfg->cipher)) {
00389       if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) {
00390          if (!client) {
00391             ast_verb(0, "SSL cipher error <%s>\n", cfg->cipher);
00392             cfg->enabled = 0;
00393             SSL_CTX_free(cfg->ssl_ctx);
00394             cfg->ssl_ctx = NULL;
00395             return 0;
00396          }
00397       }
00398    }
00399    if (!ast_strlen_zero(cfg->cafile) || !ast_strlen_zero(cfg->capath)) {
00400       if (SSL_CTX_load_verify_locations(cfg->ssl_ctx, S_OR(cfg->cafile, NULL), S_OR(cfg->capath,NULL)) == 0) {
00401          ast_verb(0, "SSL CA file(%s)/path(%s) error\n", cfg->cafile, cfg->capath);
00402       }
00403    }
00404 
00405    ast_verb(0, "SSL certificate ok\n");
00406    return 1;
00407 #endif
00408 }

int ast_ssl_setup ( struct ast_tls_config cfg  ) 

Set up an SSL server.

Parameters:
cfg Configuration for the SSL server
Return values:
1 Success
0 Failure

Definition at line 410 of file tcptls.c.

References __ssl_setup().

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

00411 {
00412    return __ssl_setup(cfg, 0);
00413 }

void ast_ssl_teardown ( struct ast_tls_config cfg  ) 

free resources used by an SSL server

Note:
This only needs to be called if ast_ssl_setup() was directly called first.
Parameters:
cfg Configuration for the SSL server

Definition at line 415 of file tcptls.c.

References ast_tls_config::ssl_ctx.

Referenced by sip_tcptls_client_args_destructor(), and unload_module().

00416 {
00417 #ifdef DO_SSL
00418    if (cfg->ssl_ctx) {
00419       SSL_CTX_free(cfg->ssl_ctx);
00420       cfg->ssl_ctx = NULL;
00421    }
00422 #endif
00423 }

struct ast_tcptls_session_instance* ast_tcptls_client_create ( struct ast_tcptls_session_args desc  )  [read]

Definition at line 462 of file tcptls.c.

References ast_tcptls_session_args::accept_fd, ao2_alloc, ao2_ref, ast_bind(), ast_debug, ast_log(), ast_sockaddr_cmp(), ast_sockaddr_copy(), ast_sockaddr_is_ipv6(), ast_sockaddr_isnull(), ast_sockaddr_setnull(), ast_sockaddr_stringify(), ast_tcptls_session_instance::client, errno, ast_tcptls_session_instance::fd, ast_tcptls_session_args::local_address, LOG_ERROR, LOG_WARNING, ast_tcptls_session_args::name, ast_tcptls_session_args::old_address, ast_tcptls_session_instance::parent, ast_tcptls_session_instance::remote_address, ast_tcptls_session_args::remote_address, and ast_tcptls_session_args::worker_fn.

Referenced by app_exec(), and sip_prepare_socket().

00463 {
00464    int x = 1;
00465    struct ast_tcptls_session_instance *tcptls_session = NULL;
00466 
00467    /* Do nothing if nothing has changed */
00468    if (!ast_sockaddr_cmp(&desc->old_address, &desc->remote_address)) {
00469       ast_debug(1, "Nothing changed in %s\n", desc->name);
00470       return NULL;
00471    }
00472 
00473    /* If we return early, there is no connection */
00474    ast_sockaddr_setnull(&desc->old_address);
00475 
00476    if (desc->accept_fd != -1) {
00477       close(desc->accept_fd);
00478    }
00479 
00480    desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->remote_address) ?
00481              AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP);
00482    if (desc->accept_fd < 0) {
00483       ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
00484          desc->name, strerror(errno));
00485       return NULL;
00486    }
00487 
00488    /* if a local address was specified, bind to it so the connection will
00489       originate from the desired address */
00490    if (!ast_sockaddr_isnull(&desc->local_address)) {
00491       setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00492       if (ast_bind(desc->accept_fd, &desc->local_address)) {
00493          ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
00494             desc->name,
00495             ast_sockaddr_stringify(&desc->local_address),
00496             strerror(errno));
00497          goto error;
00498       }
00499    }
00500 
00501    if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), NULL))) {
00502       goto error;
00503    }
00504 
00505    tcptls_session->client = 1;
00506    tcptls_session->fd = desc->accept_fd;
00507    tcptls_session->parent = desc;
00508    tcptls_session->parent->worker_fn = NULL;
00509    ast_sockaddr_copy(&tcptls_session->remote_address,
00510            &desc->remote_address);
00511 
00512    /* Set current info */
00513    ast_sockaddr_copy(&desc->old_address, &desc->remote_address);
00514    return tcptls_session;
00515 
00516 error:
00517    close(desc->accept_fd);
00518    desc->accept_fd = -1;
00519    if (tcptls_session) {
00520       ao2_ref(tcptls_session, -1);
00521    }
00522    return NULL;
00523 }

struct ast_tcptls_session_instance* ast_tcptls_client_start ( struct ast_tcptls_session_instance tcptls_session  )  [read]

attempts to connect and start tcptls session, on error the tcptls_session's ref count is decremented, fd and file are closed, and NULL is returned.

Definition at line 425 of file tcptls.c.

References __ssl_setup(), ast_tcptls_session_args::accept_fd, ao2_ref, ast_connect(), ast_log(), ast_sockaddr_stringify(), desc, ast_tls_config::enabled, errno, handle_tcptls_connection(), LOG_ERROR, ast_tcptls_session_args::name, ast_tcptls_session_instance::parent, ast_tcptls_session_args::remote_address, and ast_tcptls_session_args::tls_cfg.

Referenced by _sip_tcp_helper_thread(), and app_exec().

00426 {
00427    struct ast_tcptls_session_args *desc;
00428    int flags;
00429 
00430    if (!(desc = tcptls_session->parent)) {
00431       goto client_start_error;
00432    }
00433 
00434    if (ast_connect(desc->accept_fd, &desc->remote_address)) {
00435       ast_log(LOG_ERROR, "Unable to connect %s to %s: %s\n",
00436          desc->name,
00437          ast_sockaddr_stringify(&desc->remote_address),
00438          strerror(errno));
00439       goto client_start_error;
00440    }
00441 
00442    flags = fcntl(desc->accept_fd, F_GETFL);
00443    fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK);
00444 
00445    if (desc->tls_cfg) {
00446       desc->tls_cfg->enabled = 1;
00447       __ssl_setup(desc->tls_cfg, 1);
00448    }
00449 
00450    return handle_tcptls_connection(tcptls_session);
00451 
00452 client_start_error:
00453    if (desc) {
00454       close(desc->accept_fd);
00455       desc->accept_fd = -1;
00456    }
00457    ao2_ref(tcptls_session, -1);
00458    return NULL;
00459 
00460 }

void ast_tcptls_close_session_file ( struct ast_tcptls_session_instance tcptls_session  ) 

Closes a tcptls session instance's file and/or file descriptor. The tcptls_session will be set to NULL and it's file descriptor will be set to -1 by this function.

Definition at line 595 of file tcptls.c.

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

Referenced by _sip_tcp_helper_thread(), ast_tcptls_server_root(), handle_tcptls_connection(), and sip_prepare_socket().

00596 {
00597    if (tcptls_session->f) {
00598       if (fclose(tcptls_session->f)) {
00599          ast_log(LOG_ERROR, "fclose() failed: %s\n", strerror(errno));
00600       }
00601       tcptls_session->f = NULL;
00602       tcptls_session->fd = -1;
00603    } else if (tcptls_session->fd != -1) {
00604       if (close(tcptls_session->fd)) {
00605          ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
00606       }
00607       tcptls_session->fd = -1;
00608    } else {
00609       ast_log(LOG_ERROR, "ast_tcptls_close_session_file invoked on session instance without file or file descriptor\n");
00610    }
00611 }

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

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

00114 {
00115    if (tcptls_session->fd == -1) {
00116       ast_log(LOG_ERROR, "server_read called with an fd of -1\n");
00117       errno = EIO;
00118       return -1;
00119    }
00120 
00121 #ifdef DO_SSL
00122    if (tcptls_session->ssl) {
00123       return ssl_read(tcptls_session->ssl, buf, count);
00124    }
00125 #endif
00126    return read(tcptls_session->fd, buf, count);
00127 }

void* ast_tcptls_server_root ( void *  data  ) 

Definition at line 268 of file tcptls.c.

References ast_tcptls_session_args::accept_fd, ao2_alloc, ao2_ref, ast_accept(), ast_log(), ast_pthread_create_detached_background, ast_sockaddr_copy(), ast_tcptls_close_session_file(), ast_wait_for_input(), ast_tcptls_session_instance::client, desc, errno, ast_tcptls_session_instance::fd, handle_tcptls_connection(), LOG_ERROR, LOG_WARNING, ast_tcptls_session_instance::parent, ast_tcptls_session_args::periodic_fn, ast_tcptls_session_args::poll_timeout, and ast_tcptls_session_instance::remote_address.

00269 {
00270    struct ast_tcptls_session_args *desc = data;
00271    int fd;
00272    struct ast_sockaddr addr;
00273    struct ast_tcptls_session_instance *tcptls_session;
00274    pthread_t launched;
00275 
00276    for (;;) {
00277       int i, flags;
00278 
00279       if (desc->periodic_fn) {
00280          desc->periodic_fn(desc);
00281       }
00282       i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
00283       if (i <= 0) {
00284          continue;
00285       }
00286       fd = ast_accept(desc->accept_fd, &addr);
00287       if (fd < 0) {
00288          if ((errno != EAGAIN) && (errno != EINTR)) {
00289             ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
00290          }
00291          continue;
00292       }
00293       tcptls_session = ao2_alloc(sizeof(*tcptls_session), NULL);
00294       if (!tcptls_session) {
00295          ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
00296          if (close(fd)) {
00297             ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
00298          }
00299          continue;
00300       }
00301 
00302       flags = fcntl(fd, F_GETFL);
00303       fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
00304       tcptls_session->fd = fd;
00305       tcptls_session->parent = desc;
00306       ast_sockaddr_copy(&tcptls_session->remote_address, &addr);
00307 
00308       tcptls_session->client = 0;
00309 
00310       /* This thread is now the only place that controls the single ref to tcptls_session */
00311       if (ast_pthread_create_detached_background(&launched, NULL, handle_tcptls_connection, tcptls_session)) {
00312          ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
00313          ast_tcptls_close_session_file(tcptls_session);
00314          ao2_ref(tcptls_session, -1);
00315       }
00316    }
00317    return NULL;
00318 }

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

References ast_tcptls_session_args::accept_fd, ast_tcptls_session_args::accept_fn, ast_bind(), ast_debug, ast_log(), ast_pthread_create_background, AST_PTHREADT_NULL, ast_sockaddr_cmp(), ast_sockaddr_copy(), ast_sockaddr_is_ipv6(), ast_sockaddr_isnull(), ast_sockaddr_setnull(), ast_sockaddr_stringify(), errno, ast_tcptls_session_args::local_address, LOG_ERROR, ast_tcptls_session_args::master, ast_tcptls_session_args::name, and ast_tcptls_session_args::old_address.

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

00526 {
00527    int flags;
00528    int x = 1;
00529 
00530    /* Do nothing if nothing has changed */
00531    if (!ast_sockaddr_cmp(&desc->old_address, &desc->local_address)) {
00532       ast_debug(1, "Nothing changed in %s\n", desc->name);
00533       return;
00534    }
00535 
00536    /* If we return early, there is no one listening */
00537    ast_sockaddr_setnull(&desc->old_address);
00538 
00539    /* Shutdown a running server if there is one */
00540    if (desc->master != AST_PTHREADT_NULL) {
00541       pthread_cancel(desc->master);
00542       pthread_kill(desc->master, SIGURG);
00543       pthread_join(desc->master, NULL);
00544    }
00545 
00546    if (desc->accept_fd != -1) {
00547       close(desc->accept_fd);
00548    }
00549 
00550    /* If there's no new server, stop here */
00551    if (ast_sockaddr_isnull(&desc->local_address)) {
00552       ast_debug(2, "Server disabled:  %s\n", desc->name);
00553       return;
00554    }
00555 
00556    desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->local_address) ?
00557              AF_INET6 : AF_INET, SOCK_STREAM, 0);
00558    if (desc->accept_fd < 0) {
00559       ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", desc->name, strerror(errno));
00560       return;
00561    }
00562 
00563    setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00564    if (ast_bind(desc->accept_fd, &desc->local_address)) {
00565       ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
00566          desc->name,
00567          ast_sockaddr_stringify(&desc->local_address),
00568          strerror(errno));
00569       goto error;
00570    }
00571    if (listen(desc->accept_fd, 10)) {
00572       ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name);
00573       goto error;
00574    }
00575    flags = fcntl(desc->accept_fd, F_GETFL);
00576    fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
00577    if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
00578       ast_log(LOG_ERROR, "Unable to launch thread for %s on %s: %s\n",
00579          desc->name,
00580          ast_sockaddr_stringify(&desc->local_address),
00581          strerror(errno));
00582       goto error;
00583    }
00584 
00585    /* Set current info */
00586    ast_sockaddr_copy(&desc->old_address, &desc->local_address);
00587 
00588    return;
00589 
00590 error:
00591    close(desc->accept_fd);
00592    desc->accept_fd = -1;
00593 }

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

References ast_tcptls_session_args::accept_fd, ast_debug, AST_PTHREADT_NULL, ast_tcptls_session_args::master, and ast_tcptls_session_args::name.

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

00614 {
00615    if (desc->master != AST_PTHREADT_NULL) {
00616       pthread_cancel(desc->master);
00617       pthread_kill(desc->master, SIGURG);
00618       pthread_join(desc->master, NULL);
00619       desc->master = AST_PTHREADT_NULL;
00620    }
00621    if (desc->accept_fd != -1) {
00622       close(desc->accept_fd);
00623    }
00624    desc->accept_fd = -1;
00625    ast_debug(2, "Stopped server :: %s\n", desc->name);
00626 }

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

Definition at line 129 of file tcptls.c.

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

Referenced by _sip_tcp_helper_thread().

00130 {
00131    if (tcptls_session->fd == -1) {
00132       ast_log(LOG_ERROR, "server_write called with an fd of -1\n");
00133       errno = EIO;
00134       return -1;
00135    }
00136 
00137 #ifdef DO_SSL
00138    if (tcptls_session->ssl) {
00139       return ssl_write(tcptls_session->ssl, buf, count);
00140    }
00141 #endif
00142    return write(tcptls_session->fd, buf, count);
00143 }

int ast_tls_read_conf ( struct ast_tls_config tls_cfg,
struct ast_tcptls_session_args tls_desc,
const char *  varname,
const char *  value 
)

Used to parse conf files containing tls/ssl options.

Definition at line 628 of file tcptls.c.

References ast_clear_flag, ast_free, ast_log(), ast_parse_arg(), ast_set2_flag, ast_set_flag, AST_SSL_DONT_VERIFY_SERVER, AST_SSL_SSLV2_CLIENT, AST_SSL_SSLV3_CLIENT, AST_SSL_TLSV1_CLIENT, AST_SSL_VERIFY_CLIENT, ast_strdup, ast_true(), ast_tls_config::cafile, ast_tls_config::capath, ast_tls_config::certfile, ast_tls_config::cipher, ast_tls_config::enabled, ast_tls_config::flags, ast_tcptls_session_args::local_address, LOG_WARNING, PARSE_ADDR, and ast_tls_config::pvtfile.

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

00629 {
00630    if (!strcasecmp(varname, "tlsenable") || !strcasecmp(varname, "sslenable")) {
00631       tls_cfg->enabled = ast_true(value) ? 1 : 0;
00632    } else if (!strcasecmp(varname, "tlscertfile") || !strcasecmp(varname, "sslcert") || !strcasecmp(varname, "tlscert")) {
00633       ast_free(tls_cfg->certfile);
00634       tls_cfg->certfile = ast_strdup(value);
00635    } else if (!strcasecmp(varname, "tlsprivatekey") || !strcasecmp(varname, "sslprivatekey")) {
00636       ast_free(tls_cfg->pvtfile);
00637       tls_cfg->pvtfile = ast_strdup(value);
00638    } else if (!strcasecmp(varname, "tlscipher") || !strcasecmp(varname, "sslcipher")) {
00639       ast_free(tls_cfg->cipher);
00640       tls_cfg->cipher = ast_strdup(value);
00641    } else if (!strcasecmp(varname, "tlscafile")) {
00642       ast_free(tls_cfg->cafile);
00643       tls_cfg->cafile = ast_strdup(value);
00644    } else if (!strcasecmp(varname, "tlscapath") || !strcasecmp(varname, "tlscadir")) {
00645       ast_free(tls_cfg->capath);
00646       tls_cfg->capath = ast_strdup(value);
00647    } else if (!strcasecmp(varname, "tlsverifyclient")) {
00648       ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_VERIFY_CLIENT);
00649    } else if (!strcasecmp(varname, "tlsdontverifyserver")) {
00650       ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_DONT_VERIFY_SERVER);
00651    } else if (!strcasecmp(varname, "tlsbindaddr") || !strcasecmp(varname, "sslbindaddr")) {
00652       if (ast_parse_arg(value, PARSE_ADDR, &tls_desc->local_address))
00653          ast_log(LOG_WARNING, "Invalid %s '%s'\n", varname, value);
00654    } else if (!strcasecmp(varname, "tlsclientmethod") || !strcasecmp(varname, "sslclientmethod")) {
00655       if (!strcasecmp(value, "tlsv1")) {
00656          ast_set_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
00657          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
00658          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
00659       } else if (!strcasecmp(value, "sslv3")) {
00660          ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
00661          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
00662          ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
00663       } else if (!strcasecmp(value, "sslv2")) {
00664          ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
00665          ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
00666          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
00667       }
00668    } else {
00669       return -1;
00670    }
00671 
00672    return 0;
00673 }

static void* handle_tcptls_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.

Note:
must decrement ref count before returning NULL on error

Definition at line 152 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_tcptls_close_session_file(), 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, ast_tls_config::ssl_ctx, 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().

00153 {
00154    struct ast_tcptls_session_instance *tcptls_session = data;
00155 #ifdef DO_SSL
00156    int (*ssl_setup)(SSL *) = (tcptls_session->client) ? SSL_connect : SSL_accept;
00157    int ret;
00158    char err[256];
00159 #endif
00160 
00161    /*
00162    * open a FILE * as appropriate.
00163    */
00164    if (!tcptls_session->parent->tls_cfg) {
00165       if ((tcptls_session->f = fdopen(tcptls_session->fd, "w+"))) {
00166          if(setvbuf(tcptls_session->f, NULL, _IONBF, 0)) {
00167             ast_tcptls_close_session_file(tcptls_session);
00168          }
00169       }
00170    }
00171 #ifdef DO_SSL
00172    else if ( (tcptls_session->ssl = SSL_new(tcptls_session->parent->tls_cfg->ssl_ctx)) ) {
00173       SSL_set_fd(tcptls_session->ssl, tcptls_session->fd);
00174       if ((ret = ssl_setup(tcptls_session->ssl)) <= 0) {
00175          ast_verb(2, "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err));
00176       } else {
00177 #if defined(HAVE_FUNOPEN)  /* the BSD interface */
00178          tcptls_session->f = funopen(tcptls_session->ssl, ssl_read, ssl_write, NULL, ssl_close);
00179 
00180 #elif defined(HAVE_FOPENCOOKIE)  /* the glibc/linux interface */
00181          static const cookie_io_functions_t cookie_funcs = {
00182             ssl_read, ssl_write, NULL, ssl_close
00183          };
00184          tcptls_session->f = fopencookie(tcptls_session->ssl, "w+", cookie_funcs);
00185 #else
00186          /* could add other methods here */
00187          ast_debug(2, "no tcptls_session->f methods attempted!\n");
00188 #endif
00189          if ((tcptls_session->client && !ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER))
00190             || (!tcptls_session->client && ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_VERIFY_CLIENT))) {
00191             X509 *peer;
00192             long res;
00193             peer = SSL_get_peer_certificate(tcptls_session->ssl);
00194             if (!peer) {
00195                ast_log(LOG_WARNING, "No peer SSL certificate\n");
00196             }
00197             res = SSL_get_verify_result(tcptls_session->ssl);
00198             if (res != X509_V_OK) {
00199                ast_log(LOG_ERROR, "Certificate did not verify: %s\n", X509_verify_cert_error_string(res));
00200             }
00201             if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) {
00202                ASN1_STRING *str;
00203                unsigned char *str2;
00204                X509_NAME *name = X509_get_subject_name(peer);
00205                int pos = -1;
00206                int found = 0;
00207 
00208                for (;;) {
00209                   /* Walk the certificate to check all available "Common Name" */
00210                   /* XXX Probably should do a gethostbyname on the hostname and compare that as well */
00211                   pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos);
00212                   if (pos < 0) {
00213                      break;
00214                   }
00215                   str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos));
00216                   ASN1_STRING_to_UTF8(&str2, str);
00217                   if (str2) {
00218                      if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) {
00219                         found = 1;
00220                      }
00221                      ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2);
00222                      OPENSSL_free(str2);
00223                   }
00224                   if (found) {
00225                      break;
00226                   }
00227                }
00228                if (!found) {
00229                   ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname);
00230                   if (peer) {
00231                      X509_free(peer);
00232                   }
00233                   ast_tcptls_close_session_file(tcptls_session);
00234                   ao2_ref(tcptls_session, -1);
00235                   return NULL;
00236                }
00237             }
00238             if (peer) {
00239                X509_free(peer);
00240             }
00241          }
00242       }
00243       if (!tcptls_session->f) {  /* no success opening descriptor stacking */
00244          SSL_free(tcptls_session->ssl);
00245       }
00246    }
00247 #endif /* DO_SSL */
00248 
00249    if (!tcptls_session->f) {
00250       ast_tcptls_close_session_file(tcptls_session);
00251       ast_log(LOG_WARNING, "FILE * open failed!\n");
00252 #ifndef DO_SSL
00253       if (tcptls_session->parent->tls_cfg) {
00254          ast_log(LOG_WARNING, "Attempted a TLS connection without OpenSSL support. This will not work!\n");
00255       }
00256 #endif
00257       ao2_ref(tcptls_session, -1);
00258       return NULL;
00259    }
00260 
00261    if (tcptls_session->parent->worker_fn) {
00262       return tcptls_session->parent->worker_fn(tcptls_session);
00263    } else {
00264       return tcptls_session;
00265    }
00266 }


Generated on Thu Oct 11 06:37:33 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6