#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_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(). | |
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) |
Brett Bryant <brettbryant@gmail.com>
Definition in file tcptls.c.
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().
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().
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.
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.
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 }