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 279 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().
00280 { 00281 #ifndef DO_SSL 00282 cfg->enabled = 0; 00283 return 0; 00284 #else 00285 if (!cfg->enabled) 00286 return 0; 00287 00288 SSL_load_error_strings(); 00289 SSLeay_add_ssl_algorithms(); 00290 00291 if (!(cfg->ssl_ctx = SSL_CTX_new( client ? SSLv23_client_method() : SSLv23_server_method() ))) { 00292 ast_debug(1, "Sorry, SSL_CTX_new call returned null...\n"); 00293 cfg->enabled = 0; 00294 return 0; 00295 } 00296 if (!ast_strlen_zero(cfg->certfile)) { 00297 if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 || 00298 SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 || 00299 SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 ) { 00300 if (!client) { 00301 /* Clients don't need a certificate, but if its setup we can use it */ 00302 ast_verb(0, "SSL cert error <%s>", cfg->certfile); 00303 sleep(2); 00304 cfg->enabled = 0; 00305 return 0; 00306 } 00307 } 00308 } 00309 if (!ast_strlen_zero(cfg->cipher)) { 00310 if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) { 00311 if (!client) { 00312 ast_verb(0, "SSL cipher error <%s>", cfg->cipher); 00313 sleep(2); 00314 cfg->enabled = 0; 00315 return 0; 00316 } 00317 } 00318 } 00319 if (!ast_strlen_zero(cfg->cafile) || !ast_strlen_zero(cfg->capath)) { 00320 if (SSL_CTX_load_verify_locations(cfg->ssl_ctx, S_OR(cfg->cafile, NULL), S_OR(cfg->capath,NULL)) == 0) 00321 ast_verb(0, "SSL CA file(%s)/path(%s) error\n", cfg->cafile, cfg->capath); 00322 } 00323 00324 ast_verb(0, "SSL certificate ok\n"); 00325 return 1; 00326 #endif 00327 }
| int ast_ssl_setup | ( | struct ast_tls_config * | cfg | ) |
Definition at line 329 of file tcptls.c.
References __ssl_setup().
Referenced by __ast_http_load(), __init_manager(), and reload_config().
00330 { 00331 return __ssl_setup(cfg, 0); 00332 }
| struct ast_tcptls_session_instance* ast_tcptls_client_create | ( | struct ast_tcptls_session_args * | desc | ) | [read] |
Definition at line 371 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().
00372 { 00373 int x = 1; 00374 struct ast_tcptls_session_instance *tcptls_session = NULL; 00375 00376 /* Do nothing if nothing has changed */ 00377 if (!memcmp(&desc->old_address, &desc->remote_address, sizeof(desc->old_address))) { 00378 ast_debug(1, "Nothing changed in %s\n", desc->name); 00379 return NULL; 00380 } 00381 00382 desc->old_address = desc->remote_address; 00383 00384 if (desc->accept_fd != -1) 00385 close(desc->accept_fd); 00386 00387 desc->accept_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 00388 if (desc->accept_fd < 0) { 00389 ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n", 00390 desc->name, strerror(errno)); 00391 return NULL; 00392 } 00393 00394 /* if a local address was specified, bind to it so the connection will 00395 originate from the desired address */ 00396 if (desc->local_address.sin_family != 0) { 00397 setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); 00398 if (bind(desc->accept_fd, (struct sockaddr *) &desc->local_address, sizeof(desc->local_address))) { 00399 ast_log(LOG_ERROR, "Unable to bind %s to %s:%d: %s\n", 00400 desc->name, 00401 ast_inet_ntoa(desc->local_address.sin_addr), ntohs(desc->local_address.sin_port), 00402 strerror(errno)); 00403 goto error; 00404 } 00405 } 00406 00407 if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor))) 00408 goto error; 00409 00410 ast_mutex_init(&tcptls_session->lock); 00411 tcptls_session->client = 1; 00412 tcptls_session->fd = desc->accept_fd; 00413 tcptls_session->parent = desc; 00414 tcptls_session->parent->worker_fn = NULL; 00415 memcpy(&tcptls_session->remote_address, &desc->remote_address, sizeof(tcptls_session->remote_address)); 00416 00417 return tcptls_session; 00418 00419 error: 00420 close(desc->accept_fd); 00421 desc->accept_fd = -1; 00422 if (tcptls_session) 00423 ao2_ref(tcptls_session, -1); 00424 return NULL; 00425 }
| 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 334 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().
00335 { 00336 struct ast_tcptls_session_args *desc; 00337 int flags; 00338 00339 if (!(desc = tcptls_session->parent)) { 00340 goto client_start_error; 00341 } 00342 00343 if (connect(desc->accept_fd, (const struct sockaddr *) &desc->remote_address, sizeof(desc->remote_address))) { 00344 ast_log(LOG_ERROR, "Unable to connect %s to %s:%d: %s\n", 00345 desc->name, 00346 ast_inet_ntoa(desc->remote_address.sin_addr), ntohs(desc->remote_address.sin_port), 00347 strerror(errno)); 00348 goto client_start_error; 00349 } 00350 00351 flags = fcntl(desc->accept_fd, F_GETFL); 00352 fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK); 00353 00354 if (desc->tls_cfg) { 00355 desc->tls_cfg->enabled = 1; 00356 __ssl_setup(desc->tls_cfg, 1); 00357 } 00358 00359 return handle_tcptls_connection(tcptls_session); 00360 00361 client_start_error: 00362 close(desc->accept_fd); 00363 desc->accept_fd = -1; 00364 if (tcptls_session) { 00365 ao2_ref(tcptls_session, -1); 00366 } 00367 return NULL; 00368 00369 }
| 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 228 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().
00229 { 00230 struct ast_tcptls_session_args *desc = data; 00231 int fd; 00232 struct sockaddr_in sin; 00233 socklen_t sinlen; 00234 struct ast_tcptls_session_instance *tcptls_session; 00235 pthread_t launched; 00236 00237 for (;;) { 00238 int i, flags; 00239 00240 if (desc->periodic_fn) 00241 desc->periodic_fn(desc); 00242 i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout); 00243 if (i <= 0) 00244 continue; 00245 sinlen = sizeof(sin); 00246 fd = accept(desc->accept_fd, (struct sockaddr *) &sin, &sinlen); 00247 if (fd < 0) { 00248 if ((errno != EAGAIN) && (errno != EINTR)) 00249 ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno)); 00250 continue; 00251 } 00252 tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor); 00253 if (!tcptls_session) { 00254 ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno)); 00255 close(fd); 00256 continue; 00257 } 00258 00259 ast_mutex_init(&tcptls_session->lock); 00260 00261 flags = fcntl(fd, F_GETFL); 00262 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); 00263 tcptls_session->fd = fd; 00264 tcptls_session->parent = desc; 00265 memcpy(&tcptls_session->remote_address, &sin, sizeof(tcptls_session->remote_address)); 00266 00267 tcptls_session->client = 0; 00268 00269 /* This thread is now the only place that controls the single ref to tcptls_session */ 00270 if (ast_pthread_create_detached_background(&launched, NULL, handle_tcptls_connection, tcptls_session)) { 00271 ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno)); 00272 close(tcptls_session->fd); 00273 ao2_ref(tcptls_session, -1); 00274 } 00275 } 00276 return NULL; 00277 }
| 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 427 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().
00428 { 00429 int flags; 00430 int x = 1; 00431 00432 /* Do nothing if nothing has changed */ 00433 if (!memcmp(&desc->old_address, &desc->local_address, sizeof(desc->old_address))) { 00434 ast_debug(1, "Nothing changed in %s\n", desc->name); 00435 return; 00436 } 00437 00438 desc->old_address = desc->local_address; 00439 00440 /* Shutdown a running server if there is one */ 00441 if (desc->master != AST_PTHREADT_NULL) { 00442 pthread_cancel(desc->master); 00443 pthread_kill(desc->master, SIGURG); 00444 pthread_join(desc->master, NULL); 00445 } 00446 00447 if (desc->accept_fd != -1) 00448 close(desc->accept_fd); 00449 00450 /* If there's no new server, stop here */ 00451 if (desc->local_address.sin_family == 0) { 00452 return; 00453 } 00454 00455 desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0); 00456 if (desc->accept_fd < 0) { 00457 ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", 00458 desc->name, strerror(errno)); 00459 return; 00460 } 00461 00462 setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); 00463 if (bind(desc->accept_fd, (struct sockaddr *) &desc->local_address, sizeof(desc->local_address))) { 00464 ast_log(LOG_ERROR, "Unable to bind %s to %s:%d: %s\n", 00465 desc->name, 00466 ast_inet_ntoa(desc->local_address.sin_addr), ntohs(desc->local_address.sin_port), 00467 strerror(errno)); 00468 goto error; 00469 } 00470 if (listen(desc->accept_fd, 10)) { 00471 ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name); 00472 goto error; 00473 } 00474 flags = fcntl(desc->accept_fd, F_GETFL); 00475 fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK); 00476 if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) { 00477 ast_log(LOG_ERROR, "Unable to launch thread for %s on %s:%d: %s\n", 00478 desc->name, 00479 ast_inet_ntoa(desc->local_address.sin_addr), ntohs(desc->local_address.sin_port), 00480 strerror(errno)); 00481 goto error; 00482 } 00483 return; 00484 00485 error: 00486 close(desc->accept_fd); 00487 desc->accept_fd = -1; 00488 }
| void ast_tcptls_server_stop | ( | struct ast_tcptls_session_args * | desc | ) |
Shutdown a running server if there is one.
Definition at line 490 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 ao2_ref(tcptls_session, -1); 00219 return NULL; 00220 } 00221 00222 if (tcptls_session && tcptls_session->parent->worker_fn) 00223 return tcptls_session->parent->worker_fn(tcptls_session); 00224 else 00225 return tcptls_session; 00226 }
| 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