- Generalize the function ssl_setup() so that the certificate info
authorLuigi Rizzo <rizzo@icir.org>
Thu, 7 Dec 2006 16:42:29 +0000 (16:42 +0000)
committerLuigi Rizzo <rizzo@icir.org>
Thu, 7 Dec 2006 16:42:29 +0000 (16:42 +0000)
  are passed as an argument.

- Update the code in main/http.c to use the new interface
  (the diff is large but mostly mechanical, due to the name change of
  several variables);

- And since now it is trivial, implement "AMI over TLS", and document
  the possible options in manager.conf

- And since the test client (openssl s_client -connect host:port )
  does not generate \r\n as a line terminator, make get_input()
  also accept just a \n as a line terminator (Mac users: do you
  also need the \r-only version ?)

The option parsing in manager.conf is not very efficient, and needs
to be cleaned up and made similar to what we have in http.conf

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@48351 65c4cc65-6c06-0410-ace0-fbb531ad65f3

configs/manager.conf.sample
include/asterisk/http.h
main/http.c
main/manager.c

index ee1b063..660ab84 100644 (file)
@@ -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)
index f14edd8..bfd39c0 100644 (file)
 #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
index ba0f5f5..96e0bd8 100644 (file)
@@ -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));
index cdb43d3..269f708 100644 (file)
@@ -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;
 }