diff --git a/configs/manager.conf.sample b/configs/manager.conf.sample index ee1b063dab6e60c548e55ad9c26183f604dd4433..660ab843e085187f13afb64580848b9745081619 100644 --- a/configs/manager.conf.sample +++ b/configs/manager.conf.sample @@ -26,6 +26,18 @@ enabled = no port = 5038 ;httptimeout = 60 bindaddr = 0.0.0.0 + +; Parameters that control AMI over TLS. ("enabled" must be set too). +; You can open a connection to this socket with e.g. +; +; openssl s_client -connect my_host:5039 +; +; sslenable=no ; set to YES to enable it +; sslbindport=5039 ; the port to bind to +; sslbindaddr=0.0.0.0 ; address to bind to, default to bindaddr +; sslcert=/tmp/asterisk.pem ; path to the certificate. + + ;displayconnects = yes ; ; Add a Unix epoch timestamp to events (not action responses) diff --git a/include/asterisk/http.h b/include/asterisk/http.h index f14edd88309999a420228d084e41f3261705efde..bfd39c03967646fe0d5876688254012e2d55f60a 100644 --- a/include/asterisk/http.h +++ b/include/asterisk/http.h @@ -60,9 +60,21 @@ #include <openssl/ssl.h> #include <openssl/err.h> #else -typedef struct {} SSL; /* so we can define a pointer to it */ +/* declare dummy types so we can define a pointer to them */ +typedef struct {} SSL; +typedef struct {} SSL_CTX; #endif /* DO_SSL */ +/* SSL support */ +#define AST_CERTFILE "asterisk.pem" + +struct tls_config { + int enabled; + char *certfile; + char *cipher; + SSL_CTX *ssl_ctx; +}; + /*! * The following code implements a generic mechanism for starting * services on a TCP or TLS socket. @@ -111,7 +123,7 @@ struct server_instance { struct server_args { struct sockaddr_in sin; struct sockaddr_in oldsin; - int is_ssl; /* is this an SSL accept ? */ + struct tls_config *tls_cfg; /* points to the SSL configuration if any */ int accept_fd; int poll_timeout; pthread_t master; @@ -123,7 +135,7 @@ struct server_args { void *server_root(void *); void server_start(struct server_args *desc); -int ssl_setup(void); +int ssl_setup(struct tls_config *cfg); /*! \brief HTTP Callbacks take the socket, the method and the path as arguments and should return the content, allocated with malloc(). Status should be changed to reflect diff --git a/main/http.c b/main/http.c index ba0f5f5e2d8c7cb2355edc7df8a27a62bf6023e5..96e0bd82b3c58de752ca922737e9358bf06de7fb 100644 --- a/main/http.c +++ b/main/http.c @@ -73,26 +73,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") * * We declare most of ssl support variables unconditionally, * because their number is small and this simplifies the code. - * - * NOTE: the ssl-support variables (ssl_ctx, do_ssl, certfile, cipher) - * and their setup should be moved to a more central place, e.g. asterisk.conf - * and the source files that processes it. Similarly, ssl_setup() should - * be run earlier in the startup process so modules have it available. */ #if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE)) #define DO_SSL /* comment in/out if you want to support ssl */ #endif -#ifdef DO_SSL -static SSL_CTX* ssl_ctx; -#endif /* DO_SSL */ - -/* SSL support */ -#define AST_CERTFILE "asterisk.pem" -static int do_ssl; -static char *certfile; -static char *cipher; +static struct tls_config http_tls_cfg; static void *httpd_helper_thread(void *arg); @@ -102,7 +89,7 @@ static void *httpd_helper_thread(void *arg); static struct server_args http_desc = { .accept_fd = -1, .master = AST_PTHREADT_NULL, - .is_ssl = 0, + .tls_cfg = NULL, .poll_timeout = -1, .name = "http server", .accept_fn = server_root, @@ -112,7 +99,7 @@ static struct server_args http_desc = { static struct server_args https_desc = { .accept_fd = -1, .master = AST_PTHREADT_NULL, - .is_ssl = 1, + .tls_cfg = &http_tls_cfg, .poll_timeout = -1, .name = "https server", .accept_fn = server_root, @@ -250,7 +237,7 @@ static char *httpstatus_callback(struct sockaddr_in *req, const char *uri, struc ast_inet_ntoa(http_desc.oldsin.sin_addr)); ast_build_string(&c, &reslen, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n", ntohs(http_desc.oldsin.sin_port)); - if (do_ssl) + if (http_tls_cfg.enabled) ast_build_string(&c, &reslen, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n", ntohs(https_desc.oldsin.sin_port)); ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n"); @@ -482,10 +469,10 @@ static void *make_file_from_fd(void *data) /* * open a FILE * as appropriate. */ - if (!ser->parent->is_ssl) + if (!ser->parent->tls_cfg) ser->f = fdopen(ser->fd, "w+"); #ifdef DO_SSL - else if ( (ser->ssl = SSL_new(ssl_ctx)) ) { + else if ( (ser->ssl = SSL_new(ser->parent->tls_cfg->ssl_ctx)) ) { SSL_set_fd(ser->ssl, ser->fd); if (SSL_accept(ser->ssl) == 0) ast_verbose(" error setting up ssl connection"); @@ -702,32 +689,32 @@ char *ast_http_setcookie(const char *var, const char *val, int expires, char *bu return buf; } -int ssl_setup(void) +int ssl_setup(struct tls_config *cfg) { #ifndef DO_SSL - do_ssl = 0; + cfg->enabled = 0; return 0; #else - if (!do_ssl) + if (!cfg->enabled) return 0; SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); - ssl_ctx = SSL_CTX_new( SSLv23_server_method() ); - if (!ast_strlen_zero(certfile)) { - if (SSL_CTX_use_certificate_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) == 0 || - SSL_CTX_use_PrivateKey_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) == 0 || - SSL_CTX_check_private_key(ssl_ctx) == 0 ) { - ast_verbose("ssl cert error <%s>", certfile); + cfg->ssl_ctx = SSL_CTX_new( SSLv23_server_method() ); + if (!ast_strlen_zero(cfg->certfile)) { + if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 || + SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 || + SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 ) { + ast_verbose("ssl cert error <%s>", cfg->certfile); sleep(2); - do_ssl = 0; + cfg->enabled = 0; return 0; } } - if (!ast_strlen_zero(cipher)) { - if (SSL_CTX_set_cipher_list(ssl_ctx, cipher) == 0 ) { - ast_verbose("ssl cipher error <%s>", cipher); + if (!ast_strlen_zero(cfg->cipher)) { + if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) { + ast_verbose("ssl cipher error <%s>", cfg->cipher); sleep(2); - do_ssl = 0; + cfg->enabled = 0; return 0; } } @@ -824,13 +811,13 @@ static int __ast_http_load(int reload) strcpy(newprefix, DEFAULT_PREFIX); cfg = ast_config_load("http.conf"); - do_ssl = 0; - if (certfile) - free(certfile); - certfile = ast_strdup(AST_CERTFILE); - if (cipher) - free(cipher); - cipher = ast_strdup(""); + http_tls_cfg.enabled = 0; + if (http_tls_cfg.certfile) + free(http_tls_cfg.certfile); + http_tls_cfg.certfile = ast_strdup(AST_CERTFILE); + if (http_tls_cfg.cipher) + free(http_tls_cfg.cipher); + http_tls_cfg.cipher = ast_strdup(""); if (cfg) { v = ast_variable_browse(cfg, "general"); @@ -838,15 +825,15 @@ static int __ast_http_load(int reload) if (!strcasecmp(v->name, "enabled")) enabled = ast_true(v->value); else if (!strcasecmp(v->name, "sslenable")) - do_ssl = ast_true(v->value); + http_tls_cfg.enabled = ast_true(v->value); else if (!strcasecmp(v->name, "sslbindport")) https_desc.sin.sin_port = htons(atoi(v->value)); else if (!strcasecmp(v->name, "sslcert")) { - free(certfile); - certfile = ast_strdup(v->value); + free(http_tls_cfg.certfile); + http_tls_cfg.certfile = ast_strdup(v->value); } else if (!strcasecmp(v->name, "sslcipher")) { - free(cipher); - cipher = ast_strdup(v->value); + free(http_tls_cfg.cipher); + http_tls_cfg.cipher = ast_strdup(v->value); } else if (!strcasecmp(v->name, "enablestatic")) newenablestatic = ast_true(v->value); @@ -886,7 +873,7 @@ static int __ast_http_load(int reload) ast_copy_string(prefix, newprefix, sizeof(prefix)); enablestatic = newenablestatic; server_start(&http_desc); - if (ssl_setup()) + if (ssl_setup(https_desc.tls_cfg)) server_start(&https_desc); return 0; } @@ -904,7 +891,7 @@ static int handle_show_http(int fd, int argc, char *argv[]) ast_cli(fd, "Server Enabled and Bound to %s:%d\n\n", ast_inet_ntoa(http_desc.oldsin.sin_addr), ntohs(http_desc.oldsin.sin_port)); - if (do_ssl) + if (http_tls_cfg.enabled) ast_cli(fd, "HTTPS Server Enabled and Bound to %s:%d\n\n", ast_inet_ntoa(https_desc.oldsin.sin_addr), ntohs(https_desc.oldsin.sin_port)); diff --git a/main/manager.c b/main/manager.c index cdb43d3e23cb84e252162cca44a9463743d6c88d..269f708d49aea985dc0dd07e18744ede69c02158 100644 --- a/main/manager.c +++ b/main/manager.c @@ -2079,12 +2079,17 @@ static int get_input(struct mansession *s, char *output) * Look for \r\n within the buffer. If found, copy to the output * buffer and return, trimming the \r\n (not used afterwards). */ - for (x = 1; x < s->inlen; x++) { - if (src[x] != '\n' || src[x-1] != '\r') + for (x = 0; x < s->inlen; x++) { + int cr; /* set if we have \r */ + if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n') + cr = 2; /* Found. Update length to include \r\n */ + else if (src[x] == '\n') + cr = 1; /* also accept \n only */ + else continue; - x++; /* Found. Update length to include \r\n */ - memmove(output, src, x-2); /*... but trim \r\n */ - output[x-2] = '\0'; /* terminate the string */ + memmove(output, src, x); /*... but trim \r\n */ + output[x] = '\0'; /* terminate the string */ + x += cr; /* number of bytes used */ s->inlen -= x; /* remaining size */ memmove(src, src + x, s->inlen); /* remove used bytes */ return 1; @@ -2871,10 +2876,11 @@ static void purge_old_stuff(void *data) purge_events(); } +struct tls_config ami_tls_cfg; static struct server_args ami_desc = { .accept_fd = -1, .master = AST_PTHREADT_NULL, - .is_ssl = 0, + .tls_cfg = NULL, .poll_timeout = 5000, /* wake up every 5 seconds */ .periodic_fn = purge_old_stuff, .name = "AMI server", @@ -2882,6 +2888,16 @@ static struct server_args ami_desc = { .worker_fn = session_do, /* thread handling the session */ }; +static struct server_args amis_desc = { + .accept_fd = -1, + .master = AST_PTHREADT_NULL, + .tls_cfg = &ami_tls_cfg, + .poll_timeout = -1, /* the other does the periodic cleanup */ + .name = "AMI TLS server", + .accept_fn = server_root, /* thread doing the accept() */ + .worker_fn = session_do, /* thread handling the session */ +}; + int init_manager(void) { struct ast_config *cfg = NULL; @@ -2890,6 +2906,9 @@ int init_manager(void) int webenabled = 0; int enabled = 0; int newhttptimeout = 60; + int have_sslbindaddr = 0; + struct hostent *hp; + struct ast_hostent ahp; struct ast_manager_user *user = NULL; if (!registered) { @@ -2930,6 +2949,42 @@ int init_manager(void) ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf. Call management disabled.\n"); return 0; } + + /* default values */ + memset(&amis_desc.sin, 0, sizeof(amis_desc.sin)); + amis_desc.sin.sin_port = htons(5039); + + ami_tls_cfg.enabled = 0; + if (ami_tls_cfg.certfile) + free(ami_tls_cfg.certfile); + ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE); + if (ami_tls_cfg.cipher) + free(ami_tls_cfg.cipher); + ami_tls_cfg.cipher = ast_strdup(""); + + /* XXX change this into a loop on ast_variable_browse(cfg, "general"); */ + + if ((val = ast_variable_retrieve(cfg, "general", "sslenable"))) + ami_tls_cfg.enabled = ast_true(val); + if ((val = ast_variable_retrieve(cfg, "general", "sslbindport"))) + amis_desc.sin.sin_port = htons(atoi(val)); + if ((val = ast_variable_retrieve(cfg, "general", "sslbindaddr"))) { + if ((hp = ast_gethostbyname(val, &ahp))) { + memcpy(&amis_desc.sin.sin_addr, hp->h_addr, sizeof(amis_desc.sin.sin_addr)); + have_sslbindaddr = 1; + } else { + ast_log(LOG_WARNING, "Invalid bind address '%s'\n", val); + } + } + if ((val = ast_variable_retrieve(cfg, "general", "sslcert"))) { + free(ami_tls_cfg.certfile); + ami_tls_cfg.certfile = ast_strdup(val); + } + if ((val = ast_variable_retrieve(cfg, "general", "sslcipher"))) { + free(ami_tls_cfg.cipher); + ami_tls_cfg.cipher = ast_strdup(val); + } + val = ast_variable_retrieve(cfg, "general", "enabled"); if (val) enabled = ast_true(val); @@ -2972,7 +3027,12 @@ int init_manager(void) memset(&ami_desc.sin.sin_addr, 0, sizeof(ami_desc.sin.sin_addr)); } } + if (!have_sslbindaddr) + amis_desc.sin.sin_addr = ami_desc.sin.sin_addr; + if (ami_tls_cfg.enabled) + amis_desc.sin.sin_family = AF_INET; + AST_LIST_LOCK(&users); while ((cat = ast_category_browse(cfg, cat))) { @@ -3073,6 +3133,8 @@ int init_manager(void) httptimeout = newhttptimeout; server_start(&ami_desc); + if (ssl_setup(amis_desc.tls_cfg)) + server_start(&amis_desc); return 0; }