Code to support TCP and TLS server/client. More...
#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"#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) |
| 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. | |
| 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, void *buf, size_t count) |
| 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. | |
| static void | session_instance_destructor (void *obj) |
Code to support TCP and TLS server/client.
Definition in file tcptls.c.
| static int __ssl_setup | ( | struct ast_tls_config * | cfg, | |
| int | client | |||
| ) | [static] |
Definition at line 284 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().
00285 { 00286 #ifndef DO_SSL 00287 cfg->enabled = 0; 00288 return 0; 00289 #else 00290 if (!cfg->enabled) 00291 return 0; 00292 00293 SSL_load_error_strings(); 00294 SSLeay_add_ssl_algorithms(); 00295 00296 if (!(cfg->ssl_ctx = SSL_CTX_new( client ? SSLv23_client_method() : SSLv23_server_method() ))) { 00297 ast_debug(1, "Sorry, SSL_CTX_new call returned null...\n"); 00298 cfg->enabled = 0; 00299 return 0; 00300 } 00301 if (!ast_strlen_zero(cfg->certfile)) { 00302 if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 || 00303 SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 || 00304 SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 ) { 00305 if (!client) { 00306 /* Clients don't need a certificate, but if its setup we can use it */ 00307 ast_verb(0, "SSL cert error <%s>", cfg->certfile); 00308 sleep(2); 00309 cfg->enabled = 0; 00310 return 0; 00311 } 00312 } 00313 } 00314 if (!ast_strlen_zero(cfg->cipher)) { 00315 if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) { 00316 if (!client) { 00317 ast_verb(0, "SSL cipher error <%s>", cfg->cipher); 00318 sleep(2); 00319 cfg->enabled = 0; 00320 return 0; 00321 } 00322 } 00323 } 00324 if (!ast_strlen_zero(cfg->cafile) || !ast_strlen_zero(cfg->capath)) { 00325 if (SSL_CTX_load_verify_locations(cfg->ssl_ctx, S_OR(cfg->cafile, NULL), S_OR(cfg->capath,NULL)) == 0) 00326 ast_verb(0, "SSL CA file(%s)/path(%s) error\n", cfg->cafile, cfg->capath); 00327 } 00328 00329 ast_verb(0, "SSL certificate ok\n"); 00330 return 1; 00331 #endif 00332 }
| int ast_ssl_setup | ( | struct ast_tls_config * | cfg | ) |
Definition at line 334 of file tcptls.c.
References __ssl_setup().
Referenced by __ast_http_load(), __init_manager(), and reload_config().
00335 { 00336 return __ssl_setup(cfg, 0); 00337 }
| struct ast_tcptls_session_instance* ast_tcptls_client_create | ( | struct ast_tcptls_session_args * | desc | ) | [read] |
Definition at line 376 of file tcptls.c.
References ast_tcptls_session_args::accept_fd, ao2_alloc, ao2_ref, ast_debug, ast_inet_ntoa(), ast_log(), ast_mutex_init(), ast_tcptls_session_instance::client, errno, ast_tcptls_session_instance::fd, ast_tcptls_session_args::local_address, ast_tcptls_session_instance::lock, 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, session_instance_destructor(), and ast_tcptls_session_args::worker_fn.
Referenced by app_exec(), and sip_prepare_socket().
00377 { 00378 int x = 1; 00379 struct ast_tcptls_session_instance *tcptls_session = NULL; 00380 00381 /* Do nothing if nothing has changed */ 00382 if (!memcmp(&desc->old_address, &desc->remote_address, sizeof(desc->old_address))) { 00383 ast_debug(1, "Nothing changed in %s\n", desc->name); 00384 return NULL; 00385 } 00386 00387 desc->old_address = desc->remote_address; 00388 00389 if (desc->accept_fd != -1) 00390 close(desc->accept_fd); 00391 00392 desc->accept_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 00393 if (desc->accept_fd < 0) { 00394 ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n", 00395 desc->name, strerror(errno)); 00396 return NULL; 00397 } 00398 00399 /* if a local address was specified, bind to it so the connection will 00400 originate from the desired address */ 00401 if (desc->local_address.sin_family != 0) { 00402 setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); 00403 if (bind(desc->accept_fd, (struct sockaddr *) &desc->local_address, sizeof(desc->local_address))) { 00404 ast_log(LOG_ERROR, "Unable to bind %s to %s:%d: %s\n", 00405 desc->name, 00406 ast_inet_ntoa(desc->local_address.sin_addr), ntohs(desc->local_address.sin_port), 00407 strerror(errno)); 00408 goto error; 00409 } 00410 } 00411 00412 if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor))) 00413 goto error; 00414 00415 ast_mutex_init(&tcptls_session->lock); 00416 tcptls_session->client = 1; 00417 tcptls_session->fd = desc->accept_fd; 00418 tcptls_session->parent = desc; 00419 tcptls_session->parent->worker_fn = NULL; 00420 memcpy(&tcptls_session->remote_address, &desc->remote_address, sizeof(tcptls_session->remote_address)); 00421 00422 return tcptls_session; 00423 00424 error: 00425 close(desc->accept_fd); 00426 desc->accept_fd = -1; 00427 if (tcptls_session) 00428 ao2_ref(tcptls_session, -1); 00429 return NULL; 00430 }
| 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 339 of file tcptls.c.
References __ssl_setup(), ast_tcptls_session_args::accept_fd, ao2_ref, ast_inet_ntoa(), ast_log(), 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().
00340 { 00341 struct ast_tcptls_session_args *desc; 00342 int flags; 00343 00344 if (!(desc = tcptls_session->parent)) { 00345 goto client_start_error; 00346 } 00347 00348 if (connect(desc->accept_fd, (const struct sockaddr *) &desc->remote_address, sizeof(desc->remote_address))) { 00349 ast_log(LOG_ERROR, "Unable to connect %s to %s:%d: %s\n", 00350 desc->name, 00351 ast_inet_ntoa(desc->remote_address.sin_addr), ntohs(desc->remote_address.sin_port), 00352 strerror(errno)); 00353 goto client_start_error; 00354 } 00355 00356 flags = fcntl(desc->accept_fd, F_GETFL); 00357 fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK); 00358 00359 if (desc->tls_cfg) { 00360 desc->tls_cfg->enabled = 1; 00361 __ssl_setup(desc->tls_cfg, 1); 00362 } 00363 00364 return handle_tcptls_connection(tcptls_session); 00365 00366 client_start_error: 00367 close(desc->accept_fd); 00368 desc->accept_fd = -1; 00369 if (tcptls_session) { 00370 ao2_ref(tcptls_session, -1); 00371 } 00372 return NULL; 00373 00374 }
| 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 85 of file tcptls.c.
References ast_log(), errno, ast_tcptls_session_instance::fd, LOG_ERROR, and ast_tcptls_session_instance::ssl.
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 233 of file tcptls.c.
References ast_tcptls_session_args::accept_fd, ao2_alloc, ao2_ref, ast_log(), ast_mutex_init(), ast_pthread_create_detached_background, ast_wait_for_input(), ast_tcptls_session_instance::client, desc, errno, ast_tcptls_session_instance::fd, handle_tcptls_connection(), ast_tcptls_session_instance::lock, LOG_WARNING, ast_tcptls_session_instance::parent, ast_tcptls_session_args::periodic_fn, ast_tcptls_session_args::poll_timeout, ast_tcptls_session_instance::remote_address, and session_instance_destructor().
00234 { 00235 struct ast_tcptls_session_args *desc = data; 00236 int fd; 00237 struct sockaddr_in sin; 00238 socklen_t sinlen; 00239 struct ast_tcptls_session_instance *tcptls_session; 00240 pthread_t launched; 00241 00242 for (;;) { 00243 int i, flags; 00244 00245 if (desc->periodic_fn) 00246 desc->periodic_fn(desc); 00247 i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout); 00248 if (i <= 0) 00249 continue; 00250 sinlen = sizeof(sin); 00251 fd = accept(desc->accept_fd, (struct sockaddr *) &sin, &sinlen); 00252 if (fd < 0) { 00253 if ((errno != EAGAIN) && (errno != EINTR)) 00254 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno)); 00255 continue; 00256 } 00257 tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor); 00258 if (!tcptls_session) { 00259 ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno)); 00260 close(fd); 00261 continue; 00262 } 00263 00264 ast_mutex_init(&tcptls_session->lock); 00265 00266 flags = fcntl(fd, F_GETFL); 00267 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); 00268 tcptls_session->fd = fd; 00269 tcptls_session->parent = desc; 00270 memcpy(&tcptls_session->remote_address, &sin, sizeof(tcptls_session->remote_address)); 00271 00272 tcptls_session->client = 0; 00273 00274 /* This thread is now the only place that controls the single ref to tcptls_session */ 00275 if (ast_pthread_create_detached_background(&launched, NULL, handle_tcptls_connection, tcptls_session)) { 00276 ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno)); 00277 close(tcptls_session->fd); 00278 ao2_ref(tcptls_session, -1); 00279 } 00280 } 00281 return NULL; 00282 }
| 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 432 of file tcptls.c.
References ast_tcptls_session_args::accept_fd, ast_tcptls_session_args::accept_fn, ast_debug, ast_inet_ntoa(), ast_log(), ast_pthread_create_background, AST_PTHREADT_NULL, 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().
00433 { 00434 int flags; 00435 int x = 1; 00436 00437 /* Do nothing if nothing has changed */ 00438 if (!memcmp(&desc->old_address, &desc->local_address, sizeof(desc->old_address))) { 00439 ast_debug(1, "Nothing changed in %s\n", desc->name); 00440 return; 00441 } 00442 00443 desc->old_address = desc->local_address; 00444 00445 /* Shutdown a running server if there is one */ 00446 if (desc->master != AST_PTHREADT_NULL) { 00447 pthread_cancel(desc->master); 00448 pthread_kill(desc->master, SIGURG); 00449 pthread_join(desc->master, NULL); 00450 } 00451 00452 if (desc->accept_fd != -1) 00453 close(desc->accept_fd); 00454 00455 /* If there's no new server, stop here */ 00456 if (desc->local_address.sin_family == 0) { 00457 return; 00458 } 00459 00460 desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0); 00461 if (desc->accept_fd < 0) { 00462 ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", 00463 desc->name, strerror(errno)); 00464 return; 00465 } 00466 00467 setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); 00468 if (bind(desc->accept_fd, (struct sockaddr *) &desc->local_address, sizeof(desc->local_address))) { 00469 ast_log(LOG_ERROR, "Unable to bind %s to %s:%d: %s\n", 00470 desc->name, 00471 ast_inet_ntoa(desc->local_address.sin_addr), ntohs(desc->local_address.sin_port), 00472 strerror(errno)); 00473 goto error; 00474 } 00475 if (listen(desc->accept_fd, 10)) { 00476 ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name); 00477 goto error; 00478 } 00479 flags = fcntl(desc->accept_fd, F_GETFL); 00480 fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK); 00481 if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) { 00482 ast_log(LOG_ERROR, "Unable to launch thread for %s on %s:%d: %s\n", 00483 desc->name, 00484 ast_inet_ntoa(desc->local_address.sin_addr), ntohs(desc->local_address.sin_port), 00485 strerror(errno)); 00486 goto error; 00487 } 00488 return; 00489 00490 error: 00491 close(desc->accept_fd); 00492 desc->accept_fd = -1; 00493 }
| void ast_tcptls_server_stop | ( | struct ast_tcptls_session_args * | desc | ) |
Shutdown a running server if there is one.
Definition at line 495 of file tcptls.c.
References ast_tcptls_session_args::accept_fd, AST_PTHREADT_NULL, and ast_tcptls_session_args::master.
Referenced by unload_module().
| 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, and ast_tcptls_session_instance::ssl.
Referenced by _sip_tcp_helper_thread().
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_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.
Definition at line 128 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, 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().
00129 { 00130 struct ast_tcptls_session_instance *tcptls_session = data; 00131 #ifdef DO_SSL 00132 int (*ssl_setup)(SSL *) = (tcptls_session->client) ? SSL_connect : SSL_accept; 00133 int ret; 00134 char err[256]; 00135 #endif 00136 00137 /* 00138 * open a FILE * as appropriate. 00139 */ 00140 if (!tcptls_session->parent->tls_cfg) { 00141 tcptls_session->f = fdopen(tcptls_session->fd, "w+"); 00142 setvbuf(tcptls_session->f, NULL, _IONBF, 0); 00143 } 00144 #ifdef DO_SSL 00145 else if ( (tcptls_session->ssl = SSL_new(tcptls_session->parent->tls_cfg->ssl_ctx)) ) { 00146 SSL_set_fd(tcptls_session->ssl, tcptls_session->fd); 00147 if ((ret = ssl_setup(tcptls_session->ssl)) <= 0) { 00148 ast_verb(2, "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err)); 00149 } else { 00150 #if defined(HAVE_FUNOPEN) /* the BSD interface */ 00151 tcptls_session->f = funopen(tcptls_session->ssl, ssl_read, ssl_write, NULL, ssl_close); 00152 00153 #elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */ 00154 static const cookie_io_functions_t cookie_funcs = { 00155 ssl_read, ssl_write, NULL, ssl_close 00156 }; 00157 tcptls_session->f = fopencookie(tcptls_session->ssl, "w+", cookie_funcs); 00158 #else 00159 /* could add other methods here */ 00160 ast_debug(2, "no tcptls_session->f methods attempted!"); 00161 #endif 00162 if ((tcptls_session->client && !ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER)) 00163 || (!tcptls_session->client && ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_VERIFY_CLIENT))) { 00164 X509 *peer; 00165 long res; 00166 peer = SSL_get_peer_certificate(tcptls_session->ssl); 00167 if (!peer) 00168 ast_log(LOG_WARNING, "No peer SSL certificate\n"); 00169 res = SSL_get_verify_result(tcptls_session->ssl); 00170 if (res != X509_V_OK) 00171 ast_log(LOG_ERROR, "Certificate did not verify: %s\n", X509_verify_cert_error_string(res)); 00172 if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) { 00173 ASN1_STRING *str; 00174 unsigned char *str2; 00175 X509_NAME *name = X509_get_subject_name(peer); 00176 int pos = -1; 00177 int found = 0; 00178 00179 for (;;) { 00180 /* Walk the certificate to check all available "Common Name" */ 00181 /* XXX Probably should do a gethostbyname on the hostname and compare that as well */ 00182 pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos); 00183 if (pos < 0) 00184 break; 00185 str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos)); 00186 ASN1_STRING_to_UTF8(&str2, str); 00187 if (str2) { 00188 if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) 00189 found = 1; 00190 ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2); 00191 OPENSSL_free(str2); 00192 } 00193 if (found) 00194 break; 00195 } 00196 if (!found) { 00197 ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname); 00198 if (peer) 00199 X509_free(peer); 00200 close(tcptls_session->fd); 00201 fclose(tcptls_session->f); 00202 ao2_ref(tcptls_session, -1); 00203 return NULL; 00204 } 00205 } 00206 if (peer) 00207 X509_free(peer); 00208 } 00209 } 00210 if (!tcptls_session->f) /* no success opening descriptor stacking */ 00211 SSL_free(tcptls_session->ssl); 00212 } 00213 #endif /* DO_SSL */ 00214 00215 if (!tcptls_session->f) { 00216 close(tcptls_session->fd); 00217 ast_log(LOG_WARNING, "FILE * open failed!\n"); 00218 #ifndef DO_SSL 00219 if (tcptls_session->parent->tls_cfg) { 00220 ast_log(LOG_WARNING, "Attempted a TLS connection without OpenSSL support. This will not work!\n"); 00221 } 00222 #endif 00223 ao2_ref(tcptls_session, -1); 00224 return NULL; 00225 } 00226 00227 if (tcptls_session && tcptls_session->parent->worker_fn) 00228 return tcptls_session->parent->worker_fn(tcptls_session); 00229 else 00230 return tcptls_session; 00231 }
| 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_create(), and ast_tcptls_server_root().
00116 { 00117 struct ast_tcptls_session_instance *i = obj; 00118 ast_mutex_destroy(&i->lock); 00119 }
1.6.1