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

Go to the source code of this file.
Functions | |
| static int | __ssl_setup (struct ast_tls_config *cfg, int client) |
| void * | ast_make_file_from_fd (void *data) |
| creates a FILE * from the fd passed by the accept thread. This operation is potentially expensive (certificate verification), so we do it in the child thread context. | |
| int | ast_ssl_setup (struct ast_tls_config *cfg) |
| struct ast_tcptls_session_instance * | ast_tcptls_client_start (struct server_args *desc) |
| A generic client routine for a TCP client and starts a thread for handling accept(). | |
| HOOK_T | ast_tcptls_server_read (struct ast_tcptls_session_instance *tcptls_session, void *buf, size_t count) |
| replacement read/write functions for SSL support. We use wrappers rather than SSL_read/SSL_write directly so we can put in some debugging. | |
| void * | ast_tcptls_server_root (void *data) |
| void | ast_tcptls_server_start (struct server_args *desc) |
| This is a generic (re)start routine for a TCP server, which does the socket/bind/listen and starts a thread for handling accept(). | |
| void | ast_tcptls_server_stop (struct server_args *desc) |
| Shutdown a running server if there is one. | |
| HOOK_T | ast_tcptls_server_write (struct ast_tcptls_session_instance *tcptls_session, void *buf, size_t count) |
| static void | session_instance_destructor (void *obj) |
Definition in file tcptls.c.
| 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 379 of file tcptls.c.
References ao2_ref(), ast_debug, ast_log(), AST_SSL_DONT_VERIFY_SERVER, AST_SSL_IGNORE_COMMON_NAME, AST_SSL_VERIFY_CLIENT, ast_test_flag, ast_verb, ast_tcptls_session_instance::client, ast_tcptls_session_instance::f, ast_tcptls_session_instance::fd, ast_tls_config::flags, server_args::hostname, LOG_ERROR, LOG_WARNING, name, ast_tcptls_session_instance::parent, ast_tcptls_session_instance::ssl, ast_tls_config::ssl_ctx, str, server_args::tls_cfg, and server_args::worker_fn.
Referenced by ast_tcptls_client_start(), and ast_tcptls_server_root().
00380 { 00381 struct ast_tcptls_session_instance *tcptls_session = data; 00382 #ifdef DO_SSL 00383 int (*ssl_setup)(SSL *) = (tcptls_session->client) ? SSL_connect : SSL_accept; 00384 int ret; 00385 char err[256]; 00386 #endif 00387 00388 /* 00389 * open a FILE * as appropriate. 00390 */ 00391 if (!tcptls_session->parent->tls_cfg) { 00392 tcptls_session->f = fdopen(tcptls_session->fd, "w+"); 00393 setvbuf(tcptls_session->f, NULL, _IONBF, 0); 00394 } 00395 #ifdef DO_SSL 00396 else if ( (tcptls_session->ssl = SSL_new(tcptls_session->parent->tls_cfg->ssl_ctx)) ) { 00397 SSL_set_fd(tcptls_session->ssl, tcptls_session->fd); 00398 if ((ret = ssl_setup(tcptls_session->ssl)) <= 0) { 00399 ast_verb(2, "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err)); 00400 } else { 00401 #if defined(HAVE_FUNOPEN) /* the BSD interface */ 00402 tcptls_session->f = funopen(tcptls_session->ssl, ssl_read, ssl_write, NULL, ssl_close); 00403 00404 #elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */ 00405 static const cookie_io_functions_t cookie_funcs = { 00406 ssl_read, ssl_write, NULL, ssl_close 00407 }; 00408 tcptls_session->f = fopencookie(tcptls_session->ssl, "w+", cookie_funcs); 00409 #else 00410 /* could add other methods here */ 00411 ast_debug(2, "no tcptls_session->f methods attempted!"); 00412 #endif 00413 if ((tcptls_session->client && !ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER)) 00414 || (!tcptls_session->client && ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_VERIFY_CLIENT))) { 00415 X509 *peer; 00416 long res; 00417 peer = SSL_get_peer_certificate(tcptls_session->ssl); 00418 if (!peer) 00419 ast_log(LOG_WARNING, "No peer SSL certificate\n"); 00420 res = SSL_get_verify_result(tcptls_session->ssl); 00421 if (res != X509_V_OK) 00422 ast_log(LOG_ERROR, "Certificate did not verify: %s\n", X509_verify_cert_error_string(res)); 00423 if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) { 00424 ASN1_STRING *str; 00425 unsigned char *str2; 00426 X509_NAME *name = X509_get_subject_name(peer); 00427 int pos = -1; 00428 int found = 0; 00429 00430 for (;;) { 00431 /* Walk the certificate to check all available "Common Name" */ 00432 /* XXX Probably should do a gethostbyname on the hostname and compare that as well */ 00433 pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos); 00434 if (pos < 0) 00435 break; 00436 str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos)); 00437 ASN1_STRING_to_UTF8(&str2, str); 00438 if (str2) { 00439 if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) 00440 found = 1; 00441 ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2); 00442 OPENSSL_free(str2); 00443 } 00444 if (found) 00445 break; 00446 } 00447 if (!found) { 00448 ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname); 00449 if (peer) 00450 X509_free(peer); 00451 fclose(tcptls_session->f); 00452 ao2_ref(tcptls_session, -1); 00453 return NULL; 00454 } 00455 } 00456 if (peer) 00457 X509_free(peer); 00458 } 00459 } 00460 if (!tcptls_session->f) /* no success opening descriptor stacking */ 00461 SSL_free(tcptls_session->ssl); 00462 } 00463 #endif /* DO_SSL */ 00464 00465 if (!tcptls_session->f) { 00466 close(tcptls_session->fd); 00467 ast_log(LOG_WARNING, "FILE * open failed!\n"); 00468 ao2_ref(tcptls_session, -1); 00469 return NULL; 00470 } 00471 00472 if (tcptls_session && tcptls_session->parent->worker_fn) 00473 return tcptls_session->parent->worker_fn(tcptls_session); 00474 else 00475 return tcptls_session; 00476 }
| int ast_ssl_setup | ( | struct ast_tls_config * | cfg | ) |
Definition at line 220 of file tcptls.c.
References __ssl_setup().
Referenced by __ast_http_load(), __init_manager(), and reload_config().
00221 { 00222 return __ssl_setup(cfg, 0); 00223 }
| struct ast_tcptls_session_instance* ast_tcptls_client_start | ( | struct server_args * | desc | ) | [read] |
A generic client routine for a TCP client and starts a thread for handling accept().
Definition at line 228 of file tcptls.c.
References __ssl_setup(), server_args::accept_fd, ao2_alloc(), ao2_ref(), ast_debug, ast_inet_ntoa(), ast_log(), ast_make_file_from_fd(), ast_mutex_init(), ast_tcptls_session_instance::client, ast_tls_config::enabled, errno, ast_tcptls_session_instance::fd, ast_tcptls_session_instance::lock, LOG_ERROR, LOG_WARNING, server_args::name, server_args::oldsin, ast_tcptls_session_instance::parent, ast_tcptls_session_instance::requestor, session_instance_destructor(), server_args::sin, server_args::tls_cfg, and server_args::worker_fn.
Referenced by sip_prepare_socket().
00229 { 00230 int flags; 00231 struct ast_tcptls_session_instance *tcptls_session = NULL; 00232 00233 /* Do nothing if nothing has changed */ 00234 if(!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) { 00235 ast_debug(1, "Nothing changed in %s\n", desc->name); 00236 return NULL; 00237 } 00238 00239 desc->oldsin = desc->sin; 00240 00241 if (desc->accept_fd != -1) 00242 close(desc->accept_fd); 00243 00244 desc->accept_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 00245 if (desc->accept_fd < 0) { 00246 ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n", 00247 desc->name, strerror(errno)); 00248 return NULL; 00249 } 00250 00251 if (connect(desc->accept_fd, (const struct sockaddr *)&desc->sin, sizeof(desc->sin))) { 00252 ast_log(LOG_ERROR, "Unable to connect %s to %s:%d: %s\n", 00253 desc->name, 00254 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port), 00255 strerror(errno)); 00256 goto error; 00257 } 00258 00259 if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor))) 00260 goto error; 00261 00262 ast_mutex_init(&tcptls_session->lock); 00263 00264 flags = fcntl(desc->accept_fd, F_GETFL); 00265 fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK); 00266 00267 tcptls_session->fd = desc->accept_fd; 00268 tcptls_session->parent = desc; 00269 tcptls_session->parent->worker_fn = NULL; 00270 memcpy(&tcptls_session->requestor, &desc->sin, sizeof(tcptls_session->requestor)); 00271 00272 tcptls_session->client = 1; 00273 00274 if (desc->tls_cfg) { 00275 desc->tls_cfg->enabled = 1; 00276 __ssl_setup(desc->tls_cfg, 1); 00277 } 00278 00279 if (!ast_make_file_from_fd(tcptls_session)) 00280 goto error; 00281 00282 return tcptls_session; 00283 00284 error: 00285 close(desc->accept_fd); 00286 desc->accept_fd = -1; 00287 if (tcptls_session) 00288 ao2_ref(tcptls_session, -1); 00289 return NULL; 00290 }
| HOOK_T ast_tcptls_server_read | ( | struct ast_tcptls_session_instance * | tcptls_session, | |
| void * | buf, | |||
| size_t | count | |||
| ) |
replacement read/write functions for SSL support. We use wrappers rather than SSL_read/SSL_write directly so we can put in some debugging.
Definition at line 84 of file tcptls.c.
References ast_log(), errno, ast_tcptls_session_instance::fd, LOG_ERROR, and ast_tcptls_session_instance::ssl.
00085 { 00086 if (tcptls_session->fd == -1) { 00087 ast_log(LOG_ERROR, "server_read called with an fd of -1\n"); 00088 errno = EIO; 00089 return -1; 00090 } 00091 00092 #ifdef DO_SSL 00093 if (tcptls_session->ssl) 00094 return ssl_read(tcptls_session->ssl, buf, count); 00095 #endif 00096 return read(tcptls_session->fd, buf, count); 00097 }
| void* ast_tcptls_server_root | ( | void * | data | ) |
Definition at line 120 of file tcptls.c.
References server_args::accept_fd, ao2_alloc(), ao2_ref(), ast_log(), ast_make_file_from_fd(), ast_mutex_init(), ast_pthread_create_detached_background, ast_wait_for_input(), ast_tcptls_session_instance::client, desc, errno, ast_tcptls_session_instance::fd, ast_tcptls_session_instance::lock, LOG_WARNING, ast_tcptls_session_instance::parent, server_args::periodic_fn, server_args::poll_timeout, ast_tcptls_session_instance::requestor, and session_instance_destructor().
00121 { 00122 struct server_args *desc = data; 00123 int fd; 00124 struct sockaddr_in sin; 00125 socklen_t sinlen; 00126 struct ast_tcptls_session_instance *tcptls_session; 00127 pthread_t launched; 00128 00129 for (;;) { 00130 int i, flags; 00131 00132 if (desc->periodic_fn) 00133 desc->periodic_fn(desc); 00134 i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout); 00135 if (i <= 0) 00136 continue; 00137 sinlen = sizeof(sin); 00138 fd = accept(desc->accept_fd, (struct sockaddr *)&sin, &sinlen); 00139 if (fd < 0) { 00140 if ((errno != EAGAIN) && (errno != EINTR)) 00141 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno)); 00142 continue; 00143 } 00144 tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor); 00145 if (!tcptls_session) { 00146 ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno)); 00147 close(fd); 00148 continue; 00149 } 00150 00151 ast_mutex_init(&tcptls_session->lock); 00152 00153 flags = fcntl(fd, F_GETFL); 00154 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); 00155 tcptls_session->fd = fd; 00156 tcptls_session->parent = desc; 00157 memcpy(&tcptls_session->requestor, &sin, sizeof(tcptls_session->requestor)); 00158 00159 tcptls_session->client = 0; 00160 00161 if (ast_pthread_create_detached_background(&launched, NULL, ast_make_file_from_fd, tcptls_session)) { 00162 ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno)); 00163 close(tcptls_session->fd); 00164 ao2_ref(tcptls_session, -1); 00165 } 00166 } 00167 return NULL; 00168 }
| void ast_tcptls_server_start | ( | struct server_args * | desc | ) |
This is a generic (re)start routine for a TCP server, which does the socket/bind/listen and starts a thread for handling accept().
Definition at line 297 of file tcptls.c.
References server_args::accept_fd, server_args::accept_fn, ast_debug, ast_inet_ntoa(), ast_log(), ast_pthread_create_background, AST_PTHREADT_NULL, errno, LOG_ERROR, server_args::master, server_args::name, server_args::oldsin, and server_args::sin.
Referenced by __ast_http_load(), __init_manager(), and reload_config().
00298 { 00299 int flags; 00300 int x = 1; 00301 00302 /* Do nothing if nothing has changed */ 00303 if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) { 00304 ast_debug(1, "Nothing changed in %s\n", desc->name); 00305 return; 00306 } 00307 00308 desc->oldsin = desc->sin; 00309 00310 /* Shutdown a running server if there is one */ 00311 if (desc->master != AST_PTHREADT_NULL) { 00312 pthread_cancel(desc->master); 00313 pthread_kill(desc->master, SIGURG); 00314 pthread_join(desc->master, NULL); 00315 } 00316 00317 if (desc->accept_fd != -1) 00318 close(desc->accept_fd); 00319 00320 /* If there's no new server, stop here */ 00321 if (desc->sin.sin_family == 0) 00322 return; 00323 00324 desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0); 00325 if (desc->accept_fd < 0) { 00326 ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", 00327 desc->name, strerror(errno)); 00328 return; 00329 } 00330 00331 setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); 00332 if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) { 00333 ast_log(LOG_ERROR, "Unable to bind %s to %s:%d: %s\n", 00334 desc->name, 00335 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port), 00336 strerror(errno)); 00337 goto error; 00338 } 00339 if (listen(desc->accept_fd, 10)) { 00340 ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name); 00341 goto error; 00342 } 00343 flags = fcntl(desc->accept_fd, F_GETFL); 00344 fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK); 00345 if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) { 00346 ast_log(LOG_ERROR, "Unable to launch thread for %s on %s:%d: %s\n", 00347 desc->name, 00348 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port), 00349 strerror(errno)); 00350 goto error; 00351 } 00352 return; 00353 00354 error: 00355 close(desc->accept_fd); 00356 desc->accept_fd = -1; 00357 }
| void ast_tcptls_server_stop | ( | struct server_args * | desc | ) |
Shutdown a running server if there is one.
Definition at line 360 of file tcptls.c.
References server_args::accept_fd, AST_PTHREADT_NULL, and server_args::master.
Referenced by unload_module().
00361 { 00362 if (desc->master != AST_PTHREADT_NULL) { 00363 pthread_cancel(desc->master); 00364 pthread_kill(desc->master, SIGURG); 00365 pthread_join(desc->master, NULL); 00366 } 00367 if (desc->accept_fd != -1) 00368 close(desc->accept_fd); 00369 desc->accept_fd = -1; 00370 }
| HOOK_T ast_tcptls_server_write | ( | struct ast_tcptls_session_instance * | tcptls_session, | |
| void * | buf, | |||
| size_t | count | |||
| ) |
Definition at line 99 of file tcptls.c.
References ast_log(), errno, ast_tcptls_session_instance::fd, LOG_ERROR, and ast_tcptls_session_instance::ssl.
Referenced by __sip_xmit().
00100 { 00101 if (tcptls_session->fd == -1) { 00102 ast_log(LOG_ERROR, "server_write called with an fd of -1\n"); 00103 errno = EIO; 00104 return -1; 00105 } 00106 00107 #ifdef DO_SSL 00108 if (tcptls_session->ssl) 00109 return ssl_write(tcptls_session->ssl, buf, count); 00110 #endif 00111 return write(tcptls_session->fd, buf, count); 00112 }
| static void session_instance_destructor | ( | void * | obj | ) | [static] |
Definition at line 114 of file tcptls.c.
References ast_mutex_destroy(), and ast_tcptls_session_instance::lock.
Referenced by ast_tcptls_client_start(), and ast_tcptls_server_root().
00115 { 00116 struct ast_tcptls_session_instance *i = obj; 00117 ast_mutex_destroy(&i->lock); 00118 }
1.5.6