[asterisk-dev] https support now in trunk (please read)
Luigi Rizzo
rizzo at icir.org
Sun Oct 22 05:14:43 MST 2006
as the subject says...
read below for the details - if someone autoconf expert
can suggest how to do steps 0 1 2 i am all ears...
I suppose step 0 and 1 just require
0. AC_CHECK_FUNCS([funopen]) in configure.ac
1. and check for HAVE_FUNOPEN instead of setting DO_SSL,
2. adding $(SSL_LIB) to AST_LIBS in main/Makefile
is that correct ?
cheers
luigi
On Sun, Oct 22, 2006 at 12:02:36PM -0000, asterisk-commits at lists.digium.com wrote:
> Author: rizzo
> Date: Sun Oct 22 07:02:35 2006
> New Revision: 45869
>
> URL: http://svn.digium.com/view/asterisk?rev=45869&view=rev
> Log:
> Implement https support.
>
> The changes are not large. Most of the diff comes from putting the
> global variables describing an accept session into a structure, so
> we can reuse the existing code for running multiple accept threads
> on different ports.
>
> Once this is done, and if your system has the funopen() library
> function (and ssl, of course), it is just a matter of calling
> the appropriate functions to set up the ssl connection on the
> existing socket, and everything works on the secure channel now.
>
> At the moment, the code is disabled because i have not implemented yet
> the autoconf code to detect the presence of funopen(), and add -lssl
> to main/Makefile if ssl libraries are present. And a bit of documentation
> on the http.conf arguments, too.
>
>
> If you want to manually enable https support, that is very simple
> (step 0 1 2 will be eventually detected by ./configure, the
> rest is something you will have to do anyways).
>
> 0. make sure your system has funopen(3). FreeBSD does, linux probably
> does too, not sure about other systems.
>
> 1. uncomment the following line in main/http.c
> // #define DO_SSL /* comment in/out if you want to support ssl */
>
> 2. add -lssl to AST_LIBS in main/Makefile
>
> 3. add the following options to http.conf
>
> sslenable=yes
> sslbindport=4433 ; pick one you like
> sslcert=/tmp/foo.pem ; path to your certificate file.
>
> 4. generate a suitable certificate e.g. (example from mini_httpd's Makefile:
>
> openssl req -new -x509 -days 365 -nodes -out /tmp/foo.pem -keyout /tmp/foo.pem
>
> and here you go:
>
> https://localhost:4433/asterisk/manager
>
> now works.
>
>
> Modified:
> trunk/main/http.c
>
> Modified: trunk/main/http.c
> URL: http://svn.digium.com/view/asterisk/trunk/main/http.c?rev=45869&r1=45868&r2=45869&view=diff
> ==============================================================================
> --- trunk/main/http.c (original)
> +++ trunk/main/http.c Sun Oct 22 07:02:35 2006
> @@ -57,21 +57,73 @@
> #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;
> -
> -static int httpfd = -1;
> -static pthread_t master = AST_PTHREADT_NULL;
> +/*!
> + * 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 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 @@
>
> 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,14 +427,72 @@
> 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 @@
>
> 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 @@
> 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 @@
> return buf;
> }
>
> -
> -static void http_server_start(struct sockaddr_in *sin)
> +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 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 (httpfd != -1)
> - close(httpfd);
> + if (desc->master != AST_PTHREADT_NULL) {
> + pthread_cancel(desc->master);
> + pthread_kill(desc->master, SIGURG);
> + pthread_join(desc->master, NULL);
> + }
> +
> + 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;
> + goto error;
> + }
> + if (listen(desc->accept_fd, 10)) {
> + ast_log(LOG_NOTICE, "Unable to listen!\n");
> + close(desc->accept_fd);
> + desc->accept_fd = -1;
> return;
> }
> - if (listen(httpfd, 10)) {
> - ast_log(LOG_NOTICE, "Unable to listen!\n");
> - close(httpfd);
> - httpfd = -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 @@
> 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 @@
> 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 @@
> return RESULT_SHOWUSAGE;
> ast_cli(fd, "HTTP Server Status:\n");
> ast_cli(fd, "Prefix: %s\n", prefix);
> - if (oldsin.sin_family)
> + 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(oldsin.sin_addr),
> - ntohs(oldsin.sin_port));
> - else
> - ast_cli(fd, "Server Disabled\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){
>
> _______________________________________________
> --Bandwidth and Colocation provided by Easynews.com --
>
> asterisk-commits mailing list
> To UNSUBSCRIBE or update options visit:
> http://lists.digium.com/mailman/listinfo/asterisk-commits
More information about the asterisk-dev
mailing list