diff options
-rw-r--r-- | main/http.c | 298 |
1 files changed, 236 insertions, 62 deletions
diff --git a/main/http.c b/main/http.c index 5bb1df269..10b855700 100644 --- a/main/http.c +++ b/main/http.c @@ -57,21 +57,73 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #define MAX_PREFIX 80 #define DEFAULT_PREFIX "/asterisk" -struct ast_http_server_instance { - FILE *f; - int fd; +/*! + * In order to have SSL support, we need the openssl libraries. + * Still we can decide whether or not to use them by commenting + * in or out the DO_SSL macro. + * We declare most of ssl support variables unconditionally, + * because their number is small and this simplifies the code. + */ +#ifdef HAVE_OPENSSL +// #define DO_SSL /* comment in/out if you want to support ssl */ +#endif + +#ifdef DO_SSL +#include <openssl/ssl.h> +#include <openssl/err.h> +SSL_CTX* ssl_ctx; +#endif /* DO_SSL */ + +/* SSL support */ +#define AST_CERTFILE "asterisk.pem" +static int do_ssl; +static char *certfile; +static char *cipher; + +/*! + * describes a server instance + */ +struct server_instance { + FILE *f; /* fopen/funopen result */ + int fd; /* the socket returned by accept() */ + int is_ssl; /* is this an ssl session ? */ +#ifdef DO_SSL + SSL *ssl; /* ssl state */ +#endif struct sockaddr_in requestor; ast_http_callback callback; }; -static struct ast_http_uri *uris; +/*! + * arguments for the accepting thread + */ +struct server_args { + struct sockaddr_in sin; + struct sockaddr_in oldsin; + int is_ssl; /* is this an SSL accept ? */ + int accept_fd; + pthread_t master; +}; + +/*! + * we have up to two accepting threads, one for http, one for https + */ +static struct server_args http_desc = { + .accept_fd = -1, + .master = AST_PTHREADT_NULL, + .is_ssl = 0, +}; + +static struct server_args https_desc = { + .accept_fd = -1, + .master = AST_PTHREADT_NULL, + .is_ssl = 1, +}; -static int httpfd = -1; -static pthread_t master = AST_PTHREADT_NULL; +static struct ast_http_uri *uris; /*!< list of supported handlers */ /* all valid URIs must be prepended by the string in prefix. */ static char prefix[MAX_PREFIX]; -static struct sockaddr_in oldsin; static int enablestatic=0; /*! \brief Limit the kinds of files we're willing to serve up */ @@ -196,9 +248,12 @@ static char *httpstatus_callback(struct sockaddr_in *req, const char *uri, struc ast_build_string(&c, &reslen, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix); ast_build_string(&c, &reslen, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n", - ast_inet_ntoa(oldsin.sin_addr)); + 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(oldsin.sin_port)); + ntohs(http_desc.oldsin.sin_port)); + if (do_ssl) + 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"); v = vars; while(v) { @@ -372,15 +427,73 @@ static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char ** return c; } +#ifdef DO_SSL +/*! + * 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 int ssl_read(void *cookie, char *buf, int len) +{ + int i; + i = SSL_read(cookie, buf, len-1); +#if 0 + if (i >= 0) + buf[i] = '\0'; + ast_verbose("ssl read size %d returns %d <%s>\n", len, i, buf); +#endif + return i; +} + +static int ssl_write(void *cookie, const char *buf, int len) +{ +#if 0 + char *s = alloca(len+1); + strncpy(s, buf, len); + s[len] = '\0'; + ast_verbose("ssl write size %d <%s>\n", len, s); +#endif + return SSL_write(cookie, buf, len); +} + +static int ssl_close(void *cookie) +{ + close(SSL_get_fd(cookie)); + SSL_shutdown(cookie); + SSL_free(cookie); + return 0; +} +#endif + static void *ast_httpd_helper_thread(void *data) { char buf[4096]; char cookie[4096]; - struct ast_http_server_instance *ser = data; + struct server_instance *ser = data; struct ast_variable *var, *prev=NULL, *vars=NULL; char *uri, *c, *title=NULL; int status = 200, contentlength = 0; +#ifdef DO_SSL + if (ser->is_ssl) { + ser->ssl = SSL_new(ssl_ctx); + SSL_set_fd(ser->ssl, ser->fd); + if (SSL_accept(ser->ssl) == 0) { + ast_verbose(" error setting up ssl connection"); + goto done; + } + ser->f = funopen(ser->ssl, ssl_read, ssl_write, NULL, ssl_close); + } else +#endif + ser->f = fdopen(ser->fd, "w+"); + + if (!ser->f) { + ast_log(LOG_WARNING, "fdopen/funopen failed!\n"); + close(ser->fd); + free(ser); + return NULL; + } + if (!fgets(buf, sizeof(buf), ser->f)) goto done; @@ -499,19 +612,20 @@ done: static void *http_root(void *data) { + struct server_args *desc = data; int fd; struct sockaddr_in sin; socklen_t sinlen; - struct ast_http_server_instance *ser; + struct server_instance *ser; pthread_t launched; pthread_attr_t attr; for (;;) { int flags; - ast_wait_for_input(httpfd, -1); + ast_wait_for_input(desc->accept_fd, -1); sinlen = sizeof(sin); - fd = accept(httpfd, (struct sockaddr *)&sin, &sinlen); + fd = accept(desc->accept_fd, (struct sockaddr *)&sin, &sinlen); if (fd < 0) { if ((errno != EAGAIN) && (errno != EINTR)) ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno)); @@ -526,18 +640,14 @@ static void *http_root(void *data) flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); ser->fd = fd; + ser->is_ssl = desc->is_ssl; memcpy(&ser->requestor, &sin, sizeof(ser->requestor)); - if ((ser->f = fdopen(ser->fd, "w+"))) { - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (ast_pthread_create_background(&launched, &attr, ast_httpd_helper_thread, ser)) { - ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno)); - fclose(ser->f); - free(ser); - } - } else { - ast_log(LOG_WARNING, "fdopen failed!\n"); + if (ast_pthread_create_background(&launched, &attr, ast_httpd_helper_thread, ser)) { + ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno)); close(ser->fd); free(ser); } @@ -556,66 +666,101 @@ char *ast_http_setcookie(const char *var, const char *val, int expires, char *bu return buf; } +static int ssl_setup(void) +{ +#ifndef DO_SSL + do_ssl = 0; + return 0; +#else + if (!do_ssl) + 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); + sleep(2); + do_ssl = 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); + sleep(2); + do_ssl = 0; + return 0; + } + } + ast_verbose("ssl cert ok"); + return 1; +#endif +} -static void http_server_start(struct sockaddr_in *sin) +static void http_server_start(struct server_args *desc) { int flags; int x = 1; /* Do nothing if nothing has changed */ - if (!memcmp(&oldsin, sin, sizeof(oldsin))) { + if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) { if (option_debug) ast_log(LOG_DEBUG, "Nothing changed in http\n"); return; } - memcpy(&oldsin, sin, sizeof(oldsin)); + desc->oldsin = desc->sin; /* Shutdown a running server if there is one */ - if (master != AST_PTHREADT_NULL) { - pthread_cancel(master); - pthread_kill(master, SIGURG); - pthread_join(master, NULL); + if (desc->master != AST_PTHREADT_NULL) { + pthread_cancel(desc->master); + pthread_kill(desc->master, SIGURG); + pthread_join(desc->master, NULL); } - if (httpfd != -1) - close(httpfd); + if (desc->accept_fd != -1) + close(desc->accept_fd); /* If there's no new server, stop here */ - if (!sin->sin_family) + if (desc->sin.sin_family == 0) return; - httpfd = socket(AF_INET, SOCK_STREAM, 0); - if (httpfd < 0) { + desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0); + if (desc->accept_fd < 0) { ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno)); return; } - setsockopt(httpfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); - if (bind(httpfd, (struct sockaddr *)sin, sizeof(*sin))) { + setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); + if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) { ast_log(LOG_NOTICE, "Unable to bind http server to %s:%d: %s\n", - ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), + ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port), strerror(errno)); - close(httpfd); - httpfd = -1; - return; + goto error; } - if (listen(httpfd, 10)) { + if (listen(desc->accept_fd, 10)) { ast_log(LOG_NOTICE, "Unable to listen!\n"); - close(httpfd); - httpfd = -1; + close(desc->accept_fd); + desc->accept_fd = -1; return; } - flags = fcntl(httpfd, F_GETFL); - fcntl(httpfd, F_SETFL, flags | O_NONBLOCK); - if (ast_pthread_create_background(&master, NULL, http_root, NULL)) { + flags = fcntl(desc->accept_fd, F_GETFL); + fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK); + if (ast_pthread_create_background(&desc->master, NULL, http_root, desc)) { ast_log(LOG_NOTICE, "Unable to launch http server on %s:%d: %s\n", - ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), + ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port), strerror(errno)); - close(httpfd); - httpfd = -1; + goto error; } + return; + +error: + close(desc->accept_fd); + desc->accept_fd = -1; } static int __ast_http_load(int reload) @@ -624,27 +769,49 @@ static int __ast_http_load(int reload) struct ast_variable *v; int enabled=0; int newenablestatic=0; - struct sockaddr_in sin; struct hostent *hp; struct ast_hostent ahp; char newprefix[MAX_PREFIX]; - memset(&sin, 0, sizeof(sin)); - sin.sin_port = htons(8088); + memset(&http_desc.sin, 0, sizeof(http_desc.sin)); + http_desc.sin.sin_port = htons(8088); + memset(&https_desc.sin, 0, sizeof(https_desc.sin)); + https_desc.sin.sin_port = htons(8089); 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(""); + if (cfg) { v = ast_variable_browse(cfg, "general"); while(v) { if (!strcasecmp(v->name, "enabled")) enabled = ast_true(v->value); + else if (!strcasecmp(v->name, "sslenable")) + do_ssl = 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); + } else if (!strcasecmp(v->name, "sslcipher")) { + free(cipher); + cipher = ast_strdup(v->value); + } else if (!strcasecmp(v->name, "enablestatic")) newenablestatic = ast_true(v->value); else if (!strcasecmp(v->name, "bindport")) - sin.sin_port = htons(atoi(v->value)); + http_desc.sin.sin_port = htons(atoi(v->value)); else if (!strcasecmp(v->name, "bindaddr")) { if ((hp = ast_gethostbyname(v->value, &ahp))) { - memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); + memcpy(&http_desc.sin.sin_addr, hp->h_addr, sizeof(http_desc.sin.sin_addr)); + memcpy(&https_desc.sin.sin_addr, hp->h_addr, sizeof(https_desc.sin.sin_addr)); } else { ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value); } @@ -662,11 +829,13 @@ static int __ast_http_load(int reload) ast_config_destroy(cfg); } if (enabled) - sin.sin_family = AF_INET; + http_desc.sin.sin_family = https_desc.sin.sin_family = AF_INET; if (strcmp(prefix, newprefix)) ast_copy_string(prefix, newprefix, sizeof(prefix)); enablestatic = newenablestatic; - http_server_start(&sin); + http_server_start(&http_desc); + if (ssl_setup()) + http_server_start(&https_desc); return 0; } @@ -677,12 +846,17 @@ static int handle_show_http(int fd, int argc, char *argv[]) return RESULT_SHOWUSAGE; ast_cli(fd, "HTTP Server Status:\n"); ast_cli(fd, "Prefix: %s\n", prefix); - if (oldsin.sin_family) - ast_cli(fd, "Server Enabled and Bound to %s:%d\n\n", - ast_inet_ntoa(oldsin.sin_addr), - ntohs(oldsin.sin_port)); - else + if (!http_desc.oldsin.sin_family) ast_cli(fd, "Server Disabled\n\n"); + else { + 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) + 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)); + } ast_cli(fd, "Enabled URI's:\n"); urih = uris; while(urih){ |