#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"
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) |
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) |
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) |
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 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 378 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, ssl_close(), ast_tls_config::ssl_ctx, ssl_read(), ssl_write(), str, server_args::tls_cfg, and server_args::worker_fn.
Referenced by ast_tcptls_client_start(), and ast_tcptls_server_root().
00379 { 00380 struct ast_tcptls_session_instance *tcptls_session = data; 00381 #ifdef DO_SSL 00382 int (*ssl_setup)(SSL *) = (tcptls_session->client) ? SSL_connect : SSL_accept; 00383 int ret; 00384 char err[256]; 00385 #endif 00386 00387 /* 00388 * open a FILE * as appropriate. 00389 */ 00390 if (!tcptls_session->parent->tls_cfg) { 00391 tcptls_session->f = fdopen(tcptls_session->fd, "w+"); 00392 setvbuf(tcptls_session->f, NULL, _IONBF, 0); 00393 } 00394 #ifdef DO_SSL 00395 else if ( (tcptls_session->ssl = SSL_new(tcptls_session->parent->tls_cfg->ssl_ctx)) ) { 00396 SSL_set_fd(tcptls_session->ssl, tcptls_session->fd); 00397 if ((ret = ssl_setup(tcptls_session->ssl)) <= 0) { 00398 ast_verb(2, "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err)); 00399 } else { 00400 #if defined(HAVE_FUNOPEN) /* the BSD interface */ 00401 tcptls_session->f = funopen(tcptls_session->ssl, ssl_read, ssl_write, NULL, ssl_close); 00402 00403 #elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */ 00404 static const cookie_io_functions_t cookie_funcs = { 00405 ssl_read, ssl_write, NULL, ssl_close 00406 }; 00407 tcptls_session->f = fopencookie(tcptls_session->ssl, "w+", cookie_funcs); 00408 #else 00409 /* could add other methods here */ 00410 ast_debug(2, "no tcptls_session->f methods attempted!"); 00411 #endif 00412 if ((tcptls_session->client && !ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER)) 00413 || (!tcptls_session->client && ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_VERIFY_CLIENT))) { 00414 X509 *peer; 00415 long res; 00416 peer = SSL_get_peer_certificate(tcptls_session->ssl); 00417 if (!peer) 00418 ast_log(LOG_WARNING, "No peer SSL certificate\n"); 00419 res = SSL_get_verify_result(tcptls_session->ssl); 00420 if (res != X509_V_OK) 00421 ast_log(LOG_ERROR, "Certificate did not verify: %s\n", X509_verify_cert_error_string(res)); 00422 if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) { 00423 ASN1_STRING *str; 00424 unsigned char *str2; 00425 X509_NAME *name = X509_get_subject_name(peer); 00426 int pos = -1; 00427 int found = 0; 00428 00429 for (;;) { 00430 /* Walk the certificate to check all available "Common Name" */ 00431 /* XXX Probably should do a gethostbyname on the hostname and compare that as well */ 00432 pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos); 00433 if (pos < 0) 00434 break; 00435 str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos)); 00436 ASN1_STRING_to_UTF8(&str2, str); 00437 if (str2) { 00438 if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) 00439 found = 1; 00440 ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2); 00441 OPENSSL_free(str2); 00442 } 00443 if (found) 00444 break; 00445 } 00446 if (!found) { 00447 ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname); 00448 if (peer) 00449 X509_free(peer); 00450 fclose(tcptls_session->f); 00451 return NULL; 00452 } 00453 } 00454 if (peer) 00455 X509_free(peer); 00456 } 00457 } 00458 if (!tcptls_session->f) /* no success opening descriptor stacking */ 00459 SSL_free(tcptls_session->ssl); 00460 } 00461 #endif /* DO_SSL */ 00462 00463 if (!tcptls_session->f) { 00464 close(tcptls_session->fd); 00465 ast_log(LOG_WARNING, "FILE * open failed!\n"); 00466 ao2_ref(tcptls_session, -1); 00467 return NULL; 00468 } 00469 00470 if (tcptls_session && tcptls_session->parent->worker_fn) 00471 return tcptls_session->parent->worker_fn(tcptls_session); 00472 else 00473 return tcptls_session; 00474 }
int ast_ssl_setup | ( | struct ast_tls_config * | cfg | ) |
Definition at line 220 of file tcptls.c.
References __ssl_setup().
00221 { 00222 return __ssl_setup(cfg, 0); 00223 }
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().
Definition at line 228 of file tcptls.c.
References __ssl_setup(), ao2_alloc(), ao2_ref(), ast_debug, ast_inet_ntoa(), ast_log(), ast_make_file_from_fd(), ast_mutex_init(), desc, errno, LOG_ERROR, LOG_WARNING, and session_instance_destructor().
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 ao2_ref(tcptls_session, +1); 00280 if (!ast_make_file_from_fd(tcptls_session)) 00281 goto error; 00282 00283 return tcptls_session; 00284 00285 error: 00286 close(desc->accept_fd); 00287 desc->accept_fd = -1; 00288 if (tcptls_session) 00289 ao2_ref(tcptls_session, -1); 00290 return NULL; 00291 }
HOOK_T ast_tcptls_server_read | ( | struct ast_tcptls_session_instance * | tcptls_session, | |
void * | buf, | |||
size_t | count | |||
) |
Definition at line 84 of file tcptls.c.
References ast_log(), errno, ast_tcptls_session_instance::fd, LOG_ERROR, ast_tcptls_session_instance::ssl, and ssl_read().
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 ao2_alloc(), ao2_ref(), ast_log(), ast_make_file_from_fd(), ast_mutex_init(), ast_pthread_create_detached_background, ast_wait_for_input(), desc, errno, LOG_WARNING, 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 298 of file tcptls.c.
References ast_debug, ast_inet_ntoa(), ast_log(), ast_pthread_create_background, AST_PTHREADT_NULL, desc, errno, and LOG_ERROR.
00299 { 00300 int flags; 00301 int x = 1; 00302 00303 /* Do nothing if nothing has changed */ 00304 if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) { 00305 ast_debug(1, "Nothing changed in %s\n", desc->name); 00306 return; 00307 } 00308 00309 desc->oldsin = desc->sin; 00310 00311 /* Shutdown a running server if there is one */ 00312 if (desc->master != AST_PTHREADT_NULL) { 00313 pthread_cancel(desc->master); 00314 pthread_kill(desc->master, SIGURG); 00315 pthread_join(desc->master, NULL); 00316 } 00317 00318 if (desc->accept_fd != -1) 00319 close(desc->accept_fd); 00320 00321 /* If there's no new server, stop here */ 00322 if (desc->sin.sin_family == 0) 00323 return; 00324 00325 desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0); 00326 if (desc->accept_fd < 0) { 00327 ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", 00328 desc->name, strerror(errno)); 00329 return; 00330 } 00331 00332 setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); 00333 if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) { 00334 ast_log(LOG_ERROR, "Unable to bind %s to %s:%d: %s\n", 00335 desc->name, 00336 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port), 00337 strerror(errno)); 00338 goto error; 00339 } 00340 if (listen(desc->accept_fd, 10)) { 00341 ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name); 00342 goto error; 00343 } 00344 flags = fcntl(desc->accept_fd, F_GETFL); 00345 fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK); 00346 if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) { 00347 ast_log(LOG_ERROR, "Unable to launch thread for %s on %s:%d: %s\n", 00348 desc->name, 00349 ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port), 00350 strerror(errno)); 00351 goto error; 00352 } 00353 return; 00354 00355 error: 00356 close(desc->accept_fd); 00357 desc->accept_fd = -1; 00358 }
void ast_tcptls_server_stop | ( | struct server_args * | desc | ) |
Shutdown a running server if there is one.
Definition at line 361 of file tcptls.c.
References AST_PTHREADT_NULL, and desc.
Referenced by unload_module().
00362 { 00363 if (desc->master != AST_PTHREADT_NULL) { 00364 pthread_cancel(desc->master); 00365 pthread_kill(desc->master, SIGURG); 00366 pthread_join(desc->master, NULL); 00367 } 00368 if (desc->accept_fd != -1) 00369 close(desc->accept_fd); 00370 desc->accept_fd = -1; 00371 }
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, ast_tcptls_session_instance::ssl, and ssl_write().
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 }
static int ssl_close | ( | void * | cookie | ) | [static] |
Definition at line 75 of file tcptls.c.
Referenced by ast_make_file_from_fd().
00076 { 00077 close(SSL_get_fd(cookie)); 00078 SSL_shutdown(cookie); 00079 SSL_free(cookie); 00080 return 0; 00081 }
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 53 of file tcptls.c.
References ast_verb.
Referenced by ast_make_file_from_fd(), and ast_tcptls_server_read().
00054 { 00055 int i = SSL_read(cookie, buf, len-1); 00056 #if 0 00057 if (i >= 0) 00058 buf[i] = '\0'; 00059 ast_verb(0, "ssl read size %d returns %d <%s>\n", (int)len, i, buf); 00060 #endif 00061 return i; 00062 }
static HOOK_T ssl_write | ( | void * | cookie, | |
const char * | buf, | |||
LEN_T | len | |||
) | [static] |
Definition at line 64 of file tcptls.c.
Referenced by ast_make_file_from_fd(), and ast_tcptls_server_write().
00065 { 00066 #if 0 00067 char *s = alloca(len+1); 00068 strncpy(s, buf, len); 00069 s[len] = '\0'; 00070 ast_verb(0, "ssl write size %d <%s>\n", (int)len, s); 00071 #endif 00072 return SSL_write(cookie, buf, len); 00073 }