]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
- 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 ee1b063dab6e60c548e55ad9c26183f604dd4433..660ab843e085187f13afb64580848b9745081619 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 f14edd88309999a420228d084e41f3261705efde..bfd39c03967646fe0d5876688254012e2d55f60a 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 ba0f5f5e2d8c7cb2355edc7df8a27a62bf6023e5..96e0bd82b3c58de752ca922737e9358bf06de7fb 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 cdb43d3e23cb84e252162cca44a9463743d6c88d..269f708d49aea985dc0dd07e18744ede69c02158 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;
 }